• Which the release of FS2020 we see an explosition of activity on the forun and of course we are very happy to see this. But having all questions about FS2020 in one forum becomes a bit messy. So therefore we would like to ask you all to use the following guidelines when posting your questions:

    • Tag FS2020 specific questions with the MSFS2020 tag.
    • Questions about making 3D assets can be posted in the 3D asset design forum. Either post them in the subforum of the modelling tool you use or in the general forum if they are general.
    • Questions about aircraft design can be posted in the Aircraft design forum
    • Questions about airport design can be posted in the FS2020 airport design forum. Once airport development tools have been updated for FS2020 you can post tool speciifc questions in the subforums of those tools as well of course.
    • Questions about terrain design can be posted in the FS2020 terrain design forum.
    • Questions about SimConnect can be posted in the SimConnect forum.

    Any other question that is not specific to an aspect of development or tool can be posted in the General chat forum.

    By following these guidelines we make sure that the forums remain easy to read for everybody and also that the right people can find your post to answer it.

MSFS WASM module exception calling fclose()

Messages
76
Country
argentina
First of all, let me clarify that I am a complete newbie in coding in C++.

I'm creating a WASM module (it's not a gauge), with a very very simple logging to a text file.

void ModLog(void)
{
FILE* logfile = fopen("\\work\\mylogfile.log", "a");
fprintf(logfile, "%s \n", debugtext);
fclose(logfile);
}
(with debugtext a global char array with some text)

When calling the function for the first time there is no problem, the first row in the log file is there, but then in the next call, I get an exception from the WASM module, doing some test I found that the exception is when calling fclose() the second time!

Is this a Sim issue? or is it related to C++?
If I remove fclose() from the function and keep the file open, and when closing the module I call fclose() only once it works without problems, but I don't want to keep the file open, if a CTD occurs I will lose the data or part of it.

Regards.
Diego.
 
Messages
98
Country
us-newyork
Hello Diego,

I have not personally seen the issue you're describing, so this is some guesswork on my part (nor have I, AFAICR, repeatedly tried to close and re-open a file from WASM). But it seems likely that fclose() isn't the culprit per-se but is maybe exposing some other issue (possibly memory-related or possibly just with the file system itself).

For one thing I don't see where you check that *logfile is not null (ie. that fopen() succeeded). Or that the first fclose() succeeded (if it didn't, it would keep *logfile's FILE handle on the heap indefinitely). Then there's a question of what actually debugtext is or what happens to it. And that's only from the little bit of your code which I can see... :) One question I had was whether the logfile already exists in both cases (crash and no-crash)... if the first call creates the file then the 2nd call is actually going to act a bit differently (though I do append to existing files in my own code and that hasn't been an issue here).

In general I would keep a log file open as long as it's needed (program's life usually) and typically at the low level one would fflush() after writing something important to the file (this ensures it's on disk, so not affected by future crashes). Same as writing to a stream, eg. cout, typically the endl (or closing '\n') performs the flush (or one streams an explicit std::flush). Opening and closing file handles isn't "cheap," though I guess if you don't care about the extra time/resources then it's not a big deal.

All that said, I'd suggest not re-inventing the wheel and just use a decent (but simple) logging library. I personally really like logfault by Jarle Aase (single header file), and here is a version I've modified a bit (eg. to not require threading support) and I know works in a WASM module (I use it from both "WASimModule" and "WASimClient" components/projects in that repo, via the "shared/utilities.h" include). It's easy to use because you can just stream to the log output (like one would to cout for example), it can log to multiple destinations at once (console, file, custom), there are multiple logging levels, and, my favorite, it does "lazy logging." So for example if you're writing a debug-level message to a logger configured to only show >= INFO level messages, the whole debug log statement is never evaluated (no useless building up of a string at all, any functions are not evaluated, etc).

HTH,
-Max
 
Messages
76
Country
argentina
Hello Max, thank for your help, any comment or idea can guide me to the solution.

Regarding your comments, I agree that my code is at least basic, but my intention was to focus on the most important features of the addon, and leave things like logging for future improvement, but I ran into this problem!

Regarding the code, it is true that I am not validating if the logfile is null, since when opening it in "w" or "a" mode the file is created no matter if it exists or not, and I can check it because the file is there, and also the text inside it, after the first call.

The "debugtext" is just a char with text, somewhere in the code there is many: strcpy(debugtext , "my text here"), anyway, I have tried the function by removing the fprinf(), that is, only opening and closing the file, and I get the same exception!

I have also tried creating 2 practically identical functions, but one with "w" fopen() to "reset" the file, and another with "a" fopen() to add records, same result.

The only way that has worked is using only one fclose(), this is keeping the file open (which does not work for me since I want to be able to see its content at runtime, and it is not a problem to open and close it since the logging demand of the addon is very very low), I've also tried fflush(), but the second call to fflush() doesn't seem to be working, the new text doesn't appear in the file!, all the text pops there after the last (and only) fclose().

I feel like it's about the "speed" with which I call the function... testing right now...
Diego.
 
Messages
98
Country
us-newyork
Regarding the code, it is true that I am not validating if the logfile is null, since when opening it in "w" or "a" mode the file is created no matter if it exists or not, and I can check it because the file is there, and also the text inside it, after the first call.
Always check for nullptr when there is any chance that it will be null before using it. It's a cheap one-liner. Just because it opened the file the first time (and wrote the line) doesn't mean it did so the 2nd time. How are you determining where exactly it crashes.... do you have a debugger attached?
I've also tried fflush(), but the second call to fflush() doesn't seem to be working, the new text doesn't appear in the file!,
Something is wrong here as well then. There should be no problem reading/opening a "live" log file and it should always have all the flushed content in it. I understand if you don't need to keep it open, just saying this is indicative of whatever else is going on with the file handling. Unfortunately I don't know what. I/O streams are typically more vulnerable to (or more likely to exhibit) memory corruption elsewhere in the program (not saying that's the issue, but it's something to look at perhaps).

Cheers,
-Max
 
Messages
76
Country
argentina
Mystery solved!!, the problem is that you can't call the function consecutively in a very short time.

Although my addon does not have a high logging demand, when I start it I am calling the function very consecutively several times, creating an interval of at least 1 sec between each call, it works perfectly, the same happens with the flush(), it must have an interval of at least 1 sec (I don't know how to test a smaller value in a controlled way, in the sim wasm we don't have sleep).

Finally I kept the option to keep the file open, with a flush() every 1 sec, and it works perfectly, including a flush() inside the function does not work for the same problem, it is not functional if the call is very consecutive.

All this indicates to me that the sim's wasm sandbox is very inefficient on disk I/O!!

Diego.
 
Messages
98
Country
us-newyork
Glad you got that solved. Not sure what you're seeing though... my module can log several lines per ms to file, MSFS console, and send the lines via SimConnect, all in real-time (I regularly "tail" the log file so I don't have to keep MSFS console open). Not sure why it would make much difference, but you could try using a std:: ofstream instead (that's what the logger I use opens for the file destination).

Cheers,
-Max
 
Messages
76
Country
argentina
Always check for nullptr when there is any chance that it will be null before using it. It's a cheap one-liner. Just because it opened the file the first time (and wrote the line) doesn't mean it did so the 2nd time. How are you determining where exactly it crashes.... do you have a debugger attached?
Totally true!!! I'm just a bit anxious and I leave those "improvements" for when the function is already working, I've already added it. The exception was displayed in the dev console of the sim.

Glad you got that solved. Not sure what you're seeing though... my module can log several lines per ms to file, MSFS console, and send the lines via SimConnect, all in real-time (I regularly "tail" the log file so I don't have to keep MSFS console open). Not sure why it would make much difference, but you could try using a std::eek:fstream instead (that's what the logger I use opens for the file destination).
Is your module a wasm module? or an external SimConnect app? I think this is due to poor I/O handling in MSFS.
I haven't used fstream, I'm very new to C++, but maybe it handles some level of cache/buffer that avoids the problem.

What I can guarantee is that two sequences of fopen()+fclose() of the same file called almost immediately generate an exception in the sim.
I regularly "tail" the log file so I don't have to keep MSFS console open
That's what I'm looking for, dev mode has a high impact on sim to have it open just to see console messages.

Thanks for your help, in a future I will try fstream for sure.
Diego
 
Messages
98
Country
us-newyork
PS. if you need to time things, std::chrono works fine in the MSFS WASM environment, both the regular and high precision versions. But keep in mind everything is on a single thread and there's no concurrency at all (eg. you can spin a loop for X ms or whatever, but nothing else will happen anywhere in your module during this time).

Is your module a wasm module? or an external SimConnect app?
Both :) Well the module part is a WASM module which acts as a server and there is an external client part which communicates with it. They use SimConnect as the network transport layer but all the data exchanged is in custom structures. https://github.com/mpaperno/WASimCommander

Best,
-Max

BTW that is meant so say ofstream but the forum editor insists on making : o into an emoji... LOL
 
Messages
76
Country
argentina
Awesome!! I remember checking it out and it's great!!!

Im working on this guy

Although most of it is solved in the in sim module, unfortunately it's a fact that for some features using a WASM module won't solve it, so I'll have to work on a simconnect app soon :(
Regards
Diego.
 
Messages
98
Country
us-newyork
Wow, tremendous! I want him on my team... :) Really, looks great. I always wondered why the (co)pilots just sit there like crash test dummies... really only looking good in screenshots.

Cheers!
-Max
 
Messages
76
Country
argentina
Thanks my dear, at the moment it's an ongoing project with a lot to do and improve (I'm not happy with its appearance and its robotic movements lol).
As soon as I have a testable version, you will be one of the first.
 

DragonflightDesign

Resource contributor
Messages
1,082
Country
northernireland
As you've found, you've fallen foul of the delay caused by Windows writing to disc. So, your file opener should be
Code:
{
   FILE* logfile = fopen("\\work\\mylogfile.log", "a");
   if(logfile != NULL)
   {
     fprintf(logfile, "%s \n", debugtext); 
     fclose(logfile);
  }
}
Secondly, by using strcpy you are opening up the very real probability of getting a buffer overflow and trashing everything in sight. At the very least use
Code:
strncpy(logfile, debugtext, sizeof(debugtext);
strncat(logfile,"\0",1);
Although if you can be absolutely sure that debugtext already has a terminator in it then you can forget the second line. Lastly, be sure you are compiling to the ISO C++17 standard (as a bare minimum). ISO C++ 20 Std is a better idea.
 
Messages
98
Country
us-newyork
ISO C++ 20 Std is a better idea.
Just to clarify, MSFS WASM C++ libs (heavily modified version of std) only support up to C++17 (and somewhat incompletely at that). C++20 is not an option in the MSFS platform tools version selector (VS project properties) and I wouldn't recommend forcing it manually.

When using C++17, to avoid a bunch of bogus warnings at build time it is necessary to add /Zc:__cplusplus as an additional compiler option (and one may find they need to add other -Wno-* options as well). Also Intellicode gives quite a few false errors/warnings, for example with every std::string("Some const char").

Not seeing usage of strcpy in the OPs code... ? Though indeed to be avoided in favor of `n` version.

Cheers,
-Max
 
Last edited:

DragonflightDesign

Resource contributor
Messages
1,082
Country
northernireland
Hi Max

strcpy in OP's second post - lots of them by his own admission. Thanks for the info about WASM support (or lack of it!) for the C++ standards.

-Dai
 
Messages
98
Country
us-newyork
Hi Dai,
strcpy in OP's second post - lots of them by his own admission
Ah, sorry, missed that... got confused by file handle. I'd only add for completeness that the `n` count should be based on the size of the destination buffer, not the source (otherwise it's same as calling the unconstrained version). Allowing for null terminator, of course.

Best,
-Max
 
Messages
2,077
Country
us-ohio
size of the destination buffer
To ensure string termination... it should be size of destination buffer - 1. Ensures you have a string termination null at the end. Otherwise, if you unintentionally pass that string to something that uses a strlen to decide how many characters there are to process... a full string with no termination can possibly come up as much, much larger than it actually is.
 
Top