정보공간_1

[3기 대전 김재원] Linux Kernel Debugging - Dynamic Probes(1) 본문

IT 놀이터/Elite Member Tech & Talk

[3기 대전 김재원] Linux Kernel Debugging - Dynamic Probes(1)

알 수 없는 사용자 2013. 1. 23. 00:07

안녕하세요! 대전멤버십 22-1기 김재원 입니다.

저는 Linux Kernel Debugging 방법들에 대해서 소개 할까 합니다.

 

다른 어플리케이션들과 달리 Kernel은 디버깅 하기가 쉽지가 않습니다.

커널을 수정하고 다시 빌드하여 사용하려면 빌드하는데 엄청난 시간이 소요됩니다. (최신형 컴퓨터라도 최소 30분 이상은 걸립니다.) 학교 수업으로 System Call 추가를 해보신분이라면 그 지루함을 알 수 있으실 겁니다.

간단한 Function 동작을 추가하여 확인하는데도 코딩하고 다시 빌드해야 하는 번거로움이 있습니다.

그래서 많이 사용하는 방법중의 한 방법인 Dynamic Probe 를 소개 합니다.

 

- Dynamic Probe(동적 프로) -

Dynamic Probe는 커널을 다시 빌드 하지 않고 동적으로 디버깅을 하는 하나의 방법입니다. 

Kernel 루틴에 원하는 코드를 디버깅 하거나 패치하는 작업도 가능해 집니다. 예를 들어 open함수가 실행될때 특정 로그를 남기거나 open함수를 수정 할 수 있도록 할 수 있습니다.  또한, Dynamic Probe는 대화식 디버거를 사용할 수 없거나 사용하기 어려운 상용 환경에서  특히 유용하게 활용할 수 있습니다.

Dynamic Probe 에는 3가지 타입의 Probe가 존재합니다.

첫번째는 Kprobe입니다. Kprobe는 가장 기본적인 Probe로 커널에 어떠한 가상의 인스트럭션을 삽입하는 것입니다.

두번째는 Jprobe입니다. Jprobe는 Jumper Probe의 약자로 Kernel Function에 진입점(entry-point)에 삽입됩니다. Entry-Point에 들어가 함수의 인자 접근이 편리 합니다.

세번째는 Kretprobe는 Return Probe의 약자로 특정 Function이 return 되는 시점에 동작하는 것입니다. Kretprobe는 Function에 return 값을 받아 올 수 있습니다.

 

Dynamic Probe방법은 모두 Kprobe를 기반으로 특정 기능을 제공 하는 것입니다.

그러므로 가장 기초가 되는  Kprobe를 먼저 소개 하겠습니다.

우선 Kprobe의 동작 원리를 살펴 보고 직접 fork함수를 추적 해보도록 하겠습니다.

동작원리.

Kprobes는 Kernel Module형태로 제작을 하며 register/unregister Fuction을 통해 등록과 해제를 합니다.

Kprobe가 register될때 Kprobes는 Breakpoint를 건 Instruction의 첫번째 Byte를 Kprobe구조체의 opcode에 복사하여 따로 저장을 해둡니다.(그림에서는 Breakpoint를 Instruction2에 걸어 놓았습니다)

그리고 Instruction2의 첫번에 Byte를 변경시켜 BreakPoint Excepton이 발생하도록 합니다.

그리고 CPU가 BreakPoint를 건 Instruction을 지날때 Instruction2 전체를 복사하고 Kprobe의 notifier_call_chain mechanism 을 지나 가도록 합니다. notifier_call_chain mechanism에서는  Kprobe를 register할때 저장한 "Pre_handler"가 실행 됩니다.

Pre_handler에서 연결된 Debugging Code를 실행하고 끝이 나면 저장 해두었던 Instruction2를 실행한 후 "Post_handler"가 실행 됩니다.

Post_handler에서는 Pre_handler와 마찬가지로 미리 등록된 Debugging Code를 실행 후 종료후 다음 Instruction을 순차적으로 실행하게 됩니다.

 

Kprobes의 구조체를 살펴 보도록 하겠습니다.

include/linux/kprobes.h 

 
struct kprobe {
     struct hlist_node hlist; 
     struct list_head list;
     unsigned long nmissed;

     kprobe_opcode_t *addr;
     const char *symbol_name;
     unsigned int offset;

     kprobe_pre_handler_t pre_handler;
     kprobe_post_handler_t post_handler;
     kprobe_fault_handler_t fault_handler;
     kprobe_break_handler_t break_handler;

     kprobe_opcode_t opcode;
     struct arch_specific_insn ainsn;
     u32 flags;
};

붉은색은 사용자가 채워야 하는 부분 입니다. *addr은 사용자가 추적할 Fuction의 주소입니다. 보통 커널을 빌드후 System.map에 함수의 주소가 나타나 있습니다. 아니면 symbol_name을 통하여 찾아낼 수 있습니다. offset은 이 Fuction의 몇번째 Instruction을 추적할 지 offset을 지정할 수 있습니다.

파란색은 pre/post/fault/break handler 중 원하는 함수를 등록하시면 됩니다.

pre_handler는 Break Pont가 지정된 instruction이 실행되기 전에 호출되며 Post_handler는 instruction이 실행된 후에 실행됩니다.

fault_handler는 kprobe가 실행되는 도중 exception이 발생된 경우

break_handler는 kprobe가 실행되는 도중 또는 다른 곳에서 breakpoint exception이 발생한 경우에 호출 됩니다.

 

이런한 Dynamic Probe의 일종인 Kprobe를 이용하여 사용하는 대표적인 디버깅 방법을 소개 하자면

특정함수에 대해 로그를 남기거나 인자값을 수정하는 방법을 많이 사용합니다.

예를 들어 커널에서 "fork"함수를 호출했을때 printk를 이용해 로그를 남길 수 있습니다.

kprobe를 이용하여 do_fork함수를 추적하고 handler를 등록후 Debugging Code에 printk를 통하여 로그를 남길 수 있습니다. Debugging Code에서 get_current()를 이용한다면 do_fork를 사용하는 프로세스의 task_struct를 가지고 올 수 있습니다.

또하나의 재미 있는 디버깅 방법도 있습니다. linux를 사용하는 System에서 함수의 인자값이 수정이 필요 하고 리부팅이나 커널 수정을 하지 못할경우 kprobe를 이용해 특정 Fuction에서 지정된 인자값을 바꿀 수 있습니다.

만약 int a= 1로 선언하였으나 int a=0로 되어야 할경우 int a =1에 해당하는 인스트럭션의 Post_handler에서 a = 0 으로 다시 선언해주면 리부팅 없이 커널의 소스를 수정 할 수 있습니다.

Kprobe는 참 재미 있는 기능이 많은 것 같습니다!!

 

자 이제 Kprobe를 사용하는 실습을 한번 해보도록 하겠습니다.

구현방법.

위에서 예를 들었던것과 같이 "do_fork"함수를 추적하여 do_fork함수를 실행하는 process의 pid를 로그로 남겨 보도록 하겠습니다.

우선 Module을 제작 하도록 하겠습니다.

Kprobe를 사용하기 위해서는 커널을 빌드할때 CONFIG_KPROBES = y가 되어 있어야 합니다.


Kernel Module 

 


위와 같이 kprobe를 등록한후 빌드하여 로그를 보시겠습니다.


로그 
 

dmesg를 통하여 로그를 확인 하면 위와 같습니다.

위와 같이 "do_fork"를 호출함 프로세스의 pid, state, comm이 나오게 됩니다.


이번 달에는 Dynamic Probe의 가장 기본인 Kprobe의 동작 원리와 구현방법을 알아 보았습니다.

그리고, 다음 시간에는 Jprobe와 kretprobe를 알아 보도록 하겠습니다.


Kprobe에 대해서 좀더 자세한 내용은 Kernel소스의 "Documentation/kprobes.txt"에 레퍼런스 문서가 있습니다. 

또한 sample역시 Kernel소스의 "sample/kprobe_example.c"에 있으니 참고 하시면 좋습니다.