4

When I run the following make command in a build directory it's almost empty (file in question is absolutely certainly not there)

strace -f -e trace=execve,vfork,open,creat -s 1024 make <target>

After it finishes, the file is totally there. So it must have been created by make or one of its child processes (or children of their children and so on).

However when I grep the strace log for either the name of the file or for creat I cannot find the system call responsible for creation of this file.

What am I missing? Are there other system calls I should be monitoring?


EDIT:

It turns out the mistake was in both my strace comamnd and my grepping. All the answers were helpful, thank you, everyone, for your time.

I actually failed to communicate that the file was in a subdir and I was grepping using the name of a file along with the subdir's name. But since strace does not provide info on current working directory this approach didn't work so well (I ended up stracing chdir and rename calls to get the desired effect).

So PaulHaldane's first suggestion was right and to the point. As well as larsks's answer in which he has actually guessed how the file got created.

  • 2
    Have you tried using -e trace=file? That should pick up anything that acts on a file name. – Paul Haldane Aug 27 '16 at 10:15
  • 1
    There are scenarios you might never be able to detect with strace. One of them: ln -s foo bar; strace -ologs touch bar; grep foo logs. – Satō Katsura Aug 27 '16 at 10:33
  • @PaulHaldane, I haven't. Trying it now... – Vladislav Ivanishin Aug 27 '16 at 10:33
  • @PaulHaldane, nope, didn't help. – Vladislav Ivanishin Aug 27 '16 at 10:53
  • 2
    You're tracing functions in user space, while the target of a symlink is created in kernel space. – Satō Katsura Aug 27 '16 at 10:57
5

Run strace without the -e option and see if that improves your result.

There are a number of ways to create a file. Instead of open-ing it, it is highly likely that whatever tool produces that file first opens a temporary file, writes the data, and then renames the file on completion.

With your current limit (execve,vfork,open,creat) you're not going to see that sort of behavior.

For example, given this simple python script:

import os
import tempfile

fd = tempfile.NamedTemporaryFile(dir='.', delete=False)
fd.write('this is a test\n')
fd.close()

os.rename(fd.name, 'output')

Running strace with your arguments and then looking for output in the results yields nothing:

$ strace -e trace=execve,vfork,open,creat -o trace -f -s 80 python tmptest.py
$ grep output trace
$

But if I remove the -e filter:

$ strace  -o trace -f -s 80 python tmptest.py
$ grep output trace
4523  rename("/home/lars/tmp/tmpZDwvPK", "output") = 0

In the comments on your question, Sato Katsura provides an example in which you will not see your target filename in the strace output, but I think you are unlikely to encounter that when running make as long as you start with a clean build environment.

  • Using -e trace=file rather than an explicit list of syscalls gets round this problem (it certainly picks up the rename call in the test program). I'm puzzled that it didn't work for the original problem with make. – Paul Haldane Aug 27 '16 at 16:17
1

Another option is sysdig, with e.g. a command something like:

sysdig -p '%proc.pname[%proc.ppid]: %proc.name -> %evt.type(%evt.args)' \
  evt.args contains /tmp/yourbuilddir

running in one terminal, then with the temporary file rename code of:

#!/usr/bin/perl
use File::AtomicWrite;
File::AtomicWrite->write_file(
    {   file   => "/tmp/yourbuilddir/foofile",
        input  => \"blah",
        MKPATH => 1
    }
);

executed and then the sysdig running back in the other terminal should reveal something like:

bash[13645]: perl -> stat(res=-2(ENOENT) path=/tmp/yourbuilddir )
bash[13645]: perl -> stat(res=-2(ENOENT) path=/tmp/yourbuilddir )
bash[13645]: perl -> mkdir(path=/tmp/yourbuilddir mode=0 )
bash[13645]: perl -> stat(res=0 path=/tmp/yourbuilddir )
bash[13645]: perl -> stat(res=0 path=/tmp/yourbuilddir/ )
bash[13645]: perl -> stat(res=0 path=/tmp/yourbuilddir/ )
bash[13645]: perl -> open(fd=3(<f>/tmp/yourbuilddir/.tmp.8XUnHhOrIn) name=/tmp/yourbuilddir/.tmp.8XUnHhOrIn flags=39(O_EXCL|O_CREAT|O_RDWR) mode=0 )
bash[13645]: perl -> ioctl(fd=3(<f>/tmp/yourbuilddir/.tmp.8XUnHhOrIn) request=5401 argument=7FFEF6BE5020 )
bash[13645]: perl -> lseek(fd=3(<f>/tmp/yourbuilddir/.tmp.8XUnHhOrIn) offset=0 whence=1(SEEK_CUR) )
bash[13645]: perl -> fstat(fd=3(<f>/tmp/yourbuilddir/.tmp.8XUnHhOrIn) )
bash[13645]: perl -> fcntl(fd=3(<f>/tmp/yourbuilddir/.tmp.8XUnHhOrIn) cmd=3(F_SETFD) )
bash[13645]: perl -> write(fd=3(<f>/tmp/yourbuilddir/.tmp.8XUnHhOrIn) size=4 )
sshd[678]: sshd -> read(res=970 data=bash[13645]: perl -> stat(res=-2(ENOENT) path=/tmp/yourbuilddir )..bash[13645]:  )
bash[13645]: perl -> close(fd=3(<f>/tmp/yourbuilddir/.tmp.8XUnHhOrIn) )
bash[13645]: perl -> rename(res=0 oldpath=/tmp/yourbuilddir/.tmp.8XUnHhOrIn newpath=/tmp/yourbuilddir/foofile )
sshd[678]: sshd -> read(res=186 data=bash[13645]: perl -> close(fd=3(<f>/tmp/yourbuilddir/.tmp.8XUnHhOrIn) )..bash[13 )

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.