Subject: [clisp-list] How to get the call stack?

From: "John K. Hinsdale" <[email-masked]>
Date: Wed Oct 2 10:42:58 2002

I'm writing a Web CGI app in CLISP and at certain points where an
error is signaled I would like to display the eval/call stack on the
Web along with the rest of the error message.  Ideally I would like to
be able to get, for each frame, at least function name, but if
possible also the arguments and even better the source file and line
number.

Any way to do this?  Or is there a better way to handle this
situation?  The process will be running as a child of the Web server
so it is not obvious to me how to detach it or save state so I could
examine it w/ an interactive debugger.

I'm already aware of solutions that involve wrapping DEFUN in a macro
so I can maintain the current stack info myself, but (a) I don't want
to have to write that and (b) I don't want all that overhead with
every function call, even in a development environment.

Is there a way to get a data structure containing such info? Poking
around w/ APROPPOS, I see EXT:SHOW-STACK but it's not documented and I
think more for CLISP-developer use.

---
John Hinsdale, Alma Mater Software, Inc., Tarrytown, NY 10591-3710 USA
[email-masked] | http://www.alma.com/staff/hin | +1 914 631 4690

---

From: Sam Steingold <[email-masked]>
Date: Wed Oct 2 11:40:09 2002

> * In message <[email-masked]>
> * On the subject of "[clisp-list] How to get the call stack?"
> * Sent on Wed, 2 Oct 2002 13:39:47 -0400
> * Honorable "John K. Hinsdale" <[email-masked]> writes:
>

with interpreted code, :bt4 will get you what you want.
that's (sys::debug-backtrace 4).

> think more for CLISP-developer use.

this is the only thing you can use now.
it is equivalent to :bt1 in the debugger.
it prints everything on the STACK at the call time, and with certain
care, you can decipher it and understand the call stack.

A priori, we do not know whether, say, a closure on the STACK is a
funcalled object or an argument to another funcall.
SP might help there.
But neither SYS::DEBUG-BACKTRACE nor EXT:SHOW-STACK examine it.

Bruno, could you please comment?

-- 
Sam Steingold (http://www.podval.org/~sds) running RedHat7.3 GNU/Linux
<http://www.camera.org> <http://www.iris.org.il> <http://www.memri.org/>
<http://www.mideasttruth.com/> <http://www.palestine-central.com/links.html>
Is there another word for synonym?

---

From: Bruno Haible <[email-masked]>
Date: Wed Oct 2 17:14:43 2002

> But neither SYS::DEBUG-BACKTRACE nor EXT:SHOW-STACK examine it.

It is correct. Many compilers nowadays generate debug information
even for optimized code, but the clisp bytecode compiler does not do
this. It would be a big but very useful project...

Bruno

---

From: Sam Steingold <[email-masked]>
Date: Wed Oct 2 17:17:26 2002

> * In message <[email-masked]>
> * On the subject of "Re: [clisp-list] How to get the call stack?"
> * Sent on Thu, 3 Oct 2002 02:07:46 +0200 (CEST)
> * Honorable Bruno Haible <[email-masked]> writes:
>
> Sam writes:

What about SP - does it already contain the information necessary to
split STACK into funcall sections or not?

Garbage In, Gospel Out

---

From: "John K. Hinsdale" <[email-masked]>
Date: Wed Oct 2 17:23:13 2002

> that's (sys::debug-backtrace 4).

OK I tried that:

	(defun a (x) (+ 2 x))
	(defun b (y) (a (- 3 y)))
	(defun c (z)
	  (describe (sys::debug-backtrace 4))
	  (* 9 (b z)))
	(c 7)

and I got this error.

*** - SYSTEM::FRAME-UP-1: NIL is not a stack pointer

 > care, you can decipher it and understand the call stack.

no problem; I don't mind wading through a data structure as long as I
can get to what I need.

I've gotten pretty used to having this info in Java & perl.  It's
indescribably handy when a critical Web site has some anomalous error
and you can see the trace right away where in the code it was, and
with what args.  (I've got some sites running at clients that will
send me an Email and can find, and sometimes even fix, the problem
before the user has literally stepped away for coffee or picked up the
phone to complain).  It scares them a bit actually :)

Another longer-term use I would make is to extend the METERING.LISP
module to compile runtime/consing stats on a function call-graph
basis, much in the way that "gprof" extends "prof" for "C" profiling.

If all else fails, I would fall back on warpping function definitions
in a macro that maintains this info in data structures of my own, but
it seems like a reasonable thing to get from the CLISP interpreter.

---

---

From: Sam Steingold <[email-masked]>
Date: Wed Oct 2 17:37:24 2002

> * In message <[email-masked]>
> * Sent on Wed, 2 Oct 2002 20:04:42 -0400

please look at reploop.lisp - you need to bind some variable &c...

> I've gotten pretty used to having this info in Java & perl.

Absolutely.
this is one of the places that __really__ need work.
any volunteers?

-- 
Sam Steingold (http://www.podval.org/~sds) running RedHat7.3 GNU/Linux
<http://www.camera.org> <http://www.iris.org.il> <http://www.memri.org/>
<http://www.mideasttruth.com/> <http://www.palestine-central.com/links.html>
I haven't lost my mind -- it's backed up on tape somewhere.

---

From: Bruno Haible <[email-masked]>
Date: Wed Oct 2 18:01:54 2002

> split STACK into funcall sections or not?

No it doesn't. Functions which don't use UNWIND-PROTECT, CATCH and
similar special forms don't have SP entries of their own at runtime.

Bruno

---

From: "John K. Hinsdale" <[email-masked]>
Date: Fri Oct 4 07:07:03 2002

> please look at reploop.lisp - you need to bind some variable &c...

OK, figured it out; thanks.  FYI I ended up w/ the code at the
bottom. It will scan the stack at the point the error was raised and
list all the APPLY frames, plus the outermost EVAL frame (which should
be the one that rased the error).  It seems to work for me, and gives
me mostly what I want - to be able to see immediately on my Web error
page and log file roughly where things were.  Seeing the function
names I can quickly enough get to the places in the source files.

One problem I'm still having: in order for me to to use the thingy
below, I have to call it *myself* at the point of error, e.g.,

    (when bad-situation
          (error "Ooops: ~A" (get-stack-as-string)))

That's fine, however, what if underlying Lisp is throwing the error?
I would still like to take snapshot of the stack, _before_ it's
unwound, and include as part of my message.  So I need to somehow
override (or at least have some hook into) the built-in Lisp error
thrower, if that makes any sense.  How do I do that?

That aside, having this is 100 times better than what I had before (no
stack).

 > this is one of the places that __really__ need work.  any volunteers?

Compiled-in debug info (source file, position-in-file) would be great,
as it would lead one right to the line of source, and also do the
"right thing" for macro expansions - take you back to the macro
definition, not just show the expansion.  This compiled-in info should
probably be designed anticipating its role in all the future things
that might use it: source-level debugger, profiler, and test-coverage
tool.  I'll volunteer after I'm done learning Lisp :)

OK, here's that code:

; GET-STACK-AS-STRING - Get the call stack as a string
(defun get-stack-as-string (&optional (nskip 0))
"Get the call stack as a string, useful for inclusion in error
messages in batch or Web programs.  Optional arg skips over N initial
frames that may be on the stack as part of calling this routine,
so as to get to the frame that \"really\" caused the error."
  ; Skip to first relevant EVAL frame
  (do* ((result (make-string-output-stream))
        (mode 4)
        (frame (system::the-frame) (system::frame-up-1 frame mode))
        (last (system::frame-up frame 5))
        (count 0 (1+ count))
        (at-last nil))
       (at-last (get-output-stream-string result))
       (cond ((< count nskip) )
             ((= count nskip)
              (system::describe-frame result frame)
              (setf mode 5))
             ((equal frame last)
              (setf at-last t))
             (t
              (system::describe-frame result frame)))))

---