// SPDX-License-Identifier: GPL-2.0/* * trace_events_synth - synthetic trace events * * Copyright (C) 2015, 2020 Tom Zanussi <tom.zanussi@linux.intel.com> */ #include <linux/module.h>#include <linux/kallsyms.h>#include <linux/security.h>#include <linux/mutex.h>#include <linux/slab.h>#include <linux/stacktrace.h>#include <linux/rculist.h>#include <linux/tracefs.h> /* for gfp flag names */#include <linux/trace_events.h>#include <trace/events/mmflags.h>#include "trace_probe.h"#include "trace_probe_kernel.h" #include "trace_synth.h" #undef ERRORS#define ERRORS \ C(BAD_NAME, "Illegal name"), \ C(INVALID_CMD, "Command must be of the form: <name> field[;field] ..."),\ C(INVALID_DYN_CMD, "Command must be of the form: s or -:[synthetic/]<name> field[;field] ..."),\ C(EVENT_EXISTS, "Event already exists"), \ C(TOO_MANY_FIELDS, "Too many fields"), \ C(INCOMPLETE_TYPE, "Incomplete type"), \ C(INVALID_TYPE, "Invalid type"), \ C(INVALID_FIELD, "Invalid field"), \ C(INVALID_ARRAY_SPEC, "Invalid array specification"), #undef C#define C(a, b) SYNTH_ERR_##a enum { ERRORS }; #undef C#define C(a, b) b static const char *err_text[] = { ERRORS }; static DEFINE_MUTEX(lastcmd_mutex);static char *last_cmd; static int errpos(const char *str){ guard(mutex)(&lastcmd_mutex); if (!str || !last_cmd) return 0; return err_pos(last_cmd, str);} static void last_cmd_set(const char *str){ if (!str) return; mutex_lock(&lastcmd_mutex); kfree(last_cmd); last_cmd = kstrdup(str, GFP_KERNEL); mutex_unlock(&lastcmd_mutex);} static void synth_err(u8 err_type, u16 err_pos){ guard(mutex)(&lastcmd_mutex); if (!last_cmd) return; tracing_log_err(NULL, "synthetic_events", last_cmd, err_text, err_type, err_pos);} static int create_synth_event(const char *raw_command);static int synth_event_show(struct seq_file *m, struct dyn_event *ev);static int synth_event_release(struct dyn_event *ev);static bool synth_event_is_busy(struct dyn_event *ev);static bool synth_event_match(const char *system, const char *event, int argc, const char **argv, struct dyn_event *ev); static struct dyn_event_operations synth_event_ops = { .create = create_synth_event, .show = synth_event_show, .is_busy = synth_event_is_busy, .free = synth_event_release, .match = synth_event_match,}; static bool is_synth_event(struct dyn_event *ev){ return ev->ops == &synth_event_ops;} static struct synth_event *to_synth_event(struct dyn_event *ev){ return container_of(ev, struct synth_event, devent);} static bool synth_event_is_busy(struct dyn_event *ev){ struct synth_event *event = to_synth_event(ev); return event->ref != 0;} static bool synth_event_match(const char *system, const char *event, int argc, const char **argv, struct dyn_event *ev){ struct synth_event *sev = to_synth_event(ev); return strcmp(sev->name, event) == 0 && (!system || strcmp(system, SYNTH_SYSTEM) == 0);} struct synth_trace_event { struct trace_entry ent; union trace_synth_field fields[];}; static int synth_event_define_fields(struct trace_event_call *call){ struct synth_trace_event trace; int offset = offsetof(typeof(trace), fields); struct synth_event *event = call->data; unsigned int i, size, n_u64; char *name, *type; int filter_type; bool is_signed; bool is_stack; int ret = 0; for (i = 0, n_u64 = 0; i < event->n_fields; i++) { size = event->fields[i]->size; is_signed = event->fields[i]->is_signed; type = event->fields[i]->type; name = event->fields[i]->name; is_stack = event->fields[i]->is_stack; filter_type = is_stack ? FILTER_STACKTRACE : FILTER_OTHER; ret = trace_define_field(call, type, name, offset, size, is_signed, filter_type); if (ret) break; event->fields[i]->offset = n_u64; if (event->fields[i]->is_string && !event->fields[i]->is_dynamic) { offset += STR_VAR_LEN_MAX; n_u64 += STR_VAR_LEN_MAX / sizeof(u64); } else { offset += sizeof(u64); n_u64++; } } event->n_u64 = n_u64; return ret;} static bool synth_field_signed(char *type){ if (str_has_prefix(type, "u")) return false; if (strcmp(type, "gfp_t") == 0) return false; return true;} static int synth_field_is_string(char *type){ if (strstr(type, "char[") != NULL) return true; return false;} static int synth_field_is_stack(char *type){ if (strstr(type, "long[") != NULL) return true; return false;} static int synth_field_string_size(char *type){ char buf[4], *end, *start; unsigned int len; int size, err; start = strstr(type, "char["); if (start == NULL) return -EINVAL; start += sizeof("char[") - 1; end = strchr(type, ']'); if (!end || end < start || type + strlen(type) > end + 1) return -EINVAL; len = end - start; if (len > 3) return -EINVAL; if (len == 0) return 0; /* variable-length string */ memcpy(buf, start, len); buf[len] = '\0'; err = kstrtouint(buf, 0, &size); if (err) return err; if (size > STR_VAR_LEN_MAX) return -EINVAL; return size;} static int synth_field_size(char *type){ int size = 0; if (strcmp(type, "s64") == 0) size = sizeof(s64); else if (strcmp(type, "u64") == 0) size = sizeof(u64); else if (strcmp(type, "s32") == 0) size = sizeof(s32); else if (strcmp(type, "u32") == 0) size = sizeof(u32); else if (strcmp(type, "s16") == 0) size = sizeof(s16); else if (strcmp(type, "u16") == 0) size = sizeof(u16); else if (strcmp(type, "s8") == 0) size = sizeof(s8); else if (strcmp(type, "u8") == 0) size = sizeof(u8); else if (strcmp(type, "char") == 0) size = sizeof(char); else if (strcmp(type, "unsigned char") == 0) size = sizeof(unsigned char); else if (strcmp(type, "int") == 0) size = sizeof(int); else if (strcmp(type, "unsigned int") == 0) size = sizeof(unsigned int); else if (strcmp(type, "long") == 0) size = sizeof(long); else if (strcmp(type, "unsigned long") == 0) size = sizeof(unsigned long); else if (strcmp(type, "bool") == 0) size = sizeof(bool); else if (strcmp(type, "pid_t") == 0) size = sizeof(pid_t); else if (strcmp(type, "gfp_t") == 0) size = sizeof(gfp_t); else if (synth_field_is_string(type)) size = synth_field_string_size(type); else if (synth_field_is_stack(type)) size = 0; return size;} static const char *synth_field_fmt(char *type){ const char *fmt = "%llu"; if (strcmp(type, "s64") == 0) fmt = "%lld"; else if (strcmp(type, "u64") == 0) fmt = "%llu"; else if (strcmp(type, "s32") == 0) fmt = "%d"; else if (strcmp(type, "u32") == 0) fmt = "%u"; else if (strcmp(type, "s16") == 0) fmt = "%d"; else if (strcmp(type, "u16") == 0) fmt = "%u"; else if (strcmp(type, "s8") == 0) fmt = "%d"; else if (strcmp(type, "u8") == 0) fmt = "%u"; else if (strcmp(type, "char") == 0) fmt = "%d"; else if (strcmp(type, "unsigned char") == 0) fmt = "%u"; else if (strcmp(type, "int") == 0) fmt = "%d"; else if (strcmp(type, "unsigned int") == 0) fmt = "%u"; else if (strcmp(type, "long") == 0) fmt = "%ld"; else if (strcmp(type, "unsigned long") == 0) fmt = "%lu"; else if (strcmp(type, "bool") == 0) fmt = "%d"; else if (strcmp(type, "pid_t") == 0) fmt = "%d"; else if (strcmp(type, "gfp_t") == 0) fmt = "%x"; else if (synth_field_is_string(type)) fmt = "%s"; else if (synth_field_is_stack(type)) fmt = "%s"; return fmt;} static void print_synth_event_num_val(struct trace_seq *s, char *print_fmt, char *name, int size, union trace_synth_field *val, char *space){ switch (size) { case 1: trace_seq_printf(s, print_fmt, name, val->as_u8, space); break; case 2: trace_seq_printf(s, print_fmt, name, val->as_u16, space); break; case 4: trace_seq_printf(s, print_fmt, name, val->as_u32, space); break; default: trace_seq_printf(s, print_fmt, name, val->as_u64, space); break; }} static enum print_line_t print_synth_event(struct trace_iterator *iter, int flags, struct trace_event *event){ struct trace_array *tr = iter->tr; struct trace_seq *s = &iter->seq; struct synth_trace_event *entry; struct synth_event *se; unsigned int i, j, n_u64; char print_fmt[32]; const char *fmt; entry = (struct synth_trace_event *)iter->ent; se = container_of(event, struct synth_event, call.event); trace_seq_printf(s, "%s: ", se->name); for (i = 0, n_u64 = 0; i < se->n_fields; i++) { if (trace_seq_has_overflowed(s)) goto end; fmt = synth_field_fmt(se->fields[i]->type); /* parameter types */ if (tr && tr->trace_flags & TRACE_ITER(VERBOSE)) trace_seq_printf(s, "%s ", fmt); snprintf(print_fmt, sizeof(print_fmt), "%%s=%s%%s", fmt); /* parameter values */ if (se->fields[i]->is_string) { if (se->fields[i]->is_dynamic) { union trace_synth_field *data = &entry->fields[n_u64]; trace_seq_printf(s, print_fmt, se->fields[i]->name, (char *)entry + data->as_dynamic.offset, i == se->n_fields - 1 ? "" : " "); n_u64++; } else { trace_seq_printf(s, print_fmt, se->fields[i]->name, (char *)&entry->fields[n_u64].as_u64, i == se->n_fields - 1 ? "" : " "); n_u64 += STR_VAR_LEN_MAX / sizeof(u64); } } else if (se->fields[i]->is_stack) { union trace_synth_field *data = &entry->fields[n_u64]; unsigned long *p = (void *)entry + data->as_dynamic.offset; trace_seq_printf(s, "%s=STACK:\n", se->fields[i]->name); for (j = 1; j < data->as_dynamic.len / sizeof(long); j++) trace_seq_printf(s, "=> %pS\n", (void *)p[j]); n_u64++; } else { struct trace_print_flags __flags[] = { __def_gfpflag_names }; char *space = (i == se->n_fields - 1 ? "" : " "); print_synth_event_num_val(s, print_fmt, se->fields[i]->name, se->fields[i]->size, &entry->fields[n_u64], space); if (strcmp(se->fields[i]->type, "gfp_t") == 0) { trace_seq_puts(s, " ("); trace_print_flags_seq(s, "|", entry->fields[n_u64].as_u64, __flags, ARRAY_SIZE(__flags)); trace_seq_putc(s, ')'); } n_u64++; } }end: trace_seq_putc(s, '\n'); return trace_handle_return(s);} static struct trace_event_functions synth_event_funcs = { .trace = print_synth_event}; static unsigned int trace_string(struct synth_trace_event *entry, struct synth_event *event, char *str_val, bool is_dynamic, unsigned int data_size, unsigned int *n_u64){ unsigned int len = 0; char *str_field; int ret; if (is_dynamic) { union trace_synth_field *data = &entry->fields[*n_u64]; len = fetch_store_strlen((unsigned long)str_val); data->as_dynamic.offset = struct_size(entry, fields, event->n_u64) + data_size; data->as_dynamic.len = len; ret = fetch_store_string((unsigned long)str_val, &entry->fields[*n_u64], entry); (*n_u64)++; } else { str_field = (char *)&entry->fields[*n_u64].as_u64; #ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE if ((unsigned long)str_val < TASK_SIZE) ret = strncpy_from_user_nofault(str_field, (const void __user *)str_val, STR_VAR_LEN_MAX); else#endif ret = strncpy_from_kernel_nofault(str_field, str_val, STR_VAR_LEN_MAX); if (ret < 0) strcpy(str_field, FAULT_STRING); (*n_u64) += STR_VAR_LEN_MAX / sizeof(u64); } return len;} static unsigned int trace_stack(struct synth_trace_event *entry, struct synth_event *event, long *stack, unsigned int data_size, unsigned int *n_u64){ union trace_synth_field *data = &entry->fields[*n_u64]; unsigned int len; u32 data_offset; void *data_loc; data_offset = struct_size(entry, fields, event->n_u64); data_offset += data_size; for (len = 0; len < HIST_STACKTRACE_DEPTH; len++) { if (!stack[len]) break; } len *= sizeof(long); /* Find the dynamic section to copy the stack into. */ data_loc = (void *)entry + data_offset; memcpy(data_loc, stack, len); /* Fill in the field that holds the offset/len combo */ data->as_dynamic.offset = data_offset; data->as_dynamic.len = len; (*n_u64)++; return len;}Lines 1–500 of 2433. Load more
// SPDX-License-Identifier: GPL-2.0 /* * trace_events_synth - synthetic trace events * * Copyright (C) 2015, 2020 Tom Zanussi <tom.zanussi@linux.intel.com> */ #include <linux/module.h> #include <linux/kallsyms.h> #include <linux/security.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/stacktrace.h> #include <linux/rculist.h> #include <linux/tracefs.h> /* for gfp flag names */ #include <linux/trace_events.h> #include <trace/events/mmflags.h> #include "trace_probe.h" #include "trace_probe_kernel.h" #include "trace_synth.h" #undef ERRORS #define ERRORS \ C(BAD_NAME, "Illegal name"), \ C(INVALID_CMD, "Command must be of the form: <name> field[;field] ..."),\ C(INVALID_DYN_CMD, "Command must be of the form: s or -:[synthetic/]<name> field[;field] ..."),\ C(EVENT_EXISTS, "Event already exists"), \ C(TOO_MANY_FIELDS, "Too many fields"), \ C(INCOMPLETE_TYPE, "Incomplete type"), \ C(INVALID_TYPE, "Invalid type"), \ C(INVALID_FIELD, "Invalid field"), \ C(INVALID_ARRAY_SPEC, "Invalid array specification"), #undef C #define C(a, b) SYNTH_ERR_##a enum { ERRORS }; #undef C #define C(a, b) b static const char *err_text[] = { ERRORS }; static DEFINE_MUTEX(lastcmd_mutex); static char *last_cmd; static int errpos(const char *str) { guard(mutex)(&lastcmd_mutex); if (!str || !last_cmd) return 0; return err_pos(last_cmd, str); } static void last_cmd_set(const char *str) { if (!str) return; mutex_lock(&lastcmd_mutex); kfree(last_cmd); last_cmd = kstrdup(str, GFP_KERNEL); mutex_unlock(&lastcmd_mutex); } static void synth_err(u8 err_type, u16 err_pos) { guard(mutex)(&lastcmd_mutex); if (!last_cmd) return; tracing_log_err(NULL, "synthetic_events", last_cmd, err_text, err_type, err_pos); } static int create_synth_event(const char *raw_command); static int synth_event_show(struct seq_file *m, struct dyn_event *ev); static int synth_event_release(struct dyn_event *ev); static bool synth_event_is_busy(struct dyn_event *ev); static bool synth_event_match(const char *system, const char *event, int argc, const char **argv, struct dyn_event *ev); static struct dyn_event_operations synth_event_ops = { .create = create_synth_event, .show = synth_event_show, .is_busy = synth_event_is_busy, .free = synth_event_release, .match = synth_event_match, }; static bool is_synth_event(struct dyn_event *ev) { return ev->ops == &synth_event_ops; } static struct synth_event *to_synth_event(struct dyn_event *ev) { return container_of(ev, struct synth_event, devent); } static bool synth_event_is_busy(struct dyn_event *ev) { struct synth_event *event = to_synth_event(ev); return event->ref != 0; } static bool synth_event_match(const char *system, const char *event, int argc, const char **argv, struct dyn_event *ev) { struct synth_event *sev = to_synth_event(ev); return strcmp(sev->name, event) == 0 && (!system || strcmp(system, SYNTH_SYSTEM) == 0); } struct synth_trace_event { struct trace_entry ent; union trace_synth_field fields[]; }; static int synth_event_define_fields(struct trace_event_call *call) { struct synth_trace_event trace; int offset = offsetof(typeof(trace), fields); struct synth_event *event = call->data; unsigned int i, size, n_u64; char *name, *type; int filter_type; bool is_signed; bool is_stack; int ret = 0; for (i = 0, n_u64 = 0; i < event->n_fields; i++) { size = event->fields[i]->size; is_signed = event->fields[i]->is_signed; type = event->fields[i]->type; name = event->fields[i]->name; is_stack = event->fields[i]->is_stack; filter_type = is_stack ? FILTER_STACKTRACE : FILTER_OTHER; ret = trace_define_field(call, type, name, offset, size, is_signed, filter_type); if (ret) break; event->fields[i]->offset = n_u64; if (event->fields[i]->is_string && !event->fields[i]->is_dynamic) { offset += STR_VAR_LEN_MAX; n_u64 += STR_VAR_LEN_MAX / sizeof(u64); } else { offset += sizeof(u64); n_u64++; } } event->n_u64 = n_u64; return ret; } static bool synth_field_signed(char *type) { if (str_has_prefix(type, "u")) return false; if (strcmp(type, "gfp_t") == 0) return false; return true; } static int synth_field_is_string(char *type) { if (strstr(type, "char[") != NULL) return true; return false; } static int synth_field_is_stack(char *type) { if (strstr(type, "long[") != NULL) return true; return false; } static int synth_field_string_size(char *type) { char buf[4], *end, *start; unsigned int len; int size, err; start = strstr(type, "char["); if (start == NULL) return -EINVAL; start += sizeof("char[") - 1; end = strchr(type, ']'); if (!end || end < start || type + strlen(type) > end + 1) return -EINVAL; len = end - start; if (len > 3) return -EINVAL; if (len == 0) return 0; /* variable-length string */ memcpy(buf, start, len); buf[len] = '\0'; err = kstrtouint(buf, 0, &size); if (err) return err; if (size > STR_VAR_LEN_MAX) return -EINVAL; return size; } static int synth_field_size(char *type) { int size = 0; if (strcmp(type, "s64") == 0) size = sizeof(s64); else if (strcmp(type, "u64") == 0) size = sizeof(u64); else if (strcmp(type, "s32") == 0) size = sizeof(s32); else if (strcmp(type, "u32") == 0) size = sizeof(u32); else if (strcmp(type, "s16") == 0) size = sizeof(s16); else if (strcmp(type, "u16") == 0) size = sizeof(u16); else if (strcmp(type, "s8") == 0) size = sizeof(s8); else if (strcmp(type, "u8") == 0) size = sizeof(u8); else if (strcmp(type, "char") == 0) size = sizeof(char); else if (strcmp(type, "unsigned char") == 0) size = sizeof(unsigned char); else if (strcmp(type, "int") == 0) size = sizeof(int); else if (strcmp(type, "unsigned int") == 0) size = sizeof(unsigned int); else if (strcmp(type, "long") == 0) size = sizeof(long); else if (strcmp(type, "unsigned long") == 0) size = sizeof(unsigned long); else if (strcmp(type, "bool") == 0) size = sizeof(bool); else if (strcmp(type, "pid_t") == 0) size = sizeof(pid_t); else if (strcmp(type, "gfp_t") == 0) size = sizeof(gfp_t); else if (synth_field_is_string(type)) size = synth_field_string_size(type); else if (synth_field_is_stack(type)) size = 0; return size; } static const char *synth_field_fmt(char *type) { const char *fmt = "%llu"; if (strcmp(type, "s64") == 0) fmt = "%lld"; else if (strcmp(type, "u64") == 0) fmt = "%llu"; else if (strcmp(type, "s32") == 0) fmt = "%d"; else if (strcmp(type, "u32") == 0) fmt = "%u"; else if (strcmp(type, "s16") == 0) fmt = "%d"; else if (strcmp(type, "u16") == 0) fmt = "%u"; else if (strcmp(type, "s8") == 0) fmt = "%d"; else if (strcmp(type, "u8") == 0) fmt = "%u"; else if (strcmp(type, "char") == 0) fmt = "%d"; else if (strcmp(type, "unsigned char") == 0) fmt = "%u"; else if (strcmp(type, "int") == 0) fmt = "%d"; else if (strcmp(type, "unsigned int") == 0) fmt = "%u"; else if (strcmp(type, "long") == 0) fmt = "%ld"; else if (strcmp(type, "unsigned long") == 0) fmt = "%lu"; else if (strcmp(type, "bool") == 0) fmt = "%d"; else if (strcmp(type, "pid_t") == 0) fmt = "%d"; else if (strcmp(type, "gfp_t") == 0) fmt = "%x"; else if (synth_field_is_string(type)) fmt = "%s"; else if (synth_field_is_stack(type)) fmt = "%s"; return fmt; } static void print_synth_event_num_val(struct trace_seq *s, char *print_fmt, char *name, int size, union trace_synth_field *val, char *space) { switch (size) { case 1: trace_seq_printf(s, print_fmt, name, val->as_u8, space); break; case 2: trace_seq_printf(s, print_fmt, name, val->as_u16, space); break; case 4: trace_seq_printf(s, print_fmt, name, val->as_u32, space); break; default: trace_seq_printf(s, print_fmt, name, val->as_u64, space); break; } } static enum print_line_t print_synth_event(struct trace_iterator *iter, int flags, struct trace_event *event) { struct trace_array *tr = iter->tr; struct trace_seq *s = &iter->seq; struct synth_trace_event *entry; struct synth_event *se; unsigned int i, j, n_u64; char print_fmt[32]; const char *fmt; entry = (struct synth_trace_event *)iter->ent; se = container_of(event, struct synth_event, call.event); trace_seq_printf(s, "%s: ", se->name); for (i = 0, n_u64 = 0; i < se->n_fields; i++) { if (trace_seq_has_overflowed(s)) goto end; fmt = synth_field_fmt(se->fields[i]->type); /* parameter types */ if (tr && tr->trace_flags & TRACE_ITER(VERBOSE)) trace_seq_printf(s, "%s ", fmt); snprintf(print_fmt, sizeof(print_fmt), "%%s=%s%%s", fmt); /* parameter values */ if (se->fields[i]->is_string) { if (se->fields[i]->is_dynamic) { union trace_synth_field *data = &entry->fields[n_u64]; trace_seq_printf(s, print_fmt, se->fields[i]->name, (char *)entry + data->as_dynamic.offset, i == se->n_fields - 1 ? "" : " "); n_u64++; } else { trace_seq_printf(s, print_fmt, se->fields[i]->name, (char *)&entry->fields[n_u64].as_u64, i == se->n_fields - 1 ? "" : " "); n_u64 += STR_VAR_LEN_MAX / sizeof(u64); } } else if (se->fields[i]->is_stack) { union trace_synth_field *data = &entry->fields[n_u64]; unsigned long *p = (void *)entry + data->as_dynamic.offset; trace_seq_printf(s, "%s=STACK:\n", se->fields[i]->name); for (j = 1; j < data->as_dynamic.len / sizeof(long); j++) trace_seq_printf(s, "=> %pS\n", (void *)p[j]); n_u64++; } else { struct trace_print_flags __flags[] = { __def_gfpflag_names }; char *space = (i == se->n_fields - 1 ? "" : " "); print_synth_event_num_val(s, print_fmt, se->fields[i]->name, se->fields[i]->size, &entry->fields[n_u64], space); if (strcmp(se->fields[i]->type, "gfp_t") == 0) { trace_seq_puts(s, " ("); trace_print_flags_seq(s, "|", entry->fields[n_u64].as_u64, __flags, ARRAY_SIZE(__flags)); trace_seq_putc(s, ')'); } n_u64++; } } end: trace_seq_putc(s, '\n'); return trace_handle_return(s); } static struct trace_event_functions synth_event_funcs = { .trace = print_synth_event }; static unsigned int trace_string(struct synth_trace_event *entry, struct synth_event *event, char *str_val, bool is_dynamic, unsigned int data_size, unsigned int *n_u64) { unsigned int len = 0; char *str_field; int ret; if (is_dynamic) { union trace_synth_field *data = &entry->fields[*n_u64]; len = fetch_store_strlen((unsigned long)str_val); data->as_dynamic.offset = struct_size(entry, fields, event->n_u64) + data_size; data->as_dynamic.len = len; ret = fetch_store_string((unsigned long)str_val, &entry->fields[*n_u64], entry); (*n_u64)++; } else { str_field = (char *)&entry->fields[*n_u64].as_u64; #ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE if ((unsigned long)str_val < TASK_SIZE) ret = strncpy_from_user_nofault(str_field, (const void __user *)str_val, STR_VAR_LEN_MAX); else #endif ret = strncpy_from_kernel_nofault(str_field, str_val, STR_VAR_LEN_MAX); if (ret < 0) strcpy(str_field, FAULT_STRING); (*n_u64) += STR_VAR_LEN_MAX / sizeof(u64); } return len; } static unsigned int trace_stack(struct synth_trace_event *entry, struct synth_event *event, long *stack, unsigned int data_size, unsigned int *n_u64) { union trace_synth_field *data = &entry->fields[*n_u64]; unsigned int len; u32 data_offset; void *data_loc; data_offset = struct_size(entry, fields, event->n_u64); data_offset += data_size; for (len = 0; len < HIST_STACKTRACE_DEPTH; len++) { if (!stack[len]) break; } len *= sizeof(long); /* Find the dynamic section to copy the stack into. */ data_loc = (void *)entry + data_offset; memcpy(data_loc, stack, len); /* Fill in the field that holds the offset/len combo */ data->as_dynamic.offset = data_offset; data->as_dynamic.len = len; (*n_u64)++; return len; }