2016-06-01 1 views
0

Ich hacke mich gerade um eine Lösung herum, um Fehler beim Debuggen zu erleichtern.AccessViolation beim Aufruf von SymSetOptions nach Stack-Überlauf

, so habe ich eine definierte allready main und der Benutzer muss wrappermain definieren, die die Verwendung von main ersetzen:

int main(int ac, char** av) 
{ 
    __try { 
    return wrappermain(ac, av); 
    } __except(HandleException(GetExceptionCode(), GetExceptionInformation()) { 
    return 1; 
    } 
} 

jetzt in der HandleException Funktion laden i die Symbolinformationen und stellen Sie einige Optionen:

dieses Konstrukt funktioniert wie ein Charme, aber wenn ich einen Stapelüberlauf mit einer endlosen Rekursion provozieren, erhalte ich eine Zugriffsverletzung beim Aufruf SymSetOptions(symOptions)

was könnte das sein und wie kann man diesen Absturz loswerden?

+0

Es gibt nur ca. 7 Kilobyte Stapelspeicher an der Handle links() -Aufruf, Notfallraum released durch den SO-Handler. Ohne Backstop an Ort und Stelle, um eine weitere SO zu generieren, wenn Sie eine Funktion aufrufen, die viel Stapelspeicherplatz benötigt. Kaboom mit AV ist der nächste. Einige Ausnahmen sind einfach zu unangenehm, um sie anmutig zu behandeln. Sie haben nach diesem die Website eines Programmierers benannt :) –

+0

@HansPassant wow, das hört sich gut an, was ist der Name dieser Seite? Vielleicht gibt es eine Menge hilfreicher Informationen darüber (die Rekursion wird wieder gestartet);) – Zaiborg

+1

Falsche Lösung. Sie sollten wirklich [MiniDumpWriteDump] (https://msdn.microsoft.com/de-us/library/windows/desktop/ms680360.aspx) statt. Bonus: Sie erhalten so viel oder so wenig Informationen in Ihrem Minidump wie Sie anfordern. Und Sie müssen PDBs mit Ihrer Anwendung nicht versenden. – IInspectable

Antwort

1

Das Kernproblem hier ist offensichtlich der Stapel. Sie haben einen Stapelüberlauf (beschädigt/unbrauchbar) und möchten ihn mit einem stapelbasierten Ausnahmebehandler behandeln. Das wird nicht gut funktionieren. Ich habe sogar überrascht, dass du so weit gekommen bist; Ihr Exception-Handler muss gefunden werden, indem Sie den Call-Stack zurücklaufen lassen, um einen Handler zu finden. Und Walking-Tat-Stack wird ein Problem sein, wenn es einen Überlauf gegeben hat und ein Teil dieses Stacks in Aktion fehlt.

Wie auch immer, ich vermute, die direkte Ursache ist das Fehlen einer Wache Seite auf dem Stapel. Der Stapel ist nicht vollständig vorne zugeordnet. Stattdessen gibt es am Ende der Zuweisung eine Schutzseite. Zu viel Druck verursacht einen Seitenfehler, der normalerweise zu einem erfolgreichen Stapelwachstum führt. Aber wenn kein Platz mehr ist, ist der Überlauf tödlich und die Wache ist weg.

Der Fix kann sehr gut anderen Thread sein. Es würde eine globale Flagge (DWORD so atomare) abfragen und schlafen, wenn nichts passiert. Ihr Ausnahmebehandler müsste nur das Flag setzen, und das erfordert keinen funktionierenden Stack. Dadurch wird der Hilfsthread entsperrt, der dann (mit seinem neuen Stapel) die relevanten Methoden aufruft.

Die vektorbezogene Ausnahmebehandlung kann wie eine Problemumgehung aussehen, unterliegt jedoch strengen Einschränkungen in Bezug darauf, was Sie in einem Handler tun können.

+0

ok diese Erklärung ist genial und awefull. so könnte ich die Fehlerbehandlung in einem getrennten Thread implementieren, kein biggie, aber wenn ich einen Anrufstapel des abgestürzten Fadens drucken möchte, laufe ich im gleichen Problem. oder habe ich etwas vermisst? – Zaiborg

+0

Lassen Sie den abgestürzten Thread kein globales 'DWORD' sondern ein' UINT_PTR' mit der Stack-Basisadresse übergeben. Aber offensichtlich ist der Aufrufstapel für einen Stapelüberlauf ziemlich groß! – MSalters

+0

dafür würde es genug sein, um den ersten Stapelrahmen zu bekommen, um die aktuelle Funktion zu erhalten, die das Problem verursacht – Zaiborg

1

"was könnte das sein" - das und muss sein. weil nicht genug Stapelplatz. In HandleException führen Sie denselben Stapelpunkt aus, an dem die Ausnahme auftritt. Sie haben nur 1 Seite (4096 Byte) Stapelspeicher. so Aufruf der Funktion, die großen Stack-Speicherplatz erfordern erneut Ausnahme auslösen. und dieses neue wird schon fatal sein - da schon gar kein stackspace mehr existiert, sogar 1 seite nicht existiert. Wenn Sie also STATUS_STACK_OVERFLOW anzeigen, müssen Sie EXCEPTION_EXECUTE_HANDLER einfach zurückgeben und bereits in __except() {..} block (in diesem Punkt wird der Stackzeiger bereits zurückgesetzt) ​​erneut nach if suchen (GetExceptionCode() == STATUS_STACK_OVERFLOW) - de allo stack zuweisen Leerzeichen und setze PAGE_GUARD erneut in der unten zugewiesenen Seite. wenn nicht tun dies - neben Stack-Überlauf für Ihre Anwendung wird bereits tödlich sein (nicht 1 Seite im Stack)

+0

ist es möglich, auf die Ausnahmebedingung Informationen innerhalb der '__except() {}' Block zugreifen? – Zaiborg

+0

direct - nein (* - aber), weil dies bereits 'unter' Stack-Pointer sein wird, aber Sie können dies kopieren und als Zugriff in __except() {} Block, aber weil wir über Benutzermodus Stack in Witwen sprechen, wir wirklich kann in einigen Fällen Zugriffsdaten und unter Stack-Pointer sichern. So können Sie einfach den Zeiger auf GetExceptionInformation() in lokalen Variablen speichern und verwenden. und create separate thread für handle diese ausnahme absolut nicht brauchen, können wir dies tun und aus dem aktuellen thread – RbMm

0
  EXCEPTION_POINTERS ep; 
      __try { 
       return wrappermain(ac, av); 
      } 
      __except(memcpy(&ep,GetExceptionInformation(), sizeof(EXCEPTION_POINTERS)), HandleException(GetExceptionCode(),GetExceptionInformation())){ 
       if (GetExceptionCode() == STATUS_STACK_OVERFLOW) 
       { 

    #ifdef _WIN64 
    #define GUARD_PAGE_COUNT 3 
    #define COMMIT_PAGE_COUNT 6 
    #else 
    #define GUARD_PAGE_COUNT 1 
    #define COMMIT_PAGE_COUNT 2 
    #endif 
//in ntdll exist special api for this - RtlResetStackOverflow, but it not exported 
        BOOL fOk = FALSE; 

        ::MEMORY_BASIC_INFORMATION mbi; 
        if (VirtualQuery(&mbi, &mbi, sizeof(mbi))) 
        { 
         if ((GUARD_PAGE_COUNT + COMMIT_PAGE_COUNT) << PAGE_SHIFT < RtlPointerToOffset(mbi.AllocationBase, mbi.BaseAddress)) 
         { 

          mbi.BaseAddress = (PBYTE)mbi.BaseAddress - ((GUARD_PAGE_COUNT + COMMIT_PAGE_COUNT) << PAGE_SHIFT); 
          if (mbi.AllocationBase < mbi.BaseAddress) 
          { 
           ULONG OldProtect; 
           fOk = VirtualFree(mbi.AllocationBase, RtlPointerToOffset(mbi.AllocationBase, mbi.BaseAddress), MEM_DECOMMIT) 
            && 
            VirtualProtect(mbi.BaseAddress, GUARD_PAGE_COUNT << PAGE_SHIFT, PAGE_READWRITE|PAGE_GUARD, &OldProtect); 
          } 

         } 
        } 
       } 
      } 
Verwandte Themen