]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
misc: tegra-profiler: add mixed unwinding mode
authorIgor Nabirushkin <inabirushkin@nvidia.com>
Tue, 25 Mar 2014 09:42:02 +0000 (13:42 +0400)
committerBharat Nihalani <bnihalani@nvidia.com>
Tue, 8 Apr 2014 13:20:52 +0000 (06:20 -0700)
Tegra Profiler: do not break the chains for mixed code -
code with exception-handling tables and code with frame pointers

Bug 1487488

Change-Id: I4fdc6708ef2b4e86b354e5a9daeaa19689abb2dd
Signed-off-by: Igor Nabirushkin <inabirushkin@nvidia.com>
Reviewed-on: http://git-master/r/386241
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Tested-by: Maxim Morin <mmorin@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
12 files changed:
drivers/misc/tegra-profiler/arm_pmu.h
drivers/misc/tegra-profiler/auth.c
drivers/misc/tegra-profiler/auth.h
drivers/misc/tegra-profiler/backtrace.c
drivers/misc/tegra-profiler/backtrace.h
drivers/misc/tegra-profiler/comm.c
drivers/misc/tegra-profiler/eh_unwind.c
drivers/misc/tegra-profiler/hrt.c
drivers/misc/tegra-profiler/main.c
drivers/misc/tegra-profiler/quadd_proc.c
drivers/misc/tegra-profiler/version.h
include/linux/tegra_profiler.h

index a2c6cc57e699daf8dc955e210441768d24a9720c..b0d139a9488a25a9ad98b49c01bcf90eb9718ef6 100644 (file)
@@ -37,7 +37,7 @@ struct quadd_pmu_ctx {
        struct list_head used_events;
 
        int l1_cache_rw;
-       int *current_map;
+       unsigned int *current_map;
 };
 
 #endif /* __ARM_PMU_H */
index 4af43bd21fd10b23b5868506e2e20f1fcfb9626f..7a89ba57f33e38694464cef2816e1f856612b8c0 100644 (file)
@@ -235,7 +235,7 @@ static const struct file_operations auth_fops = {
        .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;
index 42d6d85e7a6e679e0074fe89960e5f28f0d0f524..6ce17396a7c9552da38f507bf7a7bb454021a418 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * 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,
@@ -19,7 +19,7 @@
 
 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);
index 96f9477e88526d05824c7e6b9dbd9c6680062616..5d29b9d67aa3d946534747ab3de4f73f25d41ba0 100644 (file)
@@ -214,6 +214,29 @@ get_user_callchain_fp(struct pt_regs *regs,
        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,
@@ -338,12 +361,52 @@ get_user_callchain_fp_compat(struct pt_regs *regs,
 
        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
@@ -358,7 +421,7 @@ quadd_get_user_callchain(struct pt_regs *regs,
                         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;
 
@@ -367,6 +430,9 @@ quadd_get_user_callchain(struct pt_regs *regs,
        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
@@ -377,14 +443,17 @@ quadd_get_user_callchain(struct pt_regs *regs,
 
        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;
 }
index a5d297c184ea0be8fb292f061cba03c91e0c49cd..97ad59f8a3d1e42c8de85315d43dbbac91bf19c5 100644 (file)
@@ -33,6 +33,9 @@ struct quadd_callchain {
 
        unsigned int unw_method;
        unsigned int unw_rc;
+
+       unsigned long curr_sp;
+       unsigned long curr_fp;
 };
 
 struct quadd_ctx;
index b81b99677554202b62ade853f212172695c828ae..a50ddeed30b0c87127f5685c3350407a9aace2de 100644 (file)
@@ -290,7 +290,7 @@ static ssize_t read_sample(char __user *buffer, size_t max_length)
 
                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;
@@ -483,7 +483,8 @@ device_read(struct file *filp,
            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();
@@ -606,8 +607,8 @@ device_ioctl(struct file *file,
                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;
index b0d6a99b8287207c972d04dac9672198839bd3f7..549b2da490cef25b2769854b2afcf266bc5fdeb0 100644 (file)
@@ -448,7 +448,7 @@ unwind_find_idx(struct extab_info *exidx, u32 addr)
 }
 
 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;
 
@@ -479,9 +479,10 @@ unwind_get_byte(struct unwind_ctrl_block *ctrl, int *err)
 /*
  * 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)
@@ -538,7 +539,7 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
                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++) {
@@ -546,7 +547,7 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
                        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) {
@@ -667,7 +668,7 @@ static int unwind_exec_insn(struct unwind_ctrl_block *ctrl)
  * 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)
@@ -737,7 +738,7 @@ unwind_frame(struct extab_info *exidx,
        }
 
        while (ctrl.entries > 0) {
-               int err = unwind_exec_insn(&ctrl);
+               err = unwind_exec_insn(&ctrl);
                if (err < 0)
                        return err;
 
@@ -792,7 +793,7 @@ unwind_backtrace(struct quadd_callchain *cc,
                 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;
@@ -820,7 +821,7 @@ unwind_backtrace(struct quadd_callchain *cc,
 
                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;
                }
@@ -829,6 +830,9 @@ unwind_backtrace(struct quadd_callchain *cc,
                         where, frame.pc);
 
                quadd_callchain_store(cc, frame.pc);
+
+               cc->curr_sp = frame.sp;
+               cc->curr_fp = frame.fp_arm;
        }
 }
 
index bae0641b897c6ffdb28103a361bc2552a403085c..3b7929546d1d7941080f0b18946525516f7abddf 100644 (file)
@@ -100,7 +100,7 @@ u64 quadd_get_time(void)
 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;
@@ -290,7 +290,7 @@ read_all_sources(struct pt_regs *regs, struct task_struct *task)
                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);
@@ -309,9 +309,10 @@ read_all_sources(struct pt_regs *regs, struct task_struct *task)
                        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;
index f2ada7437e548bfd4d10cfd896cb6cbe2a816f27..dc7fddc3e39c63ee3fa6e40b54ffd3f95cf825b3 100644 (file)
@@ -158,7 +158,7 @@ static inline int is_event_supported(struct source_info *si, int event)
        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];
@@ -172,33 +172,33 @@ static int set_parameters(struct quadd_parameters *param, uid_t *debug_app_uid)
            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;
@@ -215,24 +215,24 @@ static int set_parameters(struct quadd_parameters *param, uid_t *debug_app_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));
@@ -276,7 +276,7 @@ static int set_parameters(struct quadd_parameters *param, uid_t *debug_app_uid)
                }
        }
 
-       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");
@@ -415,6 +415,7 @@ static void get_capabilities(struct quadd_comm_cap *cap)
        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;
 }
index 9f9eee91eee842c64ab26d7273f1521cc071f7dc..d7464f18a9519aa608228339f84a0686b2da382e 100644 (file)
@@ -87,6 +87,8 @@ static int show_capabilities(struct seq_file *f, void *offset)
                   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");
index ad3dd972e53c430d89ea356d588c81e884bd6c4e..aaf239405cf506d12211e05cd299972a4ffb4f63 100644 (file)
@@ -18,7 +18,7 @@
 #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 */
index ef5b7f1494c3fd9e3ac0b1bb345b7363e3d2aec5..2d885de6eb4d238de5f554a5d19a9bd9eb42dbe1 100644 (file)
@@ -19,8 +19,8 @@
 
 #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
@@ -28,6 +28,7 @@
 #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
@@ -35,6 +36,7 @@
 #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
@@ -142,6 +144,7 @@ enum quadd_cpu_mode {
 enum {
        QUADD_UNW_METHOD_FP = 0,
        QUADD_UNW_METHOD_EHT,
+       QUADD_UNW_METHOD_MIXED,
 };
 
 #define QUADD_SAMPLE_URC_SHIFT         1
@@ -162,7 +165,10 @@ enum {
        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;
@@ -297,6 +303,7 @@ enum {
 #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;
@@ -345,6 +352,7 @@ enum {
 #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,