Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/winpr/libwinpr/utils/debug.c
Line
Count
Source (jump to first uncovered line)
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/platform.h>
23
24
WINPR_PRAGMA_DIAG_PUSH
25
WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO
26
WINPR_PRAGMA_DIAG_IGNORED_UNUSED_MACRO
27
28
#define __STDC_WANT_LIB_EXT1__ 1 // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
29
30
WINPR_PRAGMA_DIAG_POP
31
32
#include <stdio.h>
33
#include <string.h>
34
#include <fcntl.h>
35
36
#include <winpr/crt.h>
37
#include <winpr/string.h>
38
39
#if defined(USE_EXECINFO)
40
#include <execinfo/debug.h>
41
#endif
42
43
#if defined(USE_UNWIND)
44
#include <unwind/debug.h>
45
#endif
46
47
#if defined(WINPR_HAVE_CORKSCREW)
48
#include <corkscrew/debug.h>
49
#endif
50
51
#if defined(_WIN32) || defined(_WIN64)
52
#include <io.h>
53
#include <windows/debug.h>
54
#endif
55
56
#include <winpr/wlog.h>
57
#include <winpr/debug.h>
58
59
#define TAG "com.winpr.utils.debug"
60
61
0
#define LINE_LENGTH_MAX 2048
62
63
static const char support_msg[] = "Invalid stacktrace buffer! check if platform is supported!";
64
65
void winpr_backtrace_free(void* buffer)
66
0
{
67
0
  if (!buffer)
68
0
    return;
69
70
0
#if defined(USE_UNWIND)
71
0
  winpr_unwind_backtrace_free(buffer);
72
#elif defined(USE_EXECINFO)
73
  winpr_execinfo_backtrace_free(buffer);
74
#elif defined(WINPR_HAVE_CORKSCREW)
75
  winpr_corkscrew_backtrace_free(buffer);
76
#elif defined(_WIN32) || defined(_WIN64)
77
  winpr_win_backtrace_free(buffer);
78
#else
79
  free(buffer);
80
  WLog_FATAL(TAG, "%s", support_msg);
81
#endif
82
0
}
83
84
void* winpr_backtrace(DWORD size)
85
0
{
86
0
#if defined(USE_UNWIND)
87
0
  return winpr_unwind_backtrace(size);
88
#elif defined(USE_EXECINFO)
89
  return winpr_execinfo_backtrace(size);
90
#elif defined(WINPR_HAVE_CORKSCREW)
91
  return winpr_corkscrew_backtrace(size);
92
#elif (defined(_WIN32) || defined(_WIN64)) && !defined(_UWP)
93
  return winpr_win_backtrace(size);
94
#else
95
  WLog_FATAL(TAG, "%s", support_msg);
96
  /* return a non NULL buffer to allow the backtrace function family to succeed without failing
97
   */
98
  return strndup(support_msg, sizeof(support_msg));
99
#endif
100
0
}
101
102
char** winpr_backtrace_symbols(void* buffer, size_t* used)
103
0
{
104
0
  if (used)
105
0
    *used = 0;
106
107
0
  if (!buffer)
108
0
  {
109
0
    WLog_FATAL(TAG, "%s", support_msg);
110
0
    return NULL;
111
0
  }
112
113
0
#if defined(USE_UNWIND)
114
0
  return winpr_unwind_backtrace_symbols(buffer, used);
115
#elif defined(USE_EXECINFO)
116
  return winpr_execinfo_backtrace_symbols(buffer, used);
117
#elif defined(WINPR_HAVE_CORKSCREW)
118
  return winpr_corkscrew_backtrace_symbols(buffer, used);
119
#elif (defined(_WIN32) || defined(_WIN64)) && !defined(_UWP)
120
  return winpr_win_backtrace_symbols(buffer, used);
121
#else
122
  WLog_FATAL(TAG, "%s", support_msg);
123
124
  /* We return a char** on heap that is compatible with free:
125
   *
126
   * 1. We allocate sizeof(char*) + strlen + 1 bytes.
127
   * 2. The first sizeof(char*) bytes contain the pointer to the string following the pointer.
128
   * 3. The at data + sizeof(char*) contains the actual string
129
   */
130
  size_t len = strnlen(support_msg, sizeof(support_msg));
131
  char* ppmsg = calloc(sizeof(char*) + len + 1, sizeof(char));
132
  if (!ppmsg)
133
    return NULL;
134
  char** msgptr = (char**)ppmsg;
135
  char* msg = &ppmsg[sizeof(char*)];
136
137
  *msgptr = msg;
138
  strncpy(msg, support_msg, len);
139
  *used = 1;
140
  return msgptr;
141
#endif
142
0
}
143
144
void winpr_backtrace_symbols_fd(void* buffer, int fd)
145
0
{
146
0
  if (!buffer)
147
0
  {
148
0
    WLog_FATAL(TAG, "%s", support_msg);
149
0
    return;
150
0
  }
151
152
#if defined(USE_EXECINFO) && !defined(USE_UNWIND)
153
  winpr_execinfo_backtrace_symbols_fd(buffer, fd);
154
#elif !defined(ANDROID)
155
0
  {
156
0
    size_t used = 0;
157
0
    char** lines = winpr_backtrace_symbols(buffer, &used);
158
159
0
    if (!lines)
160
0
      return;
161
162
0
    for (size_t i = 0; i < used; i++)
163
0
      (void)_write(fd, lines[i], (unsigned)strnlen(lines[i], LINE_LENGTH_MAX));
164
0
    free((void*)lines);
165
0
  }
166
#else
167
  WLog_FATAL(TAG, "%s", support_msg);
168
#endif
169
0
}
170
171
void winpr_log_backtrace(const char* tag, DWORD level, DWORD size)
172
0
{
173
0
  winpr_log_backtrace_ex(WLog_Get(tag), level, size);
174
0
}
175
176
void winpr_log_backtrace_ex(wLog* log, DWORD level, WINPR_ATTR_UNUSED DWORD size)
177
0
{
178
0
  size_t used = 0;
179
0
  char** msg = NULL;
180
0
  void* stack = winpr_backtrace(20);
181
182
0
  if (!stack)
183
0
  {
184
0
    WLog_Print(log, WLOG_ERROR, "winpr_backtrace failed!\n");
185
0
    goto fail;
186
0
  }
187
188
0
  msg = winpr_backtrace_symbols(stack, &used);
189
190
0
  if (msg)
191
0
  {
192
0
    for (size_t x = 0; x < used; x++)
193
0
      WLog_Print(log, level, "%" PRIuz ": %s", x, msg[x]);
194
0
  }
195
0
  free((void*)msg);
196
197
0
fail:
198
0
  winpr_backtrace_free(stack);
199
0
}
200
201
char* winpr_strerror(INT32 dw, char* dmsg, size_t size)
202
0
{
203
#ifdef __STDC_LIB_EXT1__
204
  (void)strerror_s(dw, dmsg, size);
205
#elif defined(WINPR_HAVE_STRERROR_R)
206
  (void)strerror_r(dw, dmsg, size);
207
#else
208
  (void)_snprintf(dmsg, size, "%s", strerror(dw));
209
#endif
210
0
  return dmsg;
211
0
}