better disk profiling

This commit is contained in:
Søren Sandmann Pedersen
2004-04-29 17:29:09 +00:00
parent 7e5c6cbca4
commit 649512d961
5 changed files with 208 additions and 43 deletions

View File

@ -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)

View File

@ -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

View File

@ -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;
@ -258,6 +261,8 @@ process_lookup_symbol (Process *process, gulong address)
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)
{ {
if (undefined.name) if (undefined.name)
@ -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;
} }

View File

@ -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)
{ {
@ -142,7 +188,14 @@ 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);
@ -268,6 +413,16 @@ init_module (void)
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);
printk (KERN_ALERT "starting sysprof module\n"); printk (KERN_ALERT "starting sysprof module\n");

View File

@ -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,