62

OS X 缺少 linux 的strace,但它dtrace应该更好。

但是,我想念对单个命令进行简单跟踪的能力。例如,在 linux 上,我可以写strace -f gcc hello.c来捕获所有系统调用,这给了我编译器编译我的程序所需的所有文件名的列表(优秀的memoize脚本就是建立在这个技巧上的)

我想在 mac 上移植 memoize,所以我需要某种strace. 我真正需要的是gcc读取和写入的文件列表,所以我需要的是更多的truss. 果然我可以说dtruss -f gcc hello.c并获得一些相同的功能,但是编译器以root权限运行,这显然是不可取的(除了巨大的安全风险之外,一个问题是该a.out文件现在由root拥有:-)

然后我尝试dtruss -f sudo -u myusername gcc hello.c了,但这感觉有点不对劲,无论如何都不起作用(我一直没有a.out文件,不知道为什么)

这么长的故事试图激发我最初的问题:我如何才能像在 linux 中dtrace那样以普通用户权限运行我的命令?strace

编辑:似乎我不是唯一一个想知道如何做到这一点的人:问题#1204256与我的几乎相同(并且具有相同的次优 sudo 答案:-)

4

8 回答 8

53

最简单的方法是使用 sudo:

sudo dtruss -f sudo -u $USER whoami

其他解决方案是首先运行调试器并监视新的特定进程。例如

sudo dtruss -fn whoami

然后在另一个终端中简单地运行:

whoami

就那么简单。

您可以在手册中找到更棘手的论点:man dtruss


或者,您可以将 dtruss 附加到正在运行的用户进程,例如在 Mac 上:

sudo dtruss -fp PID

或使用 strace 在 Linux/Unix 上类似:

sudo strace -fp PID

另一个技巧可能是执行命令并在附加到进程之后立即执行。这里有些例子:

sudo true; (./Pages &); sudo dtruss -fp `pgrep -n -x Pages`
sudo true; (sleep 1 &); sudo dtruss -fp `pgrep -n -x sleep`
sudo true; (tail -f /var/log/system.log &); sudo dtruss -fp `pgrep -n -x tail`

笔记:

  • first sudo 仅用于在第一次运行时缓存密码,

  • 这个技巧不适用于快速命令行ls, date,因为调试器需要一些时间才能附加到进程,

  • 你必须在两个地方输入你的命令,

  • 您可以忽略&将进程运行到后台,如果它已经这样做了,

  • 完成调试后,您必须手动终止后台进程(例如killall -v tail

于 2012-07-29T02:46:51.137 回答
9

-n参数 to将dtruss导致 dtruss 等待并检查与参数匹配的进程-n。该-f选项仍然可以遵循从匹配的进程派生的进程-n

所有这一切都意味着,如果您想以非特权用户身份运行一个进程(为了争论,假设它是whoami),请按照以下步骤操作:

  1. 打开根外壳
  2. dtruss -fn whoami
    • 这将等待名为“whoami”的进程存在
  3. 打开非特权 shell
  4. whoami
    • 这将正常执行并退出
  5. 在 dtruss 窗口中观察系统调用跟踪
    • dtruss 不会自行退出——它会继续等待匹配的进程——所以当你完成后退出它

这个答案重复了@kenorb 回复的后半部分,但它应该是一流的答案。

于 2014-01-02T21:28:10.877 回答
5

我不知道你是否可以让 dtruss 像 strace 一样无创。

“sudo [to root] dtruss sudo [back to nonroot] cmd”的一个变体在我的一些快速测试中似乎效果更好:

sudo dtruss -f su -l `whoami` cd `pwd` && cmd....

外部 sudo 当然是 dtruss 以 root 身份运行。

内部的 su 回到了我的身边,并且使用 -l 它正确地重新创建了环境,此时我们需要 cd 回到我们开始的地方。

如果您希望环境成为该用户通常获得的环境,我认为“su -l user”比“sudo -u user”更好。那将是他们的登录环境;我不知道是否有一种好方法可以通过两个用户更改来让环境继承。

在您的问题中,除了丑陋之外,您对“sudo dtruss sudo”解决方法的另一个抱怨是“我一直没有得到 a.out 文件,不知道为什么”。我也不知道为什么,但是在我的小测试脚本中,“sudo dtruss sudo”变体也无法写入测试输出文件,而上面的“sudo dtruss su”变体确实创建了输出文件。

于 2011-01-14T00:09:17.600 回答
5

不是您的问题的答案,而是要知道的事情。OpenSolaris 通过“特权”(部分)解决了这个问题 - 请参阅此页面。即使在 OpenSolaris 中,也不可能允许用户在没有任何额外权限的情况下 dtruss 自己的进程。原因在于 dtrace 的工作方式——它内核中启用了探测。因此,允许非特权用户探测内核意味着用户可以做很多不需要的事情,例如通过在键盘驱动程序中启用探测来嗅探其他用户的密码!

于 2011-01-15T12:16:03.733 回答
3

似乎 OS X 不支持使用 dtrace 来复制您需要的所有 strace 功能。但是,我建议尝试围绕合适的系统调用创建一个包装器。看起来DYLD_INSERT_LIBRARIES是您想要破解的环境变量。这与 Linux 基本相同LD_PRELOAD

进行库函数覆盖的一种更简单的方法是使用 DYLD_INSERT_LIBRARIES 环境变量(类似于 Linux 上的 LD_PRELOAD)。这个概念很简单:在加载时,动态链接器 (dyld) 将在可执行文件想要加载的任何库之前加载 DYLD_INSERT_LIBRARIES 中指定的任何动态库。通过将函数命名为与库函数中的函数相同,它将覆盖对原始函数的任何调用。

原始函数也被加载,并且可以使用 dlsym(RTLD_NEXT, “function_name”); 功能。这允许包装现有库函数的简单方法。

根据Tom Robinson的示例,您可能还需要设置。DYLD_FORCE_FLAT_NAMESPACE=1

lib_overrides.c仅覆盖的原始示例 () 的副本fopen

#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>

// for caching the original fopen implementation
FILE * (*original_fopen) (const char *, const char *) = NULL;

// our fopen override implmentation
FILE * fopen(const char * filename, const char * mode)
{
    // if we haven’t already, retrieve the original fopen implementation
    if (!original_fopen)
        original_fopen = dlsym(RTLD_NEXT, "fopen");

    // do our own processing; in this case just print the parameters
    printf("== fopen: {%s,%s} ==\n", filename, mode);

    // call the original fopen with the same arugments
    FILE* f = original_fopen(filename, mode);

    // return the result
    return f;
}

用法:

$ gcc -Wall -o lib_overrides.dylib -dynamiclib lib_overrides.c
$ DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=lib_overrides.dylib command-to-test
于 2014-08-28T14:00:00.300 回答
2

免责声明:这是来自@kenorb 的回答。但它有一些优点:PID 比 execname 更具体。我们可以让一个短暂的进程在它开始之前等待 DTrace。

这有点竞争条件,但是……</p>

假设我们要跟踪cat /etc/hosts

sudo true && \
(sleep 1; cat /etc/hosts) &; \
sudo dtrace -n 'syscall:::entry /pid == $1/ {@[probefunc] = count();}' $!; \
kill $!

我们sudo true用来确保在开始运行任何对时间敏感的东西之前清除 sudo 的密码提示。

我们启动一个后台进程(“等待 1 秒,然后做一些有趣的事情”)。同时,我们启动 DTrace。我们已将后台进程的 PID 捕获到 中$!,因此我们可以将其作为 arg 传递给 DTrace。

kill $!我们关闭 DTrace 后的运行。对于我们的cat示例来说,这不是必需的(进程自行关闭),但它可以帮助我们结束长时间运行的后台进程,例如ping. 传递-p $!给 DTrace 是执行此操作的首选方式,但在 macOS 上显然需要代码签名的可执行文件。


您可以做的另一件事是在单独的 shell 中运行命令,然后窥探该 shell。看我的回答

于 2017-06-25T18:51:31.000 回答
1

我不知道如何以普通用户的身份运行您想要的东西,因为使用 dtrace 的 dtruss 似乎需要 su 权限。

但是,我相信您正在寻找的命令而不是

dtruss -f sudo -u myusername gcc hello.c

sudo dtruss -f gcc hello.c

输入密码后,dtruss 将运行 dtrace 将 sudo 权限,您将获得跟踪以及 a.out 文件。

抱歉,我无法提供进一步的帮助。

于 2011-01-12T08:14:56.243 回答
-1

我使用这种通用方法修复了损坏的预览,以在 bash 提示符下诊断和修复 Terminal.app 中崩溃的应用程序:

$sudo dtruss -fn Preview 2>&1 | grep '/Users/'

输入密码后,我启动了预览,它在打印 dtruss 时立即崩溃

70256/0x19cd898: chdir("/Users/devon/Library/Containers/com.apple.Preview/Data\0", 0x0, 0x7FFF5EAC4BC8) = 0 0

我没有删除这个可能的嫌疑人,而是将其重命名

$(cd ~/Library/Containers && mv -i com.apple.Preview com.apple.Preview.~NOT~)

预览现在可以正常工作,并创建了一个新的、无毒版本的目录,我已经摆脱了它。

如果这是一个系统范围的问题,但您可能更喜欢grep '"/'在 中向您发送数百个无辜项目/usr/System等等。

从 MacOSX 11.6 开始,“El Capitan”dtrace/dtruss 默认已失效——请参阅如何修复它

我使用这个 bash 函数,例如 $d /Applications/Preview.app/Contents/MacOS/Preview

d () 
{ 
    case "$*" in 
        [0-9] | [0-9][0-9] | [0-9][0-9][0-9] | [0-9][0-9][0-9][0-9] | [0-9][0-9][0-9][0-9][0-9])
            dtruss -f -p "$*"
        ;;
        *)
            bash -c 'echo -en "\t <<< press return in this window to run after launching trace in root window like this >>> \t # d $$" >/dev/tty; (read -u3 3</dev/tty); exec "$0" "$@"' "$@"
        ;;
    esac
}

很久以前,我们使用 Apple 电脑来推送数据,使用 Levi's 牛仔裤来驱动拖拉机——两者都已沦为单纯的时尚公司。科幻小说中的异托邦Idiocracy似乎是相关的。

于 2018-07-10T11:26:54.093 回答