opd_ibs.c revision 5a4eb4eb367eccd4b976d1feae96cea96d2c50f2
1/**
2 * @file daemon/opd_ibs.c
3 * AMD Family10h Instruction Based Sampling (IBS) handling.
4 *
5 * @remark Copyright 2007 OProfile authors
6 * @remark Read the file COPYING
7 *
8 * @author Jason Yeh <jason.yeh@amd.com>
9 * @author Paul Drongowski <paul.drongowski@amd.com>
10 * @author Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
11 * Copyright (c) 2008 Advanced Micro Devices, Inc.
12 */
13
14#include "op_hw_config.h"
15#include "op_events.h"
16#include "op_string.h"
17#include "op_libiberty.h"
18#include "opd_printf.h"
19#include "opd_trans.h"
20#include "opd_events.h"
21#include "opd_kernel.h"
22#include "opd_anon.h"
23#include "opd_sfile.h"
24#include "opd_interface.h"
25#include "opd_mangling.h"
26#include "opd_extended.h"
27#include "opd_ibs.h"
28#include "opd_ibs_trans.h"
29#include "opd_ibs_macro.h"
30
31#include <stdlib.h>
32#include <stdio.h>
33#include <errno.h>
34#include <string.h>
35
36extern op_cpu cpu_type;
37extern int no_event_ok;
38extern int sfile_equal(struct sfile const * sf, struct sfile const * sf2);
39extern void sfile_dup(struct sfile * to, struct sfile * from);
40
41/* IBS Select Arrays/Counters */
42static unsigned int ibs_selected_size;
43static unsigned int ibs_fetch_selected_flag;
44static unsigned int ibs_fetch_selected_size;
45static unsigned int ibs_op_selected_flag;
46static unsigned int ibs_op_selected_size;
47static unsigned int ibs_op_ls_selected_flag;
48static unsigned int ibs_op_ls_selected_size;
49static unsigned int ibs_op_nb_selected_flag;
50static unsigned int ibs_op_nb_selected_size;
51
52/* IBS Statistics */
53static unsigned long ibs_fetch_sample_stats;
54static unsigned long ibs_fetch_incomplete_stats;
55static unsigned long ibs_op_sample_stats;
56static unsigned long ibs_op_incomplete_stats;
57static unsigned long ibs_derived_event_stats;
58
59/*
60 * IBS Virtual Counter
61 */
62struct opd_event ibs_vc[OP_MAX_IBS_COUNTERS];
63
64/* IBS Virtual Counter Index(VCI) Map*/
65unsigned int ibs_vci_map[OP_MAX_IBS_COUNTERS];
66
67/**
68 * This function converts IBS fetch event flags and values into
69 * derived events. If the tagged (sampled) fetched caused a derived
70 * event, the derived event is tallied.
71 */
72static void opd_log_ibs_fetch(struct transient * trans)
73{
74	struct ibs_fetch_sample * trans_fetch = ((struct ibs_sample*)(trans->ext))->fetch;
75	if (!trans_fetch)
76		return;
77
78	trans_ibs_fetch(trans, ibs_fetch_selected_flag, ibs_fetch_selected_size);
79}
80
81
82/**
83 * This function translates the IBS op event flags and values into
84 * IBS op derived events. If an op derived event occured, it's tallied.
85 */
86static void opd_log_ibs_op(struct transient * trans)
87{
88	struct ibs_op_sample * trans_op = ((struct ibs_sample*)(trans->ext))->op;
89	if (!trans_op)
90		return;
91
92	trans_ibs_op(trans, ibs_op_selected_flag, ibs_op_selected_size);
93	trans_ibs_op_ls(trans, ibs_op_ls_selected_flag, ibs_op_ls_selected_size);
94	trans_ibs_op_nb(trans, ibs_op_nb_selected_flag, ibs_op_nb_selected_size);
95}
96
97
98static void opd_put_ibs_sample(struct transient * trans)
99{
100	unsigned long long event = 0;
101	struct kernel_image * k_image = NULL;
102	struct ibs_fetch_sample * trans_fetch = ((struct ibs_sample*)(trans->ext))->fetch;
103
104	if (!enough_remaining(trans, 1)) {
105		trans->remaining = 0;
106		return;
107	}
108
109	/* IBS can generate samples with invalid dcookie and
110	 * in kernel address range. Map such samples to vmlinux
111	 * only if the user either specifies a range, or vmlinux.
112	 */
113	if (trans->cookie == INVALID_COOKIE
114	    && (k_image = find_kernel_image(trans)) != NULL
115	    && (k_image->start != 0 && k_image->end != 0)
116	    && trans->in_kernel == 0)
117		trans->in_kernel = 1;
118
119	if (trans->tracing != TRACING_ON)
120		trans->event = event;
121
122	/* sfile can change at each sample for kernel */
123	if (trans->in_kernel != 0)
124		clear_trans_current(trans);
125
126	if (!trans->in_kernel && trans->cookie == NO_COOKIE)
127		trans->anon = find_anon_mapping(trans);
128
129	/* get the current sfile if needed */
130	if (!trans->current)
131		trans->current = sfile_find(trans);
132
133	/*
134	 * can happen if kernel sample falls through the cracks, or if
135	 * it's a sample from an anon region we couldn't find
136	 */
137	if (!trans->current)
138		goto out;
139
140	if (trans_fetch)
141		opd_log_ibs_fetch(trans);
142	else
143		opd_log_ibs_op(trans);
144out:
145	/* switch to trace mode */
146	if (trans->tracing == TRACING_START)
147		trans->tracing = TRACING_ON;
148
149	update_trans_last(trans);
150}
151
152
153void code_ibs_fetch_sample(struct transient * trans)
154{
155	struct ibs_fetch_sample * trans_fetch = NULL;
156
157	if (!enough_remaining(trans, 7)) {
158		verbprintf(vext, "not enough remaining\n");
159		trans->remaining = 0;
160		ibs_fetch_incomplete_stats++;
161		return;
162	}
163
164	ibs_fetch_sample_stats++;
165
166	trans->ext = xmalloc(sizeof(struct ibs_sample));
167	((struct ibs_sample*)(trans->ext))->fetch = xmalloc(sizeof(struct ibs_fetch_sample));
168	trans_fetch = ((struct ibs_sample*)(trans->ext))->fetch;
169
170	trans_fetch->rip = pop_buffer_value(trans);
171
172	trans_fetch->ibs_fetch_lin_addr_low = pop_buffer_value(trans);
173	trans_fetch->ibs_fetch_lin_addr_high = pop_buffer_value(trans);
174
175	trans_fetch->ibs_fetch_ctl_low = pop_buffer_value(trans);
176	trans_fetch->ibs_fetch_ctl_high = pop_buffer_value(trans);
177	trans_fetch->ibs_fetch_phys_addr_low = pop_buffer_value(trans);
178	trans_fetch->ibs_fetch_phys_addr_high = pop_buffer_value(trans);
179
180	verbprintf(vsamples,
181		"FETCH_X CPU:%ld PID:%ld RIP:%lx CTL_H:%x LAT:%d P_HI:%x P_LO:%x L_HI:%x L_LO:%x\n",
182		trans->cpu,
183		(long)trans->tgid,
184		trans_fetch->rip,
185		(trans_fetch->ibs_fetch_ctl_high >> 16) & 0x3ff,
186		(trans_fetch->ibs_fetch_ctl_high) & 0xffff,
187		trans_fetch->ibs_fetch_phys_addr_high,
188		trans_fetch->ibs_fetch_phys_addr_low,
189		trans_fetch->ibs_fetch_lin_addr_high,
190		trans_fetch->ibs_fetch_lin_addr_low) ;
191
192	/* Overwrite the trans->pc with the more accurate trans_fetch->rip */
193	trans->pc = trans_fetch->rip;
194
195	opd_put_ibs_sample(trans);
196
197	free(trans_fetch);
198	free(trans->ext);
199	trans->ext = NULL;
200}
201
202
203void code_ibs_op_sample(struct transient * trans)
204{
205	struct ibs_op_sample * trans_op= NULL;
206
207	if (!enough_remaining(trans, 13)) {
208		verbprintf(vext, "not enough remaining\n");
209		trans->remaining = 0;
210		ibs_op_incomplete_stats++;
211		return;
212	}
213
214	ibs_op_sample_stats++;
215
216	trans->ext = xmalloc(sizeof(struct ibs_sample));
217	((struct ibs_sample*)(trans->ext))->op = xmalloc(sizeof(struct ibs_op_sample));
218	trans_op = ((struct ibs_sample*)(trans->ext))->op;
219
220	trans_op->rip = pop_buffer_value(trans);
221
222	trans_op->ibs_op_lin_addr_low = pop_buffer_value(trans);
223	trans_op->ibs_op_lin_addr_high = pop_buffer_value(trans);
224
225	trans_op->ibs_op_data1_low         = pop_buffer_value(trans);
226	trans_op->ibs_op_data1_high        = pop_buffer_value(trans);
227	trans_op->ibs_op_data2_low         = pop_buffer_value(trans);
228	trans_op->ibs_op_data2_high        = pop_buffer_value(trans);
229	trans_op->ibs_op_data3_low         = pop_buffer_value(trans);
230	trans_op->ibs_op_data3_high        = pop_buffer_value(trans);
231	trans_op->ibs_op_ldst_linaddr_low  = pop_buffer_value(trans);
232	trans_op->ibs_op_ldst_linaddr_high = pop_buffer_value(trans);
233	trans_op->ibs_op_phys_addr_low     = pop_buffer_value(trans);
234	trans_op->ibs_op_phys_addr_high    = pop_buffer_value(trans);
235
236	verbprintf(vsamples,
237		   "IBS_OP_X CPU:%ld PID:%d RIP:%lx D1HI:%x D1LO:%x D2LO:%x D3HI:%x D3LO:%x L_LO:%x P_LO:%x\n",
238		   trans->cpu,
239		   trans->tgid,
240		   trans_op->rip,
241		   trans_op->ibs_op_data1_high,
242		   trans_op->ibs_op_data1_low,
243		   trans_op->ibs_op_data2_low,
244		   trans_op->ibs_op_data3_high,
245		   trans_op->ibs_op_data3_low,
246		   trans_op->ibs_op_ldst_linaddr_low,
247		   trans_op->ibs_op_phys_addr_low);
248
249	/* Overwrite the trans->pc with the more accurate trans_op->rip */
250	trans->pc = trans_op->rip;
251
252	opd_put_ibs_sample(trans);
253
254	free(trans_op);
255	free(trans->ext);
256	trans->ext = NULL;
257}
258
259
260/** Convert IBS event to value used for data structure indexing */
261static unsigned long ibs_event_to_counter(unsigned long x)
262{
263	unsigned long ret = ~0UL;
264
265	if (IS_IBS_FETCH(x))
266		ret = (x - IBS_FETCH_BASE);
267	else if (IS_IBS_OP(x))
268		ret = (x - IBS_OP_BASE + IBS_FETCH_MAX);
269	else if (IS_IBS_OP_LS(x))
270		ret = (x - IBS_OP_LS_BASE + IBS_OP_MAX + IBS_FETCH_MAX);
271	else if (IS_IBS_OP_NB(x))
272		ret = (x - IBS_OP_NB_BASE + IBS_OP_LS_MAX + IBS_OP_MAX + IBS_FETCH_MAX);
273
274	return (ret != ~0UL) ? ret + OP_MAX_COUNTERS : ret;
275}
276
277
278void opd_log_ibs_event(unsigned int event,
279	struct transient * trans)
280{
281	ibs_derived_event_stats++;
282	trans->event = event;
283	sfile_log_sample_count(trans, 1);
284}
285
286
287void opd_log_ibs_count(unsigned int event,
288			struct transient * trans,
289			unsigned int count)
290{
291	ibs_derived_event_stats++;
292	trans->event = event;
293	sfile_log_sample_count(trans, count);
294}
295
296
297static unsigned long get_ibs_vci_key(unsigned int event)
298{
299	unsigned long key = ibs_event_to_counter(event);
300	if (key == ~0UL || key < OP_MAX_COUNTERS)
301		return ~0UL;
302
303	key = key - OP_MAX_COUNTERS;
304
305	return key;
306}
307
308
309static int ibs_parse_and_set_events(char * str)
310{
311	char * tmp, * ptr, * tok1, * tok2 = NULL;
312	int is_done = 0;
313	struct op_event * event = NULL;
314	op_cpu cpu_type = CPU_NO_GOOD;
315	unsigned long key;
316
317	if (!str)
318		return -1;
319
320	cpu_type = op_get_cpu_type();
321	op_events(cpu_type);
322
323	tmp = op_xstrndup(str, strlen(str));
324	ptr = tmp;
325
326	while (is_done != 1
327		&& (tok1 = strtok_r(ptr, ",", &tok2)) != NULL) {
328
329		if ((ptr = strstr(tok1, ":")) != NULL) {
330			*ptr = '\0';
331			is_done = 1;
332		}
333
334		// Resove event number
335		event = find_event_by_name(tok1, 0, 0);
336		if (!event)
337			return -1;
338
339		// Grouping
340		if (IS_IBS_FETCH(event->val)) {
341			ibs_fetch_selected_flag |= 1 << IBS_FETCH_OFFSET(event->val);
342			ibs_fetch_selected_size++;
343		} else if (IS_IBS_OP(event->val)) {
344			ibs_op_selected_flag |= 1 << IBS_OP_OFFSET(event->val);
345			ibs_op_selected_size++;
346		} else if (IS_IBS_OP_LS(event->val)) {
347			ibs_op_ls_selected_flag |= 1 << IBS_OP_LS_OFFSET(event->val);
348			ibs_op_ls_selected_size++;
349		} else if (IS_IBS_OP_NB(event->val)) {
350			ibs_op_nb_selected_flag |= 1 << IBS_OP_NB_OFFSET(event->val);
351			ibs_op_nb_selected_size++;
352		} else {
353			return -1;
354		}
355
356		key = get_ibs_vci_key(event->val);
357		if (key == ~0UL)
358			return -1;
359
360		ibs_vci_map[key] = ibs_selected_size;
361
362		/* Initialize part of ibs_vc */
363		ibs_vc[ibs_selected_size].name    = tok1;
364		ibs_vc[ibs_selected_size].value   = event->val;
365		ibs_vc[ibs_selected_size].counter = ibs_selected_size + OP_MAX_COUNTERS;
366		ibs_vc[ibs_selected_size].kernel  = 1;
367		ibs_vc[ibs_selected_size].user    = 1;
368
369		ibs_selected_size++;
370
371		ptr = NULL;
372	}
373
374	return 0;
375}
376
377
378static int ibs_parse_counts(char * str, unsigned long int * count)
379{
380	char * tmp, * tok1, * tok2 = NULL, *end = NULL;
381	if (!str)
382		return -1;
383
384	tmp = op_xstrndup(str, strlen(str));
385	tok1 = strtok_r(tmp, ":", &tok2);
386	*count = strtoul(tok1, &end, 10);
387	if ((end && *end) || *count == 0
388	    || errno == EINVAL || errno == ERANGE) {
389		fprintf(stderr,"Invalid count (%s)\n", str);
390		return -1;
391	}
392
393	return 0;
394}
395
396
397static int ibs_parse_and_set_um_fetch(char const * str)
398{
399	if (!str)
400		return -1;
401	return 0;
402}
403
404
405
406static int ibs_parse_and_set_um_op(char const * str, unsigned long int * ibs_op_um)
407{
408	char * end = NULL;
409	if (!str)
410		return -1;
411
412	*ibs_op_um = strtoul(str, &end, 16);
413	if ((end && *end) || errno == EINVAL || errno == ERANGE) {
414		fprintf(stderr,"Invalid unitmaks (%s)\n", str);
415		return -1;
416	}
417	return 0;
418}
419
420
421static int ibs_init(char const * argv)
422{
423	char * tmp, * ptr, * tok1, * tok2 = NULL;
424	unsigned int i = 0;
425	unsigned long int ibs_fetch_count = 0;
426	unsigned long int ibs_op_count = 0;
427	unsigned long int ibs_op_um = 0;
428
429	if (!argv)
430		return -1;
431
432	if (empty_line(argv) != 0)
433		return -1;
434
435	tmp = op_xstrndup(argv, strlen(argv));
436	ptr = (char *) skip_ws(tmp);
437
438	// "fetch:event1,event2,....:count:um|op:event1,event2,.....:count:um"
439	tok1 = strtok_r(ptr, "|", &tok2);
440
441	while (tok1 != NULL) {
442
443		if (!strncmp("fetch:", tok1, strlen("fetch:"))) {
444			// Get to event section
445			tok1 = tok1 + strlen("fetch:");
446			if (ibs_parse_and_set_events(tok1) == -1)
447				return -1;
448
449			// Get to count section
450			while (tok1) {
451				if (*tok1 == '\0')
452					return -1;
453				if (*tok1 != ':') {
454					tok1++;
455				} else {
456					tok1++;
457					break;
458				}
459			}
460
461			if (ibs_parse_counts(tok1, &ibs_fetch_count) == -1)
462				return -1;
463
464			// Get to um section
465			while (tok1) {
466				if (*tok1 == '\0')
467					return -1;
468				if (*tok1 != ':') {
469					tok1++;
470				} else {
471					tok1++;
472					break;
473				}
474			}
475
476			if (ibs_parse_and_set_um_fetch(tok1) == -1)
477				return -1;
478
479		} else if (!strncmp("op:", tok1, strlen("op:"))) {
480			// Get to event section
481			tok1 = tok1 + strlen("op:");
482			if (ibs_parse_and_set_events(tok1) == -1)
483				return -1;
484
485			// Get to count section
486			while (tok1) {
487				if (*tok1 == '\0')
488					return -1;
489				if (*tok1 != ':') {
490					tok1++;
491				} else {
492					tok1++;
493					break;
494				}
495			}
496
497			if (ibs_parse_counts(tok1, &ibs_op_count) == -1)
498				return -1;
499
500			// Get to um section
501			while (tok1) {
502				if (*tok1 == '\0')
503					return -1;
504				if (*tok1 != ':') {
505					tok1++;
506				} else {
507					tok1++;
508					break;
509				}
510			}
511
512			if (ibs_parse_and_set_um_op(tok1, &ibs_op_um))
513				return -1;
514
515		} else
516			return -1;
517
518		tok1 = strtok_r(NULL, "|", &tok2);
519	}
520
521	/* Initialize ibs_vc */
522	for (i = 0 ; i < ibs_selected_size ; i++)
523	{
524		if (IS_IBS_FETCH(ibs_vc[i].value)) {
525			ibs_vc[i].count   = ibs_fetch_count;
526			ibs_vc[i].um      = 0;
527		} else {
528			ibs_vc[i].count   = ibs_op_count;
529			ibs_vc[i].um      = ibs_op_um;
530		}
531	}
532
533	// Allow no event
534	no_event_ok = 1;
535	return 0;
536}
537
538
539static int ibs_print_stats()
540{
541	printf("Nr. IBS Fetch samples     : %lu (%lu entries)\n", ibs_fetch_sample_stats, (ibs_fetch_sample_stats * 7));
542	printf("Nr. IBS Fetch incompletes : %lu\n", ibs_fetch_incomplete_stats);
543	printf("Nr. IBS Op samples        : %lu (%lu entries)\n", ibs_op_sample_stats, (ibs_op_sample_stats * 13));
544	printf("Nr. IBS Op incompletes    : %lu\n", ibs_op_incomplete_stats);
545	printf("Nr. IBS derived events    : %lu\n", ibs_derived_event_stats);
546	return 0;
547}
548
549
550static int ibs_sfile_create(struct sfile * sf)
551{
552	unsigned int i;
553	sf->ext_files = xmalloc(ibs_selected_size * sizeof(odb_t));
554	for (i = 0 ; i < ibs_selected_size ; ++i)
555		odb_init(&sf->ext_files[i]);
556
557	return 0;
558}
559
560
561static int ibs_sfile_dup (struct sfile * to, struct sfile * from)
562{
563	unsigned int i;
564	if (from->ext_files != NULL) {
565		to->ext_files = xmalloc(ibs_selected_size * sizeof(odb_t));
566		for (i = 0 ; i < ibs_selected_size ; ++i)
567			odb_init(&to->ext_files[i]);
568	} else {
569		to->ext_files = NULL;
570	}
571	return 0;
572}
573
574static int ibs_sfile_close(struct sfile * sf)
575{
576	unsigned int i;
577	if (sf->ext_files != NULL) {
578		for (i = 0; i < ibs_selected_size ; ++i)
579			odb_close(&sf->ext_files[i]);
580
581		free(sf->ext_files);
582		sf->ext_files= NULL;
583	}
584	return 0;
585}
586
587static int ibs_sfile_sync(struct sfile * sf)
588{
589	unsigned int i;
590	if (sf->ext_files != NULL) {
591		for (i = 0; i < ibs_selected_size ; ++i)
592			odb_sync(&sf->ext_files[i]);
593	}
594	return 0;
595}
596
597static odb_t * ibs_sfile_get(struct transient const * trans, int is_cg)
598{
599	struct sfile * sf = trans->current;
600	struct sfile * last = trans->last;
601	struct cg_entry * cg;
602	struct list_head * pos;
603	unsigned long hash;
604	odb_t * file;
605	unsigned long counter, ibs_vci, key;
606
607	/* Note: "trans->event" for IBS is not the same as traditional
608 	 * events.  Here, it has the actual event (0xfxxx), while the
609 	 * traditional event has the event index.
610 	 */
611	key = get_ibs_vci_key(trans->event);
612	if (key == ~0UL) {
613		fprintf(stderr, "%s: Invalid IBS event %lu\n", __func__, trans->event);
614		abort();
615	}
616	ibs_vci = ibs_vci_map[key];
617	counter = ibs_vci + OP_MAX_COUNTERS;
618
619	/* Creating IBS sfile if it not already exists */
620	if (sf->ext_files == NULL)
621		ibs_sfile_create(sf);
622
623	file = &(sf->ext_files[ibs_vci]);
624	if (!is_cg)
625		goto open;
626
627	hash = last->hashval & (CG_HASH_SIZE - 1);
628
629	/* Need to look for the right 'to'. Since we're looking for
630	 * 'last', we use its hash.
631	 */
632	list_for_each(pos, &sf->cg_hash[hash]) {
633		cg = list_entry(pos, struct cg_entry, hash);
634		if (sfile_equal(last, &cg->to)) {
635			file = &(cg->to.ext_files[ibs_vci]);
636			goto open;
637		}
638	}
639
640	cg = xmalloc(sizeof(struct cg_entry));
641	sfile_dup(&cg->to, last);
642	list_add(&cg->hash, &sf->cg_hash[hash]);
643	file = &(cg->to.ext_files[ibs_vci]);
644
645open:
646	if (!odb_open_count(file))
647		opd_open_sample_file(file, last, sf, counter, is_cg);
648
649	/* Error is logged by opd_open_sample_file */
650	if (!odb_open_count(file))
651		return NULL;
652
653	return file;
654}
655
656
657/** Filled opd_event structure with IBS derived event information
658 *  from the given counter value.
659 */
660static struct opd_event * ibs_sfile_find_counter_event(unsigned long counter)
661{
662	unsigned long ibs_vci;
663
664	if (counter >= OP_MAX_COUNTERS + OP_MAX_IBS_COUNTERS
665	    || counter < OP_MAX_COUNTERS) {
666		fprintf(stderr,"Error: find_ibs_counter_event : "
667				"invalid counter value %lu.\n", counter);
668		abort();
669	}
670
671	ibs_vci = counter - OP_MAX_COUNTERS;
672	return &ibs_vc[ibs_vci];
673}
674
675
676struct opd_ext_sfile_handlers ibs_sfile_handlers =
677{
678	.create = &ibs_sfile_create,
679	.dup    = &ibs_sfile_dup,
680	.close  = &ibs_sfile_close,
681	.sync   = &ibs_sfile_sync,
682	.get    = &ibs_sfile_get,
683	.find_counter_event = &ibs_sfile_find_counter_event
684};
685
686
687struct opd_ext_handlers ibs_handlers =
688{
689	.ext_init  = &ibs_init,
690	.ext_print_stats = &ibs_print_stats,
691	.ext_sfile = &ibs_sfile_handlers
692};
693