FreeStructure() crash

Just starting out? Need help? Post your questions and find answers here.
Joubarbe
Enthusiast
Enthusiast
Posts: 703
Joined: Wed Sep 18, 2013 11:54 am
Location: France

Re: FreeStructure() crash

Post by Joubarbe »

1/ Same result.
2/ I use With everywhere :)
3/ I draw my app on a Windows canvas and I'm under the impression that putting a delay in the loop (even if it's a WaitWindowEvent loop) that there is less crash... Really a wild guess, but I think it's something with events overlapping / interfering.
Joubarbe
Enthusiast
Enthusiast
Posts: 703
Joined: Wed Sep 18, 2013 11:54 am
Location: France

Re: FreeStructure() crash

Post by Joubarbe »

Could someone try the following: https://drive.google.com/file/d/1sr8Qoo ... sp=sharing

Launch Test.exe, hit Tab once to highlight the "Test1" window, then spam through F1, F2 and F3 until it crashes. This is not the same crash, but it might be related. If it doesn't crash on your PC, then I'll know something is wrong with my system.

EDIT: I've just done an interesting debug: print all the memory addresses of all my views, then print them again just before it crashes. Result: it's the same addresses.

Code: Select all

Procedure ClearControls(*view._View) : With *view
    If ListSize(\labels()) > 0 : ClearList(\labels()) : EndIf ; Crashes here, read error at random addresses, but always a low value (ie. not starting from *view address)
    ; .......
Fred
Administrator
Administrator
Posts: 18162
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: FreeStructure() crash

Post by Fred »

It's not advised to run random .exe. To pinpoint these kind of bug, you can try to remove code small part by small part until it doesn't crash and then you add the some.of the last part and so on. I once spent 3 days on a crazy pointer in the IDE but it wasn't really any other way. That's the problem of native programs, debugging can be awful.
User avatar
spikey
Enthusiast
Enthusiast
Posts: 750
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: FreeStructure() crash

Post by spikey »

Joubarbe wrote: Wed Oct 23, 2024 4:36 pm EDIT: I've just done an interesting debug: print all the memory addresses of all my views, then print them again just before it crashes. Result: it's the same addresses.
Yup, that's feasible. Consider this code:

Code: Select all

Structure THING
  AnInt.i
  ALong.l
  AByte.b
EndStructure

Define x.i

NewList Things.THING()

AddElement(Things())
Things()\AnInt = 1
Things()\ALong = 1
Things()\AByte = 1

AddElement(Things())
Things()\AnInt = #MAXDWORD
Things()\ALong = #MAXDWORD
Things()\AByte = #MAXBYTE

x = ListSize(Things())

ForEach Things()
  Debug @Things()
Next Things()

ClearList(Things())
The (shortened) assembly for this looks like:

Code: Select all

; ...
;
; Structure THING
; AnInt.i
; ALong.l
; AByte.b
; EndStructure
; 
; Define x.i
; 
; NewList Things.THING()
  MOV    r9,7
  XOR    r8,r8
  LEA    rdx,[t_Things]
  MOV    rcx,13
  CALL   PB_NewList
; 
; AddElement(Things())
  PUSH   qword [t_Things]
  POP    rcx
  CALL   PB_AddElement
; Things()\AnInt = 1
  MOV    rbp,qword [t_Things+8]
  MOV    qword [rbp+16],1
; Things()\ALong = 1
  MOV    dword [rbp+24],1
; Things()\AByte = 1
  MOV    byte [rbp+28],1
; 
; AddElement(Things())
  PUSH   qword [t_Things]
  POP    rcx
  CALL   PB_AddElement
; Things()\AnInt = #MAXDWORD
  MOV    rbp,qword [t_Things+8]
  MOV    rax,4294967295
  MOV    qword [rbp+16],rax
; Things()\ALong = #MAXDWORD
  MOV    rax,4294967295
  MOV    dword [rbp+24],eax
; Things()\AByte = #MAXBYTE
  MOV    byte [rbp+28],255
; 
; x = ListSize(Things())
  PUSH   qword [t_Things]
  POP    rcx
  CALL   PB_ListSize
  MOV    qword [v_x],rax
; 
; ForEach Things()
  MOV    rcx,qword [t_Things]
  CALL   PB_ResetList
_ForEach1:
  MOV    rcx,qword [t_Things]
  CALL   PB_NextElement
  OR     rax,rax
  JZ    _Next1
; Debug @Things()
; Next Things()
  JMP   _ForEach1
_Next1:
; 
; ClearList(Things())
  PUSH   qword [t_Things]
  POP    rcx
  CALL   PB_ClearList
; 
; ...
;
section '.bss' readable writeable
_PB_BSSSection:
pb_bssalign 8
; 
I_BSSStart:
_PB_MemoryBase:
PB_MemoryBase: rq 1
_PB_Instance:
PB_Instance: rq 1
PB_ExitCode: rq 1
; 
pb_bssalign 8
PB_DataPointer rq 1
v_x rq 1
pb_bssalign 8
pb_bssalign 8
pb_bssalign 8
pb_bssalign 8
t_Things rq 2
I_BSSEnd:
section '.data' data readable writeable
SYS_EndDataSection:
In each list library function call the base address of the list is passed to the function via RCX. The memory address of the base of the list is not passed directly to the function. If something manages to alter the value of RCX between the setup and the function call the function will get a corrupt parameter.

For example if I were to insert an INC RCX at line 66 in the assembly after the push/pop, PB_ClearList would get a corrupt pointer to the list. (OK I know this isn't what's actually happening to you, I'm just trying to illustrate a problem.)

This is a primitive example, I've not used a procedure call. Where procedure calls are used there is a second potential problem area because the stack will get involved too so it's possible that the stack data could have been incorrectly modified by another actor.

The underlying list data structure could remain intact in both of these cases.
Last edited by spikey on Wed Oct 23, 2024 6:22 pm, edited 1 time in total.
Joubarbe
Enthusiast
Enthusiast
Posts: 703
Joined: Wed Sep 18, 2013 11:54 am
Location: France

Re: FreeStructure() crash

Post by Joubarbe »

Okay thank you Fred and spikey (and all who helped). I'm going to try to fix that myself, because at this point I'm pretty sure that it's coming from me, and not my system or PB. It just has consequences that I don't understand, like a crash on ClearList(). But yeah, that's the fun of programming :D
SMaag
Enthusiast
Enthusiast
Posts: 302
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: FreeStructure() crash

Post by SMaag »

Code: Select all

Structure _View
  List *controls._Control()
EndStructure
What happens if you use only

Code: Select all

Structure _View
  List controls.i
EndStructure

;OR

Structure _View
  List *controls
EndStructure

Or other idea for testing what's the problem
do not put the List into a structure.

use

Code: Select all

; Define my_view._View ; don't to this
NewList *my_view._Control() ; do this
Maybe my_view\controls() sometimes isn't a Pointer to _control Structure. It is a value or a Pointer to anything else (maybe *_Button or *_Label) because anywhere you did not take care of it. And because of PB is not TypeSafe you can do this without any warning!

I use in my software for such things a pointer to itself as first Element

Code: Select all

Structure _Control
  ptrMe.i  ; ptrMe must contain the Pointer to the structure itself  (@_Control) 
EndStructure

With this trick I can always validate the Pointers to my hirarchical structures!
Yes it's a little more work but it helps!


For me it's always a problem that PB hides the '*' so a pointer and a value looks identical.

I use ptr as prefix to prevent such problems.

I would use

Code: Select all

Structure _View
  List *ptrControls._Control()
EndStructure
Joubarbe
Enthusiast
Enthusiast
Posts: 703
Joined: Wed Sep 18, 2013 11:54 am
Location: France

Re: FreeStructure() crash

Post by Joubarbe »

Code: Select all

Structure _Control
  ptrMe.i  ; ptrMe must contain the Pointer to the structure itself  (@_Control) 
EndStructure

With this trick I can always validate the Pointers to my hirarchical structures!
Could you elaborate on this? I actually didn't know that you could assign a structure to a pointer. I'm not sure I fully understand the use of it though.
For me it's always a problem that PB hides the '*' so a pointer and a value looks identical.
Very true :)

Thanks for your input.
SMaag
Enthusiast
Enthusiast
Posts: 302
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: FreeStructure() crash

Post by SMaag »

I had some tricky Pointer Erros with Structures, because of that I added my own Structure checks with a Pointer on itself and a StructureID.
Now I put such things in a seperate Module.

Here is more or less my version adapted to your Structure problem

Code: Select all

EnableExplicit

DeclareModule Ctrls
  
  Enumeration EStructureID
    #Struct_unknown
    #Struct_Control_Base  = $10
    #Struct_Control_Button
    #Struct_Control_Label
  EndEnumeration
  
  Structure _ControlBase
    ptrMe.i       ; helper variable to hold the Pointer to itself. It's only for user defined Pointer Checks
    StructID.i    ; for more detailed Structure Checks
  EndStructure
  
  Structure _Button Extends _ControlBase
  EndStructure
  
  Structure _Label Extends _ControlBase
  EndStructure
  
  Declare.i Allocate_Button()
  Declare.i Allocate_Label()
  Declare.i Free_Control(*Ctrl._ControlBase)
EndDeclareModule

Module Ctrls

  Procedure.i Allocate_Button()
    Protected *ctrl._ControlBase
    *ctrl = AllocateStructure(_Button)
    *ctrl\ptrMe = *ctrl
    *ctrl\StructID = #Struct_Control_Button
    ProcedureReturn *ctrl 
  EndProcedure
  
  Procedure.i Allocate_Label()
    Protected *ctrl._ControlBase
    *ctrl = AllocateStructure(_Label)
    *ctrl\ptrMe = *ctrl
    *ctrl\StructID = #Struct_Control_Label
    ProcedureReturn *ctrl 
  EndProcedure
  
  Procedure Free_Control(*Ctrl._ControlBase)
    ; Return 0 if the FreeStructure was done
    ; Return *Ctrl if FreeStructure was not done
    ; so we can use *MyControlPointer = Free_Control(*MyControlPointer)
    
    #Free_Control = #PB_Compiler_Module + "::" + #PB_Compiler_Procedure
    ;Debug "FreeControl-Reference = " + #Free_Control
    
    Protected ret
    
    If *Ctrl
      OnErrorGoto(?Free_Control_Error)
      If *Ctrl = *Ctrl\ptrMe  ; verify if it's valid Pointer one of our hirarchical Structures
        If (*Ctrl\StructID & #Struct_Control_Base) = #Struct_Control_Base 
          FreeStructure(*Ctrl) 
          ProcedureReturn 0
        Else
          MessageRequester(#Free_Control, "Wrong Structure ID : Pointer is not a Control")
        EndIf
        
      Else
        ; here we get a Warning if we passed a value what is not a correct Pointer to our Structure
        MessageRequester(#Free_Control, "Pointer validation Error: Pointer don't match with ptrMe!")
      EndIf
      
    Else
      MessageRequester(#Free_Control, "Error: Pointer = 0") 
    EndIf
    
    ProcedureReturn *Ctrl
    
    Free_Control_Error:
    MessageRequester(#Free_Control, "Fatal Pointer Error *ptr= " +Str(*Ctrl) + " ! It seems you passed a value not a Pointer!")
    ProcedureReturn *Ctrl
  EndProcedure
  
EndModule

Procedure ControlListIsNull(List *lst.Ctrls::_ControlBase())
  Protected xIsNull = #True
  
  ForEach *lst()
    If *lst() <> 0 
      xIsNull = #False
      Debug "NotNull"
      Debug *lst()
      Break
    EndIf
  Next

  ProcedureReturn xIsNull
EndProcedure

Procedure Clear_ControlList(List *lst.Ctrls::_ControlBase())
  
  If ControlListIsNull(*lst())
    ClearList(*lst())
    ProcedureReturn #True  
  Else
    MessageRequester("ClearPointerList Error", "The PointerList you want to clear contains values <> 0") 
  EndIf
  
  ProcedureReturn #False
EndProcedure

NewList *lstView1.Ctrls::_ControlBase()
NewList *lstView2.Ctrls::_ControlBase()

AddElement(*lstView1())
*lstView1()=Ctrls::Allocate_Button()
AddElement(*lstView1())
*lstView1()=Ctrls::Allocate_Label()

AddElement(*lstView2())
*lstView2()=Ctrls::Allocate_Label()
AddElement(*lstView2())
*lstView2()=Ctrls::Allocate_Label()

Debug "List lstView1"
ForEach *lstView1()
  With *lstView1()
    Debug Str(\ptrMe) + " : " + Str(\StructID)  
  EndWith
Next

Debug ""
Debug "List lstView2"
ForEach *lstView2()
  With *lstView2()
    Debug Str(\ptrMe) + " : " + Str(\StructID)  
  EndWith
Next

Debug ""
Debug "kill lstView1 - without Error!"
ForEach *lstView1()
  *lstView1() = Ctrls::Free_Control(*lstView1()) 
Next
ForEach *lstView1()
  Debug "*ptr = " + Str(*lstView1())
Next
If Clear_ControlList(*lstView1())
  Debug "*lstView1() is cleard!"
Else
  Debug "*lstView1() is not ready to clear - it's containg Pointers <> 0" 
EndIf

Clear_ControlList(*lstView1())
    
; ATTENTION: To see the MessageBox with Error you have to run without Debugger
Define *WrongPointer = 1234567

Debug ""
Debug "kill lstView2 - Pointer=0!"
Ctrls::Free_Control(0)  

Debug ""
Debug "kill lstView2 - Pointer validation Error!"
Define *mem
*mem = *lstView2()\ptrMe
*lstView2()\ptrMe = 0
*lstView2() = Ctrls::Free_Control(*lstView2())  

Debug ""
Debug "kill lstView2 - Wrong Structure ID!"
*lstView2()\ptrMe = *mem
*lstView2()\StructID = 0
*lstView2() = Ctrls::Free_Control(*lstView2())  

Debug ""
Debug "kill lstView2 - Error Wrong Pointer!"
Ctrls::Free_Control(*WrongPointer)  

Clear_ControlList(*lstView2())



Last edited by SMaag on Fri Oct 25, 2024 1:21 pm, edited 1 time in total.
Joubarbe
Enthusiast
Enthusiast
Posts: 703
Joined: Wed Sep 18, 2013 11:54 am
Location: France

Re: FreeStructure() crash

Post by Joubarbe »

Hmm, that's an approach. I would say that it's a bit of an overkill; it really complexifies the code too much to my taste. But it's interesting! Thank you for sharing.
SMaag
Enthusiast
Enthusiast
Posts: 302
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: FreeStructure() crash

Post by SMaag »

Joubarbe wrote: Thu Oct 24, 2024 7:56 pm I would say that it's a bit of an overkill;
Yes exactly!
But a serious problems with pointer is a nightmare, especally if your programm runs long time without any error.
Your description of the problem sounds like such a nightmare pointer bug!

Here is a Demo how a little typing error or a small carlessness during programming process (maybe when your telefon rings!)
creates a serious bug.

Code: Select all

EnableExplicit

Structure TTest1
  ValI.i
  ValD.d
  ValF.f
EndStructure

Structure TTest2
  m1.l
  m2.c
  m3.i
  m4.i
EndStructure

Procedure Test1(*tst.TTest1)
  With *tst
    \ValI = 10
    \ValD = 20.0
    \ValF = 30.0
  EndWith
EndProcedure

Procedure Test2(*tst.TTest2)
  With *tst
    \m1 = 1
    \m2 = 2
    \m3 = 3
    \m4 = 4
  EndWith
EndProcedure

Define t1.TTest1
Define t2.TTest2

; here the Parameters are wrong.
; because PB is not TypeSafe we don't get any warning
Test1(t2)  ; it's just a little typing error what will create total chaos
Test2(t1)


With t1
  Debug ""
  Debug "Structure TTest1"
  Debug \ValI
  Debug \ValD
  Debug \ValF
EndWith

With t2
  Debug ""
  Debug "Structure TTest2"
  Debug \m1
  Debug \m2
  Debug \m3
  Debug \m4
EndWith


; Demo: Send a standard Int var to a pointer parameter!
; PB don't give any warning! That's like classic C
; with the same problems. 
Define k=@t1  ; set k to address of t1, otherwise we produce a crash
Test1(k)      ; in TypeSafe systems like Pascal this is not possible
; it's just to show what is possible and how fast there might be a serious problem!

Post Reply