145
|
1 //===-- sanitizer_common.cpp ----------------------------------------------===//
|
|
2 //
|
|
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
4 // See https://llvm.org/LICENSE.txt for license information.
|
|
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
6 //
|
|
7 //===----------------------------------------------------------------------===//
|
|
8 //
|
|
9 // This file is shared between sanitizers' run-time libraries.
|
|
10 //
|
|
11 //===----------------------------------------------------------------------===//
|
|
12
|
|
13 #include "sanitizer_stacktrace_printer.h"
|
|
14 #include "sanitizer_file.h"
|
|
15 #include "sanitizer_fuchsia.h"
|
|
16
|
|
17 namespace __sanitizer {
|
|
18
|
|
19 // sanitizer_symbolizer_markup.cpp implements these differently.
|
|
20 #if !SANITIZER_SYMBOLIZER_MARKUP
|
|
21
|
|
22 static const char *StripFunctionName(const char *function, const char *prefix) {
|
|
23 if (!function) return nullptr;
|
|
24 if (!prefix) return function;
|
|
25 uptr prefix_len = internal_strlen(prefix);
|
|
26 if (0 == internal_strncmp(function, prefix, prefix_len))
|
|
27 return function + prefix_len;
|
|
28 return function;
|
|
29 }
|
|
30
|
|
31 static const char *DemangleFunctionName(const char *function) {
|
|
32 if (!function) return nullptr;
|
|
33
|
|
34 // NetBSD uses indirection for old threading functions for historical reasons
|
|
35 // The mangled names are internal implementation detail and should not be
|
|
36 // exposed even in backtraces.
|
|
37 #if SANITIZER_NETBSD
|
|
38 if (!internal_strcmp(function, "__libc_mutex_init"))
|
|
39 return "pthread_mutex_init";
|
|
40 if (!internal_strcmp(function, "__libc_mutex_lock"))
|
|
41 return "pthread_mutex_lock";
|
|
42 if (!internal_strcmp(function, "__libc_mutex_trylock"))
|
|
43 return "pthread_mutex_trylock";
|
|
44 if (!internal_strcmp(function, "__libc_mutex_unlock"))
|
|
45 return "pthread_mutex_unlock";
|
|
46 if (!internal_strcmp(function, "__libc_mutex_destroy"))
|
|
47 return "pthread_mutex_destroy";
|
|
48 if (!internal_strcmp(function, "__libc_mutexattr_init"))
|
|
49 return "pthread_mutexattr_init";
|
|
50 if (!internal_strcmp(function, "__libc_mutexattr_settype"))
|
|
51 return "pthread_mutexattr_settype";
|
|
52 if (!internal_strcmp(function, "__libc_mutexattr_destroy"))
|
|
53 return "pthread_mutexattr_destroy";
|
|
54 if (!internal_strcmp(function, "__libc_cond_init"))
|
|
55 return "pthread_cond_init";
|
|
56 if (!internal_strcmp(function, "__libc_cond_signal"))
|
|
57 return "pthread_cond_signal";
|
|
58 if (!internal_strcmp(function, "__libc_cond_broadcast"))
|
|
59 return "pthread_cond_broadcast";
|
|
60 if (!internal_strcmp(function, "__libc_cond_wait"))
|
|
61 return "pthread_cond_wait";
|
|
62 if (!internal_strcmp(function, "__libc_cond_timedwait"))
|
|
63 return "pthread_cond_timedwait";
|
|
64 if (!internal_strcmp(function, "__libc_cond_destroy"))
|
|
65 return "pthread_cond_destroy";
|
|
66 if (!internal_strcmp(function, "__libc_rwlock_init"))
|
|
67 return "pthread_rwlock_init";
|
|
68 if (!internal_strcmp(function, "__libc_rwlock_rdlock"))
|
|
69 return "pthread_rwlock_rdlock";
|
|
70 if (!internal_strcmp(function, "__libc_rwlock_wrlock"))
|
|
71 return "pthread_rwlock_wrlock";
|
|
72 if (!internal_strcmp(function, "__libc_rwlock_tryrdlock"))
|
|
73 return "pthread_rwlock_tryrdlock";
|
|
74 if (!internal_strcmp(function, "__libc_rwlock_trywrlock"))
|
|
75 return "pthread_rwlock_trywrlock";
|
|
76 if (!internal_strcmp(function, "__libc_rwlock_unlock"))
|
|
77 return "pthread_rwlock_unlock";
|
|
78 if (!internal_strcmp(function, "__libc_rwlock_destroy"))
|
|
79 return "pthread_rwlock_destroy";
|
|
80 if (!internal_strcmp(function, "__libc_thr_keycreate"))
|
|
81 return "pthread_key_create";
|
|
82 if (!internal_strcmp(function, "__libc_thr_setspecific"))
|
|
83 return "pthread_setspecific";
|
|
84 if (!internal_strcmp(function, "__libc_thr_getspecific"))
|
|
85 return "pthread_getspecific";
|
|
86 if (!internal_strcmp(function, "__libc_thr_keydelete"))
|
|
87 return "pthread_key_delete";
|
|
88 if (!internal_strcmp(function, "__libc_thr_once"))
|
|
89 return "pthread_once";
|
|
90 if (!internal_strcmp(function, "__libc_thr_self"))
|
|
91 return "pthread_self";
|
|
92 if (!internal_strcmp(function, "__libc_thr_exit"))
|
|
93 return "pthread_exit";
|
|
94 if (!internal_strcmp(function, "__libc_thr_setcancelstate"))
|
|
95 return "pthread_setcancelstate";
|
|
96 if (!internal_strcmp(function, "__libc_thr_equal"))
|
|
97 return "pthread_equal";
|
|
98 if (!internal_strcmp(function, "__libc_thr_curcpu"))
|
|
99 return "pthread_curcpu_np";
|
|
100 if (!internal_strcmp(function, "__libc_thr_sigsetmask"))
|
|
101 return "pthread_sigmask";
|
|
102 #endif
|
|
103
|
|
104 return function;
|
|
105 }
|
|
106
|
|
107 static const char kDefaultFormat[] = " #%n %p %F %L";
|
|
108
|
|
109 void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
|
|
110 const AddressInfo &info, bool vs_style,
|
|
111 const char *strip_path_prefix, const char *strip_func_prefix) {
|
|
112 if (0 == internal_strcmp(format, "DEFAULT"))
|
|
113 format = kDefaultFormat;
|
|
114 for (const char *p = format; *p != '\0'; p++) {
|
|
115 if (*p != '%') {
|
|
116 buffer->append("%c", *p);
|
|
117 continue;
|
|
118 }
|
|
119 p++;
|
|
120 switch (*p) {
|
|
121 case '%':
|
|
122 buffer->append("%%");
|
|
123 break;
|
|
124 // Frame number and all fields of AddressInfo structure.
|
|
125 case 'n':
|
|
126 buffer->append("%zu", frame_no);
|
|
127 break;
|
|
128 case 'p':
|
|
129 buffer->append("0x%zx", info.address);
|
|
130 break;
|
|
131 case 'm':
|
|
132 buffer->append("%s", StripPathPrefix(info.module, strip_path_prefix));
|
|
133 break;
|
|
134 case 'o':
|
|
135 buffer->append("0x%zx", info.module_offset);
|
|
136 break;
|
|
137 case 'f':
|
|
138 buffer->append("%s",
|
|
139 DemangleFunctionName(
|
|
140 StripFunctionName(info.function, strip_func_prefix)));
|
|
141 break;
|
|
142 case 'q':
|
|
143 buffer->append("0x%zx", info.function_offset != AddressInfo::kUnknown
|
|
144 ? info.function_offset
|
|
145 : 0x0);
|
|
146 break;
|
|
147 case 's':
|
|
148 buffer->append("%s", StripPathPrefix(info.file, strip_path_prefix));
|
|
149 break;
|
|
150 case 'l':
|
|
151 buffer->append("%d", info.line);
|
|
152 break;
|
|
153 case 'c':
|
|
154 buffer->append("%d", info.column);
|
|
155 break;
|
|
156 // Smarter special cases.
|
|
157 case 'F':
|
|
158 // Function name and offset, if file is unknown.
|
|
159 if (info.function) {
|
|
160 buffer->append("in %s",
|
|
161 DemangleFunctionName(
|
|
162 StripFunctionName(info.function, strip_func_prefix)));
|
|
163 if (!info.file && info.function_offset != AddressInfo::kUnknown)
|
|
164 buffer->append("+0x%zx", info.function_offset);
|
|
165 }
|
|
166 break;
|
|
167 case 'S':
|
|
168 // File/line information.
|
|
169 RenderSourceLocation(buffer, info.file, info.line, info.column, vs_style,
|
|
170 strip_path_prefix);
|
|
171 break;
|
|
172 case 'L':
|
|
173 // Source location, or module location.
|
|
174 if (info.file) {
|
|
175 RenderSourceLocation(buffer, info.file, info.line, info.column,
|
|
176 vs_style, strip_path_prefix);
|
|
177 } else if (info.module) {
|
|
178 RenderModuleLocation(buffer, info.module, info.module_offset,
|
|
179 info.module_arch, strip_path_prefix);
|
|
180 } else {
|
|
181 buffer->append("(<unknown module>)");
|
|
182 }
|
|
183 break;
|
|
184 case 'M':
|
|
185 // Module basename and offset, or PC.
|
|
186 if (info.address & kExternalPCBit)
|
|
187 {} // There PCs are not meaningful.
|
|
188 else if (info.module)
|
|
189 // Always strip the module name for %M.
|
|
190 RenderModuleLocation(buffer, StripModuleName(info.module),
|
|
191 info.module_offset, info.module_arch, "");
|
|
192 else
|
|
193 buffer->append("(%p)", (void *)info.address);
|
|
194 break;
|
|
195 default:
|
|
196 Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n", *p,
|
|
197 *p);
|
|
198 Die();
|
|
199 }
|
|
200 }
|
|
201 }
|
|
202
|
|
203 void RenderData(InternalScopedString *buffer, const char *format,
|
|
204 const DataInfo *DI, const char *strip_path_prefix) {
|
|
205 for (const char *p = format; *p != '\0'; p++) {
|
|
206 if (*p != '%') {
|
|
207 buffer->append("%c", *p);
|
|
208 continue;
|
|
209 }
|
|
210 p++;
|
|
211 switch (*p) {
|
|
212 case '%':
|
|
213 buffer->append("%%");
|
|
214 break;
|
|
215 case 's':
|
|
216 buffer->append("%s", StripPathPrefix(DI->file, strip_path_prefix));
|
|
217 break;
|
|
218 case 'l':
|
|
219 buffer->append("%d", DI->line);
|
|
220 break;
|
|
221 case 'g':
|
|
222 buffer->append("%s", DI->name);
|
|
223 break;
|
|
224 default:
|
|
225 Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n", *p,
|
|
226 *p);
|
|
227 Die();
|
|
228 }
|
|
229 }
|
|
230 }
|
|
231
|
|
232 #endif // !SANITIZER_SYMBOLIZER_MARKUP
|
|
233
|
|
234 void RenderSourceLocation(InternalScopedString *buffer, const char *file,
|
|
235 int line, int column, bool vs_style,
|
|
236 const char *strip_path_prefix) {
|
|
237 if (vs_style && line > 0) {
|
|
238 buffer->append("%s(%d", StripPathPrefix(file, strip_path_prefix), line);
|
|
239 if (column > 0)
|
|
240 buffer->append(",%d", column);
|
|
241 buffer->append(")");
|
|
242 return;
|
|
243 }
|
|
244
|
|
245 buffer->append("%s", StripPathPrefix(file, strip_path_prefix));
|
|
246 if (line > 0) {
|
|
247 buffer->append(":%d", line);
|
|
248 if (column > 0)
|
|
249 buffer->append(":%d", column);
|
|
250 }
|
|
251 }
|
|
252
|
|
253 void RenderModuleLocation(InternalScopedString *buffer, const char *module,
|
|
254 uptr offset, ModuleArch arch,
|
|
255 const char *strip_path_prefix) {
|
|
256 buffer->append("(%s", StripPathPrefix(module, strip_path_prefix));
|
|
257 if (arch != kModuleArchUnknown) {
|
|
258 buffer->append(":%s", ModuleArchToString(arch));
|
|
259 }
|
|
260 buffer->append("+0x%zx)", offset);
|
|
261 }
|
|
262
|
|
263 } // namespace __sanitizer
|