Index: include/sanitizer/common_interface_defs.h =================================================================== --- include/sanitizer/common_interface_defs.h +++ include/sanitizer/common_interface_defs.h @@ -124,6 +124,12 @@ // Symbolizes the supplied 'pc' using the format string 'fmt'. // Outputs at most 'out_buf_size' bytes into 'out_buf'. + // If 'out_buf' is not empty then output is zero or more non empty C strings + // followed by single empty C string. Multiple strings can be returned if PC + // corresponds to inlined function. Inlined frames are printed in the order + // from "most-inlined" to the "least-inlined", so the last frame should be the + // not inlined function. + // Inlined frames can be removed with 'symbolize_inline_frames=0'. // The format syntax is described in // lib/sanitizer_common/sanitizer_stacktrace_printer.h. void __sanitizer_symbolize_pc(void *pc, const char *fmt, char *out_buf, Index: lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc +++ lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc @@ -114,11 +114,25 @@ return; } InternalScopedString frame_desc(GetPageSizeCached()); - RenderFrame(&frame_desc, fmt, 0, frame->info, - common_flags()->symbolize_vs_style, - common_flags()->strip_path_prefix); - internal_strncpy(out_buf, frame_desc.data(), out_buf_size); - out_buf[out_buf_size - 1] = 0; + uptr frame_num = 0; + // Reserve one byte for the final 0. + char *out_end = out_buf + out_buf_size - 1; + for (SymbolizedStack *cur = frame; cur && out_buf < out_end; + cur = cur->next) { + frame_desc.clear(); + RenderFrame(&frame_desc, fmt, frame_num++, cur->info, + common_flags()->symbolize_vs_style, + common_flags()->strip_path_prefix); + if (!frame_desc.length()) + continue; + // Reserve one byte for the terminating 0. + uptr n = out_end - out_buf - 1; + internal_strncpy(out_buf, frame_desc.data(), n); + out_buf += __sanitizer::Min(n, frame_desc.length()); + *out_buf++ = 0; + } + CHECK(out_buf <= out_end); + *out_buf = 0; } SANITIZER_INTERFACE_ATTRIBUTE Index: test/sanitizer_common/TestCases/symbolize_pc_inline.cc =================================================================== --- test/sanitizer_common/TestCases/symbolize_pc_inline.cc +++ test/sanitizer_common/TestCases/symbolize_pc_inline.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx -O3 %s -o %t +// RUN: %env_tool_opts=strip_path_prefix=/TestCases/ %run %t 2>&1 | FileCheck %s +// RUN: %env_tool_opts=strip_path_prefix=/TestCases/:symbolize_inline_frames=0 \ +// RUN: %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK-NOINLINE + +#include +#include +#include + +char buffer[10000]; + +__attribute__((noinline)) static void Symbolize() { + __sanitizer_symbolize_pc(__builtin_return_address(0), "%p %F %L", buffer, + sizeof(buffer)); + for (char *p = buffer; strlen(p); p += strlen(p) + 1) + printf("%s\n", p); +} + +// CHECK-NOINLINE: {{0x[0-9a-f]+}} in main symbolize_pc_inline.cc:[[@LINE+2]] +// CHECK: [[ADDR:0x[0-9a-f]+]] in C2 symbolize_pc_inline.cc:[[@LINE+1]] +static inline void C2() { Symbolize(); } + +// CHECK: [[ADDR]] in C3 symbolize_pc_inline.cc:[[@LINE+1]] +static inline void C3() { C2(); } + +// CHECK: [[ADDR]] in C4 symbolize_pc_inline.cc:[[@LINE+1]] +static inline void C4() { C3(); } + +// CHECK: [[ADDR]] in main symbolize_pc_inline.cc:[[@LINE+1]] +int main() { C4(); }