Update of /cvsroot/oprofile/oprofile/daemon In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8190/daemon Modified Files: Tag: BRANCH_ANON_MAPPING opd_anon.c opd_anon.h opd_mangling.c opd_sfile.c opd_trans.c opd_trans.h Log Message: more improvements and fixes Index: opd_anon.c =================================================================== RCS file: /cvsroot/oprofile/oprofile/daemon/Attic/opd_anon.c,v retrieving revision 1.1.2.2 retrieving revision 1.1.2.3 diff -u -p -d -r1.1.2.2 -r1.1.2.3 --- opd_anon.c 24 Apr 2005 17:24:23 -0000 1.1.2.2 +++ opd_anon.c 25 Apr 2005 20:40:35 -0000 1.1.2.3 @@ -2,6 +2,14 @@ * @file opd_anon.c * Anonymous region handling. * + * Our caching of maps has some problems: if we get tgid reuse, + * and it's the same application, we might end up with wrong + * maps. The same happens in an unmap-remap case. There's not much + * we can do about this, we just hope it's not too common... + * + * What is relatively common is expanding anon maps, which leaves us + * with lots of separate sample files. + * * @remark Copyright 2005 OProfile authors * @remark Read the file COPYING * @@ -14,106 +22,173 @@ #include "opd_printf.h" #include "op_libiberty.h" +#include <limits.h> #include <stdlib.h> #include <stdio.h> #define HASH_SIZE 1024 #define HASH_BITS (HASH_SIZE - 1) +#define LRU_SIZE 1000 +#define LRU_AMOUNT (LRU_SIZE/5) + static struct list_head hashes[HASH_SIZE]; +static struct list_head lru; +static size_t nr_lru; -static unsigned long hash_anon(pid_t tgid) +/* never called in buffer-processing context, so we don't need + * to update struct transient. Note the 'U' of LRU is based on + * parsing time, not actual use. + */ +static void do_lru(struct transient * trans) { - return (tgid >> 2) & (HASH_SIZE - 1); + size_t nr_to_kill = LRU_AMOUNT; + struct list_head * pos; + struct list_head * pos2; + struct anon_mapping * entry; + + list_for_each_safe(pos, pos2, &lru) { + entry = list_entry(pos, struct anon_mapping, lru_list); + if (trans->anon == entry) + clear_trans_current(trans); + if (trans->last_anon == entry) + clear_trans_last(trans); + list_del(&entry->list); + list_del(&entry->lru_list); + --nr_lru; + free(entry); + if (nr_to_kill-- == 0) + break; + } + + sfile_clear_anon(); +} + + +static unsigned long hash_anon(pid_t tgid, cookie_t app) +{ + return ((app >> DCOOKIE_SHIFT) ^ (tgid >> 2)) & (HASH_SIZE - 1); } -// FIXME: when? -static void clear_anon_maps(pid_t tgid) +static void clear_anon_maps(struct transient * trans) { - unsigned long hash = hash_anon(tgid); + unsigned long hash = hash_anon(trans->tgid, trans->app_cookie); + pid_t tgid = trans->tgid; + cookie_t app = trans->app_cookie; struct list_head * pos; struct list_head * pos2; struct anon_mapping * entry; + clear_trans_current(trans); + list_for_each_safe(pos, pos2, &hashes[hash]) { entry = list_entry(pos, struct anon_mapping, list); - if (entry->tgid == tgid) { + if (entry->tgid == tgid && entry->app_cookie == app) { + if (trans->last_anon == entry) + clear_trans_last(trans); list_del(&entry->list); + list_del(&entry->lru_list); + --nr_lru; free(entry); } } sfile_clear_anon(); - verbprintf(vmisc, "Cleared anon maps for tgid %u.\n", tgid); + if (vmisc) { + char const * name = verbose_cookie(app); + printf("Cleared anon maps for tgid %u (%s).\n", tgid, name); + } } -static void add_anon_mapping(pid_t tgid, vma_t start, vma_t end) +static void +add_anon_mapping(struct transient * trans, vma_t start, vma_t end) { - unsigned long hash = hash_anon(tgid); + unsigned long hash = hash_anon(trans->tgid, trans->app_cookie); struct anon_mapping * m = xmalloc(sizeof(struct anon_mapping)); - m->tgid = tgid; + m->tgid = trans->tgid; + m->app_cookie = trans->app_cookie; m->start = start; m->end = end; list_add_tail(&m->list, &hashes[hash]); - verbprintf(vmisc, "Added anon map 0x%llx-0x%llx for tgid %u.\n", - start, end, (unsigned int)tgid); + list_add_tail(&m->lru_list, &lru); + if (++nr_lru == LRU_SIZE) + do_lru(trans); + if (vmisc) { + char const * name = verbose_cookie(m->app_cookie); + printf("Added anon map 0x%llx-0x%llx for tgid %u (%s).\n", + start, end, m->tgid, name); + } } -// FIXME -#define MAX_LINE_LEN 2048 /* 42000000-4212f000 r-xp 00000000 16:03 424334 /lib/tls/libc-2.3.2.so */ -static void get_anon_maps(pid_t tgid) +static void get_anon_maps(struct transient * trans) { FILE * fp = NULL; - char line[MAX_LINE_LEN]; - char temp[MAX_LINE_LEN]; + char buf[PATH_MAX]; unsigned long start; unsigned long end; int ret; - snprintf(temp, MAX_LINE_LEN, "/proc/%d/maps", tgid); - fp = fopen(temp, "r"); + snprintf(buf, PATH_MAX, "/proc/%d/maps", trans->tgid); + fp = fopen(buf, "r"); if (!fp) return; - while (fgets(line, MAX_LINE_LEN, fp) != NULL) { - // FIXME - /* Note that this can include things such as [heap] */ - ret = sscanf(line, "%lx-%lx %s %s %s %s %s", &start, &end, - temp, temp, temp, temp, temp); + while (fgets(buf, PATH_MAX, fp) != NULL) { + char tmp[20]; + /* Note that this actually includes all mappings, + * since we want stuff like [heap] + */ + ret = sscanf(buf, "%lx-%lx %20s %20s %20s %20s %20s", + &start, &end, tmp, tmp, tmp, tmp, tmp); if (ret < 6) continue; - add_anon_mapping(tgid, start, end); + add_anon_mapping(trans, start, end); } fclose(fp); } -struct anon_mapping * find_anon_mapping(struct transient const * trans) +static int +anon_match(struct transient const * trans, struct anon_mapping const * anon) { - unsigned long hash = hash_anon(trans->tgid); + if (!anon) + return 0; + if (trans->tgid != anon->tgid) + return 0; + if (trans->app_cookie != anon->app_cookie) + return 0; + if (trans->pc < anon->start) + return 0; + return (trans->pc < anon->end); +} + + +struct anon_mapping * find_anon_mapping(struct transient * trans) +{ + unsigned long hash = hash_anon(trans->tgid, trans->app_cookie); struct list_head * pos; struct anon_mapping * entry; int tried = 0; + if (anon_match(trans, trans->anon)) + return (trans->anon); + retry: list_for_each(pos, &hashes[hash]) { entry = list_entry(pos, struct anon_mapping, list); - if (entry->tgid == trans->tgid && trans->pc >= entry->start - && trans->pc < entry->end) + if (anon_match(trans, entry)) goto success; } if (!tried) { - // FIXME: need to only do this occassionally, need to - // clear trans->last_anon probably - clear_anon_maps(trans->tgid); - get_anon_maps(trans->tgid); + clear_anon_maps(trans); + get_anon_maps(trans); tried = 1; goto retry; } @@ -141,4 +216,6 @@ void anon_init(void) for (i = 0; i < HASH_SIZE; ++i) list_init(&hashes[i]); + + list_init(&lru); } Index: opd_anon.h =================================================================== RCS file: /cvsroot/oprofile/oprofile/daemon/Attic/opd_anon.h,v retrieving revision 1.1.2.1 retrieving revision 1.1.2.2 diff -u -p -d -r1.1.2.1 -r1.1.2.2 --- opd_anon.h 24 Apr 2005 06:46:23 -0000 1.1.2.1 +++ opd_anon.h 25 Apr 2005 20:40:35 -0000 1.1.2.2 @@ -14,6 +14,8 @@ #include "op_types.h" #include "op_list.h" +#include "opd_cookie.h" + #include <sys/types.h> struct transient; @@ -24,16 +26,24 @@ struct transient; #define VMA_SHIFT 13 struct anon_mapping { + /** start of the mapping */ vma_t start; + /** end of the mapping */ vma_t end; + /** tgid of the app */ pid_t tgid; + /** cookie of the app */ + cookie_t app_cookie; + /** hash list */ struct list_head list; + /** lru list */ + struct list_head lru_list; }; /** * Try to find an anonymous mapping for the given pc/tgid pair. */ -struct anon_mapping * find_anon_mapping(struct transient const *); +struct anon_mapping * find_anon_mapping(struct transient *); void anon_init(void); Index: opd_mangling.c =================================================================== RCS file: /cvsroot/oprofile/oprofile/daemon/opd_mangling.c,v retrieving revision 1.20.4.4 retrieving revision 1.20.4.5 diff -u -p -d -r1.20.4.4 -r1.20.4.5 --- opd_mangling.c 24 Apr 2005 18:51:39 -0000 1.20.4.4 +++ opd_mangling.c 25 Apr 2005 20:40:35 -0000 1.20.4.5 @@ -27,6 +27,7 @@ #include "op_events.h" #include "op_libiberty.h" +#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -55,8 +56,7 @@ static char const * get_dep_name(struct static char * mangle_anon(struct anon_mapping const * anon) { - // FIXME - char * name = xmalloc(1024); + char * name = xmalloc(PATH_MAX); snprintf(name, 1024, "%u.0x%llx.0x%llx", (unsigned int)anon->tgid, anon->start, anon->end); return name; @@ -153,6 +153,7 @@ int opd_open_sample_file(odb_t * file, s /* locking sf will lock associated cg files too */ sfile_get(sf); + sfile_get(last); retry: err = odb_open(file, mangled, ODB_RDWR, sizeof(struct opd_header)); @@ -187,6 +188,7 @@ retry: out: sfile_put(sf); + sfile_put(last); free(mangled); return err; } Index: opd_sfile.c =================================================================== RCS file: /cvsroot/oprofile/oprofile/daemon/opd_sfile.c,v retrieving revision 1.29.4.4 retrieving revision 1.29.4.5 diff -u -p -d -r1.29.4.4 -r1.29.4.5 --- opd_sfile.c 24 Apr 2005 18:51:39 -0000 1.29.4.4 +++ opd_sfile.c 25 Apr 2005 20:40:35 -0000 1.29.4.5 @@ -227,6 +227,7 @@ lru: return sf; } + static void init_cg_id(cg_id * id, struct sfile const * sf) { id->cookie = sf->cookie; @@ -313,14 +314,39 @@ get_file(struct transient const * trans, } -static void -verbose_sample(char const * prefix, struct sfile * sf, vma_t pc, uint counter) +static void verbose_print_sample(struct sfile * sf, vma_t pc, uint counter) { - char const * name = verbose_cookie(sf->cookie); char const * app = verbose_cookie(sf->app_cookie); - printf("%s at 0x%llx(%u): %s(%llx), app %s(%llx), kernel %s\n", - prefix, pc, counter, name, sf->cookie, app, sf->app_cookie, - sf->kernel ? sf->kernel->name : "no"); + printf("0x%llx(%u): ", pc, counter); + if (sf->anon) { + printf("anon (tgid %u, 0x%llx-0x%llx), ", + (unsigned int)sf->anon->tgid, + sf->anon->start, sf->anon->end); + } else if (sf->kernel) { + printf("kern (name %s, 0x%llx-0x%llx), ", sf->kernel->name, + sf->kernel->start, sf->kernel->end); + } else { + printf("%s(%llx), ", verbose_cookie(sf->cookie), sf->cookie); + } + printf("app %s(%llx)", app, sf->app_cookie); +} + + +static void verbose_sample(struct transient const * trans, vma_t pc) +{ + printf("Sample "); + verbose_print_sample(trans->current, pc, trans->event); + printf("\n"); +} + + +static void +verbose_arc(struct transient const * trans, vma_t from, vma_t to) +{ + printf("Arc "); + verbose_print_sample(trans->current, from, trans->event); + printf(" -> 0x%llx", to); + printf("\n"); } @@ -348,7 +374,7 @@ static void sfile_log_arc(struct transie to -= trans->last->anon->start; if (varcs) - verbose_sample("Arc", trans->current, to, 0); + verbose_arc(trans, from, to); if (!file) { opd_stats[OPD_LOST_SAMPLEFILE]++; @@ -391,7 +417,7 @@ void sfile_log_sample(struct transient c pc -= trans->current->anon->start; if (vsamples) - verbose_sample("Sample", trans->current, pc, trans->event); + verbose_sample(trans, pc); if (!file) { opd_stats[OPD_LOST_SAMPLEFILE]++; @@ -524,8 +550,13 @@ void sfile_close_files(void) } -/* this value probably doesn't matter too much */ #define LRU_AMOUNT 1000 + +/* + * Clear out older sfiles. Note the current sfiles we're using + * will not be present in this list, due to sfile_get/put() pairs + * around the caller of this. + */ int sfile_lru_clear(void) { struct list_head * pos; @@ -549,13 +580,15 @@ int sfile_lru_clear(void) void sfile_get(struct sfile * sf) { - list_del(&sf->lru); + if (sf) + list_del(&sf->lru); } void sfile_put(struct sfile * sf) { - list_add_tail(&sf->lru, &lru_list); + if (sf) + list_add_tail(&sf->lru, &lru_list); } Index: opd_trans.c =================================================================== RCS file: /cvsroot/oprofile/oprofile/daemon/opd_trans.c,v retrieving revision 1.10.4.2 retrieving revision 1.10.4.3 diff -u -p -d -r1.10.4.2 -r1.10.4.3 --- opd_trans.c 24 Apr 2005 18:51:39 -0000 1.10.4.2 +++ opd_trans.c 25 Apr 2005 20:40:35 -0000 1.10.4.3 @@ -26,6 +26,29 @@ extern size_t kernel_pointer_size; + +void clear_trans_last(struct transient * trans) +{ + trans->last = NULL; + trans->last_anon = NULL; +} + + +void clear_trans_current(struct transient * trans) +{ + trans->current = NULL; + trans->anon = NULL; +} + + +void update_trans_last(struct transient * trans) +{ + trans->last = trans->current; + trans->last_anon = trans->anon; + trans->last_pc = trans->pc; +} + + static inline int is_escape_code(uint64_t code) { return kernel_pointer_size == 4 ? code == ~0LU : code == ~0LLU; @@ -84,7 +107,7 @@ static void opd_put_sample(struct transi /* sfile can change at each sample for kernel */ if (trans->in_kernel != 0) - trans->current = NULL; + clear_trans_current(trans); if (!trans->in_kernel && trans->cookie == NO_COOKIE) trans->anon = find_anon_mapping(trans); @@ -111,10 +134,7 @@ out: if (trans->tracing == TRACING_START) trans->tracing = TRACING_ON; - /* used for callgraph only */ - trans->last = trans->current; - trans->last_anon = trans->anon; - trans->last_pc = trans->pc; + update_trans_last(trans); } @@ -127,7 +147,7 @@ static void code_unknown(struct transien static void code_ctx_switch(struct transient * trans) { - trans->current = NULL; + clear_trans_current(trans); if (!enough_remaining(trans, 5)) { trans->remaining = 0; @@ -142,8 +162,6 @@ static void code_ctx_switch(struct trans pop_buffer_value(trans); pop_buffer_value(trans); trans->tgid = pop_buffer_value(trans); - trans->anon = NULL; - trans->last_anon = NULL; if (vmisc) { char const * app = find_cookie(trans->app_cookie); @@ -156,7 +174,7 @@ static void code_ctx_switch(struct trans static void code_cpu_switch(struct transient * trans) { - trans->current = NULL; + clear_trans_current(trans); if (!enough_remaining(trans, 1)) { trans->remaining = 0; @@ -170,7 +188,7 @@ static void code_cpu_switch(struct trans static void code_cookie_switch(struct transient * trans) { - trans->current = NULL; + clear_trans_current(trans); if (!enough_remaining(trans, 1)) { trans->remaining = 0; @@ -178,7 +196,6 @@ static void code_cookie_switch(struct tr } trans->cookie = pop_buffer_value(trans); - trans->anon = NULL; if (vmisc) { char const * name = verbose_cookie(trans->cookie); @@ -192,7 +209,7 @@ static void code_kernel_enter(struct tra { verbprintf(vmisc, "KERNEL_ENTER_SWITCH to kernel\n"); trans->in_kernel = 1; - trans->current = NULL; + clear_trans_current(trans); /* subtlety: we must keep trans->cookie cached, * even though it's meaningless for the kernel - * we won't necessarily get a cookie switch on @@ -205,7 +222,8 @@ static void code_kernel_exit(struct tran { verbprintf(vmisc, "KERNEL_EXIT_SWITCH to user-space\n"); trans->in_kernel = 0; - trans->current = NULL; + clear_trans_current(trans); + clear_trans_last(trans); } @@ -213,7 +231,8 @@ static void code_module_loaded(struct tr { verbprintf(vmodule, "MODULE_LOADED_CODE\n"); opd_reread_module_info(); - trans->current = NULL; + clear_trans_current(trans); + clear_trans_last(trans); } Index: opd_trans.h =================================================================== RCS file: /cvsroot/oprofile/oprofile/daemon/opd_trans.h,v retrieving revision 1.2.4.1 retrieving revision 1.2.4.2 diff -u -p -d -r1.2.4.1 -r1.2.4.2 --- opd_trans.h 24 Apr 2005 06:46:26 -0000 1.2.4.1 +++ opd_trans.h 25 Apr 2005 20:40:35 -0000 1.2.4.2 @@ -51,4 +51,10 @@ struct transient { void opd_process_samples(char const * buffer, size_t count); +/** used when we need to clear data that's been freed */ +void clear_trans_last(struct transient * trans); + +/** used when we need to clear data that's been freed */ +void clear_trans_current(struct transient * trans); + #endif /* OPD_TRANS_H */ |