是否可以直接从本机代码记录Android systrace事件,而无需JNI?

Mih*_*i B 3 c android trace android-ndk systrace

Android systrace日志系统非常棒,但它只适用于代码的Java部分,通过Trace.beginSection()Trace.endSection().在代码的C/C++ NDK(本机)部分中,它只能通过JNI使用,JNI在没有Java环境的线程中很慢或不可用...

有没有办法向主systrace跟踪缓冲区添加事件,甚至从本机C代码生成单独的日志?

这个较老的问题提到atrace/ftrace是Android的systrace使用的内部系统.这可以(轻松)进入吗?

BONUS TWIST:由于跟踪调用通常位于性能关键部分,理想情况下可以在实际事件时间之后运行调用.即我一个人宁愿能够指定记录的时间,而不是自己轮询的呼叫.但那只会锦上添花.

Mih*_*i B 9

根据fadden的指针发布一些代码的后续答案.请首先阅读他/她的回答以获得概述.

所需要的只是编写格式正确的字符串/sys/kernel/debug/tracing/trace_marker,可以毫无问题地打开.下面是一些基于cutils头文件C文件的极小代码.我倾向于重新实现它而不是引入任何依赖项,所以如果你非常关心正确性,请检查那里的严格实现,和/或添加自己的额外检查和错误处理.

这已经过测试,适用于Android 4.4.2.

必须首先打开跟踪文件,将文件描述符保存在atrace_marker_fd全局中:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define ATRACE_MESSAGE_LEN 256
int     atrace_marker_fd = -1;

void trace_init()
{
  atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY);
  if (atrace_marker_fd == -1)   { /* do error handling */ }
}
Run Code Online (Sandbox Code Playgroud)

正常的"嵌套"跟踪,如Java Trace.beginSection,Trace.endSection可通过以下方式获得:

inline void trace_begin(const char *name)
{
    char buf[ATRACE_MESSAGE_LEN];
    int len = snprintf(buf, ATRACE_MESSAGE_LEN, "B|%d|%s", getpid(), name);
    write(atrace_marker_fd, buf, len);
}

inline void trace_end()
{
    char c = 'E';
    write(atrace_marker_fd, &c, 1);
}
Run Code Online (Sandbox Code Playgroud)

还有两种跟踪类型可供使用,据我所知,Java无法访问这些类型:跟踪计数器和异步跟踪.

计数器跟踪整数的值并在systrace HTML输出中绘制一个小图.非常有用的东西:

inline void trace_counter(const char *name, const int value)
{
    char buf[ATRACE_MESSAGE_LEN];
    int len = snprintf(buf, ATRACE_MESSAGE_LEN, "C|%d|%s|%i", getpid(), name, value);
    write(atrace_marker_fd, buf, len);
}
Run Code Online (Sandbox Code Playgroud)

异步跟踪产生非嵌套(即简单重叠)的间隔.它们在systrace HTML输出中的细线程状态栏上方显示为灰色段.它们采用额外的32位整数参数来"区分同时发生的事件".结束跟踪时必须使用相同的名称和整数:

inline void trace_async_begin(const char *name, const int32_t cookie)
{
    char buf[ATRACE_MESSAGE_LEN];
    int len = snprintf(buf, ATRACE_MESSAGE_LEN, "S|%d|%s|%i", getpid(), name, cookie);
    write(atrace_marker_fd, buf, len);
}

inline void trace_async_end(const char *name, const int32_t cookie)
{
    char buf[ATRACE_MESSAGE_LEN];
    int len = snprintf(buf, ATRACE_MESSAGE_LEN, "F|%d|%s|%i", getpid(), name, cookie);
    write(atrace_marker_fd, buf, len);
}
Run Code Online (Sandbox Code Playgroud)

最后,确实似乎没有办法指定记录时间,没有重新编译Android,所以这对"奖励扭曲"没有任何作用.


fad*_*den 7

我不认为它是从NDK暴露出来的.

如果查看源代码,可以看到android.os.Trace类调用本机代码来完成实际工作.该代码的调用atrace_begin()atrace_end(),其在声明中cutils头库.

如果从完整源代码树中提取标头并链接到内部库,则可以直接使用atrace函数.但是,您可以从标题中看到atrace_begin()简单的:

static inline void atrace_begin(uint64_t tag, const char* name)
{
    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
        char buf[ATRACE_MESSAGE_LENGTH];
        size_t len;
        len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "B|%d|%s", getpid(), name);
        write(atrace_marker_fd, buf, len);
    }
}
Run Code Online (Sandbox Code Playgroud)

事件直接写入跟踪文件描述符.(请注意,时间戳不是事件的一部分;它是自动添加的.)您可以在代码中执行类似操作; 请参阅atrace_init_once() .c文件以查看文件的打开方式.

请记住,除非atrace作为NDK的一部分发布,否则使用它的任何代码都将是不可移植的,并且可能在过去或未来版本的Android中失败.但是,由于systrace是一个调试工具,而不是你真正想要在应用程序中启用的东西,兼容性可能不是一个问题.