541 lines
15 KiB
Plaintext
541 lines
15 KiB
Plaintext
libtracefs(3)
|
|
=============
|
|
|
|
NAME
|
|
----
|
|
tracefs_hist_add_sort_key, tracefs_hist_set_sort_key, tracefs_hist_sort_key_direction,
|
|
tracefs_hist_add_name, tracefs_hist_append_filter, tracefs_hist_echo_cmd, tracefs_hist_command,
|
|
tracefs_hist_get_name, tracefs_hist_get_event, tracefs_hist_get_system - Update and describe an event histogram
|
|
|
|
SYNOPSIS
|
|
--------
|
|
[verse]
|
|
--
|
|
*#include <tracefs.h>*
|
|
|
|
int *tracefs_hist_add_sort_key*(struct tracefs_hist pass:[*]_hist_,
|
|
const char pass:[*]_sort_key_);
|
|
|
|
int *tracefs_hist_set_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_append_filter*(struct tracefs_hist pass:[*]_hist_,
|
|
enum tracefs_filter _type_,
|
|
const char pass:[*]_field_,
|
|
enum tracefs_compare _compare_,
|
|
const char pass:[*]_val_);
|
|
|
|
int *tracefs_hist_echo_cmd*(struct trace_seq pass:[*]_s_, struct tracefs_instance pass:[*]_instance_,
|
|
struct tracefs_hist pass:[*]_hist_,
|
|
enum tracefs_hist_command _command_);
|
|
|
|
int *tracefs_hist_command*(struct tracefs_instance pass:[*]_instance_,
|
|
struct tracefs_hist pass:[*]_hist_,
|
|
enum tracefs_hist_command _command_);
|
|
|
|
const char pass:[*]*tracefs_hist_get_name*(struct tracefs_hist pass:[*]_hist_);
|
|
|
|
const char pass:[*]*tracefs_hist_get_event*(struct tracefs_hist pass:[*]_hist_);
|
|
|
|
const char pass:[*]*tracefs_hist_get_system*(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_add_sort_key*() will add a key to sort on. The _hist_ is the
|
|
histogram 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. If _hist_ already has sorting keys (previously added) the new
|
|
_sort_key_ will have lower priority(be secondary or so on) when sorting.
|
|
|
|
*tracefs_hist_set_sort_key*() will reset the list of key to sort on. The _hist_ is
|
|
the histogram descriptor to reset 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_append_filter*() creates a filter or appends to it for the
|
|
histogram event. Depending on _type_, it will build a string of tokens for
|
|
parenthesis or logic statements, or it may add a comparison of _field_
|
|
to _val_ based on _compare_.
|
|
|
|
If _type_ is:
|
|
*TRACEFS_FILTER_COMPARE* - See below
|
|
*TRACEFS_FILTER_AND* - Append "&&" to the filter
|
|
*TRACEFS_FILTER_OR* - Append "||" to the filter
|
|
*TRACEFS_FILTER_NOT* - Append "!" to the filter
|
|
*TRACEFS_FILTER_OPEN_PAREN* - Append "(" to the filter
|
|
*TRACEFS_FILTER_CLOSE_PAREN* - Append ")" to the filter
|
|
|
|
_field_, _compare_, and _val_ are ignored unless _type_ is equal to
|
|
*TRACEFS_FILTER_COMPARE*, then _compare_ will be used for the following:
|
|
|
|
*TRACEFS_COMPARE_EQ* - _field_ == _val_
|
|
|
|
*TRACEFS_COMPARE_NE* - _field_ != _val_
|
|
|
|
*TRACEFS_COMPARE_GT* - _field_ > _val_
|
|
|
|
*TRACEFS_COMPARE_GE* - _field_ >= _val_
|
|
|
|
*TRACEFS_COMPARE_LT* - _field_ < _val_
|
|
|
|
*TRACEFS_COMPARE_LE* - _field_ <pass:[=] _val_
|
|
|
|
*TRACEFS_COMPARE_RE* - _field_ ~ "_val_" : where _field_ is a string.
|
|
|
|
*TRACEFS_COMPARE_AND* - _field_ & _val_ : where _field_ is a flags field.
|
|
|
|
*trace_hist_echo_cmd*() prints the commands needed to create the given histogram
|
|
in the given _instance_, or NULL for the top level, into the _seq_.
|
|
The command that is printed is described by _command_ and shows the functionality
|
|
that would be done by *tracefs_hist_command*(3).
|
|
|
|
*tracefs_hist_command*() is called to process a command on the histogram
|
|
_hist_ for its event in the given _instance_, or NULL for the top level.
|
|
The _cmd_ can be one of:
|
|
|
|
*TRACEFS_HIST_CMD_START* or zero to start execution of the histogram.
|
|
|
|
*TRACEFS_HIST_CMD_PAUSE* to pause the given histogram.
|
|
|
|
*TRACEFS_HIST_CMD_CONT* to continue a paused histogram.
|
|
|
|
*TRACEFS_HIST_CMD_CLEAR* to reset the values of a histogram.
|
|
|
|
*TRACEFS_HIST_CMD_DESTROY* to destroy the histogram (undo a START).
|
|
|
|
The below functions are wrappers to tracefs_hist_command() to make the
|
|
calling conventions a bit easier to understand what is happening.
|
|
|
|
KEY TYPES
|
|
---------
|
|
|
|
*tracefs_hist_alloc_nd*() 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_get_name*() returns the name of the histogram or NULL on error.
|
|
The returned string belongs to the histogram object and is freed with the histogram
|
|
by *tracefs_hist_free*().
|
|
|
|
*tracefs_hist_get_event*() returns the event name of the histogram or NULL on error.
|
|
The returned string belongs to the histogram object and is freed with the histogram
|
|
by *tracefs_hist_free*().
|
|
|
|
*tracefs_hist_get_system*() returns the system name of the histogram or NULL on error.
|
|
The returned string belongs to the histogram object and is freed with the histogram
|
|
by *tracefs_hist_free*().
|
|
|
|
*tracefs_hist_alloc_nd*() returns an allocated histogram descriptor which must
|
|
be freed by *tracefs_hist_free*() or NULL on error.
|
|
|
|
*tracefs_hist_get_name*(), *tracefs_hist_get_event*() and *tracefs_hist_get_system*()
|
|
return strings owned by the histogram object.
|
|
|
|
All the other functions return zero on success or -1 on error.
|
|
|
|
EXAMPLE
|
|
-------
|
|
[source,c]
|
|
--
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <tracefs.h>
|
|
|
|
enum commands {
|
|
START,
|
|
PAUSE,
|
|
CONT,
|
|
RESET,
|
|
DELETE,
|
|
SHOW,
|
|
};
|
|
|
|
static void parse_system_event(char *group, char **system, char **event)
|
|
{
|
|
*system = strtok(group, "/");
|
|
*event = strtok(NULL, "/");
|
|
if (!*event) {
|
|
*event = *system;
|
|
*system = NULL;
|
|
}
|
|
}
|
|
|
|
static int parse_keys(char *keys, struct tracefs_hist_axis **axes)
|
|
{
|
|
char *sav = NULL;
|
|
char *key;
|
|
int cnt = 0;
|
|
|
|
for (key = strtok_r(keys, ",", &sav); key; key = strtok_r(NULL, ",", &sav)) {
|
|
struct tracefs_hist_axis *ax;
|
|
char *att;
|
|
|
|
ax = realloc(*axes, sizeof(*ax) * (cnt + 2));
|
|
if (!ax) {
|
|
perror("Failed to allocate axes");
|
|
exit(-1);
|
|
}
|
|
ax[cnt].key = key;
|
|
ax[cnt].type = 0;
|
|
ax[cnt + 1].key = NULL;
|
|
ax[cnt + 1].type = 0;
|
|
|
|
*axes = ax;
|
|
|
|
att = strchr(key, '.');
|
|
if (att) {
|
|
*att++ = '\0';
|
|
if (strcmp(att, "hex") == 0)
|
|
ax[cnt].type = TRACEFS_HIST_KEY_HEX;
|
|
else if (strcmp(att, "sym") == 0)
|
|
ax[cnt].type = TRACEFS_HIST_KEY_SYM;
|
|
else if (strcmp(att, "sym_offset") == 0)
|
|
ax[cnt].type = TRACEFS_HIST_KEY_SYM_OFFSET;
|
|
else if (strcmp(att, "syscall") == 0)
|
|
ax[cnt].type = TRACEFS_HIST_KEY_SYSCALL;
|
|
else if (strcmp(att, "exec") == 0)
|
|
ax[cnt].type = TRACEFS_HIST_KEY_EXECNAME;
|
|
else if (strcmp(att, "log") == 0)
|
|
ax[cnt].type = TRACEFS_HIST_KEY_LOG;
|
|
else if (strcmp(att, "usecs") == 0)
|
|
ax[cnt].type = TRACEFS_HIST_KEY_USECS;
|
|
else {
|
|
fprintf(stderr, "Undefined attribute '%s'\n", att);
|
|
fprintf(stderr," Acceptable attributes:\n");
|
|
fprintf(stderr," hex, sym, sym_offset, syscall, exe, log, usecs\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
cnt++;
|
|
}
|
|
return cnt;
|
|
}
|
|
|
|
static void process_hist(enum commands cmd, const char *instance_name,
|
|
char *group, char *keys, char *vals, char *sort,
|
|
char *ascend, char *desc)
|
|
{
|
|
struct tracefs_instance *instance = NULL;
|
|
struct tracefs_hist *hist;
|
|
struct tep_handle *tep;
|
|
struct tracefs_hist_axis *axes = NULL;
|
|
char *system;
|
|
char *event;
|
|
char *sav;
|
|
char *val;
|
|
int ret;
|
|
int cnt;
|
|
|
|
if (instance_name) {
|
|
instance = tracefs_instance_create(instance_name);
|
|
if (!instance) {
|
|
fprintf(stderr, "Failed instance create\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
tep = tracefs_local_events(NULL);
|
|
if (!tep) {
|
|
perror("Could not read events");
|
|
exit(-1);
|
|
}
|
|
|
|
parse_system_event(group, &system, &event);
|
|
|
|
if (cmd == SHOW) {
|
|
char *content;
|
|
content = tracefs_event_file_read(instance, system, event,
|
|
"hist", NULL);
|
|
if (!content) {
|
|
perror("Reading hist file");
|
|
exit(-1);
|
|
}
|
|
printf("%s\n", content);
|
|
free(content);
|
|
return;
|
|
}
|
|
|
|
if (!keys) {
|
|
fprintf(stderr, "Command needs -k option\n");
|
|
exit(-1);
|
|
}
|
|
|
|
cnt = parse_keys(keys, &axes);
|
|
if (!cnt) {
|
|
fprintf(stderr, "No keys??\n");
|
|
exit(-1);
|
|
}
|
|
|
|
/* Show examples of hist1d and hist2d */
|
|
switch (cnt) {
|
|
case 1:
|
|
hist = tracefs_hist_alloc(tep, system, event,
|
|
axes[0].key, axes[0].type);
|
|
break;
|
|
case 2:
|
|
hist = tracefs_hist_alloc_2d(tep, system, event,
|
|
axes[0].key, axes[0].type,
|
|
axes[1].key, axes[1].type);
|
|
break;
|
|
default:
|
|
/* Really, 1 and 2 could use this too */
|
|
hist = tracefs_hist_alloc_nd(tep, system, event, axes);
|
|
}
|
|
if (!hist) {
|
|
fprintf(stderr, "Failed hist create\n");
|
|
exit(-1);
|
|
}
|
|
|
|
if (vals) {
|
|
sav = NULL;
|
|
for (val = strtok_r(vals, ",", &sav); val; val = strtok_r(NULL, ",", &sav)) {
|
|
ret = tracefs_hist_add_value(hist, val);
|
|
if (ret) {
|
|
fprintf(stderr, "Failed to add value %s\n", val);
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sort) {
|
|
sav = NULL;
|
|
for (val = strtok_r(sort, ",", &sav); val; val = strtok_r(NULL, ",", &sav)) {
|
|
ret = tracefs_hist_add_sort_key(hist, val);
|
|
if (ret) {
|
|
fprintf(stderr, "Failed to add sort key/val %s\n", val);
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ascend) {
|
|
sav = NULL;
|
|
for (val = strtok_r(ascend, ",", &sav); val; val = strtok_r(NULL, ",", &sav)) {
|
|
ret = tracefs_hist_sort_key_direction(hist, val, TRACEFS_HIST_SORT_ASCENDING);
|
|
if (ret) {
|
|
fprintf(stderr, "Failed to add ascending key/val %s\n", val);
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (desc) {
|
|
sav = NULL;
|
|
for (val = strtok_r(desc, ",", &sav); val; val = strtok_r(NULL, ",", &sav)) {
|
|
ret = tracefs_hist_sort_key_direction(hist, val, TRACEFS_HIST_SORT_DESCENDING);
|
|
if (ret) {
|
|
fprintf(stderr, "Failed to add descending key/val %s\n", val);
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
tracefs_error_clear(instance);
|
|
|
|
switch (cmd) {
|
|
case START:
|
|
ret = tracefs_hist_start(instance, hist);
|
|
if (ret) {
|
|
char *err = tracefs_error_last(instance);
|
|
if (err)
|
|
fprintf(stderr, "\n%s\n", err);
|
|
}
|
|
break;
|
|
case PAUSE:
|
|
ret = tracefs_hist_pause(instance, hist);
|
|
break;
|
|
case CONT:
|
|
ret = tracefs_hist_continue(instance, hist);
|
|
break;
|
|
case RESET:
|
|
ret = tracefs_hist_reset(instance, hist);
|
|
break;
|
|
case DELETE:
|
|
ret = tracefs_hist_destroy(instance, hist);
|
|
break;
|
|
case SHOW:
|
|
/* Show was already done */
|
|
break;
|
|
}
|
|
if (ret)
|
|
fprintf(stderr, "Failed: command\n");
|
|
exit(ret);
|
|
}
|
|
|
|
int main (int argc, char **argv, char **env)
|
|
{
|
|
enum commands cmd;
|
|
char *instance = NULL;
|
|
char *cmd_str;
|
|
char *event = NULL;
|
|
char *keys = NULL;
|
|
char *vals = NULL;
|
|
char *sort = NULL;
|
|
char *desc = NULL;
|
|
char *ascend = NULL;
|
|
|
|
if (argc < 2) {
|
|
fprintf(stderr, "usage: %s command [-B instance][-e [system/]event][-k keys][-v vals][-s sort]\n", argv[0]);
|
|
fprintf(stderr, " [-a ascending][-d descending]\n");
|
|
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);
|
|
}
|
|
|
|
for (;;) {
|
|
int c;
|
|
|
|
c = getopt(argc - 1, argv + 1, "e:k:v:B:s:d:a:");
|
|
if (c == -1)
|
|
break;
|
|
|
|
switch (c) {
|
|
case 'e':
|
|
event = optarg;
|
|
break;
|
|
case 'k':
|
|
keys = optarg;
|
|
break;
|
|
case 'v':
|
|
vals = optarg;
|
|
break;
|
|
case 'B':
|
|
instance = optarg;
|
|
break;
|
|
case 's':
|
|
sort = optarg;
|
|
break;
|
|
case 'd':
|
|
desc = optarg;
|
|
break;
|
|
case 'a':
|
|
ascend = optarg;
|
|
break;
|
|
}
|
|
}
|
|
if (!event) {
|
|
event = "kmem/kmalloc";
|
|
if (!keys)
|
|
keys = "call_site.sym,bytes_req";
|
|
if (!vals)
|
|
vals = "bytes_alloc";
|
|
if (!sort)
|
|
sort = "bytes_req,bytes_alloc";
|
|
if (!desc)
|
|
desc = "bytes_alloc";
|
|
}
|
|
process_hist(cmd, instance, event, keys, vals, sort, ascend, desc);
|
|
}
|
|
--
|
|
|
|
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).
|