问题
I have a script written in Pascal. I would to debug it in this way: stop at every line, dump values of all variables in memory and go to the next line. Is it possible to do it with gdb or some other open-source tool for Linux?
回答1:
Compile file with option -g
:
fpc/gpc -g file.pas
Run gdb
for this file:
gdb file
Set all of needed variables:
display first_var
display second_var
...
Start debugging:
start
By pressing s
you can continue to the next line.
回答2:
I'll present a proof-of-concept that steps through a (single threaded) program, dumping all variables by using GDB's Python API:
# 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()
Given the following Tower of Hanoi program in C:
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;
}
Running gcc -g hanoi.c -o hanoi
then gdb -x dump-vars-each-step.py hanoi
prints:
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 <hanoi>
main: {int ()} 0x400400 <main>
peg_positions: {0, 0}
22 hanoi(N - 1, 0, 2);
N: N
hanoi: {void (int, int, int)} 0x40050c <hanoi>
main: {int ()} 0x400400 <main>
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: <optimized out>
N: N
hanoi: {void (int, int, int)} 0x40050c <hanoi>
main: {int ()} 0x400400 <main>
peg_positions: {0, 0}
9 int tmp = (0 + 1 + 2) - src - dst;
n: 1
src: 0
dst: 2
tmp: <optimized out>
N: N
hanoi: {void (int, int, int)} 0x40050c <hanoi>
main: {int ()} 0x400400 <main>
peg_positions: {0, 0}
8 {
n: 1
src: 0
dst: 2
tmp: <optimized out>
N: N
hanoi: {void (int, int, int)} 0x40050c <hanoi>
main: {int ()} 0x400400 <main>
peg_positions: {0, 0}
11 if (n == 0) {
n: 1
src: 0
dst: 2
tmp: 1
N: N
hanoi: {void (int, int, int)} 0x40050c <hanoi>
main: {int ()} 0x400400 <main>
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 <hanoi>
main: {int ()} 0x400400 <main>
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 <hanoi>
main: {int ()} 0x400400 <main>
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 <hanoi>
main: {int ()} 0x400400 <main>
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 <hanoi>
main: {int ()} 0x400400 <main>
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: <optimized out>
N: N
hanoi: {void (int, int, int)} 0x40050c <hanoi>
main: {int ()} 0x400400 <main>
peg_positions: {0, 0}
9 int tmp = (0 + 1 + 2) - src - dst;
n: 0
src: 0
dst: 1
tmp: <optimized out>
N: N
hanoi: {void (int, int, int)} 0x40050c <hanoi>
main: {int ()} 0x400400 <main>
peg_positions: {0, 0}
8 {
n: 0
src: 0
dst: 1
tmp: <optimized out>
N: N
hanoi: {void (int, int, int)} 0x40050c <hanoi>
main: {int ()} 0x400400 <main>
peg_positions: {0, 0}
11 if (n == 0) {
n: 0
src: <optimized out>
dst: 1
tmp: 2
N: N
hanoi: {void (int, int, int)} 0x40050c <hanoi>
main: {int ()} 0x400400 <main>
peg_positions: {0, 0}
12 peg_positions[n] = dst;
n: 0
src: <optimized out>
dst: 1
tmp: 2
N: N
hanoi: {void (int, int, int)} 0x40050c <hanoi>
main: {int ()} 0x400400 <main>
peg_positions: {0, 0}
18 }
n: 0
src: <optimized out>
dst: 1
tmp: 2
N: N
hanoi: {void (int, int, int)} 0x40050c <hanoi>
main: {int ()} 0x400400 <main>
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: <optimized out>
N: N
hanoi: {void (int, int, int)} 0x40050c <hanoi>
main: {int ()} 0x400400 <main>
peg_positions: {1, 0}
17 hanoi(n - 1, tmp, dst);
n: 1
src: 0
dst: 2
tmp: <optimized out>
N: N
hanoi: {void (int, int, int)} 0x40050c <hanoi>
main: {int ()} 0x400400 <main>
peg_positions: {1, 2}
11 if (n == 0) {
n: 1
src: 0
dst: 2
tmp: 0
N: N
hanoi: {void (int, int, int)} 0x40050c <hanoi>
main: {int ()} 0x400400 <main>
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 <hanoi>
main: {int ()} 0x400400 <main>
peg_positions: {1, 2}
18 }
n: 1
src: 0
dst: 2
tmp: 0
N: N
hanoi: {void (int, int, int)} 0x40050c <hanoi>
main: {int ()} 0x400400 <main>
peg_positions: {2, 2}
main () at hanoi.c:24
24 }
N: N
hanoi: {void (int, int, int)} 0x40050c <hanoi>
main: {int ()} 0x400400 <main>
peg_positions: {2, 2}
__libc_start_main (main=0x400400 <main>, argc=1, ubp_av=0x7fffffffde48, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffde38) at libc-start.c:257
257 exit (result);
__GI_exit (status=0) at exit.c:99
99 __run_exit_handlers (status, &__exit_funcs, true);
98 {
99 __run_exit_handlers (status, &__exit_funcs, true);
__run_exit_handlers (status=0, listp=0x3c777b16a8 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:36
36 {
41 while (*listp != NULL)
45 while (cur->idx > 0)
48 &cur->fns[--cur->idx];
47 const struct exit_function *const f =
49 switch (f->flavor)
73 cxafct = f->func.cxa.fn;
77 cxafct (f->func.cxa.arg, status);
75 PTR_DEMANGLE (cxafct);
77 cxafct (f->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 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:78
78 break;
45 while (cur->idx > 0)
82 *listp = cur->next;
83 if (*listp != NULL)
82 *listp = cur->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->_lock == NULL || _IO_lock_trylock (*fp->_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->_mode <= 0 && fp->_IO_write_ptr > fp->_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->_chain;
832 while (fp != NULL)
835 if (do_lock)
834 run_fp = fp;
835 if (do_lock)
838 if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_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->_chain;
832 while (fp != NULL)
835 if (do_lock)
834 run_fp = fp;
835 if (do_lock)
838 if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_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->_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->_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->_chain)
949 if (! (fp->_flags & _IO_UNBUFFERED)
983 fp->_mode = -1;
947 for (fp = (_IO_FILE *) _IO_list_all; fp; fp = fp->_chain)
949 if (! (fp->_flags & _IO_UNBUFFERED)
951 || (fp->_flags & _IO_IS_APPENDING))
950 && (! (fp->_flags & _IO_NO_WRITES)
953 && fp->_mode != 0)
983 fp->_mode = -1;
947 for (fp = (_IO_FILE *) _IO_list_all; fp; fp = fp->_chain)
949 if (! (fp->_flags & _IO_UNBUFFERED)
951 || (fp->_flags & _IO_IS_APPENDING))
950 && (! (fp->_flags & _IO_NO_WRITES)
983 fp->_mode = -1;
947 for (fp = (_IO_FILE *) _IO_list_all; fp; fp = fp->_chain)
_IO_cleanup () at genops.c:1018
1018 }
__run_exit_handlers (status=0, listp=<optimized out>, 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
Note how sometimes the local variables tmp
, src
are shown as optimized out. I'm on x86-64 where function arguments are passed in registers and sometimes tmp
was placed in register R while it is being used in the code but then gcc wants to use register R for something else and the value of tmp
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.
Producing a test program in Pascal, compiling it with debug info (pass -g) and testing it is left as an exercise for the reader ;)
回答3:
Yes it is possible to use the gdb
debugger on Pascal programs, provided your Pascal compiler is giving DWARF debug information. If using GNU Pascal (i.e. gpc
) you want to pass the -g option when invoking gpc
. If you use Free Pascal, it also accepts the -g option to fpc
.
Once you compiled your Pascal program with debugging information, you need to learn how to use GNU gdb. You probably want the display
, step
, next
, break
, print
, frame
, cont
commands (to gdb
) and some more.
回答4:
Because every graphical IDE supports does this out of the box, answers seems to gravitate to commandline tools assuming you dont what an IDE.
But if thats no problem .. Lazarus can do that. You would just compile, press F7 and F8 to step over / step into LOC, and see all variables in that scope in the inspector window.
Another IDE is Delphi but thats not opensource/free.
来源:https://stackoverflow.com/questions/15311543/specially-debugging-line-by-line