| // RUN: %clangxx %s -o %t -g && %run %t 2>&1 | FileCheck %s |
| |
| // REQUIRES: internal_symbolizer |
| |
| #include <assert.h> |
| #include <dlfcn.h> |
| #include <link.h> |
| #include <sanitizer/hwasan_interface.h> |
| #include <sanitizer/msan_interface.h> |
| #include <string.h> |
| |
| #include <string> |
| #include <vector> |
| |
| extern "C" { |
| bool __sanitizer_symbolize_code(const char *ModuleName, uint64_t ModuleOffset, |
| char *Buffer, int MaxLength, |
| bool SymbolizeInlineFrames); |
| bool __sanitizer_symbolize_data(const char *ModuleName, uint64_t ModuleOffset, |
| char *Buffer, int MaxLength); |
| bool __sanitizer_symbolize_frame(const char *ModuleName, uint64_t ModuleOffset, |
| char *Buffer, int MaxLength); |
| void __sanitizer_print_stack_trace(); |
| bool __sanitizer_symbolize_demangle(const char *Name, char *Buffer, |
| int MaxLength); |
| } |
| |
| struct ScopedInSymbolizer { |
| #if defined(__has_feature) |
| # if __has_feature(memory_sanitizer) |
| ScopedInSymbolizer() { __msan_scoped_disable_interceptor_checks(); } |
| ~ScopedInSymbolizer() { __msan_scoped_enable_interceptor_checks(); } |
| # endif |
| #endif |
| }; |
| |
| struct FrameInfo { |
| int line; |
| std::string file; |
| std::string function; |
| void *address; |
| }; |
| |
| __attribute__((noinline)) void *GetPC() { return __builtin_return_address(0); } |
| |
| __attribute__((always_inline)) FrameInfo InlineFunction() { |
| void *address = GetPC(); |
| return {0, "", "", |
| reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(address) - 1)}; |
| } |
| |
| __attribute__((noinline)) FrameInfo NoInlineFunction() { |
| void *address = GetPC(); |
| return {0, "", "", |
| reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(address) - 1)}; |
| } |
| |
| template <int N> struct A { |
| template <class T> FrameInfo RecursiveTemplateFunction(const T &t); |
| }; |
| |
| template <int N> |
| template <class T> |
| __attribute__((noinline)) FrameInfo A<N>::RecursiveTemplateFunction(const T &) { |
| std::vector<T> t; |
| return A<N - 1>().RecursiveTemplateFunction(t); |
| } |
| |
| template <> |
| template <class T> |
| __attribute__((noinline)) FrameInfo A<0>::RecursiveTemplateFunction(const T &) { |
| return NoInlineFunction(); |
| } |
| |
| __attribute__((no_sanitize_memory)) std::pair<const char *, uint64_t> |
| GetModuleAndOffset(const void *address) { |
| Dl_info di; |
| link_map *lm = nullptr; |
| #if __has_feature(hwaddress_sanitizer) |
| address = __hwasan_tag_pointer(address, 0); |
| #endif |
| assert( |
| dladdr1(address, &di, reinterpret_cast<void **>(&lm), RTLD_DL_LINKMAP)); |
| return {di.dli_fname, reinterpret_cast<uint64_t>(address) - lm->l_addr}; |
| } |
| |
| std::string Symbolize(FrameInfo frame) { |
| auto modul_offset = GetModuleAndOffset(frame.address); |
| char buffer[1024] = {}; |
| ScopedInSymbolizer in_symbolizer; |
| assert(__sanitizer_symbolize_code(modul_offset.first, modul_offset.second, |
| buffer, std::size(buffer), true)); |
| return buffer; |
| } |
| |
| std::string GetRegex(const FrameInfo &frame) { |
| return frame.function + "[^\\n]*\\n[^\\n]*" + frame.file + ":" + |
| std::to_string(frame.line); |
| } |
| |
| void TestInline() { |
| auto frame = InlineFunction(); |
| fprintf(stderr, "%s: %s\n", __FUNCTION__, Symbolize(frame).c_str()); |
| // CHECK-LABEL: TestInline: InlineFunction() |
| // CHECK-NEXT: internal_symbolizer.cpp:[[# @LINE - 58]] |
| // CHECK-NEXT: TestInline() |
| // CHECK-NEXT: internal_symbolizer.cpp:[[# @LINE - 5]] |
| } |
| |
| void TestNoInline() { |
| auto frame = NoInlineFunction(); |
| fprintf(stderr, "%s: %s\n", __FUNCTION__, Symbolize(frame).c_str()); |
| // CHECK-LABEL: TestNoInline: NoInlineFunction() |
| // CHECK-NEXT: internal_symbolizer.cpp:[[# @LINE - 61]] |
| } |
| |
| void TestLongFunctionNames() { |
| auto frame = A<10>().RecursiveTemplateFunction(0); |
| fprintf(stderr, "%s: %s\n", __FUNCTION__, Symbolize(frame).c_str()); |
| // CHECK-LABEL: TestLongFunctionNames: NoInlineFunction() |
| // CHECK-NEXT: internal_symbolizer.cpp:[[# @LINE - 68]] |
| } |
| |
| std::string SymbolizeStaticVar() { |
| static int var = 1; |
| auto modul_offset = GetModuleAndOffset(&var); |
| char buffer[1024] = {}; |
| ScopedInSymbolizer in_symbolizer; |
| assert(__sanitizer_symbolize_data(modul_offset.first, modul_offset.second, |
| buffer, std::size(buffer))); |
| return buffer; |
| } |
| |
| void TestData() { |
| fprintf(stderr, "%s: %s\n", __FUNCTION__, SymbolizeStaticVar().c_str()); |
| // CHECK-LABEL: TestData: SymbolizeStaticVar[abi:cxx11]()::var |
| // CHECK-NEXT: {{[0-9]+ +[0-9]+}} |
| // CHECK-NEXT: internal_symbolizer.cpp:[[# @LINE - 13]] |
| } |
| |
| __attribute__((noinline)) std::string SymbolizeLocalVars(const void *pc) { |
| auto modul_offset = GetModuleAndOffset(pc); |
| char buffer[1024] = {}; |
| ScopedInSymbolizer in_symbolizer; |
| __sanitizer_symbolize_frame(modul_offset.first, modul_offset.second, buffer, |
| std::size(buffer)); |
| return buffer; |
| } |
| |
| __attribute__(( |
| noinline, |
| no_sanitize_address /* Asan merges allocas destroying variable DI */)) void |
| TestFrame() { |
| volatile int var = 1; |
| void *address = GetPC(); |
| fprintf(stderr, "%s: %s\n", __FUNCTION__, |
| SymbolizeLocalVars(address).c_str()); |
| // CHECK-LABEL: TestFrame: TestFrame |
| // CHECK-NEXT: var |
| // CHECK-NEXT: internal_symbolizer.cpp:[[# @LINE - 6]] |
| // CHECK-NEXT: {{-?[0-9]+ +[0-9]+}} |
| // CHECK-NEXT: TestFrame |
| // CHECK-NEXT: address |
| // CHECK-NEXT: internal_symbolizer.cpp:[[# @LINE - 9]] |
| // CHECK-NEXT: {{-?[0-9]+ +[0-9]+}} |
| } |
| |
| void TestDemangle() { |
| char out[128]; |
| assert(!__sanitizer_symbolize_demangle("1A", out, sizeof(out))); |
| |
| const char name[] = "_Z3fooi"; |
| for (int i = 1; i < sizeof(out); ++i) { |
| memset(out, 1, sizeof(out)); |
| assert(__sanitizer_symbolize_demangle(name, out, i) == (i > 8)); |
| assert(i < 9 || 0 == strncmp(out, "foo(int)", i - 1)); |
| } |
| } |
| |
| int main() { |
| TestInline(); |
| TestNoInline(); |
| TestLongFunctionNames(); |
| TestData(); |
| TestFrame(); |
| TestDemangle(); |
| } |