#:g1: trivial-backtraceの紹介

Posted 2014-06-01 11:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の152日目です。

trivial-backtraceとはなにか

 trivial-backtraceは、Gary Warren King氏作のポータブルなバックトレースの表示ユーティリティです。

パッケージ情報

パッケージ名trivial-backtrace
Quicklisp
プロジェクトサイトtrivial-backtrace | watch where you've been
CLiKihttp://cliki.net/trivial-backtrace
Quickdocshttp://quickdocs.org/trivial-backtrace
CL Test Grid: ビルド状況trivial-backtrace | CL Test Grid

インストール方法

(ql:quickload :trivial-backtrace)

試してみる

 どんな関数があるかは、Quickdocsで確認できます。

 基本的に処理系ごとにバラバラなバックトレースの出力をポータブルなコードで統一した表示にしようというものです。
先日紹介したpreplではバックトレースの表示がドバっとしていたのでtrivial-backtraceとの組み合わせを試してみます。

(prepl:define-repl-command tbt (&optional (n most-positive-fixnum))
  "backtrace `n' stack frames, default all"
  (declare (ignore n))
  (trivial-backtrace:print-backtrace prepl:*current-error*))

(prepl:define-repl-command sbt (&optional (n most-positive-fixnum)) "backtrace `n' stack frames, default all" (block nil (let ((cnt 0)) (flet ((prfn (x) (when (< 1 (incf cnt)) (princ " <- " *debug-io*)) (prin1 x *debug-io*) (when (> cnt n) (return (values)))) (ignore-fn-p (fn) (member-if (lambda (p) (eq (symbol-package fn) (find-package p))) '(:prepl :trivial-backtrace :sb-debug)))) (trivial-backtrace:map-backtrace (lambda (x &aux (fn (trivial-backtrace::frame-func x))) (swank-match:match fn (('lambda . _) (prfn 'λ)) (('flet arg :in x . _) (unless (ignore-fn-p x) (prfn fn))) (('labels . _) (prfn fn)) (_ (typecase fn (SYMBOL (unless (ignore-fn-p fn) (prfn fn))) (STRING))))))))) (force-output *debug-io*))

trivial-backtraceのprint-backtraceを利用したtbtと、表示をAllegro CLやLucid CLっぽくシンプルにしたsbtを定義してみています。

動作

エラーを起す

CL-USER> (defun mamorlis (n) (+ n 1))
MAMORLIS
CL-USER> (mamorlis :z)

Debugger entered for condition: #<SIMPLE-TYPE-ERROR expected-type: NUMBER datum: :Z>: Argument X is not a NUMBER: :Z Available restarts: 0 [ABORT] Reduce debugger level (to debug level 0). 1 Abort to REPL 2 [ABORT-TO-OUTMOST-REPL] Abort to outmost REPL 3 Exit debugger, returning to top level.

素のpreplの:btコマンドの出力

[1] CL-USER> :bt
((LAMBDA (CONIUM::DEBUGGER-LOOP-FN) :IN "quicklisp/dists/quicklisp/software/conium-20101006-git/sbcl.lisp") #<CLOSURE (LAMBDA NIL :IN PREPL::BT-CMD) {1013FD24EB}>)
(PREPL::PROCESS-COMMAND #S(PREPL::USER-COMMAND :INPUT ":bt" :FUNC #<FUNCTION PREPL::BT-CMD> :ARGS NIL :HNUM 9))
(PREPL::%REP-ONE)
(PREPL::REP-ONE)
(PREPL::%REPL :BREAK-LEVEL NIL :NOPRINT NIL :INSPECT NIL :CONTINUABLE NIL :NOBANNER NIL)
((LAMBDA (CONIUM::HOOK CONIUM::FUN) :IN "quicklisp/dists/quicklisp/software/conium-20101006-git/sbcl.lisp") #<FUNCTION PREPL:DEBUGGER> #<CLOSURE (LAMBDA NIL :IN PREPL:REPL) {1013FD0D6B}>)
(PREPL:REPL)
((LAMBDA (CONIUM::DEBUGGER-LOOP-FN) :IN "quicklisp/dists/quicklisp/software/conium-20101006-git/sbcl.lisp") #<CLOSURE (LAMBDA NIL :IN PREPL:DEBUGGER) {1013FD0D4B}>)
(PREPL:DEBUGGER #<SIMPLE-TYPE-ERROR expected-type: NUMBER datum: :Z> #<unused argument> NIL)
(SB-DEBUG::RUN-HOOK *INVOKE-DEBUGGER-HOOK* #<SIMPLE-TYPE-ERROR expected-type: NUMBER datum: :Z>)
(INVOKE-DEBUGGER #<SIMPLE-TYPE-ERROR expected-type: NUMBER datum: :Z>)
(ERROR SIMPLE-TYPE-ERROR :DATUM :Z :EXPECTED-TYPE NUMBER :FORMAT-CONTROL "~@<Argument ~A is not a ~S: ~2I~_~S~:>" :FORMAT-ARGUMENTS (SB-KERNEL::X NUMBER :Z))
(SB-KERNEL:TWO-ARG-+ :Z 1)
(MAMORLIS :Z)
(SB-INT:SIMPLE-EVAL-IN-LEXENV (MAMORLIS :Z) #<NULL-LEXENV>)
(EVAL (MAMORLIS :Z))
(PREPL::INTERACTIVE-EVAL (MAMORLIS :Z))
(PREPL::%REP-ONE)
(PREPL::REP-ONE)
(PREPL::%REPL :BREAK-LEVEL NIL :NOPRINT NIL :INSPECT NIL :CONTINUABLE NIL :NOBANNER NIL)
(SB-THREAD::CALL-WITH-NEW-SESSION #<CLOSURE (LABELS #:FB-NAME-2294 :IN PREPL::INVOKE-WITH-SESSION-WORKAROUND-IF-ON-SBCL) {1011BE0ABB}>)
(PREPL::INVOKE-WITH-SESSION-WORKAROUND-IF-ON-SBCL #<CLOSURE (LAMBDA NIL :IN PREPL:REPL) {1011BE0A9B}>)
((LAMBDA (CONIUM::HOOK CONIUM::FUN) :IN "quicklisp/dists/quicklisp/software/conium-20101006-git/sbcl.lisp") #<FUNCTION PREPL:DEBUGGER> #<CLOSURE (LAMBDA NIL :IN PREPL:REPL) {1011BE0A5B}>)
(PREPL:REPL)
(SB-INT:SIMPLE-EVAL-IN-LEXENV (PREPL:REPL) #<NULL-LEXENV>)
(EVAL (PREPL:REPL))
(INTERACTIVE-EVAL (PREPL:REPL) :EVAL NIL)
(SB-IMPL::REPL-FUN NIL)
((LAMBDA NIL :IN SB-IMPL::TOPLEVEL-REPL))
(SB-IMPL::%WITH-REBOUND-IO-SYNTAX #<CLOSURE (LAMBDA NIL :IN SB-IMPL::TOPLEVEL-REPL) {100EBBCE9B}>)
(SB-IMPL::TOPLEVEL-REPL NIL)
(SB-IMPL::TOPLEVEL-INIT)
((FLET #:WITHOUT-INTERRUPTS-BODY-66 :IN SAVE-LISP-AND-DIE))
((LABELS SB-IMPL::RESTART-LISP :IN SAVE-LISP-AND-DIE))

trivial-backtraceを利用してみたもの〈:tbt〉

[1] CL-USER> :tbt

Date/time: 2014-06-01-19:35An unhandled error condition has been signalled: Argument X is not a NUMBER: :Z

Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {10039FD403}> 0: ((LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX)) 1: (SB-IMPL::CALL-WITH-SANE-IO-SYNTAX #<CLOSURE (LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX) {101401506B}>) 2: (SB-IMPL::%WITH-STANDARD-IO-SYNTAX #<CLOSURE (LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX) {101401503B}>) 3: (PRINT-BACKTRACE :STREAM #<SYNONYM-STREAM :SYMBOL *TERMINAL-IO* {10001D2F43}> :START 0 :FROM :DEBUGGER-FRAME :COUNT 4611686018427387903 :PRINT-THREAD T :PRINT-FRAME-SOURCE NIL :METHOD-FRAME-STYLE NIL) 4: (TRIVIAL-BACKTRACE:PRINT-BACKTRACE-TO-STREAM #<SYNONYM-STREAM :SYMBOL *TERMINAL-IO* {10001D2F43}>) 5: (TRIVIAL-BACKTRACE:PRINT-BACKTRACE #<SIMPLE-TYPE-ERROR expected-type: NUMBER datum: :Z> :OUTPUT NIL :IF-EXISTS :APPEND :VERBOSE NIL) 6: (PREPL::PROCESS-COMMAND #S(PREPL::USER-COMMAND :INPUT ":tbt" :FUNC #<FUNCTION PREPL::TBT-CMD> :ARGS NIL :HNUM 10)) 7: (PREPL::%REP-ONE) 8: (PREPL::REP-ONE) 9: (PREPL::%REPL :BREAK-LEVEL NIL :NOPRINT NIL :INSPECT NIL :CONTINUABLE NIL :NOBANNER NIL) 10: ((LAMBDA (CONIUM::HOOK CONIUM::FUN) :IN "quicklisp/dists/quicklisp/software/conium-20101006-git/sbcl.lisp") #<FUNCTION PREPL:DEBUGGER> #<CLOSURE (LAMBDA NIL :IN PREPL:REPL) {1013FD0D6B}>) 11: (PREPL:REPL) 12: ((LAMBDA (CONIUM::DEBUGGER-LOOP-FN) :IN "quicklisp/dists/quicklisp/software/conium-20101006-git/sbcl.lisp") #<CLOSURE (LAMBDA NIL :IN PREPL:DEBUGGER) {1013FD0D4B}>) 13: (PREPL:DEBUGGER #<SIMPLE-TYPE-ERROR expected-type: NUMBER datum: :Z> #<unused argument> NIL) 14: (SB-DEBUG::RUN-HOOK *INVOKE-DEBUGGER-HOOK* #<SIMPLE-TYPE-ERROR expected-type: NUMBER datum: :Z>) 15: (INVOKE-DEBUGGER #<SIMPLE-TYPE-ERROR expected-type: NUMBER datum: :Z>) 16: (ERROR SIMPLE-TYPE-ERROR :DATUM :Z :EXPECTED-TYPE NUMBER :FORMAT-CONTROL "~@<Argument ~A is not a ~S: ~2I~_~S~:>" :FORMAT-ARGUMENTS (SB-KERNEL::X NUMBER :Z)) 17: (SB-KERNEL:TWO-ARG-+ :Z 1) 18: (MAMORLIS :Z) 19: (SB-INT:SIMPLE-EVAL-IN-LEXENV (MAMORLIS :Z) #<NULL-LEXENV>) 20: (EVAL (MAMORLIS :Z)) 21: (PREPL::INTERACTIVE-EVAL (MAMORLIS :Z)) 22: (PREPL::%REP-ONE) 23: (PREPL::REP-ONE) 24: (PREPL::%REPL :BREAK-LEVEL NIL :NOPRINT NIL :INSPECT NIL :CONTINUABLE NIL :NOBANNER NIL) 25: (SB-THREAD::CALL-WITH-NEW-SESSION #<CLOSURE (LABELS #:FB-NAME-2294 :IN PREPL::INVOKE-WITH-SESSION-WORKAROUND-IF-ON-SBCL) {1011BE0ABB}>) 26: (PREPL::INVOKE-WITH-SESSION-WORKAROUND-IF-ON-SBCL #<CLOSURE (LAMBDA NIL :IN PREPL:REPL) {1011BE0A9B}>) 27: ((LAMBDA (CONIUM::HOOK CONIUM::FUN) :IN "quicklisp/dists/quicklisp/software/conium-20101006-git/sbcl.lisp") #<FUNCTION PREPL:DEBUGGER> #<CLOSURE (LAMBDA NIL :IN PREPL:REPL) {1011BE0A5B}>) 28: (PREPL:REPL) 29: (SB-INT:SIMPLE-EVAL-IN-LEXENV (PREPL:REPL) #<NULL-LEXENV>) 30: (EVAL (PREPL:REPL)) 31: (INTERACTIVE-EVAL (PREPL:REPL) :EVAL NIL) 32: (SB-IMPL::REPL-FUN NIL) 33: ((LAMBDA NIL :IN SB-IMPL::TOPLEVEL-REPL)) 34: (SB-IMPL::%WITH-REBOUND-IO-SYNTAX #<CLOSURE (LAMBDA NIL :IN SB-IMPL::TOPLEVEL-REPL) {100EBBCE9B}>) 35: (SB-IMPL::TOPLEVEL-REPL NIL) 36: (SB-IMPL::TOPLEVEL-INIT) 37: ((FLET #:WITHOUT-INTERRUPTS-BODY-66 :IN SAVE-LISP-AND-DIE)) 38: ((LABELS SB-IMPL::RESTART-LISP :IN SAVE-LISP-AND-DIE))

trivial-backtraceの部品を利用して自作してみたもの〈:sbt〉

[1] CL-USER> :sbt
Λ <- Λ <- INVOKE-DEBUGGER <- ERROR <- SB-KERNEL:TWO-ARG-+ <- MAMORLIS <- SB-INT:SIMPLE-EVAL-IN-LEXENV <- EVAL <- SB-THREAD::CALL-WITH-NEW-SESSION <- Λ <- SB-INT:SIMPLE-EVAL-IN-LEXENV <- EVAL <- INTERACTIVE-EVAL <- SB-IMPL::REPL-FUN <- Λ <- SB-IMPL::%WITH-REBOUND-IO-SYNTAX <- SB-IMPL::TOPLEVEL-REPL <- SB-IMPL::TOPLEVEL-INIT <- (FLET #:WITHOUT-INTERRUPTS-BODY-66 :IN SAVE-LISP-AND-DIE) <- (LABELS SB-IMPL::RESTART-LISP :IN SAVE-LISP-AND-DIE)

 trivial-backtraceは最初にコンディションを表示して、その後番号付きでずらずら表示しますが、preplの表示よりは読み易いかなというところです。

まとめ

 今回は、trivial-backtraceを紹介してみました。
処理系標準でないものに積み重ねていった場合、後で積み重ねた部分の余計な情報が出力されてしまったりしがちです。
余計な情報が出力されてしまわないように切り分けて表示するのもなかなか面倒ですね。

comments powered by Disqus