Commit bd82631d authored by Tom Zanussi's avatar Tom Zanussi Committed by Steven Rostedt (VMware)

tracing: Add support for dynamic strings to synthetic events

Currently, sythetic events only support static string fields such as:

  # echo 'test_latency u64 lat; char somename[32]' > /sys/kernel/debug/tracing/synthetic_events

Which is fine, but wastes a lot of space in the event.

It also prevents the most commonly-defined strings in the existing
trace events e.g. those defined using __string(), from being passed to
synthetic events via the trace() action.

With this change, synthetic events with dynamic fields can be defined:

  # echo 'test_latency u64 lat; char somename[]' > /sys/kernel/debug/tracing/synthetic_events

And the trace() action can be used to generate events using either
dynamic or static strings:

  # echo 'hist:keys=name:lat=common_timestamp.usecs-$ts0:onmatch(sys.event).test_latency($lat,name)' > /sys/kernel/debug/tracing/events

The synthetic event dynamic strings are implemented in the same way as
the existing __data_loc strings and appear as such in the format file.

[ <rostedt@goodmis.org>: added __set_synth_event_print_fmt() changes:

  I added the following to make it work with trace-cmd. Dynamic strings
  must have __get_str() for events in the print_fmt otherwise it can't be
  parsed correctly. ]

Link: https://lore.kernel.org/r/cover.1601588066.git.zanussi@kernel.org
Link: https://lkml.kernel.org/r/3ed35b6d0e390f5b94cb4a9ba1cc18f5982ab277.1601848695.git.zanussi@kernel.orgTested-by: default avatarAxel Rasmussen <axelrasmussen@google.com>
Signed-off-by: default avatarTom Zanussi <zanussi@kernel.org>
Signed-off-by: default avatarSteven Rostedt (VMware) <rostedt@goodmis.org>
parent 63a1e5de
...@@ -589,8 +589,19 @@ name:: ...@@ -589,8 +589,19 @@ name::
{ .type = "int", .name = "my_int_field" }, { .type = "int", .name = "my_int_field" },
}; };
See synth_field_size() for available types. If field_name contains [n] See synth_field_size() for available types.
the field is considered to be an array.
If field_name contains [n], the field is considered to be a static array.
If field_names contains[] (no subscript), the field is considered to
be a dynamic array, which will only take as much space in the event as
is required to hold the array.
Because space for an event is reserved before assigning field values
to the event, using dynamic arrays implies that the piecewise
in-kernel API described below can't be used with dynamic arrays. The
other non-piecewise in-kernel APIs can, however, be used with dynamic
arrays.
If the event is created from within a module, a pointer to the module If the event is created from within a module, a pointer to the module
must be passed to synth_event_create(). This will ensure that the must be passed to synth_event_create(). This will ensure that the
......
...@@ -1776,6 +1776,24 @@ consisting of the name of the new event along with one or more ...@@ -1776,6 +1776,24 @@ consisting of the name of the new event along with one or more
variables and their types, which can be any valid field type, variables and their types, which can be any valid field type,
separated by semicolons, to the tracing/synthetic_events file. separated by semicolons, to the tracing/synthetic_events file.
See synth_field_size() for available types.
If field_name contains [n], the field is considered to be a static array.
If field_names contains[] (no subscript), the field is considered to
be a dynamic array, which will only take as much space in the event as
is required to hold the array.
A string field can be specified using either the static notation:
char name[32];
Or the dynamic:
char name[];
The size limit for either is 256.
For instance, the following creates a new event named 'wakeup_latency' For instance, the following creates a new event named 'wakeup_latency'
with 3 fields: lat, pid, and prio. Each of those fields is simply a with 3 fields: lat, pid, and prio. Each of those fields is simply a
variable reference to a variable on another event:: variable reference to a variable on another event::
......
...@@ -242,9 +242,11 @@ static struct synth_field_desc create_synth_test_fields[] = { ...@@ -242,9 +242,11 @@ static struct synth_field_desc create_synth_test_fields[] = {
{ .type = "pid_t", .name = "next_pid_field" }, { .type = "pid_t", .name = "next_pid_field" },
{ .type = "char[16]", .name = "next_comm_field" }, { .type = "char[16]", .name = "next_comm_field" },
{ .type = "u64", .name = "ts_ns" }, { .type = "u64", .name = "ts_ns" },
{ .type = "char[]", .name = "dynstring_field_1" },
{ .type = "u64", .name = "ts_ms" }, { .type = "u64", .name = "ts_ms" },
{ .type = "unsigned int", .name = "cpu" }, { .type = "unsigned int", .name = "cpu" },
{ .type = "char[64]", .name = "my_string_field" }, { .type = "char[64]", .name = "my_string_field" },
{ .type = "char[]", .name = "dynstring_field_2" },
{ .type = "int", .name = "my_int_field" }, { .type = "int", .name = "my_int_field" },
}; };
...@@ -254,7 +256,7 @@ static struct synth_field_desc create_synth_test_fields[] = { ...@@ -254,7 +256,7 @@ static struct synth_field_desc create_synth_test_fields[] = {
*/ */
static int __init test_create_synth_event(void) static int __init test_create_synth_event(void)
{ {
u64 vals[7]; u64 vals[9];
int ret; int ret;
/* Create the create_synth_test event with the fields above */ /* Create the create_synth_test event with the fields above */
...@@ -292,10 +294,12 @@ static int __init test_create_synth_event(void) ...@@ -292,10 +294,12 @@ static int __init test_create_synth_event(void)
vals[0] = 777; /* next_pid_field */ vals[0] = 777; /* next_pid_field */
vals[1] = (u64)(long)"tiddlywinks"; /* next_comm_field */ vals[1] = (u64)(long)"tiddlywinks"; /* next_comm_field */
vals[2] = 1000000; /* ts_ns */ vals[2] = 1000000; /* ts_ns */
vals[3] = 1000; /* ts_ms */ vals[3] = (u64)(long)"xrayspecs"; /* dynstring_field_1 */
vals[4] = raw_smp_processor_id(); /* cpu */ vals[4] = 1000; /* ts_ms */
vals[5] = (u64)(long)"thneed"; /* my_string_field */ vals[5] = raw_smp_processor_id(); /* cpu */
vals[6] = 398; /* my_int_field */ vals[6] = (u64)(long)"thneed"; /* my_string_field */
vals[7] = (u64)(long)"kerplunk"; /* dynstring_field_2 */
vals[8] = 398; /* my_int_field */
/* Now generate a create_synth_test event */ /* Now generate a create_synth_test event */
ret = synth_event_trace_array(create_synth_test, vals, ARRAY_SIZE(vals)); ret = synth_event_trace_array(create_synth_test, vals, ARRAY_SIZE(vals));
...@@ -422,13 +426,15 @@ static int __init test_trace_synth_event(void) ...@@ -422,13 +426,15 @@ static int __init test_trace_synth_event(void)
int ret; int ret;
/* Trace some bogus values just for testing */ /* Trace some bogus values just for testing */
ret = synth_event_trace(create_synth_test, 7, /* number of values */ ret = synth_event_trace(create_synth_test, 9, /* number of values */
(u64)444, /* next_pid_field */ (u64)444, /* next_pid_field */
(u64)(long)"clackers", /* next_comm_field */ (u64)(long)"clackers", /* next_comm_field */
(u64)1000000, /* ts_ns */ (u64)1000000, /* ts_ns */
(u64)(long)"viewmaster",/* dynstring_field_1 */
(u64)1000, /* ts_ms */ (u64)1000, /* ts_ms */
(u64)raw_smp_processor_id(), /* cpu */ (u64)raw_smp_processor_id(), /* cpu */
(u64)(long)"Thneed", /* my_string_field */ (u64)(long)"Thneed", /* my_string_field */
(u64)(long)"yoyos", /* dynstring_field_2 */
(u64)999); /* my_int_field */ (u64)999); /* my_int_field */
return ret; return ret;
} }
......
...@@ -3289,6 +3289,15 @@ static int check_synth_field(struct synth_event *event, ...@@ -3289,6 +3289,15 @@ static int check_synth_field(struct synth_event *event,
field = event->fields[field_pos]; field = event->fields[field_pos];
/*
* A dynamic string synth field can accept static or
* dynamic. A static string synth field can only accept a
* same-sized static string, which is checked for later.
*/
if (strstr(hist_field->type, "char[") && field->is_string
&& field->is_dynamic)
return 0;
if (strcmp(field->type, hist_field->type) != 0) { if (strcmp(field->type, hist_field->type) != 0) {
if (field->size != hist_field->size || if (field->size != hist_field->size ||
field->is_signed != hist_field->is_signed) field->is_signed != hist_field->is_signed)
......
This diff is collapsed.
...@@ -16,6 +16,8 @@ struct synth_field { ...@@ -16,6 +16,8 @@ struct synth_field {
unsigned int offset; unsigned int offset;
bool is_signed; bool is_signed;
bool is_string; bool is_string;
bool is_dynamic;
bool field_pos;
}; };
struct synth_event { struct synth_event {
...@@ -24,6 +26,8 @@ struct synth_event { ...@@ -24,6 +26,8 @@ struct synth_event {
char *name; char *name;
struct synth_field **fields; struct synth_field **fields;
unsigned int n_fields; unsigned int n_fields;
struct synth_field **dynamic_fields;
unsigned int n_dynamic_fields;
unsigned int n_u64; unsigned int n_u64;
struct trace_event_class class; struct trace_event_class class;
struct trace_event_call call; struct trace_event_call call;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment