是否可以在没有 JNI 的情况下直接从本机代码记录 Android systrace 事件?

Is logging Android systrace events directly from native code possible, without JNI?

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

是否有任何方法可以从本机 C 代码向主 systrace 跟踪缓冲区添加事件,甚至生成单独的日志?

This 较旧的问题提到 atrace/ftrace 作为内部系统 Android 的 systrace 使用。可以(轻松)利用它吗?

额外奖励:由于跟踪调用通常处于性能关键部分,因此理想情况下应该可以 运行 调用 实际事件时间之后。也就是说,我个人更愿意能够指定记录的时间,而不是调用自己轮询的时间。但这只是锦上添花。

我认为它没有从 NDK 公开。

如果您查看源代码,您可以看到 android.os.Trace class calls into native code to do the actual work. That code calls atrace_begin() and atrace_end(), which are declared in a header in the cutils 库。

如果您从完整源代码树中提取 header 并针对内部库提取 link,则您可以直接使用 atrace 函数。但是,您可以从 header 中看到 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() in the .c file 以查看文件是如何打开的。

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

根据 fadden 的指示发布带有一些代码的后续答案。请先阅读 his/her 回答以获得概述。

只需将格式正确的字符串写入/sys/kernel/debug/tracing/trace_marker,即可毫无问题地打开。下面是一些基于 cutils header and C file 的非常简单的代码。我更喜欢重新实现它而不是引入任何依赖项,所以如果你非常关心正确性,请检查那里的严格实现,and/or 添加你自己的额外检查和错误处理。

这已经过测试,可以在 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 */ }
}

正常 'nested' 迹线 像 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 无法访问它们:跟踪计数器和异步跟踪。

Counters 跟踪一个整数的值并在 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 位整数参数 "distinguishes simultaneous events"。结束跟踪时必须使用相同的名称和整数:

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,似乎确实没有办法指定记录时间,所以这对 "bonus twist".

没有任何作用

对于以后用谷歌搜索这个问题的人。

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