Autor Beitrag
Zimond
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 49



BeitragVerfasst: Mi 21.01.09 23:49 
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. (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.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
objektdaten = record
 name :string;
 size : tpoint;
 zustand : integer;
end;

läßt zum Beispiel andere Konfusionen entstehen als :
ausblenden 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 user profile iconNarses: Delphi-Tags hinzugefügt
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10183
Erhaltene Danke: 1256

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Do 22.01.09 00:29 
Moin!

user profile iconZimond hat folgendes geschrieben Zum zitierten Posting springen:
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:

user profile iconZimond hat folgendes geschrieben Zum zitierten Posting springen:
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).

user profile iconZimond hat folgendes geschrieben Zum zitierten Posting springen:
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

_________________
There are 10 types of people - those who understand binary and those who don´t.
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: 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 ;-)).

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
Zimond Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 49



BeitragVerfasst: 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;

ausblenden 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 :

ausblenden 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.

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...

ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2971

Windows Vista Ultimate
D7 Enterprise
BeitragVerfasst: Di 27.01.09 17:54 
user profile iconZimond hat folgendes geschrieben Zum zitierten Posting springen:
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 :)
ausblenden 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 49



BeitragVerfasst: Di 27.01.09 18:37 
Ah herrlich, keine Fehler mehr. Bereichsprüfung FTW. Dankeschön.