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
或者BEGIN
D脚本的部分lib/runtime_tools/examples/efile_drv.d
...
old-name
并且new-name
是rename(2)
系统调用的源和目标的两个字符串参数。两个整数参数未使用; 无论如何,简单的格式代码打印参数0和0。
- 代表Erlang端口调用了工作池代码#Port<0.59>。
- 系统调用失败,POSIX errno值为2
ENOENT
,因为路径old-name
不存在。
efile_drv-int_entry
和efile_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;