This source file includes following definitions.
- get_program_name
- get_program_name
- form
- form
- has_children
- addr
- origin_loc
- spec_loc
- type
- working
- count_trailing_zeros
- calibrate_pc_offset
- type_name_match
- find_global_variable
- get_global_variable_name
- register_heap_object
- deregister_heap_object
- get_heap_member_name
- get_stack_variable_name
- get_source_location
- dump
- load_and_parse_object_file
- parse_object_file
- parse_debug_ranges
- parse_debug_abbrev
- parse_debug_info
- parse_debug_line
- find_containing_function
- get_sleb128
- get_uleb128
- get_variable_name
- get_source_location
- register_heap_object
- deregister_heap_object
- saves_frame_pointer
- test_compilation_unit
- get_variable_name
- get_source_location
- register_heap_object
- deregister_heap_object
- test_compilation_unit
#include "Introspection.h"
#ifdef WITH_INTROSPECTION
#include "Debug.h"
#include "LLVM_Headers.h"
#include "Error.h"
#include <string>
#include <iostream>
#include <sstream>
#include <stdio.h>
#include <execinfo.h>
using std::vector;
using std::pair;
using std::map;
namespace Halide {
namespace Internal {
namespace Introspection {
#ifdef __APPLE__
extern "C" void _NSGetExecutablePath(char *, int32_t *);
void get_program_name(char *name, int32_t size) {
_NSGetExecutablePath(name, &size);
}
#else
extern "C" char *program_invocation_name;
void get_program_name(char *name, int32_t size) {
strncpy(name, program_invocation_name, size);
}
#endif
class DebugSections {
bool calibrated;
struct FieldFormat {
uint64_t name, form;
FieldFormat() : name(0), form(0) {}
FieldFormat(uint64_t n, uint64_t f) : name(n), form(f) {}
};
struct EntryFormat {
uint64_t code, tag;
bool has_children;
EntryFormat() : code(0), tag(0), has_children(false) {}
vector<FieldFormat> fields;
};
vector<EntryFormat> entry_formats;
struct LiveRange {
uint64_t pc_begin, pc_end;
};
struct TypeInfo;
struct GlobalVariable {
std::string name;
TypeInfo *type;
uint64_t type_def_loc;
uint64_t def_loc, spec_loc;
uint64_t addr;
GlobalVariable() : name(""),
type(nullptr),
type_def_loc(0),
def_loc(0),
spec_loc(0),
addr(0) {}
bool operator<(const GlobalVariable &other) const {
return addr < other.addr;
}
};
vector<GlobalVariable> global_variables;
struct HeapObject {
uint64_t addr;
TypeInfo *type;
struct Member {
uint64_t addr;
std::string name;
TypeInfo *type;
bool operator<(const Member &other) const {
return addr < other.addr;
}
};
vector<Member> members;
};
map<uint64_t, HeapObject> heap_objects;
struct LocalVariable {
std::string name;
TypeInfo *type;
int stack_offset;
uint64_t type_def_loc;
uint64_t def_loc, origin_loc;
vector<LiveRange> live_ranges;
LocalVariable() : name(""),
type(nullptr),
stack_offset(0),
type_def_loc(0),
def_loc(0),
origin_loc(0) {}
};
struct FunctionInfo {
std::string name;
uint64_t pc_begin, pc_end;
vector<LocalVariable> variables;
uint64_t def_loc, spec_loc;
enum {Unknown = 0, GCC, ClangFP, ClangNoFP} frame_base;
FunctionInfo() : name(""), pc_begin(0), pc_end(0), def_loc(0), spec_loc(0) {}
bool operator<(const FunctionInfo &other) const {
return pc_begin < other.pc_begin;
}
};
vector<FunctionInfo> functions;
vector<std::string> source_files;
struct LineInfo {
uint64_t pc;
uint32_t line;
uint32_t file;
bool operator<(const LineInfo &other) const {
return pc < other.pc;
}
};
vector<LineInfo> source_lines;
struct TypeInfo {
std::string name;
uint64_t size;
uint64_t def_loc;
vector<LocalVariable> members;
enum {Primitive, Class, Struct, Pointer, Typedef, Const, Reference, Array} type;
TypeInfo() : size(0), def_loc(0), type(Primitive) {}
};
vector<TypeInfo> types;
public:
bool working;
DebugSections(std::string binary) : calibrated(false), working(false) {
#ifdef __APPLE__
size_t last_slash = binary.rfind('/');
if (last_slash == std::string::npos ||
last_slash >= binary.size() - 1) {
last_slash = 0;
} else {
last_slash++;
}
std::string file_only = binary.substr(last_slash, binary.size() - last_slash);
binary += ".dSYM/Contents/Resources/DWARF/" + file_only;
#endif
debug(5) << "Loading " << binary << "\n";
load_and_parse_object_file(binary);
}
int count_trailing_zeros(int64_t x) {
for (int i = 0; i < 64; i++) {
if (x & (1 << i)) {
return i;
}
}
return 64;
}
void calibrate_pc_offset(void (*fn)()) {
bool found = false;
uint64_t pc_real = (uint64_t)fn;
int64_t pc_adjust = 0;
for (size_t i = 0; i < functions.size(); i++) {
if (functions[i].name == "HalideIntrospectionCanary::offset_marker" &&
functions[i].pc_begin) {
uint64_t pc_debug = functions[i].pc_begin;
if (calibrated) {
if (pc_debug == pc_real) {
return;
}
} else {
int64_t pc_adj = pc_real - pc_debug;
if (pc_adj & (4095)) {
continue;
}
if (!found ||
count_trailing_zeros(pc_adj) > count_trailing_zeros(pc_adjust)) {
pc_adjust = pc_adj;
found = true;
}
}
}
}
if (!found) {
if (!calibrated) {
debug(2) << "Failed to find HalideIntrospectionCanary::offset_marker\n";
} else {
debug(2) << "Failed to find HalideIntrospectionCanary::offset_marker at the expected location\n";
}
working = false;
return;
}
debug(5) << "Program counter adjustment between debug info and actual code: " << pc_adjust << "\n";
for (size_t i = 0; i < functions.size(); i++) {
FunctionInfo &f = functions[i];
f.pc_begin += pc_adjust;
f.pc_end += pc_adjust;
for (size_t j = 0; j < f.variables.size(); j++) {
LocalVariable &v = f.variables[j];
for (size_t k = 0; k < v.live_ranges.size(); k++) {
v.live_ranges[k].pc_begin += pc_adjust;
v.live_ranges[k].pc_end += pc_adjust;
}
}
}
for (size_t i = 0; i < source_lines.size(); i++) {
source_lines[i].pc += pc_adjust;
}
for (size_t i = 0; i < global_variables.size(); i++) {
global_variables[i].addr += pc_adjust;
}
calibrated = true;
}
bool type_name_match(const std::string &actual_type, const std::string &query_type) {
if (query_type.empty()) {
return true;
}
if (query_type == actual_type) {
return true;
}
if (query_type[query_type.size()-1] == '?' &&
starts_with(actual_type, query_type.substr(0, query_type.size()-1))) {
return true;
}
return false;
}
int find_global_variable(const void *global_pointer) {
if (global_variables.empty()) {
debug(5) << "Considering possible global at " << global_pointer << " but global_variables is empty\n";
return -1;
}
debug(5) << "Considering possible global at " << global_pointer << "\n";
debug(5) << "Known globals range from " << std::hex << global_variables.front().addr << " to " << global_variables.back().addr << std::dec << "\n";
uint64_t address = (uint64_t)(global_pointer);
size_t hi = global_variables.size();
size_t lo = 0;
while (hi > lo + 1) {
size_t mid = (hi + lo)/2;
uint64_t addr_mid = global_variables[mid].addr;
if (address < addr_mid) {
hi = mid;
} else {
lo = mid;
}
}
if (lo >= global_variables.size()) {
return -1;
}
size_t idx = lo;
while (idx > 0 && global_variables[idx-1].addr == global_variables[lo].addr) {
idx--;
}
uint64_t end_ptr = global_variables[idx].addr;
TypeInfo *t = global_variables[idx].type;
if (t == nullptr) {
return -1;
}
uint64_t size = t->size;
while (t->type == TypeInfo::Array) {
t = t->members[0].type;
size *= t->size;
}
end_ptr += size;
if (address < global_variables[idx].addr ||
address >= end_ptr) {
return -1;
}
return (int)idx;
}
std::string get_global_variable_name(const void *global_pointer, const std::string &type_name = "") {
int idx = find_global_variable(global_pointer);
if (idx < 0) {
return "";
}
uint64_t address = (uint64_t)global_pointer;
for (; (size_t)idx < global_variables.size() && global_variables[idx].addr <= address; idx++) {
GlobalVariable &v = global_variables[idx];
TypeInfo *elem_type = nullptr;
if (v.type && v.type->type == TypeInfo::Array && v.type->size) {
elem_type = v.type->members[0].type;
}
debug(5) << "Closest global is " << v.name << " at " << std::hex << v.addr << std::dec;
if (v.type) {
debug(5) << " with type " << v.type->name << "\n";
} else {
debug(5) << "\n";
}
if (v.addr == address &&
(type_name.empty() ||
(v.type && type_name_match(v.type->name, type_name)))) {
return v.name;
} else if (elem_type &&
(type_name.empty() ||
(elem_type && type_name_match(elem_type->name, type_name)))) {
int64_t array_size_bytes = v.type->size * elem_type->size;
int64_t pos_bytes = address - v.addr;
if (pos_bytes >= 0 &&
pos_bytes < array_size_bytes &&
pos_bytes % elem_type->size == 0) {
std::ostringstream oss;
oss << v.name << '[' << (pos_bytes / elem_type->size) << ']';
debug(5) << "Successful match to array element\n";
return oss.str();
}
}
}
return "";
}
void register_heap_object(const void *obj, size_t size, const void *helper) {
int idx = find_global_variable(helper);
if (idx == -1) {
debug(5) << "Could not find helper object: " << helper << "\n";
return;
}
const GlobalVariable &ptr = global_variables[idx];
debug(5) << "helper object is " << ptr.name << " at " << std::hex << ptr.addr << std::dec;
if (ptr.type) {
debug(5) << " with type " << ptr.type->name << "\n";
} else {
debug(5) << " with unknown type!\n";
return;
}
internal_assert(ptr.type->type == TypeInfo::Pointer)
<< "The type of the helper object was supposed to be a pointer\n";
internal_assert(ptr.type->members.size() == 1);
TypeInfo *object_type = ptr.type->members[0].type;
internal_assert(object_type);
debug(5) << "The object has type: " << object_type->name << "\n";
internal_assert(size == object_type->size);
HeapObject heap_object;
heap_object.type = object_type;
heap_object.addr = (uint64_t)obj;
for (size_t i = 0; i < object_type->members.size(); i++) {
const LocalVariable &member_spec = object_type->members[i];
HeapObject::Member member;
member.name = member_spec.name;
member.type = member_spec.type;
member.addr = heap_object.addr + member_spec.stack_offset;
if (member.type) {
heap_object.members.push_back(member);
debug(5) << member.name << " - " << (int)(member.type->type) << "\n";
}
}
for (size_t i = 0; i < heap_object.members.size(); i++) {
HeapObject::Member parent = heap_object.members[i];
if (parent.type->type == TypeInfo::Pointer ||
parent.type->type == TypeInfo::Reference) continue;
for (size_t j = 0; j < parent.type->members.size(); j++) {
const LocalVariable &member_spec = parent.type->members[j];
TypeInfo *member_type = member_spec.type;
HeapObject::Member child;
child.type = member_type;
if (parent.type->type == TypeInfo::Typedef ||
parent.type->type == TypeInfo::Const) {
child.name = parent.name;
} else if (parent.type->type == TypeInfo::Array) {
child.name = "";
} else {
child.name = member_spec.name;
}
child.addr = parent.addr + member_spec.stack_offset;
if (child.type) {
debug(5) << child.name << " - " << (int)(child.type->type) << "\n";
heap_object.members.push_back(child);
}
}
}
std::stable_sort(heap_object.members.begin(), heap_object.members.end());
debug(5) << "Children of heap object of type " << object_type->name << " at " << obj << ":\n";
for (size_t i = 0; i < heap_object.members.size(); i++) {
const HeapObject::Member &mem = heap_object.members[i];
debug(5) << std::hex << mem.addr << std::dec << ": " << mem.type->name << " " << mem.name << "\n";
}
heap_objects[heap_object.addr] = heap_object;
}
void deregister_heap_object(const void *obj, size_t size) {
heap_objects.erase((uint64_t)obj);
}
std::string get_heap_member_name(const void *ptr, const std::string &type_name = "") {
debug(5) << "Getting heap member name of " << ptr << "\n";
if (heap_objects.empty()) {
debug(5) << "No registered heap objects\n";
return "";
}
uint64_t addr = (uint64_t)ptr;
std::map<uint64_t, HeapObject>::iterator it = heap_objects.upper_bound(addr);
if (it == heap_objects.begin()) {
debug(5) << "No heap objects less than this address\n";
return "";
}
it--;
const HeapObject &obj = it->second;
uint64_t object_start = it->first;
uint64_t object_end = object_start + obj.type->size;
if (addr < object_start || addr >= object_end) {
debug(5) << "Not contained in any heap object\n";
return "";
}
std::ostringstream name;
for (size_t i = 0; i < obj.members.size(); i++) {
TypeInfo *t = obj.members[i].type;
if (!t) continue;
debug(5) << "Comparing to member " << obj.members[i].name
<< " at address " << std::hex << obj.members[i].addr << std::dec
<< " with type " << t->name
<< " and type type " << (int)t->type << "\n";
if (obj.members[i].addr == addr &&
type_name_match(t->name, type_name)) {
name << obj.members[i].name;
return name.str();
}
if (t->type == TypeInfo::Array) {
TypeInfo *elem_type = t->members[0].type;
uint64_t array_start_addr = obj.members[i].addr;
uint64_t array_end_addr = array_start_addr + t->size * elem_type->size;
debug(5) << "Array runs from " << std::hex << array_start_addr << " to " << array_end_addr << "\n";
if (elem_type && addr >= array_start_addr && addr < array_end_addr) {
uint64_t containing_elem = (addr - array_start_addr) / elem_type->size;
addr -= containing_elem * elem_type->size;
debug(5) << "Query belongs to this array. Adjusting query address backwards to "
<< std::hex << addr << std::dec << "\n";
name << obj.members[i].name << '[' << containing_elem << ']';
}
} else if (t->type == TypeInfo::Struct ||
t->type == TypeInfo::Class ||
t->type == TypeInfo::Primitive) {
uint64_t struct_start_addr = obj.members[i].addr;
uint64_t struct_end_addr = struct_start_addr + t->size;
debug(5) << "Struct runs from " << std::hex << struct_start_addr << " to " << struct_end_addr << "\n";
if (addr >= struct_start_addr && addr < struct_end_addr) {
name << obj.members[i].name << '.';
}
}
}
debug(5) << "Didn't seem to be any of the members of this heap object\n";
return "";
}
std::string get_stack_variable_name(const void *stack_pointer, const std::string &type_name = "") {
int marker = 0;
uint64_t marker_addr = (uint64_t)▮
uint64_t top_of_stack;
if (marker_addr >> 63) {
top_of_stack = (uint64_t)(-1);
} else {
top_of_stack = ((marker_addr >> 30) + 1) << 30;
}
if ((uint64_t)stack_pointer > top_of_stack ||
(uint64_t)stack_pointer < marker_addr) {
return "";
}
struct frame_info {
frame_info *frame_pointer;
void *return_address;
};
frame_info *fp = (frame_info *)__builtin_frame_address(0);
frame_info *next_fp = nullptr;
debug(5) << "Walking up the stack\n";
while (fp < stack_pointer) {
debug(5) << "frame pointer: " << (void *)(fp->frame_pointer)
<< " return address: " << fp->return_address << "\n";
next_fp = fp;
if (fp->frame_pointer < fp) {
debug(5) << "Bailing out because fp decreased\n";
return "";
}
fp = fp->frame_pointer;
if (fp < (void *)&marker) {
debug(5) << "Bailing out because we're below the marker\n";
return "";
}
}
if (!next_fp) {
debug(5) << "Bailing out because we didn't even walk up one frame\n";
return "";
}
uint64_t pc = (uint64_t)(next_fp->return_address) - 5;
FunctionInfo *func = find_containing_function(next_fp->return_address);
if (!func) {
debug(5) << "Bailing out because we couldn't find the containing function\n";
return "";
}
int offset_above = (int)((int64_t)(stack_pointer) - (int64_t)(fp));
int offset_below = (int)((int64_t)(stack_pointer) - (int64_t)(next_fp));
const int addr_size = sizeof(void *);
int offset;
if (func->frame_base == FunctionInfo::GCC) {
offset = offset_above - 2*addr_size;
} else if (func->frame_base == FunctionInfo::ClangFP) {
offset = offset_above;
} else if (func->frame_base == FunctionInfo::ClangNoFP) {
offset = offset_below - 2*addr_size;
} else {
debug(5) << "Bailing out because containing function used an unknown mechanism for specifying stack offsets\n";
return "";
}
debug(5) << "Searching for var at offset " << offset << "\n";
for (size_t j = 0; j < func->variables.size(); j++) {
const LocalVariable &var = func->variables[j];
debug(5) << "Var " << var.name << " is at offset " << var.stack_offset << "\n";
if (var.live_ranges.size()) {
bool in_live_range = false;
for (size_t i = 0; i < var.live_ranges.size(); i++) {
if (pc >= var.live_ranges[i].pc_begin &&
pc < var.live_ranges[i].pc_end) {
in_live_range = true;
break;
}
}
if (!in_live_range) {
debug(5) << "Skipping var because we're not in any of its live ranges\n";
continue;
}
}
TypeInfo *type = var.type;
TypeInfo *elem_type = nullptr;
if (type && type->type == TypeInfo::Array && type->size) {
elem_type = type->members[0].type;
}
if (offset == var.stack_offset && var.type) {
debug(5) << "Considering match: " << var.type->name << ", " << var.name << "\n";
}
if (offset == var.stack_offset &&
(type_name.empty() ||
(type && type_name_match(type->name, type_name)))) {
debug(5) << "Successful match to scalar var\n";
return var.name;
} else if (elem_type &&
(type_name.empty() ||
(elem_type &&
type_name_match(elem_type->name, type_name)))) {
int64_t array_size_bytes = type->size * elem_type->size;
int64_t pos_bytes = offset - var.stack_offset;
if (pos_bytes >= 0 &&
pos_bytes < array_size_bytes &&
pos_bytes % elem_type->size == 0) {
std::ostringstream oss;
oss << var.name << '[' << (pos_bytes / elem_type->size) << ']';
debug(5) << "Successful match to array element\n";
return oss.str();
}
}
}
debug(5) << "Failed to find variable at the matching offset with the given type\n";
return "";
}
std::string get_source_location() {
debug(5) << "Finding source location\n";
if (!source_lines.size()) {
debug(5) << "Bailing out because we have no source lines\n";
return "";
}
const int max_stack_frames = 256;
vector<void *> trace(max_stack_frames);
int trace_size = backtrace(&trace[0], (int)(trace.size()));
for (int frame = 2; frame < trace_size; frame++) {
uint64_t address = (uint64_t)trace[frame];
debug(5) << "Considering address " << ((void *)address) << "\n";
const uint8_t *inst_ptr = (const uint8_t *)address;
if (inst_ptr[-5] == 0xe8) {
address -= 5;
} else if (inst_ptr[-2] == 0xff) {
address -= 2;
} else {
debug(5) << "Skipping function because there's no callq before " << (const void *)(inst_ptr) << "\n";
continue;
}
FunctionInfo *f = find_containing_function((void *)address);
if (!f) {
debug(5) << "Skipping function because we have no debug info for it\n";
continue;
}
debug(5) << "Containing function is " << f->name << "\n";
if (f->name.size() > 8 &&
f->name.substr(0, 8) == "Halide::") {
debug(5) << "Skipping function because it's in the Halide namespace\n";
continue;
}
size_t hi = source_lines.size();
size_t lo = 0;
while (hi > lo + 1) {
size_t mid = (hi + lo)/2;
uint64_t pc_mid = source_lines[mid].pc;
if (address < pc_mid) {
hi = mid;
} else {
lo = mid;
}
}
const std::string &file = source_files[source_lines[lo].file];
int line = source_lines[lo].line;
std::ostringstream oss;
oss << file << ":" << line;
debug(5) << "Source location is " << oss.str() << "\n";
return oss.str();
}
debug(5) << "Bailing out because we reached the end of the backtrace\n";
return "";
}
void dump() {
for (size_t i = 0; i < types.size(); i++) {
printf("Class %s of size %llu @ %llx: \n",
types[i].name.c_str(),
(unsigned long long)(types[i].size),
(unsigned long long)(types[i].def_loc));
for (size_t j = 0; j < types[i].members.size(); j++) {
TypeInfo *c = types[i].members[j].type;
const char *type_name = "(unknown)";
if (c) {
type_name = c->name.c_str();
}
printf(" Member %s at %d of type %s @ %llx\n",
types[i].members[j].name.c_str(),
types[i].members[j].stack_offset,
type_name,
(long long unsigned)types[i].members[j].type_def_loc);
}
}
for (size_t i = 0; i < functions.size(); i++) {
const FunctionInfo &f = functions[i];
printf("Function %s at %llx - %llx (frame_base %d): \n",
f.name.c_str(),
(unsigned long long)(f.pc_begin),
(unsigned long long)(f.pc_end),
(int)f.frame_base);
for (size_t j = 0; j < f.variables.size(); j++) {
const LocalVariable &v = f.variables[j];
TypeInfo *c = v.type;
const char *type_name = "(unknown)";
if (c) {
type_name = c->name.c_str();
}
printf(" Variable %s at %d of type %s @ %llx\n",
v.name.c_str(),
v.stack_offset,
type_name,
(long long unsigned)v.type_def_loc);
for (size_t k = 0; k < v.live_ranges.size(); k++) {
printf(" Live range: %llx - %llx\n",
(unsigned long long)v.live_ranges[k].pc_begin,
(unsigned long long)v.live_ranges[k].pc_end);
}
}
}
for (size_t i = 0; i < source_lines.size(); i++) {
printf("%p -> %s:%d\n",
(void *)(source_lines[i].pc),
source_files[source_lines[i].file].c_str(),
source_lines[i].line);
}
for (size_t i = 0; i < global_variables.size(); i++) {
const GlobalVariable &v = global_variables[i];
TypeInfo *c = v.type;
const char *type_name = "(unknown)";
if (c) {
type_name = c->name.c_str();
}
printf(" Global variable %s at %llx of type %s\n",
v.name.c_str(),
(long long unsigned)v.addr,
type_name);
}
}
private:
void load_and_parse_object_file(const std::string &binary) {
llvm::object::ObjectFile *obj = nullptr;
#if LLVM_VERSION >= 39
llvm::Expected<llvm::object::OwningBinary<llvm::object::ObjectFile>> maybe_obj =
llvm::object::ObjectFile::createObjectFile(binary);
if (!maybe_obj) {
consumeError(maybe_obj.takeError());
debug(1) << "Failed to load binary:" << binary << "\n";
return;
}
obj = maybe_obj.get().getBinary();
#else
llvm::ErrorOr<llvm::object::OwningBinary<llvm::object::ObjectFile>> maybe_obj =
llvm::object::ObjectFile::createObjectFile(binary);
if (!maybe_obj) {
debug(1) << "Failed to load binary:" << binary << "\n";
return;
}
obj = maybe_obj.get().getBinary();
#endif
if (obj) {
working = true;
parse_object_file(obj);
} else {
debug(1) << "Could not load object file: " << binary << "\n";
working = false;
}
}
void parse_object_file(llvm::object::ObjectFile *obj) {
llvm::StringRef debug_info, debug_abbrev, debug_str, debug_line, debug_ranges;
#ifdef __APPLE__
std::string prefix = "__";
#else
std::string prefix = ".";
#endif
for (llvm::object::section_iterator iter = obj->section_begin();
iter != obj->section_end(); ++iter) {
llvm::StringRef name;
iter->getName(name);
debug(2) << "Section: " << name.str() << "\n";
if (name == prefix + "debug_info") {
iter->getContents(debug_info);
} else if (name == prefix + "debug_abbrev") {
iter->getContents(debug_abbrev);
} else if (name == prefix + "debug_str") {
iter->getContents(debug_str);
} else if (name == prefix + "debug_line") {
iter->getContents(debug_line);
} else if (name == prefix + "debug_ranges") {
iter->getContents(debug_ranges);
}
}
if (debug_info.empty() ||
debug_abbrev.empty() ||
debug_str.empty() ||
debug_line.empty() ||
debug_ranges.empty()) {
debug(2) << "Debugging sections not found\n";
working = false;
return;
}
{
llvm::DataExtractor extractor(debug_info, true, obj->getBytesInAddress());
llvm::DataExtractor debug_abbrev_extractor(debug_abbrev, true, obj->getBytesInAddress());
parse_debug_info(extractor, debug_abbrev_extractor, debug_str, debug_ranges);
}
{
llvm::DataExtractor e(debug_line, true, obj->getBytesInAddress());
parse_debug_line(e);
}
}
void parse_debug_ranges(const llvm::DataExtractor &e) {
}
void parse_debug_abbrev(const llvm::DataExtractor &e, uint32_t off = 0) {
entry_formats.clear();
while (1) {
EntryFormat fmt;
fmt.code = e.getULEB128(&off);
if (!fmt.code) break;
fmt.tag = e.getULEB128(&off);
fmt.has_children = (e.getU8(&off) != 0);
while (1) {
uint64_t name = e.getULEB128(&off);
uint64_t form = e.getULEB128(&off);
if (!name && !form) break;
FieldFormat f_fmt(name, form);
fmt.fields.push_back(f_fmt);
}
entry_formats.push_back(fmt);
}
}
void parse_debug_info(const llvm::DataExtractor &e,
const llvm::DataExtractor &debug_abbrev,
llvm::StringRef debug_str,
llvm::StringRef debug_ranges) {
uint32_t off = 0;
llvm::StringRef debug_info = e.getData();
const int no_location = 0x80000000;
while (1) {
uint64_t start_of_unit_header = off;
bool dwarf_64;
uint64_t unit_length = e.getU32(&off);
if (unit_length == 0xffffffff) {
dwarf_64 = true;
unit_length = e.getU64(&off);
} else {
dwarf_64 = false;
}
if (!unit_length) {
break;
}
uint64_t start_of_unit = off;
uint16_t dwarf_version = e.getU16(&off);
uint64_t debug_abbrev_offset = 0;
if (dwarf_64) {
debug_abbrev_offset = e.getU64(&off);
} else {
debug_abbrev_offset = e.getU32(&off);
}
parse_debug_abbrev(debug_abbrev, debug_abbrev_offset);
uint8_t address_size = e.getU8(&off);
vector<pair<FunctionInfo, int>> func_stack;
vector<pair<TypeInfo, int>> type_stack;
vector<pair<std::string, int>> namespace_stack;
vector<pair<vector<LiveRange>, int>> live_range_stack;
int stack_depth = 0;
uint64_t compile_unit_base_pc = 0;
const unsigned tag_array_type = 0x01;
const unsigned tag_class_type = 0x02;
const unsigned tag_lexical_block = 0x0b;
const unsigned tag_member = 0x0d;
const unsigned tag_pointer_type = 0x0f;
const unsigned tag_reference_type = 0x10;
const unsigned tag_compile_unit = 0x11;
const unsigned tag_structure_type = 0x13;
const unsigned tag_typedef = 0x16;
const unsigned tag_inlined_subroutine = 0x1d;
const unsigned tag_subrange_type = 0x21;
const unsigned tag_base_type = 0x24;
const unsigned tag_const_type = 0x26;
const unsigned tag_function = 0x2e;
const unsigned tag_variable = 0x34;
const unsigned tag_namespace = 0x39;
const unsigned attr_location = 0x02;
const unsigned attr_name = 0x03;
const unsigned attr_byte_size = 0x0b;
const unsigned attr_low_pc = 0x11;
const unsigned attr_high_pc = 0x12;
const unsigned attr_upper_bound = 0x2f;
const unsigned attr_abstract_origin = 0x31;
const unsigned attr_count = 0x37;
const unsigned attr_data_member_location = 0x38;
const unsigned attr_frame_base = 0x40;
const unsigned attr_specification = 0x47;
const unsigned attr_type = 0x49;
const unsigned attr_ranges = 0x55;
while (off - start_of_unit < unit_length) {
uint64_t location = off;
uint64_t abbrev_code = e.getULEB128(&off);
if (abbrev_code == 0) {
if (func_stack.size() &&
stack_depth == func_stack.back().second) {
const FunctionInfo &f = func_stack.back().first;
functions.push_back(f);
func_stack.pop_back();
}
if (type_stack.size() &&
stack_depth == type_stack.back().second) {
const TypeInfo &c = type_stack.back().first;
types.push_back(c);
type_stack.pop_back();
}
if (namespace_stack.size() &&
stack_depth == namespace_stack.back().second) {
namespace_stack.pop_back();
}
if (live_range_stack.size() &&
stack_depth == live_range_stack.back().second) {
live_range_stack.pop_back();
}
stack_depth--;
continue;
}
assert(abbrev_code <= entry_formats.size());
const EntryFormat &fmt = entry_formats[abbrev_code-1];
assert(fmt.code == abbrev_code);
LocalVariable var;
GlobalVariable gvar;
FunctionInfo func;
TypeInfo type_info;
vector<LiveRange> live_ranges;
type_info.def_loc = location;
func.def_loc = location;
var.def_loc = location;
gvar.def_loc = location;
std::string namespace_name;
std::string containing_namespace;
if (type_stack.size()) {
containing_namespace = type_stack.back().first.name + "::";
} else {
for (size_t i = 0; i < namespace_stack.size(); i++) {
containing_namespace += namespace_stack[i].first + "::";
}
}
var.stack_offset = no_location;
if (fmt.has_children) {
stack_depth++;
}
for (size_t i = 0; i < fmt.fields.size(); i++) {
unsigned attr = fmt.fields[i].name;
uint64_t val = 0;
const uint8_t *payload = nullptr;
switch(fmt.fields[i].form) {
case 1:
{
if (address_size == 4) {
val = e.getU32(&off);
} else {
val = e.getU64(&off);
}
break;
}
case 2:
{
assert(false && "What's form 2?");
break;
}
case 3:
{
val = e.getU16(&off);
payload = (const uint8_t *)(debug_info.data() + off);
off += val;
break;
}
case 4:
{
val = e.getU32(&off);
payload = (const uint8_t *)(debug_info.data() + off);
off += val;
break;
}
case 5:
{
val = e.getU16(&off);
break;
}
case 6:
{
val = e.getU32(&off);
break;
}
case 7:
{
val = e.getU64(&off);
break;
}
case 8:
{
val = 0;
payload = (const uint8_t *)(debug_info.data() + off);
while (e.getU8(&off));
break;
}
case 9:
{
val = e.getULEB128(&off);
payload = (const uint8_t *)(debug_info.data() + off);
off += val;
break;
}
case 10:
{
val = e.getU8(&off);
payload = (const uint8_t *)(debug_info.data() + off);
off += val;
break;
}
case 11:
{
val = e.getU8(&off);
break;
}
case 12:
{
val = e.getU8(&off);
break;
}
case 13:
{
val = (uint64_t)e.getSLEB128(&off);
break;
}
case 14:
{
uint64_t offset;
if (dwarf_64) {
offset = e.getU64(&off);
} else {
offset = e.getU32(&off);
}
val = 0;
payload = (const uint8_t *)(debug_str.data() + offset);
break;
}
case 15:
{
val = e.getULEB128(&off);
break;
}
case 16:
{
if ((dwarf_version <= 2 && address_size == 8) ||
(dwarf_version > 2 && dwarf_64)) {
val = e.getU64(&off);
} else {
val = e.getU32(&off);
}
break;
}
case 17:
{
val = e.getU8(&off) + start_of_unit_header;
break;
}
case 18:
{
val = e.getU16(&off) + start_of_unit_header;
break;
}
case 19:
{
val = e.getU32(&off) + start_of_unit_header;
break;
}
case 20:
{
val = e.getU64(&off) + start_of_unit_header;
break;
}
case 21:
{
val = e.getULEB128(&off) + start_of_unit_header;
break;
}
case 22:
{
assert(false && "Can't handle indirect form");
break;
}
case 23:
{
if (dwarf_64) {
val = e.getU64(&off);
} else {
val = e.getU32(&off);
}
break;
}
case 24:
{
val = e.getULEB128(&off);
payload = (const uint8_t *)(debug_info.data() + off);
off += val;
break;
}
case 25:
{
val = 0;
break;
}
case 32:
{
val = e.getU64(&off);
break;
}
default:
assert(false && "Unknown form");
break;
}
if (fmt.tag == tag_function) {
if (attr == attr_name) {
func.name = containing_namespace + std::string((const char *)payload);
} else if (attr == attr_low_pc) {
func.pc_begin = val;
} else if (attr == attr_high_pc) {
if (fmt.fields[i].form == 0x1) {
func.pc_end = val;
} else {
func.pc_end = func.pc_begin + val;
}
} else if (attr == attr_frame_base) {
if (val == 1 && payload && payload[0] == 0x9c) {
func.frame_base = FunctionInfo::GCC;
} else if (val == 1 && payload && payload[0] == 0x56 && sizeof(void *) == 8) {
func.frame_base = FunctionInfo::ClangFP;
} else if (val == 1 && payload && payload[0] == 0x55 && sizeof(void *) == 4) {
func.frame_base = FunctionInfo::ClangFP;
} else if (val == 1 && payload && payload[0] == 0x57 && sizeof(void *) == 8) {
func.frame_base = FunctionInfo::ClangNoFP;
} else if (val == 1 && payload && payload[0] == 0x54 && sizeof(void *) == 4) {
func.frame_base = FunctionInfo::ClangNoFP;
} else {
func.frame_base = FunctionInfo::Unknown;
}
} else if (attr == attr_specification) {
func.spec_loc = val;
}
} else if (fmt.tag == tag_base_type) {
if (attr == attr_name) {
type_info.name = containing_namespace + std::string((const char *)payload);
type_info.type = TypeInfo::Primitive;
} else if (attr == attr_byte_size) {
type_info.size = val;
}
} else if (fmt.tag == tag_class_type) {
if (attr == attr_name) {
type_info.name = containing_namespace + std::string((const char *)payload);
type_info.type = TypeInfo::Class;
} else if (attr == attr_byte_size) {
type_info.size = val;
}
} else if (fmt.tag == tag_structure_type) {
if (attr == attr_name) {
type_info.name = containing_namespace + std::string((const char *)payload);
type_info.type = TypeInfo::Struct;
} else if (attr == attr_byte_size) {
type_info.size = val;
}
} else if (fmt.tag == tag_typedef) {
if (attr == attr_name) {
type_info.name = containing_namespace + std::string((const char *)payload);
type_info.type = TypeInfo::Typedef;
} else if (attr == attr_type) {
LocalVariable m;
m.type_def_loc = val;
m.stack_offset = 0;
type_info.members.push_back(m);
}
} else if (fmt.tag == tag_pointer_type) {
if (attr == attr_type) {
LocalVariable m;
m.type_def_loc = val;
m.stack_offset = 0;
type_info.members.push_back(m);
type_info.type = TypeInfo::Pointer;
type_info.size = address_size;
} else if (attr == attr_byte_size) {
type_info.size = val;
}
} else if (fmt.tag == tag_reference_type) {
if (attr == attr_type) {
LocalVariable m;
m.type_def_loc = val;
m.stack_offset = 0;
type_info.members.push_back(m);
type_info.type = TypeInfo::Reference;
} else if (attr == attr_byte_size) {
type_info.size = val;
}
} else if (fmt.tag == tag_const_type) {
if (attr == attr_type) {
LocalVariable m;
m.type_def_loc = val;
m.stack_offset = 0;
type_info.members.push_back(m);
type_info.type = TypeInfo::Const;
} else if (attr == attr_byte_size) {
type_info.size = val;
}
} else if (fmt.tag == tag_array_type) {
if (attr == attr_type) {
LocalVariable m;
m.type_def_loc = val;
m.stack_offset = 0;
type_info.members.push_back(m);
type_info.type = TypeInfo::Array;
} else if (attr == attr_byte_size) {
type_info.size = val;
}
} else if (fmt.tag == tag_variable) {
if (attr == attr_name) {
if (func_stack.empty()) {
gvar.name = containing_namespace + std::string((const char *)payload);
} else {
gvar.name = var.name = std::string((const char *)payload);
}
} else if (attr == attr_location) {
if (payload && payload[0] == 0x91) {
var.stack_offset = (int)(get_sleb128(payload+1));
} else if (payload && payload[0] == 0x03 && val == (sizeof(void *) + 1)) {
const void *addr = *((const void * const *)(payload + 1));
gvar.addr = (uint64_t)(addr);
} else {
var.stack_offset = no_location;
}
} else if (attr == attr_type) {
var.type_def_loc = val;
gvar.type_def_loc = val;
} else if (attr == attr_abstract_origin) {
var.origin_loc = val;
} else if (attr == attr_specification) {
gvar.spec_loc = val;
}
} else if (fmt.tag == tag_member) {
if (attr == attr_name) {
var.name = std::string((const char *)payload);
if (type_stack.size()) {
gvar.name = type_stack.back().first.name + "::" + var.name;
} else {
gvar.name = var.name;
}
} else if (attr == attr_data_member_location) {
if (!payload) {
var.stack_offset = val;
} else if (payload[0] == 0x23) {
var.stack_offset = (int)(get_uleb128(payload+1));
}
} else if (attr == attr_type) {
var.type_def_loc = val;
gvar.type_def_loc = val;
}
} else if (fmt.tag == tag_namespace) {
if (attr == attr_name) {
namespace_name = std::string((const char *)payload);
}
} else if (fmt.tag == tag_subrange_type) {
if (attr == attr_upper_bound &&
type_stack.size() &&
type_stack.back().first.type == TypeInfo::Array) {
type_stack.back().first.size = val+1;
} else if (attr == attr_count &&
type_stack.size() &&
type_stack.back().first.type == TypeInfo::Array) {
type_stack.back().first.size = val;
}
} else if (fmt.tag == tag_inlined_subroutine ||
fmt.tag == tag_lexical_block) {
if (attr == attr_low_pc) {
LiveRange r = {val, val};
live_ranges.push_back(r);
} else if (attr == attr_high_pc && live_ranges.size()) {
if (fmt.fields[i].form == 0x1) {
live_ranges.back().pc_end = val;
} else {
live_ranges.back().pc_end = live_ranges.back().pc_begin + val;
}
} else if (attr == attr_ranges) {
if (val < debug_ranges.size()) {
const void * const * ptr = (const void * const *)(debug_ranges.data() + val);
const void * const * end = (const void * const *)(debug_ranges.data() + debug_ranges.size());
while (ptr[0] && ptr < end-1) {
LiveRange r = {(uint64_t)ptr[0], (uint64_t)ptr[1]};
r.pc_begin += compile_unit_base_pc;
r.pc_end += compile_unit_base_pc;
live_ranges.push_back(r);
ptr += 2;
}
}
}
} else if (fmt.tag == tag_compile_unit) {
if (attr == attr_low_pc) {
compile_unit_base_pc = val;
}
}
}
if (fmt.tag == tag_variable) {
if (func_stack.size() && !gvar.addr) {
if (live_range_stack.size()) {
var.live_ranges = live_range_stack.back().first;
}
func_stack.back().first.variables.push_back(var);
} else {
global_variables.push_back(gvar);
}
} else if (fmt.tag == tag_member &&
type_stack.size()) {
if (var.stack_offset == no_location) {
global_variables.push_back(gvar);
} else {
type_stack.back().first.members.push_back(var);
}
} else if (fmt.tag == tag_function) {
if (fmt.has_children) {
func_stack.push_back({ func, stack_depth });
} else {
functions.push_back(func);
}
} else if (fmt.tag == tag_class_type ||
fmt.tag == tag_structure_type ||
fmt.tag == tag_array_type ||
fmt.tag == tag_base_type) {
if (fmt.has_children) {
type_stack.push_back({ type_info, stack_depth });
} else {
types.push_back(type_info);
}
} else if ((fmt.tag == tag_typedef ||
fmt.tag == tag_pointer_type ||
fmt.tag == tag_reference_type ||
fmt.tag == tag_const_type) &&
type_info.members.size() == 1) {
types.push_back(type_info);
} else if (fmt.tag == tag_namespace && fmt.has_children) {
if (namespace_name.empty()) {
namespace_name = "{anonymous}";
}
namespace_stack.push_back({ namespace_name, stack_depth });
} else if ((fmt.tag == tag_inlined_subroutine ||
fmt.tag == tag_lexical_block) &&
live_ranges.size() && fmt.has_children) {
live_range_stack.push_back({ live_ranges, stack_depth });
}
}
}
{
std::map<uint64_t, FunctionInfo *> func_map;
for (size_t i = 0; i < functions.size(); i++) {
func_map[functions[i].def_loc] = &functions[i];
}
for (size_t i = 0; i < functions.size(); i++) {
if (functions[i].spec_loc) {
FunctionInfo *spec = func_map[functions[i].spec_loc];
if (spec) {
functions[i].name = spec->name;
}
}
}
}
{
std::map<uint64_t, LocalVariable *> var_map;
for (size_t i = 0; i < functions.size(); i++) {
for (size_t j = 0; j < functions[i].variables.size(); j++) {
var_map[functions[i].variables[j].def_loc] = &(functions[i].variables[j]);
}
}
for (size_t i = 0; i < functions.size(); i++) {
for (size_t j = 0; j < functions[i].variables.size(); j++) {
LocalVariable &v = functions[i].variables[j];
uint64_t loc = v.origin_loc;
if (loc) {
LocalVariable *origin = var_map[loc];
if (origin) {
v.name = origin->name;
v.type = origin->type;
v.type_def_loc = origin->type_def_loc;
} else {
debug(5) << "Variable with bad abstract origin: " << loc << "\n";
}
}
}
}
}
{
std::map<uint64_t, GlobalVariable *> var_map;
for (size_t i = 0; i < global_variables.size(); i++) {
GlobalVariable &var = global_variables[i];
debug(5) << "var " << var.name << " is at " << var.def_loc << "\n";
if (var.spec_loc || var.name.empty()) {
continue;
}
var_map[var.def_loc] = &var;
}
for (size_t i = 0; i < global_variables.size(); i++) {
GlobalVariable &var = global_variables[i];
if (var.name.empty() && var.spec_loc) {
GlobalVariable *spec = var_map[var.spec_loc];
if (spec) {
var.name = spec->name;
var.type = spec->type;
var.type_def_loc = spec->type_def_loc;
} else {
debug(5) << "Global variable with bad spec loc: " << var.spec_loc << "\n";
}
}
}
}
{
std::map<uint64_t, TypeInfo *> type_map;
for (size_t i = 0; i < types.size(); i++) {
type_map[types[i].def_loc] = &types[i];
}
for (size_t i = 0; i < functions.size(); i++) {
for (size_t j = 0; j < functions[i].variables.size(); j++) {
functions[i].variables[j].type =
type_map[functions[i].variables[j].type_def_loc];
}
}
for (size_t i = 0; i < global_variables.size(); i++) {
global_variables[i].type =
type_map[global_variables[i].type_def_loc];
}
for (size_t i = 0; i < types.size(); i++) {
for (size_t j = 0; j < types[i].members.size(); j++) {
types[i].members[j].type =
type_map[types[i].members[j].type_def_loc];
}
}
}
for (size_t i = 0; i < types.size(); i++) {
vector<std::string> suffix;
TypeInfo *t = &types[i];
while (t) {
if (t->type == TypeInfo::Pointer) {
suffix.push_back("*");
assert(t->members.size() == 1);
t = t->members[0].type;
} else if (t->type == TypeInfo::Reference) {
suffix.push_back("&");
assert(t->members.size() == 1);
t = t->members[0].type;
} else if (t->type == TypeInfo::Const) {
suffix.push_back("const");
assert(t->members.size() == 1);
t = t->members[0].type;
} else if (t->type == TypeInfo::Array) {
if (t->size != 0) {
std::ostringstream oss;
oss << '[' << t->size << ']';
suffix.push_back(oss.str());
} else {
suffix.push_back("[]");
}
assert(t->members.size() == 1);
t = t->members[0].type;
} else {
break;
}
}
if (t && suffix.size()) {
types[i].name = t->name;
while (suffix.size()) {
types[i].name += " " + suffix.back();
suffix.pop_back();
}
}
}
for (size_t i = 0; i < functions.size(); i++) {
vector<LocalVariable> new_vars = functions[i].variables;
for (size_t j = 0; j < new_vars.size(); j++) {
const LocalVariable &v = new_vars[j];
if (v.type &&
(v.type->type == TypeInfo::Struct ||
v.type->type == TypeInfo::Class ||
v.type->type == TypeInfo::Typedef)) {
size_t members = v.type->members.size();
new_vars.insert(new_vars.begin() + j + 1,
v.type->members.begin(),
v.type->members.end());
if (new_vars[j].type->type == TypeInfo::Typedef) {
new_vars[j+1].name = new_vars[j].name;
new_vars[j+1].stack_offset = new_vars[j].stack_offset;
} else {
for (size_t k = 0; k < members; k++) {
new_vars[j+k+1].stack_offset += new_vars[j].stack_offset;
if (new_vars[j+k+1].name.size() &&
new_vars[j].name.size()) {
new_vars[j+k+1].name = new_vars[j].name + "." + new_vars[j+k+1].name;
}
}
}
}
}
functions[i].variables.swap(new_vars);
if (functions[i].variables.size()) {
debug(5) << "Function " << functions[i].name << ":\n";
for (size_t j = 0; j < functions[i].variables.size(); j++) {
if (functions[i].variables[j].type) {
debug(5) << " " << functions[i].variables[j].type->name << " " << functions[i].variables[j].name << "\n";
}
}
}
}
for (size_t i = 0; i < global_variables.size(); i++) {
GlobalVariable v = global_variables[i];
if (v.type && v.addr &&
(v.type->type == TypeInfo::Struct ||
v.type->type == TypeInfo::Class ||
v.type->type == TypeInfo::Typedef)) {
debug(5) << "Unpacking members of " << v.name << " at " << std::hex << v.addr << "\n";
vector<LocalVariable> &members = v.type->members;
for (size_t j = 0; j < members.size(); j++) {
GlobalVariable mem;
if (!v.name.empty() && !members[j].name.empty()) {
mem.name = v.name + "." + members[j].name;
} else {
mem.name = members[j].name;
}
mem.type = members[j].type;
mem.type_def_loc = members[j].type_def_loc;
mem.addr = v.addr + members[j].stack_offset;
debug(5) << " Member " << mem.name << " goes at " << mem.addr << "\n";
global_variables.push_back(mem);
}
debug(5) << std::dec;
}
}
{
vector<FunctionInfo> trimmed;
for (size_t i = 0; i < functions.size(); i++) {
FunctionInfo &f = functions[i];
if (!f.pc_begin ||
!f.pc_end ||
f.name.empty()) {
continue;
}
vector<LocalVariable> vars;
for (size_t j = 0; j < f.variables.size(); j++) {
LocalVariable &v = f.variables[j];
if (!v.name.empty() && v.type && v.stack_offset != no_location) {
vars.push_back(v);
} else {
}
}
f.variables.clear();
trimmed.push_back(f);
trimmed.back().variables = vars;
}
std::swap(functions, trimmed);
}
{
vector<GlobalVariable> trimmed;
for (size_t i = 0; i < global_variables.size(); i++) {
GlobalVariable &v = global_variables[i];
if (!v.name.empty() && v.addr) {
trimmed.push_back(v);
}
}
std::swap(global_variables, trimmed);
}
std::sort(functions.begin(), functions.end());
std::sort(global_variables.begin(), global_variables.end());
}
void parse_debug_line(const llvm::DataExtractor &e) {
uint32_t off = 0;
while (1) {
uint32_t unit_length = e.getU32(&off);
if (unit_length == 0) {
break;
}
uint32_t unit_end = off + unit_length;
debug(5) << "Parsing compilation unit from " << off << " to " << unit_end << "\n";
uint16_t version = e.getU16(&off);
assert(version >= 2);
uint32_t header_length = e.getU32(&off);
uint32_t end_header_off = off + header_length;
uint8_t min_instruction_length = e.getU8(&off);
uint8_t max_ops_per_instruction = 1;
if (version >= 4) {
max_ops_per_instruction = e.getU8(&off);
}
uint8_t default_is_stmt = e.getU8(&off);
int8_t line_base = (int8_t)e.getU8(&off);
uint8_t line_range = e.getU8(&off);
uint8_t opcode_base = e.getU8(&off);
vector<uint8_t> standard_opcode_length(opcode_base);
for (int i = 1; i < opcode_base; i++) {
standard_opcode_length[i] = e.getU8(&off);
}
vector<std::string> include_dirs;
include_dirs.push_back(".");
while (off < end_header_off) {
const char *s = e.getCStr(&off);
if (s && s[0]) {
include_dirs.push_back(s);
} else {
break;
}
}
int source_files_base = source_files.size();
while (off < end_header_off) {
const char *name = e.getCStr(&off);
if (name && name[0]) {
uint64_t dir = e.getULEB128(&off);
uint64_t mod_time = e.getULEB128(&off);
uint64_t length = e.getULEB128(&off);
(void)mod_time;
(void)length;
assert(dir <= include_dirs.size());
source_files.push_back(include_dirs[dir] + "/" + name);
} else {
break;
}
}
assert(off == end_header_off && "Failed parsing section .debug_line");
struct {
uint64_t address;
uint32_t op_index;
uint32_t file, line, column;
bool is_stmt, basic_block, end_sequence, prologue_end, epilogue_begin;
uint32_t isa;
uint32_t discriminator;
void append_row(vector<LineInfo> &lines) {
LineInfo l = {address, line, file};
lines.push_back(l);
}
} state, initial_state;
initial_state.address = 0;
initial_state.op_index = 0;
initial_state.file = 0;
initial_state.line = 1;
initial_state.column = 0;
initial_state.is_stmt = default_is_stmt;
initial_state.basic_block = false;
initial_state.end_sequence = false;
initial_state.prologue_end = false;
initial_state.epilogue_begin = false;
initial_state.isa = 0;
initial_state.discriminator = 0;
state = initial_state;
while (off < unit_end) {
uint8_t opcode = e.getU8(&off);
if (opcode == 0) {
uint32_t ext_offset = off;
uint64_t len = e.getULEB128(&off);
uint32_t arg_size = len - (off - ext_offset);
uint8_t sub_opcode = e.getU8(&off);
switch (sub_opcode) {
case 1:
{
state.end_sequence = true;
state.append_row(source_lines);
state = initial_state;
break;
}
case 2:
{
state.address = e.getAddress(&off);
break;
}
case 3:
{
const char *name = e.getCStr(&off);
uint64_t dir_index = e.getULEB128(&off);
uint64_t mod_time = e.getULEB128(&off);
uint64_t length = e.getULEB128(&off);
(void)mod_time;
(void)length;
assert(dir_index < include_dirs.size());
source_files.push_back(include_dirs[dir_index] + "/" + name);
break;
}
case 4:
{
state.discriminator = e.getULEB128(&off);
break;
}
default:
off += arg_size;
}
} else if (opcode < opcode_base) {
switch (opcode) {
case 1:
{
state.append_row(source_lines);
state.basic_block = false;
state.prologue_end = false;
state.epilogue_begin = false;
state.discriminator = 0;
break;
}
case 2:
{
uint64_t advance = e.getULEB128(&off);
state.address += min_instruction_length * ((state.op_index + advance) / max_ops_per_instruction);
state.op_index = (state.op_index + advance) % max_ops_per_instruction;
break;
}
case 3:
{
state.line += e.getSLEB128(&off);
break;
}
case 4:
{
state.file = e.getULEB128(&off) - 1 + source_files_base;
break;
}
case 5:
{
state.column = e.getULEB128(&off);
break;
}
case 6:
{
state.is_stmt = !state.is_stmt;
break;
}
case 7:
{
state.basic_block = true;
break;
}
case 8:
{
uint8_t adjust_opcode = 255 - opcode_base;
uint64_t advance = adjust_opcode / line_range;
state.address += min_instruction_length * ((state.op_index + advance) / max_ops_per_instruction);
state.op_index = (state.op_index + advance) % max_ops_per_instruction;
break;
}
case 9:
{
uint16_t advance = e.getU16(&off);
state.address += advance;
break;
}
case 10:
{
state.prologue_end = true;
break;
}
case 11:
{
state.epilogue_begin = true;
break;
}
case 12:
{
state.isa = e.getULEB128(&off);
break;
}
default:
{
uint8_t args = standard_opcode_length[opcode];
for (int i = 0; i < args; i++) {
e.getULEB128(&off);
}
}}
} else {
uint8_t adjust_opcode = opcode - opcode_base;
uint64_t advance_op = adjust_opcode / line_range;
uint64_t advance_line = line_base + adjust_opcode % line_range;
state.address += min_instruction_length * ((state.op_index + advance_op) / max_ops_per_instruction);
state.op_index = (state.op_index + advance_op) % max_ops_per_instruction;
state.line += advance_line;
state.append_row(source_lines);
state.basic_block = false;
state.prologue_end = false;
state.epilogue_begin = false;
state.discriminator = 0;
}
}
}
std::sort(source_lines.begin(), source_lines.end());
}
FunctionInfo *find_containing_function(void *addr) {
uint64_t address = (uint64_t)addr;
debug(5) << "Searching for function containing address " << addr << "\n";
size_t hi = functions.size();
size_t lo = 0;
while (hi > lo) {
size_t mid = (hi + lo)/2;
uint64_t pc_mid_begin = functions[mid].pc_begin;
uint64_t pc_mid_end = functions[mid].pc_end;
if (address < pc_mid_begin) {
hi = mid;
} else if (address > pc_mid_end) {
lo = mid + 1;
} else {
debug(5) << "At function " << functions[mid].name
<< " spanning: " << (void *)pc_mid_begin
<< ", " << (void *)pc_mid_end << "\n";
return &functions[mid];
}
}
return nullptr;
}
int64_t get_sleb128(const uint8_t *ptr) {
int64_t result = 0;
unsigned shift = 0;
uint8_t byte = 0;
while (1) {
assert(shift < 57);
byte = *ptr++;
result |= (uint64_t)(byte & 0x7f) << shift;
shift += 7;
if ((byte & 0x80) == 0) {
break;
}
}
if (shift < 64 && (byte & 0x40)) {
result |= -(1ULL << shift);
}
return result;
}
int64_t get_uleb128(const uint8_t *ptr) {
uint64_t result = 0;
unsigned shift = 0;
uint8_t byte = 0;
while (1) {
assert(shift < 57);
byte = *ptr++;
result |= (uint64_t)(byte & 0x7f) << shift;
shift += 7;
if ((byte & 0x80) == 0) {
return result;
}
}
}
};
namespace {
DebugSections *debug_sections = nullptr;
}
std::string get_variable_name(const void *var, const std::string &expected_type) {
if (!debug_sections) return "";
if (!debug_sections->working) return "";
std::string name = debug_sections->get_stack_variable_name(var, expected_type);
if (name.empty()) {
name = debug_sections->get_heap_member_name(var, expected_type);
}
if (name.empty()) {
name = debug_sections->get_global_variable_name(var, expected_type);
}
return name;
}
std::string get_source_location() {
if (!debug_sections) return "";
if (!debug_sections->working) return "";
return debug_sections->get_source_location();
}
void register_heap_object(const void *obj, size_t size, const void *helper) {
if (!debug_sections) return;
if (!debug_sections->working) return;
if (!helper) return;
debug_sections->register_heap_object(obj, size, helper);
}
void deregister_heap_object(const void *obj, size_t size) {
if (!debug_sections) return;
if (!debug_sections->working) return;
debug_sections->deregister_heap_object(obj, size);
}
bool saves_frame_pointer(void *fn) {
const uint8_t *ptr = (const uint8_t *)(fn);
return ptr[0] == 0x55;
}
void test_compilation_unit(bool (*test)(bool (*)(const void *, const std::string &)),
bool (*test_a)(const void *, const std::string &),
void (*calib)()) {
#ifdef __ARM__
return;
#else
if (sizeof(void *) == 4) {
return;
}
debug(5) << "Testing compilation unit with offset_marker at " << reinterpret_bits<void *>(calib) << "\n";
if (!debug_sections) {
char path[2048];
get_program_name(path, sizeof(path));
debug_sections = new DebugSections(path);
}
if (!saves_frame_pointer(reinterpret_bits<void *>(&test_compilation_unit)) ||
!saves_frame_pointer(reinterpret_bits<void *>(test))) {
debug_sections->working = false;
debug(5) << "Failed because frame pointer not saved\n";
} else if (debug_sections->working) {
debug_sections->calibrate_pc_offset(calib);
if (!debug_sections->working) {
debug(5) << "Failed because offset calibration failed\n";
return;
}
debug_sections->working = (*test)(test_a);
if (!debug_sections->working) {
debug(5) << "Failed because test routine failed\n";
return;
}
debug(5) << "Test passed\n";
}
#endif
}
}
}
}
#else
namespace Halide {
namespace Internal {
namespace Introspection {
std::string get_variable_name(const void *var, const std::string &expected_type) {
return "";
}
std::string get_source_location() {
return "";
}
void register_heap_object(const void *obj, size_t size, const void *helper) {
}
void deregister_heap_object(const void *obj, size_t size) {
}
void test_compilation_unit(bool (*test)(bool (*)(const void *, const std::string &)),
bool (*test_a)(const void *, const std::string &),
void (*calib)()) {
}
}
}
}
#endif