mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2026-02-10 07:00:53 +00:00
better disk profiling
This commit is contained in:
18
binfile.c
18
binfile.c
@ -24,7 +24,6 @@ struct BinFile
|
|||||||
int n_symbols;
|
int n_symbols;
|
||||||
Symbol *symbols;
|
Symbol *symbols;
|
||||||
Symbol undefined;
|
Symbol undefined;
|
||||||
gulong address;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static bfd *
|
static bfd *
|
||||||
@ -279,6 +278,7 @@ read_symbols (BinFile *bf)
|
|||||||
bfd *bfd;
|
bfd *bfd;
|
||||||
GArray *symbols;
|
GArray *symbols;
|
||||||
asection *sec;
|
asection *sec;
|
||||||
|
ulong load_address;
|
||||||
|
|
||||||
bf->symbols = NULL;
|
bf->symbols = NULL;
|
||||||
bf->n_symbols = 0;
|
bf->n_symbols = 0;
|
||||||
@ -301,13 +301,13 @@ read_symbols (BinFile *bf)
|
|||||||
if (!bfd_symbols)
|
if (!bfd_symbols)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bf->address = 0xffffffff;
|
load_address = 0xffffffff;
|
||||||
for (sec = bfd->sections; sec != NULL; sec = sec->next)
|
for (sec = bfd->sections; sec != NULL; sec = sec->next)
|
||||||
{
|
{
|
||||||
if (sec->flags & SEC_LOAD)
|
if (sec->flags & SEC_LOAD)
|
||||||
{
|
{
|
||||||
if ((gulong)sec->vma < bf->address)
|
if ((gulong)sec->vma < load_address)
|
||||||
bf->address = sec->vma & ~4095;
|
load_address = sec->vma & ~4095;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,6 +317,8 @@ read_symbols (BinFile *bf)
|
|||||||
|
|
||||||
symbols = g_array_new (FALSE, FALSE, sizeof (Symbol));
|
symbols = g_array_new (FALSE, FALSE, sizeof (Symbol));
|
||||||
|
|
||||||
|
/* g_print ("%s: text vma: %x\n", bf->filename, text_section->vma); */
|
||||||
|
|
||||||
for (i = 0; i < n_symbols; i++)
|
for (i = 0; i < n_symbols; i++)
|
||||||
{
|
{
|
||||||
Symbol symbol;
|
Symbol symbol;
|
||||||
@ -326,7 +328,7 @@ read_symbols (BinFile *bf)
|
|||||||
{
|
{
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
symbol.address = bfd_asymbol_value (bfd_symbols[i]);
|
symbol.address = bfd_asymbol_value (bfd_symbols[i]) - load_address;
|
||||||
name = demangle (bfd, bfd_asymbol_name (bfd_symbols[i]));
|
name = demangle (bfd, bfd_asymbol_name (bfd_symbols[i]));
|
||||||
symbol.name = g_strdup (name);
|
symbol.name = g_strdup (name);
|
||||||
free (name);
|
free (name);
|
||||||
@ -374,12 +376,6 @@ bin_file_free (BinFile *bf)
|
|||||||
g_free (bf);
|
g_free (bf);
|
||||||
}
|
}
|
||||||
|
|
||||||
gulong
|
|
||||||
bin_file_get_load_address (BinFile *bf)
|
|
||||||
{
|
|
||||||
return bf->address;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Symbol *
|
const Symbol *
|
||||||
bin_file_lookup_symbol (BinFile *bf,
|
bin_file_lookup_symbol (BinFile *bf,
|
||||||
gulong address)
|
gulong address)
|
||||||
|
|||||||
@ -12,7 +12,6 @@ BinFile * bin_file_new (const char *filename);
|
|||||||
void bin_file_free (BinFile *bin_file);
|
void bin_file_free (BinFile *bin_file);
|
||||||
const Symbol *bin_file_lookup_symbol (BinFile *bin_file,
|
const Symbol *bin_file_lookup_symbol (BinFile *bin_file,
|
||||||
gulong address);
|
gulong address);
|
||||||
gulong bin_file_get_load_address (BinFile *bf);
|
|
||||||
|
|
||||||
/* Symbol */
|
/* Symbol */
|
||||||
struct Symbol
|
struct Symbol
|
||||||
|
|||||||
31
process.c
31
process.c
@ -22,6 +22,7 @@ struct Map
|
|||||||
char * filename;
|
char * filename;
|
||||||
gulong start;
|
gulong start;
|
||||||
gulong end;
|
gulong end;
|
||||||
|
gulong offset;
|
||||||
gboolean do_offset;
|
gboolean do_offset;
|
||||||
|
|
||||||
BinFile * bin_file;
|
BinFile * bin_file;
|
||||||
@ -97,10 +98,12 @@ read_maps (int pid)
|
|||||||
int count;
|
int count;
|
||||||
guint start;
|
guint start;
|
||||||
guint end;
|
guint end;
|
||||||
|
guint offset;
|
||||||
|
|
||||||
count = sscanf (
|
count = sscanf (
|
||||||
buffer, "%x-%x %*15s %*x %*u:%*u %*u %255s", &start, &end, file);
|
buffer, "%x-%x %*15s %x %*u:%*u %*u %255s",
|
||||||
if (count == 3)
|
&start, &end, &offset, file);
|
||||||
|
if (count == 4)
|
||||||
{
|
{
|
||||||
Map *map;
|
Map *map;
|
||||||
|
|
||||||
@ -110,7 +113,7 @@ read_maps (int pid)
|
|||||||
map->start = start;
|
map->start = start;
|
||||||
map->end = end;
|
map->end = end;
|
||||||
|
|
||||||
map->do_offset = check_do_offset (map, pid);
|
map->offset = offset; //check_do_offset (map, pid);
|
||||||
|
|
||||||
map->bin_file = NULL;
|
map->bin_file = NULL;
|
||||||
|
|
||||||
@ -257,6 +260,8 @@ process_lookup_symbol (Process *process, gulong address)
|
|||||||
static Symbol undefined;
|
static Symbol undefined;
|
||||||
const Symbol *result;
|
const Symbol *result;
|
||||||
Map *map = process_locate_map (process, address);
|
Map *map = process_locate_map (process, address);
|
||||||
|
|
||||||
|
/* g_print ("addr: %x\n", address); */
|
||||||
|
|
||||||
if (!map)
|
if (!map)
|
||||||
{
|
{
|
||||||
@ -268,9 +273,11 @@ process_lookup_symbol (Process *process, gulong address)
|
|||||||
return &undefined;
|
return &undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if (map->do_offset)
|
/* if (map->do_offset) */
|
||||||
address -= map->start;
|
/* address -= map->start; */
|
||||||
*/
|
|
||||||
|
address -= map->start;
|
||||||
|
address += map->offset;
|
||||||
|
|
||||||
if (!map->bin_file)
|
if (!map->bin_file)
|
||||||
map->bin_file = bin_file_new (map->filename);
|
map->bin_file = bin_file_new (map->filename);
|
||||||
@ -278,10 +285,18 @@ process_lookup_symbol (Process *process, gulong address)
|
|||||||
/* FIXME - this seems to work with prelinked binaries, but is
|
/* FIXME - this seems to work with prelinked binaries, but is
|
||||||
* it correct? IE., will normal binaries always have a preferred_addres of 0?
|
* it correct? IE., will normal binaries always have a preferred_addres of 0?
|
||||||
*/
|
*/
|
||||||
address -= map->start;
|
/* address = address - map->start + map->offset + bin_file_get_load_address (map->bin_file); */
|
||||||
address += bin_file_get_load_address (map->bin_file);
|
/* address -= map->start; */
|
||||||
|
/* address += map->offset; */
|
||||||
|
/* address += bin_file_get_load_address (map->bin_file); */
|
||||||
|
|
||||||
|
/* g_print ("%s: start: %p, load: %p\n", */
|
||||||
|
/* map->filename, map->start, bin_file_get_load_address (map->bin_file)); */
|
||||||
|
|
||||||
result = bin_file_lookup_symbol (map->bin_file, address);
|
result = bin_file_lookup_symbol (map->bin_file, address);
|
||||||
|
|
||||||
|
/* g_print ("(%x) %x %x name; %s\n", address, map->start, map->offset, result->name); */
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
195
sysprof-module.c
195
sysprof-module.c
@ -10,6 +10,8 @@
|
|||||||
#include <linux/proc_fs.h>
|
#include <linux/proc_fs.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <linux/poll.h>
|
#include <linux/poll.h>
|
||||||
|
#include <asm/unistd.h>
|
||||||
|
#include <linux/pagemap.h>
|
||||||
|
|
||||||
#include "sysprof-module.h"
|
#include "sysprof-module.h"
|
||||||
|
|
||||||
@ -18,7 +20,7 @@ MODULE_AUTHOR("Soeren Sandmann (sandmann@daimi.au.dk)");
|
|||||||
|
|
||||||
#define SAMPLES_PER_SECOND 50 /* must divide HZ */
|
#define SAMPLES_PER_SECOND 50 /* must divide HZ */
|
||||||
|
|
||||||
static const int cpu_profiler = 1; /* 0: page faults, 1: cpu */
|
static const int cpu_profiler = 0; /* 0: page faults, 1: cpu */
|
||||||
|
|
||||||
static void on_timer_interrupt (void *);
|
static void on_timer_interrupt (void *);
|
||||||
|
|
||||||
@ -96,26 +98,70 @@ generate_stack_trace (struct task_struct *task,
|
|||||||
trace->truncated = 0;
|
trace->truncated = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
disk_access (struct vm_area_struct *area,
|
||||||
|
unsigned long address)
|
||||||
|
{
|
||||||
|
struct file *file = area->vm_file;
|
||||||
|
struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
|
||||||
|
struct inode *inode = mapping->host;
|
||||||
|
struct page *page, **hash;
|
||||||
|
unsigned long size, pgoff, endoff;
|
||||||
|
int disk_access = 1;
|
||||||
|
|
||||||
|
pgoff = ((address - area->vm_start) >> PAGE_CACHE_SHIFT) + area->vm_pgoff;
|
||||||
|
endoff = ((area->vm_end - area->vm_start) >> PAGE_CACHE_SHIFT) + area->vm_pgoff;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An external ptracer can access pages that normally aren't
|
||||||
|
* accessible..
|
||||||
|
*/
|
||||||
|
size = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
||||||
|
if ((pgoff >= size) && (area->vm_mm == current->mm))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* The "size" of the file, as far as mmap is concerned, isn't bigger than the mapping */
|
||||||
|
if (size > endoff)
|
||||||
|
size = endoff;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do we have something in the page cache already?
|
||||||
|
*/
|
||||||
|
hash = page_hash(mapping, pgoff);
|
||||||
|
|
||||||
|
page = __find_get_page(mapping, pgoff, hash);
|
||||||
|
if (page)
|
||||||
|
/*
|
||||||
|
* Ok, found a page in the page cache, now we need to check
|
||||||
|
* that it's up-to-date.
|
||||||
|
*/
|
||||||
|
if (Page_Uptodate(page))
|
||||||
|
disk_access = 0;
|
||||||
|
|
||||||
|
return disk_access;
|
||||||
|
}
|
||||||
|
|
||||||
struct page *
|
struct page *
|
||||||
hijacked_nopage (struct vm_area_struct * area,
|
hijacked_nopage (struct vm_area_struct * area,
|
||||||
unsigned long address,
|
unsigned long address,
|
||||||
int unused)
|
int unused)
|
||||||
{
|
{
|
||||||
if (current && current->pid != 0)
|
struct page *result;
|
||||||
|
int disk;
|
||||||
|
|
||||||
|
disk = disk_access (area, address);
|
||||||
|
|
||||||
|
printk (KERN_ALERT "disk access: %d\n", disk);
|
||||||
|
|
||||||
|
result = filemap_nopage (area, address, unused);
|
||||||
|
|
||||||
|
if (current && current->pid != 0 && disk)
|
||||||
{
|
{
|
||||||
#if 0
|
|
||||||
generate_stack_trace (current, head);
|
generate_stack_trace (current, head);
|
||||||
if (head++ == &stack_traces[N_TRACES - 1])
|
|
||||||
head = &stack_traces[0];
|
|
||||||
|
|
||||||
wake_up (&wait_for_trace);
|
/* head->pid = current->pid; */
|
||||||
#endif
|
/* head->addresses[0] = (void *)address; */
|
||||||
memset (head, 0, sizeof (SysprofStackTrace));
|
/* head->truncated = 0; */
|
||||||
|
|
||||||
head->n_addresses = 1;
|
|
||||||
head->pid = current->pid;
|
|
||||||
head->addresses[0] = (void *)address;
|
|
||||||
head->truncated = 0;
|
|
||||||
|
|
||||||
if (area->vm_file)
|
if (area->vm_file)
|
||||||
{
|
{
|
||||||
@ -141,8 +187,15 @@ hijacked_nopage (struct vm_area_struct * area,
|
|||||||
|
|
||||||
wake_up (&wait_for_trace);
|
wake_up (&wait_for_trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
return filemap_nopage (area, address, unused);
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hijack_nopage (struct vm_area_struct *vma)
|
||||||
|
{
|
||||||
|
if (vma->vm_ops && vma->vm_ops->nopage == filemap_nopage)
|
||||||
|
vma->vm_ops->nopage = hijacked_nopage;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -155,10 +208,7 @@ hijack_nopages (struct task_struct *task)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
for (vma = mm->mmap; vma != NULL; vma = vma->vm_next)
|
for (vma = mm->mmap; vma != NULL; vma = vma->vm_next)
|
||||||
{
|
hijack_nopage (vma);
|
||||||
if (vma->vm_ops && vma->vm_ops->nopage == filemap_nopage)
|
|
||||||
vma->vm_ops->nopage = hijacked_nopage;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -183,6 +233,93 @@ clean_hijacked_nopages (void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct mmap_arg_struct {
|
||||||
|
unsigned long addr;
|
||||||
|
unsigned long len;
|
||||||
|
unsigned long prot;
|
||||||
|
unsigned long flags;
|
||||||
|
unsigned long fd;
|
||||||
|
unsigned long offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef asmlinkage int (* old_mmap_func) (struct mmap_arg_struct *arg);
|
||||||
|
typedef asmlinkage long (* mmap2_func) (
|
||||||
|
unsigned long addr, unsigned long len,
|
||||||
|
unsigned long prot, unsigned long flags,
|
||||||
|
unsigned long fd, unsigned long pgoff);
|
||||||
|
|
||||||
|
static mmap2_func orig_mmap2;
|
||||||
|
static old_mmap_func orig_mmap;
|
||||||
|
|
||||||
|
static void
|
||||||
|
after_mmap (long res, unsigned long len)
|
||||||
|
{
|
||||||
|
struct vm_area_struct *vma;
|
||||||
|
unsigned long start;
|
||||||
|
|
||||||
|
if (res == -1 || !current || current->pid == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
start = res;
|
||||||
|
|
||||||
|
vma = find_vma (current->mm, start);
|
||||||
|
|
||||||
|
if (vma && vma->vm_end >= start + len)
|
||||||
|
hijack_nopage (vma);
|
||||||
|
#if 0
|
||||||
|
else if (vma)
|
||||||
|
{
|
||||||
|
printk (KERN_ALERT "nope: vm_start: %x, vm_end: %x\n"
|
||||||
|
"start: %p, len: %x, start + len: %x\n",
|
||||||
|
vma->vm_start, vma->vm_end, start, len, start + len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printk (KERN_ALERT "no vma\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static long
|
||||||
|
new_mmap2 (unsigned long addr, unsigned long len,
|
||||||
|
unsigned long prot, unsigned long flags,
|
||||||
|
unsigned long fd, unsigned long pgoff)
|
||||||
|
{
|
||||||
|
int res = orig_mmap2 (addr, len, prot, flags, fd, pgoff);
|
||||||
|
|
||||||
|
after_mmap (res, len);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
new_mmap (struct mmap_arg_struct *arg)
|
||||||
|
{
|
||||||
|
int res = orig_mmap (arg);
|
||||||
|
|
||||||
|
after_mmap (res, arg->len);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void **sys_call_table = (void **)0xc0347df0;
|
||||||
|
|
||||||
|
static void
|
||||||
|
hijack_mmaps (void)
|
||||||
|
{
|
||||||
|
orig_mmap2 = sys_call_table[__NR_mmap2];
|
||||||
|
sys_call_table[__NR_mmap2] = new_mmap2;
|
||||||
|
|
||||||
|
orig_mmap = sys_call_table[__NR_mmap];
|
||||||
|
sys_call_table[__NR_mmap] = new_mmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
restore_mmaps (void)
|
||||||
|
{
|
||||||
|
sys_call_table[__NR_mmap2] = orig_mmap2;
|
||||||
|
sys_call_table[__NR_mmap] = orig_mmap;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_timer_interrupt (void *data)
|
on_timer_interrupt (void *data)
|
||||||
{
|
{
|
||||||
@ -190,7 +327,12 @@ on_timer_interrupt (void *data)
|
|||||||
|
|
||||||
if (exiting)
|
if (exiting)
|
||||||
{
|
{
|
||||||
clean_hijacked_nopages ();
|
if (!cpu_profiler)
|
||||||
|
{
|
||||||
|
restore_mmaps();
|
||||||
|
|
||||||
|
clean_hijacked_nopages ();
|
||||||
|
}
|
||||||
wake_up (&wait_for_exit);
|
wake_up (&wait_for_exit);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -211,7 +353,9 @@ on_timer_interrupt (void *data)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
#if 0
|
||||||
hijack_nopages (current);
|
hijack_nopages (current);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,6 +402,7 @@ procfile_poll (struct file *filp, poll_table *poll_table)
|
|||||||
int
|
int
|
||||||
init_module (void)
|
init_module (void)
|
||||||
{
|
{
|
||||||
|
struct task_struct *task;
|
||||||
trace_proc_file =
|
trace_proc_file =
|
||||||
create_proc_entry ("sysprof-trace", S_IFREG | S_IRUGO, &proc_root);
|
create_proc_entry ("sysprof-trace", S_IFREG | S_IRUGO, &proc_root);
|
||||||
|
|
||||||
@ -267,6 +412,16 @@ init_module (void)
|
|||||||
trace_proc_file->read_proc = procfile_read;
|
trace_proc_file->read_proc = procfile_read;
|
||||||
trace_proc_file->proc_fops->poll = procfile_poll;
|
trace_proc_file->proc_fops->poll = procfile_poll;
|
||||||
trace_proc_file->size = sizeof (SysprofStackTrace);
|
trace_proc_file->size = sizeof (SysprofStackTrace);
|
||||||
|
|
||||||
|
if (!cpu_profiler)
|
||||||
|
{
|
||||||
|
hijack_mmaps ();
|
||||||
|
|
||||||
|
for_each_process (task)
|
||||||
|
{
|
||||||
|
hijack_nopages (task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
queue_task(&timer_task, &tq_timer);
|
queue_task(&timer_task, &tq_timer);
|
||||||
|
|
||||||
|
|||||||
@ -113,10 +113,10 @@ on_read (gpointer data)
|
|||||||
{
|
{
|
||||||
Process *process = process_get_from_pid (trace.pid);
|
Process *process = process_get_from_pid (trace.pid);
|
||||||
int i;
|
int i;
|
||||||
char *filename = NULL;
|
/* char *filename = NULL; */
|
||||||
|
|
||||||
if (*trace.filename)
|
/* if (*trace.filename) */
|
||||||
filename = trace.filename;
|
/* filename = trace.filename; */
|
||||||
|
|
||||||
for (i = 0; i < trace.n_addresses; ++i)
|
for (i = 0; i < trace.n_addresses; ++i)
|
||||||
process_ensure_map (process, trace.pid,
|
process_ensure_map (process, trace.pid,
|
||||||
|
|||||||
Reference in New Issue
Block a user