Callgrind merge: code


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@5780 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/callgrind/threads.c b/callgrind/threads.c
new file mode 100644
index 0000000..eda9d0c
--- /dev/null
+++ b/callgrind/threads.c
@@ -0,0 +1,456 @@
+/*--------------------------------------------------------------------*/
+/*--- 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 "global.h"
+
+#include <pub_tool_threadstate.h>
+
+/* forward decls */
+static exec_state* exec_state_save(void);
+static exec_state* exec_state_restore(void);
+static exec_state* push_exec_state(int);
+static exec_state* top_exec_state(void);
+
+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 CLG_(current_tid);
+
+static thread_info* thread[VG_N_THREADS];
+
+thread_info** CLG_(get_threads)()
+{
+  return thread;
+}
+
+thread_info* CLG_(get_current_thread)()
+{
+  return thread[CLG_(current_tid)];
+}
+
+void CLG_(init_threads)()
+{
+    Int i;
+    for(i=0;i<VG_N_THREADS;i++)
+	thread[i] = 0;
+    CLG_(current_tid) = VG_INVALID_THREADID;
+}
+
+/* switches through all threads and calls func */
+void CLG_(forall_threads)(void (*func)(thread_info*))
+{
+  Int t, orig_tid = CLG_(current_tid);
+
+  for(t=1;t<VG_N_THREADS;t++) {
+    if (!thread[t]) continue;
+    CLG_(switch_thread)(t);
+    (*func)(thread[t]);
+  }
+  CLG_(switch_thread)(orig_tid);
+}
+
+
+static
+thread_info* new_thread(void)
+{
+    thread_info* t;
+
+    t = (thread_info*) CLG_MALLOC(sizeof(thread_info));
+
+    /* init state */
+    CLG_(init_exec_stack)( &(t->states) );
+    CLG_(init_call_stack)( &(t->calls) );
+    CLG_(init_fn_stack)  ( &(t->fns) );
+    /* t->states.entry[0]->cxt = CLG_(get_cxt)(t->fns.bottom); */
+
+    /* event counters */
+    t->lastdump_cost   = CLG_(get_eventset_cost)( CLG_(sets).full );
+    t->sighandler_cost = CLG_(get_eventset_cost)( CLG_(sets).full );
+    CLG_(init_cost)( CLG_(sets).full, t->lastdump_cost );
+    CLG_(init_cost)( CLG_(sets).full, t->sighandler_cost );
+
+    /* init data containers */
+    CLG_(init_fn_array)( &(t->fn_active) );
+    CLG_(init_bbcc_hash)( &(t->bbccs) );
+    CLG_(init_jcc_hash)( &(t->jccs) );
+    
+    return t;
+}
+
+
+void CLG_(switch_thread)(ThreadId tid)
+{
+  if (tid == CLG_(current_tid)) return;
+
+  CLG_DEBUG(0, ">> thread %d (was %d)\n", tid, CLG_(current_tid));
+
+  if (CLG_(current_tid) != VG_INVALID_THREADID) {    
+    /* save thread state */
+    thread_info* t = thread[CLG_(current_tid)];
+
+    CLG_ASSERT(t != 0);
+
+    /* current context (including signal handler contexts) */
+    exec_state_save();
+    CLG_(copy_current_exec_stack)( &(t->states) );
+    CLG_(copy_current_call_stack)( &(t->calls) );
+    CLG_(copy_current_fn_stack)  ( &(t->fns) );
+
+    CLG_(copy_current_fn_array) ( &(t->fn_active) );
+    /* If we cumulate costs of threads, use TID 1 for all jccs/bccs */
+    if (!CLG_(clo).separate_threads) t = thread[1];
+    CLG_(copy_current_bbcc_hash)( &(t->bbccs) );
+    CLG_(copy_current_jcc_hash) ( &(t->jccs) );
+  }
+
+  CLG_(current_tid) = tid;
+  CLG_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) */
+    CLG_(set_current_exec_stack)( &(t->states) );
+    exec_state_restore();
+    CLG_(set_current_call_stack)( &(t->calls) );
+    CLG_(set_current_fn_stack)  ( &(t->fns) );
+    
+    CLG_(set_current_fn_array)  ( &(t->fn_active) );
+    /* If we cumulate costs of threads, use TID 1 for all jccs/bccs */
+    if (!CLG_(clo).separate_threads) t = thread[1];
+    CLG_(set_current_bbcc_hash) ( &(t->bbccs) );
+    CLG_(set_current_jcc_hash)  ( &(t->jccs) );
+  }
+}
+
+
+void CLG_(run_thread)(ThreadId tid)
+{
+    /* check for dumps needed */
+    static ULong bbs_done = 0;
+    static Char buf[512];
+
+    if (CLG_(clo).dump_every_bb >0) {
+       if (CLG_(stat).bb_executions - bbs_done > CLG_(clo).dump_every_bb) {
+           VG_(sprintf)(buf, "--dump-every-bb=%d", CLG_(clo).dump_every_bb);
+	   CLG_(dump_profile)(buf, False);
+           bbs_done = CLG_(stat).bb_executions;
+       }
+    }
+
+    CLG_(check_command)();
+    
+    /* now check for thread switch */
+    CLG_(switch_thread)(tid);
+}
+
+void CLG_(pre_signal)(ThreadId tid, Int sigNum, Bool alt_stack)
+{
+    exec_state *es;
+
+    CLG_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 */
+    CLG_(run_thread)(tid);
+
+    /* save current execution state */
+    exec_state_save();
+
+    /* setup current state for a spontaneous call */
+    CLG_(init_exec_state)( &CLG_(current_state) );
+    CLG_(push_cxt)(0);
+
+    /* setup new cxtinfo struct for this signal handler */
+    es = push_exec_state(sigNum);
+    CLG_(init_cost)( CLG_(sets).full, es->cost);
+    CLG_(current_state).cost = es->cost;
+    es->call_stack_bottom = CLG_(current_call_stack).sp;
+
+    CLG_(current_state).sig = sigNum;
+}
+
+/* Run post-signal if the stackpointer for call stack is at
+ * the bottom in current exec state (e.g. a signal handler)
+ *
+ * Called from CLG_(pop_call_stack)
+ */
+void CLG_(run_post_signal_on_call_stack_bottom)()
+{
+    exec_state* es = top_exec_state();
+    CLG_ASSERT(es != 0);
+    CLG_ASSERT(CLG_(current_state).sig >0);
+
+    if (CLG_(current_call_stack).sp == es->call_stack_bottom)
+	CLG_(post_signal)( CLG_(current_tid), CLG_(current_state).sig );
+}
+
+void CLG_(post_signal)(ThreadId tid, Int sigNum)
+{
+    exec_state* es;
+    UInt fn_number, *pactive;
+
+    CLG_DEBUG(0, ">> post_signal(TID %d, sig %d)\n",
+	     tid, sigNum);
+
+    CLG_ASSERT(tid == CLG_(current_tid));
+    CLG_ASSERT(sigNum == CLG_(current_state).sig);
+
+    /* Unwind call stack of this signal handler.
+     * This should only be needed at finalisation time
+     */
+    es = top_exec_state();
+    CLG_ASSERT(es != 0);
+    while(CLG_(current_call_stack).sp > es->call_stack_bottom)
+      CLG_(pop_call_stack)();
+    
+    if (CLG_(current_state).cxt) {
+      /* correct active counts */
+      fn_number = CLG_(current_state).cxt->fn[0]->number;
+      pactive = CLG_(get_fn_entry)(fn_number);
+      (*pactive)--;
+      CLG_DEBUG(0, "  set active count of %s back to %d\n",
+	       CLG_(current_state).cxt->fn[0]->name, *pactive);
+    }
+
+    if (CLG_(current_fn_stack).top > CLG_(current_fn_stack).bottom) {
+	/* set fn_stack_top back.
+	 * top can point to 0 if nothing was executed in the signal handler;
+	 * this is possible at end on unwinding handlers.
+	 */
+	if (*(CLG_(current_fn_stack).top) != 0) {
+	    CLG_(current_fn_stack).top--;
+	    CLG_ASSERT(*(CLG_(current_fn_stack).top) == 0);
+	}
+      if (CLG_(current_fn_stack).top > CLG_(current_fn_stack).bottom)
+	CLG_(current_fn_stack).top--;
+    }
+
+    /* sum up costs */
+    CLG_ASSERT(CLG_(current_state).cost == es->cost);
+    CLG_(add_and_zero_cost)( CLG_(sets).full,
+			    thread[CLG_(current_tid)]->sighandler_cost,
+			    CLG_(current_state).cost );
+    
+    /* restore previous context */
+    es->sig = -1;
+    current_states.sp--;
+    es = top_exec_state();
+    CLG_(current_state).sig = es->sig;
+    exec_state_restore();
+
+    /* There is no way to reliable get the thread ID we are switching to
+     * after this handler returns. So we sync with actual TID at start of
+     * CLG_(setup_bb)(), which should be the next for callgrind.
+     */
+}
+
+
+
+/*------------------------------------------------------------*/
+/*--- Execution states in a thread & signal handlers       ---*/
+/*------------------------------------------------------------*/
+
+/* Each thread can be interrupted by a signal handler, and they
+ * themselves 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 CLG_(init_exec_state)(exec_state* es)
+{
+  es->collect = CLG_(clo).collect_atstart;
+  es->cxt  = 0;
+  es->jmps_passed = 0;
+  es->bbcc = 0;
+  es->nonskipped = 0;
+}
+
+
+static exec_state* new_exec_state(Int sigNum)
+{
+    exec_state* es;
+    es = (exec_state*) CLG_MALLOC(sizeof(exec_state));
+
+    /* allocate real cost space: needed as incremented by
+     * simulation functions */
+    es->cost       = CLG_(get_eventset_cost)(CLG_(sets).full);
+    CLG_(init_cost)( CLG_(sets).full, es->cost );
+
+    CLG_(init_exec_state)(es);
+    es->sig        = sigNum;
+    es->call_stack_bottom  = 0;
+
+    return es;
+}
+
+void CLG_(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 CLG_(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 CLG_(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(void)
+{
+  Int sp = current_states.sp;
+  exec_state* es;
+
+  CLG_ASSERT((sp >= 0) && (sp < MAX_SIGHANDLERS));
+  es = current_states.entry[sp];
+  CLG_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;
+
+  CLG_ASSERT((sigNum > 0) && (sigNum <= _VKI_NSIG));
+  CLG_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(void)
+{
+  exec_state* es = top_exec_state();
+
+  es->cxt         = CLG_(current_state).cxt;
+  es->collect     = CLG_(current_state).collect;
+  es->jmps_passed = CLG_(current_state).jmps_passed;
+  es->bbcc        = CLG_(current_state).bbcc;
+  es->nonskipped  = CLG_(current_state).nonskipped;
+
+  CLG_DEBUGIF(1) {
+    CLG_DEBUG(1, "  cxtinfo_save(sig %d): collect %s, jmps_passed %d\n",
+	     es->sig, es->collect ? "Yes": "No", es->jmps_passed);	
+    CLG_(print_bbcc)(-9, es->bbcc, False);
+    CLG_(print_cost)(-9, CLG_(sets).full, es->cost);
+  }
+
+  /* signal number does not need to be saved */
+  CLG_ASSERT(CLG_(current_state).sig == es->sig);
+
+  return es;
+}
+
+static
+exec_state* exec_state_restore(void)
+{
+  exec_state* es = top_exec_state();
+  
+  CLG_(current_state).cxt     = es->cxt;
+  CLG_(current_state).collect = es->collect;
+  CLG_(current_state).jmps_passed = es->jmps_passed;
+  CLG_(current_state).bbcc    = es->bbcc;
+  CLG_(current_state).nonskipped = es->nonskipped;
+  CLG_(current_state).cost    = es->cost;
+  CLG_(current_state).sig     = es->sig;
+    
+  CLG_DEBUGIF(1) {
+	CLG_DEBUG(1, "  exec_state_restore(sig %d): collect %s, jmps_passed %d\n",
+		  es->sig, es->collect ? "Yes": "No", es->jmps_passed);
+	CLG_(print_bbcc)(-9, es->bbcc, False);
+	CLG_(print_cxt)(-9, es->cxt, 0);
+	CLG_(print_cost)(-9, CLG_(sets).full, es->cost);
+  }
+
+  return es;
+}
+