diff --git a/mcx/mcx.c b/mcx/mcx.c index c5c0f43..c180997 100644 --- a/mcx/mcx.c +++ b/mcx/mcx.c @@ -316,14 +316,20 @@ McxStatus RunMCX(int argc, char *argv[]) { goto cleanup; } + mcx_signal_handler_set_function("ConfigSetupFromCmdLine"); retVal = config->SetupFromCmdLine(config, argc, argv); + mcx_signal_handler_unset_function(); if (retVal == RETURN_ERROR) { goto cleanup; } + mcx_signal_handler_set_function("SetupLogFiles"); SetupLogFiles(config->logFile, config->writeAllLogFile); + mcx_signal_handler_unset_function(); logInitialized = 1; + mcx_signal_handler_set_function("ConfigSetupFromEnvironment"); retVal = config->SetupFromEnvironment(config); + mcx_signal_handler_unset_function(); if (RETURN_OK != retVal) { mcx_log(LOG_INFO, "Setting up configuration from environment failed"); retVal = RETURN_ERROR; @@ -337,14 +343,18 @@ McxStatus RunMCX(int argc, char *argv[]) { goto cleanup; } + mcx_signal_handler_set_function("ReaderSetup"); retVal = reader->Setup(reader, config->modelFile, config); + mcx_signal_handler_unset_function(); if (retVal == RETURN_ERROR) { mcx_log(LOG_ERROR, "Input reader setup failed"); retVal = RETURN_ERROR; goto cleanup; } + mcx_signal_handler_set_function("ReaderRead"); mcxInput = reader->Read(reader, config->modelFile); + mcx_signal_handler_unset_function(); if (!mcxInput) { mcx_log(LOG_ERROR, "Parsing of input file failed"); retVal = RETURN_ERROR; @@ -352,7 +362,9 @@ McxStatus RunMCX(int argc, char *argv[]) { } element = (InputElement *) mcxInput; + mcx_signal_handler_set_function("ConfigSetupFromInput"); retVal = config->SetupFromInput(config, mcxInput->config); + mcx_signal_handler_unset_function(); if (RETURN_OK != retVal) { mcx_log(LOG_ERROR, "Setting up configuration from input file failed"); retVal = RETURN_ERROR; @@ -387,20 +399,26 @@ McxStatus RunMCX(int argc, char *argv[]) { mcx_cpu_time_get(&clock_read_begin); mcx_time_get(&time_read_begin); + mcx_signal_handler_set_function("TaskRead"); retVal = task->Read(task, mcxInput->task); + mcx_signal_handler_unset_function(); if (RETURN_OK != retVal) { retVal = RETURN_ERROR; goto cleanup; } + mcx_signal_handler_set_function("ModelRead"); retVal = model->Read(model, mcxInput->model); + mcx_signal_handler_unset_function(); if (RETURN_OK != retVal) { retVal = RETURN_ERROR; goto cleanup; } object_destroy(mcxInput); + mcx_signal_handler_set_function("ReaderCleanup"); reader->Cleanup(reader); + mcx_signal_handler_unset_function(); object_destroy(reader); mcx_cpu_time_get(&clock_read_end); @@ -421,19 +439,25 @@ McxStatus RunMCX(int argc, char *argv[]) { mcx_cpu_time_get(&clock_setup_begin); mcx_time_get(&time_setup_begin); + mcx_signal_handler_set_function("TaskSetup"); retVal = task->Setup(task, model); + mcx_signal_handler_unset_function(); if (RETURN_OK != retVal) { retVal = RETURN_ERROR; goto cleanup; } + mcx_signal_handler_set_function("ModelSetup"); retVal = model->Setup(model); + mcx_signal_handler_unset_function(); if (RETURN_OK != retVal) { retVal = RETURN_ERROR; goto cleanup; } + mcx_signal_handler_set_function("TaskPrepareRun"); retVal = task->PrepareRun(task, model); + mcx_signal_handler_unset_function(); if (RETURN_OK != retVal) { retVal = RETURN_ERROR; goto cleanup; @@ -457,7 +481,9 @@ McxStatus RunMCX(int argc, char *argv[]) { mcx_cpu_time_get(&clock_init_begin); mcx_time_get(&time_init_begin); + mcx_signal_handler_set_function("TaskInitialize"); retVal = task->Initialize(task, model); + mcx_signal_handler_unset_function(); if (RETURN_OK != retVal) { retVal = RETURN_ERROR; goto cleanup; @@ -481,7 +507,9 @@ McxStatus RunMCX(int argc, char *argv[]) { mcx_log(LOG_INFO, " "); mcx_cpu_time_get(&clock_sim_begin); mcx_time_get(&time_sim_begin); + mcx_signal_handler_set_function("TaskRun"); retVal = task->Run(task, model); + mcx_signal_handler_unset_function(); if (RETURN_OK != retVal) { retVal = RETURN_ERROR; goto cleanup; @@ -512,6 +540,7 @@ McxStatus RunMCX(int argc, char *argv[]) { mcx_cpu_time_get(&clock_cleanup_begin); mcx_time_get(&time_cleanup_begin); + mcx_signal_handler_set_function("RunMCX:Cleanup"); object_destroy(task); object_destroy(model); object_destroy(config); @@ -554,6 +583,7 @@ McxStatus RunMCX(int argc, char *argv[]) { mcx_log(LOG_INFO, "**********************************************************************"); } + mcx_signal_handler_unset_function(); // RunMCX:Cleanup return retVal; } diff --git a/src/components/comp_fmu.c b/src/components/comp_fmu.c index 8dd698c..8496501 100644 --- a/src/components/comp_fmu.c +++ b/src/components/comp_fmu.c @@ -21,6 +21,7 @@ #include "objects/Map.h" #include "reader/model/components/specific_data/FmuInput.h" #include "util/string.h" +#include "util/signals.h" #ifdef __cplusplus extern "C" { @@ -229,10 +230,12 @@ static McxStatus Fmu1Initialize(Component * comp, size_t group, double startTime // Initialization Mode ComponentLog(comp, LOG_DEBUG, "fmiInitializeSlave"); + mcx_signal_handler_set_function("fmi1_import_initialize_slave"); status = fmi1_import_initialize_slave(fmu1->fmiImport, startTime, fmi1_false, 0.0); + mcx_signal_handler_unset_function(); if (fmi1_status_ok != status) { ComponentLog(comp, LOG_ERROR, "fmiInitializeSlave failed"); return RETURN_ERROR; @@ -288,7 +291,9 @@ static McxStatus Fmu1DoStep(Component * comp, size_t group, double time, double } // Do calculations + mcx_signal_handler_set_function("fmi1_import_do_step"); status = fmi1_import_do_step(fmu1->fmiImport, compFmu->lastCommunicationTimePoint, deltaTime, fmi1_true); + mcx_signal_handler_unset_function(); if (fmi1_status_ok == status) { // fine } else if (fmi1_status_discard == status) { @@ -905,12 +910,14 @@ static McxStatus Fmu2Initialize(Component * comp, size_t group, double startTime defaultTolerance = fmi2_import_get_default_experiment_tolerance(fmu2->fmiImport); compFmu->lastCommunicationTimePoint = startTime; + mcx_signal_handler_set_function("fmi2_import_setup_experiment"); status = fmi2_import_setup_experiment(fmu2->fmiImport, fmi2_false, /* toleranceDefine */ defaultTolerance, startTime, /* startTime */ fmi2_false, /* stopTimeDefined */ 0.0 /* stopTime */); + mcx_signal_handler_unset_function(); if (fmi2_status_ok != status) { ComponentLog(comp, LOG_ERROR, "SetupExperiment failed"); @@ -918,7 +925,9 @@ static McxStatus Fmu2Initialize(Component * comp, size_t group, double startTime } // Initialization Mode + mcx_signal_handler_set_function("fmi2_import_enter_initialization_mode"); status = fmi2_import_enter_initialization_mode(fmu2->fmiImport); + mcx_signal_handler_unset_function(); if (fmi2_status_ok != status) { ComponentLog(comp, LOG_ERROR, "Could not enter Initialization Mode"); return RETURN_ERROR; @@ -961,7 +970,9 @@ static McxStatus Fmu2Initialize(Component * comp, size_t group, double startTime static McxStatus Fmu2ExitInitializationMode(Component *comp) { CompFMU *compFmu = (CompFMU*)comp; + mcx_signal_handler_set_function("fmi2_import_exit_initialization_mode"); fmi2_status_t status = fmi2_import_exit_initialization_mode(compFmu->fmu2.fmiImport); + mcx_signal_handler_unset_function(); if (fmi2_status_ok != status) { ComponentLog(comp, LOG_ERROR, "Could not exit Initialization Mode"); return RETURN_ERROR; @@ -987,14 +998,18 @@ static McxStatus Fmu2DoStep(Component * comp, size_t group, double time, double TimeSnapshotEnd(&comp->data->rtData.funcTimings.rtInput); // Do calculations + mcx_signal_handler_set_function("fmi2_import_do_step"); status = fmi2_import_do_step(fmu2->fmiImport, compFmu->lastCommunicationTimePoint, deltaTime, fmi2_true); + mcx_signal_handler_unset_function(); if (fmi2_status_ok == status) { // fine } else if (fmi2_status_discard == status) { fmi2_status_t fmi2status; fmi2_boolean_t isTerminated = fmi2_false; + mcx_signal_handler_set_function("fmi2_import_get_boolean_status"); fmi2status = fmi2_import_get_boolean_status(fmu2->fmiImport, fmi2_terminated, &isTerminated); + mcx_signal_handler_unset_function(); if (fmi2_status_ok == fmi2status) { if (fmi2_true == isTerminated) { comp->SetIsFinished(comp); @@ -1330,21 +1345,29 @@ static void CompFMUDestructor(CompFMU * compFmu) { // TOOD: Move this to the common struct destructors if (fmu1->fmiImport) { if (fmi1_true == fmu1->runOk) { + mcx_signal_handler_set_function("fmi1_import_terminate_slave"); fmi1_import_terminate_slave(fmu1->fmiImport); + mcx_signal_handler_unset_function(); } if (fmi1_true == fmu1->instantiateOk) { + mcx_signal_handler_set_function("fmi1_import_free_slave_instance"); fmi1_import_free_slave_instance(fmu1->fmiImport); + mcx_signal_handler_unset_function(); } } if (fmu2->fmiImport) { if (fmi2_true == fmu2->runOk) { + mcx_signal_handler_set_function("fmi2_import_terminate"); fmi2_import_terminate(fmu2->fmiImport); + mcx_signal_handler_unset_function(); } if (fmi2_true == fmu2->instantiateOk) { + mcx_signal_handler_set_function("fmi2_import_free_instance"); fmi2_import_free_instance(fmu2->fmiImport); + mcx_signal_handler_unset_function(); } } diff --git a/src/core/Component.c b/src/core/Component.c index eb05b1c..a533d95 100644 --- a/src/core/Component.c +++ b/src/core/Component.c @@ -503,7 +503,9 @@ McxStatus ComponentDoStep(Component * comp, size_t group, double time, double de if (comp->DoStep) { mcx_signal_handler_set_name(comp->GetName(comp)); + mcx_signal_handler_set_this_function(); retVal = comp->DoStep(comp, group, time, deltaTime, endTime, isNewStep); + mcx_signal_handler_unset_function(); mcx_signal_handler_unset_name(); if (RETURN_OK != retVal) { ComponentLog(comp, LOG_DEBUG, "Component specific DoStep failed"); diff --git a/src/core/Task.c b/src/core/Task.c index 33303dc..4882a82 100644 --- a/src/core/Task.c +++ b/src/core/Task.c @@ -84,19 +84,25 @@ static McxStatus TaskPrepareRun(Task * task, Model * model) { McxStatus retVal = RETURN_OK; #if defined (ENABLE_STORAGE) + mcx_signal_handler_set_function("ResultsStorageSetup"); retVal = task->storage->Setup(task->storage, task->timeStart); + mcx_signal_handler_unset_function(); if (RETURN_OK != retVal) { mcx_log(LOG_ERROR, "Could not setup storage"); return RETURN_ERROR; } + mcx_signal_handler_set_function("ResultsStorageAddModelComponents"); retVal = task->storage->AddModelComponents(task->storage, model->subModel); + mcx_signal_handler_unset_function(); if (RETURN_OK != retVal) { mcx_log(LOG_ERROR, "Could not setup component storage"); return RETURN_ERROR; } + mcx_signal_handler_set_function("ResultsStorageSetupBackends"); retVal = task->storage->SetupBackends(task->storage); + mcx_signal_handler_unset_function(); if (RETURN_OK != retVal) { mcx_log(LOG_ERROR, "Could not setup storage backends"); return RETURN_ERROR; @@ -154,7 +160,9 @@ static McxStatus TaskRun(Task * task, Model * model) { stepParams->timeEndStep += task->params->timeStepSize; } + mcx_signal_handler_set_function("StepTypeDoStep"); status = task->stepType->DoStep(task->stepType, stepParams, subModel); + mcx_signal_handler_unset_function(); if (status != RETURN_OK) { break; } diff --git a/src/fmu/common_fmu1.c b/src/fmu/common_fmu1.c index 446599d..dc72d00 100644 --- a/src/fmu/common_fmu1.c +++ b/src/fmu/common_fmu1.c @@ -15,6 +15,7 @@ #include "reader/model/parameters/ParameterInput.h" #include "util/string.h" +#include "util/signals.h" #include "fmilib.h" @@ -601,14 +602,19 @@ McxStatus Fmu1SetVariableArray(Fmu1CommonStruct * fmu, ObjectContainer * vals) { size_t i = 0; size_t numVars = vals->Size(vals); + mcx_signal_handler_set_this_function(); + for (i = 0; i < numVars; i++) { Fmu1Value * fmuVal = (Fmu1Value *) vals->At(vals, i); if (RETURN_ERROR == Fmu1SetVariable(fmu, fmuVal)) { + mcx_signal_handler_unset_function(); return RETURN_ERROR; } } + mcx_signal_handler_unset_function(); + return RETURN_OK; } @@ -672,15 +678,20 @@ McxStatus Fmu1GetVariableArray(Fmu1CommonStruct * fmu, ObjectContainer * vals) { size_t i = 0; size_t numVars = vals->Size(vals); + mcx_signal_handler_set_this_function(); + for (i = 0; i < numVars; i++) { McxStatus retVal = RETURN_OK; Fmu1Value * fmuVal = (Fmu1Value *) vals->At(vals, i); retVal = Fmu1GetVariable(fmu, fmuVal); if (RETURN_ERROR == retVal) { + mcx_signal_handler_unset_function(); return RETURN_ERROR; } } + mcx_signal_handler_unset_function(); + return RETURN_OK; } diff --git a/src/fmu/common_fmu2.c b/src/fmu/common_fmu2.c index 21c1040..2aaf9d7 100644 --- a/src/fmu/common_fmu2.c +++ b/src/fmu/common_fmu2.c @@ -21,6 +21,7 @@ #include "util/string.h" #include "util/stdlib.h" +#include "util/signals.h" #include "fmilib.h" @@ -191,11 +192,13 @@ McxStatus Fmu2CommonStructSetup(FmuCommon * common, Fmu2CommonStruct * fmu2, fmi } mcx_log(LOG_DEBUG, "%s: instantiatefn: %x", common->instanceName, fmi2_import_instantiate); + mcx_signal_handler_set_function("fmi2_import_instantiate"), jmStatus = fmi2_import_instantiate(fmu2->fmiImport, common->instanceName, fmu_type, NULL, fmi2_false /* visible */); + mcx_signal_handler_unset_function(); if (jm_status_error == jmStatus) { mcx_log(LOG_ERROR, "%s: Instantiate failed", common->instanceName); return RETURN_ERROR; @@ -713,15 +716,20 @@ McxStatus Fmu2SetVariableArray(Fmu2CommonStruct * fmu, ObjectContainer * vals) { McxStatus retVal = RETURN_OK; + mcx_signal_handler_set_this_function(); + for (i = 0; i < numVars; i++) { Fmu2Value * const fmuVal = (Fmu2Value *) vals->At(vals, i); retVal = Fmu2SetVariable(fmu, fmuVal); if (RETURN_ERROR == retVal) { + mcx_signal_handler_unset_function(); return RETURN_ERROR; } } + mcx_signal_handler_unset_function(); + return RETURN_OK; } @@ -785,8 +793,8 @@ McxStatus Fmu2GetVariable(Fmu2CommonStruct * fmu, Fmu2Value * fmuVal) { status = fmi2_import_get_integer(fmu->fmiImport, vrs, 3, vs); - binary.len = vs[2]; - binary.data = (char *) ((((long long)vs[1] & 0xffffffff) << 32) | (vs[0] & 0xffffffff)); + binary.len = vs[2]; + binary.data = (char *) ((((long long)vs[1] & 0xffffffff) << 32) | (vs[0] & 0xffffffff)); ChannelValueSetFromReference(chVal, &binary); @@ -820,16 +828,21 @@ McxStatus Fmu2GetVariableArray(Fmu2CommonStruct * fmu, ObjectContainer * vals) { McxStatus retVal = RETURN_OK; + mcx_signal_handler_set_this_function(); + for (i = 0; i < numVars; i++) { Fmu2Value * const fmuVal = (Fmu2Value *) vals->At(vals, i); retVal = Fmu2GetVariable(fmu, fmuVal); if (RETURN_ERROR == retVal) { mcx_log(LOG_ERROR, "FMU: Getting of variable array failed at element %u", i); + mcx_signal_handler_unset_function(); return RETURN_ERROR; } } + mcx_signal_handler_unset_function(); + return RETURN_OK; } diff --git a/src/util/linux/signals.c b/src/util/linux/signals.c index aecd9de..101e510 100644 --- a/src/util/linux/signals.c +++ b/src/util/linux/signals.c @@ -18,6 +18,11 @@ /* Thread local variable to store the name of the element which is * running inside the signal-handled block. */ static __thread const char * _signalThreadName = NULL; +static __thread const char * _signalFunctionName = NULL; +static __thread const char * _signalFunctionNameStack1 = NULL; +static __thread const char * _signalFunctionNameStack2 = NULL; +static __thread const char * _signalFunctionNameStack3 = NULL; +static __thread const char * _signalFunctionNameStack4 = NULL; #ifdef __cplusplus extern "C" { @@ -25,7 +30,11 @@ extern "C" { static void sigHandlerParam(int param) { if (_signalThreadName) { - mcx_log(LOG_ERROR, "The element %s caused an unrecoverable error. Shutting down.", _signalThreadName); + if (_signalFunctionName) { + mcx_log(LOG_ERROR, "The element %s caused an unrecoverable error in %s. Shutting down.", _signalThreadName, _signalFunctionName); + } else { + mcx_log(LOG_ERROR, "The element %s caused an unrecoverable error. Shutting down.", _signalThreadName); + } } else { mcx_log(LOG_ERROR, "An element caused an unrecoverable error. Shutting down."); } @@ -48,6 +57,33 @@ void mcx_signal_handler_unset_name(void) { _signalThreadName = NULL; } +void mcx_signal_handler_set_function(const char * functionName) { +#if defined(MCX_DEBUG) + if (_signalFunctionNameStack4 != NULL) { + mcx_log(LOG_ERROR, "Signal handler function callstack overflow!"); + exit(1); // I guess there is a better way to handle this + } +#endif + _signalFunctionNameStack4 = _signalFunctionNameStack3; + _signalFunctionNameStack3 = _signalFunctionNameStack2; + _signalFunctionNameStack2 = _signalFunctionNameStack1; + _signalFunctionNameStack1 = _signalFunctionName; + _signalFunctionName = functionName; +} + +void mcx_signal_handler_unset_function(void) { +#if defined(MCX_DEBUG) + if (_signalFunctionName == NULL) { + mcx_log(LOG_WARNING, "Signal handler function callstack empty. Cannot pop non-existing element."); + } +#endif + _signalFunctionName = _signalFunctionNameStack1; + _signalFunctionNameStack1 = _signalFunctionNameStack2; + _signalFunctionNameStack2 = _signalFunctionNameStack3; + _signalFunctionNameStack3 = _signalFunctionNameStack4; + _signalFunctionNameStack4 = NULL; +} + void mcx_signal_handler_enable(void) { static struct sigaction sigHandlerSEGV; @@ -71,6 +107,10 @@ void mcx_signal_handler_disable(void) { _signalThreadName = NULL; } +const char * mcx_signal_handler_get_function_name(void) { + return _signalFunctionName; +} + #ifdef __cplusplus } /* closing brace for extern "C" */ diff --git a/src/util/signals.h b/src/util/signals.h index f468ca7..b1a3ee0 100644 --- a/src/util/signals.h +++ b/src/util/signals.h @@ -23,6 +23,15 @@ void mcx_signal_handler_enable(void); void mcx_signal_handler_set_name(const char * threadName); void mcx_signal_handler_unset_name(void); +/** + * Sets and unsets the current functions name for usage in signal handler messages + * + * Note: __func__ is part of C99 standard (ISO/IEC 9899:1999), section 6.4.2.2 + */ +#define mcx_signal_handler_set_this_function() mcx_signal_handler_set_function(__func__) +void mcx_signal_handler_set_function(const char * functionName); +void mcx_signal_handler_unset_function(void); + /** * Deletes the handler for SIGSEGV */ @@ -38,6 +47,7 @@ void mcx_signal_handler_sigint(int param); */ int mcx_signal_handler_is_interrupted(void); +const char * mcx_signal_handler_get_function_name(void); #ifdef __cplusplus } /* closing brace for extern "C" */ diff --git a/src/util/win/signals.c b/src/util/win/signals.c index e584175..156b3b5 100644 --- a/src/util/win/signals.c +++ b/src/util/win/signals.c @@ -21,6 +21,11 @@ /* Thread local variable to store the name of the element which is * running inside the signal-handled block. */ __declspec( thread ) static const char * _signalThreadName = NULL; +__declspec( thread ) static const char * _signalFunctionName = NULL; +__declspec( thread ) static const char * _signalFunctionNameStack1 = NULL; +__declspec( thread ) static const char * _signalFunctionNameStack2 = NULL; +__declspec( thread ) static const char * _signalFunctionNameStack3 = NULL; +__declspec( thread ) static const char * _signalFunctionNameStack4 = NULL; #ifdef __cplusplus extern "C" { @@ -28,7 +33,11 @@ extern "C" { static LONG WINAPI HandleException(PEXCEPTION_POINTERS exception) { if (_signalThreadName) { - mcx_log(LOG_ERROR, "The element %s caused an unrecoverable error.", _signalThreadName); + if (_signalFunctionName) { + mcx_log(LOG_ERROR, "The element %s caused an unrecoverable error in %s.", _signalThreadName, _signalFunctionName); + } else { + mcx_log(LOG_ERROR, "The element %s caused an unrecoverable error.", _signalThreadName); + } } else { mcx_log(LOG_ERROR, "An element caused an unrecoverable error."); } @@ -112,6 +121,33 @@ void mcx_signal_handler_unset_name(void) { _signalThreadName = NULL; } +void mcx_signal_handler_set_function(const char * functionName) { +#if defined(MCX_DEBUG) + if (_signalFunctionNameStack4 != NULL) { + mcx_log(LOG_ERROR, "Signal handler function callstack overflow!"); + exit(1); // I guess there is a better way to handle this + } +#endif + _signalFunctionNameStack4 = _signalFunctionNameStack3; + _signalFunctionNameStack3 = _signalFunctionNameStack2; + _signalFunctionNameStack2 = _signalFunctionNameStack1; + _signalFunctionNameStack1 = _signalFunctionName; + _signalFunctionName = functionName; +} + +void mcx_signal_handler_unset_function(void) { +#if defined(MCX_DEBUG) + if (_signalFunctionName == NULL) { + mcx_log(LOG_WARNING, "Signal handler function callstack in element %s empty. Cannot pop non-existing element.", _signalThreadName); + } +#endif + _signalFunctionName = _signalFunctionNameStack1; + _signalFunctionNameStack1 = _signalFunctionNameStack2; + _signalFunctionNameStack2 = _signalFunctionNameStack3; + _signalFunctionNameStack3 = _signalFunctionNameStack4; + _signalFunctionNameStack4 = NULL; +} + void mcx_signal_handler_enable(void) { _signalThreadName = NULL; SetUnhandledExceptionFilter(HandleException); @@ -124,6 +160,10 @@ void mcx_signal_handler_disable(void) { _signalThreadName = NULL; } +const char * mcx_signal_handler_get_function_name(void) { + return _signalFunctionName; +} + #ifdef __cplusplus } /* closing brace for extern "C" */ #endif /* __cplusplus */ \ No newline at end of file