Microsoft MVP성태의 닷넷 이야기
Linux: 96. eBPF (bpf2go) - fentry, fexit를 이용한 트레이스 [링크 복사], [링크+제목 복사],
조회: 4471
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
 

(시리즈 글이 14개 있습니다.)
Linux: 86. Golang + bpf2go를 사용한 eBPF 기본 예제
; https://www.sysnet.pe.kr/2/0/13769

Linux: 94. eBPF - vmlinux.h 헤더 포함하는 방법 (bpf2go에서 사용)
; https://www.sysnet.pe.kr/2/0/13783

Linux: 95. eBPF - kprobe를 이용한 트레이스
; https://www.sysnet.pe.kr/2/0/13784

Linux: 96. eBPF (bpf2go) - fentry, fexit를 이용한 트레이스
; https://www.sysnet.pe.kr/2/0/13788

Linux: 100.  eBPF의 2가지 방식 - libbcc와 libbpf(CO-RE)
; https://www.sysnet.pe.kr/2/0/13801

Linux: 103. eBPF (bpf2go) - Tracepoint를 이용한 트레이스 (BPF_PROG_TYPE_TRACEPOINT)
; https://www.sysnet.pe.kr/2/0/13810

Linux: 105. eBPF - bpf2go에서 전역 변수 설정 방법
; https://www.sysnet.pe.kr/2/0/13815

Linux: 106. eBPF / bpf2go - (BPF_MAP_TYPE_HASH) Map을 이용한 전역 변수 구현
; https://www.sysnet.pe.kr/2/0/13817

Linux: 107. eBPF - libbpf CO-RE의 CONFIG_DEBUG_INFO_BTF 빌드 여부에 대한 의존성
; https://www.sysnet.pe.kr/2/0/13819

Linux: 109. eBPF / bpf2go - BPF_PERF_OUTPUT / BPF_MAP_TYPE_PERF_EVENT_ARRAY 사용법
; https://www.sysnet.pe.kr/2/0/13824

Linux: 110. eBPF / bpf2go - BPF_RINGBUF_OUTPUT / BPF_MAP_TYPE_RINGBUF 사용법
; https://www.sysnet.pe.kr/2/0/13825

Linux: 115. eBPF (bpf2go) - ARRAY / HASH map 기본 사용법
; https://www.sysnet.pe.kr/2/0/13893

Linux: 116. eBPF / bpf2go - BTF Style Maps 정의 구문과 데이터 정렬 문제
; https://www.sysnet.pe.kr/2/0/13894

Linux: 117. eBPF / bpf2go - Map에 추가된 요소의 개수를 확인하는 방법
; https://www.sysnet.pe.kr/2/0/13895




eBPF (bpf2go) - fentry, fexit를 이용한 트레이스

지난 글에서 kprobe 방식을 살펴봤는데요,

eBPF - kprobe를 이용한 트레이스
; https://www.sysnet.pe.kr/2/0/13784

kprobe의 경우 int 3(0xcc) BP에 따른 오버헤드가 발생하는 문제점이 있는 반면 fentry, fexit 방식은 성능 손실이 적다고 합니다. 기술적인 차이를 보면 그 이유를 알 수 있는데요,

Program type BPF_PROG_TYPE_TRACING - Fentry
; https://docs.ebpf.io/linux/program-type/BPF_PROG_TYPE_TRACING/#fentry

Fentry programs are attached to a BPF trampoline which causes less overhead than kprobes. Fentry programs can also be attached to BPF programs such as XDP, TC or cGroup programs which makes debugging eBPF programs easier. Kprobes lack this capability.


정리해 보면, kprobe와는 다르게 fentry, fexit 방식은 trampoline 기법을 이용하기 때문에 직접적인 함수 호출에 준하는 오버헤드만 있기 때문입니다.
Extracting kprobe parameters in eBPF
; https://eyakubovich.github.io/2022-04-19-ebpf-kprobe-params/

그 외에 성능과는 별개로 kretprobe와 fexit의 인자 수가 다르다는 차이가 있는데요, 기존의 kretprobe는 함수의 반환값만을 eBPF 함수에서 받을 수 있었지만 fexit의 경우에는 fentry의 입력 인자를 중복해서 받게 됩니다.

eBPF Tutorial by Example 3: Monitoring unlink System Calls with fentry
; https://eunomia.dev/en/tutorials/3-fentry-unlink/

The main difference between fexit and kretprobe programs is that fexit programs can access both the input parameters and return values of a function, while kretprobe programs can only access the return value.


libbpf-bootstrap: demo BPF applications - fentry
; https://github.com/libbpf/libbpf-bootstrap/blob/master/README.md#fentry

Important differences, compared to kprobes, are improved performance and usability. In this example, better usability is shown with the ability to directly dereference pointer arguments, like in normal C, instead of using various read helpers. The big distinction between fexit and kretprobe programs is that fexit one has access to both input arguments and returned result, while kretprobe can only access the result.


참고로, fentry, fexit 방식은 5.5 버전부터 사용 가능합니다.




구현의 차이는 있어도, 사용하는 방식 자체는 크게 다르지 않습니다. kprobe와 마찬가지로 fentry도 커널 함수를 대상으로 하기 때문에 대상을 찾는 것도 bpftrace 도구에서는 kprobe로 검색할 수 있습니다.

$ sudo bpftrace -l 'kprobe:*tcp_connect'
kprobe:ceph_tcp_connect
kprobe:tcp_connect

대상이 결정되었다면 그것의 함수 원형을 알아내야 하고,

tcp_connect (/net/ipv4/tcp_output.c)
; https://github.com/torvalds/linux/blob/master/net/ipv4/tcp_output.c#L4065

// 함수 원형
int tcp_connect(struct sock *sk);

이에 기반해 eBPF 코드를 다음과 같은 식으로 만들 수 있습니다.

//go:build ignore

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

SEC("fentry/tcp_connect")
int BPF_PROG(tcp_connect, struct sock *sk)
{
    pid_t tgid = bpf_get_current_pid_tgid() >> 32;
    bpf_printk("fentry-tcp_connect: pid = %d\n", tgid);
    return 0;
}

SEC("fexit/tcp_connect")
int BPF_PROG(tcp_connect_exit, struct sock *sk, int ret)
{
    pid_t tgid = bpf_get_current_pid_tgid() >> 32;
    bpf_printk("fexit-tcp_connect: pid = %d, ret = %ld\n", tgid, ret);
    return 0;
}

char __license[] SEC("license") = "GPL";

보는 바와 같이 SEC의 이름 지정도 살짝 달라졌는데요, 이에 대한 Prefix 종류는 다음의 문서에 나와 있습니다.

Section Naming - Program Sections
; https://ebpf-go.dev/concepts/section-naming/#program-sections

Section(Prefix) ProgramType AttachType
...[생략]...
kprobe          Kprobe
...[생략]...
fentry          Tracing     AttachTraceFEntry

위의 표에 나온 kprobe, fentry의 ProgramType과 AttachType이 다른데요, 이것은 이후 "go generate"로 만들어진 자동 생성 코드를 이용해 연결할 때도 참고하게 됩니다. 가령, 지난 예제 코드에서 kprobe는 이렇게 연결했었는데요,

kp, err := link.Kprobe(kprobeFunc, bpfObj.KprobeSysClone, nil)

반면 Fentry 예제에서는 AttachTracing 메서드를 이용하게 됩니다.

package main

//go:generate go run github.com/cilium/ebpf/cmd/bpf2go ebpf_basic basic.c

import (
    "bufio"
    "fmt"
    "github.com/cilium/ebpf/link"
    "github.com/cilium/ebpf/rlimit"
    "log"
    "os"
)

func requirePrerequisites() {
    // ...[생략]...
}

func main() {
    fmt.Println("hello world v4")

    requirePrerequisites()

    var bpfObj ebpf_basicObjects               // bpf2go로 자동 생성된 타입
    err := loadEbpf_basicObjects(&bpfObj, nil) // bpf2go로 자동 생성된 함수
    if err != nil {
        fmt.Printf("objs == null, %v\n", err)
        return
    }
    defer func(bpfObj *ebpf_basicObjects) {
        fmt.Printf("bpfObj.defer\n")
        _ = bpfObj.Close()
    }(&bpfObj)

    fmt.Printf("loaded: %v\n", bpfObj)


    // AttachTracing links a tracing (fentry/fexit/fmod_ret) BPF program or
    // a BTF-powered raw tracepoint (tp_btf) BPF Program to a BPF hook defined
    // in kernel modules.
    {
        // https://github.com/cilium/ebpf/blob/v0.16.0/link/tracing.go#L195
        fp, err := link.AttachTracing(link.TracingOptions{
            Program: bpfObj.ebpf_basicPrograms.TcpConnect,
        })
        if err != nil {
            fmt.Printf("fp == null, %v\n", err)
            return
        }
        defer func(kp link.Link) {
            fmt.Printf("link.tcp_connect.defer\n")
            _ = kp.Close()
        }(fp)

        fmt.Printf("link.Fentry: %v\n", fp)
    }

    {
        fp, err := link.AttachTracing(link.TracingOptions{
            Program: bpfObj.ebpf_basicPrograms.TcpConnectExit,
        })
        if err != nil {
            fmt.Printf("fp == null, %v\n", err)
            return
        }
        defer func(kp link.Link) {
            fmt.Printf("link.tcp_connect.exit.defer\n")
            _ = kp.Close()
        }(fp)

        fmt.Printf("link.Fexit: %v\n", fp)
    }

    fmt.Println("Press any key to exit...")
    input := bufio.NewScanner(os.Stdin)
    input.Scan()
}

빌드 후 실행해 보면, bpf_printk 출력으로 잘 동작하는 것을 확인할 수 있습니다.

$ sudo cat /sys/kernel/debug/tracing/trace_pipe
kubelet-3903    [000] ...11 259090.102895: bpf_trace_printk: fentry-tcp_connect: pid = 3181
kubelet-3903    [000] ...11 259090.103049: bpf_trace_printk: fexit-tcp_connect: pid = 3181, ret = 0
coredns-6010    [003] ...11 259090.337794: bpf_trace_printk: fentry-tcp_connect: pid = 5239
coredns-6010    [003] ...11 259090.337868: bpf_trace_printk: fexit-tcp_connect: pid = 5239, ret = 0
coredns-5856    [007] ...11 259090.338179: bpf_trace_printk: fentry-tcp_connect: pid = 5264
coredns-5856    [007] ...11 259090.338209: bpf_trace_printk: fexit-tcp_connect: pid = 5264, ret = 0
kubelet-3908    [006] ...11 259090.533112: bpf_trace_printk: fentry-tcp_connect: pid = 3181
kubelet-3908    [006] ...11 259090.533220: bpf_trace_printk: fexit-tcp_connect: pid = 3181, ret = 0
...[생략]...




작성한 eBPF 코드가 이런 오류가 발생한다면?

field TcpConnect: program tcp_connect: apply CO-RE relocations: load kernel spec: btf: not found

검색 결과,

program: relocation of program targeting a module fails if CONFIG_DEBUG_INFO_BTF_MODULES is disabled #1436
; https://github.com/cilium/ebpf/issues/1436

CONFIG_DEBUG_INFO_BTF_MODULES 옵션이 커널 빌드 시에 적용되지 않았다고 합니다. 실제로 이와 관련한 디렉터리도 없는데요,

// 동작하지 않는 환경의 경우

$ ls /sys/kernel/btf
ls: cannot access '/sys/kernel/btf': No such file or directory

예제에서 요구하는 BTF 정보는 vmlinux이기 때문에 최소한 /sys/kernel/btf 디렉터리에 vmlinux 파일은 있어야 합니다.

// 잘 동작하는 환경의 경우

$ ll /sys/kernel/btf/vmlinux
-r--r--r-- 1 root root 5866532 Oct 25 12:59 /sys/kernel/btf/vmlinux

참고로, 문제가 있는 리눅스 배포본의 해당 커널이 빌드된 config 파일에 CONFIG_DEBUG_INFO_BTF_MODULES 옵션 설정이 없습니다.

// 동작하지 않는 환경의 경우

$ cat /boot/config-$(uname -r) | grep CONFIG_DEBUG_INFO_BTF_MODULES
$

정상적이라면 이렇게 y 설정이 있어야 합니다.

$ cat /boot/config-$(uname -r) | grep CONFIG_DEBUG_INFO_BTF_MODULES
CONFIG_DEBUG_INFO_BTF_MODULES=y




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 11/12/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 31  32  33  34  35  36  [37]  38  39  40  41  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
13011정성태3/21/202215935오류 유형: 802. 윈도우 운영체제에서 웹캠 카메라 인식이 안 되는 경우
13010정성태3/21/202212671오류 유형: 801. Oracle.ManagedDataAccess.Core - GetTypes 호출 시 "Could not load file or assembly 'System.DirectoryServices.Protocols...'" 오류
13009정성태3/20/202215260개발 환경 구성: 640. docker - ibmcom/db2 컨테이너 실행
13008정성태3/19/202214851VS.NET IDE: 176. 비주얼 스튜디오 - 솔루션 탐색기에서 프로젝트를 선택할 때 csproj 파일이 열리지 않도록 만드는 방법
13007정성태3/18/202213257.NET Framework: 1181. C# - Oracle.ManagedDataAccess의 Pool 및 그것의 연결 개체 수를 알아내는 방법파일 다운로드1
13006정성태3/17/202215659.NET Framework: 1180. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 remuxing.c 예제 포팅
13005정성태3/17/202213925오류 유형: 800. C# - System.InvalidOperationException: Late bound operations cannot be performed on fields with types for which Type.ContainsGenericParameters is true.
13004정성태3/16/202213449디버깅 기술: 182. windbg - 닷넷 메모리 덤프에서 AppDomain에 걸친 정적(static) 필드 값을 조사하는 방법
13003정성태3/15/202213774.NET Framework: 1179. C# - (.NET Framework를 위한) Oracle.ManagedDataAccess 패키지의 성능 카운터 설정 방법
13002정성태3/14/202215387.NET Framework: 1178. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 http_multiclient.c 예제 포팅
13001정성태3/13/202215977.NET Framework: 1177. C# - 닷넷에서 허용하는 메서드의 매개변수와 호출 인자의 최대 수
13000정성태3/12/202214921.NET Framework: 1176. C# - Oracle.ManagedDataAccess.Core의 성능 카운터 설정 방법
12999정성태3/10/202214768.NET Framework: 1175. Visual Studio - 프로젝트 또는 솔루션의 Clean 작업 시 응용 프로그램에서 생성한 파일을 함께 삭제파일 다운로드1
12998정성태3/10/202213174.NET Framework: 1174. C# - ELEMENT_TYPE_FNPTR 유형의 사용 예
12997정성태3/10/202222269오류 유형: 799. Oracle.ManagedDataAccess - "ORA-01882: timezone region not found" 오류가 발생하는 이유
12996정성태3/9/202223738VS.NET IDE: 175. Visual Studio - 인텔리센스에서 오버로드 메서드를 키보드로 선택하는 방법
12995정성태3/8/202216090.NET Framework: 1173. .NET에서 Producer/Consumer를 구현한 BlockingCollection<T>
12994정성태3/8/202215166오류 유형: 798. WinDbg - Failed to load data access module, 0x80004002
12993정성태3/4/202214872.NET Framework: 1172. .NET에서 Producer/Consumer를 구현하는 기초 인터페이스 - IProducerConsumerCollection<T>
12992정성태3/3/202217823.NET Framework: 1171. C# - BouncyCastle을 사용한 암호화/복호화 예제파일 다운로드1
12991정성태3/2/202215632.NET Framework: 1170. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 transcode_aac.c 예제 포팅
12990정성태3/2/202215738오류 유형: 797. msbuild - The BaseOutputPath/OutputPath property is not set for project '[...].vcxproj'
12989정성태3/2/202213752오류 유형: 796. mstest.exe - System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.VisualStudio.QualityTools.Tips.WebLoadTest.Tip
12988정성태3/2/202212159오류 유형: 795. CI 환경에서 Docker build 시 csproj의 Link 파일에 대한 빌드 오류
12987정성태3/1/202214433.NET Framework: 1169. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 demuxing_decoding.c 예제 포팅
12986정성태2/28/202216405.NET Framework: 1168. C# -IIncrementalGenerator를 적용한 Version 2 Source Generator 실습 [1]
... 31  32  33  34  35  36  [37]  38  39  40  41  42  43  44  45  ...