LTTng (Linux Trace Toolkit ng) — 고성능 시스템 추적
LTTng(Linux Trace Toolkit next generation)는 리눅스 커널과 유저스페이스를 동시에 낮은 오버헤드로 추적할 수 있는 오픈소스 추적 프레임워크입니다. RCU 기반 lockless 링 버퍼를 사용하여 프로덕션 환경에서도 초당 수천만 이벤트를 기록하고, 바이너리 CTF(Common Trace Format)로 저장하여 babeltrace2나 Trace Compass로 분석합니다. lttng-modules(커널 트레이서), lttng-ust(유저스페이스 트레이서), lttng-tools(제어 CLI)의 구성, 트레이스포인트 정의와 활성화, ftrace·perf·SystemTap과의 비교, 실전 분석 워크플로를 정리합니다.
핵심 요약
- lttng-modules — 커널 공간 트레이서입니다. 커널 tracepoint, 시스템 콜, kprobe, kretprobe를 캡처합니다.
- lttng-ust — 유저스페이스 트레이서입니다. 응용 프로그램에 링크하거나
LD_PRELOAD로 주입하여 유저스페이스 이벤트를 기록합니다. - lttng-tools —
lttngCLI로 세션 생성, 이벤트 활성화, 기록 시작/정지를 제어합니다. - CTF (Common Trace Format) — LTTng의 바이너리 출력 형식입니다. 패킷/이벤트 헤더, 스트림 ID, 타임스탬프를 압축적으로 저장합니다.
- babeltrace2 — CTF 트레이스를 텍스트로 변환하고 Python 플러그인으로 분석합니다.
- Trace Compass — Eclipse 기반 GUI 분석 도구로, 스레드 뷰, CPU 활성 뷰, 지연 분석, 임계 경로 분석을 제공합니다.
- 세션 → 채널 → 이벤트 규칙 — LTTng의 3단계 계층입니다. 세션에 채널(링 버퍼)을 만들고, 채널에 이벤트 규칙을 추가합니다.
- 오버헤드 — 프로덕션 적합 수준으로, 일반 tracepoint 기록 시 약 100~200ns/이벤트입니다.
단계별 이해
- 설치 확인 —
lttng-modules,lttng-ust,lttng-tools가 설치되어 있는지 확인합니다. - 세션 생성 —
lttng create my-session으로 트레이스 세션을 만듭니다. - 이벤트 활성화 —
lttng enable-event -k -a(커널 전체) 또는 특정 이벤트를 선택합니다. - 기록 시작/정지 —
lttng start로 기록을 시작하고, 문제 재현 후lttng stop합니다. - 분석 —
babeltrace2 ~/lttng-traces/my-session/으로 이벤트를 확인하거나 Trace Compass에서 시각화합니다.
아키텍처 개요
LTTng는 세 개의 독립 패키지로 구성됩니다.
| 패키지 | 역할 | 위치 |
|---|---|---|
lttng-modules | 커널 공간 트레이서 (트레이스포인트 프로브, 링 버퍼) | 커널 모듈 |
lttng-ust | 유저스페이스 트레이서 라이브러리 | 공유 라이브러리 |
lttng-tools | 세션 데몬(lttng-sessiond), relay 데몬, lttng CLI | 유저스페이스 데몬 |
설치
LTTng는 세 패키지가 협력하여 동작합니다. lttng-modules는 커널 공간 트레이서로, DKMS를 통해 현재 커널에 맞춰 자동으로 컴파일됩니다. liblttng-ust-dev는 유저스페이스 트레이서 라이브러리로, 애플리케이션에 직접 링크합니다. lttng-tools는 세션 데몬(lttng-sessiond)과 lttng CLI를 포함하는 제어 평면입니다. 세 패키지가 모두 설치되어야 커널·유저스페이스 동시 추적이 가능합니다.
# Ubuntu/Debian
sudo apt install lttng-tools lttng-modules-dkms liblttng-ust-dev babeltrace2
# Fedora/RHEL
sudo dnf install lttng-tools lttng-modules liblttng-ust-devel babeltrace2
# 커널 모듈 로드 확인
lsmod | grep lttng
modprobe lttng-tracer # 명시적 로드 (보통 lttng 명령 실행 시 자동)
기본 사용법 — lttng CLI
LTTng의 데이터 모델은 세션 → 채널 → 이벤트 규칙의 3단계 계층입니다. 세션은 트레이스 파일이 저장될 경로와 모드(일반·스냅샷·라이브)를 관리합니다. 채널은 링 버퍼의 크기·오버플로 정책·수신 타임아웃을 정의합니다. 이벤트 규칙은 기록할 이벤트의 이름·필터 표현식·추가 컨텍스트(pid, procname 등)를 지정합니다. 채널을 명시적으로 생성하지 않으면 기본 채널 channel0이 자동으로 생성됩니다.
커널 트레이싱
# 1. 세션 생성
lttng create my-kernel-session
# 2. 이벤트 활성화
# 커널 전체 tracepoint 활성화
lttng enable-event --kernel --all
# 특정 이벤트만 활성화 (권장 — 오버헤드 감소)
lttng enable-event -k sched_switch,sched_wakeup,sched_process_fork
lttng enable-event -k 'irq_*' # 와일드카드
# 시스템 콜 추적
lttng enable-event -k --syscall openat,read,write,close
# 모든 시스템 콜 추적 (오버헤드 높음)
lttng enable-event -k -a --syscall
# 3. 트레이싱 시작
lttng start
# 4. 부하 발생 / 문제 재현
sleep 5
some_workload.sh
# 5. 트레이싱 중지
lttng stop
# 6. 세션 정보 확인
lttng view # 빠른 텍스트 출력 (babeltrace2 호출)
# 7. 세션 종료 및 정리
lttng destroy
유저스페이스 트레이싱
# 유저스페이스 세션 생성
lttng create my-ust-session
# 애플리케이션 이벤트 활성화 (와일드카드)
lttng enable-event -u 'my_app:*'
# 특정 이벤트
lttng enable-event -u my_app:request_start,my_app:request_end
# 공유 라이브러리 함수 추적 (LD_PRELOAD 방식)
lttng enable-event -u --function malloc:entry
lttng start
# 애플리케이션 실행
LD_PRELOAD=liblttng-ust-dl.so ./my_application
lttng stop
lttng destroy
커스텀 트레이스포인트 정의 (lttng-ust)
lttng-ust 트레이스포인트는 헤더 파일을 두 번 포함하는 독특한 구조로 구현됩니다. 첫 번째 포함에서는 tracepoint() 호출 매크로를 생성하고, TRACEPOINT_CREATE_PROBES를 정의한 뒤 다시 포함하면 실제 프로브 핸들러(CTF 직렬화 코드)가 컴파일됩니다. 이 방식은 인터페이스 선언과 구현을 헤더 하나로 통합하면서도, 컴파일 유닛별로 프로브 등록 코드를 분리하는 lttng-ust 고유의 패턴입니다. 비활성화된 트레이스포인트는 빈 인라인 함수로 컴파일되어 오버헤드가 사실상 제로입니다.
트레이스포인트 헤더 파일 (my_app_tp.h)
#undef TRACEPOINT_PROVIDER
#define TRACEPOINT_PROVIDER my_app
#undef TRACEPOINT_INCLUDE
#define TRACEPOINT_INCLUDE "./my_app_tp.h"
#if !defined(_MY_APP_TP_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
#define _MY_APP_TP_H
#include <lttng/tracepoint.h>
TRACEPOINT_EVENT(
my_app, /* 프로바이더 이름 */
request_start, /* 이벤트 이름 */
TP_ARGS(
int, request_id,
const char *, url
),
TP_FIELDS(
ctf_integer(int, request_id, request_id)
ctf_string(url, url)
)
)
TRACEPOINT_EVENT(
my_app,
request_end,
TP_ARGS(
int, request_id,
int, status_code,
long long, duration_us
),
TP_FIELDS(
ctf_integer(int, request_id, request_id)
ctf_integer(int, status_code, status_code)
ctf_integer(long long, duration_us, duration_us)
)
)
#endif /* _MY_APP_TP_H */
#include <lttng/tracepoint-event.h>
트레이스포인트 구현 파일 (my_app_tp.c)
#define TRACEPOINT_CREATE_PROBES
#define TRACEPOINT_DEFINE
#include "my_app_tp.h"
애플리케이션에서 사용
#define TRACEPOINT_DEFINE
#include "my_app_tp.h"
void handle_request(int id, const char *url)
{
struct timeval start, end;
gettimeofday(&start, NULL);
/* 트레이스포인트 호출 */
tracepoint(my_app, request_start, id, url);
/* 실제 처리 */
int status = process_request(url);
gettimeofday(&end, NULL);
long long us = (end.tv_sec - start.tv_sec) * 1000000LL +
(end.tv_usec - start.tv_usec);
tracepoint(my_app, request_end, id, status, us);
}
# 컴파일
gcc -o my_app my_app.c my_app_tp.c -llttng-ust -ldl
채널 설정
채널은 링 버퍼 파라미터를 제어합니다. 고주파 이벤트와 저주파 이벤트를 분리하면 효율이 높아집니다.
LTTng의 링 버퍼는 CPU별로 독립된 서브-버퍼 배열로 구성됩니다. 프로듀서(이벤트를 기록하는 커널 코드)는 현재 활성 서브-버퍼에 CTF 레코드를 원자적으로 기록하고, 서브-버퍼가 가득 차면 다음 서브-버퍼로 교체합니다(서브-버퍼 로테이션). 컨슈머(lttng-sessiond 또는 lttng-relayd)는 완료된 서브-버퍼를 mmap으로 읽어 디스크나 네트워크로 내보냅니다. CPU별 버퍼를 사용하기 때문에 크로스-CPU 락 없이 수천만 이벤트/초를 기록할 수 있습니다.
# 빠른 이벤트용 채널 (큰 링 버퍼, 오버라이트 모드)
lttng add-channel -k fast-channel \
--subbuf-size 256K \
--num-subbuf 8 \
--overwrite # 꽉 차면 가장 오래된 데이터 덮어씀
# 느린 이벤트용 채널 (작은 버퍼, 디스카드 모드)
lttng add-channel -k slow-channel \
--subbuf-size 64K \
--num-subbuf 4
# --discard (기본값): 꽉 차면 신규 이벤트 버림
# 특정 채널에 이벤트 추가
lttng enable-event -k --channel=fast-channel sched_switch,sched_wakeup
lttng enable-event -k --channel=slow-channel 'block_*'
필터와 컨텍스트
필터 표현식은 이벤트 활성화 시 --filter로 지정하며, 이벤트 페이로드 필드를 참조합니다. 표현식은 커널 측에서 평가되므로 링 버퍼에 기록되기 전에 걸러져 오버헤드를 줄입니다. 컨텍스트(add-context)는 이벤트에 PID, TID, 프로세스 이름, PMU 카운터 등 부가 정보를 자동으로 첨부합니다. 컨텍스트 정보는 이벤트 기록 시 함께 저장되므로 후처리 시 프로세스별 상관 분석이 가능합니다.
# 필터 조건으로 이벤트 선택 (pid 기준)
lttng enable-event -k sched_switch \
--filter 'next_pid == 1234 || prev_pid == 1234'
# 이벤트에 컨텍스트 정보 추가
lttng add-context -k -t pid # PID
lttng add-context -k -t tid # TID
lttng add-context -k -t procname # 프로세스 이름
lttng add-context -k -t perf:cpu:cpu-cycles # PMU 카운터
# 유저스페이스 컨텍스트
lttng add-context -u -t vpid # 가상 PID
lttng add-context -u -t vtid # 가상 TID
lttng add-context -u -t ip # 명령어 포인터
스냅샷과 라이브 모드
일반 모드에서는 서브-버퍼가 가득 찰 때마다 파일로 플러시되지만, 스냅샷 모드는 서브-버퍼를 플러시하지 않고 링 버퍼를 순환 메모리로만 유지합니다. lttng snapshot record를 호출하는 순간의 최근 N초 데이터만 저장하므로, 드물게 발생하는 버그나 초당 수백만 이벤트를 생성하는 고빈도 시스템에서 디스크 I/O를 최소화하면서 문제 직전 컨텍스트를 보존하는 데 적합합니다. 라이브 스트리밍 모드는 lttng-relayd를 통해 트레이스를 원격 호스트로 실시간 전달하며, babeltrace2 net://<host>나 Trace Compass가 실시간으로 구독할 수 있습니다.
# 스냅샷 모드: 이벤트는 링 버퍼에만 보관, 명시적으로 덤프
lttng create my-session --snapshot
lttng enable-event -k -a
lttng start
# 문제 발생 시 스냅샷 저장
lttng snapshot record --name crash-snapshot
lttng destroy
# 라이브 스트리밍: relayd를 통해 실시간 분석 가능
lttng-relayd &
lttng create live-session --live 100000 \ # 100ms 타임아웃
--set-url net://localhost
lttng enable-event -k sched_switch
lttng start
# babeltrace2로 실시간 수신
babeltrace2 net://localhost
babeltrace2 분석
babeltrace2는 플러그인 기반 메시지 파이프라인으로 동작합니다. CTF 소스 플러그인(source.ctf.fs)이 바이너리 트레이스 파일을 읽고, 선택적 필터 플러그인이 이벤트를 걸러내며, 싱크 플러그인이 텍스트(sink.text.pretty)나 다른 CTF(sink.ctf.fs)로 출력합니다. 기본 동작인 babeltrace2 <경로>는 이 파이프라인을 자동으로 구성합니다. Python bt2 모듈을 이용하면 이벤트 순회·집계·통계를 스크립트로 자동화할 수 있어, 레이턴시 분석·이벤트 상관 관계·커스텀 리포트 생성에 활용합니다.
# 텍스트 변환
babeltrace2 ~/lttng-traces/my-session-YYYYMMDD/
# 특정 이벤트만 필터
babeltrace2 ~/lttng-traces/my-session/ \
--plugin-path /usr/lib/babeltrace2/plugins/ \
-- --names payload
# 이벤트 필터 (Python 플러그인)
babeltrace2 ~/lttng-traces/my-session/ \
--component filter.lttng-utils.debug-info \
--component sink.text.pretty
# 통계 (이벤트 횟수)
babeltrace2 ~/lttng-traces/ \
--component sink.ctf.fs --params 'path="output/"'
# Python으로 CTF 분석
python3 <<'EOF'
import bt2
for msg in bt2.TraceCollectionMessageIterator('~/lttng-traces/my-session/'):
if type(msg) is bt2._EventMessageConst:
ev = msg.event
print(f"{msg.default_clock_snapshot.ns_from_origin} "
f"{ev.name}: "
f"{dict(ev.payload_field)}")
EOF
Trace Compass 시각화
Trace Compass는 LTTng CTF 트레이스를 그래픽으로 분석하는 Eclipse 기반 IDE입니다.
- Control Flow View: 각 스레드의 상태 변화(실행/대기/차단)를 타임라인으로 표시합니다.
- Resources View: CPU별 활성 스레드와 IRQ를 한눈에 보여줍니다.
- Critical Path Analysis: 특정 스레드가 지연된 이유(락, I/O, CPU 선점 등)를 임계 경로로 추적합니다.
- Latency Analysis: 시스템 콜/인터럽트 지연 시간 분포 히스토그램을 제공합니다.
- XML 분석기: 커스텀 상태 기계와 분석 패턴을 XML로 정의하여 적용합니다.
LTTng vs ftrace vs perf
| 항목 | LTTng | ftrace | perf |
|---|---|---|---|
| 출력 형식 | CTF 바이너리 | 텍스트 (ring buffer) | perf.data 바이너리 |
| 오버헤드 | 매우 낮음 (lockless) | 낮음 | 중간~높음 (샘플링) |
| 커널+유저 동시 추적 | 가능 (lttng-ust) | 부분적 | 가능 (stack unwinding) |
| 프로덕션 적합성 | 높음 | 중간 | 중간 |
| GUI 분석 도구 | Trace Compass | KernelShark | Flame Graph, hotspot |
| PMU 이벤트 | 컨텍스트로 제한적 | 불가 | 핵심 기능 |
| 스트리밍 지원 | relayd 기반 | 없음 | 없음 |
| 설정 복잡도 | 중간 | 낮음 | 낮음 |
실전 시나리오
다음 시나리오들은 성능 디버깅 현장에서 자주 사용하는 패턴입니다. 핵심은 수집할 이벤트를 최소화하는 것입니다. 모든 커널 이벤트(-a)를 활성화하면 오버헤드가 크게 증가하고 링 버퍼 오버플로로 정작 중요한 이벤트를 잃을 수 있습니다. 목적에 맞는 이벤트 그룹(스케줄링, I/O, 네트워크 등)만 선택하고, 필터 표현식으로 대상 프로세스나 PID를 좁히는 것이 좋은 습관입니다.
시나리오 1: 레이턴시 스파이크 분석
# 스케줄링 이벤트 + 시스템 콜 추적
lttng create latency-debug
lttng enable-event -k sched_switch,sched_wakeup,sched_wakeup_new
lttng enable-event -k --syscall nanosleep,futex,epoll_wait
lttng add-context -k -t tid -t procname
lttng start
# 레이턴시 스파이크 재현 (30초)
sleep 30
lttng stop
lttng destroy
# Trace Compass의 Critical Path Analysis로
# 지연의 원인이 CPU 선점인지, 락 경합인지, I/O인지 파악
시나리오 2: 네트워크 이벤트 추적
lttng create net-trace
lttng enable-event -k 'net_*'
lttng enable-event -k 'skb_*'
lttng enable-event -k --syscall send,recv,sendto,recvfrom,sendmsg,recvmsg
lttng start
# 네트워크 부하 발생
iperf3 -c 192.168.1.1 -t 10
lttng stop
lttng destroy
babeltrace2 ~/lttng-traces/net-trace*/ | grep net_dev_xmit
시나리오 3: 부팅 시간 분석
# 커널 커맨드라인에 추가:
# trace_event=initcall:* lttng_boot_alloc_size=8M
# systemd 서비스로 LTTng 세션을 early-boot부터 활성화
# /etc/lttng/sessions/ 에 세션 설정 파일 배치
커널 모듈에 트레이스포인트 추가
커널 모듈에 정적 트레이스포인트를 추가하면 lttng-modules가 자동으로 감지하여 lttng enable-event -k로 활성화할 수 있습니다. 커널 내장 DECLARE_TRACE()/DEFINE_TRACE() 매크로는 tracepoint 인프라에 이벤트를 등록합니다. 비활성 상태에서는 NOP 패치로 대체되어 오버헤드가 없습니다. lttng-ust의 유저스페이스 트레이스포인트와 달리, 커널 트레이스포인트는 커널 빌드 시스템을 통해 컴파일되며 trace/events/ 헤더 구조를 따릅니다.
/* my_module_trace.h */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM my_module
#if !defined(_MY_MODULE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
#define _MY_MODULE_TRACE_H
#include <linux/tracepoint.h>
DECLARE_TRACE(my_module_event,
TP_PROTO(int id, const char *msg),
TP_ARGS(id, msg));
#endif
#define TRACE_INCLUDE_PATH .
#define TRACE_INCLUDE_FILE my_module_trace
#include <trace/define_trace.h>
# 커널 tracepoint를 LTTng로 캡처
lttng enable-event -k my_module:my_module_event
babeltrace2 Python API (bt2)
babeltrace2의 Python 바인딩(bt2)을 사용하면 CTF 트레이스를 프로그래밍 방식으로 분석하고 커스텀 리포트를 생성할 수 있습니다.
기본 이벤트 순회
import bt2
# CTF 트레이스 디렉토리 열기
trace = bt2.TraceCollectionMessageIterator(
bt2.ComponentSpec.from_named_plugin_and_component_class(
"ctf", "fs",
params={"paths": ["/home/user/lttng-traces/my-session"]}
)
)
for msg in trace:
if not isinstance(msg, bt2._EventMessageConst):
continue
event = msg.event
# 특정 이벤트만 필터링
if event.name == "sched_switch":
ts_ns = msg.default_clock_snapshot.ns_from_origin
print(f"[{ts_ns:20d}] {event['prev_comm']:16s} -> {event['next_comm']:16s}")
스케줄링 레이턴시 분석 스크립트
import bt2
from collections import defaultdict
wakeup_times = {} # tid -> wakeup timestamp
latencies = defaultdict(list)
trace = bt2.TraceCollectionMessageIterator(
bt2.ComponentSpec.from_named_plugin_and_component_class(
"ctf", "fs", params={"paths": ["/home/user/lttng-traces/latency-session"]}
)
)
for msg in trace:
if not isinstance(msg, bt2._EventMessageConst):
continue
event = msg.event
ts = msg.default_clock_snapshot.ns_from_origin
if event.name == "sched_wakeup":
tid = int(event["tid"])
wakeup_times[tid] = ts
elif event.name == "sched_switch":
tid = int(event["next_tid"])
if tid in wakeup_times:
latency_us = (ts - wakeup_times.pop(tid)) / 1000
comm = str(event["next_comm"])
latencies[comm].append(latency_us)
# 통계 출력
for comm, lats in sorted(latencies.items()):
avg = sum(lats) / len(lats)
mx = max(lats)
print(f"{comm:20s} avg={avg:8.2f}us max={mx:8.2f}us n={len(lats)}")
고급 필터와 트리거
LTTng의 필터 표현식은 이벤트 필드에 대한 조건식을 지원합니다. 트리거(trigger)는 조건 만족 시 다른 세션 명령을 자동 실행합니다.
# CPU 마이그레이션 이벤트 중 특정 프로세스만 기록
lttng add-trigger \
--condition event-rule-matches \
--name sched_migrate_task \
--filter 'comm == "my_app"' \
--action notify
# 레이턴시가 임계값 초과 시 스냅샷 자동 저장
lttng add-trigger \
--condition event-rule-matches \
--name my_app:high_latency \
--filter 'latency_us > 1000' \
--action snapshot-session my-session
# 특정 함수 진입/반환 시간 측정 (kretprobe)
lttng enable-event --kernel --function my_driver_process_packet
lttng enable-event --kernel --function my_driver_process_packet.return
# 결과에서 레이턴시 계산
babeltrace2 ~/lttng-traces/session/ | grep my_driver_process_packet
lttng create --live 200000로 세션을 생성하면 babeltrace2가 트레이싱 도중에도 실시간으로 이벤트를 읽을 수 있습니다. babeltrace2 net://localhost로 연결합니다.
문제 해결
| 증상 | 원인 | 해결 |
|---|---|---|
| 이벤트가 기록되지 않음 | 모듈 미로드 | lsmod | grep lttng; modprobe lttng-tracer |
| 세션 생성 실패 | sessiond 미실행 | lttng-sessiond --daemonize |
| CTF 파싱 오류 | babeltrace2 버전 불일치 | babeltrace2 최신 버전 설치 |
| 링 버퍼 오버플로 | 버퍼가 너무 작음 | --subbuf-size/--num-subbuf 증가 |
| UST 이벤트 없음 | liblttng-ust 링크 누락 | -llttng-ust -ldl 링크 옵션 확인 |
| 권한 오류 | tracing 그룹 미포함 | sudo usermod -aG tracing $USER |