opensnoop (Mac file open watcher)

© May 2019 Anthony Lawrence



One of the fun things to do with any new OS that runs bash is to hit a random letter and then TAB twice. As you probably know, that displays commands in your search path that begin with the letter(s) you typed. That's what led me to sandbox-exec yesterday, and it's what brought me to "opensnoop" today.

Opensnoop uses the powerful Dtrace facility, which does mean that you need to run it with "sudo", but if you can do that, it can watch any or all files you like for "open" events - including failed opens (which can be useful for troubleshooting and is far less wordy than poring through strace output).

By the way, there is a large pile of pre-made Dtrace scripts available on Leopard. An "apropos Dtrace" shows me all these:

bitesize.d(1m) - analyse disk I/O size by process. Uses DTrace cpuwalk.d(1m) - Measure which CPUs a process runs on. Uses DTrace creatbyproc.d(1m) - snoop creat()s by process name. Uses DTrace dappprof(1m) - profile user and lib function usage. Uses DTrace dapptrace(1m) - trace user and library function usage. Uses DTrace diskhits(1m) - disk access by file offset. Uses DTrace dispqlen.d(1m) - dispatcher queue length by CPU. Uses DTrace dtrace(1) - generic front-end to the DTrace facility dtruss(1m) - process syscall details. Uses DTrace errinfo(1m) - print errno for syscall fails. Uses DTrace execsnoop(1m) - snoop new process execution. Uses DTrace fddist(1m) - file descriptor usage distributions. Uses DTrace filebyproc.d(1m) - snoop opens by process name. Uses DTrace hotspot.d(1m) - print disk event by location. Uses DTrace httpdstat.d(1m) - realtime httpd statistics. Uses DTrace iofile.d(1m) - I/O wait time by file and process. Uses DTrace iofileb.d(1m) - I/O bytes by file and process. Uses DTrace iopattern(1m) - print disk I/O pattern. Uses DTrace iopending(1m) - plot number of pending disk events. Uses DTrace iosnoop(1m) - snoop I/O events as they occur. Uses DTrace iotop(1m) - display top disk I/O events by process. Uses DTrace kill.d(1m) - snoop process signals as they occur. Uses DTrace lastwords(1m) - print syscalls before exit. Uses DTrace loads.d(1m) - print load averages. Uses DTrace newproc.d(1m) - snoop new processes. Uses DTrace opensnoop(1m) - snoop file opens as they occur. Uses DTrace pathopens.d(1m) - full pathnames opened ok count. Uses DTrace pidpersec.d(1m) - print new PIDs per sec. Uses DTrace plockstat(1) - front-end to DTrace to print statistics about POSIX mutexes and read/write locks priclass.d(1m) - priority distribution by scheduling class. Uses DTrace pridist.d(1m) - process priority distribution. Uses DTrace procsystime(1m) - analyse system call times. Uses DTrace runocc.d(1m) - run queue occupancy by CPU. Uses DTrace rwbypid.d(1m) - read/write calls by PID. Uses DTrace rwbytype.d(1m) - read/write bytes by vnode type. Uses DTrace rwsnoop(1m) - snoop read/write events. Uses DTrace sampleproc(1m) - sample processes on the CPUs. Uses DTrace seeksize.d(1m) - print disk event seek report. Uses DTrace setuids.d(1m) - snoop setuid calls as they occur. Uses DTrace sigdist.d(1m) - signal distribution by process. Uses DTrace syscallbypid.d(1m) - syscalls by process ID. Uses DTrace syscallbyproc.d(1m) - syscalls by process name. Uses DTrace syscallbysysc.d(1m) - syscalls by syscall. Uses DTrace topsyscall(1m) - top syscalls by syscall name. Uses DTrace topsysproc(1m) - top syscalls by process name. Uses DTrace weblatency.d(1m) - website latency statistics. Uses DTrace

I'll only look at "opensnoop" today, but obviously there's a lot of territory to be explored here..

The man page for "opensnoop" implies that you can use it to watch a specific file. You can, but it's not particularly smart about it. For example, the man page suggests:

opensnoop -f /etc/passwd

Well, there's a grand idea, right? But let's test it out, shall we? After typing that (with sudo, of course), I'll see this:

$ sudo opensnoop -f /etc/passwd Password: UID PID COMM FD PATH

Switching to another screen, I type "cat /etc/passwd", and when I return, I see:

UID PID COMM FD PATH 501 60638 cat 3 /etc/passwd

Wonderful! Looks like I can easily watch any file I like, right? Well, no.. not exactly. If I instead do:

cd /etc cat passwd

Opensnoop doesn't notice. Now, opensnoop is just a shell script that prepares a Dtrace script and runs it, so if you wanted to dig into the details of Dtrace (I'd like to, but not today), you could probably remedy this ignorance. Or you could only monitor the most likely usages (/etc/passwd, passwd, ./passwd) - after all, how likely is it that a program would open "/usr/../etc/passwd"?

Running just "opensnoop" (no arguments) watches everything. That can be an eye-opener.. there's a lot going on that you probably never thought about. Certain programs are more active than others: Parallels is constantly opening files even when I'm not using it, but even things you'd expect to sit quietly waiting for you to Alt-Tab will cause "opensnoop" to spit out some data now and then.. why did Preview suddenly need to open /.vol/234881026/9411827 just now? I sure don't know, but it is no wonder my battery life isn't so great when I leave all these other apps running and ready to Alt-Tab to..

You can also snoop a specific process by name or process id. "sudo opensnoop -n Preview" keeps an eye on that sneaky Preview app..

Got something to add? Send me email.