Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

Systrace에 관한 맞춤 이벤트 정의

Systrace는 시스템 수준에서만 프로세스 정보를 표시하므로 시스템 이벤트와 관련하여 지정된 시간에 어떤 앱 또는 게임의 메서드가 실행되고 있었는지를 파악하기 어려운 경우가 있습니다.

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
    

이 가이드에서는 관리 코드 및 네이티브 코드 모두에서 맞춤 이벤트를 정의하는 방법을 설명합니다.

관리 코드

Android 4.3(API 레벨 18) 이상에서는 다음 코드 스니펫에서와 같이 코드에서 Trace 클래스를 사용하여 맞춤 이벤트를 정의한 다음 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()
            }
        }
    }
    

자바

    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를 지원합니다. 이 API는 이후 Systrace를 사용하여 분석할 수 있는 트레이스 이벤트를 시스템 버퍼에 기록합니다. 이 API의 일반적인 사용 사례로는 특정 코드 블록을 실행하는 데 소요되는 시간을 관찰하고 코드 블록을 원치 않는 시스템 동작에 연결하는 작업이 있습니다.

앱 또는 게임 내의 네이티브 코드에서 발생하는 맞춤 이벤트를 정의하려면 다음 단계를 완료하세요.

  1. 다음 코드 스니펫에서와 같이 게임 내의 맞춤 이벤트를 캡처하는 데 사용하는 ATrace 함수의 함수 포인터를 정의합니다.

        &num;include <android/trace.h>
        &num;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);
        
  2. 다음 코드 스니펫에서와 같이 런타임 시 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를 사용하여 앞의 스니펫에 표시된 코드 주위의 관리 코드에서 메서드를 호출하면 됩니다.

  3. 맞춤 이벤트의 시작과 끝에서 각각 ATrace_beginSection()ATrace_endSection()을 호출합니다.

        &num;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을 조기에 호출하는 경우에 trycatch 블록 생성을 건너뛸 수 있습니다.

전체 함수를 추적하기 위한 매크로를 만들려면 다음 단계를 완료하세요.

  1. 다음과 같이 매크로를 정의합니다.

        &num;define ATRACE_NAME(name) ScopedTrace ___tracer(name)
    
        // ATRACE_CALL is an ATRACE_NAME that uses the current function name.
        &num;define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
    
        class ScopedTrace {
          public:
            inline ScopedTrace(const char *name) {
              ATrace_beginSection(name);
            }
    
            inline ~ScopedTrace() {
              ATrace_endSection();
            }
        };
        
  2. 추적할 함수 내에서 매크로를 호출합니다.

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

스레드 이름 지정

다음 코드 스니펫에 설명된 대로 이벤트가 발생하는 각 스레드에 이름을 지정할 수 있습니다. 이 단계를 통해 게임 내 특정 작업에 속하는 스레드를 쉽게 확인할 수 있습니다.

    &num;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");
    }