Frage

Ich bin Mitglied in einem Team, das für eine größere Anwendung Delphi 2007 und wir vermuten, dass Korruption Haufen, weil es manchmal seltsame Fehler sind, die keine andere Erklärung. Ich glaube, dass die Rangechecking Option für den Compiler nur für Arrays ist. Ich möchte ein Tool, das eine Ausnahme geben oder melden Sie sich, wenn ein Schreibvorgang auf einer Speicheradresse ist, die nicht von der Anwendung zugeordnet ist.

Viele Grüße

Bearbeiten : Der Fehler ist vom Typ:

Fehler: Zugriffsverletzung bei Adresse 00404E78 in Modul 'BoatLogisticsAMCAttracsServer.exe'. Lesen der Adresse FFFFFFDD

EDIT2 : Danke für alle Vorschläge. Leider denke ich, dass die Lösung tiefer als das. Wir verwenden eine gepatchte Version von Bold für Delphi als wir die Quelle besitzen. Wahrscheinlich gibt es einige Fehler in dem Bold framwork eingeführt. Ja, wir haben ein Protokoll mit Callstacks, die von JCL und auch Trace-Meldungen behandelt werden. So ein Aufrufliste mit Ausnahme kann wie folgt sperren:

20091210 16:02:29 (2356) [EXCEPTION] Raised EBold: Failed to derive ServerSession.mayDropSession: Boolean
OCL expression: not active and not idle and timeout and (ApplicationKernel.allinstances->first.CurrentSession <> self)
Error: Access violation at address 00404E78 in module 'BoatLogisticsAMCAttracsServer.exe'. Read of address FFFFFFDD. At Location BoldSystem.TBoldMember.CalculateDerivedMemberWithExpression (BoldSystem.pas:4016)

Inner Exception Raised EBold: Failed to derive ServerSession.mayDropSession: Boolean
OCL expression: not active and not idle and timeout and (ApplicationKernel.allinstances->first.CurrentSession <> self)
Error: Access violation at address 00404E78 in module 'BoatLogisticsAMCAttracsServer.exe'. Read of address FFFFFFDD. At Location BoldSystem.TBoldMember.CalculateDerivedMemberWithExpression (BoldSystem.pas:4016)
Inner Exception Call Stack:
 [00] System.TObject.InheritsFrom (sys\system.pas:9237)

Call Stack:
 [00] BoldSystem.TBoldMember.CalculateDerivedMemberWithExpression (BoldSystem.pas:4016)
 [01] BoldSystem.TBoldMember.DeriveMember (BoldSystem.pas:3846)
 [02] BoldSystem.TBoldMemberDeriver.DoDeriveAndSubscribe (BoldSystem.pas:7491)
 [03] BoldDeriver.TBoldAbstractDeriver.DeriveAndSubscribe (BoldDeriver.pas:180)
 [04] BoldDeriver.TBoldAbstractDeriver.SetDeriverState (BoldDeriver.pas:262)
 [05] BoldDeriver.TBoldAbstractDeriver.Derive (BoldDeriver.pas:117)
 [06] BoldDeriver.TBoldAbstractDeriver.EnsureCurrent (BoldDeriver.pas:196)
 [07] BoldSystem.TBoldMember.EnsureContentsCurrent (BoldSystem.pas:4245)
 [08] BoldSystem.TBoldAttribute.EnsureNotNull (BoldSystem.pas:4813)
 [09] BoldAttributes.TBABoolean.GetAsBoolean (BoldAttributes.pas:3069)
 [10] BusinessClasses.TLogonSession._GetMayDropSession (code\BusinessClasses.pas:31854)
 [11] DMAttracsTimers.TAttracsTimerDataModule.RemoveDanglingLogonSessions (code\DMAttracsTimers.pas:237)
 [12] DMAttracsTimers.TAttracsTimerDataModule.UpdateServerTimeOnTimerTrig (code\DMAttracsTimers.pas:482)
 [13] DMAttracsTimers.TAttracsTimerDataModule.TimerKernelWork (code\DMAttracsTimers.pas:551)
 [14] DMAttracsTimers.TAttracsTimerDataModule.AttracsTimerTimer (code\DMAttracsTimers.pas:600)
 [15] ExtCtrls.TTimer.Timer (ExtCtrls.pas:2281)
 [16] Classes.StdWndProc (common\Classes.pas:11583)

Der innere Ausnahme Teil ist die Aufrufliste im Moment eine Ausnahme reraiste wird.

EDIT3: Die Theorie ist im Moment, dass die Virtual Memory Table (VMT) ist irgendwie gebrochen. Wenn dies geschieht gibt es keinen Hinweis darauf. Erst wenn eine Methode aufgerufen wird, wird eine Ausnahme ausgelöst ( immer auf Adresse FFFFFFDD, -35 dezimal), aber dann ist es zu spät. Sie wissen nicht, die wirkliche Ursache für den Fehler. Jeder Hinweis, wie Sie einen Fehler wie diese zu fangen ist wirklich zu schätzen !!! Wir haben mit SafeMM versucht, aber das Problem ist, dass der Speicherverbrauch zu hoch ist, auch wenn das 3 GB Flag verwendet wird. So, jetzt versuche ich, eine Prämie auf die SO Gemeinschaft zu geben:)

Edit4: Ein Hinweis ist, dass das Protokoll dort nach oft (oder sogar immer) einem weiteren Ausnahme, bevor diese. Es kann in der Datenbank zum Beispiel der optimistischen Sperren sein. Wir haben versucht, Ausnahmen mit Gewalt zu erhöhen, sondern in der Testumgebung es funktioniert gut.

EDIT5: Die Geschichte geht weiter ... habe ich eine Suche auf den Protokollen für die letzten 30 Tage jetzt. Das Ergebnis:

  • "Lesen von Adresse FFFFFFDB" 0
  • "Lesen von Adresse FFFFFFDC" 24
  • "Lesen von Adresse FFFFFFDD" 270
  • "Lesen von Adresse FFFFFFDE" 22
  • "Lesen von Adresse FFFFFFDF" 7
  • "Lesen von Adresse FFFFFFE0" 20
  • "Lesen von Adresse FFFFFFE1" 0

So die aktuelle Theorie ist, dass eine Enumeration (es gibt eine viel fettgedruckt ist) einen Zeiger überschreiben. Ich habe 5 Treffer mit unterschiedlicher Adresse oben. Es könnte bedeuten, dass der ENUM-5-Werte hält, wo der zweite am häufigsten verwendete ist. Wenn es eine Ausnahme ist, sollte ein Rollback für die Datenbank auftreten und Boldobjects zerstört werden soll. Vielleicht gibt es eine Chance, dass nicht alles zerstört und eine ENUM kann noch an eine Adresse Standort schreiben. Wenn dies wahr ist vielleicht ist es möglich, den Code von einem RegExpr für eine Enumeration mit 5 Werten suchen?

EDIT6: Um es zusammenzufassen, keine gibt es keine Lösung für das Problem noch. Ich weiß, dass ich Ihnen ein wenig mit dem Aufrufliste kann täuschen. Ja, es gibt einen Timer, dass aber es gibt andere Callstacks ohne Timer. Das tut mir leid. Aber es gibt zwei gemeinsame Faktoren.

  • Eine Ausnahme mit Lesen der Adresse FFFFFFxx.
  • zum Aufrufliste ist System.TObject.InheritsFrom (sys \ system.pas: 9237)

Das überzeugt mich, dass VilleK das Problem beschreiben. Ich bin auch davon überzeugt, dass das Problem irgendwo im Bold Rahmen ist. Aber die BIG Frage ist, wie können Probleme wie diese gelöst werden? Es ist nicht genug, um eine Assert wie VilleK zu haben, als der Schaden bereits geschehen ist, und die Aufrufliste ist in diesem Moment verschwunden. Also meine Ansicht zu beschreiben, was den Fehler verursachen kann:

  1. Irgendwo wird ein Zeiger einen schlechten Wert 1 zugewiesen, aber es kann auch 0, 2, 3 usw. sein.
  2. Ein Objekt wird zu diesem Zeiger zugeordnet.
  3. Es ist Methodenaufruf in den Objekten Basisklasse. Diese Ursache Methode TObject.InheritsForm aufgerufen werden und eine Ausnahme erscheint auf Adresse FFFFFFDD.

Diese drei Ereignisse können in dem Code zusammen sein, aber sie können auch viel später verwendet werden. Ich denke, das für den letzten Methodenaufruf wahr ist.

EDIT7: Wir arbeiten eng mit dem der Autor von Bold Jan Norden und er fand vor kurzem einen Fehler in der OCL-Auswerter in Bold Rahmen. Wenn diese diese Art von Ausnahmen festgelegt wurden eine Menge verringert, aber sie kommen immer noch gelegentlich. Aber es ist eine große Erleichterung, dass dies fast gelöst ist.

War es hilfreich?

Lösung

Ich habe keine Lösung, aber es gibt einige Hinweise darauf, dass bestimmte Fehlermeldung.

System.TObject.InheritsFrom subtrahiert die Konstante vmtParent aus dem Self-Zeiger (die Klasse) Zeiger auf die Adresse der übergeordneten Klasse zu erhalten.

In Delphi 2007 vmtParent definiert:

vmtParent = -36;

So ist der Fehler $ FFFFFFDD (-35) klingt wie der Klassenzeiger ist in diesem Fall 1.

Hier ist ein Testfall zu reproduzieren:

procedure TForm1.FormCreate(Sender: TObject);
var
  I : integer;
  O : tobject;
begin
  I := 1;
  O := @I;
  O.InheritsFrom(TObject);
end;

Ich habe versucht, es in Delphi 2010 und bekommen ‚Lesen der Adresse FFFFFFD1‘, weil die vmtParent unterschiedlich zwischen Delphi-Versionen ist.

Das Problem ist, dass diese innerhalb des Bold Rahmen geschieht tief, so dass Sie Probleme Schutz vor ihm im Anwendungscode haben.

Sie können versuchen, diese auf Ihren Objekten, die im DMAttracsTimers-Code verwendet werden (was ich davon ausgehen, ist der Anwendungscode):

Assert(Integer(Obj.ClassType)<>1,'Corrupt vmt');

Andere Tipps

Sie schreiben, dass Sie es wollen eine Ausnahme, wenn

sein
  

gibt es einen Schreibvorgang auf einer Speicheradresse, die nicht von der Anwendung zugeordnet ist

aber das passiert sowieso, sowohl die Hardware und den OS Dafür sorgen.

Wenn Sie meinen, Sie für ungültige Speicher überprüfen möchten schreibt in Ihrer Anwendung zugeordnet Adressbereich, dann gibt es nur so viel Sie tun können. Sie sollten FastMM4 , verwenden und es mit seinen ausführlichen und paranoid Einstellungen im Debug-Modus der Anwendung . Dies wird eine Menge ungültige schreibt fangen, greift auf den Speicher und solche bereits freigegeben, aber es kann nicht alles. Betrachten wir ein baumelnden Zeiger, der auf eine andere beschreibbare Speicherstelle zeigt (wie die Mitte eines großen String oder ein Array von Float-Werte) - Schreiben, um es erfolgreich sein wird, und es werden andere Datenmüll, aber es gibt keine Möglichkeit für den Speicher-Manager zu fangen solche Zugang.

Es klingt wie Sie Speicherfehler von Objektinstanz Daten haben.

Die VMT selbst nicht beschädigt zu werden, FWIW: die VMT ist (normalerweise) in der ausführbaren Datei gespeichert und die Seiten, die sie abbilden sind schreibgeschützt. Vielmehr als VilleK sagt, es sieht aus wie das erste Feld der Instanzdaten in Ihrem Fall mit einem 32-Bit-Integer mit dem Wert überschrieben wurde 1. Diese ist leicht genug, um zu überprüfen: Überprüfen Sie die Instanzdaten des Objekts, dessen Methodenaufruf fehlgeschlagen ist, und stellen Sie sicher, dass die erste dword ist 00000001.

Wenn es tatsächlich der VMT-Zeiger in den Instanzdaten, die beschädigt wird, ist hier, wie ich den Code finden würde, dass es verdirbt:

  1. Stellen Sie sicher, dass es eine automatisierte Art und Weise ist, das Problem zu reproduzieren, die keine Benutzereingaben erforderlich. Das Problem kann ohne Neustarts zwischen Reproduktionen nur reproduzierbar auf einer einzigen Maschine sein aufgrund von Windows können Speicher legen.

  2. , um das Problem zu reproduzieren, und beachten Sie die Adresse der Instanzdaten, deren Speicher beschädigt ist.

  3. Rerun und überprüfen Sie die zweite Wiedergabe. Vergewissern Sie sich, dass die Adresse der Instanzdaten, die im zweiten Lauf beschädigt wurde die gleiche wie die Adresse des ersten Laufes ist

  4. Jetzt, sollte in einen dritten Durchlauf, legte einen 4-Byte-Daten Haltepunkt auf dem Abschnitt des Speichers durch die vorherigen zwei Durchläufe angegeben. Der Punkt ist, bei jeder Änderung dieser Speicher zu brechen. Mindestens eine Pause soll der TObject.InitInstance Aufruf sein, die in dem VMT-Zeiger füllt; es kann andere im Zusammenhang mit Beispiel Konstruktion, wie beispielsweise in dem Speicherzuteiler sein; und im schlimmsten Fall die entsprechenden Instanzdaten wurden möglicherweise Speicher aus früheren Fällen zurückgeführt. Zur Senkung der Menge des benötigten Schritt, den Datenhaltepunkt macht den Call-Stack einzuloggen, aber nicht wirklich brechen. Durch die Überprüfung des Anrufstapels, nachdem der virtuelle Aufruf fehlschlägt, sollten Sie in der Lage sein, die schlecht schreiben zu finden.

mghie ist natürlich recht. (Fastmm4 ruft die Flagge fulldebugmode oder so ähnlich).

Man beachte, dass die in der Regel mit Barrieren funktioniert nur vor und nach einer Heapzuordnung, die regelmäßig überprüft werden (auf jedem heapmgr Zugriff?).

Das hat zwei Konsequenzen:

  • der Ort, wo FastMM den Fehler erkennt, könnte von der Stelle abweichen, wo es passiert
  • insgesamt Random-Write (nicht Überlauf der bestehenden Zuteilung) möglicherweise nicht erkannt werden.

Also hier sind einige andere Dinge zu denken:

  • Aktivieren Sie Runtime Überprüfung
  • Überprüfen Sie alle Ihre Compiler Warnungen.
  • Versuchen Sie es mit einer anderen Version delphi oder FPC zu kompilieren. Andere Compiler / RTLS / heapmanagers hat unterschiedliche Layouts, und das könnte dazu führen, zu den Fehlern leichter erwischt zu werden.

Wenn das alle Erträge nichts, versuchen, die Anwendung zu vereinfachen, bis es weggeht. Dann untersuchen die neuesten kommentierte / ifdefed Teile.

Das erste, was ich tun würde, ist MadExcept zu Ihrer Anwendung hinzufügen und einen Stapel Zurückverfolgungs erhalten, die exakt Berufung Baum ausdruckt, die Ihnen eine Vorstellung geben wird, was hier vor sich geht. Anstelle einer zufälligen Ausnahme und eine Binär / Hex-Speicheradresse, müssen Sie mit den Werten aller Parameter und lokalen Variablen von dem Stapel einen Aufruf Baum, um zu sehen.

Wenn ich eine Speicherbeschädigung in einer Struktur vermuten, dass meine Anwendung Schlüssel ist, werde ich oft zusätzlichen Code schreiben, um diesen Fehler zu machen Tracking möglich.

Wort am Anfang und eine magic2::

Zum Beispiel kann in Speicherstrukturen (Klasse oder Datensatztypen) eine Magic1 hat angeordnet Wort am Ende jeden Datensatz im Speicher. Eine Integritätsprüfung Funktion kann die Integrität dieser Strukturen überprüfen, indem Sie für jeden Datensatz Magic1 und magic2 zu sehen gesucht haben von nicht geändert worden ist, was sie im Konstruktor festgelegt wurden. Die Destructor würde sich ändern Magic1 und magic2 auf andere Werte wie $ FFFF.

Ich würde auch Trace-Protokollierung zu meiner Anwendung hinzufügen. Trace-Logging in delphi Anwendungen beginnt oft mit mir eine Signalform in Form erklärt, mit einem TMemo dort, und die TraceForm.Trace (msg: String) Funktion beginnt als "Memo1.Lines.Add (msg)". Da meine Anwendung reift, sind die Trace-Logging Einrichtungen wie ich Ausführen von Anwendungen für die allgemeine Muster in ihrem Verhalten beobachten und schlechtes Benehmen. Wenn dann ein „random“ Absturz oder Speicherfehler mit „keine Erklärung“ dem Fall ist, habe ich ein Trace-Protokoll zurück zu gehen und sehen, was zu diesem speziellen Fall geführt hat.

Manchmal ist es nicht Speicher Korruption, aber einfache grundlegende Fehler (ich habe vergessen, zu prüfen, ob X zugeordnet ist, dann gehe ich dereferenzieren. X.DoSomething (...), die X übernimmt zugeordnet ist, aber es ist nicht

Ich habe bemerkt, dass ein Timer in dem Stack-Trace ist.
Ich habe eine Menge von seltsamen Fehlern gesehen, wo die Ursache wurde das Timer-Ereignis nach der Form gebrannt wird i free'ed.
Der Grund dafür ist, dass ein Timer-Ereignis cound auf die Nachricht que gesetzt werden, und noge brfor die Zerstörung anderer Komponenten verarbeitet bekommen.
Ein Weg, um dieses Problem zu deaktivieren Sie den Timer als erster Eintrag in dem von der Form zerstören. die Zeit Anruf Application.ProcessMessages Nach der Deaktivierung, so wird keine Timer-Ereignisse verarbeitet, bevor die Komponenten zu zerstören.
Eine weitere Möglichkeit ist die Überprüfung, ob die Form in der Timerevent zu zerstören. (CsDestroying in Component).

Können Sie den Quelltext dieser Prozedur schreiben?

  

BoldSystem.TBoldMember.CalculateDerivedMemberWithExpression   (BoldSystem.pas: 4016)

So können wir sehen, was auf der Linie 4016 passiert.

Und auch die CPU-Ansicht dieser Funktion?
(Set nur einen Haltepunkt auf Zeile 4016 dieses Verfahrens und ausführen. Und kopieren + die CPU Sichten des Inhalts einfügen, wenn Sie den Haltepunkt).
So können wir die CPU-Anweisung sehen bei der Adresse ist 00404E78.

Könnte ein Problem mit einspringenden Code da sein?

Versuchen Sie, einige Wache Code um den TTimer Ereignishandler Code setzen:

procedure TAttracsTimerDataModule.AttracsTimerTimer(ASender: TObject);
begin
  if FInTimer then
  begin
    // Let us know there is a problem or log it to a file, or something. 
    // Even throw an exception
    OutputDebugString('Timer called re-entrantly!'); 
    Exit; //======> 
  end;

  FInTimer := True;
  try

    // method contents

  finally
    FInTimer := False;
  end;
end;

N @

Ich denke, es gibt eine andere Möglichkeit: der Timer ausgelöst wird, zu prüfen, ob „baumelnde Logon Sessions“ sind. Dann wird ein Anruf auf einem TLogonSession Objekt getan zu überprüfen, ob es (_GetMayDropSession) fallen gelassen werden kann, nicht wahr? Aber was, wenn das Objekt bereits zerstört? Vielleicht wegen Sicherheitsfragen einzufädeln oder einfach nur einen .Free Anruf und keine FreeAndNil Anruf (so eine Variable ist immer noch <> nil) usw. usw. In der Zwischenzeit werden andere Objekte erstellt, so dass die Speicher wieder verwendet wird. Wenn Sie versuchen, später die Variable einige Zeit zu dem acces, können Sie / gelegentlichen Fehler erhalten ...

Ein Beispiel:

procedure TForm11.Button1Click(Sender: TObject);
var
  c: TComponent;
  i: Integer;
  p: pointer;
begin
  //create
  c := TComponent.Create(nil);
  //get size and memory
  i := c.InstanceSize;
  p := Pointer(c);
  //destroy component
  c.Free;
  //this call will succeed, object is gone, but memory still "valid"
  c.InheritsFrom(TObject);
  //overwrite memory
  FillChar(p, i, 1);
  //CRASH!
  c.InheritsFrom(TObject);
end;

Zugriffsverletzung bei Adresse 004619D9 in Modul 'Project10.exe'. Lesen von Adresse 01010101.

Ist das nicht das Problem, dass „_GetMayDropSession“ einen Session-Variable freigegeben wird, werben?

Ich habe diese Art von Fehlern gesehen in TMS vor, wo Objekte befreit wurden und in einem Onchange usw. verwiesen (nur in manchen Situationen ist es Fehler gab, sehr schwierig / unmöglich zu reproduzieren, ist nun behoben durch TMS :-)). Auch bei RemObjects Sitzungen habe ich etwas ähnliches (wegen schlechter Programmierung Fehler von mir).

Ich würde versuchen, einen Dummy-Variable auf die Sitzungs Klasse hinzuzufügen und prüfen, ob es Wert ist:

  • öffentlicher Variable iMagicNumber: integer;
  • Konstruktor erstellen: iMagicNumber: = 1234567;
  • destructor zerstören: iMagicNumber: = -1;
  • "andere Verfahren": assert (iMagicNumber = 1234567)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top