Stack traces: Difference between revisions

From Rosetta Code
Content added Content deleted
mNo edit summary
Line 319: Line 319:
Raised by primitive operation at file "test.ml", line 4, characters 14-21
Raised by primitive operation at file "test.ml", line 4, characters 14-21


== By Environment Variable ==
=== 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:
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'
export OCAMLRUNPARAM='b'

Revision as of 19:16, 9 August 2009

Task
Stack traces
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.

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.

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

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. <lang Ada> with Ada.Text_IO; use Ada.Text_IO; with GNAT.Traceback; use GNAT.Traceback; with GNAT.Traceback.Symbolic; use GNAT.Traceback.Symbolic;

procedure Test_Stack_Trace is

  procedure Call_Stack is
     Trace  : Tracebacks_Array (1..1_000);
     Length : Natural;
  begin
     Call_Chain (Trace, Length);
     Put_Line (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; </lang> 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

This example is incorrect. Please fix the code and remove this message.

Details: 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'
recently executed lines are available through ListLines
ListLines can be turned on or off... with:
ListLines, On|Off <lang autohotkey>f() return

f() { return g() }

g() { ListLines msgbox, lines recently executed x = local to g ListVars msgbox, variable bindings }

  1. Persistent</lang>

BASIC

Works with: Beta BASIC version 3.0


Works with: SAM 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

Works with: POSIX
Works with: GNU

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".

<lang c>#include <stdio.h>

  1. include <stdlib.h>
  2. include <unistd.h>
  1. include <execinfo.h>
  1. 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;

}</lang>

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]

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:

<lang lisp>(swank-backend:call-with-debugging-environment

 (lambda ()
   (swank:backtrace 0 nil)))</lang>

Here are a few lines of the result when running under SBCL, the rest is omitted since it's long and boring: <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>)")
(2 "(SWANK::EVAL-REGION \"(swank-backend:call-with-debugging-environment\\n            (lambda ()\\n              (swank:backtrace 0 nil)))\\n\\n\")")
(3 "((LAMBDA ()))") ...)</lang>

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):

<lang lisp>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}>)</lang>

SBCL's backtraces consist entirely of lists of the form (function-name args...).

Java

Works with: Java version 1.5+

<lang java5>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()); }

   }

}</lang> Demonstration code: <lang java5>public class StackTraceDemo {

   static void inner() {

StackTracer.printStackTrace();

   }
   static void middle() {

inner();

   }
   static void outer() {

middle();

   }
   public static void main(String[] args) {

outer();

   }

}</lang> Output:

Stack trace:
  StackTraceDemo.main
    StackTraceDemo.outer
      StackTraceDemo.middle
        StackTraceDemo.inner

Mathematica

Built-in function Stack does the task, example I: <lang Mathematica>

f[g[1, Print[Stack[]]; 2]]

</lang> prints, gives back: <lang Mathematica>

{f,g,CompoundExpression,Print}
f[g[1, 2]]

</lang> Example II: <lang Mathematica> f[g[1, Print[Stack[_]]; 2]] </lang> prints, gives back: <lang Mathematica>

{f[g[1,Print[Stack[_]];2]],g[1,Print[Stack[_]];2],Print[Stack[_]];2,Print[Stack[_]]}
f[g[1, 2]]

</lang> Related and similar functions are: Trace, TracePrint, TraceScan,TraceDialog, Monitor, StackInhibit, StackBegin, StackComplete. In the manual look for 'guide/SymbolicExecutionHistory'.

OCaml

Works with: OCaml version 3.11+

<lang 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;
</lang>

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 additionnal statements: <lang ocaml>let div a b = a / b

let () =

 let _ = div 3 0 in ()
</lang>

outputs:

Fatal error: exception Division_by_zero
Raised at file "test.ml", line 4, characters 10-17

Perl

<lang perl>use Carp 'cluck';

sub g {cluck 'Hello from &g';} sub f {g;}

f;</lang>

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

PHP

<lang php><?php class StackTraceDemo {

   static function inner() {
       debug_print_backtrace();
   }
   static function middle() {
       self::inner();
   }
   static function outer() {
       self::middle();
   }

}

StackTraceDemo::outer(); ?> </lang>

#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]

Python

See the traceback module <lang python>import traceback

def f(): return g() def g(): traceback.print_stack()

f()</lang>

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()

R

<lang R> foo <- function() {

  bar <- function()
  {
    sys.calls()
  }  
  bar()

}

foo() </lang>

[[1]]
foo()
[[2]]
bar()

traceback() returns the callstack of the last unhandled (i.e. not in try/catch) error.

Ruby

<lang 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</lang>

$ 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: <lang ruby>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..."</lang>

stacktrace.rb:10:in `inner'
stacktrace.rb:6:in `middle'
stacktrace.rb:2:in `outer'
stacktrace.rb:15
continuing after the rescue...

Slate

The following #printCurrentStack is already defined in the base slate image but it is replicated here.

<lang slate> 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:] </lang>

The output from calling the function:

<lang slate> 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 </lang>

Smalltalk

Works with: GNU Smalltalk

A backtrace is normally sent when some error occurs; however, it can be "forced":

<lang smalltalk>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.</lang>

Output:

Container class>>inner:
Container class>>middle:and:
Container class>>outer:and:and:
UndefinedObject>>executeStatements
Anyway, we continue with it

Tcl

<lang tcl>proc printStackTrace {} {

   puts "Stack trace:"
   for {set i 1} {$i < [info level]} {incr i} {
       puts [string repeat "  " $i][info level $i]
   }

}</lang> Demonstration code: <lang tcl>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</lang> Produces this output:

Stack trace:
  outer 2 3 5
    middle 5 8
      inner 40