Stack trace

Last updated

In computing, a stack trace (also called stack backtrace [1] or stack traceback [2] ) is a report of the active stack frames at a certain point in time during the execution of a program. When a program is run, memory is often dynamically allocated in two places: the stack and the heap. Memory is continuously allocated on a stack but not on a heap, thus reflective of their names. Stack also refers to a programming construct, thus to differentiate it, this stack is referred to as the program's function call stack . Technically, once a block of memory has been allocated on the stack, it cannot be easily removed as there can be other blocks of memory that were allocated before it. Each time a function is called in a program, a block of memory called an activation record is allocated on top of the call stack. Generally, the activation record stores the function's arguments and local variables. What exactly it contains and how it's laid out is determined by the calling convention.

Contents

Programmers commonly use stack tracing during interactive and post-mortem debugging. End-users may see a stack trace displayed as part of an error message, which the user can then report to a programmer.

A stack trace allows tracking the sequence of nested functions called - up to the point where the stack trace is generated. In a post-mortem scenario this extends up to the function where the failure occurred (but was not necessarily caused). Sibling calls do not appear in a stack trace.

Language support

Many programming languages, including Java [3] and C#, [4] have built-in support for retrieving the current stack trace via system calls. Before std::stacktrace was added in standard library as a container for std::stacktrace_entry, pre-C++23 has no built-in support for doing this, but C++ users can retrieve stack traces with (for example) the stacktrace library. In JavaScript, exceptions hold a stack property that contain the stack from the place where it was thrown.

Python

As an example, the following Python program contains an error.

defa():i=0j=b(i)returnjdefb(z):k=5ifz==0:c()returnk+zdefc():error()a()

Running the program under the standard Python interpreter produces the following error message.

Traceback (most recent call last):   File "file.py", line 15, in <module>a()   File "file.py", line 3, in aj=b(i)   File "file.py", line 9, in bc()   File "file.py", line 13, in cerror()NameError: name 'error' is not defined

The stack trace shows where the error occurs, namely in the c function. It also shows that the c function was called by b, which was called by a, which was in turn called by the code on line 15 (the last line) of the program. The activation records for each of these three functions would be arranged in a stack such that the a function would occupy the bottom of the stack and the c function would occupy the top of the stack.

Java

In Java, stack traces can be dumped manually with Thread.dumpStack() [5] Take the following input:

publicclassMain{publicstaticvoidmain(Stringargs[]){demo();}staticvoiddemo(){demo1();}staticvoiddemo1(){demo2();}staticvoiddemo2(){demo3();}staticvoiddemo3(){Thread.dumpStack();}}

The exception lists functions in descending order, so the most-inner call is first.

java.lang.Exception:Stacktraceatjava.lang.Thread.dumpStack(Thread.java:1336)atMain.demo3(Main.java:15)atMain.demo2(Main.java:12)atMain.demo1(Main.java:9)atMain.demo(Main.java:6)atMain.main(Main.java:3)

C and C++

Both C and C++ (pre-C++23) do not have native support for obtaining stack traces, but libraries such as glibc and boost provide this functionality. [6] [7] In these languages, some compiler optimizations may interfere with the call stack information that can be recovered at runtime. For instance, inlining can cause missing stack frames, tail call optimizations can replace one stack frame with another, and frame pointer elimination can prevent call stack analysis tools from correctly interpreting the contents of the call stack. [6]

For example, glibc's backtrace() function returns an output with the program function and memory address.

./a.out()[0x40067f]./a.out()[0x4006fe]./a.out()[0x40070a]/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5)[0x7f7e60738f45]./a.out()[0x400599]

As of C++23, stack traces can be dumped manually by printing the value returned by static member function std::stacktrace::current(): [8]

std::cout<<std::stacktrace::current()<<'\n';

Rust

Rust has two types of errors. Functions that use the panic macro are "unrecoverable" and the current thread will become poisoned experiencing stack unwinding. Functions that return a std::result::Result are "recoverable" and can be handled gracefully. [9] However, recoverable errors cannot generate a stack trace as they are manually added and not a result of a runtime error.

As of June 2021, Rust has experimental support for stack traces on unrecoverable errors. Rust supports printing to stderr when a thread panics, but it must be enabled by setting the RUST_BACKTRACE environment variable. [10]

When enabled, such backtraces look similar to below, with the most recent call first.

thread'main'panickedat'execute_to_panic',main.rs:3stackbacktrace:    0: std::sys::imp::backtrace::tracing::imp::unwind_backtrace1: std::panicking::default_hook::{{closure}}2: std::panicking::default_hook3: std::panicking::rust_panic_with_hook4: std::panicking::begin_panic5: futures::task_impl::with6: futures::task_impl::park...

See also

Related Research Articles

In computing, a segmentation fault or access violation is a fault, or failure condition, raised by hardware with memory protection, notifying an operating system (OS) the software has attempted to access a restricted area of memory. On standard x86 computers, this is a form of general protection fault. The operating system kernel will, in response, usually perform some corrective action, generally passing the fault on to the offending process by sending the process a signal. Processes can in some cases install a custom signal handler, allowing them to recover on their own, but otherwise the OS default signal handler is used, generally causing abnormal termination of the process, and sometimes a core dump.

In computer programming, the scope of a name binding is the part of a program where the name binding is valid; that is, where the name can be used to refer to the entity. In other parts of the program, the name may refer to a different entity, or to nothing at all. Scope helps prevent name collisions by allowing the same name to refer to different objects – as long as the names have separate scopes. The scope of a name binding is also known as the visibility of an entity, particularly in older or more technical literature—this is in relation to the referenced entity, not the referencing name.

<span class="mw-page-title-main">D (programming language)</span> Multi-paradigm system programming language

D, also known as dlang, is a multi-paradigm system programming language created by Walter Bright at Digital Mars and released in 2001. Andrei Alexandrescu joined the design and development effort in 2007. Though it originated as a re-engineering of C++, D is now a very different language. As it has developed, it has drawn inspiration from other high-level programming languages. Notably, it has been influenced by Java, Python, Ruby, C#, and Eiffel.

The C standard library, sometimes referred to as libc, is the standard library for the C programming language, as specified in the ISO C standard. Starting from the original ANSI C standard, it was developed at the same time as the C library POSIX specification, which is a superset of it. Since ANSI C was adopted by the International Organization for Standardization, the C standard library is also called the ISO C library.

Resource acquisition is initialization (RAII) is a programming idiom used in several object-oriented, statically typed programming languages to describe a particular language behavior. In RAII, holding a resource is a class invariant, and is tied to object lifetime. Resource allocation is done during object creation, by the constructor, while resource deallocation (release) is done during object destruction, by the destructor. In other words, resource acquisition must succeed for initialization to succeed. Thus the resource is guaranteed to be held between when initialization finishes and finalization starts, and to be held only when the object is alive. Thus if there are no object leaks, there are no resource leaks.

In compiler construction, name mangling is a technique used to solve various problems caused by the need to resolve unique names for programming entities in many modern programming languages.

In computer programming, an entry point is the place in a program where the execution of a program begins, and where the program has access to command line arguments.

In mathematics and in computer programming, a variadic function is a function of indefinite arity, i.e., one which accepts a variable number of arguments. Support for variadic functions differs widely among programming languages.

In computer programming, thread-local storage (TLS) is a memory management method that uses static or global memory local to a thread. The concept allows storage of data that appears to be global in a system with separate threads.

Exception handling syntax is the set of keywords and/or structures provided by a computer programming language to allow exception handling, which separates the handling of errors that arise during a program's operation from its ordinary processes. Syntax for exception handling varies between programming languages, partly to cover semantic differences but largely to fit into each language's overall syntactic structure. Some languages do not call the relevant concept "exception handling"; others may not have direct facilities for it, but can still provide means to implement it.

A foreign function interface (FFI) is a mechanism by which a program written in one programming language can call routines or make use of services written or compiled in another one. An FFI is often used in contexts where calls are made into a binary dynamic-link library.

In compiler optimization, escape analysis is a method for determining the dynamic scope of pointers – where in the program a pointer can be accessed. It is related to pointer analysis and shape analysis.

setjmp.h is a header defined in the C standard library to provide "non-local jumps": control flow that deviates from the usual subroutine call and return sequence. The complementary functions setjmp and longjmp provide this functionality.

In computing, the producer-consumer problem is a family of problems described by Edsger W. Dijkstra since 1965.

In programming languages, particularly the compiled ones like C, C++, and D, linkage describes how names can or can not refer to the same entity throughout the whole program or one single translation unit.

mtrace is the memory debugger included in the GNU C Library.

This article compares a large number of programming languages by tabulating their data types, their expression, statement, and declaration syntax, and some common operating-system interfaces.

In the C++ programming language, placement syntax allows programmers to explicitly specify the memory management of individual objects — i.e. their "placement" in memory. Normally, when an object is created dynamically, an allocation function is invoked in such a way that it will both allocate memory for the object, and initialize the object within the newly allocated memory. The placement syntax allows the programmer to supply additional arguments to the allocation function. A common use is to supply a pointer to a suitable region of storage where the object can be initialized, thus separating memory allocation from object construction.

A code sanitizer is a programming tool that detects bugs in the form of undefined or suspicious behavior by a compiler inserting instrumentation code at runtime. The class of tools was first introduced by Google's AddressSanitizer of 2012, which uses directly mapped shadow memory to detect memory corruption such as buffer overflows or accesses to a dangling pointer (use-after-free).

<span class="mw-page-title-main">Zig (programming language)</span> A general-purpose programming language, toolchain to build Zig/C/C++ code

Zig is an imperative, general-purpose, statically typed, compiled system programming language designed by Andrew Kelley. It is intended as a successor to the language C, with the intent of being even smaller and simpler to program in, while offering more functionality. It is free and open-source software, released under an MIT License.

References

  1. "libc manual: backtraces". gnu.org. Retrieved 8 July 2014.
  2. "traceback — Print or retrieve a stack traceback". python.org. Retrieved 8 July 2014.
  3. "Thread (Java SE 16 & JDK 16)". Java Platform Standard Edition & Java Development Kit Version 16 API Specification. 2021-03-04. Retrieved 2021-07-04.
  4. "Environment.StackTrace Property (System)". Microsoft Docs. 2021-05-07. Retrieved 2021-07-04.
  5. "Thread (Java Platform SE 8 )". docs.oracle.com. Retrieved 2021-06-15.
  6. 1 2 "Backtraces (The GNU C Library)". www.gnu.org. Retrieved 2021-06-15.
  7. "Getting Started - 1.76.0". www.boost.org. Retrieved 2021-06-15.
  8. "Working Draft, Standard for Programming Language C++" (PDF). open-std.org. ISO/IEC. 2021-10-23. p. 766.
  9. "rustonomicon unwinding - Rust". doc.rust-lang.org.
  10. "std::backtrace - Rust". doc.rust-lang.org. Retrieved 2021-06-15.