Stack traces: Difference between revisions
PascalABC.NET |
|||
(105 intermediate revisions by 57 users not shown) | |||
Line 2: | Line 2: | ||
Many programming languages allow for introspection of the current call stack environment. This can be for a variety of purposes such as enforcing security checks, debugging, or for getting access to the stack frame of callers. |
Many programming languages allow for introspection of the current call stack environment. This can be for a variety of purposes such as enforcing security checks, debugging, or for getting access to the stack frame of callers. |
||
This task calls for you to print out (in a manner considered suitable for the platform) the current call stack. The amount of information printed for each frame on the call stack is not constrained, but should include at least the name of the function or method at that level of the stack frame. You may explicitly add a call to produce the stack trace to the (example) code being instrumented for examination. |
|||
;Task: |
|||
The task should allow the program to continue after generating the stack trace. The task report here must include the trace from a sample program. |
|||
Print out (in a manner considered suitable for the platform) the current call stack. |
|||
<br clear=all> |
|||
The amount of information printed for each frame on the call stack is not constrained, but should include at least the name of the function or method at that level of the stack frame. |
|||
You may explicitly add a call to produce the stack trace to the (example) code being instrumented for examination. |
|||
The task should allow the program to continue after generating the stack trace. |
|||
The task report here must include the trace from a sample program. |
|||
<br><br> |
|||
=={{header|Ada}}== |
=={{header|Ada}}== |
||
{{works with|GNAT}} |
{{works with|GNAT}} |
||
The provided solution is specific to the [[GNAT]] Ada compiler. Further it is restricted to some platforms. See the description of the package GNAT.Traceback supplied with [[GNAT]]. The switch -g must be used in order to include debug information into the executable. |
The provided solution is specific to the [[GNAT]] Ada compiler. Further it is restricted to some platforms. See the description of the package GNAT.Traceback supplied with [[GNAT]]. The switch -g must be used in order to include debug information into the executable. |
||
<lang |
<syntaxhighlight lang="ada">with Ada.Text_IO; use Ada.Text_IO; |
||
with |
with GNAT.Traceback; |
||
with |
with GNAT.Traceback.Symbolic; |
||
procedure Test_Stack_Trace is |
procedure Test_Stack_Trace is |
||
procedure Call_Stack is |
procedure Call_Stack is |
||
Trace : Tracebacks_Array (1..1_000); |
Trace : GNAT.Traceback.Tracebacks_Array (1..1_000); |
||
Length : Natural; |
Length : Natural; |
||
begin |
begin |
||
Call_Chain (Trace, Length); |
GNAT.Traceback.Call_Chain (Trace, Length); |
||
Put_Line (Symbolic_Traceback (Trace (1..Length))); |
Put_Line (GNAT.Traceback.Symbolic.Symbolic_Traceback (Trace (1..Length))); |
||
end Call_Stack; |
end Call_Stack; |
||
Line 40: | Line 49: | ||
begin |
begin |
||
Outer (2,3,5); |
Outer (2,3,5); |
||
end Test_Stack_Trace;</ |
end Test_Stack_Trace;</syntaxhighlight> |
||
Sample output: |
Sample output: |
||
<pre> |
<pre> |
||
Line 54: | Line 63: | ||
7C817075 in ?? at ??:0 |
7C817075 in ?? at ??:0 |
||
</pre> |
</pre> |
||
=={{header|AutoHotkey}}== |
=={{header|AutoHotkey}}== |
||
{{incorrect|AutoHotkey|The output part of the task is missing; it is ''required'' to show what output is produced by the trace.}} |
|||
The local, static, and global bindings are viewable using 'Listvars' |
The local, static, and global bindings are viewable using 'Listvars' |
||
<br> recently executed lines are available through ListLines |
<br> recently executed lines are available through ListLines |
||
Line 61: | Line 70: | ||
ListLines can be turned on or off... with: |
ListLines can be turned on or off... with: |
||
<br> ListLines, On|Off |
<br> ListLines, On|Off |
||
< |
<syntaxhighlight lang="autohotkey">f() |
||
return |
return |
||
Line 77: | Line 86: | ||
msgbox, variable bindings |
msgbox, variable bindings |
||
} |
} |
||
#Persistent</ |
#Persistent</syntaxhighlight> |
||
Output: |
|||
<syntaxhighlight lang="autohotkey">001: f() |
|||
006: Return,g() |
|||
011: ListLines (0.05) |
|||
012: MsgBox,lines recently executed (3.81) |
|||
013: x = local to g |
|||
014: ListVars |
|||
015: MsgBox,variable bindings (3.94) |
|||
016: } |
|||
002: Return (181.66) |
|||
Global Variables (alphabetical) |
|||
-------------------------------------------------- |
|||
0[1 of 3]: 0 |
|||
ErrorLevel[1 of 3]: 0 |
|||
</syntaxhighlight> |
|||
=={{header|BASIC}}== |
=={{header|BASIC}}== |
||
Line 149: | Line 174: | ||
=={{header|C}}== |
=={{header|C}}== |
||
=== Using GNU extensions === |
|||
{{works with|POSIX}} |
{{works with|POSIX}} |
||
Line 157: | Line 183: | ||
In order to be able to see the symbols, we need to link with an option telling to export symbols names in the dynamic section (for ELF and targets supporting it); for gcc, it means using the option <tt>-rdynamic</tt> (or <tt>-export-dynamic</tt> in the GNU linker). Otherwise we see only addresses. Static functions will always have their names "hidden". |
In order to be able to see the symbols, we need to link with an option telling to export symbols names in the dynamic section (for ELF and targets supporting it); for gcc, it means using the option <tt>-rdynamic</tt> (or <tt>-export-dynamic</tt> in the GNU linker). Otherwise we see only addresses. Static functions will always have their names "hidden". |
||
< |
<syntaxhighlight lang="c">#include <stdio.h> |
||
#include <stdlib.h> |
#include <stdlib.h> |
||
#include <unistd.h> |
#include <unistd.h> |
||
Line 195: | Line 221: | ||
outer(2,3,5); |
outer(2,3,5); |
||
return EXIT_SUCCESS; |
return EXIT_SUCCESS; |
||
}</ |
}</syntaxhighlight> |
||
Sample output on my system: |
Sample output on my system: |
||
Line 207: | Line 233: | ||
/lib/i686/libc.so.6(__libc_start_main+0xe5)[0xb7e045c5] |
/lib/i686/libc.so.6(__libc_start_main+0xe5)[0xb7e045c5] |
||
./pst[0x80485d1]</pre> |
./pst[0x80485d1]</pre> |
||
=== Using no extensions === |
|||
Sometimes microcomputers do not come with any kind of debug or stack tracing routines. Typically a program would "just hang" somewhere, or crash the gadget. This then requires manually "wolf fencing" of the bug with ''printf'' statements to identify the C source file, then the procedure, and then the line at the point of system crash. |
|||
The following macros and procedures provide an alternative way of doing this trouble shooting. |
|||
The steps are: |
|||
* #include "stack_trace.h" in the suspect C source code. |
|||
* Change the initial and last { and } of each procedure in the code to BEGIN(proc_name) and END. |
|||
* #define STACK_TRACE_ON and assign stack_trace.on to TRUE |
|||
* Recompile and run.... |
|||
The resulting output can be used to locate offending procedure and - hopefully - give a hint to the location of the actual bug. |
|||
The key point is that the following can be done on systems that are equipped with only and editor and compiler, and no debugger or library extension. |
|||
'''==> stack_trace.h <==''' |
|||
<syntaxhighlight lang="c"> |
|||
/* stack_trace.c - macros for hinting/tracing where a program crashed |
|||
on a system _without_ any form of debugger. |
|||
Simple goodbye_cruel_world.c example: |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#define STACK_TRACE_ON // compile in these "stack_trace" routines |
|||
#include "stack_trace.h" |
|||
void goodbye_cruel_world() |
|||
BEGIN(goodbye_cruel_world) |
|||
print_stack_trace(); |
|||
for(;;){} |
|||
END |
|||
int main() |
|||
BEGIN(main) |
|||
stack_trace.on = TRUE; // turn on runtime tracing |
|||
goodbye_cruel_world(); |
|||
stack_trace.on = FALSE; |
|||
RETURN(EXIT_SUCCESS); |
|||
END |
|||
Output: |
|||
goodbye_cruel_world.c:8: BEGIN goodbye_cruel_world[0x80486a8], stack(depth:1, size:60) |
|||
goodbye_cruel_world.c:8: goodbye_cruel_world[0x80486a8] --- stack(depth:2, size:60) --- |
|||
goodbye_cruel_world.c:14: main[0x80486f4] --- stack(depth:1, size:0) --- |
|||
goodbye_cruel_world.c:8: --- (depth 2) --- |
|||
*/ |
|||
#ifndef _LINUX_STDDEF_H |
|||
#include <stddef.h> |
|||
#endif |
|||
typedef struct stack_trace_frame_s { |
|||
const char *file_name; |
|||
int file_line; |
|||
const char *proc_name; |
|||
void *proc_addr; |
|||
struct stack_trace_frame_s *down, *up; |
|||
} stack_trace_frame_t; |
|||
#define SKIP |
|||
typedef enum {TRUE=1, FALSE=0} bool_t; |
|||
typedef struct { |
|||
bool_t on; |
|||
struct { const char *_begin, *_print, *_return, *_exit, *_end; } fmt; |
|||
struct { int depth; stack_trace_frame_t *lwb, *upb; } stack; |
|||
struct { int lwb, by, upb; const char *prefix; } indent; |
|||
} stack_trace_t; |
|||
extern stack_trace_t stack_trace; |
|||
void stack_trace_begin(char *SKIP, stack_trace_frame_t *SKIP); |
|||
void stack_trace_end(char *SKIP, int SKIP); |
|||
void print_stack_trace(); |
|||
#ifdef STACK_TRACE_ON |
|||
/* Many ThanX to Steve R Bourne for inspiring the following macros ;-) */ |
|||
#define BEGIN(x) { auto stack_trace_frame_t this = {__FILE__, __LINE__, #x, &x, NULL, NULL}; \ |
|||
stack_trace_begin(stack_trace.fmt._begin, &this); { |
|||
#define RETURN(x) { stack_trace_end(stack_trace.fmt._return, __LINE__); return(x); } |
|||
#define EXIT(x) { stack_trace_end(stack_trace.fmt._exit, __LINE__); exit(x); } |
|||
#define END } stack_trace_end(stack_trace.fmt._end, __LINE__); } |
|||
#else |
|||
/* Apologies to Landon Curt Noll and Larry Bassel for the following macros :-) */ |
|||
#define BEGIN(x) { |
|||
#define RETURN(x) return(x) |
|||
#define EXIT(x) exit(x) |
|||
#define END } |
|||
#endif</syntaxhighlight> |
|||
'''==> stack_trace.c <==''' |
|||
<syntaxhighlight lang="c">#include <stdio.h> |
|||
#include <stddef.h> |
|||
#define STACK_TRACE_ON |
|||
#include "stack_trace.h" |
|||
#define indent_fmt "%s" |
|||
#define std_cc_diag_fmt "%s:%d: " |
|||
#define stack_trace_diag_fmt " %s[0x%x], stack(depth:%d, size:%u)\n" |
|||
#define stack_trace_fmt "%s:%d:\t%s[0x%x]\t--- stack(depth:%d, size:%u) ---\n" |
|||
stack_trace_t stack_trace = { |
|||
FALSE, /* default: stack_trace.on == FALSE */ |
|||
{ std_cc_diag_fmt""indent_fmt"BEGIN"stack_trace_diag_fmt, |
|||
stack_trace_fmt, |
|||
std_cc_diag_fmt""indent_fmt"RETURN"stack_trace_diag_fmt, |
|||
std_cc_diag_fmt""indent_fmt"EXIT"stack_trace_diag_fmt, |
|||
std_cc_diag_fmt""indent_fmt"END"stack_trace_diag_fmt }, |
|||
{ 0, (stack_trace_frame_t*)NULL, (stack_trace_frame_t*)NULL }, /* stack */ |
|||
{ 19, 2, 20, " " } /* indent wrap */ |
|||
}; |
|||
void stack_trace_begin(const char *fmt, stack_trace_frame_t *this){ |
|||
if(stack_trace.on){ |
|||
fprintf(stderr, fmt, |
|||
this->file_name, this->file_line, /* file details */ |
|||
stack_trace.indent.prefix+stack_trace.indent.lwb, |
|||
this->proc_name, this->proc_addr, /* procedure details */ |
|||
stack_trace.stack.depth, (unsigned)stack_trace.stack.lwb-(unsigned)this); |
|||
stack_trace.indent.lwb = |
|||
( stack_trace.indent.lwb - stack_trace.indent.by ) % stack_trace.indent.upb; |
|||
} |
|||
if(!stack_trace.stack.upb){ /* this IS the stack !! */ |
|||
stack_trace.stack.lwb = stack_trace.stack.upb = this; |
|||
} else { |
|||
this -> down = stack_trace.stack.upb; |
|||
stack_trace.stack.upb -> up = this; |
|||
stack_trace.stack.upb = this; |
|||
} |
|||
stack_trace.stack.depth++; |
|||
} |
|||
void stack_trace_end(const char *fmt, int line){ |
|||
stack_trace.stack.depth--; |
|||
if(stack_trace.on){ |
|||
stack_trace.indent.lwb = |
|||
( stack_trace.indent.lwb + stack_trace.indent.by ) % stack_trace.indent.upb; |
|||
stack_trace_frame_t *this = stack_trace.stack.upb; |
|||
fprintf(stderr, fmt, |
|||
this->file_name, this->file_line, /* file details */ |
|||
stack_trace.indent.prefix+stack_trace.indent.lwb, |
|||
this->proc_name, this->proc_addr, /* procedure details */ |
|||
stack_trace.stack.depth, (unsigned)stack_trace.stack.lwb-(unsigned)this); |
|||
} |
|||
stack_trace.stack.upb = stack_trace.stack.upb -> down; |
|||
} |
|||
void print_indent(){ |
|||
if(!stack_trace.stack.upb){ |
|||
/* fprintf(stderr, "STACK_TRACE_ON not #defined during compilation\n"); */ |
|||
} else { |
|||
stack_trace_frame_t *this = stack_trace.stack.upb; |
|||
fprintf(stderr, std_cc_diag_fmt""indent_fmt, |
|||
this->file_name, this->file_line, /* file details */ |
|||
stack_trace.indent.prefix+stack_trace.indent.lwb |
|||
); |
|||
} |
|||
} |
|||
void print_stack_trace() { |
|||
if(!stack_trace.stack.upb){ |
|||
/* fprintf(stderr, "STACK_TRACE_ON not #defined during compilation\n"); */ |
|||
} else { |
|||
int depth = stack_trace.stack.depth; |
|||
stack_trace_frame_t *this = stack_trace.stack.upb; |
|||
for(this = stack_trace.stack.upb; this; this = this->down, depth--){ |
|||
fprintf(stderr, stack_trace.fmt._print, |
|||
this->file_name, this->file_line, /* file details */ |
|||
this->proc_name, this->proc_addr, /* procedure details */ |
|||
depth, (unsigned)stack_trace.stack.lwb-(unsigned)this); |
|||
} |
|||
print_indent(); fprintf(stderr, "--- (depth %d) ---\n", stack_trace.stack.depth); |
|||
} |
|||
}</syntaxhighlight> |
|||
'''==> stack_trace_test.c <==''' |
|||
The following code demonstrates the usage of the macros. Note that the initial and last curly brackets have been changed to BEGIN(procedure_name) and END. This is sometimes called '''macro magic''' and is unfashionable. |
|||
<syntaxhighlight lang="c">#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#define STACK_TRACE_ON /* compile in these "stack_trace" routines */ |
|||
#include "stack_trace.h" |
|||
void inner(int k) |
|||
BEGIN(inner) |
|||
print_indent(); printf("*** Now dump the stack ***\n"); |
|||
print_stack_trace(); |
|||
END |
|||
void middle(int x, int y) |
|||
BEGIN(middle) |
|||
inner(x*y); |
|||
END |
|||
void outer(int a, int b, int c) |
|||
BEGIN(outer) |
|||
middle(a+b, b+c); |
|||
END |
|||
int main() |
|||
BEGIN(main) |
|||
stack_trace.on = TRUE; /* turn on runtime tracing */ |
|||
outer(2,3,5); |
|||
stack_trace.on = FALSE; |
|||
RETURN(EXIT_SUCCESS); |
|||
END</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
stack_trace_test.c:19: BEGIN outer[0x80487b4], stack(depth:1, size:60) |
|||
stack_trace_test.c:14: BEGIN middle[0x8048749], stack(depth:2, size:108) |
|||
stack_trace_test.c:8: BEGIN inner[0x80486d8], stack(depth:3, size:156) |
|||
stack_trace_test.c:8: *** Now dump the stack *** |
|||
stack_trace_test.c:8: inner[0x80486d8] --- stack(depth:4, size:156) --- |
|||
stack_trace_test.c:14: middle[0x8048749] --- stack(depth:3, size:108) --- |
|||
stack_trace_test.c:19: outer[0x80487b4] --- stack(depth:2, size:60) --- |
|||
stack_trace_test.c:24: main[0x804882a] --- stack(depth:1, size:0) --- |
|||
stack_trace_test.c:8: --- (depth 4) --- |
|||
stack_trace_test.c:8: END inner[0x80486d8], stack(depth:3, size:156) |
|||
stack_trace_test.c:14: END middle[0x8048749], stack(depth:2, size:108) |
|||
stack_trace_test.c:19: END outer[0x80487b4], stack(depth:1, size:60) |
|||
</pre> |
|||
=={{header|C sharp|C#}}== |
|||
<syntaxhighlight lang="csharp">using System; |
|||
using System.Diagnostics; |
|||
class Program |
|||
{ |
|||
static void Inner() |
|||
{ |
|||
Console.WriteLine(new StackTrace()); |
|||
} |
|||
static void Middle() |
|||
{ |
|||
Inner(); |
|||
} |
|||
static void Outer() |
|||
{ |
|||
Middle(); |
|||
} |
|||
static void Main() |
|||
{ |
|||
Outer(); |
|||
} |
|||
}</syntaxhighlight> |
|||
Sample output: |
|||
<syntaxhighlight lang="text">at Program.Inner() |
|||
at Program.Middle() |
|||
at Program.Outer() |
|||
at Program.Main()</syntaxhighlight> |
|||
=={{header|Clojure}}== |
|||
{{works with|Java|1.6+}} |
|||
[http://docs.oracle.com/javase/8/docs/api/java/lang/management/ThreadMXBean.html ThreadMXBean] can be used to show you the stack of all live threads. |
|||
<syntaxhighlight lang="clojure"> |
|||
(doall |
|||
(map println (.dumpAllThreads (java.lang.management.ManagementFactory/getThreadMXBean) false false))) |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
#<ThreadInfo "nREPL-worker-26" Id=64 RUNNABLE |
|||
at sun.management.ThreadImpl.dumpThreads0(Native Method) |
|||
at sun.management.ThreadImpl.dumpAllThreads(ThreadImpl.java:446) |
|||
at user$eval1285.invoke(form-init6675235431801252432.clj:1) |
|||
at clojure.lang.Compiler.eval(Compiler.java:6619) |
|||
at clojure.lang.Compiler.eval(Compiler.java:6582) |
|||
at clojure.core$eval.invoke(core.clj:2852) |
|||
at clojure.main$repl$read_eval_print__6588$fn__6591.invoke(main.clj:259) |
|||
at clojure.main$repl$read_eval_print__6588.invoke(main.clj:259) |
|||
... |
|||
</pre> |
|||
=={{header|Common Lisp}}== |
=={{header|Common Lisp}}== |
||
Line 214: | Line 529: | ||
Here we use SWANK, which a component of SLIME, a Common Lisp IDE (it is the library loaded into the target Lisp system to enable interaction and remote debugging), to make use of its portable debugging facilities: |
Here we use SWANK, which a component of SLIME, a Common Lisp IDE (it is the library loaded into the target Lisp system to enable interaction and remote debugging), to make use of its portable debugging facilities: |
||
< |
<syntaxhighlight lang="lisp">(swank-backend:call-with-debugging-environment |
||
(lambda () |
(lambda () |
||
(swank:backtrace 0 nil)))</ |
(swank:backtrace 0 nil)))</syntaxhighlight> |
||
Here are a few lines of the result when running under [[SBCL]], the rest is omitted since it's long and boring: |
Here are a few lines of the result when running under [[SBCL]], the rest is omitted since it's long and boring: |
||
< |
<syntaxhighlight lang="lisp">((0 "((LAMBDA (SWANK-BACKEND::DEBUGGER-LOOP-FN)) #<FUNCTION (LAMBDA #) {100459BBC9}>)") |
||
(1 "(SB-INT:SIMPLE-EVAL-IN-LEXENV (SWANK-BACKEND:CALL-WITH-DEBUGGING-ENVIRONMENT (LAMBDA () (SWANK:BACKTRACE 0 NIL))) #<NULL-LEXENV>)") |
(1 "(SB-INT:SIMPLE-EVAL-IN-LEXENV (SWANK-BACKEND:CALL-WITH-DEBUGGING-ENVIRONMENT (LAMBDA () (SWANK:BACKTRACE 0 NIL))) #<NULL-LEXENV>)") |
||
(2 "(SWANK::EVAL-REGION \"(swank-backend:call-with-debugging-environment\\n (lambda ()\\n (swank:backtrace 0 nil)))\\n\\n\")") |
(2 "(SWANK::EVAL-REGION \"(swank-backend:call-with-debugging-environment\\n (lambda ()\\n (swank:backtrace 0 nil)))\\n\\n\")") |
||
(3 "((LAMBDA ()))") ...)</ |
(3 "((LAMBDA ()))") ...)</syntaxhighlight> |
||
Note that this is a data structure containing the backtrace, not a format intended for presentation. In [[SBCL]], executing <code>(sb-debug:backtrace 7)</code> produces output like this (run from the SLIME-REPL, which is why it still contains mentions of SWANK): |
Note that this is a data structure containing the backtrace, not a format intended for presentation. In [[SBCL]], executing <code>(sb-debug:backtrace 7)</code> produces output like this (run from the SLIME-REPL, which is why it still contains mentions of SWANK): |
||
< |
<syntaxhighlight lang="lisp">CL-USER> (sb-debug:backtrace 7) |
||
0: (SB-DEBUG::MAP-BACKTRACE |
0: (SB-DEBUG::MAP-BACKTRACE |
||
#<CLOSURE (LAMBDA (SB-DEBUG::FRAME)) {1193EFCD}>)[:EXTERNAL] |
#<CLOSURE (LAMBDA (SB-DEBUG::FRAME)) {1193EFCD}>)[:EXTERNAL] |
||
Line 242: | Line 557: | ||
6: (SWANK::CALL-WITH-RETRY-RESTART |
6: (SWANK::CALL-WITH-RETRY-RESTART |
||
"Retry SLIME REPL evaluation request." |
"Retry SLIME REPL evaluation request." |
||
#<CLOSURE (LAMBDA ()) {1193EC4D}>)</ |
#<CLOSURE (LAMBDA ()) {1193EC4D}>)</syntaxhighlight> |
||
SBCL's backtraces consist entirely of lists of the form <code>(<var>function-name</var> <var>args...</var>)</code>. |
SBCL's backtraces consist entirely of lists of the form <code>(<var>function-name</var> <var>args...</var>)</code>. |
||
=={{header|D}}== |
|||
Compiled with the dmd compiler using the -g switch. |
|||
{{trans|Java}} |
|||
<syntaxhighlight lang="d">import std.stdio, core.runtime; |
|||
void inner() { defaultTraceHandler.writeln; } |
|||
void middle() { inner; } |
|||
void outer() { middle; } |
|||
void main() { |
|||
outer; |
|||
"After the stack trace.".writeln; |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>0x00404FBE in core.sys.windows.stacktrace.StackTrace core.sys.windows.stacktrace.StackTrace.__ctor(uint, core.sys.windows.windows.CONTEXT*) at E:\dmd2\src\druntime\import\core\sys\windows\stacktrace.d(69) |
|||
0x00404ACB in object.Throwable.TraceInfo core.runtime.defaultTraceHandler(void*) at E:\dmd2\src\druntime\import\core\runtime.d(646) |
|||
0x0040201A in void test.inner() at E:\test.d(3) |
|||
0x0040202C in void test.middle() at E:\test.d(4) |
|||
0x00402038 in void test.outer() at E:\test.d(5) |
|||
0x00402044 in _Dmain at E:\test.d(9) |
|||
0x00409AAC in void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll().void __lambda1() |
|||
0x00409A7F in void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll() |
|||
0x00409997 in _d_run_main |
|||
0x004048D8 in main |
|||
0x0041FB2D in mainCRTStartup |
|||
0x76EED2E9 in BaseThreadInitThunk |
|||
0x77821603 in RtlInitializeExceptionChain |
|||
0x778215D6 in RtlInitializeExceptionChain |
|||
After the stack trace.</pre> |
|||
=={{header|DWScript}}== |
|||
Stack traces can be obtained from exception objects |
|||
<syntaxhighlight lang="delphi">procedure Inner; |
|||
begin |
|||
try |
|||
raise Exception.Create(''); |
|||
except |
|||
on E: Exception do |
|||
PrintLn(E.StackTrace); |
|||
end; |
|||
end; |
|||
procedure Middle; |
|||
begin |
|||
Inner; |
|||
end; |
|||
procedure Outer; |
|||
begin |
|||
Middle; |
|||
end; |
|||
Outer;</syntaxhighlight> |
|||
Output:<pre>Inner [line: 4, column: 23] |
|||
Middle [line: 13, column: 4] |
|||
Outer [line: 18, column: 4] |
|||
[line: 21, column: 1]</pre> |
|||
=={{header|Elena}}== |
|||
{{trans|C#}} |
|||
ELENA 6.x : |
|||
<syntaxhighlight lang="elena">import extensions; |
|||
public singleton program |
|||
{ |
|||
inner() |
|||
{ |
|||
console.printLine(new CallStack()) |
|||
} |
|||
middle() |
|||
{ |
|||
self.inner() |
|||
} |
|||
outer() |
|||
{ |
|||
self.middle() |
|||
} |
|||
// program entry point |
|||
function() |
|||
{ |
|||
program.outer() |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
sandbox'program.inner[1]:sandbox.l(7) |
|||
sandbox'program.middle[1]:sandbox.l(12) |
|||
sandbox'program.outer[1]:sandbox.l(17) |
|||
sandbox'program.function:#invoke:sandbox.l(23) |
|||
system'$private'entry.function:#invoke:app.l(5) |
|||
system'$private'entrySymbol#sym:app.l(44) |
|||
</pre> |
|||
=={{header|Elixir}}== |
|||
{{trans|Erlang}} |
|||
<syntaxhighlight lang="elixir">defmodule Stack_traces do |
|||
def main do |
|||
{:ok, a} = outer |
|||
IO.inspect a |
|||
end |
|||
defp outer do |
|||
{:ok, a} = middle |
|||
{:ok, a} |
|||
end |
|||
defp middle do |
|||
{:ok, a} = inner |
|||
{:ok, a} |
|||
end |
|||
defp inner do |
|||
try do |
|||
throw(42) |
|||
catch 42 -> {:ok, :erlang.get_stacktrace} |
|||
end |
|||
end |
|||
end |
|||
Stack_traces.main</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
[{Stack_traces, :inner, 0, [file: 'stack_trace.exs', line: 19]}, |
|||
{Stack_traces, :middle, 0, [file: 'stack_trace.exs', line: 13]}, |
|||
{Stack_traces, :outer, 0, [file: 'stack_trace.exs', line: 8]}, |
|||
{Stack_traces, :main, 0, [file: 'stack_trace.exs', line: 3]}, |
|||
{:elixir_compiler, :dispatch_loaded, 6, |
|||
[file: 'src/elixir_compiler.erl', line: 125]}, |
|||
{:elixir_lexical, :run, 3, [file: 'src/elixir_lexical.erl', line: 16]}, |
|||
{:elixir_compiler, :quoted, 3, [file: 'src/elixir_compiler.erl', line: 30]}, |
|||
{Code, :require_file, 2, [file: 'lib/code.ex', line: 363]}] |
|||
</pre> |
|||
=={{header|Erlang}}== |
|||
{{trans|Java}} |
|||
Stack traces only can be obtained inside a catch block. Additionally, it doesn't work for tail calls. |
|||
<syntaxhighlight lang="erlang">-module(stack_traces). |
|||
-export([main/0]). |
|||
main() -> |
|||
{ok,A} = outer(), |
|||
io:format("~p\n", [A]). |
|||
outer() -> |
|||
{ok,A} = middle(), |
|||
{ok,A}. |
|||
middle() -> |
|||
{ok,A} = inner(), |
|||
{ok,A}. |
|||
inner() -> |
|||
try throw(42) catch 42 -> {ok,erlang:get_stacktrace()} end.</syntaxhighlight> |
|||
{{out}} |
|||
<pre>[{stack_traces,inner,0,[{file,"stack_traces.erl"},{line,18}]}, |
|||
{stack_traces,middle,0,[{file,"stack_traces.erl"},{line,14}]}, |
|||
{stack_traces,outer,0,[{file,"stack_traces.erl"},{line,10}]}, |
|||
{stack_traces,main,0,[{file,"stack_traces.erl"},{line,6}]}, |
|||
{init,start_it,1,[]}, |
|||
{init,start_em,1,[]}]</pre> |
|||
=={{header|F_Sharp|F#}}== |
|||
{{trans|C#}} |
|||
<syntaxhighlight lang="fsharp">open System.Diagnostics |
|||
type myClass() = |
|||
member this.inner() = printfn "%A" (new StackTrace()) |
|||
member this.middle() = this.inner() |
|||
member this.outer() = this.middle() |
|||
[<EntryPoint>] |
|||
let main args = |
|||
let that = new myClass() |
|||
that.outer() |
|||
0</syntaxhighlight> |
|||
Output |
|||
<pre> at Rosetta.myClass.inner() |
|||
at Rosetta.myClass.middle() |
|||
at Rosetta.myClass.outer() |
|||
at Rosetta.main(String[] args)</pre> |
|||
=={{header|Factor}}== |
|||
This stack trace shows the listener — Factor's Read-Eval-Print-Loop. The current execution point in each call frame is indicated by <code>=></code>. Press ctrl+w in the listener to walk through the call stack one step at a time and watch how it affects the data stack. You can even step backward in most cases. This is handy for debugging. |
|||
<syntaxhighlight lang="factor">USE: prettyprint |
|||
get-callstack callstack.</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
(U) |
|||
Quotation: [ set-namestack init-catchstack self quot>> call => stop ] |
|||
(O) |
|||
Word: listener-thread |
|||
(O) |
|||
Word: listener |
|||
(O) |
|||
Word: (listener) |
|||
(O) |
|||
Word: listener-step |
|||
(U) |
|||
Quotation: [ |
|||
[ ~quotation~ dip swap ~quotation~ dip ] dip swap |
|||
[ call get-datastack ] dip => swap [ set-datastack ] dip |
|||
] |
|||
</pre> |
|||
=={{header|Forth}}== |
=={{header|Forth}}== |
||
Line 250: | Line 775: | ||
{{works with|4tH|3.60.0}} |
{{works with|4tH|3.60.0}} |
||
< |
<syntaxhighlight lang="forth">[UNDEFINED] R.S [IF] |
||
\ Return stack counterpart of DEPTH |
\ Return stack counterpart of DEPTH |
||
\ Note the STACK-CELLS correction is there to hide RDEPTH itself |
\ Note the STACK-CELLS correction is there to hide RDEPTH itself |
||
Line 263: | Line 788: | ||
BEGIN DUP WHILE ROT DUP . >R 1- REPEAT DROP |
BEGIN DUP WHILE ROT DUP . >R 1- REPEAT DROP |
||
THEN ." (TORS) " DROP >R ; |
THEN ." (TORS) " DROP >R ; |
||
[THEN]</ |
[THEN]</syntaxhighlight> |
||
=={{header|Fortran}}== |
|||
Fortran provides nothing of the sort as a language feature. A given compiler may (possibly via an option) record information in the code file that when a fatal error such as divide-by-zero is caught, allows the error message to name the routine in which the error occurred, possibly identifying the source line and perhaps even the sequence of routines that were invoked to get there. Otherwise, a routine has no information whereby it might identify its caller - or for that matter, itself. Some compilers might make available special routines, but, there is no such requirement in the standard. Outside of Fortran's lack of standardised access, some systems such as TaskInfo on Windows can show a task's current call stack (updated each time TaskInfo re-samples, say every four seconds), possibly including the names of the called routines. Watching this being updated as the task runs a lengthy computation gives hints that otherwise could be provided via execution profiling and a lot more effort. |
|||
Otherwise, it is up to the programmer. For instance, on entry to ''every'' routine, <code>CALL SUBIN("''name''")</code> and on exit, ''without fail'', <code>CALL SUBOUT("''name''")</code> which routines maintain a ... stack of names, and further can count invocations, though perhaps not for heavily-used routines - trials suggest that around ten million SUBIN calls consume about a second of cpu time. Add to this the judicious use of a routine <code>CALL STATE("''activity description''")</code> similarly using a stack (the stack pointer being maintained by SUBIN/SUBOUT) not only provides interesting documentation in the source but also, should there be a disaster, <code>CALL CROAK("''dismayed message''")</code> can be used to end a run, routine CROAK of course displaying the current stack with at each level the declared activity message before the final STOP. A similar routine could be invoked to display the current stack state without a STOP as some sort of status report. With this protocol in place, the routine to present trace output can name its caller (and, the caller of that) to provide some context for the bewildering output. |
|||
Here for example is a run of such a programme, called Gnash (for New Zealand's national collection of half-hourly electricity data) that accepts certain commands and in particular a command "croak". A log of input and output is maintained, echoing what appears on the screen. Thus, the programme requests the next input after prompting "Gnash:" and that input is the command "croak" followed by some text... |
|||
<pre> |
|||
Gnash: croak Life is troubled |
|||
Goodbye, cruel world! |
|||
Routine XeqACard croaks: Life is troubled |
|||
...from XeqACard Confronting croak Life is troubled |
|||
...from Attack some input |
|||
...from Gnash Gnash gnashing |
|||
Omitted exit from level 3:XeqACard |
|||
Omitted exit from level 2:Attack |
|||
</pre> |
|||
So, Gnash has invoked ATTACK to deal with input commands, as if cards are being read. ATTACK states that it is dealing with some input (possibly from a script file but in this case the keyboard), and invokes XeqACard to do so. It states that it is confronting some text, then invokes the appropriate command handler - which invokes CROAK, which unwinds the stack. Its final act is <code>STOP "I STOP now. Farewell..." !Whatever pit I was in, I'm gone.</code> which of course is not written to the log file and unless you're using a DOS-style session, the window will be closed before you can read it. |
|||
Should a fatal error be declared by the run-time system, none of this will happen, but if while running, some unrecoverable or a should-never-happen but I'll check anyway type of problem be noted, then a possibly informative output will follow. |
|||
=={{header|FreeBASIC}}== |
|||
Code extracted from FreeBAsic Forum |
|||
[https://freebasic.net/forum/viewtopic.php?t=31371 https://freebasic.net/forum/viewtopic.php?t=31371] |
|||
<syntaxhighlight lang="vb">#include "windows.bi" |
|||
Private Function Fn2() As Long |
|||
Dim frames(0 To 60) As Any Ptr |
|||
Dim framesPtr As Any Ptr Ptr = @frames(0) |
|||
Dim hash As DWORD |
|||
Dim As Long caught = CaptureStackBackTrace(0, 61, framesPtr, @hash) |
|||
Print Using "Caught & frames using stack capture"; caught |
|||
For i As Long = 0 To caught - 1 |
|||
Print Using "&) &"; caught - i; Hex(frames(i)) |
|||
Next |
|||
Return caught |
|||
End Function |
|||
Private Sub Fn1(num As Ulong) |
|||
Dim As Long numFn2 = Fn2() |
|||
Print Using "Fn2 returned & with num = &"; numFn2; num |
|||
End Sub |
|||
Fn1(87) |
|||
Sleep</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Caught 7 frames using stack capture |
|||
7) 4016FE |
|||
6) 40185E |
|||
5) 4015BA |
|||
4) 4013B4 |
|||
3) 40150B |
|||
2) 7FFBD6AC7614 |
|||
1) 7FFBD7B226A1 |
|||
Fn2 returned 7 with num = 87</pre> |
|||
=={{header|Go}}== |
|||
<syntaxhighlight lang="go">package main |
|||
import ( |
|||
"fmt" |
|||
"runtime" |
|||
) |
|||
func main() { |
|||
stackTrace := make([]byte, 1024) |
|||
n := runtime.Stack(stackTrace, true) |
|||
stackTrace = stackTrace[:n] |
|||
fmt.Printf("%s\n", stackTrace) |
|||
fmt.Printf("(%d bytes)\n", len(stackTrace)) |
|||
}</syntaxhighlight> |
|||
outputs: |
|||
<pre> |
|||
goroutine 16 [running]: |
|||
main.main() |
|||
/tmpfs/gosandbox-efa5a722_5cfcad46_bebc14b4_0486dec5_bd9e34fc/prog.go:10 +0xa0 |
|||
goroutine 19 [runnable]: |
|||
runfinq() |
|||
/tmp/sandbox/go/src/pkg/runtime/mgc0.c:2606 |
|||
runtime.goexit() |
|||
/tmp/sandbox/go/src/pkg/runtime/proc.c:1445 |
|||
(259 bytes) |
|||
</pre> |
|||
=={{header|Groovy}}== |
|||
Solution: |
|||
<syntaxhighlight lang="groovy">def rawTrace = { Thread.currentThread().stackTrace }</syntaxhighlight> |
|||
Test: (demonstrates, among other things, continued execution after generating stack trace) |
|||
<syntaxhighlight lang="groovy">def trace = rawTrace().collect { |
|||
def props = it.properties |
|||
def keys = (it.properties.keySet() - (new Object().properties.keySet())) |
|||
props.findAll{ k, v -> k in keys } |
|||
} |
|||
def propNames = trace[0].keySet().sort() |
|||
def propWidths = propNames.collect { name -> [name, trace.collect{ it[name].toString() }].flatten()*.size().max() } |
|||
propNames.eachWithIndex{ name, i -> printf("%-${propWidths[i]}s ", name) }; println '' |
|||
propWidths.each{ width -> print('-' * width + ' ') }; println '' |
|||
trace.each { |
|||
propNames.eachWithIndex{ name, i -> printf("%-${propWidths[i]}s ", it[name].toString()) }; println '' |
|||
}</syntaxhighlight> |
|||
Output: |
|||
<pre style="height:45ex;overflow:scroll;">className fileName lineNumber methodName nativeMethod |
|||
----------------------------------------------------------------- --------------------------------- ---------- ------------------------------- ------------ |
|||
java.lang.Thread Thread.java 1479 getStackTrace false |
|||
sun.reflect.NativeMethodAccessorImpl NativeMethodAccessorImpl.java -2 invoke0 true |
|||
sun.reflect.NativeMethodAccessorImpl NativeMethodAccessorImpl.java 39 invoke false |
|||
sun.reflect.DelegatingMethodAccessorImpl DelegatingMethodAccessorImpl.java 25 invoke false |
|||
java.lang.reflect.Method Method.java 597 invoke false |
|||
org.codehaus.groovy.reflection.CachedMethod CachedMethod.java 90 invoke false |
|||
groovy.lang.MetaMethod MetaMethod.java 233 doMethodInvoke false |
|||
groovy.lang.MetaClassImpl$GetBeanMethodMetaProperty MetaClassImpl.java 3465 getProperty false |
|||
org.codehaus.groovy.runtime.callsite.GetEffectivePojoPropertySite GetEffectivePojoPropertySite.java 61 getProperty false |
|||
org.codehaus.groovy.runtime.callsite.AbstractCallSite AbstractCallSite.java 227 callGetProperty false |
|||
ConsoleScript38$_run_closure1 ConsoleScript38 1 doCall false |
|||
sun.reflect.NativeMethodAccessorImpl NativeMethodAccessorImpl.java -2 invoke0 true |
|||
sun.reflect.NativeMethodAccessorImpl NativeMethodAccessorImpl.java 39 invoke false |
|||
sun.reflect.DelegatingMethodAccessorImpl DelegatingMethodAccessorImpl.java 25 invoke false |
|||
java.lang.reflect.Method Method.java 597 invoke false |
|||
org.codehaus.groovy.reflection.CachedMethod CachedMethod.java 90 invoke false |
|||
groovy.lang.MetaMethod MetaMethod.java 233 doMethodInvoke false |
|||
org.codehaus.groovy.runtime.metaclass.ClosureMetaClass ClosureMetaClass.java 272 invokeMethod false |
|||
groovy.lang.MetaClassImpl MetaClassImpl.java 885 invokeMethod false |
|||
org.codehaus.groovy.runtime.callsite.PogoMetaClassSite PogoMetaClassSite.java 66 callCurrent false |
|||
org.codehaus.groovy.runtime.callsite.CallSiteArray CallSiteArray.java 46 defaultCallCurrent false |
|||
org.codehaus.groovy.runtime.callsite.AbstractCallSite AbstractCallSite.java 133 callCurrent false |
|||
org.codehaus.groovy.runtime.callsite.AbstractCallSite AbstractCallSite.java 141 callCurrent false |
|||
ConsoleScript38$_run_closure1 ConsoleScript38 -1 doCall false |
|||
sun.reflect.NativeMethodAccessorImpl NativeMethodAccessorImpl.java -2 invoke0 true |
|||
sun.reflect.NativeMethodAccessorImpl NativeMethodAccessorImpl.java 39 invoke false |
|||
sun.reflect.DelegatingMethodAccessorImpl DelegatingMethodAccessorImpl.java 25 invoke false |
|||
java.lang.reflect.Method Method.java 597 invoke false |
|||
org.codehaus.groovy.reflection.CachedMethod CachedMethod.java 90 invoke false |
|||
groovy.lang.MetaMethod MetaMethod.java 233 doMethodInvoke false |
|||
org.codehaus.groovy.runtime.metaclass.ClosureMetaClass ClosureMetaClass.java 272 invokeMethod false |
|||
groovy.lang.MetaClassImpl MetaClassImpl.java 885 invokeMethod false |
|||
org.codehaus.groovy.runtime.callsite.PogoMetaClassSite PogoMetaClassSite.java 39 call false |
|||
org.codehaus.groovy.runtime.callsite.CallSiteArray CallSiteArray.java 42 defaultCall false |
|||
org.codehaus.groovy.runtime.callsite.AbstractCallSite AbstractCallSite.java 108 call false |
|||
org.codehaus.groovy.runtime.callsite.AbstractCallSite AbstractCallSite.java 112 call false |
|||
ConsoleScript38 ConsoleScript38 3 run false |
|||
groovy.lang.GroovyShell GroovyShell.java 266 runScriptOrMainOrTestOrRunnable false |
|||
groovy.lang.GroovyShell GroovyShell.java 517 run false |
|||
groovy.lang.GroovyShell GroovyShell.java 172 run false |
|||
groovy.lang.GroovyShell$run null -1 call false |
|||
groovy.ui.Console$_runScriptImpl_closure16 Console.groovy 910 doCall false |
|||
sun.reflect.GeneratedMethodAccessor232 null -1 invoke false |
|||
sun.reflect.DelegatingMethodAccessorImpl DelegatingMethodAccessorImpl.java 25 invoke false |
|||
java.lang.reflect.Method Method.java 597 invoke false |
|||
org.codehaus.groovy.reflection.CachedMethod CachedMethod.java 90 invoke false |
|||
groovy.lang.MetaMethod MetaMethod.java 233 doMethodInvoke false |
|||
org.codehaus.groovy.runtime.metaclass.ClosureMetaClass ClosureMetaClass.java 272 invokeMethod false |
|||
groovy.lang.MetaClassImpl MetaClassImpl.java 885 invokeMethod false |
|||
org.codehaus.groovy.runtime.callsite.PogoMetaClassSite PogoMetaClassSite.java 66 callCurrent false |
|||
org.codehaus.groovy.runtime.callsite.AbstractCallSite AbstractCallSite.java 141 callCurrent false |
|||
groovy.ui.Console$_runScriptImpl_closure16 Console.groovy -1 doCall false |
|||
sun.reflect.GeneratedMethodAccessor231 null -1 invoke false |
|||
sun.reflect.DelegatingMethodAccessorImpl DelegatingMethodAccessorImpl.java 25 invoke false |
|||
java.lang.reflect.Method Method.java 597 invoke false |
|||
org.codehaus.groovy.reflection.CachedMethod CachedMethod.java 90 invoke false |
|||
groovy.lang.MetaMethod MetaMethod.java 233 doMethodInvoke false |
|||
org.codehaus.groovy.runtime.metaclass.ClosureMetaClass ClosureMetaClass.java 272 invokeMethod false |
|||
groovy.lang.MetaClassImpl MetaClassImpl.java 885 invokeMethod false |
|||
groovy.lang.Closure Closure.java 405 call false |
|||
groovy.lang.Closure Closure.java 399 call false |
|||
groovy.lang.Closure Closure.java 483 run false |
|||
java.lang.Thread Thread.java 662 run false</pre> |
|||
==Icon and {{header|Unicon}}== |
|||
This Icon solution uses Unicon extensions. An Icon only version has not been provided. |
|||
{{libheader|Unicon Code Library}} |
|||
[http://tapestry.tucson.az.us/unilib/pack_Utils.html the following code for buildStackTrace in Utils is taken verbatim and shown below the main program] |
|||
<syntaxhighlight lang="unicon"> |
|||
import Utils # for buildStackTrace |
|||
procedure main() |
|||
g() |
|||
write() |
|||
f() |
|||
end |
|||
procedure f() |
|||
g() |
|||
end |
|||
procedure g() |
|||
# Using 1 as argument omits the trace of buildStackTrace itself |
|||
every write("\t",!buildStackTrace(1)) |
|||
end</syntaxhighlight> |
|||
<syntaxhighlight lang="unicon">#<p> |
|||
# Compute the current stack trace. Starting at level <i>n</i> above |
|||
# the current procedure. Here, <i>n</i> defaults to 0, which will |
|||
# include this procedure in the stack trace. |
|||
# <i>ce</i> defaults to ¤t. |
|||
# <i>This only works with newer versions of Unicon!</i> |
|||
# <[generates the stacktrace from current call back to first |
|||
# in the co-expression]> |
|||
#</p> |
|||
procedure buildStackTrace(n:0, # starting distance from this call |
|||
ce # co-expr to trace stack in [¤t] |
|||
) |
|||
local L |
|||
/ce := ¤t |
|||
L := []; n -:= 1 |
|||
while pName := image(proc(ce, n+:=1)) do { |
|||
fName := keyword("&file",ce,n) | "no file name" |
|||
fLine := keyword("&line",ce,n) | "no line number" |
|||
put(L, pName||" ["||fName||":"||fLine||"]" ) |
|||
} |
|||
return L |
|||
end</syntaxhighlight> |
|||
The output of this example is: |
|||
<pre> |
|||
procedure g [Stacktrace.icn:13] |
|||
procedure main [Stacktrace.icn:2] |
|||
procedure g [Stacktrace.icn:13] |
|||
procedure f [Stacktrace.icn:8] |
|||
procedure main [Stacktrace.icn:4] |
|||
</pre> |
|||
=={{header|J}}== |
=={{header|J}}== |
||
Line 270: | Line 1,024: | ||
To enable suspension and record subsequent stack frames: |
To enable suspension and record subsequent stack frames: |
||
<lang |
<syntaxhighlight lang="j"> 13!:0]1</syntaxhighlight> |
||
To retrieve a current stack trace: |
To retrieve a current stack trace: |
||
<lang |
<syntaxhighlight lang="j"> 13!:13''</syntaxhighlight> |
||
See also: http://www.jsoftware.com/help/dictionary/dx013.htm |
See also: http://www.jsoftware.com/help/dictionary/dx013.htm |
||
Example: |
|||
<syntaxhighlight lang="j"> f=:g |
|||
g=:13!:13 bind '' |
|||
f 7 NB. empty stack trace because debugging has not been enabled |
|||
13!:0]1 |
|||
f 7 |
|||
┌─┬─┬─┬─┬─────────────┬┬───┬──┬─┐ |
|||
│g│0│0│3│13!:13@(''"_)││┌─┐│ │ │ |
|||
│ │ │ │ │ │││7││ │ │ |
|||
│ │ │ │ │ ││└─┘│ │ │ |
|||
├─┼─┼─┼─┼─────────────┼┼───┼──┼─┤ |
|||
│f│0│0│3│g ││┌─┐│ │ │ |
|||
│ │ │ │ │ │││7││ │ │ |
|||
│ │ │ │ │ ││└─┘│ │ │ |
|||
└─┴─┴─┴─┴─────────────┴┴───┴──┴─┘</syntaxhighlight> |
|||
Technical note: the stack trace is not displayed, here, until after the stack has been discarded. This is because we have returned the stack trace as a result and relied on J's implicit display of the result of an expression to display the stack trace. |
|||
=={{header|Java}}== |
=={{header|Java}}== |
||
{{works with|Java|1.5+}} |
{{works with|Java|1.5+}} |
||
< |
<syntaxhighlight lang="java5">public class StackTracer { |
||
public static void printStackTrace() { |
public static void printStackTrace() { |
||
StackTraceElement[] elems = Thread.currentThread().getStackTrace(); |
StackTraceElement[] elems = Thread.currentThread().getStackTrace(); |
||
Line 289: | Line 1,062: | ||
} |
} |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
Demonstration code: |
Demonstration code: |
||
< |
<syntaxhighlight lang="java5">public class StackTraceDemo { |
||
static void inner() { |
static void inner() { |
||
StackTracer.printStackTrace(); |
StackTracer.printStackTrace(); |
||
Line 304: | Line 1,077: | ||
outer(); |
outer(); |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
Output: |
Output: |
||
<pre>Stack trace: |
<pre>Stack trace: |
||
Line 315: | Line 1,088: | ||
There is no standard way to do this, but some implementations provide it.<br /> |
There is no standard way to do this, but some implementations provide it.<br /> |
||
{{works with|Firefox}} |
{{works with|Firefox}} |
||
< |
<syntaxhighlight lang="javascript">try { |
||
throw new Error; |
throw new Error; |
||
} catch(e) { |
} catch(e) { |
||
alert(e.stack); |
alert(e.stack); |
||
}</ |
}</syntaxhighlight> |
||
{{works with|Opera}} |
|||
{{untested|Javascript}} |
|||
<lang javascript>try { |
|||
throw new Error; |
|||
} catch(e) { |
|||
alert(e.message); |
|||
}</lang> |
|||
The following version works in many browsers but it infinitely loops when there is recursion: |
The following version works in many browsers but it infinitely loops when there is recursion: |
||
< |
<syntaxhighlight lang="javascript">function foo () { |
||
var stack = "Stack trace:"; |
var stack = "Stack trace:"; |
||
for (var f = arguments.callee // current function |
for (var f = arguments.callee // current function |
||
Line 336: | Line 1,103: | ||
alert(stack); |
alert(stack); |
||
} |
} |
||
foo();</ |
foo();</syntaxhighlight> |
||
=={{header| |
=={{header|Julia}}== |
||
{{works with|Julia|0.6}} |
|||
<syntaxhighlight lang="julia">f() = g() |
|||
g() = println.(stacktrace()) |
|||
f()</syntaxhighlight> |
|||
{{out}} |
|||
<pre>g() at Stack_traces.jl:5 |
|||
f() at Stack_traces.jl:4 |
|||
include_string(::String, ::String) at loading.jl:515 |
|||
include_string(::Module, ::String, ::String) at Compat.jl:464 |
|||
(::Atom.##57#60{String,String})() at eval.jl:74 |
|||
withpath(::Atom.##57#60{String,String}, ::String) at utils.jl:30 |
|||
withpath(::Function, ::String) at eval.jl:38 |
|||
macro expansion at eval.jl:72 [inlined] |
|||
(::Atom.##56#59{Dict{String,Any}})() at task.jl:80</pre> |
|||
=={{header|Kotlin}}== |
|||
<syntaxhighlight lang="scala">// version 1.1.2 (stacktrace.kt which compiles to StacktraceKt.class) |
|||
fun myFunc() { |
|||
println(Throwable().stackTrace.joinToString("\n")) |
|||
} |
|||
fun main(args:Array<String>) { |
|||
myFunc() |
|||
println("\nContinuing ... ") |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
StacktraceKt.myFunc(stacktrace.kt:4) |
|||
StacktraceKt.main(stacktrace.kt:8) |
|||
Continuing ... |
|||
</pre> |
|||
=={{header|Lang}}== |
|||
<syntaxhighlight lang="lang"> |
|||
# fn.getStackTrace() returns a text-based stack trace |
|||
fp.printStackTrace = () -> fn.println(fn.getStackTrace()) |
|||
# Example |
|||
fp.f1 = () -> { |
|||
fn.println(F1:) |
|||
fp.printStackTrace() |
|||
} |
|||
fp.f2 = () -> { |
|||
fn.println(F2:) |
|||
fp.printStackTrace() |
|||
fp.f1() |
|||
} |
|||
fp.f2() |
|||
fn.combA0(fp.f2) |
|||
# Partially called combinator functions' names are represented as "<comb...-func(...)>" |
|||
fn.combA(fn.combC(fn.combAE(), x), fp.f2) |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
The file paths were redacted. If ":x" is outputted as the line number, no line number information is available (e.g. If Lang is implemented with an interpreter, predefined functions are written in the host language) |
|||
<pre> |
|||
F2: |
|||
at "[redacted]:x" in function "getStackTrace" |
|||
at "[redacted]:2" in function "fp.printStackTrace" |
|||
at "[redacted]:11" in function "fp.f2" |
|||
at "[redacted]:14" in function "main" |
|||
F1: |
|||
at "[redacted]:x" in function "getStackTrace" |
|||
at "[redacted]:2" in function "fp.printStackTrace" |
|||
at "[redacted]:7" in function "fp.f1" |
|||
at "[redacted]:12" in function "fp.f2" |
|||
at "[redacted]:14" in function "main" |
|||
F2: |
|||
at "[redacted]:x" in function "getStackTrace" |
|||
at "[redacted]:2" in function "fp.printStackTrace" |
|||
at "[redacted]:11" in function "fp.f2" |
|||
at "[redacted]:x" in function "combA0" |
|||
at "[redacted]:16" in function "main" |
|||
F1: |
|||
at "[redacted]:x" in function "getStackTrace" |
|||
at "[redacted]:2" in function "fp.printStackTrace" |
|||
at "[redacted]:7" in function "fp.f1" |
|||
at "[redacted]:12" in function "fp.f2" |
|||
at "[redacted]:x" in function "combA0" |
|||
at "[redacted]:16" in function "main" |
|||
F2: |
|||
at "[redacted]:x" in function "getStackTrace" |
|||
at "[redacted]:2" in function "fp.printStackTrace" |
|||
at "[redacted]:11" in function "fp.f2" |
|||
at "[redacted]:x" in function "<combAE-func()>" |
|||
at "[redacted]:x" in function "<combC-func(<combAE-func()>, <arg>)>" |
|||
at "[redacted]:x" in function "combA" |
|||
at "[redacted]:19" in function "main" |
|||
F1: |
|||
at "[redacted]:x" in function "getStackTrace" |
|||
at "[redacted]:2" in function "fp.printStackTrace" |
|||
at "[redacted]:7" in function "fp.f1" |
|||
at "[redacted]:12" in function "fp.f2" |
|||
at "[redacted]:x" in function "<combAE-func()>" |
|||
at "[redacted]:x" in function "<combC-func(<combAE-func()>, <arg>)>" |
|||
at "[redacted]:x" in function "combA" |
|||
at "[redacted]:19" in function "main" |
|||
</pre> |
|||
=={{header|Lasso}}== |
|||
By default Lasso tracks the file path, line and column numbers. You can create a trace method to track type and method names illustrated below or use one of the public libraries like L-Debug [https://github.com/zeroloop/l-debug]. |
|||
<syntaxhighlight lang="lasso">// Define our own trace method |
|||
define trace => { |
|||
local(gb) = givenblock |
|||
// Set a depth counter |
|||
var(::_tracedepth)->isnota(::integer) ? $_tracedepth = 0 |
|||
handle => {$_tracedepth--} |
|||
// Only output when supplied a capture |
|||
#gb ? stdoutnl( |
|||
// Indent |
|||
('\t' * $_tracedepth++) + |
|||
// Type + Method |
|||
#gb->self->type + '.' + #gb->calledname + |
|||
// Call site file |
|||
': ' + #gb->home->callsite_file + |
|||
// Line number and column number |
|||
' (line '+#gb->home->callsite_line + ', col ' + #gb->home->callsite_col +')' |
|||
) |
|||
return #gb() |
|||
}</syntaxhighlight> |
|||
;Use Trace: |
|||
<syntaxhighlight lang="lasso">define stackexample => type { |
|||
public oncreate => trace => { return self } |
|||
public inner => trace => { } |
|||
public middle => trace => { .inner } |
|||
public outer => trace => { .middle } |
|||
} |
|||
stackexample->outer</syntaxhighlight> |
|||
{{out}} |
|||
<pre>stackexample.oncreate: adminapp_lasso_runner_thread (line 2, col 24) |
|||
stackexample.outer: adminapp_lasso_runner_thread (line 5, col 21) |
|||
stackexample.middle: adminapp_lasso_runner_thread (line 4, col 22) |
|||
stackexample.inner: adminapp_lasso_runner_thread (line 3, col 21)</pre> |
|||
=={{header|Lua}}== |
|||
<syntaxhighlight lang="lua">function Inner( k ) |
|||
print( debug.traceback() ) |
|||
print "Program continues..." |
|||
end |
|||
function Middle( x, y ) |
|||
Inner( x+y ) |
|||
end |
|||
function Outer( a, b, c ) |
|||
Middle( a*b, c ) |
|||
end |
|||
Outer( 2, 3, 5 )</syntaxhighlight> |
|||
<pre>stack traceback: |
|||
./prog:4: in function 'Inner' |
|||
./prog:9: in function 'Middle' |
|||
./prog:13: in function 'Outer' |
|||
./prog:16: in main chunk |
|||
[C]: ? |
|||
Program continues...</pre> |
|||
=={{header|Mathematica}}/{{header|Wolfram Language}}== |
|||
Built-in function Stack does the task, example I: |
Built-in function Stack does the task, example I: |
||
< |
<syntaxhighlight lang="mathematica">f[g[1, Print[Stack[]]; 2]]</syntaxhighlight> |
||
prints, gives back: |
prints, gives back: |
||
< |
<syntaxhighlight lang="mathematica">{f,g,CompoundExpression,Print} |
||
f[g[1, 2]]</ |
f[g[1, 2]]</syntaxhighlight> |
||
Example II: |
Example II: |
||
< |
<syntaxhighlight lang="mathematica">f[g[1, Print[Stack[_]]; 2]]</syntaxhighlight> |
||
prints, gives back: |
prints, gives back: |
||
< |
<syntaxhighlight lang="mathematica">{f[g[1,Print[Stack[_]];2]],g[1,Print[Stack[_]];2],Print[Stack[_]];2,Print[Stack[_]]} |
||
f[g[1, 2]]</ |
f[g[1, 2]]</syntaxhighlight> |
||
Related and similar functions are: Trace, TracePrint, TraceScan,TraceDialog, Monitor, StackInhibit, StackBegin, StackComplete. In the manual look for 'guide/SymbolicExecutionHistory'. |
Related and similar functions are: Trace, TracePrint, TraceScan,TraceDialog, Monitor, StackInhibit, StackBegin, StackComplete. In the manual look for 'guide/SymbolicExecutionHistory'. |
||
=={{header|Nanoquery}}== |
|||
__calls__ contains a list that represents a trace of calls that have been made. |
|||
<syntaxhighlight lang="nanoquery">def print_stack() |
|||
global __calls__ |
|||
println "stack trace:" |
|||
for i in range(len(__calls__) - 2, 0) |
|||
println "\t" + __calls__[i] |
|||
end |
|||
end |
|||
print_stack() |
|||
println |
|||
for i in range(1, 1) |
|||
print_stack() |
|||
end |
|||
println |
|||
println "The program would continue."</syntaxhighlight> |
|||
{{out}} |
|||
<pre>stack trace: |
|||
<global>:10 |
|||
stack trace: |
|||
<for>:1 |
|||
<global>:13 |
|||
The program would continue.</pre> |
|||
=={{header|NetRexx}}== |
|||
{{trans|Java}} |
|||
<syntaxhighlight lang="netrexx">/* NetRexx */ |
|||
options replace format comments java crossref symbols nobinary |
|||
class RStackTraces |
|||
method inner() static |
|||
StackTracer.printStackTrace() |
|||
method middle() static |
|||
inner() |
|||
method outer() static |
|||
middle() |
|||
method main(args = String[]) public static |
|||
outer() |
|||
class RStackTraces.StackTracer |
|||
method printStackTrace() public static |
|||
elems = Thread.currentThread().getStackTrace() |
|||
say 'Stack trace:' |
|||
j_ = 2 |
|||
loop i_ = elems.length - 1 to 2 by -1 |
|||
say ''.left(j_) || elems[i_].getClassName()'.'elems[i_].getMethodName() |
|||
j_ = j_ + 2 |
|||
end i_ |
|||
</syntaxhighlight> |
|||
'''Output:''' |
|||
<pre> |
|||
Stack trace: |
|||
RStackTraces.main |
|||
RStackTraces.outer |
|||
RStackTraces.middle |
|||
RStackTraces.inner |
|||
</pre> |
|||
=={{header|Nim}}== |
|||
In (normal) debug builds stacktraces are enabled, while in release builds stacktraces are disabled by default, but can be enabled like this: <code>nim c -d:release --stacktrace:on --linetrace:on file.nim</code> |
|||
<syntaxhighlight lang="nim">proc g() = |
|||
# Writes the current stack trace to stderr. |
|||
writeStackTrace() |
|||
# Or fetch the stack trace entries for the current stack trace: |
|||
echo "----" |
|||
for e in getStackTraceEntries(): |
|||
echo e.filename, "@", e.line, " in ", e.procname |
|||
proc f() = |
|||
g() |
|||
f()</syntaxhighlight> |
|||
{{out}} |
|||
For a release build: |
|||
<pre>Traceback (most recent call last) |
|||
stack_traces.nim(12) stack_traces |
|||
stack_traces.nim(10) f |
|||
stack_traces.nim(3) g |
|||
---- |
|||
stack_traces.nim@12 in stack_traces |
|||
stack_traces.nim@10 in f |
|||
stack_traces.nim@6 in g |
|||
</pre> |
|||
In a debug build the stacktrace contains the full path to the source. |
|||
=={{header|Objective-C}}== |
|||
<syntaxhighlight lang="objc">#include <execinfo.h> |
|||
void *frames[128]; |
|||
int len = backtrace(frames, 128); |
|||
char **symbols = backtrace_symbols(frames, len); |
|||
for (int i = 0; i < len; ++i) { |
|||
NSLog(@"%s", symbols[i]); |
|||
} |
|||
free(symbols);</syntaxhighlight> |
|||
Or in Mac OS X 10.6+: |
|||
<syntaxhighlight lang="objc">NSArray *symbols = [NSThread callStackSymbols]; |
|||
for (NSString *symbol in symbols) { |
|||
NSLog(@"%@", symbol); |
|||
}</syntaxhighlight> |
|||
=={{header|OCaml}}== |
=={{header|OCaml}}== |
||
{{works with|OCaml|3.11+}} |
{{works with|OCaml|3.11+}} |
||
< |
<syntaxhighlight lang="ocaml">let div a b = a / b |
||
let () = |
let () = |
||
Line 360: | Line 1,411: | ||
prerr_endline(Printexc.to_string e); |
prerr_endline(Printexc.to_string e); |
||
Printexc.print_backtrace stderr; |
Printexc.print_backtrace stderr; |
||
;;</ |
;;</syntaxhighlight> |
||
outputs: |
outputs: |
||
Line 370: | Line 1,421: | ||
export OCAMLRUNPARAM='b' |
export OCAMLRUNPARAM='b' |
||
Then the code doesn't need |
Then the code doesn't need additional statements: |
||
< |
<syntaxhighlight lang="ocaml">let div a b = a / b |
||
let () = |
let () = |
||
let _ = div 3 0 in () |
let _ = div 3 0 in () |
||
;;</ |
;;</syntaxhighlight> |
||
outputs: |
outputs: |
||
Fatal error: exception Division_by_zero |
Fatal error: exception Division_by_zero |
||
Raised at file "test.ml", line 4, characters 10-17 |
Raised at file "test.ml", line 4, characters 10-17 |
||
=={{header|Oforth}}== |
|||
Stack trace is only available : |
|||
1) When an exception is raised |
|||
2) And when oforth debug mode is set, using --D command line option. |
|||
Otherwise no stack trace is available nor printed. |
|||
<syntaxhighlight lang="oforth">: f1 Exception throw("An exception") ; |
|||
Integer method: f2 self f1 ; |
|||
: f3 f2 ; |
|||
: f4 f3 ; |
|||
10 f4</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
[1:interpreter] Exception : An exception |
|||
Into Method #throw self = (Exception) An exception |
|||
Into Method #throw self = (Class) Exception |
|||
Into Function #f1 |
|||
Into Method #f2 self = (Integer) 10 |
|||
Into Function #f3 |
|||
</pre> |
|||
=={{header|OxygenBasic}}== |
|||
<syntaxhighlight lang="oxygenbasic"> |
|||
'32bit x86 |
|||
static string Report |
|||
macro ReportStack(n) |
|||
'=================== |
|||
' |
|||
scope |
|||
' |
|||
static sys stack[0X100],stackptr,e |
|||
' |
|||
'CAPTURE IMAGE OF UP TO 256 ENTRIES |
|||
' |
|||
' |
|||
mov eax,n |
|||
cmp eax,0x100 |
|||
( |
|||
jle exit |
|||
mov eax,0x100 'UPPER LIMIT |
|||
) |
|||
mov e,eax |
|||
mov stackptr,esp |
|||
lea edx,stack |
|||
mov ecx,e |
|||
mov esi,esp |
|||
( |
|||
mov eax,[esi] |
|||
mov [edx],eax |
|||
add esi,4 |
|||
add edx,4 |
|||
dec ecx |
|||
jg repeat |
|||
) |
|||
sys i |
|||
string cr=chr(13)+chr(10), tab=chr(9) |
|||
' |
|||
for i=1 to e |
|||
report+=hex(stackptr+(i-1)*4,8) tab hex(i-1,2) tab hex(stack[i],8) cr |
|||
next |
|||
' |
|||
end scope |
|||
' |
|||
end macro |
|||
'==== |
|||
'TEST |
|||
'==== |
|||
function foo() |
|||
'============= |
|||
push 0x44556677 |
|||
push 0x33445566 |
|||
push 0x22334455 |
|||
push 0x11223344 |
|||
ReportStack(8) |
|||
end function |
|||
Report+="Trace inside foo |
|||
" |
|||
foo() |
|||
print report |
|||
'putfile "s.txt",Report |
|||
/* |
|||
RESULT: |
|||
Trace inside foo |
|||
0017FE00 00 11223344 |
|||
0017FE04 01 22334455 |
|||
0017FE08 02 33445566 |
|||
0017FE0C 03 44556677 |
|||
0017FE10 04 005EAB1C |
|||
0017FE14 05 0017FE40 |
|||
0017FE18 06 10002D5F |
|||
0017FE1C 07 00000000 |
|||
*/ |
|||
</syntaxhighlight> |
|||
=={{header|Oz}}== |
=={{header|Oz}}== |
||
System exceptions contain the current stack at the nested feature <code>debug.stack</code>. For example: |
System exceptions contain the current stack at the nested feature <code>debug.stack</code>. For example: |
||
< |
<syntaxhighlight lang="oz">declare |
||
proc {Test} |
proc {Test} |
||
_ = 1 div 0 |
_ = 1 div 0 |
||
Line 391: | Line 1,553: | ||
catch E then |
catch E then |
||
{Inspect E} |
{Inspect E} |
||
end</ |
end</syntaxhighlight> |
||
Output: |
Output: |
||
[[File:Oz_stacktrace1.png|center|Stack trace of a system exception in Oz.]] |
[[File:Oz_stacktrace1.png|center|Stack trace of a system exception in Oz.]] |
||
Line 398: | Line 1,560: | ||
To access the stack trace directly, you can use the undocumented internal Debug module. Its <code>getTaskStack</code> function takes a thread, a depth value and a boolean "verbose" flag. It returns a list of stack frames. Example: |
To access the stack trace directly, you can use the undocumented internal Debug module. Its <code>getTaskStack</code> function takes a thread, a depth value and a boolean "verbose" flag. It returns a list of stack frames. Example: |
||
< |
<syntaxhighlight lang="oz">%% make sure that simple function calls are not optimized away |
||
\switch +controlflowinfo |
\switch +controlflowinfo |
||
Line 412: | Line 1,574: | ||
end |
end |
||
in |
in |
||
{F}</ |
{F}</syntaxhighlight> |
||
Output: |
Output: |
||
[[File:Oz_stacktrace2.png|center|Stack trace created by the Debug module.]] |
[[File:Oz_stacktrace2.png|center|Stack trace created by the Debug module.]] |
||
=={{header|PascalABC.NET}}== |
|||
<syntaxhighlight lang="perl"> |
|||
procedure qqq; |
|||
begin |
|||
var st := new System.Diagnostics.StackTrace(); |
|||
Print(st); |
|||
end; |
|||
procedure ppp; |
|||
begin |
|||
qqq; |
|||
end; |
|||
begin |
|||
ppp |
|||
end. |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
at Rosetta_StackTrace.Program.qqq() |
|||
at Rosetta_StackTrace.Program.ppp() |
|||
at Rosetta_StackTrace.Program.$Main() |
|||
at Rosetta_StackTrace.Program.Main() |
|||
</pre> |
|||
=={{header|Perl}}== |
=={{header|Perl}}== |
||
< |
<syntaxhighlight lang="perl">use Carp 'cluck'; |
||
sub g {cluck 'Hello from &g';} |
sub g {cluck 'Hello from &g';} |
||
sub f {g;} |
sub f {g;} |
||
f;</ |
f;</syntaxhighlight> |
||
This prints: |
This prints: |
||
Line 430: | Line 1,619: | ||
main::g() called at Print a Stack Trace line 4 |
main::g() called at Print a Stack Trace line 4 |
||
main::f() called at Print a Stack Trace line 6</pre> |
main::f() called at Print a Stack Trace line 6</pre> |
||
=={{header|Phix}}== |
|||
There no standard method of obtaining a stack trace mid-run (as yet, non-fatally that is), but we can quickly cobble something together: |
|||
<!--<syntaxhighlight lang="phix">(notonline)--> |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">W</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">machine_word</span><span style="color: #0000FF;">(),</span> |
|||
<span style="color: #0000FF;">{</span><span style="color: #000000;">RTN</span><span style="color: #0000FF;">,</span><span style="color: #000000;">PREVEBP</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">W</span><span style="color: #0000FF;">=</span><span style="color: #000000;">4</span><span style="color: #0000FF;">?{</span><span style="color: #000000;">8</span><span style="color: #0000FF;">,</span><span style="color: #000000;">20</span><span style="color: #0000FF;">}:{</span><span style="color: #000000;">16</span><span style="color: #0000FF;">,</span><span style="color: #000000;">40</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">show_stack</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">symtab</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">symtabN</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">rtn</span> |
|||
<span style="color: #004080;">atom</span> <span style="color: #000000;">prev_ebp</span> |
|||
#ilASM{ |
|||
[32] |
|||
lea edi,[symtab] |
|||
call :%opGetST -- [edi]=symtab (ie our local:=the real symtab) |
|||
mov edi,[ebp+20] -- prev_ebp |
|||
mov eax,[edi+8] -- calling routine no |
|||
mov [rtn],eax |
|||
mov eax,edi |
|||
lea edi,[prev_ebp] |
|||
call :%pStoreMint |
|||
[64] |
|||
lea rdi,[symtab] |
|||
call :%opGetST -- [rdi]=symtab (ie our local:=the real symtab) |
|||
mov rdi,[rbp+40] -- prev_ebp |
|||
mov rax,[rdi+16] -- calling routine no |
|||
mov [rtn],rax |
|||
mov rax,rdi |
|||
lea rdi,[prev_ebp] |
|||
call :%pStoreMint |
|||
[] |
|||
} |
|||
<span style="color: #008080;">while</span> <span style="color: #000000;">rtn</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">21</span> <span style="color: #008080;">do</span> <span style="color: #000080;font-style:italic;">-- (T_maintls, main top level routine, always present)</span> |
|||
<span style="color: #000000;">symtabN</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">symtab</span><span style="color: #0000FF;">[</span><span style="color: #000000;">rtn</span><span style="color: #0000FF;">]</span> |
|||
<span style="color: #0000FF;">?</span><span style="color: #000000;">symtabN</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> |
|||
<span style="color: #000000;">prev_ebp</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">peekNS</span><span style="color: #0000FF;">(</span><span style="color: #000000;">prev_ebp</span><span style="color: #0000FF;">+</span><span style="color: #000000;">PREVEBP</span><span style="color: #0000FF;">,</span><span style="color: #000000;">W</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">rtn</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">peekNS</span><span style="color: #0000FF;">(</span><span style="color: #000000;">prev_ebp</span><span style="color: #0000FF;">+</span><span style="color: #000000;">RTN</span><span style="color: #0000FF;">,</span><span style="color: #000000;">W</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">three</span><span style="color: #0000FF;">(</span><span style="color: #004080;">bool</span> <span style="color: #000000;">die</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">die</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #0000FF;">?</span><span style="color: #000000;">9</span><span style="color: #0000FF;">/</span><span style="color: #000000;">0</span> |
|||
<span style="color: #008080;">else</span> |
|||
<span style="color: #000000;">show_stack</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">two</span><span style="color: #0000FF;">(</span><span style="color: #004080;">bool</span> <span style="color: #000000;">die</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">three</span><span style="color: #0000FF;">(</span><span style="color: #000000;">die</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">one</span><span style="color: #0000FF;">(</span><span style="color: #004080;">bool</span> <span style="color: #000000;">die</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">two</span><span style="color: #0000FF;">(</span><span style="color: #000000;">die</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
<span style="color: #000000;">one</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #0000FF;">?</span><span style="color: #7060A8;">routine_id</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"dummy"</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- see note below</span> |
|||
<span style="color: #000000;">one</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
<!--</syntaxhighlight>--> |
|||
During compilation, the symbol table entries hold an integer ternary tree index rather than a string name, and normally |
|||
things are left like that during interpretation (the proper string names are always written out when an executable is |
|||
created) unless and until a fatal error occurs, or the compiler has evidence that they might be needed, such as that |
|||
unresolved routine_id("dummy"). The -1 in the output below is that call failing to find any such routine. |
|||
{{Out}} |
|||
<pre> |
|||
"three" |
|||
"two" |
|||
"one" |
|||
-1 |
|||
C:\Program Files (x86)\Phix\e01.exw:59 in procedure three() |
|||
attempt to divide by 0 |
|||
die = 1 |
|||
... called from C:\Program Files (x86)\Phix\e01.exw:66 in procedure two() |
|||
die = 1 |
|||
... called from C:\Program Files (x86)\Phix\e01.exw:70 in procedure one() |
|||
die = 1 |
|||
... called from C:\Program Files (x86)\Phix\e01.exw:75 |
|||
Global & Local Variables |
|||
--> see C:\Program Files (x86)\Phix\ex.err |
|||
Press Enter... |
|||
</pre> |
|||
The first half (up to the -1) is the output from show_stack_trace(), the rest is standard fatal error diagnostics.<br> |
|||
For more details of how the latter is actually produced, refer to builtins\VM\pDiagN.e - in particular the conversion of |
|||
raw addresses into source code line numbers is not trivial (and not worth trying to replicate here). In fact I didn't just |
|||
type all that #ilASM gunk above in, but instead copied it from other source files in builtins\VM. |
|||
Alternatively, but only when interpreting (whereas the above works as shown both when interpreting and pre-compiled), the debugger is |
|||
started with the much saner |
|||
<!--<syntaxhighlight lang="phix">--> |
|||
<span style="color: #008080;">trace</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
<!--</syntaxhighlight>--> |
|||
though as yet it offers no means of examining the stack trace (resume-ably), the above suggests it should be relative easy to add. |
|||
=={{header|PHP}}== |
=={{header|PHP}}== |
||
< |
<syntaxhighlight lang="php"><?php |
||
class StackTraceDemo { |
class StackTraceDemo { |
||
static function inner() { |
static function inner() { |
||
Line 446: | Line 1,732: | ||
StackTraceDemo::outer(); |
StackTraceDemo::outer(); |
||
?></ |
?></syntaxhighlight> |
||
<pre>#0 StackTraceDemo::inner() called at [/home/cweiske/Dev/cvs/test/php-stacktrace.php:7] |
<pre>#0 StackTraceDemo::inner() called at [/home/cweiske/Dev/cvs/test/php-stacktrace.php:7] |
||
#1 StackTraceDemo::middle() called at [/home/cweiske/Dev/cvs/test/php-stacktrace.php:10] |
#1 StackTraceDemo::middle() called at [/home/cweiske/Dev/cvs/test/php-stacktrace.php:10] |
||
#2 StackTraceDemo::outer() called at [/home/cweiske/Dev/cvs/test/php-stacktrace.php:14]</pre> |
#2 StackTraceDemo::outer() called at [/home/cweiske/Dev/cvs/test/php-stacktrace.php:14]</pre> |
||
=={{header|PicoLisp}}== |
|||
PicoLisp doesn't keep full backtrace information at runtime. This is for performance reasons. However, existing variable bindings (environments) can be inspected with the '[http://software-lab.de/doc/refE.html#env env]' function, so this can be used to build your own stack frames. |
|||
The following is analog to (though simpler than) the built-in '[http://software-lab.de/doc/refT.html#trace trace]' mechanism. The function '$$' (corresponds to '[http://software-lab.de/doc/ref_.html#$ $]' for tracing) is inserted by 'stackAll' into every function and method definition (corresponds to '[http://software-lab.de/doc/refT.html#traceAll traceAll]'). Then, when stopping at a '[http://software-lab.de/doc/refD.html#debug debug]' breakpoint or an error handler, 'dumpStack' can be used to inspect the stack contents. |
|||
As this mechanism uses 'let' to hold the stack frames, it is robust also across catch/throw, coroutines and error handling. |
|||
<syntaxhighlight lang="picolisp">(off "Stack") |
|||
(de $$ "Prg" |
|||
(let "Stack" (cons (cons (car "Prg") (env)) "Stack") # Build stack frame |
|||
(set "Stack" |
|||
(delq (asoq '"Stack" (car "Stack")) # Remove self-created entries |
|||
(delq (asoq '"Prg" (car "Stack")) |
|||
(car "Stack") ) ) ) |
|||
(run (cdr "Prg")) ) ) # Run body |
|||
(de stackAll (Excl) |
|||
(let *Dbg NIL |
|||
(for "X" (all) |
|||
(or |
|||
(memq "X" Excl) |
|||
(memq "X" '($$ @ @@ @@@)) |
|||
(= `(char "*") (char "X")) |
|||
(cond |
|||
((= `(char "+") (char "X")) |
|||
(for "Y" (pair (val "X")) |
|||
(and |
|||
(pair "Y") |
|||
(fun? (cdr "Y")) |
|||
(unless (== '$$ (caaddr "Y")) |
|||
(con (cdr "Y") |
|||
(list |
|||
(cons '$$ (cons (car "Y" "X") (cddr "Y"))) ) ) ) ) ) ) |
|||
((pair (getd "X")) |
|||
(let "Y" @ |
|||
(unless (== '$$ (caadr "Y")) |
|||
(con "Y" |
|||
(list (cons '$$ "X" (cdr "Y"))) ) ) ) ) ) ) ) ) ) |
|||
(de dumpStack () |
|||
(more (reverse (cdr "Stack"))) |
|||
T )</syntaxhighlight> |
|||
Test: |
|||
<syntaxhighlight lang="picolisp">(de foo (A B) |
|||
(let C 3 |
|||
(bar (inc 'A) (inc 'B) (inc 'C)) ) ) |
|||
(de bar (A D E) |
|||
(let (A 7 B 8 C 9) |
|||
(! println A B C) ) ) # Set a breakpoint before (println A B C) |
|||
(stackAll)</syntaxhighlight> |
|||
<pre>: (foo 1 2) # Call 'foo' |
|||
(println A B C) # Stopped at breakpoint in 'bar' |
|||
! (dumpStack) # Dump stack history |
|||
(foo (A . 1) (B . 2) (@ . T)) # Hit <enter> on each line to continue |
|||
(bar (B . 3) (C . 4) (A . 2) (D . 3) (E . 4) (@ . T)) |
|||
-> T |
|||
! # Hit <enter> to continue execution |
|||
7 8 9 # Output of (println A B C) |
|||
-> 9 |
|||
:</pre> |
|||
=={{header|PL/I}}== |
=={{header|PL/I}}== |
||
<syntaxhighlight lang="pl/i"> |
|||
<lang PL/I> |
|||
/* The SNAP option in the ON statement is sufficient to obtain */ |
/* The SNAP option in the ON statement is sufficient to obtain */ |
||
/* a traceback. The SYSTEM option specifies that standard */ |
/* a traceback. The SYSTEM option specifies that standard */ |
||
Line 461: | Line 1,810: | ||
... |
... |
||
signal condition(traceback); |
signal condition(traceback); |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|PureBasic}}== |
|||
The [http://www.purebasic.com/documentation/debugger/showcallstack.html ShowCallstack()]command opens a interactive display allowing viewing of the procedures in the calling path an all their local variables. |
|||
<syntaxhighlight lang="purebasic">Procedure Three() |
|||
a=7 |
|||
ShowCallstack() |
|||
CallDebugger |
|||
EndProcedure |
|||
Procedure Two() |
|||
a=4 |
|||
Three() |
|||
EndProcedure |
|||
Procedure One() |
|||
a=2 |
|||
Two() |
|||
EndProcedure |
|||
One()</syntaxhighlight> |
|||
=={{header|Python}}== |
=={{header|Python}}== |
||
See the [http://docs.python.org/library/traceback.html traceback] module |
See the [http://docs.python.org/library/traceback.html traceback] module |
||
< |
<syntaxhighlight lang="python">import traceback |
||
def f(): return g() |
def f(): return g() |
||
def g(): traceback.print_stack() |
def g(): traceback.print_stack() |
||
f()</ |
f()</syntaxhighlight> |
||
Sample output from a session in the Idle IDE: |
Sample output from a session in the Idle IDE: |
||
Line 486: | Line 1,855: | ||
def g(): traceback.print_stack() |
def g(): traceback.print_stack() |
||
</pre> |
</pre> |
||
=={{header|Quackery}}== |
|||
Quackery has two system stacks, the call or return stack and a data stack. The words <code>echoreturn</code> and <code>echostack</code> display the current state of them. Each call to a word or nest places two entries on the return stack, a pointer to the word or nest, and an index into that word or nest. These are displayed as the name of the word, or <code>[...]</code> for an unnamed nest, and the index into the word or nest as an integer. Pairs of entries are displayed wrapped in braces, e.g. <code>{shell 5}</code>. |
|||
This is illustrated with a Quackery shell dialogue. The Quackery shell is written in Quackery, and as can be seen, occupies five pairs of entries on the return stack. After each interaction with the user the data stack is automatically displayed. Here the user opens the Quackery shell, displays the return stack, defines a naive recursive Fibonacci word that displays the return and data stack at the start of each call, and uses it to compute and display the fifth Fibonacci number (5). |
|||
<pre> > quackery |
|||
Welcome to Quackery. |
|||
Enter "leave" to leave the shell. |
|||
Building extensions. |
|||
/O> echoreturn |
|||
... |
|||
{[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 0} |
|||
Stack empty. |
|||
/O> [ cr echoreturn |
|||
... cr echostack |
|||
... dup 2 < if done |
|||
... dup 1 - swap 2 - |
|||
... recurse swap recurse + ] is fib |
|||
... |
|||
Stack empty. |
|||
/O> 5 fib echo |
|||
... |
|||
{[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 1} |
|||
Stack: 5 |
|||
{[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 15} {recurse 1} {fib 1} |
|||
Stack: 4 3 |
|||
{[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 15} {recurse 1} {fib 15} {recurse 1} {fib 1} |
|||
Stack: 4 2 1 |
|||
{[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 15} {recurse 1} {fib 17} {recurse 1} {fib 1} |
|||
Stack: 4 1 2 |
|||
{[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 15} {recurse 1} {fib 17} {recurse 1} {fib 15} {recurse 1} {fib 1} |
|||
Stack: 4 1 1 0 |
|||
{[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 15} {recurse 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 1} |
|||
Stack: 4 1 0 1 |
|||
{[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 1} |
|||
Stack: 2 4 |
|||
{[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 15} {recurse 1} {fib 1} |
|||
Stack: 2 3 2 |
|||
{[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 15} {recurse 1} {fib 15} {recurse 1} {fib 1} |
|||
Stack: 2 3 1 0 |
|||
{[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 15} {recurse 1} {fib 17} {recurse 1} {fib 1} |
|||
Stack: 2 3 0 1 |
|||
{[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 1} |
|||
Stack: 2 1 3 |
|||
{[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 15} {recurse 1} {fib 1} |
|||
Stack: 2 1 2 1 |
|||
{[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 1} |
|||
Stack: 2 1 1 2 |
|||
{[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 15} {recurse 1} {fib 1} |
|||
Stack: 2 1 1 1 0 |
|||
{[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 1} |
|||
Stack: 2 1 1 0 1 |
|||
5 |
|||
Stack empty. |
|||
/O></pre> |
|||
=={{header|R}}== |
=={{header|R}}== |
||
< |
<syntaxhighlight lang="r">foo <- function() |
||
{ |
{ |
||
bar <- function() |
bar <- function() |
||
Line 497: | Line 1,947: | ||
} |
} |
||
foo()</ |
foo()</syntaxhighlight> |
||
<nowiki>[[1]]</nowiki> |
<nowiki>[[1]]</nowiki> |
||
foo() |
foo() |
||
Line 505: | Line 1,955: | ||
You can also see the stack trace when a function is called (or as it exits) using the trace function. |
You can also see the stack trace when a function is called (or as it exits) using the trace function. |
||
< |
<syntaxhighlight lang="r">trace("foo", recover) |
||
foo()</ |
foo()</syntaxhighlight> |
||
<pre> |
<pre> |
||
Tracing foo() on entry |
Tracing foo() on entry |
||
Line 516: | Line 1,966: | ||
Selection: |
Selection: |
||
</pre> |
</pre> |
||
=={{header|Racket}}== |
|||
<syntaxhighlight lang="racket"> |
|||
#lang racket |
|||
;; To see these calls we do two things: mutate the binding to prevent |
|||
;; Racket from inlining the value; use a (void) call at the end so the |
|||
;; calls are not tail calls (which will otherwise not show on the |
|||
;; stack). |
|||
(define foo #f) |
|||
(set! foo (λ() (bar) (void))) |
|||
(define bar #f) |
|||
(set! bar (λ() (show-stacktrace) (void))) |
|||
(define (show-stacktrace) |
|||
(for ([s (continuation-mark-set->context (current-continuation-marks))] |
|||
[i (in-naturals)]) |
|||
;; show just the names, not the full source information |
|||
(when (car s) (printf "~s: ~s\n" i (car s))))) |
|||
(foo) |
|||
</syntaxhighlight> |
|||
Output: |
|||
<pre> |
|||
0: show-stacktrace |
|||
1: bar |
|||
2: foo |
|||
3: |[running body]| |
|||
</pre> |
|||
=={{header|Raku}}== |
|||
(formerly Perl 6) |
|||
{{works with|Rakudo|2013-03-03}} |
|||
<syntaxhighlight lang="raku" line>sub g { say Backtrace.new.concise } |
|||
sub f { g } |
|||
sub MAIN { f }</syntaxhighlight> |
|||
{{out}} |
|||
<pre> in sub g at bt:1 |
|||
in sub f at bt:2 |
|||
in sub MAIN at bt:3</pre> |
|||
=={{header|Raven}}== |
|||
<syntaxhighlight lang="raven"> [1 2 3 4] 42 { 'a' 1 'b' 2 'c' 3 } 34.1234 ( -1 -2 -3 ) "The quick brown fox" FILE dump</syntaxhighlight> |
|||
{{out}} |
|||
<pre>hash (4 items) |
|||
usage => 1 |
|||
index => 65836 |
|||
flags => 256 |
|||
buffer => " 98252f8 73 74 64 69 6e 00 stdin." |
|||
The quick brown fox |
|||
28.1234 |
|||
hash (3 items) |
|||
a => 1 |
|||
b => 2 |
|||
c => 3 |
|||
42 |
|||
list (4 items) |
|||
0 => 1 |
|||
1 => 2 |
|||
2 => 3 |
|||
3 => 4</pre> |
|||
=={{header|REXX}}== |
|||
{{works with|Regina}} |
|||
<syntaxhighlight lang="rexx">/* call stack */ |
|||
say 'Call A' |
|||
call A '123' |
|||
say result |
|||
exit 0 |
|||
A: |
|||
say 'Call B' |
|||
call B '456' |
|||
say result |
|||
return ARG(1) |
|||
B: |
|||
say 'Call C' |
|||
call C '789' |
|||
say result |
|||
return ARG(1) |
|||
C: |
|||
call callstack |
|||
return ARG(1) |
|||
callstack: procedure |
|||
getcallstack(cs.) |
|||
say 'Dump call stack with' cs.0 'items' |
|||
do i = 1 to cs.0 |
|||
parse var cs.i line func |
|||
say format(line, 3) ':' left(func, 9) ': source "' || sourceline(line) || '"' |
|||
end |
|||
return cs.0</syntaxhighlight> |
|||
{{out}} |
|||
<pre>prompt$ regina callstack.rexx |
|||
Call A |
|||
Call B |
|||
Call C |
|||
Dump call stack with 4 items |
|||
20 : CALLSTACK : source "call callstack" |
|||
15 : C : source "call C '789'" |
|||
9 : B : source "call B '456'" |
|||
3 : A : source "call A '123'" |
|||
789 |
|||
456 |
|||
123</pre> |
|||
Regina will also dump a call stack during certain error conditions. For instance, if the code listing above raised an unhandled signal (simulated here with "signal noname" in routine C). This is not a recoverable scenario though, and the program will not continue. |
|||
<syntaxhighlight lang="rexx">C: |
|||
signal noname |
|||
call callstack |
|||
return ARG(1)</syntaxhighlight> |
|||
{{out}} |
|||
<pre>prompt$ regina callstack.rexx |
|||
Call A |
|||
Call B |
|||
Call C |
|||
20 +++ signal noname |
|||
15 +++ call C '789' |
|||
9 +++ call B '456' |
|||
3 +++ call A '123' |
|||
Error 16 running "/demos/callstack.rexx", line 20: Label not found |
|||
Error 16.1: Label "NONAME" not found</pre> |
|||
=={{header|Ruby}}== |
=={{header|Ruby}}== |
||
< |
<syntaxhighlight lang="ruby">def outer(a,b,c) |
||
middle a+b, b+c |
middle a+b, b+c |
||
end |
end |
||
Line 531: | Line 2,108: | ||
end |
end |
||
outer 2,3,5</ |
outer 2,3,5</syntaxhighlight> |
||
<pre>$ ruby stacktrace.rb |
<pre>$ ruby stacktrace.rb |
||
stacktrace.rb:10:in `inner' |
stacktrace.rb:10:in `inner' |
||
Line 540: | Line 2,117: | ||
Exceptions caught in a rescue clause contain the trace information: |
Exceptions caught in a rescue clause contain the trace information: |
||
< |
<syntaxhighlight lang="ruby">def outer(a,b,c) |
||
middle a+b, b+c |
middle a+b, b+c |
||
end |
end |
||
Line 558: | Line 2,135: | ||
puts e.backtrace |
puts e.backtrace |
||
end |
end |
||
puts "continuing after the rescue..."</ |
puts "continuing after the rescue..."</syntaxhighlight> |
||
<pre>stacktrace.rb:10:in `inner' |
<pre>stacktrace.rb:10:in `inner' |
||
stacktrace.rb:6:in `middle' |
stacktrace.rb:6:in `middle' |
||
Line 564: | Line 2,141: | ||
stacktrace.rb:15 |
stacktrace.rb:15 |
||
continuing after the rescue...</pre> |
continuing after the rescue...</pre> |
||
Thread has a backtrace method: |
|||
<syntaxhighlight lang="ruby">p Thread.current.backtrace</syntaxhighlight> |
|||
=={{header|Scala}}== |
=={{header|Scala}}== |
||
Line 569: | Line 2,148: | ||
the way, could be used from Java as well, with minor modifications. |
the way, could be used from Java as well, with minor modifications. |
||
< |
<syntaxhighlight lang="scala">def callStack = try { error("exception") } catch { case ex => ex.getStackTrace drop 2 } |
||
def printStackTrace = callStack drop 1 /* don't print ourselves! */ foreach println</ |
def printStackTrace = callStack drop 1 /* don't print ourselves! */ foreach println</syntaxhighlight> |
||
Usage example: |
Usage example: |
||
Line 624: | Line 2,203: | ||
The following #printCurrentStack is already defined in the base slate image but it is replicated here. |
The following #printCurrentStack is already defined in the base slate image but it is replicated here. |
||
< |
<syntaxhighlight lang="slate">slate[1]> d@(Debugger traits) printCurrentStack &limit: limit &stream: out &showLocation: showLocation |
||
[ |
[ |
||
d clone `>> [baseFramePointer: (d interpreter framePointerOf: #printCurrentStack). |
d clone `>> [baseFramePointer: (d interpreter framePointerOf: #printCurrentStack). |
||
Line 631: | Line 2,210: | ||
]. |
]. |
||
Defining function 'printCurrentStack' on: 'Debugger traits' |
Defining function 'printCurrentStack' on: 'Debugger traits' |
||
[printCurrentStack &limit: &stream: &showLocation:]</ |
[printCurrentStack &limit: &stream: &showLocation:]</syntaxhighlight> |
||
The output from calling the function: |
The output from calling the function: |
||
< |
<syntaxhighlight lang="slate">slate[2]> Debugger printCurrentStack. |
||
Backtrace (method @ source): |
Backtrace (method @ source): |
||
frame: 0 [printCurrentStack &limit: &stream: &showLocation:] @ stdin:0 |
frame: 0 [printCurrentStack &limit: &stream: &showLocation:] @ stdin:0 |
||
Line 648: | Line 2,227: | ||
frame: 9 [start &resource:] @ src/lib/repl.slate:185 |
frame: 9 [start &resource:] @ src/lib/repl.slate:185 |
||
frame: 10 [start] @ src/mobius/prelude.slate:38 |
frame: 10 [start] @ src/mobius/prelude.slate:38 |
||
Nil</ |
Nil</syntaxhighlight> |
||
=={{header|Smalltalk}}== |
=={{header|Smalltalk}}== |
||
Line 655: | Line 2,234: | ||
A backtrace is normally sent when some error occurs; however, it can be "forced": |
A backtrace is normally sent when some error occurs; however, it can be "forced": |
||
< |
<syntaxhighlight lang="smalltalk">Object subclass: Container [ |
||
Container class >> outer: a and: b and: c [ |
Container class >> outer: a and: b and: c [ |
||
self middle: (a+b) and: (b+c) |
self middle: (a+b) and: (b+c) |
||
Line 669: | Line 2,248: | ||
Container outer: 2 and: 3 and: 5. |
Container outer: 2 and: 3 and: 5. |
||
'Anyway, we continue with it' displayNl.</ |
'Anyway, we continue with it' displayNl.</syntaxhighlight> |
||
Output: |
Output: |
||
Line 680: | Line 2,259: | ||
=={{header|Tcl}}== |
=={{header|Tcl}}== |
||
< |
<syntaxhighlight lang="tcl">proc printStackTrace {} { |
||
puts "Stack trace:" |
puts "Stack trace:" |
||
for {set i 1} {$i < [info level]} {incr i} { |
for {set i 1} {$i < [info level]} {incr i} { |
||
puts [string repeat " " $i][info level $i] |
puts [string repeat " " $i][info level $i] |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
Demonstration code: |
Demonstration code: |
||
< |
<syntaxhighlight lang="tcl">proc outer {a b c} { |
||
middle [expr {$a+$b}] [expr {$b+$c}] |
middle [expr {$a+$b}] [expr {$b+$c}] |
||
} |
} |
||
Line 696: | Line 2,275: | ||
printStackTrace |
printStackTrace |
||
} |
} |
||
outer 2 3 5</ |
outer 2 3 5</syntaxhighlight> |
||
Produces this output: |
Produces this output: |
||
<pre>Stack trace: |
<pre>Stack trace: |
||
Line 703: | Line 2,282: | ||
inner 40</pre> |
inner 40</pre> |
||
=={{header|VBA}}== |
|||
In VBE the VBA Editor hitting Control+L while stepping through your code in debug mode will pop up a window which displays the call stack. |
|||
=={{header|Wren}}== |
|||
You can always force Wren CLI to produce a stack trace by either engineering an actual error or calling Fiber.abort() at the relevant part of the script. |
|||
However, it is not possible to continue execution of the script afterwards. Whilst one can 'catch' such an error using Fiber.try() this will only allow you to inspect the error itself, not the chain of function calls that led up to it. |
|||
<syntaxhighlight lang="wren">var func2 = Fn.new { |
|||
Fiber.abort("Forced error.") |
|||
} |
|||
var func1 = Fn.new { |
|||
func2.call() |
|||
} |
|||
func1.call()</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Forced error. |
|||
[./stack_trace line 2] in new(_) block argument |
|||
[./stack_trace line 6] in new(_) block argument |
|||
[./stack_trace line 9] in (script) |
|||
</pre> |
|||
=={{header|zkl}}== |
|||
<syntaxhighlight lang="zkl">fcn f{println("F");vm.stackTrace().println()} fcn g{println("G")} |
|||
f();g();</syntaxhighlight> |
|||
{{out}} |
|||
stackTrace just returns a string. You don't get to futz with the stack. |
|||
<pre> |
|||
F |
|||
Stack trace for VM#1 (): |
|||
Cmd.f@stackTrace addr:4 args(0) reg(0) |
|||
Cmd.__constructor addr:3 args(0) reg(0) R |
|||
startup.__constructor addr:2242 args(0) reg(1) ER |
|||
startup.__constructor addr:2178 args(0) reg(22) |
|||
G |
|||
</pre> |
|||
{{omit from|AWK|No introspection}} |
|||
{{omit from|GUISS}} |
|||
{{omit from|Locomotive Basic|No introspection}} |
|||
{{omit from|Lotus 123 Macro Scripting|No introspection}} |
|||
{{omit from|PARI/GP}} |
|||
{{omit from|TI-83 BASIC}} {{omit from|TI-89 BASIC}} |
{{omit from|TI-83 BASIC}} {{omit from|TI-89 BASIC}} |
||
{{omit from|M4}} |
{{omit from|M4}} |
||
{{omit from|ZX Spectrum Basic|No introspection}} |
Latest revision as of 19:42, 8 June 2024
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
You are encouraged to solve this task according to the task description, using any language you may know.
Many programming languages allow for introspection of the current call stack environment. This can be for a variety of purposes such as enforcing security checks, debugging, or for getting access to the stack frame of callers.
- Task
Print out (in a manner considered suitable for the platform) the current call stack.
The amount of information printed for each frame on the call stack is not constrained, but should include at least the name of the function or method at that level of the stack frame.
You may explicitly add a call to produce the stack trace to the (example) code being instrumented for examination.
The task should allow the program to continue after generating the stack trace.
The task report here must include the trace from a sample program.
Ada
The provided solution is specific to the GNAT Ada compiler. Further it is restricted to some platforms. See the description of the package GNAT.Traceback supplied with GNAT. The switch -g must be used in order to include debug information into the executable.
with Ada.Text_IO; use Ada.Text_IO;
with GNAT.Traceback;
with GNAT.Traceback.Symbolic;
procedure Test_Stack_Trace is
procedure Call_Stack is
Trace : GNAT.Traceback.Tracebacks_Array (1..1_000);
Length : Natural;
begin
GNAT.Traceback.Call_Chain (Trace, Length);
Put_Line (GNAT.Traceback.Symbolic.Symbolic_Traceback (Trace (1..Length)));
end Call_Stack;
procedure Inner (K : Integer) is
begin
Call_Stack;
end Inner;
procedure Middle (X, Y : Integer) is
begin
Inner (X * Y);
end Middle;
procedure Outer (A, B, C : Integer) is
begin
Middle (A + B, B + C);
end Outer;
begin
Outer (2,3,5);
end Test_Stack_Trace;
Sample output:
00417D7F in ?? at cygming-crtend.c:0 00401A61 in test_stack_trace.call_stack at test_stack_trace.adb:10 00401A25 in test_stack_trace.inner at test_stack_trace.adb:16 00401A0C in test_stack_trace.middle at test_stack_trace.adb:21 0040193E in test_stack_trace.outer at test_stack_trace.adb:26 004018A2 in _ada_test_stack_trace at test_stack_trace.adb:30 004016BE in main at b~test_stack_trace.adb:183 00401235 in ?? at cygming-crtend.c:0 00401286 in ?? at cygming-crtend.c:0 7C817075 in ?? at ??:0
AutoHotkey
The local, static, and global bindings are viewable using 'Listvars'
recently executed lines are available through ListLines
ListLines can be turned on or off... with:
ListLines, On|Off
f()
return
f()
{
return g()
}
g()
{
ListLines
msgbox, lines recently executed
x = local to g
ListVars
msgbox, variable bindings
}
#Persistent
Output:
001: f()
006: Return,g()
011: ListLines (0.05)
012: MsgBox,lines recently executed (3.81)
013: x = local to g
014: ListVars
015: MsgBox,variable bindings (3.94)
016: }
002: Return (181.66)
Global Variables (alphabetical)
--------------------------------------------------
0[1 of 3]: 0
ErrorLevel[1 of 3]: 0
BASIC
On Beta BASIC and SAM BASIC, the call stack is used for procedure calls, GOSUBs and DO loops. The stack contains return addresses as line and statement numbers. (The parameters for procedures are stored in another stack.) POP statement can be used to pop the return address from the stack.
The following procedure pops the return addrsses from the stack and lists the corresponding lines where the call occurred. Since it is not possible to push the addresses back into the stack, it is not possible to continue the normal flow of execution after displaying the stack. However, it is possible to continue the program otherwise. If the program execution stops on error, it is possible to display the call stack by typing callstack in command mode.
100 DEF PROC callstack 110 ON ERROR GOTO 1000 120 FOR i=1 TO 100 130 POP lnum 140 LIST lnum TO lnum 150 NEXT i 190 END PROC 1000 PRINT "End of stack" 1010 STOP
Example usage. An error is generated on line 320, which causes branch to error handler on line 1100. The error handler displays error number followed by call stack.
200 DEF PROC foo count 210 PRINT "foo "; 220 IF count > 1 230 foo count-1 240 ELSE 250 bar 260 END IF 290 END PROC 300 DEF PROC bar 310 PRINT "bar" 320 x = 1/0 390 END PROC 500 ON ERROR GOTO 1100 510 foo 3 520 STOP 1100 PRINT "Error "; error; " on line "; lino 1110 PRINT "Callstack:" 1120 callstack 1130 STOP
Output:
foo foo foo bar Error 28 on line 320 Call stack: 1120 callstack 320 x = 1/0 250 bar 230 foo count-1 230 foo count-1 510 foo 3 End of stack
C
Using GNU extensions
The backtrace* functions are a GNU extension to the standard C library.
In order to be able to see the symbols, we need to link with an option telling to export symbols names in the dynamic section (for ELF and targets supporting it); for gcc, it means using the option -rdynamic (or -export-dynamic in the GNU linker). Otherwise we see only addresses. Static functions will always have their names "hidden".
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <execinfo.h>
#define MAX_BT 200
void print_stack_trace()
{
void *buffer[MAX_BT];
int n;
n = backtrace(buffer, MAX_BT);
fprintf(stderr, "--- (depth %d) ---\n", n);
backtrace_symbols_fd(buffer, n, STDERR_FILENO);
}
void inner(int k)
{
print_stack_trace();
}
void middle(int x, int y)
{
inner(x*y);
}
void outer(int a, int b, int c)
{
middle(a+b, b+c);
}
int main()
{
outer(2,3,5);
return EXIT_SUCCESS;
}
Sample output on my system:
--- (depth 7) --- ./pst(print_stack_trace+0x1f)[0x8048683] ./pst(inner+0xb)[0x80486cd] ./pst(middle+0x15)[0x80486e4] ./pst(outer+0x23)[0x8048709] ./pst(main+0x2d)[0x8048738] /lib/i686/libc.so.6(__libc_start_main+0xe5)[0xb7e045c5] ./pst[0x80485d1]
Using no extensions
Sometimes microcomputers do not come with any kind of debug or stack tracing routines. Typically a program would "just hang" somewhere, or crash the gadget. This then requires manually "wolf fencing" of the bug with printf statements to identify the C source file, then the procedure, and then the line at the point of system crash.
The following macros and procedures provide an alternative way of doing this trouble shooting.
The steps are:
- #include "stack_trace.h" in the suspect C source code.
- Change the initial and last { and } of each procedure in the code to BEGIN(proc_name) and END.
- #define STACK_TRACE_ON and assign stack_trace.on to TRUE
- Recompile and run....
The resulting output can be used to locate offending procedure and - hopefully - give a hint to the location of the actual bug.
The key point is that the following can be done on systems that are equipped with only and editor and compiler, and no debugger or library extension.
==> stack_trace.h <==
/* stack_trace.c - macros for hinting/tracing where a program crashed
on a system _without_ any form of debugger.
Simple goodbye_cruel_world.c example:
#include <stdio.h>
#include <stdlib.h>
#define STACK_TRACE_ON // compile in these "stack_trace" routines
#include "stack_trace.h"
void goodbye_cruel_world()
BEGIN(goodbye_cruel_world)
print_stack_trace();
for(;;){}
END
int main()
BEGIN(main)
stack_trace.on = TRUE; // turn on runtime tracing
goodbye_cruel_world();
stack_trace.on = FALSE;
RETURN(EXIT_SUCCESS);
END
Output:
goodbye_cruel_world.c:8: BEGIN goodbye_cruel_world[0x80486a8], stack(depth:1, size:60)
goodbye_cruel_world.c:8: goodbye_cruel_world[0x80486a8] --- stack(depth:2, size:60) ---
goodbye_cruel_world.c:14: main[0x80486f4] --- stack(depth:1, size:0) ---
goodbye_cruel_world.c:8: --- (depth 2) ---
*/
#ifndef _LINUX_STDDEF_H
#include <stddef.h>
#endif
typedef struct stack_trace_frame_s {
const char *file_name;
int file_line;
const char *proc_name;
void *proc_addr;
struct stack_trace_frame_s *down, *up;
} stack_trace_frame_t;
#define SKIP
typedef enum {TRUE=1, FALSE=0} bool_t;
typedef struct {
bool_t on;
struct { const char *_begin, *_print, *_return, *_exit, *_end; } fmt;
struct { int depth; stack_trace_frame_t *lwb, *upb; } stack;
struct { int lwb, by, upb; const char *prefix; } indent;
} stack_trace_t;
extern stack_trace_t stack_trace;
void stack_trace_begin(char *SKIP, stack_trace_frame_t *SKIP);
void stack_trace_end(char *SKIP, int SKIP);
void print_stack_trace();
#ifdef STACK_TRACE_ON
/* Many ThanX to Steve R Bourne for inspiring the following macros ;-) */
#define BEGIN(x) { auto stack_trace_frame_t this = {__FILE__, __LINE__, #x, &x, NULL, NULL}; \
stack_trace_begin(stack_trace.fmt._begin, &this); {
#define RETURN(x) { stack_trace_end(stack_trace.fmt._return, __LINE__); return(x); }
#define EXIT(x) { stack_trace_end(stack_trace.fmt._exit, __LINE__); exit(x); }
#define END } stack_trace_end(stack_trace.fmt._end, __LINE__); }
#else
/* Apologies to Landon Curt Noll and Larry Bassel for the following macros :-) */
#define BEGIN(x) {
#define RETURN(x) return(x)
#define EXIT(x) exit(x)
#define END }
#endif
==> stack_trace.c <==
#include <stdio.h>
#include <stddef.h>
#define STACK_TRACE_ON
#include "stack_trace.h"
#define indent_fmt "%s"
#define std_cc_diag_fmt "%s:%d: "
#define stack_trace_diag_fmt " %s[0x%x], stack(depth:%d, size:%u)\n"
#define stack_trace_fmt "%s:%d:\t%s[0x%x]\t--- stack(depth:%d, size:%u) ---\n"
stack_trace_t stack_trace = {
FALSE, /* default: stack_trace.on == FALSE */
{ std_cc_diag_fmt""indent_fmt"BEGIN"stack_trace_diag_fmt,
stack_trace_fmt,
std_cc_diag_fmt""indent_fmt"RETURN"stack_trace_diag_fmt,
std_cc_diag_fmt""indent_fmt"EXIT"stack_trace_diag_fmt,
std_cc_diag_fmt""indent_fmt"END"stack_trace_diag_fmt },
{ 0, (stack_trace_frame_t*)NULL, (stack_trace_frame_t*)NULL }, /* stack */
{ 19, 2, 20, " " } /* indent wrap */
};
void stack_trace_begin(const char *fmt, stack_trace_frame_t *this){
if(stack_trace.on){
fprintf(stderr, fmt,
this->file_name, this->file_line, /* file details */
stack_trace.indent.prefix+stack_trace.indent.lwb,
this->proc_name, this->proc_addr, /* procedure details */
stack_trace.stack.depth, (unsigned)stack_trace.stack.lwb-(unsigned)this);
stack_trace.indent.lwb =
( stack_trace.indent.lwb - stack_trace.indent.by ) % stack_trace.indent.upb;
}
if(!stack_trace.stack.upb){ /* this IS the stack !! */
stack_trace.stack.lwb = stack_trace.stack.upb = this;
} else {
this -> down = stack_trace.stack.upb;
stack_trace.stack.upb -> up = this;
stack_trace.stack.upb = this;
}
stack_trace.stack.depth++;
}
void stack_trace_end(const char *fmt, int line){
stack_trace.stack.depth--;
if(stack_trace.on){
stack_trace.indent.lwb =
( stack_trace.indent.lwb + stack_trace.indent.by ) % stack_trace.indent.upb;
stack_trace_frame_t *this = stack_trace.stack.upb;
fprintf(stderr, fmt,
this->file_name, this->file_line, /* file details */
stack_trace.indent.prefix+stack_trace.indent.lwb,
this->proc_name, this->proc_addr, /* procedure details */
stack_trace.stack.depth, (unsigned)stack_trace.stack.lwb-(unsigned)this);
}
stack_trace.stack.upb = stack_trace.stack.upb -> down;
}
void print_indent(){
if(!stack_trace.stack.upb){
/* fprintf(stderr, "STACK_TRACE_ON not #defined during compilation\n"); */
} else {
stack_trace_frame_t *this = stack_trace.stack.upb;
fprintf(stderr, std_cc_diag_fmt""indent_fmt,
this->file_name, this->file_line, /* file details */
stack_trace.indent.prefix+stack_trace.indent.lwb
);
}
}
void print_stack_trace() {
if(!stack_trace.stack.upb){
/* fprintf(stderr, "STACK_TRACE_ON not #defined during compilation\n"); */
} else {
int depth = stack_trace.stack.depth;
stack_trace_frame_t *this = stack_trace.stack.upb;
for(this = stack_trace.stack.upb; this; this = this->down, depth--){
fprintf(stderr, stack_trace.fmt._print,
this->file_name, this->file_line, /* file details */
this->proc_name, this->proc_addr, /* procedure details */
depth, (unsigned)stack_trace.stack.lwb-(unsigned)this);
}
print_indent(); fprintf(stderr, "--- (depth %d) ---\n", stack_trace.stack.depth);
}
}
==> stack_trace_test.c <==
The following code demonstrates the usage of the macros. Note that the initial and last curly brackets have been changed to BEGIN(procedure_name) and END. This is sometimes called macro magic and is unfashionable.
#include <stdio.h>
#include <stdlib.h>
#define STACK_TRACE_ON /* compile in these "stack_trace" routines */
#include "stack_trace.h"
void inner(int k)
BEGIN(inner)
print_indent(); printf("*** Now dump the stack ***\n");
print_stack_trace();
END
void middle(int x, int y)
BEGIN(middle)
inner(x*y);
END
void outer(int a, int b, int c)
BEGIN(outer)
middle(a+b, b+c);
END
int main()
BEGIN(main)
stack_trace.on = TRUE; /* turn on runtime tracing */
outer(2,3,5);
stack_trace.on = FALSE;
RETURN(EXIT_SUCCESS);
END
- Output:
stack_trace_test.c:19: BEGIN outer[0x80487b4], stack(depth:1, size:60) stack_trace_test.c:14: BEGIN middle[0x8048749], stack(depth:2, size:108) stack_trace_test.c:8: BEGIN inner[0x80486d8], stack(depth:3, size:156) stack_trace_test.c:8: *** Now dump the stack *** stack_trace_test.c:8: inner[0x80486d8] --- stack(depth:4, size:156) --- stack_trace_test.c:14: middle[0x8048749] --- stack(depth:3, size:108) --- stack_trace_test.c:19: outer[0x80487b4] --- stack(depth:2, size:60) --- stack_trace_test.c:24: main[0x804882a] --- stack(depth:1, size:0) --- stack_trace_test.c:8: --- (depth 4) --- stack_trace_test.c:8: END inner[0x80486d8], stack(depth:3, size:156) stack_trace_test.c:14: END middle[0x8048749], stack(depth:2, size:108) stack_trace_test.c:19: END outer[0x80487b4], stack(depth:1, size:60)
C#
using System;
using System.Diagnostics;
class Program
{
static void Inner()
{
Console.WriteLine(new StackTrace());
}
static void Middle()
{
Inner();
}
static void Outer()
{
Middle();
}
static void Main()
{
Outer();
}
}
Sample output:
at Program.Inner()
at Program.Middle()
at Program.Outer()
at Program.Main()
Clojure
ThreadMXBean can be used to show you the stack of all live threads.
(doall
(map println (.dumpAllThreads (java.lang.management.ManagementFactory/getThreadMXBean) false false)))
- Output:
#<ThreadInfo "nREPL-worker-26" Id=64 RUNNABLE at sun.management.ThreadImpl.dumpThreads0(Native Method) at sun.management.ThreadImpl.dumpAllThreads(ThreadImpl.java:446) at user$eval1285.invoke(form-init6675235431801252432.clj:1) at clojure.lang.Compiler.eval(Compiler.java:6619) at clojure.lang.Compiler.eval(Compiler.java:6582) at clojure.core$eval.invoke(core.clj:2852) at clojure.main$repl$read_eval_print__6588$fn__6591.invoke(main.clj:259) at clojure.main$repl$read_eval_print__6588.invoke(main.clj:259) ...
Common Lisp
Stack trace facilities are not specified by the Common Lisp standard. Implementations vary widely in the amount of information provided, how it can be retrieved, and the amount of internal implementation detail included.
Here we use SWANK, which a component of SLIME, a Common Lisp IDE (it is the library loaded into the target Lisp system to enable interaction and remote debugging), to make use of its portable debugging facilities:
(swank-backend:call-with-debugging-environment
(lambda ()
(swank:backtrace 0 nil)))
Here are a few lines of the result when running under SBCL, the rest is omitted since it's long and boring:
((0 "((LAMBDA (SWANK-BACKEND::DEBUGGER-LOOP-FN)) #<FUNCTION (LAMBDA #) {100459BBC9}>)")
(1 "(SB-INT:SIMPLE-EVAL-IN-LEXENV (SWANK-BACKEND:CALL-WITH-DEBUGGING-ENVIRONMENT (LAMBDA () (SWANK:BACKTRACE 0 NIL))) #<NULL-LEXENV>)")
(2 "(SWANK::EVAL-REGION \"(swank-backend:call-with-debugging-environment\\n (lambda ()\\n (swank:backtrace 0 nil)))\\n\\n\")")
(3 "((LAMBDA ()))") ...)
Note that this is a data structure containing the backtrace, not a format intended for presentation. In SBCL, executing (sb-debug:backtrace 7)
produces output like this (run from the SLIME-REPL, which is why it still contains mentions of SWANK):
CL-USER> (sb-debug:backtrace 7)
0: (SB-DEBUG::MAP-BACKTRACE
#<CLOSURE (LAMBDA (SB-DEBUG::FRAME)) {1193EFCD}>)[:EXTERNAL]
1: (BACKTRACE
7
#<TWO-WAY-STREAM
:INPUT-STREAM #<SWANK-BACKEND::SLIME-INPUT-STREAM {120F6519}>
:OUTPUT-STREAM #<SWANK-BACKEND::SLIME-OUTPUT-STREAM {1208F3E1}>>)
2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (BACKTRACE 7) #<NULL-LEXENV>)
3: (SWANK::EVAL-REGION
"(sb-debug:backtrace 7)
")
4: ((LAMBDA ()))
5: (SWANK::TRACK-PACKAGE #<CLOSURE (LAMBDA ()) {1193ECBD}>)
6: (SWANK::CALL-WITH-RETRY-RESTART
"Retry SLIME REPL evaluation request."
#<CLOSURE (LAMBDA ()) {1193EC4D}>)
SBCL's backtraces consist entirely of lists of the form (function-name args...)
.
D
Compiled with the dmd compiler using the -g switch.
import std.stdio, core.runtime;
void inner() { defaultTraceHandler.writeln; }
void middle() { inner; }
void outer() { middle; }
void main() {
outer;
"After the stack trace.".writeln;
}
- Output:
0x00404FBE in core.sys.windows.stacktrace.StackTrace core.sys.windows.stacktrace.StackTrace.__ctor(uint, core.sys.windows.windows.CONTEXT*) at E:\dmd2\src\druntime\import\core\sys\windows\stacktrace.d(69) 0x00404ACB in object.Throwable.TraceInfo core.runtime.defaultTraceHandler(void*) at E:\dmd2\src\druntime\import\core\runtime.d(646) 0x0040201A in void test.inner() at E:\test.d(3) 0x0040202C in void test.middle() at E:\test.d(4) 0x00402038 in void test.outer() at E:\test.d(5) 0x00402044 in _Dmain at E:\test.d(9) 0x00409AAC in void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll().void __lambda1() 0x00409A7F in void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll() 0x00409997 in _d_run_main 0x004048D8 in main 0x0041FB2D in mainCRTStartup 0x76EED2E9 in BaseThreadInitThunk 0x77821603 in RtlInitializeExceptionChain 0x778215D6 in RtlInitializeExceptionChain After the stack trace.
DWScript
Stack traces can be obtained from exception objects
procedure Inner;
begin
try
raise Exception.Create('');
except
on E: Exception do
PrintLn(E.StackTrace);
end;
end;
procedure Middle;
begin
Inner;
end;
procedure Outer;
begin
Middle;
end;
Outer;
Output:
Inner [line: 4, column: 23] Middle [line: 13, column: 4] Outer [line: 18, column: 4] [line: 21, column: 1]
Elena
ELENA 6.x :
import extensions;
public singleton program
{
inner()
{
console.printLine(new CallStack())
}
middle()
{
self.inner()
}
outer()
{
self.middle()
}
// program entry point
function()
{
program.outer()
}
}
- Output:
sandbox'program.inner[1]:sandbox.l(7) sandbox'program.middle[1]:sandbox.l(12) sandbox'program.outer[1]:sandbox.l(17) sandbox'program.function:#invoke:sandbox.l(23) system'$private'entry.function:#invoke:app.l(5) system'$private'entrySymbol#sym:app.l(44)
Elixir
defmodule Stack_traces do
def main do
{:ok, a} = outer
IO.inspect a
end
defp outer do
{:ok, a} = middle
{:ok, a}
end
defp middle do
{:ok, a} = inner
{:ok, a}
end
defp inner do
try do
throw(42)
catch 42 -> {:ok, :erlang.get_stacktrace}
end
end
end
Stack_traces.main
- Output:
[{Stack_traces, :inner, 0, [file: 'stack_trace.exs', line: 19]}, {Stack_traces, :middle, 0, [file: 'stack_trace.exs', line: 13]}, {Stack_traces, :outer, 0, [file: 'stack_trace.exs', line: 8]}, {Stack_traces, :main, 0, [file: 'stack_trace.exs', line: 3]}, {:elixir_compiler, :dispatch_loaded, 6, [file: 'src/elixir_compiler.erl', line: 125]}, {:elixir_lexical, :run, 3, [file: 'src/elixir_lexical.erl', line: 16]}, {:elixir_compiler, :quoted, 3, [file: 'src/elixir_compiler.erl', line: 30]}, {Code, :require_file, 2, [file: 'lib/code.ex', line: 363]}]
Erlang
Stack traces only can be obtained inside a catch block. Additionally, it doesn't work for tail calls.
-module(stack_traces).
-export([main/0]).
main() ->
{ok,A} = outer(),
io:format("~p\n", [A]).
outer() ->
{ok,A} = middle(),
{ok,A}.
middle() ->
{ok,A} = inner(),
{ok,A}.
inner() ->
try throw(42) catch 42 -> {ok,erlang:get_stacktrace()} end.
- Output:
[{stack_traces,inner,0,[{file,"stack_traces.erl"},{line,18}]}, {stack_traces,middle,0,[{file,"stack_traces.erl"},{line,14}]}, {stack_traces,outer,0,[{file,"stack_traces.erl"},{line,10}]}, {stack_traces,main,0,[{file,"stack_traces.erl"},{line,6}]}, {init,start_it,1,[]}, {init,start_em,1,[]}]
F#
open System.Diagnostics
type myClass() =
member this.inner() = printfn "%A" (new StackTrace())
member this.middle() = this.inner()
member this.outer() = this.middle()
[<EntryPoint>]
let main args =
let that = new myClass()
that.outer()
0
Output
at Rosetta.myClass.inner() at Rosetta.myClass.middle() at Rosetta.myClass.outer() at Rosetta.main(String[] args)
Factor
This stack trace shows the listener — Factor's Read-Eval-Print-Loop. The current execution point in each call frame is indicated by =>
. Press ctrl+w in the listener to walk through the call stack one step at a time and watch how it affects the data stack. You can even step backward in most cases. This is handy for debugging.
USE: prettyprint
get-callstack callstack.
- Output:
(U) Quotation: [ set-namestack init-catchstack self quot>> call => stop ] (O) Word: listener-thread (O) Word: listener (O) Word: (listener) (O) Word: listener-step (U) Quotation: [ [ ~quotation~ dip swap ~quotation~ dip ] dip swap [ call get-datastack ] dip => swap [ set-datastack ] dip ]
Forth
In Forth, calling sequence information is kept on the Return Stack. Some Forth compilers have the word "R.S" that dumps the contents of the Return Stack - just like ".S", which dumps the contents of the Data Stack. Note this may also include stack frames, local variables and temporary values. Forth has no way of knowing which is which, because that is usually left to the programmer.
[UNDEFINED] R.S [IF]
\ Return stack counterpart of DEPTH
\ Note the STACK-CELLS correction is there to hide RDEPTH itself
( -- n)
: RDEPTH STACK-CELLS -2 [+] CELLS RP@ - ;
\ Return stack counterpart of .S
\ Note the : R.S R> .. >R ; sequence is there to hide R.S itself
( --)
: R.S R> CR RDEPTH DUP 0> IF DUP
BEGIN DUP WHILE R> -ROT 1- REPEAT DROP DUP
BEGIN DUP WHILE ROT DUP . >R 1- REPEAT DROP
THEN ." (TORS) " DROP >R ;
[THEN]
Fortran
Fortran provides nothing of the sort as a language feature. A given compiler may (possibly via an option) record information in the code file that when a fatal error such as divide-by-zero is caught, allows the error message to name the routine in which the error occurred, possibly identifying the source line and perhaps even the sequence of routines that were invoked to get there. Otherwise, a routine has no information whereby it might identify its caller - or for that matter, itself. Some compilers might make available special routines, but, there is no such requirement in the standard. Outside of Fortran's lack of standardised access, some systems such as TaskInfo on Windows can show a task's current call stack (updated each time TaskInfo re-samples, say every four seconds), possibly including the names of the called routines. Watching this being updated as the task runs a lengthy computation gives hints that otherwise could be provided via execution profiling and a lot more effort.
Otherwise, it is up to the programmer. For instance, on entry to every routine, CALL SUBIN("name")
and on exit, without fail, CALL SUBOUT("name")
which routines maintain a ... stack of names, and further can count invocations, though perhaps not for heavily-used routines - trials suggest that around ten million SUBIN calls consume about a second of cpu time. Add to this the judicious use of a routine CALL STATE("activity description")
similarly using a stack (the stack pointer being maintained by SUBIN/SUBOUT) not only provides interesting documentation in the source but also, should there be a disaster, CALL CROAK("dismayed message")
can be used to end a run, routine CROAK of course displaying the current stack with at each level the declared activity message before the final STOP. A similar routine could be invoked to display the current stack state without a STOP as some sort of status report. With this protocol in place, the routine to present trace output can name its caller (and, the caller of that) to provide some context for the bewildering output.
Here for example is a run of such a programme, called Gnash (for New Zealand's national collection of half-hourly electricity data) that accepts certain commands and in particular a command "croak". A log of input and output is maintained, echoing what appears on the screen. Thus, the programme requests the next input after prompting "Gnash:" and that input is the command "croak" followed by some text...
Gnash: croak Life is troubled Goodbye, cruel world! Routine XeqACard croaks: Life is troubled ...from XeqACard Confronting croak Life is troubled ...from Attack some input ...from Gnash Gnash gnashing Omitted exit from level 3:XeqACard Omitted exit from level 2:Attack
So, Gnash has invoked ATTACK to deal with input commands, as if cards are being read. ATTACK states that it is dealing with some input (possibly from a script file but in this case the keyboard), and invokes XeqACard to do so. It states that it is confronting some text, then invokes the appropriate command handler - which invokes CROAK, which unwinds the stack. Its final act is STOP "I STOP now. Farewell..." !Whatever pit I was in, I'm gone.
which of course is not written to the log file and unless you're using a DOS-style session, the window will be closed before you can read it.
Should a fatal error be declared by the run-time system, none of this will happen, but if while running, some unrecoverable or a should-never-happen but I'll check anyway type of problem be noted, then a possibly informative output will follow.
FreeBASIC
Code extracted from FreeBAsic Forum https://freebasic.net/forum/viewtopic.php?t=31371
#include "windows.bi"
Private Function Fn2() As Long
Dim frames(0 To 60) As Any Ptr
Dim framesPtr As Any Ptr Ptr = @frames(0)
Dim hash As DWORD
Dim As Long caught = CaptureStackBackTrace(0, 61, framesPtr, @hash)
Print Using "Caught & frames using stack capture"; caught
For i As Long = 0 To caught - 1
Print Using "&) &"; caught - i; Hex(frames(i))
Next
Return caught
End Function
Private Sub Fn1(num As Ulong)
Dim As Long numFn2 = Fn2()
Print Using "Fn2 returned & with num = &"; numFn2; num
End Sub
Fn1(87)
Sleep
- Output:
Caught 7 frames using stack capture 7) 4016FE 6) 40185E 5) 4015BA 4) 4013B4 3) 40150B 2) 7FFBD6AC7614 1) 7FFBD7B226A1 Fn2 returned 7 with num = 87
Go
package main
import (
"fmt"
"runtime"
)
func main() {
stackTrace := make([]byte, 1024)
n := runtime.Stack(stackTrace, true)
stackTrace = stackTrace[:n]
fmt.Printf("%s\n", stackTrace)
fmt.Printf("(%d bytes)\n", len(stackTrace))
}
outputs:
goroutine 16 [running]: main.main() /tmpfs/gosandbox-efa5a722_5cfcad46_bebc14b4_0486dec5_bd9e34fc/prog.go:10 +0xa0 goroutine 19 [runnable]: runfinq() /tmp/sandbox/go/src/pkg/runtime/mgc0.c:2606 runtime.goexit() /tmp/sandbox/go/src/pkg/runtime/proc.c:1445 (259 bytes)
Groovy
Solution:
def rawTrace = { Thread.currentThread().stackTrace }
Test: (demonstrates, among other things, continued execution after generating stack trace)
def trace = rawTrace().collect {
def props = it.properties
def keys = (it.properties.keySet() - (new Object().properties.keySet()))
props.findAll{ k, v -> k in keys }
}
def propNames = trace[0].keySet().sort()
def propWidths = propNames.collect { name -> [name, trace.collect{ it[name].toString() }].flatten()*.size().max() }
propNames.eachWithIndex{ name, i -> printf("%-${propWidths[i]}s ", name) }; println ''
propWidths.each{ width -> print('-' * width + ' ') }; println ''
trace.each {
propNames.eachWithIndex{ name, i -> printf("%-${propWidths[i]}s ", it[name].toString()) }; println ''
}
Output:
className fileName lineNumber methodName nativeMethod ----------------------------------------------------------------- --------------------------------- ---------- ------------------------------- ------------ java.lang.Thread Thread.java 1479 getStackTrace false sun.reflect.NativeMethodAccessorImpl NativeMethodAccessorImpl.java -2 invoke0 true sun.reflect.NativeMethodAccessorImpl NativeMethodAccessorImpl.java 39 invoke false sun.reflect.DelegatingMethodAccessorImpl DelegatingMethodAccessorImpl.java 25 invoke false java.lang.reflect.Method Method.java 597 invoke false org.codehaus.groovy.reflection.CachedMethod CachedMethod.java 90 invoke false groovy.lang.MetaMethod MetaMethod.java 233 doMethodInvoke false groovy.lang.MetaClassImpl$GetBeanMethodMetaProperty MetaClassImpl.java 3465 getProperty false org.codehaus.groovy.runtime.callsite.GetEffectivePojoPropertySite GetEffectivePojoPropertySite.java 61 getProperty false org.codehaus.groovy.runtime.callsite.AbstractCallSite AbstractCallSite.java 227 callGetProperty false ConsoleScript38$_run_closure1 ConsoleScript38 1 doCall false sun.reflect.NativeMethodAccessorImpl NativeMethodAccessorImpl.java -2 invoke0 true sun.reflect.NativeMethodAccessorImpl NativeMethodAccessorImpl.java 39 invoke false sun.reflect.DelegatingMethodAccessorImpl DelegatingMethodAccessorImpl.java 25 invoke false java.lang.reflect.Method Method.java 597 invoke false org.codehaus.groovy.reflection.CachedMethod CachedMethod.java 90 invoke false groovy.lang.MetaMethod MetaMethod.java 233 doMethodInvoke false org.codehaus.groovy.runtime.metaclass.ClosureMetaClass ClosureMetaClass.java 272 invokeMethod false groovy.lang.MetaClassImpl MetaClassImpl.java 885 invokeMethod false org.codehaus.groovy.runtime.callsite.PogoMetaClassSite PogoMetaClassSite.java 66 callCurrent false org.codehaus.groovy.runtime.callsite.CallSiteArray CallSiteArray.java 46 defaultCallCurrent false org.codehaus.groovy.runtime.callsite.AbstractCallSite AbstractCallSite.java 133 callCurrent false org.codehaus.groovy.runtime.callsite.AbstractCallSite AbstractCallSite.java 141 callCurrent false ConsoleScript38$_run_closure1 ConsoleScript38 -1 doCall false sun.reflect.NativeMethodAccessorImpl NativeMethodAccessorImpl.java -2 invoke0 true sun.reflect.NativeMethodAccessorImpl NativeMethodAccessorImpl.java 39 invoke false sun.reflect.DelegatingMethodAccessorImpl DelegatingMethodAccessorImpl.java 25 invoke false java.lang.reflect.Method Method.java 597 invoke false org.codehaus.groovy.reflection.CachedMethod CachedMethod.java 90 invoke false groovy.lang.MetaMethod MetaMethod.java 233 doMethodInvoke false org.codehaus.groovy.runtime.metaclass.ClosureMetaClass ClosureMetaClass.java 272 invokeMethod false groovy.lang.MetaClassImpl MetaClassImpl.java 885 invokeMethod false org.codehaus.groovy.runtime.callsite.PogoMetaClassSite PogoMetaClassSite.java 39 call false org.codehaus.groovy.runtime.callsite.CallSiteArray CallSiteArray.java 42 defaultCall false org.codehaus.groovy.runtime.callsite.AbstractCallSite AbstractCallSite.java 108 call false org.codehaus.groovy.runtime.callsite.AbstractCallSite AbstractCallSite.java 112 call false ConsoleScript38 ConsoleScript38 3 run false groovy.lang.GroovyShell GroovyShell.java 266 runScriptOrMainOrTestOrRunnable false groovy.lang.GroovyShell GroovyShell.java 517 run false groovy.lang.GroovyShell GroovyShell.java 172 run false groovy.lang.GroovyShell$run null -1 call false groovy.ui.Console$_runScriptImpl_closure16 Console.groovy 910 doCall false sun.reflect.GeneratedMethodAccessor232 null -1 invoke false sun.reflect.DelegatingMethodAccessorImpl DelegatingMethodAccessorImpl.java 25 invoke false java.lang.reflect.Method Method.java 597 invoke false org.codehaus.groovy.reflection.CachedMethod CachedMethod.java 90 invoke false groovy.lang.MetaMethod MetaMethod.java 233 doMethodInvoke false org.codehaus.groovy.runtime.metaclass.ClosureMetaClass ClosureMetaClass.java 272 invokeMethod false groovy.lang.MetaClassImpl MetaClassImpl.java 885 invokeMethod false org.codehaus.groovy.runtime.callsite.PogoMetaClassSite PogoMetaClassSite.java 66 callCurrent false org.codehaus.groovy.runtime.callsite.AbstractCallSite AbstractCallSite.java 141 callCurrent false groovy.ui.Console$_runScriptImpl_closure16 Console.groovy -1 doCall false sun.reflect.GeneratedMethodAccessor231 null -1 invoke false sun.reflect.DelegatingMethodAccessorImpl DelegatingMethodAccessorImpl.java 25 invoke false java.lang.reflect.Method Method.java 597 invoke false org.codehaus.groovy.reflection.CachedMethod CachedMethod.java 90 invoke false groovy.lang.MetaMethod MetaMethod.java 233 doMethodInvoke false org.codehaus.groovy.runtime.metaclass.ClosureMetaClass ClosureMetaClass.java 272 invokeMethod false groovy.lang.MetaClassImpl MetaClassImpl.java 885 invokeMethod false groovy.lang.Closure Closure.java 405 call false groovy.lang.Closure Closure.java 399 call false groovy.lang.Closure Closure.java 483 run false java.lang.Thread Thread.java 662 run false
Icon and Unicon
This Icon solution uses Unicon extensions. An Icon only version has not been provided.
the following code for buildStackTrace in Utils is taken verbatim and shown below the main program
import Utils # for buildStackTrace
procedure main()
g()
write()
f()
end
procedure f()
g()
end
procedure g()
# Using 1 as argument omits the trace of buildStackTrace itself
every write("\t",!buildStackTrace(1))
end
#<p>
# Compute the current stack trace. Starting at level <i>n</i> above
# the current procedure. Here, <i>n</i> defaults to 0, which will
# include this procedure in the stack trace.
# <i>ce</i> defaults to ¤t.
# <i>This only works with newer versions of Unicon!</i>
# <[generates the stacktrace from current call back to first
# in the co-expression]>
#</p>
procedure buildStackTrace(n:0, # starting distance from this call
ce # co-expr to trace stack in [¤t]
)
local L
/ce := ¤t
L := []; n -:= 1
while pName := image(proc(ce, n+:=1)) do {
fName := keyword("&file",ce,n) | "no file name"
fLine := keyword("&line",ce,n) | "no line number"
put(L, pName||" ["||fName||":"||fLine||"]" )
}
return L
end
The output of this example is:
procedure g [Stacktrace.icn:13] procedure main [Stacktrace.icn:2] procedure g [Stacktrace.icn:13] procedure f [Stacktrace.icn:8] procedure main [Stacktrace.icn:4]
J
J's stack can be accessed only when suspension has been enabled. When suspension has not been enabled, break points will not work, errors will bubble out to the top level, and the stack data structure will not be available.
To enable suspension and record subsequent stack frames:
13!:0]1
To retrieve a current stack trace:
13!:13''
See also: http://www.jsoftware.com/help/dictionary/dx013.htm
Example:
f=:g
g=:13!:13 bind ''
f 7 NB. empty stack trace because debugging has not been enabled
13!:0]1
f 7
┌─┬─┬─┬─┬─────────────┬┬───┬──┬─┐
│g│0│0│3│13!:13@(''"_)││┌─┐│ │ │
│ │ │ │ │ │││7││ │ │
│ │ │ │ │ ││└─┘│ │ │
├─┼─┼─┼─┼─────────────┼┼───┼──┼─┤
│f│0│0│3│g ││┌─┐│ │ │
│ │ │ │ │ │││7││ │ │
│ │ │ │ │ ││└─┘│ │ │
└─┴─┴─┴─┴─────────────┴┴───┴──┴─┘
Technical note: the stack trace is not displayed, here, until after the stack has been discarded. This is because we have returned the stack trace as a result and relied on J's implicit display of the result of an expression to display the stack trace.
Java
public class StackTracer {
public static void printStackTrace() {
StackTraceElement[] elems = Thread.currentThread().getStackTrace();
System.out.println("Stack trace:");
for (int i = elems.length-1, j = 2 ; i >= 3 ; i--, j+=2) {
System.out.printf("%" + j + "s%s.%s%n", "",
elems[i].getClassName(), elems[i].getMethodName());
}
}
}
Demonstration code:
public class StackTraceDemo {
static void inner() {
StackTracer.printStackTrace();
}
static void middle() {
inner();
}
static void outer() {
middle();
}
public static void main(String[] args) {
outer();
}
}
Output:
Stack trace: StackTraceDemo.main StackTraceDemo.outer StackTraceDemo.middle StackTraceDemo.inner
JavaScript
There is no standard way to do this, but some implementations provide it.
try {
throw new Error;
} catch(e) {
alert(e.stack);
}
The following version works in many browsers but it infinitely loops when there is recursion:
function foo () {
var stack = "Stack trace:";
for (var f = arguments.callee // current function
; f; f = f.caller) {
stack += "\n" + f.name;
}
alert(stack);
}
foo();
Julia
f() = g()
g() = println.(stacktrace())
f()
- Output:
g() at Stack_traces.jl:5 f() at Stack_traces.jl:4 include_string(::String, ::String) at loading.jl:515 include_string(::Module, ::String, ::String) at Compat.jl:464 (::Atom.##57#60{String,String})() at eval.jl:74 withpath(::Atom.##57#60{String,String}, ::String) at utils.jl:30 withpath(::Function, ::String) at eval.jl:38 macro expansion at eval.jl:72 [inlined] (::Atom.##56#59{Dict{String,Any}})() at task.jl:80
Kotlin
// version 1.1.2 (stacktrace.kt which compiles to StacktraceKt.class)
fun myFunc() {
println(Throwable().stackTrace.joinToString("\n"))
}
fun main(args:Array<String>) {
myFunc()
println("\nContinuing ... ")
}
- Output:
StacktraceKt.myFunc(stacktrace.kt:4) StacktraceKt.main(stacktrace.kt:8) Continuing ...
Lang
# fn.getStackTrace() returns a text-based stack trace
fp.printStackTrace = () -> fn.println(fn.getStackTrace())
# Example
fp.f1 = () -> {
fn.println(F1:)
fp.printStackTrace()
}
fp.f2 = () -> {
fn.println(F2:)
fp.printStackTrace()
fp.f1()
}
fp.f2()
fn.combA0(fp.f2)
# Partially called combinator functions' names are represented as "<comb...-func(...)>"
fn.combA(fn.combC(fn.combAE(), x), fp.f2)
- Output:
The file paths were redacted. If ":x" is outputted as the line number, no line number information is available (e.g. If Lang is implemented with an interpreter, predefined functions are written in the host language)
F2: at "[redacted]:x" in function "getStackTrace" at "[redacted]:2" in function "fp.printStackTrace" at "[redacted]:11" in function "fp.f2" at "[redacted]:14" in function "main" F1: at "[redacted]:x" in function "getStackTrace" at "[redacted]:2" in function "fp.printStackTrace" at "[redacted]:7" in function "fp.f1" at "[redacted]:12" in function "fp.f2" at "[redacted]:14" in function "main" F2: at "[redacted]:x" in function "getStackTrace" at "[redacted]:2" in function "fp.printStackTrace" at "[redacted]:11" in function "fp.f2" at "[redacted]:x" in function "combA0" at "[redacted]:16" in function "main" F1: at "[redacted]:x" in function "getStackTrace" at "[redacted]:2" in function "fp.printStackTrace" at "[redacted]:7" in function "fp.f1" at "[redacted]:12" in function "fp.f2" at "[redacted]:x" in function "combA0" at "[redacted]:16" in function "main" F2: at "[redacted]:x" in function "getStackTrace" at "[redacted]:2" in function "fp.printStackTrace" at "[redacted]:11" in function "fp.f2" at "[redacted]:x" in function "<combAE-func()>" at "[redacted]:x" in function "<combC-func(<combAE-func()>, <arg>)>" at "[redacted]:x" in function "combA" at "[redacted]:19" in function "main" F1: at "[redacted]:x" in function "getStackTrace" at "[redacted]:2" in function "fp.printStackTrace" at "[redacted]:7" in function "fp.f1" at "[redacted]:12" in function "fp.f2" at "[redacted]:x" in function "<combAE-func()>" at "[redacted]:x" in function "<combC-func(<combAE-func()>, <arg>)>" at "[redacted]:x" in function "combA" at "[redacted]:19" in function "main"
Lasso
By default Lasso tracks the file path, line and column numbers. You can create a trace method to track type and method names illustrated below or use one of the public libraries like L-Debug [1].
// Define our own trace method
define trace => {
local(gb) = givenblock
// Set a depth counter
var(::_tracedepth)->isnota(::integer) ? $_tracedepth = 0
handle => {$_tracedepth--}
// Only output when supplied a capture
#gb ? stdoutnl(
// Indent
('\t' * $_tracedepth++) +
// Type + Method
#gb->self->type + '.' + #gb->calledname +
// Call site file
': ' + #gb->home->callsite_file +
// Line number and column number
' (line '+#gb->home->callsite_line + ', col ' + #gb->home->callsite_col +')'
)
return #gb()
}
- Use Trace
define stackexample => type {
public oncreate => trace => { return self }
public inner => trace => { }
public middle => trace => { .inner }
public outer => trace => { .middle }
}
stackexample->outer
- Output:
stackexample.oncreate: adminapp_lasso_runner_thread (line 2, col 24) stackexample.outer: adminapp_lasso_runner_thread (line 5, col 21) stackexample.middle: adminapp_lasso_runner_thread (line 4, col 22) stackexample.inner: adminapp_lasso_runner_thread (line 3, col 21)
Lua
function Inner( k )
print( debug.traceback() )
print "Program continues..."
end
function Middle( x, y )
Inner( x+y )
end
function Outer( a, b, c )
Middle( a*b, c )
end
Outer( 2, 3, 5 )
stack traceback: ./prog:4: in function 'Inner' ./prog:9: in function 'Middle' ./prog:13: in function 'Outer' ./prog:16: in main chunk [C]: ? Program continues...
Mathematica /Wolfram Language
Built-in function Stack does the task, example I:
f[g[1, Print[Stack[]]; 2]]
prints, gives back:
{f,g,CompoundExpression,Print}
f[g[1, 2]]
Example II:
f[g[1, Print[Stack[_]]; 2]]
prints, gives back:
{f[g[1,Print[Stack[_]];2]],g[1,Print[Stack[_]];2],Print[Stack[_]];2,Print[Stack[_]]}
f[g[1, 2]]
Related and similar functions are: Trace, TracePrint, TraceScan,TraceDialog, Monitor, StackInhibit, StackBegin, StackComplete. In the manual look for 'guide/SymbolicExecutionHistory'.
Nanoquery
__calls__ contains a list that represents a trace of calls that have been made.
def print_stack()
global __calls__
println "stack trace:"
for i in range(len(__calls__) - 2, 0)
println "\t" + __calls__[i]
end
end
print_stack()
println
for i in range(1, 1)
print_stack()
end
println
println "The program would continue."
- Output:
stack trace: <global>:10 stack trace: <for>:1 <global>:13 The program would continue.
NetRexx
/* NetRexx */
options replace format comments java crossref symbols nobinary
class RStackTraces
method inner() static
StackTracer.printStackTrace()
method middle() static
inner()
method outer() static
middle()
method main(args = String[]) public static
outer()
class RStackTraces.StackTracer
method printStackTrace() public static
elems = Thread.currentThread().getStackTrace()
say 'Stack trace:'
j_ = 2
loop i_ = elems.length - 1 to 2 by -1
say ''.left(j_) || elems[i_].getClassName()'.'elems[i_].getMethodName()
j_ = j_ + 2
end i_
Output:
Stack trace: RStackTraces.main RStackTraces.outer RStackTraces.middle RStackTraces.inner
Nim
In (normal) debug builds stacktraces are enabled, while in release builds stacktraces are disabled by default, but can be enabled like this: nim c -d:release --stacktrace:on --linetrace:on file.nim
proc g() =
# Writes the current stack trace to stderr.
writeStackTrace()
# Or fetch the stack trace entries for the current stack trace:
echo "----"
for e in getStackTraceEntries():
echo e.filename, "@", e.line, " in ", e.procname
proc f() =
g()
f()
- Output:
For a release build:
Traceback (most recent call last) stack_traces.nim(12) stack_traces stack_traces.nim(10) f stack_traces.nim(3) g ---- stack_traces.nim@12 in stack_traces stack_traces.nim@10 in f stack_traces.nim@6 in g
In a debug build the stacktrace contains the full path to the source.
Objective-C
#include <execinfo.h>
void *frames[128];
int len = backtrace(frames, 128);
char **symbols = backtrace_symbols(frames, len);
for (int i = 0; i < len; ++i) {
NSLog(@"%s", symbols[i]);
}
free(symbols);
Or in Mac OS X 10.6+:
NSArray *symbols = [NSThread callStackSymbols];
for (NSString *symbol in symbols) {
NSLog(@"%@", symbol);
}
OCaml
let div a b = a / b
let () =
try let _ = div 3 0 in ()
with e ->
prerr_endline(Printexc.to_string e);
Printexc.print_backtrace stderr;
;;
outputs:
Division_by_zero Raised by primitive operation at file "test.ml", line 4, characters 14-21
By Environment Variable
Another way is to set the environment variable OCAMLRUNPARAM to b, for example you can add in your ~/.bashrc file this line:
export OCAMLRUNPARAM='b'
Then the code doesn't need additional statements:
let div a b = a / b
let () =
let _ = div 3 0 in ()
;;
outputs:
Fatal error: exception Division_by_zero Raised at file "test.ml", line 4, characters 10-17
Oforth
Stack trace is only available :
1) When an exception is raised
2) And when oforth debug mode is set, using --D command line option.
Otherwise no stack trace is available nor printed.
: f1 Exception throw("An exception") ;
Integer method: f2 self f1 ;
: f3 f2 ;
: f4 f3 ;
10 f4
- Output:
[1:interpreter] Exception : An exception Into Method #throw self = (Exception) An exception Into Method #throw self = (Class) Exception Into Function #f1 Into Method #f2 self = (Integer) 10 Into Function #f3
OxygenBasic
'32bit x86
static string Report
macro ReportStack(n)
'===================
'
scope
'
static sys stack[0X100],stackptr,e
'
'CAPTURE IMAGE OF UP TO 256 ENTRIES
'
'
mov eax,n
cmp eax,0x100
(
jle exit
mov eax,0x100 'UPPER LIMIT
)
mov e,eax
mov stackptr,esp
lea edx,stack
mov ecx,e
mov esi,esp
(
mov eax,[esi]
mov [edx],eax
add esi,4
add edx,4
dec ecx
jg repeat
)
sys i
string cr=chr(13)+chr(10), tab=chr(9)
'
for i=1 to e
report+=hex(stackptr+(i-1)*4,8) tab hex(i-1,2) tab hex(stack[i],8) cr
next
'
end scope
'
end macro
'====
'TEST
'====
function foo()
'=============
push 0x44556677
push 0x33445566
push 0x22334455
push 0x11223344
ReportStack(8)
end function
Report+="Trace inside foo
"
foo()
print report
'putfile "s.txt",Report
/*
RESULT:
Trace inside foo
0017FE00 00 11223344
0017FE04 01 22334455
0017FE08 02 33445566
0017FE0C 03 44556677
0017FE10 04 005EAB1C
0017FE14 05 0017FE40
0017FE18 06 10002D5F
0017FE1C 07 00000000
*/
Oz
System exceptions contain the current stack at the nested feature debug.stack
. For example:
declare
proc {Test}
_ = 1 div 0
end
in
try
{Test}
catch E then
{Inspect E}
end
Output:
![Stack trace of a system exception in Oz.](http://static.miraheze.org/rosettacodewiki/0/0e/Oz_stacktrace1.png)
To have such a stack trace in custom exceptions, either indicate this by throwing a record value with a debug:unit
feature or use the Exception module to create exceptions.
To access the stack trace directly, you can use the undocumented internal Debug module. Its getTaskStack
function takes a thread, a depth value and a boolean "verbose" flag. It returns a list of stack frames. Example:
%% make sure that simple function calls are not optimized away
\switch +controlflowinfo
declare
[Debug] = {Link ['x-oz://boot/Debug']}
proc {F} {G} end
proc {G} {H} end
proc {H}
{Inspect {Debug.getTaskStack {Thread.this} 100 true}}
end
in
{F}
Output:
![Stack trace created by the Debug module.](http://static.miraheze.org/rosettacodewiki/d/df/Oz_stacktrace2.png)
PascalABC.NET
procedure qqq;
begin
var st := new System.Diagnostics.StackTrace();
Print(st);
end;
procedure ppp;
begin
qqq;
end;
begin
ppp
end.
- Output:
at Rosetta_StackTrace.Program.qqq() at Rosetta_StackTrace.Program.ppp() at Rosetta_StackTrace.Program.$Main() at Rosetta_StackTrace.Program.Main()
Perl
use Carp 'cluck';
sub g {cluck 'Hello from &g';}
sub f {g;}
f;
This prints:
Hello from &g at Print a Stack Trace line 3 main::g() called at Print a Stack Trace line 4 main::f() called at Print a Stack Trace line 6
Phix
There no standard method of obtaining a stack trace mid-run (as yet, non-fatally that is), but we can quickly cobble something together:
constant W = machine_word(), {RTN,PREVEBP} = iff(W=4?{8,20}:{16,40}) procedure show_stack() sequence symtab, symtabN integer rtn atom prev_ebp #ilASM{ [32] lea edi,[symtab] call :%opGetST -- [edi]=symtab (ie our local:=the real symtab) mov edi,[ebp+20] -- prev_ebp mov eax,[edi+8] -- calling routine no mov [rtn],eax mov eax,edi lea edi,[prev_ebp] call :%pStoreMint [64] lea rdi,[symtab] call :%opGetST -- [rdi]=symtab (ie our local:=the real symtab) mov rdi,[rbp+40] -- prev_ebp mov rax,[rdi+16] -- calling routine no mov [rtn],rax mov rax,rdi lea rdi,[prev_ebp] call :%pStoreMint [] } while rtn!=21 do -- (T_maintls, main top level routine, always present) symtabN = symtab[rtn] ?symtabN[1] prev_ebp = peekNS(prev_ebp+PREVEBP,W,0) rtn = peekNS(prev_ebp+RTN,W,0) end while end procedure procedure three(bool die) if die then ?9/0 else show_stack() end if end procedure procedure two(bool die) three(die) end procedure procedure one(bool die) two(die) end procedure one(0) ?routine_id("dummy") -- see note below one(1)
During compilation, the symbol table entries hold an integer ternary tree index rather than a string name, and normally things are left like that during interpretation (the proper string names are always written out when an executable is created) unless and until a fatal error occurs, or the compiler has evidence that they might be needed, such as that unresolved routine_id("dummy"). The -1 in the output below is that call failing to find any such routine.
- Output:
"three" "two" "one" -1 C:\Program Files (x86)\Phix\e01.exw:59 in procedure three() attempt to divide by 0 die = 1 ... called from C:\Program Files (x86)\Phix\e01.exw:66 in procedure two() die = 1 ... called from C:\Program Files (x86)\Phix\e01.exw:70 in procedure one() die = 1 ... called from C:\Program Files (x86)\Phix\e01.exw:75 Global & Local Variables --> see C:\Program Files (x86)\Phix\ex.err Press Enter...
The first half (up to the -1) is the output from show_stack_trace(), the rest is standard fatal error diagnostics.
For more details of how the latter is actually produced, refer to builtins\VM\pDiagN.e - in particular the conversion of
raw addresses into source code line numbers is not trivial (and not worth trying to replicate here). In fact I didn't just
type all that #ilASM gunk above in, but instead copied it from other source files in builtins\VM.
Alternatively, but only when interpreting (whereas the above works as shown both when interpreting and pre-compiled), the debugger is started with the much saner
trace(1)
though as yet it offers no means of examining the stack trace (resume-ably), the above suggests it should be relative easy to add.
PHP
<?php
class StackTraceDemo {
static function inner() {
debug_print_backtrace();
}
static function middle() {
self::inner();
}
static function outer() {
self::middle();
}
}
StackTraceDemo::outer();
?>
#0 StackTraceDemo::inner() called at [/home/cweiske/Dev/cvs/test/php-stacktrace.php:7] #1 StackTraceDemo::middle() called at [/home/cweiske/Dev/cvs/test/php-stacktrace.php:10] #2 StackTraceDemo::outer() called at [/home/cweiske/Dev/cvs/test/php-stacktrace.php:14]
PicoLisp
PicoLisp doesn't keep full backtrace information at runtime. This is for performance reasons. However, existing variable bindings (environments) can be inspected with the 'env' function, so this can be used to build your own stack frames.
The following is analog to (though simpler than) the built-in 'trace' mechanism. The function '$$' (corresponds to '$' for tracing) is inserted by 'stackAll' into every function and method definition (corresponds to 'traceAll'). Then, when stopping at a 'debug' breakpoint or an error handler, 'dumpStack' can be used to inspect the stack contents.
As this mechanism uses 'let' to hold the stack frames, it is robust also across catch/throw, coroutines and error handling.
(off "Stack")
(de $$ "Prg"
(let "Stack" (cons (cons (car "Prg") (env)) "Stack") # Build stack frame
(set "Stack"
(delq (asoq '"Stack" (car "Stack")) # Remove self-created entries
(delq (asoq '"Prg" (car "Stack"))
(car "Stack") ) ) )
(run (cdr "Prg")) ) ) # Run body
(de stackAll (Excl)
(let *Dbg NIL
(for "X" (all)
(or
(memq "X" Excl)
(memq "X" '($$ @ @@ @@@))
(= `(char "*") (char "X"))
(cond
((= `(char "+") (char "X"))
(for "Y" (pair (val "X"))
(and
(pair "Y")
(fun? (cdr "Y"))
(unless (== '$$ (caaddr "Y"))
(con (cdr "Y")
(list
(cons '$$ (cons (car "Y" "X") (cddr "Y"))) ) ) ) ) ) )
((pair (getd "X"))
(let "Y" @
(unless (== '$$ (caadr "Y"))
(con "Y"
(list (cons '$$ "X" (cdr "Y"))) ) ) ) ) ) ) ) ) )
(de dumpStack ()
(more (reverse (cdr "Stack")))
T )
Test:
(de foo (A B)
(let C 3
(bar (inc 'A) (inc 'B) (inc 'C)) ) )
(de bar (A D E)
(let (A 7 B 8 C 9)
(! println A B C) ) ) # Set a breakpoint before (println A B C)
(stackAll)
: (foo 1 2) # Call 'foo' (println A B C) # Stopped at breakpoint in 'bar' ! (dumpStack) # Dump stack history (foo (A . 1) (B . 2) (@ . T)) # Hit <enter> on each line to continue (bar (B . 3) (C . 4) (A . 2) (D . 3) (E . 4) (@ . T)) -> T ! # Hit <enter> to continue execution 7 8 9 # Output of (println A B C) -> 9 :
PL/I
/* The SNAP option in the ON statement is sufficient to obtain */
/* a traceback. The SYSTEM option specifies that standard */
/* system action is to occur, which resume execution after the */
/* SIGNAL statement. */
on condition(traceback) snap system;
...
signal condition(traceback);
PureBasic
The ShowCallstack()command opens a interactive display allowing viewing of the procedures in the calling path an all their local variables.
Procedure Three()
a=7
ShowCallstack()
CallDebugger
EndProcedure
Procedure Two()
a=4
Three()
EndProcedure
Procedure One()
a=2
Two()
EndProcedure
One()
Python
See the traceback module
import traceback
def f(): return g()
def g(): traceback.print_stack()
f()
Sample output from a session in the Idle IDE:
File "<string>", line 1, in <module> File "C:\Python26\lib\idlelib\run.py", line 93, in main ret = method(*args, **kwargs) File "C:\Python26\lib\idlelib\run.py", line 293, in runcode exec code in self.locals File "C:/Documents and Settings/All Users/Documents/Paddys/traceback.py", line 6, in <module> f() File "C:/Documents and Settings/All Users/Documents/Paddys/traceback.py", line 3, in f def f(): return g() File "C:/Documents and Settings/All Users/Documents/Paddys/traceback.py", line 4, in g def g(): traceback.print_stack()
Quackery
Quackery has two system stacks, the call or return stack and a data stack. The words echoreturn
and echostack
display the current state of them. Each call to a word or nest places two entries on the return stack, a pointer to the word or nest, and an index into that word or nest. These are displayed as the name of the word, or [...]
for an unnamed nest, and the index into the word or nest as an integer. Pairs of entries are displayed wrapped in braces, e.g. {shell 5}
.
This is illustrated with a Quackery shell dialogue. The Quackery shell is written in Quackery, and as can be seen, occupies five pairs of entries on the return stack. After each interaction with the user the data stack is automatically displayed. Here the user opens the Quackery shell, displays the return stack, defines a naive recursive Fibonacci word that displays the return and data stack at the start of each call, and uses it to compute and display the fifth Fibonacci number (5).
> quackery Welcome to Quackery. Enter "leave" to leave the shell. Building extensions. /O> echoreturn ... {[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 0} Stack empty. /O> [ cr echoreturn ... cr echostack ... dup 2 < if done ... dup 1 - swap 2 - ... recurse swap recurse + ] is fib ... Stack empty. /O> 5 fib echo ... {[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 1} Stack: 5 {[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 15} {recurse 1} {fib 1} Stack: 4 3 {[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 15} {recurse 1} {fib 15} {recurse 1} {fib 1} Stack: 4 2 1 {[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 15} {recurse 1} {fib 17} {recurse 1} {fib 1} Stack: 4 1 2 {[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 15} {recurse 1} {fib 17} {recurse 1} {fib 15} {recurse 1} {fib 1} Stack: 4 1 1 0 {[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 15} {recurse 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 1} Stack: 4 1 0 1 {[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 1} Stack: 2 4 {[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 15} {recurse 1} {fib 1} Stack: 2 3 2 {[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 15} {recurse 1} {fib 15} {recurse 1} {fib 1} Stack: 2 3 1 0 {[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 15} {recurse 1} {fib 17} {recurse 1} {fib 1} Stack: 2 3 0 1 {[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 1} Stack: 2 1 3 {[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 15} {recurse 1} {fib 1} Stack: 2 1 2 1 {[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 1} Stack: 2 1 1 2 {[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 15} {recurse 1} {fib 1} Stack: 2 1 1 1 0 {[...] 0} {quackery 1} {[...] 11} {shell 5} {quackery 1} {[...] 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 17} {recurse 1} {fib 1} Stack: 2 1 1 0 1 5 Stack empty. /O>
R
foo <- function()
{
bar <- function()
{
sys.calls()
}
bar()
}
foo()
[[1]] foo() [[2]] bar()
traceback() returns the callstack of the last unhandled (i.e. not in try/catch) error.
You can also see the stack trace when a function is called (or as it exits) using the trace function.
trace("foo", recover)
foo()
Tracing foo() on entry Enter a frame number, or 0 to exit 1: foo() Selection:
Racket
#lang racket
;; To see these calls we do two things: mutate the binding to prevent
;; Racket from inlining the value; use a (void) call at the end so the
;; calls are not tail calls (which will otherwise not show on the
;; stack).
(define foo #f)
(set! foo (λ() (bar) (void)))
(define bar #f)
(set! bar (λ() (show-stacktrace) (void)))
(define (show-stacktrace)
(for ([s (continuation-mark-set->context (current-continuation-marks))]
[i (in-naturals)])
;; show just the names, not the full source information
(when (car s) (printf "~s: ~s\n" i (car s)))))
(foo)
Output:
0: show-stacktrace 1: bar 2: foo 3: |[running body]|
Raku
(formerly Perl 6)
sub g { say Backtrace.new.concise }
sub f { g }
sub MAIN { f }
- Output:
in sub g at bt:1 in sub f at bt:2 in sub MAIN at bt:3
Raven
[1 2 3 4] 42 { 'a' 1 'b' 2 'c' 3 } 34.1234 ( -1 -2 -3 ) "The quick brown fox" FILE dump
- Output:
hash (4 items) usage => 1 index => 65836 flags => 256 buffer => " 98252f8 73 74 64 69 6e 00 stdin." The quick brown fox 28.1234 hash (3 items) a => 1 b => 2 c => 3 42 list (4 items) 0 => 1 1 => 2 2 => 3 3 => 4
REXX
/* call stack */
say 'Call A'
call A '123'
say result
exit 0
A:
say 'Call B'
call B '456'
say result
return ARG(1)
B:
say 'Call C'
call C '789'
say result
return ARG(1)
C:
call callstack
return ARG(1)
callstack: procedure
getcallstack(cs.)
say 'Dump call stack with' cs.0 'items'
do i = 1 to cs.0
parse var cs.i line func
say format(line, 3) ':' left(func, 9) ': source "' || sourceline(line) || '"'
end
return cs.0
- Output:
prompt$ regina callstack.rexx Call A Call B Call C Dump call stack with 4 items 20 : CALLSTACK : source "call callstack" 15 : C : source "call C '789'" 9 : B : source "call B '456'" 3 : A : source "call A '123'" 789 456 123
Regina will also dump a call stack during certain error conditions. For instance, if the code listing above raised an unhandled signal (simulated here with "signal noname" in routine C). This is not a recoverable scenario though, and the program will not continue.
C:
signal noname
call callstack
return ARG(1)
- Output:
prompt$ regina callstack.rexx Call A Call B Call C 20 +++ signal noname 15 +++ call C '789' 9 +++ call B '456' 3 +++ call A '123' Error 16 running "/demos/callstack.rexx", line 20: Label not found Error 16.1: Label "NONAME" not found
Ruby
def outer(a,b,c)
middle a+b, b+c
end
def middle(d,e)
inner d+e
end
def inner(f)
puts caller(0)
puts "continuing... my arg is #{f}"
end
outer 2,3,5
$ ruby stacktrace.rb stacktrace.rb:10:in `inner' stacktrace.rb:6:in `middle' stacktrace.rb:2:in `outer' stacktrace.rb:14 continuing... my arg is 13
Exceptions caught in a rescue clause contain the trace information:
def outer(a,b,c)
middle a+b, b+c
end
def middle(d,e)
inner d+e
end
def inner(f)
raise
puts "this will not be printed"
end
begin
outer 2,3,5
rescue Exception => e
puts e.backtrace
end
puts "continuing after the rescue..."
stacktrace.rb:10:in `inner' stacktrace.rb:6:in `middle' stacktrace.rb:2:in `outer' stacktrace.rb:15 continuing after the rescue...
Thread has a backtrace method:
p Thread.current.backtrace
Scala
While the code on the Java example works with Scala too, the code below is an alternative. Which, by the way, could be used from Java as well, with minor modifications.
def callStack = try { error("exception") } catch { case ex => ex.getStackTrace drop 2 }
def printStackTrace = callStack drop 1 /* don't print ourselves! */ foreach println
Usage example:
scala> def f1 = printStackTrace f1: Unit scala> def f2 = f1 f2: Unit scala> def f3 = f2 f3: Unit scala> f3 line40$object$$iw$$iw$.f1(<console>:6) line41$object$$iw$$iw$.f2(<console>:7) line42$object$$iw$$iw$.f3(<console>:8) line43$object$$iw$$iw$.<init>(<console>:10) line43$object$$iw$$iw$.<clinit>(<console>) RequestResult$line43$object$.<init>(<console>:4) RequestResult$line43$object$.<clinit>(<console>) RequestResult$line43$object.result(<console>) sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) java.lang.reflect.Method.invoke(Method.java:597) scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1$$anonfun$apply$13.apply(Interpreter.scala:788) scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1$$anonfun$apply$13.apply(Interpreter.scala:788) scala.util.control.Exception$Catch.apply(Exception.scala:79) scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1.apply(Interpreter.scala:787) scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1.apply(Interpreter.scala:787) scala.util.control.Exception$Catch.apply(Exception.scala:79) scala.tools.nsc.Interpreter$Request.loadAndRun(Interpreter.scala:786) scala.tools.nsc.Interpreter.interpret(Interpreter.scala:435) scala.tools.nsc.Interpreter.interpret(Interpreter.scala:425) scala.tools.nsc.InterpreterLoop.interpretStartingWith(InterpreterLoop.scala:331) scala.tools.nsc.InterpreterLoop.command(InterpreterLoop.scala:308) scala.tools.nsc.InterpreterLoop.processLine$1(InterpreterLoop.scala:205) scala.tools.nsc.InterpreterLoop.repl(InterpreterLoop.scala:223) scala.tools.nsc.InterpreterLoop.main(InterpreterLoop.scala:379) scala.tools.nsc.MainGenericRunner$.createLoop$1(MainGenericRunner.scala:119) scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:144) scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
Note that the stack is an array of StackTraceElement, on which it is possible to get the class and method name as well as the file name and line number of its definition.
Slate
The following #printCurrentStack is already defined in the base slate image but it is replicated here.
slate[1]> d@(Debugger traits) printCurrentStack &limit: limit &stream: out &showLocation: showLocation
[
d clone `>> [baseFramePointer: (d interpreter framePointerOf: #printCurrentStack).
buildFrames.
printBacktrace &limit: limit &stream: out &showLocation: showLocation ]
].
Defining function 'printCurrentStack' on: 'Debugger traits'
[printCurrentStack &limit: &stream: &showLocation:]
The output from calling the function:
slate[2]> Debugger printCurrentStack.
Backtrace (method @ source):
frame: 0 [printCurrentStack &limit: &stream: &showLocation:] @ stdin:0
frame: 1 [evaluateIn: &optionals:] @ src/mobius/syntax.slate:180
frame: 2 [(arity: 0)] @ src/lib/repl.slate:155
frame: 3 [on:do:] @ src/core/condition.slate:43
frame: 4 [(arity: 0)] @ src/lib/repl.slate:147
frame: 5 [handlingCases:] @ src/core/condition.slate:64
frame: 6 [interpretHook:] @ src/lib/repl.slate:42
frame: 7 [(arity: 0)] @ src/lib/repl.slate:139
frame: 8 [enter] @ src/lib/repl.slate:135
frame: 9 [start &resource:] @ src/lib/repl.slate:185
frame: 10 [start] @ src/mobius/prelude.slate:38
Nil
Smalltalk
A backtrace is normally sent when some error occurs; however, it can be "forced":
Object subclass: Container [
Container class >> outer: a and: b and: c [
self middle: (a+b) and: (b+c)
]
Container class >> middle: x and: y [
self inner: (x*y)
]
Container class >> inner: k [
Smalltalk backtrace
]
].
Container outer: 2 and: 3 and: 5.
'Anyway, we continue with it' displayNl.
Output:
Container class>>inner: Container class>>middle:and: Container class>>outer:and:and: UndefinedObject>>executeStatements Anyway, we continue with it
Tcl
proc printStackTrace {} {
puts "Stack trace:"
for {set i 1} {$i < [info level]} {incr i} {
puts [string repeat " " $i][info level $i]
}
}
Demonstration code:
proc outer {a b c} {
middle [expr {$a+$b}] [expr {$b+$c}]
}
proc middle {x y} {
inner [expr {$x*$y}]
}
proc inner k {
printStackTrace
}
outer 2 3 5
Produces this output:
Stack trace: outer 2 3 5 middle 5 8 inner 40
VBA
In VBE the VBA Editor hitting Control+L while stepping through your code in debug mode will pop up a window which displays the call stack.
Wren
You can always force Wren CLI to produce a stack trace by either engineering an actual error or calling Fiber.abort() at the relevant part of the script.
However, it is not possible to continue execution of the script afterwards. Whilst one can 'catch' such an error using Fiber.try() this will only allow you to inspect the error itself, not the chain of function calls that led up to it.
var func2 = Fn.new {
Fiber.abort("Forced error.")
}
var func1 = Fn.new {
func2.call()
}
func1.call()
- Output:
Forced error. [./stack_trace line 2] in new(_) block argument [./stack_trace line 6] in new(_) block argument [./stack_trace line 9] in (script)
zkl
fcn f{println("F");vm.stackTrace().println()} fcn g{println("G")}
f();g();
- Output:
stackTrace just returns a string. You don't get to futz with the stack.
F Stack trace for VM#1 (): Cmd.f@stackTrace addr:4 args(0) reg(0) Cmd.__constructor addr:3 args(0) reg(0) R startup.__constructor addr:2242 args(0) reg(1) ER startup.__constructor addr:2178 args(0) reg(22) G
- Programming Tasks
- Programming environment operations
- Ada
- AutoHotkey
- BASIC
- C
- C sharp
- Clojure
- Common Lisp
- D
- DWScript
- Elena
- Elixir
- Erlang
- F Sharp
- Factor
- Forth
- Fortran
- FreeBASIC
- Go
- Groovy
- Unicon
- Unicon Code Library
- J
- Java
- JavaScript
- Julia
- Kotlin
- Lang
- Lasso
- Lua
- Mathematica
- Wolfram Language
- Nanoquery
- NetRexx
- Nim
- Objective-C
- OCaml
- Oforth
- OxygenBasic
- Oz
- PascalABC.NET
- Perl
- Phix
- PHP
- PicoLisp
- PL/I
- PureBasic
- Python
- Quackery
- R
- Racket
- Raku
- Raven
- REXX
- Ruby
- Scala
- Slate
- Smalltalk
- Tcl
- VBA
- Wren
- Zkl
- AWK/Omit
- GUISS/Omit
- Locomotive Basic/Omit
- Lotus 123 Macro Scripting/Omit
- PARI/GP/Omit
- TI-83 BASIC/Omit
- TI-89 BASIC/Omit
- M4/Omit
- ZX Spectrum Basic/Omit