aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSadaf Ebrahimi <sadafebrahimi@google.com>2023-01-18 20:03:10 +0000
committerSadaf Ebrahimi <sadafebrahimi@google.com>2023-01-18 20:03:10 +0000
commite50996d4ce874aa8d1bb4abcf5af43ee9e27aaf1 (patch)
tree094674bf37cb97e2531933c2cb3ca6c5c11444b1
parent923dc354444511f7384c220fa0dcb655f11e13de (diff)
parent4e51588b1f4d6617df7e39a38acc0d0daa3d8a3f (diff)
downloadlibtraceevent-e50996d4ce874aa8d1bb4abcf5af43ee9e27aaf1.tar.gz
Upgrade libtraceevent to libtraceevent-1.7.1main-16k-with-phones
This project was upgraded with external_updater. Usage: tools/external_updater/updater.sh update libtraceevent For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md Test: TreeHugger Change-Id: If05b680336283ce6f5638029e6edde10ef774581
-rw-r--r--.gitignore5
-rw-r--r--Documentation/libtraceevent-commands.txt2
-rw-r--r--Documentation/libtraceevent-field_get_val.txt4
-rw-r--r--Documentation/libtraceevent-func_apis.txt26
-rw-r--r--Documentation/libtraceevent-func_find.txt51
-rw-r--r--Documentation/libtraceevent-handle.txt9
-rw-r--r--Documentation/libtraceevent-kbuffer-create.txt207
-rw-r--r--Documentation/libtraceevent-kbuffer-read.txt246
-rw-r--r--Documentation/libtraceevent-kbuffer-timestamp.txt208
-rw-r--r--Documentation/libtraceevent-kvm-plugin.txt252
-rw-r--r--Documentation/libtraceevent-page_size.txt10
-rw-r--r--Documentation/libtraceevent-plugins.txt9
-rw-r--r--Documentation/libtraceevent.txt49
-rw-r--r--METADATA17
-rw-r--r--Makefile18
-rwxr-xr-xcheck-manpages.sh78
-rw-r--r--include/traceevent/event-parse.h42
-rw-r--r--include/traceevent/kbuffer.h2
-rw-r--r--include/traceevent/trace-seq.h8
-rw-r--r--plugins/plugin_function.c8
-rw-r--r--plugins/plugin_kvm.c74
-rw-r--r--src/event-parse-local.h14
-rw-r--r--src/event-parse.c896
-rw-r--r--src/kbuffer-parse.c42
-rw-r--r--src/parse-filter.c118
-rw-r--r--src/parse-utils.c27
-rw-r--r--utest/.gitignore1
-rw-r--r--utest/traceevent-utest.c257
28 files changed, 2365 insertions, 315 deletions
diff --git a/.gitignore b/.gitignore
index e203d72..39a013b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,12 +2,17 @@
TRACEEVENT-CFLAGS
libtraceevent-dynamic-list
libtraceevent.so.*
+.pc
+patches
*.o
build_prefix
build_uninstall
*~
\#*\#
+.#*
*.cmd
*.so
.*.d
ep_version.h
+libtraceevent.pc
+lib/
diff --git a/Documentation/libtraceevent-commands.txt b/Documentation/libtraceevent-commands.txt
index 185ff16..cea3001 100644
--- a/Documentation/libtraceevent-commands.txt
+++ b/Documentation/libtraceevent-commands.txt
@@ -3,7 +3,7 @@ libtraceevent(3)
NAME
----
-tep_register_comm, tep_override_comm, tep_pid_is_registered,
+tep_register_comm, tep_override_comm, tep_is_pid_registered,
tep_data_comm_from_pid, tep_data_pid_from_comm, tep_cmdline_pid -
Manage pid to process name mappings.
diff --git a/Documentation/libtraceevent-field_get_val.txt b/Documentation/libtraceevent-field_get_val.txt
index 69d51bf..6a5f1cd 100644
--- a/Documentation/libtraceevent-field_get_val.txt
+++ b/Documentation/libtraceevent-field_get_val.txt
@@ -64,10 +64,10 @@ void process_record(struct tep_record *record)
{
int len;
char *comm;
- struct tep_event_format *event;
+ struct tep_event *event;
unsigned long long val;
- event = tep_find_event_by_record(pevent, record);
+ event = tep_find_event_by_record(tep, record);
if (event != NULL) {
if (tep_get_common_field_val(NULL, event, "common_type",
record, &val, 0) == 0) {
diff --git a/Documentation/libtraceevent-func_apis.txt b/Documentation/libtraceevent-func_apis.txt
index a465d63..1b836a1 100644
--- a/Documentation/libtraceevent-func_apis.txt
+++ b/Documentation/libtraceevent-func_apis.txt
@@ -3,10 +3,8 @@ libtraceevent(3)
NAME
----
-tep_find_function, tep_find_function_address, tep_set_function_resolver,
-tep_reset_function_resolver, tep_register_function, tep_register_print_string,
-tep_get_function_count -
-function related tep APIs
+tep_set_function_resolver, tep_reset_function_resolver, tep_register_function, tep_register_print_string,
+tep_get_function_count - function related tep APIs
SYNOPSIS
--------
@@ -17,8 +15,6 @@ SYNOPSIS
typedef char pass:[*](*tep_func_resolver_t*)(void pass:[*]_priv_, unsigned long long pass:[*]_addrp_, char pass:[**]_modp_);
int *tep_set_function_resolver*(struct tep_handle pass:[*]_tep_, tep_func_resolver_t pass:[*]_func_, void pass:[*]_priv_);
void *tep_reset_function_resolver*(struct tep_handle pass:[*]_tep_);
-const char pass:[*]*tep_find_function*(struct tep_handle pass:[*]_tep_, unsigned long long _addr_);
-unsigned long long *tep_find_function_address*(struct tep_handle pass:[*]_tep_, unsigned long long _addr_);
int *tep_register_function*(struct tep_handle pass:[*]_tep_, char pass:[*]_name_, unsigned long long _addr_, char pass:[*]_mod_);
int *tep_register_print_string*(struct tep_handle pass:[*]_tep_, const char pass:[*]_fmt_, unsigned long long _addr_);
int *tep_get_function_count*(struct tep_handle *_tep_)
@@ -38,8 +34,8 @@ the name of the function and its module (if any) in _modp_.
The *tep_set_function_resolver()* function registers _func_ as an alternative
kernel functions resolver. The _tep_ argument is trace event parser context.
The _priv_ argument is a custom context of the _func_ function. The function
-resolver is used by the APIs *tep_find_function()*,
-*tep_find_function_address()*, and *tep_print_func_field()* to resolve
+resolver is used by the APIs *tep_find_function*(3),
+*tep_find_function_address*(3), and *tep_print_func_field()* to resolve
a function address to a function name.
The *tep_reset_function_resolver()* function resets the kernel functions
@@ -51,14 +47,6 @@ These APIs can be used to find function name and start address, by given
address. The given address does not have to be exact, it will select
the function that would contain it.
-The *tep_find_function()* function returns the function name, which contains the
-given address _addr_. The _tep_ argument is the trace event parser context.
-
-The *tep_find_function_address()* function returns the function start address,
-by given address _addr_. The _addr_ does not have to be exact, it will select
-the function that would contain it. The _tep_ argument is the trace event
-parser context.
-
The *tep_register_function()* function registers a function name mapped to an
address and (optional) module. This mapping is used in case the function tracer
or events have "%pS" parameter in its format string. It is common to pass in
@@ -84,12 +72,6 @@ RETURN VALUE
The *tep_set_function_resolver()* function returns 0 in case of success, or -1
in case of an error.
-The *tep_find_function()* function returns the function name, or NULL in case
-it cannot be found.
-
-The *tep_find_function_address()* function returns the function start address,
-or 0 in case it cannot be found.
-
The *tep_register_function()* function returns 0 in case of success. In case of
an error -1 is returned, and errno is set to the appropriate error number.
diff --git a/Documentation/libtraceevent-func_find.txt b/Documentation/libtraceevent-func_find.txt
index 20982e9..26fac68 100644
--- a/Documentation/libtraceevent-func_find.txt
+++ b/Documentation/libtraceevent-func_find.txt
@@ -3,7 +3,7 @@ libtraceevent(3)
NAME
----
-tep_find_function,tep_find_function_address - Find function name / start address.
+tep_find_function,tep_find_function_address,tep_find_function_info - Find function name / start address.
SYNOPSIS
--------
@@ -13,6 +13,8 @@ SYNOPSIS
const char pass:[*]*tep_find_function*(struct tep_handle pass:[*]_tep_, unsigned long long _addr_);
unsigned long long *tep_find_function_address*(struct tep_handle pass:[*]_tep_, unsigned long long _addr_);
+int *tep_find_function_info*(struct tep_handle pass:[*]_tep_, unsigned long long _addr_, const char pass:[**]_name_,
+ unsigned long long pass:[*]_start_, unsigned long pass:[*]_size_);
--
DESCRIPTION
@@ -28,6 +30,11 @@ The *tep_find_function_address()* function returns the function start address,
by given address _addr_. The _addr_ does not have to be exact, it will select the
function that would contain it. The _tep_ argument is the trace event parser context.
+The *tep_find_function_info()* function retrieves the _name_, starting address (_start_),
+and the function text _size_ of the function at _address_, if it is found. Note,
+if the _tep_ handle has a function resolver (used by perf), then _size_ is set to
+zero.
+
RETURN VALUE
------------
The *tep_find_function()* function returns the function name, or NULL in case
@@ -36,6 +43,9 @@ it cannot be found.
The *tep_find_function_address()* function returns the function start address,
or 0 in case it cannot be found.
+The *tep_find_function_info()* function returns 1 if a function is found for the
+given address, or 0 if it is not.
+
EXAMPLE
-------
[source,c]
@@ -44,12 +54,45 @@ EXAMPLE
...
struct tep_handle *tep = tep_alloc();
...
-void show_function( unsigned long long addr)
+void show_function_name(unsigned long long addr)
+{
+ const char *fname = tep_find_function(tep, addr);
+
+ if (fname)
+ printf("Found function %s at 0x%0llx\n", fname, addr);
+ else
+ printf("No function found at 0x%0llx\n", addr);
+}
+
+void show_function_start_addr(unsigned long long addr)
{
const char *fname = tep_find_function(tep, addr);
- unsigned long long fstart = tep_find_function_address(tep, addr);
+ unsigned long long fstart;
+
+ if (!fname) {
+ printf("No function found at 0x%0llx\n", addr);
+ return;
+ }
+
+ fstart = tep_find_function_address(tep, addr);
+ printf("Function %s at 0x%llx starts at 0x%0llx\n",
+ fname, addr, fstart);
+}
+
+void show_function_info(unsigned long long addr)
+{
+ const char *fname;
+ unsigned long long fstart;
+ unsigned long size;
+
+ ret = tep_find_function_info(tep, addr, &fname, &fstart, &size);
+ if (!ret) {
+ printf("No function found at 0x%0lx\n", addr);
+ return;
+ }
- /* addr is in function named fname, starting at fstart address, at offset (addr - fstart) */
+ printf("Function %s at 0x%lx starts at 0x%0lx and is %ld in size\n",
+ fname, addr, fstart, size);
}
...
--
diff --git a/Documentation/libtraceevent-handle.txt b/Documentation/libtraceevent-handle.txt
index 0d6afda..64528eb 100644
--- a/Documentation/libtraceevent-handle.txt
+++ b/Documentation/libtraceevent-handle.txt
@@ -3,7 +3,7 @@ libtraceevent(3)
NAME
----
-tep_alloc, tep_free,tep_ref, tep_unref,tep_get_ref - Create, destroy, manage
+tep_alloc, tep_free,tep_ref, tep_unref,tep_get_ref, tep_kbuffer - Create, destroy, manage
references of trace event parser context.
SYNOPSIS
@@ -40,6 +40,10 @@ it had used are cleaned up.
The *tep_ref_get()* functions gets the current references of the _tep_ handler.
+The *tep_kbuffer()* function allocates a kbuffer descriptor that can be used to
+parse raw data that is represented by the _tep_ handle descriptor. It must be freed
+with *kbuf_free(3)*.
+
RETURN VALUE
------------
*tep_alloc()* returns a pointer to a newly created tep_handle structure.
@@ -48,6 +52,9 @@ NULL is returned in case there is not enough free memory to allocate it.
*tep_ref_get()* returns the current references of _tep_.
If _tep_ is NULL, 0 is returned.
+*tep_kbuffer()* returns a kbuffer descriptor that can parse the raw data that
+represents the tep handle. Must be freed with *kbuf_free(3)*.
+
EXAMPLE
-------
[source,c]
diff --git a/Documentation/libtraceevent-kbuffer-create.txt b/Documentation/libtraceevent-kbuffer-create.txt
new file mode 100644
index 0000000..12e5d6c
--- /dev/null
+++ b/Documentation/libtraceevent-kbuffer-create.txt
@@ -0,0 +1,207 @@
+libtraceevent(3)
+================
+
+NAME
+----
+kbuffer_alloc, kbuffer_free, kbuffer_load_subbuffer, kbuffer_subbuffer_size, kbuffer_start_of_data - Creating of kbuffer element to parse
+the Linux kernel tracing ring buffer
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <kbuffer.h>*
+
+enum kbuffer_endian {
+ KBUFFER_ENDIAN_BIG,
+ KBUFFER_ENDIAN_LITTLE,
+ KBUFFER_ENDIAN_SAME_AS_HOST,
+};
+
+enum kbuffer_long_size {
+ KBUFFER_LSIZE_4,
+ KBUFFER_LSIZE_8,
+ KBUFFER_LSIZE_SAME_AS_HOST,
+};
+
+struct kbuffer;
+struct tep_handle;
+
+struct kbuffer pass:[*]*kbuffer_alloc*(enum kbuffer_long_size _size_, enum kbuffer_endian _endian_);
+void *kbuffer_free*(struct kbuffer pass:[*]_kbuf_);
+int *kbuffer_load_subbuffer*(struct kbuffer pass:[*]_kbuf_, void pass:[*]_subbuffer_);
+int *kbuffer_subbuffer_size*(struct kbuffer pass:[*]_kbuf);
+int *kbuffer_start_of_data*(struct kbuffer pass:[*]_kbuf_);
+--
+
+DESCRIPTION
+-----------
+These functions create a _kbuffer_ handle that can be used to parse the raw sub buffers
+of the Linux kernel tracing ring buffer. The ring buffer is found in the tracefs
+directory, and can be retrieved by *tracefs_instance_get_file(3)* at
+*per_cpu/cpuX/trace_pipe_raw* where *X* is replaced by the per CPU number of
+the specified ring buffer. The ring buffer inside the kernel is split up per
+CPU, such that the raw ring buffer must be retrieved per CPU as well.
+
+The *kbuffer_alloc()* will create a descriptor that can be used to manage a sub buffer
+read by the ring buffer. The _size_ parameter denotes what the word size is
+for the given buffer (note, this works from reading raw data from machines other
+than the machine that is calling this function). The _endian_ denotes the endian
+for the machine.
+
+If _endian_ is set to _KBUFFER_ENDIAN_SAME_AS_HOST_ the endian will be set to the same
+as the host endianess, which is useful when the application is reading the
+ring buffer data directly from the same machine it is running on.
+
+If _size_ is set to _KBUFFER_LSIZE_SAME_AS_HOST_, if the word size is 8, it will
+set the kbuffer descriptor to long size of 8. But if the size is 4, then it
+will then perform a *uname(2)* call, and if the _machine_ field has the string "64"
+in it, it will be set to 8 byte long size and not 4 byte. This is because the
+ring buffer long size is dependent on the kernel and not user space.
+
+The *kbuffer_free()* function will free the resources created by *kbuffer_alloc()*.
+
+The *kbuffer_load_subbuffer()* will take a _subbuffer_ which is a raw data blob
+from the tracefs *trace_pipe_raw* file. The Linux tracing ring buffer is broken up
+into sub buffers. Each sub buffer is as stand alone data segment that has all the
+information to split out the individual events and time stamps. This sub buffer
+is what kbuffer uses to walk the events.
+
+The *kbuffer_subbuffer_size()* returns the location of the end of the last event
+on the sub-buffer. It does not return the size of the sub-buffer itself.
+
+The *kbuffer_start_of_data()* function returns the offset of where the actual
+data load of the sub-buffer begins.
+
+RETURN VALUE
+------------
+*kbuffer_alloc()* returns an allocated kbuffer descriptor or NULL on error.
+The returned descriptor must be freed with *kbuffer_free()*
+
+*kbuffer_load_subbuffer()* returns 0 on success and -1 on error.
+
+*kbuffer_subbuffer_size()* returns the index on the subbuffer where the end
+of the last event is located.
+
+*kbuffer_start_of_data()* returns the offset of where the data begins on the
+sub-buffer loaded in _kbuf_.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <kbuffer.h>
+
+int main (int argc, char **argv)
+{
+ unsigned long long ts;
+ struct kbuffer *kbuf;
+ struct stat st;
+ char *buf;
+ void *event;
+ int ret;
+ int fd;
+ int i = 0;
+
+ if (argc < 2) {
+ printf("usage: %s raw-subbuffer-page\n", argv[0]);
+ printf(" Try: dd count=1 bs=4096 if=/sys/kernel/tracing/per_cpu/cpu0/trace_pipe_raw of=/tmp/file\n");
+ exit(0);
+ }
+
+ if (stat(argv[1], &st) < 0) {
+ perror("stat");
+ exit(-1);
+ }
+
+ buf = malloc(st.st_size);
+ if (!buf) {
+ perror("Allocating buffer");
+ exit(-1);
+ }
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ perror(argv[1]);
+ exit(-1);
+ }
+
+ ret = read(fd, buf, st.st_size);
+ if (ret < 0) {
+ perror("Reading buffer");
+ exit(-1);
+ }
+ close(fd);
+
+ kbuf = kbuffer_alloc(KBUFFER_ENDIAN_SAME_AS_HOST,
+ KBUFFER_LSIZE_SAME_AS_HOST);
+ if (!kbuf) {
+ perror("Creating kbuffer");
+ exit(-1);
+ }
+ ret = kbuffer_load_subbuffer(kbuf, buf);
+ if (ret < 0) {
+ perror("Loading sub bufer");
+ exit(-1);
+ }
+
+ if (kbuffer_subbuffer_size(kbuf) > st.st_size) {
+ fprintf(stderr, "kbuffer is bigger than raw size %d > %ld\n",
+ kbuffer_subbuffer_size(kbuf), st.st_size);
+ exit(-1);
+ }
+
+ printf("Kbuffer data starts at %d\n", kbuffer_start_of_data(kbuf));
+ do {
+ event = kbuffer_read_event(kbuf, &ts);
+ if (event) {
+ printf(" event %3d ts:%lld\n", i++, ts);
+ event = kbuffer_next_event(kbuf, NULL);
+ }
+ } while (event);
+
+ if (!event)
+ printf("Finished sub buffer\n");
+
+ kbuffer_free(kbuf);
+
+ return 0;
+}
+--
+FILES
+-----
+[verse]
+--
+*event-parse.h*
+ Header file to include in order to have access to the library APIs.
+*-ltraceevent*
+ Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtraceevent*(3), *trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
+--
+REPORTING BUGS
+--------------
+Report bugs to <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtraceevent is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtraceevent.git/
diff --git a/Documentation/libtraceevent-kbuffer-read.txt b/Documentation/libtraceevent-kbuffer-read.txt
new file mode 100644
index 0000000..68184ad
--- /dev/null
+++ b/Documentation/libtraceevent-kbuffer-read.txt
@@ -0,0 +1,246 @@
+libtraceevent(3)
+================
+
+NAME
+----
+kbuffer_read_event, kbuffer_next_event, kbuffer_missed_events, kbuffer_event_size, kbuffer_curr_size,
+kbuffer_curr_offset, kbuffer_curr_index -
+Functions to read through the kbuffer sub buffer.
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <kbuffer.h>*
+
+void pass:[*]*kbuffer_read_event*(struct kbuffer pass:[*]_kbuf_, unsigned long long pass:[*]_ts_);
+void pass:[*]*kbuffer_next_event*(struct kbuffer pass:[*]_kbuf_, unsigned long long pass:[*]_ts_);
+void pass:[*]*kbuffer_read_at_offset*(struct kbuffer pass:[*]_kbuf_, int _offset_, unsigned long long pass:[*]_ts_);
+int *kbuffer_missed_events*(struct kbuffer pass:[*]_kbuf_);
+int *kbuffer_event_size*(struct kbuffer pass:[*]_kbuf_);
+int *kbuffer_curr_size*(struct kbuffer pass:[*]_kbuf_);
+int *kbuffer_curr_offset*(struct kbuffer pass:[*]_kbuf_);
+int *kbuffer_curr_index*(struct kbuffer pass:[*]_kbuf_);
+--
+
+DESCRIPTION
+-----------
+The function *kbuffer_read_event()* reads the next event in the _kbuf_ descriptor
+and if _ts_ is non NULL, will place its timestamp into it. This does not modify the _kbuf_
+descriptor, and calling this function mulitple times will return the same result.
+
+The function *kbuffer_next_event()* will return the next event in the _kbuf_ descriptor.
+It will also set the _ts_ to the timestamp of the returned event. NULL is returned
+if there are no more events and _ts_ will be undefined. Note, if this is called directly
+after a *kbuffer_load_subbuffer()* then it will likely give an unexpected result, as it
+will return the second event and not the first event. Usually this function is only used
+to move to the next event and to know if there's any more events to read, and
+*kbuffer_read_event()* is always called first.
+
+The function *kbuffer_read_at_offset()* returns the event located at a given _offset_ from
+the beginning of the sub-buffer. This offset can be retrieved by *kbuffer_curr_offset()*.
+If _ts_ points to an unsigned long long, then it will be set to the event at the given
+offset's timestamp.
+
+If the sub-buffer had missed events before it, then *kbuffer_missed_events()* will return
+the non zero. If it returns -1, that means there were missed events, but the exact number
+of missed events is unknown. If it returns a positive number, then the number of missed events
+is the return value.
+
+The *kbuffer_event_size()* function returns the size of the data portion of the current event
+(the one that would be returned by *kbuffer_read_event()*.
+
+The *kbuffer_curr_size()* function returns the entire record size of the current event
+(the one that would be returned by *kbuffer_read_event()*. The difference here is that the
+return value includes the size of the event record meta data that is not part of what
+is returned by *kbuffer_read_event()*.
+
+The *kbuffer_curr_offset()* function returns the offset from the beginning of the sub-buffer
+of where the current event's meta data for the record begins. The first event will
+not be at offset zero. This offset can be used to retrieve the event with
+*kbuffer_read_at_offset()*.
+
+The *kbuffer_curr_index()* function returns the index from the beginning of the data
+portion of the sub-buffer where the current evnet's meta data is located.
+The first event will likely be zero, but may not be if there's a timestamp attached to it.
+
+RETURN VALUE
+------------
+*kbuffer_read_event()* returns the event that the _kbuf_ descriptor is currently at,
+or NULL if the last event was passed (by *kbuffer_next_event()*).
+
+*kbuffer_next_event()* returns the next event after the current event or NULL if there
+are no more events.
+
+*kbuffer_read_at_offset()* returns the event at a given _offset_ from the start of
+the sub-buffer stored in _kbuf_, or NULL if there exists no event. Note, _offset_
+only needs to be an offset that lands on the record, or is at the start of it. It does
+not need to be exactly at the beginning of the record.
+
+*kbuffer_missed_events()* returns 0 if there were no missed events before loaded sub-buffer.
+Returns -1 if there were an unknown number of missed events, or if the number of missed events
+is known, that number will be returned.
+
+*kbuffer_event_size()* returns the size of the data payload of the current event of _kbuf_.
+
+*kbuffer_curr_size()* returns the size of the entire record of the current event of _kbuf_.
+This includes the size of the meta data for that record.
+
+*kbuf_curr_offset()* returns the offset of the current record from the beginning of the _kbuf_
+sub-buffer.
+
+*kbuf_curr_index()* returns the index of the current record from the beginning of the _kbuf_
+data section.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <kbuffer.h>
+
+int main (int argc, char **argv)
+{
+ unsigned long long ts;
+ struct kbuffer *kbuf;
+ struct stat st;
+ char *buf;
+ void *event;
+ int save_offset = -1;
+ int record_size;
+ int offset;
+ int index;
+ int size;
+ int ret;
+ int fd;
+ int i = 0;
+
+ if (argc < 2) {
+ printf("usage: %s raw-subbuffer-page\n", argv[0]);
+ printf(" Try: dd count=1 bs=4096 if=/sys/kernel/tracing/per_cpu/cpu0/trace_pipe_raw of=/tmp/file\n");
+ exit(0);
+ }
+
+ if (stat(argv[1], &st) < 0) {
+ perror("stat");
+ exit(-1);
+ }
+
+ buf = malloc(st.st_size);
+ if (!buf) {
+ perror("Allocating buffer");
+ exit(-1);
+ }
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ perror(argv[1]);
+ exit(-1);
+ }
+
+ ret = read(fd, buf, st.st_size);
+ if (ret < 0) {
+ perror("Reading buffer");
+ exit(-1);
+ }
+ close(fd);
+
+ kbuf = kbuffer_alloc(KBUFFER_ENDIAN_SAME_AS_HOST,
+ KBUFFER_LSIZE_SAME_AS_HOST);
+ if (!kbuf) {
+ perror("Creating kbuffer");
+ exit(-1);
+ }
+ ret = kbuffer_load_subbuffer(kbuf, buf);
+ if (ret < 0) {
+ perror("Loading sub bufer");
+ exit(-1);
+ }
+
+ if (kbuffer_subbuffer_size(kbuf) > st.st_size) {
+ fprintf(stderr, "kbuffer is bigger than raw size %d > %ld\n",
+ kbuffer_subbuffer_size(kbuf), st.st_size);
+ exit(-1);
+ }
+
+ ret = kbuffer_missed_events(kbuf);
+ if (ret) {
+ if (ret > 0)
+ printf("Missed %d events before this buffer\n", ret);
+ else
+ printf("Missed unknown number of events before this buffer\n");
+ }
+ do {
+ event = kbuffer_read_event(kbuf, &ts);
+ if (event) {
+ record_size = kbuffer_curr_size(kbuf);
+ offset = kbuffer_curr_offset(kbuf);
+ index = kbuffer_curr_index(kbuf);
+ size = kbuffer_event_size(kbuf);
+
+ if (i == 20)
+ save_offset = offset;
+ printf(" event %3d ts:%lld\trecord_size:%d size:%d\tindex:%d offset:%d\n",
+ i++, ts, record_size, size, index, offset);
+ event = kbuffer_next_event(kbuf, NULL);
+ }
+ } while (event);
+
+ if (!event)
+ printf("Finished sub buffer\n");
+
+ if (save_offset > 0) {
+ event = kbuffer_read_at_offset(kbuf, save_offset, &ts);
+ if (!event) {
+ fprintf(stderr, "Funny, can't find event 20 at offset %d\n", save_offset);
+ exit(-1);
+ }
+ record_size = kbuffer_curr_size(kbuf);
+ offset = kbuffer_curr_offset(kbuf);
+ index = kbuffer_curr_index(kbuf);
+ size = kbuffer_event_size(kbuf);
+
+ printf("\n saved event 20 ts:%lld\trecord_size:%d size:%d\tindex:%d offset:%d\n\n",
+ ts, record_size, size, index, offset);
+ }
+ kbuffer_free(kbuf);
+
+ return 0;
+}
+--
+FILES
+-----
+[verse]
+--
+*event-parse.h*
+ Header file to include in order to have access to the library APIs.
+*-ltraceevent*
+ Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtraceevent*(3), *trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
+--
+REPORTING BUGS
+--------------
+Report bugs to <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtraceevent is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtraceevent.git/
diff --git a/Documentation/libtraceevent-kbuffer-timestamp.txt b/Documentation/libtraceevent-kbuffer-timestamp.txt
new file mode 100644
index 0000000..d7de225
--- /dev/null
+++ b/Documentation/libtraceevent-kbuffer-timestamp.txt
@@ -0,0 +1,208 @@
+libtraceevent(3)
+================
+
+NAME
+----
+kbuffer_timestamp, kbuffer_subbuf_timestamp -
+Functions that read various data of a kbuffer descriptor
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <kbuffer.h>*
+
+unsigned long long *kbuffer_timestamp*(struct kbuffer pass:[*]_kbuf_);
+unsigned long long *kbuffer_subbuf_timestamp*(struct kbuffer pass:[*]_kbuf_, void pass:[*]_subbuf_);
+--
+
+DESCRIPTION
+-----------
+The function *kbuffer_timestamp()* returns the timestamp of the current event of _kbuf_.
+
+The function *kbuffer_subbuf_timestamp()* returns the timestamp for the sub-buffer
+that was loaded in _kbuf_. This usually is (but not guaranteed to be) the timestamp
+of the first event on the sub-buffer.
+
+The function *kbuffer_start_of_data()* returns the offset of where the delta
+
+RETURN VALUE
+------------
+*kbuffer_read_event()* returns the event that the _kbuf_ descriptor is currently at,
+or NULL if the last event was passed (by *kbuffer_next_event()*).
+
+*kbuffer_next_event()* returns the next event after the current event or NULL if there
+are no more events.
+
+*kbuffer_read_at_offset()* returns the event at a given _offset_ from the start of
+the sub-buffer stored in _kbuf_, or NULL if there exists no event. Note, _offset_
+only needs to be an offset that lands on the record, or is at the start of it. It does
+not need to be exactly at the beginning of the record.
+
+*kbuffer_missed_events()* returns 0 if there were no missed events before loaded sub-buffer.
+Returns -1 if there were an unknown number of missed events, or if the number of missed events
+is known, that number will be returned.
+
+*kbuffer_event_size()* returns the size of the data payload of the current event of _kbuf_.
+
+*kbuffer_curr_size()* returns the size of the entire record of the current event of _kbuf_.
+This includes the size of the meta data for that record.
+
+*kbuf_curr_offset()* returns the offset of the current record from the beginning of the _kbuf_
+sub-buffer.
+
+*kbuf_curr_index()* returns the index of the current record from the beginning of the _kbuf_
+data section.
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <kbuffer.h>
+
+int main (int argc, char **argv)
+{
+ unsigned long long ts;
+ struct kbuffer *kbuf;
+ struct stat st;
+ char *buf;
+ void *event;
+ int save_offset = -1;
+ int record_size;
+ int offset;
+ int index;
+ int size;
+ int ret;
+ int fd;
+ int i = 0;
+
+ if (argc < 2) {
+ printf("usage: %s raw-subbuffer-page\n", argv[0]);
+ printf(" Try: dd count=1 bs=4096 if=/sys/kernel/tracing/per_cpu/cpu0/trace_pipe_raw of=/tmp/file\n");
+ exit(0);
+ }
+
+ if (stat(argv[1], &st) < 0) {
+ perror("stat");
+ exit(-1);
+ }
+
+ buf = malloc(st.st_size);
+ if (!buf) {
+ perror("Allocating buffer");
+ exit(-1);
+ }
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ perror(argv[1]);
+ exit(-1);
+ }
+
+ ret = read(fd, buf, st.st_size);
+ if (ret < 0) {
+ perror("Reading buffer");
+ exit(-1);
+ }
+ close(fd);
+
+ kbuf = kbuffer_alloc(KBUFFER_ENDIAN_SAME_AS_HOST,
+ KBUFFER_LSIZE_SAME_AS_HOST);
+ if (!kbuf) {
+ perror("Creating kbuffer");
+ exit(-1);
+ }
+ ret = kbuffer_load_subbuffer(kbuf, buf);
+ if (ret < 0) {
+ perror("Loading sub bufer");
+ exit(-1);
+ }
+
+ if (kbuffer_subbuffer_size(kbuf) > st.st_size) {
+ fprintf(stderr, "kbuffer is bigger than raw size %d > %ld\n",
+ kbuffer_subbuffer_size(kbuf), st.st_size);
+ exit(-1);
+ }
+
+ ret = kbuffer_missed_events(kbuf);
+ if (ret) {
+ if (ret > 0)
+ printf("Missed %d events before this buffer\n", ret);
+ else
+ printf("Missed unknown number of events before this buffer\n");
+ }
+ do {
+ event = kbuffer_read_event(kbuf, &ts);
+ if (event) {
+ record_size = kbuffer_curr_size(kbuf);
+ offset = kbuffer_curr_offset(kbuf);
+ index = kbuffer_curr_index(kbuf);
+ size = kbuffer_event_size(kbuf);
+
+ if (i == 20)
+ save_offset = offset;
+ printf(" event %3d ts:%lld\trecord_size:%d size:%d\tindex:%d offset:%d\n",
+ i++, ts, record_size, size, index, offset);
+ event = kbuffer_next_event(kbuf, NULL);
+ }
+ } while (event);
+
+ if (!event)
+ printf("Finished sub buffer\n");
+
+ if (save_offset > 0) {
+ event = kbuffer_read_at_offset(kbuf, save_offset, &ts);
+ if (!event) {
+ fprintf(stderr, "Funny, can't find event 20 at offset %d\n", save_offset);
+ exit(-1);
+ }
+ record_size = kbuffer_curr_size(kbuf);
+ offset = kbuffer_curr_offset(kbuf);
+ index = kbuffer_curr_index(kbuf);
+ size = kbuffer_event_size(kbuf);
+
+ printf("\n saved event 20 ts:%lld\trecord_size:%d size:%d\tindex:%d offset:%d\n\n",
+ ts, record_size, size, index, offset);
+ }
+ kbuffer_free(kbuf);
+
+ return 0;
+}
+--
+FILES
+-----
+[verse]
+--
+*event-parse.h*
+ Header file to include in order to have access to the library APIs.
+*-ltraceevent*
+ Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtraceevent*(3), *trace-cmd*(1)
+
+AUTHOR
+------
+[verse]
+--
+*Steven Rostedt* <rostedt@goodmis.org>, author of *libtraceevent*.
+--
+REPORTING BUGS
+--------------
+Report bugs to <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtraceevent is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtraceevent.git/
diff --git a/Documentation/libtraceevent-kvm-plugin.txt b/Documentation/libtraceevent-kvm-plugin.txt
new file mode 100644
index 0000000..a02e786
--- /dev/null
+++ b/Documentation/libtraceevent-kvm-plugin.txt
@@ -0,0 +1,252 @@
+libtraceevent(3)
+================
+
+NAME
+----
+tep_plugin_kvm_get_func, tep_plugin_kvm_put_func - Add function name for instruction pointer of kvm plugin
+
+SYNOPSIS
+--------
+[verse]
+--
+*#include <event-parse.h>*
+
+const char pass:[*]*tep_plugin_kvm_get_func*(struct tep_event pass:[*]event,
+ struct tep_record pass:[*]record,
+ unsigned long long pass:[*]paddr);
+void *tep_plugin_kvm_put_func*(const char pass:[*]func);
+--
+
+DESCRIPTION
+-----------
+The functions *tep_plugin_kvm_get_func()* and *tep_plugin_kvm_put_func()*
+are not to be called by an application, but instead are to be defined by
+an application.
+
+Certain events (like kvm_exit and kvm_entry) have the instruction pointer
+of where in the guest the context changed from guest to host. As the host
+only knows the instruction pointer and does not have information about what
+function in the guest that instruction pointer belongs to, it can only print
+the address.
+
+But the application may have more information about the guest, and know where
+the guest was when the exit occurred, and also even know the function name
+of that address.
+
+The KVM plugin for libtraceevent is called on these events, and then calls
+*tep_plugin_kvm_get_func()* to see if that function can resolve the instruction
+pointer address to a real function name. If the return is non NULL, it will
+print the function in the output for that event.
+
+These functions are currently defined as weak functions within the plugin, as
+to not require them to be defined elsewhere. For an application to override
+the weak function, it will need to define the function in a file that gets
+compiled with *-rdynamic*. That will tell the dynamic linker to examine that
+object file and use function names to resolve weak functions in other shared
+objects (in this case the KVM plugin shared object).
+
+If the application defines *tep_plugin_kvm_get_func()*, it must use the above
+prototype. The _event_ will hold the KVM event that has the instruction pointer
+field. The _record_ will be the instance of that event. The application's function
+does not need to use these parameters, but they may be useful for finding the
+function name for the address. The _paddr_ is a pointer to a 64 bit value (where
+only 32 bits may be used on 32 bit machines). This value is the instruction
+pointer to look up. If the application knows the start address of the function
+as well, it can set _paddr_ to that address, and the KVM plugin will also
+append a "+offset" to the function name where the offset is the original
+value in _paddr_ minus the value in _paddr_ when it is called. Finally,
+the application should return the function name as a nul terminated string
+if one is found.
+
+If the returned string of *tep_plugin_kvm_get_func()* was allocated, the KVM plugin
+will call *tep_plugin_kvm_put_func()* when it is through with it, passing the
+value returned by *tep_plugin_kvm_get_func()* as _func_. This allows the application
+to free it if necessary.
+
+RETURN VALUE
+------------
+The *tep_plugin_kvm_get_func()* is not to be called by the application but instead
+is to be defined by the application. It should return a nul terminated string representing
+the function for the given instruction pointer passed to it by reference in _paddr_. It
+can then optionally update the _paddr_ to a value that holds the start of the function.
+The string returned may be freed by the *tep_plugin_kvm_put_func()* that the application
+should define to clean up the string.
+
+The below example needs to be compiled with the *-rdynamic* flag so that the dynamic
+linker can resolve the *tep_plugin_kvm_get_func()* and *tep_plugin_kvm_put_func()* functions.
+
+When run against a trace.dat file produced by *trace-cmd(1)* recording the kvm_exit and
+kvm_entry events on a guest, and then the guest's /proc/kallsyms file is passed as the
+second parameter, the output produced will look something like:
+
+[source,c]
+--
+CPU 0/KVM-20407 83156.177626 [000] kvm_exit reason APIC_ACCESS rip 0xffffffffb0056ee2 exit native_apic_mem_write+0x2 info 10b0 0
+CPU 0/KVM-20407 83156.177632 [000] kvm_entry vcpu 0 rip 0xffffffffb0056ee8 enter native_apic_mem_write+0x8
+--
+
+But without those callbacks, it would look like:
+
+[source,c]
+--
+CPU 0/KVM-20407 83156.177626 [000] kvm_exit reason APIC_ACCESS rip 0xffffffffb0056ee2 info 10b0 0
+CPU 0/KVM-20407 83156.177632 [000] kvm_entry vcpu 0 rip 0xffffffffb0056ee8
+--
+
+EXAMPLE
+-------
+[source,c]
+--
+#include <stdio.h>
+#include <stdlib.h>
+#include <event-parse.h>
+#include <trace-cmd.h>
+#include <sys/stat.h>
+
+static struct tep_handle *tep;
+
+const char *tep_plugin_kvm_get_func(struct tep_event *event, struct tep_record *record,
+ unsigned long long *paddr)
+{
+ const char *func;
+ char *event_func;
+ char *ename;
+
+ func = tep_find_function(tep, *paddr);
+ if (!func)
+ return NULL;
+
+ if (strcmp(event->name, "kvm_exit") == 0)
+ ename = "exit";
+ else
+ ename = "enter";
+
+ /*
+ * Normally, passing back func directly is sufficient and then
+ * tep_plugin_kvm_put_func() would not be required. But this example
+ * is showing how to handle allocation of the returned string.
+ */
+ event_func = malloc(strlen(ename) + strlen(func) + 2);
+ if (!event_func)
+ return NULL;
+ sprintf(event_func, "%s %s", ename, func);
+
+ *paddr = tep_find_function_address(tep, *paddr);
+
+ return event_func;
+}
+
+void tep_plugin_kvm_put_func(const char *func)
+{
+ char *f = (char *)func;
+
+ free(f);
+}
+
+static int show_event(struct tracecmd_input *handle, struct tep_event *event,
+ struct tep_record *record, int cpu, void *data)
+{
+ static struct trace_seq seq;
+ tep = data;
+
+ if (!seq.buffer)
+ trace_seq_init(&seq);
+
+ trace_seq_reset(&seq);
+ tep_print_event(tracecmd_get_tep(handle), &seq, record,
+ "%s-%d\t%6.1000d [%03d] %s\t%s\n",
+ TEP_PRINT_COMM, TEP_PRINT_PID,
+ TEP_PRINT_TIME, TEP_PRINT_CPU,
+ TEP_PRINT_NAME, TEP_PRINT_INFO);
+ trace_seq_terminate(&seq);
+ trace_seq_do_printf(&seq);
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ struct tracecmd_input *handle;
+ struct tep_handle *guest_tep;
+ struct stat st;
+ FILE *fp;
+ char *buf;
+
+ if (argc < 3) {
+ printf("usage: trace.dat guest_kallsyms_file\n");
+ exit(-1);
+ }
+
+ handle = tracecmd_open(argv[1], 0);
+ if (!handle) {
+ perror(argv[1]);
+ exit(-1);
+ }
+
+ /* Just for kallsyms parsing */
+ guest_tep = tep_alloc();
+ if (!guest_tep)
+ exit(-1);
+
+ if (stat(argv[2], &st) < 0) {
+ perror(argv[2]);
+ exit(-1);
+ }
+
+ buf = malloc(st.st_size + 1);
+ if (!buf)
+ exit(-1);
+
+ fp = fopen(argv[2], "r");
+ if (!fp) {
+ perror(argv[2]);
+ exit(-1);
+ }
+
+ if (fread(buf, st.st_size, 1, fp) < 0) {
+ perror(argv[2]);
+ exit(-1);
+ }
+
+ buf[st.st_size] = '\0';
+
+ if (tep_parse_kallsyms(guest_tep, buf) < 0) {
+ printf("Failed to parse %s\n", argv[2]);
+ exit(-1);
+ }
+ free(buf);
+
+ tracecmd_follow_event(handle, "kvm", "kvm_exit", show_event, guest_tep);
+ tracecmd_follow_event(handle, "kvm", "kvm_entry", show_event, guest_tep);
+
+ tracecmd_iterate_events(handle, NULL, 0, NULL, NULL);
+
+ tep_free(guest_tep);
+ tracecmd_close(handle);
+}
+--
+
+FILES
+-----
+[verse]
+--
+*event-parse.h*
+ Header file to include in order to have access to the library APIs.
+*-ltraceevent*
+ Linker switch to add when building a program that uses the library.
+--
+
+SEE ALSO
+--------
+*libtraceevent*(3), *trace-cmd*(1)
+
+REPORTING BUGS
+--------------
+Report bugs to <linux-trace-devel@vger.kernel.org>
+
+LICENSE
+-------
+libtraceevent is Free Software licensed under the GNU LGPL 2.1
+
+RESOURCES
+---------
+https://git.kernel.org/pub/scm/libs/libtrace/libtraceevent.git/
diff --git a/Documentation/libtraceevent-page_size.txt b/Documentation/libtraceevent-page_size.txt
index 7ffeb79..6d0dd36 100644
--- a/Documentation/libtraceevent-page_size.txt
+++ b/Documentation/libtraceevent-page_size.txt
@@ -3,7 +3,7 @@ libtraceevent(3)
NAME
----
-tep_get_page_size, tep_set_page_size - Get / set the size of a memory page on
+tep_get_page_size, tep_set_page_size, tep_get_sub_buffer_size - Get / set the size of a memory page on
the machine, where the trace is generated
SYNOPSIS
@@ -14,6 +14,7 @@ SYNOPSIS
int *tep_get_page_size*(struct tep_handle pass:[*]_tep_);
void *tep_set_page_size*(struct tep_handle pass:[*]_tep_, int _page_size_);
+int *tep_get_sub_buffer_size*(struct tep_handle pass:[*]_tep_);
--
DESCRIPTION
@@ -27,10 +28,17 @@ memory page on the machine, where the trace is generated.
The _tep_ argument is trace event parser context.
The _page_size_ argument is the size of a memory page, in bytes.
+The *tep_get_sub_buffer_size()* returns the size of each "sub buffer" of the
+ring buffer. The Linux kernel ring buffer is broken up into sections called
+sub buffers. This returns the size of those buffers.
+
RETURN VALUE
------------
The *tep_get_page_size()* function returns size of the memory page, in bytes.
+The *tep_get_sub_buffer_size()* function returns the number of bytes each sub
+buffer is made up of.
+
EXAMPLE
-------
[source,c]
diff --git a/Documentation/libtraceevent-plugins.txt b/Documentation/libtraceevent-plugins.txt
index 24d8ad8..4ca78d4 100644
--- a/Documentation/libtraceevent-plugins.txt
+++ b/Documentation/libtraceevent-plugins.txt
@@ -3,7 +3,8 @@ libtraceevent(3)
NAME
----
-tep_load_plugins, tep_unload_plugins, tep_load_plugins_hook, tep_add_plugin_path - Load / unload traceevent plugins.
+tep_load_plugins, tep_unload_plugins, tep_load_plugins_hook, tep_add_plugin_path,
+tep_plugin_add_option - Load / unload traceevent plugins.
SYNOPSIS
--------
@@ -21,6 +22,7 @@ void *tep_load_plugins_hook*(struct tep_handle pass:[*]_tep_, const char pass:[*
void pass:[*]_data_);
int *tep_add_plugin_path*(struct tep_handle pass:[*]tep, char pass:[*]path,
enum tep_plugin_load_priority prio);
+int *tep_plugin_add_option*(const char pass:[*]_name_, const char pass:[*]_val_);
--
DESCRIPTION
@@ -76,6 +78,11 @@ plugin wins. The priority can be:
Where the plugins in TEP_PLUGIN_LAST" will take precedence over the
plugins in the other directories.
+The *tep_plugin_add_option()* sets options defined by a plugin. The _name_ is the
+name of the option to set to _val_. Plugins can add options to change its behavior
+and *tep_plugin_add_option()* is used by the application to make those modifications.
+
+
RETURN VALUE
------------
The *tep_load_plugins()* function returns a list of successfully loaded plugins,
diff --git a/Documentation/libtraceevent.txt b/Documentation/libtraceevent.txt
index 6476070..0502769 100644
--- a/Documentation/libtraceevent.txt
+++ b/Documentation/libtraceevent.txt
@@ -26,10 +26,12 @@ Management of tep handler data structure and access of its members:
void *tep_set_long_size*(struct tep_handle pass:[*]_tep_, int _long_size_);
int *tep_get_page_size*(struct tep_handle pass:[*]_tep_);
void *tep_set_page_size*(struct tep_handle pass:[*]_tep_, int _page_size_);
+ int *tep_get_sub_buffer_size*(struct tep_handle pass:[*]_tep_);
int *tep_get_header_page_size*(struct tep_handle pass:[*]_tep_);
int *tep_get_header_timestamp_size*(struct tep_handle pass:[*]_tep_);
bool *tep_is_old_format*(struct tep_handle pass:[*]_tep_);
int *tep_strerror*(struct tep_handle pass:[*]_tep_, enum tep_errno _errnum_, char pass:[*]_buf_, size_t _buflen_);
+ struct kbuffer pass:[*]*tep_kbuffer*(struct tep_handle pass:[*]:_tep_);
Register / unregister APIs:
int *tep_register_function*(struct tep_handle pass:[*]_tep_, char pass:[*]_name_, unsigned long long _addr_, char pass:[*]_mod_);
@@ -38,6 +40,19 @@ Register / unregister APIs:
int *tep_register_print_string*(struct tep_handle pass:[*]_tep_, const char pass:[*]_fmt_, unsigned long long _addr_);
int *tep_register_print_function*(struct tep_handle pass:[*]_tep_, tep_func_handler _func_, enum tep_func_arg_type _ret_type_, char pass:[*]_name_, _..._);
int *tep_unregister_print_function*(struct tep_handle pass:[*]_tep_, tep_func_handler _func_, char pass:[*]_name_);
+ int *tep_get_function_count*(struct tep_handle *_tep_);
+
+Trace printk parsing:
+ void *tep_print_printk*(struct tep_handle pass:[*]tep);
+ void *tep_print_funcs*(struct tep_handle pass:[*]tep);
+ void *tep_set_test_filters*(struct tep_handle pass:[*]tep, int test_filters);
+ void *tep_plugin_print_options*(struct trace_seq pass:[*]s);
+ int *tep_plugin_add_option*(const char pass:[*]_name_, const char pass:[*]_val_);
+
+Meta data parsing:
+ int *tep_parse_saved_cmdlines*(struct tep_handle pass:[*]_tep_, const char pass:[*]_buf_);
+ int *tep_parse_printk_formats*(struct tep_handle pass:[*]_tep_, const char pass:[*]_buf_);
+ int *tep_parse_kallsyms*(struct tep_handle pass:[*]_tep_, const char pass:[*]_buf_);
Plugins management:
struct tep_plugin_list pass:[*]*tep_load_plugins*(struct tep_handle pass:[*]_tep_);
@@ -47,6 +62,14 @@ Plugins management:
int *tep_plugin_add_options*(const char pass:[*]_name_, struct tep_plugin_option pass:[*]_options_);
void *tep_plugin_remove_options*(struct tep_plugin_option pass:[*]_options_);
void *tep_print_plugins*(struct trace_seq pass:[*]_s_, const char pass:[*]_prefix_, const char pass:[*]_suffix_, const struct tep_plugin_list pass:[*]_list_);
+ void *tep_load_plugins_hook*(struct tep_handle pass:[*]_tep_, const char pass:[*]_suffix_,
+ void (pass:[*]_load_plugin_)(struct tep_handle pass:[*]tep,
+ const char pass:[*]path,
+ const char pass:[*]name,
+ void pass:[*]data),
+ void pass:[*]_data_);
+ int *tep_add_plugin_path*(struct tep_handle pass:[*]tep, char pass:[*]path,
+ enum tep_plugin_load_priority prio);
Event related APIs:
struct tep_event pass:[*]*tep_get_event*(struct tep_handle pass:[*]_tep_, int _index_);
@@ -93,6 +116,8 @@ Functions resolver:
void *tep_reset_function_resolver*(struct tep_handle pass:[*]_tep_);
const char pass:[*]*tep_find_function*(struct tep_handle pass:[*]_tep_, unsigned long long _addr_);
unsigned long long *tep_find_function_address*(struct tep_handle pass:[*]_tep_, unsigned long long _addr_);
+ int *tep_find_function_info*(struct tep_handle pass:[*]_tep_, unsigned long long _addr_, const char pass:[**]_name_,
+ unsigned long long pass:[*]_start_, unsigned long pass:[*]_size_);
Filter management:
struct tep_event_filter pass:[*]*tep_filter_alloc*(struct tep_handle pass:[*]_tep_);
@@ -132,6 +157,12 @@ Endian related APIs:
Control library logs:
int *tep_set_loglevel*(enum tep_loglevel _level_);
+KVM plugin calllbacks: (Defined by the application and complied with -rdynamic)
+ const char pass:[*]*tep_plugin_kvm_get_func*(struct tep_event pass:[*]event,
+ struct tep_record pass:[*]record,
+ unsigned long long pass:[*]paddr);
+ void *tep_plugin_kvm_put_func*(const char pass:[*]func);
+
Trace sequences:
*#include <trace-seq.h>*
void *trace_seq_init*(struct trace_seq pass:[*]_s_);
@@ -144,6 +175,24 @@ Trace sequences:
void *trace_seq_terminate*(struct trace_seq pass:[*]_s_);
int *trace_seq_do_fprintf*(struct trace_seq pass:[*]_s_, FILE pass:[*]_fp_);
int *trace_seq_do_printf*(struct trace_seq pass:[*]_s_);
+
+kbuffer parsing:
+#include <kbuffer.h>
+ struct kbuffer pass:[*]*kbuffer_alloc*(enum kbuffer_long_size _size_, enum kbuffer_endian _endian_);
+ void *kbuffer_free*(struct kbuffer pass:[*]_kbuf_);
+ int *kbuffer_load_subbuffer*(struct kbuffer pass:[*]_kbuf_, void pass:[*]_subbuffer_);
+ int *kbuffer_subbuffer_size*(struct kbuffer pass:[*]_kbuf);
+ int *kbuffer_start_of_data*(struct kbuffer pass:[*]_kbuf_);
+ unsigned long long *kbuffer_timestamp*(struct kbuffer pass:[*]_kbuf_);
+ unsigned long long *kbuffer_subbuf_timestamp*(struct kbuffer pass:[*]_kbuf_, void pass:[*]_subbuf_);
+ void pass:[*]*kbuffer_read_event*(struct kbuffer pass:[*]_kbuf_, unsigned long long pass:[*]_ts_);
+ void pass:[*]*kbuffer_next_event*(struct kbuffer pass:[*]_kbuf_, unsigned long long pass:[*]_ts_);
+ void pass:[*]*kbuffer_read_at_offset*(struct kbuffer pass:[*]_kbuf_, int _offset_, unsigned long long pass:[*]_ts_);
+ int *kbuffer_missed_events*(struct kbuffer pass:[*]_kbuf_);
+ int *kbuffer_event_size*(struct kbuffer pass:[*]_kbuf_);
+ int *kbuffer_curr_size*(struct kbuffer pass:[*]_kbuf_);
+ int *kbuffer_curr_offset*(struct kbuffer pass:[*]_kbuf_);
+ int *kbuffer_curr_index*(struct kbuffer pass:[*]_kbuf_);
--
DESCRIPTION
diff --git a/METADATA b/METADATA
index e07e64d..764071d 100644
--- a/METADATA
+++ b/METADATA
@@ -1,14 +1,19 @@
-name: "libtraceevent"
-description:
- "libtraceevent is a library that provides APIs to access and configure "
- "kernel trace events through the tracefs filesystem."
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update libtraceevent
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+name: "libtraceevent"
+description: "libtraceevent is a library that provides APIs to access and configure kernel trace events through the tracefs filesystem."
third_party {
url {
type: GIT
value: "https://git.kernel.org/pub/scm/libs/libtrace/libtraceevent.git"
}
- version: "libtraceevent-1.5.2"
- last_upgrade_date { year: 2022 month: 5 day: 02 }
+ version: "libtraceevent-1.7.1"
license_type: RESTRICTED
+ last_upgrade_date {
+ year: 2023
+ month: 1
+ day: 18
+ }
}
diff --git a/Makefile b/Makefile
index e99e7a9..20d90be 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
# libtraceevent version
EP_VERSION = 1
-EP_PATCHLEVEL = 6
-EP_EXTRAVERSION = dev
+EP_PATCHLEVEL = 7
+EP_EXTRAVERSION = 1
EVENT_PARSE_VERSION = $(EP_VERSION).$(EP_PATCHLEVEL).$(EP_EXTRAVERSION)
MAKEFLAGS += --no-print-directory
@@ -46,8 +46,8 @@ endif
libdir_relative ?= $(libdir_relative_temp)
prefix ?= /usr/local
-libdir = $(prefix)/$(libdir_relative)
-man_dir = $(prefix)/share/man
+libdir ?= $(prefix)/$(libdir_relative)
+man_dir ?= $(prefix)/share/man
man_dir_SQ = '$(subst ','\'',$(man_dir))'
pkgconfig_dir ?= $(word 1,$(shell $(PKG_CONFIG) \
--variable pc_path pkg-config | tr ":" " "))
@@ -130,7 +130,7 @@ else
CFLAGS := -g -Wall
endif
-LIBS = -ldl
+LIBS ?= -ldl
export LIBS
set_plugin_dir := 1
@@ -164,6 +164,9 @@ override CFLAGS += -fPIC
override CFLAGS += $(CONFIG_FLAGS) $(INCLUDES) $(PLUGIN_DIR_SQ)
override CFLAGS += $(udis86-flags) -D_GNU_SOURCE
+# Make sure 32 bit stat() works on large file systems
+override CFLAGS += -D_FILE_OFFSET_BITS=64
+
ifeq ($(VERBOSE),1)
Q =
else
@@ -378,7 +381,7 @@ uninstall: $(BUILD_OUTPUT)/build_uninstall
@$(foreach file,$(shell cat $(BUILD_OUTPUT)/build_uninstall),$(call uninstall_file,$(file)))
PHONY += doc
-doc:
+doc: check_doc
$(Q)$(call descend,$(src)/Documentation,)
PHONY += doc-clean
@@ -389,6 +392,9 @@ PHONY += doc-install
doc-install:
$(Q)$(call descend,$(src)/Documentation,install)
+check_doc: force
+ $(Q)$(src)/check-manpages.sh $(src)/Documentation
+
PHONY += doc-uninstall
doc-uninstall:
diff --git a/check-manpages.sh b/check-manpages.sh
new file mode 100755
index 0000000..4e9850f
--- /dev/null
+++ b/check-manpages.sh
@@ -0,0 +1,78 @@
+#!/bin/bash
+# SPDX-License-Identifier: LGPL-2.1
+# Copyright (C) 2022, Google Inc, Steven Rostedt <rostedt@goodmis.org>
+#
+# This checks if any function is listed in a man page that is not listed
+# in the main man page.
+
+if [ $# -lt 1 ]; then
+ echo "usage: check-manpages man-page-path"
+ exit 1
+fi
+
+cd $1
+
+MAIN=libtraceevent
+MAIN_FILE=${MAIN}.txt
+
+PROCESSED=""
+
+# Ignore man pages that do not contain functions
+IGNORE=""
+
+for man in ${MAIN}-*.txt; 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
+ if ! grep -q '\*'${a}'\*' $MAIN_FILE; then
+ if [ "$last" == "" ]; then
+ echo
+ fi
+ if [ "$last" != "$man" ]; then
+ echo "Missing functions from $MAIN_FILE that are in $man"
+ last=$man
+ fi
+ echo " ${a}"
+ fi
+ done
+done
+
+DEPRECATED="*tep_print_field*"
+
+# Should not be used by applications, only internal use by trace-cmd
+IGNORE="*kbuffer_set_old_format* *kbuffer_raw_get* *kbuffer_ptr_delta* *kbuffer_translate_data*"
+
+HEADER=event-parse.h
+
+sed -ne 's/^[a-z].*[ \*]\([a-z_][a-z_]*\)(.*/\1/p' -e 's/^\([a-z_][a-z_]*\)(.*/\1/p' ../include/traceevent/{event-parse,trace-seq,kbuffer}.h | while read f; do
+ if ! grep -q '\*'${f}'\*' $MAIN_FILE; then
+ if [ "${DEPRECATED/\*$f\*/}" != "${DEPRECATED}" ]; then
+ continue;
+ fi
+ if [ "${IGNORE/\*$f\*/}" != "${IGNORE}" ]; then
+ continue;
+ fi
+ for head in event-parse.h trace-seq.h kbuffer.h; do
+ if grep -q $f ../include/traceevent/$head; then
+ if [ "$HEADER" != "$head" ]; then
+ last=""
+ HEADER=$head
+ break
+ fi
+ fi
+ done
+ if [ "$last" == "" ]; then
+ echo
+ echo "Missing functions from $MAIN_FILE that are in $HEADER"
+ last=$f
+ fi
+ echo " ${f}"
+ fi
+done
diff --git a/include/traceevent/event-parse.h b/include/traceevent/event-parse.h
index 0b911e1..2171ad7 100644
--- a/include/traceevent/event-parse.h
+++ b/include/traceevent/event-parse.h
@@ -14,6 +14,10 @@
#include "trace-seq.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#ifndef __maybe_unused
#define __maybe_unused __attribute__((unused))
#endif
@@ -240,6 +244,7 @@ enum tep_print_arg_type {
TEP_PRINT_BITMASK,
TEP_PRINT_DYNAMIC_ARRAY_LEN,
TEP_PRINT_HEX_STR,
+ TEP_PRINT_CPUMASK,
};
struct tep_print_arg {
@@ -523,6 +528,9 @@ struct tep_format_field *tep_find_any_field(struct tep_event *event, const char
const char *tep_find_function(struct tep_handle *tep, unsigned long long addr);
unsigned long long
tep_find_function_address(struct tep_handle *tep, unsigned long long addr);
+int tep_find_function_info(struct tep_handle *tep, unsigned long long addr,
+ const char **name, unsigned long long *start,
+ unsigned long *size);
unsigned long long tep_read_number(struct tep_handle *tep, const void *ptr, int size);
int tep_read_number_field(struct tep_format_field *field, const void *data,
unsigned long long *value);
@@ -594,6 +602,8 @@ void tep_ref(struct tep_handle *tep);
void tep_unref(struct tep_handle *tep);
int tep_get_ref(struct tep_handle *tep);
+struct kbuffer *tep_kbuffer(struct tep_handle *tep);
+
/* for debugging */
void tep_print_funcs(struct tep_handle *tep);
void tep_print_printk(struct tep_handle *tep);
@@ -774,8 +784,40 @@ enum tep_loglevel {
};
void tep_set_loglevel(enum tep_loglevel level);
+/*
+ * Part of the KVM plugin. Will pass the current @event and @record
+ * as well as a pointer to the address to a guest kernel function.
+ * This is currently a weak function defined in the KVM plugin and
+ * should never be called. But a tool can override it, and this will
+ * be called when the kvm plugin has an address it needs the function
+ * name of.
+ *
+ * This function should return the function name for the given address
+ * and optionally, it can update @paddr to include the start of the function
+ * such that the kvm plugin can include an offset.
+ *
+ * For an application to be able to override the weak version in the
+ * plugin, it must be compiled with the gcc -rdynamic option that will
+ * allow the dynamic linker to use the application's function to
+ * override this callback.
+ */
+const char *tep_plugin_kvm_get_func(struct tep_event *event,
+ struct tep_record *record,
+ unsigned long long *paddr);
+
+/*
+ * tep_plugin_kvm_put_func() is another weak function that can be used
+ * to call back into the application if the function name returned by
+ * tep_plugin_kvm_get_func() needs to be freed.
+ */
+void tep_plugin_kvm_put_func(const char *func);
+
/* DEPRECATED */
void tep_print_field(struct trace_seq *s, void *data,
struct tep_format_field *field);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _PARSE_EVENTS_H */
diff --git a/include/traceevent/kbuffer.h b/include/traceevent/kbuffer.h
index a2b5220..ca638bc 100644
--- a/include/traceevent/kbuffer.h
+++ b/include/traceevent/kbuffer.h
@@ -13,11 +13,13 @@
enum kbuffer_endian {
KBUFFER_ENDIAN_BIG,
KBUFFER_ENDIAN_LITTLE,
+ KBUFFER_ENDIAN_SAME_AS_HOST,
};
enum kbuffer_long_size {
KBUFFER_LSIZE_4,
KBUFFER_LSIZE_8,
+ KBUFFER_LSIZE_SAME_AS_HOST,
};
enum {
diff --git a/include/traceevent/trace-seq.h b/include/traceevent/trace-seq.h
index d68ec69..217492f 100644
--- a/include/traceevent/trace-seq.h
+++ b/include/traceevent/trace-seq.h
@@ -10,6 +10,10 @@
#include <stdarg.h>
#include <stdio.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* ----------------------- trace_seq ----------------------- */
#ifndef TRACE_SEQ_BUF_SIZE
@@ -52,4 +56,8 @@ extern void trace_seq_terminate(struct trace_seq *s);
extern int trace_seq_do_fprintf(struct trace_seq *s, FILE *fp);
extern int trace_seq_do_printf(struct trace_seq *s);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _TRACE_SEQ_H */
diff --git a/plugins/plugin_function.c b/plugins/plugin_function.c
index 7777569..2d6509b 100644
--- a/plugins/plugin_function.c
+++ b/plugins/plugin_function.c
@@ -233,9 +233,11 @@ trace_stack_handler(struct trace_seq *s, struct tep_record *record,
break;
func = tep_find_function(event->tep, addr);
- if (func)
- trace_seq_printf(s, "=> %s (%llx)\n", func, addr);
- else
+ if (func) {
+ trace_seq_puts(s, "=> ");
+ show_function(s, event->tep, func, addr);
+ trace_seq_printf(s, " (%llx)\n", addr);
+ } else
trace_seq_printf(s, "=> %llx\n", addr);
}
diff --git a/plugins/plugin_kvm.c b/plugins/plugin_kvm.c
index 51ceeb9..9852c35 100644
--- a/plugins/plugin_kvm.c
+++ b/plugins/plugin_kvm.c
@@ -10,6 +10,8 @@
#include "event-parse.h"
#include "trace-seq.h"
+#define __weak __attribute__((weak))
+
#ifdef HAVE_UDIS86
#include <udis86.h>
@@ -273,15 +275,49 @@ static int print_exit_reason(struct trace_seq *s, struct tep_record *record,
return 0;
}
+__weak const char *tep_plugin_kvm_get_func(struct tep_event *event,
+ struct tep_record *record,
+ unsigned long long *val)
+{
+ return NULL;
+}
+
+__weak void tep_plugin_kvm_put_func(const char *func)
+{
+}
+
+
+static void add_rip_function(struct trace_seq *s, struct tep_record *record,
+ struct tep_event *event, unsigned long long rip)
+{
+ unsigned long long ip = rip;
+ const char *func;
+
+ func = tep_plugin_kvm_get_func(event, record, &ip);
+ if (func) {
+ trace_seq_printf(s, " %s", func);
+ /* The application may upate ip to the start of the function */
+ if (ip != rip)
+ trace_seq_printf(s, "+0x%0llx", rip - ip);
+ tep_plugin_kvm_put_func(func);
+ }
+}
+
static int kvm_exit_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context)
{
unsigned long long info1 = 0, info2 = 0;
+ unsigned long long rip;
if (print_exit_reason(s, record, event, "exit_reason") < 0)
return -1;
- tep_print_num_field(s, " rip 0x%lx", event, "guest_rip", record, 1);
+ if (tep_get_field_val(s, event, "guest_rip", record, &rip, 1) < 0)
+ return -1;
+
+ trace_seq_printf(s, " rip 0x%llx", rip);
+
+ add_rip_function(s, record, event, rip);
if (tep_get_field_val(s, event, "info1", record, &info1, 0) >= 0
&& tep_get_field_val(s, event, "info2", record, &info2, 0) >= 0)
@@ -290,6 +326,22 @@ static int kvm_exit_handler(struct trace_seq *s, struct tep_record *record,
return 0;
}
+static int kvm_entry_handler(struct trace_seq *s, struct tep_record *record,
+ struct tep_event *event, void *context)
+{
+ unsigned long long rip;
+
+ tep_print_num_field(s, " vcpu %u", event, "vcpu_id", record, 1);
+
+ if (tep_get_field_val(s, event, "rip", record, &rip, 1) < 0)
+ return -1;
+
+ trace_seq_printf(s, " rip 0x%llx", rip);
+ add_rip_function(s, record, event, rip);
+
+ return 0;
+}
+
#define KVM_EMUL_INSN_F_CR0_PE (1 << 0)
#define KVM_EMUL_INSN_F_EFL_VM (1 << 1)
#define KVM_EMUL_INSN_F_CS_D (1 << 2)
@@ -329,12 +381,12 @@ static int kvm_emulate_insn_handler(struct trace_seq *s,
flags & KVM_EMUL_INSN_F_CS_D,
flags & KVM_EMUL_INSN_F_CS_L);
- trace_seq_printf(s, "%llx:%llx: %s%s", csbase, rip, disasm,
- failed ? " FAIL" : "");
+ trace_seq_printf(s, "%llx:%llx", csbase, rip);
+ add_rip_function(s, record, event, rip);
+ trace_seq_printf(s, ": %s%s", disasm, failed ? " FAIL" : "");
return 0;
}
-
static int kvm_nested_vmexit_inject_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context)
{
@@ -352,7 +404,13 @@ static int kvm_nested_vmexit_inject_handler(struct trace_seq *s, struct tep_reco
static int kvm_nested_vmexit_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context)
{
- tep_print_num_field(s, "rip %llx ", event, "rip", record, 1);
+ unsigned long long rip;
+
+ if (tep_get_field_val(s, event, "rip", record, &rip, 1) < 0)
+ return -1;
+
+ trace_seq_printf(s, " rip %llx", rip);
+ add_rip_function(s, record, event, rip);
return kvm_nested_vmexit_inject_handler(s, record, event, context);
}
@@ -456,6 +514,9 @@ int TEP_PLUGIN_LOADER(struct tep_handle *tep)
tep_register_event_handler(tep, -1, "kvm", "kvm_exit",
kvm_exit_handler, NULL);
+ tep_register_event_handler(tep, -1, "kvm", "kvm_entry",
+ kvm_entry_handler, NULL);
+
tep_register_event_handler(tep, -1, "kvm", "kvm_emulate_insn",
kvm_emulate_insn_handler, NULL);
@@ -496,6 +557,9 @@ void TEP_PLUGIN_UNLOADER(struct tep_handle *tep)
tep_unregister_event_handler(tep, -1, "kvm", "kvm_exit",
kvm_exit_handler, NULL);
+ tep_unregister_event_handler(tep, -1, "kvm", "kvm_entry",
+ kvm_entry_handler, NULL);
+
tep_unregister_event_handler(tep, -1, "kvm", "kvm_emulate_insn",
kvm_emulate_insn_handler, NULL);
diff --git a/src/event-parse-local.h b/src/event-parse-local.h
index fd4bbcf..c6bfc61 100644
--- a/src/event-parse-local.h
+++ b/src/event-parse-local.h
@@ -85,6 +85,10 @@ struct tep_handle {
struct tep_event *last_event;
struct tep_plugins_dir *plugins_dir;
+
+ const char *input_buf;
+ unsigned long long input_buf_ptr;
+ unsigned long long input_buf_siz;
};
enum tep_print_parse_type {
@@ -113,11 +117,11 @@ unsigned int data2host4(struct tep_handle *tep, unsigned int data);
unsigned long long data2host8(struct tep_handle *tep, unsigned long long data);
/* access to the internal parser */
-int peek_char(void);
-void init_input_buf(const char *buf, unsigned long long size);
-unsigned long long get_input_buf_ptr(void);
-const char *get_input_buf(void);
-enum tep_event_type read_token(char **tok);
+int peek_char(struct tep_handle *tep);
+void init_input_buf(struct tep_handle *tep, const char *buf, unsigned long long size);
+unsigned long long get_input_buf_ptr(struct tep_handle *tep);
+const char *get_input_buf(struct tep_handle *tep);
+enum tep_event_type read_token(struct tep_handle *tep, char **tok);
void free_token(char *tok);
#endif /* _PARSE_EVENTS_INT_H */
diff --git a/src/event-parse.c b/src/event-parse.c
index f862f49..e655087 100644
--- a/src/event-parse.c
+++ b/src/event-parse.c
@@ -28,10 +28,6 @@
#include "event-utils.h"
#include "trace-seq.h"
-static const char *input_buf;
-static unsigned long long input_buf_ptr;
-static unsigned long long input_buf_siz;
-
static int is_flag_field;
static int is_symbolic_field;
@@ -62,21 +58,22 @@ static int show_warning = 1;
*
* Initializes the internal buffer that tep_read_token() will parse.
*/
-__hidden void init_input_buf(const char *buf, unsigned long long size)
+__hidden void init_input_buf(struct tep_handle *tep, const char *buf,
+ unsigned long long size)
{
- input_buf = buf;
- input_buf_siz = size;
- input_buf_ptr = 0;
+ tep->input_buf = buf;
+ tep->input_buf_siz = size;
+ tep->input_buf_ptr = 0;
}
-__hidden const char *get_input_buf(void)
+__hidden const char *get_input_buf(struct tep_handle *tep)
{
- return input_buf;
+ return tep->input_buf;
}
-__hidden unsigned long long get_input_buf_ptr(void)
+__hidden unsigned long long get_input_buf_ptr(struct tep_handle *tep)
{
- return input_buf_ptr;
+ return tep->input_buf_ptr;
}
struct event_handler {
@@ -614,6 +611,43 @@ find_func(struct tep_handle *tep, unsigned long long addr)
}
/**
+ * tep_find_function_info - find a function by a given address
+ * @tep: a handle to the trace event parser context
+ * @addr: the address to find the function with
+ * @name: Return the name of the function (if found)
+ * @start: Return the start of the function (if found)
+ * @size: Return the size of the function (if found)
+ *
+ * Returns 1 if found, and 0 if it is not.
+ * If found then @name will point to the name of the function.
+ * @start: will contain the starting address of the function.
+ * @size: will contain the size of the function.
+ */
+int tep_find_function_info(struct tep_handle *tep, unsigned long long addr,
+ const char **name, unsigned long long *start,
+ unsigned long *size)
+{
+ struct func_map *map;
+
+ map = find_func(tep, addr);
+ if (!map)
+ return 0;
+
+ if (name)
+ *name = map->func;
+ if (start)
+ *start = map->addr;
+ if (size) {
+ if (!tep->func_resolver)
+ *size = map[1].addr - map->addr;
+ else
+ *size = 0;
+ }
+
+ return 1;
+}
+
+/**
* tep_find_function - find a function by a given address
* @tep: a handle to the trace event parser context
* @addr: the address to find the function with
@@ -739,8 +773,11 @@ int tep_parse_kallsyms(struct tep_handle *tep, const char *kallsyms)
if (errno)
goto out;
- if (n != 2 || !func_end)
+ if (n != 2 || !func_end) {
+ tep_warning("Failed to parse kallsyms n=%d func_end=%d",
+ n, func_end);
goto out;
+ }
func = line + func_start;
/*
@@ -1080,6 +1117,7 @@ static void free_arg(struct tep_print_arg *arg)
free(arg->string.string);
break;
case TEP_PRINT_BITMASK:
+ case TEP_PRINT_CPUMASK:
free(arg->bitmask.bitmask);
break;
case TEP_PRINT_DYNAMIC_ARRAY:
@@ -1127,12 +1165,12 @@ static enum tep_event_type get_type(int ch)
return TEP_EVENT_OP;
}
-static int __read_char(void)
+static int __read_char(struct tep_handle *tep)
{
- if (input_buf_ptr >= input_buf_siz)
+ if (tep->input_buf_ptr >= tep->input_buf_siz)
return -1;
- return input_buf[input_buf_ptr++];
+ return tep->input_buf[tep->input_buf_ptr++];
}
/**
@@ -1140,12 +1178,12 @@ static int __read_char(void)
*
* Returns the next character read, or -1 if end of buffer.
*/
-__hidden int peek_char(void)
+__hidden int peek_char(struct tep_handle *tep)
{
- if (input_buf_ptr >= input_buf_siz)
+ if (tep->input_buf_ptr >= tep->input_buf_siz)
return -1;
- return input_buf[input_buf_ptr];
+ return tep->input_buf[tep->input_buf_ptr];
}
static int extend_token(char **tok, char *buf, int size)
@@ -1167,9 +1205,10 @@ static int extend_token(char **tok, char *buf, int size)
return 0;
}
-static enum tep_event_type force_token(const char *str, char **tok);
+static enum tep_event_type force_token(struct tep_handle *tep, const char *str,
+ char **tok);
-static enum tep_event_type __read_token(char **tok)
+static enum tep_event_type __read_token(struct tep_handle *tep, char **tok)
{
char buf[BUFSIZ];
int ch, last_ch, quote_ch, next_ch;
@@ -1180,7 +1219,7 @@ static enum tep_event_type __read_token(char **tok)
*tok = NULL;
- ch = __read_char();
+ ch = __read_char(tep);
if (ch < 0)
return TEP_EVENT_NONE;
@@ -1201,9 +1240,9 @@ static enum tep_event_type __read_token(char **tok)
case TEP_EVENT_OP:
switch (ch) {
case '-':
- next_ch = peek_char();
+ next_ch = peek_char(tep);
if (next_ch == '>') {
- buf[i++] = __read_char();
+ buf[i++] = __read_char(tep);
break;
}
/* fall through */
@@ -1213,10 +1252,10 @@ static enum tep_event_type __read_token(char **tok)
case '>':
case '<':
last_ch = ch;
- ch = peek_char();
+ ch = peek_char(tep);
if (ch != last_ch)
goto test_equal;
- buf[i++] = __read_char();
+ buf[i++] = __read_char(tep);
switch (last_ch) {
case '>':
case '<':
@@ -1236,9 +1275,9 @@ static enum tep_event_type __read_token(char **tok)
return type;
test_equal:
- ch = peek_char();
+ ch = peek_char(tep);
if (ch == '=')
- buf[i++] = __read_char();
+ buf[i++] = __read_char(tep);
goto out;
case TEP_EVENT_DQUOTE:
@@ -1258,29 +1297,34 @@ static enum tep_event_type __read_token(char **tok)
i = 0;
}
last_ch = ch;
- ch = __read_char();
+ ch = __read_char(tep);
buf[i++] = ch;
/* the '\' '\' will cancel itself */
if (ch == '\\' && last_ch == '\\')
last_ch = 0;
/* Break out if the file is corrupted and giving non print chars */
+ if (ch <= 0)
+ break;
} while ((ch != quote_ch && isprint(ch)) || last_ch == '\\' || ch == '\n');
/* remove the last quote */
i--;
+ if (ch <= 0)
+ type = TEP_EVENT_NONE;
+
/*
* For strings (double quotes) check the next token.
* If it is another string, concatinate the two.
*/
if (type == TEP_EVENT_DQUOTE) {
- unsigned long long save_input_buf_ptr = input_buf_ptr;
+ unsigned long long save_input_buf_ptr = tep->input_buf_ptr;
do {
- ch = __read_char();
+ ch = __read_char(tep);
} while (isspace(ch));
if (ch == '"')
goto concat;
- input_buf_ptr = save_input_buf_ptr;
+ tep->input_buf_ptr = save_input_buf_ptr;
}
goto out;
@@ -1291,7 +1335,7 @@ static enum tep_event_type __read_token(char **tok)
break;
}
- while (get_type(peek_char()) == type) {
+ while (get_type(peek_char(tep)) == type) {
if (i == (BUFSIZ - 1)) {
buf[i] = 0;
tok_size += BUFSIZ;
@@ -1300,7 +1344,7 @@ static enum tep_event_type __read_token(char **tok)
return TEP_EVENT_NONE;
i = 0;
}
- ch = __read_char();
+ ch = __read_char(tep);
buf[i++] = ch;
}
@@ -1321,22 +1365,23 @@ static enum tep_event_type __read_token(char **tok)
if (strcmp(*tok, "LOCAL_PR_FMT") == 0) {
free(*tok);
*tok = NULL;
- return force_token("\"%s\" ", tok);
+ return force_token(tep, "\"%s\" ", tok);
} else if (strcmp(*tok, "STA_PR_FMT") == 0) {
free(*tok);
*tok = NULL;
- return force_token("\" sta:%pM\" ", tok);
+ return force_token(tep, "\" sta:%pM\" ", tok);
} else if (strcmp(*tok, "VIF_PR_FMT") == 0) {
free(*tok);
*tok = NULL;
- return force_token("\" vif:%p(%d)\" ", tok);
+ return force_token(tep, "\" vif:%p(%d)\" ", tok);
}
}
return type;
}
-static enum tep_event_type force_token(const char *str, char **tok)
+static enum tep_event_type force_token(struct tep_handle *tep, const char *str,
+ char **tok)
{
const char *save_input_buf;
unsigned long long save_input_buf_ptr;
@@ -1344,18 +1389,18 @@ static enum tep_event_type force_token(const char *str, char **tok)
enum tep_event_type type;
/* save off the current input pointers */
- save_input_buf = input_buf;
- save_input_buf_ptr = input_buf_ptr;
- save_input_buf_siz = input_buf_siz;
+ save_input_buf = tep->input_buf;
+ save_input_buf_ptr = tep->input_buf_ptr;
+ save_input_buf_siz = tep->input_buf_siz;
- init_input_buf(str, strlen(str));
+ init_input_buf(tep, str, strlen(str));
- type = __read_token(tok);
+ type = __read_token(tep, tok);
/* reset back to original token */
- input_buf = save_input_buf;
- input_buf_ptr = save_input_buf_ptr;
- input_buf_siz = save_input_buf_siz;
+ tep->input_buf = save_input_buf;
+ tep->input_buf_ptr = save_input_buf_ptr;
+ tep->input_buf_siz = save_input_buf_siz;
return type;
}
@@ -1379,12 +1424,12 @@ __hidden void free_token(char *tok)
*
* Returns the token type.
*/
-__hidden enum tep_event_type read_token(char **tok)
+__hidden enum tep_event_type read_token(struct tep_handle *tep, char **tok)
{
enum tep_event_type type;
for (;;) {
- type = __read_token(tok);
+ type = __read_token(tep, tok);
if (type != TEP_EVENT_SPACE)
return type;
@@ -1397,12 +1442,12 @@ __hidden enum tep_event_type read_token(char **tok)
}
/* no newline */
-static enum tep_event_type read_token_item(char **tok)
+static enum tep_event_type read_token_item(struct tep_handle *tep, char **tok)
{
enum tep_event_type type;
for (;;) {
- type = __read_token(tok);
+ type = __read_token(tep, tok);
if (type != TEP_EVENT_SPACE && type != TEP_EVENT_NEWLINE)
return type;
free_token(*tok);
@@ -1443,33 +1488,35 @@ static int test_type_token(enum tep_event_type type, const char *token,
return 0;
}
-static int __read_expect_type(enum tep_event_type expect, char **tok, int newline_ok)
+static int __read_expect_type(struct tep_handle *tep, enum tep_event_type expect,
+ char **tok, int newline_ok)
{
enum tep_event_type type;
if (newline_ok)
- type = read_token(tok);
+ type = read_token(tep, tok);
else
- type = read_token_item(tok);
+ type = read_token_item(tep, tok);
return test_type(type, expect);
}
-static int read_expect_type(enum tep_event_type expect, char **tok)
+static int read_expect_type(struct tep_handle *tep, enum tep_event_type expect,
+ char **tok)
{
- return __read_expect_type(expect, tok, 1);
+ return __read_expect_type(tep, expect, tok, 1);
}
-static int __read_expected(enum tep_event_type expect, const char *str,
- int newline_ok)
+static int __read_expected(struct tep_handle *tep, enum tep_event_type expect,
+ const char *str, int newline_ok)
{
enum tep_event_type type;
char *token;
int ret;
if (newline_ok)
- type = read_token(&token);
+ type = read_token(tep, &token);
else
- type = read_token_item(&token);
+ type = read_token_item(tep, &token);
ret = test_type_token(type, token, expect, str);
@@ -1478,27 +1525,29 @@ static int __read_expected(enum tep_event_type expect, const char *str,
return ret;
}
-static int read_expected(enum tep_event_type expect, const char *str)
+static int read_expected(struct tep_handle *tep, enum tep_event_type expect,
+ const char *str)
{
- return __read_expected(expect, str, 1);
+ return __read_expected(tep, expect, str, 1);
}
-static int read_expected_item(enum tep_event_type expect, const char *str)
+static int read_expected_item(struct tep_handle *tep, enum tep_event_type expect,
+ const char *str)
{
- return __read_expected(expect, str, 0);
+ return __read_expected(tep, expect, str, 0);
}
-static char *event_read_name(void)
+static char *event_read_name(struct tep_handle *tep)
{
char *token;
- if (read_expected(TEP_EVENT_ITEM, "name") < 0)
+ if (read_expected(tep, TEP_EVENT_ITEM, "name") < 0)
return NULL;
- if (read_expected(TEP_EVENT_OP, ":") < 0)
+ if (read_expected(tep, TEP_EVENT_OP, ":") < 0)
return NULL;
- if (read_expect_type(TEP_EVENT_ITEM, &token) < 0)
+ if (read_expect_type(tep, TEP_EVENT_ITEM, &token) < 0)
goto fail;
return token;
@@ -1508,18 +1557,18 @@ static char *event_read_name(void)
return NULL;
}
-static int event_read_id(void)
+static int event_read_id(struct tep_handle *tep)
{
char *token;
int id;
- if (read_expected_item(TEP_EVENT_ITEM, "ID") < 0)
+ if (read_expected_item(tep, TEP_EVENT_ITEM, "ID") < 0)
return -1;
- if (read_expected(TEP_EVENT_OP, ":") < 0)
+ if (read_expected(tep, TEP_EVENT_OP, ":") < 0)
return -1;
- if (read_expect_type(TEP_EVENT_ITEM, &token) < 0)
+ if (read_expect_type(tep, TEP_EVENT_ITEM, &token) < 0)
goto fail;
id = strtoul(token, NULL, 0);
@@ -1607,7 +1656,8 @@ static int append(char **buf, const char *delim, const char *str)
return 0;
}
-static int event_read_fields(struct tep_event *event, struct tep_format_field **fields)
+static int event_read_fields(struct tep_handle *tep, struct tep_event *event,
+ struct tep_format_field **fields)
{
struct tep_format_field *field = NULL;
enum tep_event_type type;
@@ -1620,7 +1670,7 @@ static int event_read_fields(struct tep_event *event, struct tep_format_field **
do {
unsigned int size_dynamic = 0;
- type = read_token(&token);
+ type = read_token(tep, &token);
if (type == TEP_EVENT_NEWLINE) {
free_token(token);
return count;
@@ -1632,7 +1682,7 @@ static int event_read_fields(struct tep_event *event, struct tep_format_field **
goto fail;
free_token(token);
- type = read_token(&token);
+ type = read_token(tep, &token);
/*
* The ftrace fields may still use the "special" name.
* Just ignore it.
@@ -1640,14 +1690,14 @@ static int event_read_fields(struct tep_event *event, struct tep_format_field **
if (event->flags & TEP_EVENT_FL_ISFTRACE &&
type == TEP_EVENT_ITEM && strcmp(token, "special") == 0) {
free_token(token);
- type = read_token(&token);
+ type = read_token(tep, &token);
}
if (test_type_token(type, token, TEP_EVENT_OP, ":") < 0)
goto fail;
free_token(token);
- if (read_expect_type(TEP_EVENT_ITEM, &token) < 0)
+ if (read_expect_type(tep, TEP_EVENT_ITEM, &token) < 0)
goto fail;
last_token = token;
@@ -1660,7 +1710,7 @@ static int event_read_fields(struct tep_event *event, struct tep_format_field **
/* read the rest of the type */
for (;;) {
- type = read_token(&token);
+ type = read_token(tep, &token);
if (type == TEP_EVENT_ITEM ||
(type == TEP_EVENT_OP && strcmp(token, "*") == 0) ||
/*
@@ -1698,7 +1748,7 @@ static int event_read_fields(struct tep_event *event, struct tep_format_field **
goto fail;
delim = " ";
- while ((type = read_token(&token)) != TEP_EVENT_NONE) {
+ while ((type = read_token(tep, &token)) != TEP_EVENT_NONE) {
if (type == TEP_EVENT_DELIM) {
if (token[0] == '(')
depth++;
@@ -1737,7 +1787,7 @@ static int event_read_fields(struct tep_event *event, struct tep_format_field **
field->flags |= TEP_FIELD_IS_ARRAY;
- type = read_token(&token);
+ type = read_token(tep, &token);
if (type == TEP_EVENT_ITEM)
field->arraylen = strtoul(token, NULL, 0);
@@ -1763,7 +1813,7 @@ static int event_read_fields(struct tep_event *event, struct tep_format_field **
/* We only care about the last token */
field->arraylen = strtoul(token, NULL, 0);
free_token(token);
- type = read_token(&token);
+ type = read_token(tep, &token);
if (type == TEP_EVENT_NONE) {
free(brackets);
do_warning_event(event, "failed to find token");
@@ -1776,12 +1826,12 @@ static int event_read_fields(struct tep_event *event, struct tep_format_field **
ret = append(&brackets, "", "]");
if (ret < 0) {
free(brackets);
- goto fail;
+ goto fail_expect;
}
/* add brackets to type */
- type = read_token(&token);
+ type = read_token(tep, &token);
/*
* If the next token is not an OP, then it is of
* the format: type [] item;
@@ -1797,7 +1847,7 @@ static int event_read_fields(struct tep_event *event, struct tep_format_field **
size_dynamic = type_size(field->name);
free_token(field->name);
field->name = field->alias = token;
- type = read_token(&token);
+ type = read_token(tep, &token);
} else {
ret = append(&field->type, "", brackets);
if (ret < 0) {
@@ -1821,27 +1871,27 @@ static int event_read_fields(struct tep_event *event, struct tep_format_field **
goto fail;
free_token(token);
- if (read_expected(TEP_EVENT_ITEM, "offset") < 0)
+ if (read_expected(tep, TEP_EVENT_ITEM, "offset") < 0)
goto fail_expect;
- if (read_expected(TEP_EVENT_OP, ":") < 0)
+ if (read_expected(tep, TEP_EVENT_OP, ":") < 0)
goto fail_expect;
- if (read_expect_type(TEP_EVENT_ITEM, &token))
+ if (read_expect_type(tep, TEP_EVENT_ITEM, &token))
goto fail;
field->offset = strtoul(token, NULL, 0);
free_token(token);
- if (read_expected(TEP_EVENT_OP, ";") < 0)
+ if (read_expected(tep, TEP_EVENT_OP, ";") < 0)
goto fail_expect;
- if (read_expected(TEP_EVENT_ITEM, "size") < 0)
+ if (read_expected(tep, TEP_EVENT_ITEM, "size") < 0)
goto fail_expect;
- if (read_expected(TEP_EVENT_OP, ":") < 0)
+ if (read_expected(tep, TEP_EVENT_OP, ":") < 0)
goto fail_expect;
- if (read_expect_type(TEP_EVENT_ITEM, &token))
+ if (read_expect_type(tep, TEP_EVENT_ITEM, &token))
goto fail;
field->size = strtoul(token, NULL, 0);
free_token(token);
@@ -1856,10 +1906,10 @@ static int event_read_fields(struct tep_event *event, struct tep_format_field **
if ((field->flags & TEP_FIELD_IS_DYNAMIC) && field->size == 2)
field->flags |= TEP_FIELD_IS_STRING | TEP_FIELD_IS_ARRAY;
- if (read_expected(TEP_EVENT_OP, ";") < 0)
+ if (read_expected(tep, TEP_EVENT_OP, ";") < 0)
goto fail_expect;
- type = read_token(&token);
+ type = read_token(tep, &token);
if (type != TEP_EVENT_NEWLINE) {
/* newer versions of the kernel have a "signed" type */
if (test_type_token(type, token, TEP_EVENT_ITEM, "signed"))
@@ -1867,26 +1917,26 @@ static int event_read_fields(struct tep_event *event, struct tep_format_field **
free_token(token);
- if (read_expected(TEP_EVENT_OP, ":") < 0)
+ if (read_expected(tep, TEP_EVENT_OP, ":") < 0)
goto fail_expect;
- if (read_expect_type(TEP_EVENT_ITEM, &token))
+ if (read_expect_type(tep, TEP_EVENT_ITEM, &token))
goto fail;
if (strtoul(token, NULL, 0))
field->flags |= TEP_FIELD_IS_SIGNED;
free_token(token);
- if (read_expected(TEP_EVENT_OP, ";") < 0)
+ if (read_expected(tep, TEP_EVENT_OP, ";") < 0)
goto fail_expect;
- if (read_expect_type(TEP_EVENT_NEWLINE, &token))
+ if (read_expect_type(tep, TEP_EVENT_NEWLINE, &token))
goto fail;
}
free_token(token);
- if (field->flags & TEP_FIELD_IS_ARRAY) {
+ if (field->flags & (TEP_FIELD_IS_ARRAY | TEP_FIELD_IS_DYNAMIC)) {
if (field->arraylen)
field->elementsize = field->size / field->arraylen;
else if (field->flags & TEP_FIELD_IS_DYNAMIC)
@@ -1902,6 +1952,7 @@ static int event_read_fields(struct tep_event *event, struct tep_format_field **
*fields = field;
fields = &field->next;
+ field = NULL;
} while (1);
@@ -1923,22 +1974,22 @@ static int event_read_format(struct tep_event *event)
char *token;
int ret;
- if (read_expected_item(TEP_EVENT_ITEM, "format") < 0)
+ if (read_expected_item(event->tep, TEP_EVENT_ITEM, "format") < 0)
return -1;
- if (read_expected(TEP_EVENT_OP, ":") < 0)
+ if (read_expected(event->tep, TEP_EVENT_OP, ":") < 0)
return -1;
- if (read_expect_type(TEP_EVENT_NEWLINE, &token))
+ if (read_expect_type(event->tep, TEP_EVENT_NEWLINE, &token))
goto fail;
free_token(token);
- ret = event_read_fields(event, &event->format.common_fields);
+ ret = event_read_fields(event->tep, event, &event->format.common_fields);
if (ret < 0)
return ret;
event->format.nr_common = ret;
- ret = event_read_fields(event, &event->format.fields);
+ ret = event_read_fields(event->tep, event, &event->format.fields);
if (ret < 0)
return ret;
event->format.nr_fields = ret;
@@ -1960,7 +2011,7 @@ process_arg(struct tep_event *event, struct tep_print_arg *arg, char **tok)
enum tep_event_type type;
char *token;
- type = read_token(&token);
+ type = read_token(event->tep, &token);
*tok = token;
return process_arg_token(event, arg, tok, type);
@@ -2066,7 +2117,7 @@ process_array(struct tep_event *event, struct tep_print_arg *top, char **tok)
top->op.right = arg;
free_token(token);
- type = read_token_item(&token);
+ type = read_token_item(event->tep, &token);
*tok = token;
return type;
@@ -2144,6 +2195,120 @@ static int set_op_prio(struct tep_print_arg *arg)
return arg->op.prio;
}
+static int consolidate_op_arg(struct tep_print_arg *arg)
+{
+ unsigned long long val, left, right;
+ int ret = 0;
+
+ if (arg->type != TEP_PRINT_OP)
+ return 0;
+
+ if (arg->op.left)
+ ret = consolidate_op_arg(arg->op.left);
+ if (ret < 0)
+ return ret;
+
+ if (arg->op.right)
+ ret = consolidate_op_arg(arg->op.right);
+ if (ret < 0)
+ return ret;
+
+ if (!arg->op.left || !arg->op.right)
+ return 0;
+
+ if (arg->op.left->type != TEP_PRINT_ATOM ||
+ arg->op.right->type != TEP_PRINT_ATOM)
+ return 0;
+
+ /* Two atoms, we can do the operation now. */
+ left = strtoull(arg->op.left->atom.atom, NULL, 0);
+ right = strtoull(arg->op.right->atom.atom, NULL, 0);
+
+ switch (arg->op.op[0]) {
+ case '>':
+ switch (arg->op.op[1]) {
+ case '>':
+ val = left >> right;
+ break;
+ case '=':
+ val = left >= right;
+ break;
+ default:
+ val = left > right;
+ break;
+ }
+ break;
+ case '<':
+ switch (arg->op.op[1]) {
+ case '<':
+ val = left << right;
+ break;
+ case '=':
+ val = left <= right;
+ break;
+ default:
+ val = left < right;
+ break;
+ }
+ break;
+ case '&':
+ switch (arg->op.op[1]) {
+ case '&':
+ val = left && right;
+ break;
+ default:
+ val = left & right;
+ break;
+ }
+ break;
+ case '|':
+ switch (arg->op.op[1]) {
+ case '|':
+ val = left || right;
+ break;
+ default:
+ val = left | right;
+ break;
+ }
+ break;
+ case '-':
+ val = left - right;
+ break;
+ case '+':
+ val = left + right;
+ break;
+ case '*':
+ val = left * right;
+ break;
+ case '^':
+ val = left ^ right;
+ break;
+ case '/':
+ val = left / right;
+ break;
+ case '%':
+ val = left % right;
+ break;
+ case '=':
+ /* Only '==' is called here */
+ val = left == right;
+ break;
+ case '!':
+ /* Only '!=' is called here. */
+ val = left != right;
+ break;
+ default:
+ return 0;
+ }
+
+ free_arg(arg->op.left);
+ free_arg(arg->op.right);
+
+ arg->type = TEP_PRINT_ATOM;
+ free(arg->op.op);
+ return asprintf(&arg->atom.atom, "%lld", val) < 0 ? -1 : 0;
+}
+
/* Note, *tok does not get freed, but will most likely be saved */
static enum tep_event_type
process_op(struct tep_event *event, struct tep_print_arg *arg, char **tok)
@@ -2203,6 +2368,7 @@ process_op(struct tep_event *event, struct tep_print_arg *arg, char **tok)
arg->type = TEP_PRINT_OP;
arg->op.op = token;
arg->op.left = left;
+ arg->op.right = NULL;
arg->op.prio = 0;
/* it will set arg->op.right */
@@ -2246,7 +2412,7 @@ process_op(struct tep_event *event, struct tep_print_arg *arg, char **tok)
goto out_free;
}
- type = read_token_item(&token);
+ type = read_token_item(event->tep, &token);
*tok = token;
/* could just be a type pointer */
@@ -2308,6 +2474,7 @@ process_op(struct tep_event *event, struct tep_print_arg *arg, char **tok)
arg->type = TEP_PRINT_OP;
arg->op.op = token;
arg->op.left = left;
+ arg->op.right = NULL;
arg->op.prio = 0;
@@ -2351,7 +2518,7 @@ process_entry(struct tep_event *event __maybe_unused, struct tep_print_arg *arg,
char *field;
char *token;
- type = read_token_item(&token);
+ type = read_token_item(event->tep, &token);
/*
* Check if REC happens to be surrounded by parenthesis, and
* return if that's the case, as "(REC)->" is valid.
@@ -2367,7 +2534,7 @@ process_entry(struct tep_event *event __maybe_unused, struct tep_print_arg *arg,
free_token(token);
- if (read_expect_type(TEP_EVENT_ITEM, &token) < 0)
+ if (read_expect_type(event->tep, TEP_EVENT_ITEM, &token) < 0)
goto out_free;
field = token;
@@ -2384,7 +2551,7 @@ process_entry(struct tep_event *event __maybe_unused, struct tep_print_arg *arg,
is_symbolic_field = 0;
}
- type = read_token(&token);
+ type = read_token(event->tep, &token);
*tok = token;
return type;
@@ -2412,19 +2579,31 @@ static int alloc_and_process_delim(struct tep_event *event, char *next_token,
type = process_arg(event, field, &token);
- if (test_type_token(type, token, TEP_EVENT_DELIM, next_token)) {
- errno = EINVAL;
- ret = -1;
- free_arg(field);
- goto out_free_token;
+ /* We do allow operators */
+ if (type == TEP_EVENT_OP) {
+ type = process_op(event, field, &token);
+
+ if (consolidate_op_arg(field) < 0)
+ type = TEP_EVENT_ERROR;
+
+ if (type == TEP_EVENT_ERROR)
+ goto out_error;
}
+ if (test_type_token(type, token, TEP_EVENT_DELIM, next_token))
+ goto out_error;
+
*print_arg = field;
out_free_token:
free_token(token);
return ret;
+out_error:
+ errno = EINVAL;
+ ret = -1;
+ free_arg(field);
+ goto out_free_token;
}
static char *arg_eval (struct tep_print_arg *arg);
@@ -2437,6 +2616,10 @@ eval_type_str(unsigned long long val, const char *type, int pointer)
int len;
len = strlen(type);
+ if (len < 2) {
+ do_warning("invalid type: %s", type);
+ return val;
+ }
if (pointer) {
@@ -2692,6 +2875,7 @@ static int arg_num_eval(struct tep_print_arg *arg, long long *val)
case TEP_PRINT_STRING:
case TEP_PRINT_BSTRING:
case TEP_PRINT_BITMASK:
+ case TEP_PRINT_CPUMASK:
default:
do_warning("invalid eval type %d", arg->type);
ret = 0;
@@ -2721,6 +2905,7 @@ static char *arg_eval (struct tep_print_arg *arg)
case TEP_PRINT_STRING:
case TEP_PRINT_BSTRING:
case TEP_PRINT_BITMASK:
+ case TEP_PRINT_CPUMASK:
default:
do_warning("invalid eval type %d", arg->type);
break;
@@ -2740,7 +2925,7 @@ process_fields(struct tep_event *event, struct tep_print_flag_sym **list, char *
do {
free_token(token);
- type = read_token_item(&token);
+ type = read_token_item(event->tep, &token);
if (test_type_token(type, token, TEP_EVENT_OP, "{"))
break;
@@ -2794,7 +2979,7 @@ process_fields(struct tep_event *event, struct tep_print_flag_sym **list, char *
list = &field->next;
free_token(token);
- type = read_token_item(&token);
+ type = read_token_item(event->tep, &token);
} while (type == TEP_EVENT_DELIM && strcmp(token, ",") == 0);
*tok = token;
@@ -2838,10 +3023,10 @@ process_flags(struct tep_event *event, struct tep_print_arg *arg, char **tok)
arg->flags.field = field;
- type = read_token_item(&token);
+ type = read_token_item(event->tep, &token);
if (event_item_type(type)) {
arg->flags.delim = token;
- type = read_token_item(&token);
+ type = read_token_item(event->tep, &token);
}
if (test_type_token(type, token, TEP_EVENT_DELIM, ","))
@@ -2852,7 +3037,7 @@ process_flags(struct tep_event *event, struct tep_print_arg *arg, char **tok)
goto out_free;
free_token(token);
- type = read_token_item(tok);
+ type = read_token_item(event->tep, tok);
return type;
out_free_field:
@@ -2891,7 +3076,7 @@ process_symbols(struct tep_event *event, struct tep_print_arg *arg, char **tok)
goto out_free;
free_token(token);
- type = read_token_item(tok);
+ type = read_token_item(event->tep, tok);
return type;
out_free_field:
@@ -2915,7 +3100,7 @@ process_hex_common(struct tep_event *event, struct tep_print_arg *arg,
if (alloc_and_process_delim(event, ")", &arg->hex.size))
goto free_field;
- return read_token_item(tok);
+ return read_token_item(event->tep, tok);
free_field:
free_arg(arg->hex.field);
@@ -2953,7 +3138,7 @@ process_int_array(struct tep_event *event, struct tep_print_arg *arg, char **tok
if (alloc_and_process_delim(event, ")", &arg->int_array.el_size))
goto free_size;
- return read_token_item(tok);
+ return read_token_item(event->tep, tok);
free_size:
free_arg(arg->int_array.count);
@@ -2980,7 +3165,7 @@ process_dynamic_array(struct tep_event *event, struct tep_print_arg *arg, char *
* The item within the parenthesis is another field that holds
* the index into where the array starts.
*/
- type = read_token(&token);
+ type = read_token(event->tep, &token);
*tok = token;
if (type != TEP_EVENT_ITEM)
goto out_free;
@@ -2994,11 +3179,11 @@ process_dynamic_array(struct tep_event *event, struct tep_print_arg *arg, char *
arg->dynarray.field = field;
arg->dynarray.index = 0;
- if (read_expected(TEP_EVENT_DELIM, ")") < 0)
+ if (read_expected(event->tep, TEP_EVENT_DELIM, ")") < 0)
goto out_free;
free_token(token);
- type = read_token_item(&token);
+ type = read_token_item(event->tep, &token);
*tok = token;
if (type != TEP_EVENT_OP || strcmp(token, "[") != 0)
return type;
@@ -3019,7 +3204,7 @@ process_dynamic_array(struct tep_event *event, struct tep_print_arg *arg, char *
goto out_free_arg;
free_token(token);
- type = read_token_item(tok);
+ type = read_token_item(event->tep, tok);
return type;
out_free_arg:
@@ -3038,7 +3223,7 @@ process_dynamic_array_len(struct tep_event *event, struct tep_print_arg *arg,
enum tep_event_type type;
char *token;
- if (read_expect_type(TEP_EVENT_ITEM, &token) < 0)
+ if (read_expect_type(event->tep, TEP_EVENT_ITEM, &token) < 0)
goto out_free;
arg->type = TEP_PRINT_DYNAMIC_ARRAY_LEN;
@@ -3051,11 +3236,11 @@ process_dynamic_array_len(struct tep_event *event, struct tep_print_arg *arg,
arg->dynarray.field = field;
arg->dynarray.index = 0;
- if (read_expected(TEP_EVENT_DELIM, ")") < 0)
+ if (read_expected(event->tep, TEP_EVENT_DELIM, ")") < 0)
goto out_err;
free_token(token);
- type = read_token(&token);
+ type = read_token(event->tep, &token);
*tok = token;
return type;
@@ -3100,7 +3285,7 @@ process_paren(struct tep_event *event, struct tep_print_arg *arg, char **tok)
goto out_free;
free_token(token);
- type = read_token_item(&token);
+ type = read_token_item(event->tep, &token);
/*
* If the next token is an item or another open paren, then
@@ -3142,13 +3327,12 @@ process_paren(struct tep_event *event, struct tep_print_arg *arg, char **tok)
static enum tep_event_type
-process_str(struct tep_event *event __maybe_unused, struct tep_print_arg *arg,
- char **tok)
+process_str(struct tep_event *event, struct tep_print_arg *arg, char **tok)
{
enum tep_event_type type;
char *token;
- if (read_expect_type(TEP_EVENT_ITEM, &token) < 0)
+ if (read_expect_type(event->tep, TEP_EVENT_ITEM, &token) < 0)
goto out_free;
arg->type = TEP_PRINT_STRING;
@@ -3156,10 +3340,10 @@ process_str(struct tep_event *event __maybe_unused, struct tep_print_arg *arg,
arg->string.offset = -1;
arg->string.field = NULL;
- if (read_expected(TEP_EVENT_DELIM, ")") < 0)
+ if (read_expected(event->tep, TEP_EVENT_DELIM, ")") < 0)
goto out_err;
- type = read_token(&token);
+ type = read_token(event->tep, &token);
*tok = token;
return type;
@@ -3172,13 +3356,12 @@ process_str(struct tep_event *event __maybe_unused, struct tep_print_arg *arg,
}
static enum tep_event_type
-process_bitmask(struct tep_event *event __maybe_unused, struct tep_print_arg *arg,
- char **tok)
+process_bitmask(struct tep_event *event, struct tep_print_arg *arg, char **tok)
{
enum tep_event_type type;
char *token;
- if (read_expect_type(TEP_EVENT_ITEM, &token) < 0)
+ if (read_expect_type(event->tep, TEP_EVENT_ITEM, &token) < 0)
goto out_free;
arg->type = TEP_PRINT_BITMASK;
@@ -3186,10 +3369,10 @@ process_bitmask(struct tep_event *event __maybe_unused, struct tep_print_arg *ar
arg->bitmask.offset = -1;
arg->bitmask.field = NULL;
- if (read_expected(TEP_EVENT_DELIM, ")") < 0)
+ if (read_expected(event->tep, TEP_EVENT_DELIM, ")") < 0)
goto out_err;
- type = read_token(&token);
+ type = read_token(event->tep, &token);
*tok = token;
return type;
@@ -3201,6 +3384,17 @@ process_bitmask(struct tep_event *event __maybe_unused, struct tep_print_arg *ar
return TEP_EVENT_ERROR;
}
+static enum tep_event_type
+process_cpumask(struct tep_event *event __maybe_unused, struct tep_print_arg *arg,
+ char **tok)
+{
+ enum tep_event_type type = process_bitmask(event, arg, tok);
+ if (type != TEP_EVENT_ERROR)
+ arg->type = TEP_PRINT_CPUMASK;
+
+ return type;
+}
+
static struct tep_function_handler *
find_func_handler(struct tep_handle *tep, char *func_name)
{
@@ -3280,7 +3474,7 @@ process_func_handler(struct tep_event *event, struct tep_function_handler *func,
free_token(token);
}
- type = read_token(&token);
+ type = read_token(event->tep, &token);
*tok = token;
return type;
@@ -3306,14 +3500,14 @@ process_builtin_expect(struct tep_event *event, struct tep_print_arg *arg, char
free_token(token);
/* We don't care what the second parameter is of the __builtin_expect() */
- if (read_expect_type(TEP_EVENT_ITEM, &token) < 0)
+ if (read_expect_type(event->tep, TEP_EVENT_ITEM, &token) < 0)
goto out_free;
- if (read_expected(TEP_EVENT_DELIM, ")") < 0)
+ if (read_expected(event->tep, TEP_EVENT_DELIM, ")") < 0)
goto out_free;
free_token(token);
- type = read_token_item(tok);
+ type = read_token_item(event->tep, tok);
return type;
out_free:
@@ -3323,6 +3517,95 @@ out_free:
}
static enum tep_event_type
+process_sizeof(struct tep_event *event, struct tep_print_arg *arg, char **tok)
+{
+ struct tep_format_field *field;
+ enum tep_event_type type;
+ char *token = NULL;
+ bool ok = false;
+ int ret;
+
+ type = read_token_item(event->tep, &token);
+
+ arg->type = TEP_PRINT_ATOM;
+
+ /* We handle some sizeof types */
+ if (strcmp(token, "unsigned") == 0) {
+ free_token(token);
+ type = read_token_item(event->tep, &token);
+
+ if (type == TEP_EVENT_ERROR)
+ goto error;
+
+ if (type != TEP_EVENT_ITEM)
+ ok = true;
+ }
+
+ if (ok || strcmp(token, "int") == 0) {
+ arg->atom.atom = strdup("4");
+
+ } else if (strcmp(token, "long") == 0) {
+ free_token(token);
+ type = read_token_item(event->tep, &token);
+
+ if (token && strcmp(token, "long") == 0) {
+ arg->atom.atom = strdup("8");
+ } else {
+ switch (event->tep->long_size) {
+ case 4:
+ arg->atom.atom = strdup("4");
+ break;
+ case 8:
+ arg->atom.atom = strdup("8");
+ break;
+ default:
+ /* long size not defined yet, fail to parse it */
+ goto error;
+ }
+ /* The token is the next token */
+ ok = true;
+ }
+ } else if (strcmp(token, "REC") == 0) {
+
+ free_token(token);
+ type = read_token_item(event->tep, &token);
+
+ if (test_type_token(type, token, TEP_EVENT_OP, "->"))
+ goto error;
+ free_token(token);
+
+ if (read_expect_type(event->tep, TEP_EVENT_ITEM, &token) < 0)
+ goto error;
+
+ field = tep_find_any_field(event, token);
+ /* Can't handle arrays (yet) */
+ if (!field || field->flags & TEP_FIELD_IS_ARRAY)
+ goto error;
+
+ ret = asprintf(&arg->atom.atom, "%d", field->size);
+ if (ret < 0)
+ goto error;
+
+ } else if (!ok) {
+ goto error;
+ }
+
+ if (!ok) {
+ free_token(token);
+ type = read_token_item(event->tep, tok);
+ }
+ if (test_type_token(type, token, TEP_EVENT_DELIM, ")"))
+ goto error;
+
+ free_token(token);
+ return read_token_item(event->tep, tok);
+error:
+ free_token(token);
+ *tok = NULL;
+ return TEP_EVENT_ERROR;
+}
+
+static enum tep_event_type
process_function(struct tep_event *event, struct tep_print_arg *arg,
char *token, char **tok)
{
@@ -3360,8 +3643,15 @@ process_function(struct tep_event *event, struct tep_print_arg *arg,
free_token(token);
return process_bitmask(event, arg, tok);
}
+ if (strcmp(token, "__get_cpumask") == 0 ||
+ strcmp(token, "__get_rel_cpumask") == 0) {
+ free_token(token);
+ return process_cpumask(event, arg, tok);
+ }
if (strcmp(token, "__get_dynamic_array") == 0 ||
- strcmp(token, "__get_rel_dynamic_array") == 0) {
+ strcmp(token, "__get_rel_dynamic_array") == 0 ||
+ strcmp(token, "__get_sockaddr") == 0 ||
+ strcmp(token, "__get_sockaddr_rel") == 0) {
free_token(token);
return process_dynamic_array(event, arg, tok);
}
@@ -3374,6 +3664,10 @@ process_function(struct tep_event *event, struct tep_print_arg *arg,
free_token(token);
return process_builtin_expect(event, arg, tok);
}
+ if (strcmp(token, "sizeof") == 0) {
+ free_token(token);
+ return process_sizeof(event, arg, tok);
+ }
func = find_func_handler(event->tep, token);
if (func) {
@@ -3404,7 +3698,7 @@ process_arg_token(struct tep_event *event, struct tep_print_arg *arg,
}
atom = token;
/* test the next token */
- type = read_token_item(&token);
+ type = read_token_item(event->tep, &token);
/*
* If the next token is a parenthesis, then this
@@ -3429,7 +3723,7 @@ process_arg_token(struct tep_event *event, struct tep_print_arg *arg,
return TEP_EVENT_ERROR;
}
free_token(token);
- type = read_token_item(&token);
+ type = read_token_item(event->tep, &token);
}
arg->type = TEP_PRINT_ATOM;
@@ -3440,7 +3734,7 @@ process_arg_token(struct tep_event *event, struct tep_print_arg *arg,
case TEP_EVENT_SQUOTE:
arg->type = TEP_PRINT_ATOM;
arg->atom.atom = token;
- type = read_token_item(&token);
+ type = read_token_item(event->tep, &token);
break;
case TEP_EVENT_DELIM:
if (strcmp(token, "(") == 0) {
@@ -3481,7 +3775,7 @@ static int event_read_print_args(struct tep_event *event, struct tep_print_arg *
do {
if (type == TEP_EVENT_NEWLINE) {
- type = read_token_item(&token);
+ type = read_token_item(event->tep, &token);
continue;
}
@@ -3506,6 +3800,10 @@ static int event_read_print_args(struct tep_event *event, struct tep_print_arg *
if (type == TEP_EVENT_OP) {
type = process_op(event, arg, &token);
free_token(token);
+
+ if (consolidate_op_arg(arg) < 0)
+ type = TEP_EVENT_ERROR;
+
if (type == TEP_EVENT_ERROR) {
*list = NULL;
free_arg(arg);
@@ -3536,16 +3834,16 @@ static int event_read_print(struct tep_event *event)
char *token;
int ret;
- if (read_expected_item(TEP_EVENT_ITEM, "print") < 0)
+ if (read_expected_item(event->tep, TEP_EVENT_ITEM, "print") < 0)
return -1;
- if (read_expected(TEP_EVENT_ITEM, "fmt") < 0)
+ if (read_expected(event->tep, TEP_EVENT_ITEM, "fmt") < 0)
return -1;
- if (read_expected(TEP_EVENT_OP, ":") < 0)
+ if (read_expected(event->tep, TEP_EVENT_OP, ":") < 0)
return -1;
- if (read_expect_type(TEP_EVENT_DQUOTE, &token) < 0)
+ if (read_expect_type(event->tep, TEP_EVENT_DQUOTE, &token) < 0)
goto fail;
concat:
@@ -3553,7 +3851,7 @@ static int event_read_print(struct tep_event *event)
event->print_fmt.args = NULL;
/* ok to have no arg */
- type = read_token_item(&token);
+ type = read_token_item(event->tep, &token);
if (type == TEP_EVENT_NONE)
return 0;
@@ -3943,9 +4241,9 @@ static inline void dynamic_offset_field(struct tep_handle *tep,
{
/* Test for overflow */
if (field->offset + field->size > size) {
- if (*offset)
+ if (offset)
*offset = 0;
- if (*len)
+ if (len)
*len = 0;
return;
}
@@ -3954,6 +4252,18 @@ static inline void dynamic_offset_field(struct tep_handle *tep,
*offset += field->offset + field->size;
}
+static bool check_data_offset_size(struct tep_event *event, const char *field_name,
+ int data_size, int field_offset, int field_size)
+{
+ /* Check to make sure the field is within the data */
+ if (field_offset + field_size <= data_size)
+ return false;
+
+ tep_warning("Event '%s' field '%s' goes beyond the size of the event (%d > %d)",
+ event->name, field_name, field_offset + field_size, data_size);
+ return true;
+}
+
static unsigned long long
eval_num_arg(void *data, int size, struct tep_event *event, struct tep_print_arg *arg)
{
@@ -3980,6 +4290,12 @@ eval_num_arg(void *data, int size, struct tep_event *event, struct tep_print_arg
if (!arg->field.field)
goto out_warning_field;
}
+ if (check_data_offset_size(event, arg->field.name, size,
+ arg->field.field->offset,
+ arg->field.field->size)) {
+ val = 0;
+ break;
+ }
/* must be a number */
val = tep_read_number(tep, data + arg->field.field->offset,
arg->field.field->size);
@@ -3996,6 +4312,7 @@ eval_num_arg(void *data, int size, struct tep_event *event, struct tep_print_arg
case TEP_PRINT_STRING:
case TEP_PRINT_BSTRING:
case TEP_PRINT_BITMASK:
+ case TEP_PRINT_CPUMASK:
return 0;
case TEP_PRINT_FUNC: {
struct trace_seq s;
@@ -4047,6 +4364,11 @@ eval_num_arg(void *data, int size, struct tep_event *event, struct tep_print_arg
default:
goto default_op; /* oops, all bets off */
}
+ if (check_data_offset_size(event, arg->field.name, size,
+ offset, field_size)) {
+ val = 0;
+ break;
+ }
val = tep_read_number(tep,
data + offset, field_size);
if (typearg)
@@ -4156,7 +4478,11 @@ eval_num_arg(void *data, int size, struct tep_event *event, struct tep_print_arg
/* Without [], we pass the address to the dynamic data */
dynamic_offset_field(tep, arg->dynarray.field, data, size,
&offset, NULL);
- val = (unsigned long long)((unsigned long)data + offset);
+ if (check_data_offset_size(event, arg->field.name, size,
+ offset, 0)) {
+ val = (unsigned long)data;
+ break;
+ }
val = (unsigned long)data + offset;
break;
default: /* not sure what to do there */
@@ -4277,6 +4603,161 @@ static void print_bitmask_to_seq(struct tep_handle *tep,
free(str);
}
+#define log10(n) \
+( \
+ n < 10UL ? 0 : \
+ n < 100UL ? 1 : \
+ n < 1000UL ? 2 : \
+ n < 10000UL ? 3 : \
+ n < 100000UL ? 4 : \
+ n < 1000000UL ? 5 : \
+ n < 10000000UL ? 6 : \
+ n < 100000000UL ? 7 : \
+ n < 1000000000UL ? 8 : \
+ 9 \
+)
+
+/* ilog10(0) should be 1 but the 0 simplifies below math */
+#define ilog10(n) \
+( \
+ n == 0 ? 0UL : \
+ n == 1 ? 10UL : \
+ n == 2 ? 100UL : \
+ n == 3 ? 1000UL : \
+ n == 4 ? 10000UL : \
+ n == 5 ? 100000UL : \
+ n == 6 ? 1000000UL : \
+ n == 7 ? 10000000UL : \
+ n == 8 ? 100000000UL : \
+ 1000000000UL \
+)
+
+static unsigned int cpumask_worst_size(unsigned int nr_bits)
+{
+ /*
+ * Printing all the CPUs separated by a comma is a decent bound for the
+ * maximum memory required to print a cpumask (a slightly better bound
+ * is chunks of 2 bits set, i.e. 0-1,3-4,6-7...).
+ *
+ * e.g. for nr_bits=132:
+ * - 131 commas
+ * - 10 * 1 chars for CPUS [0, 9]
+ * - 90 * 2 chars for CPUS [10-99]
+ * - 32 * 3 chars for CPUS [100-131]
+ */
+ unsigned int last_cpu = nr_bits - 1;
+ unsigned int nr_chars = nr_bits - 1;
+ int last_lvl = log10(last_cpu);
+
+ /* All log10 levels before the last one have all values used */
+ for (int lvl = 0; lvl < last_lvl; lvl++) {
+ int nr_values = ilog10(lvl + 1) - ilog10(lvl);
+
+ nr_chars += nr_values * (lvl + 1);
+ }
+ /* Last level is incomplete */
+ nr_chars += (nr_bits - ilog10(last_lvl)) * (last_lvl + 1);
+
+ return nr_chars;
+}
+
+static void print_cpumask_to_seq(struct tep_handle *tep,
+ struct trace_seq *s, const char *format,
+ int len_arg, const void *data, int size)
+{
+ int firstone = -1, firstzero = -1;
+ int nr_bits = size * 8;
+ bool first = true;
+ int str_size = 0;
+ char buf[12]; /* '-' + log10(2^32) + 1 digits + '\0' */
+ char *str;
+ int index;
+ int i;
+
+ str = malloc(cpumask_worst_size(nr_bits) + 1);
+ if (!str) {
+ do_warning("%s: not enough memory!", __func__);
+ return;
+ }
+
+ for (i = 0; i < size; i++) {
+ unsigned char byte;
+ int fmtsize;
+
+ if (tep->file_bigendian)
+ index = size - (i + 1);
+ else
+ index = i;
+
+ /* Byte by byte scan, not the best... */
+ byte = *(((unsigned char *)data) + index);
+more:
+ /* First find a bit set to one...*/
+ if (firstone < 0 && byte) {
+ /*
+ * Set all lower bits, so a later ffz on this same byte
+ * is guaranteed to find a later bit.
+ */
+ firstone = ffs(byte) - 1;
+ byte |= (1 << firstone) - 1;
+ firstone += i * 8;
+ }
+
+ if (firstone < 0)
+ continue;
+
+ /* ...Then find a bit set to zero */
+ if ((~byte) & 0xFF) {
+ /*
+ * Clear all lower bits, so a later ffs on this same
+ * byte is guaranteed to find a later bit.
+ */
+ firstzero = ffs(~byte) - 1;
+ byte &= ~((1 << (firstzero)) - 1);
+ firstzero += i * 8;
+ } else if (i == size - 1) { /* ...Or reach the end of the mask */
+ firstzero = nr_bits;
+ byte = 0;
+ } else {
+ continue;
+ }
+
+ /* We've found a bit set to one, and a later bit set to zero. */
+ if (!first) {
+ str[str_size] = ',';
+ str_size++;
+ }
+ first = false;
+
+ /* It takes {log10(number) + 1} chars to format a number */
+ fmtsize = log10(firstone) + 1;
+ snprintf(buf, fmtsize + 1, "%d", firstone);
+ memcpy(str + str_size, buf, fmtsize);
+ str_size += fmtsize;
+
+ if (firstzero > firstone + 1) {
+ fmtsize = log10(firstzero - 1) + 2;
+ snprintf(buf, fmtsize + 1, "-%d", firstzero - 1);
+ memcpy(str + str_size, buf, fmtsize);
+ str_size += fmtsize;
+ }
+
+ firstzero = firstone = -1;
+ if (byte)
+ goto more;
+ }
+
+ str[str_size] = 0;
+ str_size++;
+
+ if (len_arg >= 0)
+ trace_seq_printf(s, format, len_arg, str);
+ else
+ trace_seq_printf(s, format, str);
+
+ free(str);
+}
+
static void print_str_arg(struct trace_seq *s, void *data, int size,
struct tep_event *event, const char *format,
int len_arg, struct tep_print_arg *arg)
@@ -4466,10 +4947,10 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
case TEP_PRINT_STRING: {
if (!arg->string.field) {
arg->string.field = tep_find_any_field(event, arg->string.string);
+ if (!arg->string.field)
+ break;
arg->string.offset = arg->string.field->offset;
}
- if (!arg->string.field)
- break;
dynamic_offset_field(tep, arg->string.field, data, size, &offset, &len);
/* Do not attempt to save zero length dynamic strings */
if (!len)
@@ -4483,12 +4964,24 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
case TEP_PRINT_BITMASK: {
if (!arg->bitmask.field) {
arg->bitmask.field = tep_find_any_field(event, arg->bitmask.bitmask);
+ if (!arg->bitmask.field)
+ break;
+ arg->bitmask.offset = arg->bitmask.field->offset;
+ }
+ dynamic_offset_field(tep, arg->bitmask.field, data, size, &offset, &len);
+ print_bitmask_to_seq(tep, s, format, len_arg,
+ data + offset, len);
+ break;
+ }
+ case TEP_PRINT_CPUMASK: {
+ if (!arg->bitmask.field) {
+ arg->bitmask.field = tep_find_any_field(event, arg->bitmask.bitmask);
arg->bitmask.offset = arg->bitmask.field->offset;
}
if (!arg->bitmask.field)
break;
dynamic_offset_field(tep, arg->bitmask.field, data, size, &offset, &len);
- print_bitmask_to_seq(tep, s, format, len_arg,
+ print_cpumask_to_seq(tep, s, format, len_arg,
data + offset, len);
break;
}
@@ -4790,8 +5283,10 @@ static struct tep_print_arg *make_bprint_args(char *fmt, void *data, int size, s
arg->next = NULL;
arg->type = TEP_PRINT_BSTRING;
arg->string.string = strdup(bptr);
- if (!arg->string.string)
+ if (!arg->string.string) {
+ free(arg);
goto out_free;
+ }
bptr += strlen(bptr) + 1;
*next = arg;
next = &arg->next;
@@ -5485,6 +5980,7 @@ static void print_field_raw(struct trace_seq *s, void *data, int size,
trace_seq_printf(s, "%llu", val);
}
}
+ trace_seq_terminate(s);
}
static int print_parse_data(struct tep_print_parse *parse, struct trace_seq *s,
@@ -5498,7 +5994,7 @@ static inline void print_field(struct trace_seq *s, void *data, int size,
struct tep_print_parse *start_parse;
struct tep_print_parse *parse;
struct tep_print_arg *arg;
- bool has_0x;
+ bool has_0x = false;
parse = parse_ptr ? *parse_ptr : event->print_fmt.print_cache;
@@ -5536,7 +6032,7 @@ static inline void print_field(struct trace_seq *s, void *data, int size,
if (has_0x)
trace_seq_puts(s, "0x");
- print_parse_data(parse, s, data, field->size, event);
+ print_parse_data(parse, s, data, size, event);
if (parse_ptr)
*parse_ptr = parse->next;
@@ -6272,8 +6768,13 @@ static void data_latency_format(struct tep_handle *tep, struct trace_seq *s,
(hardirq && softirq) ? 'H' :
hardirq ? 'h' : softirq ? 's' : '.');
- if (pc)
- trace_seq_printf(&sq, "%x", pc);
+ if (pc & 0xf)
+ trace_seq_printf(&sq, "%x", pc & 0xf);
+ else
+ trace_seq_printf(&sq, ".");
+
+ if (pc & 0xf0)
+ trace_seq_printf(&sq, "%x", pc >> 4);
else
trace_seq_printf(&sq, ".");
@@ -6999,6 +7500,9 @@ static void print_args(struct tep_print_arg *args)
case TEP_PRINT_BITMASK:
printf("__get_bitmask(%s)", args->bitmask.bitmask);
break;
+ case TEP_PRINT_CPUMASK:
+ printf("__get_cpumask(%s)", args->bitmask.bitmask);
+ break;
case TEP_PRINT_TYPE:
printf("(%s)", args->typecast.type);
print_args(args->typecast.item);
@@ -7024,7 +7528,7 @@ static void print_args(struct tep_print_arg *args)
}
}
-static void parse_header_field(const char *field,
+static void parse_header_field(struct tep_handle *tep, const char *field,
int *offset, int *size, int mandatory)
{
unsigned long long save_input_buf_ptr;
@@ -7032,16 +7536,16 @@ static void parse_header_field(const char *field,
char *token;
int type;
- save_input_buf_ptr = input_buf_ptr;
- save_input_buf_siz = input_buf_siz;
+ save_input_buf_ptr = tep->input_buf_ptr;
+ save_input_buf_siz = tep->input_buf_siz;
- if (read_expected(TEP_EVENT_ITEM, "field") < 0)
+ if (read_expected(tep, TEP_EVENT_ITEM, "field") < 0)
return;
- if (read_expected(TEP_EVENT_OP, ":") < 0)
+ if (read_expected(tep, TEP_EVENT_OP, ":") < 0)
return;
/* type */
- if (read_expect_type(TEP_EVENT_ITEM, &token) < 0)
+ if (read_expect_type(tep, TEP_EVENT_ITEM, &token) < 0)
goto fail;
free_token(token);
@@ -7049,39 +7553,39 @@ static void parse_header_field(const char *field,
* If this is not a mandatory field, then test it first.
*/
if (mandatory) {
- if (read_expected(TEP_EVENT_ITEM, field) < 0)
+ if (read_expected(tep, TEP_EVENT_ITEM, field) < 0)
return;
} else {
- if (read_expect_type(TEP_EVENT_ITEM, &token) < 0)
+ if (read_expect_type(tep, TEP_EVENT_ITEM, &token) < 0)
goto fail;
if (strcmp(token, field) != 0)
goto discard;
free_token(token);
}
- if (read_expected(TEP_EVENT_OP, ";") < 0)
+ if (read_expected(tep, TEP_EVENT_OP, ";") < 0)
return;
- if (read_expected(TEP_EVENT_ITEM, "offset") < 0)
+ if (read_expected(tep, TEP_EVENT_ITEM, "offset") < 0)
return;
- if (read_expected(TEP_EVENT_OP, ":") < 0)
+ if (read_expected(tep, TEP_EVENT_OP, ":") < 0)
return;
- if (read_expect_type(TEP_EVENT_ITEM, &token) < 0)
+ if (read_expect_type(tep, TEP_EVENT_ITEM, &token) < 0)
goto fail;
*offset = atoi(token);
free_token(token);
- if (read_expected(TEP_EVENT_OP, ";") < 0)
+ if (read_expected(tep, TEP_EVENT_OP, ";") < 0)
return;
- if (read_expected(TEP_EVENT_ITEM, "size") < 0)
+ if (read_expected(tep, TEP_EVENT_ITEM, "size") < 0)
return;
- if (read_expected(TEP_EVENT_OP, ":") < 0)
+ if (read_expected(tep, TEP_EVENT_OP, ":") < 0)
return;
- if (read_expect_type(TEP_EVENT_ITEM, &token) < 0)
+ if (read_expect_type(tep, TEP_EVENT_ITEM, &token) < 0)
goto fail;
*size = atoi(token);
free_token(token);
- if (read_expected(TEP_EVENT_OP, ";") < 0)
+ if (read_expected(tep, TEP_EVENT_OP, ";") < 0)
return;
- type = read_token(&token);
+ type = read_token(tep, &token);
if (type != TEP_EVENT_NEWLINE) {
/* newer versions of the kernel have a "signed" type */
if (type != TEP_EVENT_ITEM)
@@ -7092,17 +7596,17 @@ static void parse_header_field(const char *field,
free_token(token);
- if (read_expected(TEP_EVENT_OP, ":") < 0)
+ if (read_expected(tep, TEP_EVENT_OP, ":") < 0)
return;
- if (read_expect_type(TEP_EVENT_ITEM, &token))
+ if (read_expect_type(tep, TEP_EVENT_ITEM, &token))
goto fail;
free_token(token);
- if (read_expected(TEP_EVENT_OP, ";") < 0)
+ if (read_expected(tep, TEP_EVENT_OP, ";") < 0)
return;
- if (read_expect_type(TEP_EVENT_NEWLINE, &token))
+ if (read_expect_type(tep, TEP_EVENT_NEWLINE, &token))
goto fail;
}
fail:
@@ -7110,8 +7614,8 @@ static void parse_header_field(const char *field,
return;
discard:
- input_buf_ptr = save_input_buf_ptr;
- input_buf_siz = save_input_buf_siz;
+ tep->input_buf_ptr = save_input_buf_ptr;
+ tep->input_buf_siz = save_input_buf_siz;
*offset = 0;
*size = 0;
free_token(token);
@@ -7146,15 +7650,15 @@ int tep_parse_header_page(struct tep_handle *tep, char *buf, unsigned long size,
tep->old_format = 1;
return -1;
}
- init_input_buf(buf, size);
+ init_input_buf(tep, buf, size);
- parse_header_field("timestamp", &tep->header_page_ts_offset,
+ parse_header_field(tep, "timestamp", &tep->header_page_ts_offset,
&tep->header_page_ts_size, 1);
- parse_header_field("commit", &tep->header_page_size_offset,
+ parse_header_field(tep, "commit", &tep->header_page_size_offset,
&tep->header_page_size_size, 1);
- parse_header_field("overwrite", &tep->header_page_overwrite,
+ parse_header_field(tep, "overwrite", &tep->header_page_overwrite,
&ignore, 0);
- parse_header_field("data", &tep->header_page_data_offset,
+ parse_header_field(tep, "data", &tep->header_page_data_offset,
&tep->header_page_data_size, 1);
return 0;
@@ -7231,13 +7735,13 @@ static enum tep_errno parse_format(struct tep_event **eventp,
struct tep_event *event;
int ret;
- init_input_buf(buf, size);
+ init_input_buf(tep, buf, size);
*eventp = event = alloc_event();
if (!event)
return TEP_ERRNO__MEM_ALLOC_FAILED;
- event->name = event_read_name();
+ event->name = event_read_name(tep);
if (!event->name) {
/* Bad event? */
ret = TEP_ERRNO__MEM_ALLOC_FAILED;
@@ -7251,7 +7755,7 @@ static enum tep_errno parse_format(struct tep_event **eventp,
event->flags |= TEP_EVENT_FL_ISBPRINT;
}
- event->id = event_read_id();
+ event->id = event_read_id(tep);
if (event->id < 0) {
ret = TEP_ERRNO__READ_ID_FAILED;
/*
diff --git a/src/kbuffer-parse.c b/src/kbuffer-parse.c
index 7499950..390a789 100644
--- a/src/kbuffer-parse.c
+++ b/src/kbuffer-parse.c
@@ -6,6 +6,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stdbool.h>
+
+#include <sys/utsname.h>
#include "kbuffer.h"
@@ -14,6 +17,9 @@
#define COMMIT_MASK ((1 << 27) - 1)
+/* Absolute time stamps do not have the 5 MSB, take from the real time stamp */
+#define TS_MSB (0xf8ULL << 56)
+
enum {
KBUFFER_FL_HOST_BIG_ENDIAN = (1<<0),
KBUFFER_FL_BIG_ENDIAN = (1<<1),
@@ -156,6 +162,24 @@ static int calc_index(struct kbuffer *kbuf, void *ptr)
static int __next_event(struct kbuffer *kbuf);
+/*
+ * Just because sizeof(long) is 4 bytes, doesn't mean the OS isn't
+ * 64bits
+ */
+static bool host_is_32bit(void)
+{
+ struct utsname buf;
+ int ret;
+
+ ret = uname(&buf);
+ if (ret < 0) {
+ /* Oh well, just assume it is 32 bit */
+ return true;
+ }
+ /* If the uname machine value contains 64, assume the kernel is 64 bit */
+ return strstr(buf.machine, "64") == NULL;
+}
+
/**
* kbuffer_alloc - allocat a new kbuffer
* @size; enum to denote size of word
@@ -172,6 +196,10 @@ kbuffer_alloc(enum kbuffer_long_size size, enum kbuffer_endian endian)
switch (size) {
case KBUFFER_LSIZE_4:
break;
+ case KBUFFER_LSIZE_SAME_AS_HOST:
+ if (sizeof(long) != 8 && host_is_32bit())
+ break;
+ /* fallthrough */
case KBUFFER_LSIZE_8:
flags |= KBUFFER_FL_LONG_8;
break;
@@ -181,6 +209,7 @@ kbuffer_alloc(enum kbuffer_long_size size, enum kbuffer_endian endian)
switch (endian) {
case KBUFFER_ENDIAN_LITTLE:
+ case KBUFFER_ENDIAN_SAME_AS_HOST:
break;
case KBUFFER_ENDIAN_BIG:
flags |= KBUFFER_FL_BIG_ENDIAN;
@@ -195,8 +224,11 @@ kbuffer_alloc(enum kbuffer_long_size size, enum kbuffer_endian endian)
kbuf->flags = flags;
- if (host_is_bigendian())
+ if (host_is_bigendian()) {
+ if (endian == KBUFFER_ENDIAN_SAME_AS_HOST)
+ flags |= KBUFFER_FL_BIG_ENDIAN;
kbuf->flags |= KBUFFER_FL_HOST_BIG_ENDIAN;
+ }
if (do_swap(kbuf)) {
kbuf->read_8 = __read_8_sw;
@@ -347,7 +379,7 @@ static unsigned int
translate_data(struct kbuffer *kbuf, void *data, void **rptr,
unsigned long long *delta, int *length)
{
- unsigned long long extend;
+ unsigned long long extend, msb = 0;
unsigned int type_len_ts;
unsigned int type_len;
@@ -362,13 +394,15 @@ translate_data(struct kbuffer *kbuf, void *data, void **rptr,
*length = read_4(kbuf, data);
break;
- case KBUFFER_TYPE_TIME_EXTEND:
case KBUFFER_TYPE_TIME_STAMP:
+ msb = kbuf->timestamp & TS_MSB;
+ /* fall through */
+ case KBUFFER_TYPE_TIME_EXTEND:
extend = read_4(kbuf, data);
data += 4;
extend <<= TS_SHIFT;
extend += *delta;
- *delta = extend;
+ *delta = extend | msb;
*length = 0;
break;
diff --git a/src/parse-filter.c b/src/parse-filter.c
index 5df1770..e448ee2 100644
--- a/src/parse-filter.c
+++ b/src/parse-filter.c
@@ -30,7 +30,7 @@ struct event_list {
struct tep_event *event;
};
-static void show_error(char *error_buf, const char *fmt, ...)
+static void show_error(struct tep_handle *tep, char *error_buf, const char *fmt, ...)
{
unsigned long long index;
const char *input;
@@ -38,8 +38,8 @@ static void show_error(char *error_buf, const char *fmt, ...)
int len;
int i;
- input = get_input_buf();
- index = get_input_buf_ptr();
+ input = get_input_buf(tep);
+ index = get_input_buf_ptr(tep);
len = input ? strlen(input) : 0;
if (len) {
@@ -57,20 +57,20 @@ static void show_error(char *error_buf, const char *fmt, ...)
va_end(ap);
}
-static enum tep_event_type filter_read_token(char **tok)
+static enum tep_event_type filter_read_token(struct tep_handle *tep, char **tok)
{
enum tep_event_type type;
char *token = NULL;
do {
free_token(token);
- type = read_token(&token);
+ type = read_token(tep, &token);
} while (type == TEP_EVENT_NEWLINE || type == TEP_EVENT_SPACE);
/* If token is = or ! check to see if the next char is ~ */
if (token &&
(strcmp(token, "=") == 0 || strcmp(token, "!") == 0) &&
- peek_char() == '~') {
+ peek_char(tep) == '~') {
/* append it */
*tok = malloc(3);
if (*tok == NULL) {
@@ -80,7 +80,7 @@ static enum tep_event_type filter_read_token(char **tok)
sprintf(*tok, "%c%c", *token, '~');
free_token(token);
/* Now remove the '~' from the buffer */
- read_token(&token);
+ read_token(tep, &token);
free_token(token);
} else
*tok = token;
@@ -337,7 +337,7 @@ create_arg_item(struct tep_event *event, const char *token,
arg = allocate_arg();
if (arg == NULL) {
- show_error(error_str, "failed to allocate filter arg");
+ show_error(event->tep, error_str, "failed to allocate filter arg");
return TEP_ERRNO__MEM_ALLOC_FAILED;
}
@@ -351,7 +351,7 @@ create_arg_item(struct tep_event *event, const char *token,
arg->value.str = strdup(token);
if (!arg->value.str) {
free_arg(arg);
- show_error(error_str, "failed to allocate string filter arg");
+ show_error(event->tep, error_str, "failed to allocate string filter arg");
return TEP_ERRNO__MEM_ALLOC_FAILED;
}
break;
@@ -383,7 +383,7 @@ create_arg_item(struct tep_event *event, const char *token,
break;
default:
free_arg(arg);
- show_error(error_str, "expected a value but found %s", token);
+ show_error(event->tep, error_str, "expected a value but found %s", token);
return TEP_ERRNO__UNEXPECTED_TYPE;
}
*parg = arg;
@@ -437,7 +437,8 @@ create_arg_cmp(enum tep_filter_cmp_type ctype)
}
static enum tep_errno
-add_right(struct tep_filter_arg *op, struct tep_filter_arg *arg, char *error_str)
+add_right(struct tep_handle *tep, struct tep_filter_arg *op,
+ struct tep_filter_arg *arg, char *error_str)
{
struct tep_filter_arg *left;
char *str;
@@ -468,7 +469,7 @@ add_right(struct tep_filter_arg *op, struct tep_filter_arg *arg, char *error_str
case TEP_FILTER_ARG_FIELD:
break;
default:
- show_error(error_str, "Illegal rvalue");
+ show_error(tep, error_str, "Illegal rvalue");
return TEP_ERRNO__ILLEGAL_RVALUE;
}
@@ -514,7 +515,7 @@ add_right(struct tep_filter_arg *op, struct tep_filter_arg *arg, char *error_str
/* Left arg must be a field */
if (left->type != TEP_FILTER_ARG_FIELD) {
- show_error(error_str,
+ show_error(tep, error_str,
"Illegal lvalue for string comparison");
return TEP_ERRNO__ILLEGAL_LVALUE;
}
@@ -532,14 +533,14 @@ add_right(struct tep_filter_arg *op, struct tep_filter_arg *arg, char *error_str
case TEP_FILTER_CMP_NOT_REGEX:
ret = regcomp(&op->str.reg, str, REG_ICASE|REG_NOSUB);
if (ret) {
- show_error(error_str,
+ show_error(tep, error_str,
"RegEx '%s' did not compute",
str);
return TEP_ERRNO__INVALID_REGEX;
}
break;
default:
- show_error(error_str,
+ show_error(tep, error_str,
"Illegal comparison for string");
return TEP_ERRNO__ILLEGAL_STRING_CMP;
}
@@ -549,7 +550,7 @@ add_right(struct tep_filter_arg *op, struct tep_filter_arg *arg, char *error_str
op->str.field = left->field.field;
op->str.val = strdup(str);
if (!op->str.val) {
- show_error(error_str, "Failed to allocate string filter");
+ show_error(tep, error_str, "Failed to allocate string filter");
return TEP_ERRNO__MEM_ALLOC_FAILED;
}
/*
@@ -557,7 +558,7 @@ add_right(struct tep_filter_arg *op, struct tep_filter_arg *arg, char *error_str
*/
op->str.buffer = malloc(op->str.field->size + 1);
if (!op->str.buffer) {
- show_error(error_str, "Failed to allocate string filter");
+ show_error(tep, error_str, "Failed to allocate string filter");
return TEP_ERRNO__MEM_ALLOC_FAILED;
}
/* Null terminate this buffer */
@@ -575,7 +576,7 @@ add_right(struct tep_filter_arg *op, struct tep_filter_arg *arg, char *error_str
switch (op->num.type) {
case TEP_FILTER_CMP_REGEX:
case TEP_FILTER_CMP_NOT_REGEX:
- show_error(error_str,
+ show_error(tep, error_str,
"Op not allowed with integers");
return TEP_ERRNO__ILLEGAL_INTEGER_CMP;
@@ -597,7 +598,7 @@ add_right(struct tep_filter_arg *op, struct tep_filter_arg *arg, char *error_str
return 0;
out_fail:
- show_error(error_str, "Syntax error");
+ show_error(tep, error_str, "Syntax error");
return TEP_ERRNO__SYNTAX_ERROR;
}
@@ -748,15 +749,16 @@ enum filter_vals {
};
static enum tep_errno
-reparent_op_arg(struct tep_filter_arg *parent, struct tep_filter_arg *old_child,
- struct tep_filter_arg *arg, char *error_str)
+reparent_op_arg(struct tep_handle *tep, struct tep_filter_arg *parent,
+ struct tep_filter_arg *old_child, struct tep_filter_arg *arg,
+ char *error_str)
{
struct tep_filter_arg *other_child;
struct tep_filter_arg **ptr;
if (parent->type != TEP_FILTER_ARG_OP &&
arg->type != TEP_FILTER_ARG_OP) {
- show_error(error_str, "can not reparent other than OP");
+ show_error(tep, error_str, "can not reparent other than OP");
return TEP_ERRNO__REPARENT_NOT_OP;
}
@@ -768,7 +770,7 @@ reparent_op_arg(struct tep_filter_arg *parent, struct tep_filter_arg *old_child,
ptr = &old_child->op.left;
other_child = old_child->op.right;
} else {
- show_error(error_str, "Error in reparent op, find other child");
+ show_error(tep, error_str, "Error in reparent op, find other child");
return TEP_ERRNO__REPARENT_FAILED;
}
@@ -789,7 +791,7 @@ reparent_op_arg(struct tep_filter_arg *parent, struct tep_filter_arg *old_child,
else if (parent->op.left == old_child)
ptr = &parent->op.left;
else {
- show_error(error_str, "Error in reparent op");
+ show_error(tep, error_str, "Error in reparent op");
return TEP_ERRNO__REPARENT_FAILED;
}
@@ -800,8 +802,8 @@ reparent_op_arg(struct tep_filter_arg *parent, struct tep_filter_arg *old_child,
}
/* Returns either filter_vals (success) or tep_errno (failfure) */
-static int test_arg(struct tep_filter_arg *parent, struct tep_filter_arg *arg,
- char *error_str)
+static int test_arg(struct tep_handle *tep, struct tep_filter_arg *parent,
+ struct tep_filter_arg *arg, char *error_str)
{
int lval, rval;
@@ -818,47 +820,47 @@ static int test_arg(struct tep_filter_arg *parent, struct tep_filter_arg *arg,
return FILTER_VAL_NORM;
case TEP_FILTER_ARG_EXP:
- lval = test_arg(arg, arg->exp.left, error_str);
+ lval = test_arg(tep, arg, arg->exp.left, error_str);
if (lval != FILTER_VAL_NORM)
return lval;
- rval = test_arg(arg, arg->exp.right, error_str);
+ rval = test_arg(tep, arg, arg->exp.right, error_str);
if (rval != FILTER_VAL_NORM)
return rval;
return FILTER_VAL_NORM;
case TEP_FILTER_ARG_NUM:
- lval = test_arg(arg, arg->num.left, error_str);
+ lval = test_arg(tep, arg, arg->num.left, error_str);
if (lval != FILTER_VAL_NORM)
return lval;
- rval = test_arg(arg, arg->num.right, error_str);
+ rval = test_arg(tep, arg, arg->num.right, error_str);
if (rval != FILTER_VAL_NORM)
return rval;
return FILTER_VAL_NORM;
case TEP_FILTER_ARG_OP:
if (arg->op.type != TEP_FILTER_OP_NOT) {
- lval = test_arg(arg, arg->op.left, error_str);
+ lval = test_arg(tep, arg, arg->op.left, error_str);
switch (lval) {
case FILTER_VAL_NORM:
break;
case FILTER_VAL_TRUE:
if (arg->op.type == TEP_FILTER_OP_OR)
return FILTER_VAL_TRUE;
- rval = test_arg(arg, arg->op.right, error_str);
+ rval = test_arg(tep, arg, arg->op.right, error_str);
if (rval != FILTER_VAL_NORM)
return rval;
- return reparent_op_arg(parent, arg, arg->op.right,
+ return reparent_op_arg(tep, parent, arg, arg->op.right,
error_str);
case FILTER_VAL_FALSE:
if (arg->op.type == TEP_FILTER_OP_AND)
return FILTER_VAL_FALSE;
- rval = test_arg(arg, arg->op.right, error_str);
+ rval = test_arg(tep, arg, arg->op.right, error_str);
if (rval != FILTER_VAL_NORM)
return rval;
- return reparent_op_arg(parent, arg, arg->op.right,
+ return reparent_op_arg(tep, parent, arg, arg->op.right,
error_str);
default:
@@ -866,7 +868,7 @@ static int test_arg(struct tep_filter_arg *parent, struct tep_filter_arg *arg,
}
}
- rval = test_arg(arg, arg->op.right, error_str);
+ rval = test_arg(tep, arg, arg->op.right, error_str);
switch (rval) {
case FILTER_VAL_NORM:
default:
@@ -878,7 +880,7 @@ static int test_arg(struct tep_filter_arg *parent, struct tep_filter_arg *arg,
if (arg->op.type == TEP_FILTER_OP_NOT)
return FILTER_VAL_FALSE;
- return reparent_op_arg(parent, arg, arg->op.left,
+ return reparent_op_arg(tep, parent, arg, arg->op.left,
error_str);
case FILTER_VAL_FALSE:
@@ -887,25 +889,25 @@ static int test_arg(struct tep_filter_arg *parent, struct tep_filter_arg *arg,
if (arg->op.type == TEP_FILTER_OP_NOT)
return FILTER_VAL_TRUE;
- return reparent_op_arg(parent, arg, arg->op.left,
+ return reparent_op_arg(tep, parent, arg, arg->op.left,
error_str);
}
return rval;
default:
- show_error(error_str, "bad arg in filter tree");
+ show_error(tep, error_str, "bad arg in filter tree");
return TEP_ERRNO__BAD_FILTER_ARG;
}
return FILTER_VAL_NORM;
}
/* Remove any unknown event fields */
-static int collapse_tree(struct tep_filter_arg *arg,
+static int collapse_tree(struct tep_handle *tep, struct tep_filter_arg *arg,
struct tep_filter_arg **arg_collapsed, char *error_str)
{
int ret;
- ret = test_arg(arg, arg, error_str);
+ ret = test_arg(tep, arg, arg, error_str);
switch (ret) {
case FILTER_VAL_NORM:
break;
@@ -918,7 +920,7 @@ static int collapse_tree(struct tep_filter_arg *arg,
arg->type = TEP_FILTER_ARG_BOOLEAN;
arg->boolean.value = ret == FILTER_VAL_TRUE;
} else {
- show_error(error_str, "Failed to allocate filter arg");
+ show_error(tep, error_str, "Failed to allocate filter arg");
ret = TEP_ERRNO__MEM_ALLOC_FAILED;
}
break;
@@ -954,7 +956,7 @@ process_filter(struct tep_event *event, struct tep_filter_arg **parg,
do {
free(token);
- type = filter_read_token(&token);
+ type = filter_read_token(event->tep, &token);
switch (type) {
case TEP_EVENT_SQUOTE:
case TEP_EVENT_DQUOTE:
@@ -965,7 +967,7 @@ process_filter(struct tep_event *event, struct tep_filter_arg **parg,
if (!left_item)
left_item = arg;
else if (current_exp) {
- ret = add_right(current_exp, arg, error_str);
+ ret = add_right(event->tep, current_exp, arg, error_str);
if (ret < 0)
goto fail;
left_item = NULL;
@@ -985,20 +987,20 @@ process_filter(struct tep_event *event, struct tep_filter_arg **parg,
case TEP_EVENT_DELIM:
if (*token == ',') {
- show_error(error_str, "Illegal token ','");
+ show_error(event->tep, error_str, "Illegal token ','");
ret = TEP_ERRNO__ILLEGAL_TOKEN;
goto fail;
}
if (*token == '(') {
if (left_item) {
- show_error(error_str,
+ show_error(event->tep, error_str,
"Open paren can not come after item");
ret = TEP_ERRNO__INVALID_PAREN;
goto fail;
}
if (current_exp) {
- show_error(error_str,
+ show_error(event->tep, error_str,
"Open paren can not come after expression");
ret = TEP_ERRNO__INVALID_PAREN;
goto fail;
@@ -1007,7 +1009,7 @@ process_filter(struct tep_event *event, struct tep_filter_arg **parg,
ret = process_filter(event, &arg, error_str, 0);
if (ret != TEP_ERRNO__UNBALANCED_PAREN) {
if (ret == 0) {
- show_error(error_str,
+ show_error(event->tep, error_str,
"Unbalanced number of '('");
ret = TEP_ERRNO__UNBALANCED_PAREN;
}
@@ -1024,7 +1026,7 @@ process_filter(struct tep_event *event, struct tep_filter_arg **parg,
}
if (current_op)
- ret = add_right(current_op, arg, error_str);
+ ret = add_right(event->tep, current_op, arg, error_str);
else
current_exp = arg;
@@ -1071,7 +1073,7 @@ process_filter(struct tep_event *event, struct tep_filter_arg **parg,
goto fail_syntax;
break;
case OP_NONE:
- show_error(error_str,
+ show_error(event->tep, error_str,
"Unknown op token %s", token);
ret = TEP_ERRNO__UNKNOWN_TOKEN;
goto fail;
@@ -1096,14 +1098,14 @@ process_filter(struct tep_event *event, struct tep_filter_arg **parg,
if (arg == NULL)
goto fail_alloc;
if (current_op)
- ret = add_right(current_op, arg, error_str);
+ ret = add_right(event->tep, current_op, arg, error_str);
if (ret < 0)
goto fail;
current_exp = arg;
ret = process_filter(event, &arg, error_str, 1);
if (ret < 0)
goto fail;
- ret = add_right(current_exp, arg, error_str);
+ ret = add_right(event->tep, current_exp, arg, error_str);
if (ret < 0)
goto fail;
break;
@@ -1118,7 +1120,7 @@ process_filter(struct tep_event *event, struct tep_filter_arg **parg,
goto fail_alloc;
if (current_op)
- ret = add_right(current_op, arg, error_str);
+ ret = add_right(event->tep,current_op, arg, error_str);
if (ret < 0)
goto fail;
ret = add_left(arg, left_item);
@@ -1150,7 +1152,7 @@ process_filter(struct tep_event *event, struct tep_filter_arg **parg,
if (!current_op)
current_op = current_exp;
- ret = collapse_tree(current_op, parg, error_str);
+ ret = collapse_tree(event->tep, current_op, parg, error_str);
/* collapse_tree() may free current_op, and updates parg accordingly */
current_op = NULL;
if (ret < 0)
@@ -1160,11 +1162,11 @@ process_filter(struct tep_event *event, struct tep_filter_arg **parg,
return 0;
fail_alloc:
- show_error(error_str, "failed to allocate filter arg");
+ show_error(event->tep, error_str, "failed to allocate filter arg");
ret = TEP_ERRNO__MEM_ALLOC_FAILED;
goto fail;
fail_syntax:
- show_error(error_str, "Syntax error");
+ show_error(event->tep, error_str, "Syntax error");
ret = TEP_ERRNO__SYNTAX_ERROR;
fail:
free_arg(current_op);
@@ -1180,7 +1182,7 @@ process_event(struct tep_event *event, const char *filter_str,
{
int ret;
- init_input_buf(filter_str, strlen(filter_str));
+ init_input_buf(event->tep, filter_str, strlen(filter_str));
ret = process_filter(event, parg, error_str, 0);
if (ret < 0)
@@ -1238,7 +1240,7 @@ filter_event(struct tep_event_filter *filter, struct tep_event *event,
static void filter_init_error_buf(struct tep_event_filter *filter)
{
/* clear buffer to reset show error */
- init_input_buf("", 0);
+ init_input_buf(filter->tep, "", 0);
filter->error_buffer[0] = '\0';
}
diff --git a/src/parse-utils.c b/src/parse-utils.c
index 09059ed..9c38e1e 100644
--- a/src/parse-utils.c
+++ b/src/parse-utils.c
@@ -11,6 +11,7 @@
#include "event-utils.h"
#include "event-parse.h"
+#include "kbuffer.h"
#define __weak __attribute__((weak))
@@ -122,3 +123,29 @@ void __weak __vpr_stat(const char *fmt, va_list ap)
}
void vpr_stat(const char *fmt, va_list ap) __attribute__((weak, alias("__vpr_stat")));
+
+/**
+ * tep_kbuffer - return an allocated kbuffer that can be used for the tep handle
+ * @tep: the handle that will work with the kbuffer descriptor
+ *
+ * Allocates and returns a new kbuffer.
+ * The return must be freed by kbuffer_free();
+ */
+struct kbuffer *tep_kbuffer(struct tep_handle *tep)
+{
+ enum kbuffer_endian endian;
+ int long_size;
+
+ long_size = tep_get_long_size(tep);
+ if (long_size == 8)
+ long_size = KBUFFER_LSIZE_8;
+ else
+ long_size = KBUFFER_LSIZE_4;
+
+ if (tep_is_file_bigendian(tep))
+ endian = KBUFFER_ENDIAN_BIG;
+ else
+ endian = KBUFFER_ENDIAN_LITTLE;
+
+ return kbuffer_alloc(long_size, endian);
+}
diff --git a/utest/.gitignore b/utest/.gitignore
new file mode 100644
index 0000000..3620cd0
--- /dev/null
+++ b/utest/.gitignore
@@ -0,0 +1 @@
+trace-utest
diff --git a/utest/traceevent-utest.c b/utest/traceevent-utest.c
index 99900de..ebd5eb9 100644
--- a/utest/traceevent-utest.c
+++ b/utest/traceevent-utest.c
@@ -100,6 +100,126 @@ static char dyn_str_old_data[] = {
};
static void *dyn_str_old_event_data = (void *)dyn_str_old_data;
+#define CPUMASK_EVENT_SYSTEM "ipi"
+#define CPUMASK_EVENT_FIELD "cpumask"
+static const char cpumask_event_format[] =
+ "name: ipi_send_cpumask\n"
+ "ID: 3\n"
+ "format:\n"
+ "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\n"
+ "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\n"
+ "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\n"
+ "\tfield:int common_pid;\toffset:4;\tsize:4;\n"
+ "\n"
+ "\tfield:__data_loc cpumask_t *[] cpumask;\toffset:8;\tsize:4;\tsigned:0;\n"
+ "\n"
+ "print fmt: \"cpumask=%s\", __get_cpumask(cpumask)\n";
+
+/* Mind the endianness! */
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define DECL_CPUMASK_EVENT_DATA(name, args...) \
+ static char cpumask_##name##_event_data[] = { \
+ /* common type */ 3, 0x00, \
+ /* common flags */ 0x00, \
+ /* common_preempt_count */ 0x00, \
+ /* common_pid */ 0x00, 0x00, 0x00, 0x00, \
+ /* [offset, size] */ 16, 0x00, 8, 0x00, \
+ /* padding */ 0x00, 0x00, 0x00, 0x00, \
+ /* cpumask */ args, \
+}
+#else
+#define DECL_CPUMASK_EVENT_DATA(name, args...) \
+static char cpumask_##name##_event_data[] = { \
+ /* common type */ 0x00, 3, \
+ /* common flags */ 0x00, \
+ /* common_preempt_count */ 0x00, \
+ /* common_pid */ 0x00, 0x00, 0x00, 0x00, \
+ /* [offset, size] */ 0x00, 8, 0x00, 16, \
+ /* padding */ 0x00, 0x00, 0x00, 0x00, \
+ /* cpumask */ args, \
+}
+#endif
+
+#define SIZEOF_LONG0_FMT "[FAILED TO PARSE] s4=0 u4=0 s8=0 u8=0x0"
+#define SIZEOF_LONG4_FMT "int=4 unsigned=4 unsigned int=4 long=4 unsigned long=4 long long=8 unsigned long long=8 s4=4 u4=4 s8=8 u8=8"
+#define SIZEOF_LONG8_FMT "int=4 unsigned=4 unsigned int=4 long=8 unsigned long=8 long long=8 unsigned long long=8 s4=4 u4=4 s8=8 u8=8"
+
+static const char size_of_event[] =
+ "name: sizeof_event\n"
+ "ID: 23\n"
+ "format:\n"
+ "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;\n"
+ "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;\n"
+ "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\tsigned:0;\n"
+ "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;\n"
+ "\n"
+ "\tfield:int s4;\toffset:8;\tsize:4;\tsigned:1;\n"
+ "\tfield:unsigned int u4;\toffset:12;\tsize:4;\tsigned:0;\n"
+ "\tfield:long long s8;\toffset:16;\tsize:8;\tsigned:1;\n"
+ "\tfield:unsigned long long u8;\toffset:24;\tsize:8;\tsigned:0;\n"
+ "\n"
+ "print fmt: \"int=%d unsigned=%d unsigned int=%d long=%d unsigned long=%d long long=%d unsigned long long=%d s4=%d u4=%d s8=%d u8=%d\", "
+ "sizeof(int), sizeof(unsigned), sizeof(unsigned int), sizeof(long), sizeof(unsigned long), "
+ "sizeof(long long), sizeof(unsigned long long), sizeof(REC->s4), "
+ "sizeof(REC->u4), sizeof(REC->s8), sizeof(REC->u8))\n";
+static char sizeof_data[] = {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ /* common type */ 23, 0x00,
+#else
+ /* common type */ 0x00, 23
+#endif
+ /* common flags */ 0x00,
+ /* common_preempt_count */ 0x00,
+ /* common_pid */ 0x00, 0x00, 0x00, 0x00,
+ /* irq */ 0x00, 0x00, 0x00, 0x00,
+
+ /* s4 */ 0x00, 0x00, 0x00, 0x00,
+ /* u4 */ 0x00, 0x00, 0x00, 0x00,
+ /* s8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* u8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+static void *sizeof_event_data = (void *)sizeof_data;
+
+DECL_CPUMASK_EVENT_DATA(full, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
+#define CPUMASK_FULL "ARRAY[ff, ff, ff, ff, ff, ff, ff, ff]"
+#define CPUMASK_FULL_FMT "cpumask=0-63"
+
+DECL_CPUMASK_EVENT_DATA(empty, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+#define CPUMASK_EMPTY "ARRAY[00, 00, 00, 00, 00, 00, 00, 00]"
+#define CPUMASK_EMPTY_FMT "cpumask="
+
+DECL_CPUMASK_EVENT_DATA(half, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55);
+#define CPUMASK_HALF "ARRAY[55, 55, 55, 55, 55, 55, 55, 55]"
+#define CPUMASK_HALF_FMT "cpumask=0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62"
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+DECL_CPUMASK_EVENT_DATA(bytep1, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+#define CPUMASK_BYTEP1 "ARRAY[01, 80, 00, 00, 00, 00, 00, 00]"
+#define CPUMASK_BYTEP1_FMT "cpumask=0,15"
+
+DECL_CPUMASK_EVENT_DATA(bytep2, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00);
+#define CPUMASK_BYTEP2 "ARRAY[01, 00, 80, 00, 00, 00, 00, 00]"
+#define CPUMASK_BYTEP2_FMT "cpumask=0,23"
+
+DECL_CPUMASK_EVENT_DATA(bytepn, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80);
+#define CPUMASK_BYTEPN "ARRAY[01, 00, 00, 00, 00, 00, 00, 80]"
+#define CPUMASK_BYTEPN_FMT "cpumask=0,63"
+
+#else
+
+DECL_CPUMASK_EVENT_DATA(bytep1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01);
+#define CPUMASK_BYTEP1 "ARRAY[00, 00, 00, 00, 00, 00, 80, 01]"
+#define CPUMASK_BYTEP1_FMT "cpumask=0,15"
+
+DECL_CPUMASK_EVENT_DATA(bytep2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x01);
+#define CPUMASK_BYTEP2 "ARRAY[00, 00, 00, 00, 00, 80, 00, 01]"
+#define CPUMASK_BYTEP2_FMT "cpumask=0,23"
+
+DECL_CPUMASK_EVENT_DATA(bytepn, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01);
+#define CPUMASK_BYTEPN "ARRAY[80, 00, 00, 00, 00, 00, 80, 01]"
+#define CPUMASK_BYTEPN_FMT "cpumask=0,63"
+#endif
+
static struct tep_handle *test_tep;
static struct trace_seq *test_seq;
static struct trace_seq seq_storage;
@@ -139,6 +259,125 @@ static void test_parse_dyn_str_old_event(void)
parse_dyn_str(dyn_str_old_event, dyn_str_old_event_data, sizeof(dyn_str_old_data));
}
+static void parse_cpumask(const char *format, void *data, int size,
+ const char* expected_raw, const char* expected)
+{
+ struct tep_format_field *field;
+ struct tep_event *event;
+ struct tep_record record;
+
+ record.data = data;
+ record.size = size;
+
+ CU_TEST(tep_parse_format(test_tep, &event,
+ format, strlen(format),
+ CPUMASK_EVENT_SYSTEM) == TEP_ERRNO__SUCCESS);
+
+ field = tep_find_any_field(event, CPUMASK_EVENT_FIELD);
+ CU_TEST(field != NULL);
+
+ trace_seq_reset(test_seq);
+ tep_print_field_content(test_seq, data, size, field);
+ CU_TEST(strcmp(test_seq->buffer, expected_raw) == 0);
+
+ trace_seq_reset(test_seq);
+ tep_print_event(test_tep, test_seq, &record, "%s", TEP_PRINT_INFO);
+ trace_seq_do_printf(test_seq);
+ CU_TEST(strcmp(test_seq->buffer, expected) == 0);
+}
+
+static void test_parse_cpumask_full(void)
+{
+ parse_cpumask(cpumask_event_format,
+ cpumask_full_event_data, sizeof(cpumask_full_event_data),
+ CPUMASK_FULL, CPUMASK_FULL_FMT);
+}
+
+static void test_parse_cpumask_empty(void)
+{
+ parse_cpumask(cpumask_event_format,
+ cpumask_empty_event_data, sizeof(cpumask_empty_event_data),
+ CPUMASK_EMPTY, CPUMASK_EMPTY_FMT);
+}
+
+static void test_parse_cpumask_half(void)
+{
+ parse_cpumask(cpumask_event_format,
+ cpumask_half_event_data, sizeof(cpumask_half_event_data),
+ CPUMASK_HALF, CPUMASK_HALF_FMT);
+}
+
+static void test_parse_cpumask_bytep1(void)
+{
+ parse_cpumask(cpumask_event_format,
+ cpumask_bytep1_event_data, sizeof(cpumask_bytep1_event_data),
+ CPUMASK_BYTEP1, CPUMASK_BYTEP1_FMT);
+}
+
+static void test_parse_cpumask_bytep2(void)
+{
+ parse_cpumask(cpumask_event_format,
+ cpumask_bytep2_event_data, sizeof(cpumask_bytep2_event_data),
+ CPUMASK_BYTEP2, CPUMASK_BYTEP2_FMT);
+}
+
+static void test_parse_cpumask_bytepn(void)
+{
+ parse_cpumask(cpumask_event_format,
+ cpumask_bytepn_event_data, sizeof(cpumask_bytepn_event_data),
+ CPUMASK_BYTEPN, CPUMASK_BYTEPN_FMT);
+}
+
+static void test_parse_sizeof(int long_size, int value, const char *system,
+ const char *test_str)
+{
+ struct tep_event *event;
+ struct tep_record record;
+ char *sizeof_event;
+ char *p;
+
+ tep_set_long_size(test_tep, long_size);
+
+ record.data = sizeof_event_data;
+ record.size = sizeof(sizeof_data);
+
+ sizeof_event = strdup(size_of_event);
+ CU_TEST(sizeof_event != NULL);
+
+ /* Set a new id */
+ p = strstr(sizeof_event, "ID: 2");
+ p[5] = '0' + value;
+
+ /* Handles endianess */
+ *(unsigned short *)sizeof_data = 20 + value;
+
+ CU_TEST(tep_parse_format(test_tep, &event, sizeof_event,
+ strlen(sizeof_event),
+ system) == TEP_ERRNO__SUCCESS);
+
+ trace_seq_reset(test_seq);
+ tep_print_event(test_tep, test_seq, &record, "%s", TEP_PRINT_INFO);
+ trace_seq_do_printf(test_seq);
+ CU_TEST(strcmp(test_seq->buffer, test_str) == 0);
+
+ free(sizeof_event);
+}
+
+static void test_parse_sizeof8(void)
+{
+ test_parse_sizeof(8, 3, "sizeof8", SIZEOF_LONG8_FMT);
+}
+
+static void test_parse_sizeof4(void)
+{
+ test_parse_sizeof(4, 4, "sizeof4", SIZEOF_LONG4_FMT);
+}
+
+static void test_parse_sizeof_undef(void)
+{
+ test_parse_sizeof(0, 5, "sizeof_undef", SIZEOF_LONG0_FMT);
+}
+
static int test_suite_destroy(void)
{
tep_free(test_tep);
@@ -169,4 +408,22 @@ void test_traceevent_lib(void)
test_parse_dyn_str_event);
CU_add_test(suite, "parse old dynamic string event",
test_parse_dyn_str_old_event);
+ CU_add_test(suite, "parse full cpumask",
+ test_parse_cpumask_full);
+ CU_add_test(suite, "parse empty cpumask",
+ test_parse_cpumask_empty);
+ CU_add_test(suite, "parse half-filled cpumask",
+ test_parse_cpumask_half);
+ CU_add_test(suite, "parse cpumask spanning 2 bytes",
+ test_parse_cpumask_bytep1);
+ CU_add_test(suite, "parse cpumask spanning 3 bytes",
+ test_parse_cpumask_bytep2);
+ CU_add_test(suite, "parse cpumask spanning all bytes",
+ test_parse_cpumask_bytepn);
+ CU_add_test(suite, "parse sizeof() 8byte values",
+ test_parse_sizeof8);
+ CU_add_test(suite, "parse sizeof() 4byte values",
+ test_parse_sizeof4);
+ CU_add_test(suite, "parse sizeof() no long size defined",
+ test_parse_sizeof_undef);
}