1/**
2 * @file daemon/opd_ibs.c
3 * AMD Family10h Instruction Based Sampling (IBS) handling.
4 *
5 * @remark Copyright 2007-2010 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#include <limits.h>
36
37extern op_cpu cpu_type;
38extern int no_event_ok;
39extern int sfile_equal(struct sfile const * sf, struct sfile const * sf2);
40extern void sfile_dup(struct sfile * to, struct sfile * from);
41extern char * session_dir;
42
43/* IBS Select Counters */
44static unsigned int ibs_selected_size;
45
46/* These flags store the IBS-derived events selection. */
47static unsigned int ibs_fetch_selected_flag;
48static unsigned int ibs_op_selected_flag;
49static unsigned int ibs_op_ls_selected_flag;
50static unsigned int ibs_op_nb_selected_flag;
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/* CPUID information */
68unsigned int ibs_family;
69unsigned int ibs_model;
70unsigned int ibs_stepping;
71
72/* IBS Extended MSRs */
73static unsigned long ibs_bta_enabled;
74
75/* IBS log files */
76FILE * memaccess_log;
77FILE * bta_log;
78
79/**
80 * This function converts IBS fetch event flags and values into
81 * derived events. If the tagged (sampled) fetched caused a derived
82 * event, the derived event is tallied.
83 */
84static void opd_log_ibs_fetch(struct transient * trans)
85{
86	struct ibs_fetch_sample * trans_fetch = ((struct ibs_sample*)(trans->ext))->fetch;
87	if (!trans_fetch)
88		return;
89
90	trans_ibs_fetch(trans, ibs_fetch_selected_flag);
91}
92
93
94/**
95 * This function translates the IBS op event flags and values into
96 * IBS op derived events. If an op derived event occured, it's tallied.
97 */
98static void opd_log_ibs_op(struct transient * trans)
99{
100	struct ibs_op_sample * trans_op = ((struct ibs_sample*)(trans->ext))->op;
101	if (!trans_op)
102		return;
103
104	trans_ibs_op_mask_reserved(ibs_family, trans);
105
106	if (trans_ibs_op_rip_invalid(trans) != 0)
107		return;
108
109	trans_ibs_op(trans, ibs_op_selected_flag);
110	trans_ibs_op_ls(trans, ibs_op_ls_selected_flag);
111	trans_ibs_op_nb(trans, ibs_op_nb_selected_flag);
112	trans_ibs_op_ls_memaccess(trans);
113	trans_ibs_op_bta(trans);
114}
115
116
117static void opd_put_ibs_sample(struct transient * trans)
118{
119	unsigned long long event = 0;
120	struct kernel_image * k_image = NULL;
121	struct ibs_fetch_sample * trans_fetch = ((struct ibs_sample*)(trans->ext))->fetch;
122
123	if (!enough_remaining(trans, 1)) {
124		trans->remaining = 0;
125		return;
126	}
127
128	/* IBS can generate samples with invalid dcookie and
129	 * in kernel address range. Map such samples to vmlinux
130	 * only if the user either specifies a range, or vmlinux.
131	 */
132	if (trans->cookie == INVALID_COOKIE
133	    && (k_image = find_kernel_image(trans)) != NULL
134	    && (k_image->start != 0 && k_image->end != 0)
135	    && trans->in_kernel == 0)
136		trans->in_kernel = 1;
137
138	if (trans->tracing != TRACING_ON)
139		trans->event = event;
140
141	/* sfile can change at each sample for kernel */
142	if (trans->in_kernel != 0)
143		clear_trans_current(trans);
144
145	if (!trans->in_kernel && trans->cookie == NO_COOKIE)
146		trans->anon = find_anon_mapping(trans);
147
148	/* get the current sfile if needed */
149	if (!trans->current)
150		trans->current = sfile_find(trans);
151
152	/*
153	 * can happen if kernel sample falls through the cracks, or if
154	 * it's a sample from an anon region we couldn't find
155	 */
156	if (!trans->current)
157		goto out;
158
159	if (trans_fetch)
160		opd_log_ibs_fetch(trans);
161	else
162		opd_log_ibs_op(trans);
163out:
164	/* switch to trace mode */
165	if (trans->tracing == TRACING_START)
166		trans->tracing = TRACING_ON;
167
168	update_trans_last(trans);
169}
170
171
172static void get_ibs_bta_status()
173{
174	FILE * fp = NULL;
175	char buf[PATH_MAX];
176
177	/* Default to disable */
178	ibs_bta_enabled = 0;
179
180	snprintf(buf, PATH_MAX, "/dev/oprofile/ibs_op/branch_target");
181	fp = fopen(buf, "r");
182	if (!fp)
183		return;
184
185	while (fgets(buf, PATH_MAX, fp) != NULL)
186		ibs_bta_enabled = strtoul(buf, NULL, 10);
187
188	fclose(fp);
189}
190
191
192void code_ibs_fetch_sample(struct transient * trans)
193{
194	struct ibs_fetch_sample * trans_fetch = NULL;
195
196	if (!enough_remaining(trans, 7)) {
197		verbprintf(vext, "not enough remaining\n");
198		trans->remaining = 0;
199		ibs_fetch_incomplete_stats++;
200		return;
201	}
202
203	ibs_fetch_sample_stats++;
204
205	trans->ext = xmalloc(sizeof(struct ibs_sample));
206	((struct ibs_sample*)(trans->ext))->fetch = xmalloc(sizeof(struct ibs_fetch_sample));
207	trans_fetch = ((struct ibs_sample*)(trans->ext))->fetch;
208
209	trans_fetch->rip = pop_buffer_value(trans);
210
211	trans_fetch->ibs_fetch_lin_addr_low   = pop_buffer_value(trans);
212	trans_fetch->ibs_fetch_lin_addr_high  = pop_buffer_value(trans);
213
214	trans_fetch->ibs_fetch_ctl_low        = pop_buffer_value(trans);
215	trans_fetch->ibs_fetch_ctl_high       = pop_buffer_value(trans);
216	trans_fetch->ibs_fetch_phys_addr_low  = pop_buffer_value(trans);
217	trans_fetch->ibs_fetch_phys_addr_high = pop_buffer_value(trans);
218
219	verbprintf(vsamples,
220		"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",
221		trans->cpu,
222		(long)trans->tgid,
223		trans_fetch->rip,
224		(trans_fetch->ibs_fetch_ctl_high >> 16) & 0x3ff,
225		(trans_fetch->ibs_fetch_ctl_high) & 0xffff,
226		trans_fetch->ibs_fetch_phys_addr_high,
227		trans_fetch->ibs_fetch_phys_addr_low,
228		trans_fetch->ibs_fetch_lin_addr_high,
229		trans_fetch->ibs_fetch_lin_addr_low) ;
230
231	/* Overwrite the trans->pc with the more accurate trans_fetch->rip */
232	trans->pc = trans_fetch->rip;
233
234	opd_put_ibs_sample(trans);
235
236	free(trans_fetch);
237	free(trans->ext);
238	trans->ext = NULL;
239}
240
241
242static void get_ibs_op_bta_sample(struct transient * trans,
243				    struct ibs_op_sample * trans_op)
244{
245	// Check remaining
246	if (!enough_remaining(trans, 2)) {
247		verbprintf(vext, "not enough remaining\n");
248		trans->remaining = 0;
249		ibs_op_incomplete_stats++;
250		return;
251	}
252
253	if (ibs_bta_enabled == 1) {
254		trans_op->ibs_op_brtgt_addr = pop_buffer_value(trans);
255
256		// Check if branch target address is valid (MSRC001_1035[37] == 1]
257		if ((trans_op->ibs_op_data1_high & (0x00000001 << 5)) == 0) {
258			trans_op->ibs_op_brtgt_addr = 0;
259		}
260	} else {
261		trans_op->ibs_op_brtgt_addr = 0;
262	}
263}
264
265
266void code_ibs_op_sample(struct transient * trans)
267{
268	struct ibs_op_sample * trans_op= NULL;
269
270	if (!enough_remaining(trans, 13)) {
271		verbprintf(vext, "not enough remaining\n");
272		trans->remaining = 0;
273		ibs_op_incomplete_stats++;
274		return;
275	}
276
277	ibs_op_sample_stats++;
278
279	trans->ext = xmalloc(sizeof(struct ibs_sample));
280	((struct ibs_sample*)(trans->ext))->op = xmalloc(sizeof(struct ibs_op_sample));
281	trans_op = ((struct ibs_sample*)(trans->ext))->op;
282
283	trans_op->rip = pop_buffer_value(trans);
284
285	trans_op->ibs_op_lin_addr_low = pop_buffer_value(trans);
286	trans_op->ibs_op_lin_addr_high = pop_buffer_value(trans);
287
288	trans_op->ibs_op_data1_low         = pop_buffer_value(trans);
289	trans_op->ibs_op_data1_high        = pop_buffer_value(trans);
290	trans_op->ibs_op_data2_low         = pop_buffer_value(trans);
291	trans_op->ibs_op_data2_high        = pop_buffer_value(trans);
292	trans_op->ibs_op_data3_low         = pop_buffer_value(trans);
293	trans_op->ibs_op_data3_high        = pop_buffer_value(trans);
294	trans_op->ibs_op_ldst_linaddr_low  = pop_buffer_value(trans);
295	trans_op->ibs_op_ldst_linaddr_high = pop_buffer_value(trans);
296	trans_op->ibs_op_phys_addr_low     = pop_buffer_value(trans);
297	trans_op->ibs_op_phys_addr_high    = pop_buffer_value(trans);
298
299	get_ibs_op_bta_sample(trans, trans_op);
300
301	verbprintf(vsamples,
302	   "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",
303		   trans->cpu,
304		   trans->tgid,
305		   trans_op->rip,
306		   trans_op->ibs_op_data1_high,
307		   trans_op->ibs_op_data1_low,
308		   trans_op->ibs_op_data2_low,
309		   trans_op->ibs_op_data3_high,
310		   trans_op->ibs_op_data3_low,
311		   trans_op->ibs_op_ldst_linaddr_low,
312		   trans_op->ibs_op_phys_addr_low);
313
314	/* Overwrite the trans->pc with the more accurate trans_op->rip */
315	trans->pc = trans_op->rip;
316
317	opd_put_ibs_sample(trans);
318
319	free(trans_op);
320	free(trans->ext);
321	trans->ext = NULL;
322}
323
324
325/** Convert IBS event to value used for data structure indexing */
326static unsigned long ibs_event_to_counter(unsigned long x)
327{
328	unsigned long ret = ~0UL;
329
330	if (IS_IBS_FETCH(x))
331		ret = (x - IBS_FETCH_BASE);
332	else if (IS_IBS_OP(x))
333		ret = (x - IBS_OP_BASE + IBS_FETCH_MAX);
334	else if (IS_IBS_OP_LS(x))
335		ret = (x - IBS_OP_LS_BASE + IBS_OP_MAX + IBS_FETCH_MAX);
336	else if (IS_IBS_OP_NB(x))
337		ret = (x - IBS_OP_NB_BASE + IBS_OP_LS_MAX + IBS_OP_MAX + IBS_FETCH_MAX);
338
339	return (ret != ~0UL) ? ret + OP_MAX_COUNTERS : ret;
340}
341
342
343void opd_log_ibs_event(unsigned int event,
344	struct transient * trans)
345{
346	ibs_derived_event_stats++;
347	trans->event = event;
348	sfile_log_sample_count(trans, 1);
349}
350
351
352void opd_log_ibs_count(unsigned int event,
353			struct transient * trans,
354			unsigned int count)
355{
356	ibs_derived_event_stats++;
357	trans->event = event;
358	sfile_log_sample_count(trans, count);
359}
360
361
362static unsigned long get_ibs_vci_key(unsigned int event)
363{
364	unsigned long key = ibs_event_to_counter(event);
365	if (key == ~0UL || key < OP_MAX_COUNTERS)
366		return ~0UL;
367
368	key = key - OP_MAX_COUNTERS;
369
370	return key;
371}
372
373
374static int ibs_parse_and_set_events(char * str)
375{
376	char * tmp, * ptr, * tok1, * tok2 = NULL;
377	int is_done = 0;
378	struct op_event * event = NULL;
379	op_cpu cpu_type = CPU_NO_GOOD;
380	unsigned long key;
381
382	if (!str)
383		return -1;
384
385	cpu_type = op_get_cpu_type();
386	op_events(cpu_type);
387
388	tmp = op_xstrndup(str, strlen(str));
389	ptr = tmp;
390
391	while (is_done != 1
392		&& (tok1 = strtok_r(ptr, ",", &tok2)) != NULL) {
393
394		if ((ptr = strstr(tok1, ":")) != NULL) {
395			*ptr = '\0';
396			is_done = 1;
397		}
398
399		// Resove event number
400		event = find_event_by_name(tok1, 0, 0);
401		if (!event)
402			return -1;
403
404		// Grouping
405		if (IS_IBS_FETCH(event->val)) {
406			ibs_fetch_selected_flag |= 1 << IBS_FETCH_OFFSET(event->val);
407		} else if (IS_IBS_OP(event->val)) {
408			ibs_op_selected_flag |= 1 << IBS_OP_OFFSET(event->val);
409		} else if (IS_IBS_OP_LS(event->val)) {
410			ibs_op_ls_selected_flag |= 1 << IBS_OP_LS_OFFSET(event->val);
411		} else if (IS_IBS_OP_NB(event->val)) {
412			ibs_op_nb_selected_flag |= 1 << IBS_OP_NB_OFFSET(event->val);
413		} else {
414			return -1;
415		}
416
417		key = get_ibs_vci_key(event->val);
418		if (key == ~0UL)
419			return -1;
420
421		ibs_vci_map[key] = ibs_selected_size;
422
423		/* Initialize part of ibs_vc */
424		ibs_vc[ibs_selected_size].name    = tok1;
425		ibs_vc[ibs_selected_size].value   = event->val;
426		ibs_vc[ibs_selected_size].counter = ibs_selected_size + OP_MAX_COUNTERS;
427		ibs_vc[ibs_selected_size].kernel  = 1;
428		ibs_vc[ibs_selected_size].user    = 1;
429
430		ibs_selected_size++;
431
432		ptr = NULL;
433	}
434
435	return 0;
436}
437
438
439static int ibs_parse_counts(char * str, unsigned long int * count)
440{
441	char * tmp, * tok1, * tok2 = NULL, *end = NULL;
442	if (!str)
443		return -1;
444
445	tmp = op_xstrndup(str, strlen(str));
446	tok1 = strtok_r(tmp, ":", &tok2);
447	*count = strtoul(tok1, &end, 10);
448	if ((end && *end) || *count == 0
449	    || errno == EINVAL || errno == ERANGE) {
450		fprintf(stderr,"Invalid count (%s)\n", str);
451		return -1;
452	}
453
454	return 0;
455}
456
457
458static int ibs_parse_and_set_um_fetch(char const * str)
459{
460	if (!str)
461		return -1;
462	return 0;
463}
464
465
466static int ibs_parse_and_set_um_op(char const * str, unsigned long int * ibs_op_um)
467{
468	char * end = NULL;
469	if (!str)
470		return -1;
471
472	*ibs_op_um = strtoul(str, &end, 16);
473	if ((end && *end) || errno == EINVAL || errno == ERANGE) {
474		fprintf(stderr,"Invalid unitmaks (%s)\n", str);
475		return -1;
476	}
477	return 0;
478}
479
480
481static void check_cpuid_family_model_stepping()
482{
483#if defined(__i386__) || defined(__x86_64__)
484       union {
485                unsigned eax;
486                struct {
487                        unsigned stepping : 4;
488                        unsigned model : 4;
489                        unsigned family : 4;
490                        unsigned res : 4;
491                        unsigned ext_model : 4;
492                        unsigned ext_family : 8;
493                        unsigned res2 : 4;
494                };
495        } v;
496	unsigned ebx, ecx, edx;
497
498	/* CPUID Fn0000_0001_EAX Family, Model, Stepping */
499#ifdef __PIC__
500	__asm__ __volatile__ (
501		"pushl %%ebx\n"
502		"cpuid\n"
503		"mov %%ebx, %1\n"
504		"popl %%ebx"
505		: "=a" (v.eax), "=r" (ebx), "=c" (ecx), "=d" (edx) : "0" (1)
506	);
507#else
508	asm ("cpuid" : "=a" (v.eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "0" (1));
509#endif
510
511	ibs_family   = v.family + v.ext_family;
512	ibs_model    = v.model + v.ext_model;
513	ibs_stepping = v.stepping;
514#else
515	ibs_family   = 0;
516	ibs_model    = 0;
517	ibs_stepping = 0;
518#endif
519}
520
521
522static int ibs_init(char const * argv)
523{
524	char * tmp, * ptr, * tok1, * tok2 = NULL;
525	unsigned int i = 0;
526	unsigned long int ibs_fetch_count = 0;
527	unsigned long int ibs_op_count = 0;
528	unsigned long int ibs_op_um = 0;
529
530	if (!argv)
531		return -1;
532
533	if (empty_line(argv) != 0)
534		return -1;
535
536	tmp = op_xstrndup(argv, strlen(argv));
537	ptr = (char *) skip_ws(tmp);
538
539	// "fetch:event1,event2,....:count:um|op:event1,event2,.....:count:um"
540	tok1 = strtok_r(ptr, "|", &tok2);
541
542	while (tok1 != NULL) {
543
544		if (!strncmp("fetch:", tok1, strlen("fetch:"))) {
545			// Get to event section
546			tok1 = tok1 + strlen("fetch:");
547			if (ibs_parse_and_set_events(tok1) == -1)
548				return -1;
549
550			// Get to count section
551			while (tok1) {
552				if (*tok1 == '\0')
553					return -1;
554				if (*tok1 != ':') {
555					tok1++;
556				} else {
557					tok1++;
558					break;
559				}
560			}
561
562			if (ibs_parse_counts(tok1, &ibs_fetch_count) == -1)
563				return -1;
564
565			// Get to um section
566			while (tok1) {
567				if (*tok1 == '\0')
568					return -1;
569				if (*tok1 != ':') {
570					tok1++;
571				} else {
572					tok1++;
573					break;
574				}
575			}
576
577			if (ibs_parse_and_set_um_fetch(tok1) == -1)
578				return -1;
579
580		} else if (!strncmp("op:", tok1, strlen("op:"))) {
581			// Get to event section
582			tok1 = tok1 + strlen("op:");
583			if (ibs_parse_and_set_events(tok1) == -1)
584				return -1;
585
586			// Get to count section
587			while (tok1) {
588				if (*tok1 == '\0')
589					return -1;
590				if (*tok1 != ':') {
591					tok1++;
592				} else {
593					tok1++;
594					break;
595				}
596			}
597
598			if (ibs_parse_counts(tok1, &ibs_op_count) == -1)
599				return -1;
600
601			// Get to um section
602			while (tok1) {
603				if (*tok1 == '\0')
604					return -1;
605				if (*tok1 != ':') {
606					tok1++;
607				} else {
608					tok1++;
609					break;
610				}
611			}
612
613			if (ibs_parse_and_set_um_op(tok1, &ibs_op_um))
614				return -1;
615
616		} else
617			return -1;
618
619		tok1 = strtok_r(NULL, "|", &tok2);
620	}
621
622	/* Initialize ibs_vc */
623	for (i = 0 ; i < ibs_selected_size ; i++)
624	{
625		if (IS_IBS_FETCH(ibs_vc[i].value)) {
626			ibs_vc[i].count   = ibs_fetch_count;
627			ibs_vc[i].um      = 0;
628		} else {
629			ibs_vc[i].count   = ibs_op_count;
630			ibs_vc[i].um      = ibs_op_um;
631		}
632	}
633
634	// Allow no event
635	no_event_ok = 1;
636
637	check_cpuid_family_model_stepping();
638
639	get_ibs_bta_status();
640
641	/* Create IBS memory access log */
642	memaccess_log = NULL;
643	if (ibs_op_um & 0x2) {
644		char filename[1024];
645		strncpy(filename, session_dir, 1023);
646		strncat(filename, "/samples/ibs_memaccess.log", 1024);
647		if ((memaccess_log = fopen(filename, "w")) == NULL) {
648			verbprintf(vext, "Warning: Cannot create file %s\n", filename);
649
650		} else {
651			fprintf (memaccess_log, "# IBS Memory Access Log\n\n");
652			fprintf (memaccess_log, "# Format: app_cookie,cookie,cpu,tgid,tid,pc,branch-target-address,\n");
653			fprintf (memaccess_log, "#         phy-hi:phy-low,lin-hi:lin-low,accese-type,latency\n\n");
654		}
655	}
656
657	// Create IBS Branch Target Address (BTA) log
658	bta_log = NULL;
659	if (ibs_bta_enabled) {
660		char filename[1024];
661		strncpy(filename, session_dir, 1023);
662		strncat(filename, "/samples/ibs_bta.log", 1024);
663		if ((bta_log = fopen(filename, "w")) == NULL) {
664			verbprintf(vext, "Warning: Cannot create file %s\n", filename);
665		} else {
666			fprintf (bta_log, "# IBS Memory Access Log\n\n");
667			fprintf (bta_log, "# Format: app_cookie,cookie,cpu,tgid,tid,pc,branch-target-address\n\n");
668		}
669	}
670
671	return 0;
672}
673
674
675static int ibs_deinit()
676{
677	if (memaccess_log) {
678		fclose (memaccess_log);
679		memaccess_log = NULL;
680	}
681
682	if (bta_log) {
683		fclose (bta_log);
684		bta_log = NULL;
685	}
686	return 0;
687}
688
689
690static int ibs_print_stats()
691{
692	printf("Nr. IBS Fetch samples     : %lu (%lu entries)\n",
693		ibs_fetch_sample_stats, (ibs_fetch_sample_stats * 7));
694	printf("Nr. IBS Fetch incompletes : %lu\n", ibs_fetch_incomplete_stats);
695	printf("Nr. IBS Op samples        : %lu (%lu entries)\n",
696		ibs_op_sample_stats, (ibs_op_sample_stats * 13));
697	printf("Nr. IBS Op incompletes    : %lu\n", ibs_op_incomplete_stats);
698	printf("Nr. IBS derived events    : %lu\n", ibs_derived_event_stats);
699	return 0;
700}
701
702
703static int ibs_sfile_create(struct sfile * sf)
704{
705	unsigned int i;
706	sf->ext_files = xmalloc(ibs_selected_size * sizeof(odb_t));
707	for (i = 0 ; i < ibs_selected_size ; ++i)
708		odb_init(&sf->ext_files[i]);
709
710	return 0;
711}
712
713
714static int ibs_sfile_dup (struct sfile * to, struct sfile * from)
715{
716	unsigned int i;
717	if (from->ext_files != NULL) {
718		to->ext_files = xmalloc(ibs_selected_size * sizeof(odb_t));
719		for (i = 0 ; i < ibs_selected_size ; ++i)
720			odb_init(&to->ext_files[i]);
721	} else {
722		to->ext_files = NULL;
723	}
724	return 0;
725}
726
727static int ibs_sfile_close(struct sfile * sf)
728{
729	unsigned int i;
730	if (sf->ext_files != NULL) {
731		for (i = 0; i < ibs_selected_size ; ++i)
732			odb_close(&sf->ext_files[i]);
733
734		free(sf->ext_files);
735		sf->ext_files= NULL;
736	}
737	return 0;
738}
739
740static int ibs_sfile_sync(struct sfile * sf)
741{
742	unsigned int i;
743	if (sf->ext_files != NULL) {
744		for (i = 0; i < ibs_selected_size ; ++i)
745			odb_sync(&sf->ext_files[i]);
746	}
747	return 0;
748}
749
750static odb_t * ibs_sfile_get(struct transient const * trans, int is_cg)
751{
752	struct sfile * sf = trans->current;
753	struct sfile * last = trans->last;
754	struct cg_entry * cg;
755	struct list_head * pos;
756	unsigned long hash;
757	odb_t * file;
758	unsigned long counter, ibs_vci, key;
759
760	/* Note: "trans->event" for IBS is not the same as traditional
761 	 * events.  Here, it has the actual event (0xfxxx), while the
762 	 * traditional event has the event index.
763 	 */
764	key = get_ibs_vci_key(trans->event);
765	if (key == ~0UL) {
766		fprintf(stderr, "%s: Invalid IBS event %lu\n", __func__, trans->event);
767		abort();
768	}
769	ibs_vci = ibs_vci_map[key];
770	counter = ibs_vci + OP_MAX_COUNTERS;
771
772	/* Creating IBS sfile if it not already exists */
773	if (sf->ext_files == NULL)
774		ibs_sfile_create(sf);
775
776	file = &(sf->ext_files[ibs_vci]);
777	if (!is_cg)
778		goto open;
779
780	hash = last->hashval & (CG_HASH_SIZE - 1);
781
782	/* Need to look for the right 'to'. Since we're looking for
783	 * 'last', we use its hash.
784	 */
785	list_for_each(pos, &sf->cg_hash[hash]) {
786		cg = list_entry(pos, struct cg_entry, hash);
787		if (sfile_equal(last, &cg->to)) {
788			file = &(cg->to.ext_files[ibs_vci]);
789			goto open;
790		}
791	}
792
793	cg = xmalloc(sizeof(struct cg_entry));
794	sfile_dup(&cg->to, last);
795	list_add(&cg->hash, &sf->cg_hash[hash]);
796	file = &(cg->to.ext_files[ibs_vci]);
797
798open:
799	if (!odb_open_count(file))
800		opd_open_sample_file(file, last, sf, counter, is_cg);
801
802	/* Error is logged by opd_open_sample_file */
803	if (!odb_open_count(file))
804		return NULL;
805
806	return file;
807}
808
809
810/** Filled opd_event structure with IBS derived event information
811 *  from the given counter value.
812 */
813static struct opd_event * ibs_sfile_find_counter_event(unsigned long counter)
814{
815	unsigned long ibs_vci;
816
817	if (counter >= OP_MAX_COUNTERS + OP_MAX_IBS_COUNTERS
818	    || counter < OP_MAX_COUNTERS) {
819		fprintf(stderr,"Error: find_ibs_counter_event : "
820				"invalid counter value %lu.\n", counter);
821		abort();
822	}
823
824	ibs_vci = counter - OP_MAX_COUNTERS;
825	return &ibs_vc[ibs_vci];
826}
827
828
829struct opd_ext_sfile_handlers ibs_sfile_handlers =
830{
831	.create = &ibs_sfile_create,
832	.dup    = &ibs_sfile_dup,
833	.close  = &ibs_sfile_close,
834	.sync   = &ibs_sfile_sync,
835	.get    = &ibs_sfile_get,
836	.find_counter_event = &ibs_sfile_find_counter_event
837};
838
839
840struct opd_ext_handlers ibs_handlers =
841{
842	.ext_init        = &ibs_init,
843	.ext_deinit      = &ibs_deinit,
844	.ext_print_stats = &ibs_print_stats,
845	.ext_sfile       = &ibs_sfile_handlers
846};
847