【问题标题】:Is logging Android systrace events directly from native code possible, without JNI?是否可以在没有 JNI 的情况下直接从本机代码记录 Android systrace 事件?
【发布时间】:2015-01-17 16:59:31
【问题描述】:

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

有什么方法可以将事件添加到主 systrace 跟踪缓冲区,甚至可以从原生 C 代码生成单独的日志?

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

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

【问题讨论】:

    标签: android c android-ndk trace systrace


    【解决方案1】:

    根据 fadden 的指示,用一些代码发布后续答案。请先阅读他/她的回答以了解概述。

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

    经测试可在 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 无法访问它们:跟踪计数器和异步跟踪。

    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 位整数参数来“区分同时发生的事件”。结束跟踪时必须使用相同的名称和整数:

    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 之外,似乎确实没有办法指定记录的时间,所以这对“额外的扭曲”没有任何作用。

    【讨论】:

      【解决方案2】:

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

      如果您查看源代码,您可以看到android.os.Trace class 调用native code 来完成实际工作。该代码调用atrace_begin()atrace_end(),它们在header in the 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() in the .c file 以查看文件是如何打开的。

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

      【讨论】:

      • 非常感谢您的指点!它还解决了我关于人们如何在他们的 systrace 输出中获取跟踪计数器的另一个问题......我自己在单独的答案中添加了一些解决方案的实现代码。留下你的作为接受的答案。
      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 2019-06-26
        • 1970-01-01
        • 2016-07-17
        • 2012-12-19
        • 2017-09-17
        • 2014-06-16
        • 1970-01-01
        • 1970-01-01
        • 2021-12-17
        相关资源
        最近更新 更多