/*
* drivers/misc/tegra-profiler/mmap.c
*
- * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2015, 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,
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/module.h>
#include <linux/mm.h>
-#include <linux/crc32.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/tegra_profiler.h>
#include "mmap.h"
+#include "comm.h"
#include "hrt.h"
-#include "debug.h"
-
-static struct quadd_mmap_ctx mmap_ctx;
-
-static int binary_search_and_add(unsigned int *array,
- unsigned int length, unsigned int key)
-{
- unsigned int i_min, i_max, mid;
-
- if (length == 0) {
- array[0] = key;
- return 1;
- } else if (length == 1 && array[0] == key) {
- return 0;
- }
-
- i_min = 0;
- i_max = length;
-
- if (array[0] > key) {
- memmove((char *)((unsigned int *)array + 1), array,
- length * sizeof(unsigned int));
- array[0] = key;
- return 1;
- } else if (array[length - 1] < key) {
- array[length] = key;
- return 1;
- }
-
- while (i_min < i_max) {
- mid = i_min + (i_max - i_min) / 2;
-
- if (key <= array[mid])
- i_max = mid;
- else
- i_min = mid + 1;
- }
-
- if (array[i_max] == key) {
- return 0;
- } else {
- memmove((char *)((unsigned int *)array + i_max + 1),
- (char *)((unsigned int *)array + i_max),
- (length - i_max) * sizeof(unsigned int));
- array[i_max] = key;
- return 1;
- }
-}
-
-static int check_hash(u32 key)
-{
- int res;
- unsigned long flags;
-
- spin_lock_irqsave(&mmap_ctx.lock, flags);
-
- if (mmap_ctx.nr_hashes >= QUADD_MMAP_SIZE_ARRAY) {
- spin_unlock_irqrestore(&mmap_ctx.lock, flags);
- return 1;
- }
-
- res = binary_search_and_add(mmap_ctx.hash_array,
- mmap_ctx.nr_hashes, key);
- if (res > 0) {
- mmap_ctx.nr_hashes++;
- spin_unlock_irqrestore(&mmap_ctx.lock, flags);
- return 0;
- }
-
- spin_unlock_irqrestore(&mmap_ctx.lock, flags);
- return 1;
-}
-
-static int find_file(const char *file_name, unsigned long addr,
- unsigned long len)
-{
- u32 crc;
- size_t length;
-
- if (!file_name)
- return 0;
-
- length = strlen(file_name);
-
- crc = crc32_le(~0, file_name, length);
- crc = crc32_le(crc, (unsigned char *)&addr,
- sizeof(addr));
- crc = crc32_le(crc, (unsigned char *)&len,
- sizeof(len));
-
- return check_hash(crc);
-}
static void
-put_mmap_sample(struct quadd_mmap_data *s, char *extra_data,
- size_t extra_length)
+put_mmap_sample(struct quadd_mmap_data *s, char *filename,
+ size_t length, unsigned long pgoff, int is_file_exists)
{
+ u64 mmap_ed = 0;
struct quadd_record_data r;
+ struct quadd_iovec vec[3];
+ u64 pgoff_val = (u64)pgoff << PAGE_SHIFT;
- r.magic = QUADD_RECORD_MAGIC;
r.record_type = QUADD_RECORD_TYPE_MMAP;
- r.cpu_mode = QUADD_CPU_MODE_USER;
memcpy(&r.mmap, s, sizeof(*s));
- r.mmap.filename_length = extra_length;
+ r.mmap.filename_length = length;
+
+ if (is_file_exists)
+ mmap_ed |= QUADD_MMAP_ED_IS_FILE_EXISTS;
+
+ vec[0].base = &pgoff_val;
+ vec[0].len = sizeof(pgoff_val);
+
+ vec[1].base = &mmap_ed;
+ vec[1].len = sizeof(mmap_ed);
- pr_debug("MMAP: pid: %d, file_name: '%s', addr: %#x, length: %u",
- s->pid, extra_data, s->addr, extra_length);
+ vec[2].base = filename;
+ vec[2].len = length;
- quadd_put_sample(&r, extra_data, extra_length);
+ pr_debug("MMAP: pid: %u, file_name: '%s', addr: %#llx - %#llx, len: %llx, pgoff: %#llx\n",
+ s->pid, filename,
+ s->addr, s->addr + s->len, s->len, pgoff_val);
+
+ quadd_put_sample(&r, vec, ARRAY_SIZE(vec));
}
-void quadd_get_mmap_object(struct quadd_cpu_context *cpu_ctx,
- struct pt_regs *regs, pid_t pid)
+void quadd_process_mmap(struct vm_area_struct *vma, pid_t pid)
{
- unsigned long ip;
- size_t length, length_aligned;
- struct mm_struct *mm = current->mm;
- struct vm_area_struct *vma;
+ int is_file_exists;
struct file *vm_file;
struct path *path;
- char *file_name = NULL;
+ char *file_name, *tmp_buf = NULL;
struct quadd_mmap_data sample;
- struct quadd_mmap_cpu_ctx *mm_cpu_ctx = this_cpu_ptr(mmap_ctx.cpu_ctx);
- char *tmp_buf = mm_cpu_ctx->tmp_buf;
+ size_t length, length_aligned;
- if (!mm)
+ if (!vma)
return;
- ip = instruction_pointer(regs);
+ if (!(vma->vm_flags & VM_EXEC))
+ return;
- if (user_mode(regs)) {
- for (vma = find_vma(mm, ip); vma; vma = vma->vm_next) {
- if (ip < vma->vm_start || ip >= vma->vm_end)
- continue;
+ tmp_buf = kzalloc(PATH_MAX + sizeof(u64), GFP_ATOMIC);
+ if (!tmp_buf)
+ return;
- vm_file = vma->vm_file;
- if (!vm_file)
- break;
+ vm_file = vma->vm_file;
+ if (vm_file) {
+ path = &vm_file->f_path;
- path = &vm_file->f_path;
+ file_name = d_path(path, tmp_buf, PATH_MAX);
+ if (IS_ERR(file_name))
+ goto out;
- file_name = d_path(path, tmp_buf, PATH_MAX);
- if (IS_ERR(file_name)) {
- file_name = NULL;
- } else {
- sample.addr = vma->vm_start;
- sample.len = vma->vm_end - vma->vm_start;
- sample.pgoff =
- (u64)vma->vm_pgoff << PAGE_SHIFT;
- }
- break;
- }
+ length = strlen(file_name) + 1;
+ is_file_exists = 1;
} else {
-#ifdef CONFIG_MODULES
- struct module *mod;
-
- preempt_disable();
- mod = __module_address(ip);
- preempt_enable();
-
- if (mod) {
- file_name = mod->name;
- if (file_name) {
- sample.addr = (u32) mod->module_core;
- sample.len = mod->core_size;
- sample.pgoff = 0;
+ const char *name = NULL;
+
+ name = arch_vma_name(vma);
+ if (!name) {
+ struct mm_struct *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]";
}
}
-#endif
+
+ if (name)
+ strcpy(tmp_buf, name);
+ else
+ sprintf(tmp_buf, "[vma:%08lx-%08lx]",
+ vma->vm_start, vma->vm_end);
+
+ file_name = tmp_buf;
+ length = strlen(file_name) + 1;
+
+ is_file_exists = 0;
}
- if (file_name) {
- if (!find_file(file_name, sample.addr, sample.len)) {
- length = strlen(file_name) + 1;
- if (length > PATH_MAX)
- return;
+ length_aligned = ALIGN(length, sizeof(u64));
- sample.pid = pid;
+ sample.pid = pid;
+ sample.user_mode = 1;
- strcpy(cpu_ctx->mmap_filename, file_name);
- length_aligned = ALIGN(length, 8);
+ sample.addr = vma->vm_start;
+ sample.len = vma->vm_end - vma->vm_start;
- put_mmap_sample(&sample, cpu_ctx->mmap_filename,
- length_aligned);
- }
- }
+ put_mmap_sample(&sample, file_name, length_aligned,
+ vma->vm_pgoff, is_file_exists);
+
+out:
+ kfree(tmp_buf);
}
-int quadd_get_current_mmap(struct quadd_cpu_context *cpu_ctx, pid_t pid)
+int quadd_get_current_mmap(pid_t pid)
{
+ int is_file_exists;
struct vm_area_struct *vma;
struct file *vm_file;
struct path *path;
struct mm_struct *mm;
struct quadd_mmap_data sample;
size_t length, length_aligned;
- struct quadd_mmap_cpu_ctx *mm_cpu_ctx = this_cpu_ptr(mmap_ctx.cpu_ctx);
- char *tmp_buf = mm_cpu_ctx->tmp_buf;
+ char *tmp_buf;
rcu_read_lock();
task = pid_task(find_vpid(pid), PIDTYPE_PID);
rcu_read_unlock();
if (!task) {
- pr_err("Process not found: %u\n", pid);
+ pr_err("Process not found: %d\n", pid);
return -ESRCH;
}
+
mm = task->mm;
+ if (!mm) {
+ pr_warn("mm is not existed for task: %d\n", pid);
+ return 0;
+ }
pr_info("Get mapped memory objects\n");
- for (vma = mm->mmap; vma; vma = vma->vm_next) {
- vm_file = vma->vm_file;
- if (!vm_file)
- continue;
-
- path = &vm_file->f_path;
-
- file_name = d_path(path, tmp_buf, PATH_MAX);
- if (IS_ERR(file_name))
- continue;
+ tmp_buf = kzalloc(PATH_MAX + sizeof(u64), GFP_ATOMIC);
+ if (!tmp_buf)
+ return -ENOMEM;
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
if (!(vma->vm_flags & VM_EXEC))
continue;
- length = strlen(file_name) + 1;
- if (length > PATH_MAX)
- continue;
-
- sample.pid = pid;
- sample.addr = vma->vm_start;
- sample.len = vma->vm_end - vma->vm_start;
- sample.pgoff = (u64)vma->vm_pgoff << PAGE_SHIFT;
-
- if (!find_file(file_name, sample.addr, sample.len)) {
- strcpy(cpu_ctx->mmap_filename, file_name);
- length_aligned = ALIGN(length, 8);
-
- put_mmap_sample(&sample, file_name, length_aligned);
- }
- }
- return 0;
-}
+ vm_file = vma->vm_file;
+ if (vm_file) {
+ path = &vm_file->f_path;
-struct quadd_mmap_ctx *quadd_mmap_init(struct quadd_ctx *quadd_ctx)
-{
- int cpu_id;
- u32 *hash;
- char *tmp;
- struct quadd_mmap_cpu_ctx *cpu_ctx;
-
- mmap_ctx.quadd_ctx = quadd_ctx;
-
- hash = kzalloc(QUADD_MMAP_SIZE_ARRAY * sizeof(unsigned int),
- GFP_KERNEL);
- if (!hash) {
- pr_err("Failed to allocate mmap buffer\n");
- return ERR_PTR(-ENOMEM);
- }
- mmap_ctx.hash_array = hash;
+ file_name = d_path(path, tmp_buf, PATH_MAX);
+ if (IS_ERR(file_name))
+ continue;
- mmap_ctx.nr_hashes = 0;
- spin_lock_init(&mmap_ctx.lock);
+ length = strlen(file_name) + 1;
+ is_file_exists = 1;
+ } 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]";
+ }
+ }
- mmap_ctx.cpu_ctx = alloc_percpu(struct quadd_mmap_cpu_ctx);
- if (!mmap_ctx.cpu_ctx)
- return ERR_PTR(-ENOMEM);
+ if (name)
+ strcpy(tmp_buf, name);
+ else
+ sprintf(tmp_buf, "[vma:%08lx-%08lx]",
+ vma->vm_start, vma->vm_end);
- for (cpu_id = 0; cpu_id < nr_cpu_ids; cpu_id++) {
- cpu_ctx = per_cpu_ptr(mmap_ctx.cpu_ctx, cpu_id);
+ file_name = tmp_buf;
+ length = strlen(file_name) + 1;
- tmp = kzalloc(PATH_MAX + sizeof(unsigned long long),
- GFP_KERNEL);
- if (!tmp) {
- pr_err("Failed to allocate mmap buffer\n");
- return ERR_PTR(-ENOMEM);
+ is_file_exists = 0;
}
- cpu_ctx->tmp_buf = tmp;
- }
-
- return &mmap_ctx;
-}
-void quadd_mmap_reset(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&mmap_ctx.lock, flags);
- mmap_ctx.nr_hashes = 0;
- spin_unlock_irqrestore(&mmap_ctx.lock, flags);
-}
+ length_aligned = ALIGN(length, sizeof(u64));
-void quadd_mmap_deinit(void)
-{
- int cpu_id;
- unsigned long flags;
- struct quadd_mmap_cpu_ctx *cpu_ctx;
+ sample.pid = pid;
+ sample.user_mode = 1;
- spin_lock_irqsave(&mmap_ctx.lock, flags);
- kfree(mmap_ctx.hash_array);
- mmap_ctx.hash_array = NULL;
+ sample.addr = vma->vm_start;
+ sample.len = vma->vm_end - vma->vm_start;
- for (cpu_id = 0; cpu_id < nr_cpu_ids; cpu_id++) {
- cpu_ctx = per_cpu_ptr(mmap_ctx.cpu_ctx, cpu_id);
- kfree(cpu_ctx->tmp_buf);
+ put_mmap_sample(&sample, file_name, length_aligned,
+ vma->vm_pgoff, is_file_exists);
}
- free_percpu(mmap_ctx.cpu_ctx);
- spin_unlock_irqrestore(&mmap_ctx.lock, flags);
+ kfree(tmp_buf);
+
+ return 0;
}