linux/tools/perf/util/db-export.c
<<
>>
Prefs
   1/*
   2 * db-export.c: Support for exporting data suitable for import to a database
   3 * Copyright (c) 2014, Intel Corporation.
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms and conditions of the GNU General Public License,
   7 * version 2, as published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope it will be useful, but WITHOUT
  10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  12 * more details.
  13 *
  14 */
  15
  16#include <errno.h>
  17
  18#include "evsel.h"
  19#include "machine.h"
  20#include "thread.h"
  21#include "comm.h"
  22#include "symbol.h"
  23#include "event.h"
  24#include "util.h"
  25#include "thread-stack.h"
  26#include "db-export.h"
  27
  28struct deferred_export {
  29        struct list_head node;
  30        struct comm *comm;
  31};
  32
  33static int db_export__deferred(struct db_export *dbe)
  34{
  35        struct deferred_export *de;
  36        int err;
  37
  38        while (!list_empty(&dbe->deferred)) {
  39                de = list_entry(dbe->deferred.next, struct deferred_export,
  40                                node);
  41                err = dbe->export_comm(dbe, de->comm);
  42                list_del(&de->node);
  43                free(de);
  44                if (err)
  45                        return err;
  46        }
  47
  48        return 0;
  49}
  50
  51static void db_export__free_deferred(struct db_export *dbe)
  52{
  53        struct deferred_export *de;
  54
  55        while (!list_empty(&dbe->deferred)) {
  56                de = list_entry(dbe->deferred.next, struct deferred_export,
  57                                node);
  58                list_del(&de->node);
  59                free(de);
  60        }
  61}
  62
  63static int db_export__defer_comm(struct db_export *dbe, struct comm *comm)
  64{
  65        struct deferred_export *de;
  66
  67        de = zalloc(sizeof(struct deferred_export));
  68        if (!de)
  69                return -ENOMEM;
  70
  71        de->comm = comm;
  72        list_add_tail(&de->node, &dbe->deferred);
  73
  74        return 0;
  75}
  76
  77int db_export__init(struct db_export *dbe)
  78{
  79        memset(dbe, 0, sizeof(struct db_export));
  80        INIT_LIST_HEAD(&dbe->deferred);
  81        return 0;
  82}
  83
  84int db_export__flush(struct db_export *dbe)
  85{
  86        return db_export__deferred(dbe);
  87}
  88
  89void db_export__exit(struct db_export *dbe)
  90{
  91        db_export__free_deferred(dbe);
  92        call_return_processor__free(dbe->crp);
  93        dbe->crp = NULL;
  94}
  95
  96int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel)
  97{
  98        if (evsel->db_id)
  99                return 0;
 100
 101        evsel->db_id = ++dbe->evsel_last_db_id;
 102
 103        if (dbe->export_evsel)
 104                return dbe->export_evsel(dbe, evsel);
 105
 106        return 0;
 107}
 108
 109int db_export__machine(struct db_export *dbe, struct machine *machine)
 110{
 111        if (machine->db_id)
 112                return 0;
 113
 114        machine->db_id = ++dbe->machine_last_db_id;
 115
 116        if (dbe->export_machine)
 117                return dbe->export_machine(dbe, machine);
 118
 119        return 0;
 120}
 121
 122int db_export__thread(struct db_export *dbe, struct thread *thread,
 123                      struct machine *machine, struct comm *comm)
 124{
 125        struct thread *main_thread;
 126        u64 main_thread_db_id = 0;
 127        int err;
 128
 129        if (thread->db_id)
 130                return 0;
 131
 132        thread->db_id = ++dbe->thread_last_db_id;
 133
 134        if (thread->pid_ != -1) {
 135                if (thread->pid_ == thread->tid) {
 136                        main_thread = thread;
 137                } else {
 138                        main_thread = machine__findnew_thread(machine,
 139                                                              thread->pid_,
 140                                                              thread->pid_);
 141                        if (!main_thread)
 142                                return -ENOMEM;
 143                        err = db_export__thread(dbe, main_thread, machine,
 144                                                comm);
 145                        if (err)
 146                                goto out_put;
 147                        if (comm) {
 148                                err = db_export__comm_thread(dbe, comm, thread);
 149                                if (err)
 150                                        goto out_put;
 151                        }
 152                }
 153                main_thread_db_id = main_thread->db_id;
 154                if (main_thread != thread)
 155                        thread__put(main_thread);
 156        }
 157
 158        if (dbe->export_thread)
 159                return dbe->export_thread(dbe, thread, main_thread_db_id,
 160                                          machine);
 161
 162        return 0;
 163
 164out_put:
 165        thread__put(main_thread);
 166        return err;
 167}
 168
 169int db_export__comm(struct db_export *dbe, struct comm *comm,
 170                    struct thread *main_thread)
 171{
 172        int err;
 173
 174        if (comm->db_id)
 175                return 0;
 176
 177        comm->db_id = ++dbe->comm_last_db_id;
 178
 179        if (dbe->export_comm) {
 180                if (main_thread->comm_set)
 181                        err = dbe->export_comm(dbe, comm);
 182                else
 183                        err = db_export__defer_comm(dbe, comm);
 184                if (err)
 185                        return err;
 186        }
 187
 188        return db_export__comm_thread(dbe, comm, main_thread);
 189}
 190
 191int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
 192                           struct thread *thread)
 193{
 194        u64 db_id;
 195
 196        db_id = ++dbe->comm_thread_last_db_id;
 197
 198        if (dbe->export_comm_thread)
 199                return dbe->export_comm_thread(dbe, db_id, comm, thread);
 200
 201        return 0;
 202}
 203
 204int db_export__dso(struct db_export *dbe, struct dso *dso,
 205                   struct machine *machine)
 206{
 207        if (dso->db_id)
 208                return 0;
 209
 210        dso->db_id = ++dbe->dso_last_db_id;
 211
 212        if (dbe->export_dso)
 213                return dbe->export_dso(dbe, dso, machine);
 214
 215        return 0;
 216}
 217
 218int db_export__symbol(struct db_export *dbe, struct symbol *sym,
 219                      struct dso *dso)
 220{
 221        u64 *sym_db_id = symbol__priv(sym);
 222
 223        if (*sym_db_id)
 224                return 0;
 225
 226        *sym_db_id = ++dbe->symbol_last_db_id;
 227
 228        if (dbe->export_symbol)
 229                return dbe->export_symbol(dbe, sym, dso);
 230
 231        return 0;
 232}
 233
 234static struct thread *get_main_thread(struct machine *machine, struct thread *thread)
 235{
 236        if (thread->pid_ == thread->tid)
 237                return thread__get(thread);
 238
 239        if (thread->pid_ == -1)
 240                return NULL;
 241
 242        return machine__find_thread(machine, thread->pid_, thread->pid_);
 243}
 244
 245static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
 246                          u64 *dso_db_id, u64 *sym_db_id, u64 *offset)
 247{
 248        int err;
 249
 250        if (al->map) {
 251                struct dso *dso = al->map->dso;
 252
 253                err = db_export__dso(dbe, dso, al->machine);
 254                if (err)
 255                        return err;
 256                *dso_db_id = dso->db_id;
 257
 258                if (!al->sym) {
 259                        al->sym = symbol__new(al->addr, 0, 0, "unknown");
 260                        if (al->sym)
 261                                symbols__insert(&dso->symbols[al->map->type],
 262                                                al->sym);
 263                }
 264
 265                if (al->sym) {
 266                        u64 *db_id = symbol__priv(al->sym);
 267
 268                        err = db_export__symbol(dbe, al->sym, dso);
 269                        if (err)
 270                                return err;
 271                        *sym_db_id = *db_id;
 272                        *offset = al->addr - al->sym->start;
 273                }
 274        }
 275
 276        return 0;
 277}
 278
 279int db_export__branch_type(struct db_export *dbe, u32 branch_type,
 280                           const char *name)
 281{
 282        if (dbe->export_branch_type)
 283                return dbe->export_branch_type(dbe, branch_type, name);
 284
 285        return 0;
 286}
 287
 288int db_export__sample(struct db_export *dbe, union perf_event *event,
 289                      struct perf_sample *sample, struct perf_evsel *evsel,
 290                      struct addr_location *al)
 291{
 292        struct thread* thread = al->thread;
 293        struct export_sample es = {
 294                .event = event,
 295                .sample = sample,
 296                .evsel = evsel,
 297                .al = al,
 298        };
 299        struct thread *main_thread;
 300        struct comm *comm = NULL;
 301        int err;
 302
 303        err = db_export__evsel(dbe, evsel);
 304        if (err)
 305                return err;
 306
 307        err = db_export__machine(dbe, al->machine);
 308        if (err)
 309                return err;
 310
 311        main_thread = get_main_thread(al->machine, thread);
 312        if (main_thread)
 313                comm = machine__thread_exec_comm(al->machine, main_thread);
 314
 315        err = db_export__thread(dbe, thread, al->machine, comm);
 316        if (err)
 317                goto out_put;
 318
 319        if (comm) {
 320                err = db_export__comm(dbe, comm, main_thread);
 321                if (err)
 322                        goto out_put;
 323                es.comm_db_id = comm->db_id;
 324        }
 325
 326        es.db_id = ++dbe->sample_last_db_id;
 327
 328        err = db_ids_from_al(dbe, al, &es.dso_db_id, &es.sym_db_id, &es.offset);
 329        if (err)
 330                goto out_put;
 331
 332        if ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) &&
 333            sample_addr_correlates_sym(&evsel->attr)) {
 334                struct addr_location addr_al;
 335
 336                thread__resolve(thread, &addr_al, sample);
 337                err = db_ids_from_al(dbe, &addr_al, &es.addr_dso_db_id,
 338                                     &es.addr_sym_db_id, &es.addr_offset);
 339                if (err)
 340                        goto out_put;
 341                if (dbe->crp) {
 342                        err = thread_stack__process(thread, comm, sample, al,
 343                                                    &addr_al, es.db_id,
 344                                                    dbe->crp);
 345                        if (err)
 346                                goto out_put;
 347                }
 348        }
 349
 350        if (dbe->export_sample)
 351                err = dbe->export_sample(dbe, &es);
 352
 353out_put:
 354        thread__put(main_thread);
 355        return err;
 356}
 357
 358static struct {
 359        u32 branch_type;
 360        const char *name;
 361} branch_types[] = {
 362        {0, "no branch"},
 363        {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL, "call"},
 364        {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN, "return"},
 365        {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL, "conditional jump"},
 366        {PERF_IP_FLAG_BRANCH, "unconditional jump"},
 367        {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_INTERRUPT,
 368         "software interrupt"},
 369        {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT,
 370         "return from interrupt"},
 371        {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_SYSCALLRET,
 372         "system call"},
 373        {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET,
 374         "return from system call"},
 375        {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_ASYNC, "asynchronous branch"},
 376        {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC |
 377         PERF_IP_FLAG_INTERRUPT, "hardware interrupt"},
 378        {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TX_ABORT, "transaction abort"},
 379        {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_BEGIN, "trace begin"},
 380        {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "trace end"},
 381        {0, NULL}
 382};
 383
 384int db_export__branch_types(struct db_export *dbe)
 385{
 386        int i, err = 0;
 387
 388        for (i = 0; branch_types[i].name ; i++) {
 389                err = db_export__branch_type(dbe, branch_types[i].branch_type,
 390                                             branch_types[i].name);
 391                if (err)
 392                        break;
 393        }
 394        return err;
 395}
 396
 397int db_export__call_path(struct db_export *dbe, struct call_path *cp)
 398{
 399        int err;
 400
 401        if (cp->db_id)
 402                return 0;
 403
 404        if (cp->parent) {
 405                err = db_export__call_path(dbe, cp->parent);
 406                if (err)
 407                        return err;
 408        }
 409
 410        cp->db_id = ++dbe->call_path_last_db_id;
 411
 412        if (dbe->export_call_path)
 413                return dbe->export_call_path(dbe, cp);
 414
 415        return 0;
 416}
 417
 418int db_export__call_return(struct db_export *dbe, struct call_return *cr)
 419{
 420        int err;
 421
 422        if (cr->db_id)
 423                return 0;
 424
 425        err = db_export__call_path(dbe, cr->cp);
 426        if (err)
 427                return err;
 428
 429        cr->db_id = ++dbe->call_return_last_db_id;
 430
 431        if (dbe->export_call_return)
 432                return dbe->export_call_return(dbe, cr);
 433
 434        return 0;
 435}
 436