Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7d2d3238 authored by Neil Leeder's avatar Neil Leeder Committed by Matt Wagantall
Browse files

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: default avatarNeil Leeder <nleeder@codeaurora.org>
parent b72ca168
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -26,7 +26,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_ARM64_CPU_SUSPEND)	+= sleep.o suspend.o
arm64-obj-$(CONFIG_CPU_IDLE)		+= cpuidle.o
+1 −0
Original line number Diff line number Diff line
@@ -35,6 +35,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 −0
Original line number Diff line number Diff line
/* 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 −0
Original line number Diff line number Diff line
/* 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>