Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Mögliche Exceptions eines Objektes herausfinden (https://www.delphipraxis.net/204036-moegliche-exceptions-eines-objektes-herausfinden.html)

Caps 17. Apr 2020 09:52

Delphi-Version: 5

Mögliche Exceptions eines Objektes herausfinden
 
Hallo,

vermutlich stehe ich mächtig auf dem Schlauch, ich weiß auch nicht wirklich wie ich danach suchen soll (obgleich ich es getan habe :freak:).
Es geht um etwas sehr einfaches aber anscheinend auch mächtig schwieriges:

Nehmen wir an ich rufe eine Methode auf:

Code:
try
  myKompo.DoSomething;
except [...]
Und nehmen wir weiter folgendes an:
- die Klasse von myKompo hat ca. 40 Millionen Quellcodezeilen
- die Methode DoSomething kann 17.000 verschiedene Exceptions werfen
- es existiert eine eher dürftige Dokumentation der Komponente

Wie (zur Hölle! ^^) finde ich raus, welche Exceptions möglich sind?
Ich kann ja nicht 40 Millionen Quellcodezeilen manuell durchsuchen.
Ich kann auch nicht 17.000 fehlerhafte Programmzustände manuell herstellen, um die Exceptions zu provozieren, von denen ich nicht mal weiß dass es sie gibt.

lg Caps

ps Um mein Problem in der Realität zu verankern, zitiere ich einfach mal die Onlinehilfe von Delphi:

Zitat:

Code:
try
    ...
except
     on EZeroDivide do HandleZeroDivide;
     on EOverflow do HandleOverflow;
     on EMathError do HandleMathError;
else
     HandleAllOthers;
end;

Tja, wenn ich nur wüsste, dass es EZeroDivide, EOverflow und EMathError überhaupt gibt...(!)
:
:
:

Uwe Raabe 17. Apr 2020 09:58

AW: Mögliche Exceptions eines Objektes herausfinden
 
Zitat:

Zitat von Caps (Beitrag 1462320)
- die Methode DoSomething kann 17.000 verschiedene Exceptions werfen

Diese 17.000 verschiedenen Exceptions müssen ja irgendwo deklariert sein damit sie geworfen werden können. Das wäre doch schon mal der erste Ansatz.

Man kann auch im Source nach
Delphi-Quellcode:
raise
suchen.

himitsu 17. Apr 2020 10:21

AW: Mögliche Exceptions eines Objektes herausfinden
 
Zitat:

Ich kann auch nicht 17.000 fehlerhafte Programmzustände manuell herstellen, um die Exceptions zu provozieren
Stichwort: Unit-Testing

Man kann auch via RTTI alles Exception-Klassen suchen, die im Programm einkompiliert wurden. (liefert nur die klassen, aber natürlich nicht wo sie ausgelöst werden)
Gut dokumentierte Funktionen/Klassen würden in der Doku/Attributen/PasDocKommentaren ausgelistet haben, was plausible Rückgabewerte wären und dazu gehören auch die Namen der Exceptions.

Im EXCEPT muß man auch nur die Klassen aufnehmen, die man anders behandeln will,
da wird nicht einfach irgendwas/alles reingemacht. Und schon garnicht macht man da standardmäßig blinden Code rein, der Exceptions wahllos still und heimlich abfängt. (der also "garnichts" macht)

Sherlock 17. Apr 2020 10:30

AW: Mögliche Exceptions eines Objektes herausfinden
 
Möchtest Du tatsächlich Code für die diversen (17000) verschiedenen Exceptiontypen schreiben? Also 17000 unterschiedliche Reaktionen auf 17000 unterschiedliche Ausnahmen (verdammt viele Ausnahmen, übrigens...bei mir gälte zu diesem Zeitpunkt dann der "Normalzustand" als Ausnahme, aber sei's drum).

Bei mir gilt jede Exception als Dealbreaker, die Exception wird geloggt und der betreffende Vorgang muss erneut gestartet werden. Wenn es bei dem Problem bleibt, beendet sich das Programm.

Sherlock

Caps 17. Apr 2020 10:34

AW: Mögliche Exceptions eines Objektes herausfinden
 
Zitat:

Zitat von Sherlock (Beitrag 1462326)
Möchtest Du tatsächlich Code für die diversen (17000) verschiedenen Exceptiontypen schreiben? Also 17000 unterschiedliche Reaktionen auf 17000 unterschiedliche Ausnahmen (verdammt viele Ausnahmen, übrigens...bei mir gälte zu diesem Zeitpunkt dann der "Normalzustand" als Ausnahme, aber sei's drum).

Bei mir gilt jede Exception als Dealbreaker, die Exception wird geloggt und der betreffende Vorgang muss erneut gestartet werden. Wenn es bei dem Problem bleibt, beendet sich das Programm.

Sherlock

Naja, angenommen eine nicht vorhandene Netzwerkverbindung löst beim Connect eine Exception aus. Dann probiere ich halt solange, bis die Verbindung wieder da ist, so wie das jede Chat-App macht. Das wäre für mich kein Dealbreaker. Und ein "Connection-Closed-Gracefully" (Indy) ist ja auch so ne Sache wo eigentlich nix los... ^^

lg Caps

Caps 17. Apr 2020 10:36

AW: Mögliche Exceptions eines Objektes herausfinden
 
Hi, sowas dachte ich mir schon :?

Zitat "Man kann auch im Source nach raise suchen."
Deswegen die beispielhaften 40 Mio Zeilen. Ich möchte eben nicht danach suchen müssen.

Zitat "Gut dokumentierte Funktionen/Klassen [..]"
Deswegen die Anmerkung, dass die Klasse dürftig dokumentier ist. Das ist sie leider.

Danke trotzdem, mal sehen, wie ich rangehe.
Oder ich lasse die Kiste einfach beim Kunden hochgehen ^^…

lg Caps

himitsu 17. Apr 2020 10:57

AW: Mögliche Exceptions eines Objektes herausfinden
 
Zitat:

angenommen eine nicht vorhandene Netzwerkverbindung löst beim Connect eine Exception aus. Dann probiere ich halt solange, bis die Verbindung wieder da ist
Sowas kann man aber auch in der/den Connections erledigen.
Indy und die meisten DB-Connections sollten ein passendes Event oder Optionen haben, wo man sagen kann, wie ereagiert werden soll.
> AbbruchMitException (Standard), AbbruchOnException (tu so als sei nix), VersuchsSofortNochmal, WarteBisslUndVersuchsNochmal (x Wiederholungen und dann Erstes) oder man ändert im Event etwas und versucht es dann nochmal (z.B. Umschalten auf einen alternativen Port/Host)

Bei uns hier ist nahezu jede im Programm verwendete Komponente erstmal abgeleitet.
So kann man Bugfixes, Erweiterungen und Änderungen am Verhalten problemlos zentral anpassen, ohne zigtausend Formulare überarbeiten zu müssen.

Delphi.Narium 17. Apr 2020 13:43

AW: Mögliche Exceptions eines Objektes herausfinden
 
Bei meinem Delphi gibt es im BIN-Verzeichnis 'ne Grep.exe. Wenn ich die so aufrufe
Code:
c:\Delphi7\Bin>grep -dni except "e:\delphi\*.pas > grep.except.log"
gräbt die sich durch alle .pas-Dateien in dem Verzeichnis und raus kommt eine Datei in der alle Dateiname, die Zeilennummer und die Zeile stehen, in der die Zeichenfolge Except zu finden ist.

Das kann für eine Datei dann z. B. so aussehen:
Code:
File e:\delphi\PNG\pngimage.pas:
250         {Custom exception handler}
251         Exception = class(TObject)
254         ExceptClass = class of Exception;
259       EPNGOutMemory = class(Exception);
260       EPngError = class(Exception);
261       EPngUnexpectedEnd = class(Exception);
262       EPngInvalidCRC = class(Exception);
263       EPngInvalidIHDR = class(Exception);
264       EPNGMissingMultipleIDAT = class(Exception);
265       EPNGZLIBError = class(Exception);
266       EPNGInvalidPalette = class(Exception);
267       EPNGInvalidFileHeader = class(Exception);
268       EPNGIHDRNotFirst = class(Exception);
269       EPNGNotExists = class(Exception);
270       EPNGSizeExceeds = class(Exception);
271       EPNGMissingPalette = class(Exception);
272       EPNGUnknownCriticalChunk = class(Exception);
273       EPNGUnknownCompression = class(Exception);
274       EPNGUnknownInterlace = class(Exception);
275       EPNGNoImageData = class(Exception);
276       EPNGCouldNotLoadResource = class(Exception);
277       EPNGCannotChangeTransparent = class(Exception);
278       EPNGHeaderNotPresent = class(Exception);
279       EPNGInvalidNewSize = class(Exception);
280       EPNGInvalidSpec = class(Exception);
539         procedure RaiseError(ExceptionClass: ExceptClass; Text: String);
1163      {Exception implementation}
1164      constructor Exception.Create(Msg: String);
4565    procedure TPngObject.RaiseError(ExceptionClass: ExceptClass; Text: String);
4567      raise ExceptionClass.Create(Text);
5030        except
5143      except RaiseError(EPNGCouldNotLoadResource, EPNGCouldNotLoadResourceText);
5180                  else raise Exception.Create('');
5182              else raise Exception.Create('');
oder
Code:
c:\Delphi7\Bin>grep -dni class\(exception\) "e:\delphi\*.pas" >grep.class_Exception.log
ergibt
Code:
File e:\delphi\PNG\pngimage.pas:
259       EPNGOutMemory = class(Exception);
260       EPngError = class(Exception);
261       EPngUnexpectedEnd = class(Exception);
262       EPngInvalidCRC = class(Exception);
263       EPngInvalidIHDR = class(Exception);
264       EPNGMissingMultipleIDAT = class(Exception);
265       EPNGZLIBError = class(Exception);
266       EPNGInvalidPalette = class(Exception);
267       EPNGInvalidFileHeader = class(Exception);
268       EPNGIHDRNotFirst = class(Exception);
269       EPNGNotExists = class(Exception);
270       EPNGSizeExceeds = class(Exception);
271       EPNGMissingPalette = class(Exception);
272       EPNGUnknownCriticalChunk = class(Exception);
273       EPNGUnknownCompression = class(Exception);
274       EPNGUnknownInterlace = class(Exception);
275       EPNGNoImageData = class(Exception);
276       EPNGCouldNotLoadResource = class(Exception);
277       EPNGCannotChangeTransparent = class(Exception);
278       EPNGHeaderNotPresent = class(Exception);
279       EPNGInvalidNewSize = class(Exception);
280       EPNGInvalidSpec = class(Exception);
Und für den Sack Raise aus China ;-)
Code:
c:\Delphi7\Bin>grep -dni raise "e:\delphi\*.pas" >grep.raise.log
bekommen wir sowas:
Code:
File e:\delphi\PNG\pngimage.pas:
539         procedure RaiseError(ExceptionClass: ExceptClass; Text: String);
1625        fOwner.RaiseError(EPngError, EPNGCannotAddChunkText)
1630        fOwner.RaiseError(EPngError, EPNGCannotAddChunkText)
1634        fOwner.RaiseError(EPngError, EPNGCannotAddInvalidImageText)
1826        if ResID = 0 then raise EPNGError.Create('');
2014          Owner.RaiseError(EPngInvalidCRC, EPngInvalidCRCText);
2242        Owner.RaiseError(EPNGError, EPNGCannotAssignChunkText);
2276        Owner.RaiseError(EPNGInvalidIHDR, EPNGInvalidIHDRText);
2289        Owner.RaiseError(EPNGSizeExceeds, EPNGSizeExceedsText);
2296        Owner.RaiseError(EPNGUnknownCompression, EPNGUnknownCompressionText);
2303        Owner.RaiseError(EPNGUnknownInterlace, EPNGUnknownInterlaceText);
2593      if Size > 256 then Owner.RaiseError(EPNGInvalidPalette,
2680                Owner.RaiseError(EPNGInvalidCRC, EPNGInvalidCRCText);
2692              Owner.RaiseError(EPNGMissingMultipleIDAT, EPNGMissingMultipleIDATText);
2736            Owner.RaiseError(EPNGZLIBError,
3501          Owner.RaiseError(EPngInvalidCRC, EPngInvalidCRCText);
4230      {Test if item is valid, if not raise error}
4232        Owner.RaiseError(EPNGError, EPNGUnknownPalEntryText)
4258        {Raise error}
4260        Owner.RaiseError(EPNGInvalidPalette, EPNGInvalidPaletteText);
4321        Owner.RaiseError(EPNGError, EPNGCannotAssignChunkText);
4333        Owner.RaiseError(EPNGError, EPNGCannotAssignChunkText);
4446        RaiseError(EPNGInvalidSpec, EInvalidSpec);
4564    {Raises an error}
4565    procedure TPngObject.RaiseError(ExceptionClass: ExceptClass; Text: String);
4567      raise ExceptionClass.Create(Text);
4621          {In case it does not exists, raise error}
4622          RaiseError(EPNGNotExists, EPNGNotExistsText);
4671        RaiseError(EPNGHeaderNotPresent, EPNGHeaderNotPresentText);
4766      if (BufferDC = 0) then RaiseError(EPNGOutMemory, EPNGOutMemoryText);
4774        RaiseError(EPNGOutMemory, EPNGOutMemoryText);
4966        RaiseError(EPNGInvalidFileHeader, EPNGInvalidFileHeaderText);
4988          RaiseError(EPNGUnexpectedEnd, EPNGUnexpectedEndText);
5000          RaiseError(EPNGIHDRNotFirst, EPNGIHDRNotFirstText);
5023            RaiseError(EPNGUnknownCriticalChunk, EPNGUnknownCriticalChunkText);
5032          raise;
5043        RaiseError(EPNGNoImageData, EPNGNoImageDataText);
5143      except RaiseError(EPNGCouldNotLoadResource, EPNGCouldNotLoadResourceText);
5180                  else raise Exception.Create('');
5182              else raise Exception.Create('');
5483        COLOR_RGBALPHA, COLOR_GRAYSCALEALPHA: Self.RaiseError(
5696        RaiseError(EPNGInvalidNewSize, EInvalidNewSize)
Sollte für den Anfang und einen ersten Überblick sicherlich erstmal reichen ;-)

Ok: Bei den von Dir genannten Mengen an Zeilen und Exceptions wird es wohl erstmal sehr viel als Ergebnis geben, aber sicherlich besser als alles manuell durchsehen zu müssen.

Wird das zu Suchende in mehreren Dateien gefunden, so enthält die Ausgabe halt mehrere derartige Blöcke in der Reihenfolge des Findens der Dateien.

Hilfe zum Grep: http://docwiki.embarcadero.com/RADSt...search_utility

himitsu 17. Apr 2020 15:34

AW: Mögliche Exceptions eines Objektes herausfinden
 
Zitat:

Zitat von grep except
259 EPNGOutMemory = class(Exception);
260 EPngError = class(Exception);
261 EPngUnexpectedEnd = class(Exception);
262 EPngInvalidCRC = class(Exception);
263 EPngInvalidIHDR = class(Exception);
...

Und was ist mit vererrbten Exceptions, die nicht direkt von Exception abgeleitet wurden?

Wenn man schon nach "raise" (ganzes Wort) sucht, dann sollte man RaiseOuterException nicht vergessen.
Nach "raise" als Teilwort zu suchen kann massig Anderes Zeugs liefern,
wobei RaiseError hier wieder ein Sonderfall ist. (man findet darin das "raise" und darf dann auch noch nach der Funktion suchen)

Delphi.Narium 17. Apr 2020 15:42

AW: Mögliche Exceptions eines Objektes herausfinden
 
Man, das soll doch keine fertige Lösung sein, sondern nur ein Hinweis darauf, dass bei Delphi ein Grep dabei ist, mal drei Lösungsansätze für Teilmengen dabei und dann bitte gefälligst selbst nach der für den (nicht näher beschriebenen) Einzelfall suchen.

Grep kann reguläre Ausdrücke. Jetzt bist Du dran, weist ja zuweilen darauf hin, dass man damit viel machen kann. Sei so gut und liefere den passenden regulären Ausdruck, der alles, was Du jetzt meinst mit abfrühstücken zu müssen, liefert.

ff ;-)

Jost Riedel 17. Apr 2020 17:35

AW: Mögliche Exceptions eines Objektes herausfinden
 
Und was ist mit Exceptions, die nicht von System.SysUtils.Exception abgeleited sind?

Sowas ist leider (immer) noch möglich.

himitsu 17. Apr 2020 17:41

AW: Mögliche Exceptions eines Objektes herausfinden
 
Theoretisch ist es möglich, aber praktisch basiert die komplette Fehlerbehandlung darauf, somit sollte man sowas nicht machen.

Fehlerdialoge usw. zeigen sowas nicht an
und auch das
Delphi-Quellcode:
except on E: Exception do
ignoriert sowas. (wirkt dann ähnlich wie ein Abort/EAbort, außer dass es am ersten EXCEPT mit einem ON-DO abbricht)



Das "leider" war Absicht. (auch wenn es seit Einführung vor Jahrzehnten nie "richtig" benutzt wurde)



Ja klar kann RegEx viel, aber hier fängst dann an das RegEx rekursiv zu benutzen ... das ist dann bissl zuviel des Guten.
Dagegen ist der Weg über die RTTI ein Klacks. (nach dem Kompilieren)

Delphi.Narium 17. Apr 2020 17:50

AW: Mögliche Exceptions eines Objektes herausfinden
 
Wie wäre es denn, wenn wir den Threadersteller erstmal mit den bisher gegebenen Vorschlägen arbeiten lassen?

Das könnte schonmal 'ne nicht unerhebliche Menge an Ergebnissen liefern.

Und wenn dann noch was unklares überbleibt, kümmern wir uns um die (mehr oder weniger) exotischen Ausnahmen bei der Erstellung von Ausnahmen ;-)

Oder einfach mal nicht drauf hinweisen, dass es noch Sonderfälle gibt, sondern einfach mal direkt 'nen umsetzbaren Vorschlag, wie man die genannten Sonderfälle sinnvoll und sicher identifizieren kann ;-)

Zitat:

Zitat von Jost Riedel (Beitrag 1462373)
Und was ist mit Exceptions, die nicht von System.SysUtils.Exception abgeleited sind?

Sowas ist leider (immer) noch möglich.

Frei nach dem Motto: Und wie kann ich sie (weitgehend) sicher finden?

Zitat:

Zitat von himitsu (Beitrag 1462358)
Und was ist mit vererrbten Exceptions, die nicht direkt von Exception abgeleitet wurden?

Dito ;-)

Erster Vorschlag: Mal alle Exceptions der Sorte
Delphi-Quellcode:
EIrgendeinName = class(Exception);
suchen.
Mit dem Ergebnis suchen, ob es irgendwo ein
Delphi-Quellcode:
EIrgendeinAndererName = class(EIrgendeinName);
gibt.
Mit dem Ergebnis suchen, ob es irgendwo ein
Delphi-Quellcode:
EIrgendeinNochAndererName = class(EIrgendeinAndererName);
gibt.

Upps: Rekursionsgefahr.

Vielleicht doch erstmal nur mit dem Einfachen anfangen?

Jost Riedel 17. Apr 2020 22:12

AW: Mögliche Exceptions eines Objektes herausfinden
 
Zitat:

Zitat von himitsu (Beitrag 1462375)
Theoretisch ist es möglich, aber praktisch basiert die komplette Fehlerbehandlung darauf, somit sollte man sowas nicht machen.

Fehlerdialoge usw. zeigen sowas nicht an
und auch das
Delphi-Quellcode:
except on E: Exception do
ignoriert sowas. (wirkt dann ähnlich wie ein Abort/EAbort, außer dass es am ersten EXCEPT mit einem ON-DO abbricht)

Du machst sowas nicht, und selbst ich mache das schon seit über 20 Jahren nicht mehr. Aber das ist hir nicht die Fragestellung: Es geht darum, aus einem großvolumigen, schlecht dokumentierten Sourcecode etwas sinnvolles zu extrahieren. Und da kann alles drin sein, was der Compiler akzeptiert.

himitsu 17. Apr 2020 23:21

AW: Mögliche Exceptions eines Objektes herausfinden
 
Klar, es gibt Codes, wo Exceptions zur "normalen" Flusssteuerung benutzt werden und wo z.B. Daten-Objekte via Raise als Result nach außen durchgereicht werden,
aber sowas würde ich ignorieren, da man solch ein Verhalten "vergessen" kann.

dummzeuch 18. Apr 2020 09:45

AW: Mögliche Exceptions eines Objektes herausfinden
 
Zitat:

Zitat von Delphi.Narium (Beitrag 1462349)
Bei meinem Delphi gibt es im BIN-Verzeichnis 'ne Grep.exe. Wenn ich die so aufrufe
Code:
c:\Delphi7\Bin>grep -dni except "e:\delphi\*.pas > grep.except.log"
gräbt die sich durch alle .pas-Dateien in dem Verzeichnis und raus kommt eine Datei in der alle Dateiname, die Zeilennummer und die Zeile stehen, in der die Zeichenfolge Except zu finden ist.

Und wenn man schon dabei ist, kann man mal schauen, an wie vielen Stellen das raise oder das Create fehlt:
Delphi-Quellcode:
if SomeCondition then
  raise Exception('some error message');
Grep nach "raise Exception\(" und "raise e[a-z]*\(".

und
Delphi-Quellcode:
if SomeCondition then
  Exception.Create('some error message');
Grep nach " e[a-z]*\.Create\("

(Davon gab es eine sogar in er Delphi 10.2 RTL.)

Ich habe damals darüber geblogt:

When an exception is nil in the exception handler

himitsu 18. Apr 2020 11:17

AW: Mögliche Exceptions eines Objektes herausfinden
 
PS: Vergesst auch nicht die ErrorCodes. :angle:
Delphi-Quellcode:
Error(reOutOfMemory);


Und für gewisse "Fehler" gibt es auch schon von Haus aus Funktionen.
Delphi-Quellcode:
Abort; // ihr dürft aber auch gern raise EAbort.Create('irgndwas, wird eh ignoriert'); machen

OutOfMemoryError; // und wenn der Speicher zu wenig ist, dann wurde dafür das Objekt schon vorher erstellt

Caps 20. Apr 2020 09:57

AW: Mögliche Exceptions eines Objektes herausfinden
 
Danke an alle für Eure Ideen!
Es sind nicht wirklich 17.000 Exceptions, aber ich sehe meine Frage dahingehend beantwortet, dass es keinen einfachen Weg gibt, sich die möglichen Exceptions in einer Art "Exception-Übersicht" anzeigen zu lassen. Es gibt ja auch kein "throws" o.ä. in Delphi. Naja, passt. Ich werde eben danach suchen.

lg Caps

Uwe Raabe 20. Apr 2020 11:36

AW: Mögliche Exceptions eines Objektes herausfinden
 
Zitat:

Zitat von himitsu (Beitrag 1462390)
Klar, es gibt Codes, wo Exceptions zur "normalen" Flusssteuerung benutzt werden und wo z.B. Daten-Objekte via Raise als Result nach außen durchgereicht werden,
aber sowas würde ich ignorieren, da man solch ein Verhalten "vergessen" kann.

Sowas musst du auch ignorieren, da du sonst diese Flusssteuerung außer Kraft setzt.

himitsu 20. Apr 2020 16:18

AW: Mögliche Exceptions eines Objektes herausfinden
 
Jo, sowas vergessen wir einfach. Wer sowas macht, hat es nicht anders verdient. :angle2:


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:05 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