dump_stack() 함수는 콜스택을 커널 로그로 출력합니다. 커널 로그로 콜스택을 보고 싶은 코드에 삽입하면 됩니다. 이 함수를 호출하려면 C 코드 윗부분에 다음과 같이 "linux/kernel.h" 해더 파일을 추가해야 합니다.
#include <linux/kernel.h>
dump_stack() 함수 선언부를 봅시다.
asmlinkage __visible void dump_stack(void);
인자와 반환값 타입이 모두 void입니다. 커널 소스 코드 어디든 dump_stack() 함수만 추가하면 됩니다.
dump_stack() 함수로 커널 로그에서 콜스택 확인하기
이번엔 dump_stack() 함수를 써서 커널 로그로 콜스택을 볼 수 있는 패치 코드를 소개합니다.
diff --git a/kernel/fork.c b/kernel/fork.c
index 6a219fea4926..cdc5d9ff0900
--- a/kernel/fork.c
+++ b/kernel/fork.c
1 @@ -2017,6 +2017,8 @@ struct task_struct *fork_idle(int cpu)
2 * It copies the process, and if successful kick-starts
3 * it and waits for it to finish using the VM if required.
4 */
5 +
6 +static int debug_kernel_thread = 1;
7 long _do_fork(unsigned long clone_flags,
8 unsigned long stack_start,
9 unsigned long stack_size,
10 @@ -2049,6 +2051,11 @@ long _do_fork(unsigned long clone_flags,
11 p = copy_process(clone_flags, stack_start, stack_size,
12 child_tidptr, NULL, trace, tls, NUMA_NO_NODE);
13 add_latent_entropy();
14 +
15 + if (debug_kernel_thread) {
16 + printk("[+][%s] process n", current->comm);
17 + dump_stack();
18 + }
위 패치 코드에서 + 기호는 새롭게 추가된 코드를 의미합니다.
먼저 패치 코드 입력 방법을 소개합니다.
6 번째 줄 코드와 같이 _do_fork() 함수 바로 위에 debug_kernel_thread 전역 변수를 선언하고 _do_fork() 함수에서 add_latent_entropy(); 다음에 보이는 15~18 번째 줄 코드를 입력하면 됩니다.
이어서 패치 코드의 의미를 알아봅시다.
프로세스를 생성할 때 _do_fork() 함수가 호출됩니다. 이 때 함수 콜스택을 확인하려는 의도입니다.
_do_fork() 함수는 프로세스를 생성할 때만 호출됩니다. 자주 호출되는 함수는 아니니 안심하고 패치 코드를 입력합시다.
위와 같은 패치 코드를 입력한 다음 커널 이미지를 빌드 후 시스템에 설치합시다.
부팅 후 커널 로그에서 다음과 같은 메시지를 볼 수 있습니다.
1 [ 3.819188] CPU: 1 PID: 149 Comm: systemd-udevd Not tainted 4.14.70-v7+ #3
2 [ 3.819196] Hardware name: BCM2835
3 [ 3.819237] [<8010ffc0>] (unwind_backtrace) from [<8010c1fc>] (show_stack+0x20/0x24)
4 [ 3.819257] [<8010c1fc>] (show_stack) from [<80789dfc>] (dump_stack+0xc8/0x10c)
5 [ 3.819282] [<80789dfc>] (dump_stack) from [<8011c8ec>] (_do_fork+0xdc/0x43c)
6 [ 3.819301] [<8011c8ec>] (_do_fork) from [<8011cd68>] (SyS_clone+0x30/0x38)
7 [ 3.819320] [<8011cd68>] (SyS_clone) from [<80108180>] (__sys_trace_return+0x0/0x10)
커널 로그를 분석해봅시다.
1 번째 줄입니다.
1 [ 3.819188] CPU: 1 PID: 149 Comm: systemd-udevd Not tainted 4.14.70-v7+ #3
콜스택을 실행한 프로세스 정보입니다. 149가 PID인 "systemd-udevd" 프로세스 CPU1에서 실행 중입니다.
02~07 번째 줄은 콜스택입니다. 함수 호출은 다음 그림과 같이 07 번째 줄에서 03 번째 줄 방향입니다.
_do_fork() 함수 위에 보이는 함수 목록들은 dump_stack() 함수에서 콜스택을 출력할 때 마다 보이는 함수입니다. 무시해도 되는 함수 호출 정보입니다.
6 번째 줄 메시지는 SyS_clone() 함수에서 _do_fork() 함수를 호출한다고 알려줍니다.
6 [ 3.819301] [<8011c8ec>] (_do_fork) from [<8011cd68>] (SyS_clone+0x30/0x38)
dump_stack() 함수를 쓸 때 주의사항
1 초에 100번 이상 호출되는 함수에서 dump_stack() 함수를 추가하면 시스템 응답 속도가 매우 느려질 수 있습니다. dump_stack() 함수를 호출해서 콜스택을 보려는 코드가 자주 호출되는지 반드시 점검할 필요가 있습니다.
dump_stack() 함수 내부에서 printk보다 훨씬 많은 일을 합니다.
현재 실행 중인 프로세스 스택 주소를 읽어서 스택에 푸시된 Frame Pointer 레지스터를 읽습니다. ARM 함수 호출 규약에 따라 Frame Pointer 주소를 읽어서 함수 호출 내역을 추적하는 동작을 반복합니다.
사실 ftrace stack tracer 기능을 쓰면 코드 수정 없이 실시간으로 함수 콜스택을 볼 수 있습니다. 콜스택을 출력하는 함수가 자주 호출돼도 시스템에 부하를 많이 주지 않습니다.
'SoC : : Architecture > : : Raspberry' 카테고리의 다른 글
root in color (0) | 2024.10.15 |
---|---|
커널 빌드 / 설치 (0) | 2024.10.14 |
printk() 함수 (0) | 2024.10.10 |
(2) 디버깅과 코드 학습 능력 (0) | 2024.10.10 |
(1) 디버깅은 문제 해결 능력의 지름길 (0) | 2024.10.10 |