3

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

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

这个较早的问题提到 atrace/ftrace 是 Android 的 systrace 使用的内部系统。可以(轻松地)利用它吗?

BONUS TWIST:由于跟踪调用通常在性能关键部分,理想情况下应该可以在实际事件时间之后运行调用。即我更愿意能够指定记录的时间,而不是调用自己轮询它。但这只是锦上添花。

4

3 回答 3

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 */ }
}

正常的“嵌套”跟踪,如 Java Trace.beginSectionTrace.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);
}

还有两种可用的跟踪类型,据我所知,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);
}

异步跟踪产生非嵌套(即简单地重叠)间隔。它们在 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);
}

最后,除了重新编译 Android 之外,似乎确实没有办法指定记录时间,所以这对“额外的扭曲”没有任何作用。

于 2015-01-22T11:10:37.183 回答
7

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

如果您查看源代码,您可以看到android.os.Trace 类调用本机代码来完成实际工作。该代码调用atrace_begin()and 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);
    }
}

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

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

于 2015-01-19T02:19:16.373 回答
1

对于将来在谷歌上搜索这个问题的任何人。

自 API 级别 23 起支持本机跟踪事件,请在此处查看文档https://developer.android.com/topic/performance/tracing/custom-events-native

于 2022-02-21T09:47:02.143 回答