Debugger Endlosschleife

Hier werden, insbesondere in den Beta-Phasen, Bugmeldungen gepostet. Das offizielle BugForum ist allerdings hier.
Benubi
Beiträge: 79
Registriert: 22.10.2004 17:51
Wohnort: Berlin, Wedding

Debugger Endlosschleife

Beitrag von Benubi »

PureBasic 5,73 LTS und 5,72 LTS (...), Windows 32bit
Integrierter und Stand-alone Debugger (console noch nicht getestet)
zwischen 10.000 - 20.000 Zeilen Code

Eine Prozedur wird endlos aufgerufen nachdem sie sich eigentlich mit ProcedureReturn verabschiedet. Ohne Debugger gibt es keine Endlosschleife.

Der Fehler ist möglicherweise schwer zu reproduzieren, und weil ich mit Fullscreen arbeite komme ich meistens nicht mehr wieder in die IDE/Debugger/Taskmanager um zu unterbrechen (dafür wäre wohl ein unerreichbarer flipbuffers() nötig). Ich habe die letzten Stunden "des Öfteren" den Reset Knopf betätigen müssen. Ich denke der Fehler sollte aber auch ohne Fullscreen reproduzierbar sein (das werde ich später testen).

Der Fehler tritt erst auf, nachdem ich eine Modifikation an einer Structure vorgenommen habe.


So funktioniert (grob) der Aufruf, über eine Lua 5.4 dll

Code: Alles auswählen

UseModule Lua
main()
    => lua_dofile() ; Lua::lua_dofile() is in Purebasic gewrapt und nicht aus der DLL, benutzt aber die lua_ Befehle aus der DLL
      => (lua script) shape:contains_point()
        => (PureBasic ProcedureC) tolua_contains_point(*L)
          => (PureBasic Procedure) point_in_poly(*P.Point2D,*segments.Segment2D,segment_count)
point_in_poly() wird endlos wiederholt, tolua_contains_point kehrt nie zum Script zurrück, ohne flipbuffers bleibt der Screen blockiert.

Die Aufrufende ProcedureC tolua_contains_point() (kaum bis gar nicht verändert) macht ihren Aufruf wie folgt:

Code: Alles auswählen

  
 DeclareModule Lua
  (...)
  PrototypeC _lua_pushboolean(*L, value)
  Global lua_pushboolean._lua_pushboolean  ; <- wird bei init_lua_dll() gesetzt
 (...)
 EndDeclareModule
 
   ProcedureC tolua_contains_point(*L)
     ; 
     Debug "tolua_contains_point" ; <-- wird nur 1x ausgegeben
       (...)
        *Segments=*poly\seg 
        count=*poly\s_count
        
        result = point_in_poly(*P,*Segments,count)  ; <------ Endlosschleife started wohl hier, wird ständig aufgerufen
        ;Debug "result: "+result      ;
        lua_pushboolean(*L, result) ;
        ProcedureReturn 1          ; 
        (...)
     EndProcedure

Erst seitdem ich folgende Veränderung vorgenommen habe, und die Proceduren angepasst habe tritt der Fehler auf:

Code: Alles auswählen

Structure Point2D ; <- unverändert
	x.d
	y.d
EndStructure

Structure Segment2D ; <- alt
    x1.d
    y1.d
    x2.d
    y2.d
EndStructure

Structure Segment2D ; neu
  *A.Point2D
  *B.Point2D
EndStructure
Dadurch mußte ich einige Proceduren umschreiben und die Parameter zum Aufruf werden jetzt über 2 "Pointer Ebenen" weiter gegeben anstatt einer. Ich benutze nur Double als Parameter außer für die Zähler (Integer).

Beispiel

Code: Alles auswählen

 Protected *SA.Segment2D , *SB.Segment2D
  ; alter segment vs segment test
  segment_intersect_segment(*SA\x1,*SA\y1,*SA\x2,*SA\y2,*SA\x1,*SA\y1,*SA\x2,*SA\y2,*result.point2d)

  ; neu 
  segment_intersect_segment(*SA\A\x,*SA\A\y,*SA\b\x,*SA\b\y,*SB\A\x,*SB\A\y,*SB\b\x,*SB\B\y,*result.point2d)
 

Ich bedauere zutiefst daß aber genau in dieser point_in_poly Procedure diese Aufrufe eben nicht stattfinden, sondern erst nicht weit in einer benachbarten Procedure. Irgendwie kann es aber mit nichts Anderem als diese Verkomplizierung liegen, mit den Pointern. Es wird auch anscheinend nichts falsch verarbeitet.Außerdem benutze ich diese unfertige Procedure im test script ja nicht wenn der Fehler Auftritt, auch nicht an anderer stelle (liegt derzeit nur ungennutzt bei).

So hat sich die point_in_poly (auszugsweise)verändert:

Code: Alles auswählen

(...)
; alt:
    If *S\x1>*S\x2
      xmax=*S\x1
      xmin=*S\x2
    Else 
      xmax=*S\x2
      xmin=*S\x1
    EndIf 
    If *S\a\y1>*S\y2
      ymax=*S\y1
      ymin=*S\y2
    Else 
      ymax=*S\y2
      ymin=*S\y1
    EndIf 
(...)
        xdiff = *S\x2-*S\x1
        ydiff = *S\y2-*S\y1
(...)
; neu
    If *S\a\x>*S\b\x
      xmax=*S\a\x
      xmin=*S\b\x
    Else 
      xmax=*S\b\x
      xmin=*S\a\x
    EndIf 
    If *S\a\y>*S\b\y
      ymax=*S\a\y
      ymin=*S\b\y
    Else 
      ymax=*S\b\y
      ymin=*S\a\y
    EndIf
    (...) 
    xdiff = *S\b\x-*S\a\x
    ydiff = *S\b\y-*S\a\y
    (...)


Ich habe des Weiteren alle Proceduren nach folgenden Problemquellen durchforstet und umgeändert, weil sich mit sowas ebenso ähnliche "Heisenbugs" bemerkbar machen

Code: Alles auswählen

; alt
i+1
*P+SizeOf(Point2D)
*Structure\counter + 1  ; <- solche Sachen vermeide ich schon etwas Länger

; neu
i=i+1
*P=*P+SizeOf(Point2D)
*Structre\counter = *Structure\counter +1
Die Procedure wird dennoch bei aktiviertem Debugger endlos wiederholt.
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6848
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Debugger Endlosschleife

Beitrag von STARGÅTE »

Benubi hat geschrieben: 13.08.2021 17:31 Erst seitdem ich folgende Veränderung vorgenommen habe, und die Proceduren angepasst habe tritt der Fehler auf:

Code: Alles auswählen

Structure Point2D ; <- unverändert
	x.d
	y.d
EndStructure

Structure Segment2D ; <- alt
    x1.d
    y1.d
    x2.d
    y2.d
EndStructure

Structure Segment2D ; neu
  *A.Point2D
  *B.Point2D
EndStructure
Ich weiß nicht ob dir das Klar ist, aber mit deiner neuen Segment2D Struktur hast zu keinen sofortigen Zugriff auf A\x, A\y und selbiges für B.
Dein A und B sind hier nur Pointer, deren Speicher du auch erst mal reservieren musst.
Beispiel: Ein

Code: Alles auswählen

Define Line.Segment2D
erlaubt dir nicht Line\A\x oder Line\B\y direkt zu befüllen, zu lesen oder weiterzugeben.
Dein neuer Code müsste praktisch etwas so sein:

Code: Alles auswählen

Define A.Point2D
Define B.Point2D
Define Line.Segment2D
Line\A = @A
Line\B = @B
Meiner Meinung nach sollte dir Struktur ehr so aussehen:

Code: Alles auswählen

Structure Segment2D
  A.Point2D
  B.Point2D
EndStructure
Du kannst das ja mal kommentieren.
PB 5.73 ― Win 10, 20H2 ― Ryzen 9 3900X ― Radeon RX 5600 XT ITX ― Vivaldi 4.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benubi
Beiträge: 79
Registriert: 22.10.2004 17:51
Wohnort: Berlin, Wedding

Re: Debugger Endlosschleife

Beitrag von Benubi »

Danke für Deine Antwort, Stargate!

Code: Alles auswählen

Ich weiß nicht ob dir das Klar ist, aber mit deiner neuen Segment2D Struktur hast zu keinen sofortigen Zugriff auf A\x, A\y und selbiges für B.
Dein A und B sind hier nur Pointer, deren Speicher du auch erst mal reservieren musst.
Es funktioniert schon, außer mit Debugger. Es ist etwas Umständlicher. Zu den Details, und dem Warum weiter unten.

Zuerst das Neuste zum Bug.

UPDATE:

Der Fehler tritt selbstverständlich auch im windowed Screen Modus auf. Aber ich habe jetzt rausgefunden, daß er in der Lua-DLL (unter Debugger Einfluß) entsteht.

Anscheinend wird eine der Script-Schleifen ewig wiederholt, irgendein Register muß wohl gestört sein (oder sowas Ähnliches, meine Assemblerkenntnisse sind sehr dünn).

Es ist eine einfache for next Schleife welche das Polygon "ausfüllen" soll. Ist nur zu Demo zwecken und natürlich sehr lahm (von 300fps auf ca. 15fps auf meiner schwachen Graka) weil mit Plot Befehl.

Code: Alles auswählen

function colorMouseOver() -- singemäß (Name weicht ab)
  local shape = shapelist.getMouseOverShape(mouse:position()) -- sinngemäß
  local i,j
  
  if not shape then return end
  
  for j=shape.lim.ymin,shape.lim.ymax do 
  for i=shape.lim.xmin,shape.lim.xmax do 
  
     if shape:contains_point( i, j) then
     	draw.plot(i,j, yellow) 
     end
     
  end
  end
  return 
  end
  
Vermutlich eine dieser zwei Schleifen wird ständlig wiederholt, oder die Funktion in der diese Schleifen stehen. Ohne Debugger wird das Polygon auch gelb ausgefüllt, bzw. alle anderen shape-typen wie circle oder box.

Die colorMouseOver() wird im Script nur 1x pro Draw/Flipbuffers aufgerufen.


-------------
Zum Warum:
Durch die Änderung in Pointer muß ich die Segmentliste (Array) nicht mehr erneuern wenn das Polygon verschoben wird, es sei denn es kommen punkte dazu oder werden abgezogen. Das Polygon bewegen: nur die Punkt-Koordinaten updaten und die min/max Werte der Bounding-box.

Die Segmentliste wird spätestens bei einer Abfrage wie bspw. contains_point() erstellt. Die Pointer ändern sich nicht, weil diese aus einem Array bezogen wird daß dem Polygon zugewiesen ist

Code: Alles auswählen

Structure Polygon2D
   ID.l ; polygon oder polyline teilen sich viele gemeinsame methoden, außer "contains" artige z.B.
  *Points.Point2D ; Array an Punkten
  *Segments.Segment2D; Array von Segmenten, mit den \A \B Pointern aus *Points
  p_count.i ; Anzahl Punkte
  s_count.i ; Anzahl Segment (Polyline = Anz. Punkte-1, Polygon = Anz.Punkte) 
EndStructure 
Eine Segmentliste macht es für das Polygon einfacher eine Schleife zu schreiben wegen dem letzten Segment zwischen den letzten Punkt und dem ersten.

Durch den indirekten Pointer Zugriff kann ich primitive Objekte "verketten". Ich dachte da an die Attach/Detach befehle aus der Ogre, geteilte Pointer usw.

Code: Alles auswählen

Structure Point2D ; kann erstellt werden und von verschiedenen objekten genutzt werden
 x.d
 y.d
EndStructure

Structure Circle2D ; kann ebenfalls als Segment-Punkt genutzt werden. Ist ja ein Punkt +radius
 x.d  ; 
 y.d
radius.d
EndStructure

Structure Segment2D
  *A.Point2D
  *B.Point2D
EndStructure

Structure Segment2D_FULL ; Hilfs-Struktur
  *A.Point2D
  *B.Point2D
  DummyA.d  ; 
  DummyB.d
EndStructure
Lua Konstruktoren:

Code: Alles auswählen

P1=point.new(123,23)
P2=point.new(43,54)

segment.new(P1,P2)     -- alloziiert: Segment2D, nur Pointer von bereits existierenden Point2D Objekten
segment.new(P1,x2,y2) -- allo: Segment2D_FULL
segment.new(x1,y1,x2,y2) -- Segment2D_FULL
segment.new(x1,y1,P2) -- Segment2D_FULL
Die "Full" structure ist nicht die Bevorzugte,und wird vermutlich wegfallen. Ich kann auch die dummies über lua an die struktur binden, ohne sie dort zu implementieren, indem ich dann 2 Punkte neu alloziieren - aber hier fürchte ich mich mehr vor Speicher Fragmentierung als vor dem hässlichen Overhead in der full struktur. Unter dem Strich wird diese dann wohl gehen müssen da ja diese Fragmentierung ohnehin der Normalfall ist. Analysis paralysis...
Antworten