2 * drivers/misc/tegra-profiler/mmap.c
4 * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19 #include <linux/module.h>
21 #include <linux/crc32.h>
23 #include <linux/slab.h>
24 #include <linux/err.h>
25 #include <linux/sched.h>
27 #include <linux/tegra_profiler.h>
33 static struct quadd_mmap_ctx mmap_ctx;
35 static int binary_search_and_add(unsigned int *array,
36 unsigned int length, unsigned int key)
38 unsigned int i_min, i_max, mid;
43 } else if (length == 1 && array[0] == key) {
51 memmove((char *)((unsigned int *)array + 1), array,
52 length * sizeof(unsigned int));
55 } else if (array[length - 1] < key) {
60 while (i_min < i_max) {
61 mid = i_min + (i_max - i_min) / 2;
63 if (key <= array[mid])
69 if (array[i_max] == key) {
72 memmove((char *)((unsigned int *)array + i_max + 1),
73 (char *)((unsigned int *)array + i_max),
74 (length - i_max) * sizeof(unsigned int));
80 static int check_hash(u32 key)
85 spin_lock_irqsave(&mmap_ctx.lock, flags);
87 if (mmap_ctx.nr_hashes >= QUADD_MMAP_SIZE_ARRAY) {
88 spin_unlock_irqrestore(&mmap_ctx.lock, flags);
92 res = binary_search_and_add(mmap_ctx.hash_array,
93 mmap_ctx.nr_hashes, key);
96 spin_unlock_irqrestore(&mmap_ctx.lock, flags);
100 spin_unlock_irqrestore(&mmap_ctx.lock, flags);
104 static int find_file(const char *file_name, unsigned long addr,
113 length = strlen(file_name);
115 crc = crc32_le(~0, file_name, length);
116 crc = crc32_le(crc, (unsigned char *)&addr,
118 crc = crc32_le(crc, (unsigned char *)&len,
121 return check_hash(crc);
125 put_mmap_sample(struct quadd_mmap_data *s, char *extra_data,
128 struct quadd_record_data r;
130 r.magic = QUADD_RECORD_MAGIC;
131 r.record_type = QUADD_RECORD_TYPE_MMAP;
132 r.cpu_mode = QUADD_CPU_MODE_USER;
134 memcpy(&r.mmap, s, sizeof(*s));
135 r.mmap.filename_length = extra_length;
137 pr_debug("MMAP: pid: %d, file_name: '%s', addr: %#x, length: %u",
138 s->pid, extra_data, s->addr, extra_length);
140 quadd_put_sample(&r, extra_data, extra_length);
143 void quadd_get_mmap_object(struct quadd_cpu_context *cpu_ctx,
144 struct pt_regs *regs, pid_t pid)
147 size_t length, length_aligned;
148 struct mm_struct *mm = current->mm;
149 struct vm_area_struct *vma;
150 struct file *vm_file;
152 char *file_name = NULL;
153 struct quadd_mmap_data sample;
154 struct quadd_mmap_cpu_ctx *mm_cpu_ctx = this_cpu_ptr(mmap_ctx.cpu_ctx);
155 char *tmp_buf = mm_cpu_ctx->tmp_buf;
160 ip = instruction_pointer(regs);
162 if (user_mode(regs)) {
163 for (vma = find_vma(mm, ip); vma; vma = vma->vm_next) {
164 if (ip < vma->vm_start || ip >= vma->vm_end)
167 vm_file = vma->vm_file;
171 path = &vm_file->f_path;
173 file_name = d_path(path, tmp_buf, PATH_MAX);
174 if (IS_ERR(file_name)) {
177 sample.addr = vma->vm_start;
178 sample.len = vma->vm_end - vma->vm_start;
180 (u64)vma->vm_pgoff << PAGE_SHIFT;
185 #ifdef CONFIG_MODULES
189 mod = __module_address(ip);
193 file_name = mod->name;
195 sample.addr = (u32) mod->module_core;
196 sample.len = mod->core_size;
204 if (!find_file(file_name, sample.addr, sample.len)) {
205 length = strlen(file_name) + 1;
206 if (length > PATH_MAX)
211 strcpy(cpu_ctx->mmap_filename, file_name);
212 length_aligned = ALIGN(length, 8);
214 put_mmap_sample(&sample, cpu_ctx->mmap_filename,
220 int quadd_get_current_mmap(struct quadd_cpu_context *cpu_ctx, pid_t pid)
222 struct vm_area_struct *vma;
223 struct file *vm_file;
226 struct task_struct *task;
227 struct mm_struct *mm;
228 struct quadd_mmap_data sample;
229 size_t length, length_aligned;
230 struct quadd_mmap_cpu_ctx *mm_cpu_ctx = this_cpu_ptr(mmap_ctx.cpu_ctx);
231 char *tmp_buf = mm_cpu_ctx->tmp_buf;
234 task = pid_task(find_vpid(pid), PIDTYPE_PID);
237 pr_err("Process not found: %u\n", pid);
242 pr_info("Get mapped memory objects\n");
244 for (vma = mm->mmap; vma; vma = vma->vm_next) {
245 vm_file = vma->vm_file;
249 path = &vm_file->f_path;
251 file_name = d_path(path, tmp_buf, PATH_MAX);
252 if (IS_ERR(file_name))
255 if (!(vma->vm_flags & VM_EXEC))
258 length = strlen(file_name) + 1;
259 if (length > PATH_MAX)
263 sample.addr = vma->vm_start;
264 sample.len = vma->vm_end - vma->vm_start;
265 sample.pgoff = (u64)vma->vm_pgoff << PAGE_SHIFT;
267 if (!find_file(file_name, sample.addr, sample.len)) {
268 strcpy(cpu_ctx->mmap_filename, file_name);
269 length_aligned = ALIGN(length, 8);
271 put_mmap_sample(&sample, file_name, length_aligned);
277 struct quadd_mmap_ctx *quadd_mmap_init(struct quadd_ctx *quadd_ctx)
282 struct quadd_mmap_cpu_ctx *cpu_ctx;
284 mmap_ctx.quadd_ctx = quadd_ctx;
286 hash = kzalloc(QUADD_MMAP_SIZE_ARRAY * sizeof(unsigned int),
289 pr_err("Failed to allocate mmap buffer\n");
290 return ERR_PTR(-ENOMEM);
292 mmap_ctx.hash_array = hash;
294 mmap_ctx.nr_hashes = 0;
295 spin_lock_init(&mmap_ctx.lock);
297 mmap_ctx.cpu_ctx = alloc_percpu(struct quadd_mmap_cpu_ctx);
298 if (!mmap_ctx.cpu_ctx)
299 return ERR_PTR(-ENOMEM);
301 for (cpu_id = 0; cpu_id < nr_cpu_ids; cpu_id++) {
302 cpu_ctx = per_cpu_ptr(mmap_ctx.cpu_ctx, cpu_id);
304 tmp = kzalloc(PATH_MAX + sizeof(unsigned long long),
307 pr_err("Failed to allocate mmap buffer\n");
308 return ERR_PTR(-ENOMEM);
310 cpu_ctx->tmp_buf = tmp;
316 void quadd_mmap_reset(void)
320 spin_lock_irqsave(&mmap_ctx.lock, flags);
321 mmap_ctx.nr_hashes = 0;
322 spin_unlock_irqrestore(&mmap_ctx.lock, flags);
325 void quadd_mmap_deinit(void)
329 struct quadd_mmap_cpu_ctx *cpu_ctx;
331 spin_lock_irqsave(&mmap_ctx.lock, flags);
332 kfree(mmap_ctx.hash_array);
333 mmap_ctx.hash_array = NULL;
335 for (cpu_id = 0; cpu_id < nr_cpu_ids; cpu_id++) {
336 cpu_ctx = per_cpu_ptr(mmap_ctx.cpu_ctx, cpu_id);
337 kfree(cpu_ctx->tmp_buf);
339 free_percpu(mmap_ctx.cpu_ctx);
341 spin_unlock_irqrestore(&mmap_ctx.lock, flags);