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を見てみる。
※今日は体調が悪く眠いので、この辺にしておきます。