Commit b9ef2b6a authored by Mason Freed's avatar Mason Freed Committed by Commit Bot

[CI] Added optional prefix string to stack trace printing.

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: default avatarNico Weber <thakis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#589956}
parent 54d6c376
......@@ -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();
}
......
......@@ -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);
......
......@@ -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
......@@ -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()) {
......
......@@ -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:
......@@ -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);
......
......@@ -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);
......@@ -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");
}
......@@ -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
......
......@@ -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)
......
......@@ -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) {
......@@ -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] << "+"
......@@ -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);
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment