/src/FreeRDP/winpr/libwinpr/utils/debug.c
Line | Count | Source |
1 | | /** |
2 | | * WinPR: Windows Portable Runtime |
3 | | * Debugging Utils |
4 | | * |
5 | | * Copyright 2014 Armin Novak <armin.novak@thincast.com> |
6 | | * Copyright 2014 Thincast Technologies GmbH |
7 | | * |
8 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
9 | | * you may not use this file except in compliance with the License. |
10 | | * You may obtain a copy of the License at |
11 | | * |
12 | | * http://www.apache.org/licenses/LICENSE-2.0 |
13 | | * |
14 | | * Unless required by applicable law or agreed to in writing, software |
15 | | * distributed under the License is distributed on an "AS IS" BASIS, |
16 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
17 | | * See the License for the specific language governing permissions and |
18 | | * limitations under the License. |
19 | | */ |
20 | | |
21 | | #include <winpr/config.h> |
22 | | #include <winpr/buildflags.h> |
23 | | #include <winpr/platform.h> |
24 | | |
25 | | WINPR_PRAGMA_DIAG_PUSH |
26 | | WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO |
27 | | WINPR_PRAGMA_DIAG_IGNORED_UNUSED_MACRO |
28 | | |
29 | | #define __STDC_WANT_LIB_EXT1__ 1 // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) |
30 | | |
31 | | WINPR_PRAGMA_DIAG_POP |
32 | | |
33 | | #include <stdio.h> |
34 | | #include <string.h> |
35 | | #include <fcntl.h> |
36 | | #include <errno.h> |
37 | | |
38 | | #include <winpr/crt.h> |
39 | | #include <winpr/string.h> |
40 | | |
41 | | #if defined(USE_EXECINFO) |
42 | | #include <execinfo/debug.h> |
43 | | #endif |
44 | | |
45 | | #if defined(USE_UNWIND) |
46 | | #include <unwind/debug.h> |
47 | | #endif |
48 | | |
49 | | #if defined(WINPR_HAVE_CORKSCREW) |
50 | | #include <corkscrew/debug.h> |
51 | | #endif |
52 | | |
53 | | #if defined(_WIN32) || defined(_WIN64) |
54 | | #include <io.h> |
55 | | #include <windows/debug.h> |
56 | | #endif |
57 | | |
58 | | #include <winpr/wlog.h> |
59 | | #include <winpr/debug.h> |
60 | | |
61 | | #define TAG "com.winpr.utils.debug" |
62 | | |
63 | 0 | #define LINE_LENGTH_MAX 2048 |
64 | | |
65 | | static const char support_msg[] = "Invalid stacktrace buffer! check if platform is supported!"; |
66 | | |
67 | | void winpr_backtrace_free(void* buffer) |
68 | 519 | { |
69 | 519 | if (!buffer) |
70 | 0 | return; |
71 | | |
72 | 519 | #if defined(USE_UNWIND) |
73 | 519 | winpr_unwind_backtrace_free(buffer); |
74 | | #elif defined(USE_EXECINFO) |
75 | | winpr_execinfo_backtrace_free(buffer); |
76 | | #elif defined(WINPR_HAVE_CORKSCREW) |
77 | | winpr_corkscrew_backtrace_free(buffer); |
78 | | #elif defined(_WIN32) || defined(_WIN64) |
79 | | winpr_win_backtrace_free(buffer); |
80 | | #else |
81 | | free(buffer); |
82 | | WLog_FATAL(TAG, "%s", support_msg); |
83 | | #endif |
84 | 519 | } |
85 | | |
86 | | void* winpr_backtrace(DWORD size) |
87 | 519 | { |
88 | 519 | #if defined(USE_UNWIND) |
89 | 519 | return winpr_unwind_backtrace(size); |
90 | | #elif defined(USE_EXECINFO) |
91 | | return winpr_execinfo_backtrace(size); |
92 | | #elif defined(WINPR_HAVE_CORKSCREW) |
93 | | return winpr_corkscrew_backtrace(size); |
94 | | #elif (defined(_WIN32) || defined(_WIN64)) && !defined(_UWP) |
95 | | return winpr_win_backtrace(size); |
96 | | #else |
97 | | WLog_FATAL(TAG, "%s", support_msg); |
98 | | /* return a non nullptr buffer to allow the backtrace function family to succeed without failing |
99 | | */ |
100 | | return strndup(support_msg, sizeof(support_msg)); |
101 | | #endif |
102 | 519 | } |
103 | | |
104 | | char** winpr_backtrace_symbols(void* buffer, size_t* used) |
105 | 519 | { |
106 | 519 | if (used) |
107 | 519 | *used = 0; |
108 | | |
109 | 519 | if (!buffer) |
110 | 0 | { |
111 | 0 | WLog_FATAL(TAG, "%s", support_msg); |
112 | 0 | return nullptr; |
113 | 0 | } |
114 | | |
115 | 519 | #if defined(USE_UNWIND) |
116 | 519 | return winpr_unwind_backtrace_symbols(buffer, used); |
117 | | #elif defined(USE_EXECINFO) |
118 | | return winpr_execinfo_backtrace_symbols(buffer, used); |
119 | | #elif defined(WINPR_HAVE_CORKSCREW) |
120 | | return winpr_corkscrew_backtrace_symbols(buffer, used); |
121 | | #elif (defined(_WIN32) || defined(_WIN64)) && !defined(_UWP) |
122 | | return winpr_win_backtrace_symbols(buffer, used); |
123 | | #else |
124 | | WLog_FATAL(TAG, "%s", support_msg); |
125 | | |
126 | | /* We return a char** on heap that is compatible with free: |
127 | | * |
128 | | * 1. We allocate sizeof(char*) + strlen + 1 bytes. |
129 | | * 2. The first sizeof(char*) bytes contain the pointer to the string following the pointer. |
130 | | * 3. The at data + sizeof(char*) contains the actual string |
131 | | */ |
132 | | size_t len = strnlen(support_msg, sizeof(support_msg)); |
133 | | char* ppmsg = calloc(sizeof(char*) + len + 1, sizeof(char)); |
134 | | if (!ppmsg) |
135 | | return nullptr; |
136 | | char** msgptr = (char**)ppmsg; |
137 | | char* msg = &ppmsg[sizeof(char*)]; |
138 | | |
139 | | *msgptr = msg; |
140 | | strncpy(msg, support_msg, len); |
141 | | *used = 1; |
142 | | return msgptr; |
143 | | #endif |
144 | 519 | } |
145 | | |
146 | | void winpr_backtrace_symbols_fd(void* buffer, int fd) |
147 | 0 | { |
148 | 0 | if (!buffer) |
149 | 0 | { |
150 | 0 | WLog_FATAL(TAG, "%s", support_msg); |
151 | 0 | return; |
152 | 0 | } |
153 | | |
154 | | #if defined(USE_EXECINFO) && !defined(USE_UNWIND) |
155 | | winpr_execinfo_backtrace_symbols_fd(buffer, fd); |
156 | | #elif !defined(ANDROID) |
157 | 0 | { |
158 | 0 | size_t used = 0; |
159 | 0 | char** lines = winpr_backtrace_symbols(buffer, &used); |
160 | |
|
161 | 0 | if (!lines) |
162 | 0 | return; |
163 | | |
164 | 0 | for (size_t i = 0; i < used; i++) |
165 | 0 | (void)_write(fd, lines[i], (unsigned)strnlen(lines[i], LINE_LENGTH_MAX)); |
166 | 0 | free((void*)lines); |
167 | 0 | } |
168 | | #else |
169 | | WLog_FATAL(TAG, "%s", support_msg); |
170 | | #endif |
171 | 0 | } |
172 | | |
173 | | void winpr_log_backtrace(const char* tag, DWORD level, DWORD size) |
174 | 0 | { |
175 | 0 | winpr_log_backtrace_ex(WLog_Get(tag), level, size); |
176 | 0 | } |
177 | | |
178 | | void winpr_log_backtrace_ex(wLog* log, DWORD level, WINPR_ATTR_UNUSED DWORD size) |
179 | 519 | { |
180 | 519 | if (!WLog_IsLevelActive(log, level)) |
181 | 0 | return; |
182 | | |
183 | 519 | size_t used = 0; |
184 | 519 | char** msg = nullptr; |
185 | 519 | void* stack = winpr_backtrace(20); |
186 | | |
187 | 519 | if (!stack) |
188 | 0 | { |
189 | 0 | WLog_Print(log, WLOG_ERROR, "winpr_backtrace failed!\n"); |
190 | 0 | goto fail; |
191 | 0 | } |
192 | | |
193 | 519 | msg = winpr_backtrace_symbols(stack, &used); |
194 | | |
195 | 519 | if (msg) |
196 | 519 | { |
197 | 8.39k | for (size_t x = 0; x < used; x++) |
198 | 7.87k | WLog_Print(log, level, "%" PRIuz ": %s", x, msg[x]); |
199 | 519 | } |
200 | 519 | free((void*)msg); |
201 | | |
202 | 519 | fail: |
203 | 519 | winpr_backtrace_free(stack); |
204 | 519 | } |
205 | | |
206 | | char* winpr_strerror(INT32 dw, char* dmsg, size_t size) |
207 | 51.9k | { |
208 | | #ifdef __STDC_LIB_EXT1__ |
209 | | (void)strerror_s(dw, dmsg, size); |
210 | | #elif defined(WINPR_HAVE_STRERROR_R) |
211 | | (void)strerror_r(dw, dmsg, size); |
212 | | #else |
213 | | (void)_snprintf(dmsg, size, "%s", strerror(dw)); |
214 | | #endif |
215 | 51.9k | return dmsg; |
216 | 51.9k | } |
217 | | |
218 | | WINPR_ATTR_NODISCARD |
219 | | static BOOL starts_with(const char* tok, const char* val) |
220 | 0 | { |
221 | 0 | const size_t len = strlen(val); |
222 | 0 | if (strncmp(tok, val, len) != 0) |
223 | 0 | return FALSE; |
224 | | |
225 | 0 | if (!strchr(tok, '=')) |
226 | 0 | return FALSE; |
227 | 0 | return TRUE; |
228 | 0 | } |
229 | | |
230 | | WINPR_ATTR_NODISCARD |
231 | | static BOOL option_equals(const char* what, const char* val) |
232 | 0 | { |
233 | 0 | return _stricmp(what, val) == 0; |
234 | 0 | } |
235 | | |
236 | | WINPR_ATTR_NODISCARD |
237 | | static BOOL parse_on_off_option(const char* value) |
238 | 0 | { |
239 | 0 | WINPR_ASSERT(value); |
240 | 0 | const char* sep = strchr(value, '='); |
241 | 0 | if (!sep) |
242 | 0 | return TRUE; |
243 | 0 | if (option_equals("on", &sep[1])) |
244 | 0 | return TRUE; |
245 | 0 | if (option_equals("true", &sep[1])) |
246 | 0 | return TRUE; |
247 | 0 | if (option_equals("off", &sep[1])) |
248 | 0 | return FALSE; |
249 | 0 | if (option_equals("false", &sep[1])) |
250 | 0 | return FALSE; |
251 | | |
252 | 0 | errno = 0; |
253 | 0 | long val = strtol(value, nullptr, 0); |
254 | 0 | if (errno == 0) |
255 | 0 | return (val != 0); |
256 | | |
257 | 0 | return FALSE; |
258 | 0 | } |
259 | | |
260 | | WINPR_ATTR_NODISCARD |
261 | | static BOOL option_is_debug(wLog* log, DWORD level, const char* tok) |
262 | 0 | { |
263 | 0 | WINPR_ASSERT(log); |
264 | 0 | WINPR_UNUSED(log); |
265 | 0 | WINPR_UNUSED(level); |
266 | |
|
267 | 0 | if (starts_with(tok, "WITH_DEBUG_")) |
268 | 0 | return parse_on_off_option(tok); |
269 | | |
270 | 0 | return FALSE; |
271 | 0 | } |
272 | | |
273 | | static void log_build_warn(wLog* log, DWORD level, const char* what, const char* msg, |
274 | | BOOL (*cmp)(wLog* log, DWORD level, const char* tok), const char* input, |
275 | | size_t ilen) |
276 | 0 | { |
277 | 0 | WINPR_ASSERT(log); |
278 | |
|
279 | 0 | if (!input || (ilen == 0)) |
280 | 0 | return; |
281 | | |
282 | 0 | char* list = calloc(ilen, sizeof(char)); |
283 | 0 | char* config = _strdup(input); |
284 | |
|
285 | 0 | if (config && list) |
286 | 0 | { |
287 | 0 | char* saveptr = nullptr; |
288 | 0 | char* tok = strtok_s(config, " ", &saveptr); |
289 | 0 | while (tok) |
290 | 0 | { |
291 | 0 | if (!cmp || cmp(log, level, tok)) |
292 | 0 | winpr_str_append(tok, list, ilen, " "); |
293 | |
|
294 | 0 | tok = strtok_s(nullptr, " ", &saveptr); |
295 | 0 | } |
296 | 0 | } |
297 | 0 | free(config); |
298 | |
|
299 | 0 | if (list) |
300 | 0 | { |
301 | 0 | if (strlen(list) > 0) |
302 | 0 | { |
303 | 0 | WLog_Print(log, level, "*************************************************"); |
304 | 0 | WLog_Print(log, level, "This WinPR build is using [%s] build options:", what); |
305 | |
|
306 | 0 | char* saveptr = nullptr; |
307 | 0 | char* tok = strtok_s(list, " ", &saveptr); |
308 | 0 | while (tok) |
309 | 0 | { |
310 | 0 | WLog_Print(log, level, "* '%s'", tok); |
311 | 0 | tok = strtok_s(nullptr, " ", &saveptr); |
312 | 0 | } |
313 | 0 | WLog_Print(log, level, "*"); |
314 | 0 | WLog_Print(log, level, "[%s] build options %s", what, msg); |
315 | 0 | WLog_Print(log, level, "*************************************************"); |
316 | 0 | } |
317 | 0 | } |
318 | 0 | free(list); |
319 | 0 | } |
320 | | |
321 | | void winpr_log_build_warn(wLog* log, DWORD level) |
322 | 0 | { |
323 | | #if !defined(WINPR_ARCH_SUPPORTED) || (WINPR_ARCH_SUPPORTED == 0) || \ |
324 | | defined(DISABLE_SUPPORTED_ARCH_CHECKS) |
325 | | #define STR(x) #x |
326 | | #endif |
327 | |
|
328 | 0 | const char configurations[] = { |
329 | | #if !defined(WINPR_ARCH_SUPPORTED) || (WINPR_ARCH_SUPPORTED == 0) |
330 | | STR(WINPR_ARCH_SUPPORTED) "==0 " |
331 | | #endif |
332 | | #if defined(DISABLE_SUPPORTED_ARCH_CHECKS) |
333 | | STR(DISABLE_SUPPORTED_ARCH_CHECKS) " " |
334 | | #endif |
335 | 0 | "\0" |
336 | 0 | }; |
337 | 0 | WINPR_ASSERT(log); |
338 | 0 | log_build_warn(log, level, "experimental", |
339 | 0 | "might crash, overwrite data or steal your kitten, you have been warned!", |
340 | 0 | nullptr, configurations, sizeof(configurations)); |
341 | |
|
342 | 0 | WINPR_PRAGMA_DIAG_PUSH |
343 | 0 | WINPR_PRAGMA_DIAG_IGNORED_OVERLENGTH_STRINGS |
344 | 0 | log_build_warn(log, level, "debug", |
345 | 0 | "might leak sensitive information (credentials, ...), slow down runtime, " |
346 | 0 | "increase memory usage", |
347 | 0 | option_is_debug, WINPR_BUILD_CONFIG, sizeof(WINPR_BUILD_CONFIG)); |
348 | 0 | WINPR_PRAGMA_DIAG_POP |
349 | 0 | } |