30
votes

I have a extremely complicated shell script, within which it calls a C++ program I want to debug via GDB. It is extremely hard to separate this c++ program from the shell since it has a lot of branches and a lot of environmental variables setting.

Is there a way to invoke GDB on this shell script? Looks like gdb requires me to call on a C++ program directly.

6
Damn... The same problem still exists. I would think a simple set follow-fork-mode child would be all we need to do to get to the code we are interested in...jww

6 Answers

21
votes

There are two options that you can do:

  1. Invoke GDB directly within the shell script. This would imply that you don't have standard in and standard out redirected.

  2. Run the shell script and then attach the debugger to the already running C++ process like so: gdb progname 1234 where 1234 is the process ID of the running C++ process.

If you need to do things before the program starts running then option 1 would be the better choice, otherwise option 2 is the cleaner way.

23
votes

In addition to options mentioned by @diverscuba23, you could do the following:

gdb --args bash <script>

(assuming it's a bash script. Else adapt accordingly)

3
votes

I would probably modify the script to always call gdb (and revert this later) or add an option to call gdb. This will almost always be the easiest solution.

The next easiest would be to temporarily move your executable and replace it with a shell script that runs gdb on the moved program. For example, in the directory containing your program:

$ mv program _program
$ (echo "#!/bin/sh"; echo "exec gdb $PWD/_program") > program
$ chmod +x program
3
votes

Modify the c++ application to print its pid and sleep 30 seconds (perhaps based on environment or an argument). Attach to the running instance with gdb.

2
votes

Could you just temporarily add gdb to your script?

1
votes

Although the answers given are valid, sometimes you don't have permissions to change the script to execute gdb or to modify the program to add additional output to attach through pid.

Luckily, there is yet another way through the power of bash

Use ps, grep and awk to pick-out the pid for you after its been executed. You can do this by either wrapping the other script with your own or by just executing a command yourself.


That command might look something like this:

process.sh

#!/usr/bin/env bash

#setup for this example
#this will execute vim (with cmdline options) as a child to bash
#we will attempt to attach to this process
vim ~/.vimrc

To get gdb to attach, we'd just need to execute the following:

gdb --pid $(ps -ef | grep -ve grep | grep vim | awk '{print $2}')

  1. I use ps -ef here to list the processes and their arguments. Sometimes, you'll have multiple instances of a program running and need to further grep down to the one you want
  2. the grep -ve grep is there because the f option to ps will include the next grep in its list. If you don't need the command arguments for additional filtering, don't include the -f option for ps and ignore this piece
  3. grep vim is where we're finding our desired process. If you needed more filtering, you could just do something like grep -E "vim.*vimrc" and filter down to exactly the process that you're trying to attach to
  4. awk '{print $2}' simply outputs just the process' pid to stdout. Use $1 if you're using ps -e instead of ps -ef

My normal setup is to run such script that starts my process in 1 tmux pane and having typed something similar to the above in a bottom pane. That way if I need to adjust the filtering (for whatever reason), I can do it pretty quickly.


Usually though, it will be the same for a specific instance and I want to just attach automatically after its been started. I'll do the following instead:

runGdb.py

#!/usr/bin/env bash

./process.sh &

PID=$(ps -ef  | grep -ve grep | grep -E "vim.*vimrc" | awk '{print $2}')
#or
#PID=$(ps -e | grep vim | awk '{print $1}')

gdb --pid $PID

This assumes that the original process can be safely run in the background.