[sanitizer] Skip stack symbolization when not required for print format

Adds a check to avoid symbolization when printing stack traces if the
stack_trace_format flag does not need it. While there is a symbolize
flag that can be turned off to skip some of the symbolization,
SymbolizePC() still unconditionally looks up the module name and offset.
Avoid invoking SymbolizePC() at all if not needed.

This is an efficiency improvement when dumping all stack traces as part
of the memory profiler in D87120, for large stripped apps where we want
to symbolize as a post pass.

Differential Revision: https://reviews.llvm.org/D88361
This commit is contained in:
Teresa Johnson 2020-09-25 23:03:06 -07:00
parent d56fdc8e95
commit 4d5b1de40e
8 changed files with 104 additions and 55 deletions

View File

@ -224,7 +224,7 @@ static void PrintStackAllocations(StackAllocationsRingBuffer *sa,
frame_desc.append(" record_addr:0x%zx record:0x%zx", frame_desc.append(" record_addr:0x%zx record:0x%zx",
reinterpret_cast<uptr>(record_addr), record); reinterpret_cast<uptr>(record_addr), record);
if (SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc)) { if (SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc)) {
RenderFrame(&frame_desc, " %F %L\n", 0, frame->info, RenderFrame(&frame_desc, " %F %L\n", 0, frame->info.address, &frame->info,
common_flags()->symbolize_vs_style, common_flags()->symbolize_vs_style,
common_flags()->strip_path_prefix); common_flags()->strip_path_prefix);
frame->ClearAll(); frame->ClearAll();

View File

@ -26,17 +26,23 @@ void StackTrace::Print() const {
InternalScopedString frame_desc(GetPageSizeCached() * 2); InternalScopedString frame_desc(GetPageSizeCached() * 2);
InternalScopedString dedup_token(GetPageSizeCached()); InternalScopedString dedup_token(GetPageSizeCached());
int dedup_frames = common_flags()->dedup_token_length; int dedup_frames = common_flags()->dedup_token_length;
bool symbolize = RenderNeedsSymbolization(common_flags()->stack_trace_format);
uptr frame_num = 0; uptr frame_num = 0;
for (uptr i = 0; i < size && trace[i]; i++) { for (uptr i = 0; i < size && trace[i]; i++) {
// PCs in stack traces are actually the return addresses, that is, // PCs in stack traces are actually the return addresses, that is,
// addresses of the next instructions after the call. // addresses of the next instructions after the call.
uptr pc = GetPreviousInstructionPc(trace[i]); uptr pc = GetPreviousInstructionPc(trace[i]);
SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(pc); SymbolizedStack *frames;
if (symbolize)
frames = Symbolizer::GetOrInit()->SymbolizePC(pc);
else
frames = SymbolizedStack::New(pc);
CHECK(frames); CHECK(frames);
for (SymbolizedStack *cur = frames; cur; cur = cur->next) { for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
frame_desc.clear(); frame_desc.clear();
RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++, RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++,
cur->info, common_flags()->symbolize_vs_style, cur->info.address, symbolize ? &cur->info : nullptr,
common_flags()->symbolize_vs_style,
common_flags()->strip_path_prefix); common_flags()->strip_path_prefix);
Printf("%s\n", frame_desc.data()); Printf("%s\n", frame_desc.data());
if (dedup_frames-- > 0) { if (dedup_frames-- > 0) {
@ -108,7 +114,12 @@ void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf,
uptr out_buf_size) { uptr out_buf_size) {
if (!out_buf_size) return; if (!out_buf_size) return;
pc = StackTrace::GetPreviousInstructionPc(pc); pc = StackTrace::GetPreviousInstructionPc(pc);
SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc); SymbolizedStack *frame;
bool symbolize = RenderNeedsSymbolization(fmt);
if (symbolize)
frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
else
frame = SymbolizedStack::New(pc);
if (!frame) { if (!frame) {
internal_strncpy(out_buf, "<can't symbolize>", out_buf_size); internal_strncpy(out_buf, "<can't symbolize>", out_buf_size);
out_buf[out_buf_size - 1] = 0; out_buf[out_buf_size - 1] = 0;
@ -121,7 +132,8 @@ void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf,
for (SymbolizedStack *cur = frame; cur && out_buf < out_end; for (SymbolizedStack *cur = frame; cur && out_buf < out_end;
cur = cur->next) { cur = cur->next) {
frame_desc.clear(); frame_desc.clear();
RenderFrame(&frame_desc, fmt, frame_num++, cur->info, RenderFrame(&frame_desc, fmt, frame_num++, cur->info.address,
symbolize ? &cur->info : nullptr,
common_flags()->symbolize_vs_style, common_flags()->symbolize_vs_style,
common_flags()->strip_path_prefix); common_flags()->strip_path_prefix);
if (!frame_desc.length()) if (!frame_desc.length())

View File

@ -107,8 +107,14 @@ static const char *DemangleFunctionName(const char *function) {
static const char kDefaultFormat[] = " #%n %p %F %L"; static const char kDefaultFormat[] = " #%n %p %F %L";
void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no, void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
const AddressInfo &info, bool vs_style, uptr address, const AddressInfo *info, bool vs_style,
const char *strip_path_prefix, const char *strip_func_prefix) { const char *strip_path_prefix, const char *strip_func_prefix) {
// info will be null in the case where symbolization is not needed for the
// given format. This ensures that the code below will get a hard failure
// rather than print incorrect information in case RenderNeedsSymbolization
// ever ends up out of sync with this function. If non-null, the addresses
// should match.
CHECK(!info || address == info->address);
if (0 == internal_strcmp(format, "DEFAULT")) if (0 == internal_strcmp(format, "DEFAULT"))
format = kDefaultFormat; format = kDefaultFormat;
for (const char *p = format; *p != '\0'; p++) { for (const char *p = format; *p != '\0'; p++) {
@ -126,71 +132,69 @@ void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
buffer->append("%zu", frame_no); buffer->append("%zu", frame_no);
break; break;
case 'p': case 'p':
buffer->append("0x%zx", info.address); buffer->append("0x%zx", address);
break; break;
case 'm': case 'm':
buffer->append("%s", StripPathPrefix(info.module, strip_path_prefix)); buffer->append("%s", StripPathPrefix(info->module, strip_path_prefix));
break; break;
case 'o': case 'o':
buffer->append("0x%zx", info.module_offset); buffer->append("0x%zx", info->module_offset);
break; break;
case 'f': case 'f':
buffer->append("%s", buffer->append("%s", DemangleFunctionName(StripFunctionName(
DemangleFunctionName( info->function, strip_func_prefix)));
StripFunctionName(info.function, strip_func_prefix)));
break; break;
case 'q': case 'q':
buffer->append("0x%zx", info.function_offset != AddressInfo::kUnknown buffer->append("0x%zx", info->function_offset != AddressInfo::kUnknown
? info.function_offset ? info->function_offset
: 0x0); : 0x0);
break; break;
case 's': case 's':
buffer->append("%s", StripPathPrefix(info.file, strip_path_prefix)); buffer->append("%s", StripPathPrefix(info->file, strip_path_prefix));
break; break;
case 'l': case 'l':
buffer->append("%d", info.line); buffer->append("%d", info->line);
break; break;
case 'c': case 'c':
buffer->append("%d", info.column); buffer->append("%d", info->column);
break; break;
// Smarter special cases. // Smarter special cases.
case 'F': case 'F':
// Function name and offset, if file is unknown. // Function name and offset, if file is unknown.
if (info.function) { if (info->function) {
buffer->append("in %s", buffer->append("in %s", DemangleFunctionName(StripFunctionName(
DemangleFunctionName( info->function, strip_func_prefix)));
StripFunctionName(info.function, strip_func_prefix))); if (!info->file && info->function_offset != AddressInfo::kUnknown)
if (!info.file && info.function_offset != AddressInfo::kUnknown) buffer->append("+0x%zx", info->function_offset);
buffer->append("+0x%zx", info.function_offset);
} }
break; break;
case 'S': case 'S':
// File/line information. // File/line information.
RenderSourceLocation(buffer, info.file, info.line, info.column, vs_style, RenderSourceLocation(buffer, info->file, info->line, info->column,
strip_path_prefix); vs_style, strip_path_prefix);
break; break;
case 'L': case 'L':
// Source location, or module location. // Source location, or module location.
if (info.file) { if (info->file) {
RenderSourceLocation(buffer, info.file, info.line, info.column, RenderSourceLocation(buffer, info->file, info->line, info->column,
vs_style, strip_path_prefix); vs_style, strip_path_prefix);
} else if (info.module) { } else if (info->module) {
RenderModuleLocation(buffer, info.module, info.module_offset, RenderModuleLocation(buffer, info->module, info->module_offset,
info.module_arch, strip_path_prefix); info->module_arch, strip_path_prefix);
} else { } else {
buffer->append("(<unknown module>)"); buffer->append("(<unknown module>)");
} }
break; break;
case 'M': case 'M':
// Module basename and offset, or PC. // Module basename and offset, or PC.
if (info.address & kExternalPCBit) if (address & kExternalPCBit) {
{} // There PCs are not meaningful. } // There PCs are not meaningful.
else if (info.module) else if (info->module)
// Always strip the module name for %M. // Always strip the module name for %M.
RenderModuleLocation(buffer, StripModuleName(info.module), RenderModuleLocation(buffer, StripModuleName(info->module),
info.module_offset, info.module_arch, ""); info->module_offset, info->module_arch, "");
else else
buffer->append("(%p)", (void *)info.address); buffer->append("(%p)", (void *)address);
break; break;
default: default:
Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n", *p, Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n", *p,
@ -200,6 +204,29 @@ void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
} }
} }
bool RenderNeedsSymbolization(const char *format) {
if (0 == internal_strcmp(format, "DEFAULT"))
format = kDefaultFormat;
for (const char *p = format; *p != '\0'; p++) {
if (*p != '%')
continue;
p++;
switch (*p) {
case '%':
break;
case 'n':
// frame_no
break;
case 'p':
// address
break;
default:
return true;
}
}
return false;
}
void RenderData(InternalScopedString *buffer, const char *format, void RenderData(InternalScopedString *buffer, const char *format,
const DataInfo *DI, const char *strip_path_prefix) { const DataInfo *DI, const char *strip_path_prefix) {
for (const char *p = format; *p != '\0'; p++) { for (const char *p = format; *p != '\0'; p++) {

View File

@ -47,10 +47,12 @@ namespace __sanitizer {
// module+offset if it is known, or (<unknown module>) string. // module+offset if it is known, or (<unknown module>) string.
// %M - prints module basename and offset, if it is known, or PC. // %M - prints module basename and offset, if it is known, or PC.
void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no, void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
const AddressInfo &info, bool vs_style, uptr address, const AddressInfo *info, bool vs_style,
const char *strip_path_prefix = "", const char *strip_path_prefix = "",
const char *strip_func_prefix = ""); const char *strip_func_prefix = "");
bool RenderNeedsSymbolization(const char *format);
void RenderSourceLocation(InternalScopedString *buffer, const char *file, void RenderSourceLocation(InternalScopedString *buffer, const char *file,
int line, int column, bool vs_style, int line, int column, bool vs_style,
const char *strip_path_prefix); const char *strip_path_prefix);

View File

@ -33,7 +33,8 @@ void ReportErrorSummary(const char *error_type, const AddressInfo &info,
if (!common_flags()->print_summary) return; if (!common_flags()->print_summary) return;
InternalScopedString buff(kMaxSummaryLength); InternalScopedString buff(kMaxSummaryLength);
buff.append("%s ", error_type); buff.append("%s ", error_type);
RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style, RenderFrame(&buff, "%L %F", 0, info.address, &info,
common_flags()->symbolize_vs_style,
common_flags()->strip_path_prefix); common_flags()->strip_path_prefix);
ReportErrorSummary(buff.data(), alt_tool_name); ReportErrorSummary(buff.data(), alt_tool_name);
} }

View File

@ -79,10 +79,11 @@ TEST(SanitizerStacktracePrinter, RenderFrame) {
InternalScopedString str(256); InternalScopedString str(256);
// Dump all the AddressInfo fields. // Dump all the AddressInfo fields.
RenderFrame(&str, "%% Frame:%n PC:%p Module:%m ModuleOffset:%o " RenderFrame(&str,
"%% Frame:%n PC:%p Module:%m ModuleOffset:%o "
"Function:%f FunctionOffset:%q Source:%s Line:%l " "Function:%f FunctionOffset:%q Source:%s Line:%l "
"Column:%c", "Column:%c",
frame_no, info, false, "/path/to/", "function_"); frame_no, info.address, &info, false, "/path/to/", "function_");
EXPECT_STREQ("% Frame:42 PC:0x400000 Module:my/module ModuleOffset:0x200 " EXPECT_STREQ("% Frame:42 PC:0x400000 Module:my/module ModuleOffset:0x200 "
"Function:foo FunctionOffset:0x100 Source:my/source Line:10 " "Function:foo FunctionOffset:0x100 Source:my/source Line:10 "
"Column:5", "Column:5",
@ -92,61 +93,61 @@ TEST(SanitizerStacktracePrinter, RenderFrame) {
// Test special format specifiers. // Test special format specifiers.
info.address = 0x400000; info.address = 0x400000;
RenderFrame(&str, "%M", frame_no, info, false); RenderFrame(&str, "%M", frame_no, info.address, &info, false);
EXPECT_NE(nullptr, internal_strstr(str.data(), "400000")); EXPECT_NE(nullptr, internal_strstr(str.data(), "400000"));
str.clear(); str.clear();
RenderFrame(&str, "%L", frame_no, info, false); RenderFrame(&str, "%L", frame_no, info.address, &info, false);
EXPECT_STREQ("(<unknown module>)", str.data()); EXPECT_STREQ("(<unknown module>)", str.data());
str.clear(); str.clear();
info.module = internal_strdup("/path/to/module"); info.module = internal_strdup("/path/to/module");
info.module_offset = 0x200; info.module_offset = 0x200;
RenderFrame(&str, "%M", frame_no, info, false); RenderFrame(&str, "%M", frame_no, info.address, &info, false);
EXPECT_NE(nullptr, internal_strstr(str.data(), "(module+0x")); EXPECT_NE(nullptr, internal_strstr(str.data(), "(module+0x"));
EXPECT_NE(nullptr, internal_strstr(str.data(), "200")); EXPECT_NE(nullptr, internal_strstr(str.data(), "200"));
str.clear(); str.clear();
RenderFrame(&str, "%L", frame_no, info, false); RenderFrame(&str, "%L", frame_no, info.address, &info, false);
EXPECT_STREQ("(/path/to/module+0x200)", str.data()); EXPECT_STREQ("(/path/to/module+0x200)", str.data());
str.clear(); str.clear();
info.function = internal_strdup("my_function"); info.function = internal_strdup("my_function");
RenderFrame(&str, "%F", frame_no, info, false); RenderFrame(&str, "%F", frame_no, info.address, &info, false);
EXPECT_STREQ("in my_function", str.data()); EXPECT_STREQ("in my_function", str.data());
str.clear(); str.clear();
info.function_offset = 0x100; info.function_offset = 0x100;
RenderFrame(&str, "%F %S", frame_no, info, false); RenderFrame(&str, "%F %S", frame_no, info.address, &info, false);
EXPECT_STREQ("in my_function+0x100 <null>", str.data()); EXPECT_STREQ("in my_function+0x100 <null>", str.data());
str.clear(); str.clear();
info.file = internal_strdup("my_file"); info.file = internal_strdup("my_file");
RenderFrame(&str, "%F %S", frame_no, info, false); RenderFrame(&str, "%F %S", frame_no, info.address, &info, false);
EXPECT_STREQ("in my_function my_file", str.data()); EXPECT_STREQ("in my_function my_file", str.data());
str.clear(); str.clear();
info.line = 10; info.line = 10;
RenderFrame(&str, "%F %S", frame_no, info, false); RenderFrame(&str, "%F %S", frame_no, info.address, &info, false);
EXPECT_STREQ("in my_function my_file:10", str.data()); EXPECT_STREQ("in my_function my_file:10", str.data());
str.clear(); str.clear();
info.column = 5; info.column = 5;
RenderFrame(&str, "%S %L", frame_no, info, false); RenderFrame(&str, "%S %L", frame_no, info.address, &info, false);
EXPECT_STREQ("my_file:10:5 my_file:10:5", str.data()); EXPECT_STREQ("my_file:10:5 my_file:10:5", str.data());
str.clear(); str.clear();
RenderFrame(&str, "%S %L", frame_no, info, true); RenderFrame(&str, "%S %L", frame_no, info.address, &info, true);
EXPECT_STREQ("my_file(10,5) my_file(10,5)", str.data()); EXPECT_STREQ("my_file(10,5) my_file(10,5)", str.data());
str.clear(); str.clear();
info.column = 0; info.column = 0;
RenderFrame(&str, "%F %S", frame_no, info, true); RenderFrame(&str, "%F %S", frame_no, info.address, &info, true);
EXPECT_STREQ("in my_function my_file(10)", str.data()); EXPECT_STREQ("in my_function my_file(10)", str.data());
str.clear(); str.clear();
info.line = 0; info.line = 0;
RenderFrame(&str, "%F %S", frame_no, info, true); RenderFrame(&str, "%F %S", frame_no, info.address, &info, true);
EXPECT_STREQ("in my_function my_file", str.data()); EXPECT_STREQ("in my_function my_file", str.data());
str.clear(); str.clear();

View File

@ -128,7 +128,8 @@ void PrintStack(const ReportStack *ent) {
SymbolizedStack *frame = ent->frames; SymbolizedStack *frame = ent->frames;
for (int i = 0; frame && frame->info.address; frame = frame->next, i++) { for (int i = 0; frame && frame->info.address; frame = frame->next, i++) {
InternalScopedString res(2 * GetPageSizeCached()); InternalScopedString res(2 * GetPageSizeCached());
RenderFrame(&res, common_flags()->stack_trace_format, i, frame->info, RenderFrame(&res, common_flags()->stack_trace_format, i,
frame->info.address, &frame->info,
common_flags()->symbolize_vs_style, common_flags()->symbolize_vs_style,
common_flags()->strip_path_prefix, kInterposedFunctionPrefix); common_flags()->strip_path_prefix, kInterposedFunctionPrefix);
Printf("%s\n", res.data()); Printf("%s\n", res.data());

View File

@ -2,6 +2,7 @@
// RUN: %clangxx -O3 %s -o %t && %env_tool_opts=stack_trace_format=DEFAULT %run %t 2>&1 | FileCheck %s // RUN: %clangxx -O3 %s -o %t && %env_tool_opts=stack_trace_format=DEFAULT %run %t 2>&1 | FileCheck %s
// RUN: %env_tool_opts=stack_trace_format=frame%n_lineno%l %run %t 2>&1 | FileCheck %s --check-prefix=CUSTOM // RUN: %env_tool_opts=stack_trace_format=frame%n_lineno%l %run %t 2>&1 | FileCheck %s --check-prefix=CUSTOM
// RUN: %env_tool_opts=symbolize_inline_frames=false:stack_trace_format=DEFAULT %run %t 2>&1 | FileCheck %s --check-prefix=NOINLINE // RUN: %env_tool_opts=symbolize_inline_frames=false:stack_trace_format=DEFAULT %run %t 2>&1 | FileCheck %s --check-prefix=NOINLINE
// RUN: %env_tool_opts=stack_trace_format='"frame:%n address:%%p"' %run %t 2>&1 | FileCheck %s --check-prefix=NOSYMBOLIZE
// UNSUPPORTED: darwin // UNSUPPORTED: darwin
@ -27,3 +28,7 @@ int main() {
// NOINLINE: #0 0x{{.*}} in __sanitizer_print_stack_trace // NOINLINE: #0 0x{{.*}} in __sanitizer_print_stack_trace
// NOINLINE: #1 0x{{.*}} in main{{.*}}print-stack-trace.cpp:[[@LINE-15]] // NOINLINE: #1 0x{{.*}} in main{{.*}}print-stack-trace.cpp:[[@LINE-15]]
// NOSYMBOLIZE: frame:0 address:{{0x.*}}
// NOSYMBOLIZE: frame:1 address:{{0x.*}}
// NOSYMBOLIZE: frame:2 address:{{0x.*}}