aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSadaf Ebrahimi <sadafebrahimi@google.com>2023-01-19 16:27:59 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-01-19 16:27:59 +0000
commite2afc71443010991595eac6d1d41c971e0fdbcff (patch)
tree21be18ffc6c7cbb651dcc56278ff076a3f297bcf
parenta190bcadb63363dffc745feafd40ce49ad5d21de (diff)
parent1da5104322888f1c54f29df5c40e1c7953c6a6d0 (diff)
downloadlibtracefs-e2afc71443010991595eac6d1d41c971e0fdbcff.tar.gz
Upgrade libtracefs to libtracefs-1.6.4 am: eee6e409c7 am: 1da5104322
Original change: https://android-review.googlesource.com/c/platform/external/libtracefs/+/2394014 Change-Id: I74d3e075451e64144124f61af4b68e45b5295ead Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.gitignore2
-rw-r--r--Documentation/libtracefs-cpu-open.txt100
-rw-r--r--Documentation/libtracefs-cpu.txt240
-rw-r--r--Documentation/libtracefs-events.txt57
-rw-r--r--Documentation/libtracefs-files.txt33
-rw-r--r--Documentation/libtracefs-hist.txt53
-rw-r--r--Documentation/libtracefs-instances-utils.txt22
-rw-r--r--Documentation/libtracefs-iterator.txt229
-rw-r--r--Documentation/libtracefs-sql.txt20
-rw-r--r--Documentation/libtracefs-traceon.txt2
-rw-r--r--Documentation/libtracefs-utils.txt11
-rw-r--r--Documentation/libtracefs.txt45
-rw-r--r--METADATA17
-rw-r--r--Makefile21
-rwxr-xr-xcheck-manpages.sh9
-rw-r--r--include/tracefs-local.h22
-rw-r--r--include/tracefs.h56
-rw-r--r--samples/Makefile8
-rw-r--r--scripts/utils.mk6
-rw-r--r--src/Makefile14
-rw-r--r--src/sqlhist-parse.h3
-rw-r--r--src/tracefs-dynevents.c3
-rw-r--r--src/tracefs-events.c475
-rw-r--r--src/tracefs-filter.c17
-rw-r--r--src/tracefs-hist.c225
-rw-r--r--src/tracefs-instance.c66
-rw-r--r--src/tracefs-record.c611
-rw-r--r--src/tracefs-sqlhist.c68
-rw-r--r--src/tracefs-tools.c2
-rw-r--r--src/tracefs-utils.c163
-rw-r--r--utest/Makefile12
-rw-r--r--utest/README3
-rw-r--r--utest/tracefs-utest.c630
33 files changed, 2991 insertions, 254 deletions
diff --git a/.gitignore b/.gitignore
index 88c9b42..3e72a58 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,5 +11,5 @@ build_prefix
build_uninstall
tfs_version.h
*.o
-*.d
+.*.d
sqlhist
diff --git a/Documentation/libtracefs-cpu-open.txt b/Documentation/libtracefs-cpu-open.txt
new file mode 100644
index 0000000..c5a900a
--- /dev/null
+++ b/Documentation/libtracefs-cpu-open.txt
@@ -0,0 +1,100 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_cpu_open, tracefs_cpu_close, tracefs_cpu_alloc_fd, tracefs_cpu_free_fd - Opening trace_pipe_raw data for reading
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+struct tracefs_cpu pass:[*]*tracefs_cpu_open*(struct tracefs_instance pass:[*]_instance_,
+ int _cpu_, bool _nonblock_);
+void *tracefs_cpu_close*(struct tracefs_cpu pass:[*]_tcpu_);
+
+struct tracefs_cpu pass:[*]*tracefs_cpu_alloc_fd*(int _fd_, int _subbuf_size_, bool _nonblock_);
+void *tracefs_cpu_free_fd*(struct tracefs_cpu pass:[*]_tcpu_);
+--
+
+DESCRIPTION
+-----------
+This set of APIs can be used to open the raw data from the trace_pipe_raw
+files in the tracefs file system in oder to read them with the *tracefs_cpu_read*(3)
+functions.
+
+The *tracefs_cpu_open()* creates a descriptor that can read the tracefs
+trace_pipe_raw file for a given _cpu_ in a given _instance_. If _instance_ is
+NULL than the toplevel trace_pipe_raw file is used.
+
+The *tracefs_cpu_close()* closes all the file descriptors associated to the trace_pipe_raw
+opened by *tracefs_cpu_open()*.
+
+The *tracefs_cpu_alloc_fd()* will create a tracefs_cpu descriptor from an existing
+file descriptor _fd_. This is useful to use when connecting to a socket or pipe where
+the other end is feeding raw tracing data in the same format as the trace_pipe_raw
+file would (like in guest to host tracing). The caller is responsible for determining
+the _subbuf_size_ that will be used to break up the sub-buffers being read by the
+file descriptor. The _nonblock_ is treated the same as the same parameter in
+*tracefs_cpu_open()*.
+
+The *tracefs_cpu_free_fd()* is used to free the descriptor returned by *tracefs_cpu_alloc_fd()*.
+It does all the clean up that *tracefs_cpu_close()* performs, and that could also be
+used to free up the descriptor created by *tracefs_cpu_alloc_fd()* but will also close
+the file descriptor passed in. Note that *tracefs_cpu_free_fd()* should not be used
+on the descriptor returned by *tracefs_cpu_open()* as it will not close the file descriptor
+created by it.
+
+RETURN VALUE
+------------
+The *tracefs_cpu_open()* returns a struct tracefs_cpu descriptor that can be
+used by the other functions or NULL on error.
+
+The *tracefs_cpu_alloc_fd()* returns a struct tracefs_cpu descriptor that can
+be used by the *tracefs_cpu_read*(3) related functions, where the descriptor
+will be reading the passed in _fd_ file descriptor.
+
+EXAMPLE
+-------
+See *tracefs_cpu_read*(3) for an example.
+
+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)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+--
+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) 2022 Google, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-cpu.txt b/Documentation/libtracefs-cpu.txt
new file mode 100644
index 0000000..d6215d9
--- /dev/null
+++ b/Documentation/libtracefs-cpu.txt
@@ -0,0 +1,240 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_cpu_read_size, tracefs_cpu_read, tracefs_cpu_buffered_read, tracefs_cpu_write,
+tracefs_cpu_stop, tracefs_cpu_flush, tracefs_cpu_flush_write, tracefs_cpu_pipe
+- Reading trace_pipe_raw data
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_cpu_read_size*(struct tracefs_cpu pass:[*]_tcpu_);
+int *tracefs_cpu_read*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_, bool _nonblock_);
+int *tracefs_cpu_buffered_read*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_, bool _nonblock_);
+int *tracefs_cpu_write*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_, bool _nonblock_);
+int *tracefs_cpu_stop*(struct tracefs_cpu pass:[*]_tcpu_);
+int *tracefs_cpu_flush*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_);
+int *tracefs_cpu_flush_write*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_);
+int *tracefs_cpu_pipe*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_, bool _nonblock_);
+--
+
+DESCRIPTION
+-----------
+This set of APIs can be used to read the raw data from the trace_pipe_raw
+files in the tracefs file system.
+
+The *tracefs_cpu_read_size()* returns the subbuffer size of the trace_pipe_raw. This
+returns the minimum size of the buffer that is passed to the below functions.
+
+The *tracefs_cpu_read()* reads the trace_pipe_raw files associated to _tcpu_ into _buffer_.
+_buffer_ must be at least the size of the sub buffer of the ring buffer,
+which is returned by *tracefs_cpu_read_size()*. If _nonblock_ is set, and
+there's no data available, it will return immediately. Otherwise depending
+on how _tcpu_ was opened, it will block. If _tcpu_ was opened with nonblock
+set, then this _nonblock_ will make no difference.
+
+The *tracefs_cpu_buffered_read()* is basically the same as *tracefs_cpu_read()*
+except that it uses a pipe through splice to buffer reads. This will batch
+reads keeping the reading from the ring buffer less intrusive to the system,
+as just reading all the time can cause quite a disturbance. Note, one
+difference between this and *tracefs_cpu_read()* is that it will read only in
+sub buffer pages. If the ring buffer has not filled a page, then it will not
+return anything, even with _nonblock_ set. Calls to *tracefs_cpu_flush()*
+should be done to read the rest of the file at the end of the trace.
+
+The *tracefs_cpu_write()* will pipe the data from the trace_pipe_raw
+file associated with _tcpu_ into the _wfd_ file descriptor. If _nonblock_ is set,
+then it will not block on if there's nothing to write. Note, it will only write
+sub buffer size data to _wfd_. Calls to tracefs_cpu_flush_write() are needed to
+write out the rest.
+
+The *tracefs_cpu_stop()* will attempt to unblock a task blocked on _tcpu_ reading it.
+On older kernels, it may not do anything for the pipe reads, as older kernels do not
+wake up tasks waiting on the ring buffer. Returns 0 if it definitely woke up any possible
+waiters, but returns 1 if it is not sure it worked and waiters may need to have a signal
+sent to them.
+
+The *tracefs_cpu_flush()* reads the trace_pipe_raw file associated by the _tcpu_ and puts it
+into _buffer_, which must be the size of the sub buffer which is retrieved.
+by *tracefs_cpu_read_size()*. This should be called at the end of tracing
+to get the rest of the data. This call will convert the file descriptor of
+trace_pipe_raw into non-blocking mode.
+
+The *tracefs_cpu_flush_write()* same as *trace_cpu_flush()* except it takes a file
+descriptor _wfd_ to flush the data into.
+
+The *tracefs_cpu_pipe()* is similar to *tracefs_cpu_write()* but the _wfd_ file descriptor
+must be a pipe. This call is an optimization of *tracefs_cpu_write()* that uses two calls
+to *splice*(2) in order to connect the trace_pipe_raw file descriptor with the write file
+descriptor. *splice*(2) requires that one of the passed in file descriptors is a pipe.
+If the application wants to pass the data to an existing pipe, there's no reason for
+there to be two *splice*(2) system calls and *tracefs_cpu_pipe()* can simply use a single
+call to _wfd_.
+
+RETURN VALUE
+------------
+The *tracefs_cpu_open()* returns a struct tracefs_cpu descriptor that can be
+used by the other functions or NULL on error.
+
+The *tracefs_cpu_read_size()* returns the minimum size of the buffers to be
+used with *tracefs_cpu_read()*, *tracefs_cpu_buffered_read()* and *tracefs_cpu_flush()*.
+Returns negative on error.
+
+The *tracefs_cpu_read()* returns the number of bytes read, or negative on error.
+
+The *tracefs_cpu_buffered_read()* returns the number of bytes read or negative on error.
+
+The *tracefs_cpu_write()* returns the number of bytes written to the file
+or negative on error.
+
+The *tracefs_cpu_stop()* returns zero if any waiters were guaranteed to be
+woken up from waiting on input, or returns one if this is an older kernel
+that does not supply that guarantee, and a signal may need to be sent to
+any waiters. Returns negative on error.
+
+The *tracefs_cpu_flush()* returns the number of bytes read or negative on error.
+
+The *tracefs_cpu_flush_write()* returns the number of bytes written to the
+file or negative on error.
+
+EXAMPLE
+-------
+[source,c]
+--
+#define _LARGEFILE64_SOURCE
+#include <stdlib.h>
+#include <ctype.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <tracefs.h>
+
+struct thread_data {
+ struct tracefs_cpu *tcpu;
+ int done;
+ int fd;
+};
+
+static void *thread_run(void *arg)
+{
+ struct thread_data *data = arg;
+ struct tracefs_cpu *tcpu = data->tcpu;
+ int fd = data->fd;
+ int ret;
+
+ while (!data->done) {
+ ret = tracefs_cpu_write(tcpu, fd, false);
+ printf("wrote %d\n", ret);
+ }
+ return NULL;
+}
+
+int main (int argc, char **argv)
+{
+ struct tracefs_instance *instance;
+ struct thread_data data;
+ pthread_t thread;
+ char *file;
+ int secs = 10;
+ int cpu;
+ int ret;
+
+ if (argc < 3 || !isdigit(argv[1][0])) {
+ printf("usage: %s cpu file_destination [sleep secs]\n\n", argv[0]);
+ exit(-1);
+ }
+
+ cpu = atoi(argv[1]);
+ file = argv[2];
+
+ if (argc > 3)
+ secs = atoi(argv[3]);
+
+ instance = tracefs_instance_create("cpu_write");
+ if (!instance) {
+ perror("create instance");
+ exit(-1);
+ }
+
+ memset(&data, 0, sizeof(data));
+
+ data.tcpu = tracefs_cpu_open(instance, cpu, 0);
+ if (!data.tcpu) {
+ perror("Open instance");
+ exit(-1);
+ }
+
+ data.fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
+ if (data.fd < 0) {
+ perror(file);
+ exit(-1);
+ }
+
+ pthread_create(&thread, NULL, thread_run, &data);
+
+ sleep(secs);
+
+ data.done = 1;
+ printf("stopping\n");
+ ret = tracefs_cpu_stop(data.tcpu);
+
+ printf("joining %d\n", ret);
+ pthread_join(thread, NULL);
+
+ tracefs_trace_off(instance);
+ do {
+ ret = tracefs_cpu_flush_write(data.tcpu, data.fd);
+ printf("flushed %d\n", ret);
+ } while (ret > 0);
+ tracefs_trace_on(instance);
+
+ tracefs_cpu_close(data.tcpu);
+ close(data.fd);
+
+ return 0;
+}
+--
+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
+--------
+*tracefs_cpu_open*(3)
+*tracefs_cpu_close*(3)
+*libtracefs*(3),
+*libtraceevent*(3),
+*trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+--
+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) 2022 Google, Inc. Free use of this software is granted under
+the terms of the GNU Public License (GPL).
diff --git a/Documentation/libtracefs-events.txt b/Documentation/libtracefs-events.txt
index f998c79..90c54b8 100644
--- a/Documentation/libtracefs-events.txt
+++ b/Documentation/libtracefs-events.txt
@@ -4,7 +4,7 @@ libtracefs(3)
NAME
----
tracefs_event_systems, tracefs_system_events, tracefs_event_enable, tracefs_event_disable,
-tracefs_iterate_raw_events, tracefs_iterate_stop - Work with trace systems and events.
+tracefs_event_is_enabled - Work with trace systems and events.
SYNOPSIS
--------
@@ -12,18 +12,21 @@ SYNOPSIS
--
*#include <tracefs.h>*
+enum tracefs_event_state {
+ TRACEFS_ERROR = -1,
+ TRACEFS_ALL_DISABLED = 0,
+ TRACEFS_ALL_ENABLED = 1,
+ TRACEFS_SOME_ENABLED = 2,
+};
+
char pass:[*]pass:[*]*tracefs_event_systems*(const char pass:[*]_tracing_dir_);
char pass:[*]pass:[*]*tracefs_system_events*(const char pass:[*]_tracing_dir_, const char pass:[*]_system_);
int *tracefs_event_enable*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_,
const char pass:[*]_event_);
int *tracefs_event_disable*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_,
const char pass:[*]_event_);
-int *tracefs_iterate_raw_events*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_,
- cpu_set_t pass:[*]_cpus_, int _cpu_size_,
- int (pass:[*]_callback_)(struct tep_event pass:[*], struct tep_record pass:[*], int, void pass:[*]),
- void pass:[*]_callback_context_);
-void *tracefs_iterate_stop*(struct tracefs_instance pass:[*]_instance_);
-
+enum tracefs_enable_state *tracefs_event_is_enabled*(struct tracefs_instance pass:[*]_instance_,
+ const char pass:[*]_system_, const char pass:[*]_event_);
--
DESCRIPTION
@@ -61,25 +64,23 @@ events. That is, if _instance_ is NULL, then the top level tracing directory
is used. If both _system_ and _event_ are NULL then all events are disabled
for the given _instance_, and so on.
-The *tracefs_iterate_raw_events()* function will read the tracefs raw
-data buffers and call the specified _callback_ function for every event it
-encounters. Events are iterated in sorted order: oldest first. An initialized
-_tep_ handler is required (See *tracefs_local_events*(3)). If _instance_ is
-NULL, then the toplevel tracefs buffer is used, otherwise the buffer for
-the corresponding _instance_ is read. To filter only on a subset of CPUs,
-_cpus_ and _cpu_size_ may be set to only call _callback_ with events that
-occurred on the CPUs specified, otherwise if _cpus_ is NULL then the _callback_
-function will be called for all events, and _cpu_size_ is ignored. The
-_callback_ function will be called with the following parameters: A
-pointer to a struct tep_event that corresponds to the type of event the
-record is; The record representing the event; The CPU that the event
-occurred on; and a pointer to user specified _callback_context_. If the _callback_
-returns non-zero, the iteration stops.
-
-Use *tracefs_iterate_stop()* to force a executing *tracefs_iterate_raw_events()*
-to halt. This can be called from either a callback that is called by
-the iterator (even though a return of non-zero will stop it), or from another
-thread.
+The *tracefs_event_is_enabled()* returns if an event is enabled, a set of
+events are enabled, a system is enabled, or all events are enabled. If both
+_system_ and _event_ are NULL, then it returns the enable state of all events.
+If _system_ is not NULL and _event_ is NULL, then it will check if all the events
+in all the systems that _system_ and return the enable state of those events.
+If _system_ is NULL and _event_ is not NULL, then it will match all the events
+in all systems that match _event_ and return their enabled state. If both _system_
+and _event_ are not NULL, then it will return the enabled state of all matching
+events. The enabled state is defined as:
+
+*TRACEFS_ERROR* - An error occurred including no event were matched.
+
+*TRACEFS_ALL_DISABLED* - All matching events are disabled.
+
+*TRACEFS_ALL_ENABLED* - All matching events are enabled.
+
+*TRACEFS_SOME_ENABLED* - Some matching events were enabled while others were not.
RETURN VALUE
------------
@@ -95,8 +96,8 @@ found, it will return -1 and errno will be set. If no errors occur, but no event
are found that match the _system_ and _event_ parameters, then -1 is returned
and errno is not set.
-The *tracefs_iterate_raw_events()* function returns -1 in case of an error or
-0 otherwise.
+The *tracefs_event_is_enabled()* returns the enabled status of the matching events
+or TRACEFS_ERROR on error.
EXAMPLE
-------
diff --git a/Documentation/libtracefs-files.txt b/Documentation/libtracefs-files.txt
index 13e251d..d22e759 100644
--- a/Documentation/libtracefs-files.txt
+++ b/Documentation/libtracefs-files.txt
@@ -3,8 +3,8 @@ libtracefs(3)
NAME
----
-tracefs_get_tracing_file, tracefs_put_tracing_file, tracefs_tracing_dir -
-Find locations of trace directory and files.
+tracefs_get_tracing_file, tracefs_put_tracing_file, tracefs_tracing_dir, tracefs_debug_dir, tracefs_set_tracing_dir,
+tracefs_tracing_dir_is_mounted - Find and set locations of trace directory and files.
SYNOPSIS
--------
@@ -15,6 +15,9 @@ SYNOPSIS
char pass:[*]*tracefs_get_tracing_file*(const char pass:[*]_name_);
void *tracefs_put_tracing_file*(char pass:[*]_name_);
const char pass:[*]*tracefs_tracing_dir*(void);
+const char pass:[*]*tracefs_debug_dir*(void);
+int *tracefs_set_tracing_dir*(char pass:[*]_tracing_dir_)
+int *tracefs_tracing_dir_is_mounted*(bool _mount_, const char pass:[**]_path_);
--
DESCRIPTION
@@ -22,6 +25,13 @@ DESCRIPTION
This set of APIs can be used to find the full path of the trace file
system mount point and trace files in it.
+The *tracefs_set_tracing_dir()* function sets a custom location of the
+system's tracing directory mount point. Usually, the library auto detects
+it using the information from the /proc/mounts file. Use this API only if the
+mount point is not standard and cannot be detected by the library. The _tracing_dir_
+argument can be NULL, in that case the custom location is deleted and the library
+auto detection logic is used.
+
The *tracefs_get_tracing_file()* function returns the full path of the
file with given _name_ in the trace file system. The function works
only with files in the tracefs main directory, it is not trace instance
@@ -38,14 +48,33 @@ tracing file system is located, cached and returned. It will mount it,
if it is not mounted. On any subsequent call the cached path is returned.
The return string must _not_ be freed.
+The *tracefs_debug_dir()* is similar to *tracefs_tracing_dir()* except
+that it will return where the debugfs file system is mounted. If it
+is not mounted it will try to mount it. The return string must _not_
+be freed.
+
+*tracefs_tracing_dir_is_mounted()* returns 1 if the tracing directory is
+already mounted and 0 if it is not. If _mount_ is true, it will try to
+mount it if it is not, and returns 0 if it succesfully mounted it and -1
+if it did not. If _path_ is set, it will be assigned to the path where it
+mounted it. _path_ is internal and should not be freed.
+
RETURN VALUE
------------
+The *tracefs_set_tracing_dir()* function returns 0 on success, -1 otherwise.
+
The *tracefs_get_tracing_file()* function returns a string or NULL in case
of an error. The returned string must be freed with *tracefs_put_tracing_file()*.
The *tracefs_tracing_dir()* function returns a constant string or NULL
in case of an error. The returned string must _not_ be freed.
+The *tracefs_debug_dir()* function returns a constant string or NULL
+in case of an error. The returned string must _not_ be freed.
+
+The *tracefs_tracing_dir_is_mounted()* returns 1 if the tracing directory
+is already mounted, 0 if it is not, and -1 on error.
+
EXAMPLE
-------
[source,c]
diff --git a/Documentation/libtracefs-hist.txt b/Documentation/libtracefs-hist.txt
index 3ac21ce..7503fd0 100644
--- a/Documentation/libtracefs-hist.txt
+++ b/Documentation/libtracefs-hist.txt
@@ -3,8 +3,8 @@ libtracefs(3)
NAME
----
-tracefs_hist_alloc, tracefs_hist_alloc_2d, tracefs_hist_alloc_nd, tracefs_hist_free,
-tracefs_hist_add_key, tracefs_hist_add_value - Create and destroy event histograms
+tracefs_hist_alloc, tracefs_hist_alloc_2d, tracefs_hist_alloc_nd, tracefs_hist_alloc_nd_cnt, tracefs_hist_free,
+tracefs_hist_add_key, tracefs_hist_add_key_cnt, tracefs_hist_add_value - Create and destroy event histograms
SYNOPSIS
--------
@@ -39,10 +39,15 @@ struct tracefs_hist pass:[*]*tracefs_hist_alloc_2d*(struct tracefs_tep pass:[*]
struct tracefs_hist pass:[*]*tracefs_hist_alloc_nd*(struct tracefs_tep pass:[*] _tep_,
const char pass:[*]_system_, const char pass:[*]_event_,
struct tracefs_hist_axis pass:[*]_axes_);
+struct tracefs_hist pass:[*]*tracefs_hist_alloc_nd_cnt*(struct tep_handle pass:[*]_tep_,
+ const char pass:[*]_system_, const char pass:[*]_event_name_,
+ struct tracefs_hist_axis_cnt pass:[*]_axes_);
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_key_cnt*(struct tracefs_hist pass:[*]_hist_, const char pass:[*]_key_,
+ enum tracefs_hist_key_type _type_, int _cnt_);
int *tracefs_hist_add_value*(struct tracefs_hist pass:[*]_hist_, const char pass:[*]_value_);
--
@@ -78,6 +83,12 @@ _system_ and _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 _axes_ is an array of _key_ / _type_ pairs, defining the dimensions of the histogram.
+*tracefs_hist_alloc_nd_cnt*() will initialize a histogram descriptor that will be attached to
+the _system_/_event_. This only initializes the descriptor with the given _axes_ keys as primaries.
+This only initializes the descriptor, it does not start the histogram in the kernel.
+The difference between this and *tracefs_hist_alloc_nd()* is that the _axes_ parameter
+is of type *struct tracefs_hist_axis_cnt* and not *struct tracefs_hist_axis*.
+
*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.
@@ -90,6 +101,14 @@ a _tertiary_ key (or three dimensional histogram). The _hist_ parameter is the
histogram descriptor to add the _key_ to. The _type_ is the type of key to add
(See KEY TYPES below).
+The *tracefs_hist_add_key_cnt*() is the same as *tracefs_hist_add_key*() except
+that it allows to add a counter for the given type. Currently, there's only
+the *buckets* type that requires a counter. When adding a key with the buckets
+type, *tracefs_hist_add_key*() is not sufficient, as the *buckets* type requires
+a counter or bucket size. Use *tracefs_hist_add_key_cnt*() when specifing
+a key that is broken up into buckets, and pass in the size of those buckets
+into _cnt_.
+
*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.
@@ -140,6 +159,7 @@ EXAMPLE
[source,c]
--
#include <stdlib.h>
+#include <ctype.h>
#include <unistd.h>
#include <tracefs.h>
@@ -162,14 +182,14 @@ static void parse_system_event(char *group, char **system, char **event)
}
}
-static int parse_keys(char *keys, struct tracefs_hist_axis **axes)
+static int parse_keys(char *keys, struct tracefs_hist_axis_cnt **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;
+ struct tracefs_hist_axis_cnt *ax;
char *att;
ax = realloc(*axes, sizeof(*ax) * (cnt + 2));
@@ -201,7 +221,14 @@ static int parse_keys(char *keys, struct tracefs_hist_axis **axes)
ax[cnt].type = TRACEFS_HIST_KEY_LOG;
else if (strcmp(att, "usecs") == 0)
ax[cnt].type = TRACEFS_HIST_KEY_USECS;
- else {
+ else if (strncmp(att, "buckets", 7) == 0) {
+ if (att[7] != '=' && !isdigit(att[8])) {
+ fprintf(stderr, "'buckets' key type requires '=<size>'\n");
+ exit(-1);
+ }
+ ax[cnt].type = TRACEFS_HIST_KEY_BUCKETS;
+ ax[cnt].cnt = atoi(&att[8]);
+ } else {
fprintf(stderr, "Undefined attribute '%s'\n", att);
fprintf(stderr," Acceptable attributes:\n");
fprintf(stderr," hex, sym, sym_offset, syscall, exe, log, usecs\n");
@@ -220,7 +247,7 @@ static void process_hist(enum commands cmd, const char *instance_name,
struct tracefs_instance *instance = NULL;
struct tracefs_hist *hist;
struct tep_handle *tep;
- struct tracefs_hist_axis *axes = NULL;
+ struct tracefs_hist_axis_cnt *axes = NULL;
char *system;
char *event;
char *sav;
@@ -268,6 +295,17 @@ static void process_hist(enum commands cmd, const char *instance_name,
exit(-1);
}
+ /* buckets require the nd_cnt function */
+ switch (cnt) {
+ case 2:
+ if (axes[1].type == TRACEFS_HIST_KEY_BUCKETS)
+ cnt = -1;
+ /* fall through */
+ case 1:
+ if (axes[0].type == TRACEFS_HIST_KEY_BUCKETS)
+ cnt = -1;
+ }
+
/* Show examples of hist1d and hist2d */
switch (cnt) {
case 1:
@@ -281,7 +319,7 @@ static void process_hist(enum commands cmd, const char *instance_name,
break;
default:
/* Really, 1 and 2 could use this too */
- hist = tracefs_hist_alloc_nd(tep, system, event, axes);
+ hist = tracefs_hist_alloc_nd_cnt(tep, system, event, axes);
}
if (!hist) {
fprintf(stderr, "Failed hist create\n");
@@ -445,6 +483,7 @@ int main (int argc, char **argv, char **env)
}
process_hist(cmd, instance, event, keys, vals, sort, ascend, desc);
}
+
--
FILES
diff --git a/Documentation/libtracefs-instances-utils.txt b/Documentation/libtracefs-instances-utils.txt
index a79cc34..bc8c9a7 100644
--- a/Documentation/libtracefs-instances-utils.txt
+++ b/Documentation/libtracefs-instances-utils.txt
@@ -3,9 +3,8 @@ libtracefs(3)
NAME
----
-tracefs_instance_get_name, tracefs_instance_get_trace_dir,
-tracefs_instances_walk, tracefs_instance_exists -
-Helper functions for working with tracing instances.
+tracefs_instance_get_name, tracefs_instance_get_trace_dir, tracefs_instances_walk, tracefs_instance_exists,
+tracefs_instance_get_buffer_size, tracefs_instance_set_buffer_size - Helper functions for working with tracing instances.
SYNOPSIS
--------
@@ -17,7 +16,8 @@ const char pass:[*]*tracefs_instance_get_name*(struct tracefs_instance pass:[*]_
const char pass:[*]*tracefs_instance_get_trace_dir*(struct tracefs_instance pass:[*]_instance_);
int *tracefs_instances_walk*(int (pass:[*]_callback_)(const char pass:[*], void pass:[*]), void pass:[*]_context)_;
bool *tracefs_instance_exists*(const char pass:[*]_name_);
-
+size_t *tracefs_instance_get_buffer_size*(struct tracefs_instance pass:[*]_instance_, int _cpu_);
+int *tracefs_instance_set_buffer_size*(struct tracefs_instance pass:[*]_instance_, size_t _size_, int _cpu_);
--
DESCRIPTION
@@ -39,6 +39,15 @@ called for the top top instance.
The *tracefs_instance_exists()* function checks if an instance with the given
_name_ exists in the system.
+The *tracefs_instace_get_buffer_size()* returns the size of the ring buffer. If _cpu_
+is negative, it returns the total size of all the per CPU ring buffers, otherwise
+it returns the size of the per CPU ring buffer for _cpu_.
+
+The *tracefs_instance_set_buffer_size()* function sets the size of the ring buffer.
+If _cpu_ is negative, then it sets all the per CPU ring buffers to _size_ (note
+the total size is the number of CPUs * _size_). If _cpu_ is specified, then it only
+sets the size of the per CPU ring buffer.
+
RETURN VALUE
------------
The *tracefs_instance_get_name()* returns a string or NULL in case of the top
@@ -53,6 +62,11 @@ if the iteration was stopped by the _callback_, or -1 in case of an error.
The *tracefs_instance_exists()* returns true if an instance with the given _name_
exists in the system or false otherwise.
+The *tracefs_instance_get_buffer_size()* returns the size of the ring buffer depending on
+the _cpu_ value passed in, or -1 on error.
+
+The *tracefs_instance_set_buffer_size()* returns zero on success and -1 on error.
+
EXAMPLE
-------
[source,c]
diff --git a/Documentation/libtracefs-iterator.txt b/Documentation/libtracefs-iterator.txt
new file mode 100644
index 0000000..b971bd0
--- /dev/null
+++ b/Documentation/libtracefs-iterator.txt
@@ -0,0 +1,229 @@
+libtracefs(3)
+=============
+
+NAME
+----
+tracefs_iterate_raw_events, tracefs_iterate_stop, tracefs_follow_event, tracefs_follow_missed_events - Iterate over events in the ring buffer
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <tracefs.h>*
+
+int *tracefs_iterate_raw_events*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_,
+ cpu_set_t pass:[*]_cpus_, int _cpu_size_,
+ int (pass:[*]_callback_)(struct tep_event pass:[*], struct tep_record pass:[*], int, void pass:[*]),
+ void pass:[*]_callback_context_);
+void *tracefs_iterate_stop*(struct tracefs_instance pass:[*]_instance_);
+
+int *tracefs_follow_event*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_,
+ const char pass:[*]_system_, const char pass:[*]_event_name_,
+ int (pass:[*]_callback_)(struct tep_event pass:[*],
+ struct tep_record pass:[*],
+ int, void pass:[*]),
+ void pass:[*]_callback_data_);
+int *tracefs_follow_missed_events*(struct tracefs_instance pass:[*]_instance_,
+ int (pass:[*]_callback_)(struct tep_event pass:[*],
+ struct tep_record pass:[*],
+ int, void pass:[*]),
+ void pass:[*]_callback_data_);
+--
+
+DESCRIPTION
+-----------
+Trace iterator over raw events.
+
+The *tracefs_iterate_raw_events()* function will read the tracefs raw
+data buffers and call the specified _callback_ function for every event it
+encounters. Events are iterated in sorted order: oldest first. An initialized
+_tep_ handler is required (See *tracefs_local_events*(3)). If _instance_ is
+NULL, then the toplevel tracefs buffer is used, otherwise the buffer for
+the corresponding _instance_ is read. To filter only on a subset of CPUs,
+_cpus_ and _cpu_size_ may be set to only call _callback_ with events that
+occurred on the CPUs specified, otherwise if _cpus_ is NULL then the _callback_
+function will be called for all events, and _cpu_size_ is ignored. The
+_callback_ function will be called with the following parameters: A
+pointer to a struct tep_event that corresponds to the type of event the
+record is; The record representing the event; The CPU that the event
+occurred on; and a pointer to user specified _callback_context_. If the _callback_
+returns non-zero, the iteration stops.
+
+Use *tracefs_iterate_stop()* to force a executing *tracefs_iterate_raw_events()*
+to halt. This can be called from either a callback that is called by
+the iterator (even though a return of non-zero will stop it), or from another
+thread.
+
+The *tracefs_follow_event()* is used with *tracefs_iterate_raw_events()* but
+intead of the callback being called for every event, it is only called for the
+specified _system_ / _event_name_ given to the function. The _callback_ is the
+same as for *tracefs_iterate_raw_events()*, and the passed in _callback_context_
+will be passed to the _callback_ as well. Note, if it returns something other
+than 0, it will stop the loop before the _callback_ of *tracefs_iterate_raw_events()*
+is called.
+
+The *tracefs_follow_missed_events()* will call the _callback_ when missed
+events are detected. It will set the _record_ parameter of the callback to the
+record that came after the missed events and _event_ will be of the type of
+event _record_ is. _cpu_ will be set to the CPU that missed the events, and
+_callback_data_ will be the content that was passed in to the function.
+
+RETURN VALUE
+------------
+The *tracefs_iterate_raw_events()* function returns -1 in case of an error or
+0 otherwise.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <unistd.h>
+#include <tracefs.h>
+#include <stdbool.h>
+#include <signal.h>
+
+struct my_struct {
+ bool stopped;
+};
+
+#define MAX_COUNT 500000
+static int counter;
+
+static int callback(struct tep_event *event, struct tep_record *record,
+ int cpu, void *data)
+{
+ struct my_struct *my_data = data;
+ static struct trace_seq seq;
+
+ if (counter++ > MAX_COUNT) {
+ my_data->stopped = true;
+ return 1;
+ }
+
+ if (!seq.buffer)
+ trace_seq_init(&seq);
+
+ tep_print_event(event->tep, &seq, record, "%16s-%-5d [%03d] %6.1000d %s: %s\n",
+ TEP_PRINT_COMM, TEP_PRINT_PID, TEP_PRINT_CPU,
+ TEP_PRINT_TIME, TEP_PRINT_NAME, TEP_PRINT_INFO);
+ trace_seq_terminate(&seq);
+ trace_seq_do_printf(&seq);
+ trace_seq_reset(&seq);
+ return 0;
+}
+
+static int sched_callback(struct tep_event *event, struct tep_record *record,
+ int cpu, void *data)
+{
+ static struct tep_format_field *prev_pid;
+ static struct tep_format_field *next_pid;
+ unsigned long long pid;
+ int this_pid = *(int *)data;
+
+ if (!prev_pid) {
+ prev_pid = tep_find_field(event, "prev_pid");
+ next_pid = tep_find_field(event, "next_pid");
+ if (!prev_pid || !next_pid) {
+ fprintf(stderr, "No pid fields??\n");
+ return -1;
+ }
+ }
+
+ tep_read_number_field(prev_pid, record->data, &pid);
+ if (pid == this_pid)
+ printf("WE ARE LEAVING!\n");
+ tep_read_number_field(next_pid, record->data, &pid);
+ if (pid == this_pid)
+ printf("WE ARE ARRIVING!\n");
+ return 0;
+}
+
+static int missed_callback(struct tep_event *event, struct tep_record *record,
+ int cpu, void *data)
+{
+ printf("OOPS! cpu %d dropped ", cpu);
+ if (record->missed_events > 0)
+ printf("%lld ", record->missed_events);
+ printf("events\n");
+ return 0;
+}
+
+static struct tracefs_instance *instance;
+static struct my_struct my_data;
+
+static void sig(int s)
+{
+ tracefs_iterate_stop(instance);
+ my_data.stopped = true;
+}
+
+int main (int argc, char **argv, char **env)
+{
+ struct tep_handle *tep;
+ int this_pid = getpid();
+
+ instance = tracefs_instance_create("my-buffer");
+ if (!instance)
+ return -1;
+
+ signal(SIGINT, sig);
+
+ tracefs_event_enable(instance, NULL, NULL);
+ sleep(1);
+ tracefs_event_disable(instance, NULL, NULL);
+ tep = tracefs_local_events(NULL);
+ tep_load_plugins(tep);
+ tracefs_follow_missed_events(instance, missed_callback, NULL);
+ tracefs_follow_event(tep, instance, "sched", "sched_switch", sched_callback, &this_pid);
+ tracefs_iterate_raw_events(tep, instance, NULL, 0, callback, &my_data);
+ tracefs_instance_destroy(instance);
+
+ if (my_data.stopped) {
+ if (counter > MAX_COUNT)
+ printf("Finished max count\n");
+ else
+ printf("Finished via signal\n");
+ }
+
+ return 0;
+}
+--
+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)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>
+*Tzvetomir Stoyanov* <tz.stoyanov@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-sql.txt b/Documentation/libtracefs-sql.txt
index ba4271e..6d606db 100644
--- a/Documentation/libtracefs-sql.txt
+++ b/Documentation/libtracefs-sql.txt
@@ -195,7 +195,7 @@ That is:
[source,c]
--
- select CAST(common_pid AS comm, CAST(id AS syscall) FROM sys_enter
+ select CAST(common_pid AS comm), CAST(id AS syscall) FROM sys_enter
--
Which produces:
@@ -407,8 +407,13 @@ static int do_sql(const char *instance_name,
}
}
tracefs_synth_echo_cmd(&seq, synth);
- if (execute)
- tracefs_synth_create(synth);
+ if (execute) {
+ ret = tracefs_synth_create(synth);
+ if (ret < 0) {
+ fprintf(stderr, "%s\n", tracefs_error_last(NULL));
+ exit(-1);
+ }
+ }
} else {
struct tracefs_instance *instance = NULL;
struct tracefs_hist *hist;
@@ -430,8 +435,13 @@ static int do_sql(const char *instance_name,
}
}
tracefs_hist_echo_cmd(&seq, instance, hist, 0);
- if (execute)
- tracefs_hist_start(instance, hist);
+ if (execute) {
+ ret = tracefs_hist_start(instance, hist);
+ if (ret < 0) {
+ fprintf(stderr, "%s\n", tracefs_error_last(instance));
+ exit(-1);
+ }
+ }
}
tracefs_synth_free(synth);
diff --git a/Documentation/libtracefs-traceon.txt b/Documentation/libtracefs-traceon.txt
index 8f64928..28c79ed 100644
--- a/Documentation/libtracefs-traceon.txt
+++ b/Documentation/libtracefs-traceon.txt
@@ -62,7 +62,7 @@ EXAMPLE
ret = tracefs_trace_is_on(NULL);
if (ret == 0) {
/* Tracing is disabled in the top instance */
- } else if (ret == 1) {"
+ } else if (ret == 1) {
/* Tracing is enabled in the top instance */
} else {
/* Error getting tracing state of the top instance */
diff --git a/Documentation/libtracefs-utils.txt b/Documentation/libtracefs-utils.txt
index ddbd675..ab16cc6 100644
--- a/Documentation/libtracefs-utils.txt
+++ b/Documentation/libtracefs-utils.txt
@@ -3,8 +3,8 @@ libtracefs(3)
NAME
----
-tracefs_tracers, tracefs_get_clock, tracefs_list_free, tracefs_list_add,
-tracefs_list_size - Helper functions for working with trace file system.
+tracefs_tracers, tracefs_tracer_available, tracefs_get_clock, tracefs_list_free,
+tracefs_list_add, tracefs_list_size - Helper functions for working with trace file system.
SYNOPSIS
--------
@@ -13,6 +13,7 @@ SYNOPSIS
*#include <tracefs.h>*
char pass:[*]pass:[*]*tracefs_tracers*(const char pass:[*]_tracing_dir_);
+bool *tracefs_tracer_available*(const char pass:[*]_tracing_dir_, const char pass:[*]_tracer_);
char pass:[*]*tracefs_get_clock*(struct tracefs_instance pass:[*]_instance_);
void *tracefs_list_free*(char pass:[*]pass:[*]_list_);
char pass:[**]*tracefs_list_add*(char **_list_, const char *_string_);
@@ -30,6 +31,9 @@ for the trace systems of the local machine, or it may be a path to a copy
of the tracefs directory from another machine. The last entry in the array
as a NULL pointer. The array must be freed with *tracefs_list_free()* API.
+The *tracefs_tracer_available()* returns true if the _tracer_ is available
+in the given _tracing_dir_director_, and false otherwise.
+
The *tracefs_get_clock()* function returns name of the current trace clock,
used in the given _instance_. If _instance_ is NULL, the clock of the main
trace instance is returned. The returned string must be freed with free().
@@ -56,6 +60,9 @@ The *tracefs_tracers()* returns array of strings. The last element in that
array is a NULL pointer. The array must be freed with *tracefs_list_free()* API.
In case of an error, NULL is returned.
+The *tracefs_tracer_available()* returns true if the _tracer_ is available,
+and false otherwise.
+
The *tracefs_get_clock()* returns string, that must be freed with free(), or NULL
in case of an error.
diff --git a/Documentation/libtracefs.txt b/Documentation/libtracefs.txt
index 82cb574..c3f448d 100644
--- a/Documentation/libtracefs.txt
+++ b/Documentation/libtracefs.txt
@@ -15,6 +15,9 @@ Locations of tracing files and directories:
char pass:[*]*tracefs_get_tracing_file*(const char pass:[*]_name_);
void *tracefs_put_tracing_file*(char pass:[*]_name_);
const char pass:[*]*tracefs_tracing_dir*(void);
+ const char pass:[*]*tracefs_debug_dir*(void);
+ int *tracefs_set_tracing_dir*(char pass:[*]_tracing_dir_)
+ int *tracefs_tracing_dir_is_mounted*(bool _mount_, const char pass:[**]_path_);
Trace instances:
struct tracefs_instance pass:[*]*tracefs_instance_create*(const char pass:[*]_name_);
@@ -43,6 +46,8 @@ Trace instances:
char pass:[*]*tracefs_instance_get_affinity*(struct tracefs_instance pass:[*]_instance_);
int *tracefs_instance_get_affinity_set*(struct tracefs_instance pass:[*]_instance_, cpu_set_t pass:[*]_set_, size_t _set_size_);
char pass:[*]*tracefs_instance_get_affinity_raw*(struct tracefs_instance pass:[*]_instance_);
+ size_t *tracefs_instance_get_buffer_size*(struct tracefs_instance pass:[*]_instance_, int _cpu_);
+ int *tracefs_instance_set_buffer_size*(struct tracefs_instance pass:[*]_instance_, size_t _size_, int _cpu_);
Trace events:
char pass:[*]pass:[*]*tracefs_event_systems*(const char pass:[*]_tracing_dir_);
@@ -51,8 +56,21 @@ Trace events:
const char pass:[*]_event_);
int *tracefs_event_disable*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_,
const char pass:[*]_event_);
+ enum tracefs_enable_state *tracefs_event_is_enabled*(struct tracefs_instance pass:[*]_instance_,
+ const char pass:[*]_system_, const char pass:[*]_event_);
int *tracefs_iterate_raw_events*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_, cpu_set_t pass:[*]_cpus_, int _cpu_size_, int (pass:[*]_callback_)(struct tep_event pass:[*], struct tep_record pass:[*], int, void pass:[*]), void pass:[*]_callback_context_);
void *tracefs_iterate_stop*(struct tracefs_instance pass:[*]_instance_);
+ int *tracefs_follow_event*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_,
+ const char pass:[*]_system_, const char pass:[*]_event_name_,
+ int (pass:[*]_callback_)(struct tep_event pass:[*],
+ struct tep_record pass:[*],
+ int, void pass:[*]),
+ void pass:[*]_callback_data_);
+ int *tracefs_follow_missed_events*(struct tracefs_instance pass:[*]_instance_,
+ int (pass:[*]_callback_)(struct tep_event pass:[*],
+ struct tep_record pass:[*],
+ int, void pass:[*]),
+ void pass:[*]_callback_data_);
struct tep_handle pass:[*]*tracefs_local_events*(const char pass:[*]_tracing_dir_);
struct tep_handle pass:[*]*tracefs_local_events_system*(const char pass:[*]_tracing_dir_, const char pass:[*] const pass:[*]_sys_names_);
int *tracefs_fill_local_events*(const char pass:[*]_tracing_dir_, struct tep_handle pass:[*]_tep_, int pass:[*]_parsing_failures_);
@@ -112,6 +130,7 @@ Trace options:
Ftrace tracers:
char pass:[*]pass:[*]*tracefs_tracers*(const char pass:[*]_tracing_dir_);
+ bool *tracefs_tracer_available*(const char pass:[*]_tracing_dir_, const char pass:[*]_tracer_);
int *tracefs_tracer_set*(struct tracefs_instance pass:[*]_instance_, enum tracefs_tracers _tracer_);
int *tracefs_tracer_set*(struct tracefs_instance pass:[*]_instance_, enum tracefs_tracers _tracer_, const char pass:[*]_name_);
int *tracefs_tracer_clear*(struct tracefs_instance pass:[*]_instance_);
@@ -144,11 +163,15 @@ Dynamic event generic APIs:
Even probes (eprobes):
struct tracefs_dynevent pass:[*] *tracefs_eprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_target_system_, const char pass:[*]_target_event_, const char pass:[*]_fetchargs_);
-Kprobes and Kretprobes:
+Uprobes, Kprobes and Kretprobes:
struct tracefs_dynevent pass:[*] *tracefs_kprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_addr_, const char pass:[*]_format_);
struct tracefs_dynevent pass:[*] *tracefs_kretprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_addr_, const char pass:[*]_format_, unsigned int _max_);
int *tracefs_kprobe_raw*(const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_addr_, const char pass:[*]_format_);
int *tracefs_kretprobe_raw*(const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_addr_, const char pass:[*]_format_);
+ *tracefs_uprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_,
+ const char pass:[*]_file_, unsigned long long _offset_, const char pass:[*]_fetchargs_)
+ *tracefs_uretprobe_alloc*(const char pass:[*]_system_, const char pass:[*]_event_,
+ const char pass:[*]_file_, unsigned long long _offset_, const char pass:[*]_fetchargs_);
Synthetic events:
struct tracefs_synth pass:[*]*tracefs_sql*(struct tep_handle pass:[*]_tep_, const char pass:[*]_name_,
@@ -223,9 +246,14 @@ Histograms:
struct tracefs_hist pass:[*]*tracefs_hist_alloc_nd*(struct tracefs_tep pass:[*] _tep_,
const char pass:[*]_system_, const char pass:[*]_event_,
struct tracefs_hist_axis pass:[*]_axes_);
+ struct tracefs_hist pass:[*]*tracefs_hist_alloc_nd_cnt*(struct tep_handle pass:[*]_tep_,
+ const char pass:[*]_system_, const char pass:[*]_event_name_,
+ struct tracefs_hist_axis_cnt pass:[*]_axes_);
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_key_cnt*(struct tracefs_hist pass:[*]_hist_, const char pass:[*]_key_,
+ enum tracefs_hist_key_type _type_, int _cnt_);
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_);
@@ -255,6 +283,21 @@ Histograms:
int *tracefs_hist_continue*(struct tracefs_instance pass:[*]_instance_, struct tracefs_hist pass:[*]_hist_);
int *tracefs_hist_reset*(struct tracefs_instance pass:[*]_instance_, struct tracefs_hist pass:[*]_hist_);
+Recording of trace_pipe_raw files:
+ struct tracefs_cpu pass:[*]*tracefs_cpu_open*(struct tracefs_instance pass:[*]_instance_,
+ int _cpu_, bool _nonblock_);
+ struct tracefs_cpu pass:[*]*tracefs_cpu_alloc_fd*(int _fd_, int _subbuf_size_, bool _nonblock_);
+ void *tracefs_cpu_close*(struct tracefs_cpu pass:[*]_tcpu_);
+ void *tracefs_cpu_free_fd*(struct tracefs_cpu pass:[*]_tcpu_);
+ int *tracefs_cpu_read_size*(struct tracefs_cpu pass:[*]_tcpu_);
+ int *tracefs_cpu_read*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_, bool _nonblock_);
+ int *tracefs_cpu_buffered_read*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_, bool _nonblock_);
+ int *tracefs_cpu_write*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_, bool _nonblock_);
+ int *tracefs_cpu_stop*(struct tracefs_cpu pass:[*]_tcpu_);
+ int *tracefs_cpu_flush*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_);
+ int *tracefs_cpu_flush_write*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_);
+ int *tracefs_cpu_pipe*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_, bool _nonblock_);
+
--
DESCRIPTION
diff --git a/METADATA b/METADATA
index 7f1f1f5..33cda28 100644
--- a/METADATA
+++ b/METADATA
@@ -1,14 +1,19 @@
-name: "libtracefs"
-description:
- "libtracefs is a library that provides APIs to access to the Linux kernel "
- "tracefs file system"
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update libtracefs
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+name: "libtracefs"
+description: "libtracefs is a library that provides APIs to access to the Linux kernel tracefs file system"
third_party {
url {
type: GIT
value: "https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git"
}
- version: "libtracefs-1.3.1"
- last_upgrade_date { year: 2022 month: 5 day: 02 }
+ version: "libtracefs-1.6.4"
license_type: RESTRICTED
+ last_upgrade_date {
+ year: 2023
+ month: 1
+ day: 18
+ }
}
diff --git a/Makefile b/Makefile
index 80b8176..61ed976 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: LGPL-2.1
# libtracefs version
TFS_VERSION = 1
-TFS_PATCHLEVEL = 4
-TFS_EXTRAVERSION = dev
+TFS_PATCHLEVEL = 6
+TFS_EXTRAVERSION = 4
TRACEFS_VERSION = $(TFS_VERSION).$(TFS_PATCHLEVEL).$(TFS_EXTRAVERSION)
export TFS_VERSION
@@ -52,12 +52,12 @@ endif
libdir_relative ?= $(libdir_relative_temp)
prefix ?= /usr/local
-man_dir = $(prefix)/share/man
+man_dir ?= $(prefix)/share/man
man_dir_SQ = '$(subst ','\'',$(man_dir))'
-libdir = $(prefix)/$(libdir_relative)
+libdir ?= $(prefix)/$(libdir_relative)
libdir_SQ = '$(subst ','\'',$(libdir))'
includedir_relative ?= include/tracefs
-includedir = $(prefix)/$(includedir_relative)
+includedir ?= $(prefix)/$(includedir_relative)
includedir_SQ = '$(subst ','\'',$(includedir))'
pkgconfig_dir ?= $(word 1,$(shell $(PKG_CONFIG) \
--variable pc_path pkg-config | tr ":" " "))
@@ -132,7 +132,8 @@ LIBTRACEFS_SHARED_VERSION = $(bdir)/libtracefs.so.$(TFS_VERSION)
PKG_CONFIG_SOURCE_FILE = libtracefs.pc
PKG_CONFIG_FILE := $(addprefix $(obj)/,$(PKG_CONFIG_SOURCE_FILE))
-LIBS = $(LIBTRACEEVENT_LIBS) -lpthread
+LPTHREAD ?= -lpthread
+LIBS = $(LIBTRACEEVENT_LIBS) $(LPTHREAD)
export LIBS
export LIBTRACEFS_STATIC LIBTRACEFS_SHARED
@@ -163,6 +164,9 @@ export INCLUDES
# Append required CFLAGS
override CFLAGS += -D_GNU_SOURCE $(LIBTRACEEVENT_INCLUDES) $(INCLUDES)
+# Make sure 32 bit stat() works on large file systems
+override CFLAGS += -D_FILE_OFFSET_BITS=64
+
all: all_cmd
LIB_TARGET = libtracefs.a libtracefs.so.$(TRACEFS_VERSION)
@@ -330,7 +334,6 @@ OBJS += tracefs-instance.o
OBJS += tracefs-events.o
OBJS := $(OBJS:%.o=$(bdir)/%.o)
-DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)
all: $(DEFAULT_TARGET)
@@ -388,3 +391,7 @@ clean:
$(BUILD_PREFIX))
.PHONY: clean
+
+# libtracefs.a and libtracefs.so would concurrently enter the same directory -
+# a recipe for collisions.
+.NOTPARALLEL:
diff --git a/check-manpages.sh b/check-manpages.sh
index d3ec6af..776365c 100755
--- a/check-manpages.sh
+++ b/check-manpages.sh
@@ -15,12 +15,19 @@ cd $1
MAIN=libtracefs
MAIN_FILE=${MAIN}.txt
+PROCESSED=""
+
# Ignore man pages that do not contain functions
IGNORE="libtracefs-options.txt"
for man in ${MAIN}-*.txt; do
- sed -ne '/^NAME/,/^SYNOP/{/^[a-z]/{s/, *$//;s/,/\n/g;s/ //g;s/-.*$/-/;/-/{s/-//p;q};p}}' $man | while read a; do
+ for a in `sed -ne '/^NAME/,/^SYNOP/{/^[a-z]/{s/, *$//;s/,/\n/g;s/ //g;s/-.*$/-/;/-/{s/-//p;q};p}}' $man`; do
+ if [ "${PROCESSED/:${a} /}" != "${PROCESSED}" ]; then
+ P="${PROCESSED/:${a} */}"
+ echo "Found ${a} in ${man} and in ${P/* /}"
+ fi
+ PROCESSED="${man}:${a} ${PROCESSED}"
if [ "${IGNORE/$man/}" != "${IGNORE}" ]; then
continue
fi
diff --git a/include/tracefs-local.h b/include/tracefs-local.h
index 926fd02..2007d26 100644
--- a/include/tracefs-local.h
+++ b/include/tracefs-local.h
@@ -6,6 +6,8 @@
#ifndef _TRACE_FS_LOCAL_H
#define _TRACE_FS_LOCAL_H
+#include <pthread.h>
+
#define __hidden __attribute__((visibility ("hidden")))
#define __weak __attribute__((weak))
@@ -15,13 +17,25 @@
#define BUILD_BUG_ON(cond) \
do { if (!(1/!(cond))) { } } while (0)
+#define HASH_BITS 10
+
struct tracefs_options_mask {
unsigned long long mask;
};
+struct follow_event {
+ struct tep_event *event;
+ void *callback_data;
+ int (*callback)(struct tep_event *,
+ struct tep_record *,
+ int, void *);
+};
+
struct tracefs_instance {
struct tracefs_options_mask supported_opts;
struct tracefs_options_mask enabled_opts;
+ struct follow_event *followers;
+ struct follow_event *missed_followers;
char *trace_dir;
char *name;
pthread_mutex_t lock;
@@ -31,6 +45,8 @@ struct tracefs_instance {
int ftrace_notrace_fd;
int ftrace_marker_fd;
int ftrace_marker_raw_fd;
+ int nr_followers;
+ int nr_missed_followers;
bool pipe_keep_going;
bool iterate_keep_going;
};
@@ -50,7 +66,7 @@ void tracefs_warning(const char *fmt, ...);
int str_read_file(const char *file, char **buffer, bool warn);
char *trace_append_file(const char *dir, const char *name);
-char *trace_find_tracing_dir(void);
+char *trace_find_tracing_dir(bool debugfs);
#ifndef ACCESSPERMS
#define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO) /* 0777 */
@@ -94,7 +110,7 @@ struct tracefs_synth *synth_init_from(struct tep_handle *tep,
int synth_add_start_field(struct tracefs_synth *synth,
const char *start_field,
const char *name,
- enum tracefs_hist_key_type type);
+ enum tracefs_hist_key_type type, int cnt);
/* Internal interface for ftrace dynamic events */
@@ -120,4 +136,6 @@ int trace_rescan_events(struct tep_handle *tep,
struct tep_event *get_tep_event(struct tep_handle *tep,
const char *system, const char *name);
+unsigned int quick_hash(const char *str);
+
#endif /* _TRACE_FS_LOCAL_H */
diff --git a/include/tracefs.h b/include/tracefs.h
index 27954a4..3547b5a 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -15,6 +15,9 @@ void tracefs_put_tracing_file(char *name);
/* the returned string must *not* be freed */
const char *tracefs_tracing_dir(void);
+const char *tracefs_debug_dir(void);
+int tracefs_set_tracing_dir(char *tracing_dir);
+int tracefs_tracing_dir_is_mounted(bool mount, const char **path);
/* ftrace instances */
struct tracefs_instance;
@@ -53,6 +56,8 @@ char *tracefs_instance_get_affinity(struct tracefs_instance *instance);
char *tracefs_instance_get_affinity_raw(struct tracefs_instance *instance);
int tracefs_instance_get_affinity_set(struct tracefs_instance *instance,
cpu_set_t *set, size_t set_size);
+ssize_t tracefs_instance_get_buffer_size(struct tracefs_instance *instance, int cpu);
+int tracefs_instance_set_buffer_size(struct tracefs_instance *instance, size_t size, int cpu);
char **tracefs_instances(const char *regex);
bool tracefs_instance_exists(const char *name);
@@ -65,8 +70,17 @@ int tracefs_trace_off(struct tracefs_instance *instance);
int tracefs_trace_on_fd(int fd);
int tracefs_trace_off_fd(int fd);
+enum tracefs_enable_state {
+ TRACEFS_ERROR = -1,
+ TRACEFS_ALL_DISABLED = 0,
+ TRACEFS_ALL_ENABLED = 1,
+ TRACEFS_SOME_ENABLED = 2,
+};
+
int tracefs_event_enable(struct tracefs_instance *instance, const char *system, const char *event);
int tracefs_event_disable(struct tracefs_instance *instance, const char *system, const char *event);
+enum tracefs_enable_state tracefs_event_is_enabled(struct tracefs_instance *instance,
+ const char *system, const char *event);
char *tracefs_error_last(struct tracefs_instance *instance);
char *tracefs_error_all(struct tracefs_instance *instance);
@@ -76,6 +90,8 @@ void tracefs_list_free(char **list);
char **tracefs_list_add(char **list, const char *string);
int tracefs_list_size(char **list);
+bool tracefs_tracer_available(const char *tracing_dir, const char *tracer);
+
/**
* tracefs_trace_on_get_fd - Get a file descriptor of "tracing_on" in given instance
* @instance: ftrace instance, can be NULL for the top instance
@@ -110,6 +126,17 @@ int tracefs_iterate_raw_events(struct tep_handle *tep,
int, void *),
void *callback_context);
void tracefs_iterate_stop(struct tracefs_instance *instance);
+int tracefs_follow_event(struct tep_handle *tep, struct tracefs_instance *instance,
+ const char *system, const char *event_name,
+ int (*callback)(struct tep_event *,
+ struct tep_record *,
+ int, void *),
+ void *callback_data);
+int tracefs_follow_missed_events(struct tracefs_instance *instance,
+ int (*callback)(struct tep_event *,
+ struct tep_record *,
+ int, void *),
+ void *callback_data);
char *tracefs_event_get_file(struct tracefs_instance *instance,
const char *system, const char *event,
@@ -306,6 +333,7 @@ enum tracefs_hist_key_type {
TRACEFS_HIST_KEY_EXECNAME,
TRACEFS_HIST_KEY_LOG,
TRACEFS_HIST_KEY_USECS,
+ TRACEFS_HIST_KEY_BUCKETS,
TRACEFS_HIST_KEY_MAX
};
@@ -368,15 +396,27 @@ struct tracefs_hist_axis {
enum tracefs_hist_key_type type;
};
+struct tracefs_hist_axis_cnt {
+ const char *key;
+ enum tracefs_hist_key_type type;
+ int cnt;
+};
+
struct tracefs_hist *
tracefs_hist_alloc_nd(struct tep_handle *tep,
const char *system, const char *event_name,
struct tracefs_hist_axis *axes);
+struct tracefs_hist *
+tracefs_hist_alloc_nd_cnt(struct tep_handle *tep,
+ const char *system, const char *event_name,
+ struct tracefs_hist_axis_cnt *axes);
const char *tracefs_hist_get_name(struct tracefs_hist *hist);
const char *tracefs_hist_get_event(struct tracefs_hist *hist);
const char *tracefs_hist_get_system(struct tracefs_hist *hist);
int tracefs_hist_add_key(struct tracefs_hist *hist, const char *key,
enum tracefs_hist_key_type type);
+int tracefs_hist_add_key_cnt(struct tracefs_hist *hist, const char *key,
+ enum tracefs_hist_key_type type, int cnt);
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);
@@ -578,4 +618,20 @@ struct tracefs_synth *tracefs_sql(struct tep_handle *tep, const char *name,
struct tep_event *
tracefs_synth_get_event(struct tep_handle *tep, struct tracefs_synth *synth);
+struct tracefs_cpu;
+
+struct tracefs_cpu *tracefs_cpu_alloc_fd(int fd, int subbuf_size, bool nonblock);
+struct tracefs_cpu *tracefs_cpu_open(struct tracefs_instance *instance,
+ int cpu, bool nonblock);
+void tracefs_cpu_close(struct tracefs_cpu *tcpu);
+void tracefs_cpu_free_fd(struct tracefs_cpu *tcpu);
+int tracefs_cpu_read_size(struct tracefs_cpu *tcpu);
+int tracefs_cpu_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock);
+int tracefs_cpu_buffered_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock);
+int tracefs_cpu_write(struct tracefs_cpu *tcpu, int wfd, bool nonblock);
+int tracefs_cpu_stop(struct tracefs_cpu *tcpu);
+int tracefs_cpu_flush(struct tracefs_cpu *tcpu, void *buffer);
+int tracefs_cpu_flush_write(struct tracefs_cpu *tcpu, int wfd);
+int tracefs_cpu_pipe(struct tracefs_cpu *tcpu, int wfd, bool nonblock);
+
#endif /* _TRACE_FS_H */
diff --git a/samples/Makefile b/samples/Makefile
index 97a570d..743bddb 100644
--- a/samples/Makefile
+++ b/samples/Makefile
@@ -21,6 +21,7 @@ EXAMPLES += hist-cont
EXAMPLES += tracer
EXAMPLES += stream
EXAMPLES += instances-affinity
+EXAMPLES += cpu
TARGETS :=
TARGETS += sqlhist
@@ -46,7 +47,7 @@ sqlhist: $(sdir)/sqlhist
$(TARGETS): $(sdir)
# sqlhist is unique and stands on its own
-$(sdir)/sqlhist: $(bdir)/sqlhist.c
+$(sdir)/sqlhist: $(bdir)/sqlhist.c $(LIBTRACEFS_STATIC)
$(call do_sample_build,$@,$<)
$(sdir)/%: $(bdir)/%.o
@@ -65,5 +66,10 @@ $(EXAMPLES): $(patsubst %,$(sdir)/%,$(TARGETS))
$(bdir)/%.o: $(bdir)/%.c
$(call do_sample_obj,$@,$^)
+$(bdir)/XX.o: $(bdir)/hist.c
+ $(CC) -g -Wall $(CFLAGS) -c -o $@ $^ -I../include/ $(LIBTRACEEVENT_INCLUDES)
+
clean:
$(Q)$(call do_clean,$(sdir)/* $(bdir)/sqlhist.c $(bdir)/sqlhist.o)
+
+.PHONY: sqlhist
diff --git a/scripts/utils.mk b/scripts/utils.mk
index 5f43de1..4d0f8bc 100644
--- a/scripts/utils.mk
+++ b/scripts/utils.mk
@@ -53,12 +53,12 @@ endif
do_fpic_compile = \
($(print_fpic_compile) \
- $(CC) -c $(CPPFLAGS) $(CFLAGS) $(EXT) -fPIC $< -o $@)
+ $(CC) -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@ -MP -c $(CPPFLAGS) $(CFLAGS) $(EXT) -fPIC $< -o $@)
do_compile = \
($(if $(GENERATE_PIC), $(do_fpic_compile), \
$(print_compile) \
- $(CC) -c $(CPPFLAGS) $(CFLAGS) $(EXT) $< -o $@))
+ $(CC) -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@ -MP -c $(CPPFLAGS) $(CFLAGS) $(EXT) $< -o $@))
do_app_build = \
($(print_app_build) \
@@ -101,7 +101,7 @@ extract_example = \
do_sample_build = \
$(Q)($(print_sample_build) \
- $(CC) -o $1 $2 $(CFLAGS) $(LIBTRACEFS_STATIC) $(LIBTRACEEVENT_LIBS))
+ $(CC) -o $1 $2 $(CFLAGS) $(LIBTRACEFS_STATIC) $(LIBTRACEEVENT_LIBS) -lpthread)
do_sample_obj = \
$(Q)($(print_sample_obj) \
diff --git a/src/Makefile b/src/Makefile
index 645d518..e2965bc 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -14,6 +14,7 @@ OBJS += tracefs-filter.o
OBJS += tracefs-dynevents.o
OBJS += tracefs-eprobes.o
OBJS += tracefs-uprobes.o
+OBJS += tracefs-record.o
# Order matters for the the three below
OBJS += sqlhist-lex.o
@@ -21,7 +22,6 @@ OBJS += sqlhist.tab.o
OBJS += tracefs-sqlhist.o
OBJS := $(OBJS:%.o=$(bdir)/%.o)
-DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)
$(LIBTRACEFS_STATIC): $(OBJS)
$(Q)$(call do_build_static_lib)
@@ -48,24 +48,14 @@ sqlhist-lex.c: sqlhist.l sqlhist.tab.c
$(bdir)/%.o: %.c
$(Q)$(call do_fpic_compile)
-$(DEPS): $(bdir)/.%.d: %.c
- $(Q)$(CC) -M -MT $(bdir)/$*.o $(CPPFLAGS) $(CFLAGS) $< > $@
-
-$(OBJS): $(bdir)/%.o : $(bdir)/.%.d
-
tracefs-sqlhist.o: sqlhist.tab.h
$(OBJS): | $(bdir)
-$(DEPS): | $(bdir)
clean:
$(Q)$(call do_clean,$(OBJS) .*.d)
-dep_includes := $(wildcard $(DEPS))
-
-ifneq ($(dep_includes),)
- include $(dep_includes)
-endif
+-include .*.d
$(bdir)/tracefs-sqlhist.o tracefs-sqlhist.o: sqlhist.tab.h
diff --git a/src/sqlhist-parse.h b/src/sqlhist-parse.h
index d5b86ca..7bd2a63 100644
--- a/src/sqlhist-parse.h
+++ b/src/sqlhist-parse.h
@@ -4,8 +4,9 @@
#include <stdarg.h>
#include <tracefs.h>
+#include <tracefs-local.h>
+
struct str_hash;
-#define HASH_BITS 10
struct sql_table;
diff --git a/src/tracefs-dynevents.c b/src/tracefs-dynevents.c
index 48bb26a..7a3c45c 100644
--- a/src/tracefs-dynevents.c
+++ b/src/tracefs-dynevents.c
@@ -713,9 +713,6 @@ dynevent_info(struct tracefs_dynevent *dynevent, char **system,
&dynevent->address, &dynevent->format };
int i;
- if (!dynevent)
- return TRACEFS_DYNEVENT_UNKNOWN;
-
for (i = 0; i < ARRAY_SIZE(lv); i++) {
if (lv[i]) {
if (*rv[i]) {
diff --git a/src/tracefs-events.c b/src/tracefs-events.c
index 65d5707..c2adf41 100644
--- a/src/tracefs-events.c
+++ b/src/tracefs-events.c
@@ -20,15 +20,20 @@
#include "tracefs.h"
#include "tracefs-local.h"
+static struct follow_event *root_followers;
+static int nr_root_followers;
+
+static struct follow_event *root_missed_followers;
+static int nr_root_missed_followers;
+
struct cpu_iterate {
+ struct tracefs_cpu *tcpu;
struct tep_record record;
struct tep_event *event;
struct kbuffer *kbuf;
void *page;
int psize;
- int rsize;
int cpu;
- int fd;
};
static int read_kbuf_record(struct cpu_iterate *cpu)
@@ -46,6 +51,7 @@ static int read_kbuf_record(struct cpu_iterate *cpu)
cpu->record.ts = ts;
cpu->record.size = kbuffer_event_size(cpu->kbuf);
cpu->record.record_size = kbuffer_curr_size(cpu->kbuf);
+ cpu->record.missed_events = kbuffer_missed_events(cpu->kbuf);
cpu->record.cpu = cpu->cpu;
cpu->record.data = ptr;
cpu->record.ref_count = 1;
@@ -59,9 +65,21 @@ int read_next_page(struct tep_handle *tep, struct cpu_iterate *cpu)
{
enum kbuffer_long_size long_size;
enum kbuffer_endian endian;
+ int r;
- cpu->rsize = read(cpu->fd, cpu->page, cpu->psize);
- if (cpu->rsize <= 0)
+ if (!cpu->tcpu)
+ return -1;
+
+ r = tracefs_cpu_buffered_read(cpu->tcpu, cpu->page, true);
+ /*
+ * tracefs_cpu_buffered_read() only reads in full subbuffer size,
+ * but this wants partial buffers as well. If the function returns
+ * empty (-1 for EAGAIN), try tracefs_cpu_read() next, as that can
+ * read partially filled buffers too, but isn't as efficient.
+ */
+ if (r <= 0)
+ r = tracefs_cpu_read(cpu->tcpu, cpu->page, true);
+ if (r <= 0)
return -1;
if (!cpu->kbuf) {
@@ -81,8 +99,8 @@ int read_next_page(struct tep_handle *tep, struct cpu_iterate *cpu)
}
kbuffer_load_subbuffer(cpu->kbuf, cpu->page);
- if (kbuffer_subbuffer_size(cpu->kbuf) > cpu->rsize) {
- tracefs_warning("%s: page_size > %d", __func__, cpu->rsize);
+ if (kbuffer_subbuffer_size(cpu->kbuf) > r) {
+ tracefs_warning("%s: page_size > %d", __func__, r);
return -1;
}
@@ -105,7 +123,122 @@ int read_next_record(struct tep_handle *tep, struct cpu_iterate *cpu)
return -1;
}
-static int read_cpu_pages(struct tep_handle *tep, struct cpu_iterate *cpus, int count,
+/**
+ * tracefs_follow_missed_events - Add callback for missed events for iterators
+ * @instance: The instance to follow
+ * @callback: The function to call when missed events is detected
+ * @callback_data: The data to pass to @callback
+ *
+ * This attaches a callback to an @instance or the root instance if @instance
+ * is NULL, where if tracefs_iterate_raw_events() is called, that if missed
+ * events are detected, it will call @callback, with the following parameters:
+ * @event: The event pointer of the record with the missing events
+ * @record; The event instance of @event.
+ * @cpu: The cpu that the event happened on.
+ * @callback_data: The same as @callback_data passed to the function.
+ *
+ * If the count of missing events is available, @record->missed_events
+ * will have a positive number holding the number of missed events since
+ * the last event on the same CPU, or just -1 if that number is unknown
+ * but missed events did happen.
+ *
+ * Returns 0 on success and -1 on error.
+ */
+int tracefs_follow_missed_events(struct tracefs_instance *instance,
+ int (*callback)(struct tep_event *,
+ struct tep_record *,
+ int, void *),
+ void *callback_data)
+{
+ struct follow_event **followers;
+ struct follow_event *follower;
+ struct follow_event follow;
+ int *nr_followers;
+
+ follow.event = NULL;
+ follow.callback = callback;
+ follow.callback_data = callback_data;
+
+ if (instance) {
+ followers = &instance->missed_followers;
+ nr_followers = &instance->nr_missed_followers;
+ } else {
+ followers = &root_missed_followers;
+ nr_followers = &nr_root_missed_followers;
+ }
+ follower = realloc(*followers, sizeof(*follower) *
+ ((*nr_followers) + 1));
+ if (!follower)
+ return -1;
+
+ *followers = follower;
+ follower[(*nr_followers)++] = follow;
+
+ return 0;
+}
+
+static int call_missed_events(struct tracefs_instance *instance,
+ struct tep_event *event, struct tep_record *record, int cpu)
+{
+ struct follow_event *followers;
+ int nr_followers;
+ int ret = 0;
+ int i;
+
+ if (instance) {
+ followers = instance->missed_followers;
+ nr_followers = instance->nr_missed_followers;
+ } else {
+ followers = root_missed_followers;
+ nr_followers = nr_root_missed_followers;
+ }
+
+ if (!followers)
+ return 0;
+
+ for (i = 0; i < nr_followers; i++) {
+ ret |= followers[i].callback(event, record,
+ cpu, followers[i].callback_data);
+ }
+
+ return ret;
+}
+
+static int call_followers(struct tracefs_instance *instance,
+ struct tep_event *event, struct tep_record *record, int cpu)
+{
+ struct follow_event *followers;
+ int nr_followers;
+ int ret = 0;
+ int i;
+
+ if (record->missed_events)
+ ret = call_missed_events(instance, event, record, cpu);
+ if (ret)
+ return ret;
+
+ if (instance) {
+ followers = instance->followers;
+ nr_followers = instance->nr_followers;
+ } else {
+ followers = root_followers;
+ nr_followers = nr_root_followers;
+ }
+
+ if (!followers)
+ return 0;
+
+ for (i = 0; i < nr_followers; i++) {
+ if (followers[i].event == event)
+ ret |= followers[i].callback(event, record,
+ cpu, followers[i].callback_data);
+ }
+
+ return ret;
+}
+
+static int read_cpu_pages(struct tep_handle *tep, struct tracefs_instance *instance,
+ struct cpu_iterate *cpus, int count,
int (*callback)(struct tep_event *,
struct tep_record *,
int, void *),
@@ -131,7 +264,10 @@ static int read_cpu_pages(struct tep_handle *tep, struct cpu_iterate *cpus, int
j = i;
}
if (j < count) {
- if (callback(cpus[j].event, &cpus[j].record, cpus[j].cpu, callback_context))
+ if (call_followers(instance, cpus[j].event, &cpus[j].record, cpus[j].cpu))
+ break;
+ if (callback &&
+ callback(cpus[j].event, &cpus[j].record, cpus[j].cpu, callback_context))
break;
cpus[j].event = NULL;
read_next_record(tep, cpus + j);
@@ -146,64 +282,114 @@ static int read_cpu_pages(struct tep_handle *tep, struct cpu_iterate *cpus, int
static int open_cpu_files(struct tracefs_instance *instance, cpu_set_t *cpus,
int cpu_size, struct cpu_iterate **all_cpus, int *count)
{
+ struct tracefs_cpu *tcpu;
struct cpu_iterate *tmp;
- unsigned int p_size;
- struct dirent *dent;
- char file[PATH_MAX];
- struct stat st;
- int ret = -1;
- int fd = -1;
- char *path;
- DIR *dir;
+ int nr_cpus;
int cpu;
int i = 0;
- path = tracefs_instance_get_file(instance, "per_cpu");
- if (!path)
- return -1;
- dir = opendir(path);
- if (!dir)
- goto out;
- p_size = getpagesize();
- while ((dent = readdir(dir))) {
- const char *name = dent->d_name;
+ *all_cpus = NULL;
- if (strlen(name) < 4 || strncmp(name, "cpu", 3) != 0)
- continue;
- cpu = atoi(name + 3);
+ nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
+ for (cpu = 0; cpu < nr_cpus; cpu++) {
if (cpus && !CPU_ISSET_S(cpu, cpu_size, cpus))
continue;
- sprintf(file, "%s/%s", path, name);
- if (stat(file, &st) < 0 || !S_ISDIR(st.st_mode))
- continue;
-
- sprintf(file, "%s/%s/trace_pipe_raw", path, name);
- fd = open(file, O_RDONLY | O_NONBLOCK);
- if (fd < 0)
- continue;
- tmp = realloc(*all_cpus, (i + 1) * sizeof(struct cpu_iterate));
+ tcpu = tracefs_cpu_open(instance, cpu, true);
+ tmp = realloc(*all_cpus, (i + 1) * sizeof(*tmp));
if (!tmp) {
- close(fd);
- goto out;
+ i--;
+ goto error;
}
- memset(tmp + i, 0, sizeof(struct cpu_iterate));
- tmp[i].fd = fd;
- tmp[i].cpu = cpu;
- tmp[i].page = malloc(p_size);
- tmp[i].psize = p_size;
+
*all_cpus = tmp;
- *count = i + 1;
+
+ memset(tmp + i, 0, sizeof(*tmp));
+
+ if (!tcpu)
+ goto error;
+
+ tmp[i].tcpu = tcpu;
+ tmp[i].cpu = cpu;
+ tmp[i].psize = tracefs_cpu_read_size(tcpu);
+ tmp[i].page = malloc(tmp[i].psize);
+
if (!tmp[i++].page)
- goto out;
+ goto error;
+ }
+ *count = i;
+ return 0;
+ error:
+ tmp = *all_cpus;
+ for (; i >= 0; i--) {
+ tracefs_cpu_close(tmp[i].tcpu);
+ free(tmp[i].page);
}
+ free(tmp);
+ *all_cpus = NULL;
+ return -1;
+}
- ret = 0;
+/**
+ * tracefs_follow_event - Add callback for specific events for iterators
+ * @tep: a handle to the trace event parser context
+ * @instance: The instance to follow
+ * @system: The system of the event to track
+ * @event_name: The name of the event to track
+ * @callback: The function to call when the event is hit in an iterator
+ * @callback_data: The data to pass to @callback
+ *
+ * This attaches a callback to an @instance or the root instance if @instance
+ * is NULL, where if tracefs_iterate_raw_events() is called, that if the specified
+ * event is hit, it will call @callback, with the following parameters:
+ * @event: The event pointer that was found by @system and @event_name.
+ * @record; The event instance of @event.
+ * @cpu: The cpu that the event happened on.
+ * @callback_data: The same as @callback_data passed to the function.
+ *
+ * Returns 0 on success and -1 on error.
+ */
+int tracefs_follow_event(struct tep_handle *tep, struct tracefs_instance *instance,
+ const char *system, const char *event_name,
+ int (*callback)(struct tep_event *,
+ struct tep_record *,
+ int, void *),
+ void *callback_data)
+{
+ struct follow_event **followers;
+ struct follow_event *follower;
+ struct follow_event follow;
+ int *nr_followers;
-out:
- if (dir)
- closedir(dir);
- tracefs_put_tracing_file(path);
- return ret;
+ if (!tep) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ follow.event = tep_find_event_by_name(tep, system, event_name);
+ if (!follow.event) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ follow.callback = callback;
+ follow.callback_data = callback_data;
+
+ if (instance) {
+ followers = &instance->followers;
+ nr_followers = &instance->nr_followers;
+ } else {
+ followers = &root_followers;
+ nr_followers = &nr_root_followers;
+ }
+ follower = realloc(*followers, sizeof(*follower) *
+ ((*nr_followers) + 1));
+ if (!follower)
+ return -1;
+
+ *followers = follower;
+ follower[(*nr_followers)++] = follow;
+
+ return 0;
}
static bool top_iterate_keep_going;
@@ -233,22 +419,30 @@ int tracefs_iterate_raw_events(struct tep_handle *tep,
int, void *),
void *callback_context)
{
- bool *keep_going = instance ? &instance->pipe_keep_going :
+ bool *keep_going = instance ? &instance->iterate_keep_going :
&top_iterate_keep_going;
- struct cpu_iterate *all_cpus = NULL;
+ struct follow_event *followers;
+ struct cpu_iterate *all_cpus;
int count = 0;
int ret;
int i;
(*(volatile bool *)keep_going) = true;
- if (!tep || !callback)
+ if (!tep)
+ return -1;
+
+ if (instance)
+ followers = instance->followers;
+ else
+ followers = root_followers;
+ if (!callback && !followers)
return -1;
ret = open_cpu_files(instance, cpus, cpu_size, &all_cpus, &count);
if (ret < 0)
goto out;
- ret = read_cpu_pages(tep, all_cpus, count,
+ ret = read_cpu_pages(tep, instance, all_cpus, count,
callback, callback_context,
keep_going);
@@ -256,7 +450,7 @@ out:
if (all_cpus) {
for (i = 0; i < count; i++) {
kbuffer_free(all_cpus[i].kbuf);
- close(all_cpus[i].fd);
+ tracefs_cpu_close(all_cpus[i].tcpu);
free(all_cpus[i].page);
}
free(all_cpus);
@@ -945,6 +1139,40 @@ out:
return ret;
}
+static void set_tep_cpus(const char *tracing_dir, struct tep_handle *tep)
+{
+ struct stat st;
+ char path[PATH_MAX];
+ int cpus = sysconf(_SC_NPROCESSORS_CONF);
+ int max_cpu = 0;
+ int ret;
+ int i;
+
+ if (!tracing_dir)
+ tracing_dir = tracefs_tracing_dir();
+
+ /*
+ * Paranoid: in case sysconf() above does not work.
+ * And we also only care about the number of tracing
+ * buffers that exist. If cpus is 32, but the top half
+ * is offline, there may only be 16 tracing buffers.
+ * That's what we want to know.
+ */
+ for (i = 0; !cpus || i < cpus; i++) {
+ snprintf(path, PATH_MAX, "%s/per_cpu/cpu%d", tracing_dir, i);
+ ret = stat(path, &st);
+ if (!ret && S_ISDIR(st.st_mode))
+ max_cpu = i + 1;
+ else if (i >= cpus)
+ break;
+ }
+
+ if (!max_cpu)
+ max_cpu = cpus;
+
+ tep_set_cpus(tep, max_cpu);
+}
+
/**
* tracefs_local_events_system - create a tep from the events of the specified subsystem.
*
@@ -969,6 +1197,11 @@ struct tep_handle *tracefs_local_events_system(const char *tracing_dir,
tep = NULL;
}
+ set_tep_cpus(tracing_dir, tep);
+
+ /* Set the long size for this tep handle */
+ tep_set_long_size(tep, tep_get_header_page_size(tep));
+
return tep;
}
@@ -1004,9 +1237,68 @@ static bool match(const char *str, regex_t *re)
return regexec(re, str, 0, NULL, 0) == 0;
}
+enum event_state {
+ STATE_INIT,
+ STATE_ENABLED,
+ STATE_DISABLED,
+ STATE_MIXED,
+ STATE_ERROR,
+};
+
+static int read_event_state(struct tracefs_instance *instance, const char *file,
+ enum event_state *state)
+{
+ char *val;
+ int ret = 0;
+
+ if (*state == STATE_ERROR)
+ return -1;
+
+ val = tracefs_instance_file_read(instance, file, NULL);
+ if (!val)
+ return -1;
+
+ switch (val[0]) {
+ case '0':
+ switch (*state) {
+ case STATE_INIT:
+ *state = STATE_DISABLED;
+ break;
+ case STATE_ENABLED:
+ *state = STATE_MIXED;
+ break;
+ default:
+ break;
+ }
+ break;
+ case '1':
+ switch (*state) {
+ case STATE_INIT:
+ *state = STATE_ENABLED;
+ break;
+ case STATE_DISABLED:
+ *state = STATE_MIXED;
+ break;
+ default:
+ break;
+ }
+ break;
+ case 'X':
+ *state = STATE_MIXED;
+ break;
+ default:
+ *state = TRACEFS_ERROR;
+ ret = -1;
+ break;
+ }
+ free(val);
+
+ return ret;
+}
+
static int enable_disable_event(struct tracefs_instance *instance,
const char *system, const char *event,
- bool enable)
+ bool enable, enum event_state *state)
{
const char *str = enable ? "1" : "0";
char *system_event;
@@ -1016,14 +1308,18 @@ static int enable_disable_event(struct tracefs_instance *instance,
if (ret < 0)
return ret;
- ret = tracefs_instance_file_write(instance, system_event, str);
+ if (state)
+ ret = read_event_state(instance, system_event, state);
+ else
+ ret = tracefs_instance_file_write(instance, system_event, str);
free(system_event);
return ret;
}
static int enable_disable_system(struct tracefs_instance *instance,
- const char *system, bool enable)
+ const char *system, bool enable,
+ enum event_state *state)
{
const char *str = enable ? "1" : "0";
char *system_path;
@@ -1033,7 +1329,10 @@ static int enable_disable_system(struct tracefs_instance *instance,
if (ret < 0)
return ret;
- ret = tracefs_instance_file_write(instance, system_path, str);
+ if (state)
+ ret = read_event_state(instance, system_path, state);
+ else
+ ret = tracefs_instance_file_write(instance, system_path, str);
free(system_path);
return ret;
@@ -1071,7 +1370,7 @@ static int make_regex(regex_t *re, const char *match)
static int event_enable_disable(struct tracefs_instance *instance,
const char *system, const char *event,
- bool enable)
+ bool enable, enum event_state *state)
{
regex_t system_re, event_re;
char **systems;
@@ -1108,7 +1407,7 @@ static int event_enable_disable(struct tracefs_instance *instance,
/* Check for the short cut first */
if (!event) {
- ret = enable_disable_system(instance, systems[s], enable);
+ ret = enable_disable_system(instance, systems[s], enable, state);
if (ret < 0)
break;
ret = 0;
@@ -1123,7 +1422,7 @@ static int event_enable_disable(struct tracefs_instance *instance,
if (!match(events[e], &event_re))
continue;
ret = enable_disable_event(instance, systems[s],
- events[e], enable);
+ events[e], enable, state);
if (ret < 0)
break;
ret = 0;
@@ -1162,11 +1461,55 @@ static int event_enable_disable(struct tracefs_instance *instance,
int tracefs_event_enable(struct tracefs_instance *instance,
const char *system, const char *event)
{
- return event_enable_disable(instance, system, event, true);
+ return event_enable_disable(instance, system, event, true, NULL);
}
int tracefs_event_disable(struct tracefs_instance *instance,
const char *system, const char *event)
{
- return event_enable_disable(instance, system, event, false);
+ return event_enable_disable(instance, system, event, false, NULL);
+}
+
+/**
+ * tracefs_event_is_enabled - return if the event is enabled or not
+ * @instance: ftrace instance, can be NULL for the top instance
+ * @system: The name of the system to check
+ * @event: The name of the event to check
+ *
+ * Checks is an event or multiple events are enabled.
+ *
+ * If @system is NULL, then it will check all the systems where @event is
+ * a match.
+ *
+ * If @event is NULL, then it will check all events where @system is a match.
+ *
+ * If both @system and @event are NULL, then it will check all events
+ *
+ * Returns TRACEFS_ALL_ENABLED if all matching are enabled.
+ * Returns TRACEFS_SOME_ENABLED if some are enabled and some are not
+ * Returns TRACEFS_ALL_DISABLED if none of the events are enabled.
+ * Returns TRACEFS_ERROR if there is an error reading the events.
+ */
+enum tracefs_enable_state
+tracefs_event_is_enabled(struct tracefs_instance *instance,
+ const char *system, const char *event)
+{
+ enum event_state state = STATE_INIT;
+ int ret;
+
+ ret = event_enable_disable(instance, system, event, false, &state);
+
+ if (ret < 0)
+ return TRACEFS_ERROR;
+
+ switch (state) {
+ case STATE_ENABLED:
+ return TRACEFS_ALL_ENABLED;
+ case STATE_DISABLED:
+ return TRACEFS_ALL_DISABLED;
+ case STATE_MIXED:
+ return TRACEFS_SOME_ENABLED;
+ default:
+ return TRACEFS_ERROR;
+ }
}
diff --git a/src/tracefs-filter.c b/src/tracefs-filter.c
index 85d0be1..a3dd77b 100644
--- a/src/tracefs-filter.c
+++ b/src/tracefs-filter.c
@@ -35,6 +35,12 @@ static const struct tep_format_field common_timestamp_usecs = {
.size = 8,
};
+static const struct tep_format_field common_comm = {
+ .type = "char *",
+ .name = "common_comm",
+ .size = 16,
+};
+
/*
* This also must be able to accept fields that are OK via the histograms,
* such as common_timestamp.
@@ -42,13 +48,19 @@ static const struct tep_format_field common_timestamp_usecs = {
static const struct tep_format_field *get_event_field(struct tep_event *event,
const char *field_name)
{
+ const struct tep_format_field *field;
+
if (!strcmp(field_name, TRACEFS_TIMESTAMP))
return &common_timestamp;
if (!strcmp(field_name, TRACEFS_TIMESTAMP_USECS))
return &common_timestamp_usecs;
- return tep_find_any_field(event, field_name);
+ field = tep_find_any_field(event, field_name);
+ if (!field && (!strcmp(field_name, "COMM") || !strcmp(field_name, "comm")))
+ return &common_comm;
+
+ return field;
}
__hidden bool
@@ -423,7 +435,8 @@ int tracefs_filter_string_append(struct tep_event *event, char **filter,
free(*filter);
*filter = str;
}
- return 0;
+
+ return ret;
}
static int error_msg(char **err, char *str,
diff --git a/src/tracefs-hist.c b/src/tracefs-hist.c
index e688eb3..fb6231e 100644
--- a/src/tracefs-hist.c
+++ b/src/tracefs-hist.c
@@ -280,25 +280,11 @@ tracefs_hist_alloc_2d(struct tep_handle *tep,
return tracefs_hist_alloc_nd(tep, system, event_name, axis);
}
-/**
- * tracefs_hist_alloc_nd - Initialize N-dimensional histogram
- * @tep: The tep handle that has the @system and @event.
- * @system: The system the histogram event is in
- * @event: The event that the histogram will be attached to
- * @axes: An array of histogram axes, terminated by a {NULL, 0} entry
- *
- * Will initialize a histogram descriptor that will be attached to
- * the @system/@event. This only initializes the descriptor with the given
- * @axes keys as primaries. 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_nd(struct tep_handle *tep,
- const char *system, const char *event_name,
- struct tracefs_hist_axis *axes)
+static struct tracefs_hist *
+hist_alloc_nd(struct tep_handle *tep,
+ const char *system, const char *event_name,
+ struct tracefs_hist_axis *axes,
+ struct tracefs_hist_axis_cnt *axes_cnt)
{
struct tep_event *event;
struct tracefs_hist *hist;
@@ -326,25 +312,75 @@ tracefs_hist_alloc_nd(struct tep_handle *tep,
if (tracefs_hist_add_key(hist, axes->key, axes->type) < 0)
goto fail;
+ for (; axes_cnt && axes_cnt->key; axes_cnt++)
+ if (tracefs_hist_add_key_cnt(hist, axes_cnt->key, axes_cnt->type, axes_cnt->cnt) < 0)
+ goto fail;
+
return hist;
fail:
tracefs_hist_free(hist);
return NULL;
}
+/**
+ * tracefs_hist_alloc_nd - Initialize N-dimensional histogram
+ * @tep: The tep handle that has the @system and @event.
+ * @system: The system the histogram event is in
+ * @event: The event that the histogram will be attached to
+ * @axes: An array of histogram axes, terminated by a {NULL, 0} entry
+ *
+ * Will initialize a histogram descriptor that will be attached to
+ * the @system/@event. This only initializes the descriptor with the given
+ * @axes keys as primaries. 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_nd(struct tep_handle *tep,
+ const char *system, const char *event_name,
+ struct tracefs_hist_axis *axes)
+{
+ return hist_alloc_nd(tep, system, event_name, axes, NULL);
+}
+
+/**
+ * tracefs_hist_alloc_nd_cnt - Initialize N-dimensional histogram
+ * @tep: The tep handle that has the @system and @event.
+ * @system: The system the histogram event is in
+ * @event: The event that the histogram will be attached to
+ * @axes: An array of histogram axes, terminated by a {NULL, 0} entry
+ *
+ * Will initialize a histogram descriptor that will be attached to
+ * the @system/@event. This only initializes the descriptor with the given
+ * @axes keys as primaries. 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_nd_cnt(struct tep_handle *tep,
+ const char *system, const char *event_name,
+ struct tracefs_hist_axis_cnt *axes)
+{
+ return hist_alloc_nd(tep, system, event_name, NULL, axes);
+}
/**
* 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.
+ * @cnt: Some types require a counter, for those, this is used
*
* 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)
+int tracefs_hist_add_key_cnt(struct tracefs_hist *hist, const char *key,
+ enum tracefs_hist_key_type type, int cnt)
{
bool use_key = false;
char *key_type = NULL;
@@ -377,6 +413,9 @@ int tracefs_hist_add_key(struct tracefs_hist *hist, const char *key,
case TRACEFS_HIST_KEY_USECS:
ret = asprintf(&key_type, "%s.usecs", key);
break;
+ case TRACEFS_HIST_KEY_BUCKETS:
+ ret = asprintf(&key_type, "%s.buckets=%d", key, cnt);
+ break;
case TRACEFS_HIST_KEY_MAX:
/* error */
break;
@@ -396,6 +435,22 @@ int tracefs_hist_add_key(struct tracefs_hist *hist, const char *key,
}
/**
+ * 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)
+{
+ return tracefs_hist_add_key_cnt(hist, key, type, 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.
@@ -654,6 +709,12 @@ struct action {
char *save;
};
+struct name_hash {
+ struct name_hash *next;
+ char *name;
+ char *hash;
+};
+
/*
* @name: name of the synthetic event
* @start_system: system of the starting event
@@ -684,6 +745,7 @@ struct tracefs_synth {
struct action *actions;
struct action **next_action;
struct tracefs_dynevent *dyn_event;
+ struct name_hash *name_hash[1 << HASH_BITS];
char *start_hist;
char *end_hist;
char *name;
@@ -724,6 +786,21 @@ static void action_free(struct action *action)
free(action);
}
+static void free_name_hash(struct name_hash **hash)
+{
+ struct name_hash *item;
+ int i;
+
+ for (i = 0; i < 1 << HASH_BITS; i++) {
+ while ((item = hash[i])) {
+ hash[i] = item->next;
+ free(item->name);
+ free(item->hash);
+ free(item);
+ }
+ }
+}
+
/**
* tracefs_synth_free - free the resources alloced to a synth
* @synth: The tracefs_synth descriptor
@@ -750,6 +827,7 @@ void tracefs_synth_free(struct tracefs_synth *synth)
tracefs_list_free(synth->end_keys);
tracefs_list_free(synth->start_vars);
tracefs_list_free(synth->end_vars);
+ free_name_hash(synth->name_hash);
free(synth->start_filter);
free(synth->end_filter);
free(synth->start_type);
@@ -773,6 +851,7 @@ static bool verify_event_fields(struct tep_event *start_event,
{
const struct tep_format_field *start_field;
const struct tep_format_field *end_field;
+ int start_flags, end_flags;
if (!trace_verify_event_field(start_event, start_field_name,
&start_field))
@@ -783,7 +862,11 @@ static bool verify_event_fields(struct tep_event *start_event,
&end_field))
return false;
- if (start_field->flags != end_field->flags ||
+ /* A pointer can still match a long */
+ start_flags = start_field->flags & ~TEP_FIELD_IS_POINTER;
+ end_flags = end_field->flags & ~TEP_FIELD_IS_POINTER;
+
+ if (start_flags != end_flags ||
start_field->size != end_field->size) {
errno = EBADE;
return false;
@@ -1068,9 +1151,8 @@ struct tracefs_synth *tracefs_synth_alloc(struct tep_handle *tep,
if (!synth->name || !synth->start_keys || !synth->end_keys || ret) {
tracefs_synth_free(synth);
synth = NULL;
- }
-
- synth->new_format = has_new_format();
+ } else
+ synth->new_format = has_new_format();
return synth;
}
@@ -1096,7 +1178,7 @@ static int add_synth_fields(struct tracefs_synth *synth,
return -1;
synth->synthetic_fields = list;
- ret = asprintf(&str, "$%s", name);
+ ret = asprintf(&str, "$%s", var ? : name);
if (ret < 0) {
trace_list_pop(synth->synthetic_fields);
return -1;
@@ -1196,7 +1278,7 @@ static unsigned int make_rand(void)
return((unsigned)(seed/65536) % 32768);
}
-static char *new_arg(struct tracefs_synth *synth)
+static char *new_name(struct tracefs_synth *synth, const char *name)
{
int cnt = synth->arg_cnt + 1;
char *arg;
@@ -1205,9 +1287,9 @@ static char *new_arg(struct tracefs_synth *synth)
/* Create a unique argument name */
if (!synth->arg_name[0]) {
/* make_rand() returns at most 32768 (total 13 bytes in use) */
- sprintf(synth->arg_name, "__arg_%u_", make_rand());
+ sprintf(synth->arg_name, "%u", make_rand());
}
- ret = asprintf(&arg, "%s%d", synth->arg_name, cnt);
+ ret = asprintf(&arg, "__%s_%s_%d", name, synth->arg_name, cnt);
if (ret < 0)
return NULL;
@@ -1215,6 +1297,55 @@ static char *new_arg(struct tracefs_synth *synth)
return arg;
}
+static struct name_hash *find_name(struct tracefs_synth *synth, const char *name)
+{
+ unsigned int key = quick_hash(name);
+ struct name_hash *hash = synth->name_hash[key];
+
+ for (; hash; hash = hash->next) {
+ if (!strcmp(hash->name, name))
+ return hash;
+ }
+ return NULL;
+}
+
+static const char *hash_name(struct tracefs_synth *synth, const char *name)
+{
+ struct name_hash *hash;
+ int key;
+
+ hash = find_name(synth, name);
+ if (hash)
+ return hash->hash;
+
+ hash = malloc(sizeof(*hash));
+ if (!hash)
+ return name;
+
+ hash->hash = new_name(synth, name);
+ if (!hash->hash) {
+ free(hash);
+ return name;
+ }
+
+ key = quick_hash(name);
+ hash->next = synth->name_hash[key];
+ synth->name_hash[key] = hash;
+
+ hash->name = strdup(name);
+ if (!hash->name) {
+ free(hash->hash);
+ free(hash);
+ return name;
+ }
+ return hash->hash;
+}
+
+static char *new_arg(struct tracefs_synth *synth)
+{
+ return new_name(synth, "arg");
+}
+
/**
* tracefs_synth_add_compare_field - add a comparison between start and end
* @synth: The tracefs_synth descriptor
@@ -1245,6 +1376,7 @@ int tracefs_synth_add_compare_field(struct tracefs_synth *synth,
const char *name)
{
const struct tep_format_field *start_field;
+ const char *hname;
char *start_arg;
char *compare;
int ret;
@@ -1296,11 +1428,12 @@ int tracefs_synth_add_compare_field(struct tracefs_synth *synth,
if (ret < 0)
return -1;
- ret = add_var(&synth->end_vars, name, compare, false);
+ hname = hash_name(synth, name);
+ ret = add_var(&synth->end_vars, hname, compare, false);
if (ret < 0)
goto out_free;
- ret = add_synth_fields(synth, start_field, name, NULL);
+ ret = add_synth_fields(synth, start_field, name, hname);
if (ret < 0)
goto out_free;
@@ -1313,9 +1446,10 @@ int tracefs_synth_add_compare_field(struct tracefs_synth *synth,
__hidden int synth_add_start_field(struct tracefs_synth *synth,
const char *start_field,
const char *name,
- enum tracefs_hist_key_type type)
+ enum tracefs_hist_key_type type, int count)
{
const struct tep_format_field *field;
+ const char *var;
char *start_arg;
char **tmp;
int *types;
@@ -1330,6 +1464,8 @@ __hidden int synth_add_start_field(struct tracefs_synth *synth,
if (!name)
name = start_field;
+ var = hash_name(synth, name);
+
if (!trace_verify_event_field(synth->start_event, start_field, &field))
return -1;
@@ -1341,11 +1477,11 @@ __hidden int synth_add_start_field(struct tracefs_synth *synth,
if (ret)
goto out_free;
- ret = add_var(&synth->end_vars, name, start_arg, true);
+ ret = add_var(&synth->end_vars, var, start_arg, true);
if (ret)
goto out_free;
- ret = add_synth_fields(synth, field, name, NULL);
+ ret = add_synth_fields(synth, field, name, var);
if (ret)
goto out_free;
@@ -1394,7 +1530,7 @@ int tracefs_synth_add_start_field(struct tracefs_synth *synth,
const char *start_field,
const char *name)
{
- return synth_add_start_field(synth, start_field, name, 0);
+ return synth_add_start_field(synth, start_field, name, 0, 0);
}
/**
@@ -1417,6 +1553,7 @@ int tracefs_synth_add_end_field(struct tracefs_synth *synth,
const char *name)
{
const struct tep_format_field *field;
+ const char *hname = NULL;
char *tmp_var = NULL;
int ret;
@@ -1425,17 +1562,24 @@ int tracefs_synth_add_end_field(struct tracefs_synth *synth,
return -1;
}
+ if (name) {
+ if (strncmp(name, "__arg", 5) != 0)
+ hname = hash_name(synth, name);
+ else
+ hname = name;
+ }
+
if (!name)
tmp_var = new_arg(synth);
if (!trace_verify_event_field(synth->end_event, end_field, &field))
return -1;
- ret = add_var(&synth->end_vars, name ? : tmp_var, end_field, false);
+ ret = add_var(&synth->end_vars, name ? hname : tmp_var, end_field, false);
if (ret)
goto out;
- ret = add_synth_fields(synth, field, name, tmp_var);
+ ret = add_synth_fields(synth, field, name, hname ? : tmp_var);
free(tmp_var);
out:
return ret;
@@ -2194,7 +2338,7 @@ int tracefs_synth_echo_cmd(struct trace_seq *seq,
new_event = true;
}
- path = trace_find_tracing_dir();
+ path = trace_find_tracing_dir(false);
if (!path)
goto out_free;
@@ -2227,11 +2371,6 @@ int tracefs_synth_echo_cmd(struct trace_seq *seq,
hist, path, synth->end_event->system,
synth->end_event->name);
- if (new_event) {
- tracefs_dynevent_free(synth->dyn_event);
- synth->dyn_event = NULL;
- }
-
ret = 0;
out_free:
free(hist);
diff --git a/src/tracefs-instance.c b/src/tracefs-instance.c
index 1493938..57f5c7f 100644
--- a/src/tracefs-instance.c
+++ b/src/tracefs-instance.c
@@ -334,7 +334,7 @@ char *tracefs_instance_get_dir(struct tracefs_instance *instance)
int ret;
if (!instance) /* Top instance of default system trace directory */
- return trace_find_tracing_dir();
+ return trace_find_tracing_dir(false);
if (!instance->name)
return strdup(instance->trace_dir);
@@ -364,6 +364,70 @@ const char *tracefs_instance_get_name(struct tracefs_instance *instance)
}
/**
+ * tracefs_instance_get_buffer_size - return the buffer size of the ring buffer
+ * @instance: The instance to get the buffer size from
+ * @cpu: if less that zero, will return the total size, otherwise the cpu size
+ *
+ * Returns the buffer size. If @cpu is less than zero, it returns the total size
+ * of the ring buffer otherwise it returs the size of the buffer for the given
+ * CPU.
+ *
+ * Returns -1 on error.
+ */
+ssize_t tracefs_instance_get_buffer_size(struct tracefs_instance *instance, int cpu)
+{
+ unsigned long long size;
+ char *path;
+ char *val;
+ int ret;
+
+ if (cpu < 0) {
+ val = tracefs_instance_file_read(instance, "buffer_total_size_kb", NULL);
+ } else {
+ ret = asprintf(&path, "per_cpu/cpu%d/buffer_size_kb", cpu);
+ if (ret < 0)
+ return ret;
+
+ val = tracefs_instance_file_read(instance, path, NULL);
+ free(path);
+ }
+
+ if (!val)
+ return -1;
+
+ size = strtoull(val, NULL, 0);
+ free(val);
+ return size;
+}
+
+int tracefs_instance_set_buffer_size(struct tracefs_instance *instance, size_t size, int cpu)
+{
+ char *path;
+ char *val;
+ int ret;
+
+ ret = asprintf(&val, "%zd", size);
+ if (ret < 0)
+ return ret;
+
+ if (cpu < 0) {
+ ret = tracefs_instance_file_write(instance, "buffer_size_kb", val);
+ } else {
+ ret = asprintf(&path, "per_cpu/cpu%d/buffer_size_kb", cpu);
+ if (ret < 0) {
+ free(val);
+ return ret;
+ }
+
+ ret = tracefs_instance_file_write(instance, path, val);
+ free(path);
+ }
+ free(val);
+
+ return ret < 0 ? -1 : 0;
+}
+
+/**
* tracefs_instance_get_trace_dir - return the top trace directory, where the instance is confuigred
* @instance: ftrace instance
*
diff --git a/src/tracefs-record.c b/src/tracefs-record.c
new file mode 100644
index 0000000..b078c86
--- /dev/null
+++ b/src/tracefs-record.c
@@ -0,0 +1,611 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2022 Google Inc, Steven Rostedt <rostedt@goodmis.org>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+
+#include <kbuffer.h>
+
+#include "tracefs.h"
+#include "tracefs-local.h"
+
+enum {
+ TC_STOP = 1 << 0, /* Stop reading */
+ TC_PERM_NONBLOCK = 1 << 1, /* read is always non blocking */
+ TC_NONBLOCK = 1 << 2, /* read is non blocking */
+};
+
+struct tracefs_cpu {
+ int fd;
+ int flags;
+ int nfds;
+ int ctrl_pipe[2];
+ int splice_pipe[2];
+ int pipe_size;
+ int subbuf_size;
+ int buffered;
+ int splice_read_flags;
+};
+
+/**
+ * tracefs_cpu_alloc_fd - create a tracefs_cpu instance for an existing fd
+ * @fd: The file descriptor to attach the tracefs_cpu to
+ * @subbuf_size: The expected size to read the subbuffer with
+ * @nonblock: If true, the file will be opened in O_NONBLOCK mode
+ *
+ * Return a descriptor that can read the tracefs trace_pipe_raw file
+ * that is associated with the given @fd and must be read in @subbuf_size.
+ *
+ * Returns NULL on error.
+ */
+struct tracefs_cpu *
+tracefs_cpu_alloc_fd(int fd, int subbuf_size, bool nonblock)
+{
+ struct tracefs_cpu *tcpu;
+ int mode = O_RDONLY;
+ int ret;
+
+ tcpu = calloc(1, sizeof(*tcpu));
+ if (!tcpu)
+ return NULL;
+
+ if (nonblock) {
+ mode |= O_NONBLOCK;
+ tcpu->flags |= TC_NONBLOCK | TC_PERM_NONBLOCK;
+ }
+
+ tcpu->splice_pipe[0] = -1;
+ tcpu->splice_pipe[1] = -1;
+
+ tcpu->fd = fd;
+
+ tcpu->subbuf_size = subbuf_size;
+
+ if (tcpu->flags & TC_PERM_NONBLOCK) {
+ tcpu->ctrl_pipe[0] = -1;
+ tcpu->ctrl_pipe[1] = -1;
+ } else {
+ /* ctrl_pipe is used to break out of blocked reads */
+ ret = pipe(tcpu->ctrl_pipe);
+ if (ret < 0)
+ goto fail;
+ if (tcpu->ctrl_pipe[0] > tcpu->fd)
+ tcpu->nfds = tcpu->ctrl_pipe[0] + 1;
+ else
+ tcpu->nfds = tcpu->fd + 1;
+ }
+
+ return tcpu;
+ fail:
+ free(tcpu);
+ return NULL;
+}
+
+/**
+ * tracefs_cpu_open - open an instance raw trace file
+ * @instance: the instance (NULL for toplevel) of the cpu raw file to open
+ * @cpu: The CPU that the raw trace file is associated with
+ * @nonblock: If true, the file will be opened in O_NONBLOCK mode
+ *
+ * Return a descriptor that can read the tracefs trace_pipe_raw file
+ * for a give @cpu in a given @instance.
+ *
+ * Returns NULL on error.
+ */
+struct tracefs_cpu *
+tracefs_cpu_open(struct tracefs_instance *instance, int cpu, bool nonblock)
+{
+ struct tracefs_cpu *tcpu;
+ struct tep_handle *tep;
+ char path[128];
+ char *buf;
+ int mode = O_RDONLY;
+ int subbuf_size;
+ int len;
+ int ret;
+ int fd;
+
+ if (nonblock)
+ mode |= O_NONBLOCK;
+
+ sprintf(path, "per_cpu/cpu%d/trace_pipe_raw", cpu);
+
+ fd = tracefs_instance_file_open(instance, path, mode);
+ if (fd < 0)
+ return NULL;
+
+ tep = tep_alloc();
+ if (!tep)
+ goto fail;
+
+ /* Get the size of the page */
+ buf = tracefs_instance_file_read(NULL, "events/header_page", &len);
+ if (!buf)
+ goto fail;
+
+ ret = tep_parse_header_page(tep, buf, len, sizeof(long));
+ free(buf);
+ if (ret < 0)
+ goto fail;
+
+ subbuf_size = tep_get_sub_buffer_size(tep);
+ tep_free(tep);
+ tep = NULL;
+
+ tcpu = tracefs_cpu_alloc_fd(fd, subbuf_size, nonblock);
+ if (!tcpu)
+ goto fail;
+
+ return tcpu;
+ fail:
+ tep_free(tep);
+ close(fd);
+ return NULL;
+}
+
+static void close_fd(int fd)
+{
+ if (fd < 0)
+ return;
+ close(fd);
+}
+
+/**
+ * tracefs_cpu_free_fd - clean up the tracefs_cpu descriptor
+ * @tcpu: The descriptor created with tracefs_cpu_alloc_fd()
+ *
+ * Closes all the internal file descriptors that were opened by
+ * tracefs_cpu_alloc_fd(), and frees the descriptor.
+ */
+void tracefs_cpu_free_fd(struct tracefs_cpu *tcpu)
+{
+ close_fd(tcpu->ctrl_pipe[0]);
+ close_fd(tcpu->ctrl_pipe[1]);
+ close_fd(tcpu->splice_pipe[0]);
+ close_fd(tcpu->splice_pipe[1]);
+
+ free(tcpu);
+}
+
+/**
+ * tracefs_cpu_close - clean up and close a raw trace descriptor
+ * @tcpu: The descriptor created with tracefs_cpu_open()
+ *
+ * Closes all the file descriptors associated to the trace_pipe_raw
+ * opened by tracefs_cpu_open().
+ */
+void tracefs_cpu_close(struct tracefs_cpu *tcpu)
+{
+ if (!tcpu)
+ return;
+
+ close(tcpu->fd);
+ tracefs_cpu_free_fd(tcpu);
+}
+
+/**
+ * tracefs_cpu_read_size - Return the size of the sub buffer
+ * @tcpu: The descriptor that holds the size of the sub buffer
+ *
+ * A lot of the functions that read the data from the trace_pipe_raw
+ * expect the caller to have allocated enough space to store a full
+ * subbuffer. Calling this function is a requirement to do so.
+ */
+int tracefs_cpu_read_size(struct tracefs_cpu *tcpu)
+{
+ if (!tcpu)
+ return -1;
+ return tcpu->subbuf_size;
+}
+
+static void set_nonblock(struct tracefs_cpu *tcpu)
+{
+ long flags;
+
+ if (tcpu->flags & TC_NONBLOCK)
+ return;
+
+ flags = fcntl(tcpu->fd, F_GETFL);
+ fcntl(tcpu->fd, F_SETFL, flags | O_NONBLOCK);
+ tcpu->flags |= TC_NONBLOCK;
+}
+
+static void unset_nonblock(struct tracefs_cpu *tcpu)
+{
+ long flags;
+
+ if (!(tcpu->flags & TC_NONBLOCK))
+ return;
+
+ flags = fcntl(tcpu->fd, F_GETFL);
+ flags &= ~O_NONBLOCK;
+ fcntl(tcpu->fd, F_SETFL, flags);
+ tcpu->flags &= ~TC_NONBLOCK;
+}
+
+/*
+ * If set to blocking mode, block until the watermark has been
+ * reached, or the control has said to stop. If the contol is
+ * set, then nonblock will be set to true on the way out.
+ */
+static int wait_on_input(struct tracefs_cpu *tcpu, bool nonblock)
+{
+ fd_set rfds;
+ int ret;
+
+ if (tcpu->flags & TC_PERM_NONBLOCK)
+ return 1;
+
+ if (nonblock) {
+ set_nonblock(tcpu);
+ return 1;
+ } else {
+ unset_nonblock(tcpu);
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(tcpu->fd, &rfds);
+ FD_SET(tcpu->ctrl_pipe[0], &rfds);
+
+ ret = select(tcpu->nfds, &rfds, NULL, NULL, NULL);
+
+ /* Let the application decide what to do with signals and such */
+ if (ret < 0)
+ return ret;
+
+ if (FD_ISSET(tcpu->ctrl_pipe[0], &rfds)) {
+ /* Flush the ctrl pipe */
+ read(tcpu->ctrl_pipe[0], &ret, 1);
+
+ /* Make nonblock as it is now stopped */
+ set_nonblock(tcpu);
+ /* Permanently set unblock */
+ tcpu->flags |= TC_PERM_NONBLOCK;
+ }
+
+ return FD_ISSET(tcpu->fd, &rfds);
+}
+
+/**
+ * tracefs_cpu_read - read from the raw trace file
+ * @tcpu: The descriptor representing the raw trace file
+ * @buffer: Where to read into (must be at least the size of the subbuffer)
+ * @nonblock: Hint to not block on the read if there's no data.
+ *
+ * Reads the trace_pipe_raw files associated to @tcpu into @buffer.
+ * @buffer must be at least the size of the sub buffer of the ring buffer,
+ * which is returned by tracefs_cpu_read_size().
+ *
+ * If @nonblock is set, and there's no data available, it will return
+ * immediately. Otherwise depending on how @tcpu was opened, it will
+ * block. If @tcpu was opened with nonblock set, then this @nonblock
+ * will make no difference.
+ *
+ * Returns the amount read or -1 on error.
+ */
+int tracefs_cpu_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock)
+{
+ int ret;
+
+ /*
+ * If nonblock is set, then the wait_on_input() will return
+ * immediately, if there's nothing in the buffer, with
+ * ret == 0.
+ */
+ ret = wait_on_input(tcpu, nonblock);
+ if (ret <= 0)
+ return ret;
+
+ ret = read(tcpu->fd, buffer, tcpu->subbuf_size);
+
+ /* It's OK if there's no data to read */
+ if (ret < 0 && errno == EAGAIN) {
+ /* Reset errno */
+ errno = 0;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int init_splice(struct tracefs_cpu *tcpu)
+{
+ int ret;
+
+ if (tcpu->splice_pipe[0] >= 0)
+ return 0;
+
+ ret = pipe(tcpu->splice_pipe);
+ if (ret < 0)
+ return ret;
+
+ ret = fcntl(tcpu->splice_pipe[0], F_GETPIPE_SZ, &tcpu->pipe_size);
+ /*
+ * F_GETPIPE_SZ was introduced in 2.6.35, ftrace was introduced
+ * in 2.6.31. If we are running on an older kernel, just fall
+ * back to using subbuf_size for splice(). It could also return
+ * the size of the pipe and not set pipe_size.
+ */
+ if (ret > 0 && !tcpu->pipe_size)
+ tcpu->pipe_size = ret;
+ else if (ret < 0)
+ tcpu->pipe_size = tcpu->subbuf_size;
+
+ tcpu->splice_read_flags = SPLICE_F_MOVE;
+ if (tcpu->flags & TC_NONBLOCK)
+ tcpu->splice_read_flags |= SPLICE_F_NONBLOCK;
+
+ return 0;
+}
+
+/**
+ * tracefs_cpu_buffered_read - Read the raw trace data buffering through a pipe
+ * @tcpu: The descriptor representing the raw trace file
+ * @buffer: Where to read into (must be at least the size of the subbuffer)
+ * @nonblock: Hint to not block on the read if there's no data.
+ *
+ * This is basically the same as tracefs_cpu_read() except that it uses
+ * a pipe through splice to buffer reads. This will batch reads keeping
+ * the reading from the ring buffer less intrusive to the system, as
+ * just reading all the time can cause quite a disturbance.
+ *
+ * Note, one difference between this and tracefs_cpu_read() is that it
+ * will read only in sub buffer pages. If the ring buffer has not filled
+ * a page, then it will not return anything, even with @nonblock set.
+ * Calls to tracefs_cpu_flush() should be done to read the rest of
+ * the file at the end of the trace.
+ *
+ * Returns the amount read or -1 on error.
+ */
+int tracefs_cpu_buffered_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock)
+{
+ int mode = SPLICE_F_MOVE;
+ int ret;
+
+ if (tcpu->buffered < 0)
+ tcpu->buffered = 0;
+
+ if (tcpu->buffered)
+ goto do_read;
+
+ ret = wait_on_input(tcpu, nonblock);
+ if (ret <= 0)
+ return ret;
+
+ if (tcpu->flags & TC_NONBLOCK)
+ mode |= SPLICE_F_NONBLOCK;
+
+ ret = init_splice(tcpu);
+ if (ret < 0)
+ return ret;
+
+ ret = splice(tcpu->fd, NULL, tcpu->splice_pipe[1], NULL,
+ tcpu->pipe_size, mode);
+ if (ret <= 0)
+ return ret;
+
+ tcpu->buffered = ret;
+
+ do_read:
+ ret = read(tcpu->splice_pipe[0], buffer, tcpu->subbuf_size);
+ if (ret > 0)
+ tcpu->buffered -= ret;
+ return ret;
+}
+
+/**
+ * tracefs_cpu_stop - Stop a blocked read of the raw tracing file
+ * @tcpu: The descriptor representing the raw trace file
+ *
+ * This will attempt to unblock a task blocked on @tcpu reading it.
+ * On older kernels, it may not do anything for the pipe reads, as
+ * older kernels do not wake up tasks waiting on the ring buffer.
+ *
+ * Returns 0 if the tasks reading the raw tracing file does not
+ * need a nudge.
+ *
+ * Returns 1 if that tasks may need a nudge (send a signal).
+ *
+ * Returns negative on error.
+ */
+int tracefs_cpu_stop(struct tracefs_cpu *tcpu)
+{
+ int ret = 1;
+
+ if (tcpu->flags & TC_PERM_NONBLOCK)
+ return 0;
+
+ ret = write(tcpu->ctrl_pipe[1], &ret, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Calling ioctl() on recent kernels will wake up the waiters */
+ ret = ioctl(tcpu->fd, 0);
+ if (ret < 0)
+ ret = 1;
+ else
+ ret = 0;
+
+ set_nonblock(tcpu);
+
+ return ret;
+}
+
+/**
+ * tracefs_cpu_flush - Finish out and read the rest of the raw tracing file
+ * @tcpu: The descriptor representing the raw trace file
+ * @buffer: Where to read into (must be at least the size of the subbuffer)
+ *
+ * Reads the trace_pipe_raw file associated by the @tcpu and puts it
+ * into @buffer, which must be the size of the sub buffer which is retrieved.
+ * by tracefs_cpu_read_size(). This should be called at the end of tracing
+ * to get the rest of the data.
+ *
+ * This will set the file descriptor for reading to non-blocking mode.
+ *
+ * Returns the number of bytes read, or negative on error.
+ */
+int tracefs_cpu_flush(struct tracefs_cpu *tcpu, void *buffer)
+{
+ int ret;
+
+ /* Make sure that reading is now non blocking */
+ set_nonblock(tcpu);
+
+ if (tcpu->buffered < 0)
+ tcpu->buffered = 0;
+
+ if (tcpu->buffered) {
+ ret = read(tcpu->splice_pipe[0], buffer, tcpu->subbuf_size);
+ if (ret > 0)
+ tcpu->buffered -= ret;
+ return ret;
+ }
+
+ ret = read(tcpu->fd, buffer, tcpu->subbuf_size);
+ if (ret > 0 && tcpu->buffered)
+ tcpu->buffered -= ret;
+
+ /* It's OK if there's no data to read */
+ if (ret < 0 && errno == EAGAIN) {
+ /* Reset errno */
+ errno = 0;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/**
+ * tracefs_cpu_flush_write - Finish out and read the rest of the raw tracing file
+ * @tcpu: The descriptor representing the raw trace file
+ * @wfd: The write file descriptor to write the data to
+ *
+ * Reads the trace_pipe_raw file associated by the @tcpu and writes it to
+ * @wfd. This should be called at the end of tracing to get the rest of the data.
+ *
+ * Returns the number of bytes written, or negative on error.
+ */
+int tracefs_cpu_flush_write(struct tracefs_cpu *tcpu, int wfd)
+{
+ char buffer[tcpu->subbuf_size];
+ int ret;
+
+ ret = tracefs_cpu_flush(tcpu, buffer);
+ if (ret > 0)
+ ret = write(wfd, buffer, ret);
+
+ /* It's OK if there's no data to read */
+ if (ret < 0 && errno == EAGAIN)
+ ret = 0;
+
+ return ret;
+}
+
+/**
+ * tracefs_cpu_write - Write the raw trace file into a file descriptor
+ * @tcpu: The descriptor representing the raw trace file
+ * @wfd: The write file descriptor to write the data to
+ * @nonblock: Hint to not block on the read if there's no data.
+ *
+ * This will pipe the data from the trace_pipe_raw file associated with @tcpu
+ * into the @wfd file descriptor. If @nonblock is set, then it will not
+ * block on if there's nothing to write. Note, it will only write sub buffer
+ * size data to @wfd. Calls to tracefs_cpu_flush_write() are needed to
+ * write out the rest.
+ *
+ * Returns the number of bytes read or negative on error.
+ */
+int tracefs_cpu_write(struct tracefs_cpu *tcpu, int wfd, bool nonblock)
+{
+ char buffer[tcpu->subbuf_size];
+ int mode = SPLICE_F_MOVE;
+ int tot_write = 0;
+ int tot;
+ int ret;
+
+ ret = wait_on_input(tcpu, nonblock);
+ if (ret <= 0)
+ return ret;
+
+ if (tcpu->flags & TC_NONBLOCK)
+ mode |= SPLICE_F_NONBLOCK;
+
+ ret = init_splice(tcpu);
+ if (ret < 0)
+ return ret;
+
+ tot = splice(tcpu->fd, NULL, tcpu->splice_pipe[1], NULL,
+ tcpu->pipe_size, mode);
+ if (tot < 0)
+ return tot;
+
+ if (tot == 0)
+ return 0;
+
+ ret = splice(tcpu->splice_pipe[0], NULL, wfd, NULL,
+ tot, SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
+
+ if (ret >= 0)
+ return ret;
+
+ /* Some file systems do not allow splicing, try writing instead */
+ do {
+ int r = tcpu->subbuf_size;
+
+ if (r > tot)
+ r = tot;
+
+ ret = read(tcpu->splice_pipe[0], buffer, r);
+ if (ret > 0) {
+ tot -= ret;
+ ret = write(wfd, buffer, ret);
+ }
+ if (ret > 0)
+ tot_write += ret;
+ } while (ret > 0);
+
+ if (ret < 0)
+ return ret;
+
+ return tot_write;
+}
+
+/**
+ * tracefs_cpu_pipe - Write the raw trace file into a pipe descriptor
+ * @tcpu: The descriptor representing the raw trace file
+ * @wfd: The write file descriptor to write the data to (must be a pipe)
+ * @nonblock: Hint to not block on the read if there's no data.
+ *
+ * This will splice directly the file descriptor of the trace_pipe_raw
+ * file to the given @wfd, which must be a pipe. This can also be used
+ * if @tcpu was created with tracefs_cpu_create_fd() where the passed
+ * in @fd there was a pipe, then @wfd does not need to be a pipe.
+ *
+ * Returns the number of bytes read or negative on error.
+ */
+int tracefs_cpu_pipe(struct tracefs_cpu *tcpu, int wfd, bool nonblock)
+{
+ int mode = SPLICE_F_MOVE;
+ int ret;
+
+ ret = wait_on_input(tcpu, nonblock);
+ if (ret <= 0)
+ return ret;
+
+ if (tcpu->flags & TC_NONBLOCK)
+ mode |= SPLICE_F_NONBLOCK;
+
+ ret = splice(tcpu->fd, NULL, wfd, NULL,
+ tcpu->pipe_size, mode);
+ return ret;
+}
diff --git a/src/tracefs-sqlhist.c b/src/tracefs-sqlhist.c
index 9811362..3f571b7 100644
--- a/src/tracefs-sqlhist.c
+++ b/src/tracefs-sqlhist.c
@@ -30,6 +30,12 @@ enum alias_type {
ALIAS_FIELD,
};
+enum field_type {
+ FIELD_NONE,
+ FIELD_FROM,
+ FIELD_TO,
+};
+
#define for_each_field(expr, field, table) \
for (expr = (table)->fields; expr; expr = (field)->next)
@@ -42,6 +48,7 @@ struct field {
const char *label;
const char *field;
const char *type;
+ enum field_type ftype;
};
struct filter {
@@ -167,7 +174,7 @@ static void parse_error(struct sqlhist_bison *sb, const char *text,
va_end(ap);
}
-static inline unsigned int quick_hash(const char *str)
+__hidden unsigned int quick_hash(const char *str)
{
unsigned int val = 0;
int len = strlen(str);
@@ -253,6 +260,7 @@ __hidden int add_selection(struct sqlhist_bison *sb, void *select,
switch (expr->type) {
case EXPR_FIELD:
+ expr->field.label = name;
break;
case EXPR_COMPARE:
expr->compare.name = name;
@@ -285,6 +293,8 @@ static struct expr *find_field(struct sqlhist_bison *sb,
if (!strcmp(field->raw, raw)) {
if (label && !field->label)
field->label = label;
+ if (label && strcmp(label, field->label) != 0)
+ continue;
return expr;
}
@@ -562,6 +572,9 @@ static int test_field_exists(struct tep_handle *tep,
tfield = tep_find_any_field(field->event, field_name);
free(field_name);
+ if (!tfield && (!strcmp(field->field, "COMM") || !strcmp(field->field, "comm")))
+ tfield = (void *)1L;
+
if (tfield)
return 0;
@@ -580,6 +593,7 @@ static int update_vars(struct tep_handle *tep,
{
struct sqlhist_bison *sb = table->sb;
struct field *event_field = &expr->field;
+ enum field_type ftype = FIELD_NONE;
struct tep_event *event;
struct field *field;
const char *label;
@@ -589,6 +603,11 @@ static int update_vars(struct tep_handle *tep,
const char *p;
int label_len = 0, event_len, system_len;
+ if (expr == table->to)
+ ftype = FIELD_TO;
+ else if (expr == table->from)
+ ftype = FIELD_FROM;
+
p = strchr(raw, '.');
if (p) {
char *str;
@@ -670,6 +689,7 @@ static int update_vars(struct tep_handle *tep,
field->event_name = event_name;
field->event = event;
field->field = raw + len + 1;
+ field->ftype = ftype;
if (!strcmp(field->field, "TIMESTAMP"))
field->field = store_str(sb, TRACEFS_TIMESTAMP);
@@ -887,20 +907,21 @@ static int verify_filter_error(struct sqlhist_bison *sb, struct expr *expr,
}
static int do_verify_filter(struct sqlhist_bison *sb, struct filter *filter,
- const char **system, const char **event)
+ const char **system, const char **event,
+ enum field_type *ftype)
{
int ret;
if (filter->type == FILTER_OR ||
filter->type == FILTER_AND) {
- ret = do_verify_filter(sb, &filter->lval->filter, system, event);
+ ret = do_verify_filter(sb, &filter->lval->filter, system, event, ftype);
if (ret)
return ret;
- return do_verify_filter(sb, &filter->rval->filter, system, event);
+ return do_verify_filter(sb, &filter->rval->filter, system, event, ftype);
}
if (filter->type == FILTER_GROUP ||
filter->type == FILTER_NOT_GROUP) {
- return do_verify_filter(sb, &filter->lval->filter, system, event);
+ return do_verify_filter(sb, &filter->lval->filter, system, event, ftype);
}
/*
@@ -910,6 +931,7 @@ static int do_verify_filter(struct sqlhist_bison *sb, struct filter *filter,
if (!*system && !*event) {
*system = filter->lval->field.system;
*event = filter->lval->field.event_name;
+ *ftype = filter->lval->field.ftype;
return 0;
}
@@ -921,7 +943,8 @@ static int do_verify_filter(struct sqlhist_bison *sb, struct filter *filter,
}
static int verify_filter(struct sqlhist_bison *sb, struct filter *filter,
- const char **system, const char **event)
+ const char **system, const char **event,
+ enum field_type *ftype)
{
int ret;
@@ -932,17 +955,17 @@ static int verify_filter(struct sqlhist_bison *sb, struct filter *filter,
case FILTER_NOT_GROUP:
break;
default:
- return do_verify_filter(sb, filter, system, event);
+ return do_verify_filter(sb, filter, system, event, ftype);
}
- ret = do_verify_filter(sb, &filter->lval->filter, system, event);
+ ret = do_verify_filter(sb, &filter->lval->filter, system, event, ftype);
if (ret)
return ret;
switch (filter->type) {
case FILTER_OR:
case FILTER_AND:
- return do_verify_filter(sb, &filter->rval->filter, system, event);
+ return do_verify_filter(sb, &filter->rval->filter, system, event, ftype);
default:
return 0;
}
@@ -1260,7 +1283,7 @@ static void where_no_to_error(struct sqlhist_bison *sb, struct expr *expr,
static int verify_field_type(struct tep_handle *tep,
struct sqlhist_bison *sb,
- struct expr *expr)
+ struct expr *expr, int *cnt)
{
struct field *field = &expr->field;
struct tep_event *event;
@@ -1339,6 +1362,17 @@ static int verify_field_type(struct tep_handle *tep,
if (tfield->flags & (TEP_FIELD_IS_STRING | TEP_FIELD_IS_ARRAY))
goto fail_type;
ret = TRACEFS_HIST_KEY_LOG;
+ } else if (!strncmp(type, "buckets", 7)) {
+ if (type[7] != '=' || !isdigit(type[8])) {
+ parse_error(sb, field->raw,
+ "buckets type must have '=[number]' after it\n");
+ ret = -1;
+ goto out;
+ }
+ *cnt = atoi(&type[8]);
+ if (tfield->flags & (TEP_FIELD_IS_STRING | TEP_FIELD_IS_ARRAY))
+ goto fail_type;
+ ret = TRACEFS_HIST_KEY_BUCKETS;
} else {
parse_error(sb, field->raw,
"Cast of '%s' to unknown type '%s'\n",
@@ -1449,17 +1483,19 @@ static struct tracefs_synth *build_synth(struct tep_handle *tep,
if (expr->type == EXPR_FIELD) {
ret = -1;
field = &expr->field;
- if (field->system == start_system &&
+ if (field->ftype != FIELD_TO &&
+ field->system == start_system &&
field->event_name == start_event) {
int type;
- type = verify_field_type(tep, table->sb, expr);
+ int cnt = 0;
+ type = verify_field_type(tep, table->sb, expr, &cnt);
if (type < 0)
goto free;
if (type != HIST_COUNTER_TYPE)
non_val = true;
ret = synth_add_start_field(synth,
field->field, field->label,
- type);
+ type, cnt);
} else if (table->to) {
ret = tracefs_synth_add_end_field(synth,
field->field, field->label);
@@ -1498,16 +1534,18 @@ static struct tracefs_synth *build_synth(struct tep_handle *tep,
for (expr = table->where; expr; expr = expr->next) {
const char *filter_system = NULL;
const char *filter_event = NULL;
+ enum field_type ftype = FIELD_NONE;
bool *started;
bool start;
ret = verify_filter(table->sb, &expr->filter, &filter_system,
- &filter_event);
+ &filter_event, &ftype);
if (ret < 0)
goto free;
start = filter_system == start_system &&
- filter_event == start_event;
+ filter_event == start_event &&
+ ftype != FIELD_TO;
if (start)
started = &started_start;
diff --git a/src/tracefs-tools.c b/src/tracefs-tools.c
index 489be28..8e7b46d 100644
--- a/src/tracefs-tools.c
+++ b/src/tracefs-tools.c
@@ -1055,7 +1055,7 @@ int tracefs_tracer_set(struct tracefs_instance *instance,
out:
tracefs_put_tracing_file(tracer_path);
close(fd);
- return ret;
+ return ret > 0 ? 0 : ret;
}
int tracefs_tracer_clear(struct tracefs_instance *instance)
diff --git a/src/tracefs-utils.c b/src/tracefs-utils.c
index 7b1f816..9acf2ad 100644
--- a/src/tracefs-utils.c
+++ b/src/tracefs-utils.c
@@ -29,6 +29,7 @@
#define STR(x) _STR(x)
static int log_level = TEP_LOG_CRITICAL;
+static char *custom_tracing_dir;
/**
* tracefs_set_loglevel - set log level of the library
@@ -84,13 +85,8 @@ static int mount_debugfs(void)
return ret;
}
-/**
- * trace_find_tracing_dir - Find tracing directory
- *
- * Returns string containing the full path to the system's tracing directory.
- * The string must be freed by free()
- */
-__hidden char *trace_find_tracing_dir(void)
+/* Exported for testing purpose only */
+__hidden char *find_tracing_dir(bool debugfs, bool mount)
{
char *debug_str = NULL;
char fspath[PATH_MAX+1];
@@ -109,9 +105,11 @@ __hidden char *trace_find_tracing_dir(void)
STR(PATH_MAX)
"s %99s %*s %*d %*d\n",
fspath, type) == 2) {
- if (strcmp(type, "tracefs") == 0)
+ if (!debugfs && strcmp(type, "tracefs") == 0)
break;
if (!debug_str && strcmp(type, "debugfs") == 0) {
+ if (debugfs)
+ break;
debug_str = strdup(fspath);
if (!debug_str) {
fclose(fp);
@@ -121,14 +119,21 @@ __hidden char *trace_find_tracing_dir(void)
}
fclose(fp);
- if (strcmp(type, "tracefs") != 0) {
- if (mount_tracefs() < 0) {
+ if (debugfs) {
+ if (strcmp(type, "debugfs") != 0) {
+ if (!mount || mount_debugfs() < 0)
+ return NULL;
+ strcpy(fspath, DEBUGFS_PATH);
+ }
+ } else if (strcmp(type, "tracefs") != 0) {
+ if (!mount || mount_tracefs() < 0) {
if (debug_str) {
strncpy(fspath, debug_str, PATH_MAX);
fspath[PATH_MAX] = 0;
} else {
- if (mount_debugfs() < 0) {
- tracefs_warning("debugfs not mounted, please mount");
+ if (!mount || mount_debugfs() < 0) {
+ if (mount)
+ tracefs_warning("debugfs not mounted, please mount");
free(debug_str);
return NULL;
}
@@ -156,6 +161,86 @@ __hidden char *trace_find_tracing_dir(void)
}
/**
+ * tracefs_tracing_dir_is_mounted - test if the tracing dir is already mounted
+ * @mount: Mount it if it is not already mounted
+ * @path: the path to the tracing directory if mounted or was mounted
+ *
+ * Returns 1 if the tracing directory is already mounted and 0 if it is not.
+ * If @mount is set and it fails to mount, it returns -1.
+ *
+ * If path is not NULL, and the tracing directory is or was mounted, it holds
+ * the path to the tracing directory. It must not be freed.
+ */
+int tracefs_tracing_dir_is_mounted(bool mount, const char **path)
+{
+ const char *dir;
+
+ dir = find_tracing_dir(false, false);
+ if (dir) {
+ if (path)
+ *path = dir;
+ return 1;
+ }
+ if (!mount)
+ return 0;
+
+ dir = find_tracing_dir(false, mount);
+ if (!dir)
+ return -1;
+ if (path)
+ *path = dir;
+ return 0;
+}
+
+/**
+ * trace_find_tracing_dir - Find tracing directory
+ * @debugfs: Boolean to just return the debugfs directory
+ *
+ * Returns string containing the full path to the system's tracing directory.
+ * The string must be freed by free()
+ */
+__hidden char *trace_find_tracing_dir(bool debugfs)
+{
+ return find_tracing_dir(debugfs, false);
+}
+
+/**
+ * tracefs_set_tracing_dir - Set location of the tracing directory
+ * @tracing_dir: full path to the system's tracing directory mount point.
+ *
+ * Set the location to the system's tracing directory. This API should be used
+ * to set a custom location of the tracing directory. There is no need to call
+ * it if the location is standard, in that case the library will auto detect it.
+ *
+ * Returns 0 on success, -1 otherwise.
+ */
+int tracefs_set_tracing_dir(char *tracing_dir)
+{
+ if (custom_tracing_dir) {
+ free(custom_tracing_dir);
+ custom_tracing_dir = NULL;
+ }
+
+ if (tracing_dir) {
+ custom_tracing_dir = strdup(tracing_dir);
+ if (!custom_tracing_dir)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Used to check if the directory is still mounted */
+static int test_dir(const char *dir, const char *file)
+{
+ char path[strlen(dir) + strlen(file) + 2];
+ struct stat st;
+
+ sprintf(path, "%s/%s", dir, file);
+ return stat(path, &st) < 0 ? 0 : 1;
+}
+
+/**
* tracefs_tracing_dir - Get tracing directory
*
* Returns string containing the full path to the system's tracing directory.
@@ -165,14 +250,36 @@ const char *tracefs_tracing_dir(void)
{
static const char *tracing_dir;
- if (tracing_dir)
+ /* Do not check custom_tracing_dir */
+ if (custom_tracing_dir)
+ return custom_tracing_dir;
+
+ if (tracing_dir && test_dir(tracing_dir, "trace"))
return tracing_dir;
- tracing_dir = trace_find_tracing_dir();
+ tracing_dir = find_tracing_dir(false, true);
return tracing_dir;
}
/**
+ * tracefs_debug_dir - Get debugfs directory path
+ *
+ * Returns string containing the full path to the system's debugfs directory.
+ *
+ * The returned string must *not* be freed.
+ */
+const char *tracefs_debug_dir(void)
+{
+ static const char *debug_dir;
+
+ if (debug_dir && test_dir(debug_dir, "tracing"))
+ return debug_dir;
+
+ debug_dir = find_tracing_dir(true, true);
+ return debug_dir;
+}
+
+/**
* tracefs_get_tracing_file - Get tracing file
* @name: tracing file name
*
@@ -487,3 +594,31 @@ int tracefs_list_size(char **list)
list--;
return (int)*(unsigned long *)list;
}
+
+/**
+ * tracefs_tracer_available - test if a tracer is available
+ * @tracing_dir: The directory that contains the tracing directory
+ * @tracer: The name of the tracer
+ *
+ * Return true if the tracer is available
+ */
+bool tracefs_tracer_available(const char *tracing_dir, const char *tracer)
+{
+ bool ret = false;
+ char **tracers = NULL;
+ int i;
+
+ tracers = tracefs_tracers(tracing_dir);
+ if (!tracers)
+ return false;
+
+ for (i = 0; tracers[i]; i++) {
+ if (strcmp(tracer, tracers[i]) == 0) {
+ ret = true;
+ break;
+ }
+ }
+
+ tracefs_list_free(tracers);
+ return ret;
+}
diff --git a/utest/Makefile b/utest/Makefile
index 74bf7e6..f10b709 100644
--- a/utest/Makefile
+++ b/utest/Makefile
@@ -15,27 +15,19 @@ LIBS += -lcunit \
$(obj)/lib/libtracefs.a
OBJS := $(OBJS:%.o=$(bdir)/%.o)
-DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)
$(bdir):
@mkdir -p $(bdir)
$(OBJS): | $(bdir)
-$(DEPS): | $(bdir)
-$(bdir)/trace-utest: $(OBJS)
+$(bdir)/trace-utest: $(OBJS) $(obj)/lib/libtracefs.a
$(Q)$(do_app_build)
$(bdir)/%.o: %.c
$(Q)$(call do_fpic_compile)
-$(DEPS): $(bdir)/.%.d: %.c
- $(Q)$(CC) -M $(CPPFLAGS) $(CFLAGS) $< > $@
- $(Q)$(CC) -M -MT $(bdir)/$*.o $(CPPFLAGS) $(CFLAGS) $< > $@
-
-$(OBJS): $(bdir)/%.o : $(bdir)/.%.d
-
-dep_includes := $(wildcard $(DEPS))
+-include .*.d
test: $(TARGETS)
diff --git a/utest/README b/utest/README
index ed92042..647e460 100644
--- a/utest/README
+++ b/utest/README
@@ -13,3 +13,6 @@ using a precompiled distro package:
libcunit1
libcunit1-doc
libcunit1-dev
+
+ openSUSE and SLE:
+ cunit-devel
diff --git a/utest/tracefs-utest.c b/utest/tracefs-utest.c
index 3f63837..e0e3c07 100644
--- a/utest/tracefs-utest.c
+++ b/utest/tracefs-utest.c
@@ -11,13 +11,18 @@
#include <time.h>
#include <dirent.h>
#include <ftw.h>
+#include <libgen.h>
+#include <kbuffer.h>
+#include <pthread.h>
+
+#include <sys/mount.h>
#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>
#include "tracefs.h"
-#define TRACEFS_SUITE "trasefs library"
+#define TRACEFS_SUITE "tracefs library"
#define TEST_INSTANCE_NAME "cunit_test_iter"
#define TEST_TRACE_DIR "/tmp/trace_utest.XXXXXX"
#define TEST_ARRAY_SIZE 5000
@@ -44,6 +49,10 @@
#define SQL_5_SQL "select end.common_pid as pid, (end.common_timestamp.usecs - start.common_timestamp.usecs) as irq_lat from irq_disable as start join irq_enable as end on start.common_pid = end.common_pid, start.parent_offs == end.parent_offs where start.common_pid != 0"
#define SQL_5_START "irq_disable"
+#define DEBUGFS_DEFAULT_PATH "/sys/kernel/debug"
+#define TRACEFS_DEFAULT_PATH "/sys/kernel/tracing"
+#define TRACEFS_DEFAULT2_PATH "/sys/kernel/debug/tracing"
+
static struct tracefs_instance *test_instance;
static struct tep_handle *test_tep;
struct test_sample {
@@ -86,20 +95,51 @@ static int test_callback(struct tep_event *event, struct tep_record *record,
return 0;
}
+static cpu_set_t *cpuset_save;
+static cpu_set_t *cpuset;
+static int cpu_size;
+
+static void save_affinity(void)
+{
+ int cpus;
+
+ cpus = sysconf(_SC_NPROCESSORS_CONF);
+ cpuset_save = CPU_ALLOC(cpus);
+ cpuset = CPU_ALLOC(cpus);
+ cpu_size = CPU_ALLOC_SIZE(cpus);
+ CU_TEST(cpuset_save != NULL && cpuset != NULL);
+ CU_TEST(sched_getaffinity(0, cpu_size, cpuset_save) == 0);
+}
+
+static void thread_affinity(void)
+{
+ sched_setaffinity(0, cpu_size, cpuset_save);
+}
+
+static void reset_affinity(void)
+{
+ sched_setaffinity(0, cpu_size, cpuset_save);
+ CPU_FREE(cpuset_save);
+ CPU_FREE(cpuset);
+}
+
+static void set_affinity(int cpu)
+{
+ CPU_ZERO_S(cpu_size, cpuset);
+ CPU_SET_S(cpu, cpu_size, cpuset);
+ CU_TEST(sched_setaffinity(0, cpu_size, cpuset) == 0);
+ sched_yield(); /* Force schedule */
+}
+
static void test_iter_write(struct tracefs_instance *instance)
{
- int cpus = sysconf(_SC_NPROCESSORS_CONF);
- cpu_set_t *cpuset, *cpusave;
- int cpu_size;
char *path;
int i, fd;
+ int cpus;
int ret;
- cpuset = CPU_ALLOC(cpus);
- cpusave = CPU_ALLOC(cpus);
- cpu_size = CPU_ALLOC_SIZE(cpus);
- CPU_ZERO_S(cpu_size, cpuset);
- sched_getaffinity(0, cpu_size, cpusave);
+ cpus = sysconf(_SC_NPROCESSORS_CONF);
+ save_affinity();
path = tracefs_instance_get_file(instance, "trace_marker");
CU_TEST(path != NULL);
@@ -113,17 +153,13 @@ static void test_iter_write(struct tracefs_instance *instance)
if (!test_array[i].value)
test_array[i].value++;
CU_TEST(test_array[i].cpu < cpus);
- CPU_ZERO_S(cpu_size, cpuset);
- CPU_SET(test_array[i].cpu, cpuset);
- sched_setaffinity(0, cpu_size, cpuset);
+ set_affinity(test_array[i].cpu);
ret = write(fd, test_array + i, sizeof(struct test_sample));
CU_TEST(ret == sizeof(struct test_sample));
}
- sched_setaffinity(0, cpu_size, cpusave);
+ reset_affinity();
close(fd);
- CPU_FREE(cpuset);
- CPU_FREE(cpusave);
}
@@ -382,6 +418,538 @@ static void test_trace_sql(void)
test_instance_trace_sql(test_instance);
}
+struct test_cpu_data {
+ struct tracefs_instance *instance;
+ struct tracefs_cpu *tcpu;
+ struct kbuffer *kbuf;
+ struct tep_handle *tep;
+ unsigned long long missed_events;
+ void *buf;
+ int events_per_buf;
+ int bufsize;
+ int data_size;
+ int this_pid;
+ int fd;
+ bool done;
+};
+
+static void cleanup_trace_cpu(struct test_cpu_data *data)
+{
+ close(data->fd);
+ tep_free(data->tep);
+ tracefs_cpu_close(data->tcpu);
+ free(data->buf);
+ kbuffer_free(data->kbuf);
+}
+
+#define EVENT_SYSTEM "syscalls"
+#define EVENT_NAME "sys_enter_getppid"
+
+static int setup_trace_cpu(struct tracefs_instance *instance, struct test_cpu_data *data)
+{
+ struct tep_format_field **fields;
+ struct tep_event *event;
+ char tmpfile[] = "/tmp/utest-libtracefsXXXXXX";
+ int max = 0;
+ int ret;
+ int i;
+
+ /* Make sure tracing is on */
+ tracefs_trace_on(instance);
+
+ memset (data, 0, sizeof(*data));
+
+ data->instance = instance;
+
+ data->fd = mkstemp(tmpfile);
+ CU_TEST(data->fd >= 0);
+ unlink(tmpfile);
+ if (data->fd < 0)
+ return -1;
+
+ data->tep = tracefs_local_events(NULL);
+ CU_TEST(data->tep != NULL);
+ if (!data->tep)
+ goto fail;
+
+ data->tcpu = tracefs_cpu_open(instance, 0, true);
+ CU_TEST(data->tcpu != NULL);
+ if (!data->tcpu)
+ goto fail;
+
+ data->bufsize = tracefs_cpu_read_size(data->tcpu);
+
+ data->buf = calloc(1, data->bufsize);
+ CU_TEST(data->buf != NULL);
+ if (!data->buf)
+ goto fail;
+
+ data->kbuf = kbuffer_alloc(sizeof(long) == 8, !tep_is_bigendian());
+ CU_TEST(data->kbuf != NULL);
+ if (!data->kbuf)
+ goto fail;
+
+ data->data_size = data->bufsize - kbuffer_start_of_data(data->kbuf);
+
+ tracefs_instance_file_clear(instance, "trace");
+
+ event = tep_find_event_by_name(data->tep, EVENT_SYSTEM, EVENT_NAME);
+ CU_TEST(event != NULL);
+ if (!event)
+ goto fail;
+
+ fields = tep_event_fields(event);
+ CU_TEST(fields != NULL);
+ if (!fields)
+ goto fail;
+
+ for (i = 0; fields[i]; i++) {
+ int end = fields[i]->offset + fields[i]->size;
+ if (end > max)
+ max = end;
+ }
+ free(fields);
+
+ CU_TEST(max != 0);
+ if (!max)
+ goto fail;
+
+ data->events_per_buf = data->data_size / max;
+
+ data->this_pid = getpid();
+ ret = tracefs_event_enable(instance, EVENT_SYSTEM, EVENT_NAME);
+ CU_TEST(ret == 0);
+ if (ret)
+ goto fail;
+
+
+ save_affinity();
+ set_affinity(0);
+
+ return 0;
+ fail:
+ cleanup_trace_cpu(data);
+ return -1;
+}
+
+static void shutdown_trace_cpu(struct test_cpu_data *data)
+{
+ struct tracefs_instance *instance = data->instance;
+ int ret;
+
+ reset_affinity();
+
+ ret = tracefs_event_disable(instance, EVENT_SYSTEM, EVENT_NAME);
+ CU_TEST(ret == 0);
+
+ cleanup_trace_cpu(data);
+}
+
+static void call_getppid(int cnt)
+{
+ int i;
+
+ for (i = 0; i < cnt; i++)
+ getppid();
+}
+
+static void test_cpu_read(struct test_cpu_data *data, int expect)
+{
+ struct tracefs_cpu *tcpu = data->tcpu;
+ struct kbuffer *kbuf = data->kbuf;
+ struct tep_record record;
+ void *buf = data->buf;
+ unsigned long long ts;
+ bool first = true;
+ int pid;
+ int ret;
+ int cnt = 0;
+
+ call_getppid(expect);
+
+ for (;;) {
+ ret = tracefs_cpu_read(tcpu, buf, false);
+ CU_TEST(ret > 0 || !first);
+ if (ret <= 0)
+ break;
+ first = false;
+ ret = kbuffer_load_subbuffer(kbuf, buf);
+ CU_TEST(ret == 0);
+ for (;;) {
+ record.data = kbuffer_read_event(kbuf, &ts);
+ if (!record.data)
+ break;
+ record.ts = ts;
+ pid = tep_data_pid(data->tep, &record);
+ if (pid == data->this_pid)
+ cnt++;
+ kbuffer_next_event(kbuf, NULL);
+ }
+ }
+ CU_TEST(cnt == expect);
+}
+
+static void test_instance_trace_cpu_read(struct tracefs_instance *instance)
+{
+ struct test_cpu_data data;
+
+ if (setup_trace_cpu(instance, &data))
+ return;
+
+ test_cpu_read(&data, 1);
+ test_cpu_read(&data, data.events_per_buf / 2);
+ test_cpu_read(&data, data.events_per_buf);
+ test_cpu_read(&data, data.events_per_buf + 1);
+ test_cpu_read(&data, data.events_per_buf * 50);
+
+ shutdown_trace_cpu(&data);
+}
+
+static void test_trace_cpu_read(void)
+{
+ test_instance_trace_cpu_read(NULL);
+ test_instance_trace_cpu_read(test_instance);
+}
+
+struct follow_data {
+ struct tep_event *sched_switch;
+ struct tep_event *sched_waking;
+ struct tep_event *function;
+ int missed;
+};
+
+static int switch_callback(struct tep_event *event, struct tep_record *record,
+ int cpu, void *data)
+{
+ struct follow_data *fdata = data;
+
+ CU_TEST(cpu == record->cpu);
+ CU_TEST(event->id == fdata->sched_switch->id);
+ return 0;
+}
+
+static int waking_callback(struct tep_event *event, struct tep_record *record,
+ int cpu, void *data)
+{
+ struct follow_data *fdata = data;
+
+ CU_TEST(cpu == record->cpu);
+ CU_TEST(event->id == fdata->sched_waking->id);
+ return 0;
+}
+
+static int function_callback(struct tep_event *event, struct tep_record *record,
+ int cpu, void *data)
+{
+ struct follow_data *fdata = data;
+
+ CU_TEST(cpu == record->cpu);
+ CU_TEST(event->id == fdata->function->id);
+ return 0;
+}
+
+static int missed_callback(struct tep_event *event, struct tep_record *record,
+ int cpu, void *data)
+{
+ struct follow_data *fdata = data;
+
+ fdata->missed = record->missed_events;
+ return 0;
+}
+
+static int all_callback(struct tep_event *event, struct tep_record *record,
+ int cpu, void *data)
+{
+ struct follow_data *fdata = data;
+
+ CU_TEST(fdata->missed == record->missed_events);
+ fdata->missed = 0;
+ return 0;
+}
+
+static void *stop_thread(void *arg)
+{
+ struct tracefs_instance *instance = arg;
+
+ sleep(1);
+ tracefs_iterate_stop(instance);
+ return NULL;
+}
+
+static void test_instance_follow_events(struct tracefs_instance *instance)
+{
+ struct follow_data fdata;
+ struct tep_handle *tep;
+ pthread_t thread;
+ int ret;
+
+ memset(&fdata, 0, sizeof(fdata));
+
+ tep = tracefs_local_events(NULL);
+ CU_TEST(tep != NULL);
+ if (!tep)
+ return;
+
+ fdata.sched_switch = tep_find_event_by_name(tep, "sched", "sched_switch");
+ CU_TEST(fdata.sched_switch != NULL);
+ if (!fdata.sched_switch)
+ return;
+
+ fdata.sched_waking = tep_find_event_by_name(tep, "sched", "sched_waking");
+ CU_TEST(fdata.sched_waking != NULL);
+ if (!fdata.sched_waking)
+ return;
+
+ fdata.function = tep_find_event_by_name(tep, "ftrace", "function");
+ CU_TEST(fdata.function != NULL);
+ if (!fdata.function)
+ return;
+
+ ret = tracefs_follow_event(tep, instance, "sched", "sched_switch",
+ switch_callback, &fdata);
+ CU_TEST(ret == 0);
+
+ ret = tracefs_follow_event(tep, instance, "sched", "sched_waking",
+ waking_callback, &fdata);
+ CU_TEST(ret == 0);
+
+ ret = tracefs_follow_event(tep, instance, "ftrace", "function",
+ function_callback, &fdata);
+ CU_TEST(ret == 0);
+
+ ret = tracefs_follow_missed_events(instance, missed_callback, &fdata);
+ CU_TEST(ret == 0);
+
+ ret = tracefs_event_enable(instance, "sched", "sched_switch");
+ CU_TEST(ret == 0);
+
+ ret = tracefs_event_enable(instance, "sched", "sched_waking");
+ CU_TEST(ret == 0);
+
+ ret = tracefs_tracer_set(instance, TRACEFS_TRACER_FUNCTION);
+ CU_TEST(ret == 0);
+
+ pthread_create(&thread, NULL, stop_thread, instance);
+
+ ret = tracefs_iterate_raw_events(tep, instance, NULL, 0, all_callback, &fdata);
+ CU_TEST(ret == 0);
+
+ pthread_join(thread, NULL);
+
+ tracefs_tracer_clear(instance);
+ tracefs_event_disable(instance, NULL, NULL);
+}
+
+static void test_follow_events(void)
+{
+ test_instance_follow_events(NULL);
+ test_instance_follow_events(test_instance);
+}
+
+extern char *find_tracing_dir(bool debugfs, bool mount);
+static void test_mounting(void)
+{
+ const char *tracing_dir;
+ const char *debug_dir;
+ struct stat st;
+ char *save_tracing = NULL;
+ char *save_debug = NULL;
+ char *path;
+ char *dir;
+ int ret;
+
+ /* First, unmount all instances of debugfs */
+ do {
+ dir = find_tracing_dir(true, false);
+ if (dir) {
+ ret = umount(dir);
+ CU_TEST(ret == 0);
+ if (ret < 0)
+ return;
+ /* Save the first instance that's not /sys/kernel/debug */
+ if (!save_debug && strcmp(dir, DEBUGFS_DEFAULT_PATH) != 0)
+ save_debug = dir;
+ else
+ free(dir);
+ }
+ } while (dir);
+
+ /* Next, unmount all instances of tracefs */
+ do {
+ dir = find_tracing_dir(false, false);
+ if (dir) {
+ ret = umount(dir);
+ CU_TEST(ret == 0);
+ if (ret < 0)
+ return;
+ /* Save the first instance that's not in /sys/kernel/ */
+ if (!save_tracing && strncmp(dir, "/sys/kernel/", 12) != 0)
+ save_tracing = dir;
+ else
+ free(dir);
+ }
+ } while (dir);
+
+ /* Mount first the tracing dir (which should mount at /sys/kernel/tracing */
+ tracing_dir = tracefs_tracing_dir();
+ CU_TEST(tracing_dir != NULL);
+ if (tracing_dir != NULL) {
+ CU_TEST(strcmp(tracing_dir, TRACEFS_DEFAULT_PATH) == 0 ||
+ strcmp(tracing_dir, TRACEFS_DEFAULT2_PATH) == 0);
+ if (strncmp(tracing_dir, "/sys/kernel/", 12) != 0)
+ printf("Tracing directory mounted at '%s'\n",
+ tracing_dir);
+
+ /* Make sure the directory has content.*/
+ asprintf(&path, "%s/trace", tracing_dir);
+ CU_TEST(stat(path, &st) == 0);
+ free(path);
+ }
+
+ /* Now mount debugfs dir, which should mount at /sys/kernel/debug */
+ debug_dir = tracefs_debug_dir();
+ CU_TEST(debug_dir != NULL);
+ if (debug_dir != NULL) {
+ CU_TEST(strcmp(debug_dir, DEBUGFS_DEFAULT_PATH) == 0);
+ if (strcmp(debug_dir, DEBUGFS_DEFAULT_PATH) != 0)
+ printf("debug directory mounted at '%s'\n",
+ debug_dir);
+
+ /* Make sure the directory has content.*/
+ asprintf(&path, "%s/tracing", debug_dir);
+ CU_TEST(stat(path, &st) == 0);
+ free(path);
+ }
+
+ if (save_debug)
+ mount("debugfs", save_debug, "debugfs", 0, NULL);
+
+ if (save_tracing &&
+ (!save_debug || strncmp(save_debug, save_tracing, strlen(save_debug)) != 0))
+ mount("tracefs", save_tracing, "tracefs", 0, NULL);
+
+ free(save_debug);
+ free(save_tracing);
+}
+
+static int read_trace_cpu_file(struct test_cpu_data *data)
+{
+ unsigned long long ts;
+ struct tep_record record;
+ struct kbuffer *kbuf = data->kbuf;
+ void *buf = data->buf;
+ bool first = true;
+ int bufsize = data->bufsize;
+ int fd = data->fd;
+ int missed;
+ int pid;
+ int ret;
+ int cnt = 0;
+
+ ret = lseek64(fd, 0, SEEK_SET);
+ CU_TEST(ret == 0);
+ if (ret)
+ return -1;
+
+ for (;;) {
+ ret = read(fd, buf, bufsize);
+ CU_TEST(ret > 0 || !first);
+ if (ret <= 0)
+ break;
+ first = false;
+
+ ret = kbuffer_load_subbuffer(kbuf, buf);
+ CU_TEST(ret == 0);
+ missed = kbuffer_missed_events(kbuf);
+ if (missed)
+ printf("missed events %d\n", missed);
+ for (;;) {
+ record.data = kbuffer_read_event(kbuf, &ts);
+ if (!record.data)
+ break;
+ record.ts = ts;
+ pid = tep_data_pid(data->tep, &record);
+ if (pid == data->this_pid)
+ cnt++;
+ kbuffer_next_event(kbuf, NULL);
+ }
+ }
+ return ret == 0 ? cnt : ret;
+}
+
+static void *trace_cpu_thread(void *arg)
+{
+ struct test_cpu_data *data = arg;
+ struct tracefs_cpu *tcpu = data->tcpu;
+ int fd = data->fd;
+ long ret = 0;
+
+ thread_affinity();
+
+ while (!data->done && ret >= 0) {
+ ret = tracefs_cpu_write(tcpu, fd, false);
+ if (ret < 0 && errno == EAGAIN)
+ ret = 0;
+ }
+ if (ret >= 0 || errno == EAGAIN) {
+ do {
+ ret = tracefs_cpu_flush_write(tcpu, fd);
+ } while (ret > 0);
+ }
+
+ return (void *)ret;
+}
+
+static void test_cpu_pipe(struct test_cpu_data *data, int expect)
+{
+ pthread_t thread;
+ void *retval;
+ long ret;
+ int cnt;
+
+ tracefs_instance_file_clear(data->instance, "trace");
+ ftruncate(data->fd, 0);
+
+ data->done = false;
+
+ pthread_create(&thread, NULL, trace_cpu_thread, data);
+ sleep(1);
+
+ call_getppid(expect);
+
+ data->done = true;
+ tracefs_cpu_stop(data->tcpu);
+ pthread_join(thread, &retval);
+ ret = (long)retval;
+ CU_TEST(ret >= 0);
+
+ cnt = read_trace_cpu_file(data);
+
+ CU_TEST(cnt == expect);
+}
+
+static void test_instance_trace_cpu_pipe(struct tracefs_instance *instance)
+{
+ struct test_cpu_data data;
+
+ if (setup_trace_cpu(instance, &data))
+ return;
+
+ test_cpu_pipe(&data, 1);
+ test_cpu_pipe(&data, data.events_per_buf / 2);
+ test_cpu_pipe(&data, data.events_per_buf);
+ test_cpu_pipe(&data, data.events_per_buf + 1);
+ test_cpu_pipe(&data, data.events_per_buf * 1000);
+
+ shutdown_trace_cpu(&data);
+}
+
+static void test_trace_cpu_pipe(void)
+{
+ test_instance_trace_cpu_pipe(NULL);
+ test_instance_trace_cpu_pipe(test_instance);
+}
+
static struct tracefs_dynevent **get_dynevents_check(enum tracefs_dynevent_type types, int count)
{
struct tracefs_dynevent **devents;
@@ -1404,6 +1972,9 @@ static void test_instance_tracers(struct tracefs_instance *instance)
tracers = tracefs_tracers(tdir);
CU_TEST(tracers != NULL);
+ for (i = 0; tracers[i]; i++)
+ CU_TEST(tracefs_tracer_available(tdir, tracers[i]));
+
tfile = tracefs_instance_file_read(NULL, ALL_TRACERS, NULL);
tracer = strtok(tfile, " ");
@@ -1701,8 +2272,11 @@ void del_trace_dir(char *dir)
static void test_custom_trace_dir(void)
{
+ char *tdir = "/tmp/custom_tracefs";
struct tracefs_instance *instance;
char *dname = copy_trace_dir();
+ const char *trace_dir;
+ char *tfile;
instance = tracefs_instance_alloc(dname, NULL);
CU_TEST(instance != NULL);
@@ -1717,6 +2291,22 @@ static void test_custom_trace_dir(void)
tracefs_instance_free(instance);
del_trace_dir(dname);
free(dname);
+
+ trace_dir = tracefs_tracing_dir();
+ CU_TEST(trace_dir != NULL);
+ CU_TEST(tracefs_set_tracing_dir(tdir) == 0);
+ CU_TEST(strcmp(tdir, tracefs_tracing_dir()) == 0);
+ tfile = tracefs_get_tracing_file("trace");
+ CU_TEST(tfile != NULL);
+ CU_TEST(strcmp(tdir, dirname(tfile)) == 0);
+ free(tfile);
+
+ CU_TEST(tracefs_set_tracing_dir(NULL) == 0);
+ CU_TEST(strcmp(trace_dir, tracefs_tracing_dir()) == 0);
+ tfile = tracefs_get_tracing_file("trace");
+ CU_TEST(tfile != NULL);
+ CU_TEST(strcmp(trace_dir, dirname(tfile)) == 0);
+ free(tfile);
}
static int test_suite_destroy(void)
@@ -1750,6 +2340,12 @@ void test_tracefs_lib(void)
fprintf(stderr, "Suite \"%s\" cannot be ceated\n", TRACEFS_SUITE);
return;
}
+
+ CU_add_test(suite, "Test tracefs/debugfs mounting", test_mounting);
+ CU_add_test(suite, "trace cpu read",
+ test_trace_cpu_read);
+ CU_add_test(suite, "trace cpu pipe",
+ test_trace_cpu_pipe);
CU_add_test(suite, "trace sql",
test_trace_sql);
CU_add_test(suite, "tracing file / directory APIs",
@@ -1762,6 +2358,10 @@ void test_tracefs_lib(void)
test_system_event);
CU_add_test(suite, "tracefs_iterate_raw_events API",
test_iter_raw_events);
+
+ /* Follow events test must be after the iterate raw events above */
+ CU_add_test(suite, "Follow events", test_follow_events);
+
CU_add_test(suite, "tracefs_tracers API",
test_tracers);
CU_add_test(suite, "tracefs_local events API",