android13/external/ltp/testcases/kernel/tracing/pt_test/pt_test.c

236 lines
5.5 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2018 Intel Corporation
* Author: Ammy Yi (ammy.yi@intel.com)
*/
/*
* This test will check if Intel PT(Intel Processer Trace) is working.
*
* Intel CPU of 5th-generation Core (Broadwell) or newer is required for the test.
*
* kconfig requirement: CONFIG_PERF_EVENTS
*/
#include <sched.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "tst_test.h"
#include "lapi/syscalls.h"
#include "config.h"
#ifdef HAVE_STRUCT_PERF_EVENT_MMAP_PAGE_AUX_HEAD
# include <linux/perf_event.h>
#define PAGESIZE 4096
#define INTEL_PT_MEMSIZE (17*PAGESIZE)
#define BIT(nr) (1UL << (nr))
#define INTEL_PT_PATH "/sys/devices/intel_pt"
#define INTEL_PT_PMU_TYPE "/sys/devices/intel_pt/type"
#define INTEL_PT_FORMAT_TSC "/sys/devices/intel_pt/format/tsc"
#define INTEL_PT_FORMAT_NRT "/sys/devices/intel_pt/format/noretcomp"
//Intel PT event handle
int fde = -1;
//map head and size
uint64_t **bufm;
long buhsz;
static char *str_mode;
static char *str_exclude_info;
static char *str_branch_flag;
int mode = 1;
static uint64_t **create_map(int fde, long bufsize, int flag)
{
uint64_t **buf_ev;
int pro_flag;
struct perf_event_mmap_page *pc;
buf_ev = SAFE_MALLOC(2*sizeof(uint64_t *));
buf_ev[0] = NULL;
buf_ev[1] = NULL;
if (flag == 1) {
tst_res(TINFO, "Memory will be r/w for full trace mode");
pro_flag = PROT_READ | PROT_WRITE;
} else {
tst_res(TINFO, "Memory will be r only for snapshot mode");
pro_flag = PROT_READ;
}
buf_ev[0] = SAFE_MMAP(NULL, INTEL_PT_MEMSIZE, PROT_READ | PROT_WRITE,
MAP_SHARED, fde, 0);
tst_res(TINFO, "Open Intel PT event failed");
pc = (struct perf_event_mmap_page *)buf_ev[0];
pc->aux_offset = INTEL_PT_MEMSIZE;
pc->aux_size = bufsize;
buf_ev[1] = SAFE_MMAP(NULL, bufsize, pro_flag,
MAP_SHARED, fde, INTEL_PT_MEMSIZE);
return buf_ev;
}
int intel_pt_pmu_value(char *dir)
{
char *value;
int val = 0;
char delims[] = ":";
SAFE_FILE_SCANF(dir, "%m[^\n]", &value);
if (strstr(value, delims) == NULL) {
val = atoi(value);
} else {
strsep(&value, delims);
val = atoi(value);
}
return val;
}
static void del_map(uint64_t **buf_ev, long bufsize)
{
if (buf_ev) {
if (buf_ev[0])
munmap(buf_ev[0], INTEL_PT_MEMSIZE);
if (buf_ev[1])
munmap(buf_ev[1], bufsize);
}
free(buf_ev);
}
static void intel_pt_trace_check(void)
{
uint64_t aux_head = 0;
struct perf_event_mmap_page *pmp;
/* enable tracing */
SAFE_IOCTL(fde, PERF_EVENT_IOC_RESET);
SAFE_IOCTL(fde, PERF_EVENT_IOC_ENABLE);
/* stop tracing */
SAFE_IOCTL(fde, PERF_EVENT_IOC_DISABLE);
/* check if there is some trace generated */
pmp = (struct perf_event_mmap_page *)bufm[0];
aux_head = *(volatile uint64_t *)&pmp->aux_head;
if (aux_head == 0) {
tst_res(TFAIL, "There is no trace");
return;
}
tst_res(TPASS, "perf trace test passed");
}
static int is_affected_by_erratum_BDM106(void)
{
int family = -1, model = -1;
if (FILE_LINES_SCANF("/proc/cpuinfo", "cpu family%*s%d", &family)
|| family != 6)
return 0;
if (!FILE_LINES_SCANF("/proc/cpuinfo", "model%*s%d", &model)) {
tst_res(TINFO, "Intel FAM6 model %d", model);
switch (model) {
case 0x3D: /* INTEL_FAM6_BROADWELL */
/* fallthrough */
case 0x47: /* INTEL_FAM6_BROADWELL_G */
/* fallthrough */
case 0x4F: /* INTEL_FAM6_BROADWELL_X */
/* fallthrough */
case 0x56: /* INTEL_FAM6_BROADWELL_D */
return 1;
}
}
return 0;
}
static void setup(void)
{
struct perf_event_attr attr = {};
buhsz = 2 * PAGESIZE;
if (access(INTEL_PT_PATH, F_OK)) {
tst_brk(TCONF,
"Requires Intel Core 5th+ generation (Broadwell and newer) and CONFIG_PERF_EVENTS enabled");
}
/* set attr for Intel PT trace */
attr.type = intel_pt_pmu_value(INTEL_PT_PMU_TYPE);
attr.read_format = PERF_FORMAT_ID | PERF_FORMAT_TOTAL_TIME_RUNNING |
PERF_FORMAT_TOTAL_TIME_ENABLED;
attr.disabled = 1;
attr.config = BIT(intel_pt_pmu_value(INTEL_PT_FORMAT_TSC)) |
BIT(intel_pt_pmu_value(INTEL_PT_FORMAT_NRT));
attr.size = sizeof(struct perf_event_attr);
attr.mmap = 1;
if (str_branch_flag) {
if (is_affected_by_erratum_BDM106()) {
tst_brk(TCONF, "erratum BDM106 disallows not "
"setting BRANCH_EN on this CPU");
}
tst_res(TINFO, "Intel PT will disable branch trace");
attr.config |= 1;
}
attr.exclude_kernel = 0;
attr.exclude_user = 0;
if (str_exclude_info) {
if (!strcmp(str_exclude_info, "user")) {
tst_res(TINFO, "Intel PT will exclude user trace");
attr.exclude_user = 1;
} else if (!strcmp(str_exclude_info, "kernel")) {
tst_res(TINFO, "Intel PT will exclude kernel trace");
attr.exclude_kernel = 1;
} else {
tst_brk(TBROK, "Invalid -e '%s'", str_exclude_info);
}
}
/* only get trace for own pid */
fde = tst_syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0);
if (fde < 0) {
tst_res(TINFO, "Open Intel PT event failed");
tst_res(TFAIL, "perf trace full mode failed");
return;
}
bufm = NULL;
if (str_mode)
mode = 0;
bufm = create_map(fde, buhsz, mode);
}
static void cleanup(void)
{
if (fde != -1)
close(fde);
del_map(bufm, buhsz);
}
static struct tst_test test = {
.test_all = intel_pt_trace_check,
.options = (struct tst_option[]) {
{"m", &str_mode, "-m different mode, default is full mode"},
{"e:", &str_exclude_info, "-e exclude info, user or kernel"},
{"b", &str_branch_flag, "-b if disable branch trace"},
{}
},
.min_kver = "4.1",
.setup = setup,
.cleanup = cleanup,
.needs_root = 1,
};
#else
TST_TEST_TCONF("Missing aux_* fields in struct perf_event_mmap_page");
#endif /* HAVE_STRUCT_PERF_EVENT_MMAP_PAGE_AUX_HEAD */