forked from OSchip/llvm-project
[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:
parent
d56fdc8e95
commit
4d5b1de40e
|
@ -224,7 +224,7 @@ static void PrintStackAllocations(StackAllocationsRingBuffer *sa,
|
|||
frame_desc.append(" record_addr:0x%zx record:0x%zx",
|
||||
reinterpret_cast<uptr>(record_addr), record);
|
||||
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()->strip_path_prefix);
|
||||
frame->ClearAll();
|
||||
|
|
|
@ -26,17 +26,23 @@ void StackTrace::Print() const {
|
|||
InternalScopedString frame_desc(GetPageSizeCached() * 2);
|
||||
InternalScopedString dedup_token(GetPageSizeCached());
|
||||
int dedup_frames = common_flags()->dedup_token_length;
|
||||
bool symbolize = RenderNeedsSymbolization(common_flags()->stack_trace_format);
|
||||
uptr frame_num = 0;
|
||||
for (uptr i = 0; i < size && trace[i]; i++) {
|
||||
// PCs in stack traces are actually the return addresses, that is,
|
||||
// addresses of the next instructions after the call.
|
||||
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);
|
||||
for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
|
||||
frame_desc.clear();
|
||||
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);
|
||||
Printf("%s\n", frame_desc.data());
|
||||
if (dedup_frames-- > 0) {
|
||||
|
@ -108,7 +114,12 @@ void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf,
|
|||
uptr out_buf_size) {
|
||||
if (!out_buf_size) return;
|
||||
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) {
|
||||
internal_strncpy(out_buf, "<can't symbolize>", out_buf_size);
|
||||
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;
|
||||
cur = cur->next) {
|
||||
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()->strip_path_prefix);
|
||||
if (!frame_desc.length())
|
||||
|
|
|
@ -107,8 +107,14 @@ static const char *DemangleFunctionName(const char *function) {
|
|||
static const char kDefaultFormat[] = " #%n %p %F %L";
|
||||
|
||||
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) {
|
||||
// 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"))
|
||||
format = kDefaultFormat;
|
||||
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);
|
||||
break;
|
||||
case 'p':
|
||||
buffer->append("0x%zx", info.address);
|
||||
buffer->append("0x%zx", address);
|
||||
break;
|
||||
case 'm':
|
||||
buffer->append("%s", StripPathPrefix(info.module, strip_path_prefix));
|
||||
buffer->append("%s", StripPathPrefix(info->module, strip_path_prefix));
|
||||
break;
|
||||
case 'o':
|
||||
buffer->append("0x%zx", info.module_offset);
|
||||
buffer->append("0x%zx", info->module_offset);
|
||||
break;
|
||||
case 'f':
|
||||
buffer->append("%s",
|
||||
DemangleFunctionName(
|
||||
StripFunctionName(info.function, strip_func_prefix)));
|
||||
buffer->append("%s", DemangleFunctionName(StripFunctionName(
|
||||
info->function, strip_func_prefix)));
|
||||
break;
|
||||
case 'q':
|
||||
buffer->append("0x%zx", info.function_offset != AddressInfo::kUnknown
|
||||
? info.function_offset
|
||||
buffer->append("0x%zx", info->function_offset != AddressInfo::kUnknown
|
||||
? info->function_offset
|
||||
: 0x0);
|
||||
break;
|
||||
case 's':
|
||||
buffer->append("%s", StripPathPrefix(info.file, strip_path_prefix));
|
||||
buffer->append("%s", StripPathPrefix(info->file, strip_path_prefix));
|
||||
break;
|
||||
case 'l':
|
||||
buffer->append("%d", info.line);
|
||||
buffer->append("%d", info->line);
|
||||
break;
|
||||
case 'c':
|
||||
buffer->append("%d", info.column);
|
||||
buffer->append("%d", info->column);
|
||||
break;
|
||||
// Smarter special cases.
|
||||
case 'F':
|
||||
// Function name and offset, if file is unknown.
|
||||
if (info.function) {
|
||||
buffer->append("in %s",
|
||||
DemangleFunctionName(
|
||||
StripFunctionName(info.function, strip_func_prefix)));
|
||||
if (!info.file && info.function_offset != AddressInfo::kUnknown)
|
||||
buffer->append("+0x%zx", info.function_offset);
|
||||
if (info->function) {
|
||||
buffer->append("in %s", DemangleFunctionName(StripFunctionName(
|
||||
info->function, strip_func_prefix)));
|
||||
if (!info->file && info->function_offset != AddressInfo::kUnknown)
|
||||
buffer->append("+0x%zx", info->function_offset);
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
// File/line information.
|
||||
RenderSourceLocation(buffer, info.file, info.line, info.column, vs_style,
|
||||
strip_path_prefix);
|
||||
RenderSourceLocation(buffer, info->file, info->line, info->column,
|
||||
vs_style, strip_path_prefix);
|
||||
break;
|
||||
case 'L':
|
||||
// Source location, or module location.
|
||||
if (info.file) {
|
||||
RenderSourceLocation(buffer, info.file, info.line, info.column,
|
||||
if (info->file) {
|
||||
RenderSourceLocation(buffer, info->file, info->line, info->column,
|
||||
vs_style, strip_path_prefix);
|
||||
} else if (info.module) {
|
||||
RenderModuleLocation(buffer, info.module, info.module_offset,
|
||||
info.module_arch, strip_path_prefix);
|
||||
} else if (info->module) {
|
||||
RenderModuleLocation(buffer, info->module, info->module_offset,
|
||||
info->module_arch, strip_path_prefix);
|
||||
} else {
|
||||
buffer->append("(<unknown module>)");
|
||||
}
|
||||
break;
|
||||
case 'M':
|
||||
// Module basename and offset, or PC.
|
||||
if (info.address & kExternalPCBit)
|
||||
{} // There PCs are not meaningful.
|
||||
else if (info.module)
|
||||
if (address & kExternalPCBit) {
|
||||
} // There PCs are not meaningful.
|
||||
else if (info->module)
|
||||
// Always strip the module name for %M.
|
||||
RenderModuleLocation(buffer, StripModuleName(info.module),
|
||||
info.module_offset, info.module_arch, "");
|
||||
RenderModuleLocation(buffer, StripModuleName(info->module),
|
||||
info->module_offset, info->module_arch, "");
|
||||
else
|
||||
buffer->append("(%p)", (void *)info.address);
|
||||
buffer->append("(%p)", (void *)address);
|
||||
break;
|
||||
default:
|
||||
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,
|
||||
const DataInfo *DI, const char *strip_path_prefix) {
|
||||
for (const char *p = format; *p != '\0'; p++) {
|
||||
|
|
|
@ -47,10 +47,12 @@ namespace __sanitizer {
|
|||
// module+offset if it is known, or (<unknown module>) string.
|
||||
// %M - prints module basename and offset, if it is known, or PC.
|
||||
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 = "");
|
||||
|
||||
bool RenderNeedsSymbolization(const char *format);
|
||||
|
||||
void RenderSourceLocation(InternalScopedString *buffer, const char *file,
|
||||
int line, int column, bool vs_style,
|
||||
const char *strip_path_prefix);
|
||||
|
|
|
@ -33,7 +33,8 @@ void ReportErrorSummary(const char *error_type, const AddressInfo &info,
|
|||
if (!common_flags()->print_summary) return;
|
||||
InternalScopedString buff(kMaxSummaryLength);
|
||||
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);
|
||||
ReportErrorSummary(buff.data(), alt_tool_name);
|
||||
}
|
||||
|
|
|
@ -79,10 +79,11 @@ TEST(SanitizerStacktracePrinter, RenderFrame) {
|
|||
InternalScopedString str(256);
|
||||
|
||||
// Dump all the AddressInfo fields.
|
||||
RenderFrame(&str, "%% Frame:%n PC:%p Module:%m ModuleOffset:%o "
|
||||
"Function:%f FunctionOffset:%q Source:%s Line:%l "
|
||||
"Column:%c",
|
||||
frame_no, info, false, "/path/to/", "function_");
|
||||
RenderFrame(&str,
|
||||
"%% Frame:%n PC:%p Module:%m ModuleOffset:%o "
|
||||
"Function:%f FunctionOffset:%q Source:%s Line:%l "
|
||||
"Column:%c",
|
||||
frame_no, info.address, &info, false, "/path/to/", "function_");
|
||||
EXPECT_STREQ("% Frame:42 PC:0x400000 Module:my/module ModuleOffset:0x200 "
|
||||
"Function:foo FunctionOffset:0x100 Source:my/source Line:10 "
|
||||
"Column:5",
|
||||
|
@ -92,61 +93,61 @@ TEST(SanitizerStacktracePrinter, RenderFrame) {
|
|||
|
||||
// Test special format specifiers.
|
||||
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"));
|
||||
str.clear();
|
||||
|
||||
RenderFrame(&str, "%L", frame_no, info, false);
|
||||
RenderFrame(&str, "%L", frame_no, info.address, &info, false);
|
||||
EXPECT_STREQ("(<unknown module>)", str.data());
|
||||
str.clear();
|
||||
|
||||
info.module = internal_strdup("/path/to/module");
|
||||
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(), "200"));
|
||||
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());
|
||||
str.clear();
|
||||
|
||||
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());
|
||||
str.clear();
|
||||
|
||||
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());
|
||||
str.clear();
|
||||
|
||||
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());
|
||||
str.clear();
|
||||
|
||||
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());
|
||||
str.clear();
|
||||
|
||||
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());
|
||||
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());
|
||||
str.clear();
|
||||
|
||||
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());
|
||||
str.clear();
|
||||
|
||||
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());
|
||||
str.clear();
|
||||
|
||||
|
|
|
@ -128,7 +128,8 @@ void PrintStack(const ReportStack *ent) {
|
|||
SymbolizedStack *frame = ent->frames;
|
||||
for (int i = 0; frame && frame->info.address; frame = frame->next, i++) {
|
||||
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()->strip_path_prefix, kInterposedFunctionPrefix);
|
||||
Printf("%s\n", res.data());
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// 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=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
|
||||
|
||||
|
@ -27,3 +28,7 @@ int main() {
|
|||
|
||||
// NOINLINE: #0 0x{{.*}} in __sanitizer_print_stack_trace
|
||||
// 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.*}}
|
||||
|
|
Loading…
Reference in New Issue