Depurar erros de StackOverflow
Um StackOverflowException é lançado quando uma pilha de execução transborda porque contém muitas chamadas a métodos aninhados. Muitas vezes isso ocorre porque os métodos estão chamando uns aos outros recursivamente.
Por exemplo, suponha que você tenha um aplicativo da seguinte maneira:
using System;
namespace temp
{
class Program
{
static void Main(string[] args)
{
Main(args); // Oops, this recursion won't stop.
}
}
}
O método Main
irá chamar-se continuamente até que o espaço da pilha se esgote. Uma vez que não há mais espaço de pilha, a execução não pode continuar e, portanto, gerará um StackOverflowException.
> dotnet run
Stack overflow.
at temp.Program.Main(System.String[])
at temp.Program.Main(System.String[])
at temp.Program.Main(System.String[])
at temp.Program.Main(System.String[])
at temp.Program.Main(System.String[])
at temp.Program.Main(System.String[])
<this output repeats many more times>
Quando você vê a saída do programa com saída como esta, você pode encontrar o código-fonte para o(s) método(s) de repetição e investigar a lógica que causa o grande número de chamadas.
Usando o depurador
Muitas vezes, apenas ver essa pilha de chamadas no console acima é suficiente para identificar o código problemático. No entanto, se o problema ainda não estiver claro, você pode depurar mais.
Este exemplo cria um dump principal quando o StackOverflowException ocorre e, em seguida, carrega o dump em lldb (um depurador de linha de comando comum do Linux) e o depura.
Execute a aplicação configurada para coletar um registo de falha ao ocorrer uma falha.
> export DOTNET_DbgEnableMiniDump=1 > dotnet run Stack overflow. Writing minidump with heap to file /tmp/coredump.6412 Written 58191872 bytes (14207 pages) to core file
Observação
O .NET 6 padroniza o prefixo
DOTNET_
em vez deCOMPlus_
para variáveis de ambiente que configuram o comportamento em tempo de execução do .NET. No entanto, o prefixoCOMPlus_
continuará a funcionar. Se você estiver usando uma versão anterior do tempo de execução do .NET, ainda deverá usar o prefixoCOMPlus_
para variáveis de ambiente.Instale a extensão SOS usando dotnet-sos.
dotnet-sos install
Abra o dump em lldb e use o comando
bt
(backtrace) para exibir a pilha.lldb --core /temp/coredump.6412 (lldb) bt ... frame #261930: 0x00007f59b40900cc frame #261931: 0x00007f59b40900cc frame #261932: 0x00007f59b40900cc frame #261933: 0x00007f59b40900cc frame #261934: 0x00007f59b40900cc frame #261935: 0x00007f5a2d4a080f libcoreclr.so`CallDescrWorkerInternal at unixasmmacrosamd64.inc:867 frame #261936: 0x00007f5a2d3cc4c3 libcoreclr.so`MethodDescCallSite::CallTargetWorker(unsigned long const*, unsigned long*, int) at callhelpers.cpp:70 frame #261937: 0x00007f5a2d3cc468 libcoreclr.so`MethodDescCallSite::CallTargetWorker(this=<unavailable>, pArguments=0x00007ffe8222e7b0, pReturnValue=0x0000000000000000, cbReturnValue=0) at callhelpers.cpp:604 frame #261938: 0x00007f5a2d4b6182 libcoreclr.so`RunMain(MethodDesc*, short, int*, PtrArray**) [inlined] MethodDescCallSite::Call(this=<unavailable>, pArguments=<unavailable>) at callhelpers.h:468 ...
A
0x00007f59b40900cc
do quadro superior é repetida várias vezes. Use o comando SOSip2md
para descobrir qual método gerenciado está localizado no endereço0x00007f59b40900cc
.(lldb) ip2md 0x00007f59b40900cc MethodDesc: 00007f59b413ffa8 Method Name: temp.Program.Main(System.String[]) Class: 00007f59b4181d40 MethodTable: 00007f59b4190020 mdToken: 0000000006000001 Module: 00007f59b413dbf8 IsJitted: yes Current CodeAddr: 00007f59b40900a0 Version History: ILCodeVersion: 0000000000000000 ReJIT ID: 0 IL Addr: 0000000000000000 CodeAddr: 00007f59b40900a0 (MinOptJitted) NativeCodeVersion: 0000000000000000 Source file: /temp/Program.cs @ 9
Vá olhar para o método indicado temp. Program.Main(System.String[]) e source "/temp/Program.cs @ 9" para ver se você consegue descobrir o que o código está fazendo de errado. Se forem necessárias informações adicionais, pode-se usar comandos adicionais de depuração ou SOS para inspecionar o processo.