diff --git a/src/google_breakpad/processor/basic_source_line_resolver.h b/src/google_breakpad/processor/basic_source_line_resolver.h index 1c7bf465..fe76ad4d 100644 --- a/src/google_breakpad/processor/basic_source_line_resolver.h +++ b/src/google_breakpad/processor/basic_source_line_resolver.h @@ -40,6 +40,7 @@ #include #include +#include #include "common/using_std_string.h" #include "google_breakpad/processor/source_line_resolver_base.h" @@ -84,6 +85,8 @@ class BasicSourceLineResolver : public SourceLineResolverBase { // Helper class, containing useful methods for parsing of Breakpad symbol files. class SymbolParseHelper { public: + using MemAddr = SourceLineResolverInterface::MemAddr; + // Parses a |file_line| declaration. Returns true on success. // Format: FILE . // Notice, that this method modifies the input |file_line| which is why it @@ -94,6 +97,32 @@ class SymbolParseHelper { long* index, // out char** filename); // out + // Parses a |inline_origin_line| declaration. Returns true on success. + // Format: INLINE_ORIGIN . + // Notice, that this method modifies the input |inline_origin_line| which is + // why it can't be const. On success, , and are + // stored in |*origin_id|, |*file_id|, and |*name|. No allocation is + // done, |*name| simply points inside |inline_origin_line|. + static bool ParseInlineOrigin(char* inline_origin_line, // in + long* origin_id, // out + long* file_id, // out + char** name); // out + + // Parses a |inline| declaration. Returns true on success. + // Format: INLINE
+ // .... + // Notice, that this method modifies the input |inline| + // which is why it can't be const. On success, , + // and are stored in |*inline_nest_level|, + // |*call_site_line|, and |*origin_id|, and all pairs of (
, ) + // are added into ranges . + static bool ParseInline( + char* inline_line, // in + long* inline_nest_level, // out + long* call_site_line, // out + long* origin_id, // out + std::vector>* ranges); // out + // Parses a |function_line| declaration. Returns true on success. // Format: FUNC []
. // Notice, that this method modifies the input |function_line| which is why it diff --git a/src/google_breakpad/processor/source_line_resolver_base.h b/src/google_breakpad/processor/source_line_resolver_base.h index fea1657c..2d2e4b35 100644 --- a/src/google_breakpad/processor/source_line_resolver_base.h +++ b/src/google_breakpad/processor/source_line_resolver_base.h @@ -84,11 +84,15 @@ class SourceLineResolverBase : public SourceLineResolverInterface { virtual void UnloadModule(const CodeModule* module); virtual bool HasModule(const CodeModule* module); virtual bool IsModuleCorrupt(const CodeModule* module); - virtual void FillSourceLineInfo(StackFrame* frame); + virtual void FillSourceLineInfo( + StackFrame* frame, + std::vector>* inlined_frames); virtual WindowsFrameInfo* FindWindowsFrameInfo(const StackFrame* frame); virtual CFIFrameInfo* FindCFIFrameInfo(const StackFrame* frame); // Nested structs and classes. + struct InlineOrigin; + struct Inline; struct Line; struct Function; struct PublicSymbol; diff --git a/src/google_breakpad/processor/source_line_resolver_interface.h b/src/google_breakpad/processor/source_line_resolver_interface.h index 99011404..7880c922 100644 --- a/src/google_breakpad/processor/source_line_resolver_interface.h +++ b/src/google_breakpad/processor/source_line_resolver_interface.h @@ -34,7 +34,9 @@ #ifndef GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_INTERFACE_H__ #define GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_INTERFACE_H__ +#include #include +#include #include "common/using_std_string.h" #include "google_breakpad/common/breakpad_types.h" @@ -91,8 +93,12 @@ class SourceLineResolverInterface { // Fills in the function_base, function_name, source_file_name, // and source_line fields of the StackFrame. The instruction and - // module_name fields must already be filled in. - virtual void FillSourceLineInfo(StackFrame* frame) = 0; + // module_name fields must already be filled in. If inlined_frames is not + // nullptr, it will try to construct inlined frames by adding them into + // inlined_frames in an order from outermost frame to inner most frame. + virtual void FillSourceLineInfo( + StackFrame* frame, + std::vector>* inlined_frames) = 0; // If Windows stack walking information is available covering // FRAME's instruction address, return a WindowsFrameInfo structure diff --git a/src/google_breakpad/processor/stack_frame.h b/src/google_breakpad/processor/stack_frame.h index 1491d788..5f3932b6 100644 --- a/src/google_breakpad/processor/stack_frame.h +++ b/src/google_breakpad/processor/stack_frame.h @@ -51,7 +51,8 @@ struct StackFrame { FRAME_TRUST_FP, // Derived from frame pointer FRAME_TRUST_CFI, // Derived from call frame info FRAME_TRUST_PREWALKED, // Explicitly provided by some external stack walker. - FRAME_TRUST_CONTEXT // Given as instruction pointer in a context + FRAME_TRUST_CONTEXT, // Given as instruction pointer in a context + FRAME_TRUST_INLINE // Found by inline records in symbol files. }; StackFrame() @@ -60,9 +61,9 @@ struct StackFrame { function_name(), function_base(), source_file_name(), - source_line(), + source_line(0), source_line_base(), - trust(FRAME_TRUST_NONE) {} + trust(FRAME_TRUST_NONE){} virtual ~StackFrame() {} // Return a string describing how this stack frame was found @@ -81,6 +82,8 @@ struct StackFrame { return "previous frame's frame pointer"; case StackFrame::FRAME_TRUST_SCAN: return "stack scanning"; + case StackFrame::FRAME_TRUST_INLINE: + return "inline record"; default: return "unknown"; } diff --git a/src/google_breakpad/processor/stack_frame_symbolizer.h b/src/google_breakpad/processor/stack_frame_symbolizer.h index 0bbaae0a..0e2c6088 100644 --- a/src/google_breakpad/processor/stack_frame_symbolizer.h +++ b/src/google_breakpad/processor/stack_frame_symbolizer.h @@ -35,8 +35,10 @@ #ifndef GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_SYMBOLIZER_H__ #define GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_SYMBOLIZER_H__ +#include #include #include +#include #include "common/using_std_string.h" #include "google_breakpad/common/breakpad_types.h" @@ -79,7 +81,8 @@ class StackFrameSymbolizer { const CodeModules* modules, const CodeModules* unloaded_modules, const SystemInfo* system_info, - StackFrame* stack_frame); + StackFrame* stack_frame, + std::vector>* inlined_frames); virtual WindowsFrameInfo* FindWindowsFrameInfo(const StackFrame* frame); diff --git a/src/processor/basic_source_line_resolver.cc b/src/processor/basic_source_line_resolver.cc index 64d40015..5d4c7621 100644 --- a/src/processor/basic_source_line_resolver.cc +++ b/src/processor/basic_source_line_resolver.cc @@ -40,6 +40,7 @@ #include #include +#include #include #include @@ -52,6 +53,7 @@ using std::map; using std::vector; using std::make_pair; +using std::unique_ptr; namespace google_breakpad { @@ -202,6 +204,16 @@ bool BasicSourceLineResolver::Module::LoadMapFromMemory( // Ignore these as well, they're similarly just for housekeeping. // // INFO CODE_ID + } else if (strncmp(buffer, "INLINE ", 7) == 0) { + linked_ptr in = ParseInline(buffer); + if (!in.get()) + LogParseError("ParseInline failed", line_number, &num_errors); + else + cur_func->AppendInline(in); + } else if (strncmp(buffer, "INLINE_ORIGIN ", 14) == 0) { + if (!ParseInlineOrigin(buffer)) { + LogParseError("ParseInlineOrigin failed", line_number, &num_errors); + } } else { if (!cur_func.get()) { LogParseError("Found source line data without a function", @@ -225,7 +237,39 @@ bool BasicSourceLineResolver::Module::LoadMapFromMemory( return true; } -void BasicSourceLineResolver::Module::LookupAddress(StackFrame* frame) const { +int BasicSourceLineResolver::Module::ConstructInlineFrames( + StackFrame* frame, + MemAddr address, + const RangeMap>& inlines, + vector>* inlined_frames) const { + linked_ptr in; + MemAddr inline_base; + if (!inlines.RetrieveRange(address, &in, &inline_base, nullptr, nullptr)) + return -1; + + StackFrame new_frame = StackFrame(*frame); + new_frame.function_name = in->name; + // Use the starting adress of the inlined range as inlined function base. + new_frame.function_base = new_frame.module->base_address() + inline_base; + auto it = files_.find(in->source_file_id); + if (it != files_.end()) + new_frame.source_file_name = it->second; + + new_frame.trust = StackFrame::FRAME_TRUST_INLINE; + // Must add frames before calling ConstructInlineFrames to get correct order. + int current_idx = inlined_frames->size(); + inlined_frames->push_back(std::make_unique(new_frame)); + int source_line = ConstructInlineFrames(&new_frame, address, + in->child_inlines, inlined_frames); + if (source_line != -1) { + (*inlined_frames)[current_idx]->source_line = source_line; + } + return in->call_site_line; +} + +void BasicSourceLineResolver::Module::LookupAddress( + StackFrame* frame, + vector>* inlined_frames) const { MemAddr address = frame->instruction - frame->module->base_address(); // First, look for a FUNC record that covers address. Use @@ -256,6 +300,15 @@ void BasicSourceLineResolver::Module::LookupAddress(StackFrame* frame) const { frame->source_line = line->line; frame->source_line_base = frame->module->base_address() + line_base; } + + // Check if this is inlined function call. + if (inlined_frames) { + int source_line = + ConstructInlineFrames(frame, address, func->inlines, inlined_frames); + if (source_line != -1) { + frame->source_line = source_line; + } + } } else if (public_symbols_.Retrieve(address, &public_symbol, &public_address) && (!func.get() || public_address > function_base)) { @@ -357,6 +410,38 @@ bool BasicSourceLineResolver::Module::ParseFile(char* file_line) { return false; } +bool BasicSourceLineResolver::Module::ParseInlineOrigin( + char* inline_origin_line) { + long origin_id; + long source_file_id; + char* origin_name; + if (SymbolParseHelper::ParseInlineOrigin(inline_origin_line, &origin_id, + &source_file_id, &origin_name)) { + inline_origins_.insert(make_pair( + origin_id, new InlineOrigin(origin_id, source_file_id, origin_name))); + return true; + } + return false; +} + +linked_ptr +BasicSourceLineResolver::Module::ParseInline(char* inline_line) { + long inline_nest_level; + long call_site_line; + long origin_id; + vector> ranges; + if (SymbolParseHelper::ParseInline(inline_line, &inline_nest_level, + &call_site_line, &origin_id, &ranges)) { + auto origin = inline_origins_.find(origin_id); + if (origin != inline_origins_.end()) { + return linked_ptr( + new Inline(inline_nest_level, call_site_line, origin->second->name, + origin->second->source_file_id, ranges)); + } + } + return linked_ptr(); +} + BasicSourceLineResolver::Function* BasicSourceLineResolver::Module::ParseFunction(char* function_line) { bool is_multiple; @@ -502,6 +587,26 @@ bool BasicSourceLineResolver::Module::ParseCFIFrameInfo( return true; } +bool BasicSourceLineResolver::Function::AppendInline(linked_ptr in) { + // This happends if in's parent wasn't added due to a malformed INLINE record. + if (in->inline_nest_level > last_added_inline_nest_level + 1) + return false; + RangeMap>* current_inlines = &this->inlines; + auto iter = recent_inlines.find(in->inline_nest_level - 1); + if (iter != recent_inlines.end()) + current_inlines = &iter->second->child_inlines; + else + assert(in->inline_nest_level == 0); + + last_added_inline_nest_level = in->inline_nest_level; + recent_inlines[last_added_inline_nest_level] = in; + + // Store all ranges into current level of inlines. + for (auto range : in->inline_ranges) + current_inlines->StoreRange(range.first, range.second, in); + return true; +} + // static bool SymbolParseHelper::ParseFile(char* file_line, long* index, char** filename) { @@ -529,6 +634,97 @@ bool SymbolParseHelper::ParseFile(char* file_line, long* index, return true; } +// static +bool SymbolParseHelper::ParseInlineOrigin(char* inline_origin_line, + long* origin_id, + long* file_id, + char** name) { + // INLINE_ORIGIN + assert(strncmp(inline_origin_line, "INLINE_ORIGIN ", 14) == 0); + inline_origin_line += 14; // skip prefix + vector tokens; + if (!Tokenize(inline_origin_line, kWhitespace, 3, &tokens)) { + return false; + } + + char* after_number; + *origin_id = strtol(tokens[0], &after_number, 10); + if (!IsValidAfterNumber(after_number) || *origin_id < 0 || + *origin_id == std::numeric_limits::max()) { + return false; + } + + *file_id = strtol(tokens[1], &after_number, 10); + // If the file id is -1, it might be an artificial function that doesn't have + // file id. So, we consider -1 as a valid special case. + if (!IsValidAfterNumber(after_number) || + *file_id < -1 | *origin_id == std::numeric_limits::max()) { + return false; + } + + *name = tokens[2]; + if (!*name) { + return false; + } + + return true; +} + +// static +bool SymbolParseHelper::ParseInline( + char* inline_line, + long* inline_nest_level, + long* call_site_line, + long* origin_id, + vector>* ranges) { + // INLINE
+ // ... + assert(strncmp(inline_line, "INLINE ", 7) == 0); + inline_line += 7; // skip prefix + + vector tokens; + Tokenize(inline_line, kWhitespace, std::numeric_limits::max(), &tokens); + + // The length of the vector should be at least 5 and an odd number. + if (tokens.size() < 5 && tokens.size() % 2 == 0) + return false; + + char* after_number; + *inline_nest_level = strtol(tokens[0], &after_number, 10); + if (!IsValidAfterNumber(after_number) || *inline_nest_level < 0 || + *inline_nest_level == std::numeric_limits::max()) { + return false; + } + + *call_site_line = strtol(tokens[1], &after_number, 10); + if (!IsValidAfterNumber(after_number) || *call_site_line < 0 || + *call_site_line == std::numeric_limits::max()) { + return false; + } + + *origin_id = strtol(tokens[2], &after_number, 10); + if (!IsValidAfterNumber(after_number) || *origin_id < 0 || + *origin_id == std::numeric_limits::max()) { + return false; + } + + for (size_t i = 3; i < tokens.size();) { + MemAddr address = strtoull(tokens[i++], &after_number, 16); + if (!IsValidAfterNumber(after_number) || + address == std::numeric_limits::max()) { + return false; + } + MemAddr size = strtoull(tokens[i++], &after_number, 16); + if (!IsValidAfterNumber(after_number) || + size == std::numeric_limits::max()) { + return false; + } + ranges->push_back({address, size}); + } + + return true; +} + // static bool SymbolParseHelper::ParseFunction(char* function_line, bool* is_multiple, uint64_t* address, uint64_t* size, diff --git a/src/processor/basic_source_line_resolver_types.h b/src/processor/basic_source_line_resolver_types.h index c103040d..482176f4 100644 --- a/src/processor/basic_source_line_resolver_types.h +++ b/src/processor/basic_source_line_resolver_types.h @@ -66,10 +66,24 @@ BasicSourceLineResolver::Function : public SourceLineResolverBase::Function { code_size, set_parameter_size, is_mutiple), - lines() { } - RangeMap< MemAddr, linked_ptr > lines; + inlines(), + lines(), + last_added_inline_nest_level(0) { } + // Append inline into corresponding RangeMap. + // This function assumes it's called in the order of reading INLINE records. + bool AppendInline(linked_ptr in); + + RangeMap> inlines; + RangeMap> lines; + private: typedef SourceLineResolverBase::Function Base; + + // A map from inline_nest_level to most recently added Inline* at that level. + std::map> recent_inlines; + + // The last added inline_nest_level in recent_inlines. + int last_added_inline_nest_level; }; @@ -92,7 +106,17 @@ class BasicSourceLineResolver::Module : public SourceLineResolverBase::Module { // Looks up the given relative address, and fills the StackFrame struct // with the result. - virtual void LookupAddress(StackFrame* frame) const; + virtual void LookupAddress( + StackFrame* frame, + std::vector>* inlined_frame) const; + + // Construct inlined frame for frame and return inlined function call site + // source line. If failed to construct inlined frame, return -1. + virtual int ConstructInlineFrames( + StackFrame* frame, + MemAddr address, + const RangeMap>& inlines, + std::vector>* inline_frames) const; // If Windows stack walking information is available covering ADDRESS, // return a WindowsFrameInfo structure describing it. If the information @@ -125,6 +149,12 @@ class BasicSourceLineResolver::Module : public SourceLineResolverBase::Module { // Parses a file declaration bool ParseFile(char* file_line); + // Parses an inline origin declaration. + bool ParseInlineOrigin(char* inline_origin_line); + + // Parses an inline declaration. + linked_ptr ParseInline(char* inline_line); + // Parses a function declaration, returning a new Function object. Function* ParseFunction(char* function_line); @@ -144,6 +174,7 @@ class BasicSourceLineResolver::Module : public SourceLineResolverBase::Module { string name_; FileMap files_; + std::map> inline_origins_; RangeMap< MemAddr, linked_ptr > functions_; AddressMap< MemAddr, linked_ptr > public_symbols_; bool is_corrupt_; diff --git a/src/processor/basic_source_line_resolver_unittest.cc b/src/processor/basic_source_line_resolver_unittest.cc index 2cb2967c..914f0963 100644 --- a/src/processor/basic_source_line_resolver_unittest.cc +++ b/src/processor/basic_source_line_resolver_unittest.cc @@ -189,7 +189,7 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve) scoped_ptr cfi_frame_info; frame.instruction = 0x1000; frame.module = NULL; - resolver.FillSourceLineInfo(&frame); + resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_FALSE(frame.module); ASSERT_TRUE(frame.function_name.empty()); ASSERT_EQ(frame.function_base, 0U); @@ -198,7 +198,7 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve) ASSERT_EQ(frame.source_line_base, 0U); frame.module = &module1; - resolver.FillSourceLineInfo(&frame); + resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_EQ(frame.function_name, "Function1_1"); ASSERT_TRUE(frame.module); ASSERT_EQ(frame.module->code_file(), "module1"); @@ -216,13 +216,13 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve) ClearSourceLineInfo(&frame); frame.instruction = 0x800; frame.module = &module1; - resolver.FillSourceLineInfo(&frame); + resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_TRUE(VerifyEmpty(frame)); windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame)); ASSERT_FALSE(windows_frame_info.get()); frame.instruction = 0x1280; - resolver.FillSourceLineInfo(&frame); + resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_EQ(frame.function_name, "Function1_3"); ASSERT_TRUE(frame.source_file_name.empty()); ASSERT_EQ(frame.source_line, 0); @@ -233,7 +233,7 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve) ASSERT_TRUE(windows_frame_info->program_string.empty()); frame.instruction = 0x1380; - resolver.FillSourceLineInfo(&frame); + resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_EQ(frame.function_name, "Function1_4"); ASSERT_TRUE(frame.source_file_name.empty()); ASSERT_EQ(frame.source_line, 0); @@ -342,17 +342,17 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve) frame.instruction = 0x2900; frame.module = &module1; - resolver.FillSourceLineInfo(&frame); + resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_EQ(frame.function_name, string("PublicSymbol")); frame.instruction = 0x4000; frame.module = &module1; - resolver.FillSourceLineInfo(&frame); + resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_EQ(frame.function_name, string("LargeFunction")); frame.instruction = 0x2181; frame.module = &module2; - resolver.FillSourceLineInfo(&frame); + resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_EQ(frame.function_name, "Function2_2"); ASSERT_EQ(frame.function_base, 0x2170U); ASSERT_TRUE(frame.module); @@ -366,18 +366,18 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve) ASSERT_EQ(windows_frame_info->prolog_size, 1U); frame.instruction = 0x216f; - resolver.FillSourceLineInfo(&frame); + resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_EQ(frame.function_name, "Public2_1"); ClearSourceLineInfo(&frame); frame.instruction = 0x219f; frame.module = &module2; - resolver.FillSourceLineInfo(&frame); + resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_TRUE(frame.function_name.empty()); frame.instruction = 0x21a0; frame.module = &module2; - resolver.FillSourceLineInfo(&frame); + resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_EQ(frame.function_name, "Public2_2"); } @@ -413,6 +413,50 @@ TEST_F(TestBasicSourceLineResolver, TestUnload) ASSERT_TRUE(resolver.HasModule(&module1)); } +TEST_F(TestBasicSourceLineResolver, TestLoadAndResolveInlines) { + TestCodeModule module("linux_inline"); + ASSERT_TRUE(resolver.LoadModule( + &module, testdata_dir + + "/symbols/linux_inline/BBA6FA10B8AAB33D00000000000000000/" + "linux_inline.sym")); + ASSERT_TRUE(resolver.HasModule(&module)); + StackFrame frame; + std::vector> inlined_frames; + frame.instruction = 0x161b6; + frame.module = &module; + // main frame. + resolver.FillSourceLineInfo(&frame, &inlined_frames); + ASSERT_EQ(frame.function_name, "main"); + ASSERT_EQ(frame.function_base, 0x15b30U); + ASSERT_EQ(frame.source_file_name, "linux_inline.cpp"); + ASSERT_EQ(frame.source_line, 42); + ASSERT_EQ(frame.source_line_base, 0x161b6U); + + ASSERT_EQ(inlined_frames.size(), 3UL); + + // Inlined frames inside main frame. + ASSERT_EQ(inlined_frames[0]->function_name, "foo()"); + ASSERT_EQ(inlined_frames[0]->function_base, 0x15b45U); + ASSERT_EQ(inlined_frames[0]->source_file_name, "linux_inline.cpp"); + ASSERT_EQ(inlined_frames[0]->source_line, 39); + ASSERT_EQ(inlined_frames[0]->source_line_base, 0x161b6U); + ASSERT_EQ(inlined_frames[0]->trust, StackFrame::FRAME_TRUST_INLINE); + + ASSERT_EQ(inlined_frames[1]->function_name, "bar()"); + ASSERT_EQ(inlined_frames[1]->function_base, 0x15b72U); + ASSERT_EQ(inlined_frames[1]->source_file_name, "linux_inline.cpp"); + ASSERT_EQ(inlined_frames[1]->source_line, 32); + ASSERT_EQ(inlined_frames[1]->source_line_base, 0x161b6U); + ASSERT_EQ(inlined_frames[1]->trust, StackFrame::FRAME_TRUST_INLINE); + + ASSERT_EQ(inlined_frames[2]->function_name, "func()"); + ASSERT_EQ(inlined_frames[2]->function_base, 0x15b83U); + ASSERT_EQ(inlined_frames[2]->source_file_name, "linux_inline.cpp"); + ASSERT_EQ(inlined_frames[2]->source_line, 27); + ASSERT_EQ(inlined_frames[2]->source_line_base, 0x161b6U); + ASSERT_EQ(inlined_frames[2]->trust, StackFrame::FRAME_TRUST_INLINE); +} + // Test parsing of valid FILE lines. The format is: // FILE TEST(SymbolParseHelper, ParseFileValid) { @@ -736,6 +780,122 @@ TEST(SymbolParseHelper, ParsePublicSymbolInvalid) { &name)); } +// Test parsing of valid INLINE_ORIGIN lines. The format is: +// INLINE_ORIGIN +TEST(SymbolParseHelper, ParseInlineOriginValid) { + long origin_id; + long file_id; + char* name; + + char kTestLine[] = "INLINE_ORIGIN 1 1 function name"; + ASSERT_TRUE(SymbolParseHelper::ParseInlineOrigin(kTestLine, &origin_id, + &file_id, &name)); + EXPECT_EQ(1, origin_id); + EXPECT_EQ(1, file_id); + EXPECT_EQ("function name", string(name)); + + // -1 is a file id, which is used when the function is artifical. + char kTestLine1[] = "INLINE_ORIGIN 0 -1 function name"; + ASSERT_TRUE(SymbolParseHelper::ParseInlineOrigin(kTestLine1, &origin_id, + &file_id, &name)); + EXPECT_EQ(0, origin_id); + EXPECT_EQ(-1, file_id); + EXPECT_EQ("function name", string(name)); +} + +// Test parsing of valid INLINE ORIGIN lines. The format is: +// INLINE_ORIGIN +TEST(SymbolParseHelper, ParseInlineOriginInvalid) { + long origin_id; + long file_id; + char* name; + + // Test missing function name. + char kTestLine[] = "INLINE_ORIGIN 1 1"; + ASSERT_FALSE(SymbolParseHelper::ParseInlineOrigin(kTestLine, &origin_id, + &file_id, &name)); + + // Test bad origin id. + char kTestLine1[] = "INLINE_ORIGIN x1 1 function name"; + ASSERT_FALSE(SymbolParseHelper::ParseInlineOrigin(kTestLine1, &origin_id, + &file_id, &name)); + + // Test large origin id. + char kTestLine2[] = "INLINE_ORIGIN 123123123123123123123123 1 function name"; + ASSERT_FALSE(SymbolParseHelper::ParseInlineOrigin(kTestLine2, &origin_id, + &file_id, &name)); + + // Test negative origin id. + char kTestLine3[] = "INLINE_ORIGIN -1 1 function name"; + ASSERT_FALSE(SymbolParseHelper::ParseInlineOrigin(kTestLine3, &origin_id, + &file_id, &name)); +} + +// Test parsing of valid INLINE lines. The format is: +// INLINE
... +TEST(SymbolParseHelper, ParseInlineValid) { + long inline_nest_level; + long call_site_line; + long origin_id; + std::vector> ranges; + + char kTestLine[] = "INLINE 0 1 2 3 4"; + ASSERT_TRUE(SymbolParseHelper::ParseInline( + kTestLine, &inline_nest_level, &call_site_line, &origin_id, &ranges)); + EXPECT_EQ(0, inline_nest_level); + EXPECT_EQ(1, call_site_line); + EXPECT_EQ(2, origin_id); + EXPECT_EQ(0x3ULL, ranges[0].first); + EXPECT_EQ(0x4ULL, ranges[0].second); + ranges.clear(); + + // Test hex and discontinuous ranges. + char kTestLine1[] = "INLINE 0 1 2 a b 1a 1b"; + ASSERT_TRUE(SymbolParseHelper::ParseInline( + kTestLine1, &inline_nest_level, &call_site_line, &origin_id, &ranges)); + EXPECT_EQ(0, inline_nest_level); + EXPECT_EQ(1, call_site_line); + EXPECT_EQ(2, origin_id); + EXPECT_EQ(0xaULL, ranges[0].first); + EXPECT_EQ(0xbULL, ranges[0].second); + EXPECT_EQ(0x1aULL, ranges[1].first); + EXPECT_EQ(0x1bULL, ranges[1].second); +} + +// Test parsing of Invalid INLINE lines. The format is: +// INLINE
... +TEST(SymbolParseHelper, ParseInlineInvalid) { + long inline_nest_level; + long call_site_line; + long origin_id; + std::vector> ranges; + + // Test negative inline_nest_level. + char kTestLine[] = "INLINE -1 1 2 3 4"; + ASSERT_FALSE(SymbolParseHelper::ParseInline( + kTestLine, &inline_nest_level, &call_site_line, &origin_id, &ranges)); + + // Test negative call_site_line. + char kTestLine1[] = "INLINE 0 -1 2 3 4"; + ASSERT_FALSE(SymbolParseHelper::ParseInline( + kTestLine1, &inline_nest_level, &call_site_line, &origin_id, &ranges)); + + // Test negative origin_id. + char kTestLine2[] = "INLINE 0 1 -2 3 4"; + ASSERT_FALSE(SymbolParseHelper::ParseInline( + kTestLine2, &inline_nest_level, &call_site_line, &origin_id, &ranges)); + + // Test missing ranges. + char kTestLine3[] = "INLINE 0 1 -2"; + ASSERT_FALSE(SymbolParseHelper::ParseInline( + kTestLine3, &inline_nest_level, &call_site_line, &origin_id, &ranges)); + + // Test missing size for range. + char kTestLine4[] = "INLINE 0 1 -2 3"; + ASSERT_FALSE(SymbolParseHelper::ParseInline( + kTestLine4, &inline_nest_level, &call_site_line, &origin_id, &ranges)); +} + } // namespace int main(int argc, char* argv[]) { diff --git a/src/processor/fast_source_line_resolver.cc b/src/processor/fast_source_line_resolver.cc index 3f8ec508..029f21f4 100644 --- a/src/processor/fast_source_line_resolver.cc +++ b/src/processor/fast_source_line_resolver.cc @@ -51,6 +51,7 @@ using std::map; using std::make_pair; +using std::vector; namespace google_breakpad { @@ -61,7 +62,9 @@ bool FastSourceLineResolver::ShouldDeleteMemoryBufferAfterLoadModule() { return false; } -void FastSourceLineResolver::Module::LookupAddress(StackFrame* frame) const { +void FastSourceLineResolver::Module::LookupAddress( + StackFrame* frame, + vector>* inlined_frames) const { MemAddr address = frame->instruction - frame->module->base_address(); // First, look for a FUNC record that covers address. Use diff --git a/src/processor/fast_source_line_resolver_types.h b/src/processor/fast_source_line_resolver_types.h index 2b2b5827..aa02fc11 100644 --- a/src/processor/fast_source_line_resolver_types.h +++ b/src/processor/fast_source_line_resolver_types.h @@ -117,7 +117,9 @@ class FastSourceLineResolver::Module: public SourceLineResolverBase::Module { // Looks up the given relative address, and fills the StackFrame struct // with the result. - virtual void LookupAddress(StackFrame* frame) const; + virtual void LookupAddress( + StackFrame* frame, + std::vector>* inlined_frames) const; // Loads a map from the given buffer in char* type. virtual bool LoadMapFromMemory(char* memory_buffer, diff --git a/src/processor/fast_source_line_resolver_unittest.cc b/src/processor/fast_source_line_resolver_unittest.cc index 0ec5f91d..5109e4f1 100644 --- a/src/processor/fast_source_line_resolver_unittest.cc +++ b/src/processor/fast_source_line_resolver_unittest.cc @@ -217,7 +217,7 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) { scoped_ptr cfi_frame_info; frame.instruction = 0x1000; frame.module = NULL; - fast_resolver.FillSourceLineInfo(&frame); + fast_resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_FALSE(frame.module); ASSERT_TRUE(frame.function_name.empty()); ASSERT_EQ(frame.function_base, 0U); @@ -226,7 +226,7 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) { ASSERT_EQ(frame.source_line_base, 0U); frame.module = &module1; - fast_resolver.FillSourceLineInfo(&frame); + fast_resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_EQ(frame.function_name, "Function1_1"); ASSERT_TRUE(frame.module); ASSERT_EQ(frame.module->code_file(), "module1"); @@ -243,13 +243,13 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) { ClearSourceLineInfo(&frame); frame.instruction = 0x800; frame.module = &module1; - fast_resolver.FillSourceLineInfo(&frame); + fast_resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_TRUE(VerifyEmpty(frame)); windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame)); ASSERT_FALSE(windows_frame_info.get()); frame.instruction = 0x1280; - fast_resolver.FillSourceLineInfo(&frame); + fast_resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_EQ(frame.function_name, "Function1_3"); ASSERT_TRUE(frame.source_file_name.empty()); ASSERT_EQ(frame.source_line, 0); @@ -260,7 +260,7 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) { ASSERT_TRUE(windows_frame_info->program_string.empty()); frame.instruction = 0x1380; - fast_resolver.FillSourceLineInfo(&frame); + fast_resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_EQ(frame.function_name, "Function1_4"); ASSERT_TRUE(frame.source_file_name.empty()); ASSERT_EQ(frame.source_line, 0); @@ -369,17 +369,17 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) { frame.instruction = 0x2900; frame.module = &module1; - fast_resolver.FillSourceLineInfo(&frame); + fast_resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_EQ(frame.function_name, string("PublicSymbol")); frame.instruction = 0x4000; frame.module = &module1; - fast_resolver.FillSourceLineInfo(&frame); + fast_resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_EQ(frame.function_name, string("LargeFunction")); frame.instruction = 0x2181; frame.module = &module2; - fast_resolver.FillSourceLineInfo(&frame); + fast_resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_EQ(frame.function_name, "Function2_2"); ASSERT_EQ(frame.function_base, 0x2170U); ASSERT_TRUE(frame.module); @@ -393,18 +393,18 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) { ASSERT_EQ(windows_frame_info->prolog_size, 1U); frame.instruction = 0x216f; - fast_resolver.FillSourceLineInfo(&frame); + fast_resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_EQ(frame.function_name, "Public2_1"); ClearSourceLineInfo(&frame); frame.instruction = 0x219f; frame.module = &module2; - fast_resolver.FillSourceLineInfo(&frame); + fast_resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_TRUE(frame.function_name.empty()); frame.instruction = 0x21a0; frame.module = &module2; - fast_resolver.FillSourceLineInfo(&frame); + fast_resolver.FillSourceLineInfo(&frame, nullptr); ASSERT_EQ(frame.function_name, "Public2_2"); } diff --git a/src/processor/source_line_resolver_base.cc b/src/processor/source_line_resolver_base.cc index 45a06cf3..16c82224 100644 --- a/src/processor/source_line_resolver_base.cc +++ b/src/processor/source_line_resolver_base.cc @@ -295,11 +295,13 @@ bool SourceLineResolverBase::IsModuleCorrupt(const CodeModule* module) { return corrupt_modules_->find(module->code_file()) != corrupt_modules_->end(); } -void SourceLineResolverBase::FillSourceLineInfo(StackFrame* frame) { +void SourceLineResolverBase::FillSourceLineInfo( + StackFrame* frame, + std::vector>* inlined_frames) { if (frame->module) { ModuleMap::const_iterator it = modules_->find(frame->module->code_file()); if (it != modules_->end()) { - it->second->LookupAddress(frame); + it->second->LookupAddress(frame, inlined_frames); } } } diff --git a/src/processor/source_line_resolver_base_types.h b/src/processor/source_line_resolver_base_types.h index db9f7b8e..64cd86e0 100644 --- a/src/processor/source_line_resolver_base_types.h +++ b/src/processor/source_line_resolver_base_types.h @@ -41,12 +41,15 @@ #include #include +#include #include #include "google_breakpad/common/breakpad_types.h" #include "google_breakpad/processor/source_line_resolver_base.h" #include "google_breakpad/processor/stack_frame.h" #include "processor/cfi_frame_info.h" +#include "processor/linked_ptr.h" +#include "processor/range_map.h" #include "processor/windows_frame_info.h" #ifndef PROCESSOR_SOURCE_LINE_RESOLVER_BASE_TYPES_H__ @@ -66,6 +69,37 @@ class SourceLineResolverBase::AutoFileCloser { FILE* file_; }; +struct SourceLineResolverBase::InlineOrigin { + InlineOrigin(int32_t origin_id, int32_t source_file_id, const string& name) + : origin_id(origin_id), source_file_id(source_file_id), name(name) {} + + int32_t origin_id; + int32_t source_file_id; + string name; +}; + +struct SourceLineResolverBase::Inline { + // A vector of (address, size) pair for a INLINE record. + using InlineRanges = std::vector>; + Inline(int32_t inline_nest_level, + int32_t call_site_line, + const string& name, + int32_t source_file_id, + InlineRanges inline_ranges) + : inline_nest_level(inline_nest_level), + call_site_line(call_site_line), + name(name), + source_file_id(source_file_id), + inline_ranges(inline_ranges) {} + + int32_t inline_nest_level; + int32_t call_site_line; + string name; + int32_t source_file_id; + InlineRanges inline_ranges; + RangeMap> child_inlines; +}; + struct SourceLineResolverBase::Line { Line() { } Line(MemAddr addr, MemAddr code_size, int file_id, int source_line) @@ -142,7 +176,9 @@ class SourceLineResolverBase::Module { // Looks up the given relative address, and fills the StackFrame struct // with the result. - virtual void LookupAddress(StackFrame* frame) const = 0; + virtual void LookupAddress( + StackFrame* frame, + std::vector>* inlined_frames) const = 0; // If Windows stack walking information is available covering ADDRESS, // return a WindowsFrameInfo structure describing it. If the information diff --git a/src/processor/stack_frame_symbolizer.cc b/src/processor/stack_frame_symbolizer.cc index 7a44f243..6490ca90 100644 --- a/src/processor/stack_frame_symbolizer.cc +++ b/src/processor/stack_frame_symbolizer.cc @@ -57,7 +57,8 @@ StackFrameSymbolizer::SymbolizerResult StackFrameSymbolizer::FillSourceLineInfo( const CodeModules* modules, const CodeModules* unloaded_modules, const SystemInfo* system_info, - StackFrame* frame) { + StackFrame* frame, + std::vector>* inlined_frames) { assert(frame); const CodeModule* module = NULL; @@ -80,7 +81,7 @@ StackFrameSymbolizer::SymbolizerResult StackFrameSymbolizer::FillSourceLineInfo( // If module is already loaded, go ahead to fill source line info and return. if (resolver_->HasModule(frame->module)) { - resolver_->FillSourceLineInfo(frame); + resolver_->FillSourceLineInfo(frame, inlined_frames); return resolver_->IsModuleCorrupt(frame->module) ? kWarningCorruptSymbols : kNoError; } @@ -108,7 +109,7 @@ StackFrameSymbolizer::SymbolizerResult StackFrameSymbolizer::FillSourceLineInfo( } if (load_success) { - resolver_->FillSourceLineInfo(frame); + resolver_->FillSourceLineInfo(frame, inlined_frames); return resolver_->IsModuleCorrupt(frame->module) ? kWarningCorruptSymbols : kNoError; } else { diff --git a/src/processor/stackwalk_common.cc b/src/processor/stackwalk_common.cc index a7609b9c..856a6a66 100644 --- a/src/processor/stackwalk_common.cc +++ b/src/processor/stackwalk_common.cc @@ -57,6 +57,7 @@ namespace google_breakpad { namespace { using std::vector; +using std::unique_ptr; // Separator character for machine readable output. static const char kOutputSeparator = '|'; @@ -217,25 +218,30 @@ static void PrintStackContents(const string& indent, modules->GetModuleForAddress(pointee_frame.instruction); // Try to look up the function name. + vector> inlined_frames; if (pointee_frame.module) - resolver->FillSourceLineInfo(&pointee_frame); + resolver->FillSourceLineInfo(&pointee_frame, &inlined_frames); // Print function name. - if (!pointee_frame.function_name.empty()) { - if (word_length == 4) { - printf("%s *(0x%08x) = 0x%08x", indent.c_str(), - static_cast(address), - static_cast(pointee_frame.instruction)); - } else { - printf("%s *(0x%016" PRIx64 ") = 0x%016" PRIx64, - indent.c_str(), address, pointee_frame.instruction); + auto print_function_name = [&](StackFrame* frame) { + if (!frame->function_name.empty()) { + if (word_length == 4) { + printf("%s *(0x%08x) = 0x%08x", indent.c_str(), + static_cast(address), + static_cast(frame->instruction)); + } else { + printf("%s *(0x%016" PRIx64 ") = 0x%016" PRIx64, indent.c_str(), + address, frame->instruction); + } + printf( + " <%s> [%s : %d + 0x%" PRIx64 "]\n", frame->function_name.c_str(), + PathnameStripper::File(frame->source_file_name).c_str(), + frame->source_line, frame->instruction - frame->source_line_base); } - printf(" <%s> [%s : %d + 0x%" PRIx64 "]\n", - pointee_frame.function_name.c_str(), - PathnameStripper::File(pointee_frame.source_file_name).c_str(), - pointee_frame.source_line, - pointee_frame.instruction - pointee_frame.source_line_base); - } + }; + print_function_name(&pointee_frame); + for (unique_ptr &frame : inlined_frames) + print_function_name(frame.get()); } printf("\n"); } @@ -287,321 +293,351 @@ static void PrintStack(const CallStack* stack, } printf("\n "); - int sequence = 0; - if (cpu == "x86") { - const StackFrameX86* frame_x86 = - reinterpret_cast(frame); + // Inlined frames don't have registers info. + if (frame->trust != StackFrameAMD64::FRAME_TRUST_INLINE) { + int sequence = 0; + if (cpu == "x86") { + const StackFrameX86* frame_x86 = + reinterpret_cast(frame); - if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EIP) - sequence = PrintRegister("eip", frame_x86->context.eip, sequence); - if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP) - sequence = PrintRegister("esp", frame_x86->context.esp, sequence); - if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBP) - sequence = PrintRegister("ebp", frame_x86->context.ebp, sequence); - if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBX) - sequence = PrintRegister("ebx", frame_x86->context.ebx, sequence); - if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESI) - sequence = PrintRegister("esi", frame_x86->context.esi, sequence); - if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EDI) - sequence = PrintRegister("edi", frame_x86->context.edi, sequence); - if (frame_x86->context_validity == StackFrameX86::CONTEXT_VALID_ALL) { - sequence = PrintRegister("eax", frame_x86->context.eax, sequence); - sequence = PrintRegister("ecx", frame_x86->context.ecx, sequence); - sequence = PrintRegister("edx", frame_x86->context.edx, sequence); - sequence = PrintRegister("efl", frame_x86->context.eflags, sequence); - } - } else if (cpu == "ppc") { - const StackFramePPC* frame_ppc = - reinterpret_cast(frame); + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EIP) + sequence = PrintRegister("eip", frame_x86->context.eip, sequence); + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP) + sequence = PrintRegister("esp", frame_x86->context.esp, sequence); + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBP) + sequence = PrintRegister("ebp", frame_x86->context.ebp, sequence); + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBX) + sequence = PrintRegister("ebx", frame_x86->context.ebx, sequence); + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESI) + sequence = PrintRegister("esi", frame_x86->context.esi, sequence); + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EDI) + sequence = PrintRegister("edi", frame_x86->context.edi, sequence); + if (frame_x86->context_validity == StackFrameX86::CONTEXT_VALID_ALL) { + sequence = PrintRegister("eax", frame_x86->context.eax, sequence); + sequence = PrintRegister("ecx", frame_x86->context.ecx, sequence); + sequence = PrintRegister("edx", frame_x86->context.edx, sequence); + sequence = PrintRegister("efl", frame_x86->context.eflags, sequence); + } + } else if (cpu == "ppc") { + const StackFramePPC* frame_ppc = + reinterpret_cast(frame); - if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_SRR0) - sequence = PrintRegister("srr0", frame_ppc->context.srr0, sequence); - if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_GPR1) - sequence = PrintRegister("r1", frame_ppc->context.gpr[1], sequence); - } else if (cpu == "amd64") { - const StackFrameAMD64* frame_amd64 = - reinterpret_cast(frame); + if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_SRR0) + sequence = PrintRegister("srr0", frame_ppc->context.srr0, sequence); + if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_GPR1) + sequence = PrintRegister("r1", frame_ppc->context.gpr[1], sequence); + } else if (cpu == "amd64") { + const StackFrameAMD64* frame_amd64 = + reinterpret_cast(frame); - if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RAX) - sequence = PrintRegister64("rax", frame_amd64->context.rax, sequence); - if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDX) - sequence = PrintRegister64("rdx", frame_amd64->context.rdx, sequence); - if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RCX) - sequence = PrintRegister64("rcx", frame_amd64->context.rcx, sequence); - if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBX) - sequence = PrintRegister64("rbx", frame_amd64->context.rbx, sequence); - if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSI) - sequence = PrintRegister64("rsi", frame_amd64->context.rsi, sequence); - if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDI) - sequence = PrintRegister64("rdi", frame_amd64->context.rdi, sequence); - if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP) - sequence = PrintRegister64("rbp", frame_amd64->context.rbp, sequence); - if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP) - sequence = PrintRegister64("rsp", frame_amd64->context.rsp, sequence); - if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R8) - sequence = PrintRegister64("r8", frame_amd64->context.r8, sequence); - if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R9) - sequence = PrintRegister64("r9", frame_amd64->context.r9, sequence); - if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R10) - sequence = PrintRegister64("r10", frame_amd64->context.r10, sequence); - if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R11) - sequence = PrintRegister64("r11", frame_amd64->context.r11, sequence); - if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R12) - sequence = PrintRegister64("r12", frame_amd64->context.r12, sequence); - if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R13) - sequence = PrintRegister64("r13", frame_amd64->context.r13, sequence); - if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R14) - sequence = PrintRegister64("r14", frame_amd64->context.r14, sequence); - if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R15) - sequence = PrintRegister64("r15", frame_amd64->context.r15, sequence); - if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RIP) - sequence = PrintRegister64("rip", frame_amd64->context.rip, sequence); - } else if (cpu == "sparc") { - const StackFrameSPARC* frame_sparc = - reinterpret_cast(frame); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RAX) + sequence = PrintRegister64("rax", frame_amd64->context.rax, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDX) + sequence = PrintRegister64("rdx", frame_amd64->context.rdx, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RCX) + sequence = PrintRegister64("rcx", frame_amd64->context.rcx, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBX) + sequence = PrintRegister64("rbx", frame_amd64->context.rbx, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSI) + sequence = PrintRegister64("rsi", frame_amd64->context.rsi, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDI) + sequence = PrintRegister64("rdi", frame_amd64->context.rdi, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP) + sequence = PrintRegister64("rbp", frame_amd64->context.rbp, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP) + sequence = PrintRegister64("rsp", frame_amd64->context.rsp, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R8) + sequence = PrintRegister64("r8", frame_amd64->context.r8, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R9) + sequence = PrintRegister64("r9", frame_amd64->context.r9, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R10) + sequence = PrintRegister64("r10", frame_amd64->context.r10, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R11) + sequence = PrintRegister64("r11", frame_amd64->context.r11, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R12) + sequence = PrintRegister64("r12", frame_amd64->context.r12, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R13) + sequence = PrintRegister64("r13", frame_amd64->context.r13, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R14) + sequence = PrintRegister64("r14", frame_amd64->context.r14, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R15) + sequence = PrintRegister64("r15", frame_amd64->context.r15, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RIP) + sequence = PrintRegister64("rip", frame_amd64->context.rip, sequence); + } else if (cpu == "sparc") { + const StackFrameSPARC* frame_sparc = + reinterpret_cast(frame); - if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_SP) - sequence = PrintRegister("sp", frame_sparc->context.g_r[14], sequence); - if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_FP) - sequence = PrintRegister("fp", frame_sparc->context.g_r[30], sequence); - if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_PC) - sequence = PrintRegister("pc", frame_sparc->context.pc, sequence); - } else if (cpu == "arm") { - const StackFrameARM* frame_arm = - reinterpret_cast(frame); + if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_SP) + sequence = + PrintRegister("sp", frame_sparc->context.g_r[14], sequence); + if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_FP) + sequence = + PrintRegister("fp", frame_sparc->context.g_r[30], sequence); + if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_PC) + sequence = PrintRegister("pc", frame_sparc->context.pc, sequence); + } else if (cpu == "arm") { + const StackFrameARM* frame_arm = + reinterpret_cast(frame); - // Argument registers (caller-saves), which will likely only be valid - // for the youngest frame. - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R0) - sequence = PrintRegister("r0", frame_arm->context.iregs[0], sequence); - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R1) - sequence = PrintRegister("r1", frame_arm->context.iregs[1], sequence); - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R2) - sequence = PrintRegister("r2", frame_arm->context.iregs[2], sequence); - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R3) - sequence = PrintRegister("r3", frame_arm->context.iregs[3], sequence); + // Argument registers (caller-saves), which will likely only be valid + // for the youngest frame. + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R0) + sequence = PrintRegister("r0", frame_arm->context.iregs[0], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R1) + sequence = PrintRegister("r1", frame_arm->context.iregs[1], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R2) + sequence = PrintRegister("r2", frame_arm->context.iregs[2], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R3) + sequence = PrintRegister("r3", frame_arm->context.iregs[3], sequence); - // General-purpose callee-saves registers. - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R4) - sequence = PrintRegister("r4", frame_arm->context.iregs[4], sequence); - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R5) - sequence = PrintRegister("r5", frame_arm->context.iregs[5], sequence); - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R6) - sequence = PrintRegister("r6", frame_arm->context.iregs[6], sequence); - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R7) - sequence = PrintRegister("r7", frame_arm->context.iregs[7], sequence); - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R8) - sequence = PrintRegister("r8", frame_arm->context.iregs[8], sequence); - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R9) - sequence = PrintRegister("r9", frame_arm->context.iregs[9], sequence); - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R10) - sequence = PrintRegister("r10", frame_arm->context.iregs[10], sequence); - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R12) - sequence = PrintRegister("r12", frame_arm->context.iregs[12], sequence); + // General-purpose callee-saves registers. + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R4) + sequence = PrintRegister("r4", frame_arm->context.iregs[4], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R5) + sequence = PrintRegister("r5", frame_arm->context.iregs[5], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R6) + sequence = PrintRegister("r6", frame_arm->context.iregs[6], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R7) + sequence = PrintRegister("r7", frame_arm->context.iregs[7], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R8) + sequence = PrintRegister("r8", frame_arm->context.iregs[8], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R9) + sequence = PrintRegister("r9", frame_arm->context.iregs[9], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R10) + sequence = + PrintRegister("r10", frame_arm->context.iregs[10], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R12) + sequence = + PrintRegister("r12", frame_arm->context.iregs[12], sequence); - // Registers with a dedicated or conventional purpose. - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_FP) - sequence = PrintRegister("fp", frame_arm->context.iregs[11], sequence); - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP) - sequence = PrintRegister("sp", frame_arm->context.iregs[13], sequence); - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_LR) - sequence = PrintRegister("lr", frame_arm->context.iregs[14], sequence); - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC) - sequence = PrintRegister("pc", frame_arm->context.iregs[15], sequence); - } else if (cpu == "arm64") { - const StackFrameARM64* frame_arm64 = - reinterpret_cast(frame); + // Registers with a dedicated or conventional purpose. + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_FP) + sequence = + PrintRegister("fp", frame_arm->context.iregs[11], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP) + sequence = + PrintRegister("sp", frame_arm->context.iregs[13], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_LR) + sequence = + PrintRegister("lr", frame_arm->context.iregs[14], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC) + sequence = + PrintRegister("pc", frame_arm->context.iregs[15], sequence); + } else if (cpu == "arm64") { + const StackFrameARM64* frame_arm64 = + reinterpret_cast(frame); - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X0) { - sequence = - PrintRegister64("x0", frame_arm64->context.iregs[0], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X1) { - sequence = - PrintRegister64("x1", frame_arm64->context.iregs[1], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X2) { - sequence = - PrintRegister64("x2", frame_arm64->context.iregs[2], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X3) { - sequence = - PrintRegister64("x3", frame_arm64->context.iregs[3], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X4) { - sequence = - PrintRegister64("x4", frame_arm64->context.iregs[4], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X5) { - sequence = - PrintRegister64("x5", frame_arm64->context.iregs[5], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X6) { - sequence = - PrintRegister64("x6", frame_arm64->context.iregs[6], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X7) { - sequence = - PrintRegister64("x7", frame_arm64->context.iregs[7], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X8) { - sequence = - PrintRegister64("x8", frame_arm64->context.iregs[8], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X9) { - sequence = - PrintRegister64("x9", frame_arm64->context.iregs[9], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X10) { - sequence = - PrintRegister64("x10", frame_arm64->context.iregs[10], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X11) { - sequence = - PrintRegister64("x11", frame_arm64->context.iregs[11], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X12) { - sequence = - PrintRegister64("x12", frame_arm64->context.iregs[12], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X13) { - sequence = - PrintRegister64("x13", frame_arm64->context.iregs[13], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X14) { - sequence = - PrintRegister64("x14", frame_arm64->context.iregs[14], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X15) { - sequence = - PrintRegister64("x15", frame_arm64->context.iregs[15], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X16) { - sequence = - PrintRegister64("x16", frame_arm64->context.iregs[16], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X17) { - sequence = - PrintRegister64("x17", frame_arm64->context.iregs[17], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X18) { - sequence = - PrintRegister64("x18", frame_arm64->context.iregs[18], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X19) { - sequence = - PrintRegister64("x19", frame_arm64->context.iregs[19], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X20) { - sequence = - PrintRegister64("x20", frame_arm64->context.iregs[20], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X21) { - sequence = - PrintRegister64("x21", frame_arm64->context.iregs[21], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X22) { - sequence = - PrintRegister64("x22", frame_arm64->context.iregs[22], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X23) { - sequence = - PrintRegister64("x23", frame_arm64->context.iregs[23], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X24) { - sequence = - PrintRegister64("x24", frame_arm64->context.iregs[24], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X25) { - sequence = - PrintRegister64("x25", frame_arm64->context.iregs[25], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X26) { - sequence = - PrintRegister64("x26", frame_arm64->context.iregs[26], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X27) { - sequence = - PrintRegister64("x27", frame_arm64->context.iregs[27], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X28) { - sequence = - PrintRegister64("x28", frame_arm64->context.iregs[28], sequence); - } + if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X0) { + sequence = + PrintRegister64("x0", frame_arm64->context.iregs[0], sequence); + } + if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X1) { + sequence = + PrintRegister64("x1", frame_arm64->context.iregs[1], sequence); + } + if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X2) { + sequence = + PrintRegister64("x2", frame_arm64->context.iregs[2], sequence); + } + if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X3) { + sequence = + PrintRegister64("x3", frame_arm64->context.iregs[3], sequence); + } + if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X4) { + sequence = + PrintRegister64("x4", frame_arm64->context.iregs[4], sequence); + } + if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X5) { + sequence = + PrintRegister64("x5", frame_arm64->context.iregs[5], sequence); + } + if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X6) { + sequence = + PrintRegister64("x6", frame_arm64->context.iregs[6], sequence); + } + if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X7) { + sequence = + PrintRegister64("x7", frame_arm64->context.iregs[7], sequence); + } + if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X8) { + sequence = + PrintRegister64("x8", frame_arm64->context.iregs[8], sequence); + } + if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X9) { + sequence = + PrintRegister64("x9", frame_arm64->context.iregs[9], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X10) { + sequence = + PrintRegister64("x10", frame_arm64->context.iregs[10], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X11) { + sequence = + PrintRegister64("x11", frame_arm64->context.iregs[11], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X12) { + sequence = + PrintRegister64("x12", frame_arm64->context.iregs[12], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X13) { + sequence = + PrintRegister64("x13", frame_arm64->context.iregs[13], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X14) { + sequence = + PrintRegister64("x14", frame_arm64->context.iregs[14], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X15) { + sequence = + PrintRegister64("x15", frame_arm64->context.iregs[15], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X16) { + sequence = + PrintRegister64("x16", frame_arm64->context.iregs[16], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X17) { + sequence = + PrintRegister64("x17", frame_arm64->context.iregs[17], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X18) { + sequence = + PrintRegister64("x18", frame_arm64->context.iregs[18], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X19) { + sequence = + PrintRegister64("x19", frame_arm64->context.iregs[19], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X20) { + sequence = + PrintRegister64("x20", frame_arm64->context.iregs[20], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X21) { + sequence = + PrintRegister64("x21", frame_arm64->context.iregs[21], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X22) { + sequence = + PrintRegister64("x22", frame_arm64->context.iregs[22], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X23) { + sequence = + PrintRegister64("x23", frame_arm64->context.iregs[23], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X24) { + sequence = + PrintRegister64("x24", frame_arm64->context.iregs[24], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X25) { + sequence = + PrintRegister64("x25", frame_arm64->context.iregs[25], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X26) { + sequence = + PrintRegister64("x26", frame_arm64->context.iregs[26], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X27) { + sequence = + PrintRegister64("x27", frame_arm64->context.iregs[27], sequence); + } + if (frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_X28) { + sequence = + PrintRegister64("x28", frame_arm64->context.iregs[28], sequence); + } - // Registers with a dedicated or conventional purpose. - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_FP) { - sequence = - PrintRegister64("fp", frame_arm64->context.iregs[29], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_LR) { - sequence = - PrintRegister64("lr", frame_arm64->context.iregs[30], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_SP) { - sequence = - PrintRegister64("sp", frame_arm64->context.iregs[31], sequence); - } - if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_PC) { - sequence = - PrintRegister64("pc", frame_arm64->context.iregs[32], sequence); - } - } else if ((cpu == "mips") || (cpu == "mips64")) { - const StackFrameMIPS* frame_mips = - reinterpret_cast(frame); + // Registers with a dedicated or conventional purpose. + if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_FP) { + sequence = + PrintRegister64("fp", frame_arm64->context.iregs[29], sequence); + } + if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_LR) { + sequence = + PrintRegister64("lr", frame_arm64->context.iregs[30], sequence); + } + if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_SP) { + sequence = + PrintRegister64("sp", frame_arm64->context.iregs[31], sequence); + } + if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_PC) { + sequence = + PrintRegister64("pc", frame_arm64->context.iregs[32], sequence); + } + } else if ((cpu == "mips") || (cpu == "mips64")) { + const StackFrameMIPS* frame_mips = + reinterpret_cast(frame); - if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_GP) - sequence = PrintRegister64("gp", - frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_GP], - sequence); - if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_SP) - sequence = PrintRegister64("sp", - frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_SP], - sequence); - if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_FP) - sequence = PrintRegister64("fp", - frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_FP], - sequence); - if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_RA) - sequence = PrintRegister64("ra", - frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_RA], - sequence); - if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_PC) - sequence = PrintRegister64("pc", frame_mips->context.epc, sequence); + if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_GP) + sequence = PrintRegister64( + "gp", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_GP], + sequence); + if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_SP) + sequence = PrintRegister64( + "sp", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_SP], + sequence); + if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_FP) + sequence = PrintRegister64( + "fp", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_FP], + sequence); + if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_RA) + sequence = PrintRegister64( + "ra", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_RA], + sequence); + if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_PC) + sequence = PrintRegister64("pc", frame_mips->context.epc, sequence); - // Save registers s0-s7 - if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S0) - sequence = PrintRegister64("s0", - frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S0], - sequence); - if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S1) - sequence = PrintRegister64("s1", - frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S1], - sequence); - if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S2) - sequence = PrintRegister64("s2", - frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S2], - sequence); - if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S3) - sequence = PrintRegister64("s3", - frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S3], - sequence); - if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S4) - sequence = PrintRegister64("s4", - frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S4], - sequence); - if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S5) - sequence = PrintRegister64("s5", - frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S5], - sequence); - if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S6) - sequence = PrintRegister64("s6", - frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S6], - sequence); - if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S7) - sequence = PrintRegister64("s7", - frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S7], - sequence); + // Save registers s0-s7 + if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S0) + sequence = PrintRegister64( + "s0", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S0], + sequence); + if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S1) + sequence = PrintRegister64( + "s1", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S1], + sequence); + if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S2) + sequence = PrintRegister64( + "s2", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S2], + sequence); + if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S3) + sequence = PrintRegister64( + "s3", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S3], + sequence); + if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S4) + sequence = PrintRegister64( + "s4", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S4], + sequence); + if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S5) + sequence = PrintRegister64( + "s5", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S5], + sequence); + if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S6) + sequence = PrintRegister64( + "s6", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S6], + sequence); + if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S7) + sequence = PrintRegister64( + "s7", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S7], + sequence); + } } printf("\n Found by: %s\n", frame->trust_description().c_str()); diff --git a/src/processor/stackwalker.cc b/src/processor/stackwalker.cc index 4988ef1e..d4897d4c 100644 --- a/src/processor/stackwalker.cc +++ b/src/processor/stackwalker.cc @@ -138,11 +138,12 @@ bool Stackwalker::Walk( // frame_pointer fields. The frame structure comes from either the // context frame (above) or a caller frame (below). + vector> inlined_frames; // Resolve the module information, if a module map was provided. StackFrameSymbolizer::SymbolizerResult symbolizer_result = frame_symbolizer_->FillSourceLineInfo(modules_, unloaded_modules_, system_info_, - frame.get()); + frame.get(), &inlined_frames); switch (symbolizer_result) { case StackFrameSymbolizer::kInterrupt: BPLOG(INFO) << "Stack walk is interrupted."; @@ -173,7 +174,11 @@ bool Stackwalker::Walk( default: break; } - + // Add all nested inlined frames belonging to this frame in reverse order. + while (!inlined_frames.empty()) { + stack->frames_.push_back(inlined_frames.back().release()); + inlined_frames.pop_back(); + } // Add the frame to the call stack. Relinquish the ownership claim // over the frame, because the stack now owns it. stack->frames_.push_back(frame.release()); @@ -307,7 +312,7 @@ bool Stackwalker::InstructionAddressSeemsValid(uint64_t address) const { frame.instruction = address; StackFrameSymbolizer::SymbolizerResult symbolizer_result = frame_symbolizer_->FillSourceLineInfo(modules_, unloaded_modules_, - system_info_, &frame); + system_info_, &frame, nullptr); if (!frame.module) { // not inside any loaded module diff --git a/src/processor/testdata/linux_inline.dmp b/src/processor/testdata/linux_inline.dmp new file mode 100644 index 00000000..5b216f8b Binary files /dev/null and b/src/processor/testdata/linux_inline.dmp differ diff --git a/src/processor/testdata/symbols/linux_inline/BBA6FA10B8AAB33D00000000000000000/linux_inline.sym b/src/processor/testdata/symbols/linux_inline/BBA6FA10B8AAB33D00000000000000000/linux_inline.sym new file mode 100644 index 00000000..775640f0 --- /dev/null +++ b/src/processor/testdata/symbols/linux_inline/BBA6FA10B8AAB33D00000000000000000/linux_inline.sym @@ -0,0 +1,68 @@ +MODULE Linux x86_64 BBA6FA10B8AAB33D00000000000000000 linux_inline +INFO CODE_ID 10FAA6BBAAB83DB3 +FILE 0 linux_inline.cpp +INLINE_ORIGIN 0 0 bar() +INLINE_ORIGIN 1 0 foo() +INLINE_ORIGIN 2 0 func() +FUNC 15b30 6cf 0 main +INLINE 0 42 1 15b45 6b1 +INLINE 1 39 0 15b72 684 +INLINE 2 32 2 15b83 673 +15b30 15 41 0 +15b45 11 36 0 +15b56 a 37 0 +15b60 6 37 0 +15b66 5 38 0 +15b6b 7 0 0 +15b72 11 31 0 +15b83 a 9 0 +15b8d 4 9 0 +15b91 6 9 0 +15b97 7 0 0 +15b9e 11 10 0 +15baf 7 0 0 +15bb6 2e 12 0 +15be4 7 0 0 +15beb 5 12 0 +15bf0 1d 13 0 +15c0d 1d 14 0 +15c2a e 0 0 +15c38 1c 15 0 +15c54 a 16 0 +15c5e 7 0 0 +15c65 2c 16 0 +15c91 15 0 0 +15ca6 a 16 0 +15cb0 87 15 0 +15d37 7 0 0 +15d3e 33 15 0 +15d71 7 0 0 +15d78 24 15 0 +15d9c a 17 0 +15da6 e 0 0 +15db4 a 18 0 +15dbe e 0 0 +15dcc a 19 0 +15dd6 7 0 0 +15ddd a 20 0 +15de7 7 0 0 +15dee 2c 21 0 +15e1a 3c 22 0 +15e56 28 23 0 +15e7e 5a 18 0 +15ed8 d 28 0 +15ee5 11 12 0 +15ef6 67 28 0 +15f5d 2b 15 0 +15f88 7 0 0 +15f8f 8c 15 0 +1601b 7 0 0 +16022 3d 15 0 +1605f 67 28 0 +160c6 54 18 0 +1611a 3c 28 0 +16156 c 12 0 +16162 54 18 0 +161b6 2 27 0 +161b8 3e 28 0 +161f6 9 43 0