Skip to content

Instantly share code, notes, and snippets.

@dreness
Last active July 16, 2020 22:29
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dreness/5d8ddbc27573080c30e52f834b4a9cab to your computer and use it in GitHub Desktop.
Save dreness/5d8ddbc27573080c30e52f834b4a9cab to your computer and use it in GitHub Desktop.
Try to find out who is opening, reading from, writing to, deleting a file
#!/usr/sbin/dtrace -s
#pragma D option quiet
#pragma D option switchrate=10hz
/* pass the filename to watch for as the only cli argument */
dtrace:::BEGIN
{
/* double dollar sign to stringify cli arg */
fname = $$1;
printf("%-12s %6s %6s %-12.12s %-12s %s\n", "TIME(ms)", "UID",
"PID", "PROCESS", "CALL", "DIR/FILE");
}
/*
extern errno_t VNOP_CREATE(vnode_t, vnode_t *, struct componentname *, struct vnode_attr *, vfs_context_t);
extern errno_t VNOP_REMOVE(vnode_t, vnode_t, struct componentname *, int, vfs_context_t);
*/
/*
fbt knows the types of the structs, so we can say:
args[0]->v_name
instead of:
((struct vnode *)arg0)->v_name
*/
fbt::VNOP_CREATE:entry,
fbt::VNOP_REMOVE:entry
/fname == stringof(args[2]->cn_nameptr)/
{
/*
this->path = ((struct vnode *)arg0)->v_name;
this->name = ((struct componentname *)arg2)->cn_nameptr;
*/
this->path = args[0]->v_name;
this->name = args[2]->cn_nameptr;
printf("%-12d %6d %6d %-12.12s %-12s %s/%s\n",
timestamp / 1000000, uid, pid, execname, probefunc,
this->path != NULL ? stringof(this->path) : "<null>",
stringof(this->name));
ustack();
printf("\n");
}
/*
extern errno_t VNOP_OPEN(vnode_t, int, vfs_context_t);
*/
/*
fbt::VNOP_OPEN:entry
/fname == stringof(args[0]->v_name)/
{
printf("%-12d %6d %6d %-12.12s %-12s %s\n",
timestamp / 1000000, uid, pid, execname, probefunc,
stringof(args[0]->v_name));
ustack();
printf("\n");
}
*/
/*
open(const char *path, int oflag, ...);
returns int FD
*/
syscall::open:entry, syscall::open_nocancel:entry, syscall::open_extended:entry
{
self->pathp = arg0;
self->ok = 1;
self->theProc = pid;
}
syscall::open:return, syscall::open_nocancel:return, syscall::open_extended:return
/self->ok && errno == 0 && pid == self->theProc && fname == basename(copyinstr(self->pathp))/
{
self->fname = basename(copyinstr(self->pathp));
printf("%-12d %6d %6d %-12.12s %-12s %s\n",
timestamp / 1000000, uid, pid, execname, probefunc,
self->pathp != NULL ? copyinstr(self->pathp) : "<null>");
self->pathp = NULL;
self->ok = NULL;
self->fd = arg1;
self->e = errno;
/* ustack(); */
printf("\n");
}
/*
read(int fd, void *buf, size_t nbyte);
*/
syscall::read:entry
/self->theProc == pid && arg0 == self->fd && self->e == 0/
{
self->traceRead = 1;
self->theFD = arg0;
self->buf = arg1;
self->fd = NULL;
self->nbyte = arg2;
}
syscall::read:return
/arg0 != 0 && self->theProc == pid && self->theFD && self->traceRead == 1 && self->e == 0 && errno == 0/
{
/*
printf("theFD is: %d\n",self->theFD);
printf("theProc is: %d\n",self->theProc);
*/
printf("%-12d %6d %6d %-12.12s %s:%s\n",
timestamp / 1000000, uid, pid, execname, probefunc, probename);
printf("requested %d bytes, got %d bytes\n",self->nbyte, arg0);
printf("\n%S\n",stringof(copyin(self->buf, self->nbyte)));
/* self->e = 1; */
printf("\n");
/* self->buf += arg0; */
}
syscall::read:return
/arg0 == 0 && self->theProc == pid && self->theFD && self->traceRead == 1 && self->e == 0 && errno == 0/
{
printf("EOF for fd %d\n",self->theFD);
self->theFD = NULL;
self->thePid = NULL;
self->traceRead = 0;
self->buf = NULL;
self->nbyte = NULL;
}
syscall::rename:entry
/fname == copyinstr(arg0) || fname == copyinstr(arg1)/
{
printf("%-12d %6d %6d %-12.12s %-12s %s --> %s\n",
timestamp / 1000000, uid, pid, execname, probefunc,
copyinstr(arg0), copyinstr(arg1));
ustack();
printf("\n");
}
ERROR
{
printf("Error tracing %s!\n", execname);
}
/* maybe useful for listening with a wider net... */
/*
fbt::VNOP_*:entry
/
execname != "dtrace" &&
execname != "Terminal" &&
execname != "logd" &&
execname != "WindowServer" &&
execname != "syslogd" &&
execname != "suggestd" &&
execname != "locationd"
/
{
printf("%-12d %6d %6d %-12.12s %-12s\n",
timestamp / 1000000, uid, pid, execname, probefunc);
}
*/
@dreness
Copy link
Author

dreness commented Aug 10, 2017

h1ro% sudo watch_file.d "foo" 
TIME(ms)        UID    PID PROCESS      CALL         DIR/FILE
11466398        501   1427 rm           VNOP_REMOVE  andre/foo

              0x7fff4f5ec5f2
              0x10000e406
              0x10000e1e5
              0x7fff4f49be7c
              0x2

11473086        501    341 zsh          VNOP_CREATE  andre/foo

              libsystem_kernel.dylib`__open+0xa
              zsh`0x000000010c6ca9c8+0x13b
              zsh`0x000000010c6c621e+0x286
              zsh`execlist+0x53d
              zsh`execode+0xc8
              zsh`loop+0x317
              zsh`zsh_main+0x534
              libdyld.dylib`start+0x1
              zsh`0x1

11473091        501    341 zsh          open         foo

              libsystem_kernel.dylib`__open+0xa
              zsh`0x000000010c6ca9c8+0x13b
              zsh`0x000000010c6c621e+0x286
              zsh`execlist+0x53d
              zsh`execode+0xc8
              zsh`loop+0x317
              zsh`zsh_main+0x534
              libdyld.dylib`start+0x1
              zsh`0x1

11474168        501   1429 mdworker     open         /Users/andre/foo

              libsystem_kernel.dylib`__open+0xa
              mdworker`0x00000001090d1aa8+0xb3
              mdworker`0x00000001090d6c01+0xe25
              libsystem_pthread.dylib`_pthread_body+0x154
              libsystem_pthread.dylib`_pthread_body
              libsystem_pthread.dylib`thread_start+0xd

11474784        501    341 zsh          read:return
----------
----------
11474798        501   1430 cat          open         foo

              0x7fff4f5ebcca
              0x10e95c6d1
              0x7fff4f49be7c
              0x2

11474798        501   1430 cat          read:return
----------
1
2
3

----------
11476718        501   1431 mv           rename       foo --> bar

              0x7fff4f5ec0e2
              0x10af260f5
              0x10af2606c
              0x7fff4f49be7c
              0x3

11479086        501   1432 mv           rename       bar --> foo

              0x7fff4f5ec0e2
              0x108b5f0f5
              0x108b5f06c
              0x7fff4f49be7c
              0x3

11480099        501   1429 mdworker     open         /Users/andre/foo

              libsystem_kernel.dylib`__open+0xa
              mdworker`0x00000001090d1aa8+0xb3
              mdworker`0x00000001090d6c01+0xe25
              libsystem_pthread.dylib`_pthread_body+0x154
              libsystem_pthread.dylib`_pthread_body
              libsystem_pthread.dylib`thread_start+0xd

11480254        501   1433 rm           VNOP_REMOVE  andre/foo

              0x7fff4f5ec5f2
              0x1086ce406
              0x1086ce1e5
              0x7fff4f49be7c
              0x2

@dreness
Copy link
Author

dreness commented Aug 14, 2017

p.s. this is still not quite right, e.g.

Error tracing adid!
dtrace: error on enabled probe ID 7 (ID 163: syscall::open:return): invalid user access in predicate at DIF offset 12

Knowing which reads to trace requires state keeping across various dtrace providers, and I think I’m not quite doing it right.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment