/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 |
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 <dlfcn.h> |
33 | | |
34 | | typedef struct |
35 | | { |
36 | | uintptr_t pc; |
37 | | void* langSpecificData; |
38 | | } unwind_info_t; |
39 | | |
40 | | typedef struct |
41 | | { |
42 | | size_t pos; |
43 | | size_t size; |
44 | | unwind_info_t* info; |
45 | | } unwind_context_t; |
46 | | |
47 | | static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* context, void* arg) |
48 | 2.14M | { |
49 | 2.14M | unwind_context_t* ctx = arg; |
50 | | |
51 | 2.14M | assert(ctx); |
52 | | |
53 | 2.14M | if (ctx->pos < ctx->size) |
54 | 2.14M | { |
55 | 2.14M | unwind_info_t* info = &ctx->info[ctx->pos++]; |
56 | 2.14M | info->pc = _Unwind_GetIP(context); |
57 | 2.14M | info->langSpecificData = (void*)_Unwind_GetLanguageSpecificData(context); |
58 | 2.14M | } |
59 | | |
60 | 2.14M | return _URC_NO_REASON; |
61 | 2.14M | } |
62 | | |
63 | | void* winpr_unwind_backtrace(DWORD size) |
64 | 157k | { |
65 | 157k | _Unwind_Reason_Code rc = _URC_FOREIGN_EXCEPTION_CAUGHT; |
66 | 157k | unwind_context_t* ctx = calloc(1, sizeof(unwind_context_t)); |
67 | 157k | if (!ctx) |
68 | 0 | goto fail; |
69 | 157k | ctx->size = size; |
70 | 157k | ctx->info = calloc(size, sizeof(unwind_info_t)); |
71 | 157k | if (!ctx->info) |
72 | 0 | goto fail; |
73 | | |
74 | 157k | rc = _Unwind_Backtrace(unwind_backtrace_callback, ctx); |
75 | 157k | if (rc != _URC_END_OF_STACK) |
76 | 0 | goto fail; |
77 | | |
78 | 157k | return ctx; |
79 | 0 | fail: |
80 | 0 | winpr_unwind_backtrace_free(ctx); |
81 | 0 | return NULL; |
82 | 157k | } |
83 | | |
84 | | void winpr_unwind_backtrace_free(void* buffer) |
85 | 157k | { |
86 | 157k | unwind_context_t* ctx = buffer; |
87 | 157k | if (!ctx) |
88 | 0 | return; |
89 | 157k | free(ctx->info); |
90 | 157k | free(ctx); |
91 | 157k | } |
92 | | |
93 | 4.44M | #define UNWIND_MAX_LINE_SIZE 1024 |
94 | | |
95 | | char** winpr_unwind_backtrace_symbols(void* buffer, size_t* used) |
96 | 157k | { |
97 | 157k | union |
98 | 157k | { |
99 | 157k | char* cp; |
100 | 157k | char** cpp; |
101 | 157k | } cnv; |
102 | 157k | unwind_context_t* ctx = buffer; |
103 | 157k | cnv.cpp = NULL; |
104 | | |
105 | 157k | if (!ctx) |
106 | 0 | return NULL; |
107 | | |
108 | 157k | cnv.cpp = calloc(ctx->pos * (sizeof(char*) + UNWIND_MAX_LINE_SIZE), sizeof(char*)); |
109 | 157k | if (!cnv.cpp) |
110 | 0 | return NULL; |
111 | | |
112 | 157k | if (used) |
113 | 157k | *used = ctx->pos; |
114 | | |
115 | 2.30M | for (size_t x = 0; x < ctx->pos; x++) |
116 | 2.14M | { |
117 | 2.14M | char* msg = cnv.cp + ctx->pos * sizeof(char*) + x * UNWIND_MAX_LINE_SIZE; |
118 | 2.14M | const unwind_info_t* info = &ctx->info[x]; |
119 | 2.14M | Dl_info dlinfo = { 0 }; |
120 | 2.14M | int rc = dladdr((void*)info->pc, &dlinfo); |
121 | | |
122 | 2.14M | cnv.cpp[x] = msg; |
123 | | |
124 | 2.14M | if (rc == 0) |
125 | 157k | _snprintf(msg, UNWIND_MAX_LINE_SIZE, "unresolvable, address=%p", (void*)info->pc); |
126 | 1.98M | else |
127 | 1.98M | _snprintf(msg, UNWIND_MAX_LINE_SIZE, "dli_fname=%s [%p], dli_sname=%s [%p]", |
128 | 1.98M | dlinfo.dli_fname, dlinfo.dli_fbase, dlinfo.dli_sname, dlinfo.dli_saddr); |
129 | 2.14M | } |
130 | | |
131 | 157k | return cnv.cpp; |
132 | 157k | } |