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

Commit b49a34b2 authored by Neil Leeder's avatar Neil Leeder Committed by Gerrit - the friendly Code Review server
Browse files

msm: perf: 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: I2500268cdb58998d34480dac89be0b894f8b7232
Signed-off-by: default avatarNeil Leeder <nleeder@codeaurora.org>
parent 676f8c4b
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@ endif
obj-$(CONFIG_USE_OF) += board-dt.o

obj-y += acpuclock.o
obj-$(CONFIG_HW_PERF_EVENTS) += perf_trace_counters.o
obj-$(CONFIG_HW_PERF_EVENTS) += perf_trace_counters.o perf_trace_user.o
obj-$(CONFIG_ARCH_MSM_KRAIT) += msm-krait-l2-accessors.o perf_event_msm_krait_l2.o
obj-$(CONFIG_ARCH_MSM_KRAIT) += krait-scm.o
obj-$(CONFIG_DEBUG_FS) += perf_debug.o
+1 −0
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ static char *descriptions =
	"29 msm: perf: Refine disable/enable in tracecounters\n"
	"30 Perf: reset pmu enables when starting perf\n"
	"31 Perf: make debug dir handle exportable\n"
	"32 msm: perf: add perf trace user\n"
;

static ssize_t desc_read(struct file *fp, char __user *buf,
+127 −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
u32 perf_num_l2ctrs;

static int perf_trace_write(struct file *file,
			    const char __user *user_string_in,
			    size_t len, loff_t *ppos)
{
	u32 cnten_val;
	u32 bit;
	u32 cpu;
	u32 num_cores = nr_cpu_ids;
	u32 l2_enmask;
	u32 l2_cnten_val;
	unsigned long idx;
	int i;
	int rc;
	char buf[TRACE_USER_MAX_BUF_SIZE + 1];
	int 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);

	/* 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();
	cpu = smp_processor_id();

	/* stop counters, call the trace function, restart them */

	/* Read PMCNTENSET */
	asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r"(cnten_val));
	/* Disable all the counters that were enabled */
	asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r"(cnten_val));

	l2_cnten_val = get_l2_indirect_reg(L2PMCNTENSET);
	l2_enmask = 0;
	for (i = 0; i < NUM_L2_PERCPU; i++) {
		/*
		 * Assign L2 counters to cores sequentially
		 * starting from zero. A core could have
		 * multiple L2 counters allocated if # L2
		 * counters is more than the # cores
		 */
		idx = cpu + (num_cores * i);
		bit = BIT(idx);
		if (idx < perf_num_l2ctrs && (l2_cnten_val & bit)) {
			/* Disable */
			set_l2_indirect_reg(L2PMCNTENCLR, bit);
			l2_enmask |= bit;
		}
	}
	trace_perf_trace_user(buf, cnten_val, l2_enmask);

	/* Enable L2*/
	set_l2_indirect_reg(L2PMCNTENSET, l2_enmask);

	/* Enable all the counters that were disabled */
	asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "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)
{
	u32 val;
	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;

	val = get_l2_indirect_reg(L2PMCR);
	perf_num_l2ctrs = ((val >> 11) & 0x1f) + 1;

	return 0;
}

late_initcall(init_perf_trace);
+104 −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.
 */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM perf_trace_counters

#if !defined(_PERF_TRACE_USER_H_) || defined(TRACE_HEADER_MULTI_READ)
#define _PERF_TRACE_USER_H_

#include <linux/cpumask.h>
#include <linux/tracepoint.h>
#include <mach/msm-krait-l2-accessors.h>

#define CNTENSET_CC    0x80000000
#define NUM_L1_CTRS             4
#define NUM_L2_PERCPU           2

extern u32 perf_num_l2ctrs;

TRACE_EVENT(perf_trace_user,
	TP_PROTO(char *string, u32 cnten_val, unsigned long l2_enmask),
	TP_ARGS(string, cnten_val, l2_enmask),

	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];
		u32 l2_cnts[NUM_L2_PERCPU];
		unsigned long idx;
		int i;

		if (cnten_val & CNTENSET_CC) {
			/* Read value */
			asm volatile("mrc p15, 0, %0, c9, c13, 0"
				     : "=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("mcr p15, 0, %0, c9, c12, 5"
					     : : "r"(i));
				/* Read value */
				asm volatile("mrc p15, 0, %0, c9, c13, 2"
					     : "=r"(cnt));
				l1_cnts[i] = cnt;
			} else {
				l1_cnts[i] = 0;
			}
		}

		/* L2 counters */
		i = 0;
		l2_cnts[0] = 0;
		l2_cnts[1] = 0;
		for_each_set_bit(idx, &l2_enmask, perf_num_l2ctrs) {
			/*
			 * L2PMXEVCNTR address offsets increment by 16,
			 * so multiply idx by 16 to get the next reg
			 */
			l2_cnts[i++] = get_l2_indirect_reg(
				(idx * 16) + IA_L2PMXEVCNTR_BASE);
		}
		__entry->ctr0 = l1_cnts[0];
		__entry->ctr1 = l1_cnts[1];
		__entry->ctr2 = l1_cnts[2];
		__entry->ctr3 = l1_cnts[3];
		__entry->lctr0 = l2_cnts[0];
		__entry->lctr1 = l2_cnts[1];
		__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/arm/mach-msm
#define TRACE_INCLUDE_FILE perf_trace_user
#include <trace/define_trace.h>