Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Allgemein: Wie findet man einen Fehler? (https://www.delphipraxis.net/183112-allgemein-wie-findet-man-einen-fehler.html)

nezumi7 12. Dez 2014 08:01

Allgemein: Wie findet man einen Fehler?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Liebe Alle,

ich schreibe gerade ein Prog (Delphi 6), das mir beim Gitarre-lernen helfen soll (man gibt Akkorde in ein RichEdit ein und die werden dann aus einer Resource geladen und abgespielt). Funktioniert alles super, wegen der vielen Einstellungsmöglichkeiten (Takt, Auf- / Abschläge, Wiederholungen, Geschwindigkeit etc.) ist das Programm ziemlich umfangreich geworden.

Leider tritt immer wieder ein Fehler auf, allerdings sehr selten und ohne erkennbaren Anlass, so dass es mir kaum möglich ist, den irgendwie zu reproduzieren. Von der Fehlermeldung habe ich den angehängten Screenshot gemacht. Für mich ist das völlig unverständlich.

Kann man da irgendeine Information rausziehen, die einem bei der Fehlersuche helfen könnte oder wie geht man da am besten vor?

(der Quellcode ist ein paar tausend Zeilen lang, so dass ich mal davon absehe, ihn hier zu posten).

Greetings,
Stephan.

hoika 12. Dez 2014 08:11

AW: Allgemein: Wie findet man einen Fehler?
 
Hallo,

installiere MadExcept.


Heiko

nezumi7 12. Dez 2014 08:17

AW: Allgemein: Wie findet man einen Fehler?
 
irre, was es so alles gibt.

Danke für den Tip, werde ich gleich mal ausprobieren :)

himitsu 12. Dez 2014 09:03

AW: Allgemein: Wie findet man einen Fehler?
 
Aus der CPU-Ansicht kann man selten die fehlerverursachende Stelle erkennen.
Wichtiger wäre hier ein Blick in den Stacktrace (Ansicht > Debug-Fenser > Aufruf-Stack), denn da sieht man nicht nur die aktuelle Stelle (wo es nach dem Knall hängen blieb), sondern auch wo der Aufruf her kann. (mit etwas Glück auch die Stelle, deines Codes, welche zuletzt ausgeführt wurde)

Wenn es im laufenden Betrieb auftritt, ohne daß man einen Debugger dran hat, dann kann man sich mit dem Debugger auch naträglich verbinden und schauen wo es hängt.
Den Fehlerdialog nicht wegmachen > Delphi staten > Start > Mit Prozess verbinden > uns los geht es.
(bei Erekalog geht das nicht, da dieses Mistding den Fehlerthread weiterlaufen lässt und den Fehlerdialog in einem anderen Thrad anzeigt ... oder irgendwie sowas, aber zum Glück zeigen die einen eigenen Stacktrace an, womit nicht gleich alles verloren ist)

Neben MadExcept gibt es auch noch EurekaLog uvm.

nezumi7 12. Dez 2014 09:12

AW: Allgemein: Wie findet man einen Fehler?
 
Danke himitsu,

da ich bei madexcept noch nicht so richtig durchsteige, werde ich auch Deine Tips versuchen.

Letztlich muss ich aber bei allen Varianten warten, bis der Fehler mal wieder auftritt, oder?

Dejan Vu 12. Dez 2014 09:13

AW: Allgemein: Wie findet man einen Fehler?
 
Ich würde deinen Code mit Fail fast - Anweisungen spicken. Die tun nicht weh (im Gegenteil) und machen die Anwendung i.A. auch nicht langsamer.

Fail fast bedeutet: Wenn Du irgend etwas suchst, ermittelst oder berechnest, fragst Du sofort danach ('fast') ab, ob das Ergebnis im Rahmen dessen ist, was Du erwartest. Wenn nicht, sofort mit einer Exception abbrechen ('fail').

Delphi-Quellcode:
myObject := FindMyObject(someSearchCriteria);
if not Assiged(myObject) then raise Exception.Create('myObject is not set');
Deine Methoden prüfen als allererstes, ob die übergebenen Parameter gültig sind.
Delphi-Quellcode:
Procedure TMyClass.SomeObject (someParameter : TParameter);
Begin
  if not Assiged(someParameter ) then raise Exception.Create('someParameter is not set');
  ...
Dafür gibt es zudem die 'Assert' ('Stelle sicher das...') Methode...
Delphi-Quellcode:
myObject := FindMyObject(someSearchCriteria);
Assert (Assiged(myObject),'myObject is not set');
...
Procedure TMyClass.SomeObject (someParameter : TParameter);
Begin
  Assert (Assiged(someParameter), 'someParameter is not set.');
  ...
Dann schalte noch die Bereichsprüfung an (Compiler-Optionen), um sicherzugehen, das Du immer auf gültige Arrayelemente zugreifst.

Und zum Schluss: Beschäftige dich mit Unit-Tests. Klar, für ein Hobbyprojekt ein leichter Overkill, aber wenn man das mal gefressen hat, schreibt man einfach schneller richtig gut funktionierende Programme.

noisy_master 12. Dez 2014 09:20

AW: Allgemein: Wie findet man einen Fehler?
 
Hallo Gemeinde,

wo wir schon bei dem Thema sind hätte ich dazu auch noch eine Frage:

Bei mir ranzt mein Programm beim herunterfahren ab(genauen Fehler kann ich gerne bei Bedarf nachliefern). Ich habe Eurekalog laufen und bekomme auch einen wunderbaren callstack, ABER: es schein irgendwas mit irgendeinem Null-Pointer im Destruktor von einem TAdvEdit zu sein....und es tauchen nirgendwo meine Quellen in dem Stack auf...Was nun?
(ich glaube ich hatte sowas ähnliches schon mal war irgendwas mit nem TadvEdit auf nem Gridpanel
(bitte keine Kommentare zur Architektur :wink:) kann mich aber nicht mehr erinnern was das war bzw wie ich das gelöst habe)

Hat irgendwer ne Idee, wie ich dahinterkomme, welches meiner vielen AdvEdits da ein Problem hat?

Danke schon mal im voraus!

Gruß
Dirk

nezumi7 12. Dez 2014 09:39

AW: Allgemein: Wie findet man einen Fehler?
 
Danke Dejan Vu,

für einen Sonntag-Nachmittags Programmierer wie mich ist das starker Tobak, ich würde es aber trotzdem gerne mal so versuchen, weiß aber nicht so recht wie.

Ich glaube, dass der Fehler bei meinem Timer liegt und poste mal den extrem gekürzten Code hierzu

Delphi-Quellcode:
procedure TimeCallBack(TimerID, Msg: Uint; dwUser, dw1, dw2: DWORD); pascal;
begin
With Form1 do begin
TimerEvent.Execute;
end;
end;

procedure StartTimer(ms: Integer);
begin
 mmResult := TimeSetEvent(ms, 0, @TimeCallBack, 0, TIME_PERIODIC);
end;

procedure StopTimer;
begin
 TimeKillEvent(mmResult);
end;


procedure TForm1.startplayExecute(Sender: TObject);
begin
StartTimer(intervall);
end;


procedure TForm1.TimerEventExecute(Sender: TObject);
begin
chrd := sl.Strings[z];
If chrd <> '' then PlaySound(PChar(chrd), hInstance, snd_ASync or snd_Resource);
inc(z);
end;
Bitte nicht so kritisch anschauen, ich habs so stark gekürzt, dass es so nicht funktioniert, ich will nur das Prinzip verdeutlichen. Wie müsste ich da jetzt deine fail fast Abfrage einbauen?

noisy_master 12. Dez 2014 09:48

AW: Allgemein: Wie findet man einen Fehler?
 
Hi nezumi,


wie du da sinnvoll die Asserts einbaust weiss ich auch gerade nicht, aber wenn ich mir deinen Code so ansehe

Zitat:

Zitat von nezumi7 (Beitrag 1283203)
Danke Dejan Vu,
Delphi-Quellcode:
 
procedure TForm1.TimerEventExecute(Sender: TObject);
begin
chrd := sl.Strings[z];
If chrd <> '' then PlaySound(PChar(chrd), hInstance, snd_ASync or snd_Resource);
inc(z);
end;

dann sehe ich da sofort einen Kandidaten, der mir suspekt vorkommt:
sl.Strings[z]; und inc(z);

Wie stellst du denn sicher, dass z nie grösser wird als die Stringlist?

Gruß
Dirk

himitsu 12. Dez 2014 09:49

AW: Allgemein: Wie findet man einen Fehler?
 
Zitat:

Hat irgendwer ne Idee, wie ich dahinterkomme, welches meiner vielen AdvEdits da ein Problem hat?
Hast du mal versucht dein Programm schrittweise zu beenden? (Haltepunkt und dann F7)

Zitat:

Letztlich muss ich aber bei allen Varianten warten, bis der Fehler mal wieder auftritt, oder?
Ja, aber zum Probieren kannst du dir ja erstmal selber irgendwo einen Fehler erzeugen
Delphi-Quellcode:
raise Exception.Create('Mein Test-Fehler');
.

Dejan Vu 12. Dez 2014 10:07

AW: Allgemein: Wie findet man einen Fehler?
 
Zitat:

Zitat von noisy_master (Beitrag 1283207)
wie du da sinnvoll die Asserts einbaust weiss ich auch gerade nicht...Wie stellst du denn sicher, dass z nie grösser wird als die Stringlist?

Mit einem Assert :mrgreen:

Delphi-Quellcode:
procedure TForm1.TimerEventExecute(Sender: TObject);
begin
  Assert((z>=0) and (z<sl.count),'z ist zu klein oder größer als die Länge von sl'); // <<<<<
 chrd := sl.Strings[z];
 If chrd <> '' then PlaySound(PChar(chrd), hInstance, snd_ASync or snd_Resource);
 inc(z);
end;
Es knallt an einer definierten Stelle und nicht 'irgendwo'. Obwohl das 'irgendwo' nun gerade die nächste Zeile ist, insofern... Ist das weniger FailFast und eher 'debug-code'. Aber im ein "Assert" ist es... ;-)

nezumi7 12. Dez 2014 10:26

AW: Allgemein: Wie findet man einen Fehler?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

dann sehe ich da sofort einen Kandidaten, der mir suspekt vorkommt:
sl.Strings[z]; und inc(z);

Wie stellst du denn sicher, dass z nie grösser wird als die Stringlist?
Das hab ich schon alles berücksichtigt. Es funzt ja auch fast immer, leider nur fast. Ich häng das Prog (noch lang nicht fertig) mal an. Fehlermeldung am Anfang ignorieren. Song über die Combobox links oben laden....

noisy_master 12. Dez 2014 11:26

AW: Allgemein: Wie findet man einen Fehler?
 
Hi,

Zitat:

Zitat von himitsu (Beitrag 1283208)
Hast du mal versucht dein Programm schrittweise zu beenden? (Haltepunkt und dann F7)

haltepunkt ist gut, aber wo?

Dejan Vu 12. Dez 2014 14:05

AW: Allgemein: Wie findet man einen Fehler?
 
Vielleicht solltest Du den Quellcode noch anhängen.

p80286 12. Dez 2014 15:03

AW: Allgemein: Wie findet man einen Fehler?
 
Zitat:

Zitat von noisy_master (Beitrag 1283226)
Hi,

Zitat:

Zitat von himitsu (Beitrag 1283208)
Hast du mal versucht dein Programm schrittweise zu beenden? (Haltepunkt und dann F7)

haltepunkt ist gut, aber wo?

Da wo Du das Programm beendest?
das
Delphi-Quellcode:
end.
ist u.U. auch ein guter Ort dafür oder
Delphi-Quellcode:
finalization
.

Gruß
K-H

ExceptionOverflow 12. Dez 2014 15:07

AW: Allgemein: Wie findet man einen Fehler?
 
Zitat:

Zitat von nezumi7 (Beitrag 1283217)
Zitat:

dann sehe ich da sofort einen Kandidaten, der mir suspekt vorkommt:
sl.Strings[z]; und inc(z);

Wie stellst du denn sicher, dass z nie grösser wird als die Stringlist?
Das hab ich schon alles berücksichtigt. Es funzt ja auch fast immer, leider nur fast. Ich häng das Prog (noch lang nicht fertig) mal an. Fehlermeldung am Anfang ignorieren. Song über die Combobox links oben laden....

ein kompiliertes Programm hilft uns nicht viel weiter, denn dann bekommen wir nur den Absturz zu sehen :P Die Sourcen sind wichtig.
Debuggen ist Übung und man muss ein Gefühl dafür entwickeln. Mit der Zeit (Erfahrung und Wissen) kennt man übliche Fehlerquellen oder weis auch selbst bei welcher Code Stelle man sich unsicher gefühlt hat beim schreiben. Im Notfall setzt du den Haltepunkt ganz am Anfang und gehst Schritt für Schritt durch, das klingt anfangs etwas viel aber normal sollte dein Programm ja keine Tausendzeilen Code durchlaufen wenn du nur mal einen Testfall prüfst.

BadenPower 12. Dez 2014 15:53

AW: Allgemein: Wie findet man einen Fehler?
 
Hallo zusammen,

einen Riesenfehler hast Du schon einmal, wenn Du nach dem Programmstart, ohne dass Du ein "Lied" selektiert hast, auf "Pause" drückst und dannach sofort aus "Cont".

Hier würde ich einmal ansetzten zu suchen.
"Keine Daten da, aber weitermachen" kann nicht gutgehen.


Und heisst Deine "Form1" auch tatsächlich immer "Form1"?
Delphi-Quellcode:
procedure TimeCallBack(TimerID, Msg: Uint; dwUser, dw1, dw2: DWORD); pascal;
begin
With Form1 do begin
TimerEvent.Execute;
end;
end;

Edit:

Das Teil steckt ja voller Fehler.

Wenn man wärend des Abspielens die Clear-Taste drückt, dann kommt eine Exception.

Hast Du ein paar Akkorde eingegeben und spielst diese komplett ab und löscht dann das Memo-Feld, dann wird nur das Memo gelöscht, aber die Akkorde sind weiter da und können abgespielt werden.

Zacherl 12. Dez 2014 18:09

AW: Allgemein: Wie findet man einen Fehler?
 
Die im ersten Post gezeigte CPU Ansicht lässt auf jeden Fall darauf schließen, dass versucht wird die virtuelle Methode eines nicht erzeugten Objekts zu callen. Das MOV EAX, [EAX] ist vermutlich die Dereferenzierung des Objekts mit dem Ziel die Adresse der VTable zu ermitteln. Da hagelt es dann die Exception, da EAX (das Objekt) in deinem Context noch nicht created wurde.


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:22 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz