Skip to content

Commit

Permalink
[CI] Added optional prefix string to stack trace printing.
Browse files Browse the repository at this point in the history
Useful for multi-thread or multi-process stack trace printing, when
competing threads step on each other's stack traces. For example:

base: :debug::StackTrace(50).Print(" [" + std::toString(getpid()) + "] ");
Change-Id: I6d1e686b22769de4a58c7232f93d3a9ca0a1ff0f
Reviewed-on: https://chromium-review.googlesource.com/1199698
Commit-Queue: Mason Freed <masonfreed@chromium.org>
Reviewed-by: Nico Weber <thakis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#589956}
  • Loading branch information
mfreed7 authored and Commit Bot committed Sep 10, 2018
1 parent 54d6c37 commit b9ef2b6
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 21 deletions.
13 changes: 12 additions & 1 deletion base/debug/stack_trace.cc
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,21 @@ const void *const *StackTrace::Addresses(size_t* count) const {
return nullptr;
}

void StackTrace::Print() const {
PrintWithPrefix(nullptr);
}

void StackTrace::OutputToStream(std::ostream* os) const {
OutputToStreamWithPrefix(os, nullptr);
}

std::string StackTrace::ToString() const {
return ToStringWithPrefix(nullptr);
}
std::string StackTrace::ToStringWithPrefix(const char* prefix_string) const {
std::stringstream stream;
#if !defined(__UCLIBC__) && !defined(_AIX)
OutputToStream(&stream);
OutputToStreamWithPrefix(&stream, prefix_string);
#endif
return stream.str();
}
Expand Down
12 changes: 12 additions & 0 deletions base/debug/stack_trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,26 @@ class BASE_EXPORT StackTrace {
// Prints the stack trace to stderr.
void Print() const;

// Prints the stack trace to stderr, prepending the given string before
// each output line.
void PrintWithPrefix(const char* prefix_string) const;

#if !defined(__UCLIBC__) & !defined(_AIX)
// Resolves backtrace to symbols and write to stream.
void OutputToStream(std::ostream* os) const;
// Resolves backtrace to symbols and write to stream, with the provided
// prefix string prepended to each line.
void OutputToStreamWithPrefix(std::ostream* os,
const char* prefix_string) const;
#endif

// Resolves backtrace to symbols and returns as string.
std::string ToString() const;

// Resolves backtrace to symbols and returns as string, prepending the
// provided prefix string to each line.
std::string ToStringWithPrefix(const char* prefix_string) const;

private:
#if defined(OS_WIN)
void InitTrace(const _CONTEXT* context_record);
Expand Down
10 changes: 7 additions & 3 deletions base/debug/stack_trace_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,16 @@ StackTrace::StackTrace(size_t count) {
count_ = state.frame_count;
}

void StackTrace::Print() const {
std::string backtrace = ToString();
void StackTrace::PrintWithPrefix(const char* prefix_string) const {
std::string backtrace = ToStringWithPrefix(prefix_string);
__android_log_write(ANDROID_LOG_ERROR, "chromium", backtrace.c_str());
}

// NOTE: Native libraries in APKs are stripped before installing. Print out the
// relocatable address and library names so host computers can use tools to
// symbolize and demangle (e.g., addr2line, c++filt).
void StackTrace::OutputToStream(std::ostream* os) const {
void StackTrace::OutputToStreamWithPrefix(std::ostream* os,
const char* prefix_string) const {
std::string proc_maps;
std::vector<MappedMemoryRegion> regions;
// Allow IO to read /proc/self/maps. Reading this file doesn't hit the disk
Expand Down Expand Up @@ -116,6 +117,9 @@ void StackTrace::OutputToStream(std::ostream* os) const {
++iter;
}

if (prefix_string)
*os << prefix_string;

*os << base::StringPrintf("#%02zd " FMT_ADDR " ", i, address);

if (iter != regions.end()) {
Expand Down
9 changes: 6 additions & 3 deletions base/debug/stack_trace_fuchsia.cc
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@ StackTrace::StackTrace(size_t count) : count_(0) {
_Unwind_Backtrace(&UnwindStore, &data);
}

void StackTrace::Print() const {
OutputToStream(&std::cerr);
void StackTrace::PrintWithPrefix(const char* prefix_string) const {
OutputToStreamWithPrefix(&std::cerr, prefix_string);
}

// Sample stack trace output is designed to be similar to Fuchsia's crashlogger:
Expand All @@ -185,12 +185,15 @@ void StackTrace::Print() const {
// bt#21: pc 0x1527a05b51b4 (app:/system/base_unittests,0x18e81b4)
// bt#22: pc 0x54fdbf3593de (libc.so,0x1c3de)
// bt#23: end
void StackTrace::OutputToStream(std::ostream* os) const {
void StackTrace::OutputToStreamWithPrefix(std::ostream* os,
const char* prefix_string) const {
SymbolMap map;

size_t i = 0;
for (; (i < count_) && os->good(); ++i) {
SymbolMap::Entry* entry = map.GetForAddress(trace_[i]);
if (prefix_string)
*os << prefix_string;
if (entry) {
size_t offset = reinterpret_cast<uintptr_t>(trace_[i]) -
reinterpret_cast<uintptr_t>(entry->addr);
Expand Down
25 changes: 16 additions & 9 deletions base/debug/stack_trace_posix.cc
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,18 @@ void OutputFrameId(intptr_t frame_id, BacktraceOutputHandler* handler) {
}
#endif // defined(USE_SYMBOLIZE)

void ProcessBacktrace(void *const *trace,
void ProcessBacktrace(void* const* trace,
size_t size,
const char* prefix_string,
BacktraceOutputHandler* handler) {
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.

#if defined(USE_SYMBOLIZE)
for (size_t i = 0; i < size; ++i) {
if (prefix_string)
handler->HandleOutput(prefix_string);

OutputFrameId(i, handler);
handler->HandleOutput(" ");
OutputPointer(trace[i], handler);
Expand Down Expand Up @@ -193,6 +197,8 @@ void ProcessBacktrace(void *const *trace,
for (size_t i = 0; i < size; ++i) {
std::string trace_symbol = trace_symbols.get()[i];
DemangleSymbols(&trace_symbol);
if (prefix_string)
handler->HandleOutput(prefix_string);
handler->HandleOutput(trace_symbol.c_str());
handler->HandleOutput("\n");
}
Expand Down Expand Up @@ -817,20 +823,21 @@ StackTrace::StackTrace(size_t count) {
#endif
}

void StackTrace::Print() const {
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.
void StackTrace::PrintWithPrefix(const char* prefix_string) const {
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.

#if !defined(__UCLIBC__) && !defined(_AIX)
PrintBacktraceOutputHandler handler;
ProcessBacktrace(trace_, count_, &handler);
ProcessBacktrace(trace_, count_, prefix_string, &handler);
#endif
}

#if !defined(__UCLIBC__) && !defined(_AIX)
void StackTrace::OutputToStream(std::ostream* os) const {
void StackTrace::OutputToStreamWithPrefix(std::ostream* os,
const char* prefix_string) const {
StreamBacktraceOutputHandler handler(os);
ProcessBacktrace(trace_, count_, &handler);
ProcessBacktrace(trace_, count_, prefix_string, &handler);
}
#endif

Expand Down
34 changes: 34 additions & 0 deletions base/debug/stack_trace_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,40 @@ TEST_F(StackTraceTest, DebugOutputToStream) {
TEST_F(StackTraceTest, DebugPrintBacktrace) {
StackTrace().Print();
}

// The test is used for manual testing, e.g., to see the raw output.
TEST_F(StackTraceTest, DebugPrintWithPrefixBacktrace) {
StackTrace().PrintWithPrefix("[test]");
}

// Make sure nullptr prefix doesn't crash. Output not examined, much
// like the DebugPrintBacktrace test above.
TEST_F(StackTraceTest, DebugPrintWithNullPrefixBacktrace) {
StackTrace().PrintWithPrefix(nullptr);
}

// Test OutputToStreamWithPrefix, mainly to make sure it doesn't
// crash. Any "real" stack trace testing happens above.
TEST_F(StackTraceTest, DebugOutputToStreamWithPrefix) {
StackTrace trace;
const char* prefix_string = "[test]";
std::ostringstream os;
trace.OutputToStreamWithPrefix(&os, prefix_string);
std::string backtrace_message = os.str();

// ToStringWithPrefix() should produce the same output.
EXPECT_EQ(backtrace_message, trace.ToStringWithPrefix(prefix_string));
}

// Make sure nullptr prefix doesn't crash. Output not examined, much
// like the DebugPrintBacktrace test above.
TEST_F(StackTraceTest, DebugOutputToStreamWithNullPrefix) {
StackTrace trace;
std::ostringstream os;
trace.OutputToStreamWithPrefix(&os, nullptr);
trace.ToStringWithPrefix(nullptr);
}

#endif // !defined(__UCLIBC__)

#if defined(OS_POSIX) && !defined(OS_ANDROID)
Expand Down
16 changes: 11 additions & 5 deletions base/debug/stack_trace_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ class SymbolContext {
// extensible like PathService since that can in turn fire CHECKs.
void OutputTraceToStream(const void* const* trace,
size_t count,
std::ostream* os) {
std::ostream* os,
const char* prefix_string) {
base::AutoLock lock(lock_);

for (size_t i = 0; (i < count) && os->good(); ++i) {
Expand Down Expand Up @@ -231,6 +232,8 @@ class SymbolContext {
&line_displacement, &line);

// Output the backtrace line.
if (prefix_string)
(*os) << prefix_string;
(*os) << "\t";
if (has_symbol) {
(*os) << symbol->Name << " [0x" << trace[i] << "+"
Expand Down Expand Up @@ -343,21 +346,24 @@ void StackTrace::InitTrace(const CONTEXT* context_record) {
trace_[i] = NULL;
}

void StackTrace::Print() const {
OutputToStream(&std::cerr);
void StackTrace::PrintWithPrefix(const char* prefix_string) const {
OutputToStreamWithPrefix(&std::cerr, prefix_string);
}

void StackTrace::OutputToStream(std::ostream* os) const {
void StackTrace::OutputToStreamWithPrefix(std::ostream* os,
const char* prefix_string) const {
SymbolContext* context = SymbolContext::GetInstance();
if (g_init_error != ERROR_SUCCESS) {
(*os) << "Error initializing symbols (" << g_init_error
<< "). Dumping unresolved backtrace:\n";
for (size_t i = 0; (i < count_) && os->good(); ++i) {
if (prefix_string)
(*os) << prefix_string;
(*os) << "\t" << trace_[i] << "\n";
}
} else {
(*os) << "Backtrace:\n";
context->OutputTraceToStream(trace_, count_, os);
context->OutputTraceToStream(trace_, count_, os, prefix_string);
}
}

Expand Down

0 comments on commit b9ef2b6

Please sign in to comment.