|
@@ -23,6 +23,7 @@
|
|
|
#include "event.h"
|
|
|
#include "util.h"
|
|
|
#include "thread-stack.h"
|
|
|
+#include "callchain.h"
|
|
|
#include "call-path.h"
|
|
|
#include "db-export.h"
|
|
|
|
|
@@ -277,6 +278,79 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static struct call_path *call_path_from_sample(struct db_export *dbe,
|
|
|
+ struct machine *machine,
|
|
|
+ struct thread *thread,
|
|
|
+ struct perf_sample *sample,
|
|
|
+ struct perf_evsel *evsel)
|
|
|
+{
|
|
|
+ u64 kernel_start = machine__kernel_start(machine);
|
|
|
+ struct call_path *current = &dbe->cpr->call_path;
|
|
|
+ enum chain_order saved_order = callchain_param.order;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (!symbol_conf.use_callchain || !sample->callchain)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Since the call path tree must be built starting with the root, we
|
|
|
+ * must use ORDER_CALL for call chain resolution, in order to process
|
|
|
+ * the callchain starting with the root node and ending with the leaf.
|
|
|
+ */
|
|
|
+ callchain_param.order = ORDER_CALLER;
|
|
|
+ err = thread__resolve_callchain(thread, &callchain_cursor, evsel,
|
|
|
+ sample, NULL, NULL,
|
|
|
+ sysctl_perf_event_max_stack);
|
|
|
+ if (err) {
|
|
|
+ callchain_param.order = saved_order;
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ callchain_cursor_commit(&callchain_cursor);
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ struct callchain_cursor_node *node;
|
|
|
+ struct addr_location al;
|
|
|
+ u64 dso_db_id = 0, sym_db_id = 0, offset = 0;
|
|
|
+
|
|
|
+ memset(&al, 0, sizeof(al));
|
|
|
+
|
|
|
+ node = callchain_cursor_current(&callchain_cursor);
|
|
|
+ if (!node)
|
|
|
+ break;
|
|
|
+ /*
|
|
|
+ * Handle export of symbol and dso for this node by
|
|
|
+ * constructing an addr_location struct and then passing it to
|
|
|
+ * db_ids_from_al() to perform the export.
|
|
|
+ */
|
|
|
+ al.sym = node->sym;
|
|
|
+ al.map = node->map;
|
|
|
+ al.machine = machine;
|
|
|
+ if (al.map)
|
|
|
+ al.addr = al.map->map_ip(al.map, node->ip);
|
|
|
+ else
|
|
|
+ al.addr = node->ip;
|
|
|
+
|
|
|
+ db_ids_from_al(dbe, &al, &dso_db_id, &sym_db_id, &offset);
|
|
|
+
|
|
|
+ /* add node to the call path tree if it doesn't exist */
|
|
|
+ current = call_path__findnew(dbe->cpr, current,
|
|
|
+ al.sym, node->ip,
|
|
|
+ kernel_start);
|
|
|
+
|
|
|
+ callchain_cursor_advance(&callchain_cursor);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Reset the callchain order to its prior value. */
|
|
|
+ callchain_param.order = saved_order;
|
|
|
+
|
|
|
+ if (current == &dbe->cpr->call_path) {
|
|
|
+ /* Bail because the callchain was empty. */
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return current;
|
|
|
+}
|
|
|
+
|
|
|
int db_export__branch_type(struct db_export *dbe, u32 branch_type,
|
|
|
const char *name)
|
|
|
{
|
|
@@ -330,6 +404,14 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
|
|
|
if (err)
|
|
|
goto out_put;
|
|
|
|
|
|
+ if (dbe->cpr) {
|
|
|
+ struct call_path *cp = call_path_from_sample(dbe, al->machine,
|
|
|
+ thread, sample,
|
|
|
+ evsel);
|
|
|
+ if (cp)
|
|
|
+ db_export__call_path(dbe, cp);
|
|
|
+ }
|
|
|
+
|
|
|
if ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) &&
|
|
|
sample_addr_correlates_sym(&evsel->attr)) {
|
|
|
struct addr_location addr_al;
|