如果我跑步
sudo iosnoop | grep "gem"
然后在另一个终端运行
gem env
在 iosnoop 终端中我看到:
dtrace: error on enabled probe ID 4 (ID 1106: io:mach_kernel:buf_strategy:start): illegal operation in action #3 at DIF offset 0
dtrace: error on enabled probe ID 4 (ID 1106: io:mach_kernel:buf_strategy:start): illegal operation in action #3 at DIF offset 0
dtrace: error on enabled probe ID 4 (ID 1106: io:mach_kernel:buf_strategy:start): illegal operation in action #3 at DIF offset 0
dtrace: error on enabled probe ID 4 (ID 1106: io:mach_kernel:buf_strategy:start): illegal operation in action #3 at DIF offset 0
dtrace: error on enabled probe ID 4 (ID 1106: io:mach_kernel:buf_strategy:start): illegal operation in action #3 at DIF offset 0
...
dtrace: error on enabled probe ID 4 (ID 1106: io:mach_kernel:buf_strategy:start): illegal operation in action #3 at DIF offset 0
501 54406 R 21523616 512 bash ??/bin/gem
501 94092 R 141320288 4096 bash ??/bin/gem
501 94092 R 141320168 4096 ruby ??/1.8/rubygems.rb
501 94092 R 141320208 4096 ruby ??/1.8/rubygems.rb
501 94092 R 141319208 4096 ruby ??/rubygems/errors.rb
501 94092 R 141319856 4096 ruby ??/rubygems/specification.rb
501 94092 R 141319864 4096 ruby ??/rubygems/specification.rb
501 94092 R 141319872 4096 ruby ??/rubygems/specification.rb
501 94092 R 141319888 4096 ruby ??/rubygems/specification.rb
501 94092 R 141319896 4096 ruby ??/rubygems/specification.rb
501 94092 R 141319904 4096 ruby ??/rubygems/specification.rb
501 94092 R 141319928 4096 ruby ??/rubygems/specification.rb
501 94092 R 141319936 4096 ruby ??/rubygems/specification.rb
501 94092 R 141319944 4096 ruby ??/rubygems/specification.rb
501 94092 R 141320176 4096 ruby ??/1.8/rubygems.rb
501 94092 R 141320184 4096 ruby ??/1.8/rubygems.rb
...
处理文件路径中 ruby 附近的问号是什么:
ruby ??/1.8/rubygems.rb
?我怎样才能找到所有这些文件的绝对路径?
附加问题 - 为什么这里出现错误:
dtrace: error on enabled probe ID 4 (ID 1106: io:mach_kernel:buf_strategy:start): illegal operation in action #3 at DIF offset 0
?
简短的答案是:
解释这些要点需要对 DTrace 有一定的了解; 如果合适,请从Solaris 版本简介开始。
iosnoop
是一个利用 DTrace 可观察性的脚本
框架。特别是,它使用 io
提供商的 start
和 done
探头;启动探针公开请求的 bufinfo_t
,
目标设备的 devinfo_t
和相应文件的
fileinfo_t
。 fileinfo_t
不是达尔文原生类型:
它是 dtrace(1) 提供的结构,它提供了
为了用户的利益而方便地抽象文件。
例如,其成员中有 fi_pathname
,其中
应给出文件的完整路径名。
抽象的描述会保护用户及其用户 脚本,从底层的知识和更改 操作系统的实现。理论上,它还允许 单个脚本可在不同操作系统上运行 总共。
动态构建并填充 fileinfo_t
使用 /usr/lib/dtrace/io.d
中描述的 DTrace
translator。 这显示了操作系统特定类型的转换 到抽象。就雪豹而言,我看到了这一点
fi_pathname
由字符串“??/”后跟构成
IO 缓冲区的一些衍生物。我不是达尔文专家但是
我推断它根本没有在其记录中记录完整路径名
虚拟节点。这就是“??”的由来。在输出中
你的脚本以及我认为绝对的原因
路径名不可用。
最后,您的 DTrace 错误。无论出于何种原因,苹果公司
DTrace 的端口被巧妙地“削弱”,因为它阻止了跟踪
各种进程,您看到的错误消息是
特征性症状。具体来说,投诉内容是关于
线
start_uid[this->dev, this->blk] = (int)uid;
事实证明(再次,在雪豹上),试图评估
uid
位于任何
launchd
、diskimages-help
和 kernel_task
进程会导致此错误。我认为这些
进程“越界”,您看到的错误是
Apple 修改的后果。struct vnode{
// other attrs
const char *v_name; /* name component of the vnode */
vnode_t v_parent;
}
通常要从 vnode 获取路径,您可以调用
vn_getpath
,最终调用
build_path_with_parent
。苹果甚至说,这个的实现实际上比你想象的要稍微复杂一些Paths to vnodes are not always straightforward: a file with multiple hard-links will have multiple pathnames, and it is sometimes impossible to determine a vnode's full path. vn_getpath() will not enter the filesystem.
并且正在进行一些锁定,等等。正如 Robert Harris 提到的,Applepopulates
fi_pathname
as
fi_pathname = (F == NULL) ? "<none>" :
F->fg_ops->fo_type != DTYPE_VNODE ? "<unknown (not a vnode)>" :
strjoin("??/",
strjoin(((struct vnode *)F->fg_data)->v_parent == NULL ? "<unknown (NULL v_parent)>" :
(((struct vnode *)F->fg_data)->v_parent->v_name == NULL ? "<unknown (NULL v_name)>" :
((struct vnode *)F->fg_data)->v_parent->v_name),
strjoin("/",
((struct vnode *)F->fg_data)->v_name == NULL ? "<unknown (NULL v_name)>" :
((struct vnode *)F->fg_data)->v_name)));
基本上,他们尝试手动遍历树,但他们只向上 2 层,这就是为什么你在开始时有一个
??
。现在我不知道为什么他们在填充这个时不直接调用完整的
vn_getpath
,也许正如苹果来源的评论所证明的那样,他们认为这会对性能造成太大影响/* XXX Really want vn_getpath(curproc->p_fd.fd_rdir, , ) but that takes namecache_rw_lock XXX */
或者可能是技术限制(例如 dtrace 脚本无法调用内核函数)。但无论什么原因,
fi_pathname
的深度只有 2 层。
但是我们当然没有理由不能更深入地探索自己。我不知道在任何情况下,简单的遍历都会给出错误的答案,但它在我的测试中似乎工作得很好。例如,参见这个示例
macvfssnoop
修改自 dtrace 书:
#!/usr/sbin/dtrace -s
#pragma D option quiet
#pragma D option defaultargs
#pragma D option switchrate=10hz
dtrace:::BEGIN
{
printf("%-12s %6s %6s %-12.12s %-12s %-4s %s\n", "TIME(ms)", "UID",
"PID", "PROCESS", "CALL", "KB", "PATH");
}
/* see sys/bsd/sys/vnode_if.h */
fbt::VNOP_WRITE:entry
{
this->vnode = ((struct vnode *)arg0);
this->parent = this->vnode == NULL ? NULL : this->vnode->v_parent;
this->gp = this->parent == NULL ? NULL : this->parent->v_parent;
this->ggp = this->gp == NULL ? NULL : this->gp->v_parent;
this->gggp = this->ggp == NULL ? NULL : this->ggp->v_parent;
this->selfpath = (this->vnode == NULL || this->vnode->v_name == NULL) ? "" : strjoin("/", this->vnode->v_name);
this->parentpath = (this->parent == NULL || this->parent->v_name == NULL) ? "" : strjoin("/", this->parent->v_name);
this->gpath = (this->gp == NULL || this->gp->v_name == NULL) ? "" : strjoin("/", this->gp->v_name);
this->ggpath = (this->ggp == NULL || this->ggp->v_name == NULL) ? "" : strjoin("/", this->ggp->v_name);
this->gggpath = (this->gggp == NULL || this->gggp->v_name == NULL) ? "" : strjoin("/", this->gggp->v_name);
this->path1 = this->selfpath;
this->path2 = strjoin(this->parentpath, this->path1);
this->path3 = strjoin(this->gpath, this->path2);
this->path4 = strjoin(this->ggpath, this->path3);
this->path5 = strjoin(this->gggpath, this->path4);
self->path = this->path5;
self->kb = ((struct uio *)arg1)->uio_resid_64 / 1024;
}
fbt::VNOP_WRITE:entry
/execname != "dtrace" && ($$1 == NULL || $$1 == execname)/
{
printf("%-12d %6d %6d %-12.12s %-12s %-4d %s\n", timestamp / 1000000,
uid, pid, execname, probefunc, self->kb,
self->path != NULL ? stringof(self->path) : "<null>");
}
fbt::VNOP_WRITE:entry
{
self->path = 0; self->kb = 0;
}
Anton 指出的另一个选项是在系统调用级别而不是 VFS 级别进行跟踪,在这种情况下,您的路径直接是系统调用的参数。然而,即使在这种情况下,路径仍然可以是相对路径(相对于程序的工作目录),因此您还需要打印出工作目录,就像
pathopens.d
那样。
另请参阅 https://lists.apple.com/archives/filesystem-dev/2012/Jan/msg00006.html