diff options
author | Steven Rostedt (Google) <rostedt@goodmis.org> | 2022-01-29 17:23:56 -0500 |
---|---|---|
committer | Steven Rostedt (Google) <rostedt@goodmis.org> | 2022-01-29 17:23:56 -0500 |
commit | 23a9a5bc2d65d3cfb02b1a9577caabb1180dbe1d (patch) | |
tree | 5bf15c3c416af4a0b2c06ad0baed82d11564dd5b | |
parent | ac040d0f6dec3234f7afd31fc44488ce0684f7e8 (diff) | |
download | ktrace-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.c | 546 |
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; |