Coverage Report

Created: 2026-09-14 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
134k
{
69
134k
  if (!buffer)
70
0
    return;
71
72
134k
#if defined(USE_UNWIND)
73
134k
  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
134k
}
85
86
void* winpr_backtrace(DWORD size)
87
134k
{
88
134k
#if defined(USE_UNWIND)
89
134k
  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
134k
}
103
104
char** winpr_backtrace_symbols(void* buffer, size_t* used)
105
134k
{
106
134k
  if (used)
107
134k
    *used = 0;
108
109
134k
  if (!buffer)
110
0
  {
111
0
    WLog_FATAL(TAG, "%s", support_msg);
112
0
    return nullptr;
113
0
  }
114
115
134k
#if defined(USE_UNWIND)
116
134k
  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
134k
}
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
134k
{
180
134k
  if (!WLog_IsLevelActive(log, level))
181
0
    return;
182
183
134k
  size_t used = 0;
184
134k
  char** msg = nullptr;
185
134k
  void* stack = winpr_backtrace(20);
186
187
134k
  if (!stack)
188
0
  {
189
0
    WLog_Print(log, WLOG_ERROR, "winpr_backtrace failed!\n");
190
0
    goto fail;
191
0
  }
192
193
134k
  msg = winpr_backtrace_symbols(stack, &used);
194
195
134k
  if (msg)
196
134k
  {
197
2.16M
    for (size_t x = 0; x < used; x++)
198
2.03M
      WLog_Print(log, level, "%" PRIuz ": %s", x, msg[x]);
199
134k
  }
200
134k
  free((void*)msg);
201
202
134k
fail:
203
134k
  winpr_backtrace_free(stack);
204
134k
}
205
206
char* winpr_strerror(INT32 dw, char* dmsg, size_t size)
207
0
{
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
0
  return dmsg;
216
0
}
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
}