From c3c4e00a8ddfcb888f640fc2aec3b69f53eae515 Mon Sep 17 00:00:00 2001 From: Igor Nabirushkin Date: Tue, 17 Apr 2018 16:20:12 +0300 Subject: misc: tegra-profiler: sample multiple processes - Sample multiple processes. Add a few new modes. - Fix possible crash in d_path() during multiple execs. Bug 2104957 Bug 2100416 Change-Id: Iab1568de9365e1c719b45c65f490bbd212995b69 Signed-off-by: Igor Nabirushkin Reviewed-on: https://git-master.nvidia.com/r/1696670 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Dmitry Antipov Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/misc/tegra-profiler/arm_pmu.h | 2 +- drivers/misc/tegra-profiler/comm.c | 10 +- drivers/misc/tegra-profiler/comm.h | 6 +- drivers/misc/tegra-profiler/hrt.c | 117 ++++++++++----- drivers/misc/tegra-profiler/hrt.h | 11 +- drivers/misc/tegra-profiler/main.c | 101 +++++++------ drivers/misc/tegra-profiler/mmap.c | 250 ++++++++++++++++++------------- drivers/misc/tegra-profiler/mmap.h | 10 +- drivers/misc/tegra-profiler/quadd.h | 47 +++++- drivers/misc/tegra-profiler/quadd_proc.c | 2 +- drivers/misc/tegra-profiler/version.h | 2 +- include/linux/tegra_profiler.h | 68 +++++---- 12 files changed, 382 insertions(+), 244 deletions(-) diff --git a/drivers/misc/tegra-profiler/arm_pmu.h b/drivers/misc/tegra-profiler/arm_pmu.h index 7e6558414..14aa684cb 100644 --- a/drivers/misc/tegra-profiler/arm_pmu.h +++ b/drivers/misc/tegra-profiler/arm_pmu.h @@ -34,7 +34,7 @@ struct quadd_pmu_event_info { struct quadd_arch_info { int type; - int pmuver; + unsigned int pmuver; unsigned pmuver_is_set:1; char name[QUADD_ARCH_NAME_MAX]; diff --git a/drivers/misc/tegra-profiler/comm.c b/drivers/misc/tegra-profiler/comm.c index 0f9cf2374..fb78e2542 100644 --- a/drivers/misc/tegra-profiler/comm.c +++ b/drivers/misc/tegra-profiler/comm.c @@ -46,6 +46,7 @@ struct quadd_ring_buffer { }; struct quadd_comm_ctx { + struct quadd_ctx *ctx; struct quadd_comm_control_interface *control; atomic_t active; @@ -387,7 +388,7 @@ ready_to_profile(void) if (!comm_ctx.params_ok) return 0; - if (quadd_mode_is_sampling()) { + if (quadd_mode_is_sampling(comm_ctx.ctx)) { for_each_possible_cpu(cpuid) { is_cpu_present = comm_ctx.control->is_cpu_present(cpuid); @@ -457,7 +458,7 @@ device_ioctl(struct file *file, } if (!comm_ctx.params_ok || - !quadd_mode_is_sampling()) { + !quadd_mode_is_sampling(comm_ctx.ctx)) { pr_err("error: incorrect setup ioctl\n"); err = -EPERM; goto error_out; @@ -932,10 +933,13 @@ static int comm_init(void) } struct quadd_comm_data_interface * -quadd_comm_events_init(struct quadd_comm_control_interface *control) +quadd_comm_events_init(struct quadd_ctx *ctx, + struct quadd_comm_control_interface *control) { int err; + comm_ctx.ctx = ctx; + err = comm_init(); if (err < 0) return ERR_PTR(err); diff --git a/drivers/misc/tegra-profiler/comm.h b/drivers/misc/tegra-profiler/comm.h index eca48a9b6..b3755a82d 100644 --- a/drivers/misc/tegra-profiler/comm.h +++ b/drivers/misc/tegra-profiler/comm.h @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-profiler/comm.h * - * Copyright (c) 2013-2017, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2013-2018, 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,6 +19,7 @@ #include +struct quadd_ctx; struct quadd_record_data; struct quadd_comm_cap; struct quadd_module_state; @@ -85,7 +86,8 @@ struct quadd_comm_data_interface { }; struct quadd_comm_data_interface * -quadd_comm_events_init(struct quadd_comm_control_interface *control); +quadd_comm_events_init(struct quadd_ctx *ctx, + struct quadd_comm_control_interface *control); void quadd_comm_events_exit(void); #endif /* __QUADD_COMM_H__ */ diff --git a/drivers/misc/tegra-profiler/hrt.c b/drivers/misc/tegra-profiler/hrt.c index ec45b0f4b..126c7f579 100644 --- a/drivers/misc/tegra-profiler/hrt.c +++ b/drivers/misc/tegra-profiler/hrt.c @@ -210,10 +210,18 @@ static void put_header(int cpuid) hdr->reserved |= QUADD_HDR_HAS_CPUID; - if (quadd_mode_is_trace_all()) - hdr->reserved |= QUADD_HDR_MODE_TRACE_ALL; - if (quadd_mode_is_sampling()) + if (quadd_mode_is_sampling(ctx)) hdr->reserved |= QUADD_HDR_MODE_SAMPLING; + if (quadd_mode_is_tracing(ctx)) + hdr->reserved |= QUADD_HDR_MODE_TRACING; + if (quadd_mode_is_sample_all(ctx)) + hdr->reserved |= QUADD_HDR_MODE_SAMPLE_ALL; + if (quadd_mode_is_trace_all(ctx)) + hdr->reserved |= QUADD_HDR_MODE_TRACE_ALL; + if (quadd_mode_is_sample_tree(ctx)) + hdr->reserved |= QUADD_HDR_MODE_SAMPLE_TREE; + if (quadd_mode_is_trace_tree(ctx)) + hdr->reserved |= QUADD_HDR_MODE_TRACE_TREE; if (pmu) nr_events += pmu->get_current_events(cpuid, events + nr_events, @@ -373,6 +381,14 @@ get_stack_offset(struct task_struct *task, return vma->vm_end - sp; } +static inline void +validate_um_for_task(struct task_struct *task, + pid_t param_pid, struct quadd_unw_methods *um) +{ + if (task_tgid_nr(task) != param_pid) + um->ut = um->dwarf = 0; +} + static void read_all_sources(struct pt_regs *regs, struct task_struct *task, int is_sched) { @@ -436,6 +452,7 @@ read_all_sources(struct pt_regs *regs, struct task_struct *task, int is_sched) if (ctx->param.backtrace) { cc->um = hrt.um; + validate_um_for_task(task, ctx->param.pids[0], &cc->um); bt_size = quadd_get_user_callchain(&event_ctx, cc, ctx); if (bt_size > 0) { @@ -538,55 +555,69 @@ read_all_sources(struct pt_regs *regs, struct task_struct *task, int is_sched) } static inline int -is_profile_process(struct task_struct *task) +is_profile_process(struct task_struct *task, int is_trace) { - int i; - pid_t pid, profile_pid; + pid_t pid; struct quadd_ctx *ctx = hrt.quadd_ctx; - if (!task) - return 0; + pid = ctx->param.pids[0]; + + if (task_tgid_nr(task) == pid) + return 1; + + if ((is_trace && quadd_mode_is_trace_tree(ctx)) || + (!is_trace && quadd_mode_is_sample_tree(ctx))) { + struct task_struct *p; - pid = task->tgid; + read_lock(&tasklist_lock); + for (p = task; p != &init_task;) { + if (task_pid_nr(p) == pid) { + read_unlock(&tasklist_lock); + return 1; + } - for (i = 0; i < ctx->param.nr_pids; i++) { - profile_pid = ctx->param.pids[i]; - if (profile_pid == pid) - return 1; + rcu_read_lock(); + p = rcu_dereference(p->real_parent); + rcu_read_unlock(); + } + read_unlock(&tasklist_lock); } return 0; } static inline int -is_sample_process(struct task_struct *task) +is_swapper_task(struct task_struct *task) { - return (quadd_mode_is_sampling() && - is_profile_process(task)); + return task_pid_nr(task) == 0; } static inline int -is_swapper_task(struct task_struct *task) +validate_task(struct task_struct *task) { - if (task->pid == 0) - return 1; + return task && task->mm && !is_swapper_task(task); +} - return 0; +static inline int +is_sample_process(struct task_struct *task) +{ + struct quadd_ctx *ctx = hrt.quadd_ctx; + + if (!validate_task(task) || !quadd_mode_is_sampling(ctx)) + return 0; + + return (quadd_mode_is_sample_all(ctx) || is_profile_process(task, 0)); } static inline int is_trace_process(struct task_struct *task) { - if (!task) - return 0; + struct quadd_ctx *ctx = hrt.quadd_ctx; - if (is_swapper_task(task)) + if (!validate_task(task) || !quadd_mode_is_tracing(ctx)) return 0; - if (quadd_mode_is_trace_all()) - return 1; - - return is_profile_process(task); + return (quadd_mode_is_trace_all(ctx) || is_profile_process(task, 1)); } static int @@ -702,16 +733,13 @@ void __quadd_task_sched_out(struct task_struct *prev, void __quadd_event_mmap(struct vm_area_struct *vma) { - struct quadd_parameters *param; - - if (likely(!atomic_read(&hrt.active))) + if (likely(!atomic_read(&hrt.mmap_active))) return; if (!is_sample_process(current)) return; - param = &hrt.quadd_ctx->param; - quadd_process_mmap(vma, param->pids[0]); + quadd_process_mmap(vma, current); } static void reset_cpu_ctx(void) @@ -733,7 +761,6 @@ static void reset_cpu_ctx(void) int quadd_hrt_start(void) { - int err; int cpuid; u64 period; long freq; @@ -787,14 +814,16 @@ int quadd_hrt_start(void) put_header(cpuid); } - if (quadd_mode_is_sampling()) { - if (extra & QUADD_PARAM_EXTRA_GET_MMAP) { - err = quadd_get_current_mmap(param->pids[0]); - if (err) { - pr_err("error: quadd_get_current_mmap\n"); - return err; - } - } + atomic_set(&hrt.mmap_active, 1); + + /* Enable the mmap events processing before quadd_get_mmaps() + * otherwise we can miss some events. + */ + smp_wmb(); + + if (quadd_mode_is_sampling(ctx)) { + if (extra & QUADD_PARAM_EXTRA_GET_MMAP) + quadd_get_mmaps(ctx); if (ctx->pl310) ctx->pl310->start(); @@ -802,6 +831,9 @@ int quadd_hrt_start(void) quadd_ma_start(&hrt); + /* Enable the sampling only after quadd_get_mmaps() */ + smp_wmb(); + atomic_set(&hrt.active, 1); pr_info("Start hrt: freq/period: %ld/%llu\n", freq, period); @@ -822,6 +854,7 @@ void quadd_hrt_stop(void) quadd_ma_stop(&hrt); atomic_set(&hrt.active, 0); + atomic_set(&hrt.mmap_active, 0); atomic64_set(&hrt.counter_samples, 0); atomic64_set(&hrt.skipped_samples, 0); @@ -870,7 +903,9 @@ struct quadd_hrt_ctx *quadd_hrt_init(struct quadd_ctx *ctx) struct quadd_cpu_context *cpu_ctx; hrt.quadd_ctx = ctx; + atomic_set(&hrt.active, 0); + atomic_set(&hrt.mmap_active, 0); freq = ctx->param.freq; freq = max_t(long, QUADD_HRT_MIN_FREQ, freq); diff --git a/drivers/misc/tegra-profiler/hrt.h b/drivers/misc/tegra-profiler/hrt.h index 2db0ac8e6..98b591d1e 100644 --- a/drivers/misc/tegra-profiler/hrt.h +++ b/drivers/misc/tegra-profiler/hrt.h @@ -50,6 +50,7 @@ struct quadd_hrt_ctx { struct quadd_ctx *quadd_ctx; atomic_t active; + atomic_t mmap_active; atomic_t nr_active_all_core; atomic64_t counter_samples; @@ -62,11 +63,11 @@ struct quadd_hrt_ctx { unsigned long rss_size_prev; struct timecounter *tc; - int use_arch_timer; - int arch_timer_user_access; + unsigned int use_arch_timer:1; + unsigned int arch_timer_user_access:1; struct quadd_unw_methods um; - int get_stack_offset; + unsigned int get_stack_offset:1; }; struct task_struct; @@ -76,8 +77,8 @@ struct quadd_event_context { struct task_struct *task; struct pt_regs *regs; - int user_mode; - int is_sched; + unsigned int user_mode:1; + unsigned int is_sched:1; }; #define QUADD_HRT_MIN_FREQ 100 diff --git a/drivers/misc/tegra-profiler/main.c b/drivers/misc/tegra-profiler/main.c index 1e5a5d3d2..8d76225c5 100644 --- a/drivers/misc/tegra-profiler/main.c +++ b/drivers/misc/tegra-profiler/main.c @@ -60,16 +60,6 @@ static struct quadd_comm_cap_for_cpu *get_capabilities_for_cpu_int(int cpuid) return &per_cpu(per_cpu_caps, cpuid); } -int quadd_mode_is_trace_all(void) -{ - return ctx.mode_is_trace_all; -} - -int quadd_mode_is_sampling(void) -{ - return ctx.mode_is_sampling; -} - int tegra_profiler_try_lock(void) { return atomic_cmpxchg(&ctx.tegra_profiler_lock, 0, 1); @@ -92,14 +82,12 @@ static int start(void) } if (!atomic_cmpxchg(&ctx.started, 0, 1)) { - preempt_disable(); - - if (quadd_mode_is_sampling()) { + if (quadd_mode_is_sampling(&ctx)) { if (ctx.pmu) { err = ctx.pmu->enable(); if (err) { pr_err("error: pmu enable\n"); - goto errout_preempt; + goto errout; } } @@ -107,7 +95,7 @@ static int start(void) err = ctx.pl310->enable(); if (err) { pr_err("error: pl310 enable\n"); - goto errout_preempt; + goto errout; } } } @@ -117,11 +105,9 @@ static int start(void) err = quadd_hrt_start(); if (err) { pr_err("error: hrt start\n"); - goto errout_preempt; + goto errout; } - preempt_enable(); - err = quadd_power_clk_start(); if (err < 0) { pr_err("error: power_clk start\n"); @@ -131,9 +117,6 @@ static int start(void) return 0; -errout_preempt: - preempt_enable(); - errout: atomic_set(&ctx.started, 0); tegra_profiler_unlock(); @@ -268,10 +251,10 @@ static int verify_app(struct quadd_parameters *p, uid_t task_uid) static int set_parameters(struct quadd_parameters *p) { - int i, err, nr_pl310 = 0; + int i, err = 0, nr_pl310 = 0; uid_t task_uid, current_uid; struct quadd_event *pl310_events; - struct task_struct *task; + struct task_struct *task = NULL; u64 *low_addr_p; u32 extra; @@ -279,49 +262,61 @@ set_parameters(struct quadd_parameters *p) ctx.mode_is_sampling = extra & QUADD_PARAM_EXTRA_SAMPLING ? 1 : 0; + ctx.mode_is_tracing = + extra & QUADD_PARAM_EXTRA_TRACING ? 1 : 0; + ctx.mode_is_sample_all = + extra & QUADD_PARAM_EXTRA_SAMPLE_ALL_TASKS ? 1 : 0; ctx.mode_is_trace_all = p->trace_all_tasks; - - pr_info("flag: sampling: %s\n", - ctx.mode_is_sampling ? "yes" : "no"); - pr_info("flag: trace all: %s\n", - ctx.mode_is_trace_all ? "yes" : "no"); - - if (ctx.mode_is_trace_all && !capable(CAP_SYS_ADMIN)) { - pr_err("error: trace all tasks mode is allowed only for root\n"); + ctx.mode_is_sample_tree = + extra & QUADD_PARAM_EXTRA_SAMPLE_TREE ? 1 : 0; + ctx.mode_is_trace_tree = + extra & QUADD_PARAM_EXTRA_TRACE_TREE ? 1 : 0; + + pr_info("flags: s/t/sa/ta/st/tt: %u/%u/%u/%u/%u/%u\n", + ctx.mode_is_sampling, + ctx.mode_is_tracing, + ctx.mode_is_sample_all, + ctx.mode_is_trace_all, + ctx.mode_is_sample_tree, + ctx.mode_is_trace_tree); + + if ((ctx.mode_is_trace_all || ctx.mode_is_sample_all) && + !capable(CAP_SYS_ADMIN)) { + pr_err("error: \"all tasks\" modes are allowed only for root\n"); return -EACCES; } p->package_name[sizeof(p->package_name) - 1] = '\0'; ctx.param = *p; - if (!ctx.mode_is_trace_all || ctx.mode_is_sampling) { - if (!validate_freq(p->freq)) { + current_uid = from_kuid(&init_user_ns, current_fsuid()); + pr_info("owner uid: %u\n", current_uid); + + if ((ctx.mode_is_tracing && !ctx.mode_is_trace_all) || + (ctx.mode_is_sampling && !ctx.mode_is_sample_all)) { + if (ctx.mode_is_sampling && !validate_freq(p->freq)) { pr_err("error: incorrect frequency: %u\n", p->freq); return -EINVAL; } - /* Currently only one process */ + /* Currently only first process */ if (p->nr_pids != 1) return -EINVAL; - rcu_read_lock(); - task = pid_task(find_vpid(p->pids[0]), PIDTYPE_PID); - rcu_read_unlock(); + task = get_pid_task(find_vpid(p->pids[0]), PIDTYPE_PID); if (!task) { pr_err("error: process not found: %u\n", p->pids[0]); return -ESRCH; } - current_uid = from_kuid(&init_user_ns, current_fsuid()); task_uid = from_kuid(&init_user_ns, task_uid(task)); - - pr_info("owner/task uids: %u/%u\n", current_uid, task_uid); + pr_info("task uid: %u\n", task_uid); if (!capable(CAP_SYS_ADMIN)) { if (current_uid != task_uid) { err = verify_app(p, task_uid); if (err < 0) - return err; + goto out_put_task; } ctx.collect_kernel_ips = 0; } else { @@ -335,8 +330,10 @@ set_parameters(struct quadd_parameters *p) type = event->type; id = event->id; - if (type != QUADD_EVENT_TYPE_HARDWARE) - return -EINVAL; + if (type != QUADD_EVENT_TYPE_HARDWARE) { + err = -EINVAL; + goto out_put_task; + } if (ctx.pl310 && ctx.pl310_info.nr_supp_events > 0 && @@ -348,12 +345,14 @@ set_parameters(struct quadd_parameters *p) if (nr_pl310++ > 1) { pr_err("error: multiply pl310 events\n"); - return -EINVAL; + err = -EINVAL; + goto out_put_task; } } else { pr_err("Bad event: %s\n", quadd_get_hw_event_str(id)); - return -EINVAL; + err = -EINVAL; + goto out_put_task; } } @@ -365,7 +364,7 @@ set_parameters(struct quadd_parameters *p) pl310_events, 1); if (err) { pr_info("pl310 set_parameters: error\n"); - return err; + goto out_put_task; } ctx.pl310_info.active = 1; } else { @@ -381,12 +380,16 @@ set_parameters(struct quadd_parameters *p) err = quadd_unwind_start(task); if (err) - return err; + goto out_put_task; } pr_info("New parameters have been applied\n"); - return 0; +out_put_task: + if (task) + put_task_struct(task); + + return err; } static void @@ -734,7 +737,7 @@ static int __init quadd_module_init(void) return err; } - ctx.comm = quadd_comm_events_init(&control); + ctx.comm = quadd_comm_events_init(&ctx, &control); if (IS_ERR(ctx.comm)) { pr_err("error: COMM init failed\n"); return PTR_ERR(ctx.comm); diff --git a/drivers/misc/tegra-profiler/mmap.c b/drivers/misc/tegra-profiler/mmap.c index 43b7648b6..3e8a5c729 100644 --- a/drivers/misc/tegra-profiler/mmap.c +++ b/drivers/misc/tegra-profiler/mmap.c @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-profiler/mmap.c * - * Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2015-2018, 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, @@ -24,20 +24,24 @@ #include +#include "quadd.h" #include "mmap.h" #include "comm.h" #include "hrt.h" -#define TMP_BUFFER_SIZE (PATH_MAX + sizeof(u64)) +#define TMP_BUFFER_SIZE (PATH_MAX + sizeof(u64)) +#define QUADD_MMAP_TREE_MAX_LEVEL 32 static void put_mmap_sample(struct quadd_mmap_data *s, char *filename, - size_t length, unsigned long pgoff, int is_file_exists) + size_t length, unsigned long pgoff, + int is_file_exists, pid_t __tgid) { u64 mmap_ed = 0; struct quadd_record_data r; - struct quadd_iovec vec[3]; + struct quadd_iovec vec[4]; u64 pgoff_val = (u64)pgoff << PAGE_SHIFT; + u32 tgid = (u32)__tgid; r.record_type = QUADD_RECORD_TYPE_MMAP; @@ -58,39 +62,35 @@ put_mmap_sample(struct quadd_mmap_data *s, char *filename, vec[2].base = filename; vec[2].len = length; - pr_debug("MMAP: pid: %u, file_name: '%s', addr: %#llx - %#llx, len: %llx, pgoff: %#llx\n", - s->pid, filename, + vec[3].base = &tgid; + vec[3].len = sizeof(tgid); + + pr_debug("[%d] MMAP: tid: %u,pid: %u,'%s',%#llx-%#llx(%llx,%#llx)\n", + smp_processor_id(), s->pid, tgid, filename, s->addr, s->addr + s->len, s->len, pgoff_val); - quadd_put_sample(&r, vec, ARRAY_SIZE(vec)); + quadd_put_sample_this_cpu(&r, vec, ARRAY_SIZE(vec)); } -void quadd_process_mmap(struct vm_area_struct *vma, pid_t pid) +static void +process_mmap(struct vm_area_struct *vma, struct task_struct *task, + char *buf, size_t buf_size) { + pid_t tgid; int is_file_exists; struct file *vm_file; - struct path *path; - char *file_name, *tmp_buf = NULL; + char *file_name; struct quadd_mmap_data sample; size_t length, length_aligned; - if (!vma) - return; - if (!(vma->vm_flags & VM_EXEC)) return; - tmp_buf = kzalloc(TMP_BUFFER_SIZE, GFP_ATOMIC); - if (!tmp_buf) - return; - vm_file = vma->vm_file; if (vm_file) { - path = &vm_file->f_path; - - file_name = d_path(path, tmp_buf, PATH_MAX); + file_name = file_path(vm_file, buf, PATH_MAX); if (IS_ERR(file_name)) - goto out; + return; length = strlen(file_name) + 1; is_file_exists = 1; @@ -113,12 +113,12 @@ void quadd_process_mmap(struct vm_area_struct *vma, pid_t pid) } if (name) - strlcpy(tmp_buf, name, TMP_BUFFER_SIZE); + strlcpy(buf, name, buf_size); else - snprintf(tmp_buf, TMP_BUFFER_SIZE, "[vma:%08lx-%08lx]", + snprintf(buf, buf_size, "[vma:%08lx-%08lx]", vma->vm_start, vma->vm_end); - file_name = tmp_buf; + file_name = buf; length = strlen(file_name) + 1; is_file_exists = 0; @@ -126,110 +126,160 @@ void quadd_process_mmap(struct vm_area_struct *vma, pid_t pid) length_aligned = ALIGN(length, sizeof(u64)); - sample.pid = pid; + sample.pid = task_pid_nr(task); + tgid = task_tgid_nr(task); sample.user_mode = 1; sample.addr = vma->vm_start; sample.len = vma->vm_end - vma->vm_start; put_mmap_sample(&sample, file_name, length_aligned, - vma->vm_pgoff, is_file_exists); + vma->vm_pgoff, is_file_exists, tgid); +} -out: - kfree(tmp_buf); +void quadd_process_mmap(struct vm_area_struct *vma, struct task_struct *task) +{ + char *buf; + + buf = kzalloc(TMP_BUFFER_SIZE, GFP_ATOMIC); + if (!buf) + return; + + preempt_disable(); + process_mmap(vma, task, buf, TMP_BUFFER_SIZE); + preempt_enable(); + + kfree(buf); } -int quadd_get_current_mmap(pid_t pid) +static void get_process_vmas(struct task_struct *task) { - int is_file_exists; - struct vm_area_struct *vma; - struct file *vm_file; - struct path *path; - char *file_name; - struct task_struct *task; + char *buf; struct mm_struct *mm; - struct quadd_mmap_data sample; - size_t length, length_aligned; - char *tmp_buf; - - rcu_read_lock(); - task = pid_task(find_vpid(pid), PIDTYPE_PID); - rcu_read_unlock(); - if (!task) { - pr_err("Process not found: %d\n", pid); - return -ESRCH; - } + struct vm_area_struct *vma; + + if (!task) + return; + + mm = get_task_mm(task); + if (!mm) + return; + + down_read(&mm->mmap_sem); + + buf = kzalloc(TMP_BUFFER_SIZE, GFP_ATOMIC); + if (!buf) + goto out_put_mm; + + preempt_disable(); + for (vma = mm->mmap; vma; vma = vma->vm_next) + process_mmap(vma, task, buf, TMP_BUFFER_SIZE); + preempt_enable(); + + kfree(buf); + +out_put_mm: + up_read(&mm->mmap_sem); + mmput(mm); +} - mm = task->mm; - if (!mm) { - pr_warn("mm is not existed for task: %d\n", pid); - return 0; +static void +__get_process_vmas(struct task_struct *task, + struct mm_struct *mm, char *buf, size_t buf_size) +{ + struct vm_area_struct *vma; + + for (vma = mm->mmap; vma; vma = vma->vm_next) + process_mmap(vma, task, buf, buf_size); +} + +static int +is_sample_process(struct task_struct *from, struct pid *root_pid) +{ + struct task_struct *p; + + for (p = from; p != &init_task;) { + if (task_pid_nr(p) == pid_nr(root_pid)) + return 1; + + rcu_read_lock(); + p = rcu_dereference(p->real_parent); + rcu_read_unlock(); } - pr_info("Get mapped memory objects\n"); + return 0; +} + +static void get_all_processes(struct pid *root_pid) +{ + char *buf; + struct task_struct *p; + + buf = kzalloc(TMP_BUFFER_SIZE, GFP_ATOMIC); + if (!buf) + return; - tmp_buf = kzalloc(TMP_BUFFER_SIZE, GFP_ATOMIC); - if (!tmp_buf) - return -ENOMEM; + read_lock(&tasklist_lock); + for_each_process(p) { + struct mm_struct *mm; - for (vma = mm->mmap; vma; vma = vma->vm_next) { - if (!(vma->vm_flags & VM_EXEC)) + if (p->flags & PF_KTHREAD) continue; - vm_file = vma->vm_file; - if (vm_file) { - path = &vm_file->f_path; - - file_name = d_path(path, tmp_buf, PATH_MAX); - if (IS_ERR(file_name)) - continue; + if (root_pid && !is_sample_process(p, root_pid)) + continue; - length = strlen(file_name) + 1; - is_file_exists = 1; + rcu_read_lock(); + task_lock(p); + if (likely(p->mm)) { + mm = p->mm; } else { - const char *name = NULL; - - name = arch_vma_name(vma); - if (!name) { - mm = vma->vm_mm; - - if (!mm) { - name = "[vdso]"; - } else if (vma->vm_start <= mm->start_brk && - vma->vm_end >= mm->brk) { - name = "[heap]"; - } else if (vma->vm_start <= mm->start_stack && - vma->vm_end >= mm->start_stack) { - name = "[stack]"; - } - } + mm = NULL; + task_unlock(p); + } + rcu_read_unlock(); + + if (!mm) + continue; - if (name) - strlcpy(tmp_buf, name, TMP_BUFFER_SIZE); - else - snprintf(tmp_buf, TMP_BUFFER_SIZE, - "[vma:%08lx-%08lx]", - vma->vm_start, vma->vm_end); + if (!down_read_trylock(&mm->mmap_sem)) + goto __continue; - file_name = tmp_buf; - length = strlen(file_name) + 1; + __get_process_vmas(p, mm, buf, TMP_BUFFER_SIZE); - is_file_exists = 0; - } + up_read(&mm->mmap_sem); +__continue: + task_unlock(p); + } + read_unlock(&tasklist_lock); - length_aligned = ALIGN(length, sizeof(u64)); + kfree(buf); +} - sample.pid = pid; - sample.user_mode = 1; +void quadd_get_mmaps(struct quadd_ctx *ctx) +{ + struct pid *pid; + struct task_struct *task; + struct quadd_parameters *param = &ctx->param; - sample.addr = vma->vm_start; - sample.len = vma->vm_end - vma->vm_start; + if (!quadd_mode_is_sampling(ctx)) + return; - put_mmap_sample(&sample, file_name, length_aligned, - vma->vm_pgoff, is_file_exists); + if (quadd_mode_is_sample_all(ctx)) { + get_all_processes(NULL); + return; } - kfree(tmp_buf); + pid = find_vpid(param->pids[0]); - return 0; + task = get_pid_task(pid, PIDTYPE_PID); + if (!task) + return; + + if (quadd_mode_is_sample_tree(ctx)) + get_all_processes(pid); + else + get_process_vmas(task); + + put_task_struct(task); } diff --git a/drivers/misc/tegra-profiler/mmap.h b/drivers/misc/tegra-profiler/mmap.h index 4928f939f..993a38c0c 100644 --- a/drivers/misc/tegra-profiler/mmap.h +++ b/drivers/misc/tegra-profiler/mmap.h @@ -1,7 +1,7 @@ /* * drivers/misc/tegra-profiler/mmap.h * - * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2018, 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, @@ -17,9 +17,11 @@ #ifndef __QUADD_MMAP_H #define __QUADD_MMAP_H -#include +struct vm_area_struct; +struct task_struct; +struct quadd_ctx; -void quadd_process_mmap(struct vm_area_struct *vma, pid_t pid); -int quadd_get_current_mmap(pid_t pid); +void quadd_process_mmap(struct vm_area_struct *vma, struct task_struct *task); +void quadd_get_mmaps(struct quadd_ctx *ctx); #endif /* __QUADD_MMAP_H */ diff --git a/drivers/misc/tegra-profiler/quadd.h b/drivers/misc/tegra-profiler/quadd.h index 7af3dfbb2..f85599c78 100644 --- a/drivers/misc/tegra-profiler/quadd.h +++ b/drivers/misc/tegra-profiler/quadd.h @@ -56,8 +56,8 @@ struct source_info { unsigned int raw_event_mask; - int is_present; - int active; + unsigned int is_present:1; + unsigned int active:1; }; struct quadd_ctx { @@ -77,18 +77,49 @@ struct quadd_ctx { atomic_t started; atomic_t tegra_profiler_lock; - int collect_kernel_ips; + unsigned int collect_kernel_ips:1; - int mode_is_trace_all; - int mode_is_sampling; + unsigned int mode_is_sampling:1; + unsigned int mode_is_tracing:1; + unsigned int mode_is_sample_all:1; + unsigned int mode_is_trace_all:1; + unsigned int mode_is_sample_tree:1; + unsigned int mode_is_trace_tree:1; }; +static inline int quadd_mode_is_sampling(struct quadd_ctx *ctx) +{ + return ctx->mode_is_sampling; +} + +static inline int quadd_mode_is_tracing(struct quadd_ctx *ctx) +{ + return ctx->mode_is_tracing; +} + +static inline int quadd_mode_is_sample_all(struct quadd_ctx *ctx) +{ + return ctx->mode_is_sample_all; +} + +static inline int quadd_mode_is_trace_all(struct quadd_ctx *ctx) +{ + return ctx->mode_is_trace_all; +} + +static inline int quadd_mode_is_sample_tree(struct quadd_ctx *ctx) +{ + return ctx->mode_is_sample_tree; +} + +static inline int quadd_mode_is_trace_tree(struct quadd_ctx *ctx) +{ + return ctx->mode_is_trace_tree; +} + void quadd_get_state(struct quadd_module_state *state); int tegra_profiler_try_lock(void); void tegra_profiler_unlock(void); -int quadd_mode_is_trace_all(void); -int quadd_mode_is_sampling(void); - #endif /* __QUADD_H */ diff --git a/drivers/misc/tegra-profiler/quadd_proc.c b/drivers/misc/tegra-profiler/quadd_proc.c index 0dc0490fe..8eb25558f 100644 --- a/drivers/misc/tegra-profiler/quadd_proc.c +++ b/drivers/misc/tegra-profiler/quadd_proc.c @@ -89,7 +89,7 @@ static int show_capabilities(struct seq_file *f, void *offset) seq_printf(f, "pmu arch: %s\n", arch->name); if (arch->pmuver_is_set) - seq_printf(f, "pmu arch version: %d\n", + seq_printf(f, "pmu arch version: %u\n", arch->pmuver); seq_printf(f, "l2 cache: %s\n", diff --git a/drivers/misc/tegra-profiler/version.h b/drivers/misc/tegra-profiler/version.h index e1b72d69e..6b620d727 100644 --- a/drivers/misc/tegra-profiler/version.h +++ b/drivers/misc/tegra-profiler/version.h @@ -17,7 +17,7 @@ #ifndef __QUADD_VERSION_H #define __QUADD_VERSION_H -#define QUADD_MODULE_VERSION "1.121" +#define QUADD_MODULE_VERSION "1.122" #define QUADD_MODULE_BRANCH "Dev" #endif /* __QUADD_VERSION_H */ diff --git a/include/linux/tegra_profiler.h b/include/linux/tegra_profiler.h index d101ccc1a..2b08a2f90 100644 --- a/include/linux/tegra_profiler.h +++ b/include/linux/tegra_profiler.h @@ -19,8 +19,8 @@ #include -#define QUADD_SAMPLES_VERSION 42 -#define QUADD_IO_VERSION 24 +#define QUADD_SAMPLES_VERSION 43 +#define QUADD_IO_VERSION 25 #define QUADD_IO_VERSION_DYNAMIC_RB 5 #define QUADD_IO_VERSION_RB_MAX_FILL_COUNT 6 @@ -42,33 +42,35 @@ #define QUADD_IO_VERSION_RAW_EVENTS 22 #define QUADD_IO_VERSION_SAMPLING_MODE 23 #define QUADD_IO_VERSION_FORCE_ARCH_TIMER 24 - -#define QUADD_SAMPLE_VERSION_THUMB_MODE_FLAG 17 -#define QUADD_SAMPLE_VERSION_GROUP_SAMPLES 18 -#define QUADD_SAMPLE_VERSION_THREAD_STATE_FLD 19 -#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_SAMPLE_VERSION_UNW_ENTRY_TYPE 26 -#define QUADD_SAMPLE_VERSION_USE_ARCH_TIMER 27 -#define QUADD_SAMPLE_VERSION_SCHED_SAMPLES 28 -#define QUADD_SAMPLE_VERSION_HDR_UNW_METHOD 29 -#define QUADD_SAMPLE_VERSION_HDR_ARCH_TIMER 30 -#define QUADD_SAMPLE_VERSION_STACK_OFFSET 31 -#define QUADD_SAMPLE_VERSION_SCHED_TASK_STATE 32 -#define QUADD_SAMPLE_VERSION_URCS 33 -#define QUADD_SAMPLE_VERSION_HOTPLUG 34 -#define QUADD_SAMPLE_VERSION_PER_CPU_SETUP 35 -#define QUADD_SAMPLE_VERSION_REPORT_TGID 36 -#define QUADD_SAMPLE_VERSION_MMAP_TS 37 -#define QUADD_SAMPLE_VERSION_RAW_EVENTS 38 -#define QUADD_SAMPLE_VERSION_OVERHEAD_INFO 39 -#define QUADD_SAMPLE_VERSION_REPORT_VPID 40 -#define QUADD_SAMPLE_VERSION_SCHED_REPORT_VPID 41 -#define QUADD_SAMPLE_VERSION_SAMPLING_MODE 42 - -#define QUADD_MMAP_HEADER_VERSION 1 +#define QUADD_IO_VERSION_SAMPLE_ALL_TASKS 25 + +#define QUADD_SAMPLE_VERSION_THUMB_MODE_FLAG 17 +#define QUADD_SAMPLE_VERSION_GROUP_SAMPLES 18 +#define QUADD_SAMPLE_VERSION_THREAD_STATE_FLD 19 +#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_SAMPLE_VERSION_UNW_ENTRY_TYPE 26 +#define QUADD_SAMPLE_VERSION_USE_ARCH_TIMER 27 +#define QUADD_SAMPLE_VERSION_SCHED_SAMPLES 28 +#define QUADD_SAMPLE_VERSION_HDR_UNW_METHOD 29 +#define QUADD_SAMPLE_VERSION_HDR_ARCH_TIMER 30 +#define QUADD_SAMPLE_VERSION_STACK_OFFSET 31 +#define QUADD_SAMPLE_VERSION_SCHED_TASK_STATE 32 +#define QUADD_SAMPLE_VERSION_URCS 33 +#define QUADD_SAMPLE_VERSION_HOTPLUG 34 +#define QUADD_SAMPLE_VERSION_PER_CPU_SETUP 35 +#define QUADD_SAMPLE_VERSION_REPORT_TGID 36 +#define QUADD_SAMPLE_VERSION_MMAP_TS 37 +#define QUADD_SAMPLE_VERSION_RAW_EVENTS 38 +#define QUADD_SAMPLE_VERSION_OVERHEAD_INFO 39 +#define QUADD_SAMPLE_VERSION_REPORT_VPID 40 +#define QUADD_SAMPLE_VERSION_SCHED_REPORT_VPID 41 +#define QUADD_SAMPLE_VERSION_SAMPLING_MODE 42 +#define QUADD_SAMPLE_VERSION_SAMPLE_ALL_TASKS 43 + +#define QUADD_MMAP_HEADER_VERSION 1 #define QUADD_MAX_COUNTERS 32 #define QUADD_MAX_PROCESS 64 @@ -359,6 +361,10 @@ struct quadd_debug_data { #define QUADD_HDR_HAS_CPUID (1 << 6) #define QUADD_HDR_MODE_TRACE_ALL (1 << 7) #define QUADD_HDR_MODE_SAMPLING (1 << 8) +#define QUADD_HDR_MODE_TRACING (1 << 9) +#define QUADD_HDR_MODE_SAMPLE_ALL (1 << 10) +#define QUADD_HDR_MODE_SAMPLE_TREE (1 << 11) +#define QUADD_HDR_MODE_TRACE_TREE (1 << 12) struct quadd_header_data { u16 magic; @@ -419,6 +425,10 @@ enum { #define QUADD_PARAM_EXTRA_PER_PMU_SETUP (1 << 8) #define QUADD_PARAM_EXTRA_SAMPLING (1 << 9) #define QUADD_PARAM_EXTRA_FORCE_ARCH_TIMER (1 << 10) +#define QUADD_PARAM_EXTRA_SAMPLE_ALL_TASKS (1 << 11) +#define QUADD_PARAM_EXTRA_SAMPLE_TREE (1 << 12) +#define QUADD_PARAM_EXTRA_TRACING (1 << 13) +#define QUADD_PARAM_EXTRA_TRACE_TREE (1 << 14) enum { QUADD_EVENT_TYPE_RAW = 0, -- cgit v1.2.2