@@ -778,4 +778,33 @@ void tep_set_loglevel(enum tep_loglevel level);
void tep_print_field(struct trace_seq *s, void *data,
struct tep_format_field *field);
+
+/*
+ * 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);
+
#endif /* _PARSE_EVENTS_H */
@@ -10,6 +10,8 @@
#include "event-parse.h"
#include "trace-seq.h"
+#define __weak __attribute__((weak))
+
#ifdef HAVE_UDIS86
#include <udis86.h>
@@ -273,15 +275,49 @@ static int print_exit_reason(struct trace_seq *s, struct tep_record *record,
return 0;
}
+__weak const char *tep_plugin_kvm_get_func(struct tep_event *event,
+ struct tep_record *record,
+ unsigned long long *val)
+{
+ return NULL;
+}
+
+__weak void tep_plugin_kvm_put_func(const char *func)
+{
+}
+
+
+static void add_rip_function(struct trace_seq *s, struct tep_record *record,
+ struct tep_event *event, unsigned long long rip)
+{
+ unsigned long long ip = rip;
+ const char *func;
+
+ func = tep_plugin_kvm_get_func(event, record, &ip);
+ if (func) {
+ trace_seq_printf(s, " %s", func);
+ /* The application may upate ip to the start of the function */
+ if (ip != rip)
+ trace_seq_printf(s, "+0x%0llx", rip - ip);
+ tep_plugin_kvm_put_func(func);
+ }
+}
+
static int kvm_exit_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context)
{
unsigned long long info1 = 0, info2 = 0;
+ unsigned long long rip;
if (print_exit_reason(s, record, event, "exit_reason") < 0)
return -1;
- tep_print_num_field(s, " rip 0x%lx", event, "guest_rip", record, 1);
+ if (tep_get_field_val(s, event, "guest_rip", record, &rip, 1) < 0)
+ return -1;
+
+ trace_seq_printf(s, " rip 0x%llx", rip);
+
+ add_rip_function(s, record, event, rip);
if (tep_get_field_val(s, event, "info1", record, &info1, 0) >= 0
&& tep_get_field_val(s, event, "info2", record, &info2, 0) >= 0)
@@ -290,6 +326,22 @@ static int kvm_exit_handler(struct trace_seq *s, struct tep_record *record,
return 0;
}
+static int kvm_entry_handler(struct trace_seq *s, struct tep_record *record,
+ struct tep_event *event, void *context)
+{
+ unsigned long long rip;
+
+ tep_print_num_field(s, " vcpu %u", event, "vcpu_id", record, 1);
+
+ if (tep_get_field_val(s, event, "rip", record, &rip, 1) < 0)
+ return -1;
+
+ trace_seq_printf(s, " rip 0x%llx", rip);
+ add_rip_function(s, record, event, rip);
+
+ return 0;
+}
+
#define KVM_EMUL_INSN_F_CR0_PE (1 << 0)
#define KVM_EMUL_INSN_F_EFL_VM (1 << 1)
#define KVM_EMUL_INSN_F_CS_D (1 << 2)
@@ -329,12 +381,12 @@ static int kvm_emulate_insn_handler(struct trace_seq *s,
flags & KVM_EMUL_INSN_F_CS_D,
flags & KVM_EMUL_INSN_F_CS_L);
- trace_seq_printf(s, "%llx:%llx: %s%s", csbase, rip, disasm,
- failed ? " FAIL" : "");
+ trace_seq_printf(s, "%llx:%llx", csbase, rip);
+ add_rip_function(s, record, event, rip);
+ trace_seq_printf(s, ": %s%s", disasm, failed ? " FAIL" : "");
return 0;
}
-
static int kvm_nested_vmexit_inject_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context)
{
@@ -352,7 +404,13 @@ static int kvm_nested_vmexit_inject_handler(struct trace_seq *s, struct tep_reco
static int kvm_nested_vmexit_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context)
{
- tep_print_num_field(s, "rip %llx ", event, "rip", record, 1);
+ unsigned long long rip;
+
+ if (tep_get_field_val(s, event, "rip", record, &rip, 1) < 0)
+ return -1;
+
+ trace_seq_printf(s, " rip %llx", rip);
+ add_rip_function(s, record, event, rip);
return kvm_nested_vmexit_inject_handler(s, record, event, context);
}
@@ -456,6 +514,9 @@ int TEP_PLUGIN_LOADER(struct tep_handle *tep)
tep_register_event_handler(tep, -1, "kvm", "kvm_exit",
kvm_exit_handler, NULL);
+ tep_register_event_handler(tep, -1, "kvm", "kvm_entry",
+ kvm_entry_handler, NULL);
+
tep_register_event_handler(tep, -1, "kvm", "kvm_emulate_insn",
kvm_emulate_insn_handler, NULL);
@@ -496,6 +557,9 @@ void TEP_PLUGIN_UNLOADER(struct tep_handle *tep)
tep_unregister_event_handler(tep, -1, "kvm", "kvm_exit",
kvm_exit_handler, NULL);
+ tep_unregister_event_handler(tep, -1, "kvm", "kvm_entry",
+ kvm_entry_handler, NULL);
+
tep_unregister_event_handler(tep, -1, "kvm", "kvm_emulate_insn",
kvm_emulate_insn_handler, NULL);