- Exception. Stack Trace Свойство
- Определение
- Значение свойства
- Реализации
- Примеры
- Комментарии
- Примечания для тех, кто наследует этот метод
- How do I find the stack trace in Visual Studio?
- 7 Answers 7
- ExceptionHandler stacktrace on Windows
- Atomic Spin
- Atomic Object’s blog on everything we find fascinating.
- Catching Exceptions and Printing Stack Traces for C on Windows, Linux, & Mac
- A Troubled Program
- Just Catching Exceptions
- Things to Note
- Catching Exceptions in Posix
- Stack Traces in Posix
- Catching Exceptions in Windows (MinGW)
- Stack Traces in Windows (MinGW)
- Does it work!?
- Что такое stack trace, и как с его помощью находить ошибки при разработке приложений?
- 1 ответ 1
Exception. Stack Trace Свойство
Определение
Получает строковое представление непосредственных кадров в стеке вызова. Gets a string representation of the immediate frames on the call stack.
Значение свойства
Строка, описывающая непосредственные фреймы стека вызова. A string that describes the immediate frames of the call stack.
Реализации
Примеры
Следующий пример кода создает исключение, Exception а затем перехватывает его и отображает трассировку стека с помощью StackTrace Свойства. The following code example throws an Exception and then catches it and displays a stack trace using the StackTrace property.
Комментарии
В стеке выполнения отслеживаются все методы, выполняемые в данный момент времени. The execution stack keeps track of all the methods that are in execution at a given instant. Трассировка вызовов метода называется трассировкой стека. A trace of the method calls is called a stack trace. Список трассировки стека позволяет отслеживать стек вызовов до номера строки в методе, где возникает исключение. The stack trace listing provides a way to follow the call stack to the line number in the method where the exception occurs.
StackTraceСвойство возвращает кадры стека вызовов, которые происходят в том месте, где было создано исключение. The StackTrace property returns the frames of the call stack that originate at the location where the exception was thrown. Сведения о дополнительных кадрах в стеке вызовов можно получить, создав новый экземпляр System.Diagnostics.StackTrace класса и используя его StackTrace.ToString метод. You can obtain information about additional frames in the call stack by creating a new instance of the System.Diagnostics.StackTrace class and using its StackTrace.ToString method.
Среда CLR обновляет трассировку стека всякий раз, когда в коде приложения создается исключение (с помощью throw ключевого слова). The common language runtime (CLR) updates the stack trace whenever an exception is thrown in application code (by using the throw keyword). Если исключение было повторно создано в методе, отличном от метода, в котором он был изначально создан, трассировка стека содержит как расположение в методе, где было создано исключение, так и место в методе, где исключение было создано повторно. If the exception was rethrown in a method that is different than the method where it was originally thrown, the stack trace contains both the location in the method where the exception was originally thrown, and the location in the method where the exception was rethrown. Если возникло исключение, а позднее повторно выбрасывается в том же методе, трассировка стека содержит только расположение, в котором было повторно создано исключение, и не включает место первоначального возникновения исключения. If the exception is thrown, and later rethrown, in the same method, the stack trace only contains the location where the exception was rethrown and does not include the location where the exception was originally thrown.
StackTraceСвойство может не сообщать о количестве вызовов методов, ожидаемых из-за преобразований кода, таких как встраивание, которые происходят во время оптимизации. The StackTrace property may not report as many method calls as expected because of code transformations, such as inlining, that occur during optimization.
Примечания для тех, кто наследует этот метод
StackTrace Свойство переопределяется в классах, требующих управления содержимым или форматом трассировки стека. The StackTrace property is overridden in classes that require control over the stack trace content or format.
По умолчанию трассировка стека записывается непосредственно перед созданием объекта исключения. By default, the stack trace is captured immediately before an exception object is thrown. Используется StackTrace для получения сведений о трассировке стека, когда исключение не создается. Use StackTrace to get stack trace information when no exception is being thrown.
How do I find the stack trace in Visual Studio?
I ask because I couldn’t find the stack trace in Visual Studio, while debugging an exception that occurred.
7 Answers 7
While debugging, Go to Debug -> Windows -> Call Stack
While debugging, when you hit a break-point.
The default shortcut key is Ctrl-Alt-C.
Do you mean finding a stack trace of the thrown exception location? That’s either Debug/Exceptions, or better — Ctrl-Alt-E. Set filters for the exceptions you want to break on.
There’s even a way to reconstruct the thrower stack after the exception was caught, but it’s really unpleasant. Much, much easier to set a break on the throw.
Using the Call Stack Window
To open the Call Stack window in Visual Studio, from the Debug menu, choose Windows>Call Stack. To set the local context to a particular row in the stack trace display, double click the first column of the row.
Consider this as the current update (Windows 10 (Version 1803) and Visual Studio 2017): I was unable to view the stack trace window and did find an option/menu item to view it. On investigating further, it seems this feature is not available on Windows 10. For further information please refer:
Copied from the above link: «This feature is not available in Windows 10, version 1507 and later versions of the WDK.»
For Visual Studio 2019, the shortcut (while debugging and stopped at a breakpoint) is:
Ctrl + Alt + C and now you can also use Ctrl + L
The screenshot is pretty old. Here is one for Visual Studio 2019 (under the debug menu):
ExceptionHandler stacktrace on Windows
So, I see this line in one of the callstacks of a dump (x86/Windows 7) I am looking at. I have heavily snipped it as my question is pertaining only to this call.
It looks like the first two arguments on the stack must be the context record and the exception record. However with some experimentation I noted that 01111111 is not the first argument. It actually starts from 04444444 . So, in this case .cxr 04444444 and .exr 05555555 display sane output.
Now, I understand the debugger might just show whatever is on the stack at that point and I shouldn’t probably rely on that alone but if everything below that line is marked with «stack unwind information not available. Following frames may be wrong», how am I to know that I should ignore 01111111?
UPDATE
Lets try a different approach. Here are 2 relevant pieces of information:
So, check this out. The lone parameter to UnhandledExceptionFilter is an EXCEPTION_POINTERS* structure, right?
In the EXCEPTION_POINTERS struct, the first field is the EXCEPTION_RECORD (0bbbbbbb) while the second is the CONTEXT_RECORD (0aaaaaaa). However in the call to KiUserEceptionDispatcher, these arguments appear in the opposite way. Not to mention the fact 0eeeeeee is NOT the first parameter to KiUserExceptionDispatcher but 0ccccccc IS the first parameter to UnhandledExceptionFilter.
Atomic Spin
Atomic Object’s blog on everything we find fascinating.
Catching Exceptions and Printing Stack Traces for C on Windows, Linux, & Mac
Debugging C can be a real pain, especially when all you have to go by is that it was a segfault. Great!
In an effort to make testing C code a little less painful, I’ve recently added stack trace support to Unity (for gcc on Windows or Posix systems). That way, when a test crashes, I will at least know where it crashed. I learned quite a bit in the process and thought I’d share.
A Troubled Program
First, let’s start with a test program that will emit some of the signals we want to catch. It is entirely self contained except for a set_signal_handler() that we will define separately for windows and Posix systems, with a slight variation on the Posix version for OS X.
The test program:
Just Catching Exceptions
There is basically no portable way to get stack traces. However, if you’re just interested in catching the exceptions and not doing much more that printing an error message, there is a semi-portable c99 solution.
Here’s a simple implementation:
You can paste this into our test program and compile it with the usual: gcc main.c . If you need anything more than that, you’re going to have to stick with something specific for your platform.
This being C, there are of course loads of caveats. The C99 standard guarantees basically nothing about the signals. The only mechanism for invoking your signal handlers that is guarantee to work is calling raise() . (Note, the abort() function must call raise(SIGABRT) , and assert() must call abort() .)
If your signal occurs as a result of an abort() or raise() , then your code in your signal handler is just as safe as normal C code is…
If your signal occurs in any other way, the only interactions with the outside world guaranteed to be safe inside your signal handler are writes to values of type sig_atomic_t and calls to _Exit() .
This pretty much means the only “guaranteed safe” thing you can do is change the exit code. However, most implementations let you get away with quite a bit more.
Things to Note
Even though SIGFPE stands for “Signal: Floating Point Exception,” this signal is generated on pretty much any arithmetic exception (floating point or integer).
Don’t expect to be able to catch stack overflows. Often the signal handlers are invoked on the same stack that caused the signal to occur. So when a stack overflow occurs, your signal handler is called immediately, causing another stack overflow, and your program just dies with a segfault. On Posix systems there is a way around this.
For more details, see section 7.14 (page 246) of the C99 standard.
Catching Exceptions in Posix
One of the nice things about the Posix signal handler is that we can define an alternate signal stack that our signal handler will use. This allows us to catch and handle things like a stack overflow that would normally kill our handler instantly.
Unfortunately, we have to disable the signal stack on OS X, or backtrace() won’t work. Apparently linux does some magic that lets backtrace still look at the right stack when called from the signal handler that OS X lacks. Turning off the signal stack lets us get our stack traces, but it means our signal handler will fail for stack overflows. We’ll just get a normal segfault. I don’t know how to fix this. If you know a way around this please let me know. Most other errors should still be caught just fine, though.
Stack Traces in Posix
The backtrace() function is the preferred method of getting a stack trace on Posix systems. However, as you’ll see in a second, we’ll need a little extra processing to get to line numbers.
Something like the following works nicely:
#include #include /* Resolve symbol name and source location given the path to the executable and an address */ int addr2line(char const * const program_name, void const * const addr) < char addr2line_cmd[512] = <0>; /* have addr2line map the address to the relent line in the code */ #ifdef __APPLE__ /* apple does things differently. */ sprintf(addr2line_cmd,»atos -o %.256s %p», program_name, addr); #else sprintf(addr2line_cmd,»addr2line -f -p -e %.256s %p», program_name, addr); #endif /* This will print a nicely formatted string specifying the function and source line of the address */ return system(addr2line_cmd); >
You can compile all this with gcc -g main.c in linux, but you’ll need gcc -g -fno-pie main.c in OS X. (Pie does address randomizing to prevent code injection attacks. It also happens to prevent us from resolving addresses.)
Note that the addresses from stack traces are the return addresses. These lines are actually where the functions are going to return to — not where they are called from! Often it’s just the line after where they are called.
Catching Exceptions in Windows (MinGW)
As usual, there is the way everyone else does things, and the way Windows does things. In Windows, you can catch exceptions using the SetUnhandledExceptionFilter() function like so:
The windows_print_stacktrace() is defined in the next section.
Stack Traces in Windows (MinGW)
With MinGW, we can use the same addr2line() function that we defined earlier once we have an address:
#include #include void windows_print_stacktrace(CONTEXT* context) < SymInitialize(GetCurrentProcess(), 0, true); STACKFRAME frame = < 0 >; /* setup initial stack frame */ frame.AddrPC.Offset = context->Eip; frame.AddrPC.Mode = AddrModeFlat; frame.AddrStack.Offset = context->Esp; frame.AddrStack.Mode = AddrModeFlat; frame.AddrFrame.Offset = context->Ebp; frame.AddrFrame.Mode = AddrModeFlat; while (StackWalk(IMAGE_FILE_MACHINE_I386 , GetCurrentProcess(), GetCurrentThread(), &frame, context, 0, SymFunctionTableAccess, SymGetModuleBase, 0 ) ) < addr2line(icky_global_program_name, (void*)frame.AddrPC.Offset); >SymCleanup( GetCurrentProcess() ); >
You can compile the windows version with gcc -g main.c -limagehlp . And you can use the same addr2line() as with the Posix examples. Yay MinGW!
You can also get this to work with the Visual C compiler if you use the SymGetSymFromAddr64() function instead of the addr2line utility as demonstrated here. That post is about trying to get stack traces to work in MinGW, but the code should work in Visual Studio.
If you want to print out a stack trace without having an exception, you’ll have to get the local context with the RtlCaptureContext() function.
I also imagine that if you’re using cygwin, you can just stick with the Posix versions of everything, and things should just work. But I haven’t tried it.
Does it work!?
Sample output on Windows when I uncomment cause_segfault() :
Error: EXCEPTION_ACCESS_VIOLATION
cause_segfault at Z:\Projects\stack_traces/c_signal.c:428
cause_calamity at Z:\Projects\\stack_traces/c_signal.c:414
main at Z:\Projects\stack_traces/c_signal.c:398
?? at crt1.c:0
??
. 0
??
. 0
A compete collection of all the variants in a single .c file that can be built on Window, Linux and OS X is here.
I hope this saves someone else a few hours of time. Happy hacking 🙂
Что такое stack trace, и как с его помощью находить ошибки при разработке приложений?
Иногда при запуске своего приложения я получаю подобную ошибку:
Мне сказали, что это называется «трассировкой стека» или «stack trace». Что такое трассировка? Какую полезную информацию об ошибке в разрабатываемой программе она содержит?
Немного по существу: довольно часто я вижу вопросы, в которых начинающие разработчики, получая ошибку, просто берут трассировки стека и какой-либо случайный фрагмент кода без понимания, что собой представляет трассировка и как с ней работать. Данный вопрос предназначен специально для начинающих разработчиков, которым может понадобиться помощь в понимании ценности трассировки стека вызовов.
1 ответ 1
Простыми словами, трассировка стека – это список методов, которые были вызваны до момента, когда в приложении произошло исключение.
Простой случай
В указанном примере мы можем точно определить, когда именно произошло исключение. Рассмотрим трассировку стека:
Это пример очень простой трассировки. Если пойти по списку строк вида «at…» с самого начала, мы можем понять, где произошла ошибка. Мы смотрим на верхний вызов функции. В нашем случае, это:
Для отладки этого фрагмента открываем Book.java и смотрим, что находится на строке 16 :
Это означает то, что в приведенном фрагменте кода какая-то переменная (вероятно, title ) имеет значение null .
Пример цепочки исключений
Иногда приложения перехватывают исключение и выбрасывают его в виде другого исключения. Обычно это выглядит так:
Трассировка в этом случае может иметь следующий вид:
В этом случае разница состоит в атрибуте «Caused by» («Чем вызвано»). Иногда исключения могут иметь несколько секций «Caused by». Обычно необходимо найти исходную причину, которой оказывается в самой последней (нижней) секции «Caused by» трассировки. В нашем случае, это:
Аналогично, при подобном исключении необходимо обратиться к строке 22 книги Book.java , чтобы узнать, что вызвало данное исключение – NullPointerException .
Еще один пугающий пример с библиотечным кодом
Как правило, трассировка имеет гораздо более сложный вид, чем в рассмотренных выше случаях. Приведу пример (длинная трассировка, демонстрирующая несколько уровней цепочек исключений):
В этом примере приведен далеко не полный стек вызовов. Что вызывает здесь наибольший интерес, так это поиск функций из нашего кода – из пакета com.example.myproject . В предыдущем примере мы сначала хотели отыскать «первопричину», а именно:
Однако все вызовы методов в данном случае относятся к библиотечному коду. Поэтому мы перейдем к предыдущей секции «Caused by» и найдем первый вызов метода из нашего кода, а именно: