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>
This commit is contained in:
parent
32096a2dc8
commit
f080350795
18 changed files with 948 additions and 363 deletions
|
@ -40,6 +40,7 @@
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "common/using_std_string.h"
|
#include "common/using_std_string.h"
|
||||||
#include "google_breakpad/processor/source_line_resolver_base.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.
|
// Helper class, containing useful methods for parsing of Breakpad symbol files.
|
||||||
class SymbolParseHelper {
|
class SymbolParseHelper {
|
||||||
public:
|
public:
|
||||||
|
using MemAddr = SourceLineResolverInterface::MemAddr;
|
||||||
|
|
||||||
// Parses a |file_line| declaration. Returns true on success.
|
// Parses a |file_line| declaration. Returns true on success.
|
||||||
// Format: FILE <id> <filename>.
|
// Format: FILE <id> <filename>.
|
||||||
// Notice, that this method modifies the input |file_line| which is why it
|
// Notice, that this method modifies the input |file_line| which is why it
|
||||||
|
@ -94,6 +97,32 @@ class SymbolParseHelper {
|
||||||
long* index, // out
|
long* index, // out
|
||||||
char** filename); // 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.
|
// Parses a |function_line| declaration. Returns true on success.
|
||||||
// Format: FUNC [<multiple>] <address> <size> <stack_param_size> <name>.
|
// Format: FUNC [<multiple>] <address> <size> <stack_param_size> <name>.
|
||||||
// Notice, that this method modifies the input |function_line| which is why it
|
// Notice, that this method modifies the input |function_line| which is why it
|
||||||
|
|
|
@ -84,11 +84,15 @@ class SourceLineResolverBase : public SourceLineResolverInterface {
|
||||||
virtual void UnloadModule(const CodeModule* module);
|
virtual void UnloadModule(const CodeModule* module);
|
||||||
virtual bool HasModule(const CodeModule* module);
|
virtual bool HasModule(const CodeModule* module);
|
||||||
virtual bool IsModuleCorrupt(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 WindowsFrameInfo* FindWindowsFrameInfo(const StackFrame* frame);
|
||||||
virtual CFIFrameInfo* FindCFIFrameInfo(const StackFrame* frame);
|
virtual CFIFrameInfo* FindCFIFrameInfo(const StackFrame* frame);
|
||||||
|
|
||||||
// Nested structs and classes.
|
// Nested structs and classes.
|
||||||
|
struct InlineOrigin;
|
||||||
|
struct Inline;
|
||||||
struct Line;
|
struct Line;
|
||||||
struct Function;
|
struct Function;
|
||||||
struct PublicSymbol;
|
struct PublicSymbol;
|
||||||
|
|
|
@ -34,7 +34,9 @@
|
||||||
#ifndef GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_INTERFACE_H__
|
#ifndef GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_INTERFACE_H__
|
||||||
#define GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_INTERFACE_H__
|
#define GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_INTERFACE_H__
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "common/using_std_string.h"
|
#include "common/using_std_string.h"
|
||||||
#include "google_breakpad/common/breakpad_types.h"
|
#include "google_breakpad/common/breakpad_types.h"
|
||||||
|
@ -91,8 +93,12 @@ class SourceLineResolverInterface {
|
||||||
|
|
||||||
// Fills in the function_base, function_name, source_file_name,
|
// Fills in the function_base, function_name, source_file_name,
|
||||||
// and source_line fields of the StackFrame. The instruction and
|
// and source_line fields of the StackFrame. The instruction and
|
||||||
// module_name fields must already be filled in.
|
// module_name fields must already be filled in. If inlined_frames is not
|
||||||
virtual void FillSourceLineInfo(StackFrame* frame) = 0;
|
// 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
|
// If Windows stack walking information is available covering
|
||||||
// FRAME's instruction address, return a WindowsFrameInfo structure
|
// FRAME's instruction address, return a WindowsFrameInfo structure
|
||||||
|
|
|
@ -51,7 +51,8 @@ struct StackFrame {
|
||||||
FRAME_TRUST_FP, // Derived from frame pointer
|
FRAME_TRUST_FP, // Derived from frame pointer
|
||||||
FRAME_TRUST_CFI, // Derived from call frame info
|
FRAME_TRUST_CFI, // Derived from call frame info
|
||||||
FRAME_TRUST_PREWALKED, // Explicitly provided by some external stack walker.
|
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()
|
StackFrame()
|
||||||
|
@ -60,9 +61,9 @@ struct StackFrame {
|
||||||
function_name(),
|
function_name(),
|
||||||
function_base(),
|
function_base(),
|
||||||
source_file_name(),
|
source_file_name(),
|
||||||
source_line(),
|
source_line(0),
|
||||||
source_line_base(),
|
source_line_base(),
|
||||||
trust(FRAME_TRUST_NONE) {}
|
trust(FRAME_TRUST_NONE){}
|
||||||
virtual ~StackFrame() {}
|
virtual ~StackFrame() {}
|
||||||
|
|
||||||
// Return a string describing how this stack frame was found
|
// Return a string describing how this stack frame was found
|
||||||
|
@ -81,6 +82,8 @@ struct StackFrame {
|
||||||
return "previous frame's frame pointer";
|
return "previous frame's frame pointer";
|
||||||
case StackFrame::FRAME_TRUST_SCAN:
|
case StackFrame::FRAME_TRUST_SCAN:
|
||||||
return "stack scanning";
|
return "stack scanning";
|
||||||
|
case StackFrame::FRAME_TRUST_INLINE:
|
||||||
|
return "inline record";
|
||||||
default:
|
default:
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,10 @@
|
||||||
#ifndef GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_SYMBOLIZER_H__
|
#ifndef GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_SYMBOLIZER_H__
|
||||||
#define GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_SYMBOLIZER_H__
|
#define GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_SYMBOLIZER_H__
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "common/using_std_string.h"
|
#include "common/using_std_string.h"
|
||||||
#include "google_breakpad/common/breakpad_types.h"
|
#include "google_breakpad/common/breakpad_types.h"
|
||||||
|
@ -79,7 +81,8 @@ class StackFrameSymbolizer {
|
||||||
const CodeModules* modules,
|
const CodeModules* modules,
|
||||||
const CodeModules* unloaded_modules,
|
const CodeModules* unloaded_modules,
|
||||||
const SystemInfo* system_info,
|
const SystemInfo* system_info,
|
||||||
StackFrame* stack_frame);
|
StackFrame* stack_frame,
|
||||||
|
std::vector<std::unique_ptr<StackFrame>>* inlined_frames);
|
||||||
|
|
||||||
virtual WindowsFrameInfo* FindWindowsFrameInfo(const StackFrame* frame);
|
virtual WindowsFrameInfo* FindWindowsFrameInfo(const StackFrame* frame);
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -52,6 +53,7 @@
|
||||||
using std::map;
|
using std::map;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
using std::make_pair;
|
using std::make_pair;
|
||||||
|
using std::unique_ptr;
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
@ -202,6 +204,16 @@ bool BasicSourceLineResolver::Module::LoadMapFromMemory(
|
||||||
// Ignore these as well, they're similarly just for housekeeping.
|
// Ignore these as well, they're similarly just for housekeeping.
|
||||||
//
|
//
|
||||||
// INFO CODE_ID <code id> <filename>
|
// 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 {
|
} else {
|
||||||
if (!cur_func.get()) {
|
if (!cur_func.get()) {
|
||||||
LogParseError("Found source line data without a function",
|
LogParseError("Found source line data without a function",
|
||||||
|
@ -225,7 +237,39 @@ bool BasicSourceLineResolver::Module::LoadMapFromMemory(
|
||||||
return true;
|
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();
|
MemAddr address = frame->instruction - frame->module->base_address();
|
||||||
|
|
||||||
// First, look for a FUNC record that covers address. Use
|
// 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 = line->line;
|
||||||
frame->source_line_base = frame->module->base_address() + line_base;
|
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,
|
} else if (public_symbols_.Retrieve(address,
|
||||||
&public_symbol, &public_address) &&
|
&public_symbol, &public_address) &&
|
||||||
(!func.get() || public_address > function_base)) {
|
(!func.get() || public_address > function_base)) {
|
||||||
|
@ -357,6 +410,38 @@ bool BasicSourceLineResolver::Module::ParseFile(char* file_line) {
|
||||||
return false;
|
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::Function*
|
||||||
BasicSourceLineResolver::Module::ParseFunction(char* function_line) {
|
BasicSourceLineResolver::Module::ParseFunction(char* function_line) {
|
||||||
bool is_multiple;
|
bool is_multiple;
|
||||||
|
@ -502,6 +587,26 @@ bool BasicSourceLineResolver::Module::ParseCFIFrameInfo(
|
||||||
return true;
|
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
|
// static
|
||||||
bool SymbolParseHelper::ParseFile(char* file_line, long* index,
|
bool SymbolParseHelper::ParseFile(char* file_line, long* index,
|
||||||
char** filename) {
|
char** filename) {
|
||||||
|
@ -529,6 +634,97 @@ bool SymbolParseHelper::ParseFile(char* file_line, long* index,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// static
|
||||||
bool SymbolParseHelper::ParseFunction(char* function_line, bool* is_multiple,
|
bool SymbolParseHelper::ParseFunction(char* function_line, bool* is_multiple,
|
||||||
uint64_t* address, uint64_t* size,
|
uint64_t* address, uint64_t* size,
|
||||||
|
|
|
@ -66,10 +66,24 @@ BasicSourceLineResolver::Function : public SourceLineResolverBase::Function {
|
||||||
code_size,
|
code_size,
|
||||||
set_parameter_size,
|
set_parameter_size,
|
||||||
is_mutiple),
|
is_mutiple),
|
||||||
lines() { }
|
inlines(),
|
||||||
RangeMap< MemAddr, linked_ptr<Line> > lines;
|
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:
|
private:
|
||||||
typedef SourceLineResolverBase::Function Base;
|
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
|
// Looks up the given relative address, and fills the StackFrame struct
|
||||||
// with the result.
|
// 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,
|
// If Windows stack walking information is available covering ADDRESS,
|
||||||
// return a WindowsFrameInfo structure describing it. If the information
|
// return a WindowsFrameInfo structure describing it. If the information
|
||||||
|
@ -125,6 +149,12 @@ class BasicSourceLineResolver::Module : public SourceLineResolverBase::Module {
|
||||||
// Parses a file declaration
|
// Parses a file declaration
|
||||||
bool ParseFile(char* file_line);
|
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.
|
// Parses a function declaration, returning a new Function object.
|
||||||
Function* ParseFunction(char* function_line);
|
Function* ParseFunction(char* function_line);
|
||||||
|
|
||||||
|
@ -144,6 +174,7 @@ class BasicSourceLineResolver::Module : public SourceLineResolverBase::Module {
|
||||||
|
|
||||||
string name_;
|
string name_;
|
||||||
FileMap files_;
|
FileMap files_;
|
||||||
|
std::map<int, linked_ptr<InlineOrigin>> inline_origins_;
|
||||||
RangeMap< MemAddr, linked_ptr<Function> > functions_;
|
RangeMap< MemAddr, linked_ptr<Function> > functions_;
|
||||||
AddressMap< MemAddr, linked_ptr<PublicSymbol> > public_symbols_;
|
AddressMap< MemAddr, linked_ptr<PublicSymbol> > public_symbols_;
|
||||||
bool is_corrupt_;
|
bool is_corrupt_;
|
||||||
|
|
|
@ -189,7 +189,7 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve)
|
||||||
scoped_ptr<CFIFrameInfo> cfi_frame_info;
|
scoped_ptr<CFIFrameInfo> cfi_frame_info;
|
||||||
frame.instruction = 0x1000;
|
frame.instruction = 0x1000;
|
||||||
frame.module = NULL;
|
frame.module = NULL;
|
||||||
resolver.FillSourceLineInfo(&frame);
|
resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_FALSE(frame.module);
|
ASSERT_FALSE(frame.module);
|
||||||
ASSERT_TRUE(frame.function_name.empty());
|
ASSERT_TRUE(frame.function_name.empty());
|
||||||
ASSERT_EQ(frame.function_base, 0U);
|
ASSERT_EQ(frame.function_base, 0U);
|
||||||
|
@ -198,7 +198,7 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve)
|
||||||
ASSERT_EQ(frame.source_line_base, 0U);
|
ASSERT_EQ(frame.source_line_base, 0U);
|
||||||
|
|
||||||
frame.module = &module1;
|
frame.module = &module1;
|
||||||
resolver.FillSourceLineInfo(&frame);
|
resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_EQ(frame.function_name, "Function1_1");
|
ASSERT_EQ(frame.function_name, "Function1_1");
|
||||||
ASSERT_TRUE(frame.module);
|
ASSERT_TRUE(frame.module);
|
||||||
ASSERT_EQ(frame.module->code_file(), "module1");
|
ASSERT_EQ(frame.module->code_file(), "module1");
|
||||||
|
@ -216,13 +216,13 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve)
|
||||||
ClearSourceLineInfo(&frame);
|
ClearSourceLineInfo(&frame);
|
||||||
frame.instruction = 0x800;
|
frame.instruction = 0x800;
|
||||||
frame.module = &module1;
|
frame.module = &module1;
|
||||||
resolver.FillSourceLineInfo(&frame);
|
resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_TRUE(VerifyEmpty(frame));
|
ASSERT_TRUE(VerifyEmpty(frame));
|
||||||
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
|
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
|
||||||
ASSERT_FALSE(windows_frame_info.get());
|
ASSERT_FALSE(windows_frame_info.get());
|
||||||
|
|
||||||
frame.instruction = 0x1280;
|
frame.instruction = 0x1280;
|
||||||
resolver.FillSourceLineInfo(&frame);
|
resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_EQ(frame.function_name, "Function1_3");
|
ASSERT_EQ(frame.function_name, "Function1_3");
|
||||||
ASSERT_TRUE(frame.source_file_name.empty());
|
ASSERT_TRUE(frame.source_file_name.empty());
|
||||||
ASSERT_EQ(frame.source_line, 0);
|
ASSERT_EQ(frame.source_line, 0);
|
||||||
|
@ -233,7 +233,7 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve)
|
||||||
ASSERT_TRUE(windows_frame_info->program_string.empty());
|
ASSERT_TRUE(windows_frame_info->program_string.empty());
|
||||||
|
|
||||||
frame.instruction = 0x1380;
|
frame.instruction = 0x1380;
|
||||||
resolver.FillSourceLineInfo(&frame);
|
resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_EQ(frame.function_name, "Function1_4");
|
ASSERT_EQ(frame.function_name, "Function1_4");
|
||||||
ASSERT_TRUE(frame.source_file_name.empty());
|
ASSERT_TRUE(frame.source_file_name.empty());
|
||||||
ASSERT_EQ(frame.source_line, 0);
|
ASSERT_EQ(frame.source_line, 0);
|
||||||
|
@ -342,17 +342,17 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve)
|
||||||
|
|
||||||
frame.instruction = 0x2900;
|
frame.instruction = 0x2900;
|
||||||
frame.module = &module1;
|
frame.module = &module1;
|
||||||
resolver.FillSourceLineInfo(&frame);
|
resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_EQ(frame.function_name, string("PublicSymbol"));
|
ASSERT_EQ(frame.function_name, string("PublicSymbol"));
|
||||||
|
|
||||||
frame.instruction = 0x4000;
|
frame.instruction = 0x4000;
|
||||||
frame.module = &module1;
|
frame.module = &module1;
|
||||||
resolver.FillSourceLineInfo(&frame);
|
resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_EQ(frame.function_name, string("LargeFunction"));
|
ASSERT_EQ(frame.function_name, string("LargeFunction"));
|
||||||
|
|
||||||
frame.instruction = 0x2181;
|
frame.instruction = 0x2181;
|
||||||
frame.module = &module2;
|
frame.module = &module2;
|
||||||
resolver.FillSourceLineInfo(&frame);
|
resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_EQ(frame.function_name, "Function2_2");
|
ASSERT_EQ(frame.function_name, "Function2_2");
|
||||||
ASSERT_EQ(frame.function_base, 0x2170U);
|
ASSERT_EQ(frame.function_base, 0x2170U);
|
||||||
ASSERT_TRUE(frame.module);
|
ASSERT_TRUE(frame.module);
|
||||||
|
@ -366,18 +366,18 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve)
|
||||||
ASSERT_EQ(windows_frame_info->prolog_size, 1U);
|
ASSERT_EQ(windows_frame_info->prolog_size, 1U);
|
||||||
|
|
||||||
frame.instruction = 0x216f;
|
frame.instruction = 0x216f;
|
||||||
resolver.FillSourceLineInfo(&frame);
|
resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_EQ(frame.function_name, "Public2_1");
|
ASSERT_EQ(frame.function_name, "Public2_1");
|
||||||
|
|
||||||
ClearSourceLineInfo(&frame);
|
ClearSourceLineInfo(&frame);
|
||||||
frame.instruction = 0x219f;
|
frame.instruction = 0x219f;
|
||||||
frame.module = &module2;
|
frame.module = &module2;
|
||||||
resolver.FillSourceLineInfo(&frame);
|
resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_TRUE(frame.function_name.empty());
|
ASSERT_TRUE(frame.function_name.empty());
|
||||||
|
|
||||||
frame.instruction = 0x21a0;
|
frame.instruction = 0x21a0;
|
||||||
frame.module = &module2;
|
frame.module = &module2;
|
||||||
resolver.FillSourceLineInfo(&frame);
|
resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_EQ(frame.function_name, "Public2_2");
|
ASSERT_EQ(frame.function_name, "Public2_2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,6 +413,50 @@ TEST_F(TestBasicSourceLineResolver, TestUnload)
|
||||||
ASSERT_TRUE(resolver.HasModule(&module1));
|
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:
|
// Test parsing of valid FILE lines. The format is:
|
||||||
// FILE <id> <filename>
|
// FILE <id> <filename>
|
||||||
TEST(SymbolParseHelper, ParseFileValid) {
|
TEST(SymbolParseHelper, ParseFileValid) {
|
||||||
|
@ -736,6 +780,122 @@ TEST(SymbolParseHelper, ParsePublicSymbolInvalid) {
|
||||||
&name));
|
&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
|
} // namespace
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
|
|
||||||
using std::map;
|
using std::map;
|
||||||
using std::make_pair;
|
using std::make_pair;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
@ -61,7 +62,9 @@ bool FastSourceLineResolver::ShouldDeleteMemoryBufferAfterLoadModule() {
|
||||||
return false;
|
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();
|
MemAddr address = frame->instruction - frame->module->base_address();
|
||||||
|
|
||||||
// First, look for a FUNC record that covers address. Use
|
// First, look for a FUNC record that covers address. Use
|
||||||
|
|
|
@ -117,7 +117,9 @@ class FastSourceLineResolver::Module: public SourceLineResolverBase::Module {
|
||||||
|
|
||||||
// Looks up the given relative address, and fills the StackFrame struct
|
// Looks up the given relative address, and fills the StackFrame struct
|
||||||
// with the result.
|
// 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.
|
// Loads a map from the given buffer in char* type.
|
||||||
virtual bool LoadMapFromMemory(char* memory_buffer,
|
virtual bool LoadMapFromMemory(char* memory_buffer,
|
||||||
|
|
|
@ -217,7 +217,7 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) {
|
||||||
scoped_ptr<CFIFrameInfo> cfi_frame_info;
|
scoped_ptr<CFIFrameInfo> cfi_frame_info;
|
||||||
frame.instruction = 0x1000;
|
frame.instruction = 0x1000;
|
||||||
frame.module = NULL;
|
frame.module = NULL;
|
||||||
fast_resolver.FillSourceLineInfo(&frame);
|
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_FALSE(frame.module);
|
ASSERT_FALSE(frame.module);
|
||||||
ASSERT_TRUE(frame.function_name.empty());
|
ASSERT_TRUE(frame.function_name.empty());
|
||||||
ASSERT_EQ(frame.function_base, 0U);
|
ASSERT_EQ(frame.function_base, 0U);
|
||||||
|
@ -226,7 +226,7 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) {
|
||||||
ASSERT_EQ(frame.source_line_base, 0U);
|
ASSERT_EQ(frame.source_line_base, 0U);
|
||||||
|
|
||||||
frame.module = &module1;
|
frame.module = &module1;
|
||||||
fast_resolver.FillSourceLineInfo(&frame);
|
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_EQ(frame.function_name, "Function1_1");
|
ASSERT_EQ(frame.function_name, "Function1_1");
|
||||||
ASSERT_TRUE(frame.module);
|
ASSERT_TRUE(frame.module);
|
||||||
ASSERT_EQ(frame.module->code_file(), "module1");
|
ASSERT_EQ(frame.module->code_file(), "module1");
|
||||||
|
@ -243,13 +243,13 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) {
|
||||||
ClearSourceLineInfo(&frame);
|
ClearSourceLineInfo(&frame);
|
||||||
frame.instruction = 0x800;
|
frame.instruction = 0x800;
|
||||||
frame.module = &module1;
|
frame.module = &module1;
|
||||||
fast_resolver.FillSourceLineInfo(&frame);
|
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_TRUE(VerifyEmpty(frame));
|
ASSERT_TRUE(VerifyEmpty(frame));
|
||||||
windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
|
windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
|
||||||
ASSERT_FALSE(windows_frame_info.get());
|
ASSERT_FALSE(windows_frame_info.get());
|
||||||
|
|
||||||
frame.instruction = 0x1280;
|
frame.instruction = 0x1280;
|
||||||
fast_resolver.FillSourceLineInfo(&frame);
|
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_EQ(frame.function_name, "Function1_3");
|
ASSERT_EQ(frame.function_name, "Function1_3");
|
||||||
ASSERT_TRUE(frame.source_file_name.empty());
|
ASSERT_TRUE(frame.source_file_name.empty());
|
||||||
ASSERT_EQ(frame.source_line, 0);
|
ASSERT_EQ(frame.source_line, 0);
|
||||||
|
@ -260,7 +260,7 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) {
|
||||||
ASSERT_TRUE(windows_frame_info->program_string.empty());
|
ASSERT_TRUE(windows_frame_info->program_string.empty());
|
||||||
|
|
||||||
frame.instruction = 0x1380;
|
frame.instruction = 0x1380;
|
||||||
fast_resolver.FillSourceLineInfo(&frame);
|
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_EQ(frame.function_name, "Function1_4");
|
ASSERT_EQ(frame.function_name, "Function1_4");
|
||||||
ASSERT_TRUE(frame.source_file_name.empty());
|
ASSERT_TRUE(frame.source_file_name.empty());
|
||||||
ASSERT_EQ(frame.source_line, 0);
|
ASSERT_EQ(frame.source_line, 0);
|
||||||
|
@ -369,17 +369,17 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) {
|
||||||
|
|
||||||
frame.instruction = 0x2900;
|
frame.instruction = 0x2900;
|
||||||
frame.module = &module1;
|
frame.module = &module1;
|
||||||
fast_resolver.FillSourceLineInfo(&frame);
|
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_EQ(frame.function_name, string("PublicSymbol"));
|
ASSERT_EQ(frame.function_name, string("PublicSymbol"));
|
||||||
|
|
||||||
frame.instruction = 0x4000;
|
frame.instruction = 0x4000;
|
||||||
frame.module = &module1;
|
frame.module = &module1;
|
||||||
fast_resolver.FillSourceLineInfo(&frame);
|
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_EQ(frame.function_name, string("LargeFunction"));
|
ASSERT_EQ(frame.function_name, string("LargeFunction"));
|
||||||
|
|
||||||
frame.instruction = 0x2181;
|
frame.instruction = 0x2181;
|
||||||
frame.module = &module2;
|
frame.module = &module2;
|
||||||
fast_resolver.FillSourceLineInfo(&frame);
|
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_EQ(frame.function_name, "Function2_2");
|
ASSERT_EQ(frame.function_name, "Function2_2");
|
||||||
ASSERT_EQ(frame.function_base, 0x2170U);
|
ASSERT_EQ(frame.function_base, 0x2170U);
|
||||||
ASSERT_TRUE(frame.module);
|
ASSERT_TRUE(frame.module);
|
||||||
|
@ -393,18 +393,18 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) {
|
||||||
ASSERT_EQ(windows_frame_info->prolog_size, 1U);
|
ASSERT_EQ(windows_frame_info->prolog_size, 1U);
|
||||||
|
|
||||||
frame.instruction = 0x216f;
|
frame.instruction = 0x216f;
|
||||||
fast_resolver.FillSourceLineInfo(&frame);
|
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_EQ(frame.function_name, "Public2_1");
|
ASSERT_EQ(frame.function_name, "Public2_1");
|
||||||
|
|
||||||
ClearSourceLineInfo(&frame);
|
ClearSourceLineInfo(&frame);
|
||||||
frame.instruction = 0x219f;
|
frame.instruction = 0x219f;
|
||||||
frame.module = &module2;
|
frame.module = &module2;
|
||||||
fast_resolver.FillSourceLineInfo(&frame);
|
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_TRUE(frame.function_name.empty());
|
ASSERT_TRUE(frame.function_name.empty());
|
||||||
|
|
||||||
frame.instruction = 0x21a0;
|
frame.instruction = 0x21a0;
|
||||||
frame.module = &module2;
|
frame.module = &module2;
|
||||||
fast_resolver.FillSourceLineInfo(&frame);
|
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||||
ASSERT_EQ(frame.function_name, "Public2_2");
|
ASSERT_EQ(frame.function_name, "Public2_2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -295,11 +295,13 @@ bool SourceLineResolverBase::IsModuleCorrupt(const CodeModule* module) {
|
||||||
return corrupt_modules_->find(module->code_file()) != corrupt_modules_->end();
|
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) {
|
if (frame->module) {
|
||||||
ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
|
ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
|
||||||
if (it != modules_->end()) {
|
if (it != modules_->end()) {
|
||||||
it->second->LookupAddress(frame);
|
it->second->LookupAddress(frame, inlined_frames);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,12 +41,15 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "google_breakpad/common/breakpad_types.h"
|
#include "google_breakpad/common/breakpad_types.h"
|
||||||
#include "google_breakpad/processor/source_line_resolver_base.h"
|
#include "google_breakpad/processor/source_line_resolver_base.h"
|
||||||
#include "google_breakpad/processor/stack_frame.h"
|
#include "google_breakpad/processor/stack_frame.h"
|
||||||
#include "processor/cfi_frame_info.h"
|
#include "processor/cfi_frame_info.h"
|
||||||
|
#include "processor/linked_ptr.h"
|
||||||
|
#include "processor/range_map.h"
|
||||||
#include "processor/windows_frame_info.h"
|
#include "processor/windows_frame_info.h"
|
||||||
|
|
||||||
#ifndef PROCESSOR_SOURCE_LINE_RESOLVER_BASE_TYPES_H__
|
#ifndef PROCESSOR_SOURCE_LINE_RESOLVER_BASE_TYPES_H__
|
||||||
|
@ -66,6 +69,37 @@ class SourceLineResolverBase::AutoFileCloser {
|
||||||
FILE* file_;
|
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 {
|
struct SourceLineResolverBase::Line {
|
||||||
Line() { }
|
Line() { }
|
||||||
Line(MemAddr addr, MemAddr code_size, int file_id, int source_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
|
// Looks up the given relative address, and fills the StackFrame struct
|
||||||
// with the result.
|
// 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,
|
// If Windows stack walking information is available covering ADDRESS,
|
||||||
// return a WindowsFrameInfo structure describing it. If the information
|
// return a WindowsFrameInfo structure describing it. If the information
|
||||||
|
|
|
@ -57,7 +57,8 @@ StackFrameSymbolizer::SymbolizerResult StackFrameSymbolizer::FillSourceLineInfo(
|
||||||
const CodeModules* modules,
|
const CodeModules* modules,
|
||||||
const CodeModules* unloaded_modules,
|
const CodeModules* unloaded_modules,
|
||||||
const SystemInfo* system_info,
|
const SystemInfo* system_info,
|
||||||
StackFrame* frame) {
|
StackFrame* frame,
|
||||||
|
std::vector<std::unique_ptr<StackFrame>>* inlined_frames) {
|
||||||
assert(frame);
|
assert(frame);
|
||||||
|
|
||||||
const CodeModule* module = NULL;
|
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 module is already loaded, go ahead to fill source line info and return.
|
||||||
if (resolver_->HasModule(frame->module)) {
|
if (resolver_->HasModule(frame->module)) {
|
||||||
resolver_->FillSourceLineInfo(frame);
|
resolver_->FillSourceLineInfo(frame, inlined_frames);
|
||||||
return resolver_->IsModuleCorrupt(frame->module) ?
|
return resolver_->IsModuleCorrupt(frame->module) ?
|
||||||
kWarningCorruptSymbols : kNoError;
|
kWarningCorruptSymbols : kNoError;
|
||||||
}
|
}
|
||||||
|
@ -108,7 +109,7 @@ StackFrameSymbolizer::SymbolizerResult StackFrameSymbolizer::FillSourceLineInfo(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (load_success) {
|
if (load_success) {
|
||||||
resolver_->FillSourceLineInfo(frame);
|
resolver_->FillSourceLineInfo(frame, inlined_frames);
|
||||||
return resolver_->IsModuleCorrupt(frame->module) ?
|
return resolver_->IsModuleCorrupt(frame->module) ?
|
||||||
kWarningCorruptSymbols : kNoError;
|
kWarningCorruptSymbols : kNoError;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -57,6 +57,7 @@ namespace google_breakpad {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
using std::unique_ptr;
|
||||||
|
|
||||||
// Separator character for machine readable output.
|
// Separator character for machine readable output.
|
||||||
static const char kOutputSeparator = '|';
|
static const char kOutputSeparator = '|';
|
||||||
|
@ -217,25 +218,30 @@ static void PrintStackContents(const string& indent,
|
||||||
modules->GetModuleForAddress(pointee_frame.instruction);
|
modules->GetModuleForAddress(pointee_frame.instruction);
|
||||||
|
|
||||||
// Try to look up the function name.
|
// Try to look up the function name.
|
||||||
|
vector<unique_ptr<StackFrame>> inlined_frames;
|
||||||
if (pointee_frame.module)
|
if (pointee_frame.module)
|
||||||
resolver->FillSourceLineInfo(&pointee_frame);
|
resolver->FillSourceLineInfo(&pointee_frame, &inlined_frames);
|
||||||
|
|
||||||
// Print function name.
|
// Print function name.
|
||||||
if (!pointee_frame.function_name.empty()) {
|
auto print_function_name = [&](StackFrame* frame) {
|
||||||
if (word_length == 4) {
|
if (!frame->function_name.empty()) {
|
||||||
printf("%s *(0x%08x) = 0x%08x", indent.c_str(),
|
if (word_length == 4) {
|
||||||
static_cast<uint32_t>(address),
|
printf("%s *(0x%08x) = 0x%08x", indent.c_str(),
|
||||||
static_cast<uint32_t>(pointee_frame.instruction));
|
static_cast<uint32_t>(address),
|
||||||
} else {
|
static_cast<uint32_t>(frame->instruction));
|
||||||
printf("%s *(0x%016" PRIx64 ") = 0x%016" PRIx64,
|
} else {
|
||||||
indent.c_str(), address, pointee_frame.instruction);
|
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(),
|
print_function_name(&pointee_frame);
|
||||||
PathnameStripper::File(pointee_frame.source_file_name).c_str(),
|
for (unique_ptr<StackFrame> &frame : inlined_frames)
|
||||||
pointee_frame.source_line,
|
print_function_name(frame.get());
|
||||||
pointee_frame.instruction - pointee_frame.source_line_base);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
@ -287,321 +293,351 @@ static void PrintStack(const CallStack* stack,
|
||||||
}
|
}
|
||||||
printf("\n ");
|
printf("\n ");
|
||||||
|
|
||||||
int sequence = 0;
|
// Inlined frames don't have registers info.
|
||||||
if (cpu == "x86") {
|
if (frame->trust != StackFrameAMD64::FRAME_TRUST_INLINE) {
|
||||||
const StackFrameX86* frame_x86 =
|
int sequence = 0;
|
||||||
reinterpret_cast<const StackFrameX86*>(frame);
|
if (cpu == "x86") {
|
||||||
|
const StackFrameX86* frame_x86 =
|
||||||
|
reinterpret_cast<const StackFrameX86*>(frame);
|
||||||
|
|
||||||
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EIP)
|
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EIP)
|
||||||
sequence = PrintRegister("eip", frame_x86->context.eip, sequence);
|
sequence = PrintRegister("eip", frame_x86->context.eip, sequence);
|
||||||
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP)
|
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP)
|
||||||
sequence = PrintRegister("esp", frame_x86->context.esp, sequence);
|
sequence = PrintRegister("esp", frame_x86->context.esp, sequence);
|
||||||
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBP)
|
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBP)
|
||||||
sequence = PrintRegister("ebp", frame_x86->context.ebp, sequence);
|
sequence = PrintRegister("ebp", frame_x86->context.ebp, sequence);
|
||||||
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBX)
|
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBX)
|
||||||
sequence = PrintRegister("ebx", frame_x86->context.ebx, sequence);
|
sequence = PrintRegister("ebx", frame_x86->context.ebx, sequence);
|
||||||
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESI)
|
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESI)
|
||||||
sequence = PrintRegister("esi", frame_x86->context.esi, sequence);
|
sequence = PrintRegister("esi", frame_x86->context.esi, sequence);
|
||||||
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EDI)
|
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EDI)
|
||||||
sequence = PrintRegister("edi", frame_x86->context.edi, sequence);
|
sequence = PrintRegister("edi", frame_x86->context.edi, sequence);
|
||||||
if (frame_x86->context_validity == StackFrameX86::CONTEXT_VALID_ALL) {
|
if (frame_x86->context_validity == StackFrameX86::CONTEXT_VALID_ALL) {
|
||||||
sequence = PrintRegister("eax", frame_x86->context.eax, sequence);
|
sequence = PrintRegister("eax", frame_x86->context.eax, sequence);
|
||||||
sequence = PrintRegister("ecx", frame_x86->context.ecx, sequence);
|
sequence = PrintRegister("ecx", frame_x86->context.ecx, sequence);
|
||||||
sequence = PrintRegister("edx", frame_x86->context.edx, sequence);
|
sequence = PrintRegister("edx", frame_x86->context.edx, sequence);
|
||||||
sequence = PrintRegister("efl", frame_x86->context.eflags, sequence);
|
sequence = PrintRegister("efl", frame_x86->context.eflags, sequence);
|
||||||
}
|
}
|
||||||
} else if (cpu == "ppc") {
|
} else if (cpu == "ppc") {
|
||||||
const StackFramePPC* frame_ppc =
|
const StackFramePPC* frame_ppc =
|
||||||
reinterpret_cast<const StackFramePPC*>(frame);
|
reinterpret_cast<const StackFramePPC*>(frame);
|
||||||
|
|
||||||
if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_SRR0)
|
if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_SRR0)
|
||||||
sequence = PrintRegister("srr0", frame_ppc->context.srr0, sequence);
|
sequence = PrintRegister("srr0", frame_ppc->context.srr0, sequence);
|
||||||
if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_GPR1)
|
if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_GPR1)
|
||||||
sequence = PrintRegister("r1", frame_ppc->context.gpr[1], sequence);
|
sequence = PrintRegister("r1", frame_ppc->context.gpr[1], sequence);
|
||||||
} else if (cpu == "amd64") {
|
} else if (cpu == "amd64") {
|
||||||
const StackFrameAMD64* frame_amd64 =
|
const StackFrameAMD64* frame_amd64 =
|
||||||
reinterpret_cast<const StackFrameAMD64*>(frame);
|
reinterpret_cast<const StackFrameAMD64*>(frame);
|
||||||
|
|
||||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RAX)
|
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RAX)
|
||||||
sequence = PrintRegister64("rax", frame_amd64->context.rax, sequence);
|
sequence = PrintRegister64("rax", frame_amd64->context.rax, sequence);
|
||||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDX)
|
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDX)
|
||||||
sequence = PrintRegister64("rdx", frame_amd64->context.rdx, sequence);
|
sequence = PrintRegister64("rdx", frame_amd64->context.rdx, sequence);
|
||||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RCX)
|
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RCX)
|
||||||
sequence = PrintRegister64("rcx", frame_amd64->context.rcx, sequence);
|
sequence = PrintRegister64("rcx", frame_amd64->context.rcx, sequence);
|
||||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBX)
|
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBX)
|
||||||
sequence = PrintRegister64("rbx", frame_amd64->context.rbx, sequence);
|
sequence = PrintRegister64("rbx", frame_amd64->context.rbx, sequence);
|
||||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSI)
|
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSI)
|
||||||
sequence = PrintRegister64("rsi", frame_amd64->context.rsi, sequence);
|
sequence = PrintRegister64("rsi", frame_amd64->context.rsi, sequence);
|
||||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDI)
|
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RDI)
|
||||||
sequence = PrintRegister64("rdi", frame_amd64->context.rdi, sequence);
|
sequence = PrintRegister64("rdi", frame_amd64->context.rdi, sequence);
|
||||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP)
|
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP)
|
||||||
sequence = PrintRegister64("rbp", frame_amd64->context.rbp, sequence);
|
sequence = PrintRegister64("rbp", frame_amd64->context.rbp, sequence);
|
||||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP)
|
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP)
|
||||||
sequence = PrintRegister64("rsp", frame_amd64->context.rsp, sequence);
|
sequence = PrintRegister64("rsp", frame_amd64->context.rsp, sequence);
|
||||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R8)
|
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R8)
|
||||||
sequence = PrintRegister64("r8", frame_amd64->context.r8, sequence);
|
sequence = PrintRegister64("r8", frame_amd64->context.r8, sequence);
|
||||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R9)
|
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R9)
|
||||||
sequence = PrintRegister64("r9", frame_amd64->context.r9, sequence);
|
sequence = PrintRegister64("r9", frame_amd64->context.r9, sequence);
|
||||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R10)
|
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R10)
|
||||||
sequence = PrintRegister64("r10", frame_amd64->context.r10, sequence);
|
sequence = PrintRegister64("r10", frame_amd64->context.r10, sequence);
|
||||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R11)
|
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R11)
|
||||||
sequence = PrintRegister64("r11", frame_amd64->context.r11, sequence);
|
sequence = PrintRegister64("r11", frame_amd64->context.r11, sequence);
|
||||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R12)
|
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R12)
|
||||||
sequence = PrintRegister64("r12", frame_amd64->context.r12, sequence);
|
sequence = PrintRegister64("r12", frame_amd64->context.r12, sequence);
|
||||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R13)
|
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R13)
|
||||||
sequence = PrintRegister64("r13", frame_amd64->context.r13, sequence);
|
sequence = PrintRegister64("r13", frame_amd64->context.r13, sequence);
|
||||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R14)
|
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R14)
|
||||||
sequence = PrintRegister64("r14", frame_amd64->context.r14, sequence);
|
sequence = PrintRegister64("r14", frame_amd64->context.r14, sequence);
|
||||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R15)
|
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R15)
|
||||||
sequence = PrintRegister64("r15", frame_amd64->context.r15, sequence);
|
sequence = PrintRegister64("r15", frame_amd64->context.r15, sequence);
|
||||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RIP)
|
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RIP)
|
||||||
sequence = PrintRegister64("rip", frame_amd64->context.rip, sequence);
|
sequence = PrintRegister64("rip", frame_amd64->context.rip, sequence);
|
||||||
} else if (cpu == "sparc") {
|
} else if (cpu == "sparc") {
|
||||||
const StackFrameSPARC* frame_sparc =
|
const StackFrameSPARC* frame_sparc =
|
||||||
reinterpret_cast<const StackFrameSPARC*>(frame);
|
reinterpret_cast<const StackFrameSPARC*>(frame);
|
||||||
|
|
||||||
if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_SP)
|
if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_SP)
|
||||||
sequence = PrintRegister("sp", frame_sparc->context.g_r[14], sequence);
|
sequence =
|
||||||
if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_FP)
|
PrintRegister("sp", frame_sparc->context.g_r[14], sequence);
|
||||||
sequence = PrintRegister("fp", frame_sparc->context.g_r[30], sequence);
|
if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_FP)
|
||||||
if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_PC)
|
sequence =
|
||||||
sequence = PrintRegister("pc", frame_sparc->context.pc, sequence);
|
PrintRegister("fp", frame_sparc->context.g_r[30], sequence);
|
||||||
} else if (cpu == "arm") {
|
if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_PC)
|
||||||
const StackFrameARM* frame_arm =
|
sequence = PrintRegister("pc", frame_sparc->context.pc, sequence);
|
||||||
reinterpret_cast<const StackFrameARM*>(frame);
|
} else if (cpu == "arm") {
|
||||||
|
const StackFrameARM* frame_arm =
|
||||||
|
reinterpret_cast<const StackFrameARM*>(frame);
|
||||||
|
|
||||||
// Argument registers (caller-saves), which will likely only be valid
|
// Argument registers (caller-saves), which will likely only be valid
|
||||||
// for the youngest frame.
|
// for the youngest frame.
|
||||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R0)
|
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R0)
|
||||||
sequence = PrintRegister("r0", frame_arm->context.iregs[0], sequence);
|
sequence = PrintRegister("r0", frame_arm->context.iregs[0], sequence);
|
||||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R1)
|
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R1)
|
||||||
sequence = PrintRegister("r1", frame_arm->context.iregs[1], sequence);
|
sequence = PrintRegister("r1", frame_arm->context.iregs[1], sequence);
|
||||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R2)
|
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R2)
|
||||||
sequence = PrintRegister("r2", frame_arm->context.iregs[2], sequence);
|
sequence = PrintRegister("r2", frame_arm->context.iregs[2], sequence);
|
||||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R3)
|
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R3)
|
||||||
sequence = PrintRegister("r3", frame_arm->context.iregs[3], sequence);
|
sequence = PrintRegister("r3", frame_arm->context.iregs[3], sequence);
|
||||||
|
|
||||||
// General-purpose callee-saves registers.
|
// General-purpose callee-saves registers.
|
||||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R4)
|
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R4)
|
||||||
sequence = PrintRegister("r4", frame_arm->context.iregs[4], sequence);
|
sequence = PrintRegister("r4", frame_arm->context.iregs[4], sequence);
|
||||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R5)
|
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R5)
|
||||||
sequence = PrintRegister("r5", frame_arm->context.iregs[5], sequence);
|
sequence = PrintRegister("r5", frame_arm->context.iregs[5], sequence);
|
||||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R6)
|
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R6)
|
||||||
sequence = PrintRegister("r6", frame_arm->context.iregs[6], sequence);
|
sequence = PrintRegister("r6", frame_arm->context.iregs[6], sequence);
|
||||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R7)
|
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R7)
|
||||||
sequence = PrintRegister("r7", frame_arm->context.iregs[7], sequence);
|
sequence = PrintRegister("r7", frame_arm->context.iregs[7], sequence);
|
||||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R8)
|
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R8)
|
||||||
sequence = PrintRegister("r8", frame_arm->context.iregs[8], sequence);
|
sequence = PrintRegister("r8", frame_arm->context.iregs[8], sequence);
|
||||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R9)
|
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R9)
|
||||||
sequence = PrintRegister("r9", frame_arm->context.iregs[9], sequence);
|
sequence = PrintRegister("r9", frame_arm->context.iregs[9], sequence);
|
||||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R10)
|
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R10)
|
||||||
sequence = PrintRegister("r10", frame_arm->context.iregs[10], sequence);
|
sequence =
|
||||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R12)
|
PrintRegister("r10", frame_arm->context.iregs[10], sequence);
|
||||||
sequence = PrintRegister("r12", frame_arm->context.iregs[12], 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.
|
// Registers with a dedicated or conventional purpose.
|
||||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_FP)
|
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_FP)
|
||||||
sequence = PrintRegister("fp", frame_arm->context.iregs[11], sequence);
|
sequence =
|
||||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP)
|
PrintRegister("fp", frame_arm->context.iregs[11], sequence);
|
||||||
sequence = PrintRegister("sp", frame_arm->context.iregs[13], sequence);
|
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP)
|
||||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_LR)
|
sequence =
|
||||||
sequence = PrintRegister("lr", frame_arm->context.iregs[14], sequence);
|
PrintRegister("sp", frame_arm->context.iregs[13], sequence);
|
||||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC)
|
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_LR)
|
||||||
sequence = PrintRegister("pc", frame_arm->context.iregs[15], sequence);
|
sequence =
|
||||||
} else if (cpu == "arm64") {
|
PrintRegister("lr", frame_arm->context.iregs[14], sequence);
|
||||||
const StackFrameARM64* frame_arm64 =
|
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC)
|
||||||
reinterpret_cast<const StackFrameARM64*>(frame);
|
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) {
|
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X0) {
|
||||||
sequence =
|
sequence =
|
||||||
PrintRegister64("x0", frame_arm64->context.iregs[0], sequence);
|
PrintRegister64("x0", frame_arm64->context.iregs[0], sequence);
|
||||||
}
|
}
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X1) {
|
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X1) {
|
||||||
sequence =
|
sequence =
|
||||||
PrintRegister64("x1", frame_arm64->context.iregs[1], sequence);
|
PrintRegister64("x1", frame_arm64->context.iregs[1], sequence);
|
||||||
}
|
}
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X2) {
|
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X2) {
|
||||||
sequence =
|
sequence =
|
||||||
PrintRegister64("x2", frame_arm64->context.iregs[2], sequence);
|
PrintRegister64("x2", frame_arm64->context.iregs[2], sequence);
|
||||||
}
|
}
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X3) {
|
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X3) {
|
||||||
sequence =
|
sequence =
|
||||||
PrintRegister64("x3", frame_arm64->context.iregs[3], sequence);
|
PrintRegister64("x3", frame_arm64->context.iregs[3], sequence);
|
||||||
}
|
}
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X4) {
|
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X4) {
|
||||||
sequence =
|
sequence =
|
||||||
PrintRegister64("x4", frame_arm64->context.iregs[4], sequence);
|
PrintRegister64("x4", frame_arm64->context.iregs[4], sequence);
|
||||||
}
|
}
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X5) {
|
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X5) {
|
||||||
sequence =
|
sequence =
|
||||||
PrintRegister64("x5", frame_arm64->context.iregs[5], sequence);
|
PrintRegister64("x5", frame_arm64->context.iregs[5], sequence);
|
||||||
}
|
}
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X6) {
|
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X6) {
|
||||||
sequence =
|
sequence =
|
||||||
PrintRegister64("x6", frame_arm64->context.iregs[6], sequence);
|
PrintRegister64("x6", frame_arm64->context.iregs[6], sequence);
|
||||||
}
|
}
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X7) {
|
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X7) {
|
||||||
sequence =
|
sequence =
|
||||||
PrintRegister64("x7", frame_arm64->context.iregs[7], sequence);
|
PrintRegister64("x7", frame_arm64->context.iregs[7], sequence);
|
||||||
}
|
}
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X8) {
|
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X8) {
|
||||||
sequence =
|
sequence =
|
||||||
PrintRegister64("x8", frame_arm64->context.iregs[8], sequence);
|
PrintRegister64("x8", frame_arm64->context.iregs[8], sequence);
|
||||||
}
|
}
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X9) {
|
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X9) {
|
||||||
sequence =
|
sequence =
|
||||||
PrintRegister64("x9", frame_arm64->context.iregs[9], sequence);
|
PrintRegister64("x9", frame_arm64->context.iregs[9], sequence);
|
||||||
}
|
}
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X10) {
|
if (frame_arm64->context_validity &
|
||||||
sequence =
|
StackFrameARM64::CONTEXT_VALID_X10) {
|
||||||
PrintRegister64("x10", frame_arm64->context.iregs[10], sequence);
|
sequence =
|
||||||
}
|
PrintRegister64("x10", frame_arm64->context.iregs[10], sequence);
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X11) {
|
}
|
||||||
sequence =
|
if (frame_arm64->context_validity &
|
||||||
PrintRegister64("x11", frame_arm64->context.iregs[11], sequence);
|
StackFrameARM64::CONTEXT_VALID_X11) {
|
||||||
}
|
sequence =
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X12) {
|
PrintRegister64("x11", frame_arm64->context.iregs[11], sequence);
|
||||||
sequence =
|
}
|
||||||
PrintRegister64("x12", frame_arm64->context.iregs[12], sequence);
|
if (frame_arm64->context_validity &
|
||||||
}
|
StackFrameARM64::CONTEXT_VALID_X12) {
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X13) {
|
sequence =
|
||||||
sequence =
|
PrintRegister64("x12", frame_arm64->context.iregs[12], sequence);
|
||||||
PrintRegister64("x13", frame_arm64->context.iregs[13], sequence);
|
}
|
||||||
}
|
if (frame_arm64->context_validity &
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X14) {
|
StackFrameARM64::CONTEXT_VALID_X13) {
|
||||||
sequence =
|
sequence =
|
||||||
PrintRegister64("x14", frame_arm64->context.iregs[14], sequence);
|
PrintRegister64("x13", frame_arm64->context.iregs[13], sequence);
|
||||||
}
|
}
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X15) {
|
if (frame_arm64->context_validity &
|
||||||
sequence =
|
StackFrameARM64::CONTEXT_VALID_X14) {
|
||||||
PrintRegister64("x15", frame_arm64->context.iregs[15], sequence);
|
sequence =
|
||||||
}
|
PrintRegister64("x14", frame_arm64->context.iregs[14], sequence);
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X16) {
|
}
|
||||||
sequence =
|
if (frame_arm64->context_validity &
|
||||||
PrintRegister64("x16", frame_arm64->context.iregs[16], sequence);
|
StackFrameARM64::CONTEXT_VALID_X15) {
|
||||||
}
|
sequence =
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X17) {
|
PrintRegister64("x15", frame_arm64->context.iregs[15], sequence);
|
||||||
sequence =
|
}
|
||||||
PrintRegister64("x17", frame_arm64->context.iregs[17], sequence);
|
if (frame_arm64->context_validity &
|
||||||
}
|
StackFrameARM64::CONTEXT_VALID_X16) {
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X18) {
|
sequence =
|
||||||
sequence =
|
PrintRegister64("x16", frame_arm64->context.iregs[16], sequence);
|
||||||
PrintRegister64("x18", frame_arm64->context.iregs[18], sequence);
|
}
|
||||||
}
|
if (frame_arm64->context_validity &
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X19) {
|
StackFrameARM64::CONTEXT_VALID_X17) {
|
||||||
sequence =
|
sequence =
|
||||||
PrintRegister64("x19", frame_arm64->context.iregs[19], sequence);
|
PrintRegister64("x17", frame_arm64->context.iregs[17], sequence);
|
||||||
}
|
}
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X20) {
|
if (frame_arm64->context_validity &
|
||||||
sequence =
|
StackFrameARM64::CONTEXT_VALID_X18) {
|
||||||
PrintRegister64("x20", frame_arm64->context.iregs[20], sequence);
|
sequence =
|
||||||
}
|
PrintRegister64("x18", frame_arm64->context.iregs[18], sequence);
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X21) {
|
}
|
||||||
sequence =
|
if (frame_arm64->context_validity &
|
||||||
PrintRegister64("x21", frame_arm64->context.iregs[21], sequence);
|
StackFrameARM64::CONTEXT_VALID_X19) {
|
||||||
}
|
sequence =
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X22) {
|
PrintRegister64("x19", frame_arm64->context.iregs[19], sequence);
|
||||||
sequence =
|
}
|
||||||
PrintRegister64("x22", frame_arm64->context.iregs[22], sequence);
|
if (frame_arm64->context_validity &
|
||||||
}
|
StackFrameARM64::CONTEXT_VALID_X20) {
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X23) {
|
sequence =
|
||||||
sequence =
|
PrintRegister64("x20", frame_arm64->context.iregs[20], sequence);
|
||||||
PrintRegister64("x23", frame_arm64->context.iregs[23], sequence);
|
}
|
||||||
}
|
if (frame_arm64->context_validity &
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X24) {
|
StackFrameARM64::CONTEXT_VALID_X21) {
|
||||||
sequence =
|
sequence =
|
||||||
PrintRegister64("x24", frame_arm64->context.iregs[24], sequence);
|
PrintRegister64("x21", frame_arm64->context.iregs[21], sequence);
|
||||||
}
|
}
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X25) {
|
if (frame_arm64->context_validity &
|
||||||
sequence =
|
StackFrameARM64::CONTEXT_VALID_X22) {
|
||||||
PrintRegister64("x25", frame_arm64->context.iregs[25], sequence);
|
sequence =
|
||||||
}
|
PrintRegister64("x22", frame_arm64->context.iregs[22], sequence);
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X26) {
|
}
|
||||||
sequence =
|
if (frame_arm64->context_validity &
|
||||||
PrintRegister64("x26", frame_arm64->context.iregs[26], sequence);
|
StackFrameARM64::CONTEXT_VALID_X23) {
|
||||||
}
|
sequence =
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X27) {
|
PrintRegister64("x23", frame_arm64->context.iregs[23], sequence);
|
||||||
sequence =
|
}
|
||||||
PrintRegister64("x27", frame_arm64->context.iregs[27], sequence);
|
if (frame_arm64->context_validity &
|
||||||
}
|
StackFrameARM64::CONTEXT_VALID_X24) {
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X28) {
|
sequence =
|
||||||
sequence =
|
PrintRegister64("x24", frame_arm64->context.iregs[24], sequence);
|
||||||
PrintRegister64("x28", frame_arm64->context.iregs[28], 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.
|
// Registers with a dedicated or conventional purpose.
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_FP) {
|
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_FP) {
|
||||||
sequence =
|
sequence =
|
||||||
PrintRegister64("fp", frame_arm64->context.iregs[29], sequence);
|
PrintRegister64("fp", frame_arm64->context.iregs[29], sequence);
|
||||||
}
|
}
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_LR) {
|
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_LR) {
|
||||||
sequence =
|
sequence =
|
||||||
PrintRegister64("lr", frame_arm64->context.iregs[30], sequence);
|
PrintRegister64("lr", frame_arm64->context.iregs[30], sequence);
|
||||||
}
|
}
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_SP) {
|
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_SP) {
|
||||||
sequence =
|
sequence =
|
||||||
PrintRegister64("sp", frame_arm64->context.iregs[31], sequence);
|
PrintRegister64("sp", frame_arm64->context.iregs[31], sequence);
|
||||||
}
|
}
|
||||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_PC) {
|
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_PC) {
|
||||||
sequence =
|
sequence =
|
||||||
PrintRegister64("pc", frame_arm64->context.iregs[32], sequence);
|
PrintRegister64("pc", frame_arm64->context.iregs[32], sequence);
|
||||||
}
|
}
|
||||||
} else if ((cpu == "mips") || (cpu == "mips64")) {
|
} else if ((cpu == "mips") || (cpu == "mips64")) {
|
||||||
const StackFrameMIPS* frame_mips =
|
const StackFrameMIPS* frame_mips =
|
||||||
reinterpret_cast<const StackFrameMIPS*>(frame);
|
reinterpret_cast<const StackFrameMIPS*>(frame);
|
||||||
|
|
||||||
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_GP)
|
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_GP)
|
||||||
sequence = PrintRegister64("gp",
|
sequence = PrintRegister64(
|
||||||
frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_GP],
|
"gp", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_GP],
|
||||||
sequence);
|
sequence);
|
||||||
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_SP)
|
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_SP)
|
||||||
sequence = PrintRegister64("sp",
|
sequence = PrintRegister64(
|
||||||
frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_SP],
|
"sp", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_SP],
|
||||||
sequence);
|
sequence);
|
||||||
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_FP)
|
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_FP)
|
||||||
sequence = PrintRegister64("fp",
|
sequence = PrintRegister64(
|
||||||
frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_FP],
|
"fp", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_FP],
|
||||||
sequence);
|
sequence);
|
||||||
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_RA)
|
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_RA)
|
||||||
sequence = PrintRegister64("ra",
|
sequence = PrintRegister64(
|
||||||
frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_RA],
|
"ra", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_RA],
|
||||||
sequence);
|
sequence);
|
||||||
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_PC)
|
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_PC)
|
||||||
sequence = PrintRegister64("pc", frame_mips->context.epc, sequence);
|
sequence = PrintRegister64("pc", frame_mips->context.epc, sequence);
|
||||||
|
|
||||||
// Save registers s0-s7
|
// Save registers s0-s7
|
||||||
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S0)
|
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S0)
|
||||||
sequence = PrintRegister64("s0",
|
sequence = PrintRegister64(
|
||||||
frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S0],
|
"s0", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S0],
|
||||||
sequence);
|
sequence);
|
||||||
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S1)
|
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S1)
|
||||||
sequence = PrintRegister64("s1",
|
sequence = PrintRegister64(
|
||||||
frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S1],
|
"s1", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S1],
|
||||||
sequence);
|
sequence);
|
||||||
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S2)
|
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S2)
|
||||||
sequence = PrintRegister64("s2",
|
sequence = PrintRegister64(
|
||||||
frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S2],
|
"s2", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S2],
|
||||||
sequence);
|
sequence);
|
||||||
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S3)
|
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S3)
|
||||||
sequence = PrintRegister64("s3",
|
sequence = PrintRegister64(
|
||||||
frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S3],
|
"s3", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S3],
|
||||||
sequence);
|
sequence);
|
||||||
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S4)
|
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S4)
|
||||||
sequence = PrintRegister64("s4",
|
sequence = PrintRegister64(
|
||||||
frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S4],
|
"s4", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S4],
|
||||||
sequence);
|
sequence);
|
||||||
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S5)
|
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S5)
|
||||||
sequence = PrintRegister64("s5",
|
sequence = PrintRegister64(
|
||||||
frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S5],
|
"s5", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S5],
|
||||||
sequence);
|
sequence);
|
||||||
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S6)
|
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S6)
|
||||||
sequence = PrintRegister64("s6",
|
sequence = PrintRegister64(
|
||||||
frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S6],
|
"s6", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S6],
|
||||||
sequence);
|
sequence);
|
||||||
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S7)
|
if (frame_mips->context_validity & StackFrameMIPS::CONTEXT_VALID_S7)
|
||||||
sequence = PrintRegister64("s7",
|
sequence = PrintRegister64(
|
||||||
frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S7],
|
"s7", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S7],
|
||||||
sequence);
|
sequence);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
printf("\n Found by: %s\n", frame->trust_description().c_str());
|
printf("\n Found by: %s\n", frame->trust_description().c_str());
|
||||||
|
|
||||||
|
|
|
@ -138,11 +138,12 @@ bool Stackwalker::Walk(
|
||||||
// frame_pointer fields. The frame structure comes from either the
|
// frame_pointer fields. The frame structure comes from either the
|
||||||
// context frame (above) or a caller frame (below).
|
// 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.
|
// Resolve the module information, if a module map was provided.
|
||||||
StackFrameSymbolizer::SymbolizerResult symbolizer_result =
|
StackFrameSymbolizer::SymbolizerResult symbolizer_result =
|
||||||
frame_symbolizer_->FillSourceLineInfo(modules_, unloaded_modules_,
|
frame_symbolizer_->FillSourceLineInfo(modules_, unloaded_modules_,
|
||||||
system_info_,
|
system_info_,
|
||||||
frame.get());
|
frame.get(), &inlined_frames);
|
||||||
switch (symbolizer_result) {
|
switch (symbolizer_result) {
|
||||||
case StackFrameSymbolizer::kInterrupt:
|
case StackFrameSymbolizer::kInterrupt:
|
||||||
BPLOG(INFO) << "Stack walk is interrupted.";
|
BPLOG(INFO) << "Stack walk is interrupted.";
|
||||||
|
@ -173,7 +174,11 @@ bool Stackwalker::Walk(
|
||||||
default:
|
default:
|
||||||
break;
|
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
|
// Add the frame to the call stack. Relinquish the ownership claim
|
||||||
// over the frame, because the stack now owns it.
|
// over the frame, because the stack now owns it.
|
||||||
stack->frames_.push_back(frame.release());
|
stack->frames_.push_back(frame.release());
|
||||||
|
@ -307,7 +312,7 @@ bool Stackwalker::InstructionAddressSeemsValid(uint64_t address) const {
|
||||||
frame.instruction = address;
|
frame.instruction = address;
|
||||||
StackFrameSymbolizer::SymbolizerResult symbolizer_result =
|
StackFrameSymbolizer::SymbolizerResult symbolizer_result =
|
||||||
frame_symbolizer_->FillSourceLineInfo(modules_, unloaded_modules_,
|
frame_symbolizer_->FillSourceLineInfo(modules_, unloaded_modules_,
|
||||||
system_info_, &frame);
|
system_info_, &frame, nullptr);
|
||||||
|
|
||||||
if (!frame.module) {
|
if (!frame.module) {
|
||||||
// not inside any loaded module
|
// not inside any loaded module
|
||||||
|
|
BIN
src/processor/testdata/linux_inline.dmp
vendored
Normal file
BIN
src/processor/testdata/linux_inline.dmp
vendored
Normal file
Binary file not shown.
68
src/processor/testdata/symbols/linux_inline/BBA6FA10B8AAB33D00000000000000000/linux_inline.sym
vendored
Normal file
68
src/processor/testdata/symbols/linux_inline/BBA6FA10B8AAB33D00000000000000000/linux_inline.sym
vendored
Normal file
|
@ -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
|
Loading…
Reference in a new issue