blob: ce0753cd8c5ac44cc42e2d63061f2324011e8a7e [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022 Google Inc, Steven Rostedt <rostedt@goodmis.org>
*/
#include "ktrace.h"
static void create_usage(struct ccli *ccli)
{
ccli_printf(ccli, "usage: create <type> <type-command>\n"
" <type> : kprobe, eprobe, synthetic_event\n");
}
static int create_kprobe(struct ccli *ccli, void *data,
int argc, char **argv)
{
return 0;
}
static int add_event_var(struct ccli *ccli, struct tep_handle *tep,
char **command, struct tep_event *event,
char *line)
{
struct tep_format_field *field;
long long offset;
bool neg;
char *fname;
char *type;
char *var;
char *tmp;
char *end;
char *p;
char ch;
int ret;
p = strchr(line, '=');
if (!p) {
ccli_printf(ccli, "Invalid variable '%s'\n", line);
return -1;
}
*p = '\0';
fname = p + 1;
for (p++; *p; p++) {
if (*p == '.' || *p == '-')
break;
}
ch = *p;
*p = '\0';
field = tep_find_any_field(event, fname);
if (!field) {
ccli_printf(ccli, "# Cannot find field '%s' for event '%s'\n",
fname, event->name);
return -1;
}
ret = asprintf(&var, "$%s", fname);
if (ret < 0)
return -1;
*p = ch;
for (; *p; p++) {
switch(*p) {
case '.':
p++;
type = p;
for (; *p; p++) {
if (*p == '.' || *p == '-')
break;
}
if (*p == '.') {
ccli_printf(ccli, "# Two types can not be togethe '%s'\n",
line);
goto out;
}
if (strncmp(type, "string", 6) == 0 ||
strncmp(type, "ustring", 7) == 0) {
if (*p) {
ccli_printf(ccli, "# Strings can not be deferenced '%s'\n",
type);
goto out;
}
ret = asprintf(&tmp, "+0(%s):%.*s",
var, (int)(p - type),
type);
} else {
ret = asprintf(&tmp, "%s:%.*s",
var, (int)(p - type),
type);
}
if (ret < 0)
goto out;
free(var);
var = tmp;
/* We need to compare current p again */
p--;
break;
case '-':
p++;
if (*p != '>') {
ccli_printf(ccli, "# Invalid variable '%s'\n",
type);
goto out;
}
p++;
if (*p == '-') {
p++;
neg = true;
}
offset = strtoll(p, &end, 0);
ret = asprintf(&tmp, "%s%llu(%s)",
neg ? "-" : "+", offset, var);
if (ret < 0)
goto out;
free(var);
var = tmp;
break;
}
}
/* Finished */
ret = asprintf(&tmp, "%s %s=%s", *command, line, var);
out:
free(var);
if (ret < 0)
return -1;
free(*command);
*command = tmp;
return 0;
}
static int create_eprobe(struct ccli *ccli, void *data,
int argc, char **argv)
{
struct tep_handle *tep = data;
struct tep_event *event;
char *command;
char *system;
char *ename;
char *name;
char *sav;
int ret;
int i;
if (argc < 3) {
ccli_printf(ccli, "# usage: create eprobe name system/event fields\n");
return 0;
}
name = argv[0];
system = strtok_r(argv[1], "/", &sav);
ename = strtok_r(NULL, "/", &sav);
if (!ename) {
event = tep_find_event_by_name(tep, NULL, system);
if (!event) {
ccli_printf(ccli, "# Event %s not found\n", system);
return 0;
}
system = event->system;
} else {
event = tep_find_event_by_name(tep, system, ename);
if (!event) {
ccli_printf(ccli, "# Event %s/%s not found\n",
system, ename);
return 0;
}
}
ret = asprintf(&command, "e:%s %s/%s", name, system, ename);
if (ret < 0)
return 0;
for (i = 2 ; i < argc; i++ ) {
ret = add_event_var(ccli, tep, &command, event, argv[i]);
if (ret < 0)
goto out;
}
ccli_printf(ccli, "# echo '%s' >> %s/dynamic_events\n",
command, tracefs_tracing_dir());
out:
free(command);
return 0;
}
int cmd_create(struct ccli *ccli, const char *command, const char *line,
void *data, int argc, char **argv)
{
if (argc < 2) {
create_usage(ccli);
return 0;
}
if (strcmp(argv[1], "kprobe") == 0)
return create_kprobe(ccli, data, argc - 2, argv + 2);
if (strcmp(argv[1], "eprobe") == 0)
return create_eprobe(ccli, data, argc - 2, argv + 2);
return 0;
}
static int kprobe_completion(struct ccli *ccli, void *data,
int argc, char **argv,
char ***list, int word, char *match)
{
return 0;
}
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_format_field **common_fields;
struct tep_format_field **fields;
struct tep_event *event;
static char *types[] = {"string" , "ustring", "x8", "x16", "x32", "x64",
"u8", "u16", "u32", "u64", "s8", "s16", "s32", "s64" };
char **systems;
char **events;
char **words;
char **tmp;
char *system;
char *name;
char *sav;
char *p, *m;
int len;
int i, x;
switch (word) {
case 0:
ccli_printf(ccli, "\n# Name the event probe\n");
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;
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);
if (!event) {
ccli_printf(ccli, "\n# Event %s/%s not found\n",
system, name);
return 0;
}
p = strchr(match, '=');
if (!p) {
ccli_printf(ccli, "\n# var=field[.type][->offset[.type]\n");
ccli_line_refresh(ccli);
return 0;
}
m = p;
while (*p) {
if (*p == '.' || *p == '-')
m = p;
p++;
}
len = m - match;
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;
case '.':
x = ARRAY_SIZE(types);
words = calloc(x, sizeof(char *));
if (!words)
return 0;
for (i = 0; i < x; i++) {
asprintf(&words[i], "%.*s.%s",
len, match, types[i]);
}
*list = words;
match[strlen(match)] = CCLI_NOSPACE;
return x;
case '-':
if (!m[1]) {
words = calloc(1, sizeof(char *));
if (!words)
return 0;
asprintf(&words[0], "%.*s->",
len, match);
*list = words;
match[strlen(match)] = CCLI_NOSPACE;
return 1;
}
return 0;
}
}
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)
{
char *types[] = { "kprobe", "eprobe", "synthetic" };
char **words;
char **argv;
int argc;
int ret = 0;
int i;
if (word == 1) {
words = calloc(ARRAY_SIZE(types), sizeof(char *));
if (!words)
return 0;
for (i = 0; i < ARRAY_SIZE(types); i++)
words[i] = strdup(types[i]);
*list = words;
return i;
}
argc = ccli_line_parse(line, &argv);
if (argc < 0)
return 0;
if (strcmp(argv[1], "kprobe") == 0)
ret = kprobe_completion(ccli, data, argc - 2, argv + 2,
list, word - 2, match);
if (strcmp(argv[1], "eprobe") == 0)
ret = eprobe_completion(ccli, data, argc - 2, argv + 2,
list, word - 2, match);
ccli_argv_free(argv);
return ret;
}