性能优化必学基础:perfetto/systrace添加抓取trace块java/c++方法汇总

背景:

经常大家做系统性能优化时候,会使用抓取perfetto的trace来进行分析,主要就是分析perfetto上的各个执行块耗时长短,或者说count计数块等。
具体如下图所示:
在这里插入图片描述这些perfetto上展示的执行块slice,其实一般都是在aosp的原生代码中进行的埋点,简单说看到每个slice其实都是有对应的埋点代码,当然这个代码可能是java,c++等,而且哪怕都是java和c++这种也分为是基于源码编译的java,c++代码还是说基于sdk和ndk的java、c++代码,这些都是有差异的,所以针对这些不同的代码和编译情况今天都进行汇总,给大家展示应该如何在自己程序埋入perfetto的slice。
主要会汇总以下4个部分的代码:

原生开发的两个

Java(platform private)
c/c++(platform private)

SDK和NDK开发的两个

Java(SDK)
c/c++(NDK)

perfetto中trace设置的方法汇总

普通线程执行的同步块

Thread-scoped synchronous slices

最为常见的一种slice设置方法,主要就是在要执行体的前后加trace埋点接口方法既可以

Java(platform private)
比如framework开发可以修改原生的java源码情况,就可以使用该方法

代码参考:
frameworks/base/core/java/android/os/Trace.java

案例

import android.os.Trace;
import static android.os.Trace.TRACE_TAG_AUDIO;

public void playSound(String path) {
  Trace.traceBegin(TRACE_TAG_AUDIO, "PlaySound");
  try {
    // Measure the time it takes to open the sound sevice.
    Trace.traceBegin(TRACE_TAG_AUDIO, "OpenAudioDevice");
    try {
      SoundDevice dev = openAudioDevice();
    } finally {
      Trace.traceEnd();
    }

    for(...) {
      Trace.traceBegin(TRACE_TAG_AUDIO, "SendBuffer");
      try {
        sendAudioBuffer(dev, ...)
      } finally {
        Trace.traceEnd();
      }
      // Log buffer usage statistics in the trace.
      Trace.setCounter(TRACE_TAG_AUDIO, "SndBufferUsage", dev->buffer)
      ...
    }
  } finally {
    Trace.traceEnd();  // End of the root PlaySound slice
  }
}

c++(platform private)

比如framework开发可以修改原生的c++源码情况,就可以使用该方法
案例

// ATRACE_TAG is the category that will be used in this translation unit.
// Pick one of the categories defined in Android's
// system/core/libcutils/include/cutils/trace.h
#define ATRACE_TAG ATRACE_TAG_AUDIO

#include <cutils/trace.h>

void PlaySound(const char* path) {
  ATRACE_BEGIN("PlaySound");

  // Measure the time it takes to open the sound sevice.
  ATRACE_BEGIN("OpenAudioDevice");
  struct snd_dev* dev = OpenAudioDevice();
  ATRACE_END();

  for(...) {
    ATRACE_BEGIN("SendBuffer");
    SendAudioBuffer(dev, ...)
    ATRACE_END();

    // Log buffer usage statistics in the trace.
    ATRACE_INT("SndBufferUsage", dev->buffer);
    ...
  }

  ATRACE_END();  // End of the root PlaySound slice
}

Java(SDK)
比如普通的app开发就需要使用该方法

参考: https://developer.android.com/reference/android/os/Trace
案例

// You cannot choose a tag/category when using the SDK API.
// Implicitly all calls use the ATRACE_TAG_APP tag.
import android.os.Trace;

public void playSound(String path) {
  try {
    Trace.beginSection("PlaySound");

    // Measure the time it takes to open the sound sevice.
    Trace.beginSection("OpenAudioDevice");
    try {
      SoundDevice dev = openAudioDevice();
    } finally {
      Trace.endSection();
    }

    for(...) {
      Trace.beginSection("SendBuffer");
      try {
        sendAudioBuffer(dev, ...)
      } finally {
        Trace.endSection();
      }

      // Log buffer usage statistics in the trace.
      Trace.setCounter("SndBufferUsage", dev->buffer)
      ...
    }
  } finally {
    Trace.endSection();  // End of the root PlaySound slice
  }
}

c/c++(NDK)

比如普通的app的NDK开发,就需要使用该方法

参考:https://developer.android.com/ndk/reference/group/tracing
案例

// You cannot choose a tag/category when using the NDK API.
// Implicitly all calls use the ATRACE_TAG_APP tag.
#include <android/trace.h>

void PlaySound(const char* path) {
  ATrace_beginSection("PlaySound");

  // Measure the time it takes to open the sound sevice.
  ATRACE_BEGIN("OpenAudioDevice");
  struct snd_dev* dev = OpenAudioDevice();
  ATrace_endSection();

  for(...) {
    ATrace_beginSection("SendBuffer");
    SendAudioBuffer(dev, ...)
    ATrace_endSection();

    // Log buffer usage statistics in the trace.
    ATrace_setCounter("SndBufferUsage", dev->buffer)
    ...
  }

  ATrace_endSection();  // End of the root PlaySound slice
}

计数块

Counters
主要用于一些计数相关的统计,比如Input分析时候需要IQ,OQ,WQ这些值。
数值的改变可以任何线程调用触发对Counter值的改变
在这里插入图片描述
那么这些计数块如何在程序中进行埋点设置呢?

Java(platform private)

使用案例

import android.os.Trace;
import static android.os.Trace.TRACE_TAG_AUDIO;

public void playSound(String path) {
  SoundDevice dev = openAudioDevice();
  for(...) {
    sendAudioBuffer(dev, ...)
    ...
    // Log buffer usage statistics in the trace.
    Trace.setCounter(TRACE_TAG_AUDIO, "SndBufferUsage", dev->buffer.used_bytes)
  }
}

c++(platform private)

使用案例

// ATRACE_TAG is the category that will be used in this translation unit.
// Pick one of the categories defined in Android's
// system/core/libcutils/include/cutils/trace.h
#define ATRACE_TAG ATRACE_TAG_AUDIO

#include <cutils/trace.h>

void PlaySound(const char* path) {
  struct snd_dev* dev = OpenAudioDevice();

  for(...) {
    SendAudioBuffer(dev, ...)

    // Log buffer usage statistics in the trace.
    ATRACE_INT("SndBufferUsage", dev->buffer.used_bytes);
  }
}

Java(SDK)
使用案例

// You cannot choose a tag/category when using the SDK API.
// Implicitly all calls use the ATRACE_TAG_APP tag.
import android.os.Trace;

public void playSound(String path) {
  SoundDevice dev = openAudioDevice();

  for(...) {
    sendAudioBuffer(dev, ...)

    // Log buffer usage statistics in the trace.
    Trace.setCounter("SndBufferUsage", dev->buffer.used_bytes)
  }
}

c/c++(NDK)

使用案例

// You cannot choose a tag/category when using the NDK API.
// Implicitly all calls use the ATRACE_TAG_APP tag.
#include <android/trace.h>

void PlaySound(const char* path) {
  struct snd_dev* dev = OpenAudioDevice();

  for(...) {
    SendAudioBuffer(dev, ...)

    // Log buffer usage statistics in the trace.
    ATrace_setCounter("SndBufferUsage", dev->buffer.used_bytes)
  }
}

异步块

上面最开始有介绍同步块,也就是必须在一个线程中执行,Android其实是支持异步trace的,源码中应用的比较多,在代码中打上异步trace就不需要保证在一个线程之中,也不要求一定要把一段代码包起来,这种可能平时自己开发时候使用较少,下面以一个案例说明。

跟踪一个动画的开始与结束,可以看出它的执行时长,和同步执行那种单线程完全不一样。
在这里插入图片描述对应的代码:
在这里插入图片描述

Java(platform private)

使用案例

import android.os.Trace;
import static android.os.Trace.TRACE_TAG_NETWORK;

public class AudioRecordActivity extends Activity {
  private AtomicInteger lastJobId = new AtomicInteger(0);
  private static final String TRACK_NAME = "User Journeys";

    ...
    button.setOnClickListener(v -> {
        int jobId = lastJobId.incrementAndGet();
        Trace.asyncTraceForTrackBegin(TRACE_TAG_NETWORK, TRACK_NAME, "Load profile", jobId);

        // Simulate async work (e.g., a network request)
        new Thread(() -> {
            Thread.sleep(800); // emulate latency
            Trace.asyncTraceForTrackEnd(TRACE_TAG_NETWORK, TRACK_NAME, jobId);
        }).start();
    });
    ...
}

c++(platform private)

使用案例

// ATRACE_TAG is the category that will be used in this translation unit.
// Pick one of the categories defined in Android's
// system/core/libcutils/include/cutils/trace.h
#define ATRACE_TAG ATRACE_TAG_NETWORK

#include <cutils/trace.h>
#include <thread>
#include <chrono>
#include <atomic>

static constexpr const char* kTrackName = "User Journeys";

void onButtonClicked() {
  static std::atomic<int> lastJobId{0};

  int jobId = ++lastJobId;
  ATRACE_ASYNC_FOR_TRACK_BEGIN(kTrackName, "Load profile", jobId);

  std::thread([jobId]() {
      std::this_thread::sleep_for(std::chrono::milliseconds(800));
      ATRACE_ASYNC_FOR_TRACK_END(kTrackName, jobId);
  }).detach();
}

Java(SDK)
使用案例

// You cannot choose a tag/category when using the SDK API.
// Implicitly all calls use the ATRACE_TAG_APP tag.
import android.os.Trace;

public class AudioRecordActivity extends Activity {
  private AtomicInteger lastJobId = new AtomicInteger(0);

    ...
    button.setOnClickListener(v -> {
        int jobId = lastJobId.incrementAndGet();
        Trace.beginAsyncSection("Load profile", jobId);

        // Simulate async work (e.g., a network request)
        new Thread(() -> {
            Thread.sleep(800); // emulate latency
             Trace.endAsyncSection("Load profile", jobId);
        }).start();
    });
    ...
}

c/c++(NDK)

使用案例

// You cannot choose a tag/category when using the NDK API.
// Implicitly all calls use the ATRACE_TAG_APP tag.
#include <android/trace.h>
#include <thread>
#include <chrono>
#include <atomic>

void onButtonClicked() {
  static std::atomic<int> lastJobId{0};

  int jobId = ++lastJobId;
  ATrace_beginAsyncSection("Load profile", jobId);

  std::thread([jobId]() {
      std::this_thread::sleep_for(std::chrono::milliseconds(800));
      ATrace_endAsyncSection("Load profile", jobId);
  }).detach();
}

上面就是针对perfetto/systrace进行埋点相关slice的详细介绍,更多framework实战开发,可以关注下面“千里马学框架”

Perfetto 是一个功能强大的性能分析工具,适用于多种场景下的系统性能追踪与优化工作。其核心优势在于能够提供完整的上下文信息,从而帮助开发者进行问题的根本原因分析。相比其他仅提供指标监控的工具,Perfetto 不仅支持传统的 systrace 分析,还可以整合 logcat 数据、内存泄漏检测等功能,使得不同维度的信息可以在同一时间轴上进行交叉比对,提升问题诊断效率[^2]。 ### Perfetto 的基本使用流程 1. **数据采集** Perfetto 支持通过配置文件定义追踪内容,并启动追踪会话。以下是一个典型的配置文件示例(`config.cfg`): ```text data_sources: { config { name: "linux.ftrace" ftrace_config { ftrace_events: "sched/sched_switch" ftrace_events: "power/cpu_frequency" atrace_categories: "gfx" atrace_categories: "view" buffer_size_kb: 1024 } } } duration_ms: 10000 write_into_file: true ``` 使用如下命令启动追踪: ```bash perfetto -c config.cfg -o trace.pb ``` 2. **数据解析与可视化** 追踪完成后,可以使用 `trace_processor` 工具进行分析或导出为 JSON 格式供浏览器查看: ```bash ./tools/trace_processor --query "SELECT * FROM slices LIMIT 10" trace.pb ``` 也可以使用 Chrome 浏览器打开 `.pb` 文件,直接在 UI 中浏览追踪结果。 3. **Trace-based Metrics 分析** Perfetto 提供了基于 Trace 的 Metrics 功能,可以用于自动化提取性能指标。例如,分析 CPU 相关的指标可以通过如下命令: ```bash ./tools/trace_processor --run-metrics android_cpu --metric-extension src/trace_processor/metrics@/ <trace.pb> ``` 这将输出一组结构化的性能数据,便于后续统计和监控使用[^3]。 ### Perfetto 在系统优化中的典型应用场景 - **CPU 调度分析**:通过追踪 `sched_switch` 和 `cpu_frequency` 等事件,识别线程调度延迟、CPU 瓶颈等问题。 - **图形渲染性能分析**:结合 `atrace` 中的 `gfx` 类别,分析 GPU 渲染流水线中的卡顿点。 - **内存泄漏检测**:支持 ION 和 DMA-BUF 泄漏分析,帮助定位 Android 系统中的原生内存问题。 - **多源日志融合分析**:将 logcat 日志与 trace 事件同步显示,便于关联异常行为与具体代码逻辑。 ### 总结 Perfetto 作为现代性能分析工具链的重要组成部分,提供了从数据采集、分析到可视化的完整解决方案。其灵活性和可扩展性使其不仅适用于短期问题排查,也适合构建长期性能监控体系。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值