/*--------------------------------------------------------------------*/
/*--- Callgrind                                                    ---*/
/*---                                                 ct_threads.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"

/* forward decls */
static exec_state* exec_state_save();
static exec_state* exec_state_restore();
static exec_state* push_exec_state(int);
static exec_state* top_exec_state();

static exec_stack current_states;


/*------------------------------------------------------------*/
/*--- Support for multi-threading                          ---*/
/*------------------------------------------------------------*/


/*
 * For Valgrind, MT is cooperative (no preemting in our code),
 * so we don't need locks...
 *
 * Per-thread data:
 *  - BBCCs
 *  - call stack
 *  - call hash
 *  - event counters: last, current
 *
 * Even when ignoring MT, we need this functions to set up some
 * datastructures for the process (= Thread 1).
 */

/* current running thread */
ThreadId SK_(current_tid);

static thread_info* thread[VG_N_THREADS];

thread_info** SK_(get_threads)()
{
  return thread;
}

thread_info* SK_(get_current_thread)()
{
  return thread[SK_(current_tid)];
}

void SK_(init_threads)()
{
    Int i;
    for(i=0;i<VG_N_THREADS;i++)
	thread[i] = 0;
    SK_(current_tid) = VG_INVALID_THREADID;
}

/* switches through all threads and calls func */
void SK_(forall_threads)(void (*func)(thread_info*))
{
  Int t, orig_tid = SK_(current_tid);

  for(t=1;t<VG_N_THREADS;t++) {
    if (!thread[t]) continue;
    SK_(switch_thread)(t);
    (*func)(thread[t]);
  }
  SK_(switch_thread)(orig_tid);
}


static thread_info* new_thread()
{
    thread_info* t;

    t = VG_(malloc)(sizeof(thread_info));

    /* init state */
    SK_(init_exec_stack)( &(t->states) );
    SK_(init_call_stack)( &(t->calls) );
    SK_(init_fn_stack)  ( &(t->fns) );
    /* t->states.entry[0]->cxt = SK_(get_cxt)(t->fns.bottom); */

    /* event counters */
    t->lastdump_cost   = SK_(get_eventset_cost)( SK_(sets).full );
    t->sighandler_cost = SK_(get_eventset_cost)( SK_(sets).full );
    SK_(init_cost)( SK_(sets).full, t->lastdump_cost );
    SK_(init_cost)( SK_(sets).full, t->sighandler_cost );

    /* init data containers */
    SK_(init_fn_array)( &(t->fn_active) );
    SK_(init_bbcc_hash)( &(t->bbccs) );
    SK_(init_jcc_hash)( &(t->jccs) );
    
    return t;
}


void SK_(switch_thread)(ThreadId tid)
{
  if (tid == SK_(current_tid)) return;

  CT_DEBUG(1, "switch_thread: %d -> %d\n", SK_(current_tid), tid);

  if (SK_(current_tid) != VG_INVALID_THREADID) {    
    /* save thread state */
    thread_info* t = thread[SK_(current_tid)];

    CT_ASSERT(t != 0);

    /* current context (including signal handler contexts) */
    exec_state_save();
    SK_(copy_current_exec_stack)( &(t->states) );
    SK_(copy_current_call_stack)( &(t->calls) );
    SK_(copy_current_fn_stack)  ( &(t->fns) );

    SK_(copy_current_fn_array) ( &(t->fn_active) );
    /* If we cumulate costs of threads, use TID 1 for all jccs/bccs */
    if (!SK_(clo).separate_threads) t = thread[1];
    SK_(copy_current_bbcc_hash)( &(t->bbccs) );
    SK_(copy_current_jcc_hash) ( &(t->jccs) );
  }

  SK_(current_tid) = tid;
  CT_ASSERT(tid < VG_N_THREADS);

  if (tid != VG_INVALID_THREADID) {
    thread_info* t;

    /* load thread state */

    if (thread[tid] == 0) thread[tid] = new_thread();
    t = thread[tid];

    /* current context (including signal handler contexts) */
    SK_(set_current_exec_stack)( &(t->states) );
    exec_state_restore();
    SK_(set_current_call_stack)( &(t->calls) );
    SK_(set_current_fn_stack)  ( &(t->fns) );
    
    SK_(set_current_fn_array)  ( &(t->fn_active) );
    /* If we cumulate costs of threads, use TID 1 for all jccs/bccs */
    if (!SK_(clo).separate_threads) t = thread[1];
    SK_(set_current_bbcc_hash) ( &(t->bbccs) );
    SK_(set_current_jcc_hash)  ( &(t->jccs) );
  }
}


void SK_(run_thread)(ThreadId tid)
{
    /* check for dumps needed */
    static ULong last_bbs_done = 0;
    static Char buf[512];

    if (SK_(clo).dump_every_bb >0) {
       if (VG_(bbs_done) - last_bbs_done > SK_(clo).dump_every_bb) {
           VG_(sprintf)(buf, "--dumps=%d", SK_(clo).dump_every_bb);
	   SK_(dump_profile)(buf, False);
           last_bbs_done = VG_(bbs_done);
       }
    }

    SK_(check_command)();
    
    /* now check for thread switch */
    SK_(switch_thread)(tid);
}

void SK_(pre_signal)(ThreadId tid, Int sigNum, Bool alt_stack)
{
    exec_state *es;

    CT_DEBUG(0, "pre_signal(TID %d, sig %d, alt_st %s)\n",
	     tid, sigNum, alt_stack ? "yes":"no");

    /* switch to the thread the handler runs in */
    SK_(run_thread)(tid);

    /* save current execution state */
    exec_state_save();

    /* setup current state for a spontaneous call */
    SK_(init_exec_state)( &SK_(current_state) );
    SK_(push_cxt)(0);

    /* setup new cxtinfo struct for this signal handler */
    es = push_exec_state(sigNum);
    SK_(init_cost)( SK_(sets).full, es->cost);
    SK_(current_state).cost = es->cost;
    es->call_stack_bottom = SK_(current_call_stack).sp;

    SK_(current_state).sig = sigNum;
}

void SK_(post_signal)(ThreadId tid, Int sigNum)
{
    exec_state* es;
    Int fn_number, *pactive;

    CT_DEBUG(0, "post_signal(TID %d, sig %d)\n",
	     tid, sigNum);

    CT_ASSERT(tid == SK_(current_tid));
    CT_ASSERT(sigNum == SK_(current_state).sig);

    /* Unwind call stack of this signal handler.
     * This should only be needed at finalisation time
     */
    es = top_exec_state();
    CT_ASSERT(es != 0);
    while(SK_(current_call_stack).sp > es->call_stack_bottom)
      SK_(pop_call_stack)();
    
    if (SK_(current_state).cxt) {
      /* correct active counts */
      fn_number = SK_(current_state).cxt->fn[0]->number;
      pactive = SK_(get_fn_entry)(fn_number);
      (*pactive)--;
      CT_DEBUG(0, "  set active count of %s back to %d\n",
	       SK_(current_state).cxt->fn[0]->name, *pactive);
    }

    if (SK_(current_fn_stack).top > SK_(current_fn_stack).bottom) {
      /* set fn_stack_top back */
      SK_(current_fn_stack).top--;
      CT_ASSERT(*(SK_(current_fn_stack).top) == 0);
      if (SK_(current_fn_stack).top > SK_(current_fn_stack).bottom)
	SK_(current_fn_stack).top--;
    }

    /* sum up costs */
    CT_ASSERT(SK_(current_state).cost == es->cost);
    SK_(add_and_zero_cost)( SK_(sets).full,
			    thread[SK_(current_tid)]->sighandler_cost,
			    SK_(current_state).cost );
    
    /* restore previous context */
    es->sig = -1;
    current_states.sp--;
    es = top_exec_state();
    SK_(current_state).sig = es->sig;
    exec_state_restore();
}



/*------------------------------------------------------------*/
/*--- Execution states in a thread & signal handlers       ---*/
/*------------------------------------------------------------*/

/* Each thread can be interrupted by a signal handler, and they
 * themselve again. But as there's no scheduling among handlers
 * of the same thread, we don't need additional stacks.
 * So storing execution contexts and
 * adding separators in the callstack(needed to not intermix normal/handler
 * functions in contexts) should be enough.
 */

/* not initialized: call_stack_bottom, sig */
void SK_(init_exec_state)(exec_state* es)
{
  es->collect = SK_(clo).collect_atstart;
  es->cxt  = 0;
  es->jmpkind = -1;
  es->bbcc = 0;
  es->nonskipped = 0;
}


static exec_state* new_exec_state(Int sigNum)
{
    exec_state* es;
    es = (exec_state*) VG_(malloc)(sizeof(exec_state));

    /* allocate real cost space: needed as incremented by
     * simulation functions */
    es->cost       = SK_(get_eventset_cost)(SK_(sets).full);
    SK_(init_cost)( SK_(sets).full, es->cost );

    SK_(init_exec_state)(es);
    es->sig        = sigNum;
    es->call_stack_bottom  = 0;

    return es;
}

void SK_(init_exec_stack)(exec_stack* es)
{
  Int i;

  /* The first element is for the main thread */
  es->entry[0] = new_exec_state(0);
  for(i=1;i<MAX_SIGHANDLERS;i++)
    es->entry[i] = 0;
  es->sp = 0;
}

void SK_(copy_current_exec_stack)(exec_stack* dst)
{
  Int i;

  dst->sp = current_states.sp;
  for(i=0;i<MAX_SIGHANDLERS;i++)
    dst->entry[i] = current_states.entry[i];
}

void SK_(set_current_exec_stack)(exec_stack* dst)
{
  Int i;

  current_states.sp = dst->sp;
  for(i=0;i<MAX_SIGHANDLERS;i++)
    current_states.entry[i] = dst->entry[i];
}


/* Get top context info struct of current thread */
static exec_state* top_exec_state()
{
  Int sp = current_states.sp;
  exec_state* es;

  CT_ASSERT((sp >= 0) && (sp < MAX_SIGHANDLERS));
  es = current_states.entry[sp];
  CT_ASSERT(es != 0);
  return es;
}

/* Allocates a free context info structure for a new entered
 * signal handler, putting it on the context stack.
 * Returns a pointer to the structure.
 */
static exec_state* push_exec_state(int sigNum)
{   
  Int sp;
  exec_state* es;

  current_states.sp++;
  sp = current_states.sp;

  CT_ASSERT((sigNum > 0) && (sigNum <= VKI_KNSIG));
  CT_ASSERT((sp > 0) && (sp < MAX_SIGHANDLERS));
  es = current_states.entry[sp];
  if (!es) {
    es = new_exec_state(sigNum);
    current_states.entry[sp] = es;
  }
  else
    es->sig = sigNum;

  return es;
}

/* Save current context to top cxtinfo struct */
static exec_state* exec_state_save()
{
  exec_state* es = top_exec_state();

  es->cxt        = SK_(current_state).cxt;
  es->collect    = SK_(current_state).collect;
  es->jmpkind    = SK_(current_state).jmpkind;
  es->bbcc       = SK_(current_state).bbcc;
  es->nonskipped = SK_(current_state).nonskipped;

  CT_DEBUGIF(1) {
    CT_DEBUG(1, "  cxtinfo_save(sig %d): collect %s, jmpKind %d\n",
	     es->sig, es->collect ? "Yes": "No", es->jmpkind);	
    SK_(print_bbcc)(-9, es->bbcc, False);
    SK_(print_cost)(-9, SK_(sets).full, es->cost);
  }

  /* signal number does not need to be saved */
  CT_ASSERT(SK_(current_state).sig == es->sig);

  return es;
}

static exec_state* exec_state_restore()
{
  exec_state* es = top_exec_state();
  
  SK_(current_state).cxt     = es->cxt;
  SK_(current_state).collect = es->collect;
  SK_(current_state).jmpkind = es->jmpkind;
  SK_(current_state).bbcc    = es->bbcc;
  SK_(current_state).nonskipped = es->nonskipped;
  SK_(current_state).cost    = es->cost;
  SK_(current_state).sig     = es->sig;
    
  CT_DEBUGIF(1) {
	CT_DEBUG(1, "  exec_state_restore(sig %d): collect %s, jmpKind %d\n",
		 es->sig, es->collect ? "Yes": "No", es->jmpkind);
	SK_(print_bbcc)(-9, es->bbcc, False);
	SK_(print_cxt)(-9, es->cxt, 0);
	SK_(print_cost)(-9, SK_(sets).full, es->cost);
  }

  return es;
}



syntax highlighted by Code2HTML, v. 0.9.1