6

I wrote the following code to determine which files a program writes to. I want to capture the filenames of course.

strace -f -t -e trace=file -p 8804 2>&1 | grep -oP "\"(.*)\".*O_WRONLY"

This outputs something like

/tmp/11111111.txt", O_WRONLY

The problem is I can't pipe the output of all this to any command

strace -f -t -e trace=file -p 8804 2>&1 | grep -oP "\"(.*)\".*O_WRONLY" | echo
# does not show anything

And also I can't save the output of all this for later use:

strace -f -t -e trace=file -p 8804 2>&1 | grep -oP "\"(.*)\".*O_WRONLY" > asd.out
# file is empty

Your help is appreciated. :)

  • 2
    | echo is wrong. echo echos the parameters, it does nothing with data given on stdin. Just remove it. – icarus Jan 18 '17 at 14:53
  • FWIW strace has a regex option, and in the strace man (see below) it tells you that you can pass the -o switch like strace -e trace=%file -o "| grep NEEDLE" COMMAND to search for string NEEDLE in the output from strace-ing an executable COMMAND. – pbhj Jun 05 '19 at 22:47

2 Answers2

4

You can write the output to a file (with strace -o asd.out) and then grep it:

From strace manual:

-o filename Write  the  trace  output to the file filename rather than 
to stderr.  Use filename.pid if -ff is used. If the argument begins with
`|' or with `!' then the rest of the argument is treated as a command
and all output is piped to it. This is convenient for piping the
debugging output to a program without affecting the redirections of 
executed programs.
  • 1
    That's great, I tried and it works as it should. But if anyone knows the black magic behind the original problem, share me - out of curiosity. – Rápli András Jan 18 '17 at 14:43
1

Ran into the same problem today.

FWIW you can use | cat instead of | echo in your example, echo apparently does nothing by default with no parameters.

On my box strace ls 2>&1 | grep "execve" > asd.out actually works OK...so you're doing it right.

My hunch is the problem is in the "attaching" like strace -p xxx because typically you have to hit ctrl+c to exit it and intermediate buffers in your command chain are not flushed.

So this works for me (I think it flushes on a new line because it detects it it outputting to "the screen"/terminal/tty):

strace ruby -e "sleep" 2>&1 | grep execve

but this doesn't

strace ruby -e "sleep" 2>&1 | grep execve > my_output_file

(I think because the final redirect is "not to a tty" so it uses a larger 4k internal buffer or something, or uses an internal buffer instead of buffering "by line" anymore).

But using a hint from here, this works:

strace ruby -e "sleep" 2>&1 | stdbuf -oL grep execve > me

Pretty weird stuff. In a disconcerting way. And my theory is that with longer chains (ex: grep | grep | grep the size of the buffer grows linearly...4k each time...including for the one that writes it finally out to disk...)

Another thing that can help is insert an "output to screen" peeker in your chain, which is apparently tee /dev/tty:

strace ruby -e "sleep" 2>&1 | tee /dev/tty | grep execve > my_output_file

Then at least you'll see it on the screen even if it never makes it to the output file.

So anyway the take away from this is that strace logs to "normal" stderr (so if it's "running a process" eg. strace ls then the output from the process and strace will be mixed). You can avoid the mixing by using the -o output_file parameter.

Also be careful you must redirect stdout first, then stderr, ex:

progname >out.txt 2>&1

order matters, it can't be the other way around.

rogerdpack
  • 1,715