~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/perf/util/db-export.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * db-export.c: Support for exporting data suitable for import to a database
  4  * 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 
 24 int db_export__init(struct db_export *dbe)
 25 {
 26         memset(dbe, 0, sizeof(struct db_export));
 27         return 0;
 28 }
 29 
 30 void db_export__exit(struct db_export *dbe)
 31 {
 32         call_return_processor__free(dbe->crp);
 33         dbe->crp = NULL;
 34 }
 35 
 36 int 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 
 49 int 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 
 62 int 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 
 82 static 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 
 93 int 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 command
104  * name at the time it first executes. It is used to group threads for the same
105  * program. Note that the main thread pid (or thread group id tgid) cannot be
106  * used because it does not change when a new program is exec'ed.
107  */
108 int 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 can
122          * have many "exec" comms because there will be a new one every time it
123          * 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 because
125          * exec() will effectively kill them, so the relationship between the
126          * "exec" comm and non-main threads is 1-to-1. That is why
127          * db_export__comm_thread() is called here for the main thread, but it
128          * is called for non-main threads when they are exported.
129          */
130         return db_export__comm_thread(dbe, comm, main_thread);
131 }
132 
133 int 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 
146 int 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 
160 int 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 
176 static 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 
209 static 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, we
226          * must use ORDER_CALL for call chain resolution, in order to process
227          * 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 by
251                  * constructing an addr_location struct and then passing it to
252                  * 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 
285 int 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 
294 static 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 the
305                  * 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 because
312                  * 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 be
326                  * 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 
348 int 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 
415 out_put:
416         thread__put(main_thread);
417         return err;
418 }
419 
420 static 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 
448 int 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 
483 int 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 
504 int 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 
528 static 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);
548 out_put:
549         thread__put(thread);
550 
551         return err;
552 }
553 
554 int 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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php