Merge tag 'trace-v4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace
Pull tracing updates from Steven Rostedt:
"Some clean ups and small fixes, but the biggest change is the addition
of the TRACE_DEFINE_ENUM() macro that can be used by tracepoints.
Tracepoints have helper functions for the TP_printk() called
__print_symbolic() and __print_flags() that lets a numeric number be
displayed as a a human comprehensible text. What is placed in the
TP_printk() is also shown in the tracepoint format file such that user
space tools like perf and trace-cmd can parse the binary data and
express the values too. Unfortunately, the way the TRACE_EVENT()
macro works, anything placed in the TP_printk() will be shown pretty
much exactly as is. The problem arises when enums are used. That's
because unlike macros, enums will not be changed into their values by
the C pre-processor. Thus, the enum string is exported to the format
file, and this makes it useless for user space tools.
The TRACE_DEFINE_ENUM() solves this by converting the enum strings in
the TP_printk() format into their number, and that is what is shown to
user space. For example, the tracepoint tlb_flush currently has this
in its format file:
__print_symbolic(REC->reason,
{ TLB_FLUSH_ON_TASK_SWITCH, "flush on task switch" },
{ TLB_REMOTE_SHOOTDOWN, "remote shootdown" },
{ TLB_LOCAL_SHOOTDOWN, "local shootdown" },
{ TLB_LOCAL_MM_SHOOTDOWN, "local mm shootdown" })
After adding:
TRACE_DEFINE_ENUM(TLB_FLUSH_ON_TASK_SWITCH);
TRACE_DEFINE_ENUM(TLB_REMOTE_SHOOTDOWN);
TRACE_DEFINE_ENUM(TLB_LOCAL_SHOOTDOWN);
TRACE_DEFINE_ENUM(TLB_LOCAL_MM_SHOOTDOWN);
Its format file will contain this:
__print_symbolic(REC->reason,
{ 0, "flush on task switch" },
{ 1, "remote shootdown" },
{ 2, "local shootdown" },
{ 3, "local mm shootdown" })"
* tag 'trace-v4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace: (27 commits)
tracing: Add enum_map file to show enums that have been mapped
writeback: Export enums used by tracepoint to user space
v4l: Export enums used by tracepoints to user space
SUNRPC: Export enums in tracepoints to user space
mm: tracing: Export enums in tracepoints to user space
irq/tracing: Export enums in tracepoints to user space
f2fs: Export the enums in the tracepoints to userspace
net/9p/tracing: Export enums in tracepoints to userspace
x86/tlb/trace: Export enums in used by tlb_flush tracepoint
tracing/samples: Update the trace-event-sample.h with TRACE_DEFINE_ENUM()
tracing: Allow for modules to convert their enums to values
tracing: Add TRACE_DEFINE_ENUM() macro to map enums to their values
tracing: Update trace-event-sample with TRACE_SYSTEM_VAR documentation
tracing: Give system name a pointer
brcmsmac: Move each system tracepoints to their own header
iwlwifi: Move each system tracepoints to their own header
mac80211: Move message tracepoints to their own header
tracing: Add TRACE_SYSTEM_VAR to xhci-hcd
tracing: Add TRACE_SYSTEM_VAR to kvm-s390
tracing: Add TRACE_SYSTEM_VAR to intel-sst
...
This commit is contained in:
@@ -1704,6 +1704,125 @@ __register_event(struct ftrace_event_call *call, struct module *mod)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *enum_replace(char *ptr, struct trace_enum_map *map, int len)
|
||||
{
|
||||
int rlen;
|
||||
int elen;
|
||||
|
||||
/* Find the length of the enum value as a string */
|
||||
elen = snprintf(ptr, 0, "%ld", map->enum_value);
|
||||
/* Make sure there's enough room to replace the string with the value */
|
||||
if (len < elen)
|
||||
return NULL;
|
||||
|
||||
snprintf(ptr, elen + 1, "%ld", map->enum_value);
|
||||
|
||||
/* Get the rest of the string of ptr */
|
||||
rlen = strlen(ptr + len);
|
||||
memmove(ptr + elen, ptr + len, rlen);
|
||||
/* Make sure we end the new string */
|
||||
ptr[elen + rlen] = 0;
|
||||
|
||||
return ptr + elen;
|
||||
}
|
||||
|
||||
static void update_event_printk(struct ftrace_event_call *call,
|
||||
struct trace_enum_map *map)
|
||||
{
|
||||
char *ptr;
|
||||
int quote = 0;
|
||||
int len = strlen(map->enum_string);
|
||||
|
||||
for (ptr = call->print_fmt; *ptr; ptr++) {
|
||||
if (*ptr == '\\') {
|
||||
ptr++;
|
||||
/* paranoid */
|
||||
if (!*ptr)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
if (*ptr == '"') {
|
||||
quote ^= 1;
|
||||
continue;
|
||||
}
|
||||
if (quote)
|
||||
continue;
|
||||
if (isdigit(*ptr)) {
|
||||
/* skip numbers */
|
||||
do {
|
||||
ptr++;
|
||||
/* Check for alpha chars like ULL */
|
||||
} while (isalnum(*ptr));
|
||||
/*
|
||||
* A number must have some kind of delimiter after
|
||||
* it, and we can ignore that too.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
if (isalpha(*ptr) || *ptr == '_') {
|
||||
if (strncmp(map->enum_string, ptr, len) == 0 &&
|
||||
!isalnum(ptr[len]) && ptr[len] != '_') {
|
||||
ptr = enum_replace(ptr, map, len);
|
||||
/* Hmm, enum string smaller than value */
|
||||
if (WARN_ON_ONCE(!ptr))
|
||||
return;
|
||||
/*
|
||||
* No need to decrement here, as enum_replace()
|
||||
* returns the pointer to the character passed
|
||||
* the enum, and two enums can not be placed
|
||||
* back to back without something in between.
|
||||
* We can skip that something in between.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
skip_more:
|
||||
do {
|
||||
ptr++;
|
||||
} while (isalnum(*ptr) || *ptr == '_');
|
||||
/*
|
||||
* If what comes after this variable is a '.' or
|
||||
* '->' then we can continue to ignore that string.
|
||||
*/
|
||||
if (*ptr == '.' || (ptr[0] == '-' && ptr[1] == '>')) {
|
||||
ptr += *ptr == '.' ? 1 : 2;
|
||||
goto skip_more;
|
||||
}
|
||||
/*
|
||||
* Once again, we can skip the delimiter that came
|
||||
* after the string.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void trace_event_enum_update(struct trace_enum_map **map, int len)
|
||||
{
|
||||
struct ftrace_event_call *call, *p;
|
||||
const char *last_system = NULL;
|
||||
int last_i;
|
||||
int i;
|
||||
|
||||
down_write(&trace_event_sem);
|
||||
list_for_each_entry_safe(call, p, &ftrace_events, list) {
|
||||
/* events are usually grouped together with systems */
|
||||
if (!last_system || call->class->system != last_system) {
|
||||
last_i = 0;
|
||||
last_system = call->class->system;
|
||||
}
|
||||
|
||||
for (i = last_i; i < len; i++) {
|
||||
if (call->class->system == map[i]->system) {
|
||||
/* Save the first system if need be */
|
||||
if (!last_i)
|
||||
last_i = i;
|
||||
update_event_printk(call, map[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
up_write(&trace_event_sem);
|
||||
}
|
||||
|
||||
static struct ftrace_event_file *
|
||||
trace_create_new_event(struct ftrace_event_call *call,
|
||||
struct trace_array *tr)
|
||||
@@ -1915,7 +2034,7 @@ static int trace_module_notify(struct notifier_block *self,
|
||||
|
||||
static struct notifier_block trace_module_nb = {
|
||||
.notifier_call = trace_module_notify,
|
||||
.priority = 0,
|
||||
.priority = 1, /* higher than trace.c module notify */
|
||||
};
|
||||
#endif /* CONFIG_MODULES */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user