bcc로 추적할 수 있는 함수 추가

EEEFFEE·2024년 1월 27일

eBPF

목록 보기
3/3

24.01.27 최초 작성

  • 어떤 페이지에서 page fault가 발생하는지 알아보기

1. handle_mm_fault()을 kprobe를 통해 동작 확인

  • handle_mm_fault()를 감지해 프로세스 이름, 주소, 플래그를 출력하는 코드

from bcc import BPF

prog = """
#include <linux/sched.h>
#include <linux/mm.h>

BPF_HASH(start, u32);
BPF_PERF_OUTPUT(events);

struct data_t {
	u32 pid;
    u32 cpu;
    char comm[16];
    unsigned long address;
    unsigned int flags;
}

int kprobe__handle_mm_fault(struct pt_regs *ctx, struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, unsigned int flags) {
    u32 pid = bpf_get_current_pid_tgid();
    u32 cpu = bpf_get_smp_processor_id();
    u64 ts = bpf_ktime_get_ns();
    struct data_t data = {};
    
    bpf_get_current_comm(&data.comm, sizeof(data.comm));
    
    for (int i = 0; i < sizeof(data.comm); i++) {
        if (data.comm[i] == 'p' && data.comm[i + 1] == 'y' && data.comm[i + 2] == 't' && data.comm[i + 3] == 'h' && data.comm[i + 4] == 'o' && data.comm[i + 5] == 'n') {
            data.pid = pid;
            data.cpu = cpu;
            data.address = address;
            data.flags = flags;
            
            events.perf_submit(ctx, &data, sizeof(data));
            start.update(&pid, &ts);
            
            break;
        }
    }
    return 0;
}
"""

b = BPF(text=prog)

def print_event(cpu, data, size):
    event = b["events"].event(data)
    print("PID: %d, CPU: %d, Comm: %s, Address: %lx, Flags: %s" % (event.pid, event.cpu, event.comm, event.address, format_flags(event.flags)))

def format_flags(flags):
    flags_str = []
    if flags & 0x01: flags_str.append("FAULT_FLAG_ALLOW_RETRY")
    if flags & 0x02: flags_str.append("FAULT_FLAG_RETRY_NOWAIT")
    if flags & 0x04: flags_str.append("FAULT_FLAG_KILLABLE")
    if flags & 0x08: flags_str.append("FAULT_FLAG_USER")
    if flags & 0x10: flags_str.append("FAULT_FLAG_REMOTE")
    if flags & 0x20: flags_str.append("FAULT_FLAG_TRIED")
    if flags & 0x40: flags_str.append("FAULT_FLAG_ALLOW_ASYNC")
    if flags & 0x80: flags_str.append("FAULT_FLAG_MKWRITE")
    if flags & 0x100: flags_str.append("FAULT_FLAG_NO_KILL")
    return ", ".join(flags_str)

b["events"].open_perf_buffer(print_event)

print("Tracing handle_mm_fault for python processes... Ctrl-C to end.")
while True:
    try:
        b.perf_buffer_poll()
    except KeyboardInterrupt:
        exit()

  • 1GB 공간을 10MB로 공간을 나눠 할당하는 어플리케이션

import time 

MB = 1 * int(1e6)

blocks = 100

memory_blocks = []

for _ in range(blocks):
	memory_blocks.append(bytearray(MB))
    
print("1GB of memory has been allocated.")

2. handle_pte_fault()를 통해 page fault 발생한 페이지 확인

  • handle_pte_fault()에서 인자로 받은 struct vm_faultpgd오프셋 필드를 확인
  • 아래 코드를 실행하면 handle_pte_fault()가 없다고 오류 발생
  • 이를 해결하기 위해 handle_pte_fault()
    1. 해당 함수의 static 지워 다른 소스코드에서 참조할 수 있도록 설정
    2. 해당 함수가 정의된 위치 아래 EXPORT_SYMBOL_GPL(handle_pte_fault);입력해 다른 모듈에서도 참조할 수 있도록 설정
    3. 관련 헤더파일에 함수를 추가해 유저가 볼 수 있도록 설정

from bcc import BPF

bpf_text = """
#include <linux/sched.h>
#include <linux/mm_types.h>
#include <linux/mm.h>

struct data_t {
    u32 pid;
    u64 cpu;
    u64 addr;
    u64 pgoff;
    char comm[TASK_COMM_LEN];
};

BPF_PERF_OUTPUT(events);

int kprobe__handle_pte_fault(struct pt_regs *ctx, struct vm_fault *vmf) {
    struct data_t data = {};
    u64 pid_tgid = bpf_get_current_pid_tgid();
    data.pid = pid_tgid >> 32;
    bpf_get_current_comm(&data.comm, sizeof(data.comm));
    
    if (data.comm[0] == 'p' && data.comm[1] == 'y' && data.comm[2] == 't' && data.comm[3] == 'h' && data.comm[4] == 'o' && data.comm[5] == 'n') {
        data.cpu = bpf_get_smp_processor_id();
        data.addr = vmf->address;
        data.pgoff = vmf->pgoff;
        events.perf_submit(ctx, &data, sizeof(data));
    }
    return 0;
}
"""

bpf = BPF(text=bpf_text)
bpf.attach_kprobe(event="handle_pte_fault", fn_name="kprobe__handle_pte_fault")

def print_event(cpu, data, size):
    event = b["events"].event(data)
    print(f"CPU: {event.cpu}, PID: {event.pid}, Comm: {event.comm}, Address: {hex(event.addr)}, Pgoff: {event.pgoff}")

b["events"].open_perf_buffer(print_event)

while True:
    try:
        bpf.perf_buffer_poll()
    except KeyboardInterrupt:
        exit()

0개의 댓글