Debugowanie błędów StackOverflow
StackOverflowException jest zgłaszany, gdy stos wykonywania przepełnia się, ponieważ zawiera zbyt wiele zagnieżdżonych wywołań metod. Bardzo często dzieje się tak, ponieważ metody wywołuje się rekursywnie.
Załóżmy na przykład, że masz aplikację w następujący sposób:
using System;
namespace temp
{
class Program
{
static void Main(string[] args)
{
Main(args); // Oops, this recursion won't stop.
}
}
}
Metoda Main
będzie stale wywoływać sama siebie, dopóki nie będzie więcej miejsca na stosie. Gdy nie ma więcej miejsca na stosie, nie można kontynuować wykonywania, więc zostanie wyrzucony błąd 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>
Gdy zobaczysz, że program zakończy działanie z danymi wyjściowymi w następujący sposób, możesz znaleźć kod źródłowy dla powtarzających się metod i zbadać logikę, która powoduje dużą liczbę wywołań.
Korzystanie z debugera
Często wystarczy zobaczyć ten stos wywołań w powyższej konsoli, aby zidentyfikować problematyczny kod. Jeśli jednak problem nadal nie jest jasny, możesz go debugować dalej.
W tym przykładzie tworzony jest zrzut podstawowy, gdy wystąpi wyjątek StackOverflowException, a następnie ładuje zrzut do lldb (typowy debuger wiersza polecenia systemu Linux) i debuguje go.
Uruchom aplikację, która została skonfigurowana do zbierania zrzutu w przypadku awarii.
> 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
Notatka
.NET 6 standaryzuje prefiks
DOTNET_
zamiastCOMPlus_
dla zmiennych środowiskowych, które konfigurują zachowanie środowiska uruchomieniowego .NET. Jednak prefiksCOMPlus_
będzie nadal działać. Jeśli używasz poprzedniej wersji środowiska uruchomieniowego platformy .NET, nadal należy użyć prefiksuCOMPlus_
dla zmiennych środowiskowych.Zainstaluj rozszerzenie SOS przy użyciu dotnet-sos.
dotnet-sos install
Otwórz zrzut w bazie danych lldb i użyj polecenia
bt
(backtrace), aby wyświetlić stos.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 ...
Górna ramka
0x00007f59b40900cc
jest powtarzana wielokrotnie. Użyj polecenia SOSip2md
, aby dowiedzieć się, jaka metoda zarządzana znajduje się pod adresem0x00007f59b40900cc
.(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
Przejdź do wskazanej metody temp. Program.Main(System.String[]) i źródło "/temp/Program.cs @ 9", aby sprawdzić, czy możesz dowiedzieć się, co kod robi źle. Jeśli potrzebne są dodatkowe informacje, możesz użyć dalszych poleceń debugera lub SOS w celu sprawdzenia procesu.