/*--------------------------------------------------------------------*/ /*--- Callgrind ---*/ /*--- ct_bb.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" /*------------------------------------------------------------*/ /*--- Basic block (BB) operations ---*/ /*------------------------------------------------------------*/ /* BB hash, resizable */ bb_hash bbs; void SK_(init_bb_hash)() { Int i; bbs.size = 8437; bbs.entries = 0; bbs.table = (BB**) VG_(malloc)(bbs.size * sizeof(BB*)); for (i = 0; i < bbs.size; i++) bbs.table[i] = NULL; } bb_hash* SK_(get_bb_hash)() { return &bbs; } /* The hash stores BBs according to * - ELF object (is 0 for code in anonymous mapping) * - BB base as object file offset */ static __inline__ UInt hash_bb(obj_node* obj, UInt offset, UInt size) { return (((Addr)obj) + offset) % size; } /* double size of bb table */ static void resize_bb_table() { Int i, new_size, conflicts1 = 0, conflicts2 = 0; BB **new_table, *curr, *next; UInt new_hash; new_size = 2* bbs.size +3; new_table = (BB**) VG_(malloc)(new_size * sizeof(BB*)); if (!new_table) return; for (i = 0; i < new_size; i++) new_table[i] = NULL; for (i = 0; i < bbs.size; i++) { if (bbs.table[i] == NULL) continue; curr = bbs.table[i]; while (NULL != curr) { next = curr->next; new_hash = hash_bb(curr->obj, curr->offset, 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)(bbs.table); CT_DEBUG(0, "Resize BB Hash: %d => %d (entries %d, conflicts %d/%d)\n", bbs.size, new_size, bbs.entries, conflicts1, conflicts2); bbs.size = new_size; bbs.table = new_table; SK_(stat).bb_hash_resizes++; } /** * Allocate new BB structure (including space for event type list) * Not initialized: * - instr_len, cost_count, instr[] */ static BB* new_bb(obj_node* obj, UInt offset, Int instr_count) { BB* new; UInt new_hash; /* check fill degree of jcc hash table and resize if needed (>80%) */ bbs.entries++; if (10 * bbs.entries / bbs.size > 8) resize_bb_table(); new = (BB*) VG_(malloc)(sizeof(BB) + instr_count * sizeof(InstrInfo)); new->obj = obj; new->offset = offset; new->instr_count = instr_count; new->jmp_offset = 0; new->instr_len = 0; new->cost_count = 0; new->sect_kind = VG_(seg_sect_kind)(offset + obj->offset); new->fn = 0; new->line = 0; new->is_entry = 0; new->bbcc_list = 0; new->last_bbcc = 0; /* insert into BB hash table */ new_hash = hash_bb(obj, offset, bbs.size); new->next = bbs.table[new_hash]; bbs.table[new_hash] = new; SK_(stat).distinct_bbs++; #if CT_ENABLE_DEBUG CT_DEBUGIF(3) { VG_(printf)(" new_bb (instr %d) [now %d]: ", instr_count, SK_(stat).distinct_bbs); SK_(print_bb)(0, new); VG_(printf)("\n"); } #endif SK_(get_fn_node)(new); return new; } /* get the BB structure for a BB start address */ static __inline__ BB* lookup_bb(obj_node* obj, UInt offset) { BB* bb; Int hash; hash = hash_bb(obj, offset, bbs.size); bb = bbs.table[hash]; while(bb) { if ((bb->obj == obj) && (bb->offset == offset)) break; bb = bb->next; } CT_DEBUG(5, " lookup_bb (Obj %s, off %x): %p\n", obj->name, offset, bb); return bb; } /* Get the BB structure for a BB start address. * If the BB has to be created, the UCodeBlock is needed to * compute the event type list for costs, and seen_before is * set to False. Otherwise, seen_before is set to True. * * BBs are never discarded. There are 2 cases where this function * is called from SK_(instrument)() and a BB already exists: * - The instrumented version was removed from Valgrinds TT cache * - The ELF object of the BB was unmapped and mapped again. * This involves a possibly different address, but is handled by * looking up a BB keyed by (obj_node, file offset). */ BB* SK_(get_bb)(Addr addr, UCodeBlock* cb_in, Bool *seen_before) { BB* bb; obj_node* obj; Int i, n_instrs; UInstr* u_in; CT_DEBUG(5, "+ get_bb(BB 0x%x)\n", addr); obj = SK_(get_obj_node)( VG_(get_obj)(addr) ); bb = lookup_bb(obj, addr - obj->offset); if (cb_in) { n_instrs = 1; // start at 1 because last x86 instr has no INCEIP for (i = 0; i < VG_(get_num_instrs)(cb_in); i++) { u_in = VG_(get_instr)(cb_in, i); if (INCEIP == u_in->opcode) n_instrs++; } } else { /* Artifical BB without real code. * Needed when returning to an unknown function. */ n_instrs = 0; } *seen_before = bb ? True : False; if (*seen_before) { sk_assert(bb->instr_count == n_instrs ); SK_(stat).bb_retranslations++; CT_DEBUG(5, "- get_bb(BB %p): seen before.\n", addr); return bb; } bb = new_bb(obj, addr - obj->offset, n_instrs); CT_DEBUG(5, "- get_bb(BB %p)\n", addr); return bb; }