Re: CSV output

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On Mon, 13 Mar 2023, Steven Rostedt wrote:

> On Sun, 12 Mar 2023 08:07:01 +0100 (CET)
> Julia Lawall <julia.lawall@xxxxxxxx> wrote:
>
> > Would it be possible with libtraceevent to get some kind of CSV output?
> > Ie something that is very easy or some other tool to parse, without having
> > to spend a lot of time removing spaces?
> >
>
> You mean to convert a trace.dat file into a csv output?
>
> I guess I don't know what you want to convert to CSV output.

Thanks for the patch.  But actually, I was hoping for something like this:

cpus=96
<...>-20420,074,3057.962369,sched_process_exec,/tmp/tmp2abf06.sh,20420,20420
<...>-20420,074,3057.963487,sched_switch,tmp2abf06.sh,20420,120,D,swapper/74,0,120
<idle>-0,034,3057.963602,sched_waking,kworker/u192,6,500,120,075
<idle>-0,075,3057.963632,sched_wakeup,kworker/u192,6,500,120,075
<idle>-0,075,3057.963637,sched_switch,swapper/75,0,120,R,kworker/u192,6,500,120
kworker/u192,6-500,075,3057.963656,sched_waking,tmp2abf06.sh,20420,120,074
kworker/u192,6-500,075,3057.963665,sched_switch,kworker/u192,6,500,120,W,swapper/75,0,120
<idle>-0,074,3057.963686,sched_wakeup,tmp2abf06.sh,20420,120,074
<idle>-0,074,3057.963691,sched_switch,swapper/74,0,120,R,tmp2abf06.sh,20420,120

It still remains human readable (more or less, with practice), but there
are not a lot of spaces that a parser has to get rid of.  The separator
character would have to be something that can't occur in a command name.

thanks,
julia



>
> I once made this, but never pushed it out.
>
> It adds "trace-cmd sqldump" where you pass it a trace.dat file and it
> returns an mysqldump formatted file, where each event is a table, its fields
> are columns, and the instances of the events are rows. This output (in
> theory, and use to work but I haven't tested it) allows you to pull in the
> trace.dat data into a mysql database.
>
> Is this something you are looking for? (I just forward ported it to the
> latest trace-cmd).
>
> -- Steve
>
>
> commit c78bbfa863b0d5ed9053945ebae87aa20ca5a1df
> Author: Steven Rostedt (VMware) <rostedt@xxxxxxxxxxx>
> Date:   Tue Jan 14 12:51:58 2020 -0500
>
>     trace-cmd: Add sqldump
>
>     TBD
>
>     Signed-off-by: Steven Rostedt (VMware) <rostedt@xxxxxxxxxxx>
>
> diff --git a/tracecmd/Makefile b/tracecmd/Makefile
> index 66387526bc4b..1a65d94196aa 100644
> --- a/tracecmd/Makefile
> +++ b/tracecmd/Makefile
> @@ -37,6 +37,7 @@ TRACE_CMD_OBJS += trace-dump.o
>  TRACE_CMD_OBJS += trace-clear.o
>  TRACE_CMD_OBJS += trace-vm.o
>  TRACE_CMD_OBJS += trace-convert.o
> +TRACE_CMD_OBJS += trace-sqldump.o
>  TRACE_CMD_OBJS += trace-agent.o
>  TRACE_CMD_OBJS += trace-tsync.o
>  TRACE_CMD_OBJS += trace-setup-guest.o
> diff --git a/tracecmd/include/trace-local.h b/tracecmd/include/trace-local.h
> index ae208fb6b593..1172c94fc52a 100644
> --- a/tracecmd/include/trace-local.h
> +++ b/tracecmd/include/trace-local.h
> @@ -126,6 +126,12 @@ int trace_record_agent(struct tracecmd_msg_handle *msg_handle,
>  		       bool use_fifos, struct tracecmd_time_sync *tsync,
>  		       unsigned long long trace_id, int rcid, const char *host);
>
> +int trace_sql_record(struct tracecmd_input *handle, struct tep_record *record);
> +
> +int trace_sql_init(struct tracecmd_input *handle);
> +
> +int trace_sql_finish(struct tracecmd_input *handle);
> +
>  struct hook_list;
>
>  void trace_init_profile(struct tracecmd_input *handle, struct hook_list *hooks,
> diff --git a/tracecmd/trace-cmd.c b/tracecmd/trace-cmd.c
> index 9646921ca268..8de506885948 100644
> --- a/tracecmd/trace-cmd.c
> +++ b/tracecmd/trace-cmd.c
> @@ -130,6 +130,7 @@ struct command {
>   */
>  struct command commands[] = {
>  	{"report", trace_report},
> +	{"sqldump", trace_report},
>  	{"snapshot", trace_snapshot},
>  	{"hist", trace_hist},
>  	{"mem", trace_mem},
> diff --git a/tracecmd/trace-read.c b/tracecmd/trace-read.c
> index 52ba818e13b6..9902c7b28212 100644
> --- a/tracecmd/trace-read.c
> +++ b/tracecmd/trace-read.c
> @@ -73,6 +73,10 @@ struct pid_list {
>  	int			free;
>  } *pid_list;
>
> +static const char *command;
> +
> +#define IS_SQLDUMP (strcmp(command, "sqldump") == 0)
> +
>  struct pid_list *comm_list;
>
>  static unsigned int page_size;
> @@ -1152,6 +1156,27 @@ static int process_record(struct tracecmd_input *handle, struct tep_record *reco
>  	return 0;
>  }
>
> +static int process_sql_record(struct tracecmd_input *handle, struct tep_record *record,
> +			  int cpu, void *data)
> +{
> +	struct handle_list *handles = tracecmd_get_private(handle);
> +	unsigned long long *last_timestamp = data;
> +
> +	if (skip_record(handles, record, cpu))
> +		return 0;
> +
> +	if (tscheck && *last_timestamp > record->ts) {
> +		errno = 0;
> +		warning("WARNING: Record on cpu %d went backwards: %lld to %lld delta: -%lld\n",
> +			cpu, *last_timestamp, record->ts, *last_timestamp - record->ts);
> +	}
> +	*last_timestamp = record->ts;
> +
> +	trace_sql_record(handle, record);
> +
> +	return 0;
> +}
> +
>  enum output_type {
>  	OUTPUT_NORMAL,
>  	OUTPUT_STAT_ONLY,
> @@ -1166,6 +1191,7 @@ static void read_data_info(struct list_head *handle_list, enum output_type otype
>  	struct handle_list *handles;
>  	struct tracecmd_input **handle_array;
>  	unsigned long long last_timestamp = 0;
> +	bool sqldump = IS_SQLDUMP;
>  	int nr_handles = 0;
>  	int first = 1;
>  	int ret;
> @@ -1196,7 +1222,8 @@ static void read_data_info(struct list_head *handle_list, enum output_type otype
>  			first = 0;
>  		}
>  		print_handle_file(handles);
> -		printf("cpus=%d\n", cpus);
> +		if (!sqldump)
> +			printf("cpus=%d\n", cpus);
>
>  		/* Latency trace is just all ASCII */
>  		if (ret > 0) {
> @@ -1222,13 +1249,15 @@ static void read_data_info(struct list_head *handle_list, enum output_type otype
>  			continue;
>  		}
>
> -		init_wakeup(handles->handle);
> -		if (last_hook)
> -			last_hook->next = tracecmd_hooks(handles->handle);
> -		else
> -			hooks = tracecmd_hooks(handles->handle);
> -		if (profile)
> -			trace_init_profile(handles->handle, hooks, global);
> +		if (!sqldump) {
> +			init_wakeup(handles->handle);
> +			if (last_hook)
> +				last_hook->next = tracecmd_hooks(handles->handle);
> +			else
> +				hooks = tracecmd_hooks(handles->handle);
> +			if (profile)
> +				trace_init_profile(handles->handle, hooks, global);
> +		}
>
>  		/* If this file has buffer instances, get the handles for them */
>  		instances = tracecmd_buffer_instances(handles->handle);
> @@ -1263,10 +1292,6 @@ static void read_data_info(struct list_head *handle_list, enum output_type otype
>  			}
>  		}
>  	}
> -
> -	if (otype != OUTPUT_NORMAL)
> -		return;
> -
>  	if (align_ts) {
>  		list_for_each_entry(handles, handle_list, list) {
>  			tracecmd_add_ts_offset(handles->handle, -first_ts);
> @@ -1285,8 +1310,25 @@ static void read_data_info(struct list_head *handle_list, enum output_type otype
>
>  	map_vcpus(handle_array, nr_handles);
>
> -	tracecmd_iterate_events_multi(handle_array, nr_handles,
> -				      process_record, &last_timestamp);
> +	if (sqldump) {
> +		struct tracecmd_input *handle;
> +		int i;
> +
> +		for (i = 0; i < nr_handles; i++) {
> +			handle = handle_array[i];
> +
> +			trace_sql_init(handle);
> +
> +			tracecmd_iterate_events(handle, NULL, 0,process_sql_record,
> +						&last_timestamp);
> +
> +			trace_sql_finish(handle);
> +		}
> +	} else {
> +
> +		tracecmd_iterate_events_multi(handle_array, nr_handles,
> +					      process_record, &last_timestamp);
> +	}
>
>  	free(handle_array);
>
> @@ -1559,9 +1601,12 @@ void trace_report (int argc, char **argv)
>  	if (argc < 2)
>  		usage(argv);
>
> -	if (strcmp(argv[1], "report") != 0)
> +	if ((strcmp(argv[1], "report") != 0) &&
> +	    (strcmp(argv[1], "sqldump") != 0))
>  		usage(argv);
>
> +	command = argv[1];
> +
>  	signal(SIGINT, sig_end);
>
>  	trace_set_loglevel(TEP_LOG_ERROR);
> @@ -1743,9 +1788,13 @@ void trace_report (int argc, char **argv)
>  			tracecmd_set_debug(true);
>  			break;
>  		case OPT_profile:
> +			if (IS_SQLDUMP)
> +				usage(argv);
>  			profile = 1;
>  			break;
>  		case OPT_uname:
> +			if (IS_SQLDUMP)
> +				usage(argv);
>  			show_uname = 1;
>  			break;
>  		case OPT_version:
> @@ -1965,6 +2014,10 @@ void trace_report (int argc, char **argv)
>  	/* and version overrides uname! */
>  	if (show_version)
>  		otype = OUTPUT_VERSION_ONLY;
> +
> +	if (IS_SQLDUMP && otype != OUTPUT_NORMAL)
> +		usage(argv);
> +
>  	read_data_info(&handle_list, otype, global, align_ts);
>
>  	list_for_each_entry(handles, &handle_list, list) {
> diff --git a/tracecmd/trace-sqldump.c b/tracecmd/trace-sqldump.c
> new file mode 100644
> index 000000000000..b7ce99d28642
> --- /dev/null
> +++ b/tracecmd/trace-sqldump.c
> @@ -0,0 +1,268 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2020 VMware Inc, Steven Rostedt
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <sys/types.h>
> +
> +#include "trace-cmd.h"
> +#include "trace-local.h"
> +
> +extern void breakpoint(void);
> +
> +struct event_table {
> +	struct tep_event	*event;
> +	struct tep_format_field **event_fields;
> +	int			*event_field_len;
> +	size_t			cnt;
> +	FILE			*fp;
> +};
> +
> +static struct tep_format_field **event_common_fields;
> +static int nr_event_common_fields;
> +
> +static struct event_table *event_table;
> +static int event_table_size;
> +
> +int trace_sql_init(struct tracecmd_input *handle)
> +{
> +	struct tep_handle *tep;
> +	struct tep_event **event_list;
> +	struct tep_event *event;
> +	struct event_table *et;
> +	unsigned int max_id = 0;
> +	int i, f;
> +
> +	tep = tracecmd_get_tep(handle);
> +	if (!tep)
> +		return -1;
> +
> +	event_list = tep_list_events(tep, TEP_EVENT_SORT_ID);
> +	if (!event_list)
> +		return -1;
> +
> +	for (i = 0; event_list[i]; i++) {
> +		event = event_list[i];
> +
> +		if (event->id > max_id)
> +			max_id = event->id;
> +	}
> +
> +	event_table = calloc(max_id + 1, sizeof(*event_table));
> +	event_table_size = max_id + 1;
> +
> +	for (i = 0; event_list[i]; i++) {
> +		int e;
> +
> +		event = event_list[i];
> +
> +		if (event->id == 302)
> +			breakpoint();
> +
> +		et = &event_table[event->id];
> +		et->event = event;
> +
> +		if (!event_common_fields) {
> +			event_common_fields = tep_event_common_fields(event);
> +			if (!event_common_fields)
> +				goto fail;
> +			for (f = 0; event_common_fields[f]; f++)
> +				;
> +			nr_event_common_fields = f + 1;
> +		}
> +
> +		et->event_fields = tep_event_fields(event);
> +		if (!et->event_fields)
> +			goto fail;
> +
> +		for (e = 0; et->event_fields[e]; e++)
> +			;
> +		et->event_field_len = calloc(e, sizeof(*et->event_field_len));
> +		if (!et->event_field_len)
> +			goto fail;
> +
> +		et->fp = tmpfile();
> +		if (!et->fp)
> +			goto fail;
> +	}
> +	return 0;
> + fail:
> +	die("done!\n");
> +	return -1;
> +}
> +
> +static void write_string(FILE *fp, const char *str, int len)
> +{
> +	if (!len)
> +		len = strlen(str);
> +
> +	fputc('\'', fp);
> +	fwrite(str, len, 1, fp);
> +	fputc('\'', fp);
> +}
> +
> +static void write_data(FILE *fp, struct tep_format_field *field,
> +		       struct tep_record *record, int *elen)
> +{
> +	struct tep_handle *tep = field->event->tep;
> +	unsigned int offset, len;
> +	unsigned long long val;
> +	char *data = record->data;
> +	char buffer[64];
> +
> +	if (field->flags & TEP_FIELD_IS_ARRAY) {
> +		offset = field->offset;
> +		len = field->size;
> +
> +		if (field->flags & TEP_FIELD_IS_DYNAMIC) {
> +			val = tep_read_number(tep, data + offset, len);
> +			offset = val;
> +			len = offset >> 16;
> +			offset &= 0xffff;
> +		}
> +		if (field->flags & TEP_FIELD_IS_STRING) {
> +			int l = strlen(data + offset);
> +
> +			if (l < len)
> +				len = l;
> +			write_string(fp, data + offset, len);
> +		} else {
> +			write_string(fp, "ARRAY", 0);
> +			len = 5;
> +		}
> +		if (elen && (*elen) < len)
> +			*elen = len;
> +		return;
> +	}
> +	val = tep_read_number(tep, data + field->offset,
> +			      field->size);
> +	sprintf(buffer, "%lld", val);
> +	fwrite(buffer, strlen(buffer), 1, fp);
> +
> +}
> +
> +int trace_sql_record(struct tracecmd_input *handle, struct tep_record *record)
> +{
> +	struct tep_handle *tep = tracecmd_get_tep(handle);
> +	struct tep_event *event = tep_find_event_by_record(tep, record);
> +	struct event_table *et = &event_table[event->id];
> +	char buffer[64];
> +	int i;
> +
> +	if (event->id > event_table_size || !et->event)
> +		return -1;
> +
> +	if (event->id == 302)
> +		breakpoint();
> +
> +	if (et->cnt++)
> +		fwrite(",(", 2, 1, et->fp);
> +	else
> +		fwrite("(", 1, 1, et->fp);
> +
> +	i = sprintf(buffer, "%lld", record->ts);
> +	fwrite(buffer, i, 1, et->fp);
> +
> +	for (i = 0; event_common_fields[i]; i++) {
> +		fwrite(",", 1, 1, et->fp);
> +		write_data(et->fp, event_common_fields[i], record, NULL);
> +	}
> +
> +	for (i = 0; et->event_fields[i]; i++) {
> +		fwrite(",", 1, 1, et->fp);
> +		write_data(et->fp, et->event_fields[i], record,
> +			   &et->event_field_len[i]);
> +	}
> +
> +	fwrite(")", 1, 1, et->fp);
> +
> +	return 0;
> +}
> +
> +static void write_header(struct tep_format_field *field, int flen)
> +{
> +
> +	printf("`%s` ", field->name);
> +
> +	if (field->flags & TEP_FIELD_IS_ARRAY) {
> +		if (field->flags & TEP_FIELD_IS_STRING) {
> +			if (field->flags & TEP_FIELD_IS_DYNAMIC) {
> +				if (flen)
> +					printf("VARCHAR(%d)", flen);
> +				else
> +					printf("VARCHAR(64)");
> +			} else {
> +				printf("CHAR(%d)", field->size);
> +			}
> +		} else {
> +			printf("CHAR(5)");
> +		}
> +	} else {
> +		printf("BIGINT");
> +	}
> +
> +	printf(" NOT NULL");
> +}
> +
> +static void print_sql_header(struct event_table *et)
> +{
> +	const char *name = et->event->name;
> +	int i;
> +
> +	printf("\n---\n--- Table structure for table `%s`\n---\n\n", name);
> +	printf("DROP TABLE IF EXISTS `%s`;\n", name);
> +
> +	printf("CREATE TABLE `%s` (\n", name);
> +
> +	printf("  `common_timestamp` BIGINT NOT NULL");
> +
> +	for (i = 0; event_common_fields[i]; i++) {
> +		printf(",\n  ");
> +		write_header(event_common_fields[i], 0);
> +	}
> +
> +	for (i = 0; et->event_fields[i]; i++) {
> +		printf(",\n  ");
> +		write_header(et->event_fields[i], et->event_field_len[i]);
> +	}
> +
> +	printf("\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n");
> +	printf("\n---\n--- Dumping data for table `%s`\n---\n\n", name);
> +	printf("LOCK TABLES `%s` WRITE;\n", name);
> +	printf("INSERT INTO `%s` VALUES ", name);
> +
> +	fflush(stdout);
> +	fflush(et->fp);
> +}
> +
> +int trace_sql_finish(struct tracecmd_input *handle)
> +{
> +	struct event_table *et;
> +	char buffer[BUFSIZ];
> +	int i, r;
> +	int fd;
> +
> +	for (i = 0; i < event_table_size; i++) {
> +		et = &event_table[i];
> +		if (!et->cnt)
> +			continue;
> +
> +		print_sql_header(et);
> +
> +		fd = fileno(et->fp);
> +		lseek(fd, 0, SEEK_SET);
> +		do {
> +			r = read(fd, buffer, BUFSIZ);
> +			if (r > 0)
> +				write(1, buffer, r);
> +		} while (r > 0);
> +		fclose(et->fp);
> +		printf(";\n");
> +		printf("UNLOCK TABLES;\n");
> +	}
> +
> +	return 0;
> +}
>



[Index of Archives]     [Linux USB Development]     [Linux USB Development]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux