系统跟踪仅在系统级别显示进程的相关信息,因此有时很难知道应用或游戏的哪些方法是在给定时间针对系统事件执行的。
Android 平台提供了一个跟踪 API,可用于为特定的代码段添加标签。如果您捕获应用的“调试”版本的新系统跟踪并添加 -a
选项(如以下代码段所示),这些自定义事件便会显示在 Systrace 报告中:
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
必须提供 -a
选项才能跟踪应用;如果没有此选项,应用的方法将不会显示在 Systrace 报告中。
本指南介绍了如何在托管代码和原生代码中定义自定义事件。
托管代码
在 Android 4.3(API 级别 18)及更高版本中,您可以在代码中使用 Trace
类来定义随后会出现在 Perfetto 和 Systrace 报告中的自定义事件,如以下代码段所示。
注意:如果您多次调用 beginSection()
,调用 endSection()
只会结束最后调用的 beginSection()
方法。因此,对于嵌套调用(如以下代码段中所示),请务必将每次对 beginSection()
的调用与一次对 endSection()
的调用正确匹配。
此外,您不能在一个线程上调用 beginSection()
,而在另一个线程上结束它;您必须在同一个线程上调用这两个方法。
Kotlin
class MyAdapter : RecyclerView.Adapter<MyViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { return try { Trace.beginSection("MyAdapter.onCreateViewHolder") 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() } } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { Trace.beginSection("MyAdapter.onBindViewHolder") try { try { Trace.beginSection("MyAdapter.queryDatabase") val rowItem = queryDatabase(position) dataset.add(rowItem) } finally { Trace.endSection() } holder.bind(dataset[position]) } finally { Trace.endSection() } } }
Java
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(); } } }
原生代码
Android 6.0(API 级别 23)及更高版本支持原生跟踪 API trace.h
,用于将跟踪事件写入系统缓冲区,以供您随后使用 Perfetto 或 Systrace 进行分析。此 API 的常见用例包括观察特定代码块的执行时间以及确定引起不良系统行为的代码块。
在搭载 API 级别 27 及更低级别的设备和模拟器上,如果没有足够的可用内存或内存过于碎片化,您将收到以下消息:Atrace could not allocate enough memory to record a trace
。如果发生这种情况,并且您没有捕获到完整的数据集,请关闭后台进程或重新启动设备或模拟器。
如需定义应用或游戏内的原生代码中发生的自定义事件,请完成以下步骤:
定义在游戏内捕获自定义事件所用 ATrace 函数的函数指针,如以下代码段所示:
#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);
在运行时加载 ATrace 符号,如以下代码段所示。通常在对象构造函数中执行此过程。
// 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")); }
注意:出于安全考虑,请仅在应用或游戏的调试版本中包含对
dlopen()
的调用。注意:为了进一步在 Android 4.3(API 级别 18)中提供跟踪支持,您可以使用 JNI 调用托管代码(在上述代码段中显示的代码附近)中的方法。
在自定义事件的开头和结尾分别调用
ATrace_beginSection()
和ATrace_endSection()
:#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();
注意:如果您多次调用
ATrace_beginSection()
,调用ATrace_endSection()
只会结束最后调用的ATrace_beginSection()
方法。因此,对于嵌套调用,请务必将每次对ATrace_beginSection()
的调用与一次对ATrace_endSection()
的调用正确匹配。此外,您不能在一个线程上调用
ATrace_beginSection()
,而在另一个线程上结束它。您必须在同一线程中调用这两个函数。
温馨提示
以下提示是可选的,但可能会帮助分析人员更轻松地分析原生代码。
跟踪整个函数
在检测调用堆栈或函数计时时,您可能会发现跟踪整个函数非常有用。使用 ATRACE_CALL()
宏可以更轻松地设置此类跟踪。此外,如果跟踪的函数会抛出异常或提前调用 return
,使用这样的宏还可以跳过创建 try
和 catch
代码块。
如需创建用于跟踪整个函数的宏,请完成以下步骤:
定义宏:
#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(); } };
在要跟踪的函数中调用此宏:
void myExpensiveFunction() { ATRACE_CALL(); // Code that you want to trace. }
为线程命名
您可以为发生事件的每个线程命名,如以下代码段所示。此步骤可让您更轻松地识别属于游戏中特定操作的线程。
#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"); }