Home | History | Annotate | Download | only in tests
      1 #include "perf.h"
      2 #include "tests.h"
      3 #include "debug.h"
      4 #include "symbol.h"
      5 #include "sort.h"
      6 #include "evsel.h"
      7 #include "evlist.h"
      8 #include "machine.h"
      9 #include "thread.h"
     10 #include "parse-events.h"
     11 
     12 static struct {
     13 	u32 pid;
     14 	const char *comm;
     15 } fake_threads[] = {
     16 	{ 100, "perf" },
     17 	{ 200, "perf" },
     18 	{ 300, "bash" },
     19 };
     20 
     21 static struct {
     22 	u32 pid;
     23 	u64 start;
     24 	const char *filename;
     25 } fake_mmap_info[] = {
     26 	{ 100, 0x40000, "perf" },
     27 	{ 100, 0x50000, "libc" },
     28 	{ 100, 0xf0000, "[kernel]" },
     29 	{ 200, 0x40000, "perf" },
     30 	{ 200, 0x50000, "libc" },
     31 	{ 200, 0xf0000, "[kernel]" },
     32 	{ 300, 0x40000, "bash" },
     33 	{ 300, 0x50000, "libc" },
     34 	{ 300, 0xf0000, "[kernel]" },
     35 };
     36 
     37 struct fake_sym {
     38 	u64 start;
     39 	u64 length;
     40 	const char *name;
     41 };
     42 
     43 static struct fake_sym perf_syms[] = {
     44 	{ 700, 100, "main" },
     45 	{ 800, 100, "run_command" },
     46 	{ 900, 100, "cmd_record" },
     47 };
     48 
     49 static struct fake_sym bash_syms[] = {
     50 	{ 700, 100, "main" },
     51 	{ 800, 100, "xmalloc" },
     52 	{ 900, 100, "xfree" },
     53 };
     54 
     55 static struct fake_sym libc_syms[] = {
     56 	{ 700, 100, "malloc" },
     57 	{ 800, 100, "free" },
     58 	{ 900, 100, "realloc" },
     59 };
     60 
     61 static struct fake_sym kernel_syms[] = {
     62 	{ 700, 100, "schedule" },
     63 	{ 800, 100, "page_fault" },
     64 	{ 900, 100, "sys_perf_event_open" },
     65 };
     66 
     67 static struct {
     68 	const char *dso_name;
     69 	struct fake_sym *syms;
     70 	size_t nr_syms;
     71 } fake_symbols[] = {
     72 	{ "perf", perf_syms, ARRAY_SIZE(perf_syms) },
     73 	{ "bash", bash_syms, ARRAY_SIZE(bash_syms) },
     74 	{ "libc", libc_syms, ARRAY_SIZE(libc_syms) },
     75 	{ "[kernel]", kernel_syms, ARRAY_SIZE(kernel_syms) },
     76 };
     77 
     78 static struct machine *setup_fake_machine(struct machines *machines)
     79 {
     80 	struct machine *machine = machines__find(machines, HOST_KERNEL_ID);
     81 	size_t i;
     82 
     83 	if (machine == NULL) {
     84 		pr_debug("Not enough memory for machine setup\n");
     85 		return NULL;
     86 	}
     87 
     88 	for (i = 0; i < ARRAY_SIZE(fake_threads); i++) {
     89 		struct thread *thread;
     90 
     91 		thread = machine__findnew_thread(machine, fake_threads[i].pid,
     92 						 fake_threads[i].pid);
     93 		if (thread == NULL)
     94 			goto out;
     95 
     96 		thread__set_comm(thread, fake_threads[i].comm);
     97 	}
     98 
     99 	for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) {
    100 		union perf_event fake_mmap_event = {
    101 			.mmap = {
    102 				.header = { .misc = PERF_RECORD_MISC_USER, },
    103 				.pid = fake_mmap_info[i].pid,
    104 				.start = fake_mmap_info[i].start,
    105 				.len = 0x1000ULL,
    106 				.pgoff = 0ULL,
    107 			},
    108 		};
    109 
    110 		strcpy(fake_mmap_event.mmap.filename,
    111 		       fake_mmap_info[i].filename);
    112 
    113 		machine__process_mmap_event(machine, &fake_mmap_event);
    114 	}
    115 
    116 	for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) {
    117 		size_t k;
    118 		struct dso *dso;
    119 
    120 		dso = __dsos__findnew(&machine->user_dsos,
    121 				      fake_symbols[i].dso_name);
    122 		if (dso == NULL)
    123 			goto out;
    124 
    125 		/* emulate dso__load() */
    126 		dso__set_loaded(dso, MAP__FUNCTION);
    127 
    128 		for (k = 0; k < fake_symbols[i].nr_syms; k++) {
    129 			struct symbol *sym;
    130 			struct fake_sym *fsym = &fake_symbols[i].syms[k];
    131 
    132 			sym = symbol__new(fsym->start, fsym->length,
    133 					  STB_GLOBAL, fsym->name);
    134 			if (sym == NULL)
    135 				goto out;
    136 
    137 			symbols__insert(&dso->symbols[MAP__FUNCTION], sym);
    138 		}
    139 	}
    140 
    141 	return machine;
    142 
    143 out:
    144 	pr_debug("Not enough memory for machine setup\n");
    145 	machine__delete_threads(machine);
    146 	machine__delete(machine);
    147 	return NULL;
    148 }
    149 
    150 struct sample {
    151 	u32 pid;
    152 	u64 ip;
    153 	struct thread *thread;
    154 	struct map *map;
    155 	struct symbol *sym;
    156 };
    157 
    158 static struct sample fake_common_samples[] = {
    159 	/* perf [kernel] schedule() */
    160 	{ .pid = 100, .ip = 0xf0000 + 700, },
    161 	/* perf [perf]   main() */
    162 	{ .pid = 200, .ip = 0x40000 + 700, },
    163 	/* perf [perf]   cmd_record() */
    164 	{ .pid = 200, .ip = 0x40000 + 900, },
    165 	/* bash [bash]   xmalloc() */
    166 	{ .pid = 300, .ip = 0x40000 + 800, },
    167 	/* bash [libc]   malloc() */
    168 	{ .pid = 300, .ip = 0x50000 + 700, },
    169 };
    170 
    171 static struct sample fake_samples[][5] = {
    172 	{
    173 		/* perf [perf]   run_command() */
    174 		{ .pid = 100, .ip = 0x40000 + 800, },
    175 		/* perf [libc]   malloc() */
    176 		{ .pid = 100, .ip = 0x50000 + 700, },
    177 		/* perf [kernel] page_fault() */
    178 		{ .pid = 100, .ip = 0xf0000 + 800, },
    179 		/* perf [kernel] sys_perf_event_open() */
    180 		{ .pid = 200, .ip = 0xf0000 + 900, },
    181 		/* bash [libc]   free() */
    182 		{ .pid = 300, .ip = 0x50000 + 800, },
    183 	},
    184 	{
    185 		/* perf [libc]   free() */
    186 		{ .pid = 200, .ip = 0x50000 + 800, },
    187 		/* bash [libc]   malloc() */
    188 		{ .pid = 300, .ip = 0x50000 + 700, }, /* will be merged */
    189 		/* bash [bash]   xfee() */
    190 		{ .pid = 300, .ip = 0x40000 + 900, },
    191 		/* bash [libc]   realloc() */
    192 		{ .pid = 300, .ip = 0x50000 + 900, },
    193 		/* bash [kernel] page_fault() */
    194 		{ .pid = 300, .ip = 0xf0000 + 800, },
    195 	},
    196 };
    197 
    198 static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
    199 {
    200 	struct perf_evsel *evsel;
    201 	struct addr_location al;
    202 	struct hist_entry *he;
    203 	struct perf_sample sample = { .cpu = 0, };
    204 	size_t i = 0, k;
    205 
    206 	/*
    207 	 * each evsel will have 10 samples - 5 common and 5 distinct.
    208 	 * However the second evsel also has a collapsed entry for
    209 	 * "bash [libc] malloc" so total 9 entries will be in the tree.
    210 	 */
    211 	list_for_each_entry(evsel, &evlist->entries, node) {
    212 		for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) {
    213 			const union perf_event event = {
    214 				.header = {
    215 					.misc = PERF_RECORD_MISC_USER,
    216 				},
    217 			};
    218 
    219 			sample.pid = fake_common_samples[k].pid;
    220 			sample.ip = fake_common_samples[k].ip;
    221 			if (perf_event__preprocess_sample(&event, machine, &al,
    222 							  &sample) < 0)
    223 				goto out;
    224 
    225 			he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1);
    226 			if (he == NULL)
    227 				goto out;
    228 
    229 			fake_common_samples[k].thread = al.thread;
    230 			fake_common_samples[k].map = al.map;
    231 			fake_common_samples[k].sym = al.sym;
    232 		}
    233 
    234 		for (k = 0; k < ARRAY_SIZE(fake_samples[i]); k++) {
    235 			const union perf_event event = {
    236 				.header = {
    237 					.misc = PERF_RECORD_MISC_USER,
    238 				},
    239 			};
    240 
    241 			sample.pid = fake_samples[i][k].pid;
    242 			sample.ip = fake_samples[i][k].ip;
    243 			if (perf_event__preprocess_sample(&event, machine, &al,
    244 							  &sample) < 0)
    245 				goto out;
    246 
    247 			he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1);
    248 			if (he == NULL)
    249 				goto out;
    250 
    251 			fake_samples[i][k].thread = al.thread;
    252 			fake_samples[i][k].map = al.map;
    253 			fake_samples[i][k].sym = al.sym;
    254 		}
    255 		i++;
    256 	}
    257 
    258 	return 0;
    259 
    260 out:
    261 	pr_debug("Not enough memory for adding a hist entry\n");
    262 	return -1;
    263 }
    264 
    265 static int find_sample(struct sample *samples, size_t nr_samples,
    266 		       struct thread *t, struct map *m, struct symbol *s)
    267 {
    268 	while (nr_samples--) {
    269 		if (samples->thread == t && samples->map == m &&
    270 		    samples->sym == s)
    271 			return 1;
    272 		samples++;
    273 	}
    274 	return 0;
    275 }
    276 
    277 static int __validate_match(struct hists *hists)
    278 {
    279 	size_t count = 0;
    280 	struct rb_root *root;
    281 	struct rb_node *node;
    282 
    283 	/*
    284 	 * Only entries from fake_common_samples should have a pair.
    285 	 */
    286 	if (sort__need_collapse)
    287 		root = &hists->entries_collapsed;
    288 	else
    289 		root = hists->entries_in;
    290 
    291 	node = rb_first(root);
    292 	while (node) {
    293 		struct hist_entry *he;
    294 
    295 		he = rb_entry(node, struct hist_entry, rb_node_in);
    296 
    297 		if (hist_entry__has_pairs(he)) {
    298 			if (find_sample(fake_common_samples,
    299 					ARRAY_SIZE(fake_common_samples),
    300 					he->thread, he->ms.map, he->ms.sym)) {
    301 				count++;
    302 			} else {
    303 				pr_debug("Can't find the matched entry\n");
    304 				return -1;
    305 			}
    306 		}
    307 
    308 		node = rb_next(node);
    309 	}
    310 
    311 	if (count != ARRAY_SIZE(fake_common_samples)) {
    312 		pr_debug("Invalid count for matched entries: %zd of %zd\n",
    313 			 count, ARRAY_SIZE(fake_common_samples));
    314 		return -1;
    315 	}
    316 
    317 	return 0;
    318 }
    319 
    320 static int validate_match(struct hists *leader, struct hists *other)
    321 {
    322 	return __validate_match(leader) || __validate_match(other);
    323 }
    324 
    325 static int __validate_link(struct hists *hists, int idx)
    326 {
    327 	size_t count = 0;
    328 	size_t count_pair = 0;
    329 	size_t count_dummy = 0;
    330 	struct rb_root *root;
    331 	struct rb_node *node;
    332 
    333 	/*
    334 	 * Leader hists (idx = 0) will have dummy entries from other,
    335 	 * and some entries will have no pair.  However every entry
    336 	 * in other hists should have (dummy) pair.
    337 	 */
    338 	if (sort__need_collapse)
    339 		root = &hists->entries_collapsed;
    340 	else
    341 		root = hists->entries_in;
    342 
    343 	node = rb_first(root);
    344 	while (node) {
    345 		struct hist_entry *he;
    346 
    347 		he = rb_entry(node, struct hist_entry, rb_node_in);
    348 
    349 		if (hist_entry__has_pairs(he)) {
    350 			if (!find_sample(fake_common_samples,
    351 					 ARRAY_SIZE(fake_common_samples),
    352 					 he->thread, he->ms.map, he->ms.sym) &&
    353 			    !find_sample(fake_samples[idx],
    354 					 ARRAY_SIZE(fake_samples[idx]),
    355 					 he->thread, he->ms.map, he->ms.sym)) {
    356 				count_dummy++;
    357 			}
    358 			count_pair++;
    359 		} else if (idx) {
    360 			pr_debug("A entry from the other hists should have pair\n");
    361 			return -1;
    362 		}
    363 
    364 		count++;
    365 		node = rb_next(node);
    366 	}
    367 
    368 	/*
    369 	 * Note that we have a entry collapsed in the other (idx = 1) hists.
    370 	 */
    371 	if (idx == 0) {
    372 		if (count_dummy != ARRAY_SIZE(fake_samples[1]) - 1) {
    373 			pr_debug("Invalid count of dummy entries: %zd of %zd\n",
    374 				 count_dummy, ARRAY_SIZE(fake_samples[1]) - 1);
    375 			return -1;
    376 		}
    377 		if (count != count_pair + ARRAY_SIZE(fake_samples[0])) {
    378 			pr_debug("Invalid count of total leader entries: %zd of %zd\n",
    379 				 count, count_pair + ARRAY_SIZE(fake_samples[0]));
    380 			return -1;
    381 		}
    382 	} else {
    383 		if (count != count_pair) {
    384 			pr_debug("Invalid count of total other entries: %zd of %zd\n",
    385 				 count, count_pair);
    386 			return -1;
    387 		}
    388 		if (count_dummy > 0) {
    389 			pr_debug("Other hists should not have dummy entries: %zd\n",
    390 				 count_dummy);
    391 			return -1;
    392 		}
    393 	}
    394 
    395 	return 0;
    396 }
    397 
    398 static int validate_link(struct hists *leader, struct hists *other)
    399 {
    400 	return __validate_link(leader, 0) || __validate_link(other, 1);
    401 }
    402 
    403 static void print_hists(struct hists *hists)
    404 {
    405 	int i = 0;
    406 	struct rb_root *root;
    407 	struct rb_node *node;
    408 
    409 	if (sort__need_collapse)
    410 		root = &hists->entries_collapsed;
    411 	else
    412 		root = hists->entries_in;
    413 
    414 	pr_info("----- %s --------\n", __func__);
    415 	node = rb_first(root);
    416 	while (node) {
    417 		struct hist_entry *he;
    418 
    419 		he = rb_entry(node, struct hist_entry, rb_node_in);
    420 
    421 		pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n",
    422 			i, he->thread->comm, he->ms.map->dso->short_name,
    423 			he->ms.sym->name, he->stat.period);
    424 
    425 		i++;
    426 		node = rb_next(node);
    427 	}
    428 }
    429 
    430 int test__hists_link(void)
    431 {
    432 	int err = -1;
    433 	struct machines machines;
    434 	struct machine *machine = NULL;
    435 	struct perf_evsel *evsel, *first;
    436 	struct perf_evlist *evlist = perf_evlist__new();
    437 
    438 	if (evlist == NULL)
    439                 return -ENOMEM;
    440 
    441 	err = parse_events(evlist, "cpu-clock");
    442 	if (err)
    443 		goto out;
    444 	err = parse_events(evlist, "task-clock");
    445 	if (err)
    446 		goto out;
    447 
    448 	/* default sort order (comm,dso,sym) will be used */
    449 	if (setup_sorting() < 0)
    450 		goto out;
    451 
    452 	machines__init(&machines);
    453 
    454 	/* setup threads/dso/map/symbols also */
    455 	machine = setup_fake_machine(&machines);
    456 	if (!machine)
    457 		goto out;
    458 
    459 	if (verbose > 1)
    460 		machine__fprintf(machine, stderr);
    461 
    462 	/* process sample events */
    463 	err = add_hist_entries(evlist, machine);
    464 	if (err < 0)
    465 		goto out;
    466 
    467 	list_for_each_entry(evsel, &evlist->entries, node) {
    468 		hists__collapse_resort(&evsel->hists);
    469 
    470 		if (verbose > 2)
    471 			print_hists(&evsel->hists);
    472 	}
    473 
    474 	first = perf_evlist__first(evlist);
    475 	evsel = perf_evlist__last(evlist);
    476 
    477 	/* match common entries */
    478 	hists__match(&first->hists, &evsel->hists);
    479 	err = validate_match(&first->hists, &evsel->hists);
    480 	if (err)
    481 		goto out;
    482 
    483 	/* link common and/or dummy entries */
    484 	hists__link(&first->hists, &evsel->hists);
    485 	err = validate_link(&first->hists, &evsel->hists);
    486 	if (err)
    487 		goto out;
    488 
    489 	err = 0;
    490 
    491 out:
    492 	/* tear down everything */
    493 	perf_evlist__delete(evlist);
    494 	machines__exit(&machines);
    495 
    496 	return err;
    497 }
    498