Logo Search packages:      
Sourcecode: callgrind version File versions

bb.c

/*--------------------------------------------------------------------*/
/*--- Callgrind                                                    ---*/
/*---                                                         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 "global.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 %p (instr %d) [now %d]: ",
             new, 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;
  SegInfo* si;
  UInt current_offset;

  CT_DEBUG(5, "+ get_bb(BB 0x%x)\n", addr);

  si = VG_(get_obj)(addr);
  obj = SK_(get_obj_node)( si );

  /* Update symbol offset in object if remapped */
  current_offset = si ? VG_(seg_sym_offset)(si):0;
  if (obj->offset != current_offset) {
    Addr current_start = si ? VG_(seg_start)(si) : 0;

    CT_DEBUG(0, "Mapping changed for '%s': %x -> %x\n",
           obj->name, obj->start, current_start);
    /* Size should be the same, and offset diff == start diff */
    CT_ASSERT( obj->size == (si ? VG_(seg_size)(si) : 0) );
    CT_ASSERT( obj->start - current_start ==
             obj->offset - current_offset );
    obj->offset = current_offset;
    obj->start = current_start;
  }

  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) {
    if (bb->instr_count != n_instrs) {
      VG_(message)(Vg_DebugMsg, 
               "ERROR: BB Retranslation Mismatch at BB %x", addr);
      VG_(message)(Vg_DebugMsg,
               "  new: Obj %s, Off %x, BBOff %x, Instrs %d",
               obj->name, obj->offset,
               addr - obj->offset, n_instrs);
      VG_(message)(Vg_DebugMsg,
               "  old: Obj %s, Off %x, BBOff %x, Instrs %d",
               bb->obj->name, bb->obj->offset,
               bb->offset, bb->instr_count);
      CT_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;
}

Generated by  Doxygen 1.6.0   Back to index