mirror of
https://github.com/team-infusion-developers/android_kernel_samsung_msm8976.git
synced 2024-11-07 04:09:21 +00:00
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:
parent
b49a34b251
commit
295e31f6fe
4 changed files with 184 additions and 1 deletions
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
96
arch/arm64/kernel/perf_trace_user.c
Normal file
96
arch/arm64/kernel/perf_trace_user.c
Normal 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);
|
85
arch/arm64/kernel/perf_trace_user.h
Normal file
85
arch/arm64/kernel/perf_trace_user.h
Normal 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>
|
Loading…
Reference in a new issue