약속된 자리와 아무 자리
한 줄 요약
커널은 처음부터 계측기를 갖고 있습니다. tracepoint 는 개발자가 유지하겠다고 약속한 자리이고, kprobe 는 아무 함수에나 거는 자리입니다. ftrace 는 이 자리들을 tracefs 파일로, perf 는 표본 추출과 카운터로 보여 줍니다. eBPF 프로그램이 붙는 곳도 바로 이 자리들입니다.
왜 알아야 하나
eBPF 도구를 고르거나 직접 쓸 때 가장 먼저 정하는 것이 "어디에 붙일까" 입니다. 그 선택지가 tracepoint·kprobe·uprobe·perf 이벤트이고, 각각의 수명과 비용이 다릅니다. kprobe 로 만든 도구는 커널을 올리는 날 조용히 깨질 수 있습니다. 함수 이름이 바뀌었거나, 컴파일러가 그 함수를 다른 함수 안에 끼워 넣어(inline) 자리가 사라졌기 때문입니다. tracepoint 는 이름과 필드가 판을 넘어 유지되도록 관리됩니다.
그리고 tracefs 는 파일에 쓰면 바로 켜지는 손잡이라, 켜 둔 채 잊기 쉽습니다. 켜진 function tracer 는 모든 커널 함수 호출에 비용을 붙입니다.
어떻게 동작하나
ftrace 의 손잡이는 /sys/kernel/tracing 입니다. available_tracers 에서 tracer 를 골라 current_tracer 에 쓰면 켜집니다. function 은 커널 함수 호출을 한 줄씩, function_graph 는 들어가고 나오는 것을 중괄호로 들여 써서 호출 그래프로 보여 줍니다. 모든 함수를 보면 감당이 안 되므로 set_graph_function 으로 뿌리를 좁힙니다. trace-cmd 는 이 파일들을 대신 만져 주고 결과를 trace.dat 으로 저장하는 도구입니다.
tracepoint 는 events/<분류>/<이름>/ 아래에 enable·filter·format 파일을 갖습니다. format 에 필드 목록이 있고, filter 에 comm == "cat" 같은 조건을 걸 수 있습니다. 기록은 trace 파일에 쌓이고, 전체 스위치는 tracing_on 입니다.
kprobe 이벤트는 kprobe_events 에 한 줄을 써서 만듭니다. p:kx_open do_sys_openat2 filename=+0($arg2):ustring 은 "do_sys_openat2 의 첫 명령에 걸고, 두 번째 인자가 가리키는 사용자 공간 문자열을 filename 으로 꺼내라" 는 뜻입니다. 만들면 events/kprobes/kx_open/ 이 생겨 tracepoint 와 같은 방식으로 켜고 끕니다. 커널은 그 함수의 첫 명령을 중단점(int3)이나 ftrace 의 진입 지점으로 바꿔 끼워 제어를 가로챕니다.
perf 는 다른 길로 잽니다. perf stat 은 CPU 카운터와 소프트웨어 이벤트의 개수를 세고, perf record 는 일정한 간격으로 그 순간 CPU 가 어느 함수를 실행하고 있었는지 표본을 뜹니다. -g 를 주면 호출 사슬까지 남기는데, 사슬을 잇는 가장 값싼 방법은 프레임 포인터입니다. 우분투 24.04 는 이 이유로 배포판 전체를 프레임 포인터를 남기고 다시 빌드했습니다.
현장에서 만나는 모습
"CPU 가 바쁜데 무엇 때문인지 모르겠다" 면 perf top 이나 perf record -g 로 표본부터 뜹니다. 추측해서 고친 곳이 실제로는 2% 였다는 일이 흔합니다. 커널 쪽 지연이 의심되면 function_graph 를 좁게 켜서 어느 하위 함수에서 시간이 걸리는지 봅니다.
운영 서버에서 tracefs 를 직접 만졌다면 끝날 때 반드시 되돌립니다. 다른 사람이 켜 둔 tracer 가 몇 주째 돌고 있어서 시스템 콜이 느렸다는 사례가 실제로 있습니다. trace-cmd 와 perf 는 끝나면 스스로 정리하므로 손으로 쓰기보다 안전합니다.
English corner
- tracepoint is ABI: tracepoint 는 사용자 공간과의 약속(ABI)처럼 다뤄진다는 말입니다. 반대로 "kprobes are not ABI" 라고 합니다. 커널 함수 이름에 기대는 도구는 깨져도 커널 탓이 아니라는 뜻입니다.
- overhead is negligible: 비용이 무시할 만하다는 주장입니다. 리뷰어는 보통 "Do you have numbers?" 라고 되묻습니다. 숫자가 없는 주장은 받아들여지지 않습니다.
- inlined: 함수가 호출하는 쪽 안에 끼워 넣어져 따로 존재하지 않는 상태입니다. "The probe point disappeared because the function got inlined."
- hot path: 아주 자주 지나는 코드 경로입니다. "Avoid adding a tracepoint in the hot path without a static key."
다음 실습에서 할 것
tracer 목록을 보고, trace-cmd 로 파일을 여는 함수의 호출 그래프를 받습니다. tracefs 에 직접 써서 exec tracepoint 를 켜고 끄고, do_sys_openat2 에 파일 이름을 꺼내는 kprobe 이벤트를 만듭니다. perf stat 과 perf record 로 작은 프로그램이 시간을 어디에 쓰는지 잰 뒤, 켠 것을 전부 되돌립니다.