Specially debugging line by line

北战南征 提交于 2020-01-02 07:36:12

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!