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