// RUN: %clangxx %s -o %t -g && %run %t 2>&1 | FileCheck %s // REQUIRES: internal_symbolizer #include #include #include #include #include #include #include #include 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(reinterpret_cast(address) - 1)}; } __attribute__((noinline)) FrameInfo NoInlineFunction() { void *address = GetPC(); return {0, "", "", reinterpret_cast(reinterpret_cast(address) - 1)}; } template struct A { template FrameInfo RecursiveTemplateFunction(const T &t); }; template template __attribute__((noinline)) FrameInfo A::RecursiveTemplateFunction(const T &) { std::vector t; return A().RecursiveTemplateFunction(t); } template <> template __attribute__((noinline)) FrameInfo A<0>::RecursiveTemplateFunction(const T &) { return NoInlineFunction(); } __attribute__((no_sanitize_memory)) std::pair 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(&lm), RTLD_DL_LINKMAP)); return {di.dli_fname, reinterpret_cast(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(); }