Skip to content

Instantly share code, notes, and snippets.

@quark-zju
Last active December 31, 2023 14:14
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save quark-zju/a20ae638601d2fac476e to your computer and use it in GitHub Desktop.
Save quark-zju/a20ae638601d2fac476e to your computer and use it in GitHub Desktop.
Trace all function calls using gdb
#!/usr/bin/env python
try:
import gdb
inside_gdb = True
except ImportError:
inside_gdb = False
if inside_gdb:
import re
class FunctionEnterBreakpoint(gdb.Breakpoint):
def __init__(self, spec):
self._function_name = spec
super(FunctionEnterBreakpoint, self).__init__(spec, internal=True)
@staticmethod
def stack_depth():
depth = -1
frame = gdb.newest_frame()
while frame:
frame = frame.older()
depth += 1
return depth
def stop(self):
info_args = gdb.execute(
'info args', from_tty=False, to_string=True)
if info_args == 'No arguments.\n':
args_str = ''
else:
args_str = ', '.join(info_args.splitlines())
gdb.write('%s%s (%s)\n' % (' ' * self.stack_depth(),
self._function_name,
args_str),
gdb.STDLOG)
return False # continue immediately
class TraceFunctionCommand(gdb.Command):
def __init__(self):
super(TraceFunctionCommand, self).__init__(
'trace-functions',
gdb.COMMAND_SUPPORT,
gdb.COMPLETE_NONE,
True)
@staticmethod
def extract_function_names():
info_functions = gdb.execute(
'info functions', from_tty=False, to_string=True)
result = []
current_file = None
for line in info_functions.splitlines():
if line.startswith('File '):
current_file = line[5:-1]
elif line.startswith('Non-debugging'):
break
elif current_file:
match = re.search('[\s*]([^\s*]+)\(', line)
if match and current_file.find('/usr/include') == -1:
function_name = match.group(1)
result.append(function_name)
return result
def invoke(self, arg, from_tty):
# arg: quick name filter
function_names = self.extract_function_names()
if arg:
function_names = [n for n in function_names
if n.find(arg) >= 0]
count = 0
verbose = (len(function_names) > 1000)
for name in function_names:
FunctionEnterBreakpoint(name)
count += 1
if verbose and count % 128 == 0:
gdb.write('\r%d / %d breakpoints set' %
(count, len(function_names)),
gdb.STDERR)
if verbose:
gdb.write('\r%(n)d / %(n)d breakpoints set\n' %
{'n': len(function_names)}, gdb.STDERR)
TraceFunctionCommand()
else: # outside gdb
import os
import subprocess
import sys
import tempfile
if len(sys.argv) < 2:
arg0 = os.path.basename(__file__)
print('Usage: [NAME=name] %s binary [args]' % arg0)
sys.exit(0)
self_path = os.path.realpath(__file__)
command_path = os.path.join(tempfile.gettempdir(),
'gdb-trace.%d.cmd' % os.getpid())
with open(command_path, 'w') as f:
f.write('source %s\n'
'trace-functions %s\n'
'run\n'
'quit\n'
% (self_path, os.getenv('NAME', '')))
gdb_cmd = ['gdb', '--batch', '--command=%s' % command_path,
'--args'] + sys.argv[1:]
subprocess.call(gdb_cmd)
os.unlink(command_path)
@quark-zju
Copy link
Author

Example:

gdb-trace.py factor 5555

main (argc = 2, argv = 0x7fffffffe4a8)
  set_program_name (argv0 = 0x7fffffffe7a5 "/usr/bin/factor")
  print_factors (input = 0x7fffffffe7d3 "5555")
    strto2uintmax (hip = 0x7fffffffe358, lop = 0x7fffffffe350, s = 0x7fffffffe7d3 "5555")
    devmsg (fmt = 0x4163a0 "[using single-precision arithmetic] ")
    print_factors_single (t1 = 0, t0 = 5555)
      print_uintmaxes (t1 = 0, t0 = 5555)
      factor (t1 = 0, t0 = 5555, factors = 0x7fffffffe1e0)
        factor_using_division (t1p = 0x7fffffffe198, t1 = 0, t0 = 5555, factors = 0x7fffffffe1e0)
          factor_insert_refind (factors = 0x7fffffffe1e0, p = 3, i = 1, off = 1)
            factor_insert_multiplicity (factors = 0x7fffffffe1e0, prime = 5, m = 1)
          factor_insert_refind (factors = 0x7fffffffe1e0, p = 3, i = 1, off = 3)
            factor_insert_multiplicity (factors = 0x7fffffffe1e0, prime = 11, m = 1)
        prime2_p (n1 = 0, n0 = 101)
          prime_p (n = 101)
        factor_insert_large (factors = 0x7fffffffe1e0, p1 = 0, p0 = 101)
          factor_insert_multiplicity (factors = 0x7fffffffe1e0, prime = 101, m = 1)
      umaxtostr (i = 5, buf = 0x7fffffffe1c0 "\f")
      umaxtostr (i = 11, buf = 0x7fffffffe1c0 "\f")
      umaxtostr (i = 101, buf = 0x7fffffffe1c0 "\f")
5555: 5 11 101
      close_stdout ()
        close_stream (stream = 0x7ffff7b5f800 <_IO_2_1_stdout_>)
          rpl_fclose (fp = 0x7ffff7b5f800 <_IO_2_1_stdout_>)
            rpl_fflush (stream = 0x7ffff7b5f800 <_IO_2_1_stdout_>)
        close_stream (stream = 0x7ffff7b5f700 <_IO_2_1_stderr_>)
          rpl_fclose (fp = 0x7ffff7b5f700 <_IO_2_1_stderr_>)
            rpl_fflush (stream = 0x7ffff7b5f700 <_IO_2_1_stderr_>)
[Inferior 1 (process 3516) exited normally]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment