brintos

brintos / linux-shallow public Read only

0
0
Text · 14.7 KiB · 50f9163 Raw
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