Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Automatische Problem-Erkennung (https://www.delphipraxis.net/213952-automatische-problem-erkennung.html)

Incocnito 26. Okt 2023 08:52

Automatische Problem-Erkennung
 
Moin zusammen.

Wir hatten heute einen Fehler gefunden, der nicht sofort ersichtlich ist.

Delphi-Quellcode:
...
type TWhatever = class(TForm)
  public
    Procedure DoSomething(const sVal : String);
    Procedure DoMore();
  private
    FMyVal : String;
  end;
...
procedure TWhatever.DoMore();
begin
  DoSomeThing(FMyVal);
end;

procedure TWhatever.DoSomething(const sVal : String);
begin
  FMyVal := '';
  if (sVal = 'bla') then Close();
end;
...
Hier wird in DoMore die Variable FMyVal übergeben und da sVal in DoSomething als const deklariert ist,
erstellt Delphi hier keine Kopie, sondern arbeitet auf dem Original.
Jetzt nehme ich aber in der ersten Zeile ihm das Original unter dem Arsch weg.
Das führt zu Speicherfehlern und dann random irgendwo zu Zugriffsverletzungen.

Lösung hier: "const" weg nehmen, so dass Delphi für sVal eine Kopie anlegt.

Frage: Gibt es eine (automatisierbare) Möglichkeit solche Konstellationen zu erkennen?
Ich würde gerne ausschließen, dass wir solche Fehler irgendwo vertseckt haben und
ich weiß nicht, wie ich sowas suchen kann. Selbst wenn das Problem nicht mehr erkannt werden
kann, wenn Funktionen andere Funktionen aufrufen, die dann den Ursprungswert modifizieren.
Dieser Fehler beunruhigt mich nun doch schon etwas. Und da jetzt "überall" etwas zu bauen, dass
beim Funktionsaufruf den Speicher schützt und entsprechende Exceptions wirft, wenn dieser doch
bearbeitet wird, würde zudem ewig dauern und Ressourcen und Performance fressen.

Vielleicht hat da ja jemand eine Idee zu.

Liebe Grüße
Incocnito

Sherlock 26. Okt 2023 09:05

AW: Automatische Problem-Erkennung
 
Kurz gesagt ist der Fehler, innerhalb der Klasse ein Feld als Parameter zu übergeben. Vermutlich könnte man das prüfen lassen, aber es könnte manchmal auch Absicht sein.

jaenicke 26. Okt 2023 10:58

AW: Automatische Problem-Erkennung
 
Noch schöner:
Delphi-Quellcode:
begin
  FMyVal := '42';
  DoMore;
end;

procedure TWhatever.DoMore();
begin
  DoSomeThing(FMyVal);
end;

procedure TWhatever.DoSomething(const sVal : String);
begin
  FMyVal := '';
  FMyVal := '43';
  if (sVal = '43') then ShowMessage('Aua');
end;
Da der Speicherbereich erneut verwendet wird, ist in sVal der neue Wert direkt drin.

Das ist wirklich eine schöne Falle, die man nicht sofort sieht.

Sinspin 26. Okt 2023 12:53

AW: Automatische Problem-Erkennung
 
Mir ist gerade ein bisschen Schwarz vor Augen geworden. Ein Glück dass ich kein Freund von Const-Parametern bin.
Ich werde trotzdem mal nach Const in Parametern suchen und mir alle Übergaben ansehen.

Incocnito 26. Okt 2023 13:30

AW: Automatische Problem-Erkennung
 
Zitat:

Zitat von jaenicke (Beitrag 1528629)
...Da der Speicherbereich erneut verwendet wird, ist in sVal der neue Wert direkt drin.
Das ist wirklich eine schöne Falle, die man nicht sofort sieht.

Tatsächlich stand da nach dem Zuweisen des Leerstring zu der privaten Variable (also FMyVal in meinem Beispiel) auf einmal
"#10#13" random in der lokalen Variable (sVal) bei einem Testlauf. Also kann da bei solch einer Konstellation auch
Speichermüll drin stehen. Es muss nicht sein, dass er den aktuellen Wert der Hauptvariable übernimmt.
Das war bei dir vielleicht nur Zufall (oder es liegt daran, dass du eine andere Delphi-Version hast).

Liebe Grüße
Incocnito

himitsu 26. Okt 2023 13:38

AW: Automatische Problem-Erkennung
 
ByReference-Parameter.

Ich lasse zu oft gern das CONST weg, weil es hässlich aussieht.
Ich hab solche Probleme also fast nie. :lol:

Ohne CONST gibt es im String ein RefCount+1, womit es in der Funktion zwei Variablen sind, beim := wird es ordentlich aufgelöst und somit wird nichts überschrieben.

Ein
Delphi-Quellcode:
const Val: Integer
hätte dieses Problem nicht, da es in den Speicher eines Registers passt und somit keine Referenz genutzt wird (obwohl man denkt das befohlen zu haben),
aber darum hat Emba vor 'nem Weilchen das
Delphi-Quellcode:
const [Ref] Val: Integer
erfunden, damit man auch dort dieses Fehlerchen bewundern darf. :party:

Incocnito 26. Okt 2023 13:47

AW: Automatische Problem-Erkennung
 
Zitat:

Zitat von himitsu (Beitrag 1528636)
... Ein
Delphi-Quellcode:
const Val: Integer
hätte dieses Problem nicht, da es in den Speicher eines Registers passt und somit keine Referenz genutzt wird (obwohl man denkt das befohlen zu haben),
aber darum hat Emba vor 'nem Weilchen das
Delphi-Quellcode:
const [Ref] Val: Integer
erfunden, damit man auch dort dieses Fehlerchen bewundern darf. :party:

Wieder einer dieser Momente, wo nur Entwickler den Witz verstehen. 😉🤣

Blup 27. Okt 2023 08:18

AW: Automatische Problem-Erkennung
 
Eine ungünstige Klassenstruktur könnte solche Fehler begünstigen.
Wenn man sich des Problems bewusst ist, lässt sich das auch vermeiden:
Delphi-Quellcode:
procedure TWhatever.DoMore();
begin
  DoSomeThing(Copy(FMyVal));
end;

skybibo 28. Okt 2023 19:08

AW: Automatische Problem-Erkennung
 
Zitat:

Zitat von Incocnito (Beitrag 1528634)
Tatsächlich stand da nach dem Zuweisen des Leerstring zu der privaten Variable (also FMyVal in meinem Beispiel) auf einmal
"#10#13" random in der lokalen Variable (sVal) bei einem Testlauf. Also kann da bei solch einer Konstellation auch
Speichermüll drin stehen. Es muss nicht sein, dass er den aktuellen Wert der Hauptvariable übernimmt.
Das war bei dir vielleicht nur Zufall (oder es liegt daran, dass du eine andere Delphi-Version hast).

Liebe Grüße
Incocnito

Welche Delphi Version verwendest du?

Bei Delphi 7 kann ich das so produzieren, aber bei Delphi 11 kann ich hier kein Problem oder Absturz reproduzieren.

jaenicke 28. Okt 2023 22:31

AW: Automatische Problem-Erkennung
 
Nach Delphi 7 wurde der neue Speichermanager eingeführt. Ich hatte mit Delphi 11 getestet.

Abstürzen tut da nichts, aber der geänderte Speicherinhalt ist direkt zu sehen.

Incocnito 30. Okt 2023 07:22

AW: Automatische Problem-Erkennung
 
Zitat:

Zitat von skybibo (Beitrag 1528698)
...
Welche Delphi Version verwendest du?
Bei Delphi 7 kann ich das so produzieren, aber bei Delphi 11 kann ich hier kein Problem oder Absturz reproduzieren.

Ich nutze Delphi 10.4

skybibo 30. Okt 2023 19:34

AW: Automatische Problem-Erkennung
 
Ich habe mal eine komplette Unit erstellt. In dieser Variante bekomme ich auch mit Delphi 11 einen EInvalidPointer Fehler. Was genau Delphi in der Procedure "DoSomething" macht ist für mich noch nicht so richtig nachvollziehbar. Ob diese Kombination in einem normalen Programm so vorkommen kann weiß ich nicht. Bei mir hatte ich dies noch nicht. Ich sehe hier auch bei Delphi selbst keinen Fehler. Wenn ich Unsinn programmiere, dann bekomme ich auch ein Unsinniges Ergebnis.

Delphi-Quellcode:
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    FMyVal: String;
  public
    procedure DoMore;
    procedure DoSomething(const sVal:String);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.DoMore;
  var
    Val: String;
begin
  DoSomething(FMyVal);
end;

procedure TForm1.DoSomething(const sVal: String);
  var
    Val: String;
begin
  FMyVal := '';    //ohne diese Zeile gibt es keinen Fehler
  Val := sVal;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FMyVal := 'XXX'; //ohne diese Zeile gibt es keinen Fehler
  DoMore;
end;

end.

Incocnito 2. Nov 2023 07:33

AW: Automatische Problem-Erkennung
 
Es ist halt in unserem Programm genau so vorgekommen und ich habe das für hier nur auf die wesentlichen Eckpunkte zusammen gekürzt.
Das der Fehler nur kommt, wenn man ihn zuvor mit einem Wert beschreibt ging aus meinem Beispiel nicht hervor, da aber der Parameter ein
const ist, war es eigentlich auch klar, dass er vorher "initialisiert" sein muss. Ist er in unserem Programm übrigens.
Ja, Delphi macht hier genau das, was es soll, es optimiert den Aufruf indem es die Referenz übergibt und nicht den Speicher kopiert.
Ja, wenn ich das const weg lasse (was wir dann auch getan haben) kopiert er den Wert wieder und es gibt keinen Fehler mehr an der Stelle.
Nein, ich hätte trotzdem gerne dass der Compiler sich beschwert, wenn eine Variable übergeben wird, per const-Ref-Dings, und diese in
der Funktion selbst beschrieben wird. Meinetwegen kann man diese Prüfung auch per Schalter aktivieren, wenn diese Zusatzprüfung zu lange dauert.
Ja, ich weiß, dass es etwas (aber meines Erachtens nach nicht so viel) aufwendiger ist, wie zu prüfen ob der Const-Paramter innerhalb der
Methode beschrieben wird. Naja, wird wohl nicht kommen, also Augen auf bei const-Parametern und nochmal 5 Minuten länger selbst nachdenken.

shebang 2. Nov 2023 14:07

AW: Automatische Problem-Erkennung
 
Ich kann deinen Punkt mit dem Compiler verstehen. Allerdings verstehe ich nicht, warum man der Funktion einer Klasse einen (privaten) Parameter dieser Klasse als Argument übergibt? Auf den Parameter kann doch sowieso innerhalb der Funktion zugegriffen werden?

Incocnito 2. Nov 2023 16:06

AW: Automatische Problem-Erkennung
 
Ganz einfach, weil die Funktion teils von woanders mit anderen Parametern aufgerufen wird nur in diesem einen Aufruf wird genau der aktuelle Wert gebraucht. ;-)
Ich habe das Gefühl ihr denkt zu kompliziert. 😅

jaenicke 2. Nov 2023 16:23

AW: Automatische Problem-Erkennung
 
Die Kapselung ohne äußere Werte in Funktionen ist auch in Klassen sehr sinnvoll, weil der Ablauf viel besser nachvollziehbar ist. Ich lagere solche Funktionen aber auch gerne aus der Klasse aus in eigene Toolklassen, aber das macht natürlich auch nicht immer Sinn.

Die Problematik hatte ich auch schon einmal im Debugger suchen müssen bei fremdem Code.


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