aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZequan Wu <zequanwu@google.com>2021-07-22 11:13:37 -0700
committerJoshua Peraza <jperaza@chromium.org>2021-07-22 19:09:50 +0000
commitf0803507950279bd1e48d05f5fb4b4377c2917f7 (patch)
tree3a7b541e52021318e8d0f53cba76bb439dd1e575
parent32096a2dc8f8a7d5aac4097e34912bb7e06a5277 (diff)
downloadgoogle-breakpad-f0803507950279bd1e48d05f5fb4b4377c2917f7.tar.gz
Add support to process INLINE records in symbol files
This adds the support to process INLINE and INLINE_ORIGIN records in symbol files and to generate inlined frames using those records if possible. Bug: 1190878 Change-Id: Ia0b6d56c9de37cf818d9bb6842d58c9b68f235b2 Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/3024690 Reviewed-by: Lei Zhang <thestig@chromium.org> Reviewed-by: Joshua Peraza <jperaza@chromium.org>
-rw-r--r--src/google_breakpad/processor/basic_source_line_resolver.h29
-rw-r--r--src/google_breakpad/processor/source_line_resolver_base.h6
-rw-r--r--src/google_breakpad/processor/source_line_resolver_interface.h10
-rw-r--r--src/google_breakpad/processor/stack_frame.h9
-rw-r--r--src/google_breakpad/processor/stack_frame_symbolizer.h5
-rw-r--r--src/processor/basic_source_line_resolver.cc198
-rw-r--r--src/processor/basic_source_line_resolver_types.h37
-rw-r--r--src/processor/basic_source_line_resolver_unittest.cc182
-rw-r--r--src/processor/fast_source_line_resolver.cc5
-rw-r--r--src/processor/fast_source_line_resolver_types.h4
-rw-r--r--src/processor/fast_source_line_resolver_unittest.cc22
-rw-r--r--src/processor/source_line_resolver_base.cc6
-rw-r--r--src/processor/source_line_resolver_base_types.h38
-rw-r--r--src/processor/stack_frame_symbolizer.cc7
-rw-r--r--src/processor/stackwalk_common.cc694
-rw-r--r--src/processor/stackwalker.cc11
-rw-r--r--src/processor/testdata/linux_inline.dmpbin0 -> 21456 bytes
-rw-r--r--src/processor/testdata/symbols/linux_inline/BBA6FA10B8AAB33D00000000000000000/linux_inline.sym68
18 files changed, 958 insertions, 373 deletions
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 <map>
#include <string>
+#include <vector>
#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 <id> <filename>.
// 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 <origin_id> <file_id> <name>.
+ // Notice, that this method modifies the input |inline_origin_line| which is
+ // why it can't be const. On success, <origin_id>, <file_id> and <name> 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 <inline_nest_level> <call_site_line> <origin_id> <address>
+ // <size> ....
+ // Notice, that this method modifies the input |inline|
+ // which is why it can't be const. On success, <inline_nest_level>,
+ // <call_site_line> and <origin_id> are stored in |*inline_nest_level|,
+ // |*call_site_line|, and |*origin_id|, and all pairs of (<address>, <size>)
+ // 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<std::pair<MemAddr, MemAddr>>* ranges); // out
+
// Parses a |function_line| declaration. Returns true on success.
// Format: FUNC [<multiple>] <address> <size> <stack_param_size> <name>.
// 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<std::unique_ptr<StackFrame>>* 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 <memory>
#include <string>
+#include <vector>
#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<std::unique_ptr<StackFrame>>* 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 <memory>
#include <set>
#include <string>
+#include <vector>
#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<std::unique_ptr<StackFrame>>* 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 <limits>
#include <map>
+#include <memory>
#include <utility>
#include <vector>
@@ -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 <code id> <filename>
+ } else if (strncmp(buffer, "INLINE ", 7) == 0) {
+ linked_ptr<Inline> 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<uint64_t, linked_ptr<Inline>>& inlines,
+ vector<unique_ptr<StackFrame>>* inlined_frames) const {
+ linked_ptr<Inline> 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<StackFrame>(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<unique_ptr<StackFrame>>* 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::Inline>
+BasicSourceLineResolver::Module::ParseInline(char* inline_line) {
+ long inline_nest_level;
+ long call_site_line;
+ long origin_id;
+ vector<std::pair<MemAddr, MemAddr>> 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<Inline>(
+ new Inline(inline_nest_level, call_site_line, origin->second->name,
+ origin->second->source_file_id, ranges));
+ }
+ }
+ return linked_ptr<Inline>();
+}
+
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<Inline> 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<MemAddr, linked_ptr<Inline>>* 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) {
@@ -530,6 +635,97 @@ bool SymbolParseHelper::ParseFile(char* file_line, long* index,
}
// static
+bool SymbolParseHelper::ParseInlineOrigin(char* inline_origin_line,
+ long* origin_id,
+ long* file_id,
+ char** name) {
+ // INLINE_ORIGIN <origin_id> <file_id> <name>
+ assert(strncmp(inline_origin_line, "INLINE_ORIGIN ", 14) == 0);
+ inline_origin_line += 14; // skip prefix
+ vector<char*> 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<long>::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<long>::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<std::pair<MemAddr, MemAddr>>* ranges) {
+ // INLINE <inline_nest_level> <call_site_line> <origin_id> <address> <size>
+ // ...
+ assert(strncmp(inline_line, "INLINE ", 7) == 0);
+ inline_line += 7; // skip prefix
+
+ vector<char*> tokens;
+ Tokenize(inline_line, kWhitespace, std::numeric_limits<int>::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<long>::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<long>::max()) {
+ return false;
+ }
+
+ *origin_id = strtol(tokens[2], &after_number, 10);
+ if (!IsValidAfterNumber(after_number) || *origin_id < 0 ||
+ *origin_id == std::numeric_limits<long>::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<unsigned long long>::max()) {
+ return false;
+ }
+ MemAddr size = strtoull(tokens[i++], &after_number, 16);
+ if (!IsValidAfterNumber(after_number) ||
+ size == std::numeric_limits<unsigned long long>::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,
long* stack_param_size, char** name) {
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<Line> > 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<Inline> in);
+
+ RangeMap<MemAddr, linked_ptr<Inline>> inlines;
+ RangeMap<MemAddr, linked_ptr<Line>> lines;
+
private:
typedef SourceLineResolverBase::Function Base;
+
+ // A map from inline_nest_level to most recently added Inline* at that level.
+ std::map<int, linked_ptr<Inline>> 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<std::unique_ptr<StackFrame>>* 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<uint64_t, linked_ptr<Inline>>& inlines,
+ std::vector<std::unique_ptr<StackFrame>>* 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<Inline> 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<int, linked_ptr<InlineOrigin>> inline_origins_;
RangeMap< MemAddr, linked_ptr<Function> > functions_;
AddressMap< MemAddr, linked_ptr<PublicSymbol> > 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<CFIFrameInfo> 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<std::unique_ptr<StackFrame>> 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 <id> <filename>
TEST(SymbolParseHelper, ParseFileValid) {
@@ -736,6 +780,122 @@ TEST(SymbolParseHelper, ParsePublicSymbolInvalid) {
&name));
}
+// Test parsing of valid INLINE_ORIGIN lines. The format is:
+// INLINE_ORIGIN <origin_id> <file_id> <name>
+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 <origin_id> <file_id> <name>
+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 <inline_nest_level> <call_site_line> <origin_id> <address> <size> ...
+TEST(SymbolParseHelper, ParseInlineValid) {
+ long inline_nest_level;
+ long call_site_line;
+ long origin_id;
+ std::vector<std::pair<uint64_t, uint64_t>> 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 <inline_nest_level> <call_site_line> <origin_id> <address> <size> ...
+TEST(SymbolParseHelper, ParseInlineInvalid) {
+ long inline_nest_level;
+ long call_site_line;
+ long origin_id;
+ std::vector<std::pair<uint64_t, uint64_t>> 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<std::unique_ptr<StackFrame>>* 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<std::unique_ptr<StackFrame>>* 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<CFIFrameInfo> 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<std::unique_ptr<StackFrame>>* 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 <stdio.h>
#include <map>
+#include <memory>
#include <string>
#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<std::pair<MemAddr, MemAddr>>;
+ 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<MemAddr, linked_ptr<Inline>> 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<std::unique_ptr<StackFrame>>* 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<std::unique_ptr<StackFrame>>* 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<unique_ptr<StackFrame>> 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<uint32_t>(address),
- static_cast<uint32_t>(pointee_frame.instruction));
- } else {
- printf("%s *(0x%016" PRIx64 ") = 0x%016" PRIx64,
- indent.c_str(), address, pointee_frame.instruction);
- }
- 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);
- }
+ 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<uint32_t>(address),
+ static_cast<uint32_t>(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);
+ }
+ };
+ print_function_name(&pointee_frame);
+ for (unique_ptr<StackFrame> &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<const StackFrameX86*>(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<const StackFramePPC*>(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<const StackFrameAMD64*>(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<const StackFrameSPARC*>(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<const StackFrameARM*>(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);
-
- // 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<const StackFrameARM64*>(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);
- }
+ // 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<const StackFrameX86*>(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<const StackFramePPC*>(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<const StackFrameAMD64*>(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<const StackFrameSPARC*>(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<const StackFrameARM*>(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);
+
+ // 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<const StackFrameARM64*>(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);
+ }
- // 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);
+ // 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<const StackFrameMIPS*>(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);
+
+ // 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);
}
- } else if ((cpu == "mips") || (cpu == "mips64")) {
- const StackFrameMIPS* frame_mips =
- reinterpret_cast<const StackFrameMIPS*>(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);
-
- // 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<std::unique_ptr<StackFrame>> 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
--- /dev/null
+++ b/src/processor/testdata/linux_inline.dmp
Binary files 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