/*--------------------------------------------------------------------*/
/*--- 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;
}
syntax highlighted by Code2HTML, v. 0.9.1