I've been off programming Nim for a while. Now I was revisiting and old project and I am hitting a failure I don't know how to address.
I am creating a library in Linux: inc.so which complies with the FMU standard (I was doing this about 2 years ago).
The code compiles fine, but when I pass the library through the FMU checker I get the following error:
[...]
0x7fc4aed10060"(r: ..., i: ..., b: ..., s: ..., isPositive: ..., time: 0.0, instanceName: Test FMI 2.0 ME, type: fmi2ModelExchange, GUID: {8c4e810f-3df3-4a00-8276-176fa3c9f008}, functions: ..., loggingOn: 1, logCategories: [1, 1, 1, 1], componentEnvironment: ..., state: modelInstantiated, eventInfo: (newDiscreteStatesNeeded: 0, terminateSimulation: 0, nominalsOfContinuousStatesChanged: 0, valuesOfContinuousStatesChanged: 0, nextEventTimeDefined: 0, nextEventTime: 0.0), isDirtyValues: 1, isNewEventIteration: 0)"
Traceback (most recent call last)
/home/jose/src/nimlang/fmu/ex02_moving_to_nim/lib/functions/others.nim(203) fmi2Instantiate
/home/jose/.choosenim/toolchains/nim-1.6.10/lib/system/assign.nim(143) genericAssign
/home/jose/.choosenim/toolchains/nim-1.6.10/lib/system/assign.nim(132) genericAssignAux
/home/jose/.choosenim/toolchains/nim-1.6.10/lib/system/assign.nim(25) genericAssignAux
/home/jose/.choosenim/toolchains/nim-1.6.10/lib/system/assign.nim(22) genericAssignAux
/home/jose/.choosenim/toolchains/nim-1.6.10/lib/system/assign.nim(140) genericAssignAux
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
./build.sh: line 8: 569360 Segmentation fault (core dumped) ./fmuCheck.linux64 inc.fmu
The file others.nim simply instantiate an object:
{.push exportc:"$1",dynlib,cdecl.}
proc fmi2Instantiate*( instanceName: fmi2String;
fmuType: fmi2Type;
fmuGUID: fmi2String;
fmuResourceLocation: fmi2String;
functions: fmi2CallbackFunctions;
visible: fmi2Boolean;
loggingOn: fmi2Boolean): ModelInstance =
....
var comp:ModelInstance
comp.time = 0
comp.instanceName = instanceName
...
echo repr $comp
return comp #<--- THIS IS THE LINE FAILING
Any clue about in which direction I should look-at?
My bet: Your code was never correct to begin with and a Nim version change exposes the problems.
Sorry I wasn't clear. The code is not the same. The old code relied on a fmuTemplate.h`and a `fmuTemplate.c that I am trying to port to Nim.
From all the functions and data models that I have ported so far, in this example the problem is on the fmi2Instantiate function. If I use the .c version it works, while the .nim one fails.
The C version is:
fmi2Component fmi2Instantiate(fmi2String instanceName, fmi2Type fmuType, fmi2String fmuGUID,
fmi2String fmuResourceLocation, const fmi2CallbackFunctions *functions,
fmi2Boolean visible, fmi2Boolean loggingOn) {
// ignoring arguments: fmuResourceLocation, visible
ModelInstance *comp;
if (!functions->logger) {
return NULL;
}
if (!functions->allocateMemory || !functions->freeMemory) {
functions->logger(functions->componentEnvironment, instanceName, fmi2Error, "error",
"fmi2Instantiate: Missing callback function.");
return NULL;
}
if (!instanceName || strlen(instanceName) == 0) {
functions->logger(functions->componentEnvironment, "?", fmi2Error, "error",
"fmi2Instantiate: Missing instance name.");
return NULL;
}
if (!fmuGUID || strlen(fmuGUID) == 0) {
functions->logger(functions->componentEnvironment, instanceName, fmi2Error, "error",
"fmi2Instantiate: Missing GUID.");
return NULL;
}
if (strcmp(fmuGUID, MODEL_GUID)) {
functions->logger(functions->componentEnvironment, instanceName, fmi2Error, "error",
"fmi2Instantiate: Wrong GUID %s. Expected %s.", fmuGUID, MODEL_GUID);
return NULL;
}
comp = (ModelInstance *)functions->allocateMemory(1, sizeof(ModelInstance));
if (comp) {
int i;
comp->r = (fmi2Real *) functions->allocateMemory(NUMBER_OF_REALS, sizeof(fmi2Real));
comp->i = (fmi2Integer *)functions->allocateMemory(NUMBER_OF_INTEGERS, sizeof(fmi2Integer));
comp->b = (fmi2Boolean *)functions->allocateMemory(NUMBER_OF_BOOLEANS, sizeof(fmi2Boolean));
comp->s = (fmi2String *) functions->allocateMemory(NUMBER_OF_STRINGS, sizeof(fmi2String));
comp->isPositive = (fmi2Boolean *)functions->allocateMemory(NUMBER_OF_EVENT_INDICATORS,
sizeof(fmi2Boolean));
comp->instanceName = (char *)functions->allocateMemory(1 + strlen(instanceName), sizeof(char));
comp->GUID = (char *)functions->allocateMemory(1 + strlen(fmuGUID), sizeof(char));
// set all categories to on or off. fmi2SetDebugLogging should be called to choose specific categories.
for (i = 0; i < NUMBER_OF_CATEGORIES; i++) {
comp->logCategories[i] = loggingOn;
}
}
if (!comp || !comp->r || !comp->i || !comp->b || !comp->s || !comp->isPositive
|| !comp->instanceName || !comp->GUID) {
functions->logger(functions->componentEnvironment, instanceName, fmi2Error, "error",
"fmi2Instantiate: Out of memory.");
return NULL;
}
comp->time = 0; // overwrite in fmi2SetupExperiment, fmi2SetTime
strcpy((char *)comp->instanceName, (char *)instanceName);
comp->type = fmuType;
strcpy((char *)comp->GUID, (char *)fmuGUID);
comp->functions = functions;
comp->componentEnvironment = functions->componentEnvironment;
comp->loggingOn = loggingOn;
comp->state = modelInstantiated;
setStartValues(comp); // to be implemented by the includer of this file
comp->isDirtyValues = fmi2True; // because we just called setStartValues
comp->isNewEventIteration = fmi2False;
comp->eventInfo.newDiscreteStatesNeeded = fmi2False;
comp->eventInfo.terminateSimulation = fmi2False;
comp->eventInfo.nominalsOfContinuousStatesChanged = fmi2False;
comp->eventInfo.valuesOfContinuousStatesChanged = fmi2False;
comp->eventInfo.nextEventTimeDefined = fmi2False;
comp->eventInfo.nextEventTime = 0;
FILTERED_LOG(comp, fmi2OK, LOG_FMI_CALL, "fmi2Instantiate: GUID=%s", fmuGUID)
return comp;
}
The Nim version is:
proc fmi2Instantiate*( instanceName: fmi2String;
fmuType: fmi2Type;
fmuGUID: fmi2String;
fmuResourceLocation: fmi2String;
functions: fmi2CallbackFunctions; # <--- I suspect of this
visible: fmi2Boolean;
loggingOn: fmi2Boolean): ModelInstance =
var comp:ModelInstance
echo functions.logger.isNil
echo functions.allocateMemory.isNil
#echo functions.allocateMemory.isNil
#[
# ignoring arguments: fmuResourceLocation, visible
if f.logger.isNil:
return nil
if f.allocateMemory.isNil or f.freeMemory.isNil:
f.logger( functions.componentEnvironment, instanceName, fmi2Error, "error".fmi2String,
"fmi2Instantiate: Missing callback function.".fmi2String)
return nil
if instanceName.isNil or instanceName.len == 0:
f.logger( functions.componentEnvironment, "?", fmi2Error, "error",
"fmi2Instantiate: Missing instance name.")
return nil
if fmuGUID.isNil or fmuGUID.len == 0:
f.logger( functions.componentEnvironment, instanceName, fmi2Error, "error",
"fmi2Instantiate: Missing GUID.")
return nil
]#
#[
if not ($(fmuGUID) == MODEL_GUID): #strcmp(fmuGUID, MODEL_GUID)) {
f.logger( functions.componentEnvironment, instanceName, fmi2Error, "error",
fmt"fmi2Instantiate: Wrong GUID {$(fmuGUID)}. Expected {MODEL_GUID}.")
return nil
]#
#comp = (ModelInstance *)functions.allocateMemory(1, sizeof(ModelInstance));
# var comp = ModelInstance( time:0,
# instanceName:instanceName,
# `type`:fmuType,
# GUID: fmuGUID )
comp.time = 0
comp.instanceName = instanceName
comp.`type` = fmuType
comp.GUID = fmuGUID
comp.r = cast[typeof(comp.r)](realloc(comp.r, NUMBER_OF_REALS * sizeof(fmi2Real)))
comp.i = cast[typeof(comp.i)](realloc(comp.i, NUMBER_OF_INTEGERS * sizeof(fmi2Integer)))
comp.b = cast[typeof(comp.b)](realloc(comp.b, NUMBER_OF_BOOLEANS * sizeof(fmi2Boolean)))
comp.s = cast[typeof(comp.s)](realloc(comp.s, NUMBER_OF_STRINGS * sizeof(fmi2String)))
comp.isPositive = cast[typeof(comp.isPositive)](realloc(comp.isPositive, NUMBER_OF_EVENT_INDICATORS * sizeof(fmi2Boolean)))
for i in 0 ..< NUMBER_OF_CATEGORIES:
comp.logCategories[i] = loggingOn
#[
if not comp.isNil:
#[
comp.r = cast[typeof(comp.r)](realloc(comp.r, NUMBER_OF_REALS * sizeof(fmi2Real)))
comp.i = cast[typeof(comp.i)](realloc(comp.i, NUMBER_OF_INTEGERS * sizeof(fmi2Integer)))
comp.b = cast[typeof(comp.b)](realloc(comp.b, NUMBER_OF_BOOLEANS * sizeof(fmi2Boolean)))
comp.s = cast[typeof(comp.s)](realloc(comp.s, NUMBER_OF_STRINGS * sizeof(fmi2String)))
comp.isPositive = cast[typeof(comp.isPositive)](realloc(comp.isPositive, NUMBER_OF_EVENT_INDICATORS * sizeof(fmi2Boolean)))
]#
#comp.instanceName = (char *)functions.allocateMemory(1 + strlen(instanceName), sizeof(char));
#comp.GUID = (char *)functions.allocateMemory(1 + strlen(fmuGUID), sizeof(char));
# set all categories to on or off. fmi2SetDebugLogging should be called to choose specific categories.
for i in 0 ..< NUMBER_OF_CATEGORIES:
comp.logCategories[i] = loggingOn
]#
#[
if comp.isNil or comp.r.isNil or comp.i.isNil or comp.b.isNil or comp.s.isNil or comp.isPositive.isNil or
comp.instanceName.isNil or comp.GUID.isNil:
functions.logger(functions.componentEnvironment, instanceName, fmi2Error, "error",
"fmi2Instantiate: Out of memory.")
return nil
]#
comp.functions = functions
comp.componentEnvironment = functions.componentEnvironment
comp.loggingOn = loggingOn
comp.state = modelInstantiated # State changed
setStartValues( comp ) # <------ to be implemented by the includer of this file
comp.isDirtyValues = fmi2True # because we just called setStartValues
comp.isNewEventIteration = fmi2False
comp.eventInfo.newDiscreteStatesNeeded = fmi2False
comp.eventInfo.terminateSimulation = fmi2False
comp.eventInfo.nominalsOfContinuousStatesChanged = fmi2False
comp.eventInfo.valuesOfContinuousStatesChanged = fmi2False
comp.eventInfo.nextEventTimeDefined = fmi2False
comp.eventInfo.nextEventTime = 0
filteredLog( comp, fmi2OK, LOG_FMI_CALL, fmt"fmi2Instantiate: GUID={$fmuGUID}")
return comp
I suspect about the functions: fmi2CallbackFunctions; ` argument, because the following shows: `false
echo functions.logger.isNil
echo functions.allocateMemory.isNil
I struggled today to create the repository: https://github.com/mantielero/fmu.nim
I managed to fix the above mentioned issue but I hit a new one.
First fmi2Instantiate is called in order to create a ModelInstance.
Later fmu2SetupExperiment is called.
The library compiles succesfully but when I test it I get:
ENTERING: fmi2SetupExperiment
fmi2CallbackFunctions
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
./build.sh: line 8: 640559 Segmentation fault (core dumped) ./fmuCheck.linux64 inc.fmu
Could it be that the ModelInstance object is freed by Nim before fmi2SetupExperiment is called? If that is happening how can I prevent it?
I keep on thinking on why the object returned by fmi2Instantiate seem to disappear after returning.
I was thinking about maybe the garbage collector being the one freeing the object, but if I compile with --mm:none I understand there is no garbage collector and the result is the same.
The generated C (this is quite obscure to me), looks like, in case there is something obvious for yourselves:
N_LIB_EXPORT N_CDECL(tyObject_ModelInstanceObj__Is9b2DZXPR8Y9bKvGe9cHkhRQ*, fmi2Instantiate)(NCSTRING instanceName, tyEnum_fmi2Type__LD1XJNzaCO9cJIzGLj9aIbNg fmuType, NCSTRING fmuGUID, NCSTRING fmuResourceLocation, tyObject_fmi2CallbackFunctionscolonObjectType___A1KlnnLQITV8pwRiDi9bxmg* functions, NI32 visible, NI32 loggingOn);
...
LA26_: ;
T31_ = NIM_NIL;
T31_ = (tyObject_ModelInstanceObj__Is9b2DZXPR8Y9bKvGe9cHkhRQ*) nimNewObj(sizeof(tyObject_ModelInstanceObj__Is9b2DZXPR8Y9bKvGe9cHkhRQ), NIM_ALIGNOF(tyObject_ModelInstanceObj__Is9b2DZXPR8Y9bKvGe9cHkhRQ));
(*T31_).time = 0.0;
colontmpD__2 = instanceName;
(*T31_).instanceName = colontmpD__2;
colontmpD__3 = fmuType;
(*T31_).type_0 = colontmpD__3;
colontmpD__4 = fmuGUID;
(*T31_).GUID = colontmpD__4;
comp = T31_;
{
void* T36_;
void* T37_;
void* T38_;
void* T39_;
void* T40_;
if (!!((comp == 0))) goto LA34_;
T36_ = (void*)0;
T36_ = reallocImpl__system_1731(((void*) ((*comp).r)), ((NI) 0));
(*comp).r = ((NF*) (T36_));
T37_ = (void*)0;
T37_ = reallocImpl__system_1731(((void*) ((*comp).i)), ((NI) 4));
(*comp).i = ((NI32*) (T37_));
T38_ = (void*)0;
T38_ = reallocImpl__system_1731(((void*) ((*comp).b)), ((NI) 0));
(*comp).b = ((NI32*) (T38_));
T39_ = (void*)0;
T39_ = reallocImpl__system_1731(((void*) ((*comp).s)), ((NI) 0));
(*comp).s = ((NCSTRING*) (T39_));
T40_ = (void*)0;
T40_ = reallocImpl__system_1731(((void*) ((*comp).isPositive)), ((NI) 0));
(*comp).isPositive = ((NI32*) (T40_));
{
NI i;
NI i_2;
i = (NI)0;
i_2 = ((NI) 0);
{
while (1) {
NI TM__FLsth87a6UwB9cclkU39ctDQ_227;
if (!(i_2 < ((NI) 4))) goto LA43;
i = i_2;
if ((NU)(i) > (NU)(3)){ raiseIndexError2(i, 3); goto LA1_;
}
(*comp).logCategories[(i)- 0] = loggingOn;
if (nimAddInt(i_2, ((NI) 1), &TM__FLsth87a6UwB9cclkU39ctDQ_227)) { raiseOverflow(); goto LA1_;
};
i_2 = (NI)(TM__FLsth87a6UwB9cclkU39ctDQ_227);
} LA43: ;
}
}
}
LA34_: ;
eqcopy___inc_832(&(*comp).functions, functions, NIM_TRUE);
(*comp).componentEnvironment = (*functions).componentEnvironment;
(*comp).loggingOn = loggingOn;
(*comp).state = ((tyEnum_ModelState__l0WFLfxMAj02XiTiGdjAlw) 2);
setStartValues(&comp);
if (NIM_UNLIKELY(*nimErr_)) goto LA1_;
(*comp).isDirtyValues = ((NI32) 1);
(*comp).isNewEventIteration = ((NI32) 0);
(*comp).eventInfo.newDiscreteStatesNeeded = ((NI32) 0);
(*comp).eventInfo.terminateSimulation = ((NI32) 0);
(*comp).eventInfo.nominalsOfContinuousStatesChanged = ((NI32) 0);
(*comp).eventInfo.valuesOfContinuousStatesChanged = ((NI32) 0);
(*comp).eventInfo.nextEventTimeDefined = ((NI32) 0);
(*comp).eventInfo.nextEventTime = 0.0;
fmtRes = rawNewString(((NI) 42));
prepareAdd((&fmtRes), 22);
appendString((&fmtRes), TM__FLsth87a6UwB9cclkU39ctDQ_229);
colontmpD__5 = cstrToNimstr(fmuGUID);
formatValue__pureZstrformat_162((&fmtRes), colontmpD__5, TM__FLsth87a6UwB9cclkU39ctDQ_230);
if (NIM_UNLIKELY(*nimErr_)) goto LA1_;
filteredLog__libZfunctionsZlogger_13((&comp), ((tyEnum_fmi2Status__09bP9cMJJIvYyzDhgSyGcd0w) 0), ((int) 2), nimToCStringConv(fmtRes));
if (NIM_UNLIKELY(*nimErr_)) goto LA1_;
echoBinSafe(TM__FLsth87a6UwB9cclkU39ctDQ_231, 1);
colontmpD__6 = cstrToNimstr((*comp).GUID);
T44_[0] = colontmpD__6;
echoBinSafe(T44_, 1);
echoBinSafe(TM__FLsth87a6UwB9cclkU39ctDQ_233, 1);
result = comp;
comp = 0;
eqdestroy___system_3263((&colontmpD__6));
eqdestroy___system_3263((&colontmpD__5));
eqdestroy___system_3263((&fmtRes));
eqdestroy___inc_722(&comp);
eqdestroy___system_3263((&colontmpD_));
goto BeforeRet_;
{
LA1_:;
}
{
eqdestroy___system_3263((&colontmpD__6));
eqdestroy___system_3263((&colontmpD__5));
eqdestroy___system_3263((&fmtRes));
eqdestroy___inc_722(&comp);
eqdestroy___system_3263((&colontmpD_));
}
if (NIM_UNLIKELY(*nimErr_)) goto BeforeRet_;
}BeforeRet_: ;
return result;
}
Fixed. It looks like using comp: var ModelInstance while ModelInstance was a ref object was the reason why it wasn't working.
Now facing a different problem. I will probably post it in a different thread.