summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt (Google) <rostedt@goodmis.org>2022-01-29 17:23:56 -0500
committerSteven Rostedt (Google) <rostedt@goodmis.org>2022-01-29 17:23:56 -0500
commit23a9a5bc2d65d3cfb02b1a9577caabb1180dbe1d (patch)
tree5bf15c3c416af4a0b2c06ad0baed82d11564dd5b
parentac040d0f6dec3234f7afd31fc44488ce0684f7e8 (diff)
downloadktrace-23a9a5bc2d65d3cfb02b1a9577caabb1180dbe1d.tar.gz
ktrace: Add code to create a synthetic event
Add the "create synthetic" command and the completion to create a synthetic event. Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
-rw-r--r--src/create.c546
1 files changed, 462 insertions, 84 deletions
diff --git a/src/create.c b/src/create.c
index d1ba688..38cb0b6 100644
--- a/src/create.c
+++ b/src/create.c
@@ -264,6 +264,181 @@ static int create_eprobe(struct ccli *ccli, void *data,
return 0;
}
+static int usage_synthetic(struct ccli *ccli)
+{
+ ccli_printf(ccli, "# usage: create synthetic name system/event.field1[,field2,..] system2/event2.field1[,field2,..] field1=systemX/eventX.field[ field2=systemX/eventX.field ...]\n"
+ "# Where the synthetic event is created when the fields of system/event\n"
+ "# match system/event2 fields. Then add the fields of the synthetic event\n"
+ "# on how they will map to the other fields.\n#\n"
+ "# A synthetic event field may also equal timestamps:\n"
+ "# start=system/event.TIMESTAMP\n#\n"
+ "# Or even a delta:\n"
+ "# delta=system/event.TIMESTAMP-system2/event2.TIMESTAMP\n"
+ );
+ return 0;
+}
+
+int add_synth_field(struct ccli *ccli, struct tracefs_synth *synth,
+ char *start_system, char *start_event,
+ char *end_system, char *end_event, char *field)
+{
+ char *system;
+ char *event;
+ char *name;
+ char *val;
+ char *sav;
+
+ name = strtok_r(field, "=", &sav);
+ system = strtok_r(NULL, "/", &sav);
+ if (!system) {
+ ccli_printf(ccli, "# Missing '=system/event.field' in '%s'\n", name);
+ return -1;
+ }
+
+ event = strtok_r(NULL, ".", &sav);
+ if (!event) {
+ ccli_printf(ccli, "# Missing /event.field in '%s'\n", system);
+ return -1;
+ }
+
+ val = strtok_r(NULL, ".", &sav);
+ if (!system) {
+ ccli_printf(ccli, "# Missing .field in '%s' \n", event);
+ return -1;
+ }
+
+ if ((strcmp(system, start_system) == 0) &&
+ (strcmp(event, start_event) == 0)) {
+ return tracefs_synth_add_start_field(synth, val, name);
+ }
+
+ if ((strcmp(system, end_system) == 0) &&
+ (strcmp(event, end_event) == 0)) {
+ return tracefs_synth_add_end_field(synth, val, name);
+ }
+
+ ccli_printf(ccli, "# %s/%s does not match either start or end events\n",
+ system, event);
+ return -1;
+}
+
+static int create_synthetic(struct ccli *ccli, void *data,
+ int argc, char **argv)
+{
+ struct tep_handle *tep = data;
+ struct tracefs_synth *synth;
+ struct trace_seq seq;
+ char *system1;
+ char *event1;
+ char *system2;
+ char *event2;
+ char **fields1 = NULL;
+ char **fields2 = NULL;
+ char **svals = NULL;
+ char **tmp;
+ char *field;
+ char *name;
+ char *sav;
+ int ret;
+ int f = 0;
+ int i;
+
+ if (argc < 4)
+ return usage_synthetic(ccli);
+
+ name = argv[0];
+
+ system1 = strtok_r(argv[1], "/", &sav);
+ event1 = strtok_r(NULL, ".", &sav);
+ if (!system1 || !event1) {
+ ccli_printf(ccli, "# No slash found in first event %s\n", system1);
+ return 0;
+ }
+
+ field = strtok_r(NULL, ",", &sav);
+ if (!field) {
+ ccli_printf(ccli, "# No field found for %s/%s\n",
+ system1, event1);
+ return 0;
+ }
+
+ do {
+ tmp = realloc(fields1, sizeof(*tmp) * (f + 1));
+ if (!tmp)
+ return 0;
+ tmp[f++] = field;
+ fields1 = tmp;
+ field = strtok_r(NULL, ",", &sav);
+ } while (field);
+
+ /* Get the second event */
+ system2 = strtok_r(argv[2], "/", &sav);
+ event2 = strtok_r(NULL, ".", &sav);
+ if (!system2 || !event2) {
+ ccli_printf(ccli, "# No slash found in second event %s\n", system2);
+ goto free;
+ }
+
+ fields2 = calloc(f, sizeof(*fields2));
+ if (!fields2)
+ goto free;
+
+ for (i = 0; i < f; i++) {
+ field = strtok_r(NULL, ",", &sav);
+ if (!field)
+ goto free_fields;
+ fields2[i] = field;
+ }
+ field = strtok_r(NULL, ",", &sav);
+ if (field)
+ goto free_fields;
+
+ svals = calloc(argc - 3, sizeof(*svals));
+ if (!svals)
+ goto free;
+
+ synth = tracefs_synth_alloc(tep, name, system1, event1,
+ system2, event2, fields1[0],
+ fields2[0], NULL);
+ if (!synth) {
+ ccli_printf(ccli, "# Failed to create synthetic event\n");
+ goto free;
+ }
+
+ for (i = 1; i < f; i++) {
+ ret = tracefs_synth_add_match_field(synth, fields1[i],
+ fields2[i], NULL);
+ if (ret < 0) {
+ ccli_printf(ccli, "# Failed to match %s with %s\n",
+ fields1[i], fields2[i]);
+ goto free_synth;
+ }
+ }
+
+ for (i = 3; i < argc; i++) {
+ ret = add_synth_field(ccli, synth, system1, event1,
+ system2, event2, argv[i]);
+ if (ret < 0)
+ goto free_synth;
+ }
+
+ trace_seq_init(&seq);
+ tracefs_synth_echo_cmd(&seq, synth);
+ trace_seq_terminate(&seq);
+ ccli_printf(ccli, "%s\n", seq.buffer);
+ trace_seq_destroy(&seq);
+free_synth:
+ tracefs_synth_free(synth);
+free:
+ free(fields1);
+ free(fields2);
+ free(svals);
+ return 0;
+free_fields:
+ ccli_printf(ccli, "# End event must have same number of fields as start event\n");
+ goto free;
+}
+
int cmd_create(struct ccli *ccli, const char *command, const char *line,
void *data, int argc, char **argv)
{
@@ -278,6 +453,9 @@ int cmd_create(struct ccli *ccli, const char *command, const char *line,
if (strcmp(argv[1], "eprobe") == 0)
return create_eprobe(ccli, data, argc - 2, argv + 2);
+ if (strcmp(argv[1], "synthetic") == 0)
+ return create_synthetic(ccli, data, argc - 2, argv + 2);
+
return 0;
}
@@ -478,24 +656,126 @@ static int kprobe_completion(struct ccli *ccli, void *data,
return 0;
}
-static int eprobe_completion(struct ccli *ccli, void *data,
- int argc, char **argv,
- char ***list, int word, char *match)
+static int event_completion(struct ccli *ccli, struct tep_handle *tep,
+ char ***list, char *match, char append)
{
- struct tep_handle *tep = data;
- struct tep_format_field **common_fields;
- struct tep_format_field **fields;
- struct tep_event *event;
char **systems;
char **events;
char **words;
- char **tmp;
+ char *system;
+ char *event;
+ char *p;
+ int i;
+
+ p = strchr(match, '/');
+ if (p) {
+ system = strdup(match);
+ if (!system)
+ return 0;
+ system[p - match] = '\0';
+ events = tracefs_system_events(NULL, system);
+ if (!events) {
+ free(system);
+ return 0;
+ }
+ words = calloc(tracefs_list_size(events), sizeof(char *));
+ i = 0;
+ if (words) {
+ for (; events[i]; i++) {
+ asprintf(&event, "%s/%s", system, events[i]);
+ if (append && event && !strcmp(event, match)) {
+ asprintf(words + i, "%s.", event);
+ free(event);
+ } else
+ words[i] = event;
+ }
+ }
+ tracefs_list_free(events);
+ } else {
+ systems = tracefs_event_systems(NULL);
+ if (!systems)
+ return 0;
+ words = calloc(tracefs_list_size(systems), sizeof(char *));
+ i = 0;
+ if (words) {
+ for (; systems[i]; i++)
+ words[i] = strdup(systems[i]);
+ }
+ tracefs_list_free(systems);
+ /* Use '/' as a delim */
+ match[strlen(match)] = '/';
+ }
+ *list = words;
+ return i;
+}
+
+static struct tep_event *find_event(struct tep_handle *tep, char *ename)
+{
+ struct tep_event *event;
char *system;
char *name;
char *sav;
+
+ system = strtok_r(ename, "/", &sav);
+ name = strtok_r(NULL, "/", &sav);
+ if (!system || !name)
+ return NULL;
+
+ event = tep_find_event_by_name(tep, system, name);
+ /* put back ename to how we found it */
+ sav = system + strlen(system);
+ *sav = '/';
+ return event;
+}
+
+static int field_completion(struct tep_event *event, char ***list, char *prefix)
+{
+ struct tep_format_field **common_fields;
+ struct tep_format_field **fields;
+ char **words;
+ char **tmp;
+ int i, x;
+
+ common_fields = tep_event_common_fields(event);
+ fields = tep_event_fields(event);
+ words = NULL;
+ x = 0;
+ for (i = 0; common_fields && common_fields[i]; i++) {
+ tmp = realloc(words, sizeof(char *) * (x + 2));
+ if (!tmp) {
+ ccli_argv_free(words);
+ return 0;
+ }
+ words = tmp;
+ asprintf(&words[x++], "%s%s", prefix, common_fields[i]->name);
+ words[x] = NULL;
+ }
+ for (i = 0; fields && fields[i]; i++) {
+ tmp = realloc(words, sizeof(char *) * (x + 2));
+ if (!tmp) {
+ ccli_argv_free(words);
+ return 0;
+ }
+ words = tmp;
+ asprintf(&words[x++], "%s%s", prefix, fields[i]->name);
+ words[x] = NULL;
+ }
+ free(common_fields);
+ free(fields);
+ *list = words;
+ return x;
+}
+
+static int eprobe_completion(struct ccli *ccli, void *data,
+ int argc, char **argv,
+ char ***list, int word, char *match)
+{
+ struct tep_handle *tep = data;
+ struct tep_event *event;
+ char *prefix;
char *p, *m;
int len;
- int i, x;
+ int ret;
switch (word) {
case 0:
@@ -503,50 +783,11 @@ static int eprobe_completion(struct ccli *ccli, void *data,
ccli_line_refresh(ccli);
return 0;
case 1:
- p = strchr(match, '/');
- if (p) {
- system = strdup(match);
- if (!system)
- return 0;
- system[p - match] = '\0';
- events = tracefs_system_events(NULL, system);
- if (!events) {
- free(system);
- return 0;
- }
- words = calloc(tracefs_list_size(events), sizeof(char *));
- i = 0;
- if (words) {
- for (; events[i]; i++)
- asprintf(words + i, "%s/%s",
- system, events[i]);
- }
- tracefs_list_free(events);
- } else {
- systems = tracefs_event_systems(NULL);
- if (!systems)
- return 0;
- words = calloc(tracefs_list_size(systems), sizeof(char *));
- i = 0;
- if (words) {
- for (; systems[i]; i++)
- words[i] = strdup(systems[i]);
- }
- tracefs_list_free(systems);
- /* Use '/' as a delim */
- match[strlen(match)] = '/';
- }
- *list = words;
- return i;
+ return event_completion(ccli, tep, list, match, 0);
default:
- system = strtok_r(argv[1], "/", &sav);
- name = strtok_r(NULL, "/", &sav);
- if (!system || !name)
- return 0;
- event = tep_find_event_by_name(tep, system, name);
+ event = find_event(tep, argv[1]);
if (!event) {
- ccli_printf(ccli, "\n# Event %s/%s not found\n",
- system, name);
+ ccli_printf(ccli, "\n# Event %s not found\n", argv[1]);
return 0;
}
p = strchr(match, '=');
@@ -566,39 +807,14 @@ static int eprobe_completion(struct ccli *ccli, void *data,
switch (*m) {
default:
- common_fields = tep_event_common_fields(event);
- fields = tep_event_fields(event);
- words = NULL;
- x = 0;
- for (i = 0; common_fields && common_fields[i]; i++) {
- tmp = realloc(words, sizeof(char *) * (x + 2));
- if (!tmp) {
- ccli_argv_free(words);
- return 0;
- }
- words = tmp;
- asprintf(&words[x++], "%.*s=%s",
- len, match,
- common_fields[i]->name);
- words[x] = NULL;
- }
- for (i = 0; fields && fields[i]; i++) {
- tmp = realloc(words, sizeof(char *) * (x + 2));
- if (!tmp) {
- ccli_argv_free(words);
- return 0;
- }
- words = tmp;
- asprintf(&words[x++], "%.*s=%s",
- len, match,
- fields[i]->name);
- words[x] = NULL;
- }
- free(common_fields);
- free(fields);
- *list = words;
- match[strlen(match)] = CCLI_NOSPACE;
- return x;
+ asprintf(&prefix, "%.*s=", len, match);
+ if (!prefix)
+ return 0;
+ ret = field_completion(event, list, prefix);
+ free(prefix);
+ if (ret > 0)
+ match[strlen(match)] = CCLI_NOSPACE;
+ return ret;
case '.':
return type_completion(list, match, len);
case '-':
@@ -609,6 +825,164 @@ static int eprobe_completion(struct ccli *ccli, void *data,
return 0;
}
+static int append_event_field(struct ccli *ccli, struct tep_handle *tep, char ***list,
+ char *ename, char *match)
+{
+ struct tep_event *event;
+ char *p;
+
+ /* Find the end of the event */
+ for (p = ename; *p && *p != '.'; p++)
+ ;
+ if (!*p) // Should not happen!
+ return 0;
+ *p = '\0';
+
+ event = find_event(tep, ename);
+ if (!event) {
+ ccli_printf(ccli, "\n# Event %s not found\n", ename);
+ return 0;
+ }
+ *p = '.';
+ /* Find the start of the last field in the comma separated list */
+ for (p += strlen(p) - 1; *p != '.' && *p != ',' ; p--)
+ ;
+ p++;
+ *p = '\0';
+
+ return field_completion(event, list, ename);
+}
+
+static int append_field_ts(struct ccli *ccli, struct tep_handle *tep, char ***list,
+ char *ename)
+{
+ struct tep_event *event;
+ char **words;
+ char *e;
+ char *p;
+ int ret;
+
+ /* Find start of the event */
+ e = strchr(ename, '=');
+ if (!e) // should not happen
+ return 0;
+ e++;
+
+ /* Find the end of the event */
+ for (p = e; *p && *p != '.'; p++)
+ ;
+ if (!*p) // Should not happen!
+ return 0;
+ *p = '\0';
+
+ event = find_event(tep, e);
+ if (!event) {
+ ccli_printf(ccli, "\n# Event %s not found\n", e);
+ ccli_line_refresh(ccli);
+ return 0;
+ }
+
+ *p = '.';
+ p[1] = '\0';
+
+ ret = field_completion(event, list, ename);
+
+ if (ret > 0) {
+ words = realloc(*list, sizeof(*words) * (ret + 1));
+ if (words) {
+ asprintf(words + ret, "%sTIMESTAMP", ename);
+ *list = words;
+ ret++;
+ }
+ }
+ return ret;
+}
+
+static int synthetic_completion(struct ccli *ccli, void *data,
+ int argc, char **argv,
+ char ***list, int word, char *match)
+{
+ struct tep_handle *tep = data;
+ struct tep_event *event1, *event2;
+ char **words;
+ char *p;
+ int len;
+ int ret;
+
+ switch (word) {
+ case 0:
+ ccli_printf(ccli, "\n# Name the synthetic event\n");
+ ccli_line_refresh(ccli);
+ return 0;
+ case 1:
+ case 2:
+ len = strlen(match);
+
+ /* See if this already an event */
+ if (strchr(match, '.'))
+ return append_event_field(ccli, tep, list, argv[word], match);
+
+ ret = event_completion(ccli, tep, list, match, '.');
+ /* Do not add a space if this is an event */
+ if (ret > 0 && !match[len])
+ match[len] = CCLI_NOSPACE;
+ return ret;
+ default:
+ p = strchr(match, '=');
+ if (!p) {
+ ccli_printf(ccli, "\n# field=system/event.[field|TIMESTAMP]\n");
+ ccli_line_refresh(ccli);
+ return 0;
+ }
+ if (strchr(p, '.'))
+ return append_field_ts(ccli, tep, list, argv[word]);
+
+ argv[word][p - match] = '\0';
+
+ /* Can only be one of the previous events */
+ p = strchr(argv[1], '.');
+ if (!p) {
+ ccli_printf(ccli, "# %s needs a field\n", argv[1]);
+ ccli_line_refresh(ccli);
+ return 0;
+ }
+ *p = '\0';
+ event1 = find_event(tep, argv[1]);
+ if (!event1) {
+ ccli_printf(ccli, "\n# Event %s not found\n", argv[1]);
+ ccli_line_refresh(ccli);
+ return 0;
+ }
+ p = strchr(argv[2], '.');
+ if (!p) {
+ ccli_printf(ccli, "\n# %s needs a field\n", argv[2]);
+ ccli_line_refresh(ccli);
+ return 0;
+ }
+ *p = '\0';
+ event2 = find_event(tep, argv[2]);
+ if (!event2) {
+ ccli_printf(ccli, "\n# Event %s not found\n", argv[2]);
+ ccli_line_refresh(ccli);
+ return 0;
+ }
+
+ words = calloc(2, sizeof(*words));
+ if (!words)
+ return 0;
+ asprintf(words, "%s=%s/%s", argv[word],
+ event1->system, event1->name);
+ asprintf(words + 1, "%s=%s/%s", argv[word],
+ event2->system, event2->name);
+ *list = words;
+ match[strlen(match)] = '.';
+
+ return 2;
+ }
+ printf("\neprobe word=%d match=%s\n", word, match);
+ return 0;
+}
+
int create_completion(struct ccli *ccli, const char *command,
const char *line, int word,
char *match, char ***list, void *data)
@@ -642,6 +1016,10 @@ int create_completion(struct ccli *ccli, const char *command,
ret = eprobe_completion(ccli, data, argc - 2, argv + 2,
list, word - 2, match);
+ if (strcmp(argv[1], "synthetic") == 0)
+ ret = synthetic_completion(ccli, data, argc - 2, argv + 2,
+ list, word - 2, match);
+
ccli_argv_free(argv);
return ret;