Upgrade libtraceevent to libtraceevent-1.7.1 am: e50996d4ce am: 5ca0b9f527 am: 469cb9091d
Original change: https://android-review.googlesource.com/c/platform/external/libtraceevent/+/2394172
Change-Id: I9f49550ee21b814d17a765432e84836dc41e8a20
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
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 @@
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 @@
{
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 @@
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 @@
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 *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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
...
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);
- unsigned long long fstart = tep_find_function_address(tep, addr);
- /* addr is in function named fname, starting at fstart address, at offset (addr - fstart) */
+ 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;
+
+ 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;
+ }
+
+ 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 @@
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 @@
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 @@
*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 @@
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 @@
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 @@
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 @@
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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
CFLAGS := -g -Wall
endif
-LIBS = -ldl
+LIBS ?= -ldl
export LIBS
set_plugin_dir := 1
@@ -164,6 +164,9 @@
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 @@
@$(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 @@
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 @@
TEP_PRINT_BITMASK,
TEP_PRINT_DYNAMIC_ARRAY_LEN,
TEP_PRINT_HEX_STR,
+ TEP_PRINT_CPUMASK,
};
struct tep_print_arg {
@@ -523,6 +528,9 @@
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_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 @@
};
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 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 @@
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 @@
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 @@
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 @@
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_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 @@
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 @@
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_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 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 @@
*
* 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 @@
}
/**
+ * 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 @@
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 @@
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 @@
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 @@
*
* 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 @@
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 @@
*tok = NULL;
- ch = __read_char();
+ ch = __read_char(tep);
if (ch < 0)
return TEP_EVENT_NONE;
@@ -1201,9 +1240,9 @@
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 @@
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 @@
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 @@
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 @@
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 @@
return TEP_EVENT_NONE;
i = 0;
}
- ch = __read_char();
+ ch = __read_char(tep);
buf[i++] = ch;
}
@@ -1321,22 +1365,23 @@
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 @@
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 @@
*
* 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 @@
}
/* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
/* 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 @@
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 @@
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 @@
/* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
*fields = field;
fields = &field->next;
+ field = NULL;
} while (1);
@@ -1923,22 +1974,22 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
is_symbolic_field = 0;
}
- type = read_token(&token);
+ type = read_token(event->tep, &token);
*tok = token;
return type;
@@ -2412,19 +2579,31 @@
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 @@
int len;
len = strlen(type);
+ if (len < 2) {
+ do_warning("invalid type: %s", type);
+ return val;
+ }
if (pointer) {
@@ -2692,6 +2875,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
free_token(token);
}
- type = read_token(&token);
+ type = read_token(event->tep, &token);
*tok = token;
return type;
@@ -3306,14 +3500,14 @@
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
do {
if (type == TEP_EVENT_NEWLINE) {
- type = read_token_item(&token);
+ type = read_token_item(event->tep, &token);
continue;
}
@@ -3506,6 +3800,10 @@
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 @@
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 @@
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 @@
{
/* 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 @@
*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 @@
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 @@
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 @@
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 @@
/* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
(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 @@
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 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 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 __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 @@
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 @@
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 @@
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 @@
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 @@
*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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
/* 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 @@
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 @@
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 @@
*/
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 @@
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 @@
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 @@
};
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 @@
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 @@
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 @@
}
/* 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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
{
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 @@
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 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 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 @@
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 @@
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);
}