Skip to content

Commit 4d5b1de

Browse files
committed
[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
1 parent d56fdc8 commit 4d5b1de

File tree

8 files changed

+104
-55
lines changed

8 files changed

+104
-55
lines changed

compiler-rt/lib/hwasan/hwasan_report.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ static void PrintStackAllocations(StackAllocationsRingBuffer *sa,
224224
frame_desc.append(" record_addr:0x%zx record:0x%zx",
225225
reinterpret_cast<uptr>(record_addr), record);
226226
if (SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc)) {
227-
RenderFrame(&frame_desc, " %F %L\n", 0, frame->info,
227+
RenderFrame(&frame_desc, " %F %L\n", 0, frame->info.address, &frame->info,
228228
common_flags()->symbolize_vs_style,
229229
common_flags()->strip_path_prefix);
230230
frame->ClearAll();

compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,23 @@ void StackTrace::Print() const {
2626
InternalScopedString frame_desc(GetPageSizeCached() * 2);
2727
InternalScopedString dedup_token(GetPageSizeCached());
2828
int dedup_frames = common_flags()->dedup_token_length;
29+
bool symbolize = RenderNeedsSymbolization(common_flags()->stack_trace_format);
2930
uptr frame_num = 0;
3031
for (uptr i = 0; i < size && trace[i]; i++) {
3132
// PCs in stack traces are actually the return addresses, that is,
3233
// addresses of the next instructions after the call.
3334
uptr pc = GetPreviousInstructionPc(trace[i]);
34-
SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(pc);
35+
SymbolizedStack *frames;
36+
if (symbolize)
37+
frames = Symbolizer::GetOrInit()->SymbolizePC(pc);
38+
else
39+
frames = SymbolizedStack::New(pc);
3540
CHECK(frames);
3641
for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
3742
frame_desc.clear();
3843
RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++,
39-
cur->info, common_flags()->symbolize_vs_style,
44+
cur->info.address, symbolize ? &cur->info : nullptr,
45+
common_flags()->symbolize_vs_style,
4046
common_flags()->strip_path_prefix);
4147
Printf("%s\n", frame_desc.data());
4248
if (dedup_frames-- > 0) {
@@ -108,7 +114,12 @@ void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf,
108114
uptr out_buf_size) {
109115
if (!out_buf_size) return;
110116
pc = StackTrace::GetPreviousInstructionPc(pc);
111-
SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
117+
SymbolizedStack *frame;
118+
bool symbolize = RenderNeedsSymbolization(fmt);
119+
if (symbolize)
120+
frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
121+
else
122+
frame = SymbolizedStack::New(pc);
112123
if (!frame) {
113124
internal_strncpy(out_buf, "<can't symbolize>", out_buf_size);
114125
out_buf[out_buf_size - 1] = 0;
@@ -121,7 +132,8 @@ void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf,
121132
for (SymbolizedStack *cur = frame; cur && out_buf < out_end;
122133
cur = cur->next) {
123134
frame_desc.clear();
124-
RenderFrame(&frame_desc, fmt, frame_num++, cur->info,
135+
RenderFrame(&frame_desc, fmt, frame_num++, cur->info.address,
136+
symbolize ? &cur->info : nullptr,
125137
common_flags()->symbolize_vs_style,
126138
common_flags()->strip_path_prefix);
127139
if (!frame_desc.length())

compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,14 @@ static const char *DemangleFunctionName(const char *function) {
107107
static const char kDefaultFormat[] = " #%n %p %F %L";
108108

109109
void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
110-
const AddressInfo &info, bool vs_style,
110+
uptr address, const AddressInfo *info, bool vs_style,
111111
const char *strip_path_prefix, const char *strip_func_prefix) {
112+
// info will be null in the case where symbolization is not needed for the
113+
// given format. This ensures that the code below will get a hard failure
114+
// rather than print incorrect information in case RenderNeedsSymbolization
115+
// ever ends up out of sync with this function. If non-null, the addresses
116+
// should match.
117+
CHECK(!info || address == info->address);
112118
if (0 == internal_strcmp(format, "DEFAULT"))
113119
format = kDefaultFormat;
114120
for (const char *p = format; *p != '\0'; p++) {
@@ -126,71 +132,69 @@ void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
126132
buffer->append("%zu", frame_no);
127133
break;
128134
case 'p':
129-
buffer->append("0x%zx", info.address);
135+
buffer->append("0x%zx", address);
130136
break;
131137
case 'm':
132-
buffer->append("%s", StripPathPrefix(info.module, strip_path_prefix));
138+
buffer->append("%s", StripPathPrefix(info->module, strip_path_prefix));
133139
break;
134140
case 'o':
135-
buffer->append("0x%zx", info.module_offset);
141+
buffer->append("0x%zx", info->module_offset);
136142
break;
137143
case 'f':
138-
buffer->append("%s",
139-
DemangleFunctionName(
140-
StripFunctionName(info.function, strip_func_prefix)));
144+
buffer->append("%s", DemangleFunctionName(StripFunctionName(
145+
info->function, strip_func_prefix)));
141146
break;
142147
case 'q':
143-
buffer->append("0x%zx", info.function_offset != AddressInfo::kUnknown
144-
? info.function_offset
148+
buffer->append("0x%zx", info->function_offset != AddressInfo::kUnknown
149+
? info->function_offset
145150
: 0x0);
146151
break;
147152
case 's':
148-
buffer->append("%s", StripPathPrefix(info.file, strip_path_prefix));
153+
buffer->append("%s", StripPathPrefix(info->file, strip_path_prefix));
149154
break;
150155
case 'l':
151-
buffer->append("%d", info.line);
156+
buffer->append("%d", info->line);
152157
break;
153158
case 'c':
154-
buffer->append("%d", info.column);
159+
buffer->append("%d", info->column);
155160
break;
156161
// Smarter special cases.
157162
case 'F':
158163
// Function name and offset, if file is unknown.
159-
if (info.function) {
160-
buffer->append("in %s",
161-
DemangleFunctionName(
162-
StripFunctionName(info.function, strip_func_prefix)));
163-
if (!info.file && info.function_offset != AddressInfo::kUnknown)
164-
buffer->append("+0x%zx", info.function_offset);
164+
if (info->function) {
165+
buffer->append("in %s", DemangleFunctionName(StripFunctionName(
166+
info->function, strip_func_prefix)));
167+
if (!info->file && info->function_offset != AddressInfo::kUnknown)
168+
buffer->append("+0x%zx", info->function_offset);
165169
}
166170
break;
167171
case 'S':
168172
// File/line information.
169-
RenderSourceLocation(buffer, info.file, info.line, info.column, vs_style,
170-
strip_path_prefix);
173+
RenderSourceLocation(buffer, info->file, info->line, info->column,
174+
vs_style, strip_path_prefix);
171175
break;
172176
case 'L':
173177
// Source location, or module location.
174-
if (info.file) {
175-
RenderSourceLocation(buffer, info.file, info.line, info.column,
178+
if (info->file) {
179+
RenderSourceLocation(buffer, info->file, info->line, info->column,
176180
vs_style, strip_path_prefix);
177-
} else if (info.module) {
178-
RenderModuleLocation(buffer, info.module, info.module_offset,
179-
info.module_arch, strip_path_prefix);
181+
} else if (info->module) {
182+
RenderModuleLocation(buffer, info->module, info->module_offset,
183+
info->module_arch, strip_path_prefix);
180184
} else {
181185
buffer->append("(<unknown module>)");
182186
}
183187
break;
184188
case 'M':
185189
// Module basename and offset, or PC.
186-
if (info.address & kExternalPCBit)
187-
{} // There PCs are not meaningful.
188-
else if (info.module)
190+
if (address & kExternalPCBit) {
191+
} // There PCs are not meaningful.
192+
else if (info->module)
189193
// Always strip the module name for %M.
190-
RenderModuleLocation(buffer, StripModuleName(info.module),
191-
info.module_offset, info.module_arch, "");
194+
RenderModuleLocation(buffer, StripModuleName(info->module),
195+
info->module_offset, info->module_arch, "");
192196
else
193-
buffer->append("(%p)", (void *)info.address);
197+
buffer->append("(%p)", (void *)address);
194198
break;
195199
default:
196200
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,
200204
}
201205
}
202206

207+
bool RenderNeedsSymbolization(const char *format) {
208+
if (0 == internal_strcmp(format, "DEFAULT"))
209+
format = kDefaultFormat;
210+
for (const char *p = format; *p != '\0'; p++) {
211+
if (*p != '%')
212+
continue;
213+
p++;
214+
switch (*p) {
215+
case '%':
216+
break;
217+
case 'n':
218+
// frame_no
219+
break;
220+
case 'p':
221+
// address
222+
break;
223+
default:
224+
return true;
225+
}
226+
}
227+
return false;
228+
}
229+
203230
void RenderData(InternalScopedString *buffer, const char *format,
204231
const DataInfo *DI, const char *strip_path_prefix) {
205232
for (const char *p = format; *p != '\0'; p++) {

compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,12 @@ namespace __sanitizer {
4747
// module+offset if it is known, or (<unknown module>) string.
4848
// %M - prints module basename and offset, if it is known, or PC.
4949
void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
50-
const AddressInfo &info, bool vs_style,
50+
uptr address, const AddressInfo *info, bool vs_style,
5151
const char *strip_path_prefix = "",
5252
const char *strip_func_prefix = "");
5353

54+
bool RenderNeedsSymbolization(const char *format);
55+
5456
void RenderSourceLocation(InternalScopedString *buffer, const char *file,
5557
int line, int column, bool vs_style,
5658
const char *strip_path_prefix);

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_report.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ void ReportErrorSummary(const char *error_type, const AddressInfo &info,
3333
if (!common_flags()->print_summary) return;
3434
InternalScopedString buff(kMaxSummaryLength);
3535
buff.append("%s ", error_type);
36-
RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style,
36+
RenderFrame(&buff, "%L %F", 0, info.address, &info,
37+
common_flags()->symbolize_vs_style,
3738
common_flags()->strip_path_prefix);
3839
ReportErrorSummary(buff.data(), alt_tool_name);
3940
}

compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cpp

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,11 @@ TEST(SanitizerStacktracePrinter, RenderFrame) {
7979
InternalScopedString str(256);
8080

8181
// Dump all the AddressInfo fields.
82-
RenderFrame(&str, "%% Frame:%n PC:%p Module:%m ModuleOffset:%o "
83-
"Function:%f FunctionOffset:%q Source:%s Line:%l "
84-
"Column:%c",
85-
frame_no, info, false, "/path/to/", "function_");
82+
RenderFrame(&str,
83+
"%% Frame:%n PC:%p Module:%m ModuleOffset:%o "
84+
"Function:%f FunctionOffset:%q Source:%s Line:%l "
85+
"Column:%c",
86+
frame_no, info.address, &info, false, "/path/to/", "function_");
8687
EXPECT_STREQ("% Frame:42 PC:0x400000 Module:my/module ModuleOffset:0x200 "
8788
"Function:foo FunctionOffset:0x100 Source:my/source Line:10 "
8889
"Column:5",
@@ -92,61 +93,61 @@ TEST(SanitizerStacktracePrinter, RenderFrame) {
9293

9394
// Test special format specifiers.
9495
info.address = 0x400000;
95-
RenderFrame(&str, "%M", frame_no, info, false);
96+
RenderFrame(&str, "%M", frame_no, info.address, &info, false);
9697
EXPECT_NE(nullptr, internal_strstr(str.data(), "400000"));
9798
str.clear();
9899

99-
RenderFrame(&str, "%L", frame_no, info, false);
100+
RenderFrame(&str, "%L", frame_no, info.address, &info, false);
100101
EXPECT_STREQ("(<unknown module>)", str.data());
101102
str.clear();
102103

103104
info.module = internal_strdup("/path/to/module");
104105
info.module_offset = 0x200;
105-
RenderFrame(&str, "%M", frame_no, info, false);
106+
RenderFrame(&str, "%M", frame_no, info.address, &info, false);
106107
EXPECT_NE(nullptr, internal_strstr(str.data(), "(module+0x"));
107108
EXPECT_NE(nullptr, internal_strstr(str.data(), "200"));
108109
str.clear();
109110

110-
RenderFrame(&str, "%L", frame_no, info, false);
111+
RenderFrame(&str, "%L", frame_no, info.address, &info, false);
111112
EXPECT_STREQ("(/path/to/module+0x200)", str.data());
112113
str.clear();
113114

114115
info.function = internal_strdup("my_function");
115-
RenderFrame(&str, "%F", frame_no, info, false);
116+
RenderFrame(&str, "%F", frame_no, info.address, &info, false);
116117
EXPECT_STREQ("in my_function", str.data());
117118
str.clear();
118119

119120
info.function_offset = 0x100;
120-
RenderFrame(&str, "%F %S", frame_no, info, false);
121+
RenderFrame(&str, "%F %S", frame_no, info.address, &info, false);
121122
EXPECT_STREQ("in my_function+0x100 <null>", str.data());
122123
str.clear();
123124

124125
info.file = internal_strdup("my_file");
125-
RenderFrame(&str, "%F %S", frame_no, info, false);
126+
RenderFrame(&str, "%F %S", frame_no, info.address, &info, false);
126127
EXPECT_STREQ("in my_function my_file", str.data());
127128
str.clear();
128129

129130
info.line = 10;
130-
RenderFrame(&str, "%F %S", frame_no, info, false);
131+
RenderFrame(&str, "%F %S", frame_no, info.address, &info, false);
131132
EXPECT_STREQ("in my_function my_file:10", str.data());
132133
str.clear();
133134

134135
info.column = 5;
135-
RenderFrame(&str, "%S %L", frame_no, info, false);
136+
RenderFrame(&str, "%S %L", frame_no, info.address, &info, false);
136137
EXPECT_STREQ("my_file:10:5 my_file:10:5", str.data());
137138
str.clear();
138139

139-
RenderFrame(&str, "%S %L", frame_no, info, true);
140+
RenderFrame(&str, "%S %L", frame_no, info.address, &info, true);
140141
EXPECT_STREQ("my_file(10,5) my_file(10,5)", str.data());
141142
str.clear();
142143

143144
info.column = 0;
144-
RenderFrame(&str, "%F %S", frame_no, info, true);
145+
RenderFrame(&str, "%F %S", frame_no, info.address, &info, true);
145146
EXPECT_STREQ("in my_function my_file(10)", str.data());
146147
str.clear();
147148

148149
info.line = 0;
149-
RenderFrame(&str, "%F %S", frame_no, info, true);
150+
RenderFrame(&str, "%F %S", frame_no, info.address, &info, true);
150151
EXPECT_STREQ("in my_function my_file", str.data());
151152
str.clear();
152153

compiler-rt/lib/tsan/rtl/tsan_report.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ void PrintStack(const ReportStack *ent) {
128128
SymbolizedStack *frame = ent->frames;
129129
for (int i = 0; frame && frame->info.address; frame = frame->next, i++) {
130130
InternalScopedString res(2 * GetPageSizeCached());
131-
RenderFrame(&res, common_flags()->stack_trace_format, i, frame->info,
131+
RenderFrame(&res, common_flags()->stack_trace_format, i,
132+
frame->info.address, &frame->info,
132133
common_flags()->symbolize_vs_style,
133134
common_flags()->strip_path_prefix, kInterposedFunctionPrefix);
134135
Printf("%s\n", res.data());

compiler-rt/test/sanitizer_common/TestCases/print-stack-trace.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// RUN: %clangxx -O3 %s -o %t && %env_tool_opts=stack_trace_format=DEFAULT %run %t 2>&1 | FileCheck %s
33
// RUN: %env_tool_opts=stack_trace_format=frame%n_lineno%l %run %t 2>&1 | FileCheck %s --check-prefix=CUSTOM
44
// RUN: %env_tool_opts=symbolize_inline_frames=false:stack_trace_format=DEFAULT %run %t 2>&1 | FileCheck %s --check-prefix=NOINLINE
5+
// RUN: %env_tool_opts=stack_trace_format='"frame:%n address:%%p"' %run %t 2>&1 | FileCheck %s --check-prefix=NOSYMBOLIZE
56

67
// UNSUPPORTED: darwin
78

@@ -27,3 +28,7 @@ int main() {
2728

2829
// NOINLINE: #0 0x{{.*}} in __sanitizer_print_stack_trace
2930
// NOINLINE: #1 0x{{.*}} in main{{.*}}print-stack-trace.cpp:[[@LINE-15]]
31+
32+
// NOSYMBOLIZE: frame:0 address:{{0x.*}}
33+
// NOSYMBOLIZE: frame:1 address:{{0x.*}}
34+
// NOSYMBOLIZE: frame:2 address:{{0x.*}}

0 commit comments

Comments
 (0)