Sự kiện theo dõi tuỳ chỉnh trong mã gốc
Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang
Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.
Android 6.0 (API cấp 23) trở lên hỗ trợ API theo dõi gốc, trace.h
, để viết các sự kiện theo dõi vào bộ đệm hệ thống mà sau đó bạn có thể phân tích bằng
Perfetto hoặc systrace. Các trường hợp sử dụng phổ biến cho API này bao gồm việc quan sát thời gian mà một khối mã cụ thể cần để thực thi và liên kết khối mã với hành vi không mong muốn của hệ thống.
Lưu ý: Trên các thiết bị và trình mô phỏng chạy API cấp 27 trở xuống, nếu không có đủ bộ nhớ hoặc bộ nhớ phân quá nhiều mảnh, bạn sẽ nhận được thông báo sau: Atrace could not allocate enough memory to record a trace
.
Nếu điều này xảy ra và ảnh chụp của bạn không có tập dữ liệu hoàn chỉnh, thì bạn nên đóng các quy trình trong nền hoặc khởi động lại thiết bị hoặc trình mô phỏng.
Để xác định các sự kiện tùy chỉnh xuất hiện trong mã gốc trong ứng dụng hoặc trò chơi của bạn, hãy hoàn thành các bước sau:
Xác định con trỏ hàm cho các hàm ATrace mà bạn dùng để ghi lại các sự kiện tùy chỉnh trong ứng dụng hoặc trò chơi của mình, như minh họa trong đoạn mã sau:
#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);
Tải các biểu tượng ATrace vào thời gian chạy, như minh họa trong đoạn mã sau. Thông thường, bạn thực hiện quá trình này trong một hàm dựng đối tượng.
// 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"));
}
Cẩn trọng: Để bảo mật, hãy chỉ gọi dlopen()
theo phiên bản gỡ lỗi của ứng dụng hoặc trò chơi của bạn.
Lưu ý: Để hỗ trợ kỹ thuật truy vết trở lại cho Android 4.3 (API cấp 18), bạn có thể dùng JNI để gọi các phương thức trong mã được quản lý có liên quan đến mã hiển thị trong đoạn mã trước.
Gọi ATrace_beginSection()
và ATrace_endSection()
ở đầu và cuối của sự kiện tùy chỉnh:
#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();
Lưu ý: Khi bạn gọi ATrace_beginSection()
nhiều lần, việc gọi ATrace_endSection()
chỉ kết thúc bằng phương thức ATrace_beginSection()
được gọi gần đây nhất. Vì vậy, đối với các lệnh gọi được lồng, hãy đảm bảo bạn so khớp đúng cách từng lệnh gọi tới ATrace_beginSection()
với lệnh gọi tới ATrace_endSection()
.
Ngoài ra, bạn không thể gọi ATrace_beginSection()
trên một chuỗi và kết thúc chuỗi này từ một chuỗi khác. Bạn phải gọi cả hai hàm từ cùng một chuỗi.
Mẹo thuận tiện
Các mẹo sau đây không bắt buộc nhưng có thể giúp bạn phân tích mã gốc dễ dàng hơn.
Tính năng theo dõi toàn bộ một hàm
Khi đo lường ngăn xếp lệnh gọi hoặc thời gian gọi hàm, bạn có thể thấy tính năng theo dõi toàn bộ hàm này hữu ích. Bạn có thể sử dụng macro ATRACE_CALL()
để giúp cho việc thiết lập tính năng theo dõi này dễ dàng hơn. Hơn nữa, một macro như thế này cho phép bạn bỏ qua việc tạo các khối try
và catch
, phòng trường hợp hàm theo dõi có thể gửi một trường hợp ngoại lệ hoặc gọi return
sớm.
Để tạo macro theo dõi toàn bộ hàm, hãy hoàn tất các bước sau:
Xác định 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();
}
};
Gọi macro trong hàm mà bạn muốn theo dõi:
void myExpensiveFunction() {
ATRACE_CALL();
// Code that you want to trace.
}
Đặt tên cho chuỗi của bạn
Bạn có thể đặt tên cho từng chuỗi mà sự kiện xảy ra, như được minh họa
trong đoạn mã sau. Bước này giúp bạn dễ dàng hơn trong việc xác định các luồng thuộc về những hành động cụ thể trong trò chơi của mình.
#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");
}
Đề xuất cho bạn
Nội dung và mã mẫu trên trang này phải tuân thủ các giấy phép như mô tả trong phần Giấy phép nội dung. Java và OpenJDK là nhãn hiệu hoặc nhãn hiệu đã đăng ký của Oracle và/hoặc đơn vị liên kết của Oracle.
Cập nhật lần gần đây nhất: 2025-07-27 UTC.
[[["Dễ hiểu","easyToUnderstand","thumb-up"],["Giúp tôi giải quyết được vấn đề","solvedMyProblem","thumb-up"],["Khác","otherUp","thumb-up"]],[["Thiếu thông tin tôi cần","missingTheInformationINeed","thumb-down"],["Quá phức tạp/quá nhiều bước","tooComplicatedTooManySteps","thumb-down"],["Đã lỗi thời","outOfDate","thumb-down"],["Vấn đề về bản dịch","translationIssue","thumb-down"],["Vấn đề về mẫu/mã","samplesCodeIssue","thumb-down"],["Khác","otherDown","thumb-down"]],["Cập nhật lần gần đây nhất: 2025-07-27 UTC."],[],[],null,["# Custom trace events in native code\n\nAndroid 6.0 (API level 23) and higher support a native tracing API, `trace.h`,\nto write trace events to the system buffer that you can then analyze using\nPerfetto or systrace. Common use cases for this API include observing the time\nthat a particular block of code takes to execute and associating a block of code\nwith undesirable system behavior.\n\n**Note:** On devices and emulators running API level 27 and lower, if there\nisn't enough memory available or the memory is too fragmented, you'll get the\nfollowing message: `Atrace could not allocate enough memory to record a trace`.\nIf this happens and your capture does not have a complete set of data, then you\nshould close background processes or restart the device or emulator.\n\nTo define custom events that occur in the native code within your app or game,\ncomplete the following steps:\n\n1. Define function pointers for the ATrace functions that you use to\n capture custom events within your app or game, as shown in the following code\n snippet:\n\n ```c++\n #include \u003candroid/trace.h\u003e\n #include \u003cdlfcn.h\u003e\n\n void *(*ATrace_beginSection) (const char* sectionName);\n void *(*ATrace_endSection) (void);\n\n typedef void *(*fp_ATrace_beginSection) (const char* sectionName);\n typedef void *(*fp_ATrace_endSection) (void);\n ```\n2. Load the ATrace symbols at runtime, as shown in the following code\n snippet. Usually, you perform this process in an object constructor.\n\n ```c++\n // Retrieve a handle to libandroid.\n void *lib = dlopen(\"libandroid.so\", RTLD_NOW | RTLD_LOCAL);\n\n // Access the native tracing functions.\n if (lib != NULL) {\n // Use dlsym() to prevent crashes on devices running Android 5.1\n // (API level 22) or lower.\n ATrace_beginSection = reinterpret_cast\u003cfp_ATrace_beginSection\u003e(\n dlsym(lib, \"ATrace_beginSection\"));\n ATrace_endSection = reinterpret_cast\u003cfp_ATrace_endSection\u003e(\n dlsym(lib, \"ATrace_endSection\"));\n }\n ```\n\n **Caution:** For security reasons, include calls to\n `dlopen()` only in the debug version of your app or game.\n\n **Note:** To provide tracing support further back to\n Android 4.3 (API level 18), you can use JNI to call the methods in\n [managed code](#managed-code) around the code shown in the\n preceding snippet.\n3. Call `ATrace_beginSection()` and\n `ATrace_endSection()` at the beginning and end, respectively, of\n your custom event:\n\n ```c++\n #include \u003candroid/trace.h\u003e\n\n char *customEventName = new char[32];\n sprintf(customEventName, \"User tapped %s button\", buttonName);\n\n ATrace_beginSection(customEventName);\n // Your app or game's response to the button being pressed.\n ATrace_endSection();\n ``` \n **Note:** When you call `ATrace_beginSection()` multiple\n times, calling `ATrace_endSection()` ends only the most\n recently called `ATrace_beginSection()` method. So, for nested\n calls, make sure that you properly match each call to\n `ATrace_beginSection()` with a call to\n `ATrace_endSection()`.\n\n Additionally, you cannot call `ATrace_beginSection()` on one\n thread and end it from another. You must call both functions from the same\n thread.\n\nConvenience tips\n================\n\nThe following tips are optional but might make it easier to analyze your native\ncode.\n\nTrace an entire function\n------------------------\n\nWhen instrumenting your call stack or function timing, you might find it useful\nto trace entire functions. You can use the `ATRACE_CALL()` macro to make this\ntype of tracing easier to set up. Furthermore, such a macro allows you to skip\ncreating `try` and `catch` blocks for cases where the traced function might\nthrow an exception or call `return` early.\n\nTo create a macro for tracing an entire function, complete the following steps:\n\n1. Define the macro:\n\n ```c++\n #define ATRACE_NAME(name) ScopedTrace ___tracer(name)\n\n // ATRACE_CALL is an ATRACE_NAME that uses the current function name.\n #define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)\n\n class ScopedTrace {\n public:\n inline ScopedTrace(const char *name) {\n ATrace_beginSection(name);\n }\n\n inline ~ScopedTrace() {\n ATrace_endSection();\n }\n };\n ```\n2. Call the macro within the function that you want to trace:\n\n ```c++\n void myExpensiveFunction() {\n ATRACE_CALL();\n // Code that you want to trace.\n }\n ```\n\nName your threads\n-----------------\n\nYou can give a name to each thread in which your events occur, as demonstrated\nin the following code snippet. This step makes it easier to identify the threads\nthat belong to specific actions within your game. \n\n```c++\n#include \u003cpthread.h\u003e\n\nstatic void *render_scene(void *parm) {\n // Code for preparing your app or game's visual components.\n}\n\nstatic void *load_main_menu(void *parm) {\n // Code that executes your app or game's main logic.\n}\n\nvoid init_threads() {\n pthread_t render_thread, main_thread;\n\n pthread_create(&render_thread, NULL, render_scene, NULL);\n pthread_create(&main_thread, NULL, load_main_menu, NULL);\n\n pthread_setname_np(render_thread, \"MyRenderer\");\n pthread_setname_np(main_thread, \"MyMainMenu\");\n}\n```\n\nRecommended for you\n-------------------\n\n- Note: link text is displayed when JavaScript is off\n- [Best practices for SQLite performance](/topic/performance/sqlite-performance-best-practices)\n- [Create and measure Baseline Profiles without Macrobenchmark](/topic/performance/baselineprofiles/manually-create-measure)"]]