LTTng (Linux Trace Toolkit ng) — 고성능 시스템 추적

LTTng(Linux Trace Toolkit next generation)는 리눅스 커널과 유저스페이스를 동시에 낮은 오버헤드로 추적할 수 있는 오픈소스 추적 프레임워크입니다. RCU 기반 lockless 링 버퍼를 사용하여 프로덕션 환경에서도 초당 수천만 이벤트를 기록하고, 바이너리 CTF(Common Trace Format)로 저장하여 babeltrace2Trace Compass로 분석합니다. lttng-modules(커널 트레이서), lttng-ust(유저스페이스 트레이서), lttng-tools(제어 CLI)의 구성, 트레이스포인트 정의와 활성화, ftrace·perf·SystemTap과의 비교, 실전 분석 워크플로를 정리합니다.

전제 조건: ftrace / Tracepoints디버깅 & 트러블슈팅 문서를 먼저 읽으세요. LTTng는 커널 tracepoint 인프라 위에서 동작합니다.
일상 비유: LTTng는 항공기 블랙박스와 같습니다. 비행 중 모든 계기 데이터를 지속적으로 기록하되, 기록 자체가 비행 성능에 영향을 미치지 않습니다. 사고 후 블랙박스를 꺼내 분석하듯, 시스템 문제 발생 후 트레이스를 재생하여 인과관계를 파악합니다.

핵심 요약

  • lttng-modules — 커널 공간 트레이서입니다. 커널 tracepoint, 시스템 콜, kprobe, kretprobe를 캡처합니다.
  • lttng-ust — 유저스페이스 트레이서입니다. 응용 프로그램에 링크하거나 LD_PRELOAD로 주입하여 유저스페이스 이벤트를 기록합니다.
  • lttng-toolslttng CLI로 세션 생성, 이벤트 활성화, 기록 시작/정지를 제어합니다.
  • CTF (Common Trace Format) — LTTng의 바이너리 출력 형식입니다. 패킷/이벤트 헤더, 스트림 ID, 타임스탬프를 압축적으로 저장합니다.
  • babeltrace2 — CTF 트레이스를 텍스트로 변환하고 Python 플러그인으로 분석합니다.
  • Trace Compass — Eclipse 기반 GUI 분석 도구로, 스레드 뷰, CPU 활성 뷰, 지연 분석, 임계 경로 분석을 제공합니다.
  • 세션 → 채널 → 이벤트 규칙 — LTTng의 3단계 계층입니다. 세션에 채널(링 버퍼)을 만들고, 채널에 이벤트 규칙을 추가합니다.
  • 오버헤드 — 프로덕션 적합 수준으로, 일반 tracepoint 기록 시 약 100~200ns/이벤트입니다.

단계별 이해

  1. 설치 확인lttng-modules, lttng-ust, lttng-tools가 설치되어 있는지 확인합니다.
  2. 세션 생성lttng create my-session으로 트레이스 세션을 만듭니다.
  3. 이벤트 활성화lttng enable-event -k -a(커널 전체) 또는 특정 이벤트를 선택합니다.
  4. 기록 시작/정지lttng start로 기록을 시작하고, 문제 재현 후 lttng stop합니다.
  5. 분석babeltrace2 ~/lttng-traces/my-session/으로 이벤트를 확인하거나 Trace Compass에서 시각화합니다.

아키텍처 개요

LTTng는 세 개의 독립 패키지로 구성됩니다.

패키지역할위치
lttng-modules커널 공간 트레이서 (트레이스포인트 프로브, 링 버퍼)커널 모듈
lttng-ust유저스페이스 트레이서 라이브러리공유 라이브러리
lttng-tools세션 데몬(lttng-sessiond), relay 데몬, lttng CLI유저스페이스 데몬
커널 공간 tracepoint DEFINE_TRACE() syscall 훅 sys_enter/exit kprobe / uprobe 동적 계측 perf 이벤트 PMU / sched lttng-modules 프로브 핸들러 커널 링 버퍼 (per-CPU, lockless) — CTF 패킷 포맷 유저스페이스 lttng-sessiond 세션 관리 · 채널 설정 lttng-relayd 네트워크 스트리밍 lttng UST 앱 트레이스포인트 CTF 트레이스 디렉토리 ~/lttng-traces/ — metadata + stream0, stream1 ... 분석 도구 babeltrace2 (CLI/Python) Trace Compass (GUI) lttng-analyses / bt2 mmap / relay

설치

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 락 없이 수천만 이벤트/초를 기록할 수 있습니다.

CPU 0 — 링 버퍼 (4 서브-버퍼) sub0 기록 중 sub1 완료 sub2 완료 sub3 비어 있음 ▲ write CPU 1 — 링 버퍼 (4 서브-버퍼) sub0 완료 sub1 기록 중 sub2 비어 있음 sub3 비어 있음 ▲ write lttng-sessiond mmap 읽기 → 디스크 / relayd read (sub1/2) read (sub0) overwrite 모드: write가 read를 추월하면 가장 오래된 sub 덮어씀 | discard 모드: write 포인터가 꽉 찬 sub를 만나면 신규 이벤트 버림
# 빠른 이벤트용 채널 (큰 링 버퍼, 오버라이트 모드)
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입니다.

LTTng vs ftrace vs perf

항목LTTngftraceperf
출력 형식CTF 바이너리텍스트 (ring buffer)perf.data 바이너리
오버헤드매우 낮음 (lockless)낮음중간~높음 (샘플링)
커널+유저 동시 추적가능 (lttng-ust)부분적가능 (stack unwinding)
프로덕션 적합성높음중간중간
GUI 분석 도구Trace CompassKernelSharkFlame 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로 연결합니다.
관련 페이지: ftrace — 커널 내장 트레이싱 프레임워크, perf — 성능 분석 도구, eBPF — 동적 커널 계측 프로그래밍

문제 해결

증상원인해결
이벤트가 기록되지 않음모듈 미로드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