/*--------------------------------------------------------------------*/ /*--- Callgrind ---*/ /*--- ct_threads.c ---*/ /*--------------------------------------------------------------------*/ /* This file is part of Callgrind, a Valgrind tool for call tracing. Copyright (C) 2002-2007, 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 /* 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;istates) ); 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=%llu", 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;ientry[i] = 0; es->sp = 0; } void CLG_(copy_current_exec_stack)(exec_stack* dst) { Int i; dst->sp = current_states.sp; for(i=0;ientry[i] = current_states.entry[i]; } void CLG_(set_current_exec_stack)(exec_stack* dst) { Int i; current_states.sp = dst->sp; for(i=0;ientry[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; }