mirror of
https://github.com/intel/llvm.git
synced 2026-01-16 05:32:28 +08:00
[LLDB][Breakpad] Make lldb understand INLINE and INLINE_ORIGIN records in breakpad.
Teach LLDB to understand INLINE and INLINE_ORIGIN records in breakpad. They have the following formats: ``` INLINE inline_nest_level call_site_line call_site_file_num origin_num [address size]+ INLINE_ORIGIN origin_num name ``` `INLNIE_ORIGIN` is simply a string pool for INLINE so that we won't have duplicated names for inlined functions and can show up anywhere in the symbol file. `INLINE` follows immediately after `FUNC` represents the ranges of momery address that has functions inlined inside the function. Differential Revision: https://reviews.llvm.org/D113330
This commit is contained in:
@@ -338,6 +338,8 @@ public:
|
||||
|
||||
Block *FindBlockByID(lldb::user_id_t block_id);
|
||||
|
||||
Block *FindInnermostBlockByOffset(const lldb::addr_t offset);
|
||||
|
||||
size_t GetNumRanges() const { return m_ranges.GetSize(); }
|
||||
|
||||
bool GetRangeContainingOffset(const lldb::addr_t offset, Range &range);
|
||||
|
||||
@@ -23,6 +23,8 @@ enum class Token {
|
||||
CodeID,
|
||||
File,
|
||||
Func,
|
||||
Inline,
|
||||
InlineOrigin,
|
||||
Public,
|
||||
Stack,
|
||||
CFI,
|
||||
@@ -41,6 +43,8 @@ template <> Token stringTo<Token>(llvm::StringRef Str) {
|
||||
.Case("CODE_ID", Token::CodeID)
|
||||
.Case("FILE", Token::File)
|
||||
.Case("FUNC", Token::Func)
|
||||
.Case("INLINE", Token::Inline)
|
||||
.Case("INLINE_ORIGIN", Token::InlineOrigin)
|
||||
.Case("PUBLIC", Token::Public)
|
||||
.Case("STACK", Token::Stack)
|
||||
.Case("CFI", Token::CFI)
|
||||
@@ -145,7 +149,10 @@ llvm::Optional<Record::Kind> Record::classify(llvm::StringRef Line) {
|
||||
default:
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
case Token::Inline:
|
||||
return Record::Inline;
|
||||
case Token::InlineOrigin:
|
||||
return Record::InlineOrigin;
|
||||
case Token::Unknown:
|
||||
// Optimistically assume that any unrecognised token means this is a line
|
||||
// record, those don't have a special keyword and start directly with a
|
||||
@@ -216,9 +223,11 @@ llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
|
||||
return OS << "INFO CODE_ID " << R.ID.GetAsString();
|
||||
}
|
||||
|
||||
llvm::Optional<FileRecord> FileRecord::parse(llvm::StringRef Line) {
|
||||
// FILE number name
|
||||
if (consume<Token>(Line) != Token::File)
|
||||
template <typename T>
|
||||
static llvm::Optional<T> parseNumberName(llvm::StringRef Line,
|
||||
Token TokenType) {
|
||||
// TOKEN number name
|
||||
if (consume<Token>(Line) != TokenType)
|
||||
return llvm::None;
|
||||
|
||||
llvm::StringRef Str;
|
||||
@@ -231,7 +240,12 @@ llvm::Optional<FileRecord> FileRecord::parse(llvm::StringRef Line) {
|
||||
if (Name.empty())
|
||||
return llvm::None;
|
||||
|
||||
return FileRecord(Number, Name);
|
||||
return T(Number, Name);
|
||||
}
|
||||
|
||||
llvm::Optional<FileRecord> FileRecord::parse(llvm::StringRef Line) {
|
||||
// FILE number name
|
||||
return parseNumberName<FileRecord>(Line, Token::File);
|
||||
}
|
||||
|
||||
llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
|
||||
@@ -239,6 +253,17 @@ llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
|
||||
return OS << "FILE " << R.Number << " " << R.Name;
|
||||
}
|
||||
|
||||
llvm::Optional<InlineOriginRecord>
|
||||
InlineOriginRecord::parse(llvm::StringRef Line) {
|
||||
// INLINE_ORIGIN number name
|
||||
return parseNumberName<InlineOriginRecord>(Line, Token::InlineOrigin);
|
||||
}
|
||||
|
||||
llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
|
||||
const InlineOriginRecord &R) {
|
||||
return OS << "INLINE_ORIGIN " << R.Number << " " << R.Name;
|
||||
}
|
||||
|
||||
static bool parsePublicOrFunc(llvm::StringRef Line, bool &Multiple,
|
||||
lldb::addr_t &Address, lldb::addr_t *Size,
|
||||
lldb::addr_t &ParamSize, llvm::StringRef &Name) {
|
||||
@@ -299,6 +324,58 @@ llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
|
||||
R.ParamSize, R.Name);
|
||||
}
|
||||
|
||||
llvm::Optional<InlineRecord> InlineRecord::parse(llvm::StringRef Line) {
|
||||
// INLINE inline_nest_level call_site_line call_site_file_num origin_num
|
||||
// [address size]+
|
||||
if (consume<Token>(Line) != Token::Inline)
|
||||
return llvm::None;
|
||||
|
||||
llvm::SmallVector<llvm::StringRef> Tokens;
|
||||
SplitString(Line, Tokens, " ");
|
||||
if (Tokens.size() < 6 || Tokens.size() % 2 == 1)
|
||||
return llvm::None;
|
||||
|
||||
size_t InlineNestLevel;
|
||||
uint32_t CallSiteLineNum;
|
||||
size_t CallSiteFileNum;
|
||||
size_t OriginNum;
|
||||
if (!(to_integer(Tokens[0], InlineNestLevel) &&
|
||||
to_integer(Tokens[1], CallSiteLineNum) &&
|
||||
to_integer(Tokens[2], CallSiteFileNum) &&
|
||||
to_integer(Tokens[3], OriginNum)))
|
||||
return llvm::None;
|
||||
|
||||
InlineRecord Record = InlineRecord(InlineNestLevel, CallSiteLineNum,
|
||||
CallSiteFileNum, OriginNum);
|
||||
for (size_t i = 4; i < Tokens.size(); i += 2) {
|
||||
lldb::addr_t Address;
|
||||
if (!to_integer(Tokens[i], Address, 16))
|
||||
return llvm::None;
|
||||
lldb::addr_t Size;
|
||||
if (!to_integer(Tokens[i + 1].trim(), Size, 16))
|
||||
return llvm::None;
|
||||
Record.Ranges.emplace_back(Address, Size);
|
||||
}
|
||||
return Record;
|
||||
}
|
||||
|
||||
bool breakpad::operator==(const InlineRecord &L, const InlineRecord &R) {
|
||||
return L.InlineNestLevel == R.InlineNestLevel &&
|
||||
L.CallSiteLineNum == R.CallSiteLineNum &&
|
||||
L.CallSiteFileNum == R.CallSiteFileNum && L.OriginNum == R.OriginNum &&
|
||||
L.Ranges == R.Ranges;
|
||||
}
|
||||
|
||||
llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
|
||||
const InlineRecord &R) {
|
||||
OS << llvm::formatv("INLINE {0} {1} {2} {3}", R.InlineNestLevel,
|
||||
R.CallSiteLineNum, R.CallSiteFileNum, R.OriginNum);
|
||||
for (const auto &range : R.Ranges) {
|
||||
OS << llvm::formatv(" {0:x-} {1:x-}", range.first, range.second);
|
||||
}
|
||||
return OS;
|
||||
}
|
||||
|
||||
llvm::Optional<LineRecord> LineRecord::parse(llvm::StringRef Line) {
|
||||
lldb::addr_t Address;
|
||||
llvm::StringRef Str;
|
||||
@@ -490,6 +567,10 @@ llvm::StringRef breakpad::toString(Record::Kind K) {
|
||||
return "FILE";
|
||||
case Record::Func:
|
||||
return "FUNC";
|
||||
case Record::Inline:
|
||||
return "INLINE";
|
||||
case Record::InlineOrigin:
|
||||
return "INLINE_ORIGIN";
|
||||
case Record::Line:
|
||||
return "LINE";
|
||||
case Record::Public:
|
||||
|
||||
@@ -20,7 +20,18 @@ namespace breakpad {
|
||||
|
||||
class Record {
|
||||
public:
|
||||
enum Kind { Module, Info, File, Func, Line, Public, StackCFI, StackWin };
|
||||
enum Kind {
|
||||
Module,
|
||||
Info,
|
||||
File,
|
||||
Func,
|
||||
Inline,
|
||||
InlineOrigin,
|
||||
Line,
|
||||
Public,
|
||||
StackCFI,
|
||||
StackWin
|
||||
};
|
||||
|
||||
/// Attempt to guess the kind of the record present in the argument without
|
||||
/// doing a full parse. The returned kind will always be correct for valid
|
||||
@@ -89,6 +100,23 @@ inline bool operator==(const FileRecord &L, const FileRecord &R) {
|
||||
}
|
||||
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const FileRecord &R);
|
||||
|
||||
class InlineOriginRecord : public Record {
|
||||
public:
|
||||
static llvm::Optional<InlineOriginRecord> parse(llvm::StringRef Line);
|
||||
InlineOriginRecord(size_t Number, llvm::StringRef Name)
|
||||
: Record(InlineOrigin), Number(Number), Name(Name) {}
|
||||
|
||||
size_t Number;
|
||||
llvm::StringRef Name;
|
||||
};
|
||||
|
||||
inline bool operator==(const InlineOriginRecord &L,
|
||||
const InlineOriginRecord &R) {
|
||||
return L.Number == R.Number && L.Name == R.Name;
|
||||
}
|
||||
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
|
||||
const InlineOriginRecord &R);
|
||||
|
||||
class FuncRecord : public Record {
|
||||
public:
|
||||
static llvm::Optional<FuncRecord> parse(llvm::StringRef Line);
|
||||
@@ -107,6 +135,26 @@ public:
|
||||
bool operator==(const FuncRecord &L, const FuncRecord &R);
|
||||
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const FuncRecord &R);
|
||||
|
||||
class InlineRecord : public Record {
|
||||
public:
|
||||
static llvm::Optional<InlineRecord> parse(llvm::StringRef Line);
|
||||
InlineRecord(size_t InlineNestLevel, uint32_t CallSiteLineNum,
|
||||
size_t CallSiteFileNum, size_t OriginNum)
|
||||
: Record(Inline), InlineNestLevel(InlineNestLevel),
|
||||
CallSiteLineNum(CallSiteLineNum), CallSiteFileNum(CallSiteFileNum),
|
||||
OriginNum(OriginNum) {}
|
||||
|
||||
size_t InlineNestLevel;
|
||||
uint32_t CallSiteLineNum;
|
||||
size_t CallSiteFileNum;
|
||||
size_t OriginNum;
|
||||
// A vector of address range covered by this inline
|
||||
std::vector<std::pair<lldb::addr_t, lldb::addr_t>> Ranges;
|
||||
};
|
||||
|
||||
bool operator==(const InlineRecord &L, const InlineRecord &R);
|
||||
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const InlineRecord &R);
|
||||
|
||||
class LineRecord : public Record {
|
||||
public:
|
||||
static llvm::Optional<LineRecord> parse(llvm::StringRef Line);
|
||||
|
||||
@@ -148,9 +148,9 @@ void ObjectFileBreakpad::CreateSections(SectionList &unified_section_list) {
|
||||
std::tie(line, text) = text.split('\n');
|
||||
|
||||
llvm::Optional<Record::Kind> next_section = Record::classify(line);
|
||||
if (next_section == Record::Line) {
|
||||
// Line records logically belong to the preceding Func record, so we put
|
||||
// them in the same section.
|
||||
if (next_section == Record::Line || next_section == Record::Inline) {
|
||||
// Line/Inline records logically belong to the preceding Func record, so
|
||||
// we put them in the same section.
|
||||
next_section = Record::Func;
|
||||
}
|
||||
if (next_section == current_section)
|
||||
|
||||
@@ -204,6 +204,10 @@ CompUnitSP SymbolFileBreakpad::ParseCompileUnitAtIndex(uint32_t index) {
|
||||
End(*m_objfile_sp);
|
||||
assert(Record::classify(*It) == Record::Func);
|
||||
++It; // Skip FUNC record.
|
||||
// Skip INLINE records.
|
||||
while (It != End && Record::classify(*It) == Record::Inline)
|
||||
++It;
|
||||
|
||||
if (It != End) {
|
||||
auto record = LineRecord::parse(*It);
|
||||
if (record && record->FileNum < m_files->size())
|
||||
@@ -282,13 +286,88 @@ bool SymbolFileBreakpad::ParseSupportFiles(CompileUnit &comp_unit,
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t SymbolFileBreakpad::ParseBlocksRecursive(Function &func) {
|
||||
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
||||
CompileUnit *comp_unit = func.GetCompileUnit();
|
||||
lldbassert(comp_unit);
|
||||
ParseInlineOriginRecords();
|
||||
// A vector of current each level's parent block. For example, when parsing
|
||||
// "INLINE 0 ...", the current level is 0 and its parent block is the
|
||||
// funciton block at index 0.
|
||||
std::vector<Block *> blocks;
|
||||
Block &block = func.GetBlock(false);
|
||||
block.AddRange(Block::Range(0, func.GetAddressRange().GetByteSize()));
|
||||
blocks.push_back(&block);
|
||||
|
||||
size_t blocks_added = 0;
|
||||
addr_t func_base = func.GetAddressRange().GetBaseAddress().GetOffset();
|
||||
CompUnitData &data = m_cu_data->GetEntryRef(comp_unit->GetID()).data;
|
||||
LineIterator It(*m_objfile_sp, Record::Func, data.bookmark),
|
||||
End(*m_objfile_sp);
|
||||
++It; // Skip the FUNC record.
|
||||
size_t last_added_nest_level = 0;
|
||||
while (It != End && Record::classify(*It) == Record::Inline) {
|
||||
if (auto record = InlineRecord::parse(*It)) {
|
||||
if (record->InlineNestLevel == 0 ||
|
||||
record->InlineNestLevel <= last_added_nest_level + 1) {
|
||||
last_added_nest_level = record->InlineNestLevel;
|
||||
BlockSP block_sp = std::make_shared<Block>(It.GetBookmark().offset);
|
||||
FileSpec callsite_file;
|
||||
if (record->CallSiteFileNum < m_files->size())
|
||||
callsite_file = (*m_files)[record->CallSiteFileNum];
|
||||
llvm::StringRef name;
|
||||
if (record->OriginNum < m_inline_origins->size())
|
||||
name = (*m_inline_origins)[record->OriginNum];
|
||||
|
||||
Declaration callsite(callsite_file, record->CallSiteLineNum);
|
||||
block_sp->SetInlinedFunctionInfo(name.str().c_str(),
|
||||
/*mangled=*/nullptr,
|
||||
/*decl_ptr=*/nullptr, &callsite);
|
||||
for (const auto &range : record->Ranges) {
|
||||
block_sp->AddRange(
|
||||
Block::Range(range.first - func_base, range.second));
|
||||
}
|
||||
block_sp->FinalizeRanges();
|
||||
|
||||
blocks[record->InlineNestLevel]->AddChild(block_sp);
|
||||
if (record->InlineNestLevel + 1 >= blocks.size()) {
|
||||
blocks.resize(blocks.size() + 1);
|
||||
}
|
||||
blocks[record->InlineNestLevel + 1] = block_sp.get();
|
||||
++blocks_added;
|
||||
}
|
||||
}
|
||||
++It;
|
||||
}
|
||||
return blocks_added;
|
||||
}
|
||||
|
||||
void SymbolFileBreakpad::ParseInlineOriginRecords() {
|
||||
if (m_inline_origins)
|
||||
return;
|
||||
m_inline_origins.emplace();
|
||||
|
||||
Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS);
|
||||
for (llvm::StringRef line : lines(Record::InlineOrigin)) {
|
||||
auto record = InlineOriginRecord::parse(line);
|
||||
if (!record) {
|
||||
LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (record->Number >= m_inline_origins->size())
|
||||
m_inline_origins->resize(record->Number + 1);
|
||||
(*m_inline_origins)[record->Number] = record->Name;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
SymbolFileBreakpad::ResolveSymbolContext(const Address &so_addr,
|
||||
SymbolContextItem resolve_scope,
|
||||
SymbolContext &sc) {
|
||||
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
||||
if (!(resolve_scope & (eSymbolContextCompUnit | eSymbolContextLineEntry |
|
||||
eSymbolContextFunction)))
|
||||
eSymbolContextFunction | eSymbolContextBlock)))
|
||||
return 0;
|
||||
|
||||
ParseCUData();
|
||||
@@ -305,11 +384,20 @@ SymbolFileBreakpad::ResolveSymbolContext(const Address &so_addr,
|
||||
result |= eSymbolContextLineEntry;
|
||||
}
|
||||
}
|
||||
if (resolve_scope & eSymbolContextFunction) {
|
||||
|
||||
if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) {
|
||||
FunctionSP func_sp = GetOrCreateFunction(*sc.comp_unit);
|
||||
if (func_sp) {
|
||||
sc.function = func_sp.get();
|
||||
result |= eSymbolContextFunction;
|
||||
if (resolve_scope & eSymbolContextBlock) {
|
||||
Block &block = func_sp->GetBlock(true);
|
||||
sc.block = block.FindInnermostBlockByOffset(
|
||||
so_addr.GetFileAddress() -
|
||||
sc.function->GetAddressRange().GetBaseAddress().GetFileAddress());
|
||||
if (sc.block)
|
||||
result |= eSymbolContextBlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -774,6 +862,10 @@ void SymbolFileBreakpad::ParseLineTableAndSupportFiles(CompileUnit &cu,
|
||||
End(*m_objfile_sp);
|
||||
assert(Record::classify(*It) == Record::Func);
|
||||
for (++It; It != End; ++It) {
|
||||
// Skip INLINE records
|
||||
if (Record::classify(*It) == Record::Inline)
|
||||
continue;
|
||||
|
||||
auto record = LineRecord::parse(*It);
|
||||
if (!record)
|
||||
break;
|
||||
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t ParseBlocksRecursive(Function &func) override { return 0; }
|
||||
size_t ParseBlocksRecursive(Function &func) override;
|
||||
|
||||
void FindGlobalVariables(ConstString name,
|
||||
const CompilerDeclContext &parent_decl_ctx,
|
||||
@@ -223,11 +223,13 @@ private:
|
||||
UnwindPlan::Row &row);
|
||||
lldb::UnwindPlanSP ParseWinUnwindPlan(const Bookmark &bookmark,
|
||||
const RegisterInfoResolver &resolver);
|
||||
void ParseInlineOriginRecords();
|
||||
|
||||
using CompUnitMap = RangeDataVector<lldb::addr_t, lldb::addr_t, CompUnitData>;
|
||||
|
||||
llvm::Optional<std::vector<FileSpec>> m_files;
|
||||
llvm::Optional<CompUnitMap> m_cu_data;
|
||||
llvm::Optional<std::vector<llvm::StringRef>> m_inline_origins;
|
||||
|
||||
using UnwindMap = RangeDataVector<lldb::addr_t, lldb::addr_t, Bookmark>;
|
||||
struct UnwindData {
|
||||
|
||||
@@ -122,6 +122,16 @@ Block *Block::FindBlockByID(user_id_t block_id) {
|
||||
return matching_block;
|
||||
}
|
||||
|
||||
Block *Block::FindInnermostBlockByOffset(const lldb::addr_t offset) {
|
||||
if (!Contains(offset))
|
||||
return nullptr;
|
||||
for (const BlockSP &block_sp : m_children) {
|
||||
if (Block *block = block_sp->FindInnermostBlockByOffset(offset))
|
||||
return block;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
void Block::CalculateSymbolContext(SymbolContext *sc) {
|
||||
if (m_parent_scope)
|
||||
m_parent_scope->CalculateSymbolContext(sc);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
MODULE Linux x86_64 761550E08086333960A9074A9CE2895C0 a.out
|
||||
INFO CODE_ID E05015768680393360A9074A9CE2895C
|
||||
FILE 0 /tmp/a.c
|
||||
FILE 1 /tmp/b.c
|
||||
INLINE_ORIGIN 0 inlined_f1
|
||||
INLINE_ORIGIN 1 inlined_f2
|
||||
FUNC 10 10 0 f1
|
||||
INLINE 0 8 0 0 10 5 17 4
|
||||
10 10 3 0
|
||||
FUNC 20 10 0 f2
|
||||
INLINE 0 3 0 0 23 a
|
||||
INLINE 1 4 1 1 23 5
|
||||
20 10 2 1
|
||||
FUNC 30 10 0 f3
|
||||
INLINE 0 3 999 0 30 5
|
||||
INLINE 0 3 0 999 35 5
|
||||
30 10 3 1
|
||||
56
lldb/test/Shell/SymbolFile/Breakpad/inline-record.test
Normal file
56
lldb/test/Shell/SymbolFile/Breakpad/inline-record.test
Normal file
@@ -0,0 +1,56 @@
|
||||
# RUN: yaml2obj %S/Inputs/basic-elf.yaml -o %T/inline-record.out
|
||||
# RUN: %lldb %T/inline-record.out -o "target symbols add -s inline-record.out %S/Inputs/inline-record.syms" \
|
||||
# RUN: -s %s | FileCheck --match-full-lines %s
|
||||
|
||||
# CHECK-LABEL: (lldb) image lookup -a 0x400010 -v
|
||||
# CHECK: Summary: inline-record.out`f1 [inlined] inlined_f1 at a.c:3
|
||||
# CHECK-NEXT: inline-record.out`f1 at a.c:8
|
||||
# CHECK: Function: id = {0x00000000}, name = "f1", range = [0x0000000000400010-0x0000000000400020)
|
||||
# CHECK-NEXT: Blocks: id = {0x00000000}, range = [0x00400010-0x00400020)
|
||||
# CHECK-NEXT: id = {0x00000010}, ranges = [0x00400010-0x00400015)[0x00400017-0x0040001b), name = "inlined_f1"
|
||||
|
||||
# CHECK-LABEL: (lldb) image lookup -a 0x400016 -v
|
||||
# CHECK: Summary: inline-record.out`f1 + 6 at a.c:3
|
||||
# CHECK-NOT: inline-record.out`f1
|
||||
# CHECK: Function: id = {0x00000000}, name = "f1", range = [0x0000000000400010-0x0000000000400020)
|
||||
# CHECK-NEXT: Blocks: id = {0x00000000}, range = [0x00400010-0x00400020)
|
||||
|
||||
# CHECK-LABEL: (lldb) image lookup -a 0x400023 -v
|
||||
# CHECK: Summary: inline-record.out`f2 + 3 [inlined] inlined_f2 at b.c:2
|
||||
# CHECK-NEXT: inline-record.out`f2 + 3 [inlined] inlined_f1 at b.c:4
|
||||
# CHECK-NEXT: inline-record.out`f2 + 3 at a.c:3
|
||||
# CHECK: Function: id = {0x00000001}, name = "f2", range = [0x0000000000400020-0x0000000000400030)
|
||||
# CHECK-NEXT: Blocks: id = {0x00000001}, range = [0x00400020-0x00400030)
|
||||
# CHECK-NEXT: id = {0x00000043}, range = [0x00400023-0x0040002d), name = "inlined_f1"
|
||||
# CHECK-NEXT: id = {0x00000057}, range = [0x00400023-0x00400028), name = "inlined_f2"
|
||||
|
||||
# CHECK-LABEL: (lldb) image lookup -a 0x400029 -v
|
||||
# CHECK: Summary: inline-record.out`f2 + 9 [inlined] inlined_f1 + 6 at b.c:2
|
||||
# CHECK-NEXT: inline-record.out`f2 + 3 at a.c:3
|
||||
# CHECK: Function: id = {0x00000001}, name = "f2", range = [0x0000000000400020-0x0000000000400030)
|
||||
# CHECK-NEXT: Blocks: id = {0x00000001}, range = [0x00400020-0x00400030)
|
||||
# CHECK-NEXT: id = {0x00000043}, range = [0x00400023-0x0040002d), name = "inlined_f1"
|
||||
|
||||
# CHECK-LABEL: (lldb) image lookup -a 0x400030 -v
|
||||
# CHECK: Summary: inline-record.out`f3 [inlined] inlined_f1 at b.c:3
|
||||
# CHECK-NEXT: inline-record.out`f3 at 3
|
||||
# CHECK: Function: id = {0x00000002}, name = "f3", range = [0x0000000000400030-0x0000000000400040)
|
||||
# CHECK-NEXT: Blocks: id = {0x00000002}, range = [0x00400030-0x00400040)
|
||||
# CHECK-NEXT: id = {0x00000085}, range = [0x00400030-0x00400035), name = "inlined_f1"
|
||||
|
||||
|
||||
# CHECK-LABEL: (lldb) image lookup -a 0x400035 -v
|
||||
# CHECK: Summary: inline-record.out`f3 + 5 [inlined] at b.c:3
|
||||
# CHECK-NEXT: inline-record.out`f3 + 5 at a.c:3
|
||||
# CHECK: Function: id = {0x00000002}, name = "f3", range = [0x0000000000400030-0x0000000000400040)
|
||||
# CHECK-NEXT: Blocks: id = {0x00000002}, range = [0x00400030-0x00400040)
|
||||
# CHECK-NEXT: id = {0x0000009b}, range = [0x00400035-0x0040003a)
|
||||
|
||||
image lookup -a 0x400010 -v
|
||||
image lookup -a 0x400016 -v
|
||||
image lookup -a 0x400023 -v
|
||||
image lookup -a 0x400029 -v
|
||||
# Folling addresses are inside INLINE records that have file index or origin index out of range.
|
||||
image lookup -a 0x400030 -v
|
||||
image lookup -a 0x400035 -v
|
||||
exit
|
||||
@@ -18,6 +18,8 @@ TEST(Record, classify) {
|
||||
EXPECT_EQ(Record::Info, Record::classify("INFO"));
|
||||
EXPECT_EQ(Record::File, Record::classify("FILE"));
|
||||
EXPECT_EQ(Record::Func, Record::classify("FUNC"));
|
||||
EXPECT_EQ(Record::Inline, Record::classify("INLINE"));
|
||||
EXPECT_EQ(Record::InlineOrigin, Record::classify("INLINE_ORIGIN"));
|
||||
EXPECT_EQ(Record::Public, Record::classify("PUBLIC"));
|
||||
EXPECT_EQ(Record::StackCFI, Record::classify("STACK CFI"));
|
||||
EXPECT_EQ(Record::StackWin, Record::classify("STACK WIN"));
|
||||
@@ -76,6 +78,27 @@ TEST(FuncRecord, parse) {
|
||||
EXPECT_EQ(llvm::None, FuncRecord::parse("FUNC"));
|
||||
}
|
||||
|
||||
TEST(InlineOriginRecord, parse) {
|
||||
EXPECT_EQ(InlineOriginRecord(47, "foo"),
|
||||
InlineOriginRecord::parse("INLINE_ORIGIN 47 foo"));
|
||||
EXPECT_EQ(llvm::None, InlineOriginRecord::parse("INLINE_ORIGIN 47"));
|
||||
EXPECT_EQ(llvm::None, InlineOriginRecord::parse("INLINE_ORIGIN"));
|
||||
EXPECT_EQ(llvm::None, InlineOriginRecord::parse(""));
|
||||
}
|
||||
|
||||
TEST(InlineRecord, parse) {
|
||||
InlineRecord record1 = InlineRecord(0, 1, 2, 3);
|
||||
record1.Ranges.emplace_back(4, 5);
|
||||
EXPECT_EQ(record1, InlineRecord::parse("INLINE 0 1 2 3 4 5"));
|
||||
record1.Ranges.emplace_back(6, 7);
|
||||
EXPECT_EQ(record1, InlineRecord::parse("INLINE 0 1 2 3 4 5 6 7"));
|
||||
EXPECT_EQ(llvm::None, InlineRecord::parse("INLINE 0 1 2 3"));
|
||||
EXPECT_EQ(llvm::None, InlineRecord::parse("INLINE 0 1 2 3 4 5 6"));
|
||||
EXPECT_EQ(llvm::None, InlineRecord::parse("INLNIE 0"));
|
||||
EXPECT_EQ(llvm::None, InlineRecord::parse(""));
|
||||
EXPECT_EQ(llvm::None, InlineRecord::parse("FUNC"));
|
||||
}
|
||||
|
||||
TEST(LineRecord, parse) {
|
||||
EXPECT_EQ(LineRecord(0x47, 0x74, 47, 74), LineRecord::parse("47 74 47 74"));
|
||||
EXPECT_EQ(llvm::None, LineRecord::parse("47 74 47"));
|
||||
|
||||
Reference in New Issue
Block a user