cli/probe: start implementing variable fetching

Resolving parameters, variables with function scope, and global
variables should work. This is just the variable resolution, no fetching
yet, but a bunch of refactors snuck in here so committing it all now.
This commit is contained in:
Omar Sandoval 2017-08-27 23:56:14 -07:00
parent e6a58f533a
commit 682fd172a4
11 changed files with 415 additions and 149 deletions

View File

@ -1,3 +1,4 @@
from drgn import lldwarf
from drgn.dwarf import DwarfFile
from drgn.dwarf.defs import *
import fnmatch
@ -19,7 +20,8 @@ def dump_cu(dwarf_file, cu, cu_name, *, indent=0):
print(f'{prefix} is_64_bit = {cu.is_64_bit}')
def dump_die(dwarf_file, die, *, indent=0, recurse=False):
def dump_die(dwarf_file: DwarfFile, die: lldwarf.DwarfDie, *,
indent: int=0, recurse: bool=False) -> None:
prefix = ' ' * indent
print(f'{prefix}<{die.cu_offset}> {tag_name(die.tag)}')
for name, form, value in die:
@ -29,14 +31,8 @@ def dump_die(dwarf_file, die, *, indent=0, recurse=False):
value = repr(value)[1:]
print(f'{prefix} {at_name(name)} ({form_name(form)}) = {value}')
if recurse:
try:
children = die.children
except AttributeError:
pass
else:
if children is not None:
for child in children:
dump_die(dwarf_file, child, indent=indent + 2, recurse=True)
for child in dwarf_file.die_children(die):
dump_die(dwarf_file, child, indent=indent + 2, recurse=True)
def dump_lnp_include_directories(lnp, *, indent=0):
@ -139,9 +135,9 @@ def dump_line_number_matrix(cu, lnp, matrix, *, indent=0):
print(f'{prefix}}}')
def dump_cus(dwarf_file, args):
def dump_cus(dwarf_file: DwarfFile, args) -> None:
for cu in dwarf_file.cu_headers():
cu_name = dwarf_file.cu_name(cu).decode()
cu_name = dwarf_file.cu_name(cu)
for pattern in args.cu:
if fnmatch.fnmatch(cu_name, pattern):
break
@ -151,8 +147,6 @@ def dump_cus(dwarf_file, args):
dump_cu(dwarf_file, cu, cu_name)
if args.die:
die = dwarf_file.cu_die(cu)
if args.recursive:
dwarf_file.parse_die_children(die, recurse=True)
dump_die(dwarf_file, die, indent=2, recurse=args.recursive)
if (args.include_directories or args.file_names or args.lines or
args.line_number_program):

View File

@ -1,4 +1,5 @@
from drgn.dwarf import DwarfProgram
from drgn.dwarf.defs import *
from drgn.ftrace import Kprobe, FtraceInstance
import re
import os
@ -26,9 +27,26 @@ def cmd_probe(args):
binary = f'/lib/modules/{os.uname().release}/build/vmlinux'
with DwarfProgram(binary) as dwarf_program:
if function is not None:
scope = dwarf_program.find_subprogram_by_name(function)
probe_addr = scope.cu.file.die_address(scope)
probe_location = function
else:
probe_location = dwarf_program.find_breakpoint_location(filename, lineno)
cu = dwarf_program.find_cu_by_name(filename)
row = dwarf_program.find_breakpoint(cu, filename, lineno)
scope = dwarf_program.find_scope_containing_address(cu, row.address)
subprogram = scope
while subprogram.tag != DW_TAG.subprogram:
subprogram = subprogram.parent
subprogram_name = cu.file.die_name(subprogram)
subprogram_addr = cu.file.die_address(subprogram)
assert row.address >= subprogram_addr
probe_addr = row.address
probe_location = f'{subprogram_name}+0x{row.address - subprogram_addr:x}'
for var in args.variables:
resolved = dwarf_program.resolve_variable(scope, var)
# TODO: deal with probe name collisions
with FtraceInstance(f'drgn_{os.getpid()}') as instance, \
@ -49,6 +67,9 @@ def register(subparsers):
'location', metavar='LOCATION',
help='location to probe; either a function name or file:line')
subparser.add_argument(
'variables', metavar='VAR', nargs='*', help='variables to fetch')
group = subparser.add_mutually_exclusive_group()
group.add_argument(
'--line', '-l', action='store_true',

View File

@ -38,6 +38,8 @@ class DwarfFile:
self._sections = parse_elf_sections(self._mmap, self._ehdr)
self._abbrev_tables = {}
self._cus = {}
self._range_lists = {}
self._symbols = None
def close(self):
@ -60,7 +62,7 @@ class DwarfFile:
def section(self, name: str):
return self._sections[name]
def string_at(self, offset):
def string_at(self, offset: int) -> bytes:
nul = self._mmap.find(b'\0', offset)
assert nul != -1 # XXX
return self._mmap[offset:nul]
@ -124,28 +126,32 @@ class DwarfFile:
# Compilation units
def cu_headers(self):
debug_info = self.section('.debug_info')
offset = debug_info.sh_offset
end = debug_info.sh_offset + debug_info.sh_size
while offset < end:
cu = lldwarf.parse_compilation_unit_header(self._mmap, offset)
cu.offset = offset
yield cu
offset += (12 if cu.is_64_bit else 4) + cu.unit_length
def cu_header(self, offset: int) -> lldwarf.CompilationUnitHeader:
try:
return self._cus[offset]
except KeyError:
pass
debug_info = self.section('.debug_info')
offset += debug_info.sh_offset
cu = lldwarf.parse_compilation_unit_header(self._mmap, offset)
cu.file = self
cu.offset = offset
return cu
def cu_name(self, cu: lldwarf.CompilationUnitHeader) -> bytes:
def cu_headers(self):
debug_info = self.section('.debug_info')
offset = 0
while offset < debug_info.sh_size:
cu = self.cu_header(offset)
yield cu
offset += (12 if cu.is_64_bit else 4) + cu.unit_length
def cu_name(self, cu: lldwarf.CompilationUnitHeader) -> str:
try:
return self.die_name(self.cu_die(cu))
except KeyError:
return b''
return ''
# Debugging information entries
@ -164,28 +170,44 @@ class DwarfFile:
cu.die = die
return die
def parse_die_children(self, die: lldwarf.DwarfDie, *,
recurse: bool=False) -> None:
if not hasattr(die, 'children'):
cu = die.cu
debug_info = self.section('.debug_info')
abbrev_table = self.abbrev_table(cu.debug_abbrev_offset)
offset = cu.offset + die.cu_offset + die.die_length
die.children = lldwarf.parse_die_siblings(cu, die, abbrev_table,
cu.offset, self._mmap,
offset=offset,
recurse=recurse)
def die_children(self, die: lldwarf.DwarfDie, *,
recurse: bool=False) -> List[lldwarf.DwarfDie]:
try:
return die.children
except AttributeError:
pass
def die_contains_address(self, die: lldwarf.DwarfDie, address: int) -> bool:
cu = die.cu
debug_info = self.section('.debug_info')
abbrev_table = self.abbrev_table(cu.debug_abbrev_offset)
offset = cu.offset + die.cu_offset + die.die_length
children = lldwarf.parse_die_siblings(cu, die, abbrev_table, cu.offset,
self._mmap, offset=offset,
recurse=recurse)
die.children = children
return children
def die_contains_address(self, die: lldwarf.DwarfDie, addr: int) -> bool:
try:
ranges_form, ranges_value = die.find(DW_AT.ranges)
assert False
except KeyError:
pass
else:
cu = die.cu
base_addr = self.die_address(cu.die)
offset = self.at_sec_offset(ranges_form, ranges_value)
for range in self.range_list(cu.address_size, offset):
if range.start == 0xffffffffffffffff:
base_addr = range.end
elif base_addr + range.start <= addr < base_addr + range.end:
return True
return False
try:
low_pc_form, low_pc = die.find(DW_AT.low_pc)
except KeyError:
return False
high_pc_form, high_pc_value = die.find(DW_AT.high_pc)
assert low_pc_form == DW_FORM.addr
if at_class_constant_int(high_pc_form):
@ -195,11 +217,11 @@ class DwarfFile:
else:
assert high_pc_form == DW_FORM.addr
high_pc = high_pc_value
return low_pc <= address < high_pc
return low_pc <= addr < high_pc
def die_name(self, die: lldwarf.DwarfDie) -> bytes:
def die_name(self, die: lldwarf.DwarfDie) -> str:
form, value = die.find(DW_AT.name)
return self.at_string(die.cu, form, value)
return self.at_string(die.cu, form, value).decode()
def die_address(self, die: lldwarf.DwarfDie) -> int:
try:
@ -240,6 +262,11 @@ class DwarfFile:
# Line number programs
def cu_line_number_program_header(self, cu: lldwarf.CompilationUnitHeader) -> lldwarf.LineNumberProgramHeader:
try:
return cu.lnp
except AttributeError:
pass
debug_line = self.section('.debug_line')
die = self.cu_die(cu)
try:
@ -249,18 +276,26 @@ class DwarfFile:
offset = debug_line.sh_offset + self.at_sec_offset(form, value)
lnp = lldwarf.parse_line_number_program_header(self._mmap, offset)
lnp.offset = offset
cu.lnp = lnp
return lnp
def execute_line_number_program(self, lnp: lldwarf.LineNumberProgramHeader) -> List[lldwarf.LineNumberRow]:
return lldwarf.execute_line_number_program(lnp, lnp_end_offset(lnp),
self._mmap,
lnp_program_offset(lnp))
try:
return lnp.matrix
except AttributeError:
pass
matrix= lldwarf.execute_line_number_program(lnp, lnp_end_offset(lnp),
self._mmap,
lnp_program_offset(lnp))
lnp.matrix = matrix
return matrix
def line_number_row_name(self, cu: lldwarf.CompilationUnitHeader,
lnp: lldwarf.LineNumberProgramHeader,
row: lldwarf.LineNumberRow) -> str:
if row.file == 0:
return self.cu_name(cu).decode()
return self.cu_name(cu)
file_name, directory_index, mtime, file_size = lnp.file_names[row.file - 1]
file_name = file_name.decode()
@ -298,3 +333,17 @@ class DwarfFile:
operation_advance = opcode // lnp.line_range
line_increment = lnp.line_base + (opcode % lnp.line_range)
yield 'special', opcode, (operation_advance, line_increment)
# Range lists
def range_list(self, address_size: int, offset: int) -> List[lldwarf.Range]:
try:
return self._range_lists[offset]
except KeyError:
pass
debug_ranges = self.section('.debug_ranges')
offset += debug_ranges.sh_offset
ranges = lldwarf.parse_range_list(address_size, self._mmap, offset)
self._range_lists[offset] = ranges
return ranges

View File

@ -1,9 +1,19 @@
import drgn.lldwarf as lldwarf
from drgn import lldwarf
from drgn.dwarf.defs import *
from drgn.dwarf.file import DwarfFile
from typing import List, Tuple
def best_breakpoint(rows: List[lldwarf.LineNumberRow]) -> lldwarf.LineNumberRow:
# The first row which is a statement, or the first row if none are
# statements.
for row in rows:
if row.is_stmt:
return row
else:
return rows[0]
class DwarfProgram:
def __init__(self, path):
self._closed = False
@ -24,82 +34,81 @@ class DwarfProgram:
def __exit__(self, exc_type, exc_value, traceback):
self.close()
def find_cu_by_name(self, name: str) -> Tuple[DwarfFile, lldwarf.CompilationUnitHeader]:
def find_cu_by_name(self, name: str) -> lldwarf.CompilationUnitHeader:
for cu in self._file.cu_headers():
die = self._file.cu_die(cu)
try:
cu_name = self._file.die_name(die).decode()
except KeyError:
continue
if cu_name == name:
return self._file, cu
if self._file.cu_name(cu) == name:
return cu
else:
raise ValueError('CU not found')
def find_cu_by_addr(self, addr: int) -> Tuple[DwarfFile, lldwarf.CompilationUnitHeader]:
def find_cu_by_addr(self, addr: int) -> lldwarf.CompilationUnitHeader:
dwarf_file = self._file
for art in dwarf_file.arange_table_headers():
for arange in dwarf_file.arange_table(art):
if arange.address <= addr <= arange.address + arange.length:
return dwarf_file, dwarf_file.cu_header(art.debug_info_offset)
return dwarf_file.cu_header(art.debug_info_offset)
else:
raise ValueError('CU containing address not found')
def find_subprogram_by_name(self, name: str):
dwarf_file = self._file
symbol = dwarf_file.symbol(name)
dwarf_file, cu = self.find_cu_by_addr(symbol.st_value)
def find_subprogram_by_name(self, name: str) -> lldwarf.DwarfDie:
symbol = self._file.symbol(name)
cu = self.find_cu_by_addr(symbol.st_value)
dwarf_file = cu.file
die = self._file.cu_die(cu)
dwarf_file.parse_die_children(die)
for child in die.children:
for child in dwarf_file.die_children(die):
if (child.tag == DW_TAG.subprogram and
dwarf_file.die_name(child).decode() == name):
dwarf_file.die_name(child) == name):
return child
else:
raise ValueError('subprogram not found')
@staticmethod
def _best_breakpoint_row(dwarf_file: DwarfFile,
cu: lldwarf.CompilationUnitHeader,
lnp: lldwarf.LineNumberProgramHeader,
matrix: List[lldwarf.LineNumberRow],
filename: str,
lineno: int) -> lldwarf.LineNumberRow:
# Find the first row which is a statement, or the first row if none are
# statements.
first_row = None
for row in matrix:
if (dwarf_file.line_number_row_name(cu, lnp, row) == filename and row.line == lineno):
if row.is_stmt:
return row
if first_row is None:
first_row = row
else:
assert first_row is not None # XXX
return first_row
@staticmethod
def _find_subprogram_containing_address(dwarf_file: DwarfFile,
cu: lldwarf.CompilationUnitHeader,
addr: int) -> lldwarf.DwarfDie:
def find_subprogram_containing_address(self, cu: lldwarf.CompilationUnitHeader,
addr: int) -> lldwarf.DwarfDie:
dwarf_file = cu.file
die = dwarf_file.cu_die(cu)
dwarf_file.parse_die_children(die)
for child in die.children:
for child in dwarf_file.die_children(die):
if (child.tag == DW_TAG.subprogram and
dwarf_file.die_contains_address(child, addr)):
return child
assert False # XXX
def find_breakpoint_location(self, filename: str, lineno: int) -> str:
dwarf_file, cu = self.find_cu_by_name(filename)
def find_scope_containing_address(self, cu: lldwarf.CompilationUnitHeader,
addr: int) -> lldwarf.DwarfDie:
dwarf_file = cu.file
die = dwarf_file.cu_die(cu)
assert dwarf_file.die_contains_address(die, addr)
while True:
for child in dwarf_file.die_children(die):
if ((child.tag == DW_TAG.subprogram or child.tag == DW_TAG.lexical_block) and
dwarf_file.die_contains_address(child, addr)):
die = child
break
else:
return die
def find_lines(self, cu: lldwarf.CompilationUnitHeader,
filename: str, lineno: int) -> List[lldwarf.LineNumberRow]:
dwarf_file = cu.file
lnp = dwarf_file.cu_line_number_program_header(cu)
matrix = dwarf_file.execute_line_number_program(lnp)
row = self._best_breakpoint_row(dwarf_file, cu, lnp, matrix, filename, lineno)
rows = []
for row in dwarf_file.execute_line_number_program(lnp):
if (dwarf_file.line_number_row_name(cu, lnp, row) == filename and row.line == lineno):
rows.append(row)
return rows
subprogram = self._find_subprogram_containing_address(dwarf_file, cu, row.address)
subprogram_name = dwarf_file.die_name(subprogram).decode()
subprogram_address = dwarf_file.die_address(subprogram)
assert row.address >= subprogram_address
return f'{subprogram_name}+0x{row.address - subprogram_address:x}'
def find_breakpoint(self, cu: lldwarf.CompilationUnitHeader,
filename: str, lineno: int) -> lldwarf.LineNumberRow:
return best_breakpoint(self.find_lines(cu, filename, lineno))
def resolve_variable(self, die: lldwarf.DwarfDie, var: str):
dwarf_file = die.cu.file
while die is not None:
for child in dwarf_file.die_children(die):
if ((child.tag == DW_TAG.formal_parameter or
child.tag == DW_TAG.variable) and
dwarf_file.die_name(child) == var):
return child
die = die.parent
raise ValueError('could not resolve variable')

View File

@ -180,10 +180,9 @@ PyObject *LLDwarf_ParseArangeTable(Py_buffer *buffer, Py_ssize_t *offset,
if (segment == 0 && address == 0 && length == 0)
break;
arange = PyMem_Malloc(sizeof(AddressRange));
arange = PyObject_New(AddressRange, &AddressRange_type);
if (!arange)
goto err;
PyObject_Init((PyObject *)arange, &AddressRange_type);
arange->segment = segment;
arange->address = address;
arange->length = length;

View File

@ -153,7 +153,7 @@ static PyObject *DwarfDie_new(PyTypeObject *subtype, PyObject *args,
Py_INCREF(Py_None);
die->children = Py_None;
} else {
die->children = PySequence_List(children);
die->children = PySequence_Tuple(children);
if (!die->children)
goto err;
}
@ -517,11 +517,7 @@ PyObject *LLDwarf_ParseDieSiblings(Py_buffer *buffer, Py_ssize_t *offset,
PyObject *abbrev_table, Py_ssize_t cu_offset,
bool recurse)
{
PyObject *children;
children = PyList_New(0);
if (!children)
return NULL;
PyObject *tmp = NULL;
for (;;) {
PyObject *child;
@ -533,17 +529,33 @@ PyObject *LLDwarf_ParseDieSiblings(Py_buffer *buffer, Py_ssize_t *offset,
goto err;
if (!child)
break;
if (PyList_Append(children, child) == -1) {
if (!tmp) {
tmp = PyList_New(0);
if (!tmp) {
Py_DECREF(child);
goto err;
}
}
if (PyList_Append(tmp, child) == -1) {
Py_DECREF(child);
goto err;
}
Py_DECREF(child);
}
return children;
if (tmp) {
PyObject *children;
children = PyList_AsTuple(tmp);
Py_DECREF(tmp);
return children;
} else {
Py_INCREF(empty_tuple);
return empty_tuple;
}
err:
Py_DECREF(children);
Py_XDECREF(tmp);
return NULL;
}
@ -611,8 +623,8 @@ PyObject *LLDwarf_ParseDie(Py_buffer *buffer, Py_ssize_t *offset,
die->die_length = *offset - orig_offset;
if (!decl->children) {
Py_INCREF(Py_None);
die->children = Py_None;
Py_INCREF(empty_tuple);
die->children = empty_tuple;
} else if (recurse || (jump_to_sibling && !sibling)) {
die->children = LLDwarf_ParseDieSiblings(buffer, offset, cu,
(PyObject *)die,
@ -677,7 +689,7 @@ static PyMemberDef DwarfDie_members[] = {
"cu_offset -- integer offset\n" \
"die_length -- intger length\n" \
"tag -- integer tag of the DIE\n" \
"children -- list of children DIEs\n" \
"children -- tuple of children DIEs\n" \
"attribs -- iterable of (name, form, value) triples"
PyTypeObject DwarfDie_type = {

View File

@ -137,10 +137,25 @@ typedef struct {
extern PyTypeObject LineNumberRow_type;
typedef struct {
PyObject_VAR_HEAD
uint64_t start;
uint64_t end;
} Range;
extern PyTypeObject Range_type;
#ifdef TEST_LLDWARFOBJECT
extern PyTypeObject TestObject_type;
#endif
/*
* This tuple is used for the children attribute of any DwarfDie with no
* children. It's much cheaper to reuse this rather than create a new object
* every time, and avoids special cases like if we used None.
*/
PyObject *empty_tuple;
void LLDwarfObject_dealloc(PyObject *self);
int LLDwarfObject_traverse(PyObject *self, visitproc visit, void *arg);
int LLDwarfObject_clear(PyObject *self);
@ -170,6 +185,8 @@ PyObject *LLDwarf_ExecuteLineNumberProgram(Py_buffer *buffer,
Py_ssize_t *offset,
LineNumberProgramHeader *lnp,
Py_ssize_t lnp_end_offset);
PyObject *LLDwarf_ParseRangeList(Py_buffer *buffer, Py_ssize_t *offset,
Py_ssize_t address_size);
int read_uleb128(Py_buffer *buffer, Py_ssize_t *offset, uint64_t *ret);
int read_sleb128(Py_buffer *buffer, Py_ssize_t *offset, int64_t *ret);

View File

@ -380,6 +380,34 @@ static PyObject *execute_line_number_program(PyObject *self, PyObject *args,
return ret;
}
static PyObject *parse_range_list(PyObject *self, PyObject *args,
PyObject *kwds)
{
static char *keywords[] = {
"address_size", "buffer", "offset", NULL
};
Py_ssize_t address_size;
Py_buffer buffer;
Py_ssize_t offset = 0;
PyObject *ret;
if (!PyArg_ParseTupleAndKeywords(args, kwds,
"ny*|n:execute_line_number_program",
keywords, &address_size, &buffer,
&offset))
return NULL;
if (offset < 0) {
PyErr_SetString(PyExc_ValueError, "offset cannot be negative");
PyBuffer_Release(&buffer);
return NULL;
}
ret = LLDwarf_ParseRangeList(&buffer, &offset, address_size);
PyBuffer_Release(&buffer);
return ret;
}
static PyMethodDef lldwarf_methods[] = {
{"parse_uleb128", (PyCFunction)parse_uleb128,
METH_VARARGS | METH_KEYWORDS,
@ -474,6 +502,15 @@ static PyMethodDef lldwarf_methods[] = {
"lnp_end_offset -- offset into the buffer where the line number program ends\n"
"buffer -- readable source buffer\n"
"offset -- optional offset into the buffer"},
{"parse_range_list",
(PyCFunction)parse_range_list,
METH_VARARGS | METH_KEYWORDS,
"parse_range_list(address_size, buffer, offset=0) -> list of Range\n\n"
"Parse a range list.\n\n"
"Arguments:\n"
"address_size -- size of an address in this range list\n"
"buffer -- readable source buffer\n"
"offset -- optional offset into the buffer"},
{},
};
@ -490,6 +527,10 @@ PyInit_lldwarf(void)
{
PyObject *m;
empty_tuple = PyTuple_New(0);
if (!empty_tuple)
return NULL;
if (PyType_Ready(&AbbrevDecl_type) < 0)
return NULL;
@ -516,6 +557,10 @@ PyInit_lldwarf(void)
if (PyType_Ready(&LineNumberRow_type) < 0)
return NULL;
Range_type.tp_new = PyType_GenericNew;
if (PyType_Ready(&Range_type) < 0)
return NULL;
#ifdef TEST_LLDWARFOBJECT
TestObject_type.tp_new = PyType_GenericNew;
if (PyType_Ready(&TestObject_type) < 0)
@ -549,6 +594,9 @@ PyInit_lldwarf(void)
Py_INCREF(&LineNumberRow_type);
PyModule_AddObject(m, "LineNumberRow", (PyObject *)&LineNumberRow_type);
Py_INCREF(&Range_type);
PyModule_AddObject(m, "Range", (PyObject *)&Range_type);
#ifdef TEST_LLDWARFOBJECT
Py_INCREF(&TestObject_type);
PyModule_AddObject(m, "_TestObject", (PyObject *)&TestObject_type);

116
lldwarf/ranges.c Normal file
View File

@ -0,0 +1,116 @@
#include "lldwarf.h"
PyObject *LLDwarf_ParseRangeList(Py_buffer *buffer, Py_ssize_t *offset,
Py_ssize_t address_size)
{
PyObject *ranges;
ranges = PyList_New(0);
if (!ranges)
return NULL;
for (;;) {
Range *range;
uint64_t start, end;
uint32_t tmp;
int ret;
switch (address_size) {
case 4:
if (read_u32(buffer, offset, &tmp) == -1)
goto err;
if (tmp == UINT32_C(0xffffffff))
start = UINT64_C(0xffffffffffffffff);
else
start = tmp;
if (read_u32(buffer, offset, &tmp) == -1)
goto err;
end = tmp;
break;
case 8:
if (read_u64(buffer, offset, &start) == -1)
goto err;
if (read_u64(buffer, offset, &end) == -1)
goto err;
break;
default:
PyErr_Format(PyExc_ValueError, "unsupported address size %ld",
(long)address_size);
goto err;
}
if (start == 0 && end == 0)
break;
range = PyObject_New(Range, &Range_type);
if (!range)
goto err;
range->start = start;
range->end = end;
ret = PyList_Append(ranges, (PyObject *)range);
Py_DECREF((PyObject *)range);
if (ret == -1)
goto err;
}
return ranges;
err:
Py_DECREF(ranges);
return NULL;
}
static PyMemberDef Range_members[] = {
{"start", T_UINT64T, offsetof(Range, start), 0,
"starting address (inclusive) of the range"},
{"end", T_UINT64T, offsetof(Range, end), 0,
"ending address (exclusive) of the range"},
{},
};
#define Range_DOC \
"Range(address, length) -> new address range\n" \
"Create a new address range.\n\n" \
"Arguments:\n" \
"start -- integer start address\n" \
"end -- integer end address\n"
PyTypeObject Range_type = {
PyVarObject_HEAD_INIT(NULL, 0)
"drgn.lldwarf.Range", /* tp_name */
sizeof(Range), /* tp_basicsize */
0, /* tp_itemsize */
LLDwarfObject_dealloc, /* tp_dealloc */
NULL, /* tp_print */
NULL, /* tp_getattr */
NULL, /* tp_setattr */
NULL, /* tp_as_async */
LLDwarfObject_repr, /* tp_repr */
NULL, /* tp_as_number */
NULL, /* tp_as_sequence */
NULL, /* tp_as_mapping */
NULL, /* tp_hash */
NULL, /* tp_call */
NULL, /* tp_str */
NULL, /* tp_getattro */
NULL, /* tp_setattro */
NULL, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
Range_DOC, /* tp_doc */
NULL, /* tp_traverse */
NULL, /* tp_clear */
LLDwarfObject_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
NULL, /* tp_iter */
NULL, /* tp_iternext */
NULL, /* tp_methods */
Range_members, /* tp_members */
NULL, /* tp_getset */
NULL, /* tp_base */
NULL, /* tp_dict */
NULL, /* tp_descr_get */
NULL, /* tp_descr_set */
0, /* tp_dictoffset */
LLDwarfObject_init, /* tp_init */
};

View File

@ -57,10 +57,11 @@ module = Extension(
'lldwarf/module.c',
'lldwarf/object.c',
'lldwarf/abbrev.c',
'lldwarf/arange.c',
'lldwarf/aranges.c',
'lldwarf/cu.c',
'lldwarf/die.c',
'lldwarf/line.c',
'lldwarf/ranges.c',
],
extra_compile_args=['-DTEST_LLDWARFOBJECT'],
)

View File

@ -162,9 +162,9 @@ class TestParseDie(unittest.TestCase):
}
self.assertDie(header, abbrev_table, b'\x01\xff\xff\xff\xff\xff\xff\xff\x7f',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.addr, 2**63 - 1),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.addr, 2**63 - 1),)))
self.assertDie(header32addr, abbrev_table, b'\x01\xff\xff\xff\x7f',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.addr, 2**31 - 1),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.addr, 2**31 - 1),)))
bogus_header = lldwarf.CompilationUnitHeader(
unit_length=200,
@ -186,15 +186,15 @@ class TestParseDie(unittest.TestCase):
}
self.assertDie(header, abbrev_table, b'\x01\x04aaaa',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.block1, (2, 4)),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.block1, (2, 4)),)))
self.assertDie(header, abbrev_table, b'\x02\x01\x00b',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.block2, (3, 1)),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.block2, (3, 1)),)))
self.assertDie(header, abbrev_table, b'\x03\x10\x00\x00\x00' + b'z' * 16,
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.block4, (5, 16)),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.block4, (5, 16)),)))
self.assertDie(header, abbrev_table, b'\x04\x03xyz',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.block, (2, 3)),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.block, (2, 3)),)))
self.assertDie(header, abbrev_table, b'\x05\x0f012345678901234',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.exprloc, (2, 15)),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.exprloc, (2, 15)),)))
with self.assertRaisesRegex(ValueError, 'attribute length too big'):
lldwarf.parse_die(header, None, abbrev_table, 0, b'\x05\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01')
@ -207,16 +207,16 @@ class TestParseDie(unittest.TestCase):
}
self.assertDie(header, abbrev_table, b'\x01a',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.data1, b'a'),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.data1, b'a'),)))
self.assertDie(header, abbrev_table, b'\x02ab',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.data2, b'ab'),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.data2, b'ab'),)))
self.assertDie(header, abbrev_table, b'\x03abcd',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.data4, b'abcd'),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.data4, b'abcd'),)))
self.assertDie(header, abbrev_table, b'\x04abcdefgh',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.data8, b'abcdefgh'),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.data8, b'abcdefgh'),)))
def test_constant(self):
abbrev_table = {
@ -225,10 +225,10 @@ class TestParseDie(unittest.TestCase):
}
self.assertDie(header, abbrev_table, b'\x01\x64',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.udata, 100),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.udata, 100),)))
self.assertDie(header, abbrev_table, b'\x02\x7f',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.sdata, -1),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.sdata, -1),)))
with self.assertRaises(OverflowError):
lldwarf.DwarfDie(None, None, 0, 0, DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.udata, 2**64),))
@ -244,11 +244,11 @@ class TestParseDie(unittest.TestCase):
}
self.assertDie(header, abbrev_table, b'\x01\x01',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.flag, True),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.flag, True),)))
self.assertDie(header, abbrev_table, b'\x01\x00',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.flag, False),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.flag, False),)))
self.assertDie(header, abbrev_table, b'\x02',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.flag_present, True),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.flag_present, True),)))
def test_reference(self):
abbrev_table = {
@ -261,17 +261,17 @@ class TestParseDie(unittest.TestCase):
}
self.assertDie(header, abbrev_table, b'\x01\xff',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.ref1, 255),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.ref1, 255),)))
self.assertDie(header, abbrev_table, b'\x02\x10\x27',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.ref2, 10000),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.ref2, 10000),)))
self.assertDie(header, abbrev_table, b'\x03\x00\x00\x00\x80',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.ref4, 2**31),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.ref4, 2**31),)))
self.assertDie(header, abbrev_table, b'\x04\x00\x00\x00\x00\x00\x00\x00\x80',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.ref8, 2**63),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.ref8, 2**63),)))
self.assertDie(header, abbrev_table, b'\x05\x00\x00\x00\x00\x00\x00\x00\x80',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.ref_sig8, 2**63),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.ref_sig8, 2**63),)))
self.assertDie(header, abbrev_table, b'\x06\x00',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.ref_udata, 0),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.ref_udata, 0),)))
def test_sec_offset(self):
abbrev_table = {
@ -279,9 +279,9 @@ class TestParseDie(unittest.TestCase):
}
self.assertDie(header, abbrev_table, b'\x01\xff\xff\xff\x7f',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.sec_offset, 2**31 - 1),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.sec_offset, 2**31 - 1),)))
self.assertDie(header64, abbrev_table, b'\x01\xff\xff\xff\xff\xff\xff\xff\x7f',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.sec_offset, 2**63 - 1),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.sec_offset, 2**63 - 1),)))
def test_strp(self):
abbrev_table = {
@ -289,9 +289,9 @@ class TestParseDie(unittest.TestCase):
}
self.assertDie(header, abbrev_table, b'\x01\xff\xff\xff\x7f',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.strp, 2**31 - 1),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.strp, 2**31 - 1),)))
self.assertDie(header64, abbrev_table, b'\x01\xff\xff\xff\xff\xff\xff\xff\x7f',
(DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.strp, 2**63 - 1),)))
(DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.strp, 2**63 - 1),)))
def test_string(self):
abbrev_table = {
@ -301,7 +301,7 @@ class TestParseDie(unittest.TestCase):
}
self.assertDie(header, abbrev_table, b'\x01foo\0asdf\0',
(DW_TAG.lo_user, None,
(DW_TAG.lo_user, (),
((DW_AT.lo_user, DW_FORM.string, (1, 3)),
(DW_AT.lo_user, DW_FORM.string, (5, 4)))))
@ -328,10 +328,10 @@ class TestParseDie(unittest.TestCase):
2: lldwarf.AbbrevDecl(DW_TAG.lo_user + 1, False, ((DW_AT.lo_user + 1, DW_FORM.sdata),)),
}
siblings = lldwarf.parse_die_siblings(header, None, abbrev_table, 0, b'\x01\x01\x02\x02\x00')
self.assertEqual(siblings, [
lldwarf.DwarfDie(header, None, 0, 2, DW_TAG.lo_user, None, ((DW_AT.lo_user, DW_FORM.udata, 1),)),
lldwarf.DwarfDie(header, None, 2, 2, DW_TAG.lo_user + 1, None, ((DW_AT.lo_user + 1, DW_FORM.sdata, 2),)),
])
self.assertEqual(siblings, (
lldwarf.DwarfDie(header, None, 0, 2, DW_TAG.lo_user, (), ((DW_AT.lo_user, DW_FORM.udata, 1),)),
lldwarf.DwarfDie(header, None, 2, 2, DW_TAG.lo_user + 1, (), ((DW_AT.lo_user + 1, DW_FORM.sdata, 2),)),
))
def test_siblings_skip(self):
abbrev_table = {
@ -341,7 +341,7 @@ class TestParseDie(unittest.TestCase):
siblings = lldwarf.parse_die_siblings(header, None, abbrev_table, 0, b'\x01\x04\x02\x02\x02\x03\x00')
parent_die = lldwarf.DwarfDie(header, None, 0, 2, DW_TAG.lo_user, None, ((DW_AT.sibling, DW_FORM.udata, 4),))
del parent_die.children
self.assertEqual(siblings, [
self.assertEqual(siblings, (
parent_die,
lldwarf.DwarfDie(header, None, 4, 2, DW_TAG.lo_user + 1, None, ((DW_AT.lo_user + 1, DW_FORM.sdata, 3),)),
])
lldwarf.DwarfDie(header, None, 4, 2, DW_TAG.lo_user + 1, (), ((DW_AT.lo_user + 1, DW_FORM.sdata, 3),)),
))