suresh
suresh

Reputation: 4224

gdb - debugging with pipe

Say I have a two programs named blah and ret. I want to debug blah program which receives input from ret program via I/O redirection. How do I debug the blah program in the following case using gdb?

bash> ret | blah 

Upvotes: 42

Views: 25920

Answers (3)

kxxt
kxxt

Reputation: 13

To solve this problem, I added a debugger launcher feature to tracexec 0.4.0: https://github.com/kxxt/tracexec/releases/tag/v0.4.0

Basically this solution stops processes after the syscall-exit stop of execve{,at} syscall and attaches gdb to them. And it could be used to debug both ends of the pipe.

This answer is the same as my answer in Debugging a specific subprocess because my solution is general enough.

let's say we have the following two simple programs a and b:

fn main() {
    println!("Hello from A!");
}
use std::io::stdin;
use std::io::Read;

fn main() {
  println!("Hello from B!");
  let mut input = String::new();
  stdin().read_to_string(&mut input).unwrap();
  println!("Stdin: {}", input);
}

and a script that executes them. (I am using a shell script here but it doesn't matter if a python/perl script is used here.)

./a --complex-arguments-here | ./b --other --complex --arguments --here

On a linux system with display and konsole installed, the following command launches tracexec tui with the shell script:

tracexec tui -t -b sysexit:in-filename:/a -b sysexit:in-filename:/b --default-external-command "konsole -e gdb -ex cont -ex cont -p {{PID}}" -- ./shell-script

Then in the TUI, gdb could be attached to a and b in the hit manager.

A video is available here if the text above is not clear: https://github.com/kxxt/tracexec/assets/18085551/72c755a5-0f2f-4bf9-beb9-98c8d6b5e5fd

Full code is available at https://github.com/kxxt/tracexec/tree/main/demonstration/gdb-launcher

Upvotes: 0

Cheng Sun
Cheng Sun

Reputation: 1035

GDB's run command uses bash to perform redirection. A simple way to achieve the equivalent of ret | blah is to use bash's process substitution feature.

$ gdb blah
...
(gdb) run < <(ret)

Explanation: bash substitutes <(ret) with something like /dev/fd/123, which is a file descriptor of the stdout of ret. We can use that fd similarly to a named FIFO as described in the other answer, except that we don't have to manually create it ourselves, nor worry about the lifetime of the ret process.

Upvotes: 19

P Shved
P Shved

Reputation: 99274

At first, you may run the program and debug it by pid. This solution, of course, doesn't cover all cases.

Another approach is to use Linux capabilities for inter-process communication. In short, you redirect the output of ret to a FIFO special file ("named pipe") and then read from that FIFO via debugger. Here's how it's done. From bash, run:

mkfifo foo

This creates a special file in your directory that will serve as a named pipe. When you write text to this file (using the same syntax echo "Hello" >foo), the writing program will block until someone reads the data from the file (cat <foo, for instance). In our case, a gdb-controlled process will read from this file.

After you created a fifo, run from bash:

ret > foo &   # ampersand because it may block as nobody is reading from foo
gdb blah

Then, in gdb prompt, run

run <foo

And get the desired effect. Note that you can't read the data from the fifo (as well as from a usual pipe) twice: when you've read all the data, the blah process dies and you should repeat the command writing to foo (you may do it from the other shell window).

When you're done, remove the fifo with rm foo (or place it into the directory where it will automatically be removed upon system restart, such as /tmp).

Upvotes: 51

Related Questions