aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre Gondois <pierre.gondois@arm.com>2024-02-22 17:54:18 +0100
committerSteven Rostedt (Google) <rostedt@goodmis.org>2024-02-22 20:41:40 -0500
commit55adb82d8d7baa68f87f2b300eb44028db1a544a (patch)
treebd21ed01b2e2015ba87bc7f5e0ad6391b48d7d00
parent08c32816684461f464f738cd8f5773bc14af66db (diff)
downloadtrace-cmd-master.tar.gz
trace-cmd split: Enable support for buffer selectionHEADupstream-mastermaster
The 'trace-cmd split' command conserves all buffers/instances of the input .dat file. Add support to select the instances to keep: - '--top' to keep the top instance - '-b' top rename the top instance. Must follow '--top'. - '-B buffer' to keep a buffer/instance - t top promote a buffer to the top instance. Must follow '-B'. For example, with a trace recorded with: $ trace-cmd record -e sched_wakeup -B switch_instance \ -e sched_switch Creating a test.dat file containing the top instance and the switch_instance: $ trace-cmd split --top -B switch_instance -o test.dat Creating a test.dat file containing the switch_instance as the top instance, and the initial top instance as an instance named 'old_top': $ trace-cmd split --top -b old_top -B switch_instance -t \ -o test.dat Also update the trace-usage.c and the relevant documentation for the new options. Link: https://lore.kernel.org/linux-trace-devel/20240222165418.2153026-2-pierre.gondois@arm.com Signed-off-by: Pierre Gondois <pierre.gondois@arm.com> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
-rw-r--r--Documentation/trace-cmd/trace-cmd-split.1.txt25
-rw-r--r--tracecmd/trace-split.c184
-rw-r--r--tracecmd/trace-usage.c7
3 files changed, 203 insertions, 13 deletions
diff --git a/Documentation/trace-cmd/trace-cmd-split.1.txt b/Documentation/trace-cmd/trace-cmd-split.1.txt
index 25385796..d2fea252 100644
--- a/Documentation/trace-cmd/trace-cmd-split.1.txt
+++ b/Documentation/trace-cmd/trace-cmd-split.1.txt
@@ -86,6 +86,31 @@ OPTIONS
This will split out all the events for cpu 1 in the file.
+*--top*::
+ This allows to keep the top buffer.
+ The top buffer can be renamed using the '-b' option.
+
+ trace-cmd split --top
+
+ This will keep only the top buffer.
+
+ trace-cmd split --top -b old_top
+
+ This will keep only the top buffer and rename it 'old_top'.
+
+*-B* 'buffer'::
+ This allows to keep the selected buffer.
+ A buffer can be promoted to the top buffer using the '-t' option.
+
+ trace-cmd split -B timer -B sched
+
+ This will keep the 'timer' and 'sched' buffers.
+
+ trace-cmd split -B timer -t -B sched
+
+ This will keep the 'timer' and 'sched' buffers, with the events
+ from the 'timer' buffer promoted to the top instance.
+
SEE ALSO
--------
trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1),
diff --git a/tracecmd/trace-split.c b/tracecmd/trace-split.c
index cb6242d8..13a10c89 100644
--- a/tracecmd/trace-split.c
+++ b/tracecmd/trace-split.c
@@ -59,9 +59,6 @@ struct handle_list {
/* Identify the top instance in the input trace. */
bool was_top_instance;
-
- /* Identify the top instance in each output trace. */
- bool is_top_instance;
};
static struct list_head handle_list;
@@ -109,10 +106,9 @@ static void add_handle(const char *name, int index, bool was_top_instance)
static void free_handles(struct list_head *list)
{
- struct handle_list *item;
+ struct handle_list *item, *n;
- while (!list_empty(list)) {
- item = container_of(list->next, struct handle_list, list);
+ list_for_each_entry_safe(item, n, list, list) {
list_del(&item->list);
free(item->name);
tracecmd_close(item->handle);
@@ -120,6 +116,50 @@ static void free_handles(struct list_head *list)
}
}
+static struct list_head inst_list;
+
+struct inst_list {
+ struct list_head list;
+ char *name;
+ struct handle_list *handle;
+
+ /* Identify the top instance in the input trace. */
+ bool was_top_instance;
+
+ /* Identify the top instance in the output trace. */
+ bool is_top_instance;
+};
+
+static void free_inst(struct list_head *list)
+{
+ struct inst_list *item, *n;
+
+ list_for_each_entry_safe(item, n, list, list) {
+ list_del(&item->list);
+ free(item->name);
+ free(item);
+ }
+}
+
+static struct inst_list *add_inst(const char *name, bool was_top_instance,
+ bool is_top_instance)
+{
+ struct inst_list *item;
+
+ item = calloc(1, sizeof(*item));
+ if (!item)
+ die("Failed to allocate output_file item");
+
+ item->name = strdup(name);
+ if (!item->name)
+ die("Failed to duplicate %s", name);
+
+ item->was_top_instance = was_top_instance;
+ item->is_top_instance = is_top_instance;
+ list_add_tail(&item->list, &inst_list);
+ return item;
+}
+
static int create_type_len(struct tep_handle *pevent, int time, int len)
{
static int bigendian = -1;
@@ -481,8 +521,8 @@ static unsigned long long parse_file(struct tracecmd_input *handle,
bool *end_reached)
{
unsigned long long current = 0;
- struct handle_list *handle_entry;
struct tracecmd_output *ohandle;
+ struct inst_list *inst_entry;
struct cpu_data *cpu_data;
struct tep_record *record;
bool all_end_reached = true;
@@ -496,18 +536,18 @@ static unsigned long long parse_file(struct tracecmd_input *handle,
ohandle = tracecmd_copy(handle, output_file, TRACECMD_FILE_CMD_LINES, 0, NULL);
tracecmd_set_out_clock(ohandle, tracecmd_get_trace_clock(handle));
- list_for_each_entry(handle_entry, &handle_list, list) {
+ list_for_each_entry(inst_entry, &inst_list, list) {
struct tracecmd_input *curr_handle;
bool curr_end_reached = false;
- curr_handle = handle_entry->handle;
+ curr_handle = inst_entry->handle->handle;
cpus = tracecmd_cpus(curr_handle);
cpu_data = malloc(sizeof(*cpu_data) * cpus);
if (!cpu_data)
die("Failed to allocate cpu_data for %d cpus", cpus);
for (cpu = 0; cpu < cpus; cpu++) {
- file = get_temp_file(output_file, handle_entry->name, cpu);
+ file = get_temp_file(output_file, inst_entry->name, cpu);
touch_file(file);
fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
@@ -540,10 +580,10 @@ static unsigned long long parse_file(struct tracecmd_input *handle,
for (cpu = 0; cpu < cpus; cpu++)
cpu_list[cpu] = cpu_data[cpu].file;
- if (handle_entry->was_top_instance)
+ if (inst_entry->is_top_instance)
ret = tracecmd_append_cpu_data(ohandle, cpus, cpu_list);
else
- ret = tracecmd_append_buffer_cpu_data(ohandle, handle_entry->name, cpus,
+ ret = tracecmd_append_buffer_cpu_data(ohandle, inst_entry->name, cpus,
cpu_list);
if (ret < 0)
die("Failed to append tracing data\n");
@@ -573,11 +613,80 @@ static unsigned long long parse_file(struct tracecmd_input *handle,
return current;
}
+/* Map the instance names to their handle. */
+static void map_inst_handle(void)
+{
+ struct handle_list *handle_entry;
+ struct inst_list *inst_entry;
+
+ /*
+ * No specific instance was given for this output file.
+ * Add all the available instances.
+ */
+ if (list_empty(&inst_list)) {
+ list_for_each_entry(handle_entry, &handle_list, list) {
+ add_inst(handle_entry->name, handle_entry->was_top_instance,
+ handle_entry->was_top_instance);
+ }
+ }
+
+ list_for_each_entry(inst_entry, &inst_list, list) {
+ list_for_each_entry(handle_entry, &handle_list, list) {
+ if ((inst_entry->was_top_instance &&
+ handle_entry->was_top_instance) ||
+ (!inst_entry->was_top_instance &&
+ !strcmp(handle_entry->name, inst_entry->name))) {
+ inst_entry->handle = handle_entry;
+ goto found;
+ }
+ }
+
+ warning("Requested instance %s was not found in trace.", inst_entry->name);
+ break;
+found:
+ continue;
+ }
+}
+
+static bool is_top_instance_unique(void)
+{
+ struct inst_list *inst_entry;
+ bool has_top_buffer = false;
+
+ /* Check there is at most one top buffer. */
+ list_for_each_entry(inst_entry, &inst_list, list) {
+ if (inst_entry->is_top_instance) {
+ if (has_top_buffer)
+ return false;
+ has_top_buffer = true;
+ }
+ }
+
+ return true;
+}
+
+enum {
+ OPT_top = 237,
+};
+
+/*
+ * Used to identify the arg. previously parsed.
+ * E.g. '-b' can only follow '--top'.
+ */
+enum prev_arg_type {
+ PREV_IS_NONE,
+ PREV_IS_TOP,
+ PREV_IS_BUFFER,
+};
+
void trace_split (int argc, char **argv)
{
struct tracecmd_input *handle;
unsigned long long start_ns = 0, end_ns = 0;
unsigned long long current;
+ enum prev_arg_type prev_arg_type;
+ struct inst_list *prev_inst = NULL;
+ int prev_arg_idx;
bool end_reached = false;
double start, end;
char *endptr;
@@ -593,12 +702,22 @@ void trace_split (int argc, char **argv)
int ac;
int c;
+ static struct option long_options[] = {
+ {"top", optional_argument, NULL, OPT_top},
+ {NULL, 0, NULL, 0},
+ };
+ int option_index = 0;
+
+ prev_arg_type = PREV_IS_NONE;
+
list_head_init(&handle_list);
+ list_head_init(&inst_list);
if (strcmp(argv[1], "split") != 0)
usage(argv);
- while ((c = getopt(argc-1, argv+1, "+ho:i:s:m:u:e:p:rcC:")) >= 0) {
+ while ((c = getopt_long(argc - 1, argv + 1, "+ho:i:s:m:u:e:p:rcC:B:b:t",
+ long_options, &option_index)) >= 0) {
switch (c) {
case 'h':
usage(argv);
@@ -641,11 +760,47 @@ void trace_split (int argc, char **argv)
case 'i':
input_file = optarg;
break;
+ case OPT_top:
+ prev_arg_type = PREV_IS_TOP;
+ prev_arg_idx = optind;
+ prev_inst = add_inst(default_top_instance_name, true, true);
+ break;
+ case 'b':
+ /* 1 as --top takes no argument. */
+ if (prev_arg_type != PREV_IS_TOP &&
+ (prev_arg_idx != optind - 1))
+ usage(argv);
+ prev_arg_type = PREV_IS_NONE;
+
+ prev_inst->is_top_instance = false;
+
+ free(prev_inst->name);
+ prev_inst->name = strdup(optarg);
+ if (!prev_inst->name)
+ die("Failed to duplicate %s", optarg);
+ break;
+ case 'B':
+ prev_arg_type = PREV_IS_BUFFER;
+ prev_arg_idx = optind;
+ prev_inst = add_inst(optarg, false, false);
+ break;
+ case 't':
+ /* 2 as -B takes an argument. */
+ if (prev_arg_type != PREV_IS_BUFFER &&
+ (prev_arg_idx != optind - 2))
+ usage(argv);
+ prev_arg_type = PREV_IS_NONE;
+
+ prev_inst->is_top_instance = true;
+ break;
default:
usage(argv);
}
}
+ if (!is_top_instance_unique())
+ die("Can only have one top instance.");
+
ac = (argc - optind);
if (ac >= 2) {
@@ -713,6 +868,8 @@ void trace_split (int argc, char **argv)
}
}
+ map_inst_handle();
+
do {
if (repeat)
sprintf(output_file, "%s.%04d", output, c++);
@@ -732,6 +889,7 @@ void trace_split (int argc, char **argv)
tracecmd_close(handle);
free_handles(&handle_list);
+ free_inst(&inst_list);
return;
}
diff --git a/tracecmd/trace-usage.c b/tracecmd/trace-usage.c
index 6944a2c7..8bbf2e3e 100644
--- a/tracecmd/trace-usage.c
+++ b/tracecmd/trace-usage.c
@@ -311,6 +311,13 @@ static struct usage_help usage_help[] = {
" -u n split file up by n microseconds\n"
" -e n split file up by n events\n"
" -p n split file up by n pages\n"
+ " -C n select CPU n\n"
+ " -B buffer keep buffer in resulting .dat file\n"
+ " Use -t to promote the buffer to the top instance.\n"
+ " -t promote preceding buffer to the top instance.\n"
+ " Must follow -B.\n"
+ " --top keep top buffer in resulting .dat file.\n"
+ " -b new name of the top instance. Must follow --top.\n"
" -r repeat from start to end\n"
" -c per cpu, that is -p 2 will be 2 pages for each CPU\n"
" if option is specified, it will split the file\n"