woensdag 19 september 2007

Adv. debugging: hook all exceptions!

In mijn vorige artikel schreef ik hoe je een “stack trace” kunt maken. Maar soms is dat niet genoeg. Je krijgt bijvoorbeeld een ongrijpbare “Catastrophic failure” of de stack trace geeft niet precies de echte exceptie weer. Dit komt omdat een stack trace niet goed werkt als deze in een try..except (of try..catch in C) afgevangen wordt. Als je namelijk onderstaande code probeert:


try
raise Exception.Create(’hier gaat het fout’);
except
raise; //deze regel geeft stack trace weer
end;


dan geeft de stack trace niet de 2e regel (Exception.Create) aan als de plek waar het fout ging, maar de 4e, waar de exception opnieuw geraised wordt. Nu is het in het bovenstaande voorbeeld wel duidelijk waar het fout ging, maar niet als de code groter is en geneste procedures heeft.

Ik heb dit namelijk zelf vaak meegemaakt. Dan dacht ik: ik heb een mooie exception handler met stack trace gebouwd, nu kan ik alle fouten precies lokaliseren en oplossen. Maar kwam ik menigmaal bij een stack trace uit op een try..except combinatie… Dan weet je nog niet waar het fout ging (vooral als het geen code van jezelf is, waar je de try..except kunt aanpassen).

Gelukkig kwam ik een andere JEDI unit tegen: JclHookExcept.pas. Hiermee is het mogelijk om ALLE exceptions binnen te krijgen! Het maakt namelijk een exception hook: de “RaiseException” functie van “kernel32.dll” wordt omgeleid (detouring, hier hoop ik ook een stuk over te schrijven) naar een eigen functie. Omdat hier alle fouten binnen komen, voordat ze door een try..except afgevangen worden, kun je deze fouten eenvoudig loggen!. Als je dan vaak smerige trucs toepast zoals:

try
i := StrToInt(string);
except
i := -1;
end;

dan val je gauw door de mand: de Econversion exception komt dan netjes via de exception hook binnen :-).

Wat ik vooral handig vind met deze exception hook, is dat je “verborgen” fouten in bijvoorbeeld dll’s kunt traceren, of “Catastrophic failures” kunt achterhalen. Voor dit laatste heb ik een voorbeeld gebruikt van een QC posting: http://qc.borland.com/wc/qcmain.aspx?d=6069
Hieraan heb ik de volgende code toegevoegd:



uses
JclDebug, JclHookExcept;



procedure AnyExceptionNotify(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean);
var
sError:string;
begin
if ExceptObj is Exception then
begin
sError := Format(’%s at adress %p: %s’,
[ExceptObj.ClassName, ExceptAddr,
(ExceptObj as Exception).Message]);

if OSException then
sError := ‘OS ‘ + sError;
sError := ‘HOOK: ‘ + sError;
MessageDlg(sError, mtError, [mbOK], 0);
end;
end;

initialization
JclStartExceptionTracking;
JclAddExceptNotifier(AnyExceptionNotify);

Dit geeft bij het uitvoeren de volgende afgehandelde fout:
—————————
Error
—————————
HOOK: Exception at adress 0047D639:
IErrorServer.RaiseServerError
Error at server side
—————————
OK
—————————

Normaal zou je alleen “Catastrophic failure” krijgen!

dinsdag 4 september 2007

Favoriet programma 2: Total Commander

Total Commander is het meest gebruikte Windows programma door mij (naast Delphi en Firefox :-).
Het is typisch zo’n programma waarvan je moet houden: je vind het super of je vindt het niets.

Total Commandor is een bestandsbeheer programma, als vervanger voor Windows Verkenner.
Het is een cloon van uit het dos tijdperk bekende Norton Commander. Het belangrijkste kenmerk is
de dubbele weergave van directories (naast elkaar). Dit heeft grote voordelen, omdat je zo eenvoudig bestanden
kunt kopieren van de ene directory naar de andere. Of de bestanden vergelijken, een selectie maken en
deze inpakken, etc. Zip bestanden kunnen net als directories bekeken en genavigeerd worden. Op dezelfde
manier kan ook via de ingebouwde FTP client bestanden geup- en download worden.

Een screenshot als voorbeeld:

Sinds de laatste versie is ook tab-support toegevoegd, net als Firefox en IE7. Hierdoor kun je nog
makkelijker meerdere directories open hebben en vergelijken.

Verder onderscheidt het zich door een snelle en krachtige zoekfunctie (eentje die WEL werkt, in
vergelijking met F3 van Verkenner…). Het resultaat kan ook als directorie bekeken, opgeslagen,
verwijderd, gekopieerd, ingepakt, etc worden.

Selecties kunnen eenvoudig gemaakt worden met de muis of toetsenbord, en op basis van een gedeelte
van een naam. Door de + te gebruiken wordt een nieuwe selectie toegevoegd, met de - verwijderd.

De kopieer functie is ook handig en krachtig: alles overschrijven, alleen oude bestanden, alles overslaan, etc.
Daarnaast kunnen veel acties (kopieren, inpakken, downloaden) op de achtergrond (in een thread) uitgevoerd
worden, zodat je ondertussen gewoon door kunt werken!

De ingebouwde viewer verdient ook aandacht: het ondersteunt verschillende bestandtypes en weergave modi
(oa hex). Maar vooral grote bestanden kan het moeiteloos inlezen: alleen bijvoorbeeld het begin of einde wordt
ingelezen, zodat een tekstbestand van 2Gb direct getoond wordt!

Naast de normale browse functionaliteit heeft het onder de motorkap veel andere handige tools en functies, zoals:
- multi rename tool
- split/combine files (!)
- CRC check
- file/directorie comparing
- plugins
- etc

Maar alle voordelen hebben ook een nadeel: het is geen gratis programma. Maar het is wel als shareware te gebruiken.
Je moet dan bij het opstarten willekeurig en 1,2 of 3 in toetsen. Dus gelukkig geen tijd controle of andere
beperkingen. Zelf heb ik op een gegeven moment het programma gewoon gekocht (als 1 van de weinige).

Maar hoe zit het dan Linux, zal misschien iemand denken? Daar werkt Total Commander ook! Echter wel
via Wine, en je moet eventueel een aantal fonts installeren. Maar verder werkt het als een zonnetje.

Kortom, Total Commander is echt een tool voor programmeurs, want dan gebruik je veel tekst bestanden, directories,
zip bestanden, zoeken en vergelijken, etc. En dan kun je veel van de functionaliteit goed kunnen gebruiken.
Laat ik tot slot een discussie uitlokken door de stellen: je bent geen goede programmeur zonder Total Commander! :-)

Ik hoop een volgende keer een aantal plugins te bespreken, want deze geven Total Commander nog meer
functionaliteit.

Adv. Delphi debugging: Exception stack trace

Je kent het wel, je bent zelf je code aan het testen op een andere pc of door gebruikers c.q. testers,
en je krijgt allerlei ongrijpbare “acces violations”, ontraceerbare “index out of bounds” etc.
Normaal kun je in Delphi deze fouten debuggen, onder andere met de “stack view”, maar die heb
je niet op het test systeem staan.

Als oplossing hiervoor is de jclDebug unit van JEDI library:
http://sourceforge.net/projects/jcl

Met het volgende commando zet je “exception tracing” aan:
jclDebug.JclStartExceptionTracking

Om de stack trace bij een exception uit te lezen:
jclDebug.JclLastExceptStackListToStrings(str, True, True, True);

Bijvoorbeeld:
procedure TForm1.FormCreate(Sender: TObject);
begin
jclDebug.JclStartExceptionTracking;
Application.OnException := HandleException;
end;

procedure TForm1.HandleException(Sender: TObject; E: Exception);
var
str:TStrings;
begin
str := TStringList.Create;
jclDebug.JclLastExceptStackListToStrings(str, False, False, True);
MessageDlg( e.Message + #10#13 + str.Text, mtError, [mbOK], 0 );
str.free;
end;

Je zou dan bij een error het volgende kunnen krijgen:

EAccessViolation with message ‘Access violation at address 00403924. Read of address 2009FFD4′.
[00403924] System.TObject.ClassName (Line 8726, “sys\system.pas” + 3)
[0046A8AC] frmMain.TForm1.DoSomethingStupid (Line 77, “frmMain.pas” + 1)
[0046A88A] frmMain.TForm1.DoSomething (Line 72, “frmMain.pas” + 1)
[0046A877] frmMain.TForm1.btnTestClick (Line 67, “frmMain.pas” + 1)

Bij een stack trace is de bovenste altijd de laatste en nieuwste item, je moet dus van
onderen naar boven lezen. Dan zien we dat we op “btnTest” geklikt hebben (”btnTestClick”), waardoor
procedure “DoSomething” uitgevoerd werd. Deze voerde “DoSomethingStupid”, die de “ClassName” opvroeg
van een niet-bestaand object. Dit had een “acces violation” tot gevolg.

Dit geeft een duidelijk beeld wanneer en hoe de fout tot stand kwam. Zelf dump ik als deze informatie
in een log bestand en niet op het scherm (gebruikers schrikken daar zo van :-) ), zodat ik eenvoudig
alle stack traces van de fouten kan bekijken.

Dit is nog maar een enkele mogelijkheid van de jclDebug unit: het bevat nog meer handige debug functies,
maar die kan ik hier niet allemaal behandelen. Speel er eens mee, zou ik zeggen.