함수 코드를 디버깅하는 데 아주 재미있는 함수 dtrace.lisp
Common Lisp에는 원래 추적 함수trace가 있습니다. 사용할 때 추적할 함수의 이름을 매개 변수로 하고 함수에 내부 정보를 표시할 수 있습니다.
dtrace는 좀 더 형상적인 기호를 사용했는데 보기에 약간 직관적인 것 같다. 나는 원래의 기호를 탭 기호로 바꾸었는데 효과는 다음과 같다.
CL-USER> (defun ( )
(if (> )
( (* )
(+ 1)
)))
CL-USER> (defun ( )
( 1 1 ))
CL-USER> ( 5)
120
여기에는 두 개의 계층 곱하기 함수가 정의되어 있으며 다음에는 두 함수에 대한 추적을 설정하고 추적을 설정한 후 이 함수를 실행합니다. 명령은 다음과 같습니다.
CL-USER> (dtrace )
( )
CL-USER> ( 15)
┌─── [ ->
│ = 15
│ ┌─── [ ->
│ │ = 1
│ │ = 1
│ │ = 15
│ └─── <- ] 1307674368000
└─── <- ] 1307674368000
1307674368000
CL-USER>
재미있지 않아요? 여기는 dtracez예요.lisp의 소스 코드:
;;; -*- Mode: Lisp; Package: DTRACE -*-
;;; DTRACE is a portable alternative to the Common Lisp TRACE and UNTRACE
;;; macros. It offers a more detailed display than most tracing tools.
;;;
;;; From the book "Common Lisp: A Gentle Introduction to
;;; Symbolic Computation" by David S. Touretzky.
;;; The Benjamin/Cummings Publishing Co., 1989.
;;;
;;; User-level routines:
;;; DTRACE - same syntax as TRACE
;;; DUNTRACE - same syntax as UNTRACE
;(in-package "DTRACE" :use "LISP")
#|(export ’(dtrace::dtrace dtrace::duntrace
*dtrace-print-length* *dtrace-print-level*
*dtrace-print-circle* *dtrace-print-pretty*
*dtrace-print-array*))
(shadowing-import ’(dtrace::dtrace dtrace::duntrace) (find-package "USER"))
(use-package "DTRACE" "USER")
|#
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; DTRACE and subordinate routines.
(defparameter *dtrace-print-length* 7)
(defparameter *dtrace-print-level* 4)
(defparameter *dtrace-print-circle* t)
(defparameter *dtrace-print-pretty* nil)
(defparameter *dtrace-print-array* *print-array*)
(defparameter *entry-arrow-string* "┌─── ")
(defparameter *vertical-string* "│ ")
(defparameter *exit-arrow-string* "└─── ")
(defparameter *trace-wraparound* 15)
(defvar *traced-functions* nil)
(defvar *trace-level* 0)
(defmacro with-dtrace-printer-settings (&body body)
`(let ((*print-length* *dtrace-print-length*)
(*print-level* *dtrace-print-level*)
(*print-circle* *dtrace-print-circle*)
(*print-pretty* *dtrace-print-pretty*)
(*print-array* *dtrace-print-array*))
,@body))
(defmacro dtrace (&rest function-names)
"Turns on detailed tracing for specified functions. Undo with DUNTRACE."
(if (null function-names)
(list `quote *traced-functions*)
(list `quote (mapcan #'dtrace1 function-names))))
(defun dtrace1 (name)
(unless (symbolp name)
(format *error-output* "~&~S is an invalid function name." name)
(return-from dtrace1 nil))
(unless (fboundp name)
(format *error-output* "~&~S undefined function." name)
(return-from dtrace1 nil))
(eval `(untrace ,name)) ;; if they’re tracing it, undo their trace
(duntrace1 name) ;; if we’re tracing it, undo our trace
(when (special-form-p name)
(format *error-output*
"~&Can’t trace ~S because it’s a special form." name)
(return-from dtrace1 nil))
(if (macro-function name)
(trace-macro name)
(trace-function1 name))
(setf *traced-functions* (nconc *traced-functions* (list name)))
(list name))
;;; The functions below reference DISPLAY-xxx routines that can be made
;;; implementation specific for fancy graphics. Generic versions of
;;; these routines are defined later in this file.
(defun trace-function1 (name)
(let* ((formal-arglist (fetch-arglist name))
(old-defn (symbol-function name))
(new-defn
#'(lambda (&rest argument-list)
(let ((result nil))
(display-function-entry name)
(let ((*trace-level* (1+ *trace-level*)))
(with-dtrace-printer-settings
(show-function-args argument-list formal-arglist))
(setf result (multiple-value-list
(apply old-defn argument-list))))
(display-function-return name result)
(values-list result)))))
(setf (get name `original-definition) old-defn)
(setf (get name `traced-definition) new-defn)
(setf (get name `traced-type) `defun)
(setf (symbol-function name) new-defn)))
(defun trace-macro (name)
(let* ((formal-arglist (fetch-arglist name))
(old-defn (macro-function name))
(new-defn
#'(lambda (macro-args env)
(let ((result nil))
(display-function-entry name `macro)
(let ((*trace-level* (1+ *trace-level*)))
(with-dtrace-printer-settings
(show-function-args macro-args formal-arglist))
(setf result (funcall old-defn macro-args env)))
(display-function-return name (list result) `macro)
(values result)))))
(setf (get name `original-definition) old-defn)
(setf (get name `traced-definition) new-defn)
(setf (get name `traced-type) `defmacro)
(setf (macro-function name) new-defn)))
(defun show-function-args (actuals formals &optional (argcount 0))
(cond ((null actuals) nil)
((null formals) (handle-args-numerically actuals argcount))
(t (case (first formals)
(&optional (show-function-args
actuals (rest formals) argcount))
(&rest (show-function-args
(list actuals) (rest formals) argcount))
(&key (handle-keyword-args actuals))
(&aux (show-function-args actuals nil argcount))
(t (handle-one-arg (first actuals) (first formals))
(show-function-args (rest actuals)
(rest formals)
(1+ argcount)))))))
(defun handle-args-numerically (actuals argcount)
(dolist (x actuals)
(incf argcount)
(display-arg-numeric x argcount)))
(defun handle-one-arg (val varspec)
(cond ((atom varspec) (display-one-arg val varspec))
(t (display-one-arg val (first varspec))
(if (third varspec)
(display-one-arg t (third varspec))))))
(defun handle-keyword-args (actuals)
(cond ((null actuals))
((keywordp (first actuals))
(display-one-arg (second actuals) (first actuals))
(handle-keyword-args (rest (rest actuals))))
(t (display-one-arg actuals "Extra args:"))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; DUNTRACE and subordinate routines.
(defmacro duntrace (&rest function-names)
"Turns off tracing for specified functions.
With no args, turns off all tracing."
(setf *trace-level* 0) ;; safety precaution
(list `quote
(mapcan #'duntrace1 (or function-names *traced-functions*))))
(defun duntrace1 (name)
(unless (symbolp name)
(format *error-output* "~&~S is an invalid function name." name)
(return-from duntrace1 nil))
(setf *traced-functions* (delete name *traced-functions*))
(let ((orig-defn (get name `original-definition `none))
(traced-defn (get name `traced-definition))
(traced-type (get name `traced-type `none)))
(unless (or (eq orig-defn `none)
(not (fboundp name))
(not (equal traced-defn ;; did it get redefined?
(ecase traced-type
(defun (symbol-function name))
(defmacro (macro-function name))))))
(ecase traced-type
(defun (setf (symbol-function name) orig-defn))
(defmacro (setf (macro-function name) orig-defn)))))
(remprop name `traced-definition)
(remprop name `traced-type)
(remprop name `original-definition)
(list name))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Display routines.
;;;
;;; The code below generates vanilla character output for ordinary
;;; displays. It can be replaced with special graphics code if the
;;; implementation permits, e.g., on a PC you can use the IBM graphic
;;; character set to draw nicer-looking arrows. On a color PC you
;;; can use different colors for arrows, for function names, for
;;; argument values, and so on.
(defun display-function-entry (name &optional ftype)
(space-over)
(draw-entry-arrow)
(format *trace-output* "[ -> ~S" name)
(if (eq ftype `macro)
(format *trace-output* " macro")))
(defun display-one-arg (val name)
(space-over)
(format *trace-output*
(typecase name
(keyword " ~S ~S")
(string " ~A ~S")
(t " ~S = ~S"))
name val))
(defun display-arg-numeric (val num)
(space-over)
(format *trace-output* " -~D = ~S" num val))
(defun display-function-return (name results &optional ftype)
(with-dtrace-printer-settings
(space-over)
(draw-exit-arrow)
(format *trace-output* "~S ~A"
name
(if (eq ftype `macro) "[ ->" "<- ]"))
(cond ((null results))
((null (rest results))
(format *trace-output* " ~S" (first results)))
(t (format *trace-output* " values ~{~S, ~}~s"
(butlast results)
(car (last results)))))))
(defun space-over ()
(format *trace-output* "~&")
(dotimes (i (mod *trace-level* *trace-wraparound*))
(format *trace-output* "~A" *vertical-string*)))
(defun draw-entry-arrow ()
(format *trace-output* "~A" *entry-arrow-string*))
(defun draw-exit-arrow ()
(format *trace-output* "~A" *exit-arrow-string*))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; The function FETCH-ARGLIST is implementation dependent. It
;;; returns the formal argument list of a function as it would
;;; appear in a DEFUN or lambda expression, including any lambda
;;; list keywords. Here are versions of FETCH-ARGLIST for three
;;; Lisp implementations.
;;; CCL version
#+ccl
(defun fetch-arglist (fn)
(arglist fn))
;;; Lucid version
#+LUCID
(defun fetch-arglist (fn)
(system::arglist fn))
;;; GCLisp 3.1 version
#+GCLISP
(defun fetch-arglist (name)
(let* ((s (sys:lambda-list name))
(a (read-from-string s)))
(if s
(if (eql (elt s 0) #\Newline)
(edit-arglist (rest a))
a))))
#+GCLISP
(defun edit-arglist (arglist)
(let ((result nil)
(skip-non-keywords nil))
(dolist (arg arglist (nreverse result))
(unless (and skip-non-keywords
(symbolp arg)
(not (keywordp arg)))
(push arg result))
(if (eq arg ’&key) (setf skip-non-keywords t)))))
;;; CMU Common Lisp version. This version looks in a symbol’s
;;; function cell and knows how to take apart lexical closures
;;; and compiled code objects found there.
#+cmu
(defun fetch-arglist (x &optional original-x)
(cond ((symbolp x) (fetch-arglist (symbol-function x) x))
((compiled-function-p x)
(read-from-string
(lisp::%primitive header-ref x
lisp::%function-arg-names-slot)))
((listp x) (case (first x)
(lambda (second x))
(%lexical-closure% (fetch-arglist (second x)))
(system:macro ’(&rest "Form ="))
(t ’(&rest "Arglist:"))))
(t (cerror (format nil
"Use a reasonable default argument list for ~S"
original-x)
"Unkown object in function cell of ~S: ~S" original-x x)
’())))
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.