FreeRDP
Loading...
Searching...
No Matches
debug.c
1
21#include <winpr/config.h>
22#include <winpr/buildflags.h>
23#include <winpr/platform.h>
24
25WINPR_PRAGMA_DIAG_PUSH
26WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO
27WINPR_PRAGMA_DIAG_IGNORED_UNUSED_MACRO
28
29#define __STDC_WANT_LIB_EXT1__ 1 // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
30
31WINPR_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#define LINE_LENGTH_MAX 2048
64
65static const char support_msg[] = "Invalid stacktrace buffer! check if platform is supported!";
66
67void winpr_backtrace_free(void* buffer)
68{
69 if (!buffer)
70 return;
71
72#if defined(USE_UNWIND)
73 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}
85
86void* winpr_backtrace(DWORD size)
87{
88#if defined(USE_UNWIND)
89 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}
103
104char** winpr_backtrace_symbols(void* buffer, size_t* used)
105{
106 if (used)
107 *used = 0;
108
109 if (!buffer)
110 {
111 WLog_FATAL(TAG, "%s", support_msg);
112 return nullptr;
113 }
114
115#if defined(USE_UNWIND)
116 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}
145
146void winpr_backtrace_symbols_fd(void* buffer, int fd)
147{
148 if (!buffer)
149 {
150 WLog_FATAL(TAG, "%s", support_msg);
151 return;
152 }
153
154#if defined(USE_EXECINFO) && !defined(USE_UNWIND)
155 winpr_execinfo_backtrace_symbols_fd(buffer, fd);
156#elif !defined(ANDROID)
157 {
158 size_t used = 0;
159 char** lines = winpr_backtrace_symbols(buffer, &used);
160
161 if (!lines)
162 return;
163
164 for (size_t i = 0; i < used; i++)
165 (void)_write(fd, lines[i], (unsigned)strnlen(lines[i], LINE_LENGTH_MAX));
166 free((void*)lines);
167 }
168#else
169 WLog_FATAL(TAG, "%s", support_msg);
170#endif
171}
172
173void winpr_log_backtrace(const char* tag, DWORD level, DWORD size)
174{
175 winpr_log_backtrace_ex(WLog_Get(tag), level, size);
176}
177
178void winpr_log_backtrace_ex(wLog* log, DWORD level, WINPR_ATTR_UNUSED DWORD size)
179{
180 if (!WLog_IsLevelActive(log, level))
181 return;
182
183 size_t used = 0;
184 char** msg = nullptr;
185 void* stack = winpr_backtrace(20);
186
187 if (!stack)
188 {
189 WLog_Print(log, WLOG_ERROR, "winpr_backtrace failed!\n");
190 goto fail;
191 }
192
193 msg = winpr_backtrace_symbols(stack, &used);
194
195 if (msg)
196 {
197 for (size_t x = 0; x < used; x++)
198 WLog_Print(log, level, "%" PRIuz ": %s", x, msg[x]);
199 }
200 free((void*)msg);
201
202fail:
203 winpr_backtrace_free(stack);
204}
205
206char* winpr_strerror(INT32 dw, char* dmsg, size_t size)
207{
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 return dmsg;
216}
217
218WINPR_ATTR_NODISCARD
219static BOOL starts_with(const char* tok, const char* val)
220{
221 const size_t len = strlen(val);
222 if (strncmp(tok, val, len) != 0)
223 return FALSE;
224
225 if (!strchr(tok, '='))
226 return FALSE;
227 return TRUE;
228}
229
230WINPR_ATTR_NODISCARD
231static BOOL option_equals(const char* what, const char* val)
232{
233 return _stricmp(what, val) == 0;
234}
235
236WINPR_ATTR_NODISCARD
237static BOOL parse_on_off_option(const char* value)
238{
239 WINPR_ASSERT(value);
240 const char* sep = strchr(value, '=');
241 if (!sep)
242 return TRUE;
243 if (option_equals("on", &sep[1]))
244 return TRUE;
245 if (option_equals("true", &sep[1]))
246 return TRUE;
247 if (option_equals("off", &sep[1]))
248 return FALSE;
249 if (option_equals("false", &sep[1]))
250 return FALSE;
251
252 errno = 0;
253 long val = strtol(value, nullptr, 0);
254 if (errno == 0)
255 return (val != 0);
256
257 return FALSE;
258}
259
260WINPR_ATTR_NODISCARD
261static BOOL option_is_debug(wLog* log, DWORD level, const char* tok)
262{
263 WINPR_ASSERT(log);
264 WINPR_UNUSED(log);
265 WINPR_UNUSED(level);
266
267 if (starts_with(tok, "WITH_DEBUG_"))
268 return parse_on_off_option(tok);
269
270 return FALSE;
271}
272
273static 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{
277 WINPR_ASSERT(log);
278
279 if (!input || (ilen == 0))
280 return;
281
282 char* list = calloc(ilen, sizeof(char));
283 char* config = _strdup(input);
284
285 if (config && list)
286 {
287 char* saveptr = nullptr;
288 char* tok = strtok_s(config, " ", &saveptr);
289 while (tok)
290 {
291 if (!cmp || cmp(log, level, tok))
292 winpr_str_append(tok, list, ilen, " ");
293
294 tok = strtok_s(nullptr, " ", &saveptr);
295 }
296 }
297 free(config);
298
299 if (list)
300 {
301 if (strlen(list) > 0)
302 {
303 WLog_Print(log, level, "*************************************************");
304 WLog_Print(log, level, "This WinPR build is using [%s] build options:", what);
305
306 char* saveptr = nullptr;
307 char* tok = strtok_s(list, " ", &saveptr);
308 while (tok)
309 {
310 WLog_Print(log, level, "* '%s'", tok);
311 tok = strtok_s(nullptr, " ", &saveptr);
312 }
313 WLog_Print(log, level, "*");
314 WLog_Print(log, level, "[%s] build options %s", what, msg);
315 WLog_Print(log, level, "*************************************************");
316 }
317 }
318 free(list);
319}
320
321void winpr_log_build_warn(wLog* log, DWORD level)
322{
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 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 ""
336 };
337 WINPR_ASSERT(log);
338 log_build_warn(log, level, "experimental",
339 "might crash, overwrite data or steal your kitten, you have been warned!",
340 nullptr, configurations, sizeof(configurations));
341
342 WINPR_PRAGMA_DIAG_PUSH
343 WINPR_PRAGMA_DIAG_IGNORED_OVERLENGTH_STRINGS
344 log_build_warn(log, level, "debug",
345 "might leak sensitive information (credentials, ...), slow down runtime, "
346 "increase memory usage",
347 option_is_debug, WINPR_BUILD_CONFIG, sizeof(WINPR_BUILD_CONFIG));
348 WINPR_PRAGMA_DIAG_POP
349}