Debugging und Threads allgemein?

Für allgemeine Fragen zur Programmierung mit PureBasic.
Benubi
Beiträge: 186
Registriert: 22.10.2004 17:51
Wohnort: Berlin, Wedding

Debugging und Threads allgemein?

Beitrag von Benubi »

Hallo

Die IDE ist ja mittlerweile sehr gut vorangeschritten und Sachen wie Callstack usw. haben mir sehr geholfen. Aber...ich bastel seit einer Weile wieder an einem Featuritis reichen Projekt und habe große Schwierigkeiten Fehlerquellen in Threads zu ermitteln.

Was ist denn die Beste vorgehensweise um eine Multi-Threaded Applikation zu Debuggen?

Problem: die SW (webserver) kann in verschiedenen multi- single- oder no-threaded Variationen ausgeführt werden (hatte bis jetzt auch immer seine Vorteile zur Fehlersuche und Optimierung). Der Server soll wahlweise das "Hauptprojekt" sein können, oder als Nebenschnittstelle fungieren können. Das Backend für eine WebApp oder für ein Hauptprojekt oder als sonstige Schnittstelle für ein Spielchen.

Es gibt leider mehrere Abstraktionsschichten welche das Debugging in Echtzeit verlangsamen "wegen" Purifier. Ich habe alle Datei Operation beispielsweise in Module verkappselt, damit man transparent aus dem RAM, Datei oder sonstiger Quelle "streamen" kann, z.B. StreamLOF(), das ganze noch mit einem Cache System verkompliziert. Oder ein "Archive2D" das noch dazwischen geschaltet sein kann ... Daher dauert das verarbeiten von kleinen INI Dateien (eigene Implementation) eben fast eine Minute mit Debugger wenn es eigentlich nur 0ms ohne dauert.

Nun es sind jetzt über 600kb an Code geworden über dutzende Module verteilt. :coderselixir: Fehler treten viel seltener auf (bis gar nicht) wenn ich ohne Threads alles laufen lasse. Ich begnüge mich meistens mit OnError wenn dann was abranzt. Wenn der Purifier auf <=64 eingestellt ist, ist alles unbrauchbar langsam - insbesondere für den Stresstest.

Ich benutze viel eingebettete PB Listen und Maps und nutze diese auch als Eingabe/Rückgabe Parameter. z.B. ListServerConntections(*Server, List result.i()) oder sowas. Auch viel String- und Speicher- Manipulation. Ich bin mir manchmal nicht ganz sicher, ob es auch nicht an PB liegt. Weil ich denke es könnte sein, daß wenn PB maps und listen pre-alloziiert, es dann beim nächsten neuen Block stecken bleibt oder sowas weil das über einen anderen (?) thread passiert. Reinste Spekulation! EnableThreads is selbstverständlich auch immer an, sonst geht es ganz schnell nicht mehr weiter.

Fehler sind Endlosschleifen (bei List oder Map ForEach Next), Invalid Memory (meist bei Procedure Rücksprung, oder bei AllocateMemory/AllocateStructure) - aber erst nach einer unbestimmt langen Wartezeit oder nach vielen Anfragen (+10,000 requests) mit Stresstest. Manchmal passiert nichts Sichtbares, es wird nichts auf der Console ausgegeben oder in die Logdatei geschrieben. Und die OnError Zeilen sind wie gesagt nicht immer zielführend. :bluescreen:

Es gibt nur einen Sourcecode den ich benutze, und der nicht von mir geschrieben wurde - das sind die numerischen Listen von wilbert im englischen Forum. Aber da hängt es nur äußerst selten und es dürfte eigentlich nicht (so wie ein IMA bei AllocateMemory eben). Das Mysterium besteht allgemein und ich vermute irgend welche memory leaks oder ein Stack Problem weil ich irgendwo meine Prototypen und Prozeduren nicht harmoniert habe. Doch das sollte dann eigentlich einfacher erkennbar sein!!!

Schämen sollte ich mich, weil ich noch keinen Chat in das System eingebaut habe. Ich will einen Chat erstmal per selbstverbockten Templates + Sqlite lösen, bevor ich Fast/CGI implementiere. Der Chat sollte eben nicht hard-coded sein. Und es müssen ja erst die Grundlagen funzen, ohne GUI weil ja möglicherweise reine Consolen oder Fullscreen Anwendungen dabei entstehen sollten.

Wenn es etwas gibt was mir jemand anraten oder abraten kann... ich bitte inständig um Aufkärung :praise:


Edit: NewList macht auch probleme. Seit 5,73 beta zeigt mir PB auch an wenn eine Liste mehrfach instanziiert wurde. Das hat auch solche Probleme verursacht, ist jetzt aber beseitigt worden. Trotzdem gibt es Fehler bei "Protected Newlist txt.s()" und Ähnlichen Code-stellen.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Debugging und Threads allgemein?

Beitrag von NicTheQuick »

Kennst du dich denn allgemein gut mit Threads aus? Weißt du wie man Ressourcen sauber mit Locks (Mutex), Semaphoren und Co schützt?
Bild
Benutzeravatar
juergenkulow
Beiträge: 188
Registriert: 22.12.2016 12:49
Wohnort: :D_üsseldorf-Wersten

Re: Debugging und Threads allgemein?

Beitrag von juergenkulow »

Hallo Benubi,

wie sieht denn Dein OnError ErrorHandler aus?

Code: Alles auswählen

; Thread ErrorHandler 64 bit
CompilerIf #PB_Compiler_Debugger=1 Or #PB_Compiler_LineNumbering=0 Or #PB_Compiler_Thread=0
  CompilerError "Debugger ausschalten und OnError Unterstüztung, Thread sicheres Executable einschalten." 
CompilerEndIf 

Procedure myThread(*ptr.Byte)
  *ptr\b=42 ; Verursacht einen Fehler in Zeile Nummer 7.
EndProcedure

Procedure ErrorHandler()
  Protected Fehlermeldung$=FormatDate("%dd.%mm.%yyyy %hh:%ii:%ss ", Date())+Str(ElapsedMilliseconds())+" ms "+
                           ErrorMessage()+" "+ErrorFile()+" Zeile:"+ErrorLine()+" "+ErrorAddress()+" "+
                           Hex(ErrorRegister(#PB_OnError_RSP))+" "+Hex(ErrorTargetAddress())
  Protected Datei=OpenFile(#PB_Any,"FehlerDatei.txt",#PB_File_Append)
  WriteStringN(Datei,Fehlermeldung$)
  CloseFile(Datei)
  MessageRequester("keine Fehlermeldung!", Fehlermeldung$)
  End -1
EndProcedure
  
MessageRequester("OnError Test", "Test starten")
OnErrorCall(@ErrorHandler())
CreateThread(@myThread(),4711) 
Delay(5000) 
; 09.12.2020 09:26:07 248 ms Segmentation violation /mnt/myErrorHandler.pb Zeile:7 4207724 7F3A08131B60 1267
; 09.12.2020 09:27:51 20 ms Segmentation violation /mnt/myErrorHandler.pb Zeile:7 4207724 7F732EB55B60 1267
Edit: @myThread() und Ergebnisse
Zuletzt geändert von juergenkulow am 09.12.2020 10:32, insgesamt 1-mal geändert.
Bitte stelle Deine Fragen, denn den Erkenntnisapparat einschalten entscheidet über das einzig bekannte Leben im Universum.

Jürgen Kulow Wersten :D_üsseldorf NRW D Europa Erde Sonnensystem Lokale_Flocke Lokale_Blase Orion-Arm
Milchstraße Lokale_Gruppe Virgo-Superhaufen Laniakea Sichtbares_Universum
Benutzeravatar
HeX0R
Beiträge: 2959
Registriert: 10.09.2004 09:59
Computerausstattung: AMD Ryzen 7 5800X
96Gig Ram
NVIDIA GEFORCE RTX 3060TI/8Gig
Win10 64Bit
G19 Tastatur
2x 24" + 1x27" Monitore
Glorious O Wireless Maus
PB 3.x-PB 6.x
Oculus Quest 2
Kontaktdaten:

Re: Debugging und Threads allgemein?

Beitrag von HeX0R »

Hier gibt's das Ganze etwas weniger rudimentär.
Hab's bei der Gelegenheit ein wenig aktualisiert (das Fenster war ziemlich winzig hier).

Wahnsinn... vor 14 Jahren hab ich das gepostet :shock:
Benubi
Beiträge: 186
Registriert: 22.10.2004 17:51
Wohnort: Berlin, Wedding

Re: Debugging und Threads allgemein?

Beitrag von Benubi »

Danke für die Rückmeldung!

@NicTheQuick
Ich gebe zu meine Erfahrungen mit Threads sind limitiert. Einen ThreadPool habe ich schon vor Jahren geschrieben, aber außerhalb von ein paar Tests damit nichts gemacht.

Ich Sperre immer das, worauf ich zugreifen möchte per Mutex. Es könnte aber immer sein, daß ich irgendwo etwas übersehe. Semaphoren benutze ich derzeit nicht - vielleicht irgendwo in einer nicht oder kaum genutzten queue, aber das weiß ich auf anhieb jetzt nicht. Ich benutze Referenz-Zähler für die kritischen Objekte, weil auf die mit mehreren Threads zugegriffen wird, ich die zu leb- und tot- zeiten in console/logfile/web-zugang auflisten/debuggen könnte. Außerdem werden beispielsweise die Cache-Dateien im RAM von mehreren Clients gleichzeitig gelesen. Da habe ich mich als unglücklicher "Assemblero" versucht, dann wieder auf Mutex umgestiegen. Damit meine ich ALLE Referenz-Zähler haben eine gemeinsame AddRef() procedure die per mutex jetzt geblockt wird um jedes einzelne objekt seine Referenzen zu erhöhen/dekrementieren. Es können keine 2 Objekte parallel inkrementiert/dekrementiert werden so zu sagen. Hierbei wird auch jedesmal nachgeschaut ob der Objekt-Zeiger in einer Registry steckt (ein Zeiger + AutoKey vergleich). Bringt aber auch nix.

Beispiel 1:1 kopiert. Das wird beispielsweise benutzt um einen "Job" hinzuzufügen. Das tut der (optionale) "Server thread". Der (optionale) Worker generiert dann die Antwort und sendet sie ab, indem er die Jobs in den Client-Listen abarbeitet (jeder Client hat eine Jobliste). Jobs haben einen Callback in ihrer Structure, je nach "Datei Handler" oder sonstwas geschehen soll (admin, statische Inhalte, Templates, später z.B. CGI).

Code: Alles auswählen

  Procedure.i AddSocketJob(*Job.N2D_Job, *Socket.N2D_Socket,TopOfList=#False,IsBlocking=#True)
    Shared _REG
    Protected *SERVER.N2D_Server
    If Not *Socket 
      ProcedureReturn #False 
    EndIf 
    
    *SERVER = *Socket\SERVER 
    
    If *SERVER = #Null 
      *Job\JobType  =#JOBTYPE_OUTSOCKET

    Else 
      *Job\JobType  =#JOBTYPE_SOCKET
    EndIf   
    
    ;N2D_LogFile::MSG("add socket job")
    
    
    *Job\IsBlocking = IsBlocking

    If *Job
      
       N2D_LogFile::DBG("Adding job to list")
      LockSocket(*Socket)
        ResetList(*Socket\Job())
        If TopOfList
          InsertElement(*Socket\Job())
          *Socket\Job() = *Job
        Else 
          AddElement(*Socket\Job())
          *Socket\Job() = *Job
        EndIf 
       UnlockSocket(*Socket)
       N2D_LogFile::DBG("Job added to list") 
       ProcedureReturn *Job
     EndIf 
     n2d::Release(*Job)
     ProcedureReturn #Null
    ; n2d::MakeRefer(*Socket,@*Job\SocketRef,1)
    
  EndProcedure
Meine kritischen Objekte sehen so aus (im Header)

Code: Alles auswählen

  Structure N2D_Ref
    iVT.i ; optional, ungenutzt
    Ref.i ; Referenz-Zähler
    OnFree.N2D_OnFree ; Objekt-Freigabe Procedure (wird von Garbage collector bei gc(0) ausgeführt, wenn das objekt in der Freigabeliste ist)
    RefAutoKey.i ; Objekt-Autokey (ID)
    gcExtra.i     ; Garbage collection Verzögerung in ms (optional)
    TypeID.s{16} ; ID ... z.B. "socket"  "cache-file"   "server"  ...
  EndStructure

@juergenkulow


Procedure MainOnError()
Protected NewList objids.s()
N2D_LogFile::LOGLINE("FATAL ERROR")
N2D_LogFile::FATAL("FATAL ERROR --- ON ERROR")
N2D_LogFile::FATAL("Error Address: "+N2D_String::StrPointer(ErrorAddress()))
N2D_LogFile::FATAL("Error Code: "+Str(ErrorCode()))
N2D_LogFile::FATAL("Error Message: "+ErrorMessage())
N2D_LogFile::FATAL("Error Target Address: "+N2D_String::StrPointer(ErrorAddress()))
N2D_LogFile::FATAL("Error File: "+ErrorFile())
N2D_LogFile::FATAL("Error Line: "+Str(ErrorLine()))

N2D_LogFile::LOGLINE("Registered object pointers")
n2d::ListPointers(objids())
ForEach objids()
N2D_LogFile::FATAL(objids())
Next
N2D_LogFile::LOGLINE("End of object pointers registry")

ErrorDialog() ; <- HeXOR
N2D_LogFile::Close()
Delay(1000)

End
EndProcedure
@HeXOR

Wurde jetzt gerade eingebaut. Stresstest läuft (10 Seiten auf dem Browser die auf refreshpage=0 die letzten 1000 requests sich ausgebelen lassen). Mir fehlt "leider" ein Paket logger.
Ini (Auszug). Es liefen 2 Server threads (80, 8080) auf der Eingangsseite und jeder Server hat einen WorkerThread auf der Ausgangsseite. SpamThread öffnet X URLs auf dem Browser für Stresstest.
MainThread=false
ServerThread=true
WorkerThread=true
CronJobThread=false
CacheThread=false
SpamThread=true
Das ist neu aber wie immer sagt es mir nichts.
Error Message: Invalid memory access
Error Code: -1073741819
Code Address: 4376397
Target Address: 60
Sourcecode line: 144
Sourcecode file: F:\PB_Sources\Nubi2D\src\NumericKeyMapModule.pb

Register content:
EAX = 0x18F59E0
EBX = 0x0
ECX = 0x18F59E0
EDX = 0x1E
EBP = 0x0
ESI = 0x0
EDI = 0x0
ESP = 0x24BFDDC
Zeile 144
Procedure.i NKMap_GetValue(*Map.NKMap, Key)

Zweiter test
Error Message: Invalid memory access
Error Code: -1073741819
Code Address: 4363290
Target Address: 84
Sourcecode line: 198
Sourcecode file: F:\PB_Sources\Nubi2D\src\Nubi2D_LogFile.pb

Register content:
EAX = 0x8
EBX = 0x3E8
ECX = 0x0
EDX = 0x0
EBP = 0x0
ESI = 0x1D6CE2A
EDI = 0xFFFFFFFFFFFFFFFF
ESP = 0x13FDB4
Zeile 198 :lol:
Format$ = FormatDate(N2D_LOG_FORMAT$,Date())
Mir fällt gerade auf, daß sowas anscheinend erst passiert wenn ich über meine externe Adresse zugreifen möchte (jene vom Internet Provider). Oder es geht einfach früher etwas schief. Der Stresstest läuft über localhost:80 und localhost:8080, pc-LAN-IP:80, pc-LAN-IP:8080. Nunja...



Dritter Versuch (vorletzer) : Vorführeffekt jetzt geht es mal wieder auch über die Externe... :freak: Dialog Fenster taucht nicht auf! Threads laufen im Hintergrund weiter. Auch die Logdatei wird nicht geschrieben. Letzte Zeile der Log-Datei:
09/12/2020 14:13:24 ::MainOnError FTL [55] FATAL ERROR --- ON ERROR########################################################################################################################
Unbekannte Zeile >_<

*Logdatei -> String-manipulation (inkl. Lokalisierung/Mehrsprachmodul) -> *Streamverkappsellung -> Dateiklasse
*= mit extra Mutex "verpackt".

Wenn das am Mehrsprachmodul liegt... Normalerweise bekommt jedes request eine Kopie per CopyMap(), und die sind dann auch ge-mutext. Aber es könnte auch sein daß ich irgendwo was dort übersehen habe. Aber die LogFile bockt ja erst nach dem fatalen Fehler, und wer weiß schon... Sorry, daß ich laut denken muß ;D

Ich setze die ErrorDialog() jetzt an den Anfang meiner selbstverhunzten OnError Procedure!

Es sind immer eine Handvoll von Zeilen auf die ich verwiesen werde. Diese ändern sich Heisenbug-artig. An den ersten Fehler mit NumericKeyMap kann ich mich nicht erinnern. Den zweiten kannte ich schon. Beim dritten bin ich mir weniger sicher, weil ich hatte schon viele Zombiethread-Kombinationen. Ai caramba.


Danke an alle, auch wenn ich immer noch im Dunkeln tappe. :allright:

Melde mich später wieder. Wenn jemand einen Tipp hat, immer her damit. Ich bin eine quasi Null in Assembler also bitte etwas Nachsichtig sein. :doh:
Benubi
Beiträge: 186
Registriert: 22.10.2004 17:51
Wohnort: Berlin, Wedding

Re: Debugging und Threads allgemein?

Beitrag von Benubi »

Es geht weiter... diesmal mit MessageBox Version von HeXOR und diese dann gleich an den Kopf der Procedure.

Ein Stresstest-Durchgang dauert ca. eine Minute, bis ich dann alles erfolgreich sabotiere oder es sich von selbst aufhängt.
Hi Unknown Autor,
i discovered an Error with Unknown Application V 0.1 :
Error Message: Invalid memory access
Error Code: -1073741819
Code Address: 4363295
Target Address: 84
Sourcecode line: 598
Sourcecode file: F:\PB_Sources\Nubi2D\src\Nubi2D_HTTP.pb

Register content:
EAX=0x8
EBX=0x3E8
ECX=0x0
EDX=0x0
EBP=0x0
ESI=0x1D6CE3B
EDI=0xFFFFFFFFFFFFFFFF
ESP=0x13FDB4
Zeile 598 =>
tFileSize =FileSize(*A\Filename + "/" + fname$ )
Hatte ich schon öfter.

Target Address ist wieder 84. Habe ich doch irgendwo anders schon gesehen. Was kann das bedeuten? :roll:

Log Datei ging auch durch. Hier ein Ausschnitt aus dem untersten Ende. Es werden Dutzende Streams je Request erzeugt. Header, Body von Anfrage und Antwort haben jeweils einen Stream als Minimalkonfiguration - das macht also 4 mindestens pro Request (wenn eine Antwort hard-coded wird wie beispiel ein 500 oder 404 Fehler). Ein Template kann aus X Schnippseln bestehen, welche ebenso als streams "transparent" included werden. Das alles, so wie andere volatile Objekte tragen dazu bei, daß die Autokeys schnell in die 10.000er gehen. Die Logdatei ist 7840 Zeilen lang und ab 3800 kommt die Fehlermeldung + Objekt-Auflistung. Es sind also tausende Objekte da die darauf warten recycled zu werden - ca. 4000. Und ca. 2000 requests rein und 2000 antworten raus.

(Ausschnitt/Beispiel)
09/12/2020 16:02:45 ::MainOnError FTL [70] 0x07DE5C74:[http-response] Ref[1] AutoKey[29267]################################################################################################
09/12/2020 16:02:45 ::MainOnError FTL [70] 0x0535715C:[stream] Ref[1] AutoKey[29879]#######################################################################################################
09/12/2020 16:02:45 ::MainOnError FTL [70] 0x04FDF28C:[stream] Ref[1] AutoKey[27772]#######################################################################################################
09/12/2020 16:02:45 ::MainOnError FTL [70] 0x04EB79AC:[stream] Ref[1] AutoKey[26835]#######################################################################################################
09/12/2020 16:02:45 ::MainOnError FTL [70] 0x083BF09C:[stream] Ref[1] AutoKey[30588]#######################################################################################################
09/12/2020 16:02:45 ::MainOnError FTL [70] 0x0512E514:[http-response] Ref[1] AutoKey[28915]################################################################################################

Ich kann auch die Threads anders konfigurieren. Oder einen Server abschalten (habe in der MainApp nur 2 vorgesehen). Wird sowieso schief gehen.


Jetzt läuft es wieder Minuten lang ohne Probleme.
:lamer:


OOOOH... 2 MessageRequester mit OnError. Das hatte ich auch noch nie. Habe aber auch 4 Threads die das vielleicht können? kA

Wieder Target Address: 84. Beim zweiten. Den ersten (älteren) muß ich weiter unten abtippen.

Letzte on error
Error Message: Invalid memory access
Error Code: -1073741819
Code Address: 4363295
Target Address: 84
Sourcecode line: 1919
Sourcecode file: F:\PB_Sources\Nubi2D\src\Nubi2D_HTTP.pb

Register content:
EAX=0x8
EBX=0x3E8
ECX=0x0
EDX=0x0
EBP=0x0
ESI=0x1D6CE3C
EDI=0xFFFFFFFFFFFFFFFF
ESP=0x13FDB4
Zeile 1919 : Hier könnte *server=#Null sein, wäre aber unlogisch.
N2D_LogFile::LOGFILE("OUT "+RSet(Str(*Server\ServerPort),5)+" "+HistoryLine$,"HTTP")

Erster OnError aufruf (von screenshot abtippen :freak:)
Error Message: Invalid memory access
Error Code: -1073741819
Code Address: 4338940
Target Address: 112
Sourcecode line: 3013
Sourcecode file: F:\PB_Sources\Nubi2D\src\Nubi2D_HTTP.pb

Register content:
EAX=0x0
EBX=0x0
ECX=0x6BF0060
EDX=0x0
EBP=0x0
ESI=0x0
EDI=0x0
ESP=0x2BFFE98
Zeile 3013
Protected NewList Batch.s(), NewMap Pairs.s(), NewList txt.s()
Und diesmal steht nix in der Log-Datei von Fehlern... Naja egal eben. :lol:


Glaubt ihr ich sollte das anders machen zum Beispiel nur mit einem Server auf einmal? Dann mache ich das beim nächsten mal. Für heute ist schluß! :freak:
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Debugging und Threads allgemein?

Beitrag von NicTheQuick »

Puh, das ist viel zu lesen und etwas abstrakt um sich reinzudenken.
Aber die ständige 84 lässt mich vermuten, dass du vielleicht irgendwo mit @ einen Pointer zu einem Structure-Field erhältst, dass zufällig an Byte-Position 84 in der Struktur zu finden ist. Und der Pointer, zu dem die Struktur gehört, ist aber 0.
Blödes Beispiel:

Code: Alles auswählen

Structure Belag
	dies.s
	und.s
	das.s
EndStructure
Structure Pizza
	a.q[10]
	b.l
	belag.Belag
EndStructure

Define *pizza.Pizza = 0

Define *belag.Belag = @*pizza\belag

Debug *belag
Zum Beispiel deine Zeile 598:

Code: Alles auswählen

tFileSize =FileSize(*A\Filename + "/" + fname$ )
Wo kommt das *A denn eigentlich her?

Bei deiner AddSocketJob-Procedure finde ich komisch, dass du am Anfang schon *Job\JobType zuweist und später erst testest, ob *Job überhaupt ungleich 0 ist.
Bild
Benutzeravatar
juergenkulow
Beiträge: 188
Registriert: 22.12.2016 12:49
Wohnort: :D_üsseldorf-Wersten

Re: Debugging und Threads allgemein?

Beitrag von juergenkulow »

Hallo Benubi,

Du suchst soetwas wie PeekI(60), PeekI(84) oder PeekI(112) aber in ASM-Code in Deinem Programm.
Vielleicht hilft schon "pbcompiler -c".

Gruß
Benubi
Beiträge: 186
Registriert: 22.10.2004 17:51
Wohnort: Berlin, Wedding

Re: Debugging und Threads allgemein?

Beitrag von Benubi »

@NicTheQuick

Genau das ist auch meine Vermutung. Ich habe den Eindruck auf nicht-initialisierte Strukturen zuzugreifen, oder daß sich durch einen Überlauf irgendwie dies ergibt. Ich glaube die 84 bezieht sich auf "leere" PB Objekte (PBListe/PBMap +84), oder es hat etwas mit Task-Switching zu tun.


@jurgenkulow

Ich werde mich daran machen. Poke und peek benutze ich sehr selten - ich werde aber nach den magischen Fehlerzahlen suchen.


Bis hierhin erstmal danke für die Antworten. Sollte ich Erfolg damit haben oder einen anderen Ansatz zur Fehlersuche finden melde ich mich.
Benutzeravatar
juergenkulow
Beiträge: 188
Registriert: 22.12.2016 12:49
Wohnort: :D_üsseldorf-Wersten

Re: Debugging und Threads allgemein?

Beitrag von juergenkulow »

Hallo,

ich habe die Fährte zu *pointer84.Byte=84 : *pointer84\b=42 gesucht:

Code: Alles auswählen

; ErrorHandler der Threadfehler überlebt und Zeilen um den Fehler ausgiebt. 
; Informationen werden in FehlerDatei.txt geschrieben, Schreibrecht im Verzeichnis sollten da sein.
; Teste Pointer mit der Speicheraddresse 84.
CompilerIf #PB_Compiler_Debugger=1 Or #PB_Compiler_LineNumbering=0 Or #PB_Compiler_Thread=0
  CompilerError "Debugger ausschalten und OnError Unterstüztung, Thread sicheres Executable einschalten." 
CompilerEndIf 
CompilerIf #PB_Processor_x64<>#PB_Compiler_Processor
  CompilerError "x64 wird unterstützt."
CompilerEndIf
EnableExplicit
Global AlleFehler$
Global MutexErrorHandler

Structure variant_Typ
  Typ.i
  StructureUnion
    aWert.a : bWert.b : cWert.c : dblWert.d : fltWert.f : iWert.i : lWert.l : qWert.q : *ptr : uWert.u : wWert.w   ;#PB_Word
  EndStructureUnion
  sWert.s     
  Zeilennr.l
EndStructure  ; offen Array, Interface, List, Map, Structure 

Define *zeilen.variant_Typ=AllocateMemory(1000000*SizeOf(variant_Typ)) ; P)latz für 1. Mio 
Define *ptrzeilen.variant_Typ=*zeilen 
Define *ptrCLN.long ; Pointer auf Zeilennummer besorgen.
! MOV rax,CLN 
! MOV [p_ptrCLN],rax

#PB_Zeilennr=$100
Macro Zeile
  *ptrzeilen\Zeilennr=*ptrCLN\l 
  *ptrzeilen\Typ=#PB_Zeilennr 
  *ptrzeilen+SizeOf(variant_Typ) 
EndMacro

Macro Wertl(dw) ;#PB_Long
  *ptrzeilen\lWert=dw
  *ptrzeilen\Zeilennr=*ptrCLN\l 
  *ptrzeilen\Typ=#PB_Long
  *ptrzeilen+SizeOf(variant_Typ) 
EndMacro

Procedure Thread_Pointer84(Wert)
  Shared *zeilen.variant_Typ
  Shared *ptrzeilen.variant_Typ
  Shared *ptrCLN.long
  WertL(84) : Protected *pointer84.Byte=84 
  ;*pointer84\b=42
  Zeile : Protected b.b=*pointer84\b
EndProcedure

Procedure ErrorHandlerThread()
  Shared AlleFehler$
  Shared *zeilen.variant_Typ
  Shared *ptrzeilen.variant_Typ 
  Shared MutexErrorHandler
  OnErrorCall(@ErrorHandlerThread())
  LockMutex(MutexErrorHandler)
  Protected Fehlermeldung$=FormatDate("Thread %dd.%mm.%yyyy %hh:%ii:%ss ", Date())+Str(ElapsedMilliseconds())+" ms "+
                           ErrorMessage()+" "+ErrorFile()+" Zeile:"+ErrorLine()+" "+ErrorAddress()+" "+
                           Hex(ErrorRegister(#PB_OnError_RSP))+" "+ErrorTargetAddress()
  Protected *zptr.variant_Typ=*zeilen
  While *zptr<*ptrzeilen
    Select *zptr\Typ ; offen: weitere Case.
      Case #PB_Zeilennr
        Protected s$=Str(*zptr\Zeilennr)
      Case #PB_Long
        s$=Str(*zptr\Zeilennr)+" L:"+Str(*zptr\lWert)
      Default
        s$="Fehler: unbekannter Typ"
    EndSelect
    Fehlermeldung$+" "+s$
    *zptr+SizeOf(variant_Typ)
  Wend   
  Protected Datei=OpenFile(#PB_Any,"FehlerDatei.txt",#PB_File_Append)
  WriteStringN(Datei,Fehlermeldung$)
  CloseFile(Datei)
  AlleFehler$+Fehlermeldung$+#LF$
  UnlockMutex(MutexErrorHandler)
  Repeat : Delay(1000) : ForEver
  ; Hier ist hat MessageRequester keine Wirkung.
  ; Es gibt auch andere Auswirkungen im Hauptprogramm.
EndProcedure

Procedure ErrorHandler()
  Shared AlleFehler$
  Protected Fehlermeldung$=FormatDate("Haupt %dd.%mm.%yyyy %hh:%ii:%ss ", Date())+Str(ElapsedMilliseconds())+" ms "+
                           ErrorMessage()+" "+ErrorFile()+" Zeile:"+ErrorLine()+" "+ErrorAddress()+" "+
                           Hex(ErrorRegister(#PB_OnError_RSP))+" "+ErrorTargetAddress()
  Protected Datei=OpenFile(#PB_Any,"FehlerDatei.txt",#PB_File_Append)
  WriteStringN(Datei,Fehlermeldung$)
  CloseFile(Datei)
  MessageRequester("pointer84 Fehler",Fehlermeldung$)
  End
EndProcedure

If #PB_MessageRequester_Yes=MessageRequester("pointer84 Fehler auslösen","Thread(ja) oder Hauptprogramm(nein)",#PB_MessageRequester_YesNo)
  MutexErrorHandler=CreateMutex()
  OnErrorCall(@ErrorHandlerThread())
  ;MessageRequester("Thread","Thread")
  CreateThread(@Thread_Pointer84(),0)
  CreateThread(@Thread_Pointer84(),0)
  CreateThread(@Thread_Pointer84(),0)
  Delay(5000)
  MessageRequester("fertig",AlleFehler$)
Else
  OnErrorCall(@ErrorHandler())
  ;MessageRequester("HP","HP")
  Define *pointer84.Byte=84:  
  ;*pointer84\b=42
  Define b.b=*pointer84\b
EndIf 

; Haupt 16.12.2020 03:01:54 55 ms Segmentation violation /mnt/p84.pb Zeile:111 4207086 7FFF619579C0 84

; Thread 16.12.2020 03:01:58 0 ms Segmentation violation /mnt/p84.pb Zeile:49 4207440 7F45DDD33B40 84 47 L:84 49 47 L:84 49 47 L:84 49
; Thread 16.12.2020 03:01:58 0 ms Segmentation violation /mnt/p84.pb Zeile:49 4207440 7F45DDD33B40 84 47 L:84 49 47 L:84 49 47 L:84 49
; Thread 16.12.2020 03:01:58 0 ms Segmentation violation /mnt/p84.pb Zeile:49 4207440 7F45DDD33B40 84 47 L:84 49 47 L:84 49 47 L:84 49
; offen: Anzeige von Fehlern im Thread in der Hauptschleife unter (Console, Console mit 
;   EnableGraphicalConsole, OpenWindow, ORGE, ORGE mit OpenScreen, OpenWindow3D, OpenGL, ...)
;   Makros WertTyp und Auswertung *zptr\Typ

; Problem: Ein für Thread und Hauptprogramm gemeinsamer ErrorHandler müßte prüfen ob der aktuelle 
;   Stack der Hauptprogramm-Stack ist.
Antworten