preserving arguments through the parent-child exec chain and keeping history

Daniel Gutson <[email protected]>
Newsgroups gmane.comp.gdb.devel
Message-ID <CAF5HaEXi+Z5fWfCht_KgvgNoVY92Sh=GmWjtt+WRrjwiUHtY2A@mail.gmail.com>
Consider these facts in a scenario of an exec chain with set
follow-fork-mode child, for example gcc -> cc1plus:

 * after the last inferior finishes, gdb "remains" (remembers) in the
last exec, so issueing "run" runs the latest.
 * the arguments passed to the main() of the last exec are lost, so
"show args" shows ""
 * the very initial arguments passed to gdb with set args or --args
used for the first program are also lost
 * there is no way to "back trace" the history of executed programs,
of course not knowing their arguments; only the last program is
preserved.

To do a proof of concept, the attached py script keeps tracks of the
arguments passed to main() and set args accordingly, so
in the example of gcc calling cc1plus now we can repeatedly run
cc1plus with the arguments that gcc passed.

However, I would like to discuss a more general and better solution:
the ability to keep track of all executed
binaries with their arguments, and to be able to "navigate" through
that list once all the inferiors finished, so we can
re-run starting at any point in the history of chained execs with the
same arguments it was called before. This list
would be re-populated per-run basis.

Despite I think we could develop this enhancement as a python script,
do you people think that, in case of interest
in the general idea, implementing this as a core feature would be better?

For example, given the command line
    gdb --args ~/bin/gcc61/bin/g++61 -Wall -Wextra -Wpedantic
-std=c++14 -ggdb3 shuffle.cpp
and issueing
   (gdb) set follow-fork-mode child
   (gdb) run

Then, after everything finishes, I propose something like this:

(gdb) info execs
  Id    Path
                                                 args
  1     ../../../gcc-6.1.0/gcc/gcc-main.c
                                     -Wall -Wextra -Wpedantic
-std=c++14 -ggdb3 shuffle.cpp
  2     /home/dgutson/bin/gcc61/libexec/gcc/x86_64-pc-linux-gnu/6.1.0/cc1plus
 -quiet -imultiarch x86_64-linux-gnu -dD -D_GNU_SOURCE shuffle.cpp
-quiet -dumpbase shuffle.cpp -mtune=generic -march=x86-64 -auxbase
shuffle -ggdb3 -Wall -Wextra -Wpedantic -std=c++14 -o /tmp/ccuquQNR.s

(gdb) exec 2


The first command (info execs) shows what programs where exec'd after
a full run (assuming follow-fork-mode child).
The second command re-runs starting from cc1plus with the arguments
that gcc passed to cc1plus. Similarly,
issueing "exec 1" re-runs everything again.

Feedback?

Thanks,

   Daniel.

ps: sorry for exceeding the 79-cols limit

-- 

Daniel F. Gutson
Engineering Manager

San Lorenzo 47, 3rd Floor, Office 5
Córdoba, Argentina

Phone:   +54 351 4217888 / +54 351 4218211
Skype:    dgutson
LinkedIn: http://ar.linkedin.com/in/danielgutson
preserve_args.py (text/x-python, 1.4 KB)
# Copyright (C) Martin Galvan 2016, [email protected]
import gdb

class MainBreakpoint(gdb.Breakpoint):
    def stop(self):
        frame = gdb.selected_frame()
        block = frame.block()
        args = []

        # Go upwards until we find the block representing the function.
        # We probably don't need to check if we arrived to the static block,
        # but it shouldn't hurt.
        while not block.function and not block.is_static:
            block = block.superblock

        if block.function:
            args = [symbol.value(frame) for symbol in block if symbol.is_argument]

        if args:
            argc = int(args[0])
            argv = args[1]

            # argv is a gdb.Value representing an array. We can access the array
            # elements using the [] operator.
            # Don't grab the program name since gdb automatically sets it as an
            # arg, even if 'show args' doesn't show it.
            args = [argv[i].string() for i in range(1, argc)]

        args = ' '.join(args)
        gdb.execute('set args {0}'.format(args))

        return True

#gdb.execute('set follow-fork-mode child')
MainBreakpoint('main')

# Run to a's main.
#gdb.execute('run')

# Run to b's main.
#gdb.execute('continue')
#gdb.execute('show args')

# Re-run b.
#gdb.execute('run')
#gdb.execute('show args')
#gdb.execute('continue')

# b should show us the same arguments we used when execv'ing it from a.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.