Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I'll present a proof-of-concept that steps through a (single threaded) program, dumping all variables by using GDB's Python API:</p> <pre><code># Usage: gdb -x dump-vars-each-step.py PROGRAM import gdb import re import logging LOG_LEVEL = logging.INFO def dump_all_vars(skip_libc_symbols=True): # gdb calls the source of its debug info an 'objfile' # libc_objfile_name. e.g. '/usr/lib/debug/lib64/libc-2.16.so.debug' libc_objfile_name_pattern = r'libc.*\.so' frame = gdb.newest_frame() while frame: symtab = frame.find_sal().symtab if symtab is not None: objfile_name = symtab.objfile.filename else: objfile_name = '' logging.debug('F: %s, %s' % (frame, objfile_name)) if skip_libc_symbols and re.match(r'libc.*\.so', os.path.basename(objfile_name)): return try: block = frame.block() except RuntimeError: block = None while block: logging.debug('B: %s, %s' % (block, block.function)) for symbol in block: try: value = frame.read_var(symbol, block) except gdb.error: # typedefs etc don't have a value pass else: sys.stdout.write('%s: %s\n' % (symbol, value)) block = block.superblock frame = frame.newer() def dump_globals(names): for i in names: s = gdb.lookup_global_symbol(i) if s is not None: sys.stdout.write('%s: %s\n' % (s, s.value())) inferior_alive = False def inferior_exited(e): global inferior_alive inferior_alive = False sys.stdout.write('inferior exited with code: %d\n' % (e.exit_code)) def run_and_dump_vars_each_step(): # precondition: inferior not running # NOTE: only handles single threaded programs global inferior_alive gdb.execute('start') inferior_alive = True gdb.events.exited.connect(inferior_exited) while inferior_alive: dump_all_vars() gdb.execute('step') gdb.execute('quit') logging.basicConfig(format='%(message)s', level=LOG_LEVEL) gdb.execute('set pagination no') gdb.execute('set python print-stack full') run_and_dump_vars_each_step() </code></pre> <p>Given the following Tower of Hanoi program in C:</p> <pre><code>enum { N = 2, }; int peg_positions[N]; static void hanoi(int n, int src, int dst) { int tmp = (0 + 1 + 2) - src - dst; if (n == 0) { peg_positions[n] = dst; return; } hanoi(n - 1, src, tmp); peg_positions[n] = dst; hanoi(n - 1, tmp, dst); } int main() { hanoi(N - 1, 0, 2); return 0; } </code></pre> <p>Running <code>gcc -g hanoi.c -o hanoi</code> then <code>gdb -x dump-vars-each-step.py hanoi</code> prints:</p> <pre><code>Reading symbols from /home/scottt/work/gdb-python-scripts/hanoi...done. Temporary breakpoint 1 at 0x400400: file hanoi.c, line 21. Temporary breakpoint 1, main () at hanoi.c:21 21 { N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {0, 0} 22 hanoi(N - 1, 0, 2); N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {0, 0} hanoi (n=n@entry=1, src=src@entry=0, dst=dst@entry=2) at hanoi.c:8 8 { n: 1 src: 0 dst: 2 tmp: &lt;optimized out&gt; N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {0, 0} 9 int tmp = (0 + 1 + 2) - src - dst; n: 1 src: 0 dst: 2 tmp: &lt;optimized out&gt; N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {0, 0} 8 { n: 1 src: 0 dst: 2 tmp: &lt;optimized out&gt; N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {0, 0} 11 if (n == 0) { n: 1 src: 0 dst: 2 tmp: 1 N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {0, 0} 9 int tmp = (0 + 1 + 2) - src - dst; n: 1 src: 0 dst: 2 tmp: 1 N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {0, 0} 15 hanoi(n - 1, src, tmp); n: 1 src: 0 dst: 2 tmp: 1 N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {0, 0} 9 int tmp = (0 + 1 + 2) - src - dst; n: 1 src: 0 dst: 2 tmp: 1 N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {0, 0} 15 hanoi(n - 1, src, tmp); n: 1 src: 0 dst: 2 tmp: 1 N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {0, 0} hanoi (n=n@entry=0, src=0, dst=dst@entry=1) at hanoi.c:8 8 { n: 0 src: 0 dst: 1 tmp: &lt;optimized out&gt; N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {0, 0} 9 int tmp = (0 + 1 + 2) - src - dst; n: 0 src: 0 dst: 1 tmp: &lt;optimized out&gt; N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {0, 0} 8 { n: 0 src: 0 dst: 1 tmp: &lt;optimized out&gt; N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {0, 0} 11 if (n == 0) { n: 0 src: &lt;optimized out&gt; dst: 1 tmp: 2 N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {0, 0} 12 peg_positions[n] = dst; n: 0 src: &lt;optimized out&gt; dst: 1 tmp: 2 N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {0, 0} 18 } n: 0 src: &lt;optimized out&gt; dst: 1 tmp: 2 N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {1, 0} hanoi (n=n@entry=1, src=src@entry=0, dst=dst@entry=2) at hanoi.c:16 16 peg_positions[n] = dst; n: 1 src: 0 dst: 2 tmp: &lt;optimized out&gt; N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {1, 0} 17 hanoi(n - 1, tmp, dst); n: 1 src: 0 dst: 2 tmp: &lt;optimized out&gt; N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {1, 2} 11 if (n == 0) { n: 1 src: 0 dst: 2 tmp: 0 N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {1, 2} 12 peg_positions[n] = dst; n: 1 src: 0 dst: 2 tmp: 0 N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {1, 2} 18 } n: 1 src: 0 dst: 2 tmp: 0 N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {2, 2} main () at hanoi.c:24 24 } N: N hanoi: {void (int, int, int)} 0x40050c &lt;hanoi&gt; main: {int ()} 0x400400 &lt;main&gt; peg_positions: {2, 2} __libc_start_main (main=0x400400 &lt;main&gt;, argc=1, ubp_av=0x7fffffffde48, init=&lt;optimized out&gt;, fini=&lt;optimized out&gt;, rtld_fini=&lt;optimized out&gt;, stack_end=0x7fffffffde38) at libc-start.c:257 257 exit (result); __GI_exit (status=0) at exit.c:99 99 __run_exit_handlers (status, &amp;__exit_funcs, true); 98 { 99 __run_exit_handlers (status, &amp;__exit_funcs, true); __run_exit_handlers (status=0, listp=0x3c777b16a8 &lt;__exit_funcs&gt;, run_list_atexit=run_list_atexit@entry=true) at exit.c:36 36 { 41 while (*listp != NULL) 45 while (cur-&gt;idx &gt; 0) 48 &amp;cur-&gt;fns[--cur-&gt;idx]; 47 const struct exit_function *const f = 49 switch (f-&gt;flavor) 73 cxafct = f-&gt;func.cxa.fn; 77 cxafct (f-&gt;func.cxa.arg, status); 75 PTR_DEMANGLE (cxafct); 77 cxafct (f-&gt;func.cxa.arg, status); 0x00000000004004c0 in __do_global_dtors_aux () Single stepping until exit from function __do_global_dtors_aux, which has no line number information. 0x0000000000400450 in deregister_tm_clones () Single stepping until exit from function deregister_tm_clones, which has no line number information. 0x00000000004004d2 in __do_global_dtors_aux () Single stepping until exit from function __do_global_dtors_aux, which has no line number information. 0x00000000004005f4 in _fini () Single stepping until exit from function _fini, which has no line number information. __run_exit_handlers (status=0, listp=0x3c777b16a8 &lt;__exit_funcs&gt;, run_list_atexit=run_list_atexit@entry=true) at exit.c:78 78 break; 45 while (cur-&gt;idx &gt; 0) 82 *listp = cur-&gt;next; 83 if (*listp != NULL) 82 *listp = cur-&gt;next; 83 if (*listp != NULL) 89 if (run_list_atexit) 90 RUN_HOOK (__libc_atexit, ()); _IO_cleanup () at genops.c:1003 1003 { 1006 int result = _IO_flush_all_lockp (0); 1003 { 1015 _IO_unbuffer_write (); _IO_unbuffer_write () at genops.c:958 958 if (fp-&gt;_lock == NULL || _IO_lock_trylock (*fp-&gt;_lock) == 0) _IO_cleanup () at genops.c:1003 1003 { 1006 int result = _IO_flush_all_lockp (0); _IO_flush_all_lockp (do_lock=do_lock@entry=0) at genops.c:819 819 { 825 __libc_cleanup_region_start (do_lock, flush_cleanup, 0); 819 { 825 __libc_cleanup_region_start (do_lock, flush_cleanup, 0); 831 fp = (_IO_FILE *) _IO_list_all; 832 while (fp != NULL) 830 last_stamp = _IO_list_all_stamp; 832 while (fp != NULL) 836 _IO_flockfile (fp); 835 if (do_lock) 834 run_fp = fp; 835 if (do_lock) 838 if (((fp-&gt;_mode &lt;= 0 &amp;&amp; fp-&gt;_IO_write_ptr &gt; fp-&gt;_IO_write_base) 848 if (do_lock) 852 if (last_stamp != _IO_list_all_stamp) 850 run_fp = NULL; 852 if (last_stamp != _IO_list_all_stamp) 859 fp = fp-&gt;_chain; 832 while (fp != NULL) 835 if (do_lock) 834 run_fp = fp; 835 if (do_lock) 838 if (((fp-&gt;_mode &lt;= 0 &amp;&amp; fp-&gt;_IO_write_ptr &gt; fp-&gt;_IO_write_base) 848 if (do_lock) 852 if (last_stamp != _IO_list_all_stamp) 850 run_fp = NULL; 852 if (last_stamp != _IO_list_all_stamp) 859 fp = fp-&gt;_chain; 832 while (fp != NULL) 835 if (do_lock) 834 run_fp = fp; 835 if (do_lock) 838 if (((fp-&gt;_mode &lt;= 0 &amp;&amp; fp-&gt;_IO_write_ptr &gt; fp-&gt;_IO_write_base) 848 if (do_lock) 852 if (last_stamp != _IO_list_all_stamp) 850 run_fp = NULL; 852 if (last_stamp != _IO_list_all_stamp) 859 fp = fp-&gt;_chain; 832 while (fp != NULL) 863 if (do_lock) 865 __libc_cleanup_region_end (0); 869 } _IO_cleanup () at genops.c:1015 1015 _IO_unbuffer_write (); _IO_unbuffer_write () at genops.c:947 947 for (fp = (_IO_FILE *) _IO_list_all; fp; fp = fp-&gt;_chain) _IO_cleanup () at genops.c:1006 1006 int result = _IO_flush_all_lockp (0); 1015 _IO_unbuffer_write (); _IO_unbuffer_write () at genops.c:947 947 for (fp = (_IO_FILE *) _IO_list_all; fp; fp = fp-&gt;_chain) 949 if (! (fp-&gt;_flags &amp; _IO_UNBUFFERED) 983 fp-&gt;_mode = -1; 947 for (fp = (_IO_FILE *) _IO_list_all; fp; fp = fp-&gt;_chain) 949 if (! (fp-&gt;_flags &amp; _IO_UNBUFFERED) 951 || (fp-&gt;_flags &amp; _IO_IS_APPENDING)) 950 &amp;&amp; (! (fp-&gt;_flags &amp; _IO_NO_WRITES) 953 &amp;&amp; fp-&gt;_mode != 0) 983 fp-&gt;_mode = -1; 947 for (fp = (_IO_FILE *) _IO_list_all; fp; fp = fp-&gt;_chain) 949 if (! (fp-&gt;_flags &amp; _IO_UNBUFFERED) 951 || (fp-&gt;_flags &amp; _IO_IS_APPENDING)) 950 &amp;&amp; (! (fp-&gt;_flags &amp; _IO_NO_WRITES) 983 fp-&gt;_mode = -1; 947 for (fp = (_IO_FILE *) _IO_list_all; fp; fp = fp-&gt;_chain) _IO_cleanup () at genops.c:1018 1018 } __run_exit_handlers (status=0, listp=&lt;optimized out&gt;, run_list_atexit=run_list_atexit@entry=true) at exit.c:92 92 _exit (status); __GI__exit (status=status@entry=0) at ../sysdeps/unix/sysv/linux/_exit.c:28 28 { 32 INLINE_SYSCALL (exit_group, 1, status); 34 INLINE_SYSCALL (exit, 1, status); 32 INLINE_SYSCALL (exit_group, 1, status); [Inferior 1 (process 32305) exited normally] inferior exited with code: 0 </code></pre> <p>Note how sometimes the local variables <code>tmp</code>, <code>src</code> are shown as <strong>optimized out</strong>. I'm on x86-64 where function arguments are passed in registers and sometimes <code>tmp</code> was placed in register <strong>R</strong> while it is being used in the code but then gcc wants to use register <strong>R</strong> for something else and the value of <code>tmp</code> gets overwritten. This shows that the quality of the variable dump will depend on the quality of the debug info your compiler generates. Recent gcc produces fairly good debug info for C/C++, I'm not sure how up to date the gcc Pascal port is or how good FPC does here.</p> <p>Producing a test program in Pascal, compiling it with debug info (pass <strong>-g</strong>) and testing it is left as an exercise for the reader ;)</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload