• Wonhyuk Yang

Trace Point와 Event Tracer에 대한 Note


Intro

커널 코드를 살펴보면, 종종 "trace_" 접두사가 붙은 함수들을 살펴볼 수 있다. 해당 함수가 어떤 식으로 구현되어 있는지 살펴보려고 태그를 따라가려 하면 태그를 찾을 수 없을 것이다. 만약 해당 함수에 대한 간략한 설명이 있다면 디버깅을 위한 함수 또는 TracePoint를 위한 함수라고 설명되어 있을 것이다.


설명과 같이 trace 함수들은 TracePoint에 의해 정의된 함수들이며, 이름 그대로 커널 내부 특정한 위치에 "Trace Point"를 제공하는 hook point이며 매크로를 통해 선언된다. 이 뿐만 아니라 TracePoint는 다른 tracing/profile 서브 시스템과 연관 관계가 존재한다. 그렇기 때문에 해당 포스트에서는 Trace Point 그 자체 뿐만 아니라 다른 서브 시스템(ftrace, perf)과의 연결 고리에 대해 알아보도록 하겠다.


TracePoint

Trace Point는 Mathieu Desnoyers가 "tracing: Kernel Tracepoints" 패치를 통해 도입하였다. ftrace와 비슷하게, 특정 포인트에 다양한 Hook 함수들을 register/unregister할 수 있는 기능을 제공한다. 또한 모듈에서도 Trace Point를 지원하도록 설계되었다.


흥미로운 부분은, 매크로를 통해 trace point를 선언하면 hook point로 사용되는 trace 접두사를 붙인 함수와 해당 hook point에 register/unregister할 수 있게 하는 함수들이 생성되는 부분이다.


기본적인 구조에 대한 이해를 돕게 초기 버전의 매크로의 구현을 살펴보도록 하겠다.

초기 버전이라도 핵심 구조는 동일하여 최신 버전에서도 원리는 동일한 것을 확인할 수 있다.

Trace point 자료 구조는

  1. name: 해당 tracepoint에 이름

  2. state: enabled/disabled를 나타내는 state

  3. funcs: 설치된(registered) 프로브들을 저장하는 벡터

로 구성된다. Trace Point의 상태가 활성화되어 있다면 아래의 __DO_TRACE 매크로에 정의된 것처럼, funcs 벡터를 순회하면서 함수들을 실행하게 된다.

struct tracepoint는 사용자가 직접 다루는 구조체가 아니며 별도의 매크로(DEFINE_TRACEC)를 통해 선언된다. 또한 __DO_TRACE 매크로도 별도의 매크로(DECLARE_TRACE)를 통해 사용된다. 위 2개의 매크로의 구현은 아래와 같다.

DEFINE_TRACE 매크로를 사용하면, tracepoint 구조체가 별도의 섹션("__tracepoints")에 생성되고 이름을 저장하는 string은 "__tracepoints_strings" 섹션에 저장된다.


DECLARE_TRACE 매크로에서는 앞서 말한 대로,

  1. hook point로 기능하는 trace_ 접두사를 붙인 함수

  2. trace point 벡터에 등록/해제할 수 있는 api

를 생성한다.


하지만 TracePoint를 선언할 때 (최신 버전에서는) TRACE_EVENT 매크로를 사용하기도 한다. 이 매크로는 event tracer를 지원하기 위한 매크로이며 해당 매크로를 사용하면 자동으로 hook point에 event tracer callback이 등록된다. 이 부분은 꽤나 흥미롭게 구현되어 있어서 아래의 단락에서 좀 더 설명하도록 하겠다.



Event tracer

event tracer은 Steven Rostedt가 "tracing: add event trace infrastructure" 패치로 도입하였으며, ftrace에서 event를 관찰할 수 있도록 하며, debugfs를 통해 컨트롤 할 수 있는 기능을 제공하는 패치이다. 해당 기능은 앞에서 본 TracePoint에 기반하였고, 독특하게 구현되어 있다.


이해하기 쉽게 초기 버전("tracing: add event trace infrastructure")을 바탕으로 기본적인 디자인을 설명하도록 하겠다.

해당 버전에서 event를 정의하는데 사용되는 매크로는 최신 버전과는 다르다. 하지만 개념 자체는 동일하다.

event tracer의 기본적인 자료 구조는 ftrace_event_call이며, 그 구조는 tracepoint 구조체와 흡사하다. 아래의 코드와 같이 정의된 멤버 변수들은 다음과 같은 역할을 한다.

  • name: ftrace_event_call의 이름을 저장한다.

  • enabled: 해당 ftrace_event_call이 활성화 되어있는지를 저장한다.

  • regfunc/unregfunc: 선택된 hookpoint에 register/unregister 하는 함수이다.


ftrace_event_call 구조체도 사용자가 직접 다루는 구조체가 아니고 특정 매크로(DEFINE_TRACE_FMT)을 통해 구성된다. 즉, initialize 매크로를 사용하면 구조체와, 관련 함수까지 모두 생성된다. 구현은 아래의 코드와 같이 구성되어 있다.


여기서 살펴보아야 할 부분은, ftrace_reg_event_x/ftrace_unreg_event_x 함수들이다. 해당 함수들은 register_trace_x/unregister_trace_x 함수를 내부적으로 호출한다. 이 함수들은 TracePoint에서 매크로로 생성되는 함수들이다.


즉, Event를 생성하기 위해서는 동일한 이름의 TracePoint가 존재해야 하고, 해당 TracePoint에 ftrace_event_x 함수를 등록/해제하는 방식으로 구성된 것이다.


이러한 구조는 TracePoint를 생성하기만 하면 자동으로 Event도 생성하기 위함이다. 해당 버전에서 TracePoint를 생성하기 위한 매크로로 동일한 이름의 DEFINE_TRACE_FMT이 tracepoint.h에 아래와 같이 정의되어 있다.


DEFINE_TRACE_FMT를 사용해서 TracePoint를 생성하는 부분은 아래와 같이 하나의 헤더 파일에 모여 있다.


이것은 하나의 헤더 파일을 이용하여 2개 오브젝트 파일(TracePoint/Event를 위한 오브젝트 파일)을 만들기 위함이다. 실제로 위 파일은 두 군데에서 include 된다.


sched.h에서는 tracepoint.h를 include하고 shed_event_types.h 파일을 include한다. 이러면, tracepoint.h에 정의된 DECLARE_TRACE_FMT가 사용되며, TracePoint가 생성된다(해당 헤더 파일을 include한 C 파일에서)


sched_event_types.h가 사용되는 다른 사용처는 events.c 파일로 아래와 같이 구성되어 있다. 우선 trace_events.h를 ㅑinclude하여, DECLARE_TRACE_FMT의 정의가 Event를 위한 매크로로 override된다. 따라서 해당 파일을 컴파일하면, Event를 위한 구조체, 함수들이 포함된 오브젝트 파일이 생성된다.

이와 같은 구성으로 TracePoint(+Event)를 추가하고 싶다면, 단순히 TracePoint를 생성하는 매크로를 사용하는 것 뿐만 아니라, 해당 매크로의 사용된 파일의 위치도 고려해야 한다.


앞에서 말한 대로, Event tracer는 Debugfs를 통해 제어할 수 있다고 하였다. 해당 버전에서는 단순하게 2가지 기능을 제공하는 개별적인 파일을 구현한다.

최신 버전에서는 더 다양한 기능을 제공하므로 다양한 기능에 관심이 있다면 최신 버전을 참고하면 된다.

아래와 같이 2가지 파일이 tracing 파일에 존재하며 2가지 기능을 제공한다.

  1. available_events: 이용 가능한 Event들을 출력한다.

  2. set_event: Event를 활성/비활성화 한다.

커밋 메시지에 포함된 예제를 보면, 어떤 식으로 사용하는지 금방 알 수 있다.


정리

지금까지 TracePont와 Event Tracer에 대해 기본적인 컨셉에 대해 살펴 보았다. Event Trace가 어떤 방식으로 TracePoint에 기생(?)하는 지 살펴보는 것은 꽤나 재미있는 부분이였다고 생각한다.


아쉽게도 해당 글에서는 TracePoint를 새로 선언하고 사용하는 방법에 대해 다루진 않았다. 이 주제에 대해서는 아래 reference에 달린 LWN에 article을 참고하면 도움이 될 것이라 생각한다. 해당 article에서 새로운 TracePoint를 만들 때 왜 복잡하게 header 파일에 관련한 제약 사항이 있는지 궁금할 때 해당 글이 도움이 되었으면 한다. 이 밖에 perf와 연동되는 기능은 나중에 다른 글을 통해 정리해보도록 하겠다.

참고 자료

  1. Using the TRACE_EVENT() macro (Part 1)

  2. Using the TRACE_EVENT() macro (Part 2)

  3. Using the TRACE_EVENT() macro (Part 3)

조회수 80회댓글 0개

관련 게시물

전체 보기