linux-trace-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Steven Rostedt <rostedt@goodmis.org>
To: linux-trace-devel@vger.kernel.org
Cc: "Steven Rostedt (VMware)" <rostedt@goodmis.org>,
	Tom Zanussi <zanussi@kernel.org>,
	Masami Hiramatsu <mhiramat@kernel.org>,
	Namhyung Kim <namhyung@kernel.org>,
	Daniel Bristot de Oliveira <bristot@redhat.com>
Subject: [PATCH 4/4] libtracefs: Implement API to create / modify and display histograms
Date: Tue,  6 Jul 2021 23:11:10 -0400	[thread overview]
Message-ID: <20210707031110.249759-5-rostedt@goodmis.org> (raw)
In-Reply-To: <20210707031110.249759-1-rostedt@goodmis.org>

From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>

Add an API to facilitate the modification of histograms in ftrace:

  tracefs_hist_alloc()
  tracefs_hist_add_key()
  tracefs_hist_add_value()
  tracefs_hist_add_name()
  tracefs_hist_start()
  tracefs_hist_pause()
  tracefs_hist_continue()
  tracefs_hist_reset()
  tracefs_hist_delete()
  tracefs_hist_add_sort_key()
  tracefs_hist_sort_key_direction()

The above functions can create a tracefs_hist descriptor that can
facilitate the creation and modification of ftrace event histograms.

Cc: Tom Zanussi <zanussi@kernel.org>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Daniel Bristot de Oliveira <bristot@redhat.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
---
 Documentation/libtracefs-hist-cont.txt | 192 +++++++++
 Documentation/libtracefs-hist.txt      | 290 ++++++++++++++
 include/tracefs.h                      |  46 +++
 src/Makefile                           |   1 +
 src/tracefs-hist.c                     | 529 +++++++++++++++++++++++++
 5 files changed, 1058 insertions(+)
 create mode 100644 Documentation/libtracefs-hist-cont.txt
 create mode 100644 Documentation/libtracefs-hist.txt
 create mode 100644 src/tracefs-hist.c

diff --git a/Documentation/libtracefs-hist-cont.txt b/Documentation/libtracefs-hist-cont.txt
new file mode 100644
index 000000000000..1b0153c19481
--- /dev/null
+++ b/Documentation/libtracefs-hist-cont.txt
@@ -0,0 +1,192 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_hist_pause, tracefs_hist_continue, tracefs_hist_reset - Pause, continue, or clear an existing histogram
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int tracefs_hist_pause(struct tracefs_hist pass:[*]hist);
+int tracefs_hist_continue(struct tracefs_hist pass:[*]hist);
+int tracefs_hist_reset(struct tracefs_hist pass:[*]hist);
+--
+
+DESCRIPTION
+-----------
+*tracefs_hist_pause* is called to pause the histogram _hist_.
+
+*tracefs_hist_continue* is called to continue a paused histogram _hist_.
+
+*tracefs_hist_reset* is called to clear / reset the histogram _hist_.
+
+RETURN VALUE
+------------
+All the return zero on success or -1 on error.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <tracefs/tracefs.h>
+
+enum commands {
+	START,
+	PAUSE,
+	CONT,
+	RESET,
+	DELETE,
+	SHOW,
+};
+
+int main (int argc, char **argv, char **env)
+{
+	struct tracefs_instance *instance;
+	struct tracefs_hist *hist;
+	enum commands cmd;
+	char *cmd_str;
+	int ret;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s command\n", argv[0]);
+		exit(-1);
+	}
+
+	cmd_str = argv[1];
+
+	if (!strcmp(cmd_str, "start"))
+		cmd = START;
+	else if (!strcmp(cmd_str, "pause"))
+		cmd = PAUSE;
+	else if (!strcmp(cmd_str, "cont"))
+		cmd = CONT;
+	else if (!strcmp(cmd_str, "reset"))
+		cmd = RESET;
+	else if (!strcmp(cmd_str, "delete"))
+		cmd = DELETE;
+	else if (!strcmp(cmd_str, "show"))
+		cmd = SHOW;
+	else {
+		fprintf(stderr, "Unknown command %s\n", cmd_str);
+		exit(-1);
+	}
+
+	instance = tracefs_instance_create("hist_test");
+	if (!instance) {
+		fprintf(stderr, "Failed instance create\n");
+		exit(-1);
+	}
+
+	hist = tracefs_hist_alloc(instance, "kmem", "kmalloc",
+				  "call_site", TRACEFS_HIST_KEY_SYM);
+	if (!hist) {
+		fprintf(stderr, "Failed hist create\n");
+		exit(-1);
+	}
+
+	ret = tracefs_hist_add_key(hist, "bytes_req", 0);
+	ret |= tracefs_hist_add_value(hist, "bytes_alloc");
+	ret |= tracefs_hist_add_sort_key(hist, "bytes_req", "bytes_alloc", NULL);
+
+	ret |= tracefs_hist_sort_key_direction(hist, "bytes_alloc",
+					       TRACEFS_HIST_SORT_DESCENDING);
+	if (ret) {
+		fprintf(stderr, "Failed modifying histogram\n");
+		exit(-1);
+	}
+
+	tracefs_error_clear(instance);
+
+	switch (cmd) {
+	case START:
+		ret = tracefs_hist_start(hist);
+		if (ret) {
+			char *err = tracefs_error_last(instance);
+			if (err)
+				fprintf(stderr, "\n%s\n", err);
+		}
+		break;
+	case PAUSE:
+		ret = tracefs_hist_pause(hist);
+		break;
+	case CONT:
+		ret = tracefs_hist_continue(hist);
+		break;
+	case RESET:
+		ret = tracefs_hist_reset(hist);
+		break;
+	case DELETE:
+		ret = tracefs_hist_destroy(hist);
+		break;
+	case SHOW: {
+		char *content;
+		content = tracefs_event_file_read(instance, "kmem", "kmalloc",
+						  "hist", NULL);
+		ret = content ? 0 : -1;
+		if (content) {
+			printf("%s\n", content);
+			free(content);
+		}
+		break;
+	}
+	}
+	if (ret)
+		fprintf(stderr, "Failed: command\n");
+	exit(ret);
+}
+--
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+_libtracefs(3)_,
+_libtraceevent(3)_,
+_trace-cmd(1)_,
+_tracefs_hist_alloc(3)_,
+_tracefs_hist_free(3)_,
+_tracefs_hist_add_key(3)_,
+_tracefs_hist_add_value(3)_,
+_tracefs_hist_add_name(3)_,
+_tracefs_hist_start(3)_,
+_tracefs_hist_destory(3)_,
+_tracefs_hist_add_sort_key(3)_,
+_tracefs_hist_sort_key_direction(3)_
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+*sameeruddin shaik* <sameeruddin.shaik8@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-hist.txt b/Documentation/libtracefs-hist.txt
new file mode 100644
index 000000000000..67b2a068ac45
--- /dev/null
+++ b/Documentation/libtracefs-hist.txt
@@ -0,0 +1,290 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_hist_alloc, tracefs_hist_free, tracefs_hist_add_key, tracefs_hist_add_value, tracefs_hist_add_name, tracefs_hist_start,
+tracefs_hist_destory, tracefs_hist_add_sort_key, tracefs_hist_sort_key_direction - Create and update event histograms
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+struct tracefs_hist pass:[*]tracefs_hist_alloc(struct tracefs_instance pass:[*] instance,
+			const char pass:[*]system, const char pass:[*]event,
+			const char pass:[*]key, enum tracefs_hist_key_type type);
+void tracefs_hist_free(struct tracefs_hist pass:[*]hist);
+int tracefs_hist_add_key(struct tracefs_hist pass:[*]hist, const char pass:[*]key,
+			 enum tracefs_hist_key_type type);
+int tracefs_hist_add_value(struct tracefs_hist pass:[*]hist, const char pass:[*]value);
+int tracefs_hist_add_sort_key(struct tracefs_hist pass:[*]hist,
+			      const char pass:[*]sort_key, ...);
+int tracefs_hist_sort_key_direction(struct tracefs_hist pass:[*]hist,
+				    const char pass:[*]sort_key,
+				    enum tracefs_hist_sort_direction dir);
+int tracefs_hist_add_name(struct tracefs_hist pass:[*]hist, const char pass:[*]name);
+int tracefs_hist_start(struct tracefs_hist pass:[*]hist);
+int tracefs_hist_destory(struct tracefs_hist pass:[*]hist);
+--
+
+DESCRIPTION
+-----------
+Event histograms are created by the trigger file in the event directory.
+The syntax can be complex and difficult to get correct. This API handles the
+syntax, and facilitates the creation and interaction with the event histograms.
+See https://www.kernel.org/doc/html/latest/trace/histogram.html for more information.
+
+*tracefs_hist_alloc*() allocates a "struct tracefs_hist" descriptor and returns
+the address of it. This descriptor must be freed by *tracefs_hist_free*().
+The _instance_ is the instance that contains the event that the histogram will be
+attached to. The _system_ is the system or group of the event. The _event_ is the event
+to attach the histogram to. The _key_ is a field of the event that will be used as
+the key of the histogram. The _type_ is the type of the _key_. See KEY TYPES below.
+
+*tracefs_hist_free*() frees the _tracefs_hist_ descriptor. Note, it does not stop
+or disable the running histogram if it was started. *tracefs_hist_destroy*() needs
+to be called to do so.
+
+*tracefs_hist_add_key*() Adds a secondary or tertiary key to the histogram.
+The key passed to *tracefs_hist_alloc*() is the primary key of the histogram.
+The first time this function is called, it will add a secondary key (or two dimensional
+histogram). If this function is called again on the same histogram, it will add
+a _tertiary_ key (or three dimensional histogram). The _hist_ parameter is the
+histrogram descriptor to add the _key_ to. The _type_ is the type of key to add
+(See KEY TYPES below).
+
+*tracefs_hist_add_value*() will add a value to record. By default, the value is
+simply the "hitcount" of the number of times a instance of the histogram's
+key was hit. The _hist_ is the histogram descriptor to add the value to.
+The _value_ is a field in the histogram to add to when an instane of the
+key is hit.
+
+*tracefs_hist_add_sort_key*() will add a key to sort on. Th _hist_ is the
+the histrogram descriptor to add the sort key to. The _sort_key_ is a string
+that must match either an already defined key of the histogram, or an already
+defined value. Multiple sort keys may be added to denote a secondary, sort order
+and so on, but all sort keys must match an existing key or value, or be
+TRACEFS_HIST_HITCOUNT. The last parameter of *tracefs_hist_add_sort_key*() must
+be NULL.
+
+*tracefs_hist_sort_key_direction*() allows to change the direction of an
+existing sort key of _hist_. The _sort_key_ is the sort key to change, and
+_dir_ can be either TRACEFS_HIST_SORT_ASCENDING or TRACEFS_HIST_SORT_DESCENDING,
+to make the direction of the sort key either ascending or descending respectively.
+
+*tracefs_hist_add_name*() adds a name to a histogram. A histogram may be
+named and if the name matches between more than one event, and they have
+compatible keys, the multiple histograms with the same name will be merged
+into a single histogram (shown by either event's hist file). The _hist_
+is the histogram to name, and the _name_ is the name to give it.
+
+*tracefs_hist_start* is called to actually start the histogram _hist_.
+
+*tracefs_hist_pause* is called to pause the histogram _hist_.
+
+*tracefs_hist_continue* is called to continue a paused histogram _hist_.
+
+*tracefs_hist_reset* is called to clear / reset the histogram _hist_.
+
+*tracefs_hist_destory* is called to delete the histogram where it will no longer
+exist.
+
+
+KEY TYPES
+---------
+
+*tracefs_hist_alloc*() and *tracefs_hist_add_key*() both add a key and requires
+that key to have a type. The types may be:
+
+ *TRACEFS_HIST_KEY_NORMAL* or zero (0) which is to not modify the type.
+
+ *TRACEFS_HIST_KEY_HEX* to display the key in hex.
+
+ *TRACEFS_HIST_KEY_SYM* to display the key as a kernel symbol (if found). If
+the key is an address, this is useful as it will display the function names instead
+of just a number.
+
+ *TRACEFS_HIST_KEY_SYM_OFFSET* similar to *TRACEFS_HIST_KEY_SYM* but will also include
+the offset of the function to match the exact address.
+
+*TRACEFS_HIST_KEY_SYSCALL* If the key is a system call "id" (the number passed from user
+space to the kernel to tell it what system call it is calling), then the name of
+the system call is displayed.
+
+*TRACEFS_HIST_KEY_EXECNAME* If "common_pid" is the key (the pid of the executing task), 
+instead of showing the number, show the name of the running task.
+
+*TRACEFS_HIST_KEY_LOG* will display the key in a binary logarithmic scale.
+
+*TRACEFS_HIST_KEY_USECS* for use with "common_timestamp" or TRACEFS_HIST_TIMESTAMP,
+in which case it will show the timestamp in microseconds instead of nanoseconds.
+
+
+RETURN VALUE
+------------
+*tracefs_hist_alloc*() returns an allocated histogram descriptor which must
+be freed by *tracefs_hist_free*() or NULL on error.
+
+All the other functions return zero on success or -1 on error.
+
+If *tracefs_hist_start*() returns an error, a message may be displayed
+in the kernel that can be retrieved by *tracefs_error_last()*.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdlib.h>
+#include <tracefs/tracefs.h>
+
+enum commands {
+	START,
+	PAUSE,
+	CONT,
+	RESET,
+	DELETE,
+	SHOW,
+};
+
+int main (int argc, char **argv, char **env)
+{
+	struct tracefs_instance *instance;
+	struct tracefs_hist *hist;
+	enum commands cmd;
+	char *cmd_str;
+	int ret;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s command\n", argv[0]);
+		exit(-1);
+	}
+
+	cmd_str = argv[1];
+
+	if (!strcmp(cmd_str, "start"))
+		cmd = START;
+	else if (!strcmp(cmd_str, "pause"))
+		cmd = PAUSE;
+	else if (!strcmp(cmd_str, "cont"))
+		cmd = CONT;
+	else if (!strcmp(cmd_str, "reset"))
+		cmd = RESET;
+	else if (!strcmp(cmd_str, "delete"))
+		cmd = DELETE;
+	else if (!strcmp(cmd_str, "show"))
+		cmd = SHOW;
+	else {
+		fprintf(stderr, "Unknown command %s\n", cmd_str);
+		exit(-1);
+	}
+
+	instance = tracefs_instance_create("hist_test");
+	if (!instance) {
+		fprintf(stderr, "Failed instance create\n");
+		exit(-1);
+	}
+
+	hist = tracefs_hist_alloc(instance, "kmem", "kmalloc",
+				  "call_site", TRACEFS_HIST_KEY_SYM);
+	if (!hist) {
+		fprintf(stderr, "Failed hist create\n");
+		exit(-1);
+	}
+
+	ret = tracefs_hist_add_key(hist, "bytes_req", 0);
+	ret |= tracefs_hist_add_value(hist, "bytes_alloc");
+	ret |= tracefs_hist_add_sort_key(hist, "bytes_req", "bytes_alloc", NULL);
+
+	ret |= tracefs_hist_sort_key_direction(hist, "bytes_alloc",
+					       TRACEFS_HIST_SORT_DESCENDING);
+	if (ret) {
+		fprintf(stderr, "Failed modifying histogram\n");
+		exit(-1);
+	}
+
+	tracefs_error_clear(instance);
+
+	switch (cmd) {
+	case START:
+		ret = tracefs_hist_start(hist);
+		if (ret) {
+			char *err = tracefs_error_last(instance);
+			if (err)
+				fprintf(stderr, "\n%s\n", err);
+		}
+		break;
+	case PAUSE:
+		ret = tracefs_hist_pause(hist);
+		break;
+	case CONT:
+		ret = tracefs_hist_continue(hist);
+		break;
+	case RESET:
+		ret = tracefs_hist_reset(hist);
+		break;
+	case DELETE:
+		ret = tracefs_hist_destroy(hist);
+		break;
+	case SHOW: {
+		char *content;
+		content = tracefs_event_file_read(instance, "kmem", "kmalloc",
+						  "hist", NULL);
+		ret = content ? 0 : -1;
+		if (content) {
+			printf("%s\n", content);
+			free(content);
+		}
+		break;
+	}
+	}
+	if (ret)
+		fprintf(stderr, "Failed: command\n");
+	exit(ret);
+}
+--
+
+FILES
+-----
+[verse]
+--
+*tracefs.h*
+	Header file to include in order to have access to the library APIs.
+*-ltracefs*
+	Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+_libtracefs(3)_,
+_libtraceevent(3)_,
+_trace-cmd(1)_,
+_tracefs_hist_pause(3)_,
+_tracefs_hist_continue(3)_,
+_tracefs_hist_reset(3)_
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@gmail.com>
+*sameeruddin shaik* <sameeruddin.shaik8@gmail.com>
+--
+REPORTING BUGS
+--------------
+Report bugs to  <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtracefs is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/
+
+COPYING
+-------
+Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/include/tracefs.h b/include/tracefs.h
index afbfd4eb01d6..7e1927b078d3 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -253,4 +253,50 @@ enum tracefs_kprobe_type tracefs_kprobe_info(const char *group, const char *even
 					     char **type, char **addr, char **format);
 int tracefs_kprobe_clear_all(bool force);
 int tracefs_kprobe_clear_probe(const char *system, const char *event, bool force);
+
+enum tracefs_hist_key_type {
+	TRACEFS_HIST_KEY_NORMAL = 0,
+	TRACEFS_HIST_KEY_HEX,
+	TRACEFS_HIST_KEY_SYM,
+	TRACEFS_HIST_KEY_SYM_OFFSET,
+	TRACEFS_HIST_KEY_SYSCALL,
+	TRACEFS_HIST_KEY_EXECNAME,
+	TRACEFS_HIST_KEY_LOG,
+	TRACEFS_HIST_KEY_USECS,
+};
+
+enum tracefs_hist_sort_direction {
+	TRACEFS_HIST_SORT_ASCENDING,
+	TRACEFS_HIST_SORT_DESCENDING,
+};
+
+#define TRACEFS_HIST_TIMESTAMP		"common_timestamp"
+#define TRACEFS_HIST_TIMESTAMP_USECS	"common_timestamp.usecs"
+#define TRACEFS_HIST_CPU		"cpu"
+
+#define TRACEFS_HIST_HITCOUNT		"hitcount"
+
+struct tracefs_hist;
+
+void tracefs_hist_free
+(struct tracefs_hist *hist);
+struct tracefs_hist *
+tracefs_hist_alloc(struct tracefs_instance * instance,
+		       const char *system, const char *event,
+		       const char *key, enum tracefs_hist_key_type type);
+int tracefs_hist_add_key(struct tracefs_hist *hist, const char *key,
+			 enum tracefs_hist_key_type type);
+int tracefs_hist_add_value(struct tracefs_hist *hist, const char *value);
+int tracefs_hist_add_sort_key(struct tracefs_hist *hist,
+			      const char *sort_key, ...);
+int tracefs_hist_sort_key_direction(struct tracefs_hist *hist,
+				    const char *sort_key,
+				    enum tracefs_hist_sort_direction dir);
+int tracefs_hist_add_name(struct tracefs_hist *hist, const char *name);
+int tracefs_hist_start(struct tracefs_hist *hist);
+int tracefs_hist_pause(struct tracefs_hist *hist);
+int tracefs_hist_continue(struct tracefs_hist *hist);
+int tracefs_hist_reset(struct tracefs_hist *hist);
+int tracefs_hist_destroy(struct tracefs_hist *hist);
+
 #endif /* _TRACE_FS_H */
diff --git a/src/Makefile b/src/Makefile
index 0697a047f4bc..c7f7c1cc1680 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -9,6 +9,7 @@ OBJS += tracefs-events.o
 OBJS += tracefs-tools.o
 OBJS += tracefs-marker.o
 OBJS += tracefs-kprobes.o
+OBJS += tracefs-hist.o
 
 OBJS := $(OBJS:%.o=$(bdir)/%.o)
 DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)
diff --git a/src/tracefs-hist.c b/src/tracefs-hist.c
new file mode 100644
index 000000000000..9031a77eba4b
--- /dev/null
+++ b/src/tracefs-hist.c
@@ -0,0 +1,529 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2021 VMware Inc, Steven Rostedt <rostedt@goodmis.org>
+ *
+ * Updates:
+ * Copyright (C) 2021, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+#define HIST_FILE "hist"
+
+#define ASCENDING ".ascending"
+#define DESCENDING ".descending"
+
+struct tracefs_hist {
+	struct tracefs_instance *instance;
+	char			*system;
+	char			*event;
+	char			*name;
+	char			**keys;
+	char			**values;
+	char			**sort;
+	char			*filter;
+	int			size;
+};
+
+enum tracefs_hist_command {
+	HIST_CMD_NONE = 0,
+	HIST_CMD_PAUSE,
+	HIST_CMD_CONT,
+	HIST_CMD_CLEAR,
+	HIST_CMD_DESTROY,
+};
+
+static void add_list(struct trace_seq *seq, const char *start,
+		     char **list)
+{
+	int i;
+
+	trace_seq_puts(seq, start);
+	for (i = 0; list[i]; i++) {
+		if (i)
+			trace_seq_putc(seq, ',');
+		trace_seq_puts(seq, list[i]);
+	}
+}
+
+/*
+ * trace_hist_start - Create and start a histogram for an event
+ * @hist: The histogram to write into the trigger file
+ * @command: If not zero, can pause, continue or clear the histogram
+ *
+ * This creates a histogram for an event with the given fields.
+ *
+ * Returns 0 on succes -1 on error.
+ */
+static int
+trace_hist_start(struct tracefs_hist *hist,
+		 enum tracefs_hist_command command)
+{
+	struct tracefs_instance *instance = hist->instance;
+	const char *system = hist->system;
+	const char *event = hist->event;
+	struct trace_seq seq;
+	int ret;
+
+	errno = -EINVAL;
+	if (!hist->keys)
+		return -1;
+
+	trace_seq_init(&seq);
+
+	if (command == HIST_CMD_DESTROY)
+		trace_seq_putc(&seq, '!');
+
+	add_list(&seq, "hist:keys=", hist->keys);
+
+	if (hist->values)
+		add_list(&seq, ":vals=", hist->values);
+
+	if (hist->sort)
+		add_list(&seq, ":sort=", hist->sort);
+
+	if (hist->size)
+		trace_seq_printf(&seq, ":size=%d", hist->size);
+
+	switch(command) {
+	case HIST_CMD_NONE: break;
+	case HIST_CMD_PAUSE: trace_seq_puts(&seq, ":pause"); break;
+	case HIST_CMD_CONT: trace_seq_puts(&seq, ":cont"); break;
+	case HIST_CMD_CLEAR: trace_seq_puts(&seq, ":clear"); break;
+	default: break;
+	}
+
+	if (hist->name)
+		trace_seq_printf(&seq, ":name=%s", hist->name);
+
+	if (hist->filter)
+		trace_seq_printf(&seq, " if %s\n", hist->filter);
+
+	trace_seq_terminate(&seq);
+
+	ret = -1;
+	if (seq.state == TRACE_SEQ__GOOD)
+		ret = tracefs_event_file_append(instance, system, event,
+						"trigger", seq.buffer);
+
+	trace_seq_destroy(&seq);
+
+	return ret < 0 ? -1 : 0;
+}
+
+/**
+ * tracefs_hist_free - free a tracefs_hist element
+ * @hist: The histogram to free
+ */
+void tracefs_hist_free(struct tracefs_hist *hist)
+{
+	if (!hist)
+		return;
+
+	trace_put_instance(hist->instance);
+	free(hist->system);
+	free(hist->event);
+	free(hist->name);
+	free(hist->filter);
+	tracefs_list_free(hist->keys);
+	tracefs_list_free(hist->values);
+	tracefs_list_free(hist->sort);
+	free(hist);
+}
+
+/**
+ * tracefs_hist_alloc - Initialize a histogram
+ * @instance: The instance the histogram will be in (NULL for toplevel)
+ * @system: The system the histogram event is in.
+ * @event: The event that the histogram will be attached to.
+ * @key: The primary key the histogram will use
+ * @type: The format type of the key.
+ *
+ * Will initialize a histogram descriptor that will be attached to
+ * the @system/@event with the given @key as the primary. This only
+ * initializes the descriptor, it does not start the histogram
+ * in the kernel.
+ *
+ * Returns an initialized histogram on success.
+ * NULL on failure.
+ */
+struct tracefs_hist *
+tracefs_hist_alloc(struct tracefs_instance * instance,
+			const char *system, const char *event,
+			const char *key, enum tracefs_hist_key_type type)
+{
+	struct tracefs_hist *hist;
+	int ret;
+
+	if (!system || !event || !key)
+		return NULL;
+
+	if (!tracefs_event_file_exists(instance, system, event, HIST_FILE))
+		return NULL;
+
+	hist = calloc(1, sizeof(*hist));
+	if (!hist)
+		return NULL;
+
+	ret = trace_get_instance(instance);
+	if (ret < 0) {
+		free(hist);
+		return NULL;
+	}
+
+	hist->instance = instance;
+
+	hist->system = strdup(system);
+	hist->event = strdup(event);
+
+	ret = tracefs_hist_add_key(hist, key, type);
+
+	if (!hist->system || !hist->event || ret < 0) {
+		tracefs_hist_free(hist);
+		return NULL;
+	}
+
+
+	return hist;
+}
+
+/**
+ * tracefs_hist_add_key - add to a key to a histogram
+ * @hist: The histogram to add the key to.
+ * @key: The name of the key field.
+ * @type: The type of the key format.
+ *
+ * This adds a secondary or tertiary key to the histogram.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int tracefs_hist_add_key(struct tracefs_hist *hist, const char *key,
+			 enum tracefs_hist_key_type type)
+{
+	bool use_key = false;
+	char *key_type = NULL;
+	char **new_list;
+	int ret;
+
+	switch (type) {
+	case TRACEFS_HIST_KEY_NORMAL:
+		use_key = true;
+		ret = 0;
+		break;
+	case TRACEFS_HIST_KEY_HEX:
+		ret = asprintf(&key_type, "%s.hex", key);
+		break;
+	case TRACEFS_HIST_KEY_SYM:
+		ret = asprintf(&key_type, "%s.sym", key);
+		break;
+	case TRACEFS_HIST_KEY_SYM_OFFSET:
+		ret = asprintf(&key_type, "%s.sym-offset", key);
+		break;
+	case TRACEFS_HIST_KEY_SYSCALL:
+		ret = asprintf(&key_type, "%s.syscall", key);
+		break;
+	case TRACEFS_HIST_KEY_EXECNAME:
+		ret = asprintf(&key_type, "%s.execname", key);
+		break;
+	case TRACEFS_HIST_KEY_LOG:
+		ret = asprintf(&key_type, "%s.log2", key);
+		break;
+	case TRACEFS_HIST_KEY_USECS:
+		ret = asprintf(&key_type, "%s.usecs", key);
+		break;
+	}
+
+	if (ret < 0)
+		return -1;
+
+	new_list = tracefs_list_add(hist->keys, use_key ? key : key_type);
+	free(key_type);
+	if (!new_list)
+		return -1;
+
+	hist->keys = new_list;
+
+	return 0;
+}
+
+/**
+ * tracefs_hist_add_value - add to a value to a histogram
+ * @hist: The histogram to add the value to.
+ * @key: The name of the value field.
+ *
+ * This adds a value field to the histogram.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int tracefs_hist_add_value(struct tracefs_hist *hist, const char *value)
+{
+	char **new_list;
+
+	new_list = tracefs_list_add(hist->values, value);
+	if (!new_list)
+		return -1;
+
+	hist->values = new_list;
+
+	return 0;
+}
+
+/**
+ * tracefs_hist_add_name - name a histogram
+ * @hist: The histogram to name.
+ * @name: The name of the histogram.
+ *
+ * Adds a name to the histogram. Named histograms will share their
+ * data with other events that have the same name as if it was
+ * a single histogram.
+ *
+ * If the histogram already has a name, this will fail.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int tracefs_hist_add_name(struct tracefs_hist *hist, const char *name)
+{
+	if (hist->name)
+		return -1;
+
+	hist->name = strdup(name);
+
+	return hist->name ? 0 : -1;
+}
+
+/**
+ * tracefs_hist_start - enable a histogram
+ * @hist: The histogram to start
+ *
+ * Starts executing a histogram.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int tracefs_hist_start(struct tracefs_hist *hist)
+{
+	return trace_hist_start(hist, 0);
+}
+
+/**
+ * tracefs_hist_pause - pause a histogram
+ * @hist: The histogram to pause
+ *
+ * Pause a histogram.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int tracefs_hist_pause(struct tracefs_hist *hist)
+{
+	return trace_hist_start(hist, HIST_CMD_PAUSE);
+}
+
+/**
+ * tracefs_hist_continue - continue a paused histogram
+ * @hist: The histogram to continue
+ *
+ * Continue a histogram.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int tracefs_hist_continue(struct tracefs_hist *hist)
+{
+	return trace_hist_start(hist, HIST_CMD_CONT);
+}
+
+/**
+ * tracefs_hist_reset - clear a histogram
+ * @hist: The histogram to reset
+ *
+ * Resets a histogram.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int tracefs_hist_reset(struct tracefs_hist *hist)
+{
+	return trace_hist_start(hist, HIST_CMD_CLEAR);
+}
+
+/**
+ * tracefs_hist_destroy - deletes a histogram (needs to be enabled again)
+ * @hist: The histogram to delete
+ *
+ * Deletes (removes) a running histogram. This is different than
+ * clear, as clear only clears the data but the histogram still exists.
+ * This deletes the histogram and should be called before
+ * tracefs_hist_free() to clean up properly.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int tracefs_hist_destroy(struct tracefs_hist *hist)
+{
+	return trace_hist_start(hist, HIST_CMD_DESTROY);
+}
+
+static char **
+add_sort_key(struct tracefs_hist *hist, const char *sort_key, char **list)
+{
+	char **key_list = hist->keys;
+	char **val_list = hist->values;
+	int i;
+
+	if (strcmp(sort_key, TRACEFS_HIST_HITCOUNT) == 0)
+		goto out;
+
+	for (i = 0; key_list[i]; i++) {
+		if (strcmp(key_list[i], sort_key) == 0)
+			break;
+	}
+
+	if (!key_list[i]) {
+		for (i = 0; val_list[i]; i++) {
+		if (strcmp(val_list[i], sort_key) == 0)
+			break;
+		if (!val_list[i])
+			return NULL;
+		}
+	}
+
+
+ out:
+	return tracefs_list_add(list, sort_key);
+}
+
+/**
+ * tracefs_hist_add_sort_key - add a key for sorting the histogram
+ * @hist: The histogram to add the sort key to
+ * @sort_key: The key to sort (and the strings after it)
+ *  Last one must be NULL.
+ *
+ * Add a list of sort keys in the order of priority that the
+ * keys would be sorted on output. Keys must be added first.
+ *
+ * Returns 0 on success, -1 on error.
+ */
+int tracefs_hist_add_sort_key(struct tracefs_hist *hist,
+			      const char *sort_key, ...)
+{
+	char **list = NULL;
+	char **tmp;
+	va_list ap;
+
+	if (!hist || !sort_key)
+		return -1;
+
+	tmp = add_sort_key(hist, sort_key, list);
+	if (!tmp)
+		goto fail;
+	list = tmp;
+
+	va_start(ap, sort_key);
+	for (;;) {
+		sort_key = va_arg(ap, const char *);
+		if (!sort_key)
+			break;
+		tmp = add_sort_key(hist, sort_key, list);
+		if (!tmp)
+			goto fail;
+		list = tmp;
+	}
+	va_end(ap);
+
+	tracefs_list_free(hist->sort);
+	hist->sort = list;
+
+	return 0;
+ fail:
+	tracefs_list_free(list);
+	return -1;
+}
+
+static int end_match(const char *sort_key, const char *ending)
+{
+	int key_len = strlen(sort_key);
+	int end_len = strlen(ending);
+
+	if (key_len <= end_len)
+		return 0;
+
+	sort_key += key_len - end_len;
+
+	return strcmp(sort_key, ending) == 0 ? key_len - end_len : 0;
+}
+
+/**
+ * tracefs_hist_sort_key_direction - set direction of a sort key
+ * @hist: The histogram to modify.
+ * @sort_str: The sort key to set the direction for
+ * @dir: The direction to set the sort key to.
+ *
+ * Returns 0 on success, and -1 on error;
+ */
+int tracefs_hist_sort_key_direction(struct tracefs_hist *hist,
+				    const char *sort_str,
+				    enum tracefs_hist_sort_direction dir)
+{
+	char **sort = hist->sort;
+	char *sort_key;
+	char *direct;
+	int match;
+	int i;
+
+	if (!sort)
+		return -1;
+
+	for (i = 0; sort[i]; i++) {
+		if (strcmp(sort[i], sort_str) == 0)
+			break;
+	}
+	if (!sort[i])
+		return -1;
+
+	sort_key = sort[i];
+
+	switch (dir) {
+	case TRACEFS_HIST_SORT_ASCENDING:
+		direct = ASCENDING;
+		break;
+	case TRACEFS_HIST_SORT_DESCENDING:
+		direct = DESCENDING;
+		break;
+	default:
+		return -1;
+	}
+
+	match = end_match(sort_key, ASCENDING);
+	if (match) {
+		/* Already match? */
+		if (dir == TRACEFS_HIST_SORT_ASCENDING)
+			return 0;
+	} else {
+		match = end_match(sort_key, DESCENDING);
+		/* Already match? */
+		if (match && dir == TRACEFS_HIST_SORT_DESCENDING)
+			return 0;
+	}
+
+	if (match)
+		/* Clear the original text */
+		sort_key[match] = '\0';
+
+	sort_key = realloc(sort_key, strlen(sort_key) + strlen(direct) + 1);
+	if (!sort_key) {
+		/* Failed to alloc, may need to put back the match */
+		sort_key = sort[i];
+		if (match)
+			sort_key[match] = '.';
+		return -1;
+	}
+
+	strcat(sort_key, direct);
+	sort[i] = sort_key;
+	return 0;
+}
-- 
2.30.2


  parent reply	other threads:[~2021-07-07  3:11 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-07-07  3:11 [PATCH 0/4] libtracefs: Work to add histogram APIs Steven Rostedt
2021-07-07  3:11 ` [PATCH 1/4] libtracefs: Implement tracefs_list_size() Steven Rostedt
2021-07-07  3:11 ` [PATCH 2/4] libtracefs: Implement functions to work on event directory files Steven Rostedt
2021-07-07  3:11 ` [PATCH 3/4] libtracefs: Have instances have internal ref counting Steven Rostedt
2021-07-07  3:11 ` Steven Rostedt [this message]
2021-07-07  8:12   ` [PATCH 4/4] libtracefs: Implement API to create / modify and display histograms Yordan Karadzhov (VMware)
2021-07-07 13:13     ` Steven Rostedt
2021-07-07 13:21       ` Yordan Karadzhov (VMware)
2021-07-07 13:48         ` Steven Rostedt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210707031110.249759-5-rostedt@goodmis.org \
    --to=rostedt@goodmis.org \
    --cc=bristot@redhat.com \
    --cc=linux-trace-devel@vger.kernel.org \
    --cc=mhiramat@kernel.org \
    --cc=namhyung@kernel.org \
    --cc=zanussi@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).