Compartir vía


Depuración de errores de StackOverflow

Se produce una excepción StackOverflowException cuando la pila de ejecución se desborda debido a que contiene demasiadas llamadas a métodos anidadas. A menudo esto ocurre porque los métodos se llaman entre sí de forma recursiva.

Por ejemplo, supongamos que tiene una aplicación de la siguiente manera:

using System;

namespace temp
{
    class Program
    {
        static void Main(string[] args)
        {
            Main(args); // Oops, this recursion won't stop.
        }
    }
}

El método Main se auto-llamará de forma recursiva hasta que no haya más espacio en la memoria de pila. Cuando ya no haya más espacio de pila, no se podrá continuar con la ejecución y, por tanto, se producirá una excepción 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>

Cuando vea que el programa finaliza con una salida como esta, puede encontrar el código fuente de los métodos que se repiten e investigar la lógica que provoca el gran número de llamadas.

Empleo del depurador

A menudo, simplemente ver esta traza de llamadas en la consola mencionada arriba es suficiente para identificar el código problemático. Sin embargo, si el problema sigue sin estar claro, puede continuar depurando.

En este ejemplo, se crea un volcado de núcleo cuando ocurre la StackOverflowException y, a continuación, se carga el volcado en lldb (un depurador común de línea de comandos de Linux) y lo depura.

  1. Ejecute la aplicación con la configuración para recopilar un volcado de memoria durante un bloqueo.

    > 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
    

    Nota

    .NET 6 normaliza el prefijo DOTNET_ en lugar de COMPlus_ para las variables de entorno que configuran el comportamiento en tiempo de ejecución de .NET. Sin embargo, el prefijo COMPlus_ seguirá funcionando. Si usa una versión anterior del entorno de ejecución de .NET, debe seguir usando el prefijo COMPlus_ para las variables de entorno.

  2. Instale la extensión SOS mediante dotnet-sos.

    dotnet-sos install
    
  3. Abra el volcado de memoria en lldb y use el comando bt (backtrace) para mostrar la pila.

    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
    ...
    
  4. El marco superior 0x00007f59b40900cc se repite varias veces. Use el comando SOSip2md para averiguar qué método administrado se encuentra en la dirección 0x00007f59b40900cc.

    (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
    
  5. Vaya a ver el método indicado temp.Program.Main(System.String[]) y el código fuente "/temp/Program.cs @ 9" para ver si puede determinar qué está mal en el código. Si se necesita información adicional, puede utilizar comandos adicionales del depurador o de SOS para inspeccionar el proceso.