Strange non debug error

BlitzMax Forums/BlitzMax Programming/Strange non debug error

col(Posted 2011) [#1]
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


Czar Flavius(Posted 2011) [#2]
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?


col(Posted 2011) [#3]
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.


col(Posted 2011) [#4]
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


col(Posted 2011) [#5]
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


Czar Flavius(Posted 2011) [#6]
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


jsp(Posted 2011) [#7]
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.


Czar Flavius(Posted 2011) [#8]
If you can still access a variable and it was collected, then it was collected too early and this is a bug.


jsp(Posted 2011) [#9]
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.


col(Posted 2011) [#10]
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


col(Posted 2011) [#11]
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


col(Posted 2011) [#12]
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


Czar Flavius(Posted 2011) [#13]
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.


col(Posted 2011) [#14]
Whats with the double postings ??

Last edited 2011


col(Posted 2011) [#15]
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


col(Posted 2011) [#16]
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


Czar Flavius(Posted 2011) [#17]
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



col(Posted 2011) [#18]
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


Czar Flavius(Posted 2011) [#19]
This is an interesting question - is it guaranteed that globals in an *imported* file will always be initialized first?


col(Posted 2011) [#20]
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.


Czar Flavius(Posted 2011) [#21]
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.


col(Posted 2011) [#22]
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.


Scienthsine(Posted 2011) [#23]
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.


col(Posted 2011) [#24]
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