Strange non debug error
BlitzMax Forums/BlitzMax Programming/Strange non debug error
| ||
Hi all Has anyone ever experienced a program working perfectly in debug mode, but then when you run in 'release', it doesnt work ?? I use superstrict in every file i include so i know its not a variable type error. I know the line of code that fails, its a call to a DirectInput8 device method ( something I'm working on ), but how is it possible for code to work ok in debug and not in 'release' ?? Many thanks Last edited 2011 |
| ||
Make sure that none of your asserts have side effects that are needed for the program to run? That's quite a specific error, could you post the line which is failing? |
| ||
Its pretty much a standard way to setup and use the DirectInput8 interface. I've created the interface and everything works perfect in DEBUG mode. The code is spread across a couple of files. Posting code would mean posting the whole DirectInput8 interface code as well as the test code I'm using which also uses my custom engine setup - which isnt going to be open source. All DirectX COM interfaces / methods and functions fail silently. So for catching errors they return a value which needs to be checked for success or failure. I'm setting up a mouse device, when I register it with DirectInput in DEBUG mode, the return code is a success and indeed the whole program works. Without any alterations to the code at all, I select to 'Release Build' and the same call gives me a failure code and indeed the code fails. I handle all errors correctly so my code doesnt actually break, it fails silently but with error reporting. I am using types which contains byte pointers and integers, these are initialized and set properly before any calls are made that use them. I've also double checked everything is valid during run-time. I just cant understand how it works in debug and not release when the code doesn't change in any way at all. |
| ||
I've posted a cut down of the code below with the types that are referenced to initialize mouse input, Ive removed the error checking from the methods to make it easier to read. The error occurs at pDIMouse.SetDataFormat( c_dfDIMouse ) but only in 'release', in 'debug' it succeeds and the code works as normal As you can see, everything is initialized upon an a new instance, and indeed they are valid and also the values are correct during runtime in debug and release. Its almost as though the compiler is or isnt including something to reference the type?? or maybe optimising it and mangling it?? The code relies on the order of the fields to be correct. Thanks Last edited 2011 |
| ||
I've found the bug, and I need help to clarify if it is a bug or not!! In the source file, the values for GUIDs are defined AFTER the Type that uses them. This is ok in Debug mode but NOT in Release mode. By simply putting the GUID definitions BEFORE the Type definitions, the code compiles and runs as expected in debug and release. The GUIDs are defined as Global arrays. The DIDFTs are defined as Const and declared AFTER the Type definition, which seems to be OK. This has to be a bug as the value of the GUIDs in the type is valid during runtime in release and debug mode. [EDIT]Maybe this issue was caused by some kind of optimisation when in Release mode?? [EDIT2]Im not sure if its the same as if in a main source file, but this particular source file code is imported only via a module. Last edited 2011 Last edited 2011 |
| ||
Program-wide global variables (including those in types) can be initialized in any order and you should not rely upon any particular order. It seems that hey are being initialized in a different order in release mode. Create a Load function which you call at the begining of your program. You can place it anywhere as, so long as it's not called inside any Methods, it will 100% be called after all program-wide global variables have been initialized. It's ok to "new" global variables on the same line which don't have any dependencies. All those which depend upon other global variables, you can "new" in your function, when the other globals have definately been created. Consts can be used in any order, as they are resolved at compile-time before your program even executes. Last edited 2011 |
| ||
One difference between debug and release is also the garbage collector. In debug mode variables are still kept to show them in the debugger, in release mode they are freed when possible. May try to manually stop the garbage collection and see if that then works in release mode. If yes, you try to access a variable somewhere which was collected in the meantime. |
| ||
If you can still access a variable and it was collected, then it was collected too early and this is a bug. |
| ||
I came across a similar behavior when working with MaxGui and WinAPI stuff, maybe it's the same here. One thinks the variable is there and valid, but was collected in the meantime and every access bombs your program but only in release mode. As it is an easy test to put GCSuspend() I would try it anyway. If it works there is also the possibility to test a threaded build as it uses a different GC. |
| ||
Hmm. To me it seems... In release mode, in the initial code, because the Type came first and needs a value that is a Global, that value would still be initialized, probably to 0 or Null as its a BytePtr, then the global variable is added to the compilation, but for some reason the variable in the Type isnt getting the value of the Global variable. This would be a bug no? Maybe not, as this kind of coding style wouldn't work in c++ edit - I've actually changed the code to use an array instead of a Type, but still the same applies if the Global value comes after its usage in this scenario. Again, easily fixed by moving the Global declaration to before the Array/Type definition. Last edited 2011 |
| ||
Hmm. To me it seems... In release mode, in the initial code, because the Type came first and needs a value that is a Global, that value would still be initialized, probably to 0 or Null as its a BytePtr, then the global variable is added to the compilation, but for some reason the variable in the Type isnt getting the value of the Global variable. This would be a bug no? Maybe not, as this kind of coding style wouldn't work in c++ either and would probably get caught during compilation. edit - I've actually changed the code to use an array instead of a Type, but still the same applies if the Global value comes after its usage in this scenario. Again, easily fixed by moving the Global declaration to before the Array/Type definition. Last edited 2011 |
| ||
Hmm. To me it seems... In release mode, in the initial code, because the Type came first and needs a value that is a Global, that value would still be initialized, probably to 0 or Null as its a BytePtr, then the global variable is added to the compilation, but for some reason the variable in the Type isnt getting the value of the Global variable. This would be a bug no? Maybe not, as this kind of coding style wouldn't work in c++ either and would probably get caught during compilation. edit - I've actually changed the code to use an array instead of a Type, but still the same applies if the Global value comes after its usage in this scenario. Again, easily fixed by moving the Global declaration to before the Array/Type definition. Last edited 2011 |
| ||
Global glob1:Test1 = New Test1 Global glob2:test2 = New Test2 Type Test1 Field t:Test2 = glob2 End Type Type Test2 Field t:Test1 = glob1 End Type DebugStop Clearly one of these will have a field set to null. Global variables can be set up in any order and you should not try to predict what that is. What do you mean "the variable in the Type isnt getting the value of the Global variable"? If when it accesses the variable, it is Null, it will copy over that value and any future changes to the global variable will have no effect. |
| ||
Whats with the double postings ?? Last edited 2011 |
| ||
Ive managed to reproduce the bug using similar code :- Move 'Global GUID:Int[]... above the Global T2:Test2..... and it works. I can understand now that the value of GUID isnt defined until its used. Last edited 2011 |
| ||
It cant be exactly the same as my working code though, I've tried to make it the same but smaller. The working code works in debug but not in release. This doesnt work in either. It must have something to do with Debug mode and importing the file? I dont know. But I agree with you in with 'prediction of order'. My bad. Many thanks Last edited 2011 |
| ||
You could try this.Type TGUID Field _data:Int[] = [$11111111, $22222222, $33333333, $44444444] Function Get:Int[]() Global g:TGUID = New TGUID Return g._data End Function End Type Type Test1 Field data:Int[] = TGUID.Get() End Type |
| ||
It doesnt really matter as the Global GUIDs are defined in the same file. It was a simple case of moving them to before the Type - now array. It was my mistake to begin with :-) I was getting a little lapse in my usually robust coding style :-) lol I usually have all Constants and Globals defined at the head of the source files. I did have a similar problem, ie order of definition, with putting the components of the DirectX API into Bmax files, so I created a new file with the definitions of anything that needed to be used recognised globally across the API, then just import that file when needed. No more problem. Many thanks for your prompt help Last edited 2011 |
| ||
This is an interesting question - is it guaranteed that globals in an *imported* file will always be initialized first? |
| ||
I think it does, as it executes the imported files before your own code. I remember putting a DebugStop in a specific place in a BRL file. Even with my empty source it still stopped execution and entered the debugger at that point. I tend to have every source file laid out in this format :- any imports/includes that are 'distant' - ie not in the same folder as the source file any imports/includes that are 'local' - in the same source folder Constants and Globals Types the main loop Functions This has always worked out fine for me. |
| ||
I have main source file which is the first one that includes everything else and calls a function somewhere that has my main loop. Each type has its own file named after it (occasionally small, related types in with it too) and a couple of misc files for various utility functions and constants. |
| ||
Sorry, that was a little misleading what I wrote there. The main file is structured as above. Any imported files have the same structure except for no main loop, I treat imported files as a library of types globals consts and functions. So there is just one main loop only. Code execution always returns to the main source loop every frame. |
| ||
Take the test1 type out of the top example... it's not needed and just complicates things. This is the snippet that shows the problem (well a problem, not a debug/release problem as mentioned earlier). Type Test2 Field GlobalPtr:Byte Ptr = GUID EndType Global GUID:Int[] = [ $11111111,$22222222,$33333333,$44444444 ] Global T2:Test2 = New Test2 'Global GUID:Int[] = [ $11111111,$22222222,$33333333,$44444444 ] Print T2.GlobalPtr[0] It seems that since they're both globals, it's initializing them in order that they are listed. Since it's a pointer, you would expect it to point to the correct place for the global, even though it's not initialized yet, and that the print would still work either way... If it's not pointing to the correct place, you would expect an error on compilation stating that you can't do this. It's certainly something that should be caught at compile time if there is no way for it to know that it has to initialize globals that are a dependency of other globals first. |
| ||
I agree it should really be picked up at compile time. This wont compile so why should that the previous examples :- SuperStrict Global x:Int = y Global y:Int = 10 |