Announcement

Collapse
No announcement yet.

Bizarre bug

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Bizarre bug

    I'm working on a DLL using PBWin 8 that is called by a video game written in C++. We have a bizarre problem that I'm desperately trying to fix.

    The DLL has a function:

    Code:
    FUNCTION ENG_SetEngineParameter ALIAS "ENG_SetEngineParameter" (BYVAL Constant AS LONG, BYVAL FloatValue AS SINGLE) EXPORT AS LONG
    It is called with Constant = 0, 1, 2, 3, etc., through 43, each with a different FloatValue (SINGLE). The first time we run through this everything is fine. I use CALLSTK$ and also have a separate debugging macro after that that outputs this data to a text file like this:

    Code:
    '//First text output
    DebugOutput(CALLSTK$(1))
    
    '//Second text output
    TEXT$ = STR$(Constant) + STR$(FloatValue)
    DebugOutput(TEXT$)
    The first time around that we call this set of functions the debugging output for the first few lines looks like this:

    Code:
     0 20000
    ENG_SetEngineParameter([b]1[/b],45000)
    [b]1[/b] 45000
    ENG_SetEngineParameter(2,1000)
     2 1000
    ENG_SetEngineParameter(3,4)
     3 4
    ENG_SetEngineParameter(4,54)
     4 54
    ENG_SetEngineParameter(5,13.8000001907349)
     5 13.8
    ENG_SetEngineParameter(6,14)
     6 14
    ENG_SetEngineParameter(7,8.5)
     7 8.5
    ENG_SetEngineParameter(8,26.3999996185303)
     8 26.4
    .
    .
    .
    ENG_SetEngineParameter(28,[b]1[/b])
     28 [b]1[/b]
    ENG_SetEngineParameter(29,[b]1[/b])
     29 [b]1[/b]
    Question 1) Why is CALLSTK$ showing a number that is not the same as what STR$() shows? When we look in the C++ debugger it is sending for example 13.8, not 13.8000001907349. This question is merely a technical curiosity on my part. This discrepency does not hurt the program at all and we can live with it.

    The real problem that has me in dire straights at the moment is that the second time we call this same sequence of function calls with identical values, the output suddenly changes to this:

    Code:
     0 20000
    ENG_SetEngineParameter([b]9.99999984306749[/b],44999.9975758168)
    [b] 9.99999984306749 [/b]45000
    ENG_SetEngineParameter(1.9999999686135,999.999984306749)
     1.9999999686135 1000
    ENG_SetEngineParameter(2.99999995292025,3.999999937227)
     2.99999995292025 4
    ENG_SetEngineParameter(3.999999937227,54.0000019013435)
     3.999999937227 54
    ENG_SetEngineParameter(4.99999992153375,13.7999996459942)
     4.99999992153375 13.8
    ENG_SetEngineParameter(5.9999999058405,13.9999990930997)
     5.9999999058405 14
    ENG_SetEngineParameter(6.99999954654986,8.50000003840606)
     6.99999954654986 8.5
    ENG_SetEngineParameter(7.999999874454,26.4000008913682)
     7.999999874454 26.4
    .
    .
    .
    ENG_SetEngineParameter(28.0000016221733,[b]9.99999984306749[/b])
     28.0000016221733 [b]10[/b]
    ENG_SetEngineParameter(29.0000005756879,[b]9.99999984306749[/b])
     29.0000005756879 [b]10[/b]
    Question 2) Why are the integers showing up as floating point values suddenly the second time we do this? CALLSTK$ and STR$ are both showing the same this time around.

    Question 3) The biggest problem we have (the one that crashes the game) are the bold 1 and 10 values. Why does prefix = 1 suddenly become almost 10 as follows, for instance?

    Code:
    ENG_SetEngineParameter(9.99999984306749,44999.9975758168)
     9.99999984306749 45000
    And why does the following:
    Code:
    ENG_SetEngineParameter(28,[b]1[/b])
     28 [b]1[/b]
    ENG_SetEngineParameter(29,[b]1[/b])
     29 [b]1[/b]
    Suddenly become:

    Code:
    ENG_SetEngineParameter(28.0000016221733,[b]9.99999984306749[/b])
     28.0000016221733 [b]10[/b]
    ENG_SetEngineParameter(29.0000005756879,[b]9.99999984306749[/b])
     29.0000005756879 [b]10[/b]
    How can the ones become tens or 9.99999984306749 and so forth?

    We have checked this thoroughly through the C++ debugger (I'm in the office now with the C++ team) and the VC++ debugger shows that it is sending the correct values. Yet somehow the DLL is receiving garbage on the second set of calls.

    Even more mysterious, when we call this set for a third, fourth, fifth, tenth, etc., time, it reverts back to what we have on the very first call. I don't care if CALLSTK$ shows something different from STR$ there. The way it works on the first call (and third, fourth, etc.) is correct enough for us. The problem is call #2.

    Any ideas or suggestions on how to track this problem down? I'm completely lost...
    Todd Wasson
    http://PerformanceSimulations.Com
    PowerBasic Racing Simulator (October 2007 clip - 15.1MB wmv file) http:http://www.performancesimulations.co...m-GenIV-12.wmv

    #2
    STR$() or FORMAT$() or USING$(), express or implied, with a float argument (SINGLE, DOUBLE, EXT) show that float arguments are always approximations.

    STR$(), FORMAT$() and USING$() don't know about the statement where you said "X=13.8" they only know what is currently in the data.

    Bottom line is, when you choose to use floating-point data types you are responsible for rounding the values when necessary and possible, and living with the consequences when necessary but not possible.

    So... I wouldn't worry too much about what CALLSTK$ shows.. I'd worry about how my procedures interpret the passed data.
    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]
    http://www.talsystems.com

    Comment


      #3
      Thanks for your reply, Michael. I think my point and the big problems might have been missed, however. In the code below, the first value that is sent is 1 (integer). What I'm baffled by is why it shows up as:

      1) A floating point number when it is an integer, and only when the function series is called for the second time, not the first, third, fourth, fiftieth, etc., whereby it promptly returns to a nice and clean "1"

      2) Why is it virtually 10 rather than 1?

      Code:
      ENG_SetEngineParameter(9.99999984306749,44999.9975758168)
       9.99999984306749 45000
      Check out all the bold values in my original post. I don't understand how or why this happens. Integer 1 is 1. Not 10 or 9.9999... That's not a rounding error, is it?
      Todd Wasson
      http://PerformanceSimulations.Com
      PowerBasic Racing Simulator (October 2007 clip - 15.1MB wmv file) http:http://www.performancesimulations.co...m-GenIV-12.wmv

      Comment


        #4
        Haven't looked in depth, but could it be that what you are seeing is the mantissa part of a number in scientific notation, and the rest is truncated because it starts with a non-numeric character?

        Comment


          #5
          No, that's not it. The first, third, fourth, tenth, etc., time this series is called you get this:

          ENG_SetEngineParameter(5,13.8000001907349)
          5 13.8 '<-----This is what STR$ shows, which is correct
          The values that are really sent are 5 (integer) and 13.8 (floating point) in this example. That's it. We manually type those numbers into a file. The second time this same data is sent we instead see:

          ENG_SetEngineParameter(4.99999992153375,13.7999996459942)
          4.99999992153375 13.8
          How does an integer value of 5 become 4.99999992153375 when STR$ or CALLSTK$ shows it?

          What's worse, how does 1 become 10 (or nearly 10) in the example in post #3?

          10 here is not an exponent. It's 10 as in 1,2,3,4,5, etc., 10.

          Even worse, how does integer 1 in the first argument below wind up being as follows on the second set of function calls?

          Code:
          ENG_SetEngineParameter(9.99999984306749,44999.9975758168)
          How does sending the integer 1 turn into 9.99999984306749 here?
          Todd Wasson
          http://PerformanceSimulations.Com
          PowerBasic Racing Simulator (October 2007 clip - 15.1MB wmv file) http:http://www.performancesimulations.co...m-GenIV-12.wmv

          Comment


            #6
            Originally posted by Michael Mattias View Post
            So... I wouldn't worry too much about what CALLSTK$ shows.. I'd worry about how my procedures interpret the passed data.
            I have a problem with interpreting the integer 1 as 9.999999. Should I not?
            Todd Wasson
            http://PerformanceSimulations.Com
            PowerBasic Racing Simulator (October 2007 clip - 15.1MB wmv file) http:http://www.performancesimulations.co...m-GenIV-12.wmv

            Comment


              #7
              >I have a problem with interpreting the integer 1 as 9.999999. Should I not

              Well, I would worry about it, but only if I were POSITIVE I had passed the value 1& to the function. Insufficient code shown.

              PB just does not make errors like that, so I would first get a second look at the passed parameter... try TRACE PRINTing it.

              If that comes up the expected 1& then you have a problem with CALLSTK$ and should report that bug to PB; but if that, too, comes up 9.99998 or whatever, I think you have some stack corruption... which is not going to be any fun at all to find and fix.

              Basically, I put "compiler error" at the absolute end of the "list of things which could be causing my problem." PB's track record is just too good for me to do otherwise.
              Michael Mattias
              Tal Systems (retired)
              Port Washington WI USA
              [email protected]
              http://www.talsystems.com

              Comment


                #8
                ENG_SetEngineParameter(5,13.8000001907349)
                5 13.8 '<-----This is what STR$ shows, which is correct
                If you say STR$(FloatValue, 15) it will say 13.8000001907349 too. That's how it's stored as a SINGLE.

                If the problem is somehow related to this discrepancy, for debugging purposes can you change SINGLE to EXT in ENG_SetEngineParameter? If so you can be sure you pass the exact values by then using ENG_SetEngineParameter(5,VAL("13.8")).

                Comment


                  #9
                  Originally posted by John Gleason View Post
                  If you say STR$(FloatValue, 15) it will say 13.8000001907349 too. That's how it's stored as a SINGLE.
                  and 9.99999984306749e1 = 1 (near as dammit) - see post #4

                  Comment


                    #10
                    > and 9.99999984306749e1 = 1 (near as dammit) - see post #4

                    I think you meant "e-1" at the end makes this value near to unity.

                    Above is nearer one hundred than any other integer.
                    Michael Mattias
                    Tal Systems (retired)
                    Port Washington WI USA
                    [email protected]
                    http://www.talsystems.com

                    Comment


                      #11
                      Originally posted by Todd Wasson View Post
                      I'm working on a DLL using PBWin 8 that is called by a video game written in C++. We have a bizarre problem that I'm desperately trying to fix.
                      ...
                      It is called with Constant = 0, 1, 2, 3, etc., through 43, each with a different FloatValue (SINGLE). The first time we run through this everything is fine. I use CALLSTK$ and also have a separate debugging macro after that that outputs this data to a text file like this:
                      ..
                      I'm no doubt WAY over my head here offering suggestions to PB wizards, but Todd, have you tried an additional macro before the ENG_SetEngineParameter call to check the constants before they are sent? Perhaps somehow the corruption is somehow elsewhere. (Forest, Trees, ...)

                      Or as is almost certainly the case, I'm not understanding the problem (even though I've read all the posts several times). Just a thought anyway.

                      ==========================================
                      I do not believe in political movements.
                      I believe in personal movement,
                      that movement of the soul when a man
                      who looks at himself is so ashamed
                      that he tries to make some sort of change
                      within himself, not on the outside.
                      Joseph Brodsky
                      ==========================================
                      It's a pretty day. I hope you enjoy it.

                      Gösta

                      JWAM: (Quit Smoking): http://www.SwedesDock.com/smoking
                      LDN - A Miracle Drug: http://www.SwedesDock.com/LDN/

                      Comment


                        #12
                        Originally posted by Chris Holbrook View Post
                        and 9.99999984306749e1 = 1 (near as dammit) - see post #4
                        Hi Chris. The value displayed by CALLSTK$ and STR$ is 9.99999984306749, not 9.99999984306749e-1 If it was that close to 1 I would have no problem and wouldn't be here, but it's not. It's 10. Not 10e-1. There is no exponent here. The C++ debugger shows precisely "1" being sent, which is correct in this case.

                        In the particular lines below it is an integer. The values being sent are 1 (LONG) and 50000 (SINGLE). Not 10 and 50000. This has been verified on the C++ exe side in the debugger as well as in the DLL function using BIN$ and HEX$. I can live with 1 and 49999.999999, but not 10 (or something infinitely close to 10 rather than 1) and 49999.99999.

                        Code:
                        ENG_SetEngineParameter(9.99999984306749,44999.9975758168)
                         9.99999984306749 45000
                        Why is there any decimal component at all shown by CALLSTK$ and STR$ here in the first argument? It is a LONG, not a SINGLE.

                        I'm not suggesting that there is a compiler bug here that is responsible (I hope not). I sincerely hope it's just another ignorant thing I have done somewhere else that is causing this which has always been the case in the past. My thinking up until now was that having an integer value show up as a decimal at all was impossible. I guess I'm wondering what I might have done that could have caused this. Normally when I have really bizarre things happen it's a result of doing something goofy with a pointer or going out of bounds with an array. However, in this case I would have thought even that wouldn't cause an integer to show up in decimal format, let alone showing up 10 times too big.

                        Michael: I have verified the value "1" with BIN$ and HEX$. It is definitely a 1 being sent, not a 10 or a 9.9999... The C++ debugger also shows a 1 being sent. You mentioned a corrupted stack. This is a multithreaded scenario on the C++ exe side. Could there be something there?

                        Gosta: That's a good idea and is actually the first thing we did. In our case a C++ program that I did not write is making this function call so I can't go stick code in it, but the C++ guys verified that it's a 1 being sent through the C++ debugger.

                        Thanks for your time on this, guys. Any more ideas are appreciated.
                        Last edited by Todd Wasson; 24 Oct 2008, 05:24 AM.
                        Todd Wasson
                        http://PerformanceSimulations.Com
                        PowerBasic Racing Simulator (October 2007 clip - 15.1MB wmv file) http:http://www.performancesimulations.co...m-GenIV-12.wmv

                        Comment


                          #13
                          You mentioned a corrupted stack. This is a multithreaded scenario on the C++ exe side. Could there be something there?
                          Oh, yes. Yes, indeed.

                          There is no such thing as "multithreaded on the C++ exe side." Functions in all attached modules must be thread-safe (re-entrant) and/or protected with synchronization objects if the process is multi-threaded (well, Ok, only functions which might be called from multiple thread contexts, or functions which rely on any variables which may be modified within any thread context).

                          It may not necessarily be a corrupted stack, but variables mysteriously changing value is one of the classic symptoms of thread mismanagement... somewhere.

                          And that's the GOOD news. Finding the problem is not going to be any fun at all. No siree, it shore ain't.


                          MCM
                          Michael Mattias
                          Tal Systems (retired)
                          Port Washington WI USA
                          [email protected]
                          http://www.talsystems.com

                          Comment


                            #14
                            Problem solved. Thanks for your help, guys. It was indeed not the compiler, but an error on my end involving a couple of data types that had changed (SINGLE vs DOUBLE elsewhere), more or less. Once this was fixed CALLSTK$ and STR$ began reporting proper values.

                            Michael, when you described stack corruption, is that the type of thing that could happen when inadvertantly using 32 and 64 bit floats interchangeably through function calls and so on? For instance, sending a 64 bit float to a function expecting a 32 bit one? (In this case this is not what was happening, but it seems very roughly similar).
                            Todd Wasson
                            http://PerformanceSimulations.Com
                            PowerBasic Racing Simulator (October 2007 clip - 15.1MB wmv file) http:http://www.performancesimulations.co...m-GenIV-12.wmv

                            Comment


                              #15
                              Well, you CAN corrupt the stack if you have mismatched DECLARES (or in C language, mismatched inline casting at point of call). Passing the wrong number of total bytes is, well, fatal.. sooner or later (sooner likely).

                              But most stack corruption I have committed is because I overwrote LOCAL variables, or overran a buffer because, say, what should have been a null-terminated string was not null-terminated. Well, that, and "CopyMemory" with a wrong byte count. Throw in a few array bound violations left untrapped and I think I've covered most of the situations.

                              The 32 v 64 bit parameter can cause stack corruption if...
                              - Passed by value and mismatched DECLAREs (or point of call casting)
                              OR
                              - passed by reference and the variable itself is stack-based ; in PB-speak, LOCAL to the calling procedure or a passed stack-based variable further up the call stack chain.
                              Michael Mattias
                              Tal Systems (retired)
                              Port Washington WI USA
                              [email protected]
                              http://www.talsystems.com

                              Comment


                                #16
                                but an error on my end involving a couple of data types that had changed (SINGLE vs DOUBLE elsewhere),
                                FWIW, macros can help you here, a lot.

                                I recently did an application using the Haru PDF library. It uses a datatype "HPDF_REAL" . I had to guess if this was SINGLE or DOUBLE or something else, but I could not really test until I had converted a few headers and written a few functions. So I did this...

                                Code:
                                ...
                                MACRO  HPDF_REAL  =  SINGLE   ' DOUBLE 
                                ....
                                
                                
                                DECLARE FUNCTION HPDF_Page_SetWidth libharu_call libharu_dll ALIAS "HPDF_Page_SetWidth" _
                                     (BYVAL hPage AS LONG, BYVAL page_width AS HPDF_REAL) AS LONG
                                ' valid values for width are 3 to 14400
                                
                                DECLARE FUNCTION HPDF_Page_Setheight libharu_call libharu_dll ALIAS "HPDF_Page_SetHeight" _
                                     (BYVAL hPage AS LONG, BYVAL page_height AS HPDF_REAL) AS LONG
                                ' valid values for height are 3 to 14400
                                
                                DECLARE FUNCTION HPDF_Page_GetWidth libharu_call libharu_dll ALIAS "HPDF_Page_GetWidth" _
                                     (BYVAL hPage AS LONG) AS HPDF_REAL
                                
                                DECLARE FUNCTION HPDF_Page_GetHeight libharu_call libharu_dll ALIAS "HPDF_Page_GetHeight" _
                                     (BYVAL hPage AS LONG) AS HPDF_REAL
                                
                                ....
                                
                                FUNCTION InchtoDot (x AS HPDF_REAL, y AS HPDF_REAL ) AS LONG
                                    X = X * 72!    ' convert inches to points
                                
                                    Y = 720! - y*72
                                
                                'if I send Y = 0  (top0 it actually means top of page (Y = 720)
                                'If I send Y = 720 (bottom) it means Y = 0
                                'If I send Y = +200 (200 from Top) it means (720 -200) 520 or 520 from nottm
                                END FUNCTION
                                Turned out SINGLE was correct, but I only had to change one line of code (the MACRO) and recompile to make the change effective everywhere.

                                If you are developing a new Application and are not sure what datatypes you will ultimately be using for various things, coding with MACROs like this is a really good way to avoid the problem you had: "missing" one or two when you made a change.

                                MCM
                                Michael Mattias
                                Tal Systems (retired)
                                Port Washington WI USA
                                [email protected]
                                http://www.talsystems.com

                                Comment


                                  #17
                                  Good ideas and information. Thanks for the lessons, Michael.
                                  Todd Wasson
                                  http://PerformanceSimulations.Com
                                  PowerBasic Racing Simulator (October 2007 clip - 15.1MB wmv file) http:http://www.performancesimulations.co...m-GenIV-12.wmv

                                  Comment


                                    #18
                                    FWIW....

                                    When I started I also did not know what I was going to name the library (it was not the current version) and I didn't know the call convention used, either...that's why I also used .....
                                    Code:
                                    MACRO libharu_dll  = LIB "LIBHARU.DLL"
                                    MACRO libharu_call = SDECL
                                    ' current library is not cdecl, it got lost when I tried that
                                    ' could be SDECL, BDECL or CDECL. SDECL works Ok.
                                    ... and those macro names appear in the DECLARE lines.
                                    Michael Mattias
                                    Tal Systems (retired)
                                    Port Washington WI USA
                                    [email protected]
                                    http://www.talsystems.com

                                    Comment


                                      #19
                                      Todd,

                                      How is the call declared in the C++ header file ?

                                      should be something like StdApi ENG_SetEngineParameter(int constant, float x);
                                      So here we are, this is the end.
                                      But all that dies, is born again.
                                      - From The Ashes (In This Moment)

                                      Comment

                                      Working...
                                      X
                                      😀
                                      🥰
                                      🤢
                                      😎
                                      😡
                                      👍
                                      👎