가끔 아래와 같이 printk를 추가해서 로그를 보려고 빌드를 하면, 컴파일러가 아래와 같이 에러를 토해내고 컴파일이 중지되는 경우가 있다.
printk("=====!!!!!current task[%s]=======\n", current->comm);

/android/kernel/arch/arm/mach-tegra/timer.c:111:58: error: dereferencing pointer to incomplete type
make[3]: *** [arch/arm/mach-tegra/timer.o] Error 1
make[2]: *** [arch/arm/mach-tegra] Error 2
make[2]: *** Waiting for unfinished jobs....
  CHK     kernel/config_data.h
make[1]: *** [sub-make] Error 2

printk("=====!!!!!current task[%s]=======\n", current->comm);

이 때는 아래와 같은 해더 파일을 추가하면 된다.
#include <linux/sched.h>

printk("[callstack %s,%d] task[%s]========= \n", __func__,__LINE__, current->comm);

Overview
When debugging ramdump with stability issues, I have been spending most of time casting various data structure as belows.
v.v %s (struct task_struct*)0xC1917FCC
For this matter, I made several macros to minimize the debugging time.
The definition of the macro is below.
sYmbol.NEW.MACRO offsetof(type,member) ((int)(&((type*)0)->member))
sYmbol.NEW.MACRO container_of(ptr,type,member) ((type *)((char *)(ptr)-offsetof(type,member)))
 
offsetof(type,member)
When analyzing assembly code, I have to know the offset of certain element, assembly code is generated based upon offset value of data structure.
[1]: offset: struct kgsl_context.device is calculated as 0x1C
v.v % offsetof(struct kgsl_context,device)
  (int) offsetof(struct kgsl_context,device) = 28 = 0x1C = '....'
[assemble code]
NSR:C0500F68|E594501C                                  ldr     r5,[r4,#0x1C]
 
[2]: offset: struct adreno_context.rb
v.v % offsetof(struct adreno_context,rb)
  (int) offsetof(struct adreno_context,rb) = 768 = 0x0300 = '....'
[assemble code]
NSR:C0501054|E5943300                                  ldr     r3,[r4,#0x300]
 
Code segment for this example
449void adreno_drawctxt_detach(struct kgsl_context *context)
450{
451 struct kgsl_device *device;
452 struct adreno_device *adreno_dev;
453 struct adreno_context *drawctxt;
454 struct adreno_ringbuffer *rb;
455 int ret, count, i;
456 struct kgsl_cmdbatch *list[ADRENO_CONTEXT_CMDQUEUE_SIZE];
//snip
460
461 device = context->device;  // <<--[1]
462 adreno_dev = ADRENO_DEVICE(device);
463 drawctxt = ADRENO_CONTEXT(context);
 464 rb = drawctxt->rb; // <<--[2]
465
 
container_of(ptr,type,member)
(Example1)
In order to find out the total element of "struct task_struct" as per "struct task_struct.tasks.next",
I have to manipulate the T32 many times. For this, let me introduce the container_of(ptr,type,member) macro
v.v %h container_of(0xEE458238,struct task_struct,tasks)
  container_of(0xEE458238,struct task_struct,tasks) = 0xEE458000 -> (
    state = 0x1,
    stack = 0xEE44A000,
//snip
    cputime_expires = (utime = 0x0, stime = 0x0, sum_exec_runtime = 0x0),
    cpu_timers = ((next = 0xEE458380, prev = 0xEE458380), (next = 0xEE458388, pr
    real_cred = 0xE2494D00,
    cred = 0xE2494D00,
    comm = "init",
 
(where)
  [D:0xC16141E8] init_task = (
    [D:0xC16141E8] state = 0x0,
    [D:0xC16141EC] stack = 0xC1600000,
//snip
    [D:0xC161441C] rcu_blocked_node = 0x0,
    [D:0xC1614420] tasks = (
      [D:0xC1614420] next = 0xEE458238,  // <<--
      [D:0xC1614424] prev = 0xC288B938),
    [D:0xC1614428] pushable_tasks = ([D:0xC1614428] prio = 0x8C, [D:0xC161442C] prio_list = ([D:0xC161442C] next = 0x
//snip
    [D:0xC1614584] cred = 0xC1619D18,
    [D:0xC1614588] comm = "swapper/0",
    [D:0xC1614598] link_count = 0x0,
    [D:0xC161459C] total_link_count = 0x0,
 
v.v %h %s container_of(0xDB63FE68,struct mutex,wait_list)
  container_of(0xDB63FE68,struct mutex,wait_list) = 0xDB63FE54 -> (
    count = (counter = 0xC0C0A56C),
    wait_lock = (rlock = (raw_lock = (slock = 0xC16A057C, tickets = (owner = 0x057C, next = 0xC16A)), magic = 0xC0C08C70, o
    wait_list = (next = 0xDAC43DB8, prev = 0xC15DBC40),
    owner = 0xE0396E00,
    name = 0xDB63FE68 -> ".=..@.].",
    magic = 0xFFFFFFFF)
 
  container_of(0xDAC43DB8,struct mutex,wait_list) = 0xDAC43DA4 -> (
    count = (counter = 0xC0C0A56C),
    wait_lock = (rlock = (raw_lock = (slock = 0xC16A057C, tickets = (owner = 0x0
    wait_list = (next = 0xDAC55E68, prev = 0xDB63FE68),
    owner = 0xDD5C5D80,
    name = 0xDAC43DB8 -> "h^..h.c..]\..=..``v.,.].",
    magic = 0xDB766060)
 
(where)  binder_main_lock = (
    count = (counter = 0xFFFFFFFF),
    wait_lock = (rlock = (raw_lock = (slock = 0xF1CCF1CC, tickets = (owner = 0xF
    wait_list = (
      next = 0xDB63FE68 // <<--
        next = 0xDAC43DB8 // <<--
          next = 0xDAC55E68 -> (
            next = 0xC15DBC40 -> (
              next = 0xDB63FE68,
 
container_of_double_vcast(ptr,type,member,new_member,cast_type)
Definition
sYmbol.NEW.MACRO container_double_vcast(ptr,type,member,new_member,cast_type) ((cast_type *)(*(type *)((char *)(ptr)-offsetof(type,member)+offsetof(type,new_member))))
(Example1: PHONEMODEL-1958)
container_of_double_vcast(0xDAC43DB8,struct mutex,wait_list,owner,struct task_struct)
  container_of_double_vcast(0xDAC43DB8,struct mutex,wait_list,owner,struct task_struct) = 0xDD5C5D80 -> (
    state = 0x2,
    stack = 0xDAC42000,
    usage = (counter = 0x2),
    flags = 0x00400040,
    ptrace = 0x0,
    wake_entry = (next = 0x0),
    on_cpu = 0x0,
    on_rq = 0x0,
    prio = 0x78,
    static_prio = 0x78,
 
(where)
  binder_main_lock = (  // <<-- type: struct mutex
    count = (counter = 0xFFFFFFFF),
    wait_lock = (rlock = (raw_lock = (slock = 0xF1CCF1CC, tickets = (owner = 0xF
    wait_list // <<-- member
      next = 0xDB63FE68 
        next = 0xDAC43DB8 // <<--  ptr
          next = 0xDAC55E68 -> (
            next = 0xC15DBC40 -> (
              next = 0xDB63FE68,
              prev = 0xDAC55E68),
            prev = 0xDAC43DB8),
          prev = 0xDB63FE68),
        prev = 0xC15DBC40),
      prev = 0xDAC55E68),
    owner = 0xD9E6A100,  // <<-- new_member, cast_type: struct task_struct
    name = 0x0,
    magic = 0xC15DBC2C)
 
container_down_vcast(ptr,type,member,cast)
Definition
sYmbol.NEW.MACRO container_down_vcast(ptr,type,member,cast) ((cast *)(*(type *)((char *)(ptr)+offsetof(type,member))))
 
v.v % container_down_vcast(0xC15DBC2C,struct mutex,owner,struct task_struct)
v.v % container_down_vcast(0xC15DBC2C,struct mutex,owner,struct task_struct)
  container_down_vcast(0xC15DBC2C,struct mutex,owner,struct task_struct) = 0xD9E6A100 -> (
    state = 1,
    stack = 0xD9ECC000,
    usage = (counter = 2),
//snip
    min_flt = 304,
    maj_flt = 0,
    cputime_expires = (utime = 0, stime = 0, sum_exec_runtime = 0),
    cpu_timers = ((next = 0xD9E6A448, prev = 0xD9E6A448), (next = 0xD9E6A450, prev = 0xD9E6A450), (next = 0xD9E6A45
    real_cred = 0xDEEF1900,
    cred = 0xDEEF1900,
    comm = "Binder_4",
    link_count = 0,
 
(where)
  [D:0xC15DBC2C] binder_main_lock = (  // <<-- ptr: 0xC15DBC2C, type: struct mutex
    [D:0xC15DBC2C] count = ([D:0xC15DBC2C] counter = -1),
    [D:0xC15DBC30] wait_lock = ([D:0xC15DBC30] rlock = ([D:0xC15DBC30] raw_lock
    [D:0xC15DBC40] wait_list = ([D:0xC15DBC40] next = 0xDB63FE68, [D:0xC15DBC44]
    [D:0xC15DBC48] owner = 0xD9E6A100,  // <<--member, cast: struct task_struct
    [D:0xC15DBC4C] name = 0x0,
    [D:0xC15DBC50] magic = 0xC15DBC2C)
 
threadoffset(ptr)
Definition
sYmbol.NEW.MACRO threadoffset(ptr) ((ptr ~0x1fff))
sYmbol.NEW.MACRO thread_of(ptr) ((struct thread_info *)((int *)threadoffset(ptr)))
When kernel crash occurs, the kernel dumps below logs
[ 1894.897301] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP ARM
[ 1894.897314] Modules linked in: texfat(PO)
[ 1894.897333] CPU: 2 PID: 4324 Comm: Binder_4 Tainted: P        W  O 3.10.49-g184f2e4 #1
[ 1894.897347] task: d9e6a100 ti: d9ecc000 task.ti: d9ecc000
[ 1894.897362] PC is at __list_add+0x9c/0xd0
[ 1894.897376] LR is at __list_add+0x58/0xd0
[ 1894.897390] pc : [<c032e9e8>]    lr : [<c032e9a4>]    psr: 000f0093
[ 1894.897390] sp : d9ecdd90  ip : 00000000  fp : dc08da00
[ 1894.897409] r10: d9ecc000  r9 : c16a39ec  r8 : d9e6a100
[ 1894.897422] r7 : 00000000  r6 : d9ecddb8  r5 : c15dbc40  r4 : c0004860
[ 1894.897435] r3 : 00000000  r2 : 00001201  r1 : c16a28a8  r0 : 00000000
[ 1894.897450] Flags: nzcv  IRQs off  FIQs on  Mode SVC_32  ISA ARM  Segment user
[ 1894.897464] Control: 10c0383d  Table: 9e06006a  DAC: 00000015
[ 1894.897476] Process Binder_4 (pid: 4324, stack limit = 0xd9ecc238)
[ 1894.897489] Stack: (0xd9ecdd90 to 0xd9ece000)
[ 1894.897504] dd80:                                     00000000 c0004860 c15dbc40 c15dbc2c
[ 1894.897522] dda0: 600f0013 d9ecc030 c15dbc30 c0c08bcc d9ecdddc c15dbc40 d9ecddb8 d9ecddb8
[ 1894.897540] ddc0: 11111111 d9ecddb8 600f0013 c15dbc2c de364e00 b786a844 d9ecc038 c1660598
[ 1894.897557] dde0: 00000000 d9ecc000 dc08da00 c0c08e70 ddf0f000 c072d020 ded48000 c018b544
[ 1894.897574] de00: 00000000 800f0193 00000028 dd041600 c8002ab0 b781ee04 00000000 db0d1c00
[ 1894.897591] de20: c8002ad0 00000000 00000000 b786a840 b786a940 ddf0f01c d9ecdee0 d9ecc000
 
With any stack address, the (struct thread_info*) can be casted with the single command.
v.v %all thread_of(0xd9ecdd90)
  (struct thread_info *) thread_of(0xd9ecdd90) = 0xD9ECC000 = __bss_stop+0x1855F
    (long unsigned int) flags = 0 = 0x0 = '....',
    (int) preempt_count = 3 = 0x3 = '....',
    (mm_segment_t) addr_limit = 3204448256 = 0xBF000000 = '....',
    (struct task_struct *) task = 0xD9E6A100 = __bss_stop+0x184FD964 -> ((long i
    (struct exec_domain *) exec_domain = 0xC1579CDC = default_exec_domain -> ((c
    (__u32) cpu = 2 = 0x2 = '....',
    (__u32) cpu_domain = 21 = 0x15 = '....',
    (struct cpu_context_save) cpu_context = ((__u32) r4 = 3740230976 = 0xDEEF654
    (__u32) syscall = 0 = 0x0 = '....',
    (__u8 [16]) used_cp = "",
    (long unsigned int [2]) tp_value = ([0] = 3001371000 = 0xB2E54978 = '..Ix',
    (union fp_state) fpstate = ((struct fp_hard_struct) hard = ((unsigned int [3
    (union vfp_state) vfpstate = ((struct vfp_hard_struct) hard = ((__u64 [32])
    (struct restart_block) restart_block = ((long int (*)()) fn = 0xC0131A64 = d
 
v.v %all thread_of(0xd9ecddc0)
  (struct thread_info *) thread_of(0xd9ecddc0) = 0xD9ECC000 = __bss_stop+0x1855F
    (long unsigned int) flags = 0 = 0x0 = '....',
    (int) preempt_count = 3 = 0x3 = '....',
    (mm_segment_t) addr_limit = 3204448256 = 0xBF000000 = '....',
    (struct task_struct *) task = 0xD9E6A100 = __bss_stop+0x184FD964 -> ((long i
    (struct exec_domain *) exec_domain = 0xC1579CDC = default_exec_domain -> ((c
    (__u32) cpu = 2 = 0x2 = '....',
    (__u32) cpu_domain = 21 = 0x15 = '....',
    (struct cpu_context_save) cpu_context = ((__u32) r4 = 3740230976 = 0xDEEF654
    (__u32) syscall = 0 = 0x0 = '....',
    (__u8 [16]) used_cp = "",
    (long unsigned int [2]) tp_value = ([0] = 3001371000 = 0xB2E54978 = '..Ix',
    (union fp_state) fpstate = ((struct fp_hard_struct) hard = ((unsigned int [3
    (union vfp_state) vfpstate = ((struct vfp_hard_struct) hard = ((__u64 [32])
    (struct restart_block) restart_block = ((long int (*)()) fn = 0xC0131A64 = d
 
;****************************************************************************
;**           WalkThruStackDump_QMC_32BitARM.cmm                                     
;**           This script is designed to restore a call stack per process in ARM32 QMC Platform.           
;**                        
;**
;**       
;** when        who      what, where, why
;** --------------   ------------     ------------------------------------------------------
;** 01/30/2016   austin.kim     First version: This script is based upon TraceStackDumpWithCallstack_64BitARM
;** 11/26/2016   austin.kim     Callstack hit/File Restore function
;****************************************************************************
 
;****************************************************************************
// The way to use this cmm
// cd [ramdump directory]
// do WalkThruStackDump_32BitARM.cmm  [task descriptor address]   
// do WalkThruStackDump_32BitARM.cmm  0xC0188000   
// do WalkThruStackDump_32BitARM.cmm  0xC0188000  D:\kernel_panic\11_Nov_2016\1103_oom_starvation_SOMETARGET-457 
 
Area.Create IO 80. 100.
Area.Select IO
Area IO
 
print
print
print "Start execution of dumptrace of a process in 32-bit process, austin.kim@lge.com, 01/30/2016"
 
ENTRY &TASK_ADDRESS &DUMP_DIRECTORY
 
// 콜 스택
v.f
 
// 파일 덤프 모드 
&FILE_DUMP_MODE=0
 
if "&DUMP_DIRECTORY"==""
(
   print "Manual Mode"
)
 
else
(
   print "File Dump Mode"
   &FILE_DUMP_MODE=0x1
)
 
// **********주요 변수 선언, [START] *************
// Dump File Path/Name
&DUMP_OUT_FILENAME=0x0
 
// Callstack을 잡은 횟수 기록
&CALLSTACK_HIT_TIMES=0
 
// &CURRENT_SP_ADDRESS 주소가 이 값이 되면 프로그램 종료. 추후 수정 요망
&STACK_SCAN_BOUNDARY_ADDRESS=0x0
 
&STACK_BOTTOM_ADDRESS=0
 
// 구동중인 CPU Number
&CPU_ON=0x0
// 스택이 자라나는 시작 주소임, 0x4000 만큼 오프셋
&STACK_STARTING_ADDRESS=0
 
// 현재 검색 중인 스택 주소
&CURRENT_SP_ADDRESS=0
 
// 임시로 선택된 스택 주소
&CANDIDATE_SP_ADDRESS=0
 
// FP 주소를 담고 있음
&CURRENT_FP_ADDRESS=0
 
// 현재 스택 덤프 주소
&CURRENT_SP_DUMP_ADDRESS=0
 
// 현재 스택 상단에 있는 Text 주소를 확인하기 위한 버퍼
&BUFFER_TEXT_ADDRESS_WITH_SP=0
 
// 현재 스택 상단에 있는 Text 주소를 가리키기 위한  버퍼
&ADDRESS_TEXT_ADDRESS_WITH_SP=0
 
// 현재 스택 덤프 주소
&BOUND_CONDITION=0
 
// 32 비트 프로세스 주소 처리 단위
&NEXT_STACK_ADDRESS_OFFSET=0x4
 
 
// due to MSM8909 memory section attribute
//     P:C00081C0--C0A03B13|\\vmlinux\.text                        |R-X|L-  |
//&KERNEL_TEXT_START_ADDR=0xC0100000
&KERNEL_TEXT_START_ADDR=0xC0008000
//&KERNEL_TEXT_END_ADDR=0xC0D00000
&KERNEL_TEXT_END_ADDR=0xC0F00000
y.create.var __curr_stack_address 0 unsigned int
y.create.done
 
y.create.var __curr_stack_contents 0 unsigned int
y.create.done
 
y.create.var __stack_program_counter_addr_buff 0 unsigned int
y.create.done
 
// **********주요 변수 선언, [END] *************
// 스택 초기 주소를 설정함
GOSUB configureStackAddress
 
if &FILE_DUMP_MODE==0x1
(
    // 아웃풋 파일 이름 설정
    gosub configureOutputFileName
 
     // RunQueue Dump
//    gosub dumpRunqueueDebugInformation   
    
    // 프린터 오픈
    printer.FILE &DUMP_OUT_FILENAME
    printer.OPEN &DUMP_OUT_FILENAME
)
 
// 프로세스 정보를 찍어줌
GOSUB dumpProcessInformation
 
&CALLSTACK_HIT_TIMES=0x0
while &CURRENT_SP_ADDRESS<&STACK_STARTING_ADDRESS
(
   // 스택에 있는 덤프 주소를 가져옴
   symbol.modify.address __curr_stack_address V.VALUE(&CURRENT_SP_ADDRESS)
   &CURR_CONTENTS_OF_SP_ADDR=data.long(__curr_stack_address)
   
   symbol.modify.address __curr_stack_contents V.VALUE(&CURR_CONTENTS_OF_SP_ADDR)
   
   gosub checkValidFPAddressWithSymbol &fpAddrDumpBuff
   entry &real_fp_status
   
   if &real_fp_status==0x1
   (
    GOSUB dumpProcessInformation
    GOSUB configureRegisterForCallstack
   )
   
   &CURRENT_SP_ADDRESS=&CURRENT_SP_ADDRESS-&NEXT_STACK_ADDRESS_OFFSET
 
   if &FILE_DUMP_MODE==0x1
   (
    if &CURRENT_SP_ADDRESS==&STACK_SCAN_BOUNDARY_ADDRESS
    (
    printer.CLOSE
    enddo
    )  
   )    
)  // ; end of while &CURRENT_SP_ADDRESS<&STACK_STARTING_ADDRESS  
 
enddo
 
// ***********************************************
// 아웃풋 파일 이름 설정
//Function: configureOutputFileName
//Parameter: NONE
// ***********************************************
configureOutputFileName:
   LOCAL &task_comm &task_temp &cpu_num &pid_temp &pid_dec &filename
   
    // 프로세스 이름
   &task_temp=VAR.STRING( ((struct task_struct *)&TASK_ADDRESS).comm)
   &task_comm=string.mid("&task_temp", 0., 3.) 
   
   &pid_temp=VAR.VALUE( ((struct task_struct*)&TASK_ADDRESS).pid)
   &pid_dec=FORMAT.DECIMAL(0., &pid_temp)
   
   &CPU_ON=V.VALUE( ((struct thread_info *)&STACK_BOTTOM_ADDRESS).cpu) 
 
   &filename="&TASK_ADDRESS"+"_"+"&task_comm"+"_"+"&pid_dec"+"_"+"&CPU_ON"+".c"
//   &filename="&TASK_ADDRESS"+"_"+"&pid_temp"+"_"+"&CPU_ON"+".txt"
   
   &DUMP_OUT_FILENAME="&DUMP_DIRECTORY"+"/&filename"
   
   RETURN
// End: configureOutputFileName
// ***********************************************
 
// ***********************************************
// Runqueue Dump
//Function: dumpRunqueueDebugInformation
//Parameter: NONE
// ***********************************************
dumpRunqueueDebugInformation:
   LOCAL &dump_runq_name
   
   &dump_runq_name="&DUMP_DIRECTORY"+"/demiGod_Runqueue.c"
   
   printer.FILE &dump_runq_name
   printer.OPEN &dump_runq_name
   
   wp.v.v %s %y %h (*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[0])))).curr (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[0])))).curr)).comm (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[0])))).curr)).pid v.v %s %y (*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[1])))).curr (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[1])))).curr)).comm (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[1])))).curr)).pid v.v %s %y (*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[2])))).curr (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[2])))).curr)).comm (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[2])))).curr)).pid v.v %s %y (*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[3])))).curr (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[3])))).curr)).comm (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[3])))).curr)).pid
   wp.v.v %s %y %h (*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[4])))).curr (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[4])))).curr)).comm (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[4])))).curr)).pid v.v %s %y (*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[5])))).curr (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[5])))).curr)).comm (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[5])))).curr)).pid v.v %s %y (*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[6])))).curr (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[6])))).curr)).comm (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[6])))).curr)).pid v.v %s %y (*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[7])))).curr (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[7])))).curr)).comm (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[7])))).curr)).pid
 
   wp.v.v %all (struct rq*)(((void*)&runqueues)+__per_cpu_offset[0]) (struct rq*)(((void*)&runqueues)+__per_cpu_offset[1]) (struct rq*)(((void*)&runqueues)+__per_cpu_offset[2]) (struct rq*)(((void*)&runqueues)+__per_cpu_offset[3])  (struct rq*)(((void*)&runqueues)+__per_cpu_offset[4]) (struct rq*)(((void*)&runqueues)+__per_cpu_offset[5]) (struct rq*)(((void*)&runqueues)+__per_cpu_offset[6]) (struct rq*)(((void*)&runqueues)+__per_cpu_offset[7])
   
   printer.CLOSE
   
   RETURN
// End: dumpRunqueueDebugInformation
// ***********************************************
 
 
 
// ***********************************************
// 스택 초기 주소를 설정함
//Function: configureStackAddress
//Parameter: NONE
// ***********************************************
configureStackAddress:
 
   &STACK_BOTTOM_ADDRESS=v.value( ((struct task_struct *)&TASK_ADDRESS).stack )
   &STACK_STARTING_ADDRESS=&STACK_BOTTOM_ADDRESS+0x2000
   
   // 이 값은 램덤프 관찰 후 설정한 값이다. 업데이트가 필요함
   &STACK_SCAN_BOUNDARY_ADDRESS=&STACK_BOTTOM_ADDRESS+0x500
   &CURRENT_SP_ADDRESS=&STACK_BOTTOM_ADDRESS+0x1ffc
   
   RETURN
// End: configureStackAddress
// ***********************************************
 
// ***********************************************
// 프로세스 정보를 찍어줌
//Function: dumpProcessInformation
//Parameter: NONE
// ***********************************************
dumpProcessInformation:
 
   wp.v.v %s ((struct task_struct *)&TASK_ADDRESS).comm
   wp.v.v %all ((struct task_struct *)&TASK_ADDRESS).pid
       
   RETURN
// End: dumpProcessInformation
// ***********************************************
 
 
// ***********************************************
// 레지스터를 설정하고 콜스택을 뿌려줌
//Function: configureRegisterForCallstack
//Parameter: NONE
// ***********************************************
configureRegisterForCallstack:
 
   r.s sp &CANDIDATE_SP_ADDRESS
   r.s pc &ADDRESS_TEXT_ADDRESS_WITH_SP
   
   &CALLSTACK_HIT_TIMES=&CALLSTACK_HIT_TIMES+1
   print "Callstack hit time: " format.udecimal(4,&CALLSTACK_HIT_TIMES)
   
   // dump callstacks in file
   if &FILE_DUMP_MODE==0x1
   (
    wp.v.f
    wp.r
    if &CALLSTACK_HIT_TIMES==0x60
    (
printer.CLOSE
enddo   
    )
   )
  
   RETURN
// End: configureRegisterForCallstack
// ***********************************************
 
// ***********************************************
// 현재 스택 덤프 주소가 Text 주소 공간 내에 있는 지
//
//확인하고 있으면 1을 리턴 아니면 0 
//Function: checkValidFPAddressWithSymbol
//Parameter: NONE
// ***********************************************
checkValidFPAddressWithSymbol:
 
   ENTRY &stackAddress
   LOCAL &stackWith4Offset &stackDump &cur_stack_content &retVal
   
   &retVal=0
   &stackWith4Offset=&CURRENT_SP_ADDRESS+0x4
   
   if &CURR_CONTENTS_OF_SP_ADDR==0x0
   (
    RETURN &retVal
   )
      // 현재 스택덤프 값이 Text 영역 내에 있는 지 확인
   if (&KERNEL_TEXT_START_ADDR<&CURR_CONTENTS_OF_SP_ADDR)&&(&CURR_CONTENTS_OF_SP_ADDR<&KERNEL_TEXT_END_ADDR)
   (
     &ADDRESS_TEXT_ADDRESS_WITH_SP=&CURR_CONTENTS_OF_SP_ADDR
      &CANDIDATE_SP_ADDRESS=&stackWith4Offset
 
&retVal=1
   )
      
   RETURN &retVal
// End: checkValidFPAddressWithSymbol
// ***********************************************
 
;****************************************************************************
;**           TraceStackDumpWithCallstack_64BitARM.cmm                                           
;**           This script is designed to restore a call stack per process in ARM64 bit process.           
;**                        
;**
;**       
;** when        who      what, where, why
;** --------------   ------------     ------------------------------------------------------
;** 01/30/2016   austin.kim     First version
;** 11/26/2016   austin.kim     Callstack hit/File Restore function
;****************************************************************************
 
;****************************************************************************
// The way to use this cmm
// cd [ramdump directory]
// do TraceStackDumpWithCallstack_64BitARM  [task descriptor address]   
// do TraceStackDumpWithCallstack_64BitARM  0xFFFFFFC011388000   
// do MTCallStackDump_cortexA9ARM.cmm   0xFFFFFFC011388000   D:\kernel_panic\11_Nov_2016\1103_oom_starvation_SOMETARGET-457  
 
Area.Create IO 80. 100.
Area.Select IO
Area IO
 
print
print
print "Start execution of dumptrace of a process in 64-bit process, austin.kim@lge.com, 01/30/2016"
 
ENTRY &TASK_ADDRESS &DUMP_DIRECTORY
 
// 콜 스택
v.f
 
// 파일 덤프 모드 
&FILE_DUMP_MODE=0
 
if "&DUMP_DIRECTORY"==""
(
   print "Manual Mode"
)
 
else
(
   print "File Dump Mode"
   &FILE_DUMP_MODE=0x1
)
 
// **********주요 변수 선언, [START] *************
// Dump File Path/Name
&DUMP_OUT_FILENAME=0x0
 
// Callstack을 잡은 횟수 기록
&CALLSTACK_HIT_TIMES=0
 
// &CURRENT_SP_ADDRESS 주소가 이 값이 되면 프로그램 종료. 추후 수정 요망
&STACK_SCAN_BOUNDARY_ADDRESS=0x0
 
&STACK_BOTTOM_ADDRESS=0
 
// 구동중인 CPU Number
&CPU_ON=0x0
 
// 스택이 자라나는 시작 주소임, 0x4000 만큼 오프셋
&STACK_STARTING_ADDRESS=0
 
// 현재 검색 중인 스택 주소
&CURRENT_SP_ADDRESS=0
 
// 임시로 선택된 스택 주소
&CANDIDATE_FP_ADDRESS=0
 
// FP 주소를 담고 있음
&CURRENT_FP_ADDRESS=0
 
// 현재 스택 덤프 주소
&CURRENT_SP_DUMP_ADDRESS=0
 
// 현재 스택 상단에 있는 Text 주소를 확인하기 위한 버퍼
&BUFFER_TEXT_ADDRESS_WITH_SP=0
 
// 현재 스택 상단에 있는 Text 주소를 가리키기 위한  버퍼
&ADDRESS_TEXT_ADDRESS_WITH_SP=0
 
// 현재 스택 덤프 주소
&BOUND_CONDITION=0
 
// 64 비트 프로세스 주소 처리 단위
&NEXT_STACK_ADDRESS_OFFSET=0x8
 
// This code will be throughly examined for many times.
&KERNEL_TEXT_START_ADDR=ADDRESS.OFFSET(sYmbol.SECADDRESS(\\vmlinux\.text))
&KERNEL_TEXT_END_ADDR=&KERNEL_TEXT_START_ADDR+0x1000000
 
//&KERNEL_TEXT_START_ADDR=0xFFFFFFC000080000
//&KERNEL_TEXT_END_ADDR=0xFFFFFFC000D2983F
 
// Implemented using "02322247_SKT_Crash" ramdump for RenderThread process 
//&TASK_ADDRESS=0xffffffc098b56200
 
v.f
 
y.create.var __curr_stack_address 0 unsigned long
y.create.done
 
y.create.var __curr_stack_contents 0 unsigned long
y.create.done
 
y.create.var __stack_program_counter_addr_buff 0 unsigned long
y.create.done
 
// **********주요 변수 선언, [END] *************
 
;ENTRY &STACK_ADDRESS &PROCESS_NAME
;ENTRY &TASK_ADDRESS //&PROCESS_NAME
 
// 스택 초기 주소를 설정함
GOSUB configureStackAddress
 
if &FILE_DUMP_MODE==0x1
(
    // 아웃풋 파일 이름 설정
    gosub configureOutputFileName
 
     // RunQueue Dump
    gosub dumpRunqueueDebugInformation   
    
    // 프린터 오픈
    printer.FILE &DUMP_OUT_FILENAME
    printer.OPEN &DUMP_OUT_FILENAME
)
 
// 프로세스 정보를 찍어줌
GOSUB dumpProcessInformation
 
&CALLSTACK_HIT_TIMES=0x0
while &CURRENT_SP_ADDRESS<&STACK_STARTING_ADDRESS
(
   // 스택에 있는 덤프 주소를 가져옴
   symbol.modify.address __curr_stack_address V.VALUE(&CURRENT_SP_ADDRESS)
   &CURR_CONTENTS_OF_SP_ADDR=data.quad(__curr_stack_address)
   
   symbol.modify.address __curr_stack_contents V.VALUE(&CURR_CONTENTS_OF_SP_ADDR)
   
   gosub checkValidFPAddressWithSymbol &fpAddrDumpBuff
   entry &real_fp_status
   
   if &real_fp_status==0x1
   (
    GOSUB configureRegisterForCallstack
   )
   
   &CURRENT_SP_ADDRESS=&CURRENT_SP_ADDRESS-0x8
   if &FILE_DUMP_MODE==0x1
   (
    if &CURRENT_SP_ADDRESS==&STACK_SCAN_BOUNDARY_ADDRESS
    (
    printer.CLOSE
    enddo
    )  
   )    
)  // ; end of while &CURRENT_SP_ADDRESS<&STACK_STARTING_ADDRESS  
 
enddo
 
// ***********************************************
// 아웃풋 파일 이름 설정
//Function: configureOutputFileName
//Parameter: NONE
// ***********************************************
configureOutputFileName:
   LOCAL &task_comm &task_temp &cpu_num &pid_temp &pid_dec &filename
   
    // 프로세스 이름
   &task_temp=VAR.STRING( ((struct task_struct *)&TASK_ADDRESS).comm)
   &task_comm=string.mid("&task_temp", 0., 3.) 
   
   &pid_temp=VAR.VALUE( ((struct task_struct*)&TASK_ADDRESS).pid)
   &pid_dec=FORMAT.DECIMAL(0., &pid_temp)
   
   &CPU_ON=V.VALUE( ((struct thread_info *)&STACK_BOTTOM_ADDRESS).cpu) 
   
   &filename="&TASK_ADDRESS"+"_"+"&task_comm"+"_"+"&pid_dec"+"_"+"&CPU_ON"+".c"
//   &filename="&TASK_ADDRESS"+"_"+"&pid_temp"+"_"+"&CPU_ON"+".txt"
   
   &DUMP_OUT_FILENAME="&DUMP_DIRECTORY"+"/&filename"
   
   RETURN
// End: configureOutputFileName
// ***********************************************
 
// ***********************************************
// Runqueue Dump
//Function: dumpRunqueueDebugInformation
//Parameter: NONE
// ***********************************************
dumpRunqueueDebugInformation:
   LOCAL &dump_runq_name
   
   &dump_runq_name="&DUMP_DIRECTORY"+"/demiGod_Runqueue.c"
   
   printer.FILE &dump_runq_name
   printer.OPEN &dump_runq_name
   
   wp.v.v %s %y %h (*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[0])))).curr (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[0])))).curr)).comm (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[0])))).curr)).pid v.v %s %y (*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[1])))).curr (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[1])))).curr)).comm (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[1])))).curr)).pid v.v %s %y (*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[2])))).curr (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[2])))).curr)).comm (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[2])))).curr)).pid v.v %s %y (*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[3])))).curr (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[3])))).curr)).comm (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[3])))).curr)).pid
   wp.v.v %s %y %h (*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[4])))).curr (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[4])))).curr)).comm (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[4])))).curr)).pid v.v %s %y (*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[5])))).curr (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[5])))).curr)).comm (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[5])))).curr)).pid v.v %s %y (*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[6])))).curr (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[6])))).curr)).comm (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[6])))).curr)).pid v.v %s %y (*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[7])))).curr (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[7])))).curr)).comm (*((*(((struct rq*)(((void*)&runqueues)+__per_cpu_offset[7])))).curr)).pid
 
   wp.v.v %all (struct rq*)(((void*)&runqueues)+__per_cpu_offset[0]) (struct rq*)(((void*)&runqueues)+__per_cpu_offset[1]) (struct rq*)(((void*)&runqueues)+__per_cpu_offset[2]) (struct rq*)(((void*)&runqueues)+__per_cpu_offset[3])  (struct rq*)(((void*)&runqueues)+__per_cpu_offset[4]) (struct rq*)(((void*)&runqueues)+__per_cpu_offset[5]) (struct rq*)(((void*)&runqueues)+__per_cpu_offset[6]) (struct rq*)(((void*)&runqueues)+__per_cpu_offset[7])
   
   printer.CLOSE
   
   RETURN
// End: dumpRunqueueDebugInformation
// ***********************************************
 
 
 
// ***********************************************
// 스택 초기 주소를 설정함
//Function: configureStackAddress
//Parameter: NONE
// ***********************************************
configureStackAddress:
 
   &STACK_BOTTOM_ADDRESS=v.value( ((struct task_struct *)&TASK_ADDRESS).stack )
   &STACK_STARTING_ADDRESS=&STACK_BOTTOM_ADDRESS+0x4000
   // 이 값은 램덤프 관찰 후 설정한 값이다. 업데이트가 필요함
   &STACK_SCAN_BOUNDARY_ADDRESS=&STACK_BOTTOM_ADDRESS+0x500
   &CURRENT_SP_ADDRESS=&STACK_BOTTOM_ADDRESS+0x3ff0
   
   RETURN
// End: configureStackAddress
// ***********************************************
 
 
// ***********************************************
// 프로세스 정보를 찍어줌
//Function: dumpProcessInformation
//Parameter: NONE
// ***********************************************
dumpProcessInformation:
 
   wp.v.v %s ((struct task_struct *)&TASK_ADDRESS).comm
   wp.v.v %all ((struct task_struct *)&TASK_ADDRESS).pid
       
   RETURN
// End: dumpProcessInformation
// ***********************************************
 
 
// ***********************************************
// 레지스터를 설정하고 콜스택을 뿌려줌
//Function: configureRegisterForCallstack
//Parameter: NONE
// ***********************************************
configureRegisterForCallstack:
 
   r.s sp &CANDIDATE_FP_ADDRESS
   r.s pc &ADDRESS_TEXT_ADDRESS_WITH_SP
   
   &CALLSTACK_HIT_TIMES=&CALLSTACK_HIT_TIMES+1
   print "Callstack hit time: " format.udecimal(4,&CALLSTACK_HIT_TIMES)
   
   // dump callstacks in file
   if &FILE_DUMP_MODE==0x1
   (
    wp.v.f
    wp.r
    if &CALLSTACK_HIT_TIMES==0x60
    (
printer.CLOSE
enddo   
    )
   )
  
   RETURN
// End: configureRegisterForCallstack
// ***********************************************
 
// ***********************************************
// 현재 스택 덤프 주소가 Stack 주소 공간 내에 있는 지
// 현재 스택 0x8 offset 공간에 Text Symbol이 있는 지 
//
//확인하고 있으면 1을 리턴 아니면 0 
//Function: checkValidFPAddressWithSymbol
//Parameter: NONE
// ***********************************************
checkValidFPAddressWithSymbol:
 
   ENTRY &stackAddress
   LOCAL &stackWith8Offset &stackDump &cur_stack_content &retVal
   
   &retVal=0
   &stackWith8Offset=&CURRENT_SP_ADDRESS+0x8
   
   if &CURR_CONTENTS_OF_SP_ADDR==0x0
   (
    RETURN &retVal
   )
      // 현재 스택덤프 값이 스택 내부 주소를 지정하는 지 확인
   if (&STACK_BOTTOM_ADDRESS<&CURR_CONTENTS_OF_SP_ADDR)&&(&CURR_CONTENTS_OF_SP_ADDR<&STACK_STARTING_ADDRESS)
   (
       symbol.modify.address __stack_program_counter_addr_buff V.VALUE(&stackWith8Offset)
      
       &cur_stack_content=data.quad(__stack_program_counter_addr_buff)
       if (&KERNEL_TEXT_START_ADDR<&cur_stack_content)&&(&cur_stack_content<&KERNEL_TEXT_END_ADDR)
       (
       &CANDIDATE_FP_ADDRESS=&CURR_CONTENTS_OF_SP_ADDR
       &ADDRESS_TEXT_ADDRESS_WITH_SP=&cur_stack_content
          &retVal=1
       )
   )
   
   RETURN &retVal
// End: checkValidFPAddressWithSymbol
// ***********************************************
 
T32로 디버깅을 하다보면 struct의 선언부를 확인하고 싶을 때가 있어요.
예를 들어서 struct tty_struct 구조체의 멤버를 갑자기 확인하고 싶은 경우,
 
"do do_struct.cmm tty_struct" 요렇게 입력하면 상세 멤버 변수를 확인할 수 있어요.
struct tty_struct struct tty_struct struct(620 bytes, [0] int magic (signed 32 bits),
                                                      [4] struct kref kref struct(4 bytes, [0] atomic_t refcount struct(4 bytes, [0] int counter (signed 3
                                                      [8] struct device * dev (pointer to struct device, 32 bits),
                                                      [12] struct tty_driver * driver (pointer to struct tty_driver, 32 bits),
                                                      [16] struct tty_operations * ops (pointer to struct tty_operations, 32 bits),
                                                      [20] int index (signed 32 bits),
                                                      [24] struct ld_semaphore ldisc_sem struct(40 bytes, [0] long int count (signed 32 bits), [4] raw_spi
                                                      [64] struct tty_ldisc * ldisc (pointer to struct tty_ldisc, 32 bits),
                                                      [68] struct mutex atomic_write_lock struct(40 bytes, [0] atomic_t count struct(4 bytes, [0] int coun
                                                      [108] struct mutex legacy_mutex struct(40 bytes, [0] atomic_t count struct(4 bytes, [0] int counter
                                                      [148] struct mutex throttle_mutex struct(40 bytes, [0] atomic_t count struct(4 bytes, [0] int counte
                                                      [188] struct rw_semaphore termios_rwsem struct(36 bytes, [0] long int count (signed 32 bits), [4] st
                                                      [224] struct mutex winsize_mutex struct(40 bytes, [0] atomic_t count struct(4 bytes, [0] int counter
                                                      [264] spinlock_t ctrl_lock struct(16 bytes, [0] union (untagged) union(16 bytes, [0] struct raw_spin
                                                      [280] spinlock_t flow_lock struct(16 bytes, [0] union (untagged) union(16 bytes, [0] struct raw_spin
                                                      [296] struct ktermios termios struct(44 bytes, [0] tcflag_t c_iflag (unsigned 32 bits), [4] tcflag_t
                                                      [340] struct ktermios termios_locked struct(44 bytes, [0] tcflag_t c_iflag (unsigned 32 bits), [4] t
                                                      [384] struct termiox * termiox (pointer to struct termiox, 32 bits),
                                                      [388] char [64] name (array of char, 64 bytes, 0..63),
                                                      [452] struct pid * pgrp (pointer to struct pid, 32 bits),
                                                      [456] struct pid * session (pointer to struct pid, 32 bits),
                                                      [460] long unsigned int flags (unsigned 32 bits),
                                                      [464] int count (signed 32 bits),
                                                      [468] struct winsize winsize struct(8 bytes, [0] short unsigned int ws_row (unsigned 16 bits), [2] s
                                                      [476.0] long unsigned int stopped:1 (unsigned 32 bits),
                                                      [476.1] long unsigned int flow_stopped:1 (unsigned 32 bits),
                                                      [476.2] long unsigned int unused:30 (unsigned 32 bits),
                                                      [480] int hw_stopped (signed 32 bits),
                                                      [484.0] long unsigned int ctrl_status:8 (unsigned 32 bits),
                                                      [485.0] long unsigned int packet:1 (unsigned 32 bits),
                                                      [485.1] long unsigned int unused_ctrl:23 (unsigned 32 bits),
                                                      [488] unsigned int receive_room (unsigned 32 bits),
                                                      [492] int flow_change (signed 32 bits),
                                                      [496] struct tty_struct * link (pointer to struct tty_struct, 32 bits),
                                                      [500] struct fasync_struct * fasync (pointer to struct fasync_struct, 32 bits),
                                                      [504] int alt_speed (signed 32 bits),
                                                      [508] wait_queue_head_t write_wait struct(24 bytes, [0] spinlock_t lock struct(16 bytes, [0] union (
                                                      [532] wait_queue_head_t read_wait struct(24 bytes, [0] spinlock_t lock struct(16 bytes, [0] union (u
                                                      [556] struct work_struct hangup_work struct(16 bytes, [0] atomic_long_t data struct(4 bytes, [0] int
                                                      [572] void * disc_data (pointer to void, 32 bits),
                                                      [576] void * driver_data (pointer to void, 32 bits),
                                                      [580] struct list_head tty_files struct(8 bytes, [0] struct list_head * next (pointer to struct list
                                                      [588.0] unsigned char closing:1 (unsigned 8 bits),
                                                      [592] unsigned char * write_buf (pointer to unsigned char, 32 bits),
                                                      [596] int write_cnt (signed 32 bits),
                                                      [600] struct work_struct SAK_work struct(16 bytes, [0] atomic_long_t data struct(4 bytes, [0] int co
                                                      [616] struct tty_port * port (pointer to struct tty_port, 32 bits))
 
do_struct.cmm이란 T32 script의 구현부는 아래와 같습니다.
Area.Create IO 80. 100.
Area.Select IO
Area IO
 
;=====specify the output directory of dump file, start =====
ENTRY &struct_name
 
v.type % %m %l %hi %sp struct &struct_name
ENDDO
 
참 유용하죠.
한 4년 전 인가요? 아래 코드를 열심히 분석 했었어요. 그런데 나중에 알고 보니 CONFIG_KMEMCHECK, CONFIG_LOCKDEP 컨피그 내 코드가 컴파일 되지 않는 죽은 코드라는 걸 알게 되었어요. 그 때 참 머리를 쥐어 뜯으며 자책했죠. 
static inline void slab_free_hook(struct kmem_cache *s, void *x)
{

 

 kmemleak_free_recursive(x, s->flags);
 
 /*
  * Trouble is that we may no longer disable interrupts in the fast path
  * So in order to make the debug calls that expect irqs to be
  * disabled we need to disable interrupts temporarily.
  */
#if defined(CONFIG_KMEMCHECK) || defined(CONFIG_LOCKDEP)
 {
  unsigned long flags;
 
  local_irq_save(flags);
  kmemcheck_slab_free(s, x, s->object_size);
  debug_check_no_locks_freed(x, s->object_size);
  local_irq_restore(flags);
 }
#endif
 if (!(s->flags & SLAB_DEBUG_OBJECTS))
  debug_check_no_obj_freed(x, s->object_size);
 
 kasan_slab_free(s, x);
 
}
 
이 사실을 어떻게 알아 냈나면, 전처리 파일을 보고 나서죠.
전처리 파일로 slab_free_hook() 함수 구현부를 보았더니 완전 다른 코드로 바뀌어 있었어요.
SLAB_DEBUG_OBJECTS 매크로가 0x00000000UL로 뒤바꾸어 있네요.
static inline __attribute__((always_inline)) __attribute__((no_instrument_function)) void slab_free_hook(struct kmem_cache *s, void *x)
{
 kmemleak_free_recursive(x, s->flags);
# 1314 "/home001/kernel_lover/kernel/mm/slub.c"
 if (!(s->flags & 0x00000000UL))
  debug_check_no_obj_freed(x, s->object_size);
 
 kasan_slab_free(s, x);
}
 
다른 예를 살펴볼까요? get_partial_node() 함수 내 너무나도 유명한 list_for_each_entry_safe API가 보이죠.
static void *get_partial_node(struct kmem_cache *s, struct kmem_cache_node *n,
    struct kmem_cache_cpu *c, gfp_t flags)
{
 struct page *page, *page2;
 void *object = NULL;
 int available = 0;
 int objects;
 
 if (!n || !n->nr_partial)
  return NULL;
 
 spin_lock(&n->list_lock);
 list_for_each_entry_safe(page, page2, &n->partial, lru) {
  void *t;
 
  if (!pfmemalloc_match(page, flags))
   continue;
 
  t = acquire_slab(s, n, page, object == NULL, &objects);
//skip
 }
 spin_unlock(&n->list_lock);
 
list_for_each_entry_safe 함수를 전처리 파일로 보면 어떨까요?
어라, 참 다르죠.
static void *get_partial_node(struct kmem_cache *s, struct kmem_cache_node *n,
    struct kmem_cache_cpu *c, gfp_t flags)
{
 struct page *page, *page2;
 void *object = ((void *)0);
 int available = 0;
 int objects;
 
 
 if (!n || !n->nr_partial)
  return ((void *)0);
 
 spin_lock(&n->list_lock);
 for (page = ({ const typeof( ((typeof(*page) *)0)->lru ) *__mptr = ((&n->partial)->next); (typeof(*page) *)( (char *)__mptr - __builtin_offsetof(typeof(*page), lru) );}), page2 = ({ const typeof( ((typeof(*(page)) *)0)->lru ) *__mptr = ((page)->lru.next); (typeof(*(page)) *)( (char *)__mptr - __builtin_offsetof(typeof(*(page)), lru) );}); &page->lru != (&n->partial); page = page2, page2 = ({ const typeof( ((typeof(*(page2)) *)0)->lru ) *__mptr = ((page2)->lru.next); (typeof(*(page2)) *)( (char *)__mptr - __builtin_offsetof(typeof(*(page2)), lru) );})) {
  void *t;
 
또 다른 예를 살펴볼까요?
static void init_pwq(struct pool_workqueue *pwq, struct workqueue_struct *wq,
       struct worker_pool *pool)
{
 BUG_ON((unsigned long)pwq & WORK_STRUCT_FLAG_MASK);
 
 memset(pwq, 0, sizeof(*pwq));
 
 pwq->pool = pool;
 
역시 전처리 파일 내 함수는 다르게 구현되어 있네요.
static void init_pwq(struct pool_workqueue *pwq, struct workqueue_struct *wq,
       struct worker_pool *pool)
{
 do { if (__builtin_expect(!!((unsigned long)pwq & WORK_STRUCT_FLAG_MASK), 0)) do { asm volatile(".long " "((0xe7f001f2) & 0xFFFFFFFF)" "\n\t" "\n"); __builtin_unreachable(); } while (0); } while (0);
 
 ({ void *__p = (pwq); size_t __n = sizeof(*pwq); if ((__n) != 0) { if (__builtin_constant_p((0)) && (0) == 0) __memzero((__p),(__n)); else memset((__p),(0),(__n)); } (__p); });
 
 pwq->pool = pool;
 
 
전처리 파일을 추출은 하는 방법이 뭐냐구요?
아래 패치를 적용하고 커널 컴파일을 하세요. 그럼 리눅스 커널 오브젝트 파일이 생기는 디렉토리에
.tmp_slub.i 형태로 생성되요.
 
diff --git a/Makefile b/Makefile
index b03ca98..f52240c 100644
--- a/Makefile
+++ b/Makefile
@@ -406,6 +406,7 @@ KBUILD_CFLAGS   := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
      -fno-strict-aliasing -fno-common \
      -Werror-implicit-function-declaration \
      -Wno-format-security \
+     -save-temps=obj \
      -std=gnu89
리눅스 커널 데이터 스트럭쳐 내 수 많은 linked list를 확인할 수 있어요.
예를 들어 struct kset이란 구조체도 첫번 째 멤버로 list란 링크드 리스트죠.
 
struct kset {
struct list_head list; //<<--
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
};
 
실제 코어 덤프를 T32로 열어서 상세 구조체를 보면 아래와 같거든요.
kset_find_obj(
    [R5] kset = 0xED74A980 -> (
      [NSD:0xED74A980] list = (  //<<--
        [NSD:0xED74A980] next = 0xED751104 -> (
          [NSD:0xED751104] next = 0xED5A4404,
          [NSD:0xED751108] prev = 0xED74A980),
        [NSD:0xED74A984] prev = 0xEC3A7384),
      [NSD:0xED74A988] list_lock = ([NSD:0xED74A988] rlock = ([NSD:0xED74A988] raw_lock = ([NSD:0xED
      [NSD:0xED74A998] kobj = ([NSD:0xED74A998] name = 0xED74A9C0, [NSD:0xED74A99C] entry = ([NSD:0x
      [NSD:0xED74A9BC] uevent_ops = 0x0),
    [R7] name = 0xC1C33FB8)
    
그런데 T32에서는 Linked List를 좀 더 비쥬얼하게 트리 형태로 볼 수 있는 아주 훌륭한 명령어가 있거든요.
 
자 그럼 _fake_list_sym.cmm이란 이름으로 T32 cmm script를 만들어볼까요?
원리는 간단해요. __pompeii_list_head 란 가상 심볼을 만들어서 링크드 리스트를 표현하는 거죠.
ENTRY &list_address
  
// create fake list symbol
y.create.var __pompeii_list_head 0 struct list_head
y.create.done
  
symbol.modify.address __pompeii_list_head &list_address
  
v.v __pompeii_list_head
v.chain %m %l %tree.open %hex __pompeii_list_head  __pompeii_list_head.next
 
자 이제 위 스크립트를  _fake_list_sym.cmm로 저장하구요,    
Linked List가 위치한 주소를 아규먼트로 입력합니다.
do _fake_list.sym 0xED74A980
   
(where)   
kset_find_obj(
    [R5] kset = 0xED74A980 -> (
      [NSD:0xED74A980] list = (  //<<--
        [NSD:0xED74A980] next = 0xED751104 -> (
    
짜잔, 이제 결과는? 아래와 같이 출력되는데요. 음, 111 번째 링크드 리스트 멤버가 깨져 있네요.
이런 스크립트를 안 쓰면 좀 디버깅이 불편하죠.
  0x0 (0)|  [C:0xED74A980] (
         |    [C:0xED74A980] next = 0xED751104,
         |    [C:0xED74A984] prev = 0xEC3A7384),
  0x1 (1)|  [C:0xED751104] (
         |    [C:0xED751104] next = 0xED5A4404,
         |    [C:0xED751108] prev = 0xED74A980),
  0x2 (2)|  [C:0xED5A4404] (
         |    [C:0xED5A4404] next = 0xED5A4E84,
         |    [C:0xED5A4408] prev = 0xED751104),
  0x3 (3)|  [C:0xED5A4E84] (
         |    [C:0xED5A4E84] next = 0xED5A4F04,
         |    [C:0xED5A4E88] prev = 0xED5A4404),
  0x4 (4)|  [C:0xED5A4F04] (
         |    [C:0xED5A4F04] next = 0xED5A4F84,
         |    [C:0xED5A4F08] prev = 0xED5A4E84),
  0x5 (5)|  [C:0xED5A4F84] (
         |    [C:0xED5A4F84] next = 0xED760204,
         |    [C:0xED5A4F88] prev = 0xED5A4F04),
  0x6 (6)|  [C:0xED760204] (
         |    [C:0xED760204] next = 0xED175F84,
         |    [C:0xED760208] prev = 0xED5A4F84),
  0x7 (7)|  [C:0xED175F84] (
         |    [C:0xED175F84] next = 0xED198004,
         |    [C:0xED175F88] prev = 0xED760204),
  0x8 (8)|  [C:0xED198004] (
         |    [C:0xED198004] next = 0xED198184,
         |    [C:0xED198008] prev = 0xED175F84),
  0x9 (9)|  [C:0xED198184] (
         |    [C:0xED198184] next = 0xED198204,
         |    [C:0xED198188] prev = 0xED198004),
0x0A (10)|  [C:0xED198204] (
         |    [C:0xED198204] next = 0xED198284,
         |    [C:0xED198208] prev = 0xED198184),
0x0B (11)|  [C:0xED198284] (
         |    [C:0xED198284] next = 0xED198304,
         |    [C:0xED198288] prev = 0xED198204),
0x0C (12)|  [C:0xED198304] (
         |    [C:0xED198304] next = 0xED198404,
         |    [C:0xED198308] prev = 0xED198284),
0x0D (13)|  [C:0xED198404] (
         |    [C:0xED198404] next = 0xED198484,
         |    [C:0xED198408] prev = 0xED198304),
0x0E (14)|  [C:0xED198484] (
         |    [C:0xED198484] next = 0xED198904,
         |    [C:0xED198488] prev = 0xED198404),
0x0F (15)|  [C:0xED198904] (
         |    [C:0xED198904] next = 0xED1BAC04,
         |    [C:0xED198908] prev = 0xED198484),
0x10 (16)|  [C:0xED1BAC04] (
         |    [C:0xED1BAC04] next = 0xED142E04,
         |    [C:0xED1BAC08] prev = 0xED198904),
0x11 (17)|  [C:0xED142E04] (
         |    [C:0xED142E04] next = 0xED142E84,
         |    [C:0xED142E08] prev = 0xED1BAC04),
0x12 (18)|  [C:0xED142E84] (
         |    [C:0xED142E84] next = 0xED142F04,
         |    [C:0xED142E88] prev = 0xED142E04),
0x13 (19)|  [C:0xED142F04] (
         |    [C:0xED142F04] next = 0xED142F84,
         |    [C:0xED142F08] prev = 0xED142E84),
0x14 (20)|  [C:0xED142F84] (
         |    [C:0xED142F84] next = 0xED145184,
         |    [C:0xED142F88] prev = 0xED142F04),
//snip
x6A (106)|  [C:0xECD3DE04] (
         |    [C:0xECD3DE04] next = 0xECF04184,
         |    [C:0xECD3DE08] prev = 0xECD3D604),
x6B (107)|  [C:0xECF04184] (
         |    [C:0xECF04184] next = 0xECE1DE84,
         |    [C:0xECF04188] prev = 0xECD3DE04),
x6C (108)|  [C:0xECE1DE84] (
         |    [C:0xECE1DE84] next = 0xECE1DF04,
         |    [C:0xECE1DE88] prev = 0xECF04184),
x6D (109)|  [C:0xECE1DF04] (
         |    [C:0xECE1DF04] next = 0xECE1DF84,
         |    [C:0xECE1DF08] prev = 0xECE1DE84),
x6E (110)|  [C:0xECE1DF84] (
         |    [C:0xECE1DF84] next = 0xECDDC004, //<<--
         |    [C:0xECE1DF88] prev = 0xECE1DF04),
x6F (111)|  [C:0xECDDC004] (
         |    [C:0xECDDC004] next = 0xECDDC09F,  //<<--
         |    [C:0xECDDC008] prev = 0xECE1DF9F),
x70 (112)|  [C:0xECDDC09F] (  //<<--
         |    [C:0xECDDC09F] next = 0x0700,  //<<--
         |    [C:0xECDDC0A3] prev = 0x01000100),
x71 (113)|  [C:0x700] (
         |    [C:0x700] next = 0x0,
         |    [C:0x704] prev = 0x0),    
 
리눅스 커널 동네에서 전 세계적으로 쓰고 있는 디버깅 툴을 소개하고자 합니다.
 
레드헷의 앤더슨이란 형님이 만든 오픈 소스로 배포한 리눅스 크래시(커널 패닉)을 분석할 수 있는 툴인데요, 
리눅스 커널 커뮤니티에서도 이 툴을 활발히 쓰고 있죠.
 
소스를 좀 받아 볼까요? 리눅스 머신에서 아래 커맨드로 소스를 땡겨옵니다.
 
소스를 받고 나서 아래 명령어로 빌드를 해요.
ARM machine인 경우
make target=ARM
make target=ARM extensions
 
X86인 경우
make target=X86_64
make target=X86_64 extensions
 
이제 trace command란 오픈소스 툴을 가져올 차례입니다. 이 툴로 ftrace 각 event를 파싱할 수 있어요.
 
빌드 명령어는 초간단, 그냥 make 만 때리면 되요.
 
자자, 이제 좀 정리해볼까요?
 
crash tool을 빌드한 결과물은 crash, trace command을 빌드한 이미지는 trace-cmd 거거든요.
 
/bin/CrashTool 폴더에 각각 결과물을 복사하구요. 
"make target=ARM extensions" 빌드하면 생성되는 extensions 폴더도 잊지 마세요.
austin.kim@Linux:~/bin/CrashTool$ ls -l
total 52400
-rwxrwxrwx 1 austin.kim home001  8536852 Jul 13 11:30 crash
drwxrwxrwx 4 austin.kim home001     4096 May  4 19:20 extensions
-rwxrwxrwx 1 austin.kim home001   868663 Feb 15 09:23 trace-cmd
 
crash_load.sh란 파일을 아래와 같이 만들고, "./crash_load.sh vmlinux" 명령어를 입력하면 실행 끝이죠.
#!/bin/sh
export CRASH_EXTENSIONS=/home001/austin.kim/bin/CrashTool/extensions
export TRACE_CMD=/home001/austin.kim/bin/CrashTool/trace-cmd 
/home001/austin.kim/bin/CrashTool/crash coredump0.BIN@0x0,coredump1.BIN@0xc0000000 -p 4096 -m phys_base=0x80000000 --no_panic --smp $1
 
실행 결과 화면입니다. 참 쉽죠.
      KERNEL: vmlinux
   DUMPFILES: /var/tmp/ramdump_elf_1X1HKt [temporary ELF header]
              DDRCS0.BIN
              DDRCS1.BIN
        CPUS: 4
        DATE: Wed Nov 29 15:24:00 2017
      UPTIME: 00:35:30
LOAD AVERAGE: 6.66, 7.02, 5.86
       TASKS: 363
    NODENAME: localhost
     RELEASE: 3.18.66-g715eee2
     VERSION: #2 SMP PREEMPT Wed Nov 29 12:37:14 KST 2017
     MACHINE: armv7l  (unknown Mhz)
      MEMORY: 2 GB
       PANIC: "kernel BUG at /src/3.18_kernel_ver/kernel/lib/list_debug.c:40!"
         PID: 0
     COMMAND: "swapper/0"
        TASK: c1917578  (1 of 4)  [THREAD_INFO: c1900000]
         CPU: 0
       STATE: TASK_RUNNING
     WARNING: reported panic task e6357840 not found
 
crash>
 
#Reference
 
 
# Reference: For more information on 'Linux Kernel';
 
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1
 
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2
 
 
 
 
 
 
얼마 전에 아래 패치를 봤어요. 이게 뭐지? 잘 이해가 안 가더라구요.
배열을 초기화하는 것 같기도 한데.
 
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index ce7cd96..fb252ec 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -402,7 +402,7 @@ static ssize_t dwc3_mode_write(struct file *file,
  struct dwc3 *dwc = s->private;
  unsigned long flags;
  u32 mode = 0;
- char buf[32];
+ char buf[32] = {0};
 
  if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
  return -EFAULT;
@@ -482,7 +482,7 @@ static ssize_t dwc3_testmode_write(struct file *file,
  struct dwc3 *dwc = s->private;
  unsigned long flags;
  u32 testmode = 0;
- char buf[32];
+ char buf[32] = {0};
 
  if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
  return -EFAULT;
@@ -589,7 +589,7 @@ static ssize_t dwc3_link_state_write(struct file *file,
  struct dwc3 *dwc = s->private;
  unsigned long flags;
  enum dwc3_link_state state = 0;
- char buf[32];
+ char buf[32] = {0};
 
  if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
  return -EFAULT;
@@ -630,12 +630,10 @@ static ssize_t dwc3_store_ep_num(struct file *file, const char __user *ubuf,
 {
  struct seq_file *s = file->private_data;
  struct dwc3 *dwc = s->private;
- char kbuf[10];
+ char kbuf[10] = {0};
  unsigned int num, dir, temp;
  unsigned long flags;
 
- memset(kbuf, 0, 10);
-
  if (copy_from_user(kbuf, ubuf, count > 10 ? 10 : count))
  return -EFAULT;
 
한 가지 이해가 안 간건. 위 코드에서 왜? memset이란 API를 뺐을까요?
 
커널 패치를 컴파일 한 다음에, 위 패치 반영 전과 후를 비교해보았어요.
아래 ARM instruction은 패치 적용 전 코드인데요, c0629e08 주소에서  __copy_from_user() 함수를
Branch 명령어로 이동하는 Instruction 이외에 특이 코드는 없네요.
static ssize_t dwc3_mode_write(struct file *file,
                const char __user *ubuf, size_t count, loff_t *ppos)
{
c0629d9c:       e1a0c00d        mov     ip, sp
c0629da0:       e92ddbf0             
{r4, r5, r6, r7, r8, r9, fp, ip, lr, pc}
c0629da4:       e24cb004        sub     fp, ip, #4
c0629da8:       e24dd028        sub     sp, sp, #40     ; 0x28
c0629dac:       e52de004        push    {lr}            ; (str lr, [sp, #-4]!)
c0629db0:       ebe7958c        bl      c000f3e8 <__gnu_mcount_nc>
c0629db4:       e1a05002        mov     r5, r2
c0629db8:       e59f2118        ldr     r2, [pc, #280]  ; c0629ed8 <dwc3_mode_write+0x13c>
c0629dbc:       e5923000        ldr     r3, [r2]
c0629dc0:       e1a08002        mov     r8, r2
c0629dc4:       e50b3028        str     r3, [fp, #-40]  ; 0xffffffd8
c0629dc8:       e59030bc        ldr     r3, [r0, #188]  ; 0xbc
c0629dcc:       e1a0000d        mov     r0, sp
c0629dd0:       e3c0cd7f        bic     ip, r0, #8128   ; 0x1fc0
c0629dd4:       e3ccc03f        bic     ip, ip, #63     ; 0x3f
c0629dd8:       e5936060        ldr     r6, [r3, #96]   ; 0x60
c0629ddc:       e355001f        cmp     r5, #31
c0629de0:       31a03005        movcc   r3, r5
c0629de4:       23a0301f        movcs   r3, #31
c0629de8:       e59cc008        ldr     ip, [ip, #8]
c0629dec:       e0910003        adds    r0, r1, r3
c0629df0:       30d0000c        sbcscc  r0, r0, ip
c0629df4:       33a0c000        movcc   ip, #0
c0629df8:       e35c0000        cmp     ip, #0
c0629dfc:       1a000005        bne     c0629e18 <dwc3_mode_write+0x7c>
c0629e00:       e24b0048        sub     r0, fp, #72     ; 0x48
c0629e04:       e1a02003        mov     r2, r3
c0629e08:       ebf2e301        bl      c02e2a14 <__copy_from_user> //<<--
c0629e0c:       e3500000        cmp     r0, #0
 
자자, 이제 패치 적용 후 컴파일을해본 후 어셈블 코드 좀 확인해 보았어요.
근데 ARM GCC 컴파일가 다른 코드를 생성한 것 같아요. 좀 살펴보면,
 
[1]: R1에 0x0을 이동시킴.
[2]: R2에 32를 이동시킴
[3]: R0을 현재 스택 주소에서 0x48 떨어진 곳에 위치시킴(스택 공간을 주는 거죠) 
[4]: 아래 포멧으로 memset 호출
memset(R0: buf, R1: 0x0, R2: 32)
 
static ssize_t dwc3_mode_write(struct file *file,
                 const char __user *ubuf, size_t count, loff_t *ppos)
 {
c0629d9c:       e1a0c00d        mov     ip, sp
c0629da0:       e92ddbf0        push    {r4, r5, r6, r7, r8, r9, fp, ip, lr, pc}
c0629da4:       e24cb004        sub     fp, ip, #4
c0629da8:       e24dd028        sub     sp, sp, #40     ; 0x28
c0629dac:       e52de004        push    {lr}            ; (str lr, [sp, #-4]!)
c0629db0:       ebe7958c        bl      c000f3e8 <__gnu_mcount_nc>
c0629db4:       e59f7130        ldr     r7, [pc, #304]  ; c0629eec <dwc3_mode_write+0x150>
c0629db8:       e1a04001        mov     r4, r1
c0629dbc:       e1a05002        mov     r5, r2
c0629dc0:       e3a01000        mov     r1, #0  //<<--[1]
c0629dc4:       e3a02020        mov     r2, #32  //<<--[2]
c0629dc8:       e5973000        ldr     r3, [r7]
c0629dcc:       e50b3028        str     r3, [fp, #-40]  ; 0xffffffd8
c0629dd0:       e59030bc        ldr     r3, [r0, #188]  ; 0xbc
c0629dd4:       e24b0048        sub     r0, fp, #72     ; 0x48  //<<--[3]
c0629dd8:       e5936060        ldr     r6, [r3, #96]   ; 0x60      
c0629ddc:       ebf2ec6f        bl      c02e4fa0 <memset>        //<<--[4]     
c0629de0:       e1a0200d        mov     r2, sp                       
c0629de4:       e3c21d7f        bic     r1, r2, #8128   ; 0x1fc0
c0629de8:       e3c1103f        bic     r1, r1, #63     ; 0x3f
c0629dec:       e355001f        cmp     r5, #31
c0629df0:       31a03005        movcc   r3, r5
c0629df4:       23a0301f        movcs   r3, #31
c0629df8:       e591c008        ldr     ip, [r1, #8]
c0629dfc:       e0942003        adds    r2, r4, r3
c0629e00:       30d2200c        sbcscc  r2, r2, ip
c0629e04:       33a0c000        movcc   ip, #0
c0629e08:       e35c0000        cmp     ip, #0
c0629e0c:       1a000006        bne     c0629e2c <dwc3_mode_write+0x90>
c0629e10:       e24b0048        sub     r0, fp, #72     ; 0x48
c0629e14:       e1a01004        mov     r1, r4
c0629e18:       e1a02003        mov     r2, r3
c0629e1c:       ebf2e2fc        bl      c02e2a14 <__copy_from_user>
 
 
우아...
char buf[32] = {0}; 와 같이 선언하니 ARM GCC compiler가 친절하게도 memset을 붙혀서
0x0으로 초기화 시켜주네요. char buf[32] = {0}; 구문을 자주 쓰도록 합시다. 그리고 C 코드가 어셈블 코드로 어떻게 변환되는 지도 종종 확인해야 할 것 같아요.
 
 
그리고, 위 패치를 작성하신 분은 이미 이 원리를 알고 있었던 것 같아요.
리눅스 커널에서 제공하는 여러 profile tool 중에 ftrace가 있어요. 매우 강력해요. 
잠깐 기억하는게, 저번 리눅스 커널 세미나에 갔었을 때 리눅스 커널 고수들이 ftrace로 벌이는 향연을 보고 참 라이브 공연에 있는 듯한 착각을 받았어요. ftrace 기능 중 stack tracer 기능을 잠깐 소개하고자 해요.
 
리눅스 커널 고수들 ftrace 로그를 밥 먹듯이 본다고 하네요. 저도 밥 먹듯이 보고는 있지만, 영 실력이 느는 것 같지 않아 짜증이 나지만요. 우선 stack tracer 기능을 좀 소개하려고 해요. 기능도 참 강력해요.
 
아래와 같이 커널 Config를 설정합니다.
--- a/arch/arm/configs/pompeii_com_defconfig
+++ b/arch/arm/configs/pompeii_com_defconfig
@@ -778,6 +777,14 @@ CONFIG_DEBUG_SET_MODULE_RONX=y
 CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
 CONFIG_SECURITY=y
 CONFIG_SECURITY_NETWORK=y
+CONFIG_DYNAMIC_FTRACE=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_FUNCTION_GRAPH_TRACER=y
+CONFIG_IRQSOFF_TRACER=y
+CONFIG_SCHED_TRACER=y
+CONFIG_BLK_DEV_IO_TRACER=y
+CONFIG_FUNCTION_PROFILER=y
+CONFIG_STACK_TRACER=y
+CONFIG_TRACER_SNAPSHOT=y
 CONFIG_LSM_MMAP_MIN_ADDR=4096
 CONFIG_HARDENED_USERCOPY=y
 CONFIG_SECURITY_SELINUX=y
 
컴파일 후 아래 available_tracers를 확인하면 끝이죠.
cat /sys/kernel/debug/tracing/available_tracers
 
blk function_graph wakeup_dl wakeup_rt wakeup irqsoff function nop
 
자, 이제 ftrace를 설정해볼까요?
__insert_vmap_area  함수가 어떤 콜스택으로 호출되는지 알고 싶거든요.
 
/d/tracing/buffer_size_kb ftrace 버퍼 사이즈 좀 늘려주시고,
d/tracing/set_ftrace_filter에 __insert_vmap_area 추가하고, 
 d/tracing/options/func_stack_trace을 1로 설정하면 끝이죠.
adb shell "echo  > d/tracing/set_event"
adb shell "sleep 1"
 
adb shell "echo 0 > d/tracing/tracing_on"
adb shell "sleep 1"
 
adb shell " echo 50000 > /d/tracing/buffer_size_kb"
 
adb shell "echo function > d/tracing/current_tracer"
adb shell "sleep 1"
 
adb shell "echo 1 > /d/tracing/events/sched/sched_switch/enable"
adb shell "sleep 1"
 
adb shell "echo __insert_vmap_area  > d/tracing/set_ftrace_filter"
adb shell "sleep 1"
 
adb shell "echo 1 > d/tracing/options/func_stack_trace"
adb shell "sleep 1"
 
adb shell "echo 1 > d/tracing/tracing_on"
adb shell "sleep 1"
 
여기서 중요한 건 ftrace를 설정할 때 잠깐 d/tracing/tracing_on을 0으로 
세팅하여 주시고, 각각 ftrace 설정 시 sleep을 줄 필요가 있어요.
 
왜냐면, ftrace 옵션을 다 설정하고 ftrace가 돌면 시스템 과부하로 시스템이 와치독 리셋으로 죽어버리는 수가 있거든요.
아래 리눅스 커널 커뮤니티에서도 제가 업데이트한 로그가 있는데요. 
 
위 메일링 리스트에서 논의한 내용은, (기억이 가물 가물 하네요) 
ftrace를 graph_tracer로 설정하면 엄청난 ftrace 로그가 찍히는데, Daniel Lezcano이 분은 CPU clock을 높혔더니 stall현상이 안 나왔다는 군요.
 
아무튼 다른 개발자들도 위 설정으로 ftrace을 키면 다른 사이드 이팩트는 없다고 동의했어요.(다른 메일이 날라왔죠.)
 
자, 그럼 위와 같이 설정하고, 5분 후, d/tracing/tracing_on을 0으로 다시 바꿔서 ftrace를 동작을 잠깐 멈추고, 
"d/tracing/trace" 로그를 보면 아래 정보를 볼 수 있어요.
            adbd-5414  [000] ...2   246.224086: __insert_vmap_area <-alloc_vmap_area
            adbd-5414  [000] ...2   246.224118: <stack trace>
 => __insert_vmap_area
 => alloc_vmap_area
 => __get_vm_area_node
 => __vmalloc_node_range
 => __vmalloc_node
 => vmalloc
 => n_tty_open
 => tty_ldisc_open
 => tty_ldisc_hangup
 => __tty_hangup
 => tty_vhangup
 => pty_close
 => tty_release
 => __fput
 => ____fput
 => task_work_run
 => do_work_pending
 => work_pending
// ...생략...
 
            adbd-5414  [000] ...2   247.567047: __insert_vmap_area <-alloc_vmap_area
            adbd-5414  [000] ...2   247.567071: <stack trace>
 => __insert_vmap_area
 => alloc_vmap_area
 => __get_vm_area_node
 => __vmalloc_node_range
 => __vmalloc_node
 => vmalloc
 => n_tty_open
 => tty_ldisc_open
 => tty_ldisc_hangup
 => __tty_hangup
 => tty_vhangup
 => pty_close
 => tty_release
 => __fput
 => ____fput
 => task_work_run
 => do_work_pending
 => work_pending
// ... 생략...
           <...>-5568  [002] ...2   247.572389: __insert_vmap_area <-alloc_vmap_area
           <...>-5568  [002] ...2   247.572407: <stack trace>
 => __insert_vmap_area
 => alloc_vmap_area
 => __get_vm_area_node
 => get_vm_area
 => binder_alloc_mmap_handler
 => binder_mmap
 => mmap_region
 => do_mmap_pgoff
 => vm_mmap_pgoff
 => SyS_mmap_pgoff
 => ret_fast_syscall
 
* 유튜브 강의 동영상도 있으니 같이 들으시면 더 많은 걸 배울 수 있습니다. 



 
 
 
 
#Reference 시스템 콜
 
 
Reference(워크큐)
워크큐(Workqueue) Overview
 

+ Recent posts