/*--------------------------------------------------------------------*/ /*--- Callgrind ---*/ /*--- ct_context.c ---*/ /*--------------------------------------------------------------------*/ /* This file is part of Callgrind, a Valgrind tool for call tracing. Copyright (C) 2002-2004, Josef Weidendorfer (Josef.Weidendorfer@gmx.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The GNU General Public License is contained in the file COPYING. */ #include "ct_include.h" /*------------------------------------------------------------*/ /*--- Context operations ---*/ /*------------------------------------------------------------*/ #define N_FNSTACK_INITIAL_ENTRIES 500 #define N_CXT_INITIAL_ENTRIES 2537 fn_stack SK_(current_fn_stack); void SK_(init_fn_stack)(fn_stack* s) { CT_ASSERT(s != 0); s->size = N_FNSTACK_INITIAL_ENTRIES; s->bottom = (fn_node**) VG_(malloc)(s->size * sizeof(fn_node*)); s->top = s->bottom; s->bottom[0] = 0; } void SK_(copy_current_fn_stack)(fn_stack* dst) { CT_ASSERT(dst != 0); dst->size = SK_(current_fn_stack).size; dst->bottom = SK_(current_fn_stack).bottom; dst->top = SK_(current_fn_stack).top; } void SK_(set_current_fn_stack)(fn_stack* s) { CT_ASSERT(s != 0); SK_(current_fn_stack).size = s->size; SK_(current_fn_stack).bottom = s->bottom; SK_(current_fn_stack).top = s->top; } static cxt_hash cxts; void SK_(init_cxt_table)() { Int i; cxts.size = N_CXT_INITIAL_ENTRIES; cxts.entries = 0; cxts.table = (Context**) VG_(malloc)(cxts.size * sizeof(Context*)); for (i = 0; i < cxts.size; i++) cxts.table[i] = 0; } cxt_hash* SK_(get_cxt_hash)() { return &cxts; } /* double size of cxt table */ static void resize_cxt_table() { Int i, new_size, conflicts1 = 0, conflicts2 = 0; Context **new_table, *curr, *next; UInt new_hash; new_size = 2* cxts.size +3; new_table = (Context**) VG_(malloc)(new_size * sizeof(Context*)); if (!new_table) return; for (i = 0; i < new_size; i++) new_table[i] = NULL; for (i = 0; i < cxts.size; i++) { if (cxts.table[i] == NULL) continue; curr = cxts.table[i]; while (NULL != curr) { next = curr->next; new_hash = curr->hash % new_size; curr->next = new_table[new_hash]; new_table[new_hash] = curr; if (curr->next) { conflicts1++; if (curr->next->next) conflicts2++; } curr = next; } } VG_(free)(cxts.table); CT_DEBUG(0, "Resize Context Hash: %d => %d (entries %d, conflicts %d/%d)\n", cxts.size, new_size, cxts.entries, conflicts1, conflicts2); cxts.size = new_size; cxts.table = new_table; SK_(stat).cxt_hash_resizes++; } __inline__ static UInt cxt_hash_val(fn_node** fn, UInt size) { UInt hash = 0; UInt count = size; while(*fn != 0) { hash = (hash<<7) + (hash>>25) + (UInt)(*fn); fn--; count--; if (count==0) break; } return hash; } __inline__ static Bool is_cxt(UInt hash, fn_node** fn, Context* cxt) { int count; fn_node** cxt_fn; if (hash != cxt->hash) return False; count = cxt->size; cxt_fn = &(cxt->fn[0]); while((*fn != 0) && (count>0)) { if (*cxt_fn != *fn) return False; fn--; cxt_fn++; count--; } return True; } /** * Allocate new Context structure */ static Context* new_cxt(fn_node** fn) { Context* new; UInt idx, hash, offset; int size, recs; fn_node* top_fn; CT_ASSERT(fn); top_fn = *fn; if (top_fn == 0) return 0; size = top_fn->separate_callers +1; recs = top_fn->separate_recursions; if (recs<1) recs=1; /* check fill degree of context hash table and resize if needed (>80%) */ cxts.entries++; if (10 * cxts.entries / cxts.size > 8) resize_cxt_table(); new = VG_(malloc)(sizeof(Context)+sizeof(fn_node*)*(size-1)); hash = 0; offset = 0; while(*fn != 0) { hash = (hash<<7) + (hash>>25) + (UInt)(*fn); new->fn[offset] = *fn; offset++; fn--; if (offset >= size) break; } if (offset < size) size = offset; new->size = size; new->base_number = SK_(stat).context_counter; new->hash = hash; SK_(stat).context_counter += recs; SK_(stat).distinct_contexts++; /* insert into Context hash table */ idx = hash % cxts.size; new->next = cxts.table[idx]; cxts.table[idx] = new; #if CT_ENABLE_DEBUG CT_DEBUGIF(3) { VG_(printf)(" new_cxt: "); SK_(print_cxt)(12, new, 0); } #endif return new; } /* get the Context structure for current context */ Context* SK_(get_cxt)(fn_node** fn) { Context* cxt; UInt hash, size, idx; CT_ASSERT(fn != 0); if (*fn == 0) return 0; size = (*fn)->separate_callers+1; CT_DEBUG(5, "+ get_cxt(fn '%s'): size %d\n", (*fn)->name, size); hash = cxt_hash_val(fn, size); if ( ((cxt = (*fn)->last_cxt) != 0) && is_cxt(hash, fn, cxt)) { CT_DEBUG(5, "- get_cxt: 0x%p\n", cxt); return cxt; } SK_(stat).cxt_lru_misses++; idx = hash % cxts.size; cxt = cxts.table[idx]; while(cxt) { if (is_cxt(hash,fn,cxt)) break; cxt = cxt->next; } if (!cxt) cxt = new_cxt(fn); (*fn)->last_cxt = cxt; CT_DEBUG(5, "- get_cxt: %p\n", cxt); return cxt; } /** * Change execution context by calling a new function from current context * */ void SK_(push_cxt)(fn_node* fn) { call_stack* cs = &SK_(current_call_stack); Int fn_entries; /* save old context on stack (even if not changed at all!) */ CT_ASSERT(cs->sp < cs->size); CT_ASSERT(cs->entry[cs->sp].cxt == 0); cs->entry[cs->sp].cxt = SK_(current_state).cxt; cs->entry[cs->sp].fn_sp = SK_(current_fn_stack).top - SK_(current_fn_stack).bottom; if (*(SK_(current_fn_stack).top) == fn) return; if (fn && (fn->group>0) && ((*(SK_(current_fn_stack).top))->group == fn->group)) return; /* resizing needed ? */ fn_entries = SK_(current_fn_stack).top - SK_(current_fn_stack).bottom; if (fn_entries == SK_(current_fn_stack).size-1) { int new_size = SK_(current_fn_stack).size *2; fn_node** new = VG_(malloc)(new_size * sizeof(fn_node*)); int i; for(i=0;i %d (pushing '%s')\n", SK_(current_fn_stack).size, new_size, fn ? fn->name : (Char*)"0x0"); SK_(current_fn_stack).size = new_size; } if (*(SK_(current_fn_stack).top) == 0) { Int *pactive; /* this is first function: increment its active count */ CT_ASSERT(fn != 0); pactive = SK_(get_fn_entry)(fn->number); (*pactive)++; } SK_(current_fn_stack).top++; *(SK_(current_fn_stack).top) = fn; SK_(current_state).cxt = SK_(get_cxt)(SK_(current_fn_stack).top); CT_DEBUG(5, " push_cxt(fn '%s'): %d\n", fn ? fn->name : (Char*)"0x0", SK_(current_fn_stack).top - SK_(current_fn_stack).bottom); }