为什么iosnoop(磁盘上的IO监听文件)返回带问号的路径?

问题描述 投票:0回答:3

如果我跑步

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

macos path io trace dtrace
3个回答
10
投票

简短的答案是:

  1. 的?表示剩余的路径名未知,
  2. 你无法提取绝对路径并且
  3. 这是 OS X 上 DTrace 的一个功能,而不是错误。

解释这些要点需要对 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 修改的后果。
    


7
投票

sudo dtrace -n 'syscall::open*:entry { printf("%s %s",execname,copyinstr(arg0)); }'

这将显示文件打开,但不显示读取或写入。我从 Brendan Gregg 方便的 DTrace oneliners 页面上得到了这个:
http://www.brendangregg.com/DTrace/dtrace_oneliners.txt

可悲的是,我仍然不明白为什么 iosnoop 没有给出完整路径,尽管理论上这是可行的。


0
投票

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 提到的,Apple 
populates

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

© www.soinside.com 2019 - 2024. All rights reserved.