/* PURPOSE: (Graceful Simulation termination with timing data output.) PROGRAMMERS: (((Robert W. Bailey) (LinCom) (Feb 1991) (v1.0) (Initial Release.)) ((Robert W. Bailey) (LinCom) (April 1992) (--) (Realtime clock)) ((Scott Killingsworth) (LinCom) (September 1997) (--) (Issue #1016))) */ #include #include #include #include #include #include "trick/Executive.hh" #include "trick/ExecutiveException.hh" #include "trick/message_proto.h" #include "trick/message_type.h" #include "trick/release.h" #include "trick/SysThread.hh" #include "trick/VariableServer.hh" extern Trick::VariableServer * the_vs ; /** @design -# Set the mode to Exit. Requirement [@ref r_exec_mode_3] -# Wait for asynchronous or AMF threads to complete if the user has specified to do so -# The scheduler calls shutdown jobs Requirement [@ref r_exec_discrete_4] -# If an exception is caught during calling the shutdown jobs the exception is ignored as we are already terminating. -# The scheduler prints a simulation shutdown message -# The scheduler kills all child threads -# Return the exception_return value. (Set to 0 if no exception thrown.) */ int Trick::Executive::shutdown() { double sim_elapsed_time; double cpu_time; double user_cpu_time, kernal_cpu_time; double sim_to_cpu; unsigned int ii; int process_id = 0 ; struct rusage cpu_usage_buf ; double sim_mem; long v_context_switch_run, iv_context_switch_run; SIM_MODE prev_mode = mode ; /* Set mode to ExitMode. */ mode = ExitMode; /* If we made it past initialization */ if ( prev_mode != Initialization ) { /* Wait for asynchronous threads to complete if the user has specified to wait. */ for (ii = 1; ii < threads.size() ; ii++) { if ( (threads[ii]->process_type == Trick::PROCESS_TYPE_ASYNC_CHILD || threads[ii]->process_type == Trick::PROCESS_TYPE_AMF_CHILD) && threads[ii]->shutdown_wait_async ) { message_publish(MSG_INFO, "Waiting for asynchronous or AMF thread %d to finish\n", ii ) ; while (threads[ii]->child_complete == false) { RELEASE_1(); } } } } /* Stop new connections to the Variable Server. */ the_vs->shutdownConnections(); try { /* Call the shutdown jobs. */ if (initialization_complete == true) { shutdown_queue.reset_curr_index() ; while ( (curr_job = shutdown_queue.get_next_job()) != NULL ) { curr_job->call() ; } } } catch (Trick::ExecutiveException & ex ) { /* Handle exception type Trick::ExecutiveException. Set the file name and error message. Return the exception return code. */ except_return = ex.ret_code ; except_file += std::string(" then exception caught in ") + ex.file ; except_message += std::string(" then exception Message: ") + ex.message ; } Trick::SysThread::ensureAllShutdown(); getrusage(RUSAGE_SELF, &cpu_usage_buf); cpu_time = ((double) cpu_usage_buf.ru_utime.tv_sec) + ((double) cpu_usage_buf.ru_utime.tv_usec / 1000000.0); /* Get memory usage in MB for the calling process. Note that ru_maxrss returns long value in bytes on Mac and kilobytes on Linux. */ #if __APPLE__ sim_mem = (double)cpu_usage_buf.ru_maxrss / (1024 * 1024); #else sim_mem = (double)cpu_usage_buf.ru_maxrss / 1024; #endif /* Calculate simulation elapsed sim time and actual cpu time */ sim_elapsed_time = get_sim_time() - sim_start; user_cpu_time = cpu_time - user_cpu_start; /* */ cpu_time = ((double) cpu_usage_buf.ru_stime.tv_sec) + ((double) cpu_usage_buf.ru_stime.tv_usec / 1000000.0); kernal_cpu_time = cpu_time - kernal_cpu_start; if ((user_cpu_time + kernal_cpu_time) <= 1e-6) { sim_to_cpu = 1e8; } else { sim_to_cpu = sim_elapsed_time / (user_cpu_time + kernal_cpu_time); } /* Calculate voluntary and involuntary context switch usage during run */ v_context_switch_run = cpu_usage_buf.ru_nvcsw - v_context_switch_init ; iv_context_switch_run = cpu_usage_buf.ru_nivcsw - iv_context_switch_init ; /* Print a shutdown message. */ message_publish(MSG_NORMAL , "\n" "SIMULATION TERMINATED IN\n" " PROCESS: %d\n" " ROUTINE: %s\n" " DIAGNOSTIC: %s\n\n" " SIMULATION START TIME: %12.3f\n" " SIMULATION STOP TIME: %12.3f\n" " SIMULATION ELAPSED TIME: %12.3f\n" " USER CPU TIME USED: %12.3f\n" " SYSTEM CPU TIME USED: %12.3f\n" " SIMULATION / CPU TIME: %12.3f\n" " INITIALIZATION USER CPU TIME: %12.3f\n" " INITIALIZATION SYSTEM CPU TIME: %12.3f\n" " SIMULATION RAM USAGE: %12.3fMB\n" " (External program RAM usage not included!)\n" " VOLUNTARY CONTEXT SWITCHES (INIT): %12ld\n" "INVOLUNTARY CONTEXT SWITCHES (INIT): %12ld\n" " VOLUNTARY CONTEXT SWITCHES (RUN): %12ld\n" " INVOLUNTARY CONTEXT SWITCHES (RUN): %12ld\n\n", process_id, except_file.c_str(), except_message.c_str(), sim_start, get_sim_time(), sim_elapsed_time, user_cpu_time, kernal_cpu_time, sim_to_cpu, user_cpu_init, kernal_cpu_init, sim_mem, v_context_switch_init, iv_context_switch_init, v_context_switch_run, iv_context_switch_run) ; /* Kill all threads. */ for (ii = 1; ii < threads.size() ; ii++) { if ( threads[ii]->running ) { pthread_cancel(threads[ii]->get_pthread_id()) ; } #if ! ( __APPLE__ || __CYGWIN__ || __Lynx__ ) // Even Linux is hanging on this call right now... hmmm. //pthread_join(threads[ii]->thread_id, (void **) NULL); #endif } /* Return the exception_return value. This defaults to 0 if no exceptions were thrown. */ return(except_return) ; }