Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Statt einer Zugriffsverletzung entsteht Variablen Wirrwarr.
Zimond - Mi 21.01.09 23:49
Titel: Statt einer Zugriffsverletzung entsteht Variablen Wirrwarr.
N'abend.
Ich hab mich entschlossen ein seltenes Problem welches mich schon wieder ereilt hat nun mal ausführlich zu beschreiben in der Hoffnung das es eine Erklärung dafür gibt.
Kurz als Erklärung : Vor mehr als mittlerweile 4 Jahren habe ich eine Point & Click Engine und das dazugehörige Autorensystem erstellt welches ich seit dem konstant erweitert und verbessert habe. (
http://www.adventure-creator.com) Die aktuelle Version ist bereits der 52. Release. Ich erwähne das nur deshalb um zu zeigen das ich schon viele Bugs finden musste und eigentlich immer weis wie ich dabei vorgehen muss um einen Fehler einzugrenzen. Bei dieser Art Problem versagt die übliche Herangehensweise leider. Also was ist passiert...
An einer bestimmten reproduzierbaren Stelle in einem Adventureprojekt entsteht ein Fehler der dafür sorgt das viele Variablen durcheinander gemischt werden, also wirklich so das a auf einmal den Inhalt von c hat und c den von x und so weiter. Beim letzten Male ist es mir gelungen eine Ursache zu finden. In einer Spieldatei waren noch Daten vorhanden die zu einem gelöschten Objekt verwiesen. Ich konnte diesen Fehler genau nachvollziehen und genau an der Stelle im Spiel wo der Fehler begann muss im Code versucht worden zu sein aus einem Array von 1..2000 den Eintrag -1 herauszulesen. Das hätte zu einer Zugriffsverletzung führen müssen!
Doch genau das geschah nicht. Statt dessen wurden eben viele Variablen durcheinander geschmissen was wiederrum zu zahlreichen Fehlern führte die mit dem Ursprungsbug gar nichts zu tun hatten. Um zu verdeutlichen das dieses Mischverhalten wirklich nicht logisch durch einen Fehler (oder mehrere) in meinem Code erklärt werden kann folgende Beobachtung :
Dieser Variablentausch ist reproduzierbar aber er läßt sich ändern und zwar in dem ich in den Variablendefinitionen Position von Einträge vertausche(!!) d.h.
Delphi-Quelltext
1: 2: 3: 4: 5:
| objektdaten = record name :string; size : tpoint; zustand : integer; end; |
läßt zum Beispiel andere Konfusionen entstehen als :
Delphi-Quelltext
1: 2: 3: 4: 5:
| objektdaten = record zustand : integer; name :string; size : tpoint; end; |
Nun stehe ich wieder vor diesem Effekt und habe nur die Erkenntniss das irgendwo im Code ein Fehler sein kann der eine Meldung auslösen hätte müssen aber stattdessen Chaos auslöst.
Ich muss dazu sagen, ich verwende das nun nicht mehr wirklich frische Delphi 7. Ein Upgrade würde mich zwingen einiges anzupassen da viele Komponenten die ich verwende entweder auch upgegradet werden müssten oder nicht vorhanden sind. Viel Arbeit die ich mir wirklich nur dann machen möchte wenn ich mir sicher sein kann das Problem damit zu beheben.
Wie kann es passieren das eine Zugriffsverletzung übergangen wird und zum Datenchaos führt? Achja, ich habe in meinem Code nicht eine einzige TRY FINALLY Stelle drin. Zugriffsverletzungen jeglicher Art müssten also immer zum Halt des Programms führen.
Danke schon mal für jeden der bis hier hin durchgelesen hat.
Moderiert von
Narses: Delphi-Tags hinzugefügt
Narses - Do 22.01.09 00:29
Titel: Re: Statt einer Zugriffsverletzung entsteht Variablen Wirrwarr.
Moin!
Zimond hat folgendes geschrieben : |
| genau an der Stelle im Spiel wo der Fehler begann muss im Code versucht worden zu sein aus einem Array von 1..2000 den Eintrag -1 herauszulesen. Das hätte zu einer Zugriffsverletzung führen müssen! |
Dazu musst du allerdings in den Compiler-Optionen die Bereichsprüfung aktivieren (normalerweise ist das aus, da hier zusätzlicher Code generiert wird). :idea:
Zimond hat folgendes geschrieben : |
| Ich muss dazu sagen, ich verwende das nun nicht mehr wirklich frische Delphi 7. |
Das sehe ich überhaupt nicht als Problem an, ich verwende auch D7pro (und werde es nicht updaten - wozu auch, für Win32 ist das genau richtig).
Zimond hat folgendes geschrieben : |
| Achja, ich habe in meinem Code nicht eine einzige TRY FINALLY Stelle drin. Zugriffsverletzungen jeglicher Art müssten also immer zum Halt des Programms führen. |
Das halte ich eher für bedenklich, als für ein "Qualitätsmerkmal"... :? Ressourcen-Schutzblöcke sind keine Kosmetik, sondern wichtiger Teil einer defensiven Programmierweise! ;)
Du könntest auch mal einen Memory-Checker (MemCheck z.B.) probieren, damit habe ich auch schon einige Speicherlöcher gefunden.
cu
Narses
BenBE - Do 22.01.09 02:35
Ferner kann ich Dir empfehlen, mal das Omorphia Debugging Interface (OdbgIntf.pas) mit in dein Projekt zu linken und dir dort einen Hook selber zu schreiben, der "In-Process" debuggt (ACHTUNG: Ich weiß nicht, wie wohlgesinnt Vista auf diese Technik reagiert! - Bin für Erfahrungsberichte gern zu haben ;-)). Lass Dir auf jeden Fall beim Auftreten von Fehlern vom Debug-Interface den aktuellen Stacktrace geben (und schreib den in ne Logfile). Dann kannst Du z.B. mit OmMAP ggf. auch nachträglich den Callstack zum Fehlerzeitpunkt mit Debug-Informationen wie Zeilennummern versehen. (Es gibt bereits zufriedene Nutzer, die damit Fehler gefunden haben, die nur einmal alle 4 Wochen auftraten ...) Alternativ könntest Du auch EurekaLog oder madExcept verwenden (Das ODbgIntf ist aber frei ;-)).
Zimond - Di 27.01.09 17:31
Zunächst mal vielen Dank für die Antworten, die sollten mich hoffentlich weiterbringen.
Es ist tatsächlich so das ich bisher angenommen habe das jegliche Verletzungen von Bereichen in Fehlermeldungen resultieren würden. Tatsächlich befinden sich zahlreiche dieser Fehler in meinen Codes die ich zur Zeit am korrigieren bin. :oops: Ich hoffe damit am Ende das eigentliche Problem beseitigen zu können. Das war wohl leider wieder etwas was mir völlig unbewusst war. Das ist das Problem von reinem Learning by Doing ohne wenigstens einmal eine Fachlektüre konsumiert zu haben. Man erweitert zwar seine Fähigkeiten und ist in der Lage durchaus umfangreiche Software zu erstellen macht dafür aber bei Begriffen wie Stacktrace oder Hook etwa so ein Gesicht --> :shock:
Ich habe allerdings jetzt beim Debuggen der Bereichsverletzungen etwas anderes festgestellt. Scheinbar könnte Delphi7 doch Teilschuld sein (oder mir ist auch hier wieder etwas grundlegendes nicht bewusst, bitte sagt es mir dann). Es scheint Probleme zu geben wenn große Zahlen direkt in den Quellcode eingegeben werden.
Ich hatte eine Bereichsverletzung festgestellt bei einer Berechnung die zum Fehlerzeitpunkt die Werte hatte :
175 * 16777216;
Delphi-Quelltext
1: 2: 3:
| var Farbe : cardinal;
Farbe := staerke * 16777216; |
Das Ergebniss sollte auf jeden Fall in einen Cardinal reinpassen solange Staerke nicht größer als 256 ist. Testweise habe ich einfach mal probiert Staerke im Code direkt durch 175 zu ersetzen um sicher zu gehen das der zum Haltepunkt angezeigte Wert von staerke auch nicht außerhalb des zulässigen Bereichs war.... sprich :
Delphi-Quelltext
1:
| Farbe := 175 * 16777216; |
Und nun wird beim komplilieren der Fehler ausgegeben :
Overflow in conversion or arithmetic operation. Danach gegoogelt fand ich dies hier.
http://qc.codegear.com/wc/qcmain.aspx?d=2258
Ein Fehler der laut Kommentaren erst ab Delphi2005 korrigiert sein soll. Tatsächlich funktionierte auch der vorgeschlagene WorkAround indem die große Zahl einfach nicht direkt abgelegt wird denn...
Delphi-Quelltext
1: 2: 3: 4:
| var farbe,zahl167 : cardinal;
zahl167 := 256*256*256; farbe := staerke * zahl167; |
funktioniert tadellos.
PS : Wegen TRY FINALLY. Was ich bisher dazu verstanden habe ist das diese Befehle dazu da sind Daten im Falle eines kritischen Fehlers noch zu retten. Sprich eine Datei noch fertig abzuspeichern ect. Welchen Nutzen hätte ich aber in einer Spiele-Engine davon wo es doch keine Daten zu retten sind, da ja nur gelesen und nicht gespeicgert wird (mal abgesehen von den Savegames) Außer also dem Spieler beim Abbruch noch seinen Spielfortschritt zu sichern gäbe es sonst noch Bereiche wo man dies anwenden sollte?
JayEff - Di 27.01.09 17:54
Zimond hat folgendes geschrieben : |
| Welchen Nutzen hätte ich aber in einer Spiele-Engine davon wo es doch keine Daten zu retten sind, da ja nur gelesen und nicht gespeicgert wird (mal abgesehen von den Savegames) Außer also dem Spieler beim Abbruch noch seinen Spielfortschritt zu sichern gäbe es sonst noch Bereiche wo man dies anwenden sollte? |
Es spräche schonmal für guten Programmierstil, Speicher, den dein Program alloziiert hat, wieder freizugeben, auch wenn eine AV auftritt, darum auch "Resourcenschutzblock" :)
Interessantes Verhalten, was Delphi da bezüglich hardgecodeter großer Zahlen an den Tag legt. Die Compilermagic (?) rechnet ja bereits bekannte Ergebnisse gern aus, bevor sie kompiliert, vielleicht liegt's daran, sollte aber natürlich dennoch nicht sein.
Was deine Bereichsüberschreitung betrifft: Lustige, lustige Dinge geschehen, wenn man bei dynamischen Arrays über den Speicher hinaus schreibt :)
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| var a: array of Byte; i: Integer; begin SetLength(a, 4); for i := 0 to 100 do a[High(a) + i] := 3; |
Funktionierte bei mir z.B. ohne Fehler :)
Dann eine Änderung im Quellcode, AVs. Änderungen rückgängig gemacht, und schon erhalte ich das wunderbare Verhalten, das ich schonmal hatte: Programm läuft, aber beim Beenden hagelt es EInvalidPointer Exceptions und irgendwann gibt's nen Runtime Error. Viel Spaß beim Debuggen!
Zimond - Di 27.01.09 18:37
Ah herrlich, keine Fehler mehr. Bereichsprüfung FTW. Dankeschön.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!