Background
 
부팅 도중에는 '/proc/sys/kernel/printk' 파일이 다음과 같았다가,
 
austin:/ $ cat /proc/sys/kernel/printk
6       6       1       7
 
 
부팅이 끝난 후 '/proc/sys/kernel/printk' 파일이 다음과 같이 바뀝니다.
 
austin:/ $ cat /proc/sys/kernel/printk
7       6       1       7
 
 
부팅 과정에서 어딘가 rc 파일에서 다음 명령어로  '/proc/sys/kernel/printk'을 바꾸는 것 같다는 생각이 듭니다.
 
echo "7 6 1 7" > /proc/sys/kernel/printk
 
분석
 
리눅스 시스템을 구동하기 위해 설정되는 정보는 '/proc/sys/kernel' 폴더에서 확인할 수 있습니다.
이번에는 커널 로그 레벨을 저장하는 '/proc/sys/kernel/printk' 파일을 커널의 어느 함수가 변경하는지 확인해보겠습니다.
 
먼저, '/proc/sys/kernel/printk' 파일에 어떤 값을 쓸 때 호출되는 __do_proc_dointvec() 함수에 패치 코드를 추가하겠습니다.
 
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 865152d..423bcc9 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -2440,6 +2440,7 @@ static int do_proc_douintvec_conv(unsigned long *lvalp,
 
 static const char proc_wspace_sep[] = { ' ', '\t', '\n' };
 
+static printk_debug_state = 1;
 static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
                  int write, void __user *buffer,
                  size_t *lenp, loff_t *ppos,
@@ -2456,6 +2457,19 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
                return 0;
        }
 
+       if (printk_debug_state) {
+               int debug_state;
+
+               if(table->procname) {
+                       pr_emerg("======= sys_name: %s ======\n", table->procname);
+
+                       debug_state = !strcmp(table->procname, "printk");
+                       if ( debug_state ) {
+                               BUG();
+                       }
+               }
+       }
+
        i = (int *) tbl_data;
        vleft = table->maxlen / sizeof(*i);
        left = *lenp;
 
패치 코드의 목적은 간단합니다. printk란 속성 정보를 저장할 때 커널 크래시를 유발하는 코드입니다.
 
콜 스택은 다음과 같은데 프로세스의 이름은 'rpi_printk.sh' 입니다.
 
-000|el1_dbg(asm)
 -->|exception
-001|proc_first_pos_non_zero_ignore(inline)
-001|__do_proc_dointvec(tbl_data = 0xFFFFFFAAEC104790, table = 0xFFFFFFE1701BB000, write = 1, buffer = 0x00000074CC40B0A8, lenp = 0xFFF
-002|proc_dointvec(table = 0xFFFFFFE1701BB000, ?, ?, ?, ?)
-003|proc_sys_call_handler(?, buf = 0x00000074CC40B0A8, count = 8, ppos = 0xFFFFFF80168C3E00, write = 1)
-004|proc_sys_write(?, ?, ?, ?)
-005|__vfs_write(file = 0xFFFFFFE1C12D7A00, p = 0x00000074CC40B0A8, count = 8, pos = 0xFFFFFF80168C3E00)
-006|vfs_write(file = 0xFFFFFFE1C12D7A00, buf = 0x00000074CC40B0A8, count = 8, pos = 0xFFFFFF80168C3E00)
-007|ksys_write(?, buf = 0x00000074CC40B0A8, count = 8)
-008|__se_sys_write(inline)
-008|__arm64_sys_write(regs = 0xFFFFFF80168C3EC0)
-009|el0_svc_common(regs = 0xFFFFFF80168C3EC0, scno = 64, sc_nr = 294, syscall_table = 0xFFFFFFAAEB400798)
-010|el0_svc_handler(regs = 0xFFFFFF80168C3EC0)
-011|el0_svc(asm)
 -->|exception
-012|NUX:0x74CCB74AF8(asm)
-013|c_print(?)
-014|call_builtin(inline)
-014|comexec()
-015|execute(t = 0x00000074CC44F5C8, flags = 0, xerrok = 0x0000007FDF1CC384)
-016|execute(t = 0x00000074CC44F588, flags = 0, xerrok = 0x0000007FDF1CC384)
-017|shell(s = 0x00000074CC438008, level = 0)
-018|main(?, ?)
-019|NUX:0x74CCB2089C(asm)
 ---|end of frame
 
 
__vfs_write() 함수 인자 'p = 0x00000074CC40B0A8'을 확인해봅시다. 0x00000074CC40B0A8 주소에 '6 6 1 7'이 있습니다.
 
________________address|________0________4________8________C_0123456789ABCDEF
   NSD:00000074CC40B0A0| CC40B1E0 00000074>20362036 0A372031 ..@.t...6 6 1 7.
   NSD:00000074CC40B0B0| 746E6169 6573753D 62656472 00006775 iant=userdebug..
 
부팅 과정에서 호출되는 'rpi_printk.sh' 코드를 확인하니 다음과 같은 코드가 확인됩니다.
 
       echo "6 6 1 7" > /proc/sys/kernel/printk
 
이제 커널 로그로 '/proc/sys/kernel/printk' 파일로 로그 레벨을 설정하는 정보를 출력하는 패치를 작성해보겠습니다. 
 
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index f857650..09a946b 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -2219,6 +2219,7 @@ static int do_proc_douintvec_conv(unsigned long *lvalp,
 
 static const char proc_wspace_sep[] = { ' ', '\t', '\n' };
 
+static int printk_debug_state = 1;
 static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
                  int write, void __user *buffer,
                  size_t *lenp, loff_t *ppos,
@@ -2235,6 +2236,26 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
                return 0;
        }
 
+       if (printk_debug_state) {
+               int debug_state;
+
+               if(table->procname) {
+                       pr_emerg("[%s]sys_name: %s \n", current->comm, table->procname);
+
+                       debug_state = !strcmp(table->procname, "printk");
+                       if ( debug_state ) {
+                               char strbuffer[56] = {0,};
+
+                               strncpy_from_user(&strbuffer[0], buffer, sizeof(strbuffer) - 1);
+                               strbuffer[sizeof(strbuffer) - 1] = '\0';
+
+                               pr_emerg("======= sys_name: %s changed by %s ======\n", table->procname, current->comm);
+                               pr_emerg("config printk as [%s]\n", strbuffer);
+
+                       }
+               }
+       }
+
        i = (int *) tbl_data;
        vleft = table->maxlen / sizeof(*i);
        left = *lenp;
 
위 패치를 적용하고 라즈베리 파이에 적용하면 다음과 같은 커널 로그를 볼 수 있습니다.
 
[  408.741741 / 01-01 00:07:08.569][6] [rpi_printk.sh]sys_name: printk
[  408.748485 / 01-01 00:07:08.569][6] ======= sys_name: printk changed by rpi_printk.sh ======
[  408.757155 / 01-01 00:07:08.569][6] config printk as [6 6 1 7
[  408.757155 / 01-01 00:07:08.569][6] iant=userdebug]
...
[  450.014540 / 07-23 05:53:46.129][7] [rpi_printk.sh]sys_name: printk
[  450.021274 / 07-23 05:53:46.139][7] ======= sys_name: printk changed by rpi_printk.sh ======
 
 
Peace!
리눅스 커널에서 sys_enter ftrace 이벤트를 키면 syscall_trace_enter() 함수가 호출된다.
syscall_trace_enter() 함수의 첫 번째 인자로 'struct pt_regs *regs' 가 전달되는데 이 정체를 확인해보자.
 
다음은 syscall_trace_enter() 함수의 구현부이다.
 
asmlinkage int syscall_trace_enter(struct pt_regs *regs, int scno)
{
current_thread_info()->syscall = scno;
 
if (test_thread_flag(TIF_SYSCALL_TRACE))
tracehook_report_syscall(regs, PTRACE_SYSCALL_ENTER);
 
syscall_trace_enter() 함수는 어디서 호출될까?
다음 코드와 같이 __sys_trace 레이블에서 syscall_trace_enter() 함수를 호출한다.
 
arch\arm\kernel\entry-common.S
01 80101154 <__sys_trace>:
02 80101154:   e1a01007    mov r1, r7
03 80101158:   e28d0008    add r0, sp, #8
04 8010115c:   eb002728    bl  8010ae04 <syscall_trace_enter>
 
위 코드에서 03번째 줄을 유심히 보자. 스택 포인터에 8을 더해 r0 레지스터에 저장한다.
 
__sys_trace 레이블은 누가 호출할까?
 
vector_swi 레이블의 첫 번째 코드를 보자.
 
   NSR:80107EE0|vector_swi:           sub     r13,r13,#0x48    ; r13,r13,#72
   NSR:80107EE4|                      stm     r13,{r0-r12}
   NSR:80107EE8|                      add     r8,r13,#0x3C     ; r8,r13,#60
   NSR:80107EEC|                      stmdb   r8,{r13-r14}^
   NSR:80107EF0|                      mrs     r8,spsr
   NSR:80107EF4|                      str     r14,[r13,#0x3C]
   NSR:80107EF8|                      str     r8,[r13,#0x40]
   NSR:80107EFC|                      str     r0,[r13,#0x44]
   NSR:80107F00|                      mov     r11,#0x0         ; r11,#0
   NSR:80107F04|                      mrc     p15,0x0,r12,c1,c0,0x0   ; p15,0,r12,c1,c0,0 (system control)
   NSR:80107F08|                      ldr     r10,0x80107FC0   ; r10,__cr_alignment
   NSR:80107F0C|                      ldr     r10,[r10]
   NSR:80107F10|                      teq     r10,r12
   NSR:80107F14|                      mcrne   p15,0x0,r10,c1,c0,0x0   ; p15,0,r10,c1,c0,0 (system control)
   NSR:80107F18|                      push    {r0-r3,r12,r14}
   NSR:80107F1C|                      bl      0x801DD5C8       ; trace_hardirqs_on
   NSR:80107F20|                      pop     {r0-r3,r12,r14}
   NSR:80107F24|                      cpsie   i
   NSR:80107F28|                      lsr     r9,r13,#0x0D
   NSR:80107F2C|                      lsl     r9,r9,#0x0D
   NSR:80107F30|                      adr     r8,0x80107FC4    ; r8,sys_call_table
   NSR:80107F34|local_restart:        ldr     r10,[r9]
   NSR:80107F38|                      push    {r4-r5}
   NSR:80107F3C|                      tst     r10,#0xF0        ; r10,#240
   NSR:80107F40|                      bne     0x80107F68       ; __sys_trace
 
스택에서 0x48 만큼 뺀다.
 
다음은 유저 프로세서의 커널 공간의 스택 최하단 주소의 메모리 덤프이다.
 
________address|_data________|value_____________|symbol
   NSD:DA81BFA8| C0 E8 17 A2  0xA217E8C0
   NSD:DA81BFAC| C8 E9 17 A2  0xA217E9C8
   NSD:DA81BFB0| 90 E8 17 A2  0xA217E890 //<<--[1]
   NSD:DA81BFB4| 00 00 00 00  0x0
   NSD:DA81BFB8| 00 00 00 00  0x0   
   NSD:DA81BFBC| 08 00 00 00  0x8                
   NSD:DA81BFC0| C0 E8 17 A2  0xA217E8C0
   NSD:DA81BFC4| C8 E9 17 A2  0xA217E9C8
   NSD:DA81BFC8| 90 E8 17 A2  0xA217E890
   NSD:DA81BFCC| B1 00 00 00  0xB1                
   NSD:DA81BFD0| 20 D5 0C A4  0xA40CD520
   NSD:DA81BFD4| 59 00 00 00  0x59                
   NSD:DA81BFD8| 00 CC 8F AB  0xAB8FCC00
   NSD:DA81BFDC| 58 AF 79 AB  0xAB79AF58
   NSD:DA81BFE0| 30 D5 0C A4  0xA40CD530
   NSD:DA81BFE4| 90 E8 17 A2  0xA217E890
   NSD:DA81BFE8| F3 DB CE AE  0xAECEDBF3
   NSD:DA81BFEC| 44 7A D1 AE  0xAED17A44
   NSD:DA81BFF0| 10 00 0F 20  0x200F0010
   NSD:DA81BFF4| 90 E8 17 A2  0xA217E890
   NSD:DA81BFF8| 00 00 00 00  0x0
   NSD:DA81BFFC| FC 02 00 00  0x2FC         
   NSD:DA81C000| 80 00 00 00  0x80    //<- 스택 최하단 주소
      
[1] 스택 최하단 주소인 DA81C000이니 디폴트 스택 주소는 DA81BFF0이다. 여기서 0x48를 빼면 DA81BFA8가 된다.
 
바로 vector_swi 레이블의 첫 줄의 코드가 실행하면 업데이트되는 코드이다.
 
   NSR:80107EE0|vector_swi:           sub     r13,r13,#0x48    ; r13,r13,#72
   NSR:80107EE4|                      stm     r13,{r0-r12}
 
그런데 __sys_trace 레이블에서 스택 주소에서 +0x8만큼 더한다.
 
arch\arm\kernel\entry-common.S
01 80101154 <__sys_trace>:
02 80101154:   e1a01007    mov r1, r7
03 80101158:   e28d0008    add r0, sp, #8
 
위 코드가 실행되면 결국 스택 주소는 DA81BFB0(DA81BFA8 + 0x8)이 된다.
 
프로세스의 메모리 덤프에서 유저 공간에서 실행된 레지스터 세트의 정보와 스택 주소는 다음과 같다.
 
________address|_data________|value_____________|symbol
   NSD:DA81BFA8| C0 E8 17 A2  0xA217E8C0 //<<--[1]
   NSD:DA81BFAC| C8 E9 17 A2  0xA217E9C8
   NSD:DA81BFB0| 90 E8 17 A2  0xA217E890 //<<--[2] __sys_trace 레이블이 실행된 후 업데이트 되는 스택 주소, struct pt_regs
   NSD:DA81BFB4| 00 00 00 00  0x0  // r1
   NSD:DA81BFB8| 00 00 00 00  0x0  // r2 
   NSD:DA81BFBC| 08 00 00 00  0x8  // r3               
   NSD:DA81BFC0| C0 E8 17 A2  0xA217E8C0 // r4   
   NSD:DA81BFC4| C8 E9 17 A2  0xA217E9C8 // r5  
   NSD:DA81BFC8| 90 E8 17 A2  0xA217E890 // r6
   NSD:DA81BFCC| B1 00 00 00  0xB1           // r7: 시스템 콜 번호     
   NSD:DA81BFD0| 20 D5 0C A4  0xA40CD520
   NSD:DA81BFD4| 59 00 00 00  0x59                
   NSD:DA81BFD8| 00 CC 8F AB  0xAB8FCC00
   NSD:DA81BFDC| 58 AF 79 AB  0xAB79AF58
   NSD:DA81BFE0| 30 D5 0C A4  0xA40CD530
   NSD:DA81BFE4| 90 E8 17 A2  0xA217E890
   NSD:DA81BFE8| F3 DB CE AE  0xAECEDBF3
   NSD:DA81BFEC| 44 7A D1 AE  0xAED17A44
   NSD:DA81BFF0| 10 00 0F 20  0x200F0010
   NSD:DA81BFF4| 90 E8 17 A2  0xA217E890
   NSD:DA81BFF8| 00 00 00 00  0x0
   NSD:DA81BFFC| FC 02 00 00  0x2FC         
   NSD:DA81C000| 80 00 00 00  0x80    //<- 스택 최하단 주소
 
 
커널에서 어떤 함수를 사용하면 이 정보를 파악할 수 있을까?
 
#define task_pt_regs(p) \
((struct pt_regs *)(THREAD_START_SP + task_stack_page(p)) - 1)
 
task_pt_regs() 함수를 호출하면 되지 않을까?
이번 시간에는 64비트 기반 ARMv8 아키텍처(커널 4.19 버전)에서의 슬럽 오브젝트의 디버깅 정보를 확인해보겠습니다. 슬럽 오브젝트의 패턴을 빨리 확인하는 좋은 컨텐츠였으면 좋겠습니다.
 
크래시 유틸리티로 슬랩 페이지(kmalloc-256)를 확인 
 
ffffffbf50925d00 슬럽 오브젝트의 속성을 확인하기 위해 'kmem ffffffbf50925d00' 명령어를 입력하겠습니다. 여기서 kmem 오른쪽에 보이는 주소는 슬랩 페이지 디스크립터입니다. 
 
crash64> kmem ffffffbf50925d00
1 CACHE             OBJSIZE  ALLOCATED     TOTAL  SLABS  SSIZE  NAME
2 ffffffd3c08e7780      256      36025     36046   1718    16k  kmalloc-256
3   SLAB              MEMORY            NODE  TOTAL  ALLOCATED  FREE
4   ffffffbf50925d00  ffffffd424974000     0     21         21     0
5   FREE / [ALLOCATED]
6   [ffffffd424974000]
7   [ffffffd424974300]
8   [ffffffd424974600]
9   [ffffffd424974900]
10  [ffffffd424974c00]
11  [ffffffd424974f00]
12  [ffffffd424975200]
13  [ffffffd424975500]
14  [ffffffd424975800]
15  [ffffffd424975b00]
16  [ffffffd424975e00]
17  [ffffffd424976100]
18  [ffffffd424976400]
19  [ffffffd424976700]
20  [ffffffd424976a00]
21  [ffffffd424976d00]
22  [ffffffd424977000]
23  [ffffffd424977300]
24  [ffffffd424977600]
25  [ffffffd424977900]
26  [ffffffd424977c00]
27
28      PAGE               PHYSICAL      MAPPING       INDEX CNT FLAGS
29 ffffffbf50925d00         e4974000 ffffffd3c08e7780        0  1 10200 slab,head
 
6~26번째 줄의 정보로 20여개의 슬럽 오브젝트가 할당된 상태임을 알 수 있습니다.
위 항목의 12번째 줄에 보이는 FFFFFFD424975200 주소에 해당하는 슬럽 오브젝트를 확인해봅시다.
 
다음은 TRACE32에서 'd.v %y.ll 0xFFFFFFD424975200' 명령어를 입력했을 때 출력 결과입니다.
 
$ d.v %y.ll 0xFFFFFFD424975200
1 ________________address|_data____________________|value_____________|symbol
2    NSD:FFFFFFD424975200| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
3    NSD:FFFFFFD424975208| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
4    NSD:FFFFFFD424975210| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
5    NSD:FFFFFFD424975218| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
6    NSD:FFFFFFD424975220| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
7    NSD:FFFFFFD424975228| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
8    NSD:FFFFFFD424975230| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
9    NSD:FFFFFFD424975238| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
10   NSD:FFFFFFD424975240| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
11   NSD:FFFFFFD424975248| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
12   NSD:FFFFFFD424975250| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
13   NSD:FFFFFFD424975258| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
14   NSD:FFFFFFD424975260| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
15   NSD:FFFFFFD424975268| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
16   NSD:FFFFFFD424975270| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
17   NSD:FFFFFFD424975278| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
18   NSD:FFFFFFD424975280| 00 F8 24 01 00 00 00 00  0x124F800
19   NSD:FFFFFFD424975288| 00 01 00 00 00 00 00 00  0x100
20   NSD:FFFFFFD424975290| 00 00 00 00 00 00 00 00  0x0
21   NSD:FFFFFFD424975298| 00 48 E8 01 00 00 00 00  0x1E84800
22   NSD:FFFFFFD4249752A0| 02 01 08 00 4B 00 00 00  0x4B00080102
23   NSD:FFFFFFD4249752A8| 00 00 00 00 00 00 00 00  0x0
24   NSD:FFFFFFD4249752B0| 00 6C DC 02 00 00 00 00  0x2DC6C00
25   NSD:FFFFFFD4249752B8| 02 01 04 00 19 00 00 00  0x1900040102
26   NSD:FFFFFFD4249752C0| 00 00 00 00 00 00 00 00  0x0
27   NSD:FFFFFFD4249752C8| 00 90 D0 03 00 00 00 00  0x3D09000
28   NSD:FFFFFFD4249752D0| 02 01 10 00 4B 00 00 00  0x4B00100102
29   NSD:FFFFFFD4249752D8| 00 00 00 00 00 00 00 00  0x0
30   NSD:FFFFFFD4249752E0| 00 D8 B8 05 00 00 00 00  0x5B8D800
31   NSD:FFFFFFD4249752E8| 02 01 08 00 19 00 00 00  0x1900080102
32   NSD:FFFFFFD4249752F0| 00 00 00 00 00 00 00 00  0x0
33   NSD:FFFFFFD4249752F8| 00 E1 F5 05 00 00 00 00  0x5F5E100
34   NSD:FFFFFFD424975300| 02 05 00 00 00 00 00 00  0x502
35   NSD:FFFFFFD424975308| 00 00 00 00 00 00 00 00  0x0
36   NSD:FFFFFFD424975310| 00 0E 27 07 00 00 00 00  0x7270E00
37   NSD:FFFFFFD424975318| 02 04 00 00 00 00 00 00  0x402
38   NSD:FFFFFFD424975320| 00 00 00 00 00 00 00 00  0x0
39   NSD:FFFFFFD424975328| 00 F8 24 01 00 00 00 00  0x124F800
40   NSD:FFFFFFD424975330| 00 01 00 00 00 00 00 00  0x100
41   NSD:FFFFFFD424975338| 00 00 00 00 00 00 00 00  0x0
42   NSD:FFFFFFD424975340| 00 00 00 00 00 00 00 00  0x0
43   NSD:FFFFFFD424975348| 00 00 00 00 00 00 00 00  0x0
44   NSD:FFFFFFD424975350| 00 00 00 00 00 00 00 00  0x0
45   NSD:FFFFFFD424975358| 00 00 00 00 00 00 00 00  0x0
46   NSD:FFFFFFD424975360| 00 00 00 00 00 00 00 00  0x0
47   NSD:FFFFFFD424975368| 00 00 00 00 00 00 00 00  0x0
48   NSD:FFFFFFD424975370| 00 00 00 00 00 00 00 00  0x0
49   NSD:FFFFFFD424975378| 00 00 00 00 00 00 00 00  0x0
50   NSD:FFFFFFD424975380| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
51   NSD:FFFFFFD424975388| 31 BC 8E 72 65 1F 5E 13  0x135E1F65728EBC31
52   NSD:FFFFFFD424975390| D0 E3 9C 2E 90 FF FF FF  0xFFFFFF902E9CE3D0 \\vmlinux\clk-rcg2\clk_rcg2_dfs_determine_rate+0x110
53   NSD:FFFFFFD424975398| A8 1B 03 2E 90 FF FF FF  0xFFFFFF902E031BA8 \\vmlinux\slub\kmem_cache_alloc_trace+0x358
54   NSD:FFFFFFD4249753A0| D0 E3 9C 2E 90 FF FF FF  0xFFFFFF902E9CE3D0 \\vmlinux\clk-rcg2\clk_rcg2_dfs_determine_rate+0x110
55   NSD:FFFFFFD4249753A8| C4 CD 98 2E 90 FF FF FF  0xFFFFFF902E98CDC4 \\vmlinux\clk\clk_core_round_rate_nolock+0x2CC
56   NSD:FFFFFFD4249753B0| F0 D0 98 2E 90 FF FF FF  0xFFFFFF902E98D0F0 \\vmlinux\clk\clk_hw_round_rate+0x278
57   NSD:FFFFFFD4249753B8| 0C F1 9C 2E 90 FF FF FF  0xFFFFFF902E9CF10C \\vmlinux\clk-branch\clk_branch2_round_rate+0x3C
58   NSD:FFFFFFD4249753C0| 44 CE 98 2E 90 FF FF FF  0xFFFFFF902E98CE44 \\vmlinux\clk\clk_core_round_rate_nolock+0x34C
59   NSD:FFFFFFD4249753C8| C0 D4 98 2E 90 FF FF FF  0xFFFFFF902E98D4C0 \\vmlinux\clk\clk_round_rate+0x358
60   NSD:FFFFFFD4249753D0| 8C 80 9B 30 90 FF FF FF  0xFFFFFF90309B808C \\vmlinux\qcom-geni-se\geni_se_clk_tbl_get+0x19C
61   NSD:FFFFFFD4249753D8| B4 82 9B 30 90 FF FF FF  0xFFFFFF90309B82B4 \\vmlinux\qcom-geni-se\geni_se_clk_freq_match+0xA4
62   NSD:FFFFFFD4249753E0| 5C D4 10 2F 90 FF FF FF  0xFFFFFF902F10D45C \\vmlinux\spi-geni-qcom\get_spi_clk_cfg+0xBC
63   NSD:FFFFFFD4249753E8| DC 95 10 2F 90 FF FF FF  0xFFFFFF902F1095DC \\vmlinux\spi-geni-qcom\spi_geni_transfer_one+0x574
64   NSD:FFFFFFD4249753F0| B8 15 10 2F 90 FF FF FF  0xFFFFFF902F1015B8 \\vmlinux\spi\spi_transfer_one_message+0x458
65   NSD:FFFFFFD4249753F8| 24 92 0F 2F 90 FF FF FF  0xFFFFFF902F0F9224 \\vmlinux\spi\__spi_pump_messages+0x1034
66   NSD:FFFFFFD424975400| 58 E1 0F 2F 90 FF FF FF  0xFFFFFF902F0FE158 \\vmlinux\spi\__spi_sync+0x358
67   NSD:FFFFFFD424975408| D4 EA 0F 2F 90 FF FF FF  0xFFFFFF902F0FEAD4 \\vmlinux\spi\spi_write_then_read+0x3DC
68   NSD:FFFFFFD424975410| 48 2D AB 30 90 FF FF FF  0xFFFFFF9030AB2D48 \\vmlinux\ice40-spi\ice40_fpga_ops_write+0x1B8
69   NSD:FFFFFFD424975418| 00 00 00 00 07 00 00 00  0x700000000
70   NSD:FFFFFFD424975420| DC E9 FF FF 00 00 00 00  0xFFFFE9DC
71   NSD:FFFFFFD424975428| F0 E9 03 2E 90 FF FF FF  0xFFFFFF902E03E9F0 \\vmlinux\quarantine\qlink_free+0x18
72   NSD:FFFFFFD424975430| 08 EA 03 2E 90 FF FF FF  0xFFFFFF902E03EA08 \\vmlinux\quarantine\qlink_free+0x30
73   NSD:FFFFFFD424975438| 68 E6 03 2E 90 FF FF FF  0xFFFFFF902E03E668 \\vmlinux\quarantine\quarantine_reduce+0x158
74   NSD:FFFFFFD424975440| 94 BD 03 2E 90 FF FF FF  0xFFFFFF902E03BD94 \\vmlinux\kasan\kasan_kmalloc+0x44
75   NSD:FFFFFFD424975448| 44 BD 03 2E 90 FF FF FF  0xFFFFFF902E03BD44 \\vmlinux\kasan\kasan_slab_alloc+0x14
76   NSD:FFFFFFD424975450| BC 17 03 2E 90 FF FF FF  0xFFFFFF902E0317BC \\vmlinux\slub\kmem_cache_alloc+0x2EC
77   NSD:FFFFFFD424975458| F4 B7 08 2E 90 FF FF FF  0xFFFFFF902E08B7F4 \\vmlinux\file_table\__alloc_file+0x3C
78   NSD:FFFFFFD424975460| E4 B6 08 2E 90 FF FF FF  0xFFFFFF902E08B6E4 \\vmlinux\file_table\alloc_empty_file+0x94
79   NSD:FFFFFFD424975468| E0 B2 0A 2E 90 FF FF FF  0xFFFFFF902E0AB2E0 \\vmlinux\fs/namei\path_openat+0x100
80   NSD:FFFFFFD424975470| D4 AF 0A 2E 90 FF FF FF  0xFFFFFF902E0AAFD4 \\vmlinux\fs/namei\do_filp_open+0x1B4
81   NSD:FFFFFFD424975478| C8 E9 07 2E 90 FF FF FF  0xFFFFFF902E07E9C8 \\vmlinux\open\do_sys_open+0x250
82   NSD:FFFFFFD424975480| 2C EE 07 2E 90 FF FF FF  0xFFFFFF902E07EE2C \\vmlinux\open\__arm64_sys_openat+0x9C
83   NSD:FFFFFFD424975488| 00 05 AB 2D 90 FF FF FF  0xFFFFFF902DAB0500 \\vmlinux\kernel/syscall\el0_svc_common+0x158
84   NSD:FFFFFFD424975490| 40 03 AB 2D 90 FF FF FF  0xFFFFFF902DAB0340 \\vmlinux\kernel/syscall\el0_svc_handler+0x108
85   NSD:FFFFFFD424975498| 88 57 A8 2D 90 FF FF FF  0xFFFFFF902DA85788 \\vmlinux\Global\el0_svc+0x8
86   NSD:FFFFFFD4249754A0| 00 00 00 00 00 00 00 00  0x0
87   NSD:FFFFFFD4249754A8| 00 00 00 00 00 00 00 00  0x0
88   NSD:FFFFFFD4249754B0| 07 00 00 00 CD 05 00 00  0x5CD00000007
89   NSD:FFFFFFD4249754B8| DA E9 FF FF 00 00 00 00  0xFFFFE9DA
90   NSD:FFFFFFD4249754C0| 07 00 00 00 31 04 80 BB  0xBB80043100000007
91   NSD:FFFFFFD4249754C8| FB 16 00 00 BA 02 80 96  0x968002BA000016FB
92   NSD:FFFFFFD4249754D0| 5A 5A 5A 5A 5A 5A 5A 5A  0x5A5A5A5A5A5A5A5A
93   NSD:FFFFFFD4249754D8| 5A 5A 5A 5A 5A 5A 5A 5A  0x5A5A5A5A5A5A5A5A
94   NSD:FFFFFFD4249754E0| 5A 5A 5A 5A 5A 5A 5A 5A  0x5A5A5A5A5A5A5A5A
95   NSD:FFFFFFD4249754E8| 5A 5A 5A 5A 5A 5A 5A 5A  0x5A5A5A5A5A5A5A5A
96   NSD:FFFFFFD4249754F0| 5A 5A 5A 5A 5A 5A 5A 5A  0x5A5A5A5A5A5A5A5A
97   NSD:FFFFFFD4249754F8| 5A 5A 5A 5A 5A 5A 5A 5A  0x5A5A5A5A5A5A5A5A
98   NSD:FFFFFFD424975500| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
 
위 메모리 덤프의 내용은 다음과 같이 해석할 수 있습니다.
 
   * 1~17번째 줄: 0xCC 값으로 채워져 있는데 이는 SLUB_RED_ACTIVE 매크로를 의미한다.
   
   #define SLUB_RED_ACTIVE 0xcc
 
   * 18~49번째 줄: 슬럽 오브젝트에 해당하는 메모리 공간이다.
   
        슬럽 오브젝트의 사이즈는 0x100이다.   
         - FFFFFFD424975380 - FFFFFFD424975280 = 0x100  
 
    * 52~70번째: 해당 슬럽 오브젝트를 할당 했을 때의 콜 스택과 프로세스의 정보이며 track 구조체로 이를 표현한다.
 
    * 71~89번째: 해당 슬럽 오브젝트를 해제 했을 때의 콜 스택과 프로세스의 정보이며 track 구조체로 이를 표현한다.
 
슬럽 오브젝트의 track 정보 확인하기 
 
이어서 해당 슬럽 오브젝트의 track 정보를 확인해봅시다.
 
먼저 슬럽 오브젝트를 할당할 때의 track 구조체를 점검하겠습니다. 
 
 1  (struct track *) (struct track*)0xFFFFFFD424975390 = 0xFFFFFFD424975390 = end+0x43F00B7390 -> (
 2    (long unsigned int) addr = 18446743593455248336 = 0xFFFFFF902E9CE3D0,
 3    (long unsigned int [16]) addrs = (
 4      [0] = 18446743593445170088 = 0xFFFFFF902E031BA8,
 5      [1] = 18446743593455248336 = 0xFFFFFF902E9CE3D0,
 6      [2] = 18446743593454980548 = 0xFFFFFF902E98CDC4,
 7      [3] = 18446743593454981360 = 0xFFFFFF902E98D0F0,
 8      [4] = 18446743593455251724 = 0xFFFFFF902E9CF10C,
 9      [5] = 18446743593454980676 = 0xFFFFFF902E98CE44,
 10     [6] = 18446743593454982336 = 0xFFFFFF902E98D4C0,
 11     [7] = 18446743593488711820 = 0xFFFFFF90309B808C,
 12     [8] = 18446743593488712372 = 0xFFFFFF90309B82B4,
 13     [9] = 18446743593462846556 = 0xFFFFFF902F10D45C,
 14     [10] = 18446743593462830556 = 0xFFFFFF902F1095DC,
 15     [11] = 18446743593462797752 = 0xFFFFFF902F1015B8,
 16     [12] = 18446743593462764068 = 0xFFFFFF902F0F9224,
 17     [13] = 18446743593462784344 = 0xFFFFFF902F0FE158,
 18     [14] = 18446743593462786772 = 0xFFFFFF902F0FEAD4,
 19     [15] = 18446743593489739080 = 0xFFFFFF9030AB2D48),
 20   (int) cpu = 0 = 0x0,
 21   (int) pid = 7 = 0x7,
 22   (long unsigned int) when = 4294961628 = 0xFFFFE9DC)
 
위에서 보이는 디버깅 정보는 아래와 같이 요약할 수 잇습니다.
 
         * 2~19번째: 함수의 콜 스택 주소이다.  
* 20번째: CPU번호다.
* 21번째: PID이다.
* 22번째: 해당 슬럽 오브젝트를 할당할 때 jiffies 이다.
 
 
이번에는 슬럽 오브젝트를 해제할 때의 track 구조체를 점검하자. 
 
1   (struct track *) (struct track*)0xFFFFFFD424975428 = 0xFFFFFFD424975428 = end+0x43F00B7428 -> (
2     (long unsigned int) addr = 18446743593445222896 = 0xFFFFFF902E03E9F0,
3     (long unsigned int [16]) addrs = (
4       [0] = 18446743593445222920 = 0xFFFFFF902E03EA08,
5       [1] = 18446743593445221992 = 0xFFFFFF902E03E668,
6       [2] = 18446743593445211540 = 0xFFFFFF902E03BD94,
7       [3] = 18446743593445211460 = 0xFFFFFF902E03BD44,
8       [4] = 18446743593445169084 = 0xFFFFFF902E0317BC,
9       [5] = 18446743593445537780 = 0xFFFFFF902E08B7F4,
10      [6] = 18446743593445537508 = 0xFFFFFF902E08B6E4,
11      [7] = 18446743593445667552 = 0xFFFFFF902E0AB2E0,
12      [8] = 18446743593445666772 = 0xFFFFFF902E0AAFD4,
13      [9] = 18446743593445485000 = 0xFFFFFF902E07E9C8,
14      [10] = 18446743593445486124 = 0xFFFFFF902E07EE2C,
15      [11] = 18446743593439397120 = 0xFFFFFF902DAB0500,
16      [12] = 18446743593439396672 = 0xFFFFFF902DAB0340,
17      [13] = 18446743593439221640 = 0xFFFFFF902DA85788,
18      [14] = 0 = 0x0,
19      [15] = 0 = 0x0),
20    (int) cpu = 7 = 0x7,
21    (int) pid = 1485 = 0x05CD,
22    (long unsigned int) when = 4294961626 = 0xFFFFE9DA)
 
위에서 보이는 디버깅 정보는 아래와 같습니다.
 
        * 2~19번째: 함수의 콜 스택 주소이다.  
* 20번째: CPU번호다.
* 21번째: PID이다.
* 22번째: 해당 슬럽 오브젝트를 할당할 때 jiffies 이다.
 
이처럼 슬럽 오브젝트를 할당하고 해제할 때의 콜 스택과 프로세스 정보를 확인할 수 있습니다.
 
슬럽 오브젝트를 할당할 때의 코드 확인하기 
 
track 구조체의 정보를 잘 활용하면 해당 슬럽 오브젝트를 할당할 때의 코드와 슬럽 오브젝트를 할당 받아 사용하는 구조체를 확인할 수 있습니다.
 
이를 위해 먼저 슬럽 오브젝트를 할당할 때의 디버깅 정보를 보겠습니다.
 
52   NSD:FFFFFFD424975390| D0 E3 9C 2E 90 FF FF FF  0xFFFFFF902E9CE3D0 \\vmlinux\clk-rcg2\clk_rcg2_dfs_determine_rate+0x110
53   NSD:FFFFFFD424975398| A8 1B 03 2E 90 FF FF FF  0xFFFFFF902E031BA8 \\vmlinux\slub\kmem_cache_alloc_trace+0x358
54   NSD:FFFFFFD4249753A0| D0 E3 9C 2E 90 FF FF FF  0xFFFFFF902E9CE3D0 \\vmlinux\clk-rcg2\clk_rcg2_dfs_determine_rate+0x110
55   NSD:FFFFFFD4249753A8| C4 CD 98 2E 90 FF FF FF  0xFFFFFF902E98CDC4 \\vmlinux\clk\clk_core_round_rate_nolock+0x2CC
 
위 디버깅 정보로 슬럽 오브젝트를 할당한 심벌의 주소는 '0xFFFFFF902E031BA8'이고 kmem_cache_alloc_trace+0x358 심벌임을 알 수 있습니다. 
 
크래시 유틸리티를 활용해 확인하니 해당  '0xFFFFFF902E031BA8' 주소에 해당하는 코드의 정보를 다음과 같습니다. 
 
crash64> sym 0xFFFFFF902E9CE3D0
ffffff902e9ce3d0 (t) clk_rcg2_dfs_determine_rate+272 /home/baldcandy/drivers/clk/qcom/clk-rcg2.c: 1471
 
해당 코드를 열어보면 clk_rcg2_dfs_populate_freq_table() 함수에서 슬럽 오브젝트를 할당한다는 사실을 알 수 있습니다.
 
다음은 clk_rcg2_dfs_populate_freq_table() 함수의 구현부입니다.
 
1 static int clk_rcg2_dfs_populate_freq_table(struct clk_rcg2 *rcg)
2 {
3 struct freq_tbl *freq_tbl;
4 int i, ret;
5
6 freq_tbl = kcalloc(MAX_PERF_LEVEL + 1, sizeof(*freq_tbl), GFP_KERNEL);
7 if (!freq_tbl)
8 return -ENOMEM;
9 rcg->freq_tbl = freq_tbl;
 
 
3~6번째 줄로 해당 슬럽 오브젝트는 struct freq_tbl 구조체로 사용한다는 사실을 알 수 있습니다.
 
그런데 아래와 같이 해당 슬럽 오브젝트의 덤프와 같이 레드 존 정보를 제외한 패이로드의 시작 주소는 0xFFFFFFD424975280입니다.
 
1 ________________address|_data____________________|value_____________|symbol
2    NSD:FFFFFFD424975200| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
3    NSD:FFFFFFD424975208| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
4    NSD:FFFFFFD424975210| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
5    NSD:FFFFFFD424975218| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
6    NSD:FFFFFFD424975220| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
7    NSD:FFFFFFD424975228| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
8    NSD:FFFFFFD424975230| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
9    NSD:FFFFFFD424975238| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
10   NSD:FFFFFFD424975240| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
11   NSD:FFFFFFD424975248| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
12   NSD:FFFFFFD424975250| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
13   NSD:FFFFFFD424975258| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
14   NSD:FFFFFFD424975260| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
15   NSD:FFFFFFD424975268| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
16   NSD:FFFFFFD424975270| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
17   NSD:FFFFFFD424975278| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
18   NSD:FFFFFFD424975280| 00 F8 24 01 00 00 00 00  0x124F800
19   NSD:FFFFFFD424975288| 00 01 00 00 00 00 00 00  0x100
20   NSD:FFFFFFD424975290| 00 00 00 00 00 00 00 00  0x0
21   NSD:FFFFFFD424975298| 00 48 E8 01 00 00 00 00  0x1E84800
22   NSD:FFFFFFD4249752A0| 02 01 08 00 4B 00 00 00  0x4B00080102
 
이 정보를 토대로 0xFFFFFFD424975280 주소를 struct freq_tbl 구조체로 캐스팅하면 필드의 정보를 확인할 수 있습니다.
 
$ v.v %t %d %i %y  (struct freq_tbl*)0xFFFFFFD424975280
  (struct freq_tbl *) (struct freq_tbl*)0xFFFFFFD424975280 = 0xFFFFFFD424975280 
    (long unsigned int) freq = 19200000,
    (u8) src = 0,
    (u8) pre_div = 1,
    (u16) m = 0,
    (u16) n = 0,
    (long unsigned int) src_freq = 0)
 
 
크래시 유틸리티로 슬랩 페이지(kmalloc-256)를 확인 
 
이번에는 다른 슬럽 오브젝트(kmalloc-256)의 디버깅 정보를 확인해 봅시다. 
 
crash64> kmem ffffffbf50925d00
1 CACHE             OBJSIZE  ALLOCATED     TOTAL  SLABS  SSIZE  NAME
2 ffffffd3c08e7780      256      36025     36046   1718    16k  kmalloc-256
3   SLAB              MEMORY            NODE  TOTAL  ALLOCATED  FREE
4   ffffffbf50925d00  ffffffd424974000     0     21         21     0
5   FREE / [ALLOCATED]
6   [ffffffd424974000]
7   [ffffffd424974300]
8   [ffffffd424974600]
9   [ffffffd424974900]
10  [ffffffd424974c00]
11  [ffffffd424974f00]
12  [ffffffd424975200]
13  [ffffffd424975500]
14  [ffffffd424975800]
15  [ffffffd424975b00]
16  [ffffffd424975e00]
17  [ffffffd424976100]
18  [ffffffd424976400]
19  [ffffffd424976700]
20  [ffffffd424976a00]
21  [ffffffd424976d00]
22  [ffffffd424977000]
23  [ffffffd424977300]
24  [ffffffd424977600]
25  [ffffffd424977900]
26  [ffffffd424977c00]
27
28      PAGE               PHYSICAL      MAPPING       INDEX CNT FLAGS
29 ffffffbf50925d00         e4974000 ffffffd3c08e7780        0  1 10200 slab,head
 
위 항목의 22번째 줄에 보이는 FFFFFFD424977000 주소에 해당하는 슬럽 오브젝트를 확인합시다. 
 
1 ________________address|_data____________________|value_____________|symbol
2    NSD:FFFFFFD424977000| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
3    NSD:FFFFFFD424977008| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
4    NSD:FFFFFFD424977010| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
5    NSD:FFFFFFD424977018| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
6    NSD:FFFFFFD424977020| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
7    NSD:FFFFFFD424977028| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
8    NSD:FFFFFFD424977030| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
9    NSD:FFFFFFD424977038| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
10   NSD:FFFFFFD424977040| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
11   NSD:FFFFFFD424977048| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
12   NSD:FFFFFFD424977050| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
13   NSD:FFFFFFD424977058| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
14   NSD:FFFFFFD424977060| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
15   NSD:FFFFFFD424977068| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
16   NSD:FFFFFFD424977070| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
17   NSD:FFFFFFD424977078| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
18   NSD:FFFFFFD424977080| 80 55 9B AF D4 FF FF FF  0xFFFFFFD4AF9B5580
19   NSD:FFFFFFD424977088| 80 4C 97 24 D4 FF FF FF  0xFFFFFFD424974C80
20   NSD:FFFFFFD424977090| 01 00 00 00 00 77 6C 61  0x616C770000000001
21   NSD:FFFFFFD424977098| 6E 5F 69 70 61 5F 63 6F  0x6F635F6170695F6E
22   NSD:FFFFFFD4249770A0| 72 65 2E 63 00 00 00 00  0x632E6572
23   NSD:FFFFFFD4249770A8| 00 00 00 00 00 00 00 00  0x0
24   NSD:FFFFFFD4249770B0| 00 00 00 00 00 00 00 00  0x0
25   NSD:FFFFFFD4249770B8| 00 00 00 00 00 00 00 00  0x0
26   NSD:FFFFFFD4249770C0| 00 00 00 00 00 00 00 00  0x0
27   NSD:FFFFFFD4249770C8| 45 08 00 00 28 00 00 00  0x2800000845
28   NSD:FFFFFFD4249770D0| A0 40 58 30 90 FF FF FF  0xFFFFFF90305840A0 \\vmlinux\wlan_ipa_core\wlan_ipa_setup+0xDC0
29   NSD:FFFFFFD4249770D8| 68 67 66 65 64 63 62 61  0x6162636465666768
30   NSD:FFFFFFD4249770E0| 14 39 97 B8 00 00 00 00  0xB8973914
31   NSD:FFFFFFD4249770E8| E8 4C 97 24 D4 FF FF FF  0xFFFFFFD424974CE8
32   NSD:FFFFFFD4249770F0| E8 55 9B AF D4 FF FF FF  0xFFFFFFD4AF9B55E8
33   NSD:FFFFFFD4249770F8| 00 00 00 00 00 00 00 00  0x0
34   NSD:FFFFFFD424977100| 0A 00 00 00 00 00 00 00  0x0A
35   NSD:FFFFFFD424977108| 00 00 00 00 00 00 00 00  0x0
36   NSD:FFFFFFD424977110| 87 86 85 84 83 82 81 80  0x8081828384858687
37   NSD:FFFFFFD424977118| 00 00 00 00 00 00 00 00  0x0
38   NSD:FFFFFFD424977120| 00 00 00 00 00 00 00 00  0x0
39   NSD:FFFFFFD424977128| 00 00 00 00 00 00 00 00  0x0
40   NSD:FFFFFFD424977130| 00 00 00 00 00 00 00 00  0x0
41   NSD:FFFFFFD424977138| 00 00 00 00 00 00 00 00  0x0
42   NSD:FFFFFFD424977140| 00 00 00 00 00 00 00 00  0x0
43   NSD:FFFFFFD424977148| 00 00 00 00 00 00 00 00  0x0
44   NSD:FFFFFFD424977150| 00 00 00 00 00 00 00 00  0x0
45   NSD:FFFFFFD424977158| 00 00 00 00 00 00 00 00  0x0
46   NSD:FFFFFFD424977160| 00 00 00 00 00 00 00 00  0x0
47   NSD:FFFFFFD424977168| 00 00 00 00 00 00 00 00  0x0
48   NSD:FFFFFFD424977170| 00 00 00 00 00 00 00 00  0x0
49   NSD:FFFFFFD424977178| 00 00 00 00 00 00 00 00  0x0
50   NSD:FFFFFFD424977180| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
51   NSD:FFFFFFD424977188| 31 8C 8E 72 65 1F 5E 13  0x135E1F65728E8C31
52   NSD:FFFFFFD424977190| 44 B8 1A 30 90 FF FF FF  0xFFFFFF90301AB844 \\vmlinux\qdf_mem\qdf_mem_malloc_debug+0x13C
53   NSD:FFFFFFD424977198| 3C 43 03 2E 90 FF FF FF  0xFFFFFF902E03433C \\vmlinux\slub\__kmalloc+0x3AC
54   NSD:FFFFFFD4249771A0| 44 B8 1A 30 90 FF FF FF  0xFFFFFF90301AB844 \\vmlinux\qdf_mem\qdf_mem_malloc_debug+0x13C
55   NSD:FFFFFFD4249771A8| B8 4A 58 30 90 FF FF FF  0xFFFFFF9030584AB8 \\vmlinux\wlan_ipa_core\wlan_ipa_setup_sys_pipe+0x3A8
56   NSD:FFFFFFD4249771B0| A0 40 58 30 90 FF FF FF  0xFFFFFF90305840A0 \\vmlinux\wlan_ipa_core\wlan_ipa_setup+0xDC0
57   NSD:FFFFFFD4249771B8| 78 E3 57 30 90 FF FF FF  0xFFFFFF903057E378 \\vmlinux\wlan_ipa_main\ipa_obj_setup+0x28
58   NSD:FFFFFFD4249771C0| 00 DD 57 30 90 FF FF FF  0xFFFFFF903057DD00 \\vmlinux\wlan_ipa_obj_mgmt_api\ipa_pdev_obj_create_notification+0x138
59   NSD:FFFFFFD4249771C8| B0 C0 48 30 90 FF FF FF  0xFFFFFF903048C0B0 \\vmlinux\wlan_objmgr_pdev_obj\wlan_objmgr_pdev_obj_create+0x338
60   NSD:FFFFFFD4249771D0| C8 FF DF 2F 90 FF FF FF  0xFFFFFF902FDFFFC8 \\vmlinux\wlan_hdd_object_manager\hdd_objmgr_create_and_store_pdev+0xF8
61   NSD:FFFFFFD4249771D8| 80 61 DC 2F 90 FF FF FF  0xFFFFFF902FDC6180 \\vmlinux\wlan_hdd_main\hdd_update_tgt_cfg+0xC0
62   NSD:FFFFFFD4249771E0| 20 9D 1F 30 90 FF FF FF  0xFFFFFF90301F9D20 \\vmlinux\wma_main\wma_rx_ready_event+0x2910
63   NSD:FFFFFFD4249771E8| F8 C9 1E 30 90 FF FF FF  0xFFFFFF90301EC9F8 \\vmlinux\wma_main\wma_legacy_service_ready_event_handler+0x70
64   NSD:FFFFFFD4249771F0| 60 D2 42 30 90 FF FF FF  0xFFFFFF903042D260 \\vmlinux\init_event_handler\init_deinit_ready_event_handler+0x490
65   NSD:FFFFFFD4249771F8| E8 FB 2D 30 90 FF FF FF  0xFFFFFF90302DFBE8 \\vmlinux\wmi_unified\__wmi_control_rx+0xA78
66   NSD:FFFFFFD424977200| B8 11 2E 30 90 FF FF FF  0xFFFFFF90302E11B8 \\vmlinux\wmi_unified\wmi_rx_event_work+0x4E0
67   NSD:FFFFFFD424977208| 50 5E 1A 30 90 FF FF FF  0xFFFFFF90301A5E50 \\vmlinux\qdf_defer\__qdf_defer_func+0x68
68   NSD:FFFFFFD424977210| B8 3F B5 2D 90 FF FF FF  0xFFFFFF902DB53FB8 \\vmlinux\workqueue\process_one_work+0x900
69   NSD:FFFFFFD424977218| 06 00 00 00 37 09 00 00  0x93700000006
70   NSD:FFFFFFD424977220| 1A C4 FF FF 00 00 00 00  0xFFFFC41A
71   NSD:FFFFFFD424977228| F0 E9 03 2E 90 FF FF FF  0xFFFFFF902E03E9F0 \\vmlinux\quarantine\qlink_free+0x18
72   NSD:FFFFFFD424977230| 08 EA 03 2E 90 FF FF FF  0xFFFFFF902E03EA08 \\vmlinux\quarantine\qlink_free+0x30
73   NSD:FFFFFFD424977238| 68 E6 03 2E 90 FF FF FF  0xFFFFFF902E03E668 \\vmlinux\quarantine\quarantine_reduce+0x158
74   NSD:FFFFFFD424977240| 94 BD 03 2E 90 FF FF FF  0xFFFFFF902E03BD94 \\vmlinux\kasan\kasan_kmalloc+0x44
75   NSD:FFFFFFD424977248| 44 BD 03 2E 90 FF FF FF  0xFFFFFF902E03BD44 \\vmlinux\kasan\kasan_slab_alloc+0x14
76   NSD:FFFFFFD424977250| BC 17 03 2E 90 FF FF FF  0xFFFFFF902E0317BC \\vmlinux\slub\kmem_cache_alloc+0x2EC
77   NSD:FFFFFFD424977258| 50 52 0A 2E 90 FF FF FF  0xFFFFFF902E0A5250 \\vmlinux\fs/namei\getname_flags+0xC8
78   NSD:FFFFFFD424977260| 58 9A 0A 2E 90 FF FF FF  0xFFFFFF902E0A9A58 \\vmlinux\fs/namei\user_path_at_empty+0x40
79   NSD:FFFFFFD424977268| 18 50 09 2E 90 FF FF FF  0xFFFFFF902E095018 \\vmlinux\fs/stat\vfs_statx+0xF8
80   NSD:FFFFFFD424977270| CC 54 09 2E 90 FF FF FF  0xFFFFFF902E0954CC \\vmlinux\fs/stat\__arm64_sys_newfstatat+0x11C
81   NSD:FFFFFFD424977278| 00 05 AB 2D 90 FF FF FF  0xFFFFFF902DAB0500 \\vmlinux\kernel/syscall\el0_svc_common+0x158
82   NSD:FFFFFFD424977280| 40 03 AB 2D 90 FF FF FF  0xFFFFFF902DAB0340 \\vmlinux\kernel/syscall\el0_svc_handler+0x108
83   NSD:FFFFFFD424977288| 88 57 A8 2D 90 FF FF FF  0xFFFFFF902DA85788 \\vmlinux\Global\el0_svc+0x8
84   NSD:FFFFFFD424977290| 00 00 00 00 00 00 00 00  0x0
85   NSD:FFFFFFD424977298| 00 00 00 00 00 00 00 00  0x0
86   NSD:FFFFFFD4249772A0| 00 00 00 00 00 00 00 00  0x0
87   NSD:FFFFFFD4249772A8| 00 00 00 00 00 00 00 00  0x0
88   NSD:FFFFFFD4249772B0| 06 00 00 00 CA 07 00 00  0x7CA00000006
89   NSD:FFFFFFD4249772B8| 16 C4 FF FF 00 00 00 00  0xFFFFC416
90   NSD:FFFFFFD4249772C0| 37 09 00 00 AC 03 E0 BB  0xBBE003AC00000937
91   NSD:FFFFFFD4249772C8| 7C 04 00 00 CA 02 60 F7  0xF76002CA0000047C
92   NSD:FFFFFFD4249772D0| 5A 5A 5A 5A 5A 5A 5A 5A  0x5A5A5A5A5A5A5A5A
93   NSD:FFFFFFD4249772D8| 5A 5A 5A 5A 5A 5A 5A 5A  0x5A5A5A5A5A5A5A5A
94   NSD:FFFFFFD4249772E0| 5A 5A 5A 5A 5A 5A 5A 5A  0x5A5A5A5A5A5A5A5A
95   NSD:FFFFFFD4249772E8| 5A 5A 5A 5A 5A 5A 5A 5A  0x5A5A5A5A5A5A5A5A
96   NSD:FFFFFFD4249772F0| 5A 5A 5A 5A 5A 5A 5A 5A  0x5A5A5A5A5A5A5A5A
97   NSD:FFFFFFD4249772F8| 5A 5A 5A 5A 5A 5A 5A 5A  0x5A5A5A5A5A5A5A5A 
 
 
슬럽 오브젝트의 track 정보 확인하기 
 
이번에는 해당 슬럽 오브젝트를 할당했을 때의 track 구조체를 확인해봅시다. 
 
  (struct track *) (struct track*)0xFFFFFFD424977190 = 0xFFFFFFD424977190 = end+0x43F00B9190 -> (
    (long unsigned int) addr = 18446743593480271940 = 0xFFFFFF90301AB844, // qdf_mem\qdf_mem_malloc_debug+0x13C
    (long unsigned int [16]) addrs = (
      [0] = 18446743593445180220 = 0xFFFFFF902E03433C,  // __kmalloc+0x3AC
      [1] = 18446743593480271940 = 0xFFFFFF90301AB844, // qdf_mem\qdf_mem_malloc_debug+0x13C
      [2] = 18446743593484307128 = 0xFFFFFF9030584AB8, // wlan_ipa_core\wlan_ipa_setup_sys_pipe+0x3A8
      [3] = 18446743593484304544 = 0xFFFFFF90305840A0, // wlan_ipa_core\wlan_ipa_setup+0xDC0
      [4] = 18446743593484280696 = 0xFFFFFF903057E378, // wlan_ipa_main\ipa_obj_setup+0x28
      [5] = 18446743593484279040 = 0xFFFFFF903057DD00, // ipa_pdev_obj_create_notification+0x138
      [6] = 18446743593483288752 = 0xFFFFFF903048C0B0, // wlan_objmgr_pdev_obj_create+0x338
      [7] = 18446743593476423624 = 0xFFFFFF902FDFFFC8, // hdd_objmgr_create_and_store_pdev+0xF8
      [8] = 18446743593476186496 = 0xFFFFFF902FDC6180, // hdd_update_tgt_cfg+0xC0
      [9] = 18446743593480592672 = 0xFFFFFF90301F9D20, // wma_rx_ready_event+0x2910
      [10] = 18446743593480538616 = 0xFFFFFF90301EC9F8, // wma_legacy_service_ready_event_handler+0x70
      [11] = 18446743593482900064 = 0xFFFFFF903042D260, // init_deinit_ready_event_handler+0x490
      [12] = 18446743593481534440 = 0xFFFFFF90302DFBE8, // __wmi_control_rx+0xA78
      [13] = 18446743593481540024 = 0xFFFFFF90302E11B8, // wmi_rx_event_work+0x4E0
      [14] = 18446743593480248912 = 0xFFFFFF90301A5E50, // __qdf_defer_func+0x68
      [15] = 18446743593440067512 = 0xFFFFFF902DB53FB8),  // process_one_work+0x900
    (int) cpu = 6 = 0x6,
    (int) pid = 2359 = 0x0937,
    (long unsigned int) when = 4294951962 = 0xFFFFC41A)
 
0xFFFFFF90301AB844 주소에 해당하는 심벌은 qdf_mem_malloc_debug+0x13C이고 해당 함수의 구현부는 다음과 같습니다.
 
1 void *qdf_mem_malloc_debug(size_t size, const char *file, uint32_t line,
2     void *caller, uint32_t flag)
3 {
4  QDF_STATUS status;
5  enum qdf_debug_domain current_domain = qdf_debug_domain_get();
6  qdf_list_t *mem_list = qdf_mem_list_get(current_domain);
7  struct qdf_mem_header *header;
8  void *ptr;
9  unsigned long start, duration;
10 ...
11 start = qdf_mc_timer_get_system_time();
12 header = kzalloc(size + QDF_MEM_DEBUG_SIZE, flag);
13 duration = qdf_mc_timer_get_system_time() - start;
 
7번째와 12번째 줄로 보아 qdf_mem_header 구조체로 해당 슬럽 오브젝트를 사용했음을 알 수 있습니다.
 
0xFFFFFFD424977080 주소를 qdf_mem_header 구조체로 캐스팅하면 다음과 같은 출력 결과를 확인할 수 있습니다. 
 
  (struct qdf_mem_header *) (struct qdf_mem_header*)0xFFFFFFD424977080 
    (qdf_list_node_t) node = (
      (struct list_head *) next = 0xFFFFFFD4AF9B5580,
      (struct list_head *) prev = 0xFFFFFFD424974C80),
    (enum qdf_debug_domain) domain = QDF_DEBUG_DOMAIN_ACTIVE = 1,
    (uint8_t) freed = 0,
    (char [48]) file = "wlan_ipa_core.c",
    (uint32_t) line = 2117,
    (uint32_t) size = 40,
    (void *) caller = 0xFFFFFF90305840A0 = wlan_ipa_setup+0xDC0,
    (uint64_t) header = 7017280452245743464,
    (uint64_t) time = 3096918292)
 
크래시 유틸리티로 슬랩 페이지(kmalloc-512)를 확인 
 
이번에는 kmalloc-512 슬럽 오브젝트를 확인해보자.
 
1 crash64> kmem 0xFFFFFFBF4F6EA200
2 CACHE             OBJSIZE  ALLOCATED     TOTAL  SLABS  SSIZE  NAME
3 ffffffd3c08e0780      512      16918     18276    572    32k  kmalloc-512
4   SLAB              MEMORY            NODE  TOTAL  ALLOCATED  FREE
5   ffffffbf4f6ea200  ffffffd3dba88000     0     32         28     4
6   FREE / [ALLOCATED]
7   [ffffffd3dba88000]
8   [ffffffd3dba88400]
9   [ffffffd3dba88800]
10  [ffffffd3dba88c00]
11   ffffffd3dba89000
12  [ffffffd3dba89400]
13  [ffffffd3dba89800]
14  [ffffffd3dba89c00]
15  [ffffffd3dba8a000]
16  [ffffffd3dba8a400]
17  [ffffffd3dba8a800]
18  [ffffffd3dba8ac00]
19  [ffffffd3dba8b000]
20  [ffffffd3dba8b400]
21  [ffffffd3dba8b800]
22   ffffffd3dba8bc00
23  [ffffffd3dba8c000]
24  [ffffffd3dba8c400]
25  [ffffffd3dba8c800]
26  [ffffffd3dba8cc00]
27  [ffffffd3dba8d000]
28   ffffffd3dba8d400
29  [ffffffd3dba8d800]
30   ffffffd3dba8dc00
31  [ffffffd3dba8e000]
32  [ffffffd3dba8e400]
33  [ffffffd3dba8e800]
34  [ffffffd3dba8ec00]
35  [ffffffd3dba8f000]
36  [ffffffd3dba8f400]
37  [ffffffd3dba8f800]
38  [ffffffd3dba8fc00]
39
40      PAGE               PHYSICAL      MAPPING       INDEX CNT FLAGS
41 ffffffbf4f6ea200         9ba88000 ffffffd3c08e0780 ffffffd3dba8bc80  1 10200 slab,head
42
 
위 출력 결과에서 ffffffd3dba88000 주소에 해당하는 슬럽 오브젝트를 확인해보자.
 
1 ________________address|_data____________________|value_____________|symbol
2    NSD:FFFFFFD3DBA88000| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
3    NSD:FFFFFFD3DBA88008| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
4    NSD:FFFFFFD3DBA88010| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
5    NSD:FFFFFFD3DBA88018| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
6    NSD:FFFFFFD3DBA88020| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
7    NSD:FFFFFFD3DBA88028| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
8    NSD:FFFFFFD3DBA88030| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
9    NSD:FFFFFFD3DBA88038| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
10   NSD:FFFFFFD3DBA88040| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
11   NSD:FFFFFFD3DBA88048| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
12   NSD:FFFFFFD3DBA88050| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
13   NSD:FFFFFFD3DBA88058| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
14   NSD:FFFFFFD3DBA88060| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
15   NSD:FFFFFFD3DBA88068| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
16   NSD:FFFFFFD3DBA88070| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
17   NSD:FFFFFFD3DBA88078| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
18   NSD:FFFFFFD3DBA88080| 80 D4 47 6E D4 FF FF FF  0xFFFFFFD46E47D480
19   NSD:FFFFFFD3DBA88088| 88 D0 4B 75 D4 FF FF FF  0xFFFFFFD4754BD088
20   NSD:FFFFFFD3DBA88090| 00 00 00 00 00 00 00 00  0x0
21   NSD:FFFFFFD3DBA88098| 00 00 00 00 00 00 00 00  0x0
22   NSD:FFFFFFD3DBA880A0| A0 80 A8 DB D3 FF FF FF  0xFFFFFFD3DBA880A0
23   NSD:FFFFFFD3DBA880A8| A0 80 A8 DB D3 FF FF FF  0xFFFFFFD3DBA880A0
24   NSD:FFFFFFD3DBA880B0| B0 05 00 00 00 00 00 00  0x5B0
25   NSD:FFFFFFD3DBA880B8| 00 00 00 00 00 00 00 00  0x0
26   NSD:FFFFFFD3DBA880C0| 00 00 00 00 00 00 00 00  0x0
27   NSD:FFFFFFD3DBA880C8| C8 80 A8 DB D3 FF FF FF  0xFFFFFFD3DBA880C8
28   NSD:FFFFFFD3DBA880D0| C8 80 A8 DB D3 FF FF FF  0xFFFFFFD3DBA880C8
29   NSD:FFFFFFD3DBA880D8| 00 00 00 00 00 00 00 00  0x0
30   NSD:FFFFFFD3DBA880E0| 00 00 00 00 00 00 00 00  0x0
31   NSD:FFFFFFD3DBA880E8| 00 00 00 00 00 00 00 00  0x0
32   NSD:FFFFFFD3DBA880F0| 03 00 00 00 00 00 00 00  0x3
33   NSD:FFFFFFD3DBA880F8| 01 72 00 00 00 00 00 00  0x7201
34   NSD:FFFFFFD3DBA88100| 00 00 00 00 00 00 00 00  0x0
35   NSD:FFFFFFD3DBA88108| 00 00 00 00 00 00 00 00  0x0
36   NSD:FFFFFFD3DBA88110| 03 00 00 00 00 00 00 00  0x3
37   NSD:FFFFFFD3DBA88118| 01 72 00 00 00 00 00 00  0x7201
38   NSD:FFFFFFD3DBA88120| 00 00 00 00 00 00 00 00  0x0
39   NSD:FFFFFFD3DBA88128| 28 81 A8 DB D3 FF FF FF  0xFFFFFFD3DBA88128
40   NSD:FFFFFFD3DBA88130| 28 81 A8 DB D3 FF FF FF  0xFFFFFFD3DBA88128
41   NSD:FFFFFFD3DBA88138| 00 00 00 00 00 00 00 00  0x0
42   NSD:FFFFFFD3DBA88140| 00 00 00 00 3C 00 00 00  0x3C00000000
43 ...
44   NSD:FFFFFFD3DBA88270| 00 00 00 00 00 00 00 00  0x0
45   NSD:FFFFFFD3DBA88278| 00 00 00 00 00 00 00 00  0x0
46   NSD:FFFFFFD3DBA88280| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
47   NSD:FFFFFFD3DBA88288| 1E AF 63 1C B1 1B 93 8D  0x8D931BB11C63AF1E
48   NSD:FFFFFFD3DBA88290| CC A8 A8 30 90 FF FF FF  0xFFFFFF9030A8A8CC \\vmlinux\binder\binder_get_thread+0x1EC
49   NSD:FFFFFFD3DBA88298| A8 1B 03 2E 90 FF FF FF  0xFFFFFF902E031BA8 \\vmlinux\slub\kmem_cache_alloc_trace+0x358
50   NSD:FFFFFFD3DBA882A0| CC A8 A8 30 90 FF FF FF  0xFFFFFF9030A8A8CC \\vmlinux\binder\binder_get_thread+0x1EC
51   NSD:FFFFFFD3DBA882A8| F0 36 A8 30 90 FF FF FF  0xFFFFFF9030A836F0 \\vmlinux\binder\binder_ioctl+0x368
52   NSD:FFFFFFD3DBA882B0| 74 64 0C 2E 90 FF FF FF  0xFFFFFF902E0C6474 \\vmlinux\fs/ioctl\do_vfs_ioctl+0xA14
53   NSD:FFFFFFD3DBA882B8| 28 71 0C 2E 90 FF FF FF  0xFFFFFF902E0C7128 \\vmlinux\fs/ioctl\__arm64_sys_ioctl+0xB8
54   NSD:FFFFFFD3DBA882C0| 00 05 AB 2D 90 FF FF FF  0xFFFFFF902DAB0500 \\vmlinux\kernel/syscall\el0_svc_common+0x158
55   NSD:FFFFFFD3DBA882C8| 40 03 AB 2D 90 FF FF FF  0xFFFFFF902DAB0340 \\vmlinux\kernel/syscall\el0_svc_handler+0x108
56   NSD:FFFFFFD3DBA882D0| 88 57 A8 2D 90 FF FF FF  0xFFFFFF902DA85788 \\vmlinux\Global\el0_svc+0x8
57   NSD:FFFFFFD3DBA882D8| 00 00 00 00 00 00 00 00  0x0
58   NSD:FFFFFFD3DBA882E0| 00 00 00 00 00 00 00 00  0x0
59   NSD:FFFFFFD3DBA882E8| 00 00 00 00 00 00 00 00  0x0
60   NSD:FFFFFFD3DBA882F0| 00 00 00 00 00 00 00 00  0x0
61   NSD:FFFFFFD3DBA882F8| 00 00 00 00 00 00 00 00  0x0
62   NSD:FFFFFFD3DBA88300| 00 00 00 00 00 00 00 00  0x0
63   NSD:FFFFFFD3DBA88308| 00 00 00 00 00 00 00 00  0x0
64   NSD:FFFFFFD3DBA88310| 00 00 00 00 00 00 00 00  0x0
65   NSD:FFFFFFD3DBA88318| 06 00 00 00 B0 05 00 00  0x5B000000006
66   NSD:FFFFFFD3DBA88320| 96 C3 FF FF 00 00 00 00  0xFFFFC396
67   NSD:FFFFFFD3DBA88328| F0 E9 03 2E 90 FF FF FF  0xFFFFFF902E03E9F0 \\vmlinux\quarantine\qlink_free+0x18
68   NSD:FFFFFFD3DBA88330| 08 EA 03 2E 90 FF FF FF  0xFFFFFF902E03EA08 \\vmlinux\quarantine\qlink_free+0x30
69   NSD:FFFFFFD3DBA88338| 68 E6 03 2E 90 FF FF FF  0xFFFFFF902E03E668 \\vmlinux\quarantine\quarantine_reduce+0x158
70   NSD:FFFFFFD3DBA88340| 94 BD 03 2E 90 FF FF FF  0xFFFFFF902E03BD94 \\vmlinux\kasan\kasan_kmalloc+0x44
71   NSD:FFFFFFD3DBA88348| 44 BD 03 2E 90 FF FF FF  0xFFFFFF902E03BD44 \\vmlinux\kasan\kasan_slab_alloc+0x14
72   NSD:FFFFFFD3DBA88350| BC 17 03 2E 90 FF FF FF  0xFFFFFF902E0317BC \\vmlinux\slub\kmem_cache_alloc+0x2EC
73   NSD:FFFFFFD3DBA88358| 00 AF AE 2D 90 FF FF FF  0xFFFFFF902DAEAF00 \\vmlinux\fork\vm_area_dup+0x30
74   NSD:FFFFFFD3DBA88360| D8 63 FF 2D 90 FF FF FF  0xFFFFFF902DFF63D8 \\vmlinux\mm/mmap\__split_vma+0xA8
75   NSD:FFFFFFD3DBA88368| B4 31 FF 2D 90 FF FF FF  0xFFFFFF902DFF31B4 \\vmlinux\mm/mmap\do_munmap+0x24C
76   NSD:FFFFFFD3DBA88370| 18 1F FF 2D 90 FF FF FF  0xFFFFFF902DFF1F18 \\vmlinux\mm/mmap\mmap_region+0x4E0
77   NSD:FFFFFFD3DBA88378| E0 13 FF 2D 90 FF FF FF  0xFFFFFF902DFF13E0 \\vmlinux\mm/mmap\do_mmap+0x8E8
78   NSD:FFFFFFD3DBA88380| A8 BC F9 2D 90 FF FF FF  0xFFFFFF902DF9BCA8 \\vmlinux\mm/util\vm_mmap_pgoff+0x160
79   NSD:FFFFFFD3DBA88388| FC 2B FF 2D 90 FF FF FF  0xFFFFFF902DFF2BFC \\vmlinux\mm/mmap\ksys_mmap_pgoff+0x10C
80   NSD:FFFFFFD3DBA88390| C0 E1 A9 2D 90 FF FF FF  0xFFFFFF902DA9E1C0 \\vmlinux\arch/arm64/kernel/sys\__arm64_sys_mmap+0xE8
81   NSD:FFFFFFD3DBA88398| 00 05 AB 2D 90 FF FF FF  0xFFFFFF902DAB0500 \\vmlinux\kernel/syscall\el0_svc_common+0x158
82   NSD:FFFFFFD3DBA883A0| 40 03 AB 2D 90 FF FF FF  0xFFFFFF902DAB0340 \\vmlinux\kernel/syscall\el0_svc_handler+0x108
83   NSD:FFFFFFD3DBA883A8| 88 57 A8 2D 90 FF FF FF  0xFFFFFF902DA85788 \\vmlinux\Global\el0_svc+0x8
84   NSD:FFFFFFD3DBA883B0| 07 00 00 00 89 02 00 00  0x28900000007
 
슬럽 오브젝트의 track 정보 확인하기 
 
track 구조체로 보아 binder_get_thread+0x1EC 함수에서 kmalloc-512 슬럽 오브젝트를 할당했음을 알 수 있다.
 
binder_get_thread() 함수를 보자.
 
1 static struct binder_thread *binder_get_thread(struct binder_proc *proc)
2 {
3  struct binder_thread *thread;
4  struct binder_thread *new_thread;
6  binder_inner_proc_lock(proc);
7  thread = binder_get_thread_ilocked(proc, NULL);
8  binder_inner_proc_unlock(proc);
9  if (!thread) {
10 new_thread = kzalloc(sizeof(*thread), GFP_KERNEL);
11 if (new_thread == NULL)
 
4번째와 10번째 줄로 kmalloc-512 슬럽 오브젝트를 binder_thread 구조체로 사용한다는 사실을 알 수 있습니다.
슬럽 오브젝트의 패이 로드 주소는 다음과 같이 FFFFFFD3DBA88080 이니;
 
17   NSD:FFFFFFD3DBA88078| CC CC CC CC CC CC CC CC  0xCCCCCCCCCCCCCCCC
18   NSD:FFFFFFD3DBA88080| 80 D4 47 6E D4 FF FF FF  0xFFFFFFD46E47D480
19   NSD:FFFFFFD3DBA88088| 88 D0 4B 75 D4 FF FF FF  0xFFFFFFD4754BD088
20   NSD:FFFFFFD3DBA88090| 00 00 00 00 00 00 00 00  0x0
 
FFFFFFD3DBA88080 주소를 binder_thread 구조체로 캐스팅해보자.
 
$ v.v %t %h %i %y %s %d (struct binder_thread*)0xFFFFFFD3DBA88080
  (struct binder_thread *) (struct binder_thread*)0xFFFFFFD3DBA88080 = 0xFFFFFFD3DBA88080 = end+0x43
    (struct binder_proc *) proc = 0xFFFFFFD46E47D480 = end+0x4439BBF480,
    (struct rb_node) rb_node = ((long unsigned int) __rb_parent_color = 18446743886698893448 = 0xFFF
    (struct list_head) waiting_thread_node = ((struct list_head *) next = 0xFFFFFFD3DBA880A0 = end+0
    (int) pid = 1456 = 0x05B0,
    (int) looper = 0 = 0x0,
    (bool) looper_need_return = FALSE,
    (struct binder_transaction *) transaction_stack = 0x0 = ,
    (struct list_head) todo = (
      (struct list_head *) next = 0xFFFFFFD3DBA880C8 = end+0x43A71CA0C8,
      (struct list_head *) prev = 0xFFFFFFD3DBA880C8 = end+0x43A71CA0C8),
    (bool) process_todo = FALSE,
    (struct binder_error) return_error = ((struct binder_work) work = ((struct list_head) entry = ((
    (struct binder_error) reply_error = ((struct binder_work) work = ((struct list_head) entry = ((s
    (wait_queue_head_t) wait = ((spinlock_t) lock = ((struct raw_spinlock) rlock = ((arch_spinlock_t
    (struct binder_stats) stats = ((atomic_t [18]) br = ([0] = ((int) counter = 0 = 0x0), [1] = ((in
    (atomic_t) tmp_ref = ((int) counter = 0 = 0x0),
    (bool) is_dead = FALSE,
    (struct task_struct *) task = 0xFFFFFFD4DD85B340 = end+0x44A8F9D340 -> (
      (struct thread_info) thread_info = ((long unsigned int) flags = 2048 = 0x0800, (long unsigned
      (long int) state = 1 = 0x1,
      (void *) stack = 0xFFFFFFD4652D0000 = end+0x4430A12000,
      (atomic_t) usage = ((int) counter = 5 = 0x5),
      (unsigned int) flags = 1077952576 = 0x40404040,
      (unsigned int) ptrace = 0 = 0x0,
 
위와 같은 정보를 확인할 수 있습니다.
 
 
Overview
 
ftrace로 다양한 디버깅 정보를 출력할 수 있다.
물론 function, function_graph 트레이서를 활용해 화려한 디버깅 정보를 볼 수 있는데, nop 트레이서로 다음과 같은 정보를 보면 유용할 것이다.
 
rcu_preempt-9     [000] ...1  5429.091523: sched_preemption_execute: delta=8890468(ns) Callers: (schedule<-rcu_nocb_kthread<-kthread<-ret_from_fork)
 
ftrace에서 sched_preemption_trigger 이벤트를 추가하는 방법에 대해서 알아보자.
 
 
추가할 ftrace 이벤트의 스팩은 다음과 같다.
 
   ■ 이름: sched_preemption_execute
   ■ 디버깅 정보
      - delta: 실행 시간
      - 콜스택
 
ftrace 이벤트 정의하기
 
sched_preemption_execute 이벤트를 아래와 같이 정의하자. (참고로 아래 패치 코드는 라즈베리 파이에서 추가했다.)
 
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index 9a4bdfa..e192f5b 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -587,6 +587,35 @@ TRACE_EVENT(sched_wake_idle_without_ipi,
 
        TP_printk("cpu=%d", __entry->cpu)
 );
+TRACE_EVENT(sched_preemption_execute,
+
+       TP_PROTO(u64 delta,
+                       unsigned long caller_addr0, unsigned long caller_addr1,
+                       unsigned long caller_addr2, unsigned long caller_addr3),
+
+       TP_ARGS(delta, caller_addr0, caller_addr1, caller_addr2, caller_addr3),
+
+       TP_STRUCT__entry(
+               __field(u64, delta)
+               __field(void*, caller_addr0)
+               __field(void*, caller_addr1)
+               __field(void*, caller_addr2)
+               __field(void*, caller_addr3)
+       ),
+
+       TP_fast_assign(
+               __entry->delta = delta;
+               __entry->caller_addr0 = (void *)caller_addr0;
+               __entry->caller_addr1 = (void *)caller_addr1;
+               __entry->caller_addr2 = (void *)caller_addr2;
+               __entry->caller_addr3 = (void *)caller_addr3;
+       ),
+
+       TP_printk("delta=%llu(ns) Callers:(%ps<-%ps<-%ps<-%ps)",
+                               __entry->delta,
+                               __entry->caller_addr0, __entry->caller_addr1,
+                               __entry->caller_addr2, __entry->caller_addr3)
+);
 #endif /* _TRACE_SCHED_H */
 
 /* This part must be outside protection */
 
위 코드에서 핵심은 다음 코드이다.
 
+       TP_printk("delta=%llu(ns) Callers:(%ps<-%ps<-%ps<-%ps)",
+                               __entry->delta,
+                               __entry->caller_addr0, __entry->caller_addr1,
+                               __entry->caller_addr2, __entry->caller_addr3)
 
함수의 시각과 콜스택의 심보를 출력한다.
 
 
ftrace 이벤트를 출력하는 코드
 
ftrace 이벤트를 출력하는 패치 코드는 아래와 같다. __schedule() 함수이다.
 
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 2befd2c..2b0e6f7 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3429,6 +3429,7 @@ static void __sched notrace __schedule(bool preempt)
        struct rq_flags rf;
        struct rq *rq;
        int cpu;
+       u64 delta = 0;
 
        cpu = smp_processor_id();
        rq = cpu_rq(cpu);
@@ -3490,6 +3491,10 @@ static void __sched notrace __schedule(bool preempt)
        clear_tsk_need_resched(prev);
        clear_preempt_need_resched();
 
+       delta = sched_clock();
+       trace_sched_preemption_execute(delta, CALLER_ADDR0, CALLER_ADDR1,
+                               CALLER_ADDR2, CALLER_ADDR3);
+
        if (likely(prev != next)) {
                rq->nr_switches++;
                rq->curr = next;
 
위 패치에서 핵심 코드 조각은 아래와 같다.
 
+       trace_sched_preemption_execute(delta, CALLER_ADDR0, CALLER_ADDR1,
+                               CALLER_ADDR2, CALLER_ADDR3);
 
함수가 실행되는 시간과 함수의 콜 스택을 출력하는 기능이다.
 
CALLER_ADDR0~CALLER_ADDR3의 정체
 
이번에 소개한 패치 코드에서 CALLER_ADDR0~CALLER_ADDR3 매크로가 핵심이다.
CALLER_ADDR0~CALLER_ADDR3 매크로의 정체를 확인하기 위해 전처리 코드를 열어보자.
 
kernel/sched/.tmp_core.i
..
 delta = sched_clock();
 trace_sched_preemption_execute(delta, ((unsigned long)__builtin_return_address(0)), ((unsigned long)return_address(1)),
    ((unsigned long)return_address(2)), ((unsigned long)return_address(3)));
 
코드를 통해 CALLER_ADDR0~CALLER_ADDR3 매크로의 정체는 return_address(n) 함수라는 사실을 알 수 있다.
 
   ■ CALLER_ADDR1 = ((unsigned long)return_address(1)
   ■ CALLER_ADDR2 = ((unsigned long)return_address(2)
   ■ CALLER_ADDR3 = ((unsigned long)return_address(3)
 
 
sched_preemption_execute 이벤트의 출력 결과
 
위 패치 코드를 반영한 후 출력되는 결과는 아래와 같다.
 
rcu_preempt-9     [000] ...1  5429.091523: sched_preemption_execute: delta=8890468(ns) Callers: (schedule<-rcu_nocb_kthread<-kthread<-ret_from_fork)
 
ftrace 메시지로 콜 스택을 바로 확인할 수 있다.
 
헤더 파일
 
이 패치를 적용하기 위해서 다음 목록과 같은 헤더 파일을 추가해야 한다.
 
   ■ #include <trace/events/sched.h>
   ■ #include <linux/ftrace.h>
   ■ #include <linux/sched/clock.h>
 
샘플 패치 코드를 보자. fs/proc/interrupts.c에 있는 int_seq_start() 함수를 수정했다.
 
diff --git a/fs/proc/interrupts.c b/fs/proc/interrupts.c
index cb0edc7..a4bbc19 100644
--- a/fs/proc/interrupts.c
+++ b/fs/proc/interrupts.c
@@ -5,12 +5,23 @@
 #include <linux/irqnr.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
-
+#include <trace/events/sched.h>
+#include <linux/ftrace.h>
+#include <linux/sched/clock.h>
 /*
  * /proc/interrupts
  */
 static void *int_seq_start(struct seq_file *f, loff_t *pos)
 {
+       u64 delta = 0;
+
+       delta = sched_clock();
+       trace_sched_preemption_execute(delta, CALLER_ADDR0, CALLER_ADDR1,
+                               CALLER_ADDR2, CALLER_ADDR3);
+
+       printk("delta=%llu(ns) Callers:(%ps<-%ps<-%ps<-%ps)",
+                       delta, CALLER_ADDR0, CALLER_ADDR1, CALLER_ADDR2, CALLER_ADDR3);
+
        return (*pos <= nr_irqs) ? pos : NULL;
 }
 
이 코드를 참고하면 다양한 방식으로 디버깅할 수 있다.
 
# Reference: For more information on 'Linux Kernel';
 
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1
 
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2
 
 
 
 
프로젝트를 진행하다 보면 특정 CPU를 Isolation 시키고 싶을 때가 있습니다.
이 때 다음 패치(CPU2와 CPU3를 Isolation)를 적용하면 됩니다.
 
* 커널 4.19 버전
diff --git a/kernel/cpu.c b/kernel/cpu.c
index d9f855c..816bf4f 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -1126,6 +1126,10 @@ static int do_cpu_up(unsigned int cpu, enum cpuhp_state target)
 {
        int err = 0;
 
+       if(cpu == 2 | cpu ==3) {
+               return -EINVAL;
+       }
+
        if (!cpu_possible(cpu)) {
                pr_err("can't online cpu %d because it is not configured as may-hotadd at boot time\n",
                       cpu);
 
* 커널 3.18 버전
diff --git a/kernel/cpu.c b/kernel/cpu.c
index cd9c5c6..aeda1f8 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -518,6 +518,10 @@ int cpu_up(unsigned int cpu)
 {
    int err = 0;
 
+    if(cpu == 2 | cpu ==3) {
+       return -EINVAL;
+   }
+
    if (!cpu_possible(cpu)) {
        pr_err("can't online cpu %d because it is not configured as may-hotadd at boot time\n",
               cpu);
 
 
이처럼 논리적인 CPU를 끄면 디바이스 노드에도 CPU2와 CPU3이 보이지 않을 것입니다.
$ ls /sys/devices/system/cpu
cpu0   cpu1
 
 
# Reference: For more information on 'Linux Kernel';
 
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1
 
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2
 
 
 
 
walk_stackframe() 함수의 구현부를 보면 notrace 키워드로 선언됐음을 알 수 있습니다.
 
void notrace walk_stackframe(struct stackframe *frame,
     int (*fn)(struct stackframe *, void *), void *data)
{
while (1) {
int ret;
 
if (fn(frame, data))
break;
ret = unwind_frame(frame);
if (ret < 0)
break;
}
}
EXPORT_SYMBOL(walk_stackframe);
 
이 함수를 전처리 코드에서 확인하면 구현부는 다음과 같습니다.
 
void __attribute__((no_instrument_function)) walk_stackframe(struct stackframe *frame,
       int (*fn)(struct stackframe *, void *), void *data)
{
 while (1) {
  int ret;
 
  if (fn(frame, data))
   break;
  ret = unwind_frame(frame);
  if (ret < 0)
   break;
 }
}
 
함수 구현부와 같이 notrace는 '__attribute__((no_instrument_function))' 구문으로 치환이 됩니다.
 
 
# Reference: For more information on 'Linux Kernel';
 
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1
 
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2
 
 
ssize_t와 size_t 타입의 실체를 확인해보자.
 
아래 코드를 전처리 코드로 확인해볼까?
static ssize_t default_read_file(struct file *file, char __user *buf,
 size_t count, loff_t *ppos)
{
  return 0;
}
 
size_t 타입의 정체
 
먼저 size_t의 정체를 확인해보자.
 
 280 # 55 "/root/src/kernel_src/linux/include/linux/types.h"
  281 typedef __kernel_size_t size_t;
 
size_t 는 __kernel_size_t로 캐스팅 된다.  
 
 201 # 68 "/root/src/kernel_src/linux/include/uapi/asm-generic/posix_types.h"
  202 typedef unsigned int __kernel_size_t;
 
__kernel_size_t 타입은 'unsigned int'로 타입으로 캐스팅된다. 
 
정리하자.
size_t -> unsigned int
 
ssize_t 타입의 정체
 
이번에는 ssize_t 타입을 확인하자.
 280 # 55 "/root/src/rachman_kernel_src/linux/include/linux/types.h"
...
  282
  283
  284
  285
  286 typedef __kernel_ssize_t ssize_t;
 
ssize_t은 __kernel_ssize_t 타입으로 캐스팅된다.  
 
 201 # 68 "/root/src/rachman_kernel_src/linux/include/uapi/asm-generic/posix_types.h"
  202 typedef unsigned int __kernel_size_t;
  203 typedef int __kernel_ssize_t;
 
__kernel_ssize_t은  int 타입으로 캐스팅된다.
 
정리하면;
 ssize_t -> int
 
마무리 
 
이제 default_read_file() 함수를 볼 때, 
static ssize_t default_read_file(struct file *file, char *buf,
     size_t count, loff_t *ppos)
{
 return 0;
}
 
다음과 같이 해석해보자.
static int default_read_file(struct file *file, char *buf,
     unsigned int count, loff_t *ppos)
{
 return 0;
}
리눅스 커널에서는 라덱스 트리로 핵심 자료구조를 관리합니다.
이번 시간에는 크래시 유틸리티로 라덱스 트리를 디버깅하는 방법을 소개합니다.
 
라덱스 트리를 보기 위한 명령어
 
크래시 유틸리티로 라덱스 트리 노드를 보기 위한 명령어 포멧은 다음과 같습니다.
tree -t radix -N (struct radix_tree_node *) 구조체 주소
 
struct radix_tree_node 구조체 시작 주소만 알면 됩니다.
 
struct radix_tree_node 구조체 주소가 0xFFFFFFFF3A806E79 인 경우 출력 결과는 다음과 같습니다.
crash64_kaslr> tree -t radix -N 0xFFFFFFFF3A806E79
ffffffff3f53c180
ffffffff3f53c4c0
ffffffff3f555180
ffffffff3f5554c0
ffffffff3f56e180
ffffffff3f56e4c0
ffffffff3f587180
ffffffff3f5874c0
ffffffff3f5a0180
ffffffff3f5a04c0
ffffffff3f5b9180
ffffffff3f5b94c0
ffffffff3f5d2180
ffffffff3f5d24c0
ffffffff3f5eb180
ffffffff3f5eb4c0
fffffffe4683d480
ffffffff3657a480
 
Case Study: worker_pool_idr 전역변수로 라덱스 트리 디버깅해보기 
워커풀은 라덱스 트리로 구성돼 있습니다.
 
#define for_each_pool(pool, pi)                     \
    idr_for_each_entry(&worker_pool_idr, pool, pi)          \
        if (({ assert_rcu_or_pool_mutex(); false; })) { }   \
        else
 
 
worker_pool_idr 전역변수 타입은 struct idr worker_pool_idr입니다.
 
crash64_kaslr> whatis worker_pool_idr
struct idr worker_pool_idr;
crash64_kaslr>
 
그런데 struct idr 구조체 idr_rt 필드 타입은 struct radix_tree_root입니다.
crash64_kaslr> struct idr
struct idr {
    struct radix_tree_root idr_rt;
    unsigned int idr_base;
    unsigned int idr_next;
}
 
다음 명령어로 worker_pool_idr.idr_rt 필드의 라덱스 트리 노드는 0xffffffff3a806e79임을 알 수 있습니다.
 
crash64_kaslr> p worker_pool_idr.idr_rt.rnode
$2 = (struct radix_tree_node *) 0xffffffff3a806e79
 
 
다음 명령어를 입력하면 라덱스 트리 모든 노드를 볼 수 있습니다.
crash64_kaslr> tree -t radix -N 0xFFFFFFFF3A806E79
ffffffff3f53c180
ffffffff3f53c4c0
ffffffff3f555180
ffffffff3f5554c0
ffffffff3f56e180
ffffffff3f56e4c0
ffffffff3f587180
ffffffff3f5874c0
ffffffff3f5a0180
ffffffff3f5a04c0
ffffffff3f5b9180
ffffffff3f5b94c0
ffffffff3f5d2180
ffffffff3f5d24c0
ffffffff3f5eb180
ffffffff3f5eb4c0
fffffffe4683d480
ffffffff3657a480
 
 
위  주소 리스트는 struct worker_pool 구조체이며 다음과 같이 확인할 수 있습니다.
 
1: ffffffff3f53c4c0 struct worker_pool 디버깅 
[0]: watchdog_ts = 4295299595 // 워커 터치 시간 
[1]: 해당 워커풀에 큐잉한 워크 
[2]: 실행 중인 워커: 0xFFFFFFFEE11F5B80
 
  (struct worker_pool *) (struct worker_pool*)0xffffffff3f53c180 = 0xFFFFFFFF3F53C180 = end+0x68B4978180 -> (
    (spinlock_t) lock = ((struct raw_spinlock) rlock = ((arch_spinlock_t) raw_lock = ((atomic_t) val = ((int) counter = 0), (u8) locked = 0, (u8)
    (int) cpu = 0,
    (int) node = 0,
    (int) id = 0,
    (unsigned int) flags = 0,
    (long unsigned int) watchdog_ts = 4295299595, "//<<--[0]"
    (struct list_head) worklist = (
      (struct list_head *) next = 0xFFFFFF9689B2D098 = binder_deferred_work.entry -> (  "//<<--[1]"
        (struct list_head *) next = 0xFFFFFF9689A052C8 = delayed_fput_work.work.entry, "//<<--[1]"
        (struct list_head *) prev = 0xFFFFFFFF3F53C1A0 = end+0x68B49781A0),
      (struct list_head *) prev = 0xFFFFFF9689B65710 = nd_tbl.gc_work.work.entry), "//<<--[1]"
    (int) nr_workers = 3,
    (int) nr_idle = 2,
    (struct list_head) idle_list = ((struct list_head *) next = 0xFFFFFFFEE4BF0680 = end+0x685A02C680, (struct list_head *) prev = 0xFFFFFFFEE07B
    (struct timer_list) idle_timer = ((struct hlist_node) entry = ((struct hlist_node *) next = 0xDEAD000000000200 
    (struct timer_list) mayday_timer = ((struct hlist_node) entry = ((struct hlist_node *) next = 0xDEAD000000000200 
    (struct hlist_head [64]) busy_hash = (
      [0] = ((struct hlist_node *) first = 0x0 = ),
...
      [45] = ((struct hlist_node *) first = 0x0 = ),
      [46] = ((struct hlist_node *) first = 0xFFFFFFFEE11F5B80 = end+0x6856631B80), "//<<--[2]"
      [47] = ((struct hlist_node *) first = 0x0 = ),
 ...
      [63] = ((struct hlist_node *) first = 0x0 = )),
    (struct worker *) manager = 0x0 = ,
    (struct list_head) workers = ((struct list_head *) next = 0xFFFFFFFEE07B9BC8 = end+0x6855BF5BC8, (struct list_head *) prev = 0xFFFFFFFEE11F5B
    (struct completion *) detach_completion = 0x0 = ,
    (struct ida) worker_ida = ((struct radix_tree_root) ida_rt = ((spinlock_t) xa_lock = ((struct raw_spinlock) rlock = ((arch_spinlock_t) raw_lo
    (struct workqueue_attrs *) attrs = 0xFFFFFFFE46881C00 = end+0x67BBCBDC00,
    (struct hlist_node) hash_node = ((struct hlist_node *) next = 0x0 = , (struct hlist_node * *) pprev = 0x0 = ),
    (int) refcnt = 1,
    (atomic_t) nr_running = ((int) counter = 1),
    (struct callback_head) rcu = ((struct callback_head *) next = 0x0 = , (void (*)()) func = 0x0 = ))
 
 
"혹시 궁금하신 점이 있으면 댓글 달아 주세요. 아는 한 성실히 답변 올려드리겠습니다."
 
 
 
 
이번에는 비트 마스크를 C 코드가 아닌 어셈블리 코드로 읽는 방법을 소개합니다.
 
in_interrupt() 함수 소개
 
in_interrupt() 함수는 현재 프로세스가 인터럽트 컨택스트인지 알려주는 기능입니다.
#define in_interrupt() (irq_count())
 
in_interrupt() 함수 코드를 보면 irq_count() 함수로 치환됩니다.
#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK \
 | NMI_MASK))
 
irq_count() 함수 코드를 보면 preempt_count() 결과값과 다음 플래그간 AND BIT 오퍼레이션을 실행합니다.
HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK
 
(HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK) 의 정체는 무엇일까요?
 
다시 소스 코드를 따라가 보니, 눈이 어지럽습니다. "이것을 어떻게 계산할까!!!" 란 생각이 드는군요.
#define PREEMPT_BITS 8
#define SOFTIRQ_BITS 8
#define HARDIRQ_BITS 4
#define NMI_BITS 1
 
#define PREEMPT_SHIFT 0
#define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS)
#define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS)
#define NMI_SHIFT (HARDIRQ_SHIFT + HARDIRQ_BITS)
 
#define __IRQ_MASK(x) ((1UL << (x))-1)
 
#define PREEMPT_MASK (__IRQ_MASK(PREEMPT_BITS) << PREEMPT_SHIFT)
#define SOFTIRQ_MASK (__IRQ_MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT)
#define HARDIRQ_MASK (__IRQ_MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT)
#define NMI_MASK (__IRQ_MASK(NMI_BITS)     << NMI_SHIFT)
 
(HARDIRQ_MASK | SOFTIRQ_OFFSET | NMI_MASK) 마스크를 어셈블리 코드로 알아보기
 
C 코드 형태로 (HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK) 의 정체를 알기는 좀 복잡합니다.
그러면 위 마스크값들의 정체를 알기 위해 in_interrupt() 함수를 어셈블리 코드를 한 번 볼까요?
static struct vm_struct *__get_vm_area_node(unsigned long size,
unsigned long align, unsigned long flags, unsigned long start,
unsigned long end, int node, gfp_t gfp_mask, const void *caller)
{
struct vmap_area *va;
struct vm_struct *area;
 
BUG_ON(in_interrupt());
 
__get_vm_area_node() 함수 가장 앞단에 in_interrupt() 함수를 호출합니다. 아주 좋은 예시입니다.
01 NSR:80265FEC|__get_vm_area_node:   cpy     r12,r13
02 NSR:80265FF0|                      push    {r4-r9,r11-r12,r14,pc}
03 NSR:80265FF4|                      sub     r11,r12,#0x4     ; r11,r12,#4
04 NSR:80265FF8|                      sub     r13,r13,#0x8     ; r13,r13,#8
05 NSR:80265FFC|                      str     r14,[r13,#-0x4]!
06 NSR:80266000|                      bl      0x8010FDBC       ; __gnu_mcount_nc
07 NSR:80266004|                      cpy     r14,r13
08 NSR:80266008|                      bic     r12,r14,#0x1FC0   ; r12,r14,#8128
09 NSR:8026600C|                      bic     r12,r12,#0x3F    ; r12,r12,#63
10 NSR:80266010|                      ldr     r14,[r12,#0x4]
11 NSR:80266014|                      ldr     r12,0x80266104
12 NSR:80266018|                      cpy     r8,r1            ; r8,align
13 NSR:8026601C|                      and     r12,r12,r14
14 NSR:80266020|                      cmp     r12,#0x0         ; r12,#0
15 NSR:80266024|                      cpy     r4,r2            ; r4,flags
16 NSR:80266028|                      cpy     r9,r3            ; r9,start
 
먼저 07~09번째 줄 코드를 보겠습니다.
07 NSR:80266004|                      cpy     r14,r13
08 NSR:80266008|                      bic     r12,r14,#0x1FC0   ; r12,r14,#8128
09 NSR:8026600C|                      bic     r12,r12,#0x3F    ; r12,r12,#63
 
스택 주소를 통해 프로세스 스택 최상단 주소에 접근하는 동작입니다. 
위 어셈블리 코드 연산 결과 r12는 스택 최상단 주소를 저장하게 됩니다.
 
다음 10번째 줄 코드입니다.
10 NSR:80266010|                      ldr     r14,[r12,#0x4]
 
스택 최상단 주소에 있는 struct thread_info 구조체 preempt_count 필드를 r14에 로딩합니다.
만약 r12가 0x800c0000이면 0x800C0004주소에 있는 preempt_count 필드의 0x00010002를 r14에 저장합니다. 
  (struct thread_info *) [-] (struct thread_info*)0x800c0000  
    (long unsigned int) [D:0x800C0000] flags = 0x0,
    (int) [D:0x800C0004] preempt_count = 0x00010002,
    (mm_segment_t) [D:0x800C0008] addr_limit = 0x0,
 
핵심 코드를 볼 차례입니다.
11 NSR:80266014|                      ldr     r12,0x80266104
12 NSR:80266018|                      cpy     r8,r1            ; r8,align
13 NSR:8026601C|                      and     r12,r12,r14
14 NSR:80266020|                      cmp     r12,#0x0         ; r12,#0
 
[11 행]: 0x80266104 주소에 있는 값을 r12에 저장합니다.
 
0x80266104 주소엔 0x001FFF00가 있습니다.  
_____address|________0________4________8________C 
NSD:80266100| E7F001F2>001FFF00 00693EE0 809E02AC  
 
(HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK) 의 정체가 0x001FFF00입니다.
 
[13 행]: r12와 r14 레지스터와 AND 비트 연산을 수행합니다.
이를 쉽게 표현하면 다음과 같습니다.
(struct thread_info 구조체 preempt_count 필드) & (HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK)
 
어셈블리 코드 분석으로 다음 내용을 알게 됐습니다.
(HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK) = 0x001FFF00
 
HARDIRQ_MASK, SOFTIRQ_OFFSET, NMI_MASK 플래그를 어셈블리 코드로 계산해보기
 
어셈블리 코드 분석으로 (HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK)의 정체가 0x001FFF00이란 사실을 알게 됐습니다.
이번엔 각각 플래그 값을 알아볼까요?
 
이번엔 tracing_generic_entry_update() 함수를 어셈블리 코드로 분석해 보겠습니다.
1 void
2 tracing_generic_entry_update(struct trace_entry *entry, unsigned long flags,
3       int pc)
4 {
5  struct task_struct *tsk = current;
7  entry->preempt_count = pc & 0xff;
8  entry->pid = (tsk) ? tsk->pid : 0;
9  entry->flags =
10 #ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT
11 (irqs_disabled_flags(flags) ? TRACE_FLAG_IRQS_OFF : 0) |
12 #else
13 TRACE_FLAG_IRQS_NOSUPPORT |
14 #endif
15 ((pc & NMI_MASK    ) ? TRACE_FLAG_NMI     : 0) |
16 ((pc & HARDIRQ_MASK) ? TRACE_FLAG_HARDIRQ : 0) |
17 ((pc & SOFTIRQ_OFFSET) ? TRACE_FLAG_SOFTIRQ : 0) |
 
위 코드 15~17번째 줄을 어셈블리 코드로 분석하는 것입니다.
 
tracing_generic_entry_update() 함수를 어셈블리 코드로 보면 다음과 같습니다. 
01 NSR:801E3B50|tracing_generic_entry_update:  cpy     r12,r13
02 NSR:801E3B54|                               push    {r4-r5,r11-r12,r14,pc}
03 NSR:801E3B58|                               sub     r11,r12,#0x4     ; r11,r12,#4
04 NSR:801E3B5C|                               cpy     r12,r13
05 NSR:801E3B60|                               bic     r3,r12,#0x1FC0   ; r3,r12,#8128
06 NSR:801E3B64|                               bic     r3,r3,#0x3F      ; r3,r3,#63
07 NSR:801E3B68|                               ldr     r3,[r3,#0x0C]
08 NSR:801E3B6C|                               bic     r12,r12,#0x1FC0   ; r12,r12,#8128
09 NSR:801E3B70|                               strb    r2,[r0,#0x3]     ; pc,[r0,#3]
10 NSR:801E3B74|                               cmp     r3,#0x0          ; tsk,#0
11 NSR:801E3B78|                               ldrne   r3,[r3,#0x3A8]   ; tsk,[r3,#936]
12 NSR:801E3B7C|                               bic     r12,r12,#0x3F    ; r12,r12,#63
13 NSR:801E3B80|                               tst     r2,#0x100000     ; pc,#1048576 /* <<-- NMI_MASK */
14 NSR:801E3B84|                               ldr     r12,[r12]
15 NSR:801E3B88|                               moveq   r5,#0x0          ; r5,#0
16 NSR:801E3B8C|                               movne   r5,#0x40         ; r5,#64
17 NSR:801E3B90|                               tst     r2,#0xF0000      ; pc,#983040 /* <<-- HARDIRQ_MASK */
18 NSR:801E3B94|                               moveq   r4,#0x0          ; r4,#0
19 NSR:801E3B98|                               movne   r4,#0x8          ; r4,#8
20 NSR:801E3B9C|                               tst     r2,#0x100        ; pc,#256  /* <<-- SOFTIRQ_OFFSET */
21 NSR:801E3BA0|                               cpy     r14,r13
22 NSR:801E3BA4|                               moveq   r14,#0x0         ; r14,#0
 
HARDIRQ_MASK, SOFTIRQ_OFFSET, NMI_MASK 플래그의 정체는 다음과 같습니다.
NMI_MASK : 0x100000
HARDIRQ_MASK :  0xF0000
SOFTIRQ_OFFSET :  0x100
 
 
"이 포스팅이 유익하다고 생각되시면 댓글로 응원해주시면 감사하겠습니다.  
혹시 글을 읽고 궁금점이 있으면 댓글로 질문 남겨주세요. 상세한 답글 올려 드리겠습니다!"
 
 
// 수정 내역
SOFTIRQ_OFFSET를 SOFTIRQ_MASK로 잘못 기입했다. 
수정 완료, 02/06/2020
smpboot(smp thread) 스레드 사용 배경
 
최근에 사용되는 대부분 시스템은 멀티 프로세서 기반으로 실행됩니다. 예를 들어 인터넷이나 TV 광고에서 "최신 인텔 멀티 프로세서 코어가 적용된 제품"이란 이야기가 들리죠. 일반 대중이 하나 이상 쓰고 있는 휴대폰도 멀티 프로세서 시스템 기반입니다. 휴대폰에는 Arm 프로세서가 탑재됐는데, CPU 코어의 갯수가 8개 이상 구성돼 있죠.
 
리눅스 커널에서도 이런 멀티 프로세서 구조에서 이를 소프트웨어로 처리하는 드라이버와 같은 존재가 있는데요. 이를 smpboot 혹은 smp 핫 플러그 스레드라고 합니다.
 
smpboot(smp thread) 스레드 확인하기
 
그렇다면 smpboot로 등록한 커널 프로세스는 어떻게 확인할 수 있을까요? 리눅스 터미널에서 'ps -ely | grep smpboot' 명령어를 입력하면 smpboot로 등록한 프로세스 목록이 확인됩니다.
 
root@raspberrypi:/home/pi# ps -ely | grep smpboot
S     0    12     2  0  80   0     0     0 smpboot_th        00:00:00 ksoftirqd/0
S     0    14     2  0 -40   -     0     0 smpboot_th        00:00:00 migration/0
S     0    15     2  0  80   0     0     0 smpboot_th        00:00:00 cpuhp/0
S     0    16     2  0  80   0     0     0 smpboot_th        00:00:00 cpuhp/1
S     0    17     2  0 -40   -     0     0 smpboot_th        00:00:00 migration/1
S     0    18     2  0  80   0     0     0 smpboot_th        00:00:00 ksoftirqd/1
S     0    21     2  0  80   0     0     0 smpboot_th        00:00:00 cpuhp/2
S     0    22     2  0 -40   -     0     0 smpboot_th        00:00:00 migration/2
S     0    23     2  0  80   0     0     0 smpboot_th        00:00:00 ksoftirqd/2
S     0    26     2  0  80   0     0     0 smpboot_th        00:00:00 cpuhp/3
S     0    27     2  0 -40   -     0     0 smpboot_th        00:00:00 migration/3
S     0    28     2  0  80   0     0     0 smpboot_th        00:00:00 ksoftirqd/3
 
다른 프로세스 이름과 비교하면 smpboot 프로세스의 특징이 보이는데요. 가장 큰 특징은 다음과 같습니다.
 
   * '프로세스 이름'/CPU 코어 번호
 
smpboot 프로세스는 자신에게 지정된 CPU 코어에서만 실행됩니다. 그래서 per-cpu 타입 스레드라고 부릅니다. 참고로 per-cpu 타입 변수는 특정 CPU 코어에서만 엑세스됩니다. 1번 째 CPU 코어에서 per-cpu  runqueues 라는 변수에 접근하면 runqueues 변수의 1번째 per-cpu 주소에 접근합니다. 
 
smpboot(smp thread) 스레드의 동작 방식
 
"ksoftirqd/[0~4]" 혹은 "migration/[0~4]"와 와 같은 per-cpu 타입 쓰레드는 smp 핫플러그 쓰레드로 등록해서 실행합니다. 여기서 [0~4]는 CPU 코어 번호입니다. 
 
smpboot 스레드의 동작 원리를 설명하기 위해 간단한 예를 들겠습니다. 라즈베리 파이에서 음악이나 동영상을 재생하지 않고 가만히 있으면 CPU 코어는 한 개만 실행합니다. 대신 다른 CPU 코어는 오프라인 모드에 진입하죠. 다 소모 전력을 최소화하기 위한 메커니즘입니다.
 
간단히 설명을 드리면, 리눅스 커널에선 시스템이 일을 안 할 때는 여러 개의 CPU가 실행할 필요가 없습니다. 예를 들어 라즈베리안에서 음악이나 동영상을 재생 안 하고 아무 프로그램도 실행을 안 한 상태로 두면 CPU 하나만 실행하거든요. 
 
그래서 시스템 부하에 따라 CPU를 끄고 키는 동작(오프 라인 모드 진입, 온라인 모드 진입)을 하는데 이때 smp 핫플러그 쓰레드가 동작합니다. 
 
예를 들어 ksoftirqd 스레드는 각 CPU마다 생성된 프로세스입니다. 예를 들면 "ksoftirqd/2" 쓰레드는 CPU2에서만 일을 합니다. 시스템에 부하가 적으면 CPU2는 오프 라인 모드로 진입합니다. 유저가 아무런 동작을 하지 않아 실행할 프로세스가 없는데 구지 CPU2를 실행시킬 필요가 없겠죠. 이처럼 소프트웨어 매커니즘이 동작해 CPU2가 꺼지는 동작을 유식하게 CPU Hot-plugout이라고 합니다. 
 
만약 "ksoftirqd/2"에서 더 처리해야 할 Soft IRQ 서비스가 있는데 CPU2가 Hotplug-out될 상황이면 이 Soft IRQ 서비스를 "ksoftirqd/3”와 같이 깨어 있는 다른 ksoftirqd 쓰레드가 실행하게 처리합니다. 
 
smpboot (smp thread)스레드 핸들러 코드 리뷰 
 
smpboot 핫 플로그 스레드로 등록한 프로세스가 어떻게 동작하는지를 파악하려면 smpboot_thread_fn() 함수를 분석해야 합니다. 다음은 smpboot_thread_fn() 함수의 구현부입니다.
 
01 static int smpboot_thread_fn(void *data)
02 {
03 struct smpboot_thread_data *td = data;
04 struct smp_hotplug_thread *ht = td->ht;
05
06 while (1) {
07 set_current_state(TASK_INTERRUPTIBLE);
08 preempt_disable();
...
09 /* Check for state change setup */
10 switch (td->status) {
11 case HP_THREAD_NONE:
12 __set_current_state(TASK_RUNNING);
13 preempt_enable();
14 if (ht->setup)
15 ht->setup(td->cpu);
16 td->status = HP_THREAD_ACTIVE;
17 continue;
18
19 case HP_THREAD_PARKED:
20 __set_current_state(TASK_RUNNING);
21 preempt_enable();
22 if (ht->unpark)
23 ht->unpark(td->cpu);
24 td->status = HP_THREAD_ACTIVE;
25 continue;
26 }
27
28 if (!ht->thread_should_run(td->cpu)) {
29 preempt_enable_no_resched();
30 schedule();
31 } else {
32 __set_current_state(TASK_RUNNING);
33 preempt_enable();
34 ht->thread_fn(td->cpu);
35 }
36 }
37 }
 
함수에서 예외 처리 루틴을 제외하고 핵심 코드를 모아서 분석하겠습니다.
 
먼저 07번째 줄을 보겠습니다. 
 
07 set_current_state(TASK_INTERRUPTIBLE);
 
프로세스의 상태를 TASK_INTERRUPTIBLE로 변경하면서 슬립에 진입합니다. 
 
10~26번째 줄은 핫 플러그인 스레드의 상태 머신에 따라 세부 처리를 하는 루틴입니다. 
 
28~30번째 줄은 핫 플로그 스레드가 실행한 조건이 아닐 때 슬립에 진입하는 코드입니다.
 
28 if (!ht->thread_should_run(td->cpu)) {
29 preempt_enable_no_resched();
30 schedule();
 
슬립에 진입하는 코드는 30번째 줄입니다.
 
마지막으로 31~35번째 줄을 보겠습니다.
 
31 } else {
32 __set_current_state(TASK_RUNNING);
33 preempt_enable();
34 ht->thread_fn(td->cpu);
35 }
 
핫 플러그인 스레드로 등록함 함수가 실행되는 루틴인데요. 34번째 줄의 'ht->thread_fn' 구문에서 
핫 플러그인 스레드의 핸들러 함수가 호출되니다.
 
예를 들어 ksoftirqd/2 프로세스인 경우 34번째 줄에서 다음과 같은 함수 흐름으로 run_ksoftirqd() 함수가 호출됩니다.
 
[  234.06782 01-01 00:03:56.390 CPU2]  [<ffffffff820bbdac>] tasklet_action+0x6c/0xe0
[  234.06791 01-01 00:03:56.390 CPU2]  [<ffffffff820bb870>] __do_softirq+0x110/0x2d0
[  234.06800 01-01 00:03:56.390 CPU2]  [<ffffffff820bba5d>] run_ksoftirqd+0x2d/0x60
[  234.06809 01-01 00:03:56.390 CPU2]  [<ffffffff820e34d4>] smpboot_thread_fn+0x1d4/0x2e0
[  234.06827 01-01 00:03:56.390 CPU2]  [<ffffffff820db2bb>] kthread+0xeb/0xf0
[  234.06836 01-01 00:03:56.390 CPU2]  [<ffffffff820db1d0>] ? kthread_create_on_node+0x140/0x140
[  234.06845 01-01 00:03:56.390 CPU2]  [<ffffffff82a2345c>] ret_from_fork+0x7c/0xb0
[  234.06854 01-01 00:03:56.390 CPU2]  [<ffffffff820db1d0>] ? kthread_create_on_node+0x140/0x140

+ Recent posts