|
@@ -304,7 +304,10 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid)
|
|
|
if (!item)
|
|
|
return -1;
|
|
|
|
|
|
- item->comm = strdup(comm);
|
|
|
+ if (comm)
|
|
|
+ item->comm = strdup(comm);
|
|
|
+ else
|
|
|
+ item->comm = strdup("<...>");
|
|
|
if (!item->comm) {
|
|
|
free(item);
|
|
|
return -1;
|
|
@@ -318,9 +321,14 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-void pevent_register_trace_clock(struct pevent *pevent, char *trace_clock)
|
|
|
+int pevent_register_trace_clock(struct pevent *pevent, const char *trace_clock)
|
|
|
{
|
|
|
- pevent->trace_clock = trace_clock;
|
|
|
+ pevent->trace_clock = strdup(trace_clock);
|
|
|
+ if (!pevent->trace_clock) {
|
|
|
+ errno = ENOMEM;
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
struct func_map {
|
|
@@ -758,6 +766,11 @@ static void free_arg(struct print_arg *arg)
|
|
|
free_arg(arg->hex.field);
|
|
|
free_arg(arg->hex.size);
|
|
|
break;
|
|
|
+ case PRINT_INT_ARRAY:
|
|
|
+ free_arg(arg->int_array.field);
|
|
|
+ free_arg(arg->int_array.count);
|
|
|
+ free_arg(arg->int_array.el_size);
|
|
|
+ break;
|
|
|
case PRINT_TYPE:
|
|
|
free(arg->typecast.type);
|
|
|
free_arg(arg->typecast.item);
|
|
@@ -2014,6 +2027,38 @@ process_entry(struct event_format *event __maybe_unused, struct print_arg *arg,
|
|
|
return EVENT_ERROR;
|
|
|
}
|
|
|
|
|
|
+static int alloc_and_process_delim(struct event_format *event, char *next_token,
|
|
|
+ struct print_arg **print_arg)
|
|
|
+{
|
|
|
+ struct print_arg *field;
|
|
|
+ enum event_type type;
|
|
|
+ char *token;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ field = alloc_arg();
|
|
|
+ if (!field) {
|
|
|
+ do_warning_event(event, "%s: not enough memory!", __func__);
|
|
|
+ errno = ENOMEM;
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ type = process_arg(event, field, &token);
|
|
|
+
|
|
|
+ if (test_type_token(type, token, EVENT_DELIM, next_token)) {
|
|
|
+ errno = EINVAL;
|
|
|
+ ret = -1;
|
|
|
+ free_arg(field);
|
|
|
+ goto out_free_token;
|
|
|
+ }
|
|
|
+
|
|
|
+ *print_arg = field;
|
|
|
+
|
|
|
+out_free_token:
|
|
|
+ free_token(token);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static char *arg_eval (struct print_arg *arg);
|
|
|
|
|
|
static unsigned long long
|
|
@@ -2486,49 +2531,46 @@ out_free:
|
|
|
static enum event_type
|
|
|
process_hex(struct event_format *event, struct print_arg *arg, char **tok)
|
|
|
{
|
|
|
- struct print_arg *field;
|
|
|
- enum event_type type;
|
|
|
- char *token = NULL;
|
|
|
-
|
|
|
memset(arg, 0, sizeof(*arg));
|
|
|
arg->type = PRINT_HEX;
|
|
|
|
|
|
- field = alloc_arg();
|
|
|
- if (!field) {
|
|
|
- do_warning_event(event, "%s: not enough memory!", __func__);
|
|
|
- goto out_free;
|
|
|
- }
|
|
|
-
|
|
|
- type = process_arg(event, field, &token);
|
|
|
+ if (alloc_and_process_delim(event, ",", &arg->hex.field))
|
|
|
+ goto out;
|
|
|
|
|
|
- if (test_type_token(type, token, EVENT_DELIM, ","))
|
|
|
- goto out_free;
|
|
|
+ if (alloc_and_process_delim(event, ")", &arg->hex.size))
|
|
|
+ goto free_field;
|
|
|
|
|
|
- arg->hex.field = field;
|
|
|
+ return read_token_item(tok);
|
|
|
|
|
|
- free_token(token);
|
|
|
+free_field:
|
|
|
+ free_arg(arg->hex.field);
|
|
|
+out:
|
|
|
+ *tok = NULL;
|
|
|
+ return EVENT_ERROR;
|
|
|
+}
|
|
|
|
|
|
- field = alloc_arg();
|
|
|
- if (!field) {
|
|
|
- do_warning_event(event, "%s: not enough memory!", __func__);
|
|
|
- *tok = NULL;
|
|
|
- return EVENT_ERROR;
|
|
|
- }
|
|
|
+static enum event_type
|
|
|
+process_int_array(struct event_format *event, struct print_arg *arg, char **tok)
|
|
|
+{
|
|
|
+ memset(arg, 0, sizeof(*arg));
|
|
|
+ arg->type = PRINT_INT_ARRAY;
|
|
|
|
|
|
- type = process_arg(event, field, &token);
|
|
|
+ if (alloc_and_process_delim(event, ",", &arg->int_array.field))
|
|
|
+ goto out;
|
|
|
|
|
|
- if (test_type_token(type, token, EVENT_DELIM, ")"))
|
|
|
- goto out_free;
|
|
|
+ if (alloc_and_process_delim(event, ",", &arg->int_array.count))
|
|
|
+ goto free_field;
|
|
|
|
|
|
- arg->hex.size = field;
|
|
|
+ if (alloc_and_process_delim(event, ")", &arg->int_array.el_size))
|
|
|
+ goto free_size;
|
|
|
|
|
|
- free_token(token);
|
|
|
- type = read_token_item(tok);
|
|
|
- return type;
|
|
|
+ return read_token_item(tok);
|
|
|
|
|
|
- out_free:
|
|
|
- free_arg(field);
|
|
|
- free_token(token);
|
|
|
+free_size:
|
|
|
+ free_arg(arg->int_array.count);
|
|
|
+free_field:
|
|
|
+ free_arg(arg->int_array.field);
|
|
|
+out:
|
|
|
*tok = NULL;
|
|
|
return EVENT_ERROR;
|
|
|
}
|
|
@@ -2828,6 +2870,10 @@ process_function(struct event_format *event, struct print_arg *arg,
|
|
|
free_token(token);
|
|
|
return process_hex(event, arg, tok);
|
|
|
}
|
|
|
+ if (strcmp(token, "__print_array") == 0) {
|
|
|
+ free_token(token);
|
|
|
+ return process_int_array(event, arg, tok);
|
|
|
+ }
|
|
|
if (strcmp(token, "__get_str") == 0) {
|
|
|
free_token(token);
|
|
|
return process_str(event, arg, tok);
|
|
@@ -3356,6 +3402,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
|
|
|
break;
|
|
|
case PRINT_FLAGS:
|
|
|
case PRINT_SYMBOL:
|
|
|
+ case PRINT_INT_ARRAY:
|
|
|
case PRINT_HEX:
|
|
|
break;
|
|
|
case PRINT_TYPE:
|
|
@@ -3766,6 +3813,54 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
+ case PRINT_INT_ARRAY: {
|
|
|
+ void *num;
|
|
|
+ int el_size;
|
|
|
+
|
|
|
+ if (arg->int_array.field->type == PRINT_DYNAMIC_ARRAY) {
|
|
|
+ unsigned long offset;
|
|
|
+ struct format_field *field =
|
|
|
+ arg->int_array.field->dynarray.field;
|
|
|
+ offset = pevent_read_number(pevent,
|
|
|
+ data + field->offset,
|
|
|
+ field->size);
|
|
|
+ num = data + (offset & 0xffff);
|
|
|
+ } else {
|
|
|
+ field = arg->int_array.field->field.field;
|
|
|
+ if (!field) {
|
|
|
+ str = arg->int_array.field->field.name;
|
|
|
+ field = pevent_find_any_field(event, str);
|
|
|
+ if (!field)
|
|
|
+ goto out_warning_field;
|
|
|
+ arg->int_array.field->field.field = field;
|
|
|
+ }
|
|
|
+ num = data + field->offset;
|
|
|
+ }
|
|
|
+ len = eval_num_arg(data, size, event, arg->int_array.count);
|
|
|
+ el_size = eval_num_arg(data, size, event,
|
|
|
+ arg->int_array.el_size);
|
|
|
+ for (i = 0; i < len; i++) {
|
|
|
+ if (i)
|
|
|
+ trace_seq_putc(s, ' ');
|
|
|
+
|
|
|
+ if (el_size == 1) {
|
|
|
+ trace_seq_printf(s, "%u", *(uint8_t *)num);
|
|
|
+ } else if (el_size == 2) {
|
|
|
+ trace_seq_printf(s, "%u", *(uint16_t *)num);
|
|
|
+ } else if (el_size == 4) {
|
|
|
+ trace_seq_printf(s, "%u", *(uint32_t *)num);
|
|
|
+ } else if (el_size == 8) {
|
|
|
+ trace_seq_printf(s, "%lu", *(uint64_t *)num);
|
|
|
+ } else {
|
|
|
+ trace_seq_printf(s, "BAD SIZE:%d 0x%x",
|
|
|
+ el_size, *(uint8_t *)num);
|
|
|
+ el_size = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ num += el_size;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
case PRINT_TYPE:
|
|
|
break;
|
|
|
case PRINT_STRING: {
|
|
@@ -3997,6 +4092,10 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
|
|
|
goto process_again;
|
|
|
case '.':
|
|
|
goto process_again;
|
|
|
+ case 'z':
|
|
|
+ case 'Z':
|
|
|
+ ls = 1;
|
|
|
+ goto process_again;
|
|
|
case 'p':
|
|
|
ls = 1;
|
|
|
/* fall through */
|
|
@@ -4939,6 +5038,96 @@ const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid)
|
|
|
return comm;
|
|
|
}
|
|
|
|
|
|
+static struct cmdline *
|
|
|
+pid_from_cmdlist(struct pevent *pevent, const char *comm, struct cmdline *next)
|
|
|
+{
|
|
|
+ struct cmdline_list *cmdlist = (struct cmdline_list *)next;
|
|
|
+
|
|
|
+ if (cmdlist)
|
|
|
+ cmdlist = cmdlist->next;
|
|
|
+ else
|
|
|
+ cmdlist = pevent->cmdlist;
|
|
|
+
|
|
|
+ while (cmdlist && strcmp(cmdlist->comm, comm) != 0)
|
|
|
+ cmdlist = cmdlist->next;
|
|
|
+
|
|
|
+ return (struct cmdline *)cmdlist;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pevent_data_pid_from_comm - return the pid from a given comm
|
|
|
+ * @pevent: a handle to the pevent
|
|
|
+ * @comm: the cmdline to find the pid from
|
|
|
+ * @next: the cmdline structure to find the next comm
|
|
|
+ *
|
|
|
+ * This returns the cmdline structure that holds a pid for a given
|
|
|
+ * comm, or NULL if none found. As there may be more than one pid for
|
|
|
+ * a given comm, the result of this call can be passed back into
|
|
|
+ * a recurring call in the @next paramater, and then it will find the
|
|
|
+ * next pid.
|
|
|
+ * Also, it does a linear seach, so it may be slow.
|
|
|
+ */
|
|
|
+struct cmdline *pevent_data_pid_from_comm(struct pevent *pevent, const char *comm,
|
|
|
+ struct cmdline *next)
|
|
|
+{
|
|
|
+ struct cmdline *cmdline;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If the cmdlines have not been converted yet, then use
|
|
|
+ * the list.
|
|
|
+ */
|
|
|
+ if (!pevent->cmdlines)
|
|
|
+ return pid_from_cmdlist(pevent, comm, next);
|
|
|
+
|
|
|
+ if (next) {
|
|
|
+ /*
|
|
|
+ * The next pointer could have been still from
|
|
|
+ * a previous call before cmdlines were created
|
|
|
+ */
|
|
|
+ if (next < pevent->cmdlines ||
|
|
|
+ next >= pevent->cmdlines + pevent->cmdline_count)
|
|
|
+ next = NULL;
|
|
|
+ else
|
|
|
+ cmdline = next++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!next)
|
|
|
+ cmdline = pevent->cmdlines;
|
|
|
+
|
|
|
+ while (cmdline < pevent->cmdlines + pevent->cmdline_count) {
|
|
|
+ if (strcmp(cmdline->comm, comm) == 0)
|
|
|
+ return cmdline;
|
|
|
+ cmdline++;
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pevent_cmdline_pid - return the pid associated to a given cmdline
|
|
|
+ * @cmdline: The cmdline structure to get the pid from
|
|
|
+ *
|
|
|
+ * Returns the pid for a give cmdline. If @cmdline is NULL, then
|
|
|
+ * -1 is returned.
|
|
|
+ */
|
|
|
+int pevent_cmdline_pid(struct pevent *pevent, struct cmdline *cmdline)
|
|
|
+{
|
|
|
+ struct cmdline_list *cmdlist = (struct cmdline_list *)cmdline;
|
|
|
+
|
|
|
+ if (!cmdline)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If cmdlines have not been created yet, or cmdline is
|
|
|
+ * not part of the array, then treat it as a cmdlist instead.
|
|
|
+ */
|
|
|
+ if (!pevent->cmdlines ||
|
|
|
+ cmdline < pevent->cmdlines ||
|
|
|
+ cmdline >= pevent->cmdlines + pevent->cmdline_count)
|
|
|
+ return cmdlist->pid;
|
|
|
+
|
|
|
+ return cmdline->pid;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* pevent_data_comm_from_pid - parse the data into the print format
|
|
|
* @s: the trace_seq to write to
|
|
@@ -5256,6 +5445,15 @@ static void print_args(struct print_arg *args)
|
|
|
print_args(args->hex.size);
|
|
|
printf(")");
|
|
|
break;
|
|
|
+ case PRINT_INT_ARRAY:
|
|
|
+ printf("__print_array(");
|
|
|
+ print_args(args->int_array.field);
|
|
|
+ printf(", ");
|
|
|
+ print_args(args->int_array.count);
|
|
|
+ printf(", ");
|
|
|
+ print_args(args->int_array.el_size);
|
|
|
+ printf(")");
|
|
|
+ break;
|
|
|
case PRINT_STRING:
|
|
|
case PRINT_BSTRING:
|
|
|
printf("__get_str(%s)", args->string.string);
|
|
@@ -6346,6 +6544,7 @@ void pevent_free(struct pevent *pevent)
|
|
|
free_handler(handle);
|
|
|
}
|
|
|
|
|
|
+ free(pevent->trace_clock);
|
|
|
free(pevent->events);
|
|
|
free(pevent->sort_events);
|
|
|
|