• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 
3 /*
4  * Copyright (C) 2018 Intel Corporation
5  * Author: Ammy Yi (ammy.yi@intel.com)
6  */
7 
8 /*
9  * This test will check if Intel PT(Intel Processer Trace) is working.
10  *
11  * Intel CPU of 5th-generation Core (Broadwell) or newer is required for the test.
12  *
13  * kconfig requirement: CONFIG_PERF_EVENTS
14  */
15 
16 
17 #include <sched.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include "tst_test.h"
22 #include "lapi/syscalls.h"
23 #include "config.h"
24 
25 #ifdef HAVE_STRUCT_PERF_EVENT_MMAP_PAGE_AUX_HEAD
26 # include <linux/perf_event.h>
27 
28 #define PAGESIZE 4096
29 #define INTEL_PT_MEMSIZE (17*PAGESIZE)
30 
31 #define BIT(nr)                 (1UL << (nr))
32 
33 #define INTEL_PT_PATH "/sys/devices/intel_pt"
34 #define INTEL_PT_PMU_TYPE "/sys/devices/intel_pt/type"
35 #define INTEL_PT_FORMAT_TSC "/sys/devices/intel_pt/format/tsc"
36 #define INTEL_PT_FORMAT_NRT "/sys/devices/intel_pt/format/noretcomp"
37 
38 //Intel PT event handle
39 int fde = -1;
40 //map head and size
41 uint64_t **bufm;
42 long buhsz;
43 static char *str_mode;
44 static char *str_exclude_info;
45 static char *str_branch_flag;
46 int mode = 1;
47 
create_map(int fde,long bufsize,int flag)48 static uint64_t **create_map(int fde, long bufsize, int flag)
49 {
50 	uint64_t **buf_ev;
51 	int pro_flag;
52 	struct perf_event_mmap_page *pc;
53 
54 	buf_ev = SAFE_MALLOC(2*sizeof(uint64_t *));
55 	buf_ev[0] = NULL;
56 	buf_ev[1] = NULL;
57 	if (flag == 1) {
58 		tst_res(TINFO, "Memory will be r/w for full trace mode");
59 		pro_flag = PROT_READ | PROT_WRITE;
60 	} else {
61 		tst_res(TINFO, "Memory will be r only for snapshot mode");
62 		pro_flag = PROT_READ;
63 	}
64 	buf_ev[0] = SAFE_MMAP(NULL, INTEL_PT_MEMSIZE, PROT_READ | PROT_WRITE,
65 							MAP_SHARED, fde, 0);
66 
67 	tst_res(TINFO, "Open Intel PT event failed");
68 	pc = (struct perf_event_mmap_page *)buf_ev[0];
69 	pc->aux_offset = INTEL_PT_MEMSIZE;
70 	pc->aux_size = bufsize;
71 	buf_ev[1] = SAFE_MMAP(NULL, bufsize, pro_flag,
72 					MAP_SHARED, fde, INTEL_PT_MEMSIZE);
73 	return buf_ev;
74 }
75 
76 
intel_pt_pmu_value(char * dir)77 int intel_pt_pmu_value(char *dir)
78 {
79 	char *value;
80 	int val = 0;
81 	char delims[] = ":";
82 
83 	SAFE_FILE_SCANF(dir, "%m[^\n]", &value);
84 	if (strstr(value, delims) == NULL) {
85 		val = atoi(value);
86 	} else {
87 		strsep(&value, delims);
88 		val = atoi(value);
89 	}
90 	return val;
91 }
92 
del_map(uint64_t ** buf_ev,long bufsize)93 static void del_map(uint64_t **buf_ev, long bufsize)
94 {
95 	if (buf_ev) {
96 		if (buf_ev[0])
97 			munmap(buf_ev[0], INTEL_PT_MEMSIZE);
98 		if (buf_ev[1])
99 			munmap(buf_ev[1], bufsize);
100 	}
101 
102 	free(buf_ev);
103 }
104 
intel_pt_trace_check(void)105 static void intel_pt_trace_check(void)
106 {
107 	uint64_t aux_head = 0;
108 	struct perf_event_mmap_page *pmp;
109 	/* enable tracing */
110 	SAFE_IOCTL(fde, PERF_EVENT_IOC_RESET);
111 	SAFE_IOCTL(fde, PERF_EVENT_IOC_ENABLE);
112 
113 	/* stop tracing */
114 	SAFE_IOCTL(fde, PERF_EVENT_IOC_DISABLE);
115 
116 	/* check if there is some trace generated */
117 	pmp = (struct perf_event_mmap_page *)bufm[0];
118 	aux_head = *(volatile uint64_t *)&pmp->aux_head;
119 	if (aux_head == 0) {
120 		tst_res(TFAIL, "There is no trace");
121 		return;
122 	}
123 
124 	tst_res(TPASS, "perf trace test passed");
125 }
126 
is_affected_by_erratum_BDM106(void)127 static int is_affected_by_erratum_BDM106(void)
128 {
129 	int family = -1, model = -1;
130 
131 	if (FILE_LINES_SCANF("/proc/cpuinfo", "cpu family%*s%d", &family)
132 		|| family != 6)
133 		return 0;
134 
135 	if (!FILE_LINES_SCANF("/proc/cpuinfo", "model%*s%d", &model)) {
136 		tst_res(TINFO, "Intel FAM6 model %d", model);
137 
138 		switch (model) {
139 		case 0x3D: /* INTEL_FAM6_BROADWELL */
140 		/* fallthrough */
141 		case 0x47: /* INTEL_FAM6_BROADWELL_G */
142 		/* fallthrough */
143 		case 0x4F: /* INTEL_FAM6_BROADWELL_X */
144 		/* fallthrough */
145 		case 0x56: /* INTEL_FAM6_BROADWELL_D */
146 			return 1;
147 		}
148 	}
149 
150 	return 0;
151 }
152 
setup(void)153 static void setup(void)
154 {
155 	struct perf_event_attr attr = {};
156 
157 	buhsz = 2 * PAGESIZE;
158 
159 	if (access(INTEL_PT_PATH, F_OK)) {
160 		tst_brk(TCONF,
161 			"Requires Intel Core 5th+ generation (Broadwell and newer) and CONFIG_PERF_EVENTS enabled");
162 	}
163 
164 	/* set attr for Intel PT trace */
165 	attr.type	= intel_pt_pmu_value(INTEL_PT_PMU_TYPE);
166 	attr.read_format = PERF_FORMAT_ID | PERF_FORMAT_TOTAL_TIME_RUNNING |
167 				PERF_FORMAT_TOTAL_TIME_ENABLED;
168 	attr.disabled	= 1;
169 	attr.config	= BIT(intel_pt_pmu_value(INTEL_PT_FORMAT_TSC)) |
170 				BIT(intel_pt_pmu_value(INTEL_PT_FORMAT_NRT));
171 	attr.size	= sizeof(struct perf_event_attr);
172 	attr.mmap			= 1;
173 	if (str_branch_flag) {
174 		if (is_affected_by_erratum_BDM106()) {
175 			tst_brk(TCONF, "erratum BDM106 disallows not "
176 				"setting BRANCH_EN on this CPU");
177 		}
178 
179 		tst_res(TINFO, "Intel PT will disable branch trace");
180 		attr.config |= 1;
181 	}
182 
183 	attr.exclude_kernel	= 0;
184 	attr.exclude_user	= 0;
185 	if (str_exclude_info) {
186 		if (!strcmp(str_exclude_info, "user")) {
187 			tst_res(TINFO, "Intel PT will exclude user trace");
188 			attr.exclude_user = 1;
189 		} else if (!strcmp(str_exclude_info, "kernel")) {
190 			tst_res(TINFO, "Intel PT will exclude kernel trace");
191 			attr.exclude_kernel = 1;
192 		} else {
193 			tst_brk(TBROK, "Invalid -e '%s'", str_exclude_info);
194 		}
195 	}
196 
197 	/* only get trace for own pid */
198 	fde = tst_syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0);
199 	if (fde < 0) {
200 		tst_res(TINFO, "Open Intel PT event failed");
201 		tst_res(TFAIL, "perf trace full mode failed");
202 		return;
203 	}
204 	bufm = NULL;
205 	if (str_mode)
206 		mode = 0;
207 
208 	bufm = create_map(fde, buhsz, mode);
209 }
210 
cleanup(void)211 static void cleanup(void)
212 {
213 	if (fde != -1)
214 		close(fde);
215 
216 	del_map(bufm, buhsz);
217 }
218 
219 static struct tst_test test = {
220 	.test_all = intel_pt_trace_check,
221 	.options = (struct tst_option[]) {
222 		{"m", &str_mode, "Different mode, default is full mode"},
223 		{"e:", &str_exclude_info, "Exclude info, user or kernel"},
224 		{"b", &str_branch_flag, "Disable branch trace"},
225 		{}
226 	},
227 	.min_kver = "4.1",
228 	.setup = setup,
229 	.cleanup = cleanup,
230 	.needs_root = 1,
231 };
232 
233 #else
234 TST_TEST_TCONF("Missing aux_* fields in struct perf_event_mmap_page");
235 #endif /* HAVE_STRUCT_PERF_EVENT_MMAP_PAGE_AUX_HEAD */
236