/*--------------------------------------------------------------------*/ /*--- 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;istates) ); 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;ientry[i] = 0; es->sp = 0; } void SK_(copy_current_exec_stack)(exec_stack* dst) { Int i; dst->sp = current_states.sp; for(i=0;ientry[i] = current_states.entry[i]; } void SK_(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() { 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; }