Pergunta

Gostaria de prevenir ou lidar com uma StackOverflowException que estou recebendo a partir de uma chamada para o método XslCompiledTransform.Transform dentro de uma Xsl Editor estou escrevendo. O problema parece ser que o usuário pode escrever um Xsl script que é infinitamente recursiva, e ele simplesmente explode na chamada para o método Transform. (Ou seja, o problema não é apenas o típico erro programático, que é geralmente a causa de uma exceção tal.)

Existe uma maneira de detectar e / ou limite de quantas recursions são permitidos? Ou quaisquer outras idéias para manter este código a partir de apenas explodir em mim?

Foi útil?

Solução

De Microsoft:

Começando com o .NET Framework versão 2.0, um StackOverflowException objeto não pode ser pego por um try-catch bloquear e o processo correspondente está rescindido por padrão. Consequentemente, os usuários são aconselhados a escrever seu código para detectar e prevenir uma pilha transbordar. Por exemplo, se o seu aplicação depende de recursão, uso um contador ou de uma condição de estado encerrar o ciclo recursivo.

Estou assumindo a exceção está acontecendo dentro de um método interno NET, e não em seu código.

Você pode fazer algumas coisas.

  • Escrever código que verifica o XSL para recursão infinita e notifica o usuário antes de aplicar uma transformação (Ugh).
  • Coloque o código XslTransform em um processo separado (Hacky, mas menos trabalho).

Você pode usar a classe Process para carregar o assembly que irá aplicar a transformar-se em um processo separado, e alertar o usuário da falha, se morrer, sem matar seu aplicativo principal.

EDIT: eu só testado, aqui é como fazê-lo:

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 Processo:

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);
        }
    }
}

Outras dicas

NOTA A questão da recompensa por @WilliamJockusch e a pergunta original são diferentes.

Esta resposta é sobre StackOverflow de, no caso geral de bibliotecas de terceiros e que você pode / não pode fazer com eles. Se você está procurando sobre o caso especial com XslTransform, consulte a resposta aceita.


pilha transborda acontecer porque os dados na pilha for superior a um certo limite (em bytes). Os detalhes de como isso funciona de detecção pode ser encontrada aqui .

Eu estou querendo saber se existe uma maneira geral para rastrear StackOverflowExceptions. Em outras palavras, suponha que eu tenho infinito em algum lugar recursão no meu código, mas não tenho idéia de onde. Eu quero segui-lo para baixo por alguns meios de que é mais fácil do que percorrendo o código em todo o lugar até que eu vejo isso acontecendo. Eu não me importo como hackish que é.

Como mencionei no link, a detecção de um estouro de pilha de análise estática de código exigiria resolver o problema da parada que é undecidable . Agora que nós estabelecemos que não há nenhuma bala de prata , posso mostrar-lhe alguns truques que eu acho que ajuda a rastrear o problema.

Eu acho que esta questão pode ser interpretada de diferentes maneiras, e desde que eu sou um pouco entediado :-), eu vou dividi-la em diferentes variações.

Detectando um estouro de pilha em um ambiente de teste

Basicamente, o problema aqui é que você tem um ambiente (limitado) de teste e deseja detectar um estouro de pilha em uma (expandido) ambiente de produção.

Em vez de detectar o próprio SO, eu resolver isso, explorando o fato de que a profundidade da pilha pode ser definido. O depurador vai lhe dar todas as informações que você precisa. A maioria das linguagens permitem que você especifique o tamanho da pilha ou a profundidade de recursão máximo.

Basicamente eu tentar forçar um SO, fazendo a profundidade da pilha tão pequena quanto possível. Se ele não transborde, eu sempre pode torná-lo maior (= neste caso: mais seguro) para o ambiente de produção. O momento que você começa um estouro de pilha, você pode decidir manualmente se é um 'válido' ou não.

Para fazer isso, passe o tamanho da pilha (no nosso caso: um pequeno valor) para um parâmetro de linha, e ver o que acontece. O tamanho da pilha padrão no .NET é 1 MB, vamos usar uma forma menor valor:

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();
    }
}

. Nota: vamos usar este código abaixo, bem

Uma vez que transborda, você pode configurá-lo para um valor maior até obter um SO que faz sentido.

Criar exceções antes de SO

O StackOverflowException não é capturável. Isto significa que não há muito que você pode fazer quando isso aconteceu. Então, se você acreditar em algo é obrigado a dar errado no seu código, você pode fazer o seu próprio exceção em alguns casos. A única coisa que você precisa para isso é a profundidade da pilha atual; não há necessidade de um contador, você pode usar os valores reais da NET:

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();
    }
}

Note que esta abordagem também funciona se você está lidando com componentes de terceiros que usam um mecanismo de retorno de chamada. A única coisa necessária é que você pode interceptar alguns chamadas no rastreamento de pilha.

Detecção em um segmento separado

Você explicitamente sugeriu isso, então aqui vai um presente.

Você pode tentar a detecção de um SO em um segmento separado .. mas ele provavelmente não vai fazer nenhum bem. Um estouro de pilha pode acontecer rápido , mesmo antes de chegar uma mudança de contexto. Isto significa que este mecanismo não é confiável em tudo ... eu não recomendo realmente utilizá-lo . Foi divertido para construir embora, por isso aqui está o código: -)

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();
                }
            }
        }
    }
}

Executar este no depurador e se divertir do que acontece.

Usando as características de um estouro de pilha

Outra interpretação do seupergunta é: "Onde estão os pedaços de código que poderia potencialmente causar uma exceção de estouro de pilha?". Obviamente a resposta deste é: todo o código com recursividade. Para cada pedaço de código, você pode então fazer uma análise manual.

Também é possível determinar isso usando análise estática de código. O que você precisa fazer para isso é a decompile todos os métodos e descobrir se eles contêm uma recursão infinita. Aqui está algum código que faz isso para você:

// 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>());
        }
    }

Agora, o fato de que um ciclo de método contém recursividade, não é de forma uma garantia de que um estouro de pilha vai acontecer - é apenas a pré-condição mais provável para sua exceção de estouro de pilha. Em suma, isso significa que este código vai determinar os pedaços de código onde um estouro de pilha pode ocorrer, o que deve diminuir mais código consideravelmente.

ainda outras abordagens

Existem algumas outras abordagens que você pode tentar que eu não tenha descrito aqui.

  1. O manuseio do estouro de pilha por hospedar o processo CLR e manuseá-lo. Note que você ainda não pode 'pegar' ela.
  2. Alterar todo o código IL, construindo outra DLL, adicionando controlos de recursão. Sim, isso é perfeitamente possível (o tiver implementado no passado :-); é apenas difícil e envolve um monte de código para obtê-lo direito.
  3. Use o .NET perfil de API para capturar todas as chamadas de método e usar isso para descobrir estouros de pilha. Por exemplo, você pode implementar verificações de que, se você encontrar os mesmos tempos método X na sua árvore de chamada, você dá um sinal. Há um projeto aqui que lhe dará uma vantagem inicial.

Gostaria de sugerir a criação de um invólucro em torno XmlWriter objeto, por isso contaria quantidade de chamadas para WriteStartElement / WriteEndElement, e se você limitar quantidade de tags para algum número (fe 100), você seria capaz de lançar uma exceção diferente, por exemplo -. InvalidOperation

Isso deve resolver o problema na maioria dos casos

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));
    }
}

Essa resposta é para @WilliamJockusch.

Eu estou querendo saber se existe uma maneira geral para rastrear StackOverflowExceptions. Em outras palavras, suponha que eu tenho infinita recursão em algum lugar no meu código, mas não tenho idéia de onde. eu quero rastreá-lo por alguns meios de que é mais fácil do que percorrendo o código todo o lugar até que eu vejo isso acontecendo. Eu não me importo como hackish isto é. Por exemplo, Seria ótimo ter um módulo que podia Activar, talvez até de outro segmento, que entrevistou a pilha profundidade e queixou-se ele chegou a um nível I considerado "muito alto". Para exemplo, eu poderia definir "muito alto" para 600 quadros, imaginando que se o pilha eram muito profundo, que tem de ser um problema. É algo assim possível. Outro exemplo seria a de registrar cada chamada de método 1000 dentro do meu código para a saída de depuração. As chances isso iria obter algum evidência da overlow seria muito bom, e ele provavelmente não iria explodir a saída muito mal. A chave é que ele não pode envolver um cheque onde o excesso está acontecendo. Porque a toda problema é que eu não sei onde é. De preferência a solução de não deve depender do que meu ambiente de desenvolvimento aparência semelhante; ou seja, ele não deve assumet que estou usando C # através de um conjunto de ferramentas específicas (por exemplo, VS).

Parece que você está interessado em ouvir algumas técnicas de depuração para capturar este StackOverflow então eu pensei que eu iria partilhar um par para você experimentar.

1. Despejos de memória.

Pro : despejos de memória são uma certeza fogo maneira de trabalho fora a causa de um estouro de pilha. AC # MVP e eu trabalhamos juntos solucionar um SO e ele passou a blog sobre isso aqui .

Este método é o caminho mais rápido para rastrear o problema.

Este método não vai exigir que você para reproduzir problemas seguindo os passos visto em logs.

O Con :. Despejos de memória são muito grandes e você tem que anexar AdPlus / ProcDump o processo

2. Aspecto programação orientada.

Pro : Esta é provavelmente a maneira mais fácil para você implementar o código que verifica o tamanho da pilha de chamadas a partir de qualquer método sem escrever código em cada método de sua aplicação. Há um monte de AOP Frameworks que lhe permitem Intercept antes e após as chamadas.

irá dizer-lhe os métodos que estão causando o estouro de pilha.

Permite verificar o StackTrace().FrameCount na entrada e saída de todos os métodos em sua aplicação.

do Con:. Ela terá um impacto no desempenho - os ganchos são incorporados ao IL para cada método e você não pode realmente "de-ativar"-lo

É um pouco depende do seu desenvolvimento conjunto de ferramentas ambiente.

3. Log da atividade do usuário.

Uma semana atrás eu estava tentando caçar vários rígidos para reproduzir problemas. Eu postei isso QA Logging atividade do usuário, Telemetria (e Variáveis no global Exceção manipuladores) . A conclusão a que cheguei foi realmente um simples usuário-actions-logger para ver como reproduzir problemas em um depurador quando qualquer excepção não ocorre.

Pro : Você pode ligar ou desligar à vontade (ou seja, a subscrição de eventos).

Como acompanhar as ações do usuário não necessita de interceptar cada método.

Você pode contar o número de eventos métodos são subscritas também muito mais simples do que com AOP .

Os arquivos de log são relativamente pequenos e foco em ações que você precisa executar para reproduzir o problema.

pode ajudar você a entender como os usuários estão usando o aplicativo.

O Con :. Não é adequado para um serviço do Windows e estou certo de que há melhores ferramentas como esta para aplicações web

não necessariamente dizer-lhe os métodos que causam o estouro de pilha.

Requer que você passo através de registros reprodução manualmente problemas em vez de um despejo de memória onde você pode obtê-lo e depurá-lo imediatamente.


Talvez você pode tentar todas as técnicas que eu menciono acima e alguns que @atlaste postados e diga-nos quais um de você encontrados foram o mais fácil / rápido / mais suja / mais aceitável para ser executado em um ambiente PROD / etc.

De qualquer forma boa sorte rastrear esse SO.

Se você aplicação depende código 3d-party (em XSL-scripts), então você tem que decidir primeiro quer defender de bugs neles ou não. Se você realmente quer a defender, então eu acho que você deve executar a sua lógica que propenso a erros externos em AppDomains separadas. Catching StackOverflowException não é bom.

Verifique também esta pergunta .

Eu tinha um stackoverflow hoje e eu li alguns de seus posts e decidiu ajudar o lixo para fora Collecter.

Eu costumava ter um loop quase infinita como este:

    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
            }
        }
    }

Ao invés disto deixe o recurso funcionar fora do alcance como esta:

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
}

Ela trabalhou para mim, espero que ajude alguém.

Com o .NET 4.0 Você pode adicionar o atributo HandleProcessCorruptedStateExceptions de System.Runtime.ExceptionServices ao método que contém o bloco try / catch. Isso realmente funcionou! Talvez não recomendado, mas funciona.

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, se eu entendi corretamente a sua preocupação, não é possível (a partir de um ponto de vista matemático) para sempre identificar uma recursão infinita quanto isso significaria para resolver o Travar problema . Para resolvê-lo você precisa de um Super-recursiva algoritmo (como predicados de tentativa e erro por exemplo) ou uma máquina que pode hypercompute (um exemplo é explicado na seção seguinte - disponível como visualização - de este livro ).

Do ponto de vista prático, você tem que saber:

  • Quanto memória de pilha que você deixou no momento dado
  • Quanto pilha de memória que o método recursivo será necessário no momento determinado para a saída específico.

Tenha em mente que, com as máquinas atuais, esses dados é extremamente mutável devido à multitarefa e eu não ouvi de um software que faz a tarefa.

Deixe-me saber se algo não está claro.

Ao que parece, além de iniciar outro processo, não parece haver nenhuma maneira de lidar com uma StackOverflowException. Antes de qualquer outra pergunta, eu tentei usar AppDomain, mas que não funcionou:

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);
                }
            }
        }
    }
}

Se você acabar usando a solução de processo separado, no entanto, eu recomendo usar Process.Exited e Process.StandardOutput e lidar com os erros mesmo, para dar aos seus usuários uma experiência melhor.

Você pode ler-se esta propriedade a cada poucas chamadas, Environment.StackTrace, e se o stacktrace ultrapassado um limite específico que você memorizar, você pode retornar a função.

Você também deve tentar substituir algumas funções recursivas com loops.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top