전 리눅스 커널 및 드라이버 코드를 주로 보는데요. 그런데 리눅스 시스템 프로그램 코드가 자주 봐야 해요.
 
아 그리고 userspace에서 tombstone(무덤)이 떨어지면서 크래시가 종종 발생하거든요. 이런 이슈도 잡아야 되요.
에러 시그니처는 아래와 같아요. 흠...
 
Revision: '0'
ABI: 'arm'
pid: 1558, tid: 1891, name: RenderThread >>> com.google.launcher2 <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
r0 00000000 r1 80808080 r2 00000000 r3 00000000
r4 9a979f0c r5 9e7e474c r6 00000000 r7 00000000
backtrace:
#00 pc 00016198 /system/lib/libc.so (strlen+33)
#01 pc 0025b531 /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#02 pc 0025b5cb /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#03 pc 0025511f /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#04 pc 00240fb7 /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#05 pc 00214d67 /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#06 pc 00212f1f /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#07 pc 0021305f /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
 
이럴 때는 보통 HAL(Hardware Adaptation Layer) 혹은 데몬 코드 리뷰를 하면서 문제점을 점검하며 크래시를 잡는데요.
 
그런데 위와 같이 유저 공간에서 크래시가 발생할 때 커널 패닉을 유발시켜서 코어 덤프를 뜨고 싶을 때가 있어요.
어떤 경우 이런 짓을 하고 싶을까요?
 
뭐 mmap으로 특정 하드웨어를 제어하는 시스템 콜 이후 tombstone이 떨어지던가,
아니면 같은 시간대에 커널 로그를 보고 싶은 경우가 가끔 있어요.
 
아주 가끔은 T32 잘 쓰는 자랑질을 하기 위해서, 
T32 장비를 타겟 디바이스에 연결 후 T32로 break point를 걸고 유저 공간까지 콜스택 까지 볼 수도 있죠.
 
자, 일단 커널 Config를 아래와 같이 설정하고요.
 
diff --git a/arch/arm/configs/pompeii_defconfig b/arch/arm/configs/pompeii_defconfig
index ed1d990..4065c00 100644
--- a/arch/arm/configs/pompeii_defconfig
+++ b/arch/arm/configs/pompeii_defconfig
@@ -777,6 +777,7 @@ CONFIG_DEBUG_SET_MODULE_RONX=y
 CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
 CONFIG_SECURITY=y
 CONFIG_SECURITY_NETWORK=y
+CONFIG_DEBUG_USER=y
 CONFIG_DYNAMIC_FTRACE=y
 
부트로더에서 "user_debug=24"로 커널 커맨드 라인을 전달해요.
 static void target_common_update_static_bootcmd(void)
 {
+    bootcmd_add_pair("user_debug","24");
+
 
그러면 커널 부팅 시에 아래 코드에서 user_debug 변수를 24로 설정하죠.
[kernel/arch/arm/kernel/traps.c]
#ifdef CONFIG_DEBUG_USER
unsigned int user_debug;
 
static int __init user_debug_setup(char *str)
{
get_option(&str, &user_debug);
return 1;
}
__setup("user_debug=", user_debug_setup);
#endif
 
user_debug 변수를 24로 왜 설정하냐구요? 아래 매크로를 모두 OR 연산 해보면 알 수 있죠.
#define UDBG_UNDEFINED (1 << 0)
#define UDBG_SYSCALL (1 << 1)
#define UDBG_BADABORT (1 << 2)
#define UDBG_SEGV (1 << 3)
#define UDBG_BUS (1 << 4)
 
 
이제 마지막으로, 커널이 부팅하고 나면, 아래와 같이 print-fatal-signals 파일을 1로 설정합니다.
echo 1 > /proc/sys/kernel/print-fatal-signals
 
이제 끝...
 
100% 유저 스페이스에서 tombstone이 떨어지는 디바이스에서 위 설정을 적용하고 실행해보았더니, 아래와 같이 바로 커널 패닉으로 떨어지네요.
참 친절하게도 유저 공간의 프로그램 카운터(PC)와 스택 정보를 찍어주네요.
 
<7>[   72.248793 / 05-21 22:27:31.925][2] android.ui: unhandled page fault (11) at 0x00000000, code 0x005
<1>[   72.248810 / 05-21 22:27:31.925][2] pgd = da0fc000
<1>[   72.252580 / 05-21 22:27:31.925][2] [00000000] *pgd=00000000
<6>[   72.258764 / 05-21 22:27:31.935][2] CPU: 2 PID: 999 Comm: android.ui Tainted: G        W      3.18.66-gbbd0bc4-dirty #3
<6>[   72.258781 / 05-21 22:27:31.935][2] task: d7e0d840 ti: d7f02000 task.ti: d7f02000
<6>[   72.258793 / 05-21 22:27:31.935][2] PC is at 0x73a7c9c0
<6>[   72.258803 / 05-21 22:27:31.935][2] LR is at 0x0
<6>[   72.258814 / 05-21 22:27:31.935][2] pc : [<73a7c9c0>]    lr : [<00000000>]    psr: 00070030
<6>[   72.258814 / 05-21 22:27:31.935][2] sp : 885bc480  ip : 00000000  fp : 131e3218
<6>[   72.258830 / 05-21 22:27:31.935][2] r10: 71488688  r9 : 92778200  r8 : 00000000
<6>[   72.258840 / 05-21 22:27:31.935][2] r7 : 131e2bf0  r6 : 00000000  r5 : 00000001  r4 : 00263c64
<6>[   72.258851 / 05-21 22:27:31.935][2] r3 : 131e3218  r2 : 71488688  r1 : 00000000  r0 : 71488688
<6>[   72.258863 / 05-21 22:27:31.935][2] Flags: nzcv  IRQs on  FIQs on  Mode USER_32  ISA Thumb  Segment user
<6>[   72.258873 / 05-21 22:27:31.935][2] Control: 10c0383d  Table: 9a0fc06a  DAC: 00000051
<6>[   72.258885 / 05-21 22:27:31.935][2] CPU: 2 PID: 999 Comm: android.ui Tainted: G        W      3.18.66-gbbd0bc4-dirty #3
<6>[   72.258907 / 05-21 22:27:31.935][2] [<c010e400>] (unwind_backtrace) from [<c010b4a8>] (show_stack+0x10/0x14)
<6>[   72.258924 / 05-21 22:27:31.935][2] [<c010b4a8>] (show_stack) from [<c0fd0740>] (dump_stack+0x78/0x98)
<6>[   72.258940 / 05-21 22:27:31.935][2] [<c0fd0740>] (dump_stack) from [<c0115f8c>] (__do_user_fault+0x108/0x19c)
<6>[   72.258957 / 05-21 22:27:31.935][2] [<c0115f8c>] (__do_user_fault) from [<c0fe05b8>] (do_page_fault+0x33c/0x3e8)
<6>[   72.258972 / 05-21 22:27:31.935][2] [<c0fe05b8>] (do_page_fault) from [<c0100404>] (do_DataAbort+0x34/0x184)
<6>[   72.258986 / 05-21 22:27:31.935][2] [<c0100404>] (do_DataAbort) from [<c0fdec3c>] (__dabt_usr+0x3c/0x40)
<6>[   72.258997 / 05-21 22:27:31.935][2] Exception stack(0xd7f03fb0 to 0xd7f03ff8)
<6>[   72.259009 / 05-21 22:27:31.935][2] 3fa0:                                     71488688 00000000 71488688 131e3218
<6>[   72.259023 / 05-21 22:27:31.935][2] 3fc0: 00263c64 00000001 00000000 131e2bf0 00000000 92778200 71488688 131e3218
<6>[   72.259036 / 05-21 22:27:31.935][2] 3fe0: 00000000 885bc480 00000000 73a7c9c0 00070030 ffffffff
 
이럴 때 어느 함수에 T32 브레이크 포인트를 걸어야 할까요?
__do_user_fault() 함수의 CONFIG_DEBUG_USER 안에 걸며 딱이겠죠.
 
static void
__do_user_fault(struct task_struct *tsk, unsigned long addr,
unsigned int fsr, unsigned int sig, int code,
struct pt_regs *regs)
{
struct siginfo si;
 
trace_user_fault(tsk, addr, fsr);
 
#ifdef CONFIG_DEBUG_USER
if (((user_debug & UDBG_SEGV) && (sig == SIGSEGV)) ||  //<<-- 여기 브레이크 포인트
    ((user_debug & UDBG_BUS)  && (sig == SIGBUS))) {
printk(KERN_DEBUG "%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n",
       tsk->comm, sig, addr, fsr);
show_pte(tsk->mm, addr);
show_regs(regs);
}
#endif
 
__do_user_fault() 함수를 호출하는 루틴
static int __kprobes
do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
// ... 생략 ...
 
if (user_mode(regs))
flags |= FAULT_FLAG_USER;
if (fsr & FSR_WRITE)
flags |= FAULT_FLAG_WRITE;
 
// .. 생략 ...
 
__do_user_fault(tsk, addr, fsr, sig, code, regs);
return 0;
 
 no_context:
__do_kernel_fault(mm, addr, fsr, regs);
return 0;
}
 
가끔 T32 잘 쓴다고 자랑해보세요.
 
# Reference: For more information on 'Linux Kernel';
 
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1
 
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2
 
 
 
가끔 타겟 보드에서 특정 CPU에서만 커널 패닉으로 시스템이 리셋되는 경우가 있어요.
이럴 때 테스트할 수 있는 유용한 디버깅 패치를 소개하고자 해요.
 
아래 패치는 부팅 할 때 CPU3을 sys node에서 아예 제거시키거든요. 아예 CPU3가 안 돌게 되는거죠.
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 5b4440d..14cce91 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -510,7 +510,7 @@ static int _cpu_up(unsigned int cpu, int tasks_frozen)
 
        cpu_hotplug_begin();
 
-       if (cpu_online(cpu) || !cpu_present(cpu)) {
+       if ( (cpu == 3) ||  cpu_online(cpu) || !cpu_present(cpu)) {
                ret = -EINVAL;
                goto out;
        }
 
예전에 CPU3에서만 이상한 동작을 하는 보드에서 위 패치를 적용하고 났더니, 부팅 후 크래시 없이 정상 동작하더라구요.
 
결국 실장된 보드의 ARM CPU 불량으로 정리되었죠. 비슷한 이슈를 만났을 때 한번 써보세요.
.
 
Trace32 메모리 클래스(Memory Class)에 대해서 좀 알아 볼까요?
cpu_up이란 리눅스 커널 API를 확인해보면요.
 
"d.l  cpu_up", "d.dump cpu_up"로 코드를 확인해보았어요.
그런데 왼쪽에 NSX, NSP란 뭘 의미할까요? 
NSX:FFFFFF9F996A7678|A9BC7BFD  cpu_up:   stp     x29,x30,[SP,#-0x40]!   ; x29,x30,[SP,#-64]!
NSX:FFFFFF9F996A767C|910003FD            mov     x29,SP
NSX:FFFFFF9F996A7680|A90153F3            stp     x19,x20,[SP,#0x10]   ; x19,x20,[SP,#16]
NSX:FFFFFF9F996A7684|A9025BF5            stp     x21,x22,[SP,#0x20]   ; x21,x22,[SP,#32].
_____________address|________0________4________8________C_0123456789ABCDEF
NSP:FFFFFF9F996A7670| A8C87BFD D65F03C0>A9BC7BFD 910003FD .{...._..{......
NSP:FFFFFF9F996A7680| A90153F3 A9025BF5 F0012274 2A0003F5 .S...[..t".....*
NSP:FFFFFF9F996A7690| AA1E03E0 D503201F F9452680 F9001FA0 ..... ...&E......
 
 
각각 아래 정보를 포함하고 있어요.
N: Non-Secure Mode
P: Program Memory
X: AArch64 ARM64 Code (A64, 32bit instr. length)
S: Supervisor Memory (privileged access).
 
이번에는 contig_page_data란 전역변수를 점검해볼까요?
"d.l contig_page_data", "d.dump contig_page_data" 명령어로 확인해보았어요.
___________addr/line|code_____|label______________|mnemonic________________|comment
NSD:FFFFFF9F9BD5AB00|00000377  contig_page_data:   dcd     0x377
NSD:FFFFFF9F9BD5AB04|00000000                      dcd     0x0
NSD:FFFFFF9F9BD5AB08|00002EC7                      dcd     0x2EC7
NSD:FFFFFF9F9BD5AB0C|00000000                      dcd     0x0
 
_____________address|________0________4________8________C_0123456789ABCDEF
NSD:FFFFFF9F9BD5AB00|>00000377 00000000 00002EC7 00000000 w...............
NSD:FFFFFF9F9BD5AB10| 00003082 00000000 00000000 00000000 .0..............
NSD:FFFFFF9F9BD5AB20| 00000000 00000000 00000796 00000000 .................
.
NSD란 정보가 보이네요. D란 알파벳이 보이는데요. D는 Data Memory를 의미해요.
 
이제 Trace32의 메모리 클래스에 대해서 정리해볼께요.
class 속성
P Program Memory
D  Data Memory
S  Supervisor Memory (privileged access)
U  User Memory (non-privileged access) not yet implemented; privileged access will be performed.
R  AArch32 ARM Code (A32, 32-bit instr. length)
T  AArch32 Thumb Code (T32, 16-bit instr. length)
X  AArch64 ARM64 Code (A64, 32bit instr. length)
J  Java Code (8-bit)
Z  Secure Mode (TrustZone devices)
N  Non-Secure Mode (TrustZone devices)
A  Absolute addressing (physical address)
SPR  Access to System Register, Special Purpose Registers and System Instructions.
C15  Access to C15-Coprocessor register. 
 
만약에 Trace32를 연결한 상태에서 물리주소에 특정 데이터를 저장하고 싶을 경우 아래와 같이 A란 Class를 추가하면 되요.
MMU 없이 메모리를 설정하는 거죠.
D.S NSDA:0xFFFFFF9F9BD5AB00 %LE %long  0xEAFFFFFE  
.
 
Trace32에서 제공하는 유용한 명령어를 소개 좀 해볼께요.
 
특정 변수 이름이 잘 기어나지 않을 때 아래 변수를 입력하면 해당 심볼로 시작하는 정보를 출력해줘요.
y.b slub*
symbol___________________|type_____________________|address__________________________________|
slub_max_order           |(static int)             |     D:FFFFFF9F9BB2E500--FFFFFF9F9BB2E503
slub_min_objects         |(static int)             |     D:FFFFFF9F9BE0C694--FFFFFF9F9BE0C697
slub_min_order           |(static int)             |     D:FFFFFF9F9BE0C690--FFFFFF9F9BE0C693
 
y.b cpu_up*
symbol________________________________|type_____________________|address__________________________________|
cpu_up                                |(int ())                 |     P:FFFFFF9F996A7678--FFFFFF9F996A77CB
_cpu_up                               |(static int ())          |     P:FFFFFF9F996A7448--FFFFFF9F996A7677
cpu_upmigrate_discourage_read_u64     |(u64 ())                 |     P:FFFFFF9F996FD558--FFFFFF9F996FD587
cpu_upmigrate_discourage_write_u64    |(int ())                 |     P:FFFFFF9F996FD588--FFFFFF9F996FD5FB
 
 
아래 명령어로 섹션 정보를 알려줘요.
y.l.section
__________________address________________|path\section___________________________|acc|init|physical
     P:FFFFFF9F99680000--FFFFFF9F9968003F|\\vmlinux\.head.text                   |R-X|L-  |
     P:FFFFFF9F99680800--FFFFFF9F9A834FE7|\\vmlinux\.text                        |R-X|L-  |
     D:FFFFFF9F9A840000--FFFFFF9F9B0976C7|\\vmlinux\.rodata                      |R--|L-  |
     D:FFFFFF9F9B0976C8--FFFFFF9F9B09770F|\\vmlinux\.eh_frame                    |R--|L-  |
     D:FFFFFF9F9B097710--FFFFFF9F9B0A4317|\\vmlinux\__bug_table                  |R--|L-  |
     D:FFFFFF9F9B0A4318--FFFFFF9F9B0A6627|\\vmlinux\.pci_fixup                   |R--|L-  |
     D:FFFFFF9F9B0A6628--FFFFFF9F9B0A666F|\\vmlinux\.builtin_fw                  |R--|L-  |
     D:FFFFFF9F9B0A6670--FFFFFF9F9B0BEA2F|\\vmlinux\__ksymtab                    |R--|L-  |
     D:FFFFFF9F9B0BEA30--FFFFFF9F9B0CE9EF|\\vmlinux\__ksymtab_gpl                |R--|L-  |
     D:FFFFFF9F9B0CE9F0--FFFFFF9F9B0DABCF|\\vmlinux\__kcrctab                    |R--|L-  |
     D:FFFFFF9F9B0DABD0--FFFFFF9F9B0E2BAF|\\vmlinux\__kcrctab_gpl                |R--|L-  |
     D:FFFFFF9F9B0E2BB0--FFFFFF9F9B114ABF|\\vmlinux\__ksymtab_strings            |R--|L-  |
     D:FFFFFF9F9B114AC0--FFFFFF9F9B11B18F|\\vmlinux\__param                      |R--|L-  |
     D:FFFFFF9F9B11B190--FFFFFF9F9B11BFFF|\\vmlinux\__modver                     |R--|L-  |
     D:FFFFFF9F9B11C000--FFFFFF9F9B1203DF|\\vmlinux\__ex_table                   |R--|L-  |
     D:FFFFFF9F9B1203E0--FFFFFF9F9B120403|\\vmlinux\.notes                       |R--|L-  |
     P:FFFFFF9F9B130000--FFFFFF9F9B19B8B7|\\vmlinux\.init.text                   |R-X|L-  |
     P:FFFFFF9F9B19B8B8--FFFFFF9F9B1A3C7B|\\vmlinux\.exit.text                   |R-X|L-  |
     D:FFFFFF9F9B1A3D00--FFFFFF9F9B22610F|\\vmlinux\.init.data                   |RW-|L-  |
     D:FFFFFF9F9B227000--FFFFFF9F9B232BBF|\\vmlinux\.data..percpu                |RW-|L-  |
     D:FFFFFF9F9B232BC0--FFFFFF9F9B242CE7|\\vmlinux\.altinstructions             |R--|L-  |
     P:FFFFFF9F9B242CE8--FFFFFF9F9B248377|\\vmlinux\.altinstr_replacement        |R-X|L-  |
     D:FFFFFF9F9B248378--FFFFFF9F9BADB0E7|\\vmlinux\.rela                        |R--|L-  |
     D:FFFFFF9F9BAE0000--FFFFFF9F9BD765B7|\\vmlinux\.data                        |RW-|L-  |
     D:FFFFFF9F9BD765B8--FFFFFF9F9BD765CF|\\vmlinux\.got.plt                     |RW-|L-  |
     D:FFFFFF9F9BD765D0--FFFFFF9F9BD76697|\\vmlinux\__clk_of_table_end           |RW-|L-  |
     D:FFFFFF9F9BD77000--FFFFFF9F9C19D967|\\vmlinux\.bss                         |RW-|--  |.
 
 
특정 변수 상세 정보 파악도 가능하죠. 뭐 예를 들어 contig_page_data 변수 타입을 보고 싶으면요.
v.info contig_page_data
variable
\\vmlinux\Global\contig_page_data
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
     D:FFFFFF9F9BD5AB00--FFFFFF9F9BD5C3FF     global static
 
type
(struct pglist_data) struct(6400 bytes, [0] struct zone [3] node_zones,
                                        [6144] struct zonelist [1] node_zonelists,
                                        [6208] int nr_zones,
                                        [6216] long unsigned int node_start_pfn,
                                        [6224] long unsigned int node_present_pages,
                                        [6232] long unsigned int node_spanned_pages,
                                        [6240] int node_id,
                                        [6248] wait_queue_head_t kswapd_wait,
                                        [6272] wait_queue_head_t pfmemalloc_wait,
                                        [6296] struct task_struct * kswapd,
                                        [6304] int kswapd_max_order,
                                        [6308] enum zone_type classzone_idx,
                                        [6312] int kcompactd_max_order,
                                        [6316] enum zone_type kcompactd_classzone_idx,
                                        [6320] wait_queue_head_t kcompactd_wait,
                                        [6344] struct task_struct * kcompactd)
 
Search 기능도 있는데요.
아래 명령어로 0xFFFFFF9F9BD5AB00--0xFFFFFF9F9BD5AC80 메모리 구간에 0xFFFFFFBE53E953A0 값이 있는지 찾아줘요.
d.f 0xFFFFFF9F9BD5AB00--0xFFFFFF9F9BD5AC80 %ll 0xFFFFFFBE53E953A0
 
조금 지나니 Trace32가 친절하게 결과를 알려주네요. 0xFFFFFF9F9BD5AC68 메모리 공간에 0xFFFFFFBE53E953A0 값이 있다는 소리인데요.
data 0xFFFFFFBE53E953A0 found in NSD:0xFFFFFF9F9BD5AB00--0xFFFFFF9F9BD5AC78 at NSD:0xFFFFFF9F9BD5AC68
 
실제 0xFFFFFF9F9BD5AC68 주소에 가보니 정말 그 값이 있네요..
________________address|________0________4________8________C
   NSD:FFFFFF9F9BD5AC60| 9BD5AC58 FFFFFF9F>53E953A0 FFFFFFBE
   NSD:FFFFFF9F9BD5AC70| 53E953A0 FFFFFFBE 9BD5AC78 FFFFFF9F
.
 
Crash Tool로 커널 오브젝트의 rbtree를 디버깅하는 방법에 대해 간단히 소개하고자 해요.
 
아래와 같은 커널 오브젝트가 있다고 가정해보아요. Sys node의 위치는 /sys/devices/system/cpu/cpu3 이겠죠?
현재 struct kobject->cpu는 "cpu3"을 가르키고 있는데요, 그럼 이 커널 오브젝트의 하위 child directory는 어떻게 검색할 수 있을까요?
  (struct kobject *) (struct kobject *)0xDEF78018 = 0xDEF78018 -> (
    (char *) name = 0xDE211780 -> "cpu3",
    (struct list_head) entry = ((struct list_head *) next = 0xDEF8401C, (struct list_head *) prev = 0x
    (struct kobject *) parent = 0xDEADA288,
    (struct kset *) kset = 0xDEAF6540,
    (struct kobj_type *) ktype = 0xC12E9DF0,
    (struct kernfs_node *) sd = 0xDE213240,
    (struct kref) kref = ((atomic_t) refcount = ((int) counter = 3 = 0x3)),
    (unsigned int:1) state_initialized = 1 = 0x1,
    (unsigned int:1) state_in_sysfs = 1 = 0x1,
    (unsigned int:1) state_add_uevent_sent = 1 = 0x1,
    (unsigned int:1) state_remove_uevent_sent = 0 = 0x0,
    (unsigned int:1) uevent_suppress = 0 = 0x0)
 
커널 오브젝트에서 struct kobject->sd.dir로 child directory에 대한 속성을 확인할 수 있어요. 
struct kobject.sd.dir.children.rb_node 멤버에 rb_node 주소가 있는데요. 이 rb_node에 따라 child directory가 위치해 있어요.  
  (struct kobject *) (struct kobject *)0xDEF78018 = 0xDEF78018 -> (
    (char *) name = 0xDE211780 -> "cpu3",
    (struct list_head) entry = ((struct list_head *) next = 0xDEF8401C, (struct list_head *) prev = 0x
    (struct kobject *) parent = 0xDEADA288,
    (struct kset *) kset = 0xDEAF6540,
    (struct kobj_type *) ktype = 0xC12E9DF0,
    (struct kernfs_node *) sd = 0xDE213240 -> (
      (atomic_t) count = ((int) counter = 12 = 0x0C),
      (atomic_t) active = ((int) counter = 0 = 0x0),
      (struct kernfs_node *) parent = 0xDEADBC00,
      (char *) name = 0xDE211840 -> "cpu3",
      (struct rb_node) rb = ((long unsigned int) __rb_parent_color = 3735926096 = 0xDEADB550, (struct
      (void *) ns = 0x0,
      (unsigned int) hash = 25910027 = 0x018B5B0B,
      (struct kernfs_elem_dir) dir = (
        (long unsigned int) subdirs = 2 = 0x2,
        (struct rb_root) children = (
          (struct rb_node *) rb_node = 0xDE213310),
        (struct kernfs_root *) root = 0xDE85EF00),
      (struct kernfs_elem_symlink) symlink = ((struct kernfs_node *) target_kn = 0x2),
      (struct kernfs_elem_attr) attr = ((struct kernfs_ops *) ops = 0x2, (struct kernfs_open_node *) o
      (void *) priv = 0xDEF78018,
 
Crash tool는 rbtree을 아주 쉽게 파싱할 수 있는 명령어를 제공해요. 
아래 명령어를 입력해볼까요?
crash> tree -t rbtree -o kernfs_node.rb -N 0xDE213310
de213300
de213b40
dc9c20c0
de2133c0
de213cc0
 
output 파일은 child directory의 (struct kernfs_node *) 구조체 인스턴스이거든요.
확인해볼까요?
 
0xde213300: "online"
  (struct kernfs_node *) (struct kernfs_node *)0xde213300 = 0xDE213300 -> (
    (atomic_t) count = ((int) counter = 0x3),
    (atomic_t) active = ((int) counter = 0x0),
    (struct kernfs_node *) parent = 0xDE213240,
    (char *) name = 0xC0F82A60 -> "online",
    (struct rb_node) rb = ((long unsigned int) __rb_parent_color = 0x1, (struct rb_node *) rb_right
    (void *) ns = 0x0,
    (unsigned int) hash = 0x4EC7CDD8,
    (struct kernfs_elem_dir) dir = ((long unsigned int) subdirs = 0xC0BDD680, (struct rb_root) child
    (struct kernfs_elem_symlink) symlink = ((struct kernfs_node *) target_kn = 0xC0BDD680),
    (struct kernfs_elem_attr) attr = ((struct kernfs_ops *) ops = 0xC0BDD680, (struct kernfs_open_no
    (void *) priv = 0xC12E9E04,
    (short unsigned int) flags = 0x0252,
    (umode_t) mode = 0x81A4,
    (unsigned int) ino = 0x14C6,
    (struct kernfs_iattrs *) iattr = 0xD9DEDB00)
 
0xde213b40: "power"
  (struct kernfs_node *) (struct kernfs_node *)0xde213b40 = 0xDE213B40 -> (
    (atomic_t) count = ((int) counter = 0x3),
    (atomic_t) active = ((int) counter = 0x0),
    (struct kernfs_node *) parent = 0xDE213240,
    (char *) name = 0xDE214000 -> "power",
    (struct rb_node) rb = ((long unsigned int) __rb_parent_color = 0xDE213310, (struct rb_node *) rb
    (void *) ns = 0x0,
    (unsigned int) hash = 0x12FD28DB,
    (struct kernfs_elem_dir) dir = ((long unsigned int) subdirs = 0x0, (struct rb_root) children = (
    (struct kernfs_elem_symlink) symlink = ((struct kernfs_node *) target_kn = 0x0),
    (struct kernfs_elem_attr) attr = ((struct kernfs_ops *) ops = 0x0, (struct kernfs_open_node *) o
    (void *) priv = 0xDEF78018,
    (short unsigned int) flags = 0x11,
 
0xdc9c20c0: "cpuidle"
  (struct kernfs_node *) (struct kernfs_node *)0xdc9c20c0 = 0xDC9C20C0 -> (
    (atomic_t) count = ((int) counter = 0x0A),
    (atomic_t) active = ((int) counter = 0x0),
    (struct kernfs_node *) parent = 0xDE213240,
    (char *) name = 0xDC993C00 -> "cpuidle",
    (struct rb_node) rb = ((long unsigned int) __rb_parent_color = 0xDE213B51, (struct rb_node *) rb
    (void *) ns = 0x0,
    (unsigned int) hash = 0x0870225E,
    (struct kernfs_elem_dir) dir = ((long unsigned int) subdirs = 0x6, (struct rb_root) children = (
    (struct kernfs_elem_symlink) symlink = ((struct kernfs_node *) target_kn = 0x6),
    (struct kernfs_elem_attr) attr = ((struct kernfs_ops *) ops = 0x6, (struct kernfs_open_node *) o
    (void *) priv = 0xDC992220,
    (short unsigned int) flags = 0x11,
    (umode_t) mode = 0x41ED,
    (unsigned int) ino = 0x4136,
    (struct kernfs_iattrs *) iattr = 0xD9DE4000)
 
0xde2133c0: "subsystem"
  (struct kernfs_node *) (struct kernfs_node *)0xde2133c0 = 0xDE2133C0 -> (
    (atomic_t) count = ((int) counter = 0x3),
    (atomic_t) active = ((int) counter = 0x0),
    (struct kernfs_node *) parent = 0xDE213240,
    (char *) name = 0xDE211900 -> "subsystem",
    (struct rb_node) rb = ((long unsigned int) __rb_parent_color = 0xDE213B51, (struct rb_node *) rb
    (void *) ns = 0x0,
    (unsigned int) hash = 0x45372B66,
    (struct kernfs_elem_dir) dir = ((long unsigned int) subdirs = 0xDEADBE40, (struct rb_root) child
    (struct kernfs_elem_symlink) symlink = ((struct kernfs_node *) target_kn = 0xDEADBE40),
    (struct kernfs_elem_attr) attr = ((struct kernfs_ops *) ops = 0xDEADBE40, (struct kernfs_open_no
    (void *) priv = 0x0,
    (short unsigned int) flags = 0x14,
    (umode_t) mode = 0xA1FF,
    (unsigned int) ino = 0x14C8,
    (struct kernfs_iattrs *) iattr = 0xD9DED400)
 
0xde213cc0: "uevent"
  (struct kernfs_node *) (struct kernfs_node *)0xde213cc0 = 0xDE213CC0 -> (
    (atomic_t) count = ((int) counter = 0x3),
    (atomic_t) active = ((int) counter = 0x0),
    (struct kernfs_node *) parent = 0xDE213240,
    (char *) name = 0xC0F7485C -> "uevent",
    (struct rb_node) rb = ((long unsigned int) __rb_parent_color = 0xDE213311, (struct rb_node *) rb
    (void *) ns = 0x0,
    (unsigned int) hash = 0x57C6BB9D,
    (struct kernfs_elem_dir) dir = ((long unsigned int) subdirs = 0xC0BDD680, (struct rb_root) child
    (struct kernfs_elem_symlink) symlink = ((struct kernfs_node *) target_kn = 0xC0BDD680),
    (struct kernfs_elem_attr) attr = ((struct kernfs_ops *) ops = 0xC0BDD680, (struct kernfs_open_no
    (void *) priv = 0xC12E9E4C,
    (short unsigned int) flags = 0x0252,
    (umode_t) mode = 0x81A4,
    (unsigned int) ino = 0x14C5,
    (struct kernfs_iattrs *) iattr = 0xD9DED500)
 
정리하면,
cpu3(/sys/devices/system/cpu/cpu3)의 하위 디렉토리가 "online", "power", "cpuidle", "subsystem", "uevent" 임을 알 수 있네요.
 
최근 디바이스 드라이버는 Platform Device모델로 적용되고 있거든요. 그래서 디바이스 노드의 상하 관계를 커널 오브젝트를 써서 표현을 많이 하거든요. 그래서 커널 오브젝트 디버깅을 잘하면 좀 더 유용한 정보를 볼 수 있어요.
Crash Tool로 커널 오브젝트의 rbtree를 디버깅해서 parent node를 확인하는 방법에 대해 간단히 소개하고자 해요.
 
아래와 같은 커널 오브젝트가 있다고 가정해보아요. Sys node의 위치는 /sys/devices/system/cpu/cpu3 이겠죠?
현재 struct kobject->cpu는 "cpu3"을 가르키고 있는데요, 그럼 이 커널 오브젝트의 상위 directory는 어떻게 검색할 수 있을까요?
  (struct kobject *) (struct kobject *)0xDEF78018 = 0xDEF78018 -> (
    (char *) name = 0xDE211780 -> "cpu3",
    (struct list_head) entry = ((struct list_head *) next = 0xDEF8401C, (struct list_head *) prev = 0x
    (struct kobject *) parent = 0xDEADA288,
    (struct kset *) kset = 0xDEAF6540,
    (struct kobj_type *) ktype = 0xC12E9DF0,
    (struct kernfs_node *) sd = 0xDE213240 -> (
      (atomic_t) count = ((int) counter = 12 = 0x0C),
      (atomic_t) active = ((int) counter = 0 = 0x0),
      (struct kernfs_node *) parent = 0xDEADBC00,
      (char *) name = 0xDE211840 -> "cpu3",
      (struct rb_node) rb = (
        (long unsigned int) __rb_parent_color = 3735926096 = 0xDEADB550,
        (struct rb_node *) rb_right = 0xDE215C10,
        (struct rb_node *) rb_left = 0xDE2069D0),
 
역시 crash tool에서 rbtree를 파싱할 수 있는 기능을 제공해요.
struct kobject->sd가 갖고 있는 0xDE213240 인스턴스로 parent directory에 대한 rb_node가 있는 struct kernfs_node->rb에 접근이 가능하거든요.
crash> tree -t rbtree -o kernfs_node.rb -N 0xDE213240
de213230
deadbbf0
de862470
de862cb0
de861ff0
 
위 Output 값은 뭘 의미할까요? 좀 복잡한데요.
아래 struct kernfs_node 구조체를 보면 struct kernfs_elem_attr attr 멤버가 보이죠?
 
위 Output은 struct kernfs_elem_attr가 위치한 주소를 가르키고 있거든요.
struct kernfs_elem_attr->notify_next 오프셋이 0x10이니 0x10만큼 더해줘야 해요.
struct kernfs_elem_attr {
const struct kernfs_ops *ops;
struct kernfs_open_node *open;
loff_t size;
struct kernfs_node *notify_next;
};
(where)
struct kernfs_node {
atomic_t count;
atomic_t active;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
// .. 생략..
union {
struct kernfs_elem_dir dir;
struct kernfs_elem_symlink symlink;
struct kernfs_elem_attr attr;
};
.
뭔 소리인지 모르겠다구요? 커널 오브젝트로 디렉토리를 아래 콜 트레이스로 등록되거든요.
나중에 상세 분석할 시간을 갖도록 할께요. 핵심 함수는 kernfs_create_dir_ns/kernfs_new_node 이에요. 
\\vmlinux\kernfs/dir\__kernfs_new_node+0x60
\\vmlinux\slub\kmem_cache_alloc+0x254
\\vmlinux\kernfs/dir\__kernfs_new_node+0x60
\\vmlinux\kernfs/dir\kernfs_new_node+0x2C
\\vmlinux\kernfs/dir\kernfs_create_dir_ns+0x28
\\vmlinux\sysfs/dir\sysfs_create_dir_ns+0x5C
\\vmlinux\kobject\kobject_add_internal+0xAC
\\vmlinux\kobject\kobject_add+0x50
\\vmlinux\base/core\device_add+0x11C
 
이제 확인할 시간이에요.
(struct kernfs_node *)(0xde213230+0x10) = 0xDE213240 -> (
    count = (counter = 12),
    active = (counter = 0),
    parent = 0xDEADBC00,
    name = 0xDE211840 -> "cpu3",
(struct kernfs_node *)(0xdeadbbf0+0x10) = 0xDEADBC00 -> (
    count = (counter = 23),
    active = (counter = 0),
    parent = 0xDE862480,
    name = 0xDEAD96C0 -> "cpu",
(struct kernfs_node *)(0xde862470+0x10) = 0xDE862480 -> (
    count = (counter = 9),
    active = (counter = 0),
    parent = 0xDE862CC0,
    name = 0xDEAD7480 -> "system",
(struct kernfs_node *)(0xde862cb0+0x10) = 0xDE862CC0 -> (
    count = (counter = 36),
    active = (counter = 0),
    parent = 0xDE862000,
    name = 0xDEAF6600 -> "devices",
 
상위 디렉토리가 이렇게 확인 가능하군요.
/sys/devices/system/cpu/cpu3
IPI (Inter Processor Interrupts)란 용어를 들어본 적이 있나요?
각 CPU간 통신을 하고 싶은 경우가 있거든요. 예를 들면, 다른 CPU를 깨우고 싶거나 다른 CPU가
특정 콜백 함수를 호출해서 원하는 동작을 시키고 싶은 경우죠. 
 
CPU 부하을 점검해서 일을 덜하고 있는 다른 CPU에 일을 시키는 네트워크 디바이스 드라이버나,
CPU Governor에서 다른 CPU Frequency를 높이고 싶을 경우에 주로 실행되는데요.
 
그런데 이 때 특정 콜백 함수가 너무 오래 실행되어 응답이 늦을 경우 시스템 오동작을 유발시킬 수가 있구요.
가끔 불량 ARM 코어에서는 특정 CPU가 응답을 늦게 주는 경우가 있어요.
 
이럴 때 써먹으면 좋은 디버깅 패치가 있어요. 아래 패치를 적용하면 CPU간 IPI 통신을 ftrace로 확인할 수 있어요.
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index d031a85..e31341a 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -841,7 +841,9 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
 
        case IPI_CALL_FUNC:
                irq_enter();
+               trace_printk("++ IPI_CALL_FUNC ++\n");
                generic_smp_call_function_interrupt();
+               trace_printk("-- IPI_CALL_FUNC --\n");
                irq_exit();
                break;
                
diff --git a/kernel/smp.c b/kernel/smp.c
index b2ec21c..fc822cc 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -456,12 +456,13 @@ void smp_call_function_many(const struct cpumask *mask,
                        csd->flags |= CSD_FLAG_SYNCHRONOUS;
                csd->func = func;
                csd->info = info;
+               trace_printk("IPI is queued for target cpu : %d\n", cpu);
                llist_add(&csd->llist, &per_cpu(call_single_queue, cpu));
        }
 
        /* Send a message to all CPUs in the map */
        arch_send_call_function_ipi_mask(cfd->cpumask);
-
+       trace_printk("IPI triggered to %d cpus\n", cpumask_weight(cfd->cpumask));
        if (wait) {
                for_each_cpu(cpu, cfd->cpumask) {
                        struct call_single_data *csd;
 
ftrace log를 받아보면 아래와 같은데요. 해석을 좀 해볼께요.
[1][2]: CPU1과 CPU7에 IPI call을 Trigger해요.
[3][4]: CPU7과 CPU1이 IPI call을 잘 받고 있네요.
[5][6]: IPI call이 제대로 종료되었네요.
 <...>-822   [004] ...2    41.459370: smp_call_function_many: IPI target cpu : 1 //<<--[1]
           <...>-822   [004] ...2    41.459372: smp_call_function_many: IPI target cpu : 7 //<<--[2]
           <...>-822   [004] ...2    41.459374: smp_call_function_many: IPI sent to 2 cpus
        Binder_3-1592  [000] d..3    41.459375: sched_switch: prev_comm=Binder_3 prev_pid=1592 prev_prio=120 prev_state=D ==> next_comm=.lge.smartcover next_pid=2570 next_prio=120 extra_prev_state=m 
 logd.reader.per-834   [007] d.h.    41.459377: handle_IPI: + IPI_CALL_FUNC  //<<--[3]
 Ril Proxy Main -1250  [001] d.h.    41.459378: handle_IPI: + IPI_CALL_FUNC //<<--[4] 
 logd.reader.per-834   [007] d.h.    41.459380: handle_IPI: - IPI_CALL_FUNC  //<<--[5]
 Ril Proxy Main -1250  [001] d.h.    41.459382: handle_IPI: - IPI_CALL_FUNC //<<--[6] 
 
아래 ftrace log는 CPU3에서 CPU0/1에 IPI call을 Trigger하는군요. 
 <...>-617   [003] ...2    19.658070: smp_call_function_many: IPI target cpu : 0
           <...>-617   [003] ...2    19.658073: smp_call_function_many: IPI target cpu : 1
           <...>-617   [003] ...2    19.658075: smp_call_function_many: IPI sent to 2 cpus
           <...>-621   [001] d.h1    19.658079: handle_IPI: ++ IPI_CALL_FUNC ++
           <...>-620   [000] d.h1    19.658080: handle_IPI: ++ IPI_CALL_FUNC ++
         mmcqd/0-270   [002] d..3    19.658080: sched_switch: prev_comm=mmcqd/0 prev_pid=270 prev_prio=120 prev_state=R+ ==> next_comm=kworker/2:1H next_pid=293 next_prio=100 NORMAL/1024 ==> NORMAL/88761
           <...>-620   [000] d.h1    19.658084: handle_IPI: -- IPI_CALL_FUNC --
           <...>-621   [001] d.h1    19.658084: handle_IPI: -- IPI_CALL_FUNC --
 
이런 IPI call는 특정 변수에 큐잉(Queue)한 다음에  큐(Queue) 변수에서 요청된 큐를 처리하는 구조인데요.
그 정체는 call_single_queue이구요, per-cpu 타입이에요.
 
이제 Crash Tool로 변수를 직접 확인해 볼까요?
 
1> CPU5 IPI call
CPU5로 IPI call 큐잉(Queue) 정보를 보면 아래와 같아요. 음, CPU5에 drain_local_pages() 함수를 수행하라는 IPI call을 날렸나 보내요.
crash> p call_single_queue
PER-CPU DATA TYPE:
  struct llist_head call_single_queue;
PER-CPU ADDRESSES:
  [0]: edefc4c0
  [1]: edf074c0
  [2]: edf124c0
  [3]: edf1d4c0
  [4]: edf284c0
  [5]: edf334c0
  [6]: edf3e4c0
  [7]: edf494c0 
crash> struct list_head edf334c0 -px
struct list_head {
  struct list_head *next = 0xedf374a0
  -> {
       next = 0xedf37500,
       prev = 0xc01d63d4 <drain_local_pages>
     }  
crash> struct call_single_data 0xedf374a0
struct call_single_data {
  llist = {
    next = 0xedf37500
  },
  func = 0xc01d63d4 <drain_local_pages>,
  info = 0x0,
  flags = 0x1
}
 
2> CPU6 IPI call
이 경우는 CPU6이 smp_callback 함수를 호출하도록 IPI call을 수행하네요. 가장 많이 호출되는 callback 함수이랍니다.
crash> p call_single_queue
PER-CPU DATA TYPE:
  struct llist_head call_single_queue;
PER-CPU ADDRESSES:
  [0]: edefb4c0
  [1]edf064c0
  [2]: edf114c0
  [3]: edf1c4c0
  [4]: edf274c0
  [5]: edf324c0
  [6]: edf3d4c0
  [7]: edf484c0
crash> struct call_function_data -px 0xEC817EE8
struct call_function_data {
  struct call_single_data *csd = 0xedf364c0
  -> {
       llist = {
         next = 0x0
       },
       func = 0xc08487bc <smp_callback>, //<<--
       info = 0x0,
       flags = 0x1
     }
  cpumask = {{
      bits = {0xc042e910}
    }}
}
.
 
코어 덤프에서 wakelock 정보를 CrashTool(red hat crash-utility)로 확인할 수 있는 팁을 공유하고자 해요.
 
CrashTool "rbtree"에 대해 강력한 명령어를 제공하거든요.
wakelocks_tree란 전역 변수에서 rb_node를 확인하고요. 0xffffffc0e397b108
아래 오프셋으로 명령어를 주면 되요.
crash64> tree -t rbtree -o wakelock.node -N 0xffffffc0e397b108
ffffffc0e397b100
ffffffc0d4262200
ffffffc0c9e8e100
ffffffc066355400
ffffffc06a0a0900
ffffffc0c5cd8600
ffffffc04fddcb00
ffffffc0e397bb00
ffffffc06c55b800
ffffffc06c7c3e00
ffffffc0eb6b5100
ffffffc0d6dc2b00
ffffffc06a124500
ffffffc0c5d35300
(where)
crash64> p wakelocks_tree
wakelocks_tree = $1 = {
  rb_node = 0xffffffc0e397b108
}
 
자 하나 하나 씩 점검 좀 해볼께요. 
"PowerManagerService.Display" wakelock 소스는 active가 true이니 wakelock을 잡고 있네요.
crash64> struct wakelock   ffffffc0d4262200
struct wakelock {
  name = 0xffffffc063479080 "PowerManagerService.Display",
  node = {
    __rb_parent_color = 18446743802650013961,
    rb_right = 0xffffffc0c5cd8608,
    rb_left = 0xffffffc0c9e8e108
  },
// .. 생략...
    expire_count = 0,
    wakeup_count = 14,
    pending_count = 6,
    active = true,
    autosleep_enabled = false
 
"KeyEvents"는 안 잡고 있고요.
crash64> struct wakelock   ffffffc0c9e8e100
struct wakelock {
  name = 0xffffffc066213900 "KeyEvents",
  node = {
    __rb_parent_color = 18446743802390913545,
    rb_right = 0xffffffc06a0a0908,
    rb_left = 0xffffffc066355408
  },
// .. 생략...
    wakeup_count = 258,
    pending_count = 1,
    active = false,
    autosleep_enabled = false
 
이런 식으로 wakelock을 누가 잡고 있는 지 확인할 수 있어요.
그럼 wakelock을 잡는 코드는 어디일까요? 아래 패치를 잠깐 보면 알 수 있어요.
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 7af116e..18b8b2e 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -177,6 +177,9 @@ void wakeup_source_add(struct wakeup_source *ws)
                return;
 
        spin_lock_init(&ws->lock);
+
+       trace_printk("+ wakelock add - process(%s), from(%pF)\n", current->comm, __builtin_return_address(0));
+
        setup_timer(&ws->timer, pm_wakeup_timer_fn, (unsigned long)ws);
        ws->active = false;
        ws->last_time = ktime_get();
@@ -547,6 +550,8 @@ static void wakeup_source_activate(struct wakeup_source *ws)
        /* Increment the counter of events in progress. */
        cec = atomic_inc_return(&combined_event_count);
 
+       trace_printk("+ wakelock activate process(%s), from(%pF)\n", current->comm, __builtin_return_address(0));
+
        trace_wakeup_source_activate(ws->name, cec);
 }
 
위 패치를 적용하고 나면 아래 ftrace log로 언제 wakelock을 잡았는 지 확인할 수 있어요
<...>-4480  [000] ....    32.668732: wakeup_source_activate: + wakelock activate process(rild),  from(pm_wake_lock+0xa4/0x174) source[radio-interface]
 
상세 wakelock 동작에 대해서는 나중에 다뤄보도록 할께요.
 
커널 패닉 디버깅할 때 crash-utility를 아주 많이 쓰죠. 
수 많은 명령어 중 ps를 소개하려고 합니다.
 
ps -p 
프로세스 전체의 부모와 자식 프로세스 정보를 보여줘요.
crash64> ps -p
PID: 0      TASK: ffffffc001580e40  CPU: 0   COMMAND: "swapper/0"
 
PID: 0      TASK: ffffffc001580e40  CPU: 0   COMMAND: "swapper/0"
 PID: 0      TASK: ffffffc0f865f300  CPU: 1   COMMAND: "swapper/1"
 
PID: 0      TASK: ffffffc001580e40  CPU: 0   COMMAND: "swapper/0"
 PID: 0      TASK: ffffffc0f86b8000  CPU: 2   COMMAND: "swapper/2"
 
PID: 0      TASK: ffffffc001580e40  CPU: 0   COMMAND: "swapper/0"
 PID: 0      TASK: ffffffc0f86b8b80  CPU: 3   COMMAND: "swapper/3"
 
PID: 0      TASK: ffffffc001580e40  CPU: 0   COMMAND: "swapper/0"
 PID: 1      TASK: ffffffc0f8658000  CPU: 1   COMMAND: "init"
 
PID: 0      TASK: ffffffc001580e40  CPU: 0   COMMAND: "swapper/0"
 PID: 2      TASK: ffffffc0f8658b80  CPU: 2   COMMAND: "kthreadd"
 
PID: 0      TASK: ffffffc001580e40  CPU: 0   COMMAND: "swapper/0"
 PID: 2      TASK: ffffffc0f8658b80  CPU: 2   COMMAND: "kthreadd"
  PID: 3      TASK: ffffffc0f8659700  CPU: 0   COMMAND: "ksoftirqd/0"
 
ps -p [pid]
만약 perf라 프로세스의 부모 프로세스를 알고 싶으면 아래 명령어를 치면 되죠.
crash64> ps -p 25
PID: 0      TASK: ffffffc001580e40  CPU: 0   COMMAND: "swapper/0"
 PID: 2      TASK: ffffffc0f8658b80  CPU: 2   COMMAND: "kthreadd"
  PID: 25     TASK: ffffffc0f81f3980  CPU: 2   COMMAND: "perf"
 
ps -c
전체 프로세스의 부모/자식 프로세스 관계를 줄 세우고 싶으면 좋은 명령어에요.
PID: 1      TASK: ffffffc0f8658000  CPU: 1   COMMAND: "init"
  PID: 453    TASK: ffffffc0f7149700  CPU: 2   COMMAND: "ueventd"
  PID: 481    TASK: ffffffc0f7135080  CPU: 1   COMMAND: "logd"
  PID: 482    TASK: ffffffc0f7137300  CPU: 2   COMMAND: "servicemanager"
  PID: 483    TASK: ffffffc0f7134500  CPU: 3   COMMAND: "hwservicemanage"
  PID: 484    TASK: ffffffc0f29ba280  CPU: 2   COMMAND: "vndservicemanag"
  PID: 485    TASK: ffffffc0f29b9700  CPU: 1   COMMAND: "logd.daemon"
  PID: 486    TASK: ffffffc0f29bae00  CPU: 3   COMMAND: "logd.reader"
  PID: 487    TASK: ffffffc0f29bb980  CPU: 3   COMMAND: "logd.writer"
  PID: 488    TASK: ffffffc0f29bc500  CPU: 2   COMMAND: "logd.control"
  PID: 490    TASK: ffffffc0f7132e00  CPU: 1   COMMAND: "keymaster@3.0-s"
  PID: 491    TASK: ffffffc0f7132280  CPU: 0   COMMAND: "vold"
  PID: 499    TASK: ffffffc0f7130000  CPU: 1   COMMAND: "vold"
 
ps -a [pid]
유저 공간에서 생성된 프로세스의 Property(생성 시 Argument..)을 알고 싶을 때 써요. 
crash> ps -a 389
PID: 389    TASK: eb066940  CPU: 0   COMMAND: "sdcard"
ARG: /system/bin/sdcard -u 1023 -g 1023 -l /data/media /mnt/shell/emulated
ENV: VIBE_PIPE_PATH=/dev/pipes
     PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
     LD_LIBRARY_PATH=/vendor/lib:/system/lib
     ANDROID_BOOTLOGO=1
     ANDROID_ROOT=/system
     ANDROID_ASSETS=/system/app
     ANDROID_DATA=/data
     ANDROID_STORAGE=/storage
     ASEC_MOUNTPOINT=/mnt/asec
     LOOP_MOUNTPOINT=/mnt/obb
     BOOTCLASSPATH=/system/framework/core.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/framework2.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/mms-common.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/webviewchromium.jar:/system/framework/com.pompeii.frameworks.jar:/system/framework/qcmediaplayer.jar:/system/framework/org.codeaurora.Performance.jar
     EXTERNAL_ADD_STORAGE=/storage/external_SD
     SECONDARY_STORAGE=/storage/external_SD
     EXTERNAL_STORAGE=/storage/emulated/legacy
     EMULATED_STORAGE_SOURCE=/mnt/shell/emulated
     EMULATED_STORAGE_TARGET=/storage/emulated
     EXTERNAL_ADD_STORAGE=/storage/external_SD
     LD_PRELOAD=/system/lib/libNimsWrap.so
     ANDROID_PROPERTY_WORKSPACE=9,0
 
ps -G
각 프로세스 별로 메모리를 얼마나 쓰고 있는지 각 VSZ/RSS 정보롤 보여주고, 전체 시스템에서 쓰고 있는 비율도 보여줘요. 메모리 디버깅할 때 정말로 유용한 커맨드라고 생각되는군요.
crash64> ps -G
   PID    PPID  CPU       TASK        ST  %MEM     VSZ    RSS  COMM
      0      0   0  ffffffc001580e40  RU   0.0       0      0  [swapper/0]
      0      0   1  ffffffc0f865f300  RU   0.0       0      0  [swapper/1]
// .. 생략..
  17171    981   1  ffffffc035768b80  IN   0.5 1823116  33012  com.android.mms
  17238   2511   2  ffffffc0f1b06780  IN   0.3 1657776  18528  dboxed_process0
  17565    979   1  ffffffc070ab5080  IN   0.7 2344024  41808  droid.bluetooth
  17852      2   1  ffffffc068dc3980  IN   0.0       0      0  [kworker/1:2H]
 
 
 
가끔 모든 프로세스의 스택 주소를 알고 싶을 때가 있어요. 
이럴 때 쓰면 좋은 명령어가 있어 소개합니다. 이 기능이 Trace32보다 확실히 좋은 것 같아요.
 
우선 init process의 TCB(Task descriptor) 주소를 파악해요.
흠, 0xffffffc001580e40이네요.
crash64> p &init_task
$1 = (struct task_struct *) 0xffffffc001580e40 <init_task>
 
모든 프로세스들이 task_struct.tasks 링크드 리스트에 매달려 있잖아요?
그럼 아래 명령어를 쳐서 task_addr란 파일로 리다이렉트 시켜요.
list task_struct.tasks -h 0xffffffc001580e40 > task_addr
 
task_addr 파일을 확인해보면 task descriptor 주소로 가득차 있네요.
crash64> cat task_addr
ffffffc001580e40
ffffffc0f8658000
ffffffc0f8658b80
ffffffc0f8659700
ffffffc0f865b980
ffffffc0f865c500
 
아래 명령어로 모든 프로세스들의 스택 정보를 볼 수 있어요.
crash64> struct task_struct.stack < task_addr
  stack = 0xffffffc001574000 <init_thread_union>
  stack = 0xffffffc0f8644000
  stack = 0xffffffc0f8664000
  stack = 0xffffffc0f8678000
  stack = 0xffffffc0f868c000
  stack = 0xffffffc0f869c000
  stack = 0xffffffc0f86a0000
 
위 명령어로 스택 정보는 확인 가능한데, 어떤 프로세스인지 알 수가 없네요.
이럴 때 아래 명령어를 치면 좋아요.
crash64> task -R stack < task_addr
PID: 0      TASK: ffffffc001580e40  CPU: 0   COMMAND: "swapper/0"
  stack = 0xffffffc001574000 <init_thread_union>,
 
PID: 1      TASK: ffffffc0f8658000  CPU: 1   COMMAND: "init"
  stack = 0xffffffc0f8644000,
 
PID: 2      TASK: ffffffc0f8658b80  CPU: 2   COMMAND: "kthreadd"
  stack = 0xffffffc0f8664000,
 
PID: 3      TASK: ffffffc0f8659700  CPU: 0   COMMAND: "ksoftirqd/0"
  stack = 0xffffffc0f8678000,
 
PID: 6      TASK: ffffffc0f865b980  CPU: 1   COMMAND: "kworker/u8:0"
  stack = 0xffffffc0f868c000,
 
PID: 7      TASK: ffffffc0f865c500  CPU: 0   COMMAND: "rcu_preempt"
  stack = 0xffffffc0f869c000,
 
PID: 8      TASK: ffffffc0f865d080  CPU: 1   COMMAND: "rcu_sched"
  stack = 0xffffffc0f86a0000,
 
PID: 9      TASK: ffffffc0f865dc00  CPU: 0   COMMAND: "rcu_bh"
  stack = 0xffffffc0f86a4000,
 
 

+ Recent posts