struct list_head used_events;
int l1_cache_rw;
- int *current_map;
+ unsigned int *current_map;
};
#endif /* __ARM_PMU_H */
.release = auth_release,
};
-int quadd_auth_check_debug_flag(const char *package_name)
+int quadd_auth_is_debuggable(const char *package_name)
{
int uid, response_value;
struct quadd_auth_data *data = &auth_ctx.data;
/*
* drivers/misc/tegra-profiler/auth.h
*
- * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
struct quadd_ctx;
-int quadd_auth_check_debug_flag(const char *package_name);
+int quadd_auth_is_debuggable(const char *package_name);
int quadd_auth_is_auth_open(void);
int quadd_auth_init(struct quadd_ctx *quadd_ctx);
return cc->nr;
}
+static unsigned int
+__user_backtrace(struct quadd_callchain *cc)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ unsigned long __user *tail;
+
+ if (!mm)
+ goto out;
+
+ vma = find_vma(mm, cc->curr_sp);
+ if (!vma)
+ goto out;
+
+ tail = (unsigned long __user *)cc->curr_fp;
+
+ while (tail && !((unsigned long)tail & 0x3))
+ tail = user_backtrace(tail, cc, vma);
+
+out:
+ return cc->nr;
+}
+
#ifdef CONFIG_ARM64
static u32 __user *
user_backtrace_compat(u32 __user *tail,
return cc->nr;
}
+
+static unsigned int
+__user_backtrace_compat(struct quadd_callchain *cc)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ u32 __user *tail;
+
+ if (!mm)
+ goto out;
+
+ vma = find_vma(mm, cc->curr_sp);
+ if (!vma)
+ goto out;
+
+ tail = (u32 __user *)cc->curr_fp;
+
+ while (tail && !((unsigned long)tail & 0x3))
+ tail = user_backtrace_compat(tail, cc, vma);
+
+out:
+ return cc->nr;
+}
+
#endif /* CONFIG_ARM64 */
static unsigned int
__get_user_callchain_fp(struct pt_regs *regs,
struct quadd_callchain *cc)
{
+ if (cc->nr > 0) {
+ int nr, nr_prev = cc->nr;
+#ifdef CONFIG_ARM64
+ if (compat_user_mode(regs))
+ nr = __user_backtrace_compat(cc);
+ else
+ nr = __user_backtrace(cc);
+#else
+ nr = __user_backtrace(cc);
+#endif
+ if (nr != nr_prev)
+ cc->unw_method = QUADD_UNW_METHOD_MIXED;
+
+ return nr;
+ }
+
cc->unw_method = QUADD_UNW_METHOD_FP;
#ifdef CONFIG_ARM64
struct quadd_callchain *cc,
struct quadd_ctx *ctx)
{
- int unw_fp, unw_eht, nr = 0;
+ int unw_fp, unw_eht, unw_mix, nr = 0;
unsigned int extra;
struct quadd_parameters *param = &ctx->param;
if (!regs)
return 0;
+ cc->curr_sp = 0;
+ cc->curr_fp = 0;
+
#ifdef CONFIG_ARM64
cc->cs_64 = compat_user_mode(regs) ? 0 : 1;
#else
unw_fp = extra & QUADD_PARAM_EXTRA_BT_FP;
unw_eht = extra & QUADD_PARAM_EXTRA_BT_UNWIND_TABLES;
+ unw_mix = extra & QUADD_PARAM_EXTRA_BT_MIXED;
cc->unw_rc = 0;
if (unw_eht)
nr = quadd_get_user_callchain_ut(regs, cc);
- if (!nr && unw_fp)
- nr = __get_user_callchain_fp(regs, cc);
+ if (unw_fp) {
+ if (!nr || unw_mix)
+ nr = __get_user_callchain_fp(regs, cc);
+ }
return nr;
}
unsigned int unw_method;
unsigned int unw_rc;
+
+ unsigned long curr_sp;
+ unsigned long curr_fp;
};
struct quadd_ctx;
was_read += sizeof(sed);
- ip_size = (sed & QUADD_SAMPLE_ED_IP64) ?
+ ip_size = (sed & QUADD_SED_IP64) ?
sizeof(u64) : sizeof(u32);
length_extra = sample->callchain_nr * ip_size;
loff_t *offset)
{
int err;
- size_t res, samples_counter = 0;
+ ssize_t res;
+ size_t samples_counter = 0;
size_t was_read = 0, min_size;
err = check_access_permission();
break;
case IOCTL_GET_VERSION:
- strcpy(versions.branch, QUADD_MODULE_BRANCH);
- strcpy(versions.version, QUADD_MODULE_VERSION);
+ strcpy((char *)versions.branch, QUADD_MODULE_BRANCH);
+ strcpy((char *)versions.version, QUADD_MODULE_VERSION);
versions.samples_version = QUADD_SAMPLES_VERSION;
versions.io_version = QUADD_IO_VERSION;
}
static unsigned long
-unwind_get_byte(struct unwind_ctrl_block *ctrl, int *err)
+unwind_get_byte(struct unwind_ctrl_block *ctrl, long *err)
{
unsigned long ret, insn_word;
/*
* Execute the current unwind instruction.
*/
-static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
+static long unwind_exec_insn(struct unwind_ctrl_block *ctrl)
{
- int i, err;
+ long err;
+ unsigned int i;
unsigned long insn = unwind_get_byte(ctrl, &err);
if (err < 0)
pr_debug("CMD_REG_TO_SP: vsp = {r%lu}\n", insn & 0x0f);
} else if ((insn & 0xf0) == 0xa0) {
u32 *vsp = (u32 *)(unsigned long)ctrl->vrs[SP];
- int reg;
+ unsigned int reg;
/* pop R4-R[4+bbb] */
for (reg = 4; reg <= 4 + (insn & 7); reg++) {
if (err < 0)
return err;
- pr_debug("CMD_REG_POP: pop {r%d}\n", reg);
+ pr_debug("CMD_REG_POP: pop {r%u}\n", reg);
}
if (insn & 0x08) {
* Unwind a single frame starting with *sp for the symbol at *pc. It
* updates the *pc and *sp with the new values.
*/
-static int
+static long
unwind_frame(struct extab_info *exidx,
struct stackframe *frame,
struct vm_area_struct *vma_sp)
}
while (ctrl.entries > 0) {
- int err = unwind_exec_insn(&ctrl);
+ err = unwind_exec_insn(&ctrl);
if (err < 0)
return err;
vma_sp->vm_start - vma_sp->vm_end);
while (1) {
- int err;
+ long err;
unsigned long where = frame.pc;
struct vm_area_struct *vma_pc;
struct mm_struct *mm = current->mm;
err = unwind_frame(exidx, &frame, vma_sp);
if (err < 0) {
- pr_debug("end unwind, urc: %d\n", err);
+ pr_debug("end unwind, urc: %ld\n", err);
cc->unw_rc = -err;
break;
}
where, frame.pc);
quadd_callchain_store(cc, frame.pc);
+
+ cc->curr_sp = frame.sp;
+ cc->curr_fp = frame.fp_arm;
}
}
static void put_header(void)
{
int nr_events = 0, max_events = QUADD_MAX_COUNTERS;
- unsigned int events[QUADD_MAX_COUNTERS];
+ int events[QUADD_MAX_COUNTERS];
struct quadd_record_data record;
struct quadd_header_data *hdr = &record.hdr;
struct quadd_parameters *param = &hrt.quadd_ctx->param;
return;
if (cc->cs_64)
- extra_data |= QUADD_SAMPLE_ED_IP64;
+ extra_data |= QUADD_SED_IP64;
vec[vec_idx].base = &extra_data;
vec[vec_idx].len = sizeof(extra_data);
vec_idx++;
}
- s->reserved |= cc->unw_method << QUADD_SAMPLE_UNW_METHOD_SHIFT;
+ extra_data |= cc->unw_method << QUADD_SED_UNW_METHOD_SHIFT;
- if (cc->unw_method == QUADD_UNW_METHOD_EHT)
+ if (cc->unw_method == QUADD_UNW_METHOD_EHT ||
+ cc->unw_method == QUADD_UNW_METHOD_MIXED)
s->reserved |= cc->unw_rc << QUADD_SAMPLE_URC_SHIFT;
}
s->callchain_nr = bt_size;
return 0;
}
-static int set_parameters(struct quadd_parameters *param, uid_t *debug_app_uid)
+static int set_parameters(struct quadd_parameters *p, uid_t *debug_app_uid)
{
int i, err;
int pmu_events_id[QUADD_MAX_COUNTERS];
ctx.param.freq != 10000)
return -EINVAL;
- ctx.param.freq = param->freq;
- ctx.param.ma_freq = param->ma_freq;
- ctx.param.backtrace = param->backtrace;
- ctx.param.use_freq = param->use_freq;
- ctx.param.system_wide = param->system_wide;
- ctx.param.power_rate_freq = param->power_rate_freq;
- ctx.param.debug_samples = param->debug_samples;
+ ctx.param.freq = p->freq;
+ ctx.param.ma_freq = p->ma_freq;
+ ctx.param.backtrace = p->backtrace;
+ ctx.param.use_freq = p->use_freq;
+ ctx.param.system_wide = p->system_wide;
+ ctx.param.power_rate_freq = p->power_rate_freq;
+ ctx.param.debug_samples = p->debug_samples;
- for (i = 0; i < ARRAY_SIZE(param->reserved); i++)
- ctx.param.reserved[i] = param->reserved[i];
+ for (i = 0; i < ARRAY_SIZE(p->reserved); i++)
+ ctx.param.reserved[i] = p->reserved[i];
/* Currently only one process */
- if (param->nr_pids != 1)
+ if (p->nr_pids != 1)
return -EINVAL;
rcu_read_lock();
- task = pid_task(find_vpid(param->pids[0]), PIDTYPE_PID);
+ task = pid_task(find_vpid(p->pids[0]), PIDTYPE_PID);
rcu_read_unlock();
if (!task) {
- pr_err("Process not found: %u\n", param->pids[0]);
+ pr_err("Process not found: %u\n", p->pids[0]);
return -ESRCH;
}
pr_info("owner/task uids: %u/%u\n", current_fsuid(), task_uid(task));
if (!capable(CAP_SYS_ADMIN)) {
if (current_fsuid() != task_uid(task)) {
- uid = quadd_auth_check_debug_flag(param->package_name);
+ uid = quadd_auth_is_debuggable((char *)p->package_name);
if (uid < 0) {
pr_err("Error: QuadD security service\n");
return uid;
ctx.collect_kernel_ips = 1;
}
- for (i = 0; i < param->nr_pids; i++)
- ctx.param.pids[i] = param->pids[i];
+ for (i = 0; i < p->nr_pids; i++)
+ ctx.param.pids[i] = p->pids[i];
- ctx.param.nr_pids = param->nr_pids;
+ ctx.param.nr_pids = p->nr_pids;
- for (i = 0; i < param->nr_events; i++) {
- int event = param->events[i];
+ for (i = 0; i < p->nr_events; i++) {
+ int event = p->events[i];
if (ctx.pmu && ctx.pmu_info.nr_supported_events > 0
&& is_event_supported(&ctx.pmu_info, event)) {
- pmu_events_id[nr_pmu++] = param->events[i];
+ pmu_events_id[nr_pmu++] = p->events[i];
pr_info("PMU active event: %s\n",
quadd_get_event_str(event));
} else if (ctx.pl310 &&
ctx.pl310_info.nr_supported_events > 0 &&
is_event_supported(&ctx.pl310_info, event)) {
- pl310_events_id = param->events[i];
+ pl310_events_id = p->events[i];
pr_info("PL310 active event: %s\n",
quadd_get_event_str(event));
}
}
- extra = param->reserved[QUADD_PARAM_IDX_EXTRA];
+ extra = p->reserved[QUADD_PARAM_IDX_EXTRA];
if (extra & QUADD_PARAM_EXTRA_BT_UNWIND_TABLES)
pr_info("unwinding: exception-handling tables\n");
extra |= QUADD_COMM_CAP_EXTRA_BT_UNWIND_TABLES;
extra |= QUADD_COMM_CAP_EXTRA_SUPPORT_AARCH64;
extra |= QUADD_COMM_CAP_EXTRA_SPECIAL_ARCH_MMAP;
+ extra |= QUADD_COMM_CAP_EXTRA_UNWIND_MIXED;
cap->reserved[QUADD_COMM_CAP_IDX_EXTRA] = extra;
}
YES_NO(extra & QUADD_COMM_CAP_EXTRA_SUPPORT_AARCH64));
seq_printf(f, "support special architecture mappings: %s\n",
YES_NO(extra & QUADD_COMM_CAP_EXTRA_SPECIAL_ARCH_MMAP));
+ seq_printf(f, "support mixed unwinding mode: %s\n",
+ YES_NO(extra & QUADD_COMM_CAP_EXTRA_UNWIND_MIXED));
seq_puts(f, "\n");
seq_puts(f, "Supported events:\n");
#ifndef __QUADD_VERSION_H
#define __QUADD_VERSION_H
-#define QUADD_MODULE_VERSION "1.60"
+#define QUADD_MODULE_VERSION "1.61"
#define QUADD_MODULE_BRANCH "Dev"
#endif /* __QUADD_VERSION_H */
#include <linux/ioctl.h>
-#define QUADD_SAMPLES_VERSION 24
-#define QUADD_IO_VERSION 10
+#define QUADD_SAMPLES_VERSION 25
+#define QUADD_IO_VERSION 11
#define QUADD_IO_VERSION_DYNAMIC_RB 5
#define QUADD_IO_VERSION_RB_MAX_FILL_COUNT 6
#define QUADD_IO_VERSION_BT_KERNEL_CTX 8
#define QUADD_IO_VERSION_GET_MMAP 9
#define QUADD_IO_VERSION_BT_UNWIND_TABLES 10
+#define QUADD_IO_VERSION_UNWIND_MIXED 11
#define QUADD_SAMPLE_VERSION_THUMB_MODE_FLAG 17
#define QUADD_SAMPLE_VERSION_GROUP_SAMPLES 18
#define QUADD_SAMPLE_VERSION_BT_UNWIND_TABLES 22
#define QUADD_SAMPLE_VERSION_SUPPORT_IP64 23
#define QUADD_SAMPLE_VERSION_SPECIAL_MMAP 24
+#define QUADD_SAMPLE_VERSION_UNWIND_MIXED 25
#define QUADD_MAX_COUNTERS 32
#define QUADD_MAX_PROCESS 64
enum {
QUADD_UNW_METHOD_FP = 0,
QUADD_UNW_METHOD_EHT,
+ QUADD_UNW_METHOD_MIXED,
};
#define QUADD_SAMPLE_URC_SHIFT 1
QUADD_URC_UNSUPPORTED_PR,
};
-#define QUADD_SAMPLE_ED_IP64 (1 << 0)
+#define QUADD_SED_IP64 (1 << 0)
+
+#define QUADD_SED_UNW_METHOD_SHIFT 1
+#define QUADD_SED_UNW_METHOD_MASK (0x07 << QUADD_SED_UNW_METHOD_SHIFT)
struct quadd_sample_data {
u64 ip;
#define QUADD_PARAM_EXTRA_GET_MMAP (1 << 0)
#define QUADD_PARAM_EXTRA_BT_FP (1 << 1)
#define QUADD_PARAM_EXTRA_BT_UNWIND_TABLES (1 << 2)
+#define QUADD_PARAM_EXTRA_BT_MIXED (1 << 3)
struct quadd_parameters {
u32 freq;
#define QUADD_COMM_CAP_EXTRA_BT_UNWIND_TABLES (1 << 3)
#define QUADD_COMM_CAP_EXTRA_SUPPORT_AARCH64 (1 << 4)
#define QUADD_COMM_CAP_EXTRA_SPECIAL_ARCH_MMAP (1 << 5)
+#define QUADD_COMM_CAP_EXTRA_UNWIND_MIXED (1 << 6)
struct quadd_comm_cap {
u32 pmu:1,