Dela via


Felsöka StackOverflow-fel

En StackOverflowException genereras när körningsstacken översvämmas eftersom den innehåller alltför många kapslade metodanrop. Detta beror ofta på att metoder anropar varandra rekursivt.

Anta till exempel att du har en app på följande sätt:

using System;

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

Metoden Main anropar sig själv kontinuerligt tills det inte finns mer stackutrymme. När det inte finns mer stackutrymme kan körningen inte fortsätta och därför genererar den en 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>

När du ser att programmet avslutas med utdata som detta kan du hitta källkoden för de upprepade metoderna och undersöka logiken som orsakar det stora antalet anrop.

Använda felsökningsprogrammet

Ofta räcker det med att bara se den här anropsstacken i konsolen ovan för att identifiera den problematiska koden. Men om problemet fortfarande är oklart kan du felsöka ytterligare.

Det här exemplet skapar en kärndump när StackOverflowException inträffar och läser sedan in dumpen i lldb- (ett vanligt Linux-kommandoradsfelsökare) och felsöker den.

  1. Kör appen med den konfigurerad för att samla in en dump vid krasch.

    > 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
    

    Not

    .NET 6 standardiserar på prefixet DOTNET_ i stället för COMPlus_ för miljövariabler som konfigurerar .NET-körningsbeteende. Prefixet COMPlus_ fortsätter dock att fungera. Om du använder en tidigare version av .NET-körningen bör du fortfarande använda COMPlus_-prefixet för miljövariabler.

  2. Installera SOS-tillägget med dotnet-sos.

    dotnet-sos install
    
  3. Öppna dumpen i lldb och använd kommandot bt (backtrace) för att visa stacken.

    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. Den övre ramen 0x00007f59b40900cc upprepas flera gånger. Använd kommandot SOSip2md för att ta reda på vilken hanterad metod som finns på 0x00007f59b40900cc-adressen.

    (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. Gå och titta på angiven metod temp. Program.Main(System.String[]) och källan "/temp/Program.cs @ 9" för att se om du kan ta reda på vad koden gör fel. Om ytterligare information behövs kan du använda ytterligare felsökningsprogram eller SOS- kommandon för att inspektera processen.