From patchwork Tue Jan 23 13:42:11 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pierre Gondois X-Patchwork-Id: 13527460 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 997E35F544 for ; Tue, 23 Jan 2024 13:42:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706017357; cv=none; b=pad2HRgZZ/0Czx+yU7JsqmHC7SsvjqzJoewYcyjvEaDCZsVYoEM2MOXBbVwhwF0ui3njhvnhaeEk4e9JXtNMX4p9UFHu0t0he8jr0OBkU4K3sg5RpjW0qrV/PKP+L35gkIQGIDDdRFKYKz/+1kQaIrUGkRngkk9yioKR9fRwaKM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706017357; c=relaxed/simple; bh=GAx2bRdrHbj7QR3k8vLkNnkwc/bW4AXtT3WADmfqroE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=BtV/P7UbW6YshBSbxZdm7k09ozXNBqqoyw6Xcc+H5FX1I43J1Up6w3R58gaV597ZkkRVojL24RIe2u2dKXjAaOierR92tEViHw0uGgTTnI4gEeH2ugAy2HXXu3pOO3iPTwfgJgi+i51MCdm5pH5imLHdiscMVG8NsJL3uVHiY7M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 6A143106F; Tue, 23 Jan 2024 05:43:20 -0800 (PST) Received: from e126645.home (unknown [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 197D23F7CE; Tue, 23 Jan 2024 05:42:33 -0800 (PST) From: Pierre Gondois To: Linux Trace Devel Cc: Steven Rostedt , Pierre Gondois Subject: [PATCH v3 1/5] trace-cmd split: Store instances in local list Date: Tue, 23 Jan 2024 14:42:11 +0100 Message-Id: <20240123134215.385415-2-pierre.gondois@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240123134215.385415-1-pierre.gondois@arm.com> References: <20240123134215.385415-1-pierre.gondois@arm.com> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To prepare handling of multiple instances, store instance handles in a local list, similarly to what is currently done in tracecmd/trace-read.c. To help achieve this goal, add a 'struct handle_list' and add_handle()/free_handles() functions. 'struct handle' elements are added to the static list, but not used in this patch. Signed-off-by: Pierre Gondois --- tracecmd/trace-split.c | 89 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/tracecmd/trace-split.c b/tracecmd/trace-split.c index b6c056b5..d5b77ab7 100644 --- a/tracecmd/trace-split.c +++ b/tracecmd/trace-split.c @@ -19,10 +19,12 @@ #include #include +#include "list.h" #include "trace-local.h" static unsigned int page_size; static const char *default_input_file = DEFAULT_INPUT_FILE; +static const char *default_top_instance_name = "top"; static const char *input_file; enum split_types { @@ -49,6 +51,75 @@ struct cpu_data { char *file; }; +struct handle_list { + struct list_head list; + const char *name; + int index; + struct tracecmd_input *handle; + + /* 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; + +/** + * get_handle - Obtain a handle that must be closed once finished. + */ +static struct tracecmd_input *get_handle(struct handle_list *item) +{ + struct tracecmd_input *top_handle, *handle; + + top_handle = tracecmd_open(input_file, 0); + if (!top_handle) + die("Error reading %s", input_file); + + if (item->was_top_instance) { + return top_handle; + } else { + handle = tracecmd_buffer_instance_handle(top_handle, item->index); + if (!handle) + warning("Could not retrieve handle %s", item->name); + + tracecmd_close(top_handle); + return handle; + } +} + +static void add_handle(const char *name, int index, bool was_top_instance) +{ + struct handle_list *item; + + item = calloc(1, sizeof(*item)); + if (!item) + die("Failed to allocate handle item"); + + item->name = strdup(name); + if (!item->name) + die("Failed to duplicate %s", name); + + item->index = index; + item->was_top_instance = was_top_instance; + item->handle = get_handle(item); + list_add_tail(&item->list, &handle_list); +} + +static void free_handles(struct list_head *list) +{ + struct handle_list *item; + + while (!list_empty(list)) { + item = container_of(list->next, struct handle_list, list); + list_del(&item->list); + free((char *)item->name); + tracecmd_close(item->handle); + free(item); + } +} + static int create_type_len(struct tep_handle *pevent, int time, int len) { static int bigendian = -1; @@ -450,6 +521,7 @@ void trace_split (int argc, char **argv) char *output_file; enum split_types split_type = SPLIT_NONE; enum split_types type = SPLIT_NONE; + int instances; int count; int repeat = 0; int percpu = 0; @@ -457,6 +529,8 @@ void trace_split (int argc, char **argv) int ac; int c; + list_head_init(&handle_list); + if (strcmp(argv[1], "split") != 0) usage(argv); @@ -561,6 +635,20 @@ void trace_split (int argc, char **argv) die("Failed to allocate for %s", output); c = 1; + add_handle(default_top_instance_name, -1, true); + instances = tracecmd_buffer_instances(handle); + if (instances) { + const char *name; + int i; + + for (i = 0; i < instances; i++) { + name = tracecmd_buffer_instance_name(handle, i); + if (!name) + die("error in reading buffer instance"); + add_handle(name, i, false); + } + } + do { if (repeat) sprintf(output_file, "%s.%04d", output, c++); @@ -579,6 +667,7 @@ void trace_split (int argc, char **argv) free(output_file); tracecmd_close(handle); + free_handles(&handle_list); return; } From patchwork Tue Jan 23 13:42:12 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pierre Gondois X-Patchwork-Id: 13527461 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 39AF75EE81 for ; Tue, 23 Jan 2024 13:42:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706017359; cv=none; b=L5Voi/pJn38tBjz+v46OejNaO6AVOrp4WWmZ2CE5wU7AXDW6GJTcl3/Bi9N5k7EfI6ruo2fWyqxtjrQjso5RMz+P2B3QfyH0lYh/xTmvCEaMVMt/sRWm+JkbgFLDBH2VRzxfxEJcWEnn34lZaWyMyOph8sh+dCwP6+93hqfGO9c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706017359; c=relaxed/simple; bh=FiJS2gY0BjdjMvUELYGh39y/t8j1TJhYi1720rbn8y8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=d1oUdGTkvfYT3EJB253jypJkFKs1hIJ3k4RXQJ93Y1mMASDBVMCLm0LNS0CewHplyKEBXjhsYrodHD1LiZ6G/x0SfIu4PESHbaaRugzUGOzSd0RUDNwfvNEve/oZ7D0LjYJMmOyelEFXttZlpW9YIMYqleynt5tejrj2+TMiHec= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id E3D63113E; Tue, 23 Jan 2024 05:43:21 -0800 (PST) Received: from e126645.home (unknown [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 7C50D3F762; Tue, 23 Jan 2024 05:42:35 -0800 (PST) From: Pierre Gondois To: Linux Trace Devel Cc: Steven Rostedt , Pierre Gondois Subject: [PATCH v3 2/5] trace-cmd split: Add functions to generate temp files Date: Tue, 23 Jan 2024 14:42:12 +0100 Message-Id: <20240123134215.385415-3-pierre.gondois@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240123134215.385415-1-pierre.gondois@arm.com> References: <20240123134215.385415-1-pierre.gondois@arm.com> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To prepare handling of multiple instances and storing them in temporary files, add utility functions generating file names, removing files, creating files: - get_temp_file() - delete_temp_file() - put_temp_file() - touch_file() Also make use these functions. Signed-off-by: Pierre Gondois --- tracecmd/trace-split.c | 73 +++++++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/tracecmd/trace-split.c b/tracecmd/trace-split.c index d5b77ab7..58ea7c48 100644 --- a/tracecmd/trace-split.c +++ b/tracecmd/trace-split.c @@ -421,6 +421,55 @@ static int parse_cpu(struct tracecmd_input *handle, return 0; } +static char *get_temp_file(const char *output_file, const char *name, int cpu) +{ + const char *dot; + char *file = NULL; + char *output; + char *base; + char *dir; + int ret; + + if (name) + dot = "."; + else + dot = name = ""; + + output = strdup(output_file); + if (!output) + die("Failed to duplicate %s", output_file); + + /* Extract basename() first, as dirname() truncates output */ + base = basename(output); + dir = dirname(output); + + ret = asprintf(&file, "%s/.tmp.%s.%s%s%d", dir, base, name, dot, cpu); + if (ret < 0) + die("Failed to allocate file for %s %s %s %d", dir, base, name, cpu); + free(output); + return file; +} + +static void delete_temp_file(const char *name) +{ + unlink(name); +} + +static void put_temp_file(char *file) +{ + free(file); +} + +static void touch_file(const char *file) +{ + int fd; + + fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + die("could not create file %s\n", file); + close(fd); +} + static unsigned long long parse_file(struct tracecmd_input *handle, const char *output_file, unsigned long long start, @@ -434,19 +483,11 @@ static unsigned long long parse_file(struct tracecmd_input *handle, struct cpu_data *cpu_data; struct tep_record *record; char **cpu_list; - char *output; - char *base; char *file; - char *dir; int cpus; int cpu; int fd; - output = strdup(output_file); - /* Extract basename() first, as dirname() truncates output */ - base = basename(output); - dir = dirname(output); - ohandle = tracecmd_copy(handle, output_file, TRACECMD_FILE_CMD_LINES, 0, NULL); cpus = tracecmd_cpus(handle); @@ -455,11 +496,9 @@ static unsigned long long parse_file(struct tracecmd_input *handle, die("Failed to allocate cpu_data for %d cpus", cpus); for (cpu = 0; cpu < cpus; cpu++) { - int ret; + file = get_temp_file(output_file, NULL, cpu); + touch_file(file); - ret = asprintf(&file, "%s/.tmp.%s.%d", dir, base, cpu); - if (ret < 0) - die("Failed to allocate file for %s %s %d", dir, base, cpu); fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); cpu_data[cpu].cpu = cpu; cpu_data[cpu].fd = fd; @@ -498,12 +537,16 @@ static unsigned long long parse_file(struct tracecmd_input *handle, current = record->ts + 1; tracecmd_free_record(record); } - unlink(cpu_data[cpu].file); - free(cpu_data[cpu].file); + } + + for (cpu = 0; cpu < cpus; cpu++) { + close(cpu_data[cpu].fd); + delete_temp_file(cpu_data[cpu].file); + put_temp_file(cpu_data[cpu].file); } free(cpu_data); free(cpu_list); - free(output); + tracecmd_output_close(ohandle); return current; From patchwork Tue Jan 23 13:42:13 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pierre Gondois X-Patchwork-Id: 13527462 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 9A58F5F558 for ; Tue, 23 Jan 2024 13:42:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706017360; cv=none; b=Zjfoul1AVyfJp1Ys9u+53XJ+SS7nVwGQoI0+JSHbeCuxijchF8XCeEgQ1HHt74Vwbxpz58/pjZFcr96bigj40bJdt4aIMZZHtJ24ycqtkVAGU0BZ2rJT/8DvbSN2N5i7I/zc54b78TGCJOIi3Tm69c9fbvIJXwKhQmp3AJJGcq4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706017360; c=relaxed/simple; bh=HoW+SEY2qfkdlgcsAC42GFAYdXqRS2jm3j38dM4AdlA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ZysiiLb61k+Mo2QJaUYHFQW6AbyeTnMuTwO5V4U0M1xRZJnxYxraB9qvnqmwvYX13AVB7pGvacnuDIHK511K+9/6ukccv/de7hPJRFjfPkUAy2hRO8qCYzj34UxHaFSNb0ic4Q+guTVrSs2DwHEvcpZQ+RcmrYqPIaH6kx2wjZE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 73DD012FC; Tue, 23 Jan 2024 05:43:23 -0800 (PST) Received: from e126645.home (unknown [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 0A0F63F762; Tue, 23 Jan 2024 05:42:36 -0800 (PST) From: Pierre Gondois To: Linux Trace Devel Cc: Steven Rostedt , Pierre Gondois Subject: [PATCH v3 3/5] trace-cmd split: Handle splitting files with multiple instances Date: Tue, 23 Jan 2024 14:42:13 +0100 Message-Id: <20240123134215.385415-4-pierre.gondois@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240123134215.385415-1-pierre.gondois@arm.com> References: <20240123134215.385415-1-pierre.gondois@arm.com> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 trace-cmd can record events in multiple instances: $ trace-cmd record -e sched_wakeup -B test_instance -e sched_switch When trying to split a trace.dat file recorded with the above command, only the events located in the main buffer seems to be split. The events recorded in the test_instance buffer seem to be discarded: $ trace-cmd split -i trace.dat -o trace_2.dat 284443 284444 $ trace-cmd report trace_2.dat cpus=8 <...>-525991 [004] 284443.173879: sched_wakeup: [...] <...>-525991 [004] 284443.173879: sched_wakeup: [...] <...>-525990 [007] 284443.173885: sched_wakeup: [...] <...>-525990 [007] 284443.173885: sched_wakeup: [...] (no sign of sched_switch events) Keep all instances/buffers of a trace when it is split. Also add a 'get_handle()' function. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=218357 Signed-off-by: Pierre Gondois --- tracecmd/trace-split.c | 121 ++++++++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 50 deletions(-) diff --git a/tracecmd/trace-split.c b/tracecmd/trace-split.c index 58ea7c48..0820a3bb 100644 --- a/tracecmd/trace-split.c +++ b/tracecmd/trace-split.c @@ -398,6 +398,8 @@ static int parse_cpu(struct tracecmd_input *handle, if (record && (record->ts > end)) *end_reached = true; + else + *end_reached = false; if (record) tracecmd_free_record(record); @@ -479,76 +481,95 @@ static unsigned long long parse_file(struct tracecmd_input *handle, bool *end_reached) { unsigned long long current; + struct handle_list *handle_entry; struct tracecmd_output *ohandle; struct cpu_data *cpu_data; struct tep_record *record; + bool all_end_reached = true; char **cpu_list; char *file; int cpus; int cpu; + int ret; int fd; ohandle = tracecmd_copy(handle, output_file, TRACECMD_FILE_CMD_LINES, 0, NULL); + tracecmd_set_out_clock(ohandle, tracecmd_get_trace_clock(handle)); - cpus = tracecmd_cpus(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, NULL, cpu); - touch_file(file); - - fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); - cpu_data[cpu].cpu = cpu; - cpu_data[cpu].fd = fd; - cpu_data[cpu].file = file; - cpu_data[cpu].offset = 0; - if (start) - tracecmd_set_cpu_to_timestamp(handle, cpu, start); - } + list_for_each_entry(handle_entry, &handle_list, list) { + struct tracecmd_input *curr_handle; + bool curr_end_reached = false; + + curr_handle = handle_entry->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); + touch_file(file); + + fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); + cpu_data[cpu].cpu = cpu; + cpu_data[cpu].fd = fd; + cpu_data[cpu].file = file; + cpu_data[cpu].offset = 0; + if (start) + tracecmd_set_cpu_to_timestamp(curr_handle, cpu, start); + } + + if (only_cpu >= 0) { + parse_cpu(curr_handle, cpu_data, start, end, count, + 1, only_cpu, type, &curr_end_reached); + } else if (percpu) { + for (cpu = 0; cpu < cpus; cpu++) + parse_cpu(curr_handle, cpu_data, start, + end, count, percpu, cpu, type, &curr_end_reached); + } else { + parse_cpu(curr_handle, cpu_data, start, + end, count, percpu, -1, type, &curr_end_reached); + } - if (only_cpu >= 0) { - parse_cpu(handle, cpu_data, start, end, count, - 1, only_cpu, type, end_reached); - } else if (percpu) { + /* End is reached when all instances finished. */ + all_end_reached &= curr_end_reached; + + cpu_list = malloc(sizeof(*cpu_list) * cpus); + if (!cpu_list) + die("Failed to allocate cpu_list for %d cpus", cpus); for (cpu = 0; cpu < cpus; cpu++) - parse_cpu(handle, cpu_data, start, - end, count, percpu, cpu, type, end_reached); - } else - parse_cpu(handle, cpu_data, start, - end, count, percpu, -1, type, end_reached); - - cpu_list = malloc(sizeof(*cpu_list) * cpus); - if (!cpu_list) - die("Failed to allocate cpu_list for %d cpus", cpus); - for (cpu = 0; cpu < cpus; cpu ++) - cpu_list[cpu] = cpu_data[cpu].file; + cpu_list[cpu] = cpu_data[cpu].file; - tracecmd_set_out_clock(ohandle, tracecmd_get_trace_clock(handle)); - if (tracecmd_append_cpu_data(ohandle, cpus, cpu_list) < 0) - die("Failed to append tracing data\n"); - - for (cpu = 0; cpu < cpus; cpu++) { - /* Set the tracecmd cursor to the next set of records */ - if (cpu_data[cpu].offset) { - record = tracecmd_read_at(handle, cpu_data[cpu].offset, NULL); - if (record && (!current || record->ts > current)) - current = record->ts + 1; - tracecmd_free_record(record); + if (handle_entry->was_top_instance) + ret = tracecmd_append_cpu_data(ohandle, cpus, cpu_list); + else + ret = tracecmd_append_buffer_cpu_data(ohandle, handle_entry->name, cpus, + cpu_list); + if (ret < 0) + die("Failed to append tracing data\n"); + + for (cpu = 0; cpu < cpus; cpu++) { + /* Set the tracecmd cursor to the next set of records */ + if (cpu_data[cpu].offset) { + record = tracecmd_read_at(curr_handle, cpu_data[cpu].offset, NULL); + if (record && (!current || record->ts > current)) + current = record->ts + 1; + tracecmd_free_record(record); + } } - } - for (cpu = 0; cpu < cpus; cpu++) { - close(cpu_data[cpu].fd); - delete_temp_file(cpu_data[cpu].file); - put_temp_file(cpu_data[cpu].file); + for (cpu = 0; cpu < cpus; cpu++) { + close(cpu_data[cpu].fd); + delete_temp_file(cpu_data[cpu].file); + put_temp_file(cpu_data[cpu].file); + } + free(cpu_data); + free(cpu_list); } - free(cpu_data); - free(cpu_list); tracecmd_output_close(ohandle); + *end_reached = all_end_reached; return current; } From patchwork Tue Jan 23 13:42:14 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pierre Gondois X-Patchwork-Id: 13527463 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id CA24C5F542 for ; Tue, 23 Jan 2024 13:42:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706017362; cv=none; b=KT42KcUuB5AIbwHBuGqi50Pfm4udrEfu+Y5ffsG6W+OT+cNRj/UDSjjNaPMV65oOhiyvDHehPBE+LCJMvh8bstXozLClU9jTwko9wTOvRVXeKGP9KV6n5ucOVFa3SLgJ6m2NvBwSXuH9qyOcwW6S0xIRm8i+U+IzWrg++v2AAj8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706017362; c=relaxed/simple; bh=bUSyTBXUKVh3bUtzRKDVhB59knV8Rhx9Vrze4Lkq1LE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=cNBNvb2bjg72GaD4irf/35TCgCrOb+7h553I86C06wD3/1jWeIy/byPq4QDD0leJ6NiOIevWMyrJfeVsokD4wjuwdAR+0pkF5g1vp4GqIcqTGS8U4lFZ3DRBJzcTvKMshKvl7gP+g5hU0hj/9Fp2i0NGIV4HFfkdBIFq9qqdbtA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 04A7513D5; Tue, 23 Jan 2024 05:43:25 -0800 (PST) Received: from e126645.home (unknown [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 89A743F762; Tue, 23 Jan 2024 05:42:38 -0800 (PST) From: Pierre Gondois To: Linux Trace Devel Cc: Steven Rostedt , Pierre Gondois Subject: [PATCH v3 4/5] trace-cmd split: Enable support for buffer selection Date: Tue, 23 Jan 2024 14:42:14 +0100 Message-Id: <20240123134215.385415-5-pierre.gondois@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240123134215.385415-1-pierre.gondois@arm.com> References: <20240123134215.385415-1-pierre.gondois@arm.com> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The 'trace-cmd split' command conserves all buffers/instances of the input .dat file. Add support to select the instances to keep and to place as the top instance in an output .dat file. Multiple .dat files can be generated with different combinations of the instances to keep. The first instance in the command line is selected as the top instance in an output .dat file. For example, with a trace recorded with: $ trace-cmd record -e sched_wakeup -B switch_instance \ -e sched_switch -B timer_instance -e timer 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 -B switch_instance --top=old_top -o test.dat Splitting all instances in different .dat files: $ trace-cmd split --top -o top.dat -B switch_instance \ -o switch.dat -B timer_instance -o timer.dat To achieve this, new 'struct name_list', 'struct output_file_list' structures are created, with associated functions. Signed-off-by: Pierre Gondois --- tracecmd/trace-split.c | 307 +++++++++++++++++++++++++++++++++++------ 1 file changed, 262 insertions(+), 45 deletions(-) diff --git a/tracecmd/trace-split.c b/tracecmd/trace-split.c index 0820a3bb..73ef6e97 100644 --- a/tracecmd/trace-split.c +++ b/tracecmd/trace-split.c @@ -51,11 +51,89 @@ struct cpu_data { char *file; }; +struct name_list { + struct list_head list; + const char *name; + + /* 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 void free_names(struct list_head *list) { + struct name_list *item; + + while (!list_empty(list)) { + item = container_of(list->next, struct name_list, list); + list_del(&item->list); + free((char *)item->name); + free(item); + } +} + +struct output_file_list { + struct list_head list; + const char *output; + char *output_file; + struct list_head instance_list; + struct list_head handle_list; +}; + +static struct list_head output_file_list; + +static struct output_file_list *add_output_file(void) { + struct output_file_list *item; + + item = calloc(1, sizeof(*item)); + if (!item) + die("Failed to allocate output_file item"); + + list_head_init(&item->instance_list); + list_head_init(&item->handle_list); + list_add_tail(&item->list, &output_file_list); + return item; +} + +static void add_output_file_instance(struct output_file_list *output_file, const char *instance, + bool was_top_instance, bool is_top_instance) { + struct name_list *item; + + item = calloc(1, sizeof(*item)); + if (!item) + die("Failed to allocate output_file item"); + + item->name = strdup(instance); + if (!item->name) + die("Failed to duplicate %s", instance); + + item->was_top_instance = was_top_instance; + item->is_top_instance = is_top_instance; + list_add_tail(&item->list, &output_file->instance_list); +} + +static void free_handles(struct list_head *list); + +static void free_output_files(struct list_head *list) { + struct output_file_list *item; + + while (!list_empty(list)) { + item = container_of(list->next, struct output_file_list, list); + list_del(&item->list); + free_names(&item->instance_list); + free_handles(&item->handle_list); + free((char *)item->output); + free((char *)item->output_file); + free(item); + } +} + struct handle_list { struct list_head list; const char *name; int index; - struct tracecmd_input *handle; + struct tracecmd_input *handle; /* Identify the top instance in the input trace. */ bool was_top_instance; @@ -103,10 +181,28 @@ static void add_handle(const char *name, int index, bool was_top_instance) item->index = index; item->was_top_instance = was_top_instance; - item->handle = get_handle(item); + + /* Don't need to request a real handle for handle_list nodes. */ + item->handle = NULL; list_add_tail(&item->list, &handle_list); } +static void add_handle_copy(struct handle_list *item, struct name_list *name_entry, + struct list_head *list) { + struct handle_list *copied_item; + + copied_item = calloc(1, sizeof(*copied_item)); + copied_item->name = strdup(name_entry->name); + if (!copied_item->name) + die("Failed to duplicate %s", name_entry->name); + + copied_item->index = item->index; + copied_item->was_top_instance = item->was_top_instance; + copied_item->is_top_instance = name_entry->is_top_instance; + copied_item->handle = get_handle(copied_item); + list_add_tail(&copied_item->list, list); +} + static void free_handles(struct list_head *list) { struct handle_list *item; @@ -115,7 +211,8 @@ static void free_handles(struct list_head *list) item = container_of(list->next, struct handle_list, list); list_del(&item->list); free((char *)item->name); - tracecmd_close(item->handle); + if (item->handle) + tracecmd_close(item->handle); free(item); } } @@ -473,13 +570,10 @@ static void touch_file(const char *file) } static unsigned long long parse_file(struct tracecmd_input *handle, - const char *output_file, - unsigned long long start, - unsigned long long end, int percpu, - int only_cpu, int count, - enum split_types type, - bool *end_reached) -{ + struct output_file_list *output_file_entry, + unsigned long long start, unsigned long long end, int percpu, + int only_cpu, int count, enum split_types type, + bool *end_reached) { unsigned long long current; struct handle_list *handle_entry; struct tracecmd_output *ohandle; @@ -493,10 +587,11 @@ static unsigned long long parse_file(struct tracecmd_input *handle, int ret; int fd; - ohandle = tracecmd_copy(handle, output_file, TRACECMD_FILE_CMD_LINES, 0, NULL); + ohandle = tracecmd_copy(handle, output_file_entry->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(handle_entry, &output_file_entry->handle_list, list) { struct tracecmd_input *curr_handle; bool curr_end_reached = false; @@ -507,7 +602,8 @@ static unsigned long long parse_file(struct tracecmd_input *handle, 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_entry->output_file, + handle_entry->name, cpu); touch_file(file); fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); @@ -540,7 +636,7 @@ 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 (handle_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, @@ -573,16 +669,76 @@ static unsigned long long parse_file(struct tracecmd_input *handle, return current; } +/** + * map_handle_output - Map the buffer/instances to the output_files. + * + * The trace-cmd split command can create multiple .dat files, each containing + * various combinations of the buffers available in the input .dat file. + * Associate the list of buffers/instances to keep to each output file. A + * buffer/instance can be present in multiple output files. + */ +static void map_handle_output(void) { + struct output_file_list *output_file_entry; + struct handle_list *handle_entry; + struct name_list *name_entry; + bool found_match = false; + + list_for_each_entry(output_file_entry, &output_file_list, list) { + /* + * No specific instance were given for this output file. + * Select all the instances. + */ + if (list_empty(&output_file_entry->instance_list)) { + list_for_each_entry(handle_entry, &handle_list, list) { + /* Keep the old top instance as the top instance. */ + add_output_file_instance(output_file_entry, handle_entry->name, + handle_entry->was_top_instance, + handle_entry->was_top_instance); + } + } + + list_for_each_entry(name_entry, &output_file_entry->instance_list, list) { + list_for_each_entry(handle_entry, &handle_list, list) { + if ((name_entry->was_top_instance && + handle_entry->was_top_instance) || + !strcmp(handle_entry->name, name_entry->name)) { + found_match = true; + goto found; + } + } + + found: + if (!found_match) { + warning("Buffer %s requested, but not found.", name_entry->name); + continue; + } + + /* + * As a handle can be requested in multiple output files, + * the name_entry must be copied (and not de/en-queued). + */ + add_handle_copy(handle_entry, name_entry, &output_file_entry->handle_list); + found_match = false; + } + } +} + +enum { OPT_top = 237, +}; + void trace_split (int argc, char **argv) { struct tracecmd_input *handle; + struct output_file_list *output_file_param = NULL; + struct output_file_list *output_file_entry; unsigned long long start_ns = 0, end_ns = 0; unsigned long long current; + bool was_top_instance; + bool is_top_instance; bool end_reached = false; double start, end; char *endptr; char *output = NULL; - char *output_file; enum split_types split_type = SPLIT_NONE; enum split_types type = SPLIT_NONE; int instances; @@ -593,12 +749,26 @@ 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; + + /* The first instance is the new top buffer of the output file. */ + is_top_instance = true; + was_top_instance = false; + list_head_init(&handle_list); + list_head_init(&output_file_list); + + output_file_param = add_output_file(); 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:", + long_options, &option_index)) >= 0) { switch (c) { case 'h': usage(argv); @@ -634,13 +804,48 @@ void trace_split (int argc, char **argv) cpu = atoi(optarg); break; case 'o': - if (output) + if (!output_file_param) { + /* The first name is the main instance. */ + is_top_instance = true; + output_file_param = add_output_file(); + } + + if (output_file_param->output) die("only one output file allowed"); - output = strdup(optarg); + + output_file_param->output = strdup(optarg); + if (!output_file_param->output) + die("Failed to duplicate %s", optarg); + + /* New output file. */ + output_file_param = NULL; break; case 'i': input_file = optarg; break; + case OPT_top: + was_top_instance = true; + if (!optarg) + output = (char *)default_top_instance_name; + else + output = optarg; + /* Fall through */ + case 'B': + if (!output_file_param) { + /* The first name is the main instance. */ + is_top_instance = true; + output_file_param = add_output_file(); + } + if (!output) + output = optarg; + + add_output_file_instance(output_file_param, output, + was_top_instance, is_top_instance); + + output = NULL; + was_top_instance = false; + is_top_instance = false; + break; default: usage(argv); } @@ -686,19 +891,6 @@ void trace_split (int argc, char **argv) page_size = tracecmd_page_size(handle); - if (!output) - output = strdup(input_file); - - if (!repeat && strcmp(output, input_file) == 0) { - output = realloc(output, strlen(output) + 3); - strcat(output, ".1"); - } - - output_file = malloc(strlen(output) + 50); - if (!output_file) - die("Failed to allocate for %s", output); - c = 1; - add_handle(default_top_instance_name, -1, true); instances = tracecmd_buffer_instances(handle); if (instances) { @@ -713,25 +905,50 @@ void trace_split (int argc, char **argv) } } - do { - if (repeat) - sprintf(output_file, "%s.%04d", output, c++); - else - strcpy(output_file, output); - - current = parse_file(handle, output_file, start_ns, end_ns, - percpu, cpu, count, type, &end_reached); + if (output_file_param && !output_file_param->output) { + output_file_param->output = strdup(input_file); + if (!output_file_param->output) + die("Failed to duplicate %s", input_file); + } - if (!repeat) - break; - start_ns = 0; - } while (!end_reached && (current && (!end_ns || current < end_ns))); + map_handle_output(); - free(output); - free(output_file); + list_for_each_entry(output_file_entry, &output_file_list, list) { + output = (char *)output_file_entry->output; + + if (!repeat && strcmp(output, input_file) == 0) { + output = realloc(output, strlen(output) + 3); + strcat(output, ".1"); + } + + output_file_entry->output_file = malloc(strlen(output) + 50); + if (!output_file_entry->output_file) + die("Failed to allocate for %s", output); + + output_file_entry->output = output; + } + + list_for_each_entry(output_file_entry, &output_file_list, list) { + c = 1; + do { + if (repeat) + sprintf(output_file_entry->output_file, "%s.%04d", + output_file_entry->output, c++); + else + strcpy(output_file_entry->output_file, output_file_entry->output); + + current = parse_file(handle, output_file_entry, start_ns, end_ns, percpu, + cpu, count, type, &end_reached); + + if (!repeat) + break; + start_ns = 0; + } while (!end_reached && (current && (!end_ns || current < end_ns))); + } tracecmd_close(handle); free_handles(&handle_list); + free_output_files(&output_file_list); return; } From patchwork Tue Jan 23 13:42:15 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pierre Gondois X-Patchwork-Id: 13527464 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5699B5F558 for ; Tue, 23 Jan 2024 13:42:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706017363; cv=none; b=qc/3nkV+l9qmkyUhRXMxcCtE5BvCbCDZ9AQsQBnoA9AGDrOKG03bWq4DawlF1/TEUPkwfSTWPH/LhZReCDtKEuWH9AV9a5PrYzrcvwAFZrIdmMish3MrGOQj8h9yIIZUWta4ZBfyE/bNwg2WPqaWr5dZi67RM/U8KAi2MimjVJA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706017363; c=relaxed/simple; bh=O0aJtubHwTKtvUEesfi/qkz2utlbGFMLULZ2UKkV6Mo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=busK+j24v4Y9xAhdB2fn8BnvhIAOzFbsWmW/kVuJOoDx8Em9gk0FgbSvoMI2sRVJOAgFDsmlHgyQtDRjoVDJNQ6yb3ww0siN9Gsm+EJHaZgzgXGVEuxV34Ofh/Irg2HFOpZz2QW/JispZyj+h32wXz5by3X80aWHoTDDTXIQQcg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 880061424; Tue, 23 Jan 2024 05:43:26 -0800 (PST) Received: from e126645.home (unknown [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 1D3803F762; Tue, 23 Jan 2024 05:42:39 -0800 (PST) From: Pierre Gondois To: Linux Trace Devel Cc: Steven Rostedt , Pierre Gondois Subject: [PATCH v3 5/5] trace-cmd split: Update usage Date: Tue, 23 Jan 2024 14:42:15 +0100 Message-Id: <20240123134215.385415-6-pierre.gondois@arm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240123134215.385415-1-pierre.gondois@arm.com> References: <20240123134215.385415-1-pierre.gondois@arm.com> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Update usage for 'trace-cmd split' command: - Add missing description of -i/-C options - Add description of the newly enabled -B/--top options Signed-off-by: Pierre Gondois --- tracecmd/trace-usage.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tracecmd/trace-usage.c b/tracecmd/trace-usage.c index 6944a2c7..e5b84733 100644 --- a/tracecmd/trace-usage.c +++ b/tracecmd/trace-usage.c @@ -304,13 +304,20 @@ static struct usage_help usage_help[] = { { "split", "parse a trace.dat file into smaller file(s)", - " %s split [options] -o file [start [end]]\n" + " %s split [-i file] [options] [-B buffer] [--top[=name]] -o file [start [end]]\n" " -o output file to write to (file.1, file.2, etc)\n" " -s n split file up by n seconds\n" " -m n split file up by n milliseconds\n" " -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" + " The first buffer specified will be the top buffer.\n" + " Use --top option to select the top buffer of the input\n" + " .dat file.\n" + " --top[=name] keep top buffer in resulting .dat file.\n" + " If specified, rename the top buffer to 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"