616 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * db-export.c: Support for exporting data suitable for import to a database4 * Copyright (c) 2014, Intel Corporation.5 */6 7#include <errno.h>8#include <stdlib.h>9 10#include "dso.h"11#include "evsel.h"12#include "machine.h"13#include "thread.h"14#include "comm.h"15#include "symbol.h"16#include "map.h"17#include "event.h"18#include "thread-stack.h"19#include "callchain.h"20#include "call-path.h"21#include "db-export.h"22#include <linux/zalloc.h>23 24int db_export__init(struct db_export *dbe)25{26 memset(dbe, 0, sizeof(struct db_export));27 return 0;28}29 30void db_export__exit(struct db_export *dbe)31{32 call_return_processor__free(dbe->crp);33 dbe->crp = NULL;34}35 36int db_export__evsel(struct db_export *dbe, struct evsel *evsel)37{38 if (evsel->db_id)39 return 0;40 41 evsel->db_id = ++dbe->evsel_last_db_id;42 43 if (dbe->export_evsel)44 return dbe->export_evsel(dbe, evsel);45 46 return 0;47}48 49int db_export__machine(struct db_export *dbe, struct machine *machine)50{51 if (machine->db_id)52 return 0;53 54 machine->db_id = ++dbe->machine_last_db_id;55 56 if (dbe->export_machine)57 return dbe->export_machine(dbe, machine);58 59 return 0;60}61 62int db_export__thread(struct db_export *dbe, struct thread *thread,63 struct machine *machine, struct thread *main_thread)64{65 u64 main_thread_db_id = 0;66 67 if (thread__db_id(thread))68 return 0;69 70 thread__set_db_id(thread, ++dbe->thread_last_db_id);71 72 if (main_thread)73 main_thread_db_id = thread__db_id(main_thread);74 75 if (dbe->export_thread)76 return dbe->export_thread(dbe, thread, main_thread_db_id,77 machine);78 79 return 0;80}81 82static int __db_export__comm(struct db_export *dbe, struct comm *comm,83 struct thread *thread)84{85 comm->db_id = ++dbe->comm_last_db_id;86 87 if (dbe->export_comm)88 return dbe->export_comm(dbe, comm, thread);89 90 return 0;91}92 93int db_export__comm(struct db_export *dbe, struct comm *comm,94 struct thread *thread)95{96 if (comm->db_id)97 return 0;98 99 return __db_export__comm(dbe, comm, thread);100}101 102/*103 * Export the "exec" comm. The "exec" comm is the program / application command104 * name at the time it first executes. It is used to group threads for the same105 * program. Note that the main thread pid (or thread group id tgid) cannot be106 * used because it does not change when a new program is exec'ed.107 */108int db_export__exec_comm(struct db_export *dbe, struct comm *comm,109 struct thread *main_thread)110{111 int err;112 113 if (comm->db_id)114 return 0;115 116 err = __db_export__comm(dbe, comm, main_thread);117 if (err)118 return err;119 120 /*121 * Record the main thread for this comm. Note that the main thread can122 * have many "exec" comms because there will be a new one every time it123 * exec's. An "exec" comm however will only ever have 1 main thread.124 * That is different to any other threads for that same program because125 * exec() will effectively kill them, so the relationship between the126 * "exec" comm and non-main threads is 1-to-1. That is why127 * db_export__comm_thread() is called here for the main thread, but it128 * is called for non-main threads when they are exported.129 */130 return db_export__comm_thread(dbe, comm, main_thread);131}132 133int db_export__comm_thread(struct db_export *dbe, struct comm *comm,134 struct thread *thread)135{136 u64 db_id;137 138 db_id = ++dbe->comm_thread_last_db_id;139 140 if (dbe->export_comm_thread)141 return dbe->export_comm_thread(dbe, db_id, comm, thread);142 143 return 0;144}145 146int db_export__dso(struct db_export *dbe, struct dso *dso,147 struct machine *machine)148{149 if (dso__db_id(dso))150 return 0;151 152 dso__set_db_id(dso, ++dbe->dso_last_db_id);153 154 if (dbe->export_dso)155 return dbe->export_dso(dbe, dso, machine);156 157 return 0;158}159 160int db_export__symbol(struct db_export *dbe, struct symbol *sym,161 struct dso *dso)162{163 u64 *sym_db_id = symbol__priv(sym);164 165 if (*sym_db_id)166 return 0;167 168 *sym_db_id = ++dbe->symbol_last_db_id;169 170 if (dbe->export_symbol)171 return dbe->export_symbol(dbe, sym, dso);172 173 return 0;174}175 176static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,177 u64 *dso_db_id, u64 *sym_db_id, u64 *offset)178{179 int err;180 181 if (al->map) {182 struct dso *dso = map__dso(al->map);183 184 err = db_export__dso(dbe, dso, maps__machine(al->maps));185 if (err)186 return err;187 *dso_db_id = dso__db_id(dso);188 189 if (!al->sym) {190 al->sym = symbol__new(al->addr, 0, 0, 0, "unknown");191 if (al->sym)192 dso__insert_symbol(dso, al->sym);193 }194 195 if (al->sym) {196 u64 *db_id = symbol__priv(al->sym);197 198 err = db_export__symbol(dbe, al->sym, dso);199 if (err)200 return err;201 *sym_db_id = *db_id;202 *offset = al->addr - al->sym->start;203 }204 }205 206 return 0;207}208 209static struct call_path *call_path_from_sample(struct db_export *dbe,210 struct machine *machine,211 struct thread *thread,212 struct perf_sample *sample,213 struct evsel *evsel)214{215 u64 kernel_start = machine__kernel_start(machine);216 struct call_path *current = &dbe->cpr->call_path;217 enum chain_order saved_order = callchain_param.order;218 struct callchain_cursor *cursor;219 int err;220 221 if (!symbol_conf.use_callchain || !sample->callchain)222 return NULL;223 224 /*225 * Since the call path tree must be built starting with the root, we226 * must use ORDER_CALL for call chain resolution, in order to process227 * the callchain starting with the root node and ending with the leaf.228 */229 callchain_param.order = ORDER_CALLER;230 cursor = get_tls_callchain_cursor();231 err = thread__resolve_callchain(thread, cursor, evsel,232 sample, NULL, NULL, PERF_MAX_STACK_DEPTH);233 if (err) {234 callchain_param.order = saved_order;235 return NULL;236 }237 callchain_cursor_commit(cursor);238 239 while (1) {240 struct callchain_cursor_node *node;241 struct addr_location al;242 u64 dso_db_id = 0, sym_db_id = 0, offset = 0;243 244 245 node = callchain_cursor_current(cursor);246 if (!node)247 break;248 249 /*250 * Handle export of symbol and dso for this node by251 * constructing an addr_location struct and then passing it to252 * db_ids_from_al() to perform the export.253 */254 addr_location__init(&al);255 al.sym = node->ms.sym;256 al.map = map__get(node->ms.map);257 al.maps = maps__get(thread__maps(thread));258 al.addr = node->ip;259 260 if (al.map && !al.sym)261 al.sym = dso__find_symbol(map__dso(al.map), al.addr);262 263 db_ids_from_al(dbe, &al, &dso_db_id, &sym_db_id, &offset);264 265 /* add node to the call path tree if it doesn't exist */266 current = call_path__findnew(dbe->cpr, current,267 al.sym, node->ip,268 kernel_start);269 270 callchain_cursor_advance(cursor);271 addr_location__exit(&al);272 }273 274 /* Reset the callchain order to its prior value. */275 callchain_param.order = saved_order;276 277 if (current == &dbe->cpr->call_path) {278 /* Bail because the callchain was empty. */279 return NULL;280 }281 282 return current;283}284 285int db_export__branch_type(struct db_export *dbe, u32 branch_type,286 const char *name)287{288 if (dbe->export_branch_type)289 return dbe->export_branch_type(dbe, branch_type, name);290 291 return 0;292}293 294static int db_export__threads(struct db_export *dbe, struct thread *thread,295 struct thread *main_thread,296 struct machine *machine, struct comm **comm_ptr)297{298 struct comm *comm = NULL;299 struct comm *curr_comm;300 int err;301 302 if (main_thread) {303 /*304 * A thread has a reference to the main thread, so export the305 * main thread first.306 */307 err = db_export__thread(dbe, main_thread, machine, main_thread);308 if (err)309 return err;310 /*311 * Export comm before exporting the non-main thread because312 * db_export__comm_thread() can be called further below.313 */314 comm = machine__thread_exec_comm(machine, main_thread);315 if (comm) {316 err = db_export__exec_comm(dbe, comm, main_thread);317 if (err)318 return err;319 *comm_ptr = comm;320 }321 }322 323 if (thread != main_thread) {324 /*325 * For a non-main thread, db_export__comm_thread() must be326 * called only if thread has not previously been exported.327 */328 bool export_comm_thread = comm && !thread__db_id(thread);329 330 err = db_export__thread(dbe, thread, machine, main_thread);331 if (err)332 return err;333 334 if (export_comm_thread) {335 err = db_export__comm_thread(dbe, comm, thread);336 if (err)337 return err;338 }339 }340 341 curr_comm = thread__comm(thread);342 if (curr_comm)343 return db_export__comm(dbe, curr_comm, thread);344 345 return 0;346}347 348int db_export__sample(struct db_export *dbe, union perf_event *event,349 struct perf_sample *sample, struct evsel *evsel,350 struct addr_location *al, struct addr_location *addr_al)351{352 struct thread *thread = al->thread;353 struct export_sample es = {354 .event = event,355 .sample = sample,356 .evsel = evsel,357 .al = al,358 };359 struct thread *main_thread;360 struct comm *comm = NULL;361 struct machine *machine;362 int err;363 364 err = db_export__evsel(dbe, evsel);365 if (err)366 return err;367 368 machine = maps__machine(al->maps);369 err = db_export__machine(dbe, machine);370 if (err)371 return err;372 373 main_thread = thread__main_thread(machine, thread);374 375 err = db_export__threads(dbe, thread, main_thread, machine, &comm);376 if (err)377 goto out_put;378 379 if (comm)380 es.comm_db_id = comm->db_id;381 382 es.db_id = ++dbe->sample_last_db_id;383 384 err = db_ids_from_al(dbe, al, &es.dso_db_id, &es.sym_db_id, &es.offset);385 if (err)386 goto out_put;387 388 if (dbe->cpr) {389 struct call_path *cp = call_path_from_sample(dbe, machine,390 thread, sample,391 evsel);392 if (cp) {393 db_export__call_path(dbe, cp);394 es.call_path_id = cp->db_id;395 }396 }397 398 if (addr_al) {399 err = db_ids_from_al(dbe, addr_al, &es.addr_dso_db_id,400 &es.addr_sym_db_id, &es.addr_offset);401 if (err)402 goto out_put;403 if (dbe->crp) {404 err = thread_stack__process(thread, comm, sample, al,405 addr_al, es.db_id,406 dbe->crp);407 if (err)408 goto out_put;409 }410 }411 412 if (dbe->export_sample)413 err = dbe->export_sample(dbe, &es);414 415out_put:416 thread__put(main_thread);417 return err;418}419 420static struct {421 u32 branch_type;422 const char *name;423} branch_types[] = {424 {0, "no branch"},425 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL, "call"},426 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN, "return"},427 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL, "conditional jump"},428 {PERF_IP_FLAG_BRANCH, "unconditional jump"},429 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_INTERRUPT,430 "software interrupt"},431 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT,432 "return from interrupt"},433 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_SYSCALLRET,434 "system call"},435 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET,436 "return from system call"},437 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_ASYNC, "asynchronous branch"},438 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC |439 PERF_IP_FLAG_INTERRUPT, "hardware interrupt"},440 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TX_ABORT, "transaction abort"},441 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_BEGIN, "trace begin"},442 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "trace end"},443 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_VMENTRY, "vm entry"},444 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_VMEXIT, "vm exit"},445 {0, NULL}446};447 448int db_export__branch_types(struct db_export *dbe)449{450 int i, err = 0;451 452 for (i = 0; branch_types[i].name ; i++) {453 err = db_export__branch_type(dbe, branch_types[i].branch_type,454 branch_types[i].name);455 if (err)456 break;457 }458 459 /* Add trace begin / end variants */460 for (i = 0; branch_types[i].name ; i++) {461 const char *name = branch_types[i].name;462 u32 type = branch_types[i].branch_type;463 char buf[64];464 465 if (type == PERF_IP_FLAG_BRANCH ||466 (type & (PERF_IP_FLAG_TRACE_BEGIN | PERF_IP_FLAG_TRACE_END)))467 continue;468 469 snprintf(buf, sizeof(buf), "trace begin / %s", name);470 err = db_export__branch_type(dbe, type | PERF_IP_FLAG_TRACE_BEGIN, buf);471 if (err)472 break;473 474 snprintf(buf, sizeof(buf), "%s / trace end", name);475 err = db_export__branch_type(dbe, type | PERF_IP_FLAG_TRACE_END, buf);476 if (err)477 break;478 }479 480 return err;481}482 483int db_export__call_path(struct db_export *dbe, struct call_path *cp)484{485 int err;486 487 if (cp->db_id)488 return 0;489 490 if (cp->parent) {491 err = db_export__call_path(dbe, cp->parent);492 if (err)493 return err;494 }495 496 cp->db_id = ++dbe->call_path_last_db_id;497 498 if (dbe->export_call_path)499 return dbe->export_call_path(dbe, cp);500 501 return 0;502}503 504int db_export__call_return(struct db_export *dbe, struct call_return *cr,505 u64 *parent_db_id)506{507 int err;508 509 err = db_export__call_path(dbe, cr->cp);510 if (err)511 return err;512 513 if (!cr->db_id)514 cr->db_id = ++dbe->call_return_last_db_id;515 516 if (parent_db_id) {517 if (!*parent_db_id)518 *parent_db_id = ++dbe->call_return_last_db_id;519 cr->parent_db_id = *parent_db_id;520 }521 522 if (dbe->export_call_return)523 return dbe->export_call_return(dbe, cr);524 525 return 0;526}527 528static int db_export__pid_tid(struct db_export *dbe, struct machine *machine,529 pid_t pid, pid_t tid, u64 *db_id,530 struct comm **comm_ptr, bool *is_idle)531{532 struct thread *thread = machine__find_thread(machine, pid, tid);533 struct thread *main_thread;534 int err = 0;535 536 if (!thread || !thread__comm_set(thread))537 goto out_put;538 539 *is_idle = !thread__pid(thread) && !thread__tid(thread);540 541 main_thread = thread__main_thread(machine, thread);542 543 err = db_export__threads(dbe, thread, main_thread, machine, comm_ptr);544 545 *db_id = thread__db_id(thread);546 547 thread__put(main_thread);548out_put:549 thread__put(thread);550 551 return err;552}553 554int db_export__switch(struct db_export *dbe, union perf_event *event,555 struct perf_sample *sample, struct machine *machine)556{557 bool out = event->header.misc & PERF_RECORD_MISC_SWITCH_OUT;558 bool out_preempt = out &&559 (event->header.misc & PERF_RECORD_MISC_SWITCH_OUT_PREEMPT);560 int flags = out | (out_preempt << 1);561 bool is_idle_a = false, is_idle_b = false;562 u64 th_a_id = 0, th_b_id = 0;563 u64 comm_out_id, comm_in_id;564 struct comm *comm_a = NULL;565 struct comm *comm_b = NULL;566 u64 th_out_id, th_in_id;567 u64 db_id;568 int err;569 570 err = db_export__machine(dbe, machine);571 if (err)572 return err;573 574 err = db_export__pid_tid(dbe, machine, sample->pid, sample->tid,575 &th_a_id, &comm_a, &is_idle_a);576 if (err)577 return err;578 579 if (event->header.type == PERF_RECORD_SWITCH_CPU_WIDE) {580 pid_t pid = event->context_switch.next_prev_pid;581 pid_t tid = event->context_switch.next_prev_tid;582 583 err = db_export__pid_tid(dbe, machine, pid, tid, &th_b_id,584 &comm_b, &is_idle_b);585 if (err)586 return err;587 }588 589 /*590 * Do not export if both threads are unknown (i.e. not being traced),591 * or one is unknown and the other is the idle task.592 */593 if ((!th_a_id || is_idle_a) && (!th_b_id || is_idle_b))594 return 0;595 596 db_id = ++dbe->context_switch_last_db_id;597 598 if (out) {599 th_out_id = th_a_id;600 th_in_id = th_b_id;601 comm_out_id = comm_a ? comm_a->db_id : 0;602 comm_in_id = comm_b ? comm_b->db_id : 0;603 } else {604 th_out_id = th_b_id;605 th_in_id = th_a_id;606 comm_out_id = comm_b ? comm_b->db_id : 0;607 comm_in_id = comm_a ? comm_a->db_id : 0;608 }609 610 if (dbe->export_context_switch)611 return dbe->export_context_switch(dbe, db_id, machine, sample,612 th_out_id, comm_out_id,613 th_in_id, comm_in_id, flags);614 return 0;615}616