디버깅 및 프로파일링 지원¶
Caution
리눅스에서만 사용 가능합니다. 윈도우즈의 경우 Visual Studio 2019 기능을 활용하실 수 있습니다.
GDB를 사용해서 디버깅 하기¶
빌드 과정에서 나오는 서버 실행 스크립트를 이용해서 GDB를 연결한 상태로 실행하거나, 실행 중인 게임 서버 프로세스에 GDB를 연결할 수 있습니다.
다음과 같은 명령을 이용합니다. (프로젝트는 hello, flavor는 game 인 경우)
$ ./hello.game-local --gdb
이 때 이미 게임 서버가 실행 중이라면 실행 중인 프로세스에 연결하고, 그렇지 않은 경우에는 GDB 상에서 게임 서버를 실행합니다. 서버가 이미 시작된 상태가 아닌 경우, GDB 상에서 아래와 같이 디버깅을 시작할 수 있습니다.
Please type `run` to start debugging.
If you want to check the arguments, please type `show args`.
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
... (생략)
Reading symbols from /usr/bin/funapi_runner...(no debugging symbols found)...done.
(gdb) run
Tip
Linux 보안 설정으로 인해서 같은 유저 계정으로 띄운 게임 서버에 디버거를 연결할 수 없는 경우가 있습니다. 이 경우 sudo 를 사용하거나, 아래와 같은 명령을 실행한 후 연결할 수 있습니다. 아래 명령은 실제 서비스 환경에서는 설정해서는 안됩니다.
$ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
해당 명령으로 바꾼 설정은, 머신을 재시작하면 다시 원래 상태가 됩니다.
이미 떠 있는 게임 서버에 연결하려는데, 해당 게임 서버의 같은 flavor 가 여러 대 떠 있다면, 아래와 같이 process id 를 명시적으로 지정할 수 있습니다.
# {PID} 자리에 process id에 해당하는 숫자 지정
$ ./hello.game-local --gdb --pid {PID}
미니덤프(.dmp) 디버깅하기¶
아이펀 엔진은 게임 서버가 크래시할 때 Google Breakpad 를 통해서 덤프 디렉토리에 미니덤프(Minidump, .dmp) 파일을 생성합니다.
미니덤프는 코어덤프(Core dump)보다 훨씬 작은 크기의 덤프 파일을 생성하기 때문에 간편하게 크래시한 스레드의 스택을 확인할 수 있습니다.
미니덤프 생성 위치¶
빌드 디렉터리에서 -local
파일로 실행한 경우에는 빌드 디렉터리의 dumps/
디렉터리에 미니덤프 파일을 저장합니다.
$ ls dumps/
1d06e877-d3af-4586-1baaa8bf-e04ffd34.dmp
f381eb15-448d-4ce3-8fd76783-1e3d33b9.dmp
report-20200616_104219.json
report-20200616_104237.json
서버 패키지를 설치해서 서비스로 실행한 경우에는
/var/crash/funapi/{{project-name}}/
디렉터리에 덤프 파일을 저장합니다.
미니덤프 내용 보기¶
생성된 미니덤프 파일의 내용을 보기 위해서 아이펀 엔진에서 제공하는 funapi_stackwalk 명령을 사용하며 다음의 인자를 입력받습니다.
$ funapi_stackwalk {덤프파일 경로} {심볼 디렉터리 경로} ...
덤프파일 경로: 미니덤프가 저장된 경로이며 미니덤프 파일 이름을 포함합니다. :ref:`debugging-minidump-location`_ 을 참고해 주세요.
심볼 디렉터리 경로: 미니덤프 파일 정보를 출력하기 위해 디버깅 심볼이 저장된 디렉터리 경로입니다. 빌드 리렉터리에 symbols 라는 이름으로 생성되며 패키지로 만들어서 설치하면
/usr/share/{{project-name}}
디렉터리 아래에 같은 이름으로 생성됩니다.
# -local 파일로 실행한 경우: 빌드 디렉터리에서
$ funapi_stackwalk dumps/{{dump-uuid}}.dmp symbols/
# 서비스르 실행한 경우:
$ funapi_stackwalk /var/crash/funapi/{{project-name}}/{{dump-uuid}}.dmp \
/usr/share/{{project-name}}/symbols
아래는 실행 예시입니다.
$ funapi_stackwalk /var/crash/funapi/example/08835639-ff4e-e778-6c14c453-5147e752.dmp /usr/share/example/symbols
OS Linux 0.0.0 Linux 3.10.0-123.20.1.el7.x86_64 #1 SMP Thu Jan 29 18:05:33 UTC 2015 x86_64
CPU amd64 family 6 model 42 stepping 7 8
Crash SIGSEGV 0x0 31
0 libexample.so: /build/example-source/src/event_handlers.cc:155 (+0x4) - example::OnTick
1 libexample.so: /usr/include/boost/function/function_template.hpp:112 (+0x14) - ...
2 funapi_runner: ...
...
7 libboost_thread-mt.so.1.53.0 (+0xd24a)
8 libpthread-2.17.so (+0x7df5)
9 libc-2.17.so (+0xf61ad)
미니덤프를 GDB 로 디버깅하기¶
Note
아래 설명은 라이브 서버에서 데몬으로 실행할 때를 가정합니다.
개발 중인 경우는 /var/crash/funapi/{{project-name}}/
을 dumps/
로 바꿔주면 됩니다.
아래 명령어를 이용하면 GDB 로 미니덤프 파일을 디버깅할 수 있습니다.
$ funapi_core_analyze /usr/lib/lib{{project-name}}.so \
/var/crash/funapi/{{project-name}}/{{dump-uuid}}.dmp
$ funapi_core_analyze /usr/lib/libhello.so /var/crash/funapi/hello/3acd4cbf-5654-695b-0dd043c9-3c70e30d.dmp
Finding game server .so file
Converting dumps/3acd4cbf-5654-695b-0dd043c9-3c70e30d.dmp into coredump
Launching gdb to inspect generated coredump (hello.23945.core).
Please press 'y' to load required symbols
...
Failed to read a valid object file image from memory.
Core was generated by `/usr/bin/funapi_runner --main_program_name=hello_server.game --app_flavor=game --'.
#0 0x00007fc18d76c2de in ?? ()
add symbol table from file "src/libhello.so" at
.text_addr = 0x7fc18d0ceaf0
Reading symbols from src/libhello.so...done.
(gdb) bt
#0 0x00007fc18d76c2de in hello::TestFilters () at ../src/filter.cc:367
#1 0x00007fc18d43094f in (anonymous namespace)::HelloServer::Install (arguments=...) at ../src/hello_server.cc:113
#2 0x00007fc18d430f18 in HelloServer_install (arguments=...) at ../src/hello_server.cc:182
#3 0x00000000007328d3 in ?? ()
#4 0x0000000000601b5c in ?? ()
#5 0x00007fc1962d2ec5 in ?? ()
#6 0x0000000000000000 in ?? ()
(gdb) info locals
x = 0x0
(gdb)
코어덤프(core) 디버깅하기¶
미니덤프는 디스크 사용량을 최소화하기 위해 스택에 있는 값과 여기서 직접 참조하는 값만 확인하실 수 있기 때문에 디버깅에 필요한 정보가 충분하지 않을 수 있습니다.
반면에 코어덤프는 미니덤프에 비해 상대적으로 많은 정보를 제공하지만 프로세스가 사용하는 전체 메모리를 디스크에 저장하기 때문에 메모리 사용량에 따라서 디스크 사용량이 증가합니다.
코어덤프 활성화¶
리눅스 기본 설정에서 코어덤프는 남지 않도록 설정되어 있기 때문에 개발 환경과 서비스 환경 각각에서 코어덤프를 남기도록 설정을 변경해야 합니다.
개발 환경의 코어덤프 활성화 (
-local
파일 실행 시)일반적으로 디버깅 시에는 빌드 디렉터리에서
-local
파일을 실행하는 경우가 많은데, 이 경우 서버를 실행하는 사용자의 코어덤프 설정의 영향을 받기 때문에 아래와 같이 설정을 변경합니다.# 아래와 같이 0이 나오는 경우 코어덤프가 디스크에 남지 않습니다. $ ulimit -c 0 # 코어덤프 사이즈를 unlimited 로 설정함으로써 코어덤프를 활성화. $ ulimit -c unlimited
위와 같이 설정한 경우 터미널을 새로 연결하면 설정이 초기화됩니다. 만약, 매번 변경하는 것이 번거로우시다면
/etc/security/limits.conf
파일에 다음 행을 추가해주세요.# '*' 는 모든 사용자에게 적용한다는 뜻으로 특정 사용자를 지정할 수 있음. * soft core unlimited * hard core unlimited
다음과 같은 옵션으로 런처를 실행합니다.
$ ./hello-local --enable_breakpad=false
Note
enable_breakpad 옵션과는 무관하게 운영체제의 기본 동작에 의해서 코어덤프가 남을 수 있습니다.
서비스 환경에서 코어덤프 활성화
서버 패키지를 설치해서 서비스로 실행할 때는 코어덤프 활성화 여부가 사용자 설정의 영향을 받지 않고, 서비스 유닛 파일의 영향을 받습니다.
/lib/systemd/system
디렉터리에 있는 서버의 서비스 유닛 파일(.service) 을 다음과 같이 수정해 주시기 바랍니다.[Service] # If this limit is not enough, you SHOULD look after # "net.netfilter.nf_conntrack_tcp_timeout_time_wait" (using sysctl) LimitNOFILE=999999 LimitCORE=infinity # 생략 # 프로젝트 이름이 SomeProject, flavor가 Flavor인 경우 ExecStart=SomeProject.Flavor-launcher --enable_breakpad=false
코어덤프 저장 경로¶
코어덤프는 아이펀 엔진 설정이 아니고, 시스템 설정에 따라서 저장 경로가 결정됩니다.
기본적으로는 /proc/sys/kernel/core_pattern
파일의 내용을 따르고, 리눅스
기본설정에서는 core
입니다. 이는 코어덤프 파일 이름을 core
라고 저장하며
저장 위치는 어플리케이션을 실행한 디렉터리이거나 서비스인 경우는 /
디렉터리입니다.
Note
CentOS8 에서는 systemd-coredump 라는 코어덤프를 관리하는 데몬을 사용하도록
되어 있어서 기본 설정에서 core
파일이 남지 않습니다. 대신 coredumpctl
명령으로 코어덤프 목록과 내용을 볼 수 있습니다.
자세한 내용은 coredumpctl Man Page(영문) 를 참고하시기 바랍니다.
/proc/sys/kernel/core_pattern
파일의 내용을 변경하면 코어덤프가 저장되는
위치과 이름을 바꿀수 있습니다.
다음 절차를 따라서 저장 위치를 변경할 수 있습니다.
core_pattern 파일 내용 확인합니다.
core
: 기본 설정입니다. 2번으로 진행하시면 됩니다.
|/usr/lib/systemd/systemd-coredump ...
: systemd-coredump 데몬이 활성화되어 있지만, 2번으로 진행하시면 됩니다.
|/usr/share/apport/apport ...
: apport 프로세스를 비활성화해야 합니다.sudo systemctl stop apport sudo systemctl disable apport
|/usr/libexec/abrt-hook-ccpp ...
: abrt 프로세스를 비활성화해야 합니다.sudo systemctl stop abrt-ccpp sudo systemctl stop abrtd
/etc/sysctl.conf
파일에kernel.core_pattern
설정을 추가해 주세요. 여기서는/tmp/crash
디렉터리 아래에core-{실행파일명}-{프로세스ID}-{덤프시각}
형식으로 파일을 저장하도록 설정합니다.
Note
파일 이름형식에 대한 자세한 내용은 core Man Page(영문) 의 Naming of core dump files 항목을 참고하시기 바랍니다.
#/etc/sysctl.conf kernel.core_pattern = /tmp/crash/core-%e-%p-%t
다음 명령을 실행해서 수정한 내용을 반영해 주세요.
$ sudo sysctl -p
코어덤프 내용 보기¶
코어덤프 파일을 GDB 실행인자로 전달해서 열 수 있으며, GDB 의 bt
명령으로
콜스택을 볼수 있습니다.
$ gdb {실행 프로그램 경로} {코어파일 경로}
현재 디렉터리에 저장돼 있는 core
라는 이름의 코어덤프 파일 내용을 보는
명령은 다음과 같습니다.
$ gdb /usr/bin/funapi_runner core
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1
...
Core was generated by `/usr/bin/funapi_runner --main_program_name=hello_server.default --app_flavor=de'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00007f28715bad44 in (anonymous namespace)::HelloServer::Start () at ../src/hello_server.cc:71
71 *x = 0;
[Current thread is 1 (Thread 0x7f287e5ec800 (LWP 21649))]
(gdb) bt
#0 0x00007f28715bad44 in (anonymous namespace)::HelloServer::Start () at ../src/hello_server.cc:71
#1 0x00007f28715bad8a in HelloServer_start () at ../src/hello_server.cc:83
#2 0x0000000000845c03 in fun::StartComponents() ()
#3 0x000000000065eabe in main ()
(gdb)
메모리 오류 디버깅하기¶
아이펀 엔진은 Address Sanitizer 를 이용하여 메모리 오류, 메모리 누수 문제를 디버깅할 수 있습니다. 흔히 발생할 수 있는 메모리 이중 해제, 헤제된 메모리에 접근 등의 문제를 감지하여 그 위치를 알려주는 도구이며 자세한 내용은 Address Sanitizer 홈페이지를 참고하시기 바랍니다.
메모리 누수감지의 경우 종료 과정에 해제되는 경우 감지되지 않으니 참고하시기 바랍니다.
Warning
Windows 버전은 지원하지 않으며 Visual Studio 를 이용한 원격 개발 환경에서는 원격 리눅스 호스트에서 직접 실행할 경우에만 사용할 수 있습니다.
Warning
Centos7 버전은 GCC Version 이 낮아 메모리 누수 감지 기능이 지원되지 않습니다.
Address Sanitizer 활성화 하기¶
{ProjectName}/CMakeLists.txt 를 열어서 다음 항목을 찾아 true
로 수정합니다.
(찾을 수 없다면 include(Funapi)
앞에 추가합니다)
# Builds with Address Sanitizer for memory corruption debugging.
set(USE_ADDRESS_SANITIZER true)
수정 후 다시 빌드하면 적용됩니다.
테스트 하기¶
서버의 Start()
함수에 아래와 같이 해제된 메모리에 접근하는 코드를 추가합니다.
int *p = new int;
delete p;
*p = 1; // Error(Heap use after free)
빌드 후 서버를 실행하면 아래와 같이 감지됩니다.
=================================================================
==73232==ERROR: AddressSanitizer: heap-use-after-free on address 0x60200001feb0 at pc 0x7f5a00b0147b bp 0x7fff6ac9caf0 sp 0x7fff6ac9cae0
WRITE of size 4 at 0x60200001feb0 thread T0
#0 0x7f5a00b0147a in Start /tmp/test/example-source/src/example_server.cc:83
#1 0x7f5a00b015b6 in ExampleServer_start /tmp/test/example-source/src/example_server.cc:97
#2 0xa3e1c4 in fun::StartComponents() (/usr/bin/funapi_runner_asan+0xa3e1c4)
#3 0x7ea0bb in main (/usr/bin/funapi_runner_asan+0x7ea0bb)
#4 0x7f5a09df16a2 in __libc_start_main (/lib64/libc.so.6+0x236a2)
#5 0x825ccd in _start (/usr/bin/funapi_runner_asan+0x825ccd)
0x60200001feb0 is located 0 bytes inside of 4-byte region [0x60200001feb0,0x60200001feb4)
freed by thread T0 here:
#0 0x7f5a0e7f9d60 in operator delete(void*, unsigned long) (/lib64/libasan.so.5+0xf2d60)
#1 0x7f5a00b01440 in Start /tmp/test/example-source/src/example_server.cc:82
#2 0x7f5a00b015b6 in ExampleServer_start /tmp/test/example-source/src/example_server.cc:97
#3 0xa3e1c4 in fun::StartComponents() (/usr/bin/funapi_runner_asan+0xa3e1c4)
#4 0x7ea0bb in main (/usr/bin/funapi_runner_asan+0x7ea0bb)
#5 0x7f5a09df16a2 in __libc_start_main (/lib64/libc.so.6+0x236a2)
previously allocated by thread T0 here:
#0 0x7f5a0e7f8780 in operator new(unsigned long) (/lib64/libasan.so.5+0xf1780)
#1 0x7f5a00b01425 in Start /tmp/test/example-source/src/example_server.cc:81
#2 0x7f5a00b015b6 in ExampleServer_start /tmp/test/example-source/src/example_server.cc:97
#3 0xa3e1c4 in fun::StartComponents() (/usr/bin/funapi_runner_asan+0xa3e1c4)
#4 0x7ea0bb in main (/usr/bin/funapi_runner_asan+0x7ea0bb)
#5 0x7f5a09df16a2 in __libc_start_main (/lib64/libc.so.6+0x236a2)
SUMMARY: AddressSanitizer: heap-use-after-free /tmp/test/example-source/src/example_server.cc:83 in Start
만약 소스코드 파일과 줄 번호가 출력되지 않는다면 llvm 패키지를 설치하고 다음 명령으로 실행하시기 바랍니다.
$ ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer ASAN_OPTIONS=symbolize=1 {project-name}.{flavor-name}-local
메모리 릭 디버깅¶
Note
메모리 릭 디버깅은 C++ 에서 사용할 수 있습니다.
준비해야할 것¶
해당 기능은 linux OS의 최신 기능을 활용하기 때문에 일정 버전 이하의 kernel 에서는 동작하지 않습니다.
Ubuntu 16.04 의 경우 iovisor 의 외부 패키지 가 필요합니다.
Centos 7의 경우 kernel-ml
패키지 설치가 필요합니다.
우선 배포판과 버전에 따라서
bcc-tools
혹은bpfcc-tools
패키지를 설치합니다.memleak
스크립트를 홈 디렉터리나 작업 디렉터리로 복사합니다. 해당 파일은 패키지에 따라서/usr/share/bcc/tools
혹은/usr/sbin/
밑에서 찾을 수 있습니다. (실행 파일 이름은memleak
,memleak-bpfcc
입니다.)아이펀엔진에서 사용하는 메모리 할당자인
jemalloc
에서 쓸 수 있도록 2에서 복사한 스크립트 파일을 열어서 수정합니다.attach_probes("pvalloc")
을 찾아서attach_probes("pvalloc", can_fail=True)
로 수정합니다.
메모리 릭 따라가기¶
위에서 준비한 스크립트가 필요합니다.
# 실행 중인 게임 서버 프로세스를 찾습니다.
# 출력으로 나온 프로세스 목록 중 메모리 릭을 찾을 PID를 선택합니다.
$ pgrep -a funapi_runner
# 해당 프로그램의 메모리 사용량을 추적합니다. PID=1234 인 경우입니다.
$ sudo ./memleak -p 1234 --top=5 --older=10000 --obj=jemalloc
위 명령은 PID=1234인 프로세스의 메모리 할당을 추적합니다.
메모리 할당 후 해제되지 않은 양이 많은 콜스택 5개를 주기적으로 출력합니다.
--older=10000
인자로, 할당되고나서 10초 이상된 것만 추적하며, 이 결과를 보고 합니다.아이펀 엔진에서 사용하는 할당자인
jemalloc
을 통한 할당/해제를 추적합니다. (--obj
인자)
메모리 할당/해제 측정은 memleak
프로그램을 실행한 이후부터 이뤄지며, 그 이전에 일어난 할당은
추적하지 못합니다.
실제로 실행해보면 아래와 같은 내용이 나옵니다. 출력 순서는 할당량 순서가 아니라 임의의 순서가 나오며 그 중 할당 횟수 혹은 할당량이 많은 것을 확인하시면 됩니다.
[15:06:03] Top 5 stacks with outstanding allocations:
2056 bytes in 1 allocations from stack
operator new(unsigned long)+0x18 [libstdc++.so.6.0.25]
[unknown] [funapi_runner]
fun::UpdateCounter(...)+0x45 [funapi_runner]
... (중략)
boost::detail::thread_data<boost::function<void ()> >::run()+0x36 [funapi_runner]
[unknown] [libboost_thread.so.1.65.1]
2504 bytes in 1 allocations from stack
operator new(unsigned long)+0x18 [libstdc++.so.6.0.25]
[unknown] [funapi_runner]
fun::RawRandomGenerator::GenerateUuid(bool)+0x3b [funapi_runner]
... (중략)
fun::ThreadEntryPoint(boost::function<void ()> const&)+0x37 [funapi_runner]
boost::detail::thread_data<boost::function<void ()> >::run()+0x36 [funapi_runner]
[unknown] [libboost_thread.so.1.65.1]
3896 bytes in 158 allocations from stack
operator new(unsigned long)+0x18 [libstdc++.so.6.0.25]
[unknown] [funapi_runner]
... (중략)
fun::ThreadEntryPoint(boost::function<void ()> const&)+0x37 [funapi_runner]
boost::detail::thread_data<boost::function<void ()> >::run()+0x36 [funapi_runner]
[unknown] [libboost_thread.so.1.65.1]
22032 bytes in 162 allocations from stack
operator new(unsigned long)+0x18 [libstdc++.so.6.0.25]
... (중략)
boost::detail::thread_data<boost::function<void ()> >::run()+0x36 [funapi_runner]
[unknown] [libboost_thread.so.1.65.1]
1056964608 bytes in 126 allocations from stack
operator new(unsigned long)+0x18 [libstdc++.so.6.0.25]
hello::OnTick(unsigned long const&, boost::posix_time::ptime const&)+0x15 [libhello.so]
boost::detail::function::void_function_invoker2... +0x43 [libhello.so]
fun::OnAppTimerExpired(boost::shared_ptr<fun::AppTimerExpiredEvent const>)+0x49 [funapi_runner]
... (중략)
boost::detail::thread_data<boost::function<void ()> >::run()+0x36 [funapi_runner]
[unknown] [libboost_thread.so.1.65.1]
위의 예에서는 강조한 줄의 OnTick
함수가 126번 메모리를 할당해서 1,056,964,608 바이트의 메모리를 할당한 것을 볼 수 있습니다.
즉, 해당 OnTick
함수 부분부터 메모리 릭이 있는지 확인해야하는 상황입니다.
디버깅 빌드가 아니라 릴리즈 빌드인 경우, 콜 스택이 좀 더 부정확할 수 있으며, 이 경우에는 앞/뒤 콜스택을 보고 어떤 부분에서 발생했는지 추측하셔야 합니다.
CPU 프로파일링¶
Note
이 섹션은 일반적인 CPU 프로파일링에 대해서 다룹니다. 아이펀 엔진에 특화된 프로파일링은 이벤트 프로파일링 및 디버깅 을 참고해주세요.
Note
C#
버전 프로파일링은 추후 지원 예정입니다.
게임 서버를 다시 빌드하지 않고도 다음과 같은 과정을 통해 CPU 병목을 찾을 수 있습니다.
필요한 프로그램 설치¶
perf
유틸리티¶
Ubuntu
$ sudo apt-get install linux-tools-genericImportant
generic 이 아닌 커널을 사용하는 경우, 해당 커널에 해당하는 linux-tools 패키지가 필요합니다. 즉,
linux-tools-<커널이름>
이 됩니다.
CentOS
$ sudo yum install perf
FlameGraph¶
$ sudo wget -O /usr/local/bin/flamegraph.pl \ https://raw.githubusercontent.com/brendangregg/FlameGraph/master/flamegraph.pl $ sudo wget -O /usr/local/bin/stackcollapse-perf.pl \ https://raw.githubusercontent.com/brendangregg/FlameGraph/master/stackcollapse-perf.pl $ sudo chmod a+x /usr/local/bin/flamegraph.pl /usr/local/bin/stackcollapse-perf.pl
프로파일링 결과 추출¶
Step 1. 게임 서버를 실행합니다. (이미 실행한 경우는 제외)
$ ./hello-local
다음처럼 프로파일링 명령을 실행합니다.
$ sudo /usr/bin/funapi_profile {{ProjectName}}
Tip
만약 Flavor: 역할에 따라 서버 구분하기 를 쓰는 경우 아래처럼 flavor 를 지정합니다.
$ sudo funapi_profile {{ProjectName}} {{FlavorName}}
예시
$ sudo /usr/bin/funapi_profile hello Profiling server for activity (for 60 seconds) [sudo] password for user: [ perf record: Woken up 7 times to write data ] [ perf record: Captured and wrote 1.626 MB perf.23428.data (~71057 samples) ] Generating flamegraph [kernel.kallsyms] with build id d7fccc433a6b698638325b755df2772c15636d04 not found, continuing without symbols Generated: perf.hello_server.default.23428.svg
출력 마지막 줄에 나와있는 SVG 파일을 브라우저로 엽니다. 위의 예에서는 perf.hello_server.default.23428.svg 입니다.
이 예제에서는
hello::OnTick()
함수가 병목인 경우인데, flamegraph 에서 가로로 길게 표시됩니다. 이런 함수를 찾아 최적화하시면 됩니다.Tip
SVG 파일은 해당 함수를 클릭하면 확대되어 더 자세한 정보를 표시합니다.
Tip
SVG 에서 함수 위에 마우스를 갖다대면 좌측 하단에 해당 함수 및 하위 함수의 CPU 점유 시간 비율을 표시합니다.
Tip
SVG 파일의 우측 상단 Search 기능을 이용하면 프로젝트에서 사용한 코드 중에 어느 부분에서 CPU를 많이 쓰는지 쉽게 확인할 수 있습니다.
가령 hello 프로젝트는 Search 에
^hello::
를 입력하면 프로젝트의 함수들만 보라색으로 표시합니다.