// SPDX-License-Identifier: GPL-2.0#include <trace/syscall.h>#include <trace/events/syscalls.h>#include <linux/kernel_stat.h>#include <linux/syscalls.h>#include <linux/slab.h>#include <linux/kernel.h>#include <linux/module.h> /* for MODULE_NAME_LEN via KSYM_SYMBOL_LEN */#include <linux/ftrace.h>#include <linux/perf_event.h>#include <linux/xarray.h>#include <asm/syscall.h> #include "trace_output.h"#include "trace.h" static DEFINE_MUTEX(syscall_trace_lock); static int syscall_enter_register(struct trace_event_call *event, enum trace_reg type, void *data);static int syscall_exit_register(struct trace_event_call *event, enum trace_reg type, void *data); static struct list_head *syscall_get_enter_fields(struct trace_event_call *call){ struct syscall_metadata *entry = call->data; return &entry->enter_fields;} extern struct syscall_metadata *__start_syscalls_metadata[];extern struct syscall_metadata *__stop_syscalls_metadata[]; static DEFINE_XARRAY(syscalls_metadata_sparse);static struct syscall_metadata **syscalls_metadata; #ifndef ARCH_HAS_SYSCALL_MATCH_SYM_NAMEstatic inline bool arch_syscall_match_sym_name(const char *sym, const char *name){ /* * Only compare after the "sys" prefix. Archs that use * syscall wrappers may have syscalls symbols aliases prefixed * with ".SyS" or ".sys" instead of "sys", leading to an unwanted * mismatch. */ return !strcmp(sym + 3, name + 3);}#endif #ifdef ARCH_TRACE_IGNORE_COMPAT_SYSCALLS/* * Some architectures that allow for 32bit applications * to run on a 64bit kernel, do not map the syscalls for * the 32bit tasks the same as they do for 64bit tasks. * * *cough*x86*cough* * * In such a case, instead of reporting the wrong syscalls, * simply ignore them. * * For an arch to ignore the compat syscalls it needs to * define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS as well as * define the function arch_trace_is_compat_syscall() to let * the tracing system know that it should ignore it. */static inttrace_get_syscall_nr(struct task_struct *task, struct pt_regs *regs){ if (unlikely(arch_trace_is_compat_syscall(regs))) return -1; return syscall_get_nr(task, regs);}#elsestatic inline inttrace_get_syscall_nr(struct task_struct *task, struct pt_regs *regs){ return syscall_get_nr(task, regs);}#endif /* ARCH_TRACE_IGNORE_COMPAT_SYSCALLS */ static __init struct syscall_metadata *find_syscall_meta(unsigned long syscall){ struct syscall_metadata **start; struct syscall_metadata **stop; char str[KSYM_SYMBOL_LEN]; start = __start_syscalls_metadata; stop = __stop_syscalls_metadata; kallsyms_lookup(syscall, NULL, NULL, NULL, str); if (arch_syscall_match_sym_name(str, "sys_ni_syscall")) return NULL; for ( ; start < stop; start++) { if ((*start)->name && arch_syscall_match_sym_name(str, (*start)->name)) return *start; } return NULL;} static struct syscall_metadata *syscall_nr_to_meta(int nr){ if (IS_ENABLED(CONFIG_HAVE_SPARSE_SYSCALL_NR)) return xa_load(&syscalls_metadata_sparse, (unsigned long)nr); if (!syscalls_metadata || nr >= NR_syscalls || nr < 0) return NULL; return syscalls_metadata[nr];} const char *get_syscall_name(int syscall){ struct syscall_metadata *entry; entry = syscall_nr_to_meta(syscall); if (!entry) return NULL; return entry->name;} /* Added to user strings or arrays when max limit is reached */#define EXTRA "..." static void get_dynamic_len_ptr(struct syscall_trace_enter *trace, struct syscall_metadata *entry, int *offset_p, int *len_p, unsigned char **ptr_p){ unsigned char *ptr; int offset = *offset_p; int val; /* This arg points to a user space string */ ptr = (void *)trace->args + sizeof(long) * entry->nb_args + offset; val = *(int *)ptr; /* The value is a dynamic string (len << 16 | offset) */ ptr = (void *)trace + (val & 0xffff); *len_p = val >> 16; offset += 4; *ptr_p = ptr; *offset_p = offset;} static enum print_line_tsys_enter_openat_print(struct syscall_trace_enter *trace, struct syscall_metadata *entry, struct trace_seq *s, struct trace_event *event){ unsigned char *ptr; int offset = 0; int bits, len; bool done = false; static const struct trace_print_flags __flags[] = { { O_TMPFILE, "O_TMPFILE" }, { O_WRONLY, "O_WRONLY" }, { O_RDWR, "O_RDWR" }, { O_CREAT, "O_CREAT" }, { O_EXCL, "O_EXCL" }, { O_NOCTTY, "O_NOCTTY" }, { O_TRUNC, "O_TRUNC" }, { O_APPEND, "O_APPEND" }, { O_NONBLOCK, "O_NONBLOCK" }, { O_DSYNC, "O_DSYNC" }, { O_DIRECT, "O_DIRECT" }, { O_LARGEFILE, "O_LARGEFILE" }, { O_DIRECTORY, "O_DIRECTORY" }, { O_NOFOLLOW, "O_NOFOLLOW" }, { O_NOATIME, "O_NOATIME" }, { O_CLOEXEC, "O_CLOEXEC" }, { -1, NULL } }; trace_seq_printf(s, "%s(", entry->name); for (int i = 0; !done && i < entry->nb_args; i++) { if (trace_seq_has_overflowed(s)) goto end; if (i) trace_seq_puts(s, ", "); switch (i) { case 2: bits = trace->args[2]; trace_seq_puts(s, "flags: "); /* No need to show mode when not creating the file */ if (!(bits & (O_CREAT|O_TMPFILE))) done = true; if (!(bits & O_ACCMODE)) { if (!bits) { trace_seq_puts(s, "O_RDONLY"); continue; } trace_seq_puts(s, "O_RDONLY|"); } trace_print_flags_seq(s, "|", bits, __flags); /* * trace_print_flags_seq() adds a '\0' to the * buffer, but this needs to append more to the seq. */ if (!trace_seq_has_overflowed(s)) trace_seq_pop(s); continue; case 3: trace_seq_printf(s, "%s: 0%03o", entry->args[i], (unsigned int)trace->args[i]); continue; } trace_seq_printf(s, "%s: %lu", entry->args[i], trace->args[i]); if (!(BIT(i) & entry->user_mask)) continue; get_dynamic_len_ptr(trace, entry, &offset, &len, &ptr); trace_seq_printf(s, " \"%.*s\"", len, ptr); } trace_seq_putc(s, ')');end: trace_seq_putc(s, '\n'); return trace_handle_return(s);} static enum print_line_tprint_syscall_enter(struct trace_iterator *iter, int flags, struct trace_event *event){ struct trace_array *tr = iter->tr; struct trace_seq *s = &iter->seq; struct trace_entry *ent = iter->ent; struct syscall_trace_enter *trace; struct syscall_metadata *entry; int i, syscall, val, len; unsigned char *ptr; int offset = 0; trace = (typeof(trace))ent; syscall = trace->nr; entry = syscall_nr_to_meta(syscall); if (!entry) goto end; if (entry->enter_event->event.type != ent->type) { WARN_ON_ONCE(1); goto end; } switch (entry->syscall_nr) { case __NR_openat: if (!tr || !(tr->trace_flags & TRACE_ITER(VERBOSE))) return sys_enter_openat_print(trace, entry, s, event); break; default: break; } trace_seq_printf(s, "%s(", entry->name); for (i = 0; i < entry->nb_args; i++) { bool printable = false; char *str; if (trace_seq_has_overflowed(s)) goto end; if (i) trace_seq_puts(s, ", "); /* parameter types */ if (tr && tr->trace_flags & TRACE_ITER(VERBOSE)) trace_seq_printf(s, "%s ", entry->types[i]); /* parameter values */ if (trace->args[i] < 10) trace_seq_printf(s, "%s: %lu", entry->args[i], trace->args[i]); else trace_seq_printf(s, "%s: 0x%lx", entry->args[i], trace->args[i]); if (!(BIT(i) & entry->user_mask)) continue; get_dynamic_len_ptr(trace, entry, &offset, &len, &ptr); if (entry->user_arg_size < 0 || entry->user_arg_is_str) { trace_seq_printf(s, " \"%.*s\"", len, ptr); continue; } val = trace->args[entry->user_arg_size]; str = ptr; trace_seq_puts(s, " ("); for (int x = 0; x < len; x++, ptr++) { if (isascii(*ptr) && isprint(*ptr)) printable = true; if (x) trace_seq_putc(s, ':'); trace_seq_printf(s, "%02x", *ptr); } if (len < val) trace_seq_printf(s, ", %s", EXTRA); trace_seq_putc(s, ')'); /* If nothing is printable, don't bother printing anything */ if (!printable) continue; trace_seq_puts(s, " \""); for (int x = 0; x < len; x++) { if (isascii(str[x]) && isprint(str[x])) trace_seq_putc(s, str[x]); else trace_seq_putc(s, '.'); } if (len < val) trace_seq_printf(s, "\"%s", EXTRA); else trace_seq_putc(s, '"'); } trace_seq_putc(s, ')');end: trace_seq_putc(s, '\n'); return trace_handle_return(s);} static enum print_line_tprint_syscall_exit(struct trace_iterator *iter, int flags, struct trace_event *event){ struct trace_seq *s = &iter->seq; struct trace_entry *ent = iter->ent; struct syscall_trace_exit *trace; int syscall; struct syscall_metadata *entry; trace = (typeof(trace))ent; syscall = trace->nr; entry = syscall_nr_to_meta(syscall); if (!entry) { trace_seq_putc(s, '\n'); goto out; } if (entry->exit_event->event.type != ent->type) { WARN_ON_ONCE(1); return TRACE_TYPE_UNHANDLED; } trace_seq_printf(s, "%s -> 0x%lx\n", entry->name, trace->ret); out: return trace_handle_return(s);} #define SYSCALL_FIELD(_type, _name) { \ .type = #_type, .name = #_name, \ .size = sizeof(_type), .align = __alignof__(_type), \ .is_signed = is_signed_type(_type), .filter_type = FILTER_OTHER } /* When len=0, we just calculate the needed length */#define LEN_OR_ZERO (len ? len - pos : 0) static int __initsys_enter_openat_print_fmt(struct syscall_metadata *entry, char *buf, int len){ int pos = 0; pos += snprintf(buf + pos, LEN_OR_ZERO, "\"dfd: 0x%%08lx, filename: 0x%%08lx \\\"%%s\\\", flags: %%s%%s, mode: 0%%03o\","); pos += snprintf(buf + pos, LEN_OR_ZERO, " ((unsigned long)(REC->dfd)),"); pos += snprintf(buf + pos, LEN_OR_ZERO, " ((unsigned long)(REC->filename)),"); pos += snprintf(buf + pos, LEN_OR_ZERO, " __get_str(__filename_val),"); pos += snprintf(buf + pos, LEN_OR_ZERO, " (REC->flags & ~3) && !(REC->flags & 3) ? \"O_RDONLY|\" : \"\", "); pos += snprintf(buf + pos, LEN_OR_ZERO, " REC->flags ? __print_flags(REC->flags, \"|\", "); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_WRONLY\" }, ", O_WRONLY); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_RDWR\" }, ", O_RDWR); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_CREAT\" }, ", O_CREAT); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_EXCL\" }, ", O_EXCL); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_NOCTTY\" }, ", O_NOCTTY); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_TRUNC\" }, ", O_TRUNC); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_APPEND\" }, ", O_APPEND); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_NONBLOCK\" }, ", O_NONBLOCK); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_DSYNC\" }, ", O_DSYNC); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_DIRECT\" }, ", O_DIRECT); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_LARGEFILE\" }, ", O_LARGEFILE); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_DIRECTORY\" }, ", O_DIRECTORY); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_NOFOLLOW\" }, ", O_NOFOLLOW); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_NOATIME\" }, ", O_NOATIME); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_CLOEXEC\" }) : \"O_RDONLY\", ", O_CLOEXEC); pos += snprintf(buf + pos, LEN_OR_ZERO, " ((unsigned long)(REC->mode))"); return pos;} static int __init__set_enter_print_fmt(struct syscall_metadata *entry, char *buf, int len){ bool is_string = entry->user_arg_is_str; int i; int pos = 0; switch (entry->syscall_nr) { case __NR_openat: return sys_enter_openat_print_fmt(entry, buf, len); default: break; } pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); for (i = 0; i < entry->nb_args; i++) { if (i) pos += snprintf(buf + pos, LEN_OR_ZERO, ", "); pos += snprintf(buf + pos, LEN_OR_ZERO, "%s: 0x%%0%zulx", entry->args[i], sizeof(unsigned long)); if (!(BIT(i) & entry->user_mask)) continue; /* Add the format for the user space string or array */ if (entry->user_arg_size < 0 || is_string) pos += snprintf(buf + pos, LEN_OR_ZERO, " \\\"%%s\\\""); else pos += snprintf(buf + pos, LEN_OR_ZERO, " (%%s)"); } pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); for (i = 0; i < entry->nb_args; i++) { pos += snprintf(buf + pos, LEN_OR_ZERO, ", ((unsigned long)(REC->%s))", entry->args[i]); if (!(BIT(i) & entry->user_mask)) continue; /* The user space data for arg has name __<arg>_val */ if (entry->user_arg_size < 0 || is_string) { pos += snprintf(buf + pos, LEN_OR_ZERO, ", __get_str(__%s_val)", entry->args[i]); } else { pos += snprintf(buf + pos, LEN_OR_ZERO, ", __print_dynamic_array(__%s_val, 1)", entry->args[i]); } } #undef LEN_OR_ZERO /* return the length of print_fmt */ return pos;} static int __init set_syscall_print_fmt(struct trace_event_call *call){ char *print_fmt; int len; struct syscall_metadata *entry = call->data; if (entry->enter_event != call) { call->print_fmt = "\"0x%lx\", REC->ret";Lines 1–500 of 1679. Load more
// SPDX-License-Identifier: GPL-2.0 #include <trace/syscall.h> #include <trace/events/syscalls.h> #include <linux/kernel_stat.h> #include <linux/syscalls.h> #include <linux/slab.h> #include <linux/kernel.h> #include <linux/module.h> /* for MODULE_NAME_LEN via KSYM_SYMBOL_LEN */ #include <linux/ftrace.h> #include <linux/perf_event.h> #include <linux/xarray.h> #include <asm/syscall.h> #include "trace_output.h" #include "trace.h" static DEFINE_MUTEX(syscall_trace_lock); static int syscall_enter_register(struct trace_event_call *event, enum trace_reg type, void *data); static int syscall_exit_register(struct trace_event_call *event, enum trace_reg type, void *data); static struct list_head * syscall_get_enter_fields(struct trace_event_call *call) { struct syscall_metadata *entry = call->data; return &entry->enter_fields; } extern struct syscall_metadata *__start_syscalls_metadata[]; extern struct syscall_metadata *__stop_syscalls_metadata[]; static DEFINE_XARRAY(syscalls_metadata_sparse); static struct syscall_metadata **syscalls_metadata; #ifndef ARCH_HAS_SYSCALL_MATCH_SYM_NAME static inline bool arch_syscall_match_sym_name(const char *sym, const char *name) { /* * Only compare after the "sys" prefix. Archs that use * syscall wrappers may have syscalls symbols aliases prefixed * with ".SyS" or ".sys" instead of "sys", leading to an unwanted * mismatch. */ return !strcmp(sym + 3, name + 3); } #endif #ifdef ARCH_TRACE_IGNORE_COMPAT_SYSCALLS /* * Some architectures that allow for 32bit applications * to run on a 64bit kernel, do not map the syscalls for * the 32bit tasks the same as they do for 64bit tasks. * * *cough*x86*cough* * * In such a case, instead of reporting the wrong syscalls, * simply ignore them. * * For an arch to ignore the compat syscalls it needs to * define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS as well as * define the function arch_trace_is_compat_syscall() to let * the tracing system know that it should ignore it. */ static int trace_get_syscall_nr(struct task_struct *task, struct pt_regs *regs) { if (unlikely(arch_trace_is_compat_syscall(regs))) return -1; return syscall_get_nr(task, regs); } #else static inline int trace_get_syscall_nr(struct task_struct *task, struct pt_regs *regs) { return syscall_get_nr(task, regs); } #endif /* ARCH_TRACE_IGNORE_COMPAT_SYSCALLS */ static __init struct syscall_metadata * find_syscall_meta(unsigned long syscall) { struct syscall_metadata **start; struct syscall_metadata **stop; char str[KSYM_SYMBOL_LEN]; start = __start_syscalls_metadata; stop = __stop_syscalls_metadata; kallsyms_lookup(syscall, NULL, NULL, NULL, str); if (arch_syscall_match_sym_name(str, "sys_ni_syscall")) return NULL; for ( ; start < stop; start++) { if ((*start)->name && arch_syscall_match_sym_name(str, (*start)->name)) return *start; } return NULL; } static struct syscall_metadata *syscall_nr_to_meta(int nr) { if (IS_ENABLED(CONFIG_HAVE_SPARSE_SYSCALL_NR)) return xa_load(&syscalls_metadata_sparse, (unsigned long)nr); if (!syscalls_metadata || nr >= NR_syscalls || nr < 0) return NULL; return syscalls_metadata[nr]; } const char *get_syscall_name(int syscall) { struct syscall_metadata *entry; entry = syscall_nr_to_meta(syscall); if (!entry) return NULL; return entry->name; } /* Added to user strings or arrays when max limit is reached */ #define EXTRA "..." static void get_dynamic_len_ptr(struct syscall_trace_enter *trace, struct syscall_metadata *entry, int *offset_p, int *len_p, unsigned char **ptr_p) { unsigned char *ptr; int offset = *offset_p; int val; /* This arg points to a user space string */ ptr = (void *)trace->args + sizeof(long) * entry->nb_args + offset; val = *(int *)ptr; /* The value is a dynamic string (len << 16 | offset) */ ptr = (void *)trace + (val & 0xffff); *len_p = val >> 16; offset += 4; *ptr_p = ptr; *offset_p = offset; } static enum print_line_t sys_enter_openat_print(struct syscall_trace_enter *trace, struct syscall_metadata *entry, struct trace_seq *s, struct trace_event *event) { unsigned char *ptr; int offset = 0; int bits, len; bool done = false; static const struct trace_print_flags __flags[] = { { O_TMPFILE, "O_TMPFILE" }, { O_WRONLY, "O_WRONLY" }, { O_RDWR, "O_RDWR" }, { O_CREAT, "O_CREAT" }, { O_EXCL, "O_EXCL" }, { O_NOCTTY, "O_NOCTTY" }, { O_TRUNC, "O_TRUNC" }, { O_APPEND, "O_APPEND" }, { O_NONBLOCK, "O_NONBLOCK" }, { O_DSYNC, "O_DSYNC" }, { O_DIRECT, "O_DIRECT" }, { O_LARGEFILE, "O_LARGEFILE" }, { O_DIRECTORY, "O_DIRECTORY" }, { O_NOFOLLOW, "O_NOFOLLOW" }, { O_NOATIME, "O_NOATIME" }, { O_CLOEXEC, "O_CLOEXEC" }, { -1, NULL } }; trace_seq_printf(s, "%s(", entry->name); for (int i = 0; !done && i < entry->nb_args; i++) { if (trace_seq_has_overflowed(s)) goto end; if (i) trace_seq_puts(s, ", "); switch (i) { case 2: bits = trace->args[2]; trace_seq_puts(s, "flags: "); /* No need to show mode when not creating the file */ if (!(bits & (O_CREAT|O_TMPFILE))) done = true; if (!(bits & O_ACCMODE)) { if (!bits) { trace_seq_puts(s, "O_RDONLY"); continue; } trace_seq_puts(s, "O_RDONLY|"); } trace_print_flags_seq(s, "|", bits, __flags); /* * trace_print_flags_seq() adds a '\0' to the * buffer, but this needs to append more to the seq. */ if (!trace_seq_has_overflowed(s)) trace_seq_pop(s); continue; case 3: trace_seq_printf(s, "%s: 0%03o", entry->args[i], (unsigned int)trace->args[i]); continue; } trace_seq_printf(s, "%s: %lu", entry->args[i], trace->args[i]); if (!(BIT(i) & entry->user_mask)) continue; get_dynamic_len_ptr(trace, entry, &offset, &len, &ptr); trace_seq_printf(s, " \"%.*s\"", len, ptr); } trace_seq_putc(s, ')'); end: trace_seq_putc(s, '\n'); return trace_handle_return(s); } static enum print_line_t print_syscall_enter(struct trace_iterator *iter, int flags, struct trace_event *event) { struct trace_array *tr = iter->tr; struct trace_seq *s = &iter->seq; struct trace_entry *ent = iter->ent; struct syscall_trace_enter *trace; struct syscall_metadata *entry; int i, syscall, val, len; unsigned char *ptr; int offset = 0; trace = (typeof(trace))ent; syscall = trace->nr; entry = syscall_nr_to_meta(syscall); if (!entry) goto end; if (entry->enter_event->event.type != ent->type) { WARN_ON_ONCE(1); goto end; } switch (entry->syscall_nr) { case __NR_openat: if (!tr || !(tr->trace_flags & TRACE_ITER(VERBOSE))) return sys_enter_openat_print(trace, entry, s, event); break; default: break; } trace_seq_printf(s, "%s(", entry->name); for (i = 0; i < entry->nb_args; i++) { bool printable = false; char *str; if (trace_seq_has_overflowed(s)) goto end; if (i) trace_seq_puts(s, ", "); /* parameter types */ if (tr && tr->trace_flags & TRACE_ITER(VERBOSE)) trace_seq_printf(s, "%s ", entry->types[i]); /* parameter values */ if (trace->args[i] < 10) trace_seq_printf(s, "%s: %lu", entry->args[i], trace->args[i]); else trace_seq_printf(s, "%s: 0x%lx", entry->args[i], trace->args[i]); if (!(BIT(i) & entry->user_mask)) continue; get_dynamic_len_ptr(trace, entry, &offset, &len, &ptr); if (entry->user_arg_size < 0 || entry->user_arg_is_str) { trace_seq_printf(s, " \"%.*s\"", len, ptr); continue; } val = trace->args[entry->user_arg_size]; str = ptr; trace_seq_puts(s, " ("); for (int x = 0; x < len; x++, ptr++) { if (isascii(*ptr) && isprint(*ptr)) printable = true; if (x) trace_seq_putc(s, ':'); trace_seq_printf(s, "%02x", *ptr); } if (len < val) trace_seq_printf(s, ", %s", EXTRA); trace_seq_putc(s, ')'); /* If nothing is printable, don't bother printing anything */ if (!printable) continue; trace_seq_puts(s, " \""); for (int x = 0; x < len; x++) { if (isascii(str[x]) && isprint(str[x])) trace_seq_putc(s, str[x]); else trace_seq_putc(s, '.'); } if (len < val) trace_seq_printf(s, "\"%s", EXTRA); else trace_seq_putc(s, '"'); } trace_seq_putc(s, ')'); end: trace_seq_putc(s, '\n'); return trace_handle_return(s); } static enum print_line_t print_syscall_exit(struct trace_iterator *iter, int flags, struct trace_event *event) { struct trace_seq *s = &iter->seq; struct trace_entry *ent = iter->ent; struct syscall_trace_exit *trace; int syscall; struct syscall_metadata *entry; trace = (typeof(trace))ent; syscall = trace->nr; entry = syscall_nr_to_meta(syscall); if (!entry) { trace_seq_putc(s, '\n'); goto out; } if (entry->exit_event->event.type != ent->type) { WARN_ON_ONCE(1); return TRACE_TYPE_UNHANDLED; } trace_seq_printf(s, "%s -> 0x%lx\n", entry->name, trace->ret); out: return trace_handle_return(s); } #define SYSCALL_FIELD(_type, _name) { \ .type = #_type, .name = #_name, \ .size = sizeof(_type), .align = __alignof__(_type), \ .is_signed = is_signed_type(_type), .filter_type = FILTER_OTHER } /* When len=0, we just calculate the needed length */ #define LEN_OR_ZERO (len ? len - pos : 0) static int __init sys_enter_openat_print_fmt(struct syscall_metadata *entry, char *buf, int len) { int pos = 0; pos += snprintf(buf + pos, LEN_OR_ZERO, "\"dfd: 0x%%08lx, filename: 0x%%08lx \\\"%%s\\\", flags: %%s%%s, mode: 0%%03o\","); pos += snprintf(buf + pos, LEN_OR_ZERO, " ((unsigned long)(REC->dfd)),"); pos += snprintf(buf + pos, LEN_OR_ZERO, " ((unsigned long)(REC->filename)),"); pos += snprintf(buf + pos, LEN_OR_ZERO, " __get_str(__filename_val),"); pos += snprintf(buf + pos, LEN_OR_ZERO, " (REC->flags & ~3) && !(REC->flags & 3) ? \"O_RDONLY|\" : \"\", "); pos += snprintf(buf + pos, LEN_OR_ZERO, " REC->flags ? __print_flags(REC->flags, \"|\", "); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_WRONLY\" }, ", O_WRONLY); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_RDWR\" }, ", O_RDWR); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_CREAT\" }, ", O_CREAT); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_EXCL\" }, ", O_EXCL); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_NOCTTY\" }, ", O_NOCTTY); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_TRUNC\" }, ", O_TRUNC); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_APPEND\" }, ", O_APPEND); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_NONBLOCK\" }, ", O_NONBLOCK); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_DSYNC\" }, ", O_DSYNC); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_DIRECT\" }, ", O_DIRECT); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_LARGEFILE\" }, ", O_LARGEFILE); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_DIRECTORY\" }, ", O_DIRECTORY); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_NOFOLLOW\" }, ", O_NOFOLLOW); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_NOATIME\" }, ", O_NOATIME); pos += snprintf(buf + pos, LEN_OR_ZERO, "{ 0x%x, \"O_CLOEXEC\" }) : \"O_RDONLY\", ", O_CLOEXEC); pos += snprintf(buf + pos, LEN_OR_ZERO, " ((unsigned long)(REC->mode))"); return pos; } static int __init __set_enter_print_fmt(struct syscall_metadata *entry, char *buf, int len) { bool is_string = entry->user_arg_is_str; int i; int pos = 0; switch (entry->syscall_nr) { case __NR_openat: return sys_enter_openat_print_fmt(entry, buf, len); default: break; } pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); for (i = 0; i < entry->nb_args; i++) { if (i) pos += snprintf(buf + pos, LEN_OR_ZERO, ", "); pos += snprintf(buf + pos, LEN_OR_ZERO, "%s: 0x%%0%zulx", entry->args[i], sizeof(unsigned long)); if (!(BIT(i) & entry->user_mask)) continue; /* Add the format for the user space string or array */ if (entry->user_arg_size < 0 || is_string) pos += snprintf(buf + pos, LEN_OR_ZERO, " \\\"%%s\\\""); else pos += snprintf(buf + pos, LEN_OR_ZERO, " (%%s)"); } pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); for (i = 0; i < entry->nb_args; i++) { pos += snprintf(buf + pos, LEN_OR_ZERO, ", ((unsigned long)(REC->%s))", entry->args[i]); if (!(BIT(i) & entry->user_mask)) continue; /* The user space data for arg has name __<arg>_val */ if (entry->user_arg_size < 0 || is_string) { pos += snprintf(buf + pos, LEN_OR_ZERO, ", __get_str(__%s_val)", entry->args[i]); } else { pos += snprintf(buf + pos, LEN_OR_ZERO, ", __print_dynamic_array(__%s_val, 1)", entry->args[i]); } } #undef LEN_OR_ZERO /* return the length of print_fmt */ return pos; } static int __init set_syscall_print_fmt(struct trace_event_call *call) { char *print_fmt; int len; struct syscall_metadata *entry = call->data; if (entry->enter_event != call) { call->print_fmt = "\"0x%lx\", REC->ret";