mirror of
https://git.suyu.dev/suyu/breakpad.git
synced 2025-12-26 09:14:58 +01:00
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 <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -52,6 +53,7 @@
|
|||
using std::map;
|
||||
using std::vector;
|
||||
using std::make_pair;
|
||||
using std::unique_ptr;
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
|
|
@ -202,6 +204,16 @@ bool BasicSourceLineResolver::Module::LoadMapFromMemory(
|
|||
// Ignore these as well, they're similarly just for housekeeping.
|
||||
//
|
||||
// INFO CODE_ID <code id> <filename>
|
||||
} else if (strncmp(buffer, "INLINE ", 7) == 0) {
|
||||
linked_ptr<Inline> in = ParseInline(buffer);
|
||||
if (!in.get())
|
||||
LogParseError("ParseInline failed", line_number, &num_errors);
|
||||
else
|
||||
cur_func->AppendInline(in);
|
||||
} else if (strncmp(buffer, "INLINE_ORIGIN ", 14) == 0) {
|
||||
if (!ParseInlineOrigin(buffer)) {
|
||||
LogParseError("ParseInlineOrigin failed", line_number, &num_errors);
|
||||
}
|
||||
} else {
|
||||
if (!cur_func.get()) {
|
||||
LogParseError("Found source line data without a function",
|
||||
|
|
@ -225,7 +237,39 @@ bool BasicSourceLineResolver::Module::LoadMapFromMemory(
|
|||
return true;
|
||||
}
|
||||
|
||||
void BasicSourceLineResolver::Module::LookupAddress(StackFrame* frame) const {
|
||||
int BasicSourceLineResolver::Module::ConstructInlineFrames(
|
||||
StackFrame* frame,
|
||||
MemAddr address,
|
||||
const RangeMap<uint64_t, linked_ptr<Inline>>& inlines,
|
||||
vector<unique_ptr<StackFrame>>* inlined_frames) const {
|
||||
linked_ptr<Inline> in;
|
||||
MemAddr inline_base;
|
||||
if (!inlines.RetrieveRange(address, &in, &inline_base, nullptr, nullptr))
|
||||
return -1;
|
||||
|
||||
StackFrame new_frame = StackFrame(*frame);
|
||||
new_frame.function_name = in->name;
|
||||
// Use the starting adress of the inlined range as inlined function base.
|
||||
new_frame.function_base = new_frame.module->base_address() + inline_base;
|
||||
auto it = files_.find(in->source_file_id);
|
||||
if (it != files_.end())
|
||||
new_frame.source_file_name = it->second;
|
||||
|
||||
new_frame.trust = StackFrame::FRAME_TRUST_INLINE;
|
||||
// Must add frames before calling ConstructInlineFrames to get correct order.
|
||||
int current_idx = inlined_frames->size();
|
||||
inlined_frames->push_back(std::make_unique<StackFrame>(new_frame));
|
||||
int source_line = ConstructInlineFrames(&new_frame, address,
|
||||
in->child_inlines, inlined_frames);
|
||||
if (source_line != -1) {
|
||||
(*inlined_frames)[current_idx]->source_line = source_line;
|
||||
}
|
||||
return in->call_site_line;
|
||||
}
|
||||
|
||||
void BasicSourceLineResolver::Module::LookupAddress(
|
||||
StackFrame* frame,
|
||||
vector<unique_ptr<StackFrame>>* inlined_frames) const {
|
||||
MemAddr address = frame->instruction - frame->module->base_address();
|
||||
|
||||
// First, look for a FUNC record that covers address. Use
|
||||
|
|
@ -256,6 +300,15 @@ void BasicSourceLineResolver::Module::LookupAddress(StackFrame* frame) const {
|
|||
frame->source_line = line->line;
|
||||
frame->source_line_base = frame->module->base_address() + line_base;
|
||||
}
|
||||
|
||||
// Check if this is inlined function call.
|
||||
if (inlined_frames) {
|
||||
int source_line =
|
||||
ConstructInlineFrames(frame, address, func->inlines, inlined_frames);
|
||||
if (source_line != -1) {
|
||||
frame->source_line = source_line;
|
||||
}
|
||||
}
|
||||
} else if (public_symbols_.Retrieve(address,
|
||||
&public_symbol, &public_address) &&
|
||||
(!func.get() || public_address > function_base)) {
|
||||
|
|
@ -357,6 +410,38 @@ bool BasicSourceLineResolver::Module::ParseFile(char* file_line) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::Module::ParseInlineOrigin(
|
||||
char* inline_origin_line) {
|
||||
long origin_id;
|
||||
long source_file_id;
|
||||
char* origin_name;
|
||||
if (SymbolParseHelper::ParseInlineOrigin(inline_origin_line, &origin_id,
|
||||
&source_file_id, &origin_name)) {
|
||||
inline_origins_.insert(make_pair(
|
||||
origin_id, new InlineOrigin(origin_id, source_file_id, origin_name)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
linked_ptr<BasicSourceLineResolver::Inline>
|
||||
BasicSourceLineResolver::Module::ParseInline(char* inline_line) {
|
||||
long inline_nest_level;
|
||||
long call_site_line;
|
||||
long origin_id;
|
||||
vector<std::pair<MemAddr, MemAddr>> ranges;
|
||||
if (SymbolParseHelper::ParseInline(inline_line, &inline_nest_level,
|
||||
&call_site_line, &origin_id, &ranges)) {
|
||||
auto origin = inline_origins_.find(origin_id);
|
||||
if (origin != inline_origins_.end()) {
|
||||
return linked_ptr<Inline>(
|
||||
new Inline(inline_nest_level, call_site_line, origin->second->name,
|
||||
origin->second->source_file_id, ranges));
|
||||
}
|
||||
}
|
||||
return linked_ptr<Inline>();
|
||||
}
|
||||
|
||||
BasicSourceLineResolver::Function*
|
||||
BasicSourceLineResolver::Module::ParseFunction(char* function_line) {
|
||||
bool is_multiple;
|
||||
|
|
@ -502,6 +587,26 @@ bool BasicSourceLineResolver::Module::ParseCFIFrameInfo(
|
|||
return true;
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::Function::AppendInline(linked_ptr<Inline> in) {
|
||||
// This happends if in's parent wasn't added due to a malformed INLINE record.
|
||||
if (in->inline_nest_level > last_added_inline_nest_level + 1)
|
||||
return false;
|
||||
RangeMap<MemAddr, linked_ptr<Inline>>* current_inlines = &this->inlines;
|
||||
auto iter = recent_inlines.find(in->inline_nest_level - 1);
|
||||
if (iter != recent_inlines.end())
|
||||
current_inlines = &iter->second->child_inlines;
|
||||
else
|
||||
assert(in->inline_nest_level == 0);
|
||||
|
||||
last_added_inline_nest_level = in->inline_nest_level;
|
||||
recent_inlines[last_added_inline_nest_level] = in;
|
||||
|
||||
// Store all ranges into current level of inlines.
|
||||
for (auto range : in->inline_ranges)
|
||||
current_inlines->StoreRange(range.first, range.second, in);
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SymbolParseHelper::ParseFile(char* file_line, long* index,
|
||||
char** filename) {
|
||||
|
|
@ -529,6 +634,97 @@ bool SymbolParseHelper::ParseFile(char* file_line, long* index,
|
|||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SymbolParseHelper::ParseInlineOrigin(char* inline_origin_line,
|
||||
long* origin_id,
|
||||
long* file_id,
|
||||
char** name) {
|
||||
// INLINE_ORIGIN <origin_id> <file_id> <name>
|
||||
assert(strncmp(inline_origin_line, "INLINE_ORIGIN ", 14) == 0);
|
||||
inline_origin_line += 14; // skip prefix
|
||||
vector<char*> tokens;
|
||||
if (!Tokenize(inline_origin_line, kWhitespace, 3, &tokens)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char* after_number;
|
||||
*origin_id = strtol(tokens[0], &after_number, 10);
|
||||
if (!IsValidAfterNumber(after_number) || *origin_id < 0 ||
|
||||
*origin_id == std::numeric_limits<long>::max()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*file_id = strtol(tokens[1], &after_number, 10);
|
||||
// If the file id is -1, it might be an artificial function that doesn't have
|
||||
// file id. So, we consider -1 as a valid special case.
|
||||
if (!IsValidAfterNumber(after_number) ||
|
||||
*file_id < -1 | *origin_id == std::numeric_limits<long>::max()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*name = tokens[2];
|
||||
if (!*name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SymbolParseHelper::ParseInline(
|
||||
char* inline_line,
|
||||
long* inline_nest_level,
|
||||
long* call_site_line,
|
||||
long* origin_id,
|
||||
vector<std::pair<MemAddr, MemAddr>>* ranges) {
|
||||
// INLINE <inline_nest_level> <call_site_line> <origin_id> <address> <size>
|
||||
// ...
|
||||
assert(strncmp(inline_line, "INLINE ", 7) == 0);
|
||||
inline_line += 7; // skip prefix
|
||||
|
||||
vector<char*> tokens;
|
||||
Tokenize(inline_line, kWhitespace, std::numeric_limits<int>::max(), &tokens);
|
||||
|
||||
// The length of the vector should be at least 5 and an odd number.
|
||||
if (tokens.size() < 5 && tokens.size() % 2 == 0)
|
||||
return false;
|
||||
|
||||
char* after_number;
|
||||
*inline_nest_level = strtol(tokens[0], &after_number, 10);
|
||||
if (!IsValidAfterNumber(after_number) || *inline_nest_level < 0 ||
|
||||
*inline_nest_level == std::numeric_limits<long>::max()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*call_site_line = strtol(tokens[1], &after_number, 10);
|
||||
if (!IsValidAfterNumber(after_number) || *call_site_line < 0 ||
|
||||
*call_site_line == std::numeric_limits<long>::max()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*origin_id = strtol(tokens[2], &after_number, 10);
|
||||
if (!IsValidAfterNumber(after_number) || *origin_id < 0 ||
|
||||
*origin_id == std::numeric_limits<long>::max()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 3; i < tokens.size();) {
|
||||
MemAddr address = strtoull(tokens[i++], &after_number, 16);
|
||||
if (!IsValidAfterNumber(after_number) ||
|
||||
address == std::numeric_limits<unsigned long long>::max()) {
|
||||
return false;
|
||||
}
|
||||
MemAddr size = strtoull(tokens[i++], &after_number, 16);
|
||||
if (!IsValidAfterNumber(after_number) ||
|
||||
size == std::numeric_limits<unsigned long long>::max()) {
|
||||
return false;
|
||||
}
|
||||
ranges->push_back({address, size});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SymbolParseHelper::ParseFunction(char* function_line, bool* is_multiple,
|
||||
uint64_t* address, uint64_t* size,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue