1 | // Copyright (c) Facebook, Inc. and its affiliates. |
2 | // All rights reserved. |
3 | // |
4 | // Copyright 2022 Google LLC |
5 | // |
6 | // This source code is licensed under the BSD-style license found in the |
7 | // LICENSE file in the root directory of this source tree. |
8 | |
9 | #include <assert.h> |
10 | #include <stdarg.h> |
11 | #include <string.h> |
12 | #include <stdlib.h> |
13 | #include <stdio.h> |
14 | #ifdef _WIN32 |
15 | #include <windows.h> |
16 | #else |
17 | #include <unistd.h> |
18 | #endif |
19 | #if defined(__ANDROID__) |
20 | #include <android/log.h> |
21 | #endif |
22 | #if defined(__hexagon__) |
23 | #include <qurt_printf.h> |
24 | #endif |
25 | |
26 | #ifndef XNN_LOG_TO_STDIO |
27 | #if defined(__ANDROID__) |
28 | #define XNN_LOG_TO_STDIO 0 |
29 | #else |
30 | #define XNN_LOG_TO_STDIO 1 |
31 | #endif |
32 | #endif |
33 | |
34 | #include <xnnpack/log.h> |
35 | |
36 | |
37 | /* Messages up to this size are formatted entirely on-stack, and don't allocate heap memory */ |
38 | #define XNN_LOG_STACK_BUFFER_SIZE 1024 |
39 | |
40 | #ifdef _WIN32 |
41 | #define XNN_LOG_NEWLINE_LENGTH 2 |
42 | |
43 | #define XNN_LOG_STDERR STD_ERROR_HANDLE |
44 | #define XNN_LOG_STDOUT STD_OUTPUT_HANDLE |
45 | #elif defined(__hexagon__) |
46 | #define XNN_LOG_NEWLINE_LENGTH 1 |
47 | |
48 | #define XNN_LOG_STDERR 0 |
49 | #define XNN_LOG_STDOUT 0 |
50 | #else |
51 | #define XNN_LOG_NEWLINE_LENGTH 1 |
52 | |
53 | #define XNN_LOG_STDERR STDERR_FILENO |
54 | #define XNN_LOG_STDOUT STDOUT_FILENO |
55 | #endif |
56 | |
57 | #if XNN_LOG_TO_STDIO |
58 | static void xnn_vlog(int output_handle, const char* prefix, size_t prefix_length, const char* format, va_list args) { |
59 | char stack_buffer[XNN_LOG_STACK_BUFFER_SIZE]; |
60 | char* heap_buffer = NULL; |
61 | char* out_buffer = &stack_buffer[0]; |
62 | |
63 | /* The first call to vsnprintf will clobber args, thus need a copy in case a second vsnprintf call is needed */ |
64 | va_list args_copy; |
65 | va_copy(args_copy, args); |
66 | |
67 | memcpy(stack_buffer, prefix, prefix_length * sizeof(char)); |
68 | assert((prefix_length + XNN_LOG_NEWLINE_LENGTH) * sizeof(char) <= XNN_LOG_STACK_BUFFER_SIZE); |
69 | |
70 | const int format_chars = vsnprintf( |
71 | &stack_buffer[prefix_length], |
72 | XNN_LOG_STACK_BUFFER_SIZE - (prefix_length + XNN_LOG_NEWLINE_LENGTH) * sizeof(char), |
73 | format, |
74 | args); |
75 | if (format_chars < 0) { |
76 | /* Format error in the message: silently ignore this particular message. */ |
77 | goto cleanup; |
78 | } |
79 | const size_t format_length = (size_t) format_chars; |
80 | if ((prefix_length + format_length + XNN_LOG_NEWLINE_LENGTH) * sizeof(char) > XNN_LOG_STACK_BUFFER_SIZE) { |
81 | /* Allocate a buffer on heap, and vsnprintf to this buffer */ |
82 | const size_t heap_buffer_size = (prefix_length + format_length + XNN_LOG_NEWLINE_LENGTH) * sizeof(char); |
83 | #if _WIN32 |
84 | heap_buffer = HeapAlloc(GetProcessHeap(), 0, heap_buffer_size); |
85 | #else |
86 | heap_buffer = malloc(heap_buffer_size); |
87 | #endif |
88 | if (heap_buffer == NULL) { |
89 | goto cleanup; |
90 | } |
91 | |
92 | /* Copy pre-formatted prefix into the on-heap buffer */ |
93 | memcpy(heap_buffer, prefix, prefix_length * sizeof(char)); |
94 | vsnprintf(&heap_buffer[prefix_length], (format_length + XNN_LOG_NEWLINE_LENGTH) * sizeof(char), format, args_copy); |
95 | out_buffer = heap_buffer; |
96 | } |
97 | #ifdef _WIN32 |
98 | out_buffer[prefix_length + format_length] = '\r'; |
99 | out_buffer[prefix_length + format_length + 1] = '\n'; |
100 | |
101 | DWORD bytes_written; |
102 | WriteFile( |
103 | GetStdHandle((DWORD) output_handle), |
104 | out_buffer, (prefix_length + format_length + XNN_LOG_NEWLINE_LENGTH) * sizeof(char), |
105 | &bytes_written, NULL); |
106 | #elif defined(__hexagon__) |
107 | qurt_printf("%s" , out_buffer); |
108 | #else |
109 | out_buffer[prefix_length + format_length] = '\n'; |
110 | |
111 | ssize_t bytes_written = write(output_handle, out_buffer, (prefix_length + format_length + XNN_LOG_NEWLINE_LENGTH) * sizeof(char)); |
112 | (void) bytes_written; |
113 | #endif |
114 | |
115 | cleanup: |
116 | #ifdef _WIN32 |
117 | HeapFree(GetProcessHeap(), 0, heap_buffer); |
118 | #else |
119 | free(heap_buffer); |
120 | #endif |
121 | va_end(args_copy); |
122 | } |
123 | #elif defined(__ANDROID__) && XNN_LOG_LEVEL > XNN_LOG_NONE |
124 | static const char xnnpack_module[] = "XNNPACK" ; |
125 | #endif |
126 | |
127 | #if XNN_LOG_LEVEL >= XNN_LOG_DEBUG |
128 | void xnn_vlog_debug(const char* format, va_list args) { |
129 | #if XNN_LOG_TO_STDIO |
130 | static const char debug_prefix[17] = { |
131 | 'D', 'e', 'b', 'u', 'g', ' ', '(', 'X', 'N', 'N', 'P', 'A', 'C', 'K', ')', ':', ' ' |
132 | }; |
133 | xnn_vlog(XNN_LOG_STDOUT, debug_prefix, 17, format, args); |
134 | #elif defined(__ANDROID__) |
135 | __android_log_vprint(ANDROID_LOG_DEBUG, xnnpack_module, format, args); |
136 | #else |
137 | #error "Platform-specific implementation required" |
138 | #endif |
139 | } |
140 | #endif |
141 | |
142 | #if XNN_LOG_LEVEL >= XNN_LOG_INFO |
143 | void xnn_vlog_info(const char* format, va_list args) { |
144 | #if XNN_LOG_TO_STDIO |
145 | static const char info_prefix[16] = { |
146 | 'N', 'o', 't', 'e', ' ', '(', 'X', 'N', 'N', 'P', 'A', 'C', 'K', ')', ':', ' ' |
147 | }; |
148 | xnn_vlog(XNN_LOG_STDOUT, info_prefix, 16, format, args); |
149 | #elif defined(__ANDROID__) |
150 | __android_log_vprint(ANDROID_LOG_INFO, xnnpack_module, format, args); |
151 | #else |
152 | #error "Platform-specific implementation required" |
153 | #endif |
154 | } |
155 | #endif |
156 | |
157 | #if XNN_LOG_LEVEL >= XNN_LOG_WARNING |
158 | void xnn_vlog_warning(const char* format, va_list args) { |
159 | #if XNN_LOG_TO_STDIO |
160 | static const char warning_prefix[20] = { |
161 | 'W', 'a', 'r', 'n', 'i', 'n', 'g', ' ', 'i', 'n', ' ', 'X', 'N', 'N', 'P', 'A', 'C', 'K', ':', ' ' |
162 | }; |
163 | xnn_vlog(XNN_LOG_STDERR, warning_prefix, 20, format, args); |
164 | #elif defined(__ANDROID__) |
165 | __android_log_vprint(ANDROID_LOG_WARN, xnnpack_module, format, args); |
166 | #else |
167 | #error "Platform-specific implementation required" |
168 | #endif |
169 | } |
170 | #endif |
171 | |
172 | #if XNN_LOG_LEVEL >= XNN_LOG_ERROR |
173 | void xnn_vlog_error(const char* format, va_list args) { |
174 | #if XNN_LOG_TO_STDIO |
175 | static const char error_prefix[18] = { |
176 | 'E', 'r', 'r', 'o', 'r', ' ', 'i', 'n', ' ', 'X', 'N', 'N', 'P', 'A', 'C', 'K', ':', ' ' |
177 | }; |
178 | xnn_vlog(XNN_LOG_STDERR, error_prefix, 18, format, args); |
179 | #elif defined(__ANDROID__) |
180 | __android_log_vprint(ANDROID_LOG_ERROR, xnnpack_module, format, args); |
181 | #else |
182 | #error "Platform-specific implementation required" |
183 | #endif |
184 | } |
185 | #endif |
186 | |
187 | #if XNN_LOG_LEVEL >= XNN_LOG_FATAL |
188 | void xnn_vlog_fatal(const char* format, va_list args) { |
189 | #if XNN_LOG_TO_STDIO |
190 | static const char fatal_prefix[24] = { |
191 | 'F', 'a', 't', 'a', 'l', ' ', 'e', 'r', 'r', 'o', 'r', ' ', 'i', 'n', ' ', 'X', 'N', 'N', 'P', 'A', 'C', 'K', ':', ' ' |
192 | }; |
193 | xnn_vlog(XNN_LOG_STDERR, fatal_prefix, 24, format, args); |
194 | #elif defined(__ANDROID__) |
195 | __android_log_vprint(ANDROID_LOG_FATAL, xnnpack_module, format, args); |
196 | #else |
197 | #error "Platform-specific implementation required" |
198 | #endif |
199 | } |
200 | #endif |
201 | |