Frage

Ich möchte entweder eine StackOverflowException verhindern oder zu behandeln, die ich von einem Aufruf der XslCompiledTransform.Transform Methode innerhalb eines Xsl Editor Ich schreibe immer bin. Das Problem scheint zu sein, dass der Benutzer ein Xsl script schreiben kann, die unendlich rekursiv ist, und es weht nur auf die Transform Methode auf den Aufruf. (Das heißt, das Problem ist nicht nur die typischen programmatischen Fehler, die in der Regel ist die Ursache für eine solche Ausnahme.)

Gibt es eine Möglichkeit zu erkennen und / oder zu begrenzen, wie viel Rekursion erlaubt sind? Oder irgendwelche anderen Ideen diesen Code, um zu verhindern nur auf mich Sprengung?

War es hilfreich?

Lösung

Von Microsoft:

  

Beginnend mit dem .NET Framework   Version 2.0, eine Stackoverflow   Objekt kann nicht durch einen Try-Catch gefangen werden   Block und der entsprechende Prozess ist   Standardmäßig beendet. Folglich,   Nutzer sollten ihren Code schreiben   zu erfassen und einen Stapel zu verhindern   Überlauf. Zum Beispiel, wenn Ihr   Anwendung hängt von Rekursion, Verwendung   ein Zähler oder eine Zustandsbedingung   beendet die rekursive Schleife.

Ich gehe davon aus der Ausnahme in einer internen .NET-Methode geschieht, und nicht in Ihrem Code.

Sie können ein paar Dinge tun.

  • Schreiben Code, der die xsl für unendliche Rekursion prüft und benachrichtigt den Benutzer vor dem Anwenden einer Transformation (Pfui).
  • Laden Sie die XslTransform-Code in einem separaten Prozess (Hacky, aber weniger Arbeit).

Sie können die Process-Klasse verwenden, um die Baugruppe zu laden, die in einem separaten Prozess-Transformation angewandt werden sollen, und den Benutzer über den Fehler aufmerksam machen, wenn er stirbt, ohne Ihre Hauptanwendung zu töten.

EDIT: Ich habe getestet, hier ist, wie es geht:

MainProcess:

// This is just an example, obviously you'll want to pass args to this.
Process p1 = new Process();
p1.StartInfo.FileName = "ApplyTransform.exe";
p1.StartInfo.UseShellExecute = false;
p1.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

p1.Start();
p1.WaitForExit();

if (p1.ExitCode == 1)    
   Console.WriteLine("StackOverflow was thrown");

ApplyTransform Prozess:

class Program
{
    static void Main(string[] args)
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
        throw new StackOverflowException();
    }

    // We trap this, we can't save the process, 
    // but we can prevent the "ILLEGAL OPERATION" window 
    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        if (e.IsTerminating)
        {
            Environment.Exit(1);
        }
    }
}

Andere Tipps

  

Hinweis Die Frage in der Prämie von @WilliamJockusch und der ursprünglichen Frage unterschiedlich ist.

     

Diese Antwort ist über Stackoverflow ist im allgemeinen Fall von Bibliotheken von Drittanbietern und was Sie können / können nicht mit ihnen tun. Wenn Sie sich über den speziellen Fall mit XslTransform suchen, finden Sie in der akzeptierte Antwort.


Stapel überläuft passieren, weil die Daten auf dem Stapel eine bestimmte Grenze (in Bytes) überschreitet. Die Details, wie diese Erkennung funktioniert finden Sie hier .

  

Ich frage mich, ob es eine allgemeine Art und Weise StackOverflowExceptions aufzuspüren ist. Mit anderen Worten, ich nehme an unendliche Rekursion irgendwo in meinem Code, aber ich habe keine Ahnung, wo. Ich möchte es auf irgendeine Weise auf die Spur, die durch den Code als Schritt einfacher ist, alle über den Ort, bis ich sehe es passiert. Mir ist es egal, wie hackish es ist.

Wie ich in der Verbindung erwähnt, würde erfordern einen Stapelüberlauf von den statischen Codeanalyse Erfassung des Halteproblem zu lösen, das ist unentscheidbar . Jetzt, da wir festgestellt haben, dass gibt es keinen Königsweg , kann ich Ihnen ein paar Tricks zeigen, dass ich denke, hilft, das Problem ausfindig zu machen.

Ich denke, diese Frage kann auf verschiedene Weise interpretiert werden, und da ich ein bisschen langweilig :-) bin, werde ich es bricht in verschiedene Variationen nach unten.

einen Stapelüberlauf in einer Testumgebung Erkennung

Im Grunde hier das Problem ist, dass Sie eine (begrenzte) Testumgebung haben und wollen einen Stapelüberlauf in einer (erweitert) Produktionsumgebung zu erfassen.

Anstelle des Erfassens der SO selbst lösen I dies durch die Tatsache ausgenutzt, dass die Stapeltiefe eingestellt werden kann. Der Debugger erhalten Sie alle Informationen, die Sie benötigen. Die meisten Sprachen können Sie die Stapelgröße oder die maximale Rekursionstiefe spezifizieren.

Im Grunde versuche ich, eine so zu zwingen, indem die Stapeltiefe so klein wie möglich zu machen. Wenn es nicht überläuft, kann ich immer macht es größer (= in diesem Fall: sicherer) für die Produktionsumgebung. Sobald Sie einen Stapelüberlauf zu erhalten, können Sie manuell entscheiden, ob es ein ‚gültig‘ ist oder nicht ist.

Um dies zu tun, übergeben Sie die Stapelgröße (in unserem Fall: ein kleiner Wert) zu einem Thema Parameter, und sehen, was passiert. Die Standard-Stack-Größe in .NET ist 1 MB, wir gehen einen Weg kleineren Wert verwenden:

class StackOverflowDetector
{
    static int Recur()
    {
        int variable = 1;
        return variable + Recur();
    }

    static void Start()
    {
        int depth = 1 + Recur();
    }

    static void Main(string[] args)
    {
        Thread t = new Thread(Start, 1);
        t.Start();
        t.Join();
        Console.WriteLine();
        Console.ReadLine();
    }
}

Hinweis: Wir werden weiter unten als auch diesen Code verwenden

.

Wenn es überläuft, können Sie es auf einen größeren Wert gesetzt, bis Sie ein, so dass Sinn macht.

Erstellen von Ausnahmen, bevor Sie SO

Die StackOverflowException ist nicht abfangbar. Das bedeutet, es gibt nicht viel können Sie tun, wenn es passiert ist. Also, wenn Sie etwas glauben gebunden ist, in Ihrem Code falsch gehen, können Sie Ihre eigene Ausnahme in einigen Fällen machen. Das einzige, was Sie dafür brauchen, ist die aktuelle Stack-Tiefe; es gibt keine Notwendigkeit für einen Zähler, um die realen Werte von .NET verwenden kann:

class StackOverflowDetector
{
    static void CheckStackDepth()
    {
        if (new StackTrace().FrameCount > 10) // some arbitrary limit
        {
            throw new StackOverflowException("Bad thread.");
        }
    }

    static int Recur()
    {
        CheckStackDepth();
        int variable = 1;
        return variable + Recur();
    }

    static void Main(string[] args)
    {
        try
        {
            int depth = 1 + Recur();
        }
        catch (ThreadAbortException e)
        {
            Console.WriteLine("We've been a {0}", e.ExceptionState);
        }
        Console.WriteLine();
        Console.ReadLine();
    }
}

Beachten Sie, dass dieser Ansatz funktioniert auch, wenn Sie mit Komponenten von Drittanbietern zu tun haben, die einen Callback-Mechanismus verwenden. Das einzige, was ist erforderlich, dass Sie abfangen können einige ruft in den Stack-Trace.

Detektion in einem separaten Thread

Sie dies explizit vorgeschlagen, geht so hier, um diesen ein.

Sie können versuchen, ein SO in einem separaten Thread Erkennung .. aber es wird wahrscheinlich Ihnen nichts nützen. Ein Stapelüberlauf kann passiert schnell , noch bevor Sie einen Kontextwechsel erhalten. Dies bedeutet, dass dieser Mechanismus überhaupt nicht zuverlässig ist ... Ich würde eigentlich nicht empfehlen, sie . Es hat Spaß gemacht, obwohl zu bauen, also hier ist der Code: -)

class StackOverflowDetector
{
    static int Recur()
    {
        Thread.Sleep(1); // simulate that we're actually doing something :-)
        int variable = 1;
        return variable + Recur();
    }

    static void Start()
    {
        try
        {
            int depth = 1 + Recur();
        }
        catch (ThreadAbortException e)
        {
            Console.WriteLine("We've been a {0}", e.ExceptionState);
        }
    }

    static void Main(string[] args)
    {
        // Prepare the execution thread
        Thread t = new Thread(Start);
        t.Priority = ThreadPriority.Lowest;

        // Create the watch thread
        Thread watcher = new Thread(Watcher);
        watcher.Priority = ThreadPriority.Highest;
        watcher.Start(t);

        // Start the execution thread
        t.Start();
        t.Join();

        watcher.Abort();
        Console.WriteLine();
        Console.ReadLine();
    }

    private static void Watcher(object o)
    {
        Thread towatch = (Thread)o;

        while (true)
        {
            if (towatch.ThreadState == System.Threading.ThreadState.Running)
            {
                towatch.Suspend();
                var frames = new System.Diagnostics.StackTrace(towatch, false);
                if (frames.FrameCount > 20)
                {
                    towatch.Resume();
                    towatch.Abort("Bad bad thread!");
                }
                else
                {
                    towatch.Resume();
                }
            }
        }
    }
}

Führen Sie dies im Debugger und Spaß haben, was passiert.

Mit den Eigenschaften eines Stapelüberlauf

Eine andere Interpretation IhrerFrage: „Wo die Teile des Codes sind, die möglicherweise einen Stapelüberlauf Ausnahme verursachen könnte?“. Offensichtlich ist die Antwort hierfür ist: alle Code mit Rekursion. Für jedes Stück Code, können Sie dann eine manuelle Analyse durchführen.

Es ist auch möglich, diese mit statischer Code-Analyse zu bestimmen. Was Sie brauchen, um das zu tun, ist es, alle Methoden zu dekompilieren und herauszufinden, ob sie eine unendliche Rekursion enthalten. Hier einige Code, der das tut, für Sie:

// A simple decompiler that extracts all method tokens (that is: call, callvirt, newobj in IL)
internal class Decompiler
{
    private Decompiler() { }

    static Decompiler()
    {
        singleByteOpcodes = new OpCode[0x100];
        multiByteOpcodes = new OpCode[0x100];
        FieldInfo[] infoArray1 = typeof(OpCodes).GetFields();
        for (int num1 = 0; num1 < infoArray1.Length; num1++)
        {
            FieldInfo info1 = infoArray1[num1];
            if (info1.FieldType == typeof(OpCode))
            {
                OpCode code1 = (OpCode)info1.GetValue(null);
                ushort num2 = (ushort)code1.Value;
                if (num2 < 0x100)
                {
                    singleByteOpcodes[(int)num2] = code1;
                }
                else
                {
                    if ((num2 & 0xff00) != 0xfe00)
                    {
                        throw new Exception("Invalid opcode: " + num2.ToString());
                    }
                    multiByteOpcodes[num2 & 0xff] = code1;
                }
            }
        }
    }

    private static OpCode[] singleByteOpcodes;
    private static OpCode[] multiByteOpcodes;

    public static MethodBase[] Decompile(MethodBase mi, byte[] ildata)
    {
        HashSet<MethodBase> result = new HashSet<MethodBase>();

        Module module = mi.Module;

        int position = 0;
        while (position < ildata.Length)
        {
            OpCode code = OpCodes.Nop;

            ushort b = ildata[position++];
            if (b != 0xfe)
            {
                code = singleByteOpcodes[b];
            }
            else
            {
                b = ildata[position++];
                code = multiByteOpcodes[b];
                b |= (ushort)(0xfe00);
            }

            switch (code.OperandType)
            {
                case OperandType.InlineNone:
                    break;
                case OperandType.ShortInlineBrTarget:
                case OperandType.ShortInlineI:
                case OperandType.ShortInlineVar:
                    position += 1;
                    break;
                case OperandType.InlineVar:
                    position += 2;
                    break;
                case OperandType.InlineBrTarget:
                case OperandType.InlineField:
                case OperandType.InlineI:
                case OperandType.InlineSig:
                case OperandType.InlineString:
                case OperandType.InlineTok:
                case OperandType.InlineType:
                case OperandType.ShortInlineR:
                    position += 4;
                    break;
                case OperandType.InlineR:
                case OperandType.InlineI8:
                    position += 8;
                    break;
                case OperandType.InlineSwitch:
                    int count = BitConverter.ToInt32(ildata, position);
                    position += count * 4 + 4;
                    break;

                case OperandType.InlineMethod:
                    int methodId = BitConverter.ToInt32(ildata, position);
                    position += 4;
                    try
                    {
                        if (mi is ConstructorInfo)
                        {
                            result.Add((MethodBase)module.ResolveMember(methodId, mi.DeclaringType.GetGenericArguments(), Type.EmptyTypes));
                        }
                        else
                        {
                            result.Add((MethodBase)module.ResolveMember(methodId, mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments()));
                        }
                    }
                    catch { } 
                    break;


                default:
                    throw new Exception("Unknown instruction operand; cannot continue. Operand type: " + code.OperandType);
            }
        }
        return result.ToArray();
    }
}

class StackOverflowDetector
{
    // This method will be found:
    static int Recur()
    {
        CheckStackDepth();
        int variable = 1;
        return variable + Recur();
    }

    static void Main(string[] args)
    {
        RecursionDetector();
        Console.WriteLine();
        Console.ReadLine();
    }

    static void RecursionDetector()
    {
        // First decompile all methods in the assembly:
        Dictionary<MethodBase, MethodBase[]> calling = new Dictionary<MethodBase, MethodBase[]>();
        var assembly = typeof(StackOverflowDetector).Assembly;

        foreach (var type in assembly.GetTypes())
        {
            foreach (var member in type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).OfType<MethodBase>())
            {
                var body = member.GetMethodBody();
                if (body!=null)
                {
                    var bytes = body.GetILAsByteArray();
                    if (bytes != null)
                    {
                        // Store all the calls of this method:
                        var calls = Decompiler.Decompile(member, bytes);
                        calling[member] = calls;
                    }
                }
            }
        }

        // Check every method:
        foreach (var method in calling.Keys)
        {
            // If method A -> ... -> method A, we have a possible infinite recursion
            CheckRecursion(method, calling, new HashSet<MethodBase>());
        }
    }

Nun, die Tatsache, dass ein Verfahrenszyklus Rekursion enthält, ist keineswegs eine Garantie bedeutet, dass ein Stapelüberlauf passieren wird - es ist nur die wahrscheinlichste Voraussetzung für Ihre Stack-Überlauf Ausnahme. Kurz gesagt bedeutet dies, dass dieser Code die Teile des Codes bestimmen, wo ein Stapelüberlauf können auftreten, die deutlich die meisten Code verengen sollte.

Noch weitere Ansätze

Es gibt einige andere Ansätze können Sie versuchen, dass ich hier nicht beschrieben haben.

  1. Umgang mit dem Stack-Überlauf durch den CLR Prozess Hosting und es Handhabung. Beachten Sie, dass Sie noch können nicht ‚fangen‘ es.
  2. Ändern alle IL-Code, den Bau eines weiteren DLL, das Hinzufügen Kontrollen Rekursion. Ja, das ist durchaus möglich (ich habe es in der Vergangenheit :-) umgesetzt werden; es ist nur schwierig und erfordert eine Menge Code, um es richtig zu machen.
  3. Verwenden Sie die .NET-Profilerstellungs-API alle Methodenaufrufe zu erfassen und zu verwenden, die Stapel überläuft, um herauszufinden. Zum Beispiel können Sie Prüfungen implementieren, dass, wenn Sie die gleiche Methode X-mal in Ihrem Anruf Baum begegnen, können Sie ein Signal geben. Es gibt ein Projekt hier , dass Sie geben einen Vorsprung.

Ich möchte einen Wrapper um XmlWriter-Objekt empfehlen die Erstellung, so dass es Menge Anrufe zu Write / WriteEndElement zählen würde, und wenn Sie Menge von Tags zu einem gewissen Zahl (zB 100) begrenzen, würden Sie eine andere Ausnahme werfen können, zum Beispiel -. InvalidOperation

Das sollte das Problem in der Mehrzahl der Fälle lösen

public class LimitedDepthXmlWriter : XmlWriter
{
    private readonly XmlWriter _innerWriter;
    private readonly int _maxDepth;
    private int _depth;

    public LimitedDepthXmlWriter(XmlWriter innerWriter): this(innerWriter, 100)
    {
    }

    public LimitedDepthXmlWriter(XmlWriter innerWriter, int maxDepth)
    {
        _maxDepth = maxDepth;
        _innerWriter = innerWriter;
    }

    public override void Close()
    {
        _innerWriter.Close();
    }

    public override void Flush()
    {
        _innerWriter.Flush();
    }

    public override string LookupPrefix(string ns)
    {
        return _innerWriter.LookupPrefix(ns);
    }

    public override void WriteBase64(byte[] buffer, int index, int count)
    {
        _innerWriter.WriteBase64(buffer, index, count);
    }

    public override void WriteCData(string text)
    {
        _innerWriter.WriteCData(text);
    }

    public override void WriteCharEntity(char ch)
    {
        _innerWriter.WriteCharEntity(ch);
    }

    public override void WriteChars(char[] buffer, int index, int count)
    {
        _innerWriter.WriteChars(buffer, index, count);
    }

    public override void WriteComment(string text)
    {
        _innerWriter.WriteComment(text);
    }

    public override void WriteDocType(string name, string pubid, string sysid, string subset)
    {
        _innerWriter.WriteDocType(name, pubid, sysid, subset);
    }

    public override void WriteEndAttribute()
    {
        _innerWriter.WriteEndAttribute();
    }

    public override void WriteEndDocument()
    {
        _innerWriter.WriteEndDocument();
    }

    public override void WriteEndElement()
    {
        _depth--;

        _innerWriter.WriteEndElement();
    }

    public override void WriteEntityRef(string name)
    {
        _innerWriter.WriteEntityRef(name);
    }

    public override void WriteFullEndElement()
    {
        _innerWriter.WriteFullEndElement();
    }

    public override void WriteProcessingInstruction(string name, string text)
    {
        _innerWriter.WriteProcessingInstruction(name, text);
    }

    public override void WriteRaw(string data)
    {
        _innerWriter.WriteRaw(data);
    }

    public override void WriteRaw(char[] buffer, int index, int count)
    {
        _innerWriter.WriteRaw(buffer, index, count);
    }

    public override void WriteStartAttribute(string prefix, string localName, string ns)
    {
        _innerWriter.WriteStartAttribute(prefix, localName, ns);
    }

    public override void WriteStartDocument(bool standalone)
    {
        _innerWriter.WriteStartDocument(standalone);
    }

    public override void WriteStartDocument()
    {
        _innerWriter.WriteStartDocument();
    }

    public override void WriteStartElement(string prefix, string localName, string ns)
    {
        if (_depth++ > _maxDepth) ThrowException();

        _innerWriter.WriteStartElement(prefix, localName, ns);
    }

    public override WriteState WriteState
    {
        get { return _innerWriter.WriteState; }
    }

    public override void WriteString(string text)
    {
        _innerWriter.WriteString(text);
    }

    public override void WriteSurrogateCharEntity(char lowChar, char highChar)
    {
        _innerWriter.WriteSurrogateCharEntity(lowChar, highChar);
    }

    public override void WriteWhitespace(string ws)
    {
        _innerWriter.WriteWhitespace(ws);
    }

    private void ThrowException()
    {
        throw new InvalidOperationException(string.Format("Result xml has more than {0} nested tags. It is possible that xslt transformation contains an endless recursive call.", _maxDepth));
    }
}

Diese Antwort ist für @WilliamJockusch.

  

Ich frage mich, ob es ein allgemeiner Weg nach unten zu verfolgen   StackOverflowExceptions. Mit anderen Worten: Angenommen, ich habe unendlich   Rekursion irgendwo in meinem Code, aber ich habe keine Ahnung, wo. ich will   verfolgen sie durch irgendein Mittel nach unten, die leichter als Schritt durch Code ist   alle über den Ort, bis ich sehe es passiert. Ich interessiere mich nicht, wie hackish   es ist. Zum Beispiel wäre es toll, ein Modul zu haben, konnte ich   aktivieren, vielleicht sogar von einem anderen Thread, der den Stapel abgefragt   Tiefe und beschwerte sich, wenn es auf ein Niveau habe ich als „zu hoch“. Zum   Beispiel könnte ich „zu hoch“ zu 600 Frames gesetzt, herauszufinden, dass, wenn die   Stapel waren zu tief, dass ein Problem sein muss. Ist so ähnlich   möglich. Ein weiteres Beispiel wäre jeden 1000. Methodenaufruf zur Anmeldung   In meinem Code in der Debug-Ausgabe. Die Chancen, dies würde sich einige   Beweise für die overlow wäre ziemlich gut, und es würde wahrscheinlich nicht   Schlag den Ausgang zu schlecht auf. Der Schlüssel ist, dass es nicht beteiligen kann   einen Scheck, wo der Überlauf geschieht. Da die gesamte   Problem ist, dass ich weiß nicht, wo das ist. Vorzugsweise die Lösung   abhängen sollte nicht auf das, was meine Entwicklungsumgebung aussieht; das heißt,   es sollte nicht assumet, dass ich C # über eine spezifische Toolset bin mit (z   VS).

Es klingt wie Sie sind daran interessiert, einige Debugging-Techniken zu hören, diese Stackoverflow zu fangen, so dachte ich, ich würde ein paar teilen, damit Sie versuchen.

1. Speicher-Dumps.

Pro : Memory Dumps sind ein sicherer Weg, um die Ursache eines Stack-Überlauf zu trainieren. AC # MVP & Ich arbeitete zusammen eine SO Fehlerbehebung und er fuhr fort, darüber bloggen hier .

Diese Methode ist der schnellste Weg, um das Problem auf die Spur.

Diese Methode wird nicht verlangen, dass Sie Probleme zu reproduzieren, indem Sie wie folgt in Protokollen gesehen.

Con :. Memory-Dumps sind sehr groß und Sie haben AdPlus / ProcDump den Prozess anhängen

2. Aspect Orientierte Programmierung.

Pro : Dies ist wahrscheinlich der einfachste Weg für Sie Code zu implementieren, die in jedem Verfahren der Anwendung ohne das Schreiben von Code der Größe des Call-Stack von jeder Methode überprüft. Es gibt eine Reihe von AOP Frameworks , die Sie Intercept vor und nach Gesprächen ermöglicht.

Werden Sie die Methoden sagen, dass die Stack-Überlauf verursachen.

Hier können Sie die StackTrace().FrameCount am Eingang und am Ausgang aller Methoden in Ihrer Anwendung überprüfen.

Con . Es wird Auswirkungen auf die Leistung haben - die Haken in die IL eingebettet für jede Methode und man kann nicht wirklich "de-activate" it out

Es hängt etwas von der Entwicklungsumgebung Werkzeugset.

3. Logging Aktivität.

Vor einer Woche habe ich versucht, einige schwer zu reproduzieren Probleme zu jagen. Ich stellte diese QA Protokollierung von Benutzeraktivitäten, Telemetry (und Variablen Global Exception Handler) . Die Schlussfolgerung, die ich kam, war eine wirklich einfache Benutzer-Aktionen-Logger, um zu sehen, wie Probleme in einem Debugger zu reproduzieren, wenn eine nicht behandelte Ausnahme auftritt.

Pro : Sie können es ein- oder ausschalten nach Belieben (dh an Veranstaltungen abonnieren).

Verfolgen der Benutzeraktionen erfordert nicht jede Methode abfängt.

Sie können die Anzahl der Ereignisse, Methoden zählen auch abonniert werden weit einfacher als mit AOP .

Die Protokolldateien sind relativ klein und konzentrieren sich auf, welche Maßnahmen müssen Sie durchführen, das Problem zu reproduzieren.

kann Ihnen helfen, zu verstehen, wie Nutzer Ihre Anwendung verwenden.

Con :. Geeignet ist, nicht auf einen Windows-Dienst und ich bin sicher, es gibt bessere Tools, wie dies für Web-Anwendungen

Ist nicht unbedingt sagen Sie die Methoden, die den Stack-Überlauf verursachen.

Benötigen Sie Protokolle manuell zu Schritt durch Probleme, anstatt ein Speicherabbild reproduzieren, wo Sie es und Debug es sofort bekommen können.


Vielleicht könnten Sie alle Techniken versuchen, die ich oben und einige erwähnen, die uns @atlaste geschrieben und sagen, welche der gefundenen Sie waren die einfachste / schnellste / schmutzigsten / annehmbarste in einer ART-Umgebung / etc zu laufen.

Auf jeden Fall viel Glück Aufspüren dieses SO.

Wenn Sie Anwendung auf Code 3D-Partei abhängt (in Xsl-Skripte), dann müssen Sie zunächst entscheiden, tun Sie von Fehlern in ihnen verteidigen wollen oder nicht. Wenn Sie wirklich verteidigen wollen, dann denke ich, Sie Ihre Logik, die auf externe Fehler in getrennten AppDomains anfällig ausführen soll. Fangen Stackoverflow ist nicht gut.

Überprüfen Sie auch diese Frage .

hatte ich einen Stackoverflow heute und ich einige Ihrer Beiträge gelesen und beschlossen, die Garbage Collecter zu helfen.

Ich habe eine nahezu unendliche Schleife wie diese haben:

    class Foo
    {
        public Foo()
        {
            Go();
        }

        public void Go()
        {
            for (float i = float.MinValue; i < float.MaxValue; i+= 0.000000000000001f)
            {
                byte[] b = new byte[1]; // Causes stackoverflow
            }
        }
    }

Statt die Ressource laufen lassen den Gültigkeitsbereich wie folgt aus:

class Foo
{
    public Foo()
    {
        GoHelper();
    }

    public void GoHelper()
    {
        for (float i = float.MinValue; i < float.MaxValue; i+= 0.000000000000001f)
        {
            Go();
        }
    }

    public void Go()
    {
        byte[] b = new byte[1]; // Will get cleaned by GC
    }   // right now
}

Es ist für mich gearbeitet, hoffen, dass es jemand hilft.

Mit .NET 4.0 können Sie das HandleProcessCorruptedStateExceptions hinzufügen Attribut von System.Runtime.ExceptionServices das Verfahren der try / catch-Block enthält. Das ist wirklich funktioniert! Vielleicht nicht zu empfehlen, aber funktioniert.

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.ExceptionServices;

namespace ExceptionCatching
{
    public class Test
    {
        public void StackOverflow()
        {
            StackOverflow();
        }

        public void CustomException()
        {
            throw new Exception();
        }

        public unsafe void AccessViolation()
        {
            byte b = *(byte*)(8762765876);
        }
    }

    class Program
    {
        [HandleProcessCorruptedStateExceptions]
        static void Main(string[] args)
        {
            Test test = new Test();
            try {
                //test.StackOverflow();
                test.AccessViolation();
                //test.CustomException();
            }
            catch
            {
                Console.WriteLine("Caught.");
            }

            Console.WriteLine("End of program");

        }

    }      
}

@WilliamJockusch, wenn ich mich richtig Ihr Anliegen verstand, dann ist es nicht möglich ist (aus mathematischer Sicht) auf immer eine unendliche Rekursion identifizieren, wie es bedeuten würde, die Halting Problem . Um es zu lösen müssen Sie ein Super rekursiven Algorithmus (wie Trial and error Prädikate zum Beispiel ) oder eine Maschine, die kann hypercompute (ein Beispiel ist in der folgender Abschnitt - als Vorschau - von dieses Buch ).

Aus praktischer Sicht, dann würden Sie wissen müssen:

  • Wie viel Stapelspeicher Sie zu gegebener Zeit verlassen haben,
  • Wie viel Stapelspeicher Ihre rekursive Methode zum gegebenen Zeitpunkt für die spezifische Leistung benötigen.

Beachten Sie, dass mit den aktuellen Maschinen, diese Daten extrem wandelbar ist auf Multitasking und ich habe nicht von einer Software gehört, die die Aufgabe der Fall ist.

Lassen Sie mich wissen, wenn etwas unklar ist.

Mit den Blicken von ihm, abgesehen von anderem Prozess startet, scheint es keine Möglichkeit, um einen StackOverflowException der Handhabung. Bevor jemand fragt, ich versuchte, mit AppDomain, aber das hat nicht funktioniert:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;

namespace StackOverflowExceptionAppDomainTest
{
    class Program
    {
        static void recrusiveAlgorithm()
        {
            recrusiveAlgorithm();
        }
        static void Main(string[] args)
        {
            if(args.Length>0&&args[0]=="--child")
            {
                recrusiveAlgorithm();
            }
            else
            {
                var domain = AppDomain.CreateDomain("Child domain to test StackOverflowException in.");
                domain.ExecuteAssembly(Assembly.GetEntryAssembly().CodeBase, new[] { "--child" });
                domain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) =>
                {
                    Console.WriteLine("Detected unhandled exception: " + e.ExceptionObject.ToString());
                };
                while (true)
                {
                    Console.WriteLine("*");
                    Thread.Sleep(1000);
                }
            }
        }
    }
}

Wenn Sie am Ende mit der separaten-Prozesslösung, jedoch würde ich den Fehler selbst mit Process.Exited und Process.StandardOutput und Griff empfehlen, Ihren Benutzern eine bessere Erfahrung zu geben.

Sie können diese Eigenschaft lesen, alle paar Anrufe, Environment.StackTrace, und wenn die Stacktrace eine bestimmte Schwelle exceded, die Sie vorgegeben, können Sie die Funktion zurück.

Sie sollten auch versuchen, einige rekursiven Funktionen mit Schleifen zu ersetzen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top