7

I'm trying to debug a Linux application that can receive commands via stdin, and it would be really useful to be able to see everything that gets written to stdin.

My first approach was to execute sudo cat /proc/$pid/fd/0, but it turns out that is an invalid approach, since both cat and my process are trying to consume the contents of that file descriptor, and only one process can win the race.

Another approach would be to use tail, but that won't work for reasons outlined here.

I could easily add debug logging to my application, and that would solve the problem, but I am interested to know if there's a more general approach that I'm missing.

1
  • great question ! Tried to do same thing as you literally yesterday and viola - there's a question. Jan 15, 2017 at 11:14

3 Answers 3

3

strace ?

Example below. Start a cat process which is reading stdin and writing to /tmp/foofile. Find the pid, strace it. And in the original cat window, enter some text, hey presto.

# cat >/tmp/foofile


# ps -ef|grep cat
steve     2134  1801  0 22:25 pts/2    00:00:00 cat
# strace -fp 2134
Process 2134 attached
read(0, "test\n", 65536)                = 5
write(1, "test\n", 5)                   = 5
read(0,

To just pluck out the reads from file descriptor 0:

strace -fp 2134 -e trace=read -o "|grep read.0,"
8
  • Thanks for the suggestion. Do you know of a way to get it to print out longer strings? $ strace -fp pidof cat Process 25975 attached - interrupt to quit read(0, "afdkjdjakskljjjjjjjjjjjjjjjjjjjj"..., 32768) = 4096 write(1, "afdkjdjakskljjjjjjjjjjjjjjjjjjjj"..., 4096) = 4096 read(0, Thanks, John Jul 9, 2015 at 21:36
  • 1
    Ah, it looks like there's a "-s" parameter that will do the trick. Jul 9, 2015 at 21:40
  • 1
    My current command is strace -s 4096 -e trace=read -fp 26471. The process I'm debugging does a lot of additional I/O, so I'm trying to come up with a way to filter so that it only prints read commands from stdin, not other file descriptors. I'll be sure to post an update once I figure it out. Jul 9, 2015 at 21:52
  • @JohnSaxton -e read=0 gives you a nice dump of standard input only. Jul 10, 2015 at 0:19
  • Thanks Steve for updating the answer to include -o "|grep read.0,". The final command I am using is strace -s 4096 -e trace=read -fp $pid -o "|grep read.0,". I'm a little concerned because of the performance hit associated with strace, but this is the best solution I can come up with, so I'll mark this as the accepted answer. Jul 10, 2015 at 5:19
0

On linux you can address an application's file-descriptors as named files in /dev/fd/[0-9]. And what you definitely can do with a named file and an input stream is tee the input into that file and to stdout. And so what I usually do when I find myself in your situation (as I often do) is tee input off to both my reading application and to /dev/fd/2 - stderr.

Like this:

seq 10 | tee /dev/fd/2 | wc -c
1
2
3
4
5
6
7
8
9
10
21

Of course, even if you weren't on a linux system the same thing might be done portably - if less specifically in some cases - by just doing ...| tee /dev/tty | ...

If you're talking about the terminal when you say stdin (as your link indicates), then you might still do the same thing, though it can get a little more tricky that way because of the kernel's line-buffering. And so what I would do in that case is to log all tty i/o by wrapping my command in luit - because I find it the more convenient of the two -though script could also work in much the same way.

luit is probably already installed on your system - it is typically packaged w/ xterm - and it is a very simple cli tool intended to do UTF-8 translations (which function might be disabled entirely via cli switch, but I've never found a reason to do so) for terminal applications which don't understand it.

It works by layering its own pty - for which it owns the master fd - beneath the current tty layer and copying all i/o from the current session to its child layer, where it execs your requested application. Because it owns the master end, it can easily duplicate all i/o it reads/writes elsewhere as it likes, and it provides a convenient means of asking it to do so:

luit -olog /dev/fd/2 sh -c 'read var; echo "$var"'
eecchhoo  tthhiiss  vvaarr??????

echo this var???
echo this var???

Where, as you can see, luit logs to its named -olog file all terminal input received as soon as it does.

Using /dev/fd/2 isn't nearly as useful in that case - as all of the i/o winds up at the same place twice. I usually prefer to open a second terminal, query its name with the tty command, and use that /dev/pts/[0-9] name as luit/script's named outfile - which will copy all of the i/o to both terminals simultaneously - so I can read/review it on one, and interact with it on the other. tee can be used to do the same thing in most cases, but it doesn't typically have the advantage of the master end of a pty to recommend it.

If your purpose is just as you say - to copy over for your review all of some process's input - then you would probably do best to focus on the input. strace is useful for many things, but if you're trying to get an accurate report of typical behavior, then it stands to reason that you should probably modify that behavior as little as possible while gathering your report. In other words, if you want input, copy input, don't insert a debugging parent process which will -TRAP and pause your process every time it makes a syscall.

-1

One possibility (if that doesn't break other things) is to insert a call to tee. The command tee duplicates the data, so you can have one copy going to your application and one going to the debug output. Instead of invoking your_application, arrange to invoke tee input.log | your_application. If the input to your application was a file, this isn't an invasive change, but if it wasn't a pipe, that makes it a pipe, which has consequences (e.g. a pipe isn't seekable).

Another possibility is to trace the file read operations that your application performs. You can do this with strace:

strace -e read=0 -e trace=read -e signal=none your_application 2>&1 |
grep '^ |'

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .