10分間カーネルソースリーティング(timeシステムコールその3)

前回の続き。
今回は、CONFIG_FTRACE_SYSCALLSが有効なケースでのSYSCALL_DEFINEを追ってみる。
まずは、SYSCALL_DEFINExを見る。

#define SYSCALL_DEFINEx(x, sname, ...)        \
  static const char *types_##sname[] = {      \
    __SC_STR_TDECL##x(__VA_ARGS__)      \
  };              \
  static const char *args_##sname[] = {     \
    __SC_STR_ADECL##x(__VA_ARGS__)      \
  };              \
  SYSCALL_METADATA(sname, x);       \
  __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

これは、以下のようにマクロを展開できる。

  static const char *types__time[] = {      \
    __SC_STR_TDECL1(time_t __user *, tloc)      \
  };              \
  static const char *args__time[] = {     \
    __SC_STR_ADECL1(time_t __user *, tloc)      \
  };              \
  SYSCALL_METADATA(_time, 1);       \
  __SYSCALL_DEFINEx(1, _time, time_t __user *, tloc)

同ヘッダ内では__SC_STR_TDECL1,__SC_STR_ADECL1はそれぞれ

#define __SC_STR_TDECL1(t, a)   #t
#define __SC_STR_ADECL1(t, a)   #a

となっているので、上記のSYSCALL_DEFINExは

  static const char *types__time[] = {
    "time_t __user *",
  };
  static const char *args__time[] = {
    "tloc"
  };
  SYSCALL_METADATA(_time, 1);

となる。
※初めて知ったのだが、マクロの引数に#をつけると、ダブルクォーテーションでくくったのと同じくなるようだ。

残るはSYSCALL_METADATA。
ここではsyscall_metadata型の__syscall_meta__timeを定義しており、その格納セクションは__syscalls_metadataとなっている。どうやら、トレーサがシステムコールの属性を知るためのデータを定義してこれを__syscalls_metadataに突っ込んでいるらしい。

#define SYSCALL_METADATA(sname, nb)       \
  SYSCALL_TRACE_ENTER_EVENT(sname);     \
  SYSCALL_TRACE_EXIT_EVENT(sname);      \
  static const struct syscall_metadata __used   \
    __attribute__((__aligned__(4)))     \
    __attribute__((section("__syscalls_metadata"))) \
    __syscall_meta_##sname = {        \
    .name     = "sys"#sname,      \
    .nb_args  = nb,       \
    .types    = types_##sname,    \
    .args   = args_##sname,     \
    .enter_event  = &event_enter_##sname,   \
    .exit_event = &event_exit_##sname,    \
  };

次にSYSCALL_TRACE_ENTER_EVENTとSYSCALL_TRACE_EXIT_EVENT。
これはそれぞれ内部でftrace_event_call型の変数を定義し、それぞれenterとexitのイベントデータを作っているらしい。で、これを_ftrace_eventsセクションに押し込んでいる様だ。

#define SYSCALL_TRACE_ENTER_EVENT(sname)        \
  static const struct syscall_metadata __syscall_meta_##sname;  \
  static struct ftrace_event_call         \
  __attribute__((__aligned__(4))) event_enter_##sname;    \
  static struct trace_event enter_syscall_print_##sname = { \
    .trace                  = print_syscall_enter,    \
  };                \
  static struct ftrace_event_call __used        \
    __attribute__((__aligned__(4)))       \
    __attribute__((section("_ftrace_events")))      \
    event_enter_##sname = {         \
    .name                   = "sys_enter"#sname,    \
    .system                 = "syscalls",     \
    .event                  = &enter_syscall_print_##sname, \
    .raw_init   = init_syscall_trace,   \
    .define_fields    = syscall_enter_define_fields,  \
    .regfunc    = reg_event_syscall_enter,  \
    .unregfunc    = unreg_event_syscall_enter,  \
    .data     = (void *)&__syscall_meta_##sname,\
    TRACE_SYS_ENTER_PERF_INIT(sname)      \
  }

#define SYSCALL_TRACE_EXIT_EVENT(sname)         \
  static const struct syscall_metadata __syscall_meta_##sname;  \
  static struct ftrace_event_call         \
  __attribute__((__aligned__(4))) event_exit_##sname;   \
  static struct trace_event exit_syscall_print_##sname = {  \
    .trace                  = print_syscall_exit,   \
  };                \
  static struct ftrace_event_call __used        \
    __attribute__((__aligned__(4)))       \
    __attribute__((section("_ftrace_events")))      \
    event_exit_##sname = {          \
    .name                   = "sys_exit"#sname,   \
    .system                 = "syscalls",     \
    .event                  = &exit_syscall_print_##sname,  \
    .raw_init   = init_syscall_trace,   \
    .define_fields    = syscall_exit_define_fields, \
    .regfunc    = reg_event_syscall_exit, \
    .unregfunc    = unreg_event_syscall_exit, \
    .data     = (void *)&__syscall_meta_##sname,\
    TRACE_SYS_EXIT_PERF_INIT(sname)     \
  }

次に気になるところは、これらデータがトレーサによってどのように使われるのかである。
まずはセクション名をgrepする。

eria-laptop:~/source/linux> grep -rIw __syscalls_metadata *
include/linux/syscalls.h:	  __attribute__((section("__syscalls_metadata")))	\
include/linux/syscalls.h:	  __attribute__((section("__syscalls_metadata")))	\
include/asm-generic/vmlinux.lds.h:			 *(__syscalls_metadata)\

include/asm-generic/vmlinux.lds.hを見る。

#ifdef CONFIG_FTRACE_SYSCALLS
#define TRACE_SYSCALLS() VMLINUX_SYMBOL(__start_syscalls_metadata) = .; \
       *(__syscalls_metadata)       \
       VMLINUX_SYMBOL(__stop_syscalls_metadata) = .;
#else
#define TRACE_SYSCALLS()
#endif

どうやら、このセクションの先頭には__start_syscalls_metadataというシンボル名がつけられている。

なので、__start_syscalls_metadataをgrepする。

eria-laptop:~/source/linux> grep -rIw __start_syscalls_metadata *
include/asm-generic/vmlinux.lds.h:#define TRACE_SYSCALLS() VMLINUX_SYMBOL(__start_syscalls_metadata) = .;	\
kernel/trace/trace_syscalls.c:extern unsigned long __start_syscalls_metadata[];
kernel/trace/trace_syscalls.c:	start = (struct syscall_metadata *)__start_syscalls_metadata;

どうやらこのシンボルは、kernel/trace/trace_syscalls.cで使われている様だ。いかにもトレーサの処理が書かれたソースという感じである。
ということで、次回はtrace_syscalls.cを見てみる。
※今日は体調が悪く眠いので、この辺にしておきます。