/*
   This file is part of Callgrind, a Valgrind skin for call graph
   profiling programs.

   Copyright (C) 2002-2004, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)

   This skin is derived from and contains lot of code from Cachegrind
   Copyright (C) 2002 Nicholas Nethercote (njn25@cam.ac.uk)

   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"

/* If debugging mode of, dummy functions are provided (see below)
 */
#if CT_ENABLE_DEBUG

/*------------------------------------------------------------*/
/*--- Debug output helpers                                 ---*/
/*------------------------------------------------------------*/

static void print_indent(int s)
{
    /* max of 40 spaces */
    char sp[] = "                                        ";
    if (s>40) s=40;
    VG_(printf)(sp+40-s);
}

void SK_(print_bb)(int s, BB* bb)
{
    if (s<0) {
	s = -s;
	print_indent(s);
    }

    VG_(printf)("BB 0x%08x (Obj '%s')", bb_addr(bb), bb->obj->name);
}

static
void print_mangled_cxt(Context* cxt, int rec_index)
{
    int i;

    if (!cxt)
      VG_(printf)("(none)");
    else {
      VG_(printf)("%s", cxt->fn[0]->name);
      if (rec_index >0)
	VG_(printf)("'%d", rec_index +1);
      for(i=1;i<cxt->size;i++)
	VG_(printf)("'%s", cxt->fn[i]->name);
    }
}



void SK_(print_cxt)(int s, Context* cxt, int rec_index)
{
  if (s<0) {
    s = -s;
    print_indent(s);
  }
  
  if (cxt) {
    Int *pactive = SK_(get_fn_entry)(cxt->fn[0]->number);
    CT_ASSERT(rec_index < cxt->fn[0]->separate_recursions);
    
    VG_(printf)("Cxt %d" ,cxt->base_number + rec_index);
    if (*pactive>0)
      VG_(printf)(" [active=%d]", *pactive);
    VG_(printf)(": ");	
    print_mangled_cxt(cxt, rec_index);
    VG_(printf)("\n");
  }
  else
    VG_(printf)("(no context)\n");
}


void SK_(print_bbcc)(int s, BBCC* bbcc, Bool jumpaddr)
{
  BB* bb;

  if (s<0) {
    s = -s;
    print_indent(s);
  }
  
  if (!bbcc) {
    VG_(printf)("BBCC 0x0\n");
    return;
  }
 
  bb = bbcc->bb;
  CT_ASSERT(bb!=0);
 
  if (jumpaddr)
    VG_(printf)("%s +0x%x=0x%x, ",
	      bb->obj->name + bb->obj->last_slash_pos,
	      bb->jmp_offset, bb_jmpaddr(bb));
  else
    VG_(printf)("%s +0x%x=0x%x, ",
		bb->obj->name + bb->obj->last_slash_pos,
		bb->offset, bb_addr(bb));
  SK_(print_cxt)(s+8, bbcc->cxt, bbcc->rec_index);
}


void SK_(print_cost)(int s, EventSet* es, ULong* c)
{
  Int i, pos;

    if (s<0) {
	s = -s;
	print_indent(s);
    }

    if (!es) {
      VG_(printf)("Cost (Nothing, EventSet not set)\n");
      return;
    }
    if (!c) {
      VG_(printf)("Cost (Null, EventSet %s)\n", es->name);
      return;
    }

    if (es->size == 0) {
      VG_(printf)("Cost (Nothing, EventSet %s with len 0)\n", es->name);
      return;
    } 

    pos = s;
    pos += VG_(printf)("Cost %s: %s %llu", es->name, es->e[0].type->name, c[0]);

    i = 1;
    while(i<es->size) {
      if (pos > 70) {
	VG_(printf)(",\n");
	print_indent(s+5);
	pos = s+5;
      }
      else
	pos += VG_(printf)(", ");
      pos += VG_(printf)("%s %llu", es->e[i].type->name, c[i]);
      i++;
    }
    VG_(printf)("\n");
}


void SK_(print_short_jcc)(jCC* jcc)
{
    if (jcc)
	VG_(printf)("%x => %x [%llu/%llu,%llu,%llu]",
		    bb_jmpaddr(jcc->from->bb),
		    bb_addr(jcc->to->bb),
		    jcc->call_counter,
		    jcc->cost ? jcc->cost[SK_(sets).off_sim_Ir]:0,
		    jcc->cost ? jcc->cost[SK_(sets).off_sim_Dr]:0,
		    jcc->cost ? jcc->cost[SK_(sets).off_sim_Dw]:0);
    else
	VG_(printf)("[Skipped JCC]");
}

void SK_(print_jcc)(int s, jCC* jcc)
{
    if (s<0) {
	s = -s;
	print_indent(s);
    }

    if (!jcc) {
	VG_(printf)("JCC to skipped function\n");
	return;
    }
    VG_(printf)("JCC 0x%x from ", jcc);
    SK_(print_bbcc)(s+9, jcc->from, True);
    print_indent(s+4);    
    VG_(printf)("to   ");
    SK_(print_bbcc)(s+9, jcc->to, False);
    print_indent(s+4);
    VG_(printf)("Calls %llu\n", jcc->call_counter);
    print_indent(s+4);
    SK_(print_cost)(s+9, SK_(sets).full, jcc->cost);
}

/* dump out the current call stack */
void SK_(print_stackentry)(int s, int sp)
{
    call_entry* ce;

    if (s<0) {
	s = -s;
	print_indent(s);
    }

    ce = SK_(get_call_entry)(sp);
    VG_(printf)("[%-2d] ESP 0x%x", sp, ce->esp);
    if (ce->nonskipped)
	VG_(printf)(" NonSkipped BB 0x%x / %s",
		    bb_addr(ce->nonskipped->bb),
		    ce->nonskipped->cxt->fn[0]->name);
    VG_(printf)("\n");
    print_indent(s+5);
    SK_(print_jcc)(5,ce->jcc);
}

/* debug output */
static void print_call_stack()
{
    int c;

    VG_(printf)("Call Stack:\n");
    for(c=0;c<SK_(current_call_stack).sp;c++)
      SK_(print_stackentry)(-2, c);
}

void SK_(print_bbcc_fn)(BBCC* bbcc)
{
    obj_node* obj;

    if (!bbcc) {
	VG_(printf)("%08x", 0);
	return;
    }

    VG_(printf)("%08x/%c  %d:", bb_addr(bbcc->bb), 
		(bbcc->bb->sect_kind == Vg_SectText) ? 'T' :
		(bbcc->bb->sect_kind == Vg_SectData) ? 'D' :
		(bbcc->bb->sect_kind == Vg_SectBSS) ? 'B' :
		(bbcc->bb->sect_kind == Vg_SectGOT) ? 'G' :
		(bbcc->bb->sect_kind == Vg_SectPLT) ? 'P' : 'U',
		bbcc->cxt->base_number+bbcc->rec_index);
    print_mangled_cxt(bbcc->cxt, bbcc->rec_index);

    obj = bbcc->cxt->fn[0]->file->obj;
    if (obj->name[0])
	VG_(printf)(" %s", obj->name+obj->last_slash_pos);

    if (VG_(strcmp)(bbcc->cxt->fn[0]->file->name, "???") !=0) {
	VG_(printf)(" %s", bbcc->cxt->fn[0]->file->name);
	if ((bbcc->cxt->fn[0] == bbcc->bb->fn) && (bbcc->bb->line>0))
	    VG_(printf)(":%d", bbcc->bb->line);
    }
}	

/* dump out an address with source info if available */
void SK_(print_addr)(Addr addr)
{
    Char fl_buf[FILENAME_LEN];
    Char fn_buf[FN_NAME_LEN];
    const UChar* obj_name;
    SegInfo* si;
    int ln, i=0, opos=0;
	
    if (addr == 0) {
	VG_(printf)("%08x", addr);
	return;
    }

    SK_(get_debug_info)(addr, fl_buf, fn_buf, &ln, &si);

    if (VG_(strcmp)(fn_buf,"???")==0)
	VG_(printf)("%x", addr);
    else
	VG_(printf)("%x %s", addr, fn_buf);

    if (si) {
      obj_name = VG_(seg_filename)(si);
      if (obj_name) {
	while(obj_name[i]) {
	  if (obj_name[i]=='/') opos = i+1;
	  i++;
	}
	if (obj_name[0])
	  VG_(printf)(" %s", obj_name+opos);
      }
    }

    if (ln>0)
    	VG_(printf)(" (%s:%u)", fl_buf,ln);
}

void SK_(print_addr_ln)(Addr addr)
{
  SK_(print_addr)(addr);
  VG_(printf)("\n");
}

static ULong bb_written = 0;

void SK_(print_bbno)(void)
{
  if (bb_written != SK_(stat).bb_executions) {
    bb_written = SK_(stat).bb_executions;
    VG_(printf)("BB# %llu\n",SK_(stat).bb_executions);
  }
}

void SK_(print_context)(void)
{
  BBCC* bbcc;

  CT_DEBUG(0,"In tid %d [%d] ",
	   SK_(current_tid),  SK_(current_call_stack).sp);
  bbcc =  SK_(current_state).bbcc;
  print_mangled_cxt(SK_(current_state).cxt,
		    bbcc ? bbcc->rec_index : 0);
  VG_(printf)("\n");
}


#else /* CT_ENABLE_DEBUG */

void SK_(print_bbno)(void) {}
void SK_(print_context)(void) {}
void SK_(print_jcc)(int s, jCC* jcc) {}
void SK_(print_bbcc)(int s, BBCC* bbcc, Bool b) {}
void SK_(print_bbcc_fn)(BBCC* bbcc) {}
void SK_(print_cost)(int s, EventSet* es, ULong* cost) {}
void SK_(print_bb)(int s, BB* bb) {}
void SK_(print_cxt)(int s, Context* cxt, int rec_index) {}
void SK_(print_short_jcc)(jCC* jcc) {}
void SK_(print_stackentry)(int s, int sp) {}
void SK_(print_addr)(Addr addr) {}
void SK_(print_addr_ln)(Addr addr) {}

#endif


syntax highlighted by Code2HTML, v. 0.9.1