2009-07-31 20 views
2

Wir haben eine native C++ - Anwendung, die über COM + auf einem Windows 2003-Server läuft. Ich habe vor kurzem von der Ereignisanzeige bemerkt, dass es Ausnahmen wirft, speziell die C0000005-Ausnahme, die laut http://blogs.msdn.com/calvin_hsia/archive/2004/06/30/170344.aspx bedeutet, dass der Prozess versucht, in den Speicher nicht innerhalb seines Adressraums zu schreiben, aka Zugriffsverletzung.Wie liest man einen Call-Stack?

Der Eintrag in der Ereignisanzeige liefert einen Call-Stack:

LibFmwk UTIL_GetDateFromLogByDayDirectory (char const *, Klasse utilCDate &) + 0xa26c LibFmwk UTIL_GetDateFromLogByDayDirectory (char const *, Klasse utilCDate &) + 0x8af4 ! LibFmwk! UTIL_GetDateFromLogByDayDirectory (char const *, Klasse utilCDate &) + 0x13a1 LibFmwk! utilCLogController :: GetFLFInfoLevel (void) const + 0x1070 LibFmwk! utilCLogController :: GetFLFInfoLevel (void) const + 0x186

Jetzt verstehe ich, dass es mir Methodennamen gibt, um zu sehen, aber ich bekomme das Gefühl, dass die Adresse am Ende jeder Zeile (z. + 0xa26c) versucht mich auf eine bestimmte Zeile oder Anweisung innerhalb dieser Methode zu verweisen.

Also meine Fragen sind:

  1. Wer weiß, wie ich diese Adresse oder andere Informationen, die in einem Call-Stack, die Linie auf seine fallen innerhalb des Codes zu bestimmen, verwenden könnte über?
  2. Gibt es irgendwelche Ressourcen da draußen, die ich lesen konnte, um Anruflisten zu verstehen,
  3. Gibt es Freeware/opensource-Tools, die bei der Analyse eines Aufruf-Stacks helfen könnten, vielleicht durch Anhängen an eine Debug-Symboldatei und/oder Binärdateien ?

Edit: Wie gewünscht, hier ist die Methode, die das Problem verursacht zu sein scheint:

BOOL UTIL_GetDateFromLogByDayDirectory(LPCSTR pszDir, utilCDate& oDate) 
{ 
BOOL bRet = FALSE; 

if ((pszDir[0] == '%') && 
    ::isdigit(pszDir[1]) && ::isdigit(pszDir[2]) && 
    ::isdigit(pszDir[3]) && ::isdigit(pszDir[4]) && 
    ::isdigit(pszDir[5]) && ::isdigit(pszDir[6]) && 
    ::isdigit(pszDir[7]) && ::isdigit(pszDir[8]) && 
    !pszDir[9]) 
{ 
    char acCopy[9]; 
    ::memcpy(acCopy, pszDir + 1, 8); 
    acCopy[8] = '\0'; 

    int iDay = ::atoi(&acCopy[6]); 
    acCopy[6] = '\0'; 
    int iMonth = ::atoi(&acCopy[4]); 
    acCopy[4] = '\0'; 
    int iYear = ::atoi(&acCopy[0]); 

    oDate.Set(iDay, iMonth, iYear); 

    bRet = TRUE; 
} 

return (bRet); 

}

Dieser Code mehr als 10 Jahren von einem Mitglied unserer Gesellschaft geschrieben Wer ist schon lange weg, so nehme ich nicht an, genau zu wissen, was das tut, aber ich weiß es beteiligt in den Prozess der Umbenennung eines Log-Verzeichnisses von "Heute" zu dem bestimmten Datum, z % 20090329. Die Array-Indizierung, das Memcpy und die Adresse von Operatoren lassen es ziemlich verdächtig aussehen.

Ein anderes Problem, das wir zu haben scheinen, ist, dass dies nur auf dem Produktionssystem passiert, wir konnten es hier auf unseren Testsystemen oder Entwicklungssystemen nie reproduzieren, was uns erlauben würde, einen Debugger anzuhängen.

Sehr geschätzt! Andy

+1

Wenn Sie mit Debug-Symbolen erstellen, erhalten Sie möglicherweise eine tatsächliche Zeilennummer anstelle eines Byte-Offsets. –

+0

Vielen Dank, dass Sie darauf hingewiesen haben. –

+0

Sind Sie sicher, dass das übergebene pszDir mindestens 10 Zeichen lang ist? Es wird definitiv zum Absturz kommen, wenn das nicht der Fall ist. – jussij

Antwort

3

Wenn Sie diese Adressen wirklich Ihren Funktionen zuordnen müssen, müssen Sie mit der .MAP-Datei arbeiten und sehen, wo diese Adressen wirklich hindeuten.

Aber in Ihrer Situation würde ich lieber dieses Problem unter Debugger untersuchen (z. B. MSVS Debugger oder Windbg); Als Alternative (wenn ein Absturz auf der Kundenseite auftritt) können Sie einen Crash-Dump erzeugen und ihn lokal untersuchen - dies kann über die Windows MiniDumpWriteDump-API oder das SysInternals ProcDump-Dienstprogramm (http://download.sysinternals.com/Files/procdump.zip) erfolgen.

Stellen Sie sicher, dass alle erforderlichen Symboldateien generiert und verfügbar sind (richten Sie auch den Microsoft Symbol Serverpfad ein, damit die Windows DLLs Eingangspunkte ebenfalls aufgelöst werden).

IMHO dies ist nur die Website, die Sie benötigen: http://www.dumpanalysis.org - das ist die beste Ressource, um alle Ihre Fragen zu decken. Betrachten Sie auch dieses PDF - http://windbg.info/download/doc/pdf/WinDbg_A_to_Z_color.pdf

+0

Das ist Gold! Sehr sehr interessante Links! – Karel

1

Punkt 2 und 3 sind leicht zu beantworten:

dritten Punkt. Irgendein Debugger. Dafür sind sie gemacht. Setzen Sie Ihren Debugger, um diese spezielle Ausnahme zu unterbrechen. Sie sollten in der Lage sein, sich selbst durch den Callstack zu klicken und die verschiedenen Aufrufe auf dem Stack zu finden (zumindest Delphi kann das tun, also sollte auch Visual Studio in der Lage sein). Kompilieren Sie möglichst ohne Optimierungen. OllyDBG könnte auch funktionieren - vielleicht in Kombination mit seiner Trace-Funktionalität.

2. Punkt.Irgendwelche Informationen über x86 Assembler, Reverseengineering ... Versuchen Sie: OpenRCE, NASM Documentation, ASM Community.

1. Punkt. Der Callstack teilt Ihnen die Funktionen mit. Ich weiß nicht, ob es in der Reihenfolge oder in umgekehrter Reihenfolge geschrieben ist - so könnte es sein, dass die erste Zeile die letzte aufgerufene Funktion oder die erste aufgerufene Funktion ist. Folgen Sie den Aufrufen mit Hilfe des Debuggers. Manchmal kann man zwischen asm und code wechseln (abhängig vom Debugger, Map-Dateien ...). Wenn Sie nicht die Quelle - Assembler lernen haben, lesen Sie mehr über Reverse Engineering. Lesen Sie die Dokumentation der Funktionen, die Sie in Komponenten von Drittanbietern aufrufen. Vielleicht erfüllen Sie eine Vorbedingung nicht.

Wenn Sie ein bisschen mehr über das Programm sagen kann (welche Teile des Quellcodes haben Sie haben, ist eine Bibliothek Anruf beteiligt ?, ...)


einige Code-Lese Jetzt:

Die Funktion akzeptiert einen Zeiger auf eine mit Null beendete Zeichenfolge und einen Verweis auf ein Datumsobjekt. Der Zeiger wird als gültig angenommen!

Die Funktion prüft, ob die Zeichenfolge in einem bestimmten Format vorliegt (% gefolgt von 8 Ziffern gefolgt von einem \ 0). Ist dies nicht der Fall, wird false zurückgegeben. Diese Überprüfung (das große if) greift ohne Gültigkeitsprüfungen auf den Zeiger zu. Die Länge wird nicht überprüft, und wenn der Zeiger irgendwo in die Wildnis zeigt, wird auf diesen Platz zugegriffen. Ich weiß nicht, ob eine kürzere Saite Probleme verursacht. Es sollte nicht wegen der Art & & ausgewertet werden.

Dann ist auf dem Stapel etwas Speicher reserviert. Der Nummern-Teil der Zeichenkette wird hineinkopiert (was in Ordnung ist) und der Puffer erhält seine \ 0 Beendigung. Die Atois extrahieren die Zahlen. Dies funktioniert wegen der verschiedenen Start-Standorte und der \ 0-Terminierung nach jedem Teil. Irgendwie knifflig, aber nett. Einige Kommentare hätten alles klar gemacht.

Diese Nummern werden dann in das Objekt eingefügt. Es sollte gültig sein, da es durch Referenz in die Funktion übernommen wird. Ich weiß nicht, ob Sie einen Verweis auf ein gelöschtes Objekt weitergeben können, aber wenn dies der Fall ist, könnte dies auch Ihr Problem sein.

Wie auch immer - außer der fehlenden Überprüfung für den String-Zeiger, ist diese Funktion Sound und nicht die Ursache für Ihr Problem. Es ist nur der Ort, der die Ausnahme auslöst. Suchen Sie nach Argumenten, die an diese Funktion übergeben werden. Sind sie immer gültig? Loggen Sie ein.

Ich hoffe, ich habe keine größeren Fehler gemacht, da ich ein Delphi-Programmierer bin. Wenn ich es tat - fühlen Sie sich frei zu kommentieren.

+0

Danke, dass Sie sich den Code angeschaut haben, ich stimme Ihnen insofern zu, als es nichts in der Methode gibt, das eine Zugriffsverletzung zu verursachen scheint, wenn die übergebenen Parameter auf den richtigen Speicherbereich zeigen. Das ist etwas, was ich überprüfen muss. Ich wünschte, ich könnte diese beiden Antworten als die richtige Antwort festlegen. –

+0

np - Ich bin hier, um zu helfen, nicht für die Punkte. –

5

Andere haben dies zwischen den Zeilen gesagt, aber nicht explizit. Blick auf:

LibFmwk!UTIL_GetDateFromLogByDayDirectory(char const *,class utilCDate &) + 0xa26c 

Der Offset 0xa26c riesige ist, Art und Weise über das Ende der Funktion. der Debugger hat offensichtlich nicht die richtigen Symbole für LibFmwk, stattdessen verlässt er sich auf die DLL-Exporte und zeigt den Offset relativ zum nächsten, den er finden kann.

Also, ja, richtige Symbole bekommen und dann sollte es ein Kinderspiel sein. UTIL_GetDateFromLogByDayDirectory ist hier nicht fehlerhaft.