Announcement

Collapse
No announcement yet.

tips for debugging a crash bug

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

  • #21
    Originally posted by Eric Pearson View Post
    I would install an ERR check at the exit points of as many procedures as you can. ERR is local to each procedure, and thereby temporary. So some procedure may be setting ERR, but then it is being reset to 0 before you return to MAIN, where the WAITKEY$ is located.
    Excellent suggestion! I should have thought of that.

    Comment


    • #22
      Originally posted by Michael Mattias View Post
      [/I]

      Source? I love the statement/quote but would not want to cite it in the future without crediting the author.
      Sorry, it was a StackOverflow post IIRC. I'll see if I can track it down again.

      Edit: Posted reply by user "Lundin" at https://stackoverflow.com/questions/...ry-before-exit

      Comment


      • #23
        What about a Dr Watson log or have they taken that out now.

        Comment


        • #24
          Originally posted by Dean Gwilliam View Post
          What about a Dr Watson log or have they taken that out now.
          I haven't used that for many years.
          According to Wikipedia:
          Dr. Watson is an application debugger included with the Microsoft Windows operating system. It may be named drwatson.exe, drwtsn32.exe or dwwin.exe, depending on the version of Windows.
          I found dwwin.exe on my WIn10.
          I also found this: https://docs.microsoft.com/en-us/tro...watson-program

          Comment


          • #25
            Good man Stuart. Perhaps Christopher can use that if all else fails.

            Comment


            • #26
              Thanks for all the tips, gentleman. I haven't pinpointed the exact source, but I've narrowed it down. Each of my THREADs works on a GLOBAL ARRAY. Normally, this should not be an issue. But there are circumstances when it's necessary to REDIM the size of the ARRAY because it's not big enough. Specifically, I'm using REDIM PRESERVE, which, according to the book, is just a shortcut for several commands which begins with an ERASE() command.

              I haven't re-created the bug (not yet; if I have time, I might do it later). But I strongly suspect that if one THREAD executes an ERASE() command while another THREAD is accessing the array, this would explain the Windows Error (trying to read memory not owned by my application).

              The REDIM PRESERVE command was buried in a well-tested subroutine that never gave me an issue before. But, I also never used in a THREAD FUNCTION that could have multiple instances running concurrently.

              In retrospect, it's obvious. Yeah, I'm the idiot today. But I did learn a few new debugging tricks.
              Christopher P. Becker
              signal engineer in the defense industry
              Abu Dhabi, United Arab Emirates

              Comment


              • #27
                Hoo, boy! Redimming a global in a thread! Just writing to a Global in a thread is bad enough
                Look up THREADSAFE in Help for a start. That's the minimum you will need.
                But even that is not necessarily a complete solution - make sure you aren't using pointers to array elements anywhere if you are messing with it in threads, nor are you using DIM AT.

                (REDIM PRESERVE doesn't start with an ERASE() statement (if it did, it wouldn't "preserve". . it just "erases" any elements above the new UBOUND() if necessary )

                Comment


                • #28
                  GLOBAL ARRAY. Normally, this should not be an issue. But there are circumstances when it's necessary to REDIM the size of the ARRAY because it's not big enough. Specifically, I'm using REDIM PRESERVE, which, according to the book, is just a shortcut for several commands which begins with an ERASE() command.
                  You do realize that REDIM, with or without PRESERVE, changes the addresses of all the array elements, right? And if that is an array if "indirect" data types (STRING, VARIANT, OBJECT) then when the program ends PowerBASIC will automatically try to deallocate those items. Gotta wonder if your additional TOEs aren't messing that up. (No, it shouldn't. But Vladimir Putin should be a kindly grandfather type, too.

                  If you want to handle something "global" , you should probably store the elements in a memory object with appropriate locking, as demo'd in this "recent" post in the source code forum:
                  Win32: Memory Mapped Files/Control User Count April 26, 2001

                  That's for across processes but it will work just ducky in a single multithreaded process.

                  You can use '[RE]DIM AT address' to use PB's handy-dandy array handling statements on the data in the memory block.
                  Michael Mattias
                  Tal Systems (retired)
                  Port Washington WI USA
                  [email protected]
                  http://www.talsystems.com

                  Comment


                  • #29
                    Look up THREADSAFE in Help for a start. That's the minimum you will need.
                    The THREADSAFE directive is for individual functions (kind of a poor man's "critical section") , and is certainly not intended for the THREAD FUNCTIONs themselves.

                    Also please note the PB THREADSAFE directive uses a semaphore object and therefore is not suitable for any re-entrant circumstances as you will generate a deadlock.
                    Michael Mattias
                    Tal Systems (retired)
                    Port Washington WI USA
                    [email protected]
                    http://www.talsystems.com

                    Comment


                    • #30
                      Originally posted by Michael Mattias View Post

                      The THREADSAFE directive is for individual functions (kind of a poor man's "critical section") , and is certainly not intended for the THREAD FUNCTIONs themselves..
                      That is obvious from the Help that I pointed to.
                      "When a procedure is declared THREADSAFE, PowerBASIC automatically establishes a semaphore which allows only one thread to execute it at a time. Others must wait until the first thread exits the THREADSAFE procedure before they are allowed to begin.
                      Obviously, THREADSAFE procedures should be made as small as possible. By limiting them to just the essential data access, you will minimize the possibility of unwanted delays of execution."


                      The Global array access (and redensioning) needs to be wrapped in a Threadsafe FUNCTION or SUB which is called from the thread rather than the Global access being in-line in the thread.


                      "

                      Comment


                      • #31
                        rather than the Global access being in-line in the thread.
                        Or use a critical section object... the Windows'- provided tool for this kind of thing.

                        But with the REDIM PRESERVE in use, it's possible neither THREADSAFE nor a critical section may e good enough, as that statement will change the address of all the Array elements. Your suggestion of wrapping all array operations in a single thread-safe function or my idea to store the data in a memory block and use locking and a virtual array to access it would IMO be the ways to go.

                        Good application design is required BEFORE selecting the precise mechanism to be used for maintaining data integrity
                        Michael Mattias
                        Tal Systems (retired)
                        Port Washington WI USA
                        [email protected]
                        http://www.talsystems.com

                        Comment


                        • #32
                          Originally posted by Michael Mattias View Post
                          You do realize that REDIM, with or without PRESERVE, changes the addresses of all the array elements, right? And if that is an array if "indirect" data types (STRING, VARIANT, OBJECT) then when the program ends PowerBASIC will automatically try to deallocate those items. .
                          and
                          Originally posted by Michael Mattias View Post
                          But with the REDIM PRESERVE in use, it's possible neither THREADSAFE nor a critical section may e good enough, as that statement will change the address of all the Array elements.
                          Why do you assume that? It's simple enough to show that it is not so and that it actually functions as I stated in Post 27.
                          '
                          Code:
                          #COMPILE EXE
                          #DIM ALL
                          GLOBAL myArray() AS STRING
                          FUNCTION PBMAIN() AS LONG
                             LOCAL i AS LONG
                             LOCAL s1,s2,s3 AS STRING
                             DIM myArray(4)
                             FOR i = 0 TO 4
                                 myArray(i) = "Element " & STR$(i)
                             NEXT
                             FOR i = 0 TO 4
                                 s1+= STR$(STRPTR(myArray(i)))
                             NEXT
                             REDIM PRESERVE myArray(6)
                             FOR i = 0 TO 6
                                 s2+= STR$(STRPTR(myArray(i)))
                             NEXT
                             REDIM PRESERVE myArray(2)
                             FOR i = 0 TO 2
                                 s3+= STR$(STRPTR(myArray(i)))
                             NEXT
                             ? s1 & $LF & s2 & $LF & s3
                          END FUNCTION
                          '
                          ---------------------------
                          PowerBASIC
                          ---------------------------
                          3914580 3914724 3914412 3914772 3915012
                          3914580 3914724 3914412 3914772 3915012 2109636 2109636
                          3914580 3914724 3914412
                          ---------------------------
                          OK
                          ---------------------------



                          Comment


                          • #33
                            Just came across something that I wasn't aware.of
                            I noticed the repeated 2109636 as the STRPTR of two different array elements in the previous post.

                            It turns out that all zero length dynamic strings in an application have the same STRPTR. (they are de-allocated)

                            When the elements of a string array are DIMed, all the elements (which are set to null) have the same STRPTR
                            If an "filled" array element is set to Null (zero length string) , it's STRPTR reverts to the "uninitialised" value.
                            A LOCAL string that has not been initialsed with a value has the same STRPTR value.
                            A LOCAL string that is reset to zero length has the same STRPTR value.
                            '
                            Code:
                            #COMPILE EXE
                            #DIM ALL
                            GLOBAL myArray() AS STRING
                            GLOBAL myArray2() AS STRING
                            FUNCTION PBMAIN() AS LONG
                               LOCAL i AS LONG
                               LOCAL s1,s2,s3,s4,s5,s6,s7 AS STRING
                               DIM myArray(3)
                               FOR i = 0 TO 2
                                   myArray(i) = "Element " & STR$(i)
                               NEXT
                            
                               s1 = STR$(VARPTR(myArray())) & " - "
                               FOR i = 0 TO 3
                                   s1+= STR$(STRPTR(myArray(i)))
                               NEXT
                               REDIM PRESERVE myArray(5)
                               myArray(3) = "123"
                               myArray(5) = "12345"
                               s2 = STR$(VARPTR(myArray())) & " - "
                               FOR i = 0 TO 5
                                   s2+= STR$(STRPTR(myArray(i)))
                               NEXT
                               REDIM PRESERVE myArray(2)
                               s3 = STR$(VARPTR(myArray())) & " - "
                               FOR i = 0 TO 2
                                   s3+= STR$(STRPTR(myArray(i)))
                               NEXT
                                REDIM PRESERVE myArray(5)
                               myArray(4) = "12345"
                               s4 = STR$(VARPTR(myArray())) & " - "
                               FOR i = 0 TO 5
                                   s4+= STR$(STRPTR(myArray(i)))
                               NEXT
                               REDIM  myArray(4)
                               myArray(4) = "12345"
                               s5 = STR$(VARPTR(myArray())) & " - "
                               myArray(2) = "12"
                               FOR i = 0 TO 4
                                   s5+= STR$(STRPTR(myArray(i)))
                               NEXT
                               myArray(2) = ""
                               s6 = STR$(VARPTR(myArray())) & " - "
                               FOR i = 0 TO 4
                                   s6+= STR$(STRPTR(myArray(i)))
                               NEXT
                               DIM myArray2(3)
                               s7 = STR$(VARPTR(myArray2())) & " - "
                               myArray2(1) = "12"
                               FOR i = 0 TO 2
                                   s7+= STR$(STRPTR(myArray2(i)))
                               NEXT
                               LOCAL sTmp,s8,s9,s10 AS STRING
                               s8 = STR$(STRPTR(sTmp))
                               stmp = "123"
                               s9 = STR$(STRPTR(sTmp))
                               sTmp = ""
                               s10 = STR$(STRPTR(sTmp))
                               ? s1 & $LF & s2 & $LF & s3 & $LF & s4 & $LF & _
                                "Redim and myarray(2) set" & $LF & s5 & $LF & _
                                "MyArray(2) reset to zero length" & $LF & s6 & $LF & _
                                "A different Global array with MyArray2(1) set:" & $LF & s7 & $LF & _
                                "A local uninitialised string" & $LF & s8 & $LF & _
                                "The string once initialise" & $LF & s9  & $LF & _
                                "The string reset to zero length" & $LF & s10
                            END FUNCTION
                            '
                            In this application, the null STRPTR is always 2113732

                            ---------------------------
                            PowerBASIC
                            ---------------------------
                            2116176 - 2480780 2480948 2480708 2113732
                            2116176 - 2480780 2480948 2480708 2480612 2113732 9449852
                            2116176 - 2480780 2480948 2480708
                            2116176 - 2480780 2480948 2480708 2113732 9449732 2113732
                            Redim and myarray(2) set
                            2116176 - 2113732 2113732 9450212 2113732 9449652
                            MyArray(2) reset to zero length
                            2116176 - 2113732 2113732 2113732 2113732 9449652
                            A different Global array with MyArray2(1) set:
                            2116292 - 2113732 9449732 2113732
                            A local uninitialised string
                            2113732
                            The string once initialise
                            9449852
                            The string reset to zero length
                            2113732
                            ---------------------------
                            OK
                            ---------------------------
                            Last edited by Stuart McLachlan; 26 Jun 2022, 01:54 AM.

                            Comment


                            • #34
                              Originally posted by Stuart McLachlan View Post
                              REDIM PRESERVE doesn't start with an ERASE() statement
                              I understand your point. But from the Help screens: "The REDIM statement allows dynamic arrays ... to be erased and re-dimensioned. It is really just a shortcut for the two-step process ERASE x(), followed by DIM x()."
                              Christopher P. Becker
                              signal engineer in the defense industry
                              Abu Dhabi, United Arab Emirates

                              Comment


                              • #35
                                That statement comes from Help before the PRESERVE remarks, and only applies to REDIM (without preserve).

                                Cheers,
                                Dale

                                Comment


                                • #36
                                  Originally posted by Christopher Becker View Post
                                  understand your point. But from the Help screens: "The REDIM statement allows dynamic arrays ... to be erased and re-dimensioned. It is really just a shortcut for the two-step process ERASE x() followed by DIM x()"
                                  Help also says
                                  :ERASE deallocates all the memory for LOCAL, STATIC, and GLOBAL arrays
                                  and
                                  When a REDIM statement is executed, the location of the array elements always moves in memory;

                                  But as shown above neither of those statements applies to REDIM PRESERVE

                                  Comment


                                  • #37
                                    Originally posted by Dale Yarker View Post
                                    That statement comes from Help before the PRESERVE remarks, and only applies to REDIM (without preserve).

                                    Cheers,
                                    Exactly. Help goes on to say uder "PRESERVE":
                                    The PRESERVE keyword tells the compiler to preserve the values of all existing elements in the array.

                                    "preserve" has a completley different meaning to "erase". You can't both erase and preserve something.

                                    Comment


                                    • #38
                                      Why do you assume that? [Array elements change address during a REDIM PRESERVE] . It's simple enough to show that it is not so and that it actually functions as I stated in Post 2
                                      Because it's not true sometimes. Elements may not move during a REDIM PRESERVE if the new array size is less than the old size; however, it is not consistent, at least as far as I have been able to determine. I figure if the elements may move, it is silly to assume they never move.

                                      So yes I am making an assumption here.. but one which is very reasonable and is certainly the 'safe' path to take.
                                      Michael Mattias
                                      Tal Systems (retired)
                                      Port Washington WI USA
                                      [email protected]
                                      http://www.talsystems.com

                                      Comment


                                      • #39
                                        Just came across something that I wasn't aware.of
                                        I noticed the repeated 2109636 as the STRPTR of two different array elements in the previous post.

                                        It turns out that all zero length dynamic strings in an application have the same STRPTR. (they are de-allocated)
                                        Are you sure that is not a coincidence, the address of the data in the STRING structure being meaningless if the length element of that struct is zero? That is, whatever value is at the '*chr data" member of the STRING struct is uninitialized and therefore meaningless.. and may just be leftover bits in reused memory.

                                        In this case, Enquiring Minds don't really care but are afraid someone may try to treat this "same address" thing as a valid universal truth.
                                        Michael Mattias
                                        Tal Systems (retired)
                                        Port Washington WI USA
                                        [email protected]
                                        http://www.talsystems.com

                                        Comment


                                        • #40
                                          VARPTR of a STRING contains STRPTR for that STRING.
                                          For an unused (aka 0 length) string, memory at STRPTR - 4 must contain 0.
                                          Therefore can not be an accident.
                                          Can not be guarantied in future versions (we hope to get), but why create a separate empty string [assign a VARPTR, a STRPTR and length] for every STRING in an array? (And every string is empty when array is first DIM'ed))

                                          Cheers,
                                          Dale

                                          Comment

                                          Working...
                                          X