Coverage Report

Created: 2024-09-08 06:16

/src/FreeRDP/winpr/libwinpr/utils/unwind/debug.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * WinPR Debugging helpers
4
 *
5
 * Copyright 2022 Armin Novak <armin.novak@thincast.com>
6
 * Copyright 2022 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
#ifndef _GNU_SOURCE
22
#define _GNU_SOURCE // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
23
#endif
24
25
#include <assert.h>
26
#include <stdlib.h>
27
#include <unwind.h>
28
29
#include <winpr/string.h>
30
#include "debug.h"
31
32
#include <winpr/wlog.h>
33
#include "../log.h"
34
35
#include <dlfcn.h>
36
37
#define TAG WINPR_TAG("utils.unwind")
38
39
0
#define UNWIND_MAX_LINE_SIZE 1024ULL
40
41
typedef struct
42
{
43
  union
44
  {
45
    uintptr_t uw;
46
    void* pv;
47
  } pc;
48
  union
49
  {
50
    uintptr_t uptr;
51
    void* pv;
52
  } langSpecificData;
53
} unwind_info_t;
54
55
typedef struct
56
{
57
  size_t pos;
58
  size_t size;
59
  unwind_info_t* info;
60
} unwind_context_t;
61
62
static const char* unwind_reason_str(_Unwind_Reason_Code code)
63
0
{
64
0
  switch (code)
65
0
  {
66
#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__ARM_DWARF_EH__) && \
67
    !defined(__SEH__)
68
    case _URC_OK:
69
      return "_URC_OK";
70
#else
71
0
    case _URC_NO_REASON:
72
0
      return "_URC_NO_REASON";
73
0
#endif
74
0
    case _URC_FOREIGN_EXCEPTION_CAUGHT:
75
0
      return "_URC_FOREIGN_EXCEPTION_CAUGHT";
76
0
    case _URC_FATAL_PHASE2_ERROR:
77
0
      return "_URC_FATAL_PHASE2_ERROR";
78
0
    case _URC_FATAL_PHASE1_ERROR:
79
0
      return "_URC_FATAL_PHASE1_ERROR";
80
0
    case _URC_NORMAL_STOP:
81
0
      return "_URC_NORMAL_STOP";
82
0
    case _URC_END_OF_STACK:
83
0
      return "_URC_END_OF_STACK";
84
0
    case _URC_HANDLER_FOUND:
85
0
      return "_URC_HANDLER_FOUND";
86
0
    case _URC_INSTALL_CONTEXT:
87
0
      return "_URC_INSTALL_CONTEXT";
88
0
    case _URC_CONTINUE_UNWIND:
89
0
      return "_URC_CONTINUE_UNWIND";
90
#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__ARM_DWARF_EH__) && \
91
    !defined(__SEH__)
92
    case _URC_FAILURE:
93
      return "_URC_FAILURE";
94
#endif
95
0
    default:
96
0
      return "_URC_UNKNOWN";
97
0
  }
98
0
}
99
100
static const char* unwind_reason_str_buffer(_Unwind_Reason_Code code, char* buffer, size_t size)
101
0
{
102
0
  const char* str = unwind_reason_str(code);
103
0
  (void)_snprintf(buffer, size, "%s [0x%02x]", str, code);
104
0
  return buffer;
105
0
}
106
107
static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* context, void* arg)
108
0
{
109
0
  unwind_context_t* ctx = arg;
110
111
0
  assert(ctx);
112
113
0
  if (ctx->pos < ctx->size)
114
0
  {
115
0
    unwind_info_t* info = &ctx->info[ctx->pos++];
116
0
    info->pc.uw = _Unwind_GetIP(context);
117
118
    /* _Unwind_GetLanguageSpecificData has various return value definitions,
119
     * cast to the type we expect and disable linter warnings
120
     */
121
    // NOLINTNEXTLINE(google-readability-casting,readability-redundant-casting)
122
0
    info->langSpecificData.pv = (void*)_Unwind_GetLanguageSpecificData(context);
123
0
  }
124
125
0
  return _URC_NO_REASON;
126
0
}
127
128
void* winpr_unwind_backtrace(DWORD size)
129
0
{
130
0
  _Unwind_Reason_Code rc = _URC_FOREIGN_EXCEPTION_CAUGHT;
131
0
  unwind_context_t* ctx = calloc(1, sizeof(unwind_context_t));
132
0
  if (!ctx)
133
0
    goto fail;
134
0
  ctx->size = size;
135
0
  ctx->info = calloc(size, sizeof(unwind_info_t));
136
0
  if (!ctx->info)
137
0
    goto fail;
138
139
0
  rc = _Unwind_Backtrace(unwind_backtrace_callback, ctx);
140
0
  if (rc != _URC_END_OF_STACK)
141
0
  {
142
0
    char buffer[64] = { 0 };
143
0
    WLog_ERR(TAG, "_Unwind_Backtrace failed with %s",
144
0
             unwind_reason_str_buffer(rc, buffer, sizeof(buffer)));
145
0
    goto fail;
146
0
  }
147
148
0
  return ctx;
149
0
fail:
150
0
  winpr_unwind_backtrace_free(ctx);
151
0
  return NULL;
152
0
}
153
154
void winpr_unwind_backtrace_free(void* buffer)
155
0
{
156
0
  unwind_context_t* ctx = buffer;
157
0
  if (!ctx)
158
0
    return;
159
0
  free(ctx->info);
160
0
  free(ctx);
161
0
}
162
163
char** winpr_unwind_backtrace_symbols(void* buffer, size_t* used)
164
0
{
165
0
  union
166
0
  {
167
0
    char* cp;
168
0
    char** cpp;
169
0
  } cnv;
170
0
  unwind_context_t* ctx = buffer;
171
0
  cnv.cpp = NULL;
172
173
0
  if (!ctx)
174
0
    return NULL;
175
176
0
  cnv.cpp = calloc(ctx->pos * (sizeof(char*) + UNWIND_MAX_LINE_SIZE), sizeof(char*));
177
0
  if (!cnv.cpp)
178
0
    return NULL;
179
180
0
  if (used)
181
0
    *used = ctx->pos;
182
183
0
  for (size_t x = 0; x < ctx->pos; x++)
184
0
  {
185
0
    char* msg = cnv.cp + ctx->pos * sizeof(char*) + x * UNWIND_MAX_LINE_SIZE;
186
0
    const unwind_info_t* info = &ctx->info[x];
187
0
    Dl_info dlinfo = { 0 };
188
0
    int rc = dladdr(info->pc.pv, &dlinfo);
189
190
0
    cnv.cpp[x] = msg;
191
192
0
    if (rc == 0)
193
0
      (void)_snprintf(msg, UNWIND_MAX_LINE_SIZE, "unresolvable, address=%p", info->pc.pv);
194
0
    else
195
0
      (void)_snprintf(msg, UNWIND_MAX_LINE_SIZE, "dli_fname=%s [%p], dli_sname=%s [%p]",
196
0
                      dlinfo.dli_fname, dlinfo.dli_fbase, dlinfo.dli_sname, dlinfo.dli_saddr);
197
0
  }
198
199
0
  return cnv.cpp;
200
0
}