네이티브 코드의 맞춤 트레이스 이벤트
컬렉션을 사용해 정리하기
내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.
Android 6.0(API 수준 23) 이상에서는 네이티브 추적 API(trace.h
)를 지원합니다. 이 API는 이후 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");
}
드림
추천 서비스
이 페이지에 나와 있는 콘텐츠와 코드 샘플에는 콘텐츠 라이선스에서 설명하는 라이선스가 적용됩니다. 자바 및 OpenJDK는 Oracle 및 Oracle 계열사의 상표 또는 등록 상표입니다.
최종 업데이트: 2025-07-27(UTC)
[[["이해하기 쉬움","easyToUnderstand","thumb-up"],["문제가 해결됨","solvedMyProblem","thumb-up"],["기타","otherUp","thumb-up"]],[["필요한 정보가 없음","missingTheInformationINeed","thumb-down"],["너무 복잡함/단계 수가 너무 많음","tooComplicatedTooManySteps","thumb-down"],["오래됨","outOfDate","thumb-down"],["번역 문제","translationIssue","thumb-down"],["샘플/코드 문제","samplesCodeIssue","thumb-down"],["기타","otherDown","thumb-down"]],["최종 업데이트: 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)"]]