Bug investigation with OnError

Everything else that doesn't fall into one of the other PB categories.
User avatar
StarBootics
Addict
Addict
Posts: 984
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Bug investigation with OnError

Post by StarBootics »

Hello everyone,

I'm currently investigating a bug in a large project (74 635 lines of code). It's probably an invalid memory access but I'm not sure since the program crash only when I compile the program normally. When I run the program with the debugger I stress the program as much as I can but it doesn't crash at all.

So I have used OnError library in an attempt to figure out what is going on and I get the error message BUS error the first time I have tried. The second time I have tried I got the Segmentation violation message, The third time I got the Abort signal message.

Each an every single time in the following source code at line 156 :

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; CODE GÉNÉRÉ AUTOMATIQUEMENT, NE PAS MODIFIER À
; MOINS D'AVOIR UNE RAISON TRÈS TRÈS VALABLE !!!
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Code généré par : Dev-Object - V1.7.3
; Nom du projet : Gtk Dialog
; Nom du fichier : GtkDialog - OOP.pb
; Version du fichier : 0.0.0
; Programmation : À vérifier
; Programmé par : Guillaume Saumure
; Alias : StarBootics
; Courriel : gsaumure@cgocable.ca
; Date : 28-03-2022
; Mise à jour : 15-04-2022
; Codé pour PureBasic : V6.00 Beta 6
; Plateforme : Linux
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

DeclareModule GtkDialog
  
  Interface Dialog
    
    GetID.i()
    GetContentArea.i()
    SetModal(Modal.i)
    SetTitle(Title.s)
    SetBorderWidth(BorderWidth.l)
    SetTransientFor(*Parent)
    SetResizable(Resizable.i)
    SetContentAreaSpacing(Spacing.l)
    ContentAreaPackStart(*Child, Expand.i, Fill.i, Padding.l)
    ShowWidgets()
    AddButton.i(ButtonText.s, ResponseID.l)
    Run.l()
    Destroy()
    Free()
    
  EndInterface
  
  Declare.i New()
  
EndDeclareModule

Module GtkDialog
  
  ImportC ""
    
    gtk_dialog_get_content_area(*Dialog)
    gtk_box_set_spacing(*Box, Spacing.l)
    gtk_box_pack_start(*Box, *Child, Expand.i, Fill.i, Padding.l)
    
  EndImport 
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Déclaration de la Structure <<<<<

  Structure Private_Members
    
    VirtualTable.i
    DialogID.i
    ContentAreaID.i
    
  EndStructure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Les Observateurs <<<<<
  
  Procedure.i GetID(*This.Private_Members)
  
    ProcedureReturn *This\DialogID
  EndProcedure
  
  Procedure.i GetContentArea(*This.Private_Members)
  
    ProcedureReturn *This\ContentAreaID
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Les Mutateurs <<<<<

  Procedure SetModal(*This.Private_Members, Modal.i)
    
    gtk_window_set_modal_(*This\DialogID, Modal)
    
  EndProcedure
  
  Procedure SetTitle(*This.Private_Members, Title.s)
    
    gtk_window_set_title_(*This\DialogID, Title)
    
  EndProcedure
  
  Procedure SetBorderWidth(*This.Private_Members, BorderWidth.l)
    
    gtk_container_set_border_width_(*This\DialogID, BorderWidth)
    
  EndProcedure
  
  Procedure SetTransientFor(*This.Private_Members, *Parent)
    
    gtk_window_set_transient_for_(*This\DialogID, *Parent)
    
  EndProcedure
  
  Procedure SetResizable(*This.Private_Members, Resizable.i)
    
    gtk_window_set_resizable_(*This\DialogID, Resizable)
    
  EndProcedure
  
  Procedure SetContentAreaSpacing(*This.Private_Members, Spacing.l)
    
    gtk_box_set_spacing(*This\ContentAreaID, Spacing)
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Nom de la procédure <<<<<
  
  Procedure ContentAreaPackStart(*This.Private_Members, *Child, Expand.i, Fill.i, Padding.l)
    
    gtk_box_pack_start(*This\ContentAreaID, *Child, Expand.i, Fill.i, Padding.l)
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Nom de la procédure <<<<<
  
  Procedure ShowWidgets(*This.Private_Members)
    
    gtk_widget_show_all_(*This\ContentAreaID)
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Nom de la procédure <<<<<
  
  Procedure.i AddButton(*This.Private_Members, ButtonText.s, ResponseID.l)
    
    ProcedureReturn gtk_dialog_add_button_(*This\DialogID, ButtonText, ResponseID)
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Nom de la procédure <<<<<
  
  Procedure.l Run(*This.Private_Members)
  
    ProcedureReturn gtk_dialog_run_(*This\DialogID)
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Nom de la procédure <<<<<
  
  Procedure Destroy(*This.Private_Members)
    
    gtk_widget_destroy_(*This\DialogID) 
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Le Destructeur <<<<<

  Procedure Free(*This.Private_Members)
    
    FreeStructure(*This)
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Le Constructeur <<<<<

  Procedure.i New()
    
    *This.Private_Members = AllocateStructure(Private_Members)
    *This\VirtualTable = ?START_METHODS
    
    *This\DialogID = gtk_dialog_new_()
    *This\ContentAreaID = gtk_dialog_get_content_area(*This\DialogID)
    
    ProcedureReturn *This
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Les entrées de la table virtuelle <<<<<

  DataSection
    START_METHODS:
    Data.i @GetID()
    Data.i @GetContentArea()
    Data.i @SetModal()
    Data.i @SetTitle()
    Data.i @SetBorderWidth()
    Data.i @SetTransientFor()
    Data.i @SetResizable()
    Data.i @SetContentAreaSpacing()
    Data.i @ContentAreaPackStart()
    Data.i @ShowWidgets()
    Data.i @AddButton()
    Data.i @Run()
    Data.i @Destroy()
    Data.i @Free()
    END_METHODS:
  EndDataSection
  
EndModule

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Code généré en : 00.001 secondes (76000.00 lignes/seconde) <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; <<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< FIN DU FICHIER <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<<<<
The lline 156 correspond to this instruction :

Code: Select all

gtk_widget_destroy_(*This\DialogID)
I'm little bit lost here with the OnError library, any advice ?

Thanks beforehand.
StarBootics
The Stone Age did not end due to a shortage of stones !
User avatar
spikey
Enthusiast
Enthusiast
Posts: 581
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: Bug investigation with OnError

Post by spikey »

I've very limited experience of using GTK so you need to treat this with due scepticism but I can see a couple of potential vulnerabilities in your code:
1) There's nothing to prevent a Free occuring before a Destroy.
2) There's no verification in either Destroy or Free that the object really is valid.

This could potentially lead to an out of sequence type bug which wouldn't be guaranteed to fail in all circumstances but which could potentially give rise to an IMA in the wrong circumstances, if Free is called erroneously before Destroy; or Destroy is called a second time erroneously on a previously Freed or Destroyed object; or either is called on a invalid or obsolete pointer.

If I were writing this I'd be thinking about something like this:

Code: Select all

Procedure Destroy(*This.Private_Members)
  
  If *This = 0 Or *This\DialogID = 0
    ProcedureReturn
  EndIf
  
  gtk_widget_destroy_(*This\DialogID)
  *This\DialogID = 0
  
EndProcedure
I might even merge the Destroy and Free procedures into one, does it really make sense to leave a destroyed dialog object lying around unfreed?

The other thing that I'd be thinking about in your circumstances would be the addition of some Assert type statements and some logging to see what's occurring in the release executable that isn't occuring under the debugger.
User avatar
StarBootics
Addict
Addict
Posts: 984
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: Bug investigation with OnError

Post by StarBootics »

Thanks Spikey,

I'm using the Dialog source code in a 12 instances without problem except one.. Between Dialogs I'm using the same sequence in every each one of them so I'm pretty sure that the wrapper code I made is OK. Furthermore the last time I have compiled this program with the same library the Dialog that is causing problem now was working like a charms but this date back in the end of January. But I had to update the program since then and now I'm experiencing problem in one of Dialog.

What I don't understand is the fact that I get 3 different messages from the OnError library in 3 different program run.

To be honest this is my first time using OnError library so...

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
User avatar
spikey
Enthusiast
Enthusiast
Posts: 581
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: Bug investigation with OnError

Post by spikey »

StarBootics wrote: Mon Mar 20, 2023 5:28 pm What I don't understand is the fact that I get 3 different messages from the OnError library in 3 different program run.
The pattern of behaviour you're reporting suggests to me you have an IMA/bad pointer type error (particularly the segmentation violation). The fault may not exhibit in an especially repeatable way because it can be affected/caused by a specific runtime state. I think you're seeing symptomatic errors arising in related or called procedures that receive the bad pointer rather than errors appearing in the actual procedure which is the root cause.
User avatar
StarBootics
Addict
Addict
Posts: 984
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: Bug investigation with OnError

Post by StarBootics »

Hello Spikey,

I have try your version of the Destroy method but I still experience the problem. When you says it's not reproducible, it's not true as I have said before I'm using the Dialog wrapper library multiple times in the program without experiencing crash. Inside the program in question, when I open a particular Dialog the program crashes. As I have said I'm re-using my Dialog wrapper library multiple times without crash at all.

So my investigation continues

EDIT : Got it, the problem was inside another source code not even related to my GTK Dialog wrapper library.

In one of the library I have used inside the Dialog code has one of the Interface method defined like this :

Code: Select all

Inverse.i(*Success.Integer = #Null)
And the correspondent procedure was defined like this :

Code: Select all

Procedure.i Inverse(*This.Private_Members, *Success.Integer = #Null, *Inverse.Private_Members = #Null)
EndProcedure
This was t he cause of the problem after the method was defined correctly like this :

Code: Select all

Inverse.i(*Success.Integer = #Null, *Inverse = #Null)
the problem disappeared.

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
Post Reply