Systrace 报告解读Google官方教程

本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容:

一、前言
二、Systrace 报告解读
三、客制化Systrace event 标签
四、总结

一、前言

不要畏惧看不懂,我们要往懂的看

首先不要抗拒英文文章,本文是转载Google 官方文档,故英文偏多,只有阅读原汁原味的内容,才可以真正提升自己。

二、Systrace 报告解读

This guide explains how to navigate around and interpret a Systrace report. To interpret a Perfetto report, see the Trace Processor documentation.

2.1 Elements of a typical report

Systrace generates an output HTML file that contains a series of sections. The report lists the threads of each process. If a given thread renders UI frames, the report also indicates the rendered frames along the timeline. Time passes in the forward direction as you move left to right across the report.

From top to bottom, the report contains the following sections.

User interactions

The first section contains bars that represent specific user interactions within your app or game, such as tapping on the device's screen. These interactions serve as useful time markers.

CPU activity

The next section shows bars that represent thread activity within each CPU. The bars show CPU activity across all apps, including your app or game.

The CPU activity section is expandable, allowing you to view clock frequency for each CPU. Figure 1 shows an example of a collapsed CPU activity section, and Figure 2 shows an expanded version that displays clock frequency:

Figure 1. Sample CPU activity (collapsed view) in a Systrace report
Figure 2. Sample CPU activity (expanded view) that shows CPU clock frequency in a Systrace report

System events

The histograms in this section show specific system-level events, such as texture counts and the total size of specific objects.

A histogram worth checking more closely is the one labeled SurfaceView. The count represents the number of composed frame buffers that have been passed into the display pipeline and are waiting to be shown on the device's screen. Because most devices are double- or triple-buffered, this count is almost always 0, 1, or 2.

Other histograms depicting the Surface Flinger process, including VSync events and UI thread swap work, appear in Figure 3:


Figure 3. Sample Surface Flinger graph in a Systrace report

Display frames

This section, often the tallest in the report, depicts a multicolored line followed by stacks of bars. These shapes represent the status and frame stack of a particular thread that's been created. Each level of the stack represents a call to beginSection(), or the beginning of a custom trace event that you've defined for your app or game.

Note: The UI thread, or the main thread where your app or game typically runs, always appears as the first thread.

The multicolored line above each stack of bars represents a particular thread's set of statuses over time. Each segment of the line can contain one of the following colors:

Green: Running

The thread is completing work related to a process or is responding to an interrupt.

Blue: Runnable

The thread is available to run but isn't currently scheduled.

White: Sleeping

The thread has no work to do, perhaps because the thread is blocked on a mutex lock.

Orange: Uninterruptable sleep

The thread is blocked on I/O or waiting for a disk operation to complete.

Purple: Interruptable sleep

The thread is blocked on another kernel operation, usually memory management.

Note: Within the Systrace report, you can click on the line to determine which CPU had control of the thread at a given time.

2.2 Keyboard shortcuts

The following table lists the keyboard shortcuts that are available while viewing a Systrace report:

WASDEM 快捷键代表含义
快捷工具栏上的图标代表的含义

2.3 Investigate performance problems

When interacting with a Systrace report, you can inspect device CPU usage over the duration of the recording. For help navigating the HTML report, see the keyboard shortcuts section, or click the ? button in the top-right corner of the report.

The sections below explain how to inspect information in the report to find and fix performance problems.

Identify performance concerns

When navigating around a Systrace report, you can identify performance concerns more easily by doing one or more of the following:

  • Select a time interval of interest by drawing a rectangle around the time interval.
  • Mark or highlight a problem area using the ruler tool.
  • Show each display refresh operation by clicking View Options > Highlight VSync.

Inspect UI frames and alerts

Note: The content in this section is relevant only to managed code, as Systrace looks at the system's Java-based choreographer to provide frame information. For guidance specific to native code, particularly games, see the discussion on framerate consistency.

As shown in Figure 4, a Systrace report lists each process that renders UI frames and indicates each rendered frame along the timeline. Frames that render within the 16.6 milliseconds required to maintain a stable 60 frames per second are indicated with green frame circles. Frames that take longer than 16.6 milliseconds to render are indicated with yellow or red frame circles.

Figure 4. Systrace display after zooming in on a long-running frame

Note: On devices running Android 5.0 (API level 21) or higher, the work of rendering a frame is split between the UI thread and the render thread. On prior versions, all work in creating a frame is done on the UI thread.

Clicking on a frame circle highlights it and provides additional information about the work done by the system to render that frame, including alerts. The report also shows you the methods that the system was executing while rendering that frame. You can investigate those methods to determine potential causes of UI jank.

Figure 5. Selecting the problematic frame, an alert appears below the trace report identifying the problem

After you select a slow frame, you may see an alert in the bottom pane of the report. The alert shown in Figure 5 calls out that the primary problem with the frame is that too much time is spent inside ListView recycling and rebinding. There are links to the relevant events in the trace that explain more about what the system is doing during this time.

To see each alert that the tool discovered in your trace, as well as the number of times that the device triggered each alert, click the Alerts tab at the far right of the window, as shown in Figure 6. The Alerts panel helps you see which problems occur in the trace and how often they contribute to jank. You can think of this panel as a list of bugs to be fixed. Often, a small change or improvement in one area can remove an entire set of alerts.

Figure 6. Clicking the Alert button reveals the alert tab

If you see too much work being done on the UI thread, use one of the following approaches to help determine which methods are consuming too much CPU time:

  • If you have an idea as to which methods could be causing bottlenecks, add trace markers to these methods. To learn more, see the guide on how to define custom events in your code.
  • If you're unsure as to the source of UI bottlenecks, use the CPU Profiler that's available in Android Studio. You can generate trace logs, and then import and inspect them using the CPU Profiler.

三、客制化Systrace event 标签

System tracing shows you information about processes only at the system level, so it's sometimes difficult to know which of your app or game's methods were executing at a given time relative to system events.

The Android platform provides a tracing API which you can use to label a particular section of code. If you capture a new system trace of the "debug" version of your app and include the -a option, as shown in the following snippet, these custom events appear in a Systrace report:

$ python systrace.py -a com.example.myapp -b 16384 \
  -o my_systrace_report.html sched freq idle am wm gfx view binder_driver hal \
  dalvik camera input res

The-aoption is required for tracing your app; without this option, your app's methods will not appear in a Systrace report.

Note: This approach is different from using the Debug class, which helps you inspect detailed app CPU usage by generating .trace files.

This guide describes how to define custom events in both managed code and native code.

3.1 Managed code

In Android 4.3 (API level 18) and higher, you can use the Trace class in your code to define custom events that then appear in Perfetto and Systrace reports, as shown in the following code snippet.

Note: When you call beginSection() multiple times, calling endSection() ends only the most recently called beginSection() method. So, for nested calls, such as those in the following snippet, make sure that you properly match each call to beginSection() with a call to endSection().

Additionally, you cannot call beginSection() on one thread and end it from another thread; you must call both methods on the same thread.

public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Trace.beginSection("MyAdapter.onCreateViewHolder");
        MyViewHolder myViewHolder;
        try {
            myViewHolder = MyViewHolder.newInstance(parent);
        } finally {
            // In try and catch statements, always call "endSection()" in a
            // "finally" block. That way, the method is invoked even when an
            // exception occurs.
            Trace.endSection();
        }
        return myViewHolder;
    }

   @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        Trace.beginSection("MyAdapter.onBindViewHolder");
        try {
            try {
                Trace.beginSection("MyAdapter.queryDatabase");
                RowItem rowItem = queryDatabase(position);
                dataset.add(rowItem);
            } finally {
                Trace.endSection();
            }
            holder.bind(dataset.get(position));
        } finally {
            Trace.endSection();
        }
    }
}

3.2 Native code

Android 6.0 (API level 23) and higher support a native tracing API, trace.h, to write trace events to the system buffer that you can then analyze using Perfetto or Systrace. Common use cases for this API include observing the time that a particular block of code takes to execute and associating a block of code with undesirable system behavior.

On devices and emulators running API level 27 and lower, if there isn't enough memory available or the memory is too fragmented, you'll get the following message: Atrace could not allocate enough memory to record a trace. If this happens and your capture does not have a complete set of data, close background processes, or restart the device or emulator.

To define custom events that occur in the native code within your app or game, complete the following steps:

1.Define function pointers for the ATrace functions that you use to capture custom events within your game, as shown in the following code snippet:

#include <android/trace.h>
#include <dlfcn.h>

void *(*ATrace_beginSection) (const char* sectionName);
void *(*ATrace_endSection) (void);

typedef void *(*fp_ATrace_beginSection) (const char* sectionName);
typedef void *(*fp_ATrace_endSection) (void);
  1. Load the ATrace symbols at runtime, as shown in the following code snippet. Usually, you perform this process in an object constructor.
// Retrieve a handle to libandroid.
void *lib = dlopen("libandroid.so", RTLD_NOW || RTLD_LOCAL);

// Access the native tracing functions.
if (lib != NULL) {
    // Use dlsym() to prevent crashes on devices running Android 5.1
    // (API level 22) or lower.
    ATrace_beginSection = reinterpret_cast<fp_ATrace_beginSection>(
        dlsym(lib, "ATrace_beginSection"));
    ATrace_endSEction = reinterpret_cast<fp_ATrace_endSection>(
        dlsym(lib, "ATrace_endSection"));
}

Caution: For security reasons, include calls to dlopen() only in the debug version of your app or game.

**Note: **To provide tracing support further back to Android 4.3 (API level 18), you can use JNI to call the methods in managed code around the code shown in the preceding snippet.

3.Call ATrace_beginSection() and ATrace_endSection() at the beginning and end, respectively, of your custom event:

#include <android/trace.h>

char *customEventName = new char[32];
sprintf(customEventName, "User tapped %s button", buttonName);

ATrace_beginSection(customEventName);
// Your app or game's response to the button being pressed.
ATrace_endSection();

Note: When you call ATrace_beginSection() multiple times, calling ATrace_endSection() ends only the most recently called ATrace_beginSection() method. So, for nested calls, make sure that you properly match each call to ATrace_beginSection() with a call to ATrace_endSection().

Additionally, you cannot call ATrace_beginSection() on one thread and end it from another. You must call both functions from the same thread.

3.3 Convenience tips

The following tips are optional but might make it easier to analyze your native code.

Trace an entire function

When instrumenting your call stack or function timing, you might find it useful to trace entire functions. You can use the ATRACE_CALL() macro to make this type of tracing easier to set up. Furthermore, such a macro allows you to skip creating try and catch blocks for cases where the traced function might throw an exception or call return early.

To create a macro for tracing an entire function, complete the following steps:

1.Define the macro:

#define ATRACE_NAME(name) ScopedTrace ___tracer(name)

// ATRACE_CALL is an ATRACE_NAME that uses the current function name.
#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)

class ScopedTrace {
  public:
    inline ScopedTrace(const char *name) {
      ATrace_beginSection(name);
    }

    inline ~ScopedTrace() {
      ATrace_endSection();
    }
};

2.Call the macro within the function that you want to trace:

void myExpensiveFunction() {
  ATRACE_CALL();
  // Code that you want to trace.
}

Name your threads

You can give a name to each thread in which your events occur, as demonstrated in the following code snippet. This step makes it easier to identify the threads that belong to specific actions within your game.

#include <pthread.h>

static void *render_scene(void *parm) {
    // Code for preparing your app or game's visual components.
}

static void *load_main_menu(void *parm) {
    // Code that executes your app or game's main logic.
}

void init_threads() {
    pthread_t render_thread, main_thread;

    pthread_create(&render_thread, NULL, render_scene, NULL);
    pthread_create(&main_thread, NULL, load_main_menu, NULL);

    pthread_setname_np(render_thread, "MyRenderer");
    pthread_setname_np(main_thread, "MyMainMenu");
}

四、总结

1. app层 自定义 TAG

自定义方法如下:


import Android.os.Trace;

Trace.beginSection(String sectionName)�
... ...
Trace.EndSection() 

2. Framework 层自定义 TAG

自定义方法如下:


import Android.os.Trace; 

Trace.traceBegin(long traceTag, String methodName)
... ...
Trace.traceEnd(long traceTag)

3. Native 层自定义 TAG

自定义方法如下:


#include<utils/Trace.h> 
ATRACE_BEGIN("TEST");
... ...
ATRACE_END();

友情推荐:
Android 干货分享

至此,本篇已结束,如有不对的地方,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!