253 lines
7.3 KiB
Plaintext
253 lines
7.3 KiB
Plaintext
|
|
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/
|