2. DTrace and Erlang/OTP

2 DTrace和Erlang / OTP

2.1历史

为Erlang虚拟机提供的DTrace探测器的第一个实现在2008 Erlang User Conference。基于Erlang / OTP R12版本的这项工作由于似乎与原始开发人员沟通不畅而终止。

几个用户已经创建了Erlang端口驱动程序,链接驱动程序或允许Erlang代码尝试激活探针的NIF,例如foo_module:dtrace_probe("message goes here!")

2.2 目标

  • 尽可能多地注释ErlangVM是可行的。

  • 最初的目标是跟踪文件I/O操作。

  • 支持所有实现DTrace的平台:OS X,Solaris和(我希望)FreeBSD和NetBSD。

  • 在实际应用中,通过DTrace提供程序兼容性支持Linux上的SystemTap。

  • 允许Erlang代码提供注释。

2.3支撑平台

  • OS X 10.6.x / Snow Leopard,OS X 10.7.x / Lion以及可能更新的版本。

  • Solaris 10.我已经在Solaris 11和OpenIndiana 151a上进行了有限的测试,并且两者似乎都可以工作。

  • FreeBSD 9.0和10.0。

  • Linux通过SystemTap兼容。请参阅$ERL_TOP/HOWTO/SYSTEMTAP.md更多细节。

--with-dynamic-trace=dtrace运行configure脚本时,只需将该选项添加到您的命令中即可。如果您正在使用systemtap,则配置选项为--with-dynamic-trace=systemtap

2.4状态

从R15B01开始,动态跟踪代码包含在OTP源代码分发中,尽管它被认为是实验性的。dtrace代码的主要发展仍然发生在爱立信之外,但是不需要获取补丁版本的OTP源以获得基本的功能。

2.5执行摘要

到目前为止,大部分工作都集中efile_drv.c在代表Erlang虚拟机实现大多数文件I / O 的代码上。该驱动程序也提出了一个很大的挑战:使用I / O工作池(erl +A 8例如使用标志启用)使跟踪I / O活动变得更加困难,因为以下每一项都可能在不同的Pthread中执行:

  • I / O启动(Erlang代码)

  • I / O代理进程处理,例如文件未在raw模式下打开时的读/写操作,由代码和文件服务器进程执行的操作。(Erlang代码)

  • efile_drv 命令设置(C代码)

  • efile_drv 命令执行(C代码)

  • efile_drv 状态返回(C代码)

示例输出lib/runtime_tools/examples/efile_drv.d执行时file:rename("old-name", "new-name")*

efile_drv enter tag={3,84} user tag some-user-tag | RENAME (12) | args: old-name new-name ,\ 0 0 (port #Port<0.59>) async I/O worker tag={3,83} | RENAME (12) | efile_drv-int_entry async I/O worker tag={3,83} | RENAME (12) | efile_drv-int_return efile_drv return tag={3,83} user tag | RENAME (12) | errno 2

...以下密钥可帮助解密输出:

  • {3,83}是分配给该I / O操作的Erlang调度程序线程号(3)和操作计数器号(83)。这两个数字一起构成I / O操作的唯一ID。

  • 12重命名操作的命令号。见定义FILE_RENAME在源代码文件中efile_drv.c或者BEGIND脚本的部分lib/runtime_tools/examples/efile_drv.d...

  • old-name并且new-namerename(2)系统调用的源和目标的两个字符串参数。两个整数参数未使用; 无论如何,简单的格式代码打印参数0和0。

  • 代表Erlang端口调用了工作池代码#Port<0.59>。

  • 系统调用失败,POSIX errno值为2 ENOENT,因为路径old-name不存在。

  • efile_drv-int_entryefile_drv_int_return探针的情况下,所提供的用户感兴趣的测量仅由执行的代码的延迟efile_drv通过I / O工作池线程异步功能和OS的系统调用,它们封装。

那么,some-user-tag字符串从哪里来?

目前,用户标记来自如下代码:

dyntrace:put_tag("some-user-tag"), file:rename("old-name", "new-name"),

这种在Erlang级别标记I/O的方法可能会改变。

2.6 示例DTrace探针规范

/** * Fired when a message is sent from one local process to another. * * NOTE: The 'size' parameter is in machine-dependent words and * that the actual size of any binary terms in the message * are not included. * * @param sender the PID (string form) of the sender * @param receiver the PID (string form) of the receiver * @param size the size of the message being delivered (words) * @param token_label for the sender's sequential trace token * @param token_previous count for the sender's sequential trace token * @param token_current count for the sender's sequential trace token */ probe message__send(char *sender, char *receiver, uint32_t size, int token_label, int token_previous, int token_current /** * Fired when a message is sent from a local process to a remote process. * * NOTE: The 'size' parameter is in machine-dependent words and * that the actual size of any binary terms in the message * are not included. * * @param sender the PID (string form) of the sender * @param node_name the Erlang node name (string form) of the receiver * @param receiver the PID/name (string form) of the receiver * @param size the size of the message being delivered (words) * @param token_label for the sender's sequential trace token * @param token_previous count for the sender's sequential trace token * @param token_current count for the sender's sequential trace token */ probe message__send__remote(char *sender, char *node_name, char *receiver, uint32_t size, int token_label, int token_previous, int token_current /** * Fired when a message is queued to a local process. This probe * will not fire if the sender's pid == receiver's pid. * * NOTE: The 'size' parameter is in machine-dependent words and * that the actual size of any binary terms in the message * are not included. * * @param receiver the PID (string form) of the receiver * @param size the size of the message being delivered (words) * @param queue_len length of the queue of the receiving process * @param token_label for the sender's sequential trace token * @param token_previous count for the sender's sequential trace token * @param token_current count for the sender's sequential trace token */ probe message__queued(char *receiver, uint32_t size, uint32_t queue_len, int token_label, int token_previous, int token_current /** * Fired when a message is 'receive'd by a local process and removed * from its mailbox. * * NOTE: The 'size' parameter is in machine-dependent words and * that the actual size of any binary terms in the message * are not included. * * @param receiver the PID (string form) of the receiver * @param size the size of the message being delivered (words) * @param queue_len length of the queue of the receiving process * @param token_label for the sender's sequential trace token * @param token_previous count for the sender's sequential trace token * @param token_current count for the sender's sequential trace token */ probe message__receive(char *receiver, uint32_t size, uint32_t queue_len, int token_label, int token_previous, int token_current /* ... */ /* Async driver pool */ /** * Show the post-add length of the async driver thread pool member's queue. * * NOTE: The port name is not available: additional lock(s) must * be acquired in order to get the port name safely in an SMP * environment. The same is true for the aio__pool_get probe. * * @param port the Port (string form) * @param new queue length */ probe aio_pool__add(char *, int /** * Show the post-get length of the async driver thread pool member's queue. * * @param port the Port (string form) * @param new queue length */ probe aio_pool__get(char *, int /* Probes for efile_drv.c */ /** * Entry into the efile_drv.c file I/O driver * * For a list of command numbers used by this driver, see the section * "Guide to probe arguments" in ../../../README.md. That section * also contains explanation of the various integer and string * arguments that may be present when any particular probe fires. * * TODO: Adding the port string, args[10], is a pain. Making that * port string available to all the other efile_drv.c probes * will be more pain. Is the pain worth it? If yes, then * add them everywhere else and grit our teeth. If no, then * rip it out. * * @param thread-id number of the scheduler Pthread arg0 * @param tag number: {thread-id, tag} uniquely names a driver operation * @param user-tag string arg2 * @param command number arg3 * @param string argument 1 arg4 * @param string argument 2 arg5 * @param integer argument 1 arg6 * @param integer argument 2 arg7 * @param integer argument 3 arg8 * @param integer argument 4 arg9 * @param port the port ID of the busy port args[10] */ probe efile_drv__entry(int, int, char *, int, char *, char *, int64_t, int64_t, int64_t, int64_t, char * /** * Entry into the driver's internal work function. Computation here * is performed by a async worker pool Pthread. * * @param thread-id number * @param tag number * @param command number */ probe efile_drv__int_entry(int, int, int /** * Return from the driver's internal work function. * * @param thread-id number * @param tag number * @param command number */ probe efile_drv__int_return(int, int, int /** * Return from the efile_drv.c file I/O driver * * @param thread-id number arg0 * @param tag number arg1 * @param user-tag string arg2 * @param command number arg3 * @param Success? 1 is success, 0 is failure arg4 * @param If failure, the errno of the error. arg5 */ probe efile_drv__return(int, int, char *, int, int, int

2.7 efile_drv.c探针参数指南

/* Driver op code: used by efile_drv-entry arg3 */ /* used by efile_drv-int_entry arg3 */ /* used by efile_drv-int_return arg3 */ /* used by efile_drv-return arg3 */ #define FILE_OPEN 1 (probe arg3) probe arg6 = C driver dt_i1 = flags; probe arg4 = C driver dt_s1 = path; #define FILE_READ 2 (probe arg3) probe arg6 = C driver dt_i1 = fd; probe arg7 = C driver dt_i2 = flags; probe arg8 = C driver dt_i3 = size; #define FILE_LSEEK 3 (probe arg3) probe arg6 = C driver dt_i1 = fd; probe arg7 = C driver dt_i2 = offset; probe arg8 = C driver dt_i3 = origin; #define FILE_WRITE 4 (probe arg3) probe arg6 = C driver dt_i1 = fd; probe arg7 = C driver dt_i2 = flags; probe arg8 = C driver dt_i3 = size; #define FILE_FSTAT 5 (probe arg3) probe arg6 = C driver dt_i1 = fd; #define FILE_PWD 6 (probe arg3) none #define FILE_READDIR 7 (probe arg3) probe arg4 = C driver dt_s1 = path; #define FILE_CHDIR 8 (probe arg3) probe arg4 = C driver dt_s1 = path; #define FILE_FSYNC 9 (probe arg3) probe arg6 = C driver dt_i1 = fd; #define FILE_MKDIR 10 (probe arg3) probe arg4 = C driver dt_s1 = path; #define FILE_DELETE 11 (probe arg3) probe arg4 = C driver dt_s1 = path; #define FILE_RENAME 12 (probe arg3) probe arg4 = C driver dt_s1 = old_name; probe arg5 = C driver dt_s2 = new_name; #define FILE_RMDIR 13 (probe arg3) probe arg4 = C driver dt_s1 = path; #define FILE_TRUNCATE 14 (probe arg3) probe arg6 = C driver dt_i1 = fd; probe arg7 = C driver dt_i2 = flags; #define FILE_READ_FILE 15 (probe arg3) probe arg4 = C driver dt_s1 = path; #define FILE_WRITE_INFO 16 (probe arg3) probe arg6 = C driver dt_i1 = mode; probe arg7 = C driver dt_i2 = uid; probe arg8 = C driver dt_i3 = gid; #define FILE_LSTAT 19 (probe arg3) probe arg4 = C driver dt_s1 = path; #define FILE_READLINK 20 (probe arg3) probe arg4 = C driver dt_s1 = path; #define FILE_LINK 21 (probe arg3) probe arg4 = C driver dt_s1 = existing_path; probe arg5 = C driver dt_s2 = new_path; #define FILE_SYMLINK 22 (probe arg3) probe arg4 = C driver dt_s1 = existing_path; probe arg5 = C driver dt_s2 = new_path; #define FILE_CLOSE 23 (probe arg3) probe arg6 = C driver dt_i1 = fd; probe arg7 = C driver dt_i2 = flags; #define FILE_PWRITEV 24 (probe arg3) probe arg6 = C driver dt_i1 = fd; probe arg7 = C driver dt_i2 = flags; probe arg8 = C driver dt_i3 = size; #define FILE_PREADV 25 (probe arg3) probe arg6 = C driver dt_i1 = fd; probe arg7 = C driver dt_i2 = flags; probe arg8 = C driver dt_i3 = size; #define FILE_SETOPT 26 (probe arg3) probe arg6 = C driver dt_i1 = opt_name; probe arg7 = C driver dt_i2 = opt_specific_value; #define FILE_IPREAD 27 (probe arg3) probe arg6 = C driver dt_i1 = fd; probe arg7 = C driver dt_i2 = flags; probe arg8 = C driver dt_i3 = offsets[0]; probe arg9 = C driver dt_i4 = size; #define FILE_ALTNAME 28 (probe arg3) probe arg4 = C driver dt_s1 = path; #define FILE_READ_LINE 29 (probe arg3) probe arg6 = C driver dt_i1 = fd; probe arg7 = C driver dt_i2 = flags; probe arg8 = C driver dt_i3 = read_offset; probe arg9 = C driver dt_i4 = read_ahead; #define FILE_FDATASYNC 30 (probe arg3) probe arg6 = C driver dt_i1 = fd; #define FILE_FADVISE 31 (probe arg3) probe arg6 = C driver dt_i1 = fd; probe arg7 = C driver dt_i2 = offset; probe arg8 = C driver dt_i3 = length; probe arg9 = C driver dt_i4 = advise_type;