Perf: arm64: add perf trace user

perf trace user provides a debugfs node to which a marker string
can be written by userspace. This will create an ftrace and perf
entry containing the current value of L1 and L2 enabled
performance counters and the marker string. This allows usermode
programs to write snapshot entries at specific places in their
code, then see the difference in performance counter values
between those locations.

Change-Id: Ib6f6c6e7333f58fd98c4c0b6ed3d3bbf84a8f830
Signed-off-by: Neil Leeder <nleeder@codeaurora.org>
This commit is contained in:
Neil Leeder 2014-10-24 11:39:44 -04:00 committed by Gerrit - the friendly Code Review server
parent b49a34b251
commit 295e31f6fe
4 changed files with 184 additions and 1 deletions

View file

@ -25,7 +25,8 @@ arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o
arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o perf_debug.o \
perf_trace_counters.o
perf_trace_counters.o \
perf_trace_user.o
arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o

View file

@ -38,6 +38,7 @@ static char *descriptions =
"13 Perf: arm64: restore registers after reset\n"
"14 Perf: arm64: stop counters when going into hotplug\n"
"15 Perf: arm64: make debug dir handle exportable\n"
"16 Perf: arm64: add perf trace user\n"
;
static ssize_t desc_read(struct file *fp, char __user *buf,

View file

@ -0,0 +1,96 @@
/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/perf_event.h>
#include <linux/types.h>
#include <linux/tracepoint.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/preempt.h>
#include <linux/stat.h>
#include <asm/uaccess.h>
#define CREATE_TRACE_POINTS
#include "perf_trace_user.h"
#undef TRACE_SYSTEM
#define TRACE_SYSTEM perf_trace_counters
#define TRACE_USER_MAX_BUF_SIZE 100
static ssize_t perf_trace_write(struct file *file,
const char __user *user_string_in,
size_t len, loff_t *ppos)
{
u32 cnten_val;
int rc;
char buf[TRACE_USER_MAX_BUF_SIZE + 1];
ssize_t length;
if (len == 0)
return 0;
length = len > TRACE_USER_MAX_BUF_SIZE ? TRACE_USER_MAX_BUF_SIZE : len;
rc = copy_from_user(buf, user_string_in, length);
if (rc) {
pr_err("%s copy_from_user failed, rc=%d\n", __func__, rc);
return length;
}
/* Remove any trailing newline and make sure string is terminated */
if (buf[length - 1] == '\n')
buf[length - 1] = '\0';
else
buf[length] = '\0';
/*
* Disable preemption to ensure that all the performance counter
* accesses happen on the same cpu
*/
preempt_disable();
/* stop counters, call the trace function, restart them */
asm volatile("mrs %0, pmcntenset_el0" : "=r" (cnten_val));
/* Disable all the counters that were enabled */
asm volatile("msr pmcntenclr_el0, %0" : : "r" (cnten_val));
trace_perf_trace_user(buf, cnten_val);
/* Enable all the counters that were disabled */
asm volatile("msr pmcntenset_el0, %0" : : "r" (cnten_val));
preempt_enable();
return length;
}
static const struct file_operations perf_trace_fops = {
.write = perf_trace_write
};
static int __init init_perf_trace(void)
{
struct dentry *dir;
struct dentry *file;
unsigned int value = 1;
dir = perf_create_debug_dir();
if (!dir)
return -ENOMEM;
file = debugfs_create_file("trace_marker", S_IWUSR | S_IWGRP, dir,
&value, &perf_trace_fops);
if (!file)
return -ENOMEM;
return 0;
}
late_initcall(init_perf_trace);

View file

@ -0,0 +1,85 @@
/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#if !defined(_PERF_TRACE_USER_H_) || defined(TRACE_HEADER_MULTI_READ)
#define _PERF_TRACE_USER_H_
#undef TRACE_SYSTEM
#define TRACE_SYSTEM perf_trace_counters
#include <linux/tracepoint.h>
#define CNTENSET_CC 0x80000000
#define NUM_L1_CTRS 4
TRACE_EVENT(perf_trace_user,
TP_PROTO(char *string, u32 cnten_val),
TP_ARGS(string, cnten_val),
TP_STRUCT__entry(
__field(u32, cctr)
__field(u32, ctr0)
__field(u32, ctr1)
__field(u32, ctr2)
__field(u32, ctr3)
__field(u32, lctr0)
__field(u32, lctr1)
__string(user_string, string)
),
TP_fast_assign(
u32 cnt;
u32 l1_cnts[NUM_L1_CTRS];
int i;
if (cnten_val & CNTENSET_CC) {
/* Read value */
asm volatile("mrs %0, pmccntr_el0" : "=r" (cnt));
__entry->cctr = cnt;
} else
__entry->cctr = 0;
for (i = 0; i < NUM_L1_CTRS; i++) {
if (cnten_val & (1 << i)) {
/* Select */
asm volatile("msr pmselr_el0, %0"
: : "r" (i));
isb();
/* Read value */
asm volatile("mrs %0, pmxevcntr_el0"
: "=r" (cnt));
l1_cnts[i] = cnt;
} else {
l1_cnts[i] = 0;
}
}
__entry->ctr0 = l1_cnts[0];
__entry->ctr1 = l1_cnts[1];
__entry->ctr2 = l1_cnts[2];
__entry->ctr3 = l1_cnts[3];
__entry->lctr0 = 0;
__entry->lctr1 = 0;
__assign_str(user_string, string);
),
TP_printk("CCNTR: %u, CTR0: %u, CTR1: %u, CTR2: %u, CTR3: %u, L2CTR0: %u, L2CTR1: %u, MSG=%s",
__entry->cctr, __entry->ctr0, __entry->ctr1,
__entry->ctr2, __entry->ctr3,
__entry->lctr0, __entry->lctr1,
__get_str(user_string)
)
);
#endif
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH ../../arch/arm64/kernel
#define TRACE_INCLUDE_FILE perf_trace_user
#include <trace/define_trace.h>