def __init__(self, args, first, _): cmd_parts = self.parse_args(args, [2, 3] if first else [1, 2], ';') self.arch = gdb.current_arch() if first: self.start_address = eval_int(cmd_parts.pop(0)) end = cmd_parts.pop(0) self.end_address = None if end == 'NULL' else eval_int(end) self.count = eval_int(cmd_parts.pop(0)) if cmd_parts else None
def update_exact(self, direction, addr): '''Add or remove a function from tracing that starts at a given memory address instead of whose name matches a regular expression.''' func_name, offset = func_and_offset(int(addr, base=0)) # Checks we don't give an invalid address to remove_addr_trace(), or # add_tracer(). if func_name is None or offset != 0: raise ValueError('{} does not start a function'.format(addr)) if direction == '+': arch = gdb.current_arch() return add_tracer(helpers.FakeSymbol(func_name, addr), arch) remove_addr_trace(int(addr, 0))
def __init__(self, maxdepth, file_regexp, start_expr, unique_funcs): self.func_stack = [] self.arch = gdb.current_arch() self.maxdepth = maxdepth self.file_regexp = file_regexp # User asked for specific files, we only know the filename if there is # debugging information, hence ignore all functions that don't have # debugging info. self.dont_fallback = re.match(self.file_regexp, '') is not None self.unique_funcs = unique_funcs self.all_seen = set() # hypothetical_stack checks for recursion, but also allows the # hypothetical-call-stack walker to see what the current stack is. type(self).hypothetical_stack = [] self.start_expr = start_expr
def __init__(self, args, first, _): self.cmd_parts = self.parse_args(args, [3,3] if first else [2,2], ';') self.maxdepth = eval_int(self.cmd_parts[-1]) self.file_regex = self.cmd_parts[-2].strip() # User asked for specific files, wo only know the filename if there is # debugging information, hence ignore all functions that don't have # debugging info. self.dont_fallback = re.match(self.file_regex, '') is not None self.func_stack = [] # hypothetical_stack checks for recursion, but also allows the # hypothetical-call-stack walker to see what the current stack is. type(self).hypothetical_stack = [] if first: self.__add_addr(eval_int(self.cmd_parts[0]), 0) self.arch = gdb.current_arch()
def trace_regexp(regexp): 'Trace all functions matching `regexp` in the current symbol table.' file_regex, func_regex = file_func_split(regexp) if file_regex is None: file_regex = '.*' if gdb.parameter('call-graph-nondebug') else '.+' arch = gdb.current_arch() # We have to break on address to distinguish symbols with the same name in # different files. # # In order to have nice output, we create a string that describes the # function for a human -- though symbols with the same name will have the # same output for entry tracepoints. for symbol in gdb.search_symbols(func_regex, file_regex, gdb.parameter('call-graph-dynlibs')): add_tracer(symbol, arch)
def __init__(self, start_expr, end_expr, count_expr): self.arch = gdb.current_arch() self.start_expr = start_expr self.end_expr = end_expr self.count_expr = count_expr
def function_disassembly(func_addr, arch=None, use_fallback=True): '''Return the disassembly and filename of the function at `func_addr`. If there are no debugging symbols, return the None as the filename. (function_disassembly, function_name or None, function_filename or None) If we have debugging information, but we can't find a function at the address specified, we return '' for the function_name and function_filename. If `use_fallback` is set to False, and there is are no debugging symbols for the current function, return (None, None, None). ''' arch = arch or gdb.current_arch() try: func_block = gdb.block_for_pc(func_addr) # Bit of a hack, but I want the same thing to happen when gdb can't # find the object file (and hence raises an exception itself) as when # it just can't find the block. if func_block is None: raise RuntimeError('Cannot locate object file for block.') except RuntimeError as e: if e.args != ('Cannot locate object file for block.', ): raise e else: orig_block = func_block # This has happened a few times -- find out why/when. # When the file was compiled without debugging. if func_block is None: print(func_addr) # Often enough that it's a problem, despite being given the start of a # function as the address, we get a child block of the function. # Functions I've seen this happen with (all when inspecting a debug # build of neovim). # # src/valgrind.c je_valgrind_make_mem_undefined # src/valgrind.c je_valgrind_make_mem_defined # include/jemalloc/internal/atomic.h je_atomic_add_uint32 # # I used the below to get this output. # if not func_block.function: # print(func_addr) # above = func_block.superblock.function # if above: # print(above.symtab.filename, above.name) while func_block and func_block.function is None: func_block = func_block.superblock if func_block: function_name = func_block.function.name function_filename = func_block.function.symtab.filename else: # Print this out for my information -- I don't know of a case when # this would happen, so if it does I have a chance to learn. print('Function not found at {}'.format(func_addr)) func_block = orig_block function_name = '' function_filename = '' # No instruction is less than one byte. # If we provide an end point that is one byte less than the end of the # last instruction in the block, then this ends the disassembly on the # last instruction of the function. return (arch.disassemble(func_block.start, func_block.end-1), function_name, function_filename) if not use_fallback: return (None, None, None) # Fallback -- Use the gdb `disassemble` command to disassemble the entire # function, and find difference between the start and end of that function. # Rather than implementing a conversion function between a line in the # output of the `disassemble` command and a dictionary that matches the # output of the Architecture.disassemble() method, we just find the extent # of the current function with `disassemble {}`, and then return the list # made from Architecture.disassemble() directly. output = gdb.execute('disassemble {}'.format(func_addr), False, True) lines = output.splitlines() if not lines[0].startswith('Dump of assembler code for function'): raise RuntimeError('Failed to find function at {}'.format(func_addr)) # Avoid the 'End of assembler dump.' line that is actually last. start_addr = int(lines[1].split()[0], base=16) last_pos = int(lines[-2].split()[0], base=16) function_name = re.search('Dump of assembler code for function (\S+):', lines[0]) function_name = function_name.groups()[0] if function_name else None return arch.disassemble(start_addr, last_pos), function_name, None