]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - tools/perf/util/db-export.c
treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 288
[mirror_ubuntu-jammy-kernel.git] / tools / perf / util / db-export.c
CommitLineData
2025cf9e 1// SPDX-License-Identifier: GPL-2.0-only
0db15b1e
AH
2/*
3 * db-export.c: Support for exporting data suitable for import to a database
4 * Copyright (c) 2014, Intel Corporation.
0db15b1e
AH
5 */
6
7#include <errno.h>
8
9#include "evsel.h"
10#include "machine.h"
11#include "thread.h"
12#include "comm.h"
13#include "symbol.h"
1101f69a 14#include "map.h"
0db15b1e 15#include "event.h"
758008b2 16#include "util.h"
88f50d60 17#include "thread-stack.h"
0a3eba3a 18#include "callchain.h"
451db126 19#include "call-path.h"
0db15b1e
AH
20#include "db-export.h"
21
758008b2
AH
22struct deferred_export {
23 struct list_head node;
24 struct comm *comm;
25};
26
27static int db_export__deferred(struct db_export *dbe)
28{
29 struct deferred_export *de;
30 int err;
31
32 while (!list_empty(&dbe->deferred)) {
33 de = list_entry(dbe->deferred.next, struct deferred_export,
34 node);
35 err = dbe->export_comm(dbe, de->comm);
36 list_del(&de->node);
37 free(de);
38 if (err)
39 return err;
40 }
41
42 return 0;
43}
44
45static void db_export__free_deferred(struct db_export *dbe)
46{
47 struct deferred_export *de;
48
49 while (!list_empty(&dbe->deferred)) {
50 de = list_entry(dbe->deferred.next, struct deferred_export,
51 node);
52 list_del(&de->node);
53 free(de);
54 }
55}
56
57static int db_export__defer_comm(struct db_export *dbe, struct comm *comm)
58{
59 struct deferred_export *de;
60
61 de = zalloc(sizeof(struct deferred_export));
62 if (!de)
63 return -ENOMEM;
64
65 de->comm = comm;
66 list_add_tail(&de->node, &dbe->deferred);
67
68 return 0;
69}
70
0db15b1e
AH
71int db_export__init(struct db_export *dbe)
72{
73 memset(dbe, 0, sizeof(struct db_export));
758008b2 74 INIT_LIST_HEAD(&dbe->deferred);
0db15b1e
AH
75 return 0;
76}
77
758008b2
AH
78int db_export__flush(struct db_export *dbe)
79{
80 return db_export__deferred(dbe);
81}
82
88f50d60 83void db_export__exit(struct db_export *dbe)
0db15b1e 84{
758008b2 85 db_export__free_deferred(dbe);
88f50d60
AH
86 call_return_processor__free(dbe->crp);
87 dbe->crp = NULL;
0db15b1e
AH
88}
89
90int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel)
91{
92 if (evsel->db_id)
93 return 0;
94
95 evsel->db_id = ++dbe->evsel_last_db_id;
96
97 if (dbe->export_evsel)
98 return dbe->export_evsel(dbe, evsel);
99
100 return 0;
101}
102
103int db_export__machine(struct db_export *dbe, struct machine *machine)
104{
105 if (machine->db_id)
106 return 0;
107
108 machine->db_id = ++dbe->machine_last_db_id;
109
110 if (dbe->export_machine)
111 return dbe->export_machine(dbe, machine);
112
113 return 0;
114}
115
116int db_export__thread(struct db_export *dbe, struct thread *thread,
117 struct machine *machine, struct comm *comm)
118{
b91fc39f 119 struct thread *main_thread;
0db15b1e
AH
120 u64 main_thread_db_id = 0;
121 int err;
122
123 if (thread->db_id)
124 return 0;
125
126 thread->db_id = ++dbe->thread_last_db_id;
127
128 if (thread->pid_ != -1) {
0db15b1e
AH
129 if (thread->pid_ == thread->tid) {
130 main_thread = thread;
131 } else {
132 main_thread = machine__findnew_thread(machine,
133 thread->pid_,
134 thread->pid_);
135 if (!main_thread)
136 return -ENOMEM;
137 err = db_export__thread(dbe, main_thread, machine,
138 comm);
139 if (err)
b91fc39f 140 goto out_put;
0db15b1e
AH
141 if (comm) {
142 err = db_export__comm_thread(dbe, comm, thread);
143 if (err)
b91fc39f 144 goto out_put;
0db15b1e
AH
145 }
146 }
147 main_thread_db_id = main_thread->db_id;
b91fc39f
ACM
148 if (main_thread != thread)
149 thread__put(main_thread);
0db15b1e
AH
150 }
151
152 if (dbe->export_thread)
153 return dbe->export_thread(dbe, thread, main_thread_db_id,
154 machine);
155
156 return 0;
b91fc39f
ACM
157
158out_put:
159 thread__put(main_thread);
160 return err;
0db15b1e
AH
161}
162
163int db_export__comm(struct db_export *dbe, struct comm *comm,
164 struct thread *main_thread)
165{
166 int err;
167
168 if (comm->db_id)
169 return 0;
170
171 comm->db_id = ++dbe->comm_last_db_id;
172
173 if (dbe->export_comm) {
758008b2
AH
174 if (main_thread->comm_set)
175 err = dbe->export_comm(dbe, comm);
176 else
177 err = db_export__defer_comm(dbe, comm);
0db15b1e
AH
178 if (err)
179 return err;
180 }
181
182 return db_export__comm_thread(dbe, comm, main_thread);
183}
184
185int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
186 struct thread *thread)
187{
188 u64 db_id;
189
190 db_id = ++dbe->comm_thread_last_db_id;
191
192 if (dbe->export_comm_thread)
193 return dbe->export_comm_thread(dbe, db_id, comm, thread);
194
195 return 0;
196}
197
198int db_export__dso(struct db_export *dbe, struct dso *dso,
199 struct machine *machine)
200{
201 if (dso->db_id)
202 return 0;
203
204 dso->db_id = ++dbe->dso_last_db_id;
205
206 if (dbe->export_dso)
207 return dbe->export_dso(dbe, dso, machine);
208
209 return 0;
210}
211
212int db_export__symbol(struct db_export *dbe, struct symbol *sym,
213 struct dso *dso)
214{
215 u64 *sym_db_id = symbol__priv(sym);
216
217 if (*sym_db_id)
218 return 0;
219
220 *sym_db_id = ++dbe->symbol_last_db_id;
221
222 if (dbe->export_symbol)
223 return dbe->export_symbol(dbe, sym, dso);
224
225 return 0;
226}
227
0db15b1e
AH
228static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
229 u64 *dso_db_id, u64 *sym_db_id, u64 *offset)
230{
231 int err;
232
233 if (al->map) {
234 struct dso *dso = al->map->dso;
235
236 err = db_export__dso(dbe, dso, al->machine);
237 if (err)
238 return err;
239 *dso_db_id = dso->db_id;
240
241 if (!al->sym) {
af30bffa 242 al->sym = symbol__new(al->addr, 0, 0, 0, "unknown");
0db15b1e 243 if (al->sym)
3183f8ca 244 dso__insert_symbol(dso, al->sym);
0db15b1e
AH
245 }
246
247 if (al->sym) {
248 u64 *db_id = symbol__priv(al->sym);
249
250 err = db_export__symbol(dbe, al->sym, dso);
251 if (err)
252 return err;
253 *sym_db_id = *db_id;
254 *offset = al->addr - al->sym->start;
255 }
256 }
257
258 return 0;
259}
260
0a3eba3a
CP
261static struct call_path *call_path_from_sample(struct db_export *dbe,
262 struct machine *machine,
263 struct thread *thread,
264 struct perf_sample *sample,
265 struct perf_evsel *evsel)
266{
267 u64 kernel_start = machine__kernel_start(machine);
268 struct call_path *current = &dbe->cpr->call_path;
269 enum chain_order saved_order = callchain_param.order;
270 int err;
271
272 if (!symbol_conf.use_callchain || !sample->callchain)
273 return NULL;
274
275 /*
276 * Since the call path tree must be built starting with the root, we
277 * must use ORDER_CALL for call chain resolution, in order to process
278 * the callchain starting with the root node and ending with the leaf.
279 */
280 callchain_param.order = ORDER_CALLER;
281 err = thread__resolve_callchain(thread, &callchain_cursor, evsel,
fe176085 282 sample, NULL, NULL, PERF_MAX_STACK_DEPTH);
0a3eba3a
CP
283 if (err) {
284 callchain_param.order = saved_order;
285 return NULL;
286 }
287 callchain_cursor_commit(&callchain_cursor);
288
289 while (1) {
290 struct callchain_cursor_node *node;
291 struct addr_location al;
292 u64 dso_db_id = 0, sym_db_id = 0, offset = 0;
293
294 memset(&al, 0, sizeof(al));
295
296 node = callchain_cursor_current(&callchain_cursor);
297 if (!node)
298 break;
299 /*
300 * Handle export of symbol and dso for this node by
301 * constructing an addr_location struct and then passing it to
302 * db_ids_from_al() to perform the export.
303 */
304 al.sym = node->sym;
305 al.map = node->map;
306 al.machine = machine;
7a2544c0 307 al.addr = node->ip;
0a3eba3a 308
83302e79 309 if (al.map && !al.sym)
af07eeb0 310 al.sym = dso__find_symbol(al.map->dso, al.addr);
83302e79 311
0a3eba3a
CP
312 db_ids_from_al(dbe, &al, &dso_db_id, &sym_db_id, &offset);
313
314 /* add node to the call path tree if it doesn't exist */
315 current = call_path__findnew(dbe->cpr, current,
316 al.sym, node->ip,
317 kernel_start);
318
319 callchain_cursor_advance(&callchain_cursor);
320 }
321
322 /* Reset the callchain order to its prior value. */
323 callchain_param.order = saved_order;
324
325 if (current == &dbe->cpr->call_path) {
326 /* Bail because the callchain was empty. */
327 return NULL;
328 }
329
330 return current;
331}
332
f2bff007
AH
333int db_export__branch_type(struct db_export *dbe, u32 branch_type,
334 const char *name)
335{
336 if (dbe->export_branch_type)
337 return dbe->export_branch_type(dbe, branch_type, name);
338
339 return 0;
340}
341
0db15b1e
AH
342int db_export__sample(struct db_export *dbe, union perf_event *event,
343 struct perf_sample *sample, struct perf_evsel *evsel,
7327259d 344 struct addr_location *al)
0db15b1e 345{
7327259d 346 struct thread* thread = al->thread;
0db15b1e
AH
347 struct export_sample es = {
348 .event = event,
349 .sample = sample,
350 .evsel = evsel,
0db15b1e
AH
351 .al = al,
352 };
353 struct thread *main_thread;
354 struct comm *comm = NULL;
355 int err;
356
357 err = db_export__evsel(dbe, evsel);
358 if (err)
359 return err;
360
361 err = db_export__machine(dbe, al->machine);
362 if (err)
363 return err;
364
480ca357 365 main_thread = thread__main_thread(al->machine, thread);
0db15b1e
AH
366 if (main_thread)
367 comm = machine__thread_exec_comm(al->machine, main_thread);
368
369 err = db_export__thread(dbe, thread, al->machine, comm);
370 if (err)
427cde32 371 goto out_put;
0db15b1e
AH
372
373 if (comm) {
374 err = db_export__comm(dbe, comm, main_thread);
375 if (err)
427cde32 376 goto out_put;
0db15b1e
AH
377 es.comm_db_id = comm->db_id;
378 }
379
380 es.db_id = ++dbe->sample_last_db_id;
381
382 err = db_ids_from_al(dbe, al, &es.dso_db_id, &es.sym_db_id, &es.offset);
383 if (err)
427cde32 384 goto out_put;
0db15b1e 385
0a3eba3a
CP
386 if (dbe->cpr) {
387 struct call_path *cp = call_path_from_sample(dbe, al->machine,
388 thread, sample,
389 evsel);
568850ea 390 if (cp) {
0a3eba3a 391 db_export__call_path(dbe, cp);
568850ea
CP
392 es.call_path_id = cp->db_id;
393 }
0a3eba3a
CP
394 }
395
0db15b1e
AH
396 if ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) &&
397 sample_addr_correlates_sym(&evsel->attr)) {
398 struct addr_location addr_al;
399
c2740a87 400 thread__resolve(thread, &addr_al, sample);
0db15b1e
AH
401 err = db_ids_from_al(dbe, &addr_al, &es.addr_dso_db_id,
402 &es.addr_sym_db_id, &es.addr_offset);
403 if (err)
427cde32 404 goto out_put;
88f50d60
AH
405 if (dbe->crp) {
406 err = thread_stack__process(thread, comm, sample, al,
407 &addr_al, es.db_id,
408 dbe->crp);
409 if (err)
427cde32 410 goto out_put;
88f50d60 411 }
0db15b1e
AH
412 }
413
414 if (dbe->export_sample)
427cde32 415 err = dbe->export_sample(dbe, &es);
0db15b1e 416
427cde32
AH
417out_put:
418 thread__put(main_thread);
419 return err;
0db15b1e 420}
f2bff007
AH
421
422static struct {
423 u32 branch_type;
424 const char *name;
425} branch_types[] = {
426 {0, "no branch"},
427 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL, "call"},
428 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN, "return"},
429 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL, "conditional jump"},
430 {PERF_IP_FLAG_BRANCH, "unconditional jump"},
431 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_INTERRUPT,
432 "software interrupt"},
433 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT,
434 "return from interrupt"},
435 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_SYSCALLRET,
436 "system call"},
437 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET,
438 "return from system call"},
439 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_ASYNC, "asynchronous branch"},
440 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC |
441 PERF_IP_FLAG_INTERRUPT, "hardware interrupt"},
442 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TX_ABORT, "transaction abort"},
443 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_BEGIN, "trace begin"},
444 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "trace end"},
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 }
ff645daf
AH
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
f2bff007
AH
480 return err;
481}
88f50d60
AH
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
f435887e
AH
504int db_export__call_return(struct db_export *dbe, struct call_return *cr,
505 u64 *parent_db_id)
88f50d60
AH
506{
507 int err;
508
88f50d60
AH
509 err = db_export__call_path(dbe, cr->cp);
510 if (err)
511 return err;
512
f435887e
AH
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 }
88f50d60
AH
521
522 if (dbe->export_call_return)
523 return dbe->export_call_return(dbe, cr);
524
525 return 0;
526}