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 u64 main_thread_db_id = 0; 126 int err; 127 128 if (thread->db_id) 129 return 0; 130 131 thread->db_id = ++dbe->thread_last_db_id; 132 133 if (thread->pid_ != -1) { 134 struct thread *main_thread; 135 136 if (thread->pid_ == thread->tid) { 137 main_thread = thread; 138 } else { 139 main_thread = machine__findnew_thread(machine, 140 thread->pid_, 141 thread->pid_); 142 if (!main_thread) 143 return -ENOMEM; 144 err = db_export__thread(dbe, main_thread, machine, 145 comm); 146 if (err) 147 return err; 148 if (comm) { 149 err = db_export__comm_thread(dbe, comm, thread); 150 if (err) 151 return err; 152 } 153 } 154 main_thread_db_id = main_thread->db_id; 155 } 156 157 if (dbe->export_thread) 158 return dbe->export_thread(dbe, thread, main_thread_db_id, 159 machine); 160 161 return 0; 162} 163 164int db_export__comm(struct db_export *dbe, struct comm *comm, 165 struct thread *main_thread) 166{ 167 int err; 168 169 if (comm->db_id) 170 return 0; 171 172 comm->db_id = ++dbe->comm_last_db_id; 173 174 if (dbe->export_comm) { 175 if (main_thread->comm_set) 176 err = dbe->export_comm(dbe, comm); 177 else 178 err = db_export__defer_comm(dbe, comm); 179 if (err) 180 return err; 181 } 182 183 return db_export__comm_thread(dbe, comm, main_thread); 184} 185 186int db_export__comm_thread(struct db_export *dbe, struct comm *comm, 187 struct thread *thread) 188{ 189 u64 db_id; 190 191 db_id = ++dbe->comm_thread_last_db_id; 192 193 if (dbe->export_comm_thread) 194 return dbe->export_comm_thread(dbe, db_id, comm, thread); 195 196 return 0; 197} 198 199int db_export__dso(struct db_export *dbe, struct dso *dso, 200 struct machine *machine) 201{ 202 if (dso->db_id) 203 return 0; 204 205 dso->db_id = ++dbe->dso_last_db_id; 206 207 if (dbe->export_dso) 208 return dbe->export_dso(dbe, dso, machine); 209 210 return 0; 211} 212 213int db_export__symbol(struct db_export *dbe, struct symbol *sym, 214 struct dso *dso) 215{ 216 u64 *sym_db_id = symbol__priv(sym); 217 218 if (*sym_db_id) 219 return 0; 220 221 *sym_db_id = ++dbe->symbol_last_db_id; 222 223 if (dbe->export_symbol) 224 return dbe->export_symbol(dbe, sym, dso); 225 226 return 0; 227} 228 229static struct thread *get_main_thread(struct machine *machine, struct thread *thread) 230{ 231 if (thread->pid_ == thread->tid) 232 return thread; 233 234 if (thread->pid_ == -1) 235 return NULL; 236 237 return machine__find_thread(machine, thread->pid_, thread->pid_); 238} 239 240static int db_ids_from_al(struct db_export *dbe, struct addr_location *al, 241 u64 *dso_db_id, u64 *sym_db_id, u64 *offset) 242{ 243 int err; 244 245 if (al->map) { 246 struct dso *dso = al->map->dso; 247 248 err = db_export__dso(dbe, dso, al->machine); 249 if (err) 250 return err; 251 *dso_db_id = dso->db_id; 252 253 if (!al->sym) { 254 al->sym = symbol__new(al->addr, 0, 0, "unknown"); 255 if (al->sym) 256 symbols__insert(&dso->symbols[al->map->type], 257 al->sym); 258 } 259 260 if (al->sym) { 261 u64 *db_id = symbol__priv(al->sym); 262 263 err = db_export__symbol(dbe, al->sym, dso); 264 if (err) 265 return err; 266 *sym_db_id = *db_id; 267 *offset = al->addr - al->sym->start; 268 } 269 } 270 271 return 0; 272} 273 274int db_export__branch_type(struct db_export *dbe, u32 branch_type, 275 const char *name) 276{ 277 if (dbe->export_branch_type) 278 return dbe->export_branch_type(dbe, branch_type, name); 279 280 return 0; 281} 282 283int db_export__sample(struct db_export *dbe, union perf_event *event, 284 struct perf_sample *sample, struct perf_evsel *evsel, 285 struct addr_location *al) 286{ 287 struct thread* thread = al->thread; 288 struct export_sample es = { 289 .event = event, 290 .sample = sample, 291 .evsel = evsel, 292 .al = al, 293 }; 294 struct thread *main_thread; 295 struct comm *comm = NULL; 296 int err; 297 298 err = db_export__evsel(dbe, evsel); 299 if (err) 300 return err; 301 302 err = db_export__machine(dbe, al->machine); 303 if (err) 304 return err; 305 306 main_thread = get_main_thread(al->machine, thread); 307 if (main_thread) 308 comm = machine__thread_exec_comm(al->machine, main_thread); 309 310 err = db_export__thread(dbe, thread, al->machine, comm); 311 if (err) 312 return err; 313 314 if (comm) { 315 err = db_export__comm(dbe, comm, main_thread); 316 if (err) 317 return err; 318 es.comm_db_id = comm->db_id; 319 } 320 321 es.db_id = ++dbe->sample_last_db_id; 322 323 err = db_ids_from_al(dbe, al, &es.dso_db_id, &es.sym_db_id, &es.offset); 324 if (err) 325 return err; 326 327 if ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) && 328 sample_addr_correlates_sym(&evsel->attr)) { 329 struct addr_location addr_al; 330 331 perf_event__preprocess_sample_addr(event, sample, thread, &addr_al); 332 err = db_ids_from_al(dbe, &addr_al, &es.addr_dso_db_id, 333 &es.addr_sym_db_id, &es.addr_offset); 334 if (err) 335 return err; 336 if (dbe->crp) { 337 err = thread_stack__process(thread, comm, sample, al, 338 &addr_al, es.db_id, 339 dbe->crp); 340 if (err) 341 return err; 342 } 343 } 344 345 if (dbe->export_sample) 346 return dbe->export_sample(dbe, &es); 347 348 return 0; 349} 350 351static struct { 352 u32 branch_type; 353 const char *name; 354} branch_types[] = { 355 {0, "no branch"}, 356 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL, "call"}, 357 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN, "return"}, 358 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL, "conditional jump"}, 359 {PERF_IP_FLAG_BRANCH, "unconditional jump"}, 360 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_INTERRUPT, 361 "software interrupt"}, 362 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT, 363 "return from interrupt"}, 364 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_SYSCALLRET, 365 "system call"}, 366 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET, 367 "return from system call"}, 368 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_ASYNC, "asynchronous branch"}, 369 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC | 370 PERF_IP_FLAG_INTERRUPT, "hardware interrupt"}, 371 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TX_ABORT, "transaction abort"}, 372 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_BEGIN, "trace begin"}, 373 {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "trace end"}, 374 {0, NULL} 375}; 376 377int db_export__branch_types(struct db_export *dbe) 378{ 379 int i, err = 0; 380 381 for (i = 0; branch_types[i].name ; i++) { 382 err = db_export__branch_type(dbe, branch_types[i].branch_type, 383 branch_types[i].name); 384 if (err) 385 break; 386 } 387 return err; 388} 389 390int db_export__call_path(struct db_export *dbe, struct call_path *cp) 391{ 392 int err; 393 394 if (cp->db_id) 395 return 0; 396 397 if (cp->parent) { 398 err = db_export__call_path(dbe, cp->parent); 399 if (err) 400 return err; 401 } 402 403 cp->db_id = ++dbe->call_path_last_db_id; 404 405 if (dbe->export_call_path) 406 return dbe->export_call_path(dbe, cp); 407 408 return 0; 409} 410 411int db_export__call_return(struct db_export *dbe, struct call_return *cr) 412{ 413 int err; 414 415 if (cr->db_id) 416 return 0; 417 418 err = db_export__call_path(dbe, cr->cp); 419 if (err) 420 return err; 421 422 cr->db_id = ++dbe->call_return_last_db_id; 423 424 if (dbe->export_call_return) 425 return dbe->export_call_return(dbe, cr); 426 427 return 0; 428} 429