|
|
- ;;; -*- indent-tabs-mode: nil; outline-regexp: ";;;;+" -*-
- ;;;
- ;;; License: Public Domain
- ;;;
- ;;;; Introduction
- ;;;
- ;;; This is the CMUCL implementation of the `swank/backend' package.
-
- (defpackage swank/cmucl
- (:use cl swank/backend swank/source-path-parser swank/source-file-cache
- fwrappers))
-
- (in-package swank/cmucl)
-
- (eval-when (:compile-toplevel :load-toplevel :execute)
-
- (let ((min-version #x20c))
- (assert (>= c:byte-fasl-file-version min-version)
- () "This file requires CMUCL version ~x or newer" min-version))
-
- (require 'gray-streams))
-
- (import-swank-mop-symbols :pcl '(:slot-definition-documentation))
-
- (defun swank-mop:slot-definition-documentation (slot)
- (documentation slot t))
-
- ;;; UTF8
-
- (locally (declare (optimize (ext:inhibit-warnings 3)))
- ;; Compile and load the utf8 format, if not already loaded.
- (stream::find-external-format :utf-8))
-
- (defimplementation string-to-utf8 (string)
- (let ((ef (load-time-value (stream::find-external-format :utf-8) t)))
- (stream:string-to-octets string :external-format ef)))
-
- (defimplementation utf8-to-string (octets)
- (let ((ef (load-time-value (stream::find-external-format :utf-8) t)))
- (stream:octets-to-string octets :external-format ef)))
-
- ;;;; TCP server
- ;;;
- ;;; In CMUCL we support all communication styles. By default we use
- ;;; `:SIGIO' because it is the most responsive, but it's somewhat
- ;;; dangerous: CMUCL is not in general "signal safe", and you don't
- ;;; know for sure what you'll be interrupting. Both `:FD-HANDLER' and
- ;;; `:SPAWN' are reasonable alternatives.
-
- (defimplementation preferred-communication-style ()
- :sigio)
-
- #-(or darwin mips)
- (defimplementation create-socket (host port &key backlog)
- (let* ((addr (resolve-hostname host))
- (addr (if (not (find-symbol "SOCKET-ERROR" :ext))
- (ext:htonl addr)
- addr)))
- (ext:create-inet-listener port :stream :reuse-address t :host addr
- :backlog (or backlog 5))))
-
- ;; There seems to be a bug in create-inet-listener on Mac/OSX and Irix.
- #+(or darwin mips)
- (defimplementation create-socket (host port &key backlog)
- (declare (ignore host))
- (ext:create-inet-listener port :stream :reuse-address t))
-
- (defimplementation local-port (socket)
- (nth-value 1 (ext::get-socket-host-and-port (socket-fd socket))))
-
- (defimplementation close-socket (socket)
- (let ((fd (socket-fd socket)))
- (sys:invalidate-descriptor fd)
- (ext:close-socket fd)))
-
- (defimplementation accept-connection (socket &key
- external-format buffering timeout)
- (declare (ignore timeout))
- (make-socket-io-stream (ext:accept-tcp-connection socket)
- (ecase buffering
- ((t) :full)
- (:line :line)
- ((nil) :none))
- external-format))
-
- ;;;;; Sockets
-
- (defimplementation socket-fd (socket)
- "Return the filedescriptor for the socket represented by SOCKET."
- (etypecase socket
- (fixnum socket)
- (sys:fd-stream (sys:fd-stream-fd socket))))
-
- (defun resolve-hostname (hostname)
- "Return the IP address of HOSTNAME as an integer (in host byte-order)."
- (let ((hostent (ext:lookup-host-entry hostname)))
- (car (ext:host-entry-addr-list hostent))))
-
- (defvar *external-format-to-coding-system*
- '((:iso-8859-1 "iso-latin-1-unix")
- #+unicode
- (:utf-8 "utf-8-unix")))
-
- (defimplementation find-external-format (coding-system)
- (car (rassoc-if (lambda (x) (member coding-system x :test #'equal))
- *external-format-to-coding-system*)))
-
- (defun make-socket-io-stream (fd buffering external-format)
- "Create a new input/output fd-stream for FD."
- (cond (external-format
- (sys:make-fd-stream fd :input t :output t
- :element-type 'character
- :buffering buffering
- :external-format external-format))
- (t
- (sys:make-fd-stream fd :input t :output t
- :element-type '(unsigned-byte 8)
- :buffering buffering))))
-
- (defimplementation make-fd-stream (fd external-format)
- (make-socket-io-stream fd :full external-format))
-
- (defimplementation dup (fd)
- (multiple-value-bind (clone error) (unix:unix-dup fd)
- (unless clone (error "dup failed: ~a" (unix:get-unix-error-msg error)))
- clone))
-
- (defimplementation command-line-args ()
- ext:*command-line-strings*)
-
- (defimplementation exec-image (image-file args)
- (multiple-value-bind (ok error)
- (unix:unix-execve (car (command-line-args))
- (list* (car (command-line-args))
- "-core" image-file
- "-noinit"
- args))
- (error "~a" (unix:get-unix-error-msg error))
- ok))
-
- ;;;;; Signal-driven I/O
-
- (defimplementation install-sigint-handler (function)
- (sys:enable-interrupt :sigint (lambda (signal code scp)
- (declare (ignore signal code scp))
- (funcall function))))
-
- (defvar *sigio-handlers* '()
- "List of (key . function) pairs.
- All functions are called on SIGIO, and the key is used for removing
- specific functions.")
-
- (defun reset-sigio-handlers () (setq *sigio-handlers* '()))
- ;; All file handlers are invalid afer reload.
- (pushnew 'reset-sigio-handlers ext:*after-save-initializations*)
-
- (defun set-sigio-handler ()
- (sys:enable-interrupt :sigio (lambda (signal code scp)
- (sigio-handler signal code scp))))
-
- (defun sigio-handler (signal code scp)
- (declare (ignore signal code scp))
- (mapc #'funcall (mapcar #'cdr *sigio-handlers*)))
-
- (defun fcntl (fd command arg)
- "fcntl(2) - manipulate a file descriptor."
- (multiple-value-bind (ok error) (unix:unix-fcntl fd command arg)
- (cond (ok)
- (t (error "fcntl: ~A" (unix:get-unix-error-msg error))))))
-
- (defimplementation add-sigio-handler (socket fn)
- (set-sigio-handler)
- (let ((fd (socket-fd socket)))
- (fcntl fd unix:f-setown (unix:unix-getpid))
- (let ((old-flags (fcntl fd unix:f-getfl 0)))
- (fcntl fd unix:f-setfl (logior old-flags unix:fasync)))
- (assert (not (assoc fd *sigio-handlers*)))
- (push (cons fd fn) *sigio-handlers*)))
-
- (defimplementation remove-sigio-handlers (socket)
- (let ((fd (socket-fd socket)))
- (when (assoc fd *sigio-handlers*)
- (setf *sigio-handlers* (remove fd *sigio-handlers* :key #'car))
- (let ((old-flags (fcntl fd unix:f-getfl 0)))
- (fcntl fd unix:f-setfl (logandc2 old-flags unix:fasync)))
- (sys:invalidate-descriptor fd))
- (assert (not (assoc fd *sigio-handlers*)))
- (when (null *sigio-handlers*)
- (sys:default-interrupt :sigio))))
-
- ;;;;; SERVE-EVENT
-
- (defimplementation add-fd-handler (socket fn)
- (let ((fd (socket-fd socket)))
- (sys:add-fd-handler fd :input (lambda (_) _ (funcall fn)))))
-
- (defimplementation remove-fd-handlers (socket)
- (sys:invalidate-descriptor (socket-fd socket)))
-
- (defimplementation wait-for-input (streams &optional timeout)
- (assert (member timeout '(nil t)))
- (loop
- (let ((ready (remove-if-not #'listen streams)))
- (when ready (return ready)))
- (when timeout (return nil))
- (multiple-value-bind (in out) (make-pipe)
- (let* ((f (constantly t))
- (handlers (loop for s in (cons in (mapcar #'to-fd-stream streams))
- collect (add-one-shot-handler s f))))
- (unwind-protect
- (let ((*interrupt-queued-handler* (lambda ()
- (write-char #\! out))))
- (when (check-slime-interrupts) (return :interrupt))
- (sys:serve-event))
- (mapc #'sys:remove-fd-handler handlers)
- (close in)
- (close out))))))
-
- (defun to-fd-stream (stream)
- (etypecase stream
- (sys:fd-stream stream)
- (synonym-stream
- (to-fd-stream
- (symbol-value (synonym-stream-symbol stream))))
- (two-way-stream
- (to-fd-stream (two-way-stream-input-stream stream)))))
-
- (defun add-one-shot-handler (stream function)
- (let (handler)
- (setq handler (sys:add-fd-handler (sys:fd-stream-fd stream) :input
- (lambda (fd)
- (declare (ignore fd))
- (sys:remove-fd-handler handler)
- (funcall function stream))))))
-
- (defun make-pipe ()
- (multiple-value-bind (in out) (unix:unix-pipe)
- (values (sys:make-fd-stream in :input t :buffering :none)
- (sys:make-fd-stream out :output t :buffering :none))))
-
- ;;;; Stream handling
-
- (defimplementation gray-package-name ()
- "EXT")
-
- ;;;; Compilation Commands
-
- (defvar *previous-compiler-condition* nil
- "Used to detect duplicates.")
-
- (defvar *previous-context* nil
- "Previous compiler error context.")
-
- (defvar *buffer-name* nil
- "The name of the Emacs buffer we are compiling from.
- NIL if we aren't compiling from a buffer.")
-
- (defvar *buffer-start-position* nil)
- (defvar *buffer-substring* nil)
-
- (defimplementation call-with-compilation-hooks (function)
- (let ((*previous-compiler-condition* nil)
- (*previous-context* nil)
- (*print-readably* nil))
- (handler-bind ((c::compiler-error #'handle-notification-condition)
- (c::style-warning #'handle-notification-condition)
- (c::warning #'handle-notification-condition))
- (funcall function))))
-
- (defimplementation swank-compile-file (input-file output-file
- load-p external-format
- &key policy)
- (declare (ignore policy))
- (clear-xref-info input-file)
- (with-compilation-hooks ()
- (let ((*buffer-name* nil)
- (ext:*ignore-extra-close-parentheses* nil))
- (multiple-value-bind (output-file warnings-p failure-p)
- (compile-file input-file :output-file output-file
- :external-format external-format)
- (values output-file warnings-p
- (or failure-p
- (when load-p
- ;; Cache the latest source file for definition-finding.
- (source-cache-get input-file
- (file-write-date input-file))
- (not (load output-file)))))))))
-
- (defimplementation swank-compile-string (string &key buffer position filename
- policy)
- (declare (ignore filename policy))
- (with-compilation-hooks ()
- (let ((*buffer-name* buffer)
- (*buffer-start-position* position)
- (*buffer-substring* string)
- (source-info (list :emacs-buffer buffer
- :emacs-buffer-offset position
- :emacs-buffer-string string)))
- (with-input-from-string (stream string)
- (let ((failurep (ext:compile-from-stream stream :source-info
- source-info)))
- (not failurep))))))
-
- ;;;;; Trapping notes
- ;;;
- ;;; We intercept conditions from the compiler and resignal them as
- ;;; `SWANK:COMPILER-CONDITION's.
-
- (defun handle-notification-condition (condition)
- "Handle a condition caused by a compiler warning."
- (unless (eq condition *previous-compiler-condition*)
- (let ((context (c::find-error-context nil)))
- (setq *previous-compiler-condition* condition)
- (setq *previous-context* context)
- (signal-compiler-condition condition context))))
-
- (defun signal-compiler-condition (condition context)
- (signal 'compiler-condition
- :original-condition condition
- :severity (severity-for-emacs condition)
- :message (compiler-condition-message condition)
- :source-context (compiler-error-context context)
- :location (if (read-error-p condition)
- (read-error-location condition)
- (compiler-note-location context))))
-
- (defun severity-for-emacs (condition)
- "Return the severity of CONDITION."
- (etypecase condition
- ((satisfies read-error-p) :read-error)
- (c::compiler-error :error)
- (c::style-warning :note)
- (c::warning :warning)))
-
- (defun read-error-p (condition)
- (eq (type-of condition) 'c::compiler-read-error))
-
- (defun compiler-condition-message (condition)
- "Briefly describe a compiler error for Emacs.
- When Emacs presents the message it already has the source popped up
- and the source form highlighted. This makes much of the information in
- the error-context redundant."
- (princ-to-string condition))
-
- (defun compiler-error-context (error-context)
- "Describe context information for Emacs."
- (declare (type (or c::compiler-error-context null) error-context))
- (multiple-value-bind (enclosing source)
- (if error-context
- (values (c::compiler-error-context-enclosing-source error-context)
- (c::compiler-error-context-source error-context)))
- (if (or enclosing source)
- (format nil "~@[--> ~{~<~%--> ~1:;~A ~>~}~%~]~
- ~@[==>~{~&~A~}~]"
- enclosing source))))
-
- (defun read-error-location (condition)
- (let* ((finfo (car (c::source-info-current-file c::*source-info*)))
- (file (c::file-info-name finfo))
- (pos (c::compiler-read-error-position condition)))
- (cond ((and (eq file :stream) *buffer-name*)
- (make-location (list :buffer *buffer-name*)
- (list :offset *buffer-start-position* pos)))
- ((and (pathnamep file) (not *buffer-name*))
- (make-location (list :file (unix-truename file))
- (list :position (1+ pos))))
- (t (break)))))
-
- (defun compiler-note-location (context)
- "Derive the location of a complier message from its context.
- Return a `location' record, or (:error REASON) on failure."
- (if (null context)
- (note-error-location)
- (with-struct (c::compiler-error-context- file-name
- original-source
- original-source-path) context
- (or (locate-compiler-note file-name original-source
- (reverse original-source-path))
- (note-error-location)))))
-
- (defun note-error-location ()
- "Pseudo-location for notes that can't be located."
- (cond (*compile-file-truename*
- (make-location (list :file (unix-truename *compile-file-truename*))
- (list :eof)))
- (*buffer-name*
- (make-location (list :buffer *buffer-name*)
- (list :position *buffer-start-position*)))
- (t (list :error "No error location available."))))
-
- (defun locate-compiler-note (file source source-path)
- (cond ((and (eq file :stream) *buffer-name*)
- ;; Compiling from a buffer
- (make-location (list :buffer *buffer-name*)
- (list :offset *buffer-start-position*
- (source-path-string-position
- source-path *buffer-substring*))))
- ((and (pathnamep file) (null *buffer-name*))
- ;; Compiling from a file
- (make-location (list :file (unix-truename file))
- (list :position (1+ (source-path-file-position
- source-path file)))))
- ((and (eq file :lisp) (stringp source))
- ;; No location known, but we have the source form.
- ;; XXX How is this case triggered? -luke (16/May/2004)
- ;; This can happen if the compiler needs to expand a macro
- ;; but the macro-expander is not yet compiled. Calling the
- ;; (interpreted) macro-expander triggers IR1 conversion of
- ;; the lambda expression for the expander and invokes the
- ;; compiler recursively.
- (make-location (list :source-form source)
- (list :position 1)))))
-
- (defun unix-truename (pathname)
- (ext:unix-namestring (truename pathname)))
-
- ;;;; XREF
- ;;;
- ;;; Cross-reference support is based on the standard CMUCL `XREF'
- ;;; package. This package has some caveats: XREF information is
- ;;; recorded during compilation and not preserved in fasl files, and
- ;;; XREF recording is disabled by default. Redefining functions can
- ;;; also cause duplicate references to accumulate, but
- ;;; `swank-compile-file' will automatically clear out any old records
- ;;; from the same filename.
- ;;;
- ;;; To enable XREF recording, set `c:*record-xref-info*' to true. To
- ;;; clear out the XREF database call `xref:init-xref-database'.
-
- (defmacro defxref (name function)
- `(defimplementation ,name (name)
- (xref-results (,function name))))
-
- (defxref who-calls xref:who-calls)
- (defxref who-references xref:who-references)
- (defxref who-binds xref:who-binds)
- (defxref who-sets xref:who-sets)
-
- ;;; More types of XREF information were added since 18e:
- ;;;
-
- (defxref who-macroexpands xref:who-macroexpands)
- ;; XXX
- (defimplementation who-specializes (symbol)
- (let* ((methods (xref::who-specializes (find-class symbol)))
- (locations (mapcar #'method-location methods)))
- (mapcar #'list methods locations)))
-
- (defun xref-results (contexts)
- (mapcar (lambda (xref)
- (list (xref:xref-context-name xref)
- (resolve-xref-location xref)))
- contexts))
-
- (defun resolve-xref-location (xref)
- (let ((name (xref:xref-context-name xref))
- (file (xref:xref-context-file xref))
- (source-path (xref:xref-context-source-path xref)))
- (cond ((and file source-path)
- (let ((position (source-path-file-position source-path file)))
- (make-location (list :file (unix-truename file))
- (list :position (1+ position)))))
- (file
- (make-location (list :file (unix-truename file))
- (list :function-name (string name))))
- (t
- `(:error ,(format nil "Unknown source location: ~S ~S ~S "
- name file source-path))))))
-
- (defun clear-xref-info (namestring)
- "Clear XREF notes pertaining to NAMESTRING.
- This is a workaround for a CMUCL bug: XREF records are cumulative."
- (when c:*record-xref-info*
- (let ((filename (truename namestring)))
- (dolist (db (list xref::*who-calls*
- xref::*who-is-called*
- xref::*who-macroexpands*
- xref::*who-references*
- xref::*who-binds*
- xref::*who-sets*))
- (maphash (lambda (target contexts)
- ;; XXX update during traversal?
- (setf (gethash target db)
- (delete filename contexts
- :key #'xref:xref-context-file
- :test #'equalp)))
- db)))))
-
- ;;;; Find callers and callees
- ;;;
- ;;; Find callers and callees by looking at the constant pool of
- ;;; compiled code objects. We assume every fdefn object in the
- ;;; constant pool corresponds to a call to that function. A better
- ;;; strategy would be to use the disassembler to find actual
- ;;; call-sites.
-
- (labels ((make-stack () (make-array 100 :fill-pointer 0 :adjustable t))
- (map-cpool (code fun)
- (declare (type kernel:code-component code) (type function fun))
- (loop for i from vm:code-constants-offset
- below (kernel:get-header-data code)
- do (funcall fun (kernel:code-header-ref code i))))
-
- (callees (fun)
- (let ((callees (make-stack)))
- (map-cpool (vm::find-code-object fun)
- (lambda (o)
- (when (kernel:fdefn-p o)
- (vector-push-extend (kernel:fdefn-function o)
- callees))))
- (coerce callees 'list)))
-
- (callers (fun)
- (declare (function fun))
- (let ((callers (make-stack)))
- (ext:gc :full t)
- ;; scan :dynamic first to avoid the need for even more gcing
- (dolist (space '(:dynamic :read-only :static))
- (vm::map-allocated-objects
- (lambda (obj header size)
- (declare (type fixnum header) (ignore size))
- (when (= vm:code-header-type header)
- (map-cpool obj
- (lambda (c)
- (when (and (kernel:fdefn-p c)
- (eq (kernel:fdefn-function c) fun))
- (vector-push-extend obj callers))))))
- space)
- (ext:gc))
- (coerce callers 'list)))
-
- (entry-points (code)
- (loop for entry = (kernel:%code-entry-points code)
- then (kernel::%function-next entry)
- while entry
- collect entry))
-
- (guess-main-entry-point (entry-points)
- (or (find-if (lambda (fun)
- (ext:valid-function-name-p
- (kernel:%function-name fun)))
- entry-points)
- (car entry-points)))
-
- (fun-dspec (fun)
- (list (kernel:%function-name fun) (function-location fun)))
-
- (code-dspec (code)
- (let ((eps (entry-points code))
- (di (kernel:%code-debug-info code)))
- (cond (eps (fun-dspec (guess-main-entry-point eps)))
- (di (list (c::debug-info-name di)
- (debug-info-function-name-location di)))
- (t (list (princ-to-string code)
- `(:error "No src-loc available")))))))
- (declare (inline map-cpool))
-
- (defimplementation list-callers (symbol)
- (mapcar #'code-dspec (callers (coerce symbol 'function) )))
-
- (defimplementation list-callees (symbol)
- (mapcar #'fun-dspec (callees symbol))))
-
- (defun test-list-callers (count)
- (let ((funsyms '()))
- (do-all-symbols (s)
- (when (and (fboundp s)
- (functionp (symbol-function s))
- (not (macro-function s))
- (not (special-operator-p s)))
- (push s funsyms)))
- (let ((len (length funsyms)))
- (dotimes (i count)
- (let ((sym (nth (random len) funsyms)))
- (format t "~s -> ~a~%" sym (mapcar #'car (list-callers sym))))))))
-
- ;; (test-list-callers 100)
-
- ;;;; Resolving source locations
- ;;;
- ;;; Our mission here is to "resolve" references to code locations into
- ;;; actual file/buffer names and character positions. The references
- ;;; we work from come out of the compiler's statically-generated debug
- ;;; information, such as `code-location''s and `debug-source''s. For
- ;;; more details, see the "Debugger Programmer's Interface" section of
- ;;; the CMUCL manual.
- ;;;
- ;;; The first step is usually to find the corresponding "source-path"
- ;;; for the location. Once we have the source-path we can pull up the
- ;;; source file and `READ' our way through to the right position. The
- ;;; main source-code groveling work is done in
- ;;; `source-path-parser.lisp'.
-
- (defvar *debug-definition-finding* nil
- "When true don't handle errors while looking for definitions.
- This is useful when debugging the definition-finding code.")
-
- (defmacro safe-definition-finding (&body body)
- "Execute BODY and return the source-location it returns.
- If an error occurs and `*debug-definition-finding*' is false, then
- return an error pseudo-location.
-
- The second return value is NIL if no error occurs, otherwise it is the
- condition object."
- `(flet ((body () ,@body))
- (if *debug-definition-finding*
- (body)
- (handler-case (values (progn ,@body) nil)
- (error (c) (values `(:error ,(trim-whitespace (princ-to-string c)))
- c))))))
-
- (defun trim-whitespace (string)
- (string-trim #(#\newline #\space #\tab) string))
-
- (defun code-location-source-location (code-location)
- "Safe wrapper around `code-location-from-source-location'."
- (safe-definition-finding
- (source-location-from-code-location code-location)))
-
- (defun source-location-from-code-location (code-location)
- "Return the source location for CODE-LOCATION."
- (let ((debug-fun (di:code-location-debug-function code-location)))
- (when (di::bogus-debug-function-p debug-fun)
- ;; Those lousy cheapskates! They've put in a bogus debug source
- ;; because the code was compiled at a low debug setting.
- (error "Bogus debug function: ~A" debug-fun)))
- (let* ((debug-source (di:code-location-debug-source code-location))
- (from (di:debug-source-from debug-source))
- (name (di:debug-source-name debug-source)))
- (ecase from
- (:file
- (location-in-file name code-location debug-source))
- (:stream
- (location-in-stream code-location debug-source))
- (:lisp
- ;; The location comes from a form passed to `compile'.
- ;; The best we can do is return the form itself for printing.
- (make-location
- (list :source-form (with-output-to-string (*standard-output*)
- (debug::print-code-location-source-form
- code-location 100 t)))
- (list :position 1))))))
-
- (defun location-in-file (filename code-location debug-source)
- "Resolve the source location for CODE-LOCATION in FILENAME."
- (let* ((code-date (di:debug-source-created debug-source))
- (root-number (di:debug-source-root-number debug-source))
- (source-code (get-source-code filename code-date)))
- (with-input-from-string (s source-code)
- (make-location (list :file (unix-truename filename))
- (list :position (1+ (code-location-stream-position
- code-location s root-number)))
- `(:snippet ,(read-snippet s))))))
-
- (defun location-in-stream (code-location debug-source)
- "Resolve the source location for a CODE-LOCATION from a stream.
- This only succeeds if the code was compiled from an Emacs buffer."
- (unless (debug-source-info-from-emacs-buffer-p debug-source)
- (error "The code is compiled from a non-SLIME stream."))
- (let* ((info (c::debug-source-info debug-source))
- (string (getf info :emacs-buffer-string))
- (position (code-location-string-offset
- code-location
- string)))
- (make-location
- (list :buffer (getf info :emacs-buffer))
- (list :offset (getf info :emacs-buffer-offset) position)
- (list :snippet (with-input-from-string (s string)
- (file-position s position)
- (read-snippet s))))))
-
- ;;;;; Function-name locations
- ;;;
- (defun debug-info-function-name-location (debug-info)
- "Return a function-name source-location for DEBUG-INFO.
- Function-name source-locations are a fallback for when precise
- positions aren't available."
- (with-struct (c::debug-info- (fname name) source) debug-info
- (with-struct (c::debug-source- info from name) (car source)
- (ecase from
- (:file
- (make-location (list :file (namestring (truename name)))
- (list :function-name (string fname))))
- (:stream
- (assert (debug-source-info-from-emacs-buffer-p (car source)))
- (make-location (list :buffer (getf info :emacs-buffer))
- (list :function-name (string fname))))
- (:lisp
- (make-location (list :source-form (princ-to-string (aref name 0)))
- (list :position 1)))))))
-
- (defun debug-source-info-from-emacs-buffer-p (debug-source)
- "Does the `info' slot of DEBUG-SOURCE contain an Emacs buffer location?
- This is true for functions that were compiled directly from buffers."
- (info-from-emacs-buffer-p (c::debug-source-info debug-source)))
-
- (defun info-from-emacs-buffer-p (info)
- (and info
- (consp info)
- (eq :emacs-buffer (car info))))
-
-
- ;;;;; Groveling source-code for positions
-
- (defun code-location-stream-position (code-location stream root)
- "Return the byte offset of CODE-LOCATION in STREAM. Extract the
- toplevel-form-number and form-number from CODE-LOCATION and use that
- to find the position of the corresponding form.
-
- Finish with STREAM positioned at the start of the code location."
- (let* ((location (debug::maybe-block-start-location code-location))
- (tlf-offset (- (di:code-location-top-level-form-offset location)
- root))
- (form-number (di:code-location-form-number location)))
- (let ((pos (form-number-stream-position tlf-offset form-number stream)))
- (file-position stream pos)
- pos)))
-
- (defun form-number-stream-position (tlf-number form-number stream)
- "Return the starting character position of a form in STREAM.
- TLF-NUMBER is the top-level-form number.
- FORM-NUMBER is an index into a source-path table for the TLF."
- (multiple-value-bind (tlf position-map) (read-source-form tlf-number stream)
- (let* ((path-table (di:form-number-translations tlf 0))
- (source-path
- (if (<= (length path-table) form-number) ; source out of sync?
- (list 0) ; should probably signal a condition
- (reverse (cdr (aref path-table form-number))))))
- (source-path-source-position source-path tlf position-map))))
-
- (defun code-location-string-offset (code-location string)
- "Return the byte offset of CODE-LOCATION in STRING.
- See CODE-LOCATION-STREAM-POSITION."
- (with-input-from-string (s string)
- (code-location-stream-position code-location s 0)))
-
- ;;;; Finding definitions
-
- ;;; There are a great many different types of definition for us to
- ;;; find. We search for definitions of every kind and return them in a
- ;;; list.
-
- (defimplementation find-definitions (name)
- (append (function-definitions name)
- (setf-definitions name)
- (variable-definitions name)
- (class-definitions name)
- (type-definitions name)
- (compiler-macro-definitions name)
- (source-transform-definitions name)
- (function-info-definitions name)
- (ir1-translator-definitions name)
- (template-definitions name)
- (primitive-definitions name)
- (vm-support-routine-definitions name)
- ))
-
- ;;;;; Functions, macros, generic functions, methods
- ;;;
- ;;; We make extensive use of the compile-time debug information that
- ;;; CMUCL records, in particular "debug functions" and "code
- ;;; locations." Refer to the "Debugger Programmer's Interface" section
- ;;; of the CMUCL manual for more details.
-
- (defun function-definitions (name)
- "Return definitions for NAME in the \"function namespace\", i.e.,
- regular functions, generic functions, methods and macros.
- NAME can any valid function name (e.g, (setf car))."
- (let ((macro? (and (symbolp name) (macro-function name)))
- (function? (and (ext:valid-function-name-p name)
- (ext:info :function :definition name)
- (if (symbolp name) (fboundp name) t))))
- (cond (macro?
- (list `((defmacro ,name)
- ,(function-location (macro-function name)))))
- (function?
- (let ((function (fdefinition name)))
- (if (genericp function)
- (gf-definitions name function)
- (list (list `(function ,name)
- (function-location function)))))))))
-
- ;;;;;; Ordinary (non-generic/macro/special) functions
- ;;;
- ;;; First we test if FUNCTION is a closure created by defstruct, and
- ;;; if so extract the defstruct-description (`dd') from the closure
- ;;; and find the constructor for the struct. Defstruct creates a
- ;;; defun for the default constructor and we use that as an
- ;;; approximation to the source location of the defstruct.
- ;;;
- ;;; For an ordinary function we return the source location of the
- ;;; first code-location we find.
- ;;;
- (defun function-location (function)
- "Return the source location for FUNCTION."
- (cond ((struct-closure-p function)
- (struct-closure-location function))
- ((c::byte-function-or-closure-p function)
- (byte-function-location function))
- (t
- (compiled-function-location function))))
-
- (defun compiled-function-location (function)
- "Return the location of a regular compiled function."
- (multiple-value-bind (code-location error)
- (safe-definition-finding (function-first-code-location function))
- (cond (error (list :error (princ-to-string error)))
- (t (code-location-source-location code-location)))))
-
- (defun function-first-code-location (function)
- "Return the first code-location we can find for FUNCTION."
- (and (function-has-debug-function-p function)
- (di:debug-function-start-location
- (di:function-debug-function function))))
-
- (defun function-has-debug-function-p (function)
- (di:function-debug-function function))
-
- (defun function-code-object= (closure function)
- (and (eq (vm::find-code-object closure)
- (vm::find-code-object function))
- (not (eq closure function))))
-
- (defun byte-function-location (fun)
- "Return the location of the byte-compiled function FUN."
- (etypecase fun
- ((or c::hairy-byte-function c::simple-byte-function)
- (let* ((di (kernel:%code-debug-info (c::byte-function-component fun))))
- (if di
- (debug-info-function-name-location di)
- `(:error
- ,(format nil "Byte-function without debug-info: ~a" fun)))))
- (c::byte-closure
- (byte-function-location (c::byte-closure-function fun)))))
-
- ;;; Here we deal with structure accessors. Note that `dd' is a
- ;;; "defstruct descriptor" structure in CMUCL. A `dd' describes a
- ;;; `defstruct''d structure.
-
- (defun struct-closure-p (function)
- "Is FUNCTION a closure created by defstruct?"
- (or (function-code-object= function #'kernel::structure-slot-accessor)
- (function-code-object= function #'kernel::structure-slot-setter)
- (function-code-object= function #'kernel::%defstruct)))
-
- (defun struct-closure-location (function)
- "Return the location of the structure that FUNCTION belongs to."
- (assert (struct-closure-p function))
- (safe-definition-finding
- (dd-location (struct-closure-dd function))))
-
- (defun struct-closure-dd (function)
- "Return the defstruct-definition (dd) of FUNCTION."
- (assert (= (kernel:get-type function) vm:closure-header-type))
- (flet ((find-layout (function)
- (sys:find-if-in-closure
- (lambda (x)
- (let ((value (if (di::indirect-value-cell-p x)
- (c:value-cell-ref x)
- x)))
- (when (kernel::layout-p value)
- (return-from find-layout value))))
- function)))
- (kernel:layout-info (find-layout function))))
-
- (defun dd-location (dd)
- "Return the location of a `defstruct'."
- (let ((ctor (struct-constructor dd)))
- (cond (ctor
- (function-location (coerce ctor 'function)))
- (t
- (let ((name (kernel:dd-name dd)))
- (multiple-value-bind (location foundp)
- (ext:info :source-location :defvar name)
- (cond (foundp
- (resolve-source-location location))
- (t
- (error "No location for defstruct: ~S" name)))))))))
-
- (defun struct-constructor (dd)
- "Return the name of the constructor from a defstruct definition."
- (let* ((constructor (or (kernel:dd-default-constructor dd)
- (car (kernel::dd-constructors dd)))))
- (if (consp constructor) (car constructor) constructor)))
-
- ;;;;;; Generic functions and methods
-
- (defun gf-definitions (name function)
- "Return the definitions of a generic function and its methods."
- (cons (list `(defgeneric ,name) (gf-location function))
- (gf-method-definitions function)))
-
- (defun gf-location (gf)
- "Return the location of the generic function GF."
- (definition-source-location gf (pcl::generic-function-name gf)))
-
- (defun gf-method-definitions (gf)
- "Return the locations of all methods of the generic function GF."
- (mapcar #'method-definition (pcl::generic-function-methods gf)))
-
- (defun method-definition (method)
- (list (method-dspec method)
- (method-location method)))
-
- (defun method-dspec (method)
- "Return a human-readable \"definition specifier\" for METHOD."
- (let* ((gf (pcl:method-generic-function method))
- (name (pcl:generic-function-name gf))
- (specializers (pcl:method-specializers method))
- (qualifiers (pcl:method-qualifiers method)))
- `(method ,name ,@qualifiers ,(pcl::unparse-specializers specializers))))
-
- (defun method-location (method)
- (typecase method
- (pcl::standard-accessor-method
- (definition-source-location
- (cond ((pcl::definition-source method)
- method)
- (t
- (pcl::slot-definition-class
- (pcl::accessor-method-slot-definition method))))
- (pcl::accessor-method-slot-name method)))
- (t
- (function-location (or (pcl::method-fast-function method)
- (pcl:method-function method))))))
-
- (defun genericp (fn)
- (typep fn 'generic-function))
-
- ;;;;;; Types and classes
-
- (defun type-definitions (name)
- "Return `deftype' locations for type NAME."
- (maybe-make-definition (ext:info :type :expander name) 'deftype name))
-
- (defun maybe-make-definition (function kind name)
- "If FUNCTION is non-nil then return its definition location."
- (if function
- (list (list `(,kind ,name) (function-location function)))))
-
- (defun class-definitions (name)
- "Return the definition locations for the class called NAME."
- (if (symbolp name)
- (let ((class (kernel::find-class name nil)))
- (etypecase class
- (null '())
- (kernel::structure-class
- (list (list `(defstruct ,name) (dd-location (find-dd name)))))
- #+(or)
- (conditions::condition-class
- (list (list `(define-condition ,name)
- (condition-class-location class))))
- (kernel::standard-class
- (list (list `(defclass ,name)
- (pcl-class-location (find-class name)))))
- ((or kernel::built-in-class
- conditions::condition-class
- kernel:funcallable-structure-class)
- (list (list `(class ,name) (class-location class))))))))
-
- (defun pcl-class-location (class)
- "Return the `defclass' location for CLASS."
- (definition-source-location class (pcl:class-name class)))
-
- ;; FIXME: eval used for backward compatibility.
- (defun class-location (class)
- (declare (type kernel::class class))
- (let ((name (kernel:%class-name class)))
- (multiple-value-bind (loc found?)
- (let ((x (ignore-errors
- (multiple-value-list
- (eval `(ext:info :source-location :class ',name))))))
- (values-list x))
- (cond (found? (resolve-source-location loc))
- (`(:error
- ,(format nil "No location recorded for class: ~S" name)))))))
-
- (defun find-dd (name)
- "Find the defstruct-definition by the name of its structure-class."
- (let ((layout (ext:info :type :compiler-layout name)))
- (if layout
- (kernel:layout-info layout))))
-
- (defun condition-class-location (class)
- (let ((slots (conditions::condition-class-slots class))
- (name (conditions::condition-class-name class)))
- (cond ((null slots)
- `(:error ,(format nil "No location info for condition: ~A" name)))
- (t
- ;; Find the class via one of its slot-reader methods.
- (let* ((slot (first slots))
- (gf (fdefinition
- (first (conditions::condition-slot-readers slot)))))
- (method-location
- (first
- (pcl:compute-applicable-methods-using-classes
- gf (list (find-class name))))))))))
-
- (defun make-name-in-file-location (file string)
- (multiple-value-bind (filename c)
- (ignore-errors
- (unix-truename (merge-pathnames (make-pathname :type "lisp")
- file)))
- (cond (filename (make-location `(:file ,filename)
- `(:function-name ,(string string))))
- (t (list :error (princ-to-string c))))))
-
- (defun source-location-form-numbers (location)
- (c::decode-form-numbers (c::form-numbers-form-numbers location)))
-
- (defun source-location-tlf-number (location)
- (nth-value 0 (source-location-form-numbers location)))
-
- (defun source-location-form-number (location)
- (nth-value 1 (source-location-form-numbers location)))
-
- (defun resolve-file-source-location (location)
- (let ((filename (c::file-source-location-pathname location))
- (tlf-number (source-location-tlf-number location))
- (form-number (source-location-form-number location)))
- (with-open-file (s filename)
- (let ((pos (form-number-stream-position tlf-number form-number s)))
- (make-location `(:file ,(unix-truename filename))
- `(:position ,(1+ pos)))))))
-
- (defun resolve-stream-source-location (location)
- (let ((info (c::stream-source-location-user-info location))
- (tlf-number (source-location-tlf-number location))
- (form-number (source-location-form-number location)))
- ;; XXX duplication in frame-source-location
- (assert (info-from-emacs-buffer-p info))
- (destructuring-bind (&key emacs-buffer emacs-buffer-string
- emacs-buffer-offset) info
- (with-input-from-string (s emacs-buffer-string)
- (let ((pos (form-number-stream-position tlf-number form-number s)))
- (make-location `(:buffer ,emacs-buffer)
- `(:offset ,emacs-buffer-offset ,pos)))))))
-
- ;; XXX predicates for 18e backward compatibilty. Remove them when
- ;; we're 19a only.
- (defun file-source-location-p (object)
- (when (fboundp 'c::file-source-location-p)
- (c::file-source-location-p object)))
-
- (defun stream-source-location-p (object)
- (when (fboundp 'c::stream-source-location-p)
- (c::stream-source-location-p object)))
-
- (defun source-location-p (object)
- (or (file-source-location-p object)
- (stream-source-location-p object)))
-
- (defun resolve-source-location (location)
- (etypecase location
- ((satisfies file-source-location-p)
- (resolve-file-source-location location))
- ((satisfies stream-source-location-p)
- (resolve-stream-source-location location))))
-
- (defun definition-source-location (object name)
- (let ((source (pcl::definition-source object)))
- (etypecase source
- (null
- `(:error ,(format nil "No source info for: ~A" object)))
- ((satisfies source-location-p)
- (resolve-source-location source))
- (pathname
- (make-name-in-file-location source name))
- (cons
- (destructuring-bind ((dg name) pathname) source
- (declare (ignore dg))
- (etypecase pathname
- (pathname (make-name-in-file-location pathname (string name)))
- (null `(:error ,(format nil "Cannot resolve: ~S" source)))))))))
-
- (defun setf-definitions (name)
- (let ((f (or (ext:info :setf :inverse name)
- (ext:info :setf :expander name)
- (and (symbolp name)
- (fboundp `(setf ,name))
- (fdefinition `(setf ,name))))))
- (if f
- `(((setf ,name) ,(function-location (cond ((functionp f) f)
- ((macro-function f))
- ((fdefinition f)))))))))
-
- (defun variable-location (symbol)
- (multiple-value-bind (location foundp)
- ;; XXX for 18e compatibilty. rewrite this when we drop 18e
- ;; support.
- (ignore-errors (eval `(ext:info :source-location :defvar ',symbol)))
- (if (and foundp location)
- (resolve-source-location location)
- `(:error ,(format nil "No source info for variable ~S" symbol)))))
-
- (defun variable-definitions (name)
- (if (symbolp name)
- (multiple-value-bind (kind recorded-p) (ext:info :variable :kind name)
- (if recorded-p
- (list (list `(variable ,kind ,name)
- (variable-location name)))))))
-
- (defun compiler-macro-definitions (symbol)
- (maybe-make-definition (compiler-macro-function symbol)
- 'define-compiler-macro
- symbol))
-
- (defun source-transform-definitions (name)
- (maybe-make-definition (ext:info :function :source-transform name)
- 'c:def-source-transform
- name))
-
- (defun function-info-definitions (name)
- (let ((info (ext:info :function :info name)))
- (if info
- (append (loop for transform in (c::function-info-transforms info)
- collect (list `(c:deftransform ,name
- ,(c::type-specifier
- (c::transform-type transform)))
- (function-location (c::transform-function
- transform))))
- (maybe-make-definition (c::function-info-derive-type info)
- 'c::derive-type name)
- (maybe-make-definition (c::function-info-optimizer info)
- 'c::optimizer name)
- (maybe-make-definition (c::function-info-ltn-annotate info)
- 'c::ltn-annotate name)
- (maybe-make-definition (c::function-info-ir2-convert info)
- 'c::ir2-convert name)
- (loop for template in (c::function-info-templates info)
- collect (list `(,(type-of template)
- ,(c::template-name template))
- (function-location
- (c::vop-info-generator-function
- template))))))))
-
- (defun ir1-translator-definitions (name)
- (maybe-make-definition (ext:info :function :ir1-convert name)
- 'c:def-ir1-translator name))
-
- (defun template-definitions (name)
- (let* ((templates (c::backend-template-names c::*backend*))
- (template (gethash name templates)))
- (etypecase template
- (null)
- (c::vop-info
- (maybe-make-definition (c::vop-info-generator-function template)
- (type-of template) name)))))
-
- ;; for cases like: (%primitive NAME ...)
- (defun primitive-definitions (name)
- (let ((csym (find-symbol (string name) 'c)))
- (and csym
- (not (eq csym name))
- (template-definitions csym))))
-
- (defun vm-support-routine-definitions (name)
- (let ((sr (c::backend-support-routines c::*backend*))
- (name (find-symbol (string name) 'c)))
- (and name
- (slot-exists-p sr name)
- (maybe-make-definition (slot-value sr name)
- (find-symbol (string 'vm-support-routine) 'c)
- name))))
-
- ;;;; Documentation.
-
- (defimplementation describe-symbol-for-emacs (symbol)
- (let ((result '()))
- (flet ((doc (kind)
- (or (documentation symbol kind) :not-documented))
- (maybe-push (property value)
- (when value
- (setf result (list* property value result)))))
- (maybe-push
- :variable (multiple-value-bind (kind recorded-p)
- (ext:info variable kind symbol)
- (declare (ignore kind))
- (if (or (boundp symbol) recorded-p)
- (doc 'variable))))
- (when (fboundp symbol)
- (maybe-push
- (cond ((macro-function symbol) :macro)
- ((special-operator-p symbol) :special-operator)
- ((genericp (fdefinition symbol)) :generic-function)
- (t :function))
- (doc 'function)))
- (maybe-push
- :setf (if (or (ext:info setf inverse symbol)
- (ext:info setf expander symbol))
- (doc 'setf)))
- (maybe-push
- :type (if (ext:info type kind symbol)
- (doc 'type)))
- (maybe-push
- :class (if (find-class symbol nil)
- (doc 'class)))
- (maybe-push
- :alien-type (if (not (eq (ext:info alien-type kind symbol) :unknown))
- (doc 'alien-type)))
- (maybe-push
- :alien-struct (if (ext:info alien-type struct symbol)
- (doc nil)))
- (maybe-push
- :alien-union (if (ext:info alien-type union symbol)
- (doc nil)))
- (maybe-push
- :alien-enum (if (ext:info alien-type enum symbol)
- (doc nil)))
- result)))
-
- (defimplementation describe-definition (symbol namespace)
- (describe (ecase namespace
- (:variable
- symbol)
- ((:function :generic-function)
- (symbol-function symbol))
- (:setf
- (or (ext:info setf inverse symbol)
- (ext:info setf expander symbol)))
- (:type
- (kernel:values-specifier-type symbol))
- (:class
- (find-class symbol))
- (:alien-struct
- (ext:info :alien-type :struct symbol))
- (:alien-union
- (ext:info :alien-type :union symbol))
- (:alien-enum
- (ext:info :alien-type :enum symbol))
- (:alien-type
- (ecase (ext:info :alien-type :kind symbol)
- (:primitive
- (let ((alien::*values-type-okay* t))
- (funcall (ext:info :alien-type :translator symbol)
- (list symbol))))
- ((:defined)
- (ext:info :alien-type :definition symbol))
- (:unknown :unkown))))))
-
- ;;;;; Argument lists
-
- (defimplementation arglist (fun)
- (etypecase fun
- (function (function-arglist fun))
- (symbol (function-arglist (or (macro-function fun)
- (symbol-function fun))))))
-
- (defun function-arglist (fun)
- (let ((arglist
- (cond ((eval:interpreted-function-p fun)
- (eval:interpreted-function-arglist fun))
- ((pcl::generic-function-p fun)
- (pcl:generic-function-lambda-list fun))
- ((c::byte-function-or-closure-p fun)
- (byte-code-function-arglist fun))
- ((kernel:%function-arglist (kernel:%function-self fun))
- (handler-case (read-arglist fun)
- (error () :not-available)))
- ;; this should work both for compiled-debug-function
- ;; and for interpreted-debug-function
- (t
- (handler-case (debug-function-arglist
- (di::function-debug-function fun))
- (di:unhandled-condition () :not-available))))))
- (check-type arglist (or list (member :not-available)))
- arglist))
-
- (defimplementation function-name (function)
- (cond ((eval:interpreted-function-p function)
- (eval:interpreted-function-name function))
- ((pcl::generic-function-p function)
- (pcl::generic-function-name function))
- ((c::byte-function-or-closure-p function)
- (c::byte-function-name function))
- (t (kernel:%function-name (kernel:%function-self function)))))
-
- ;;; A simple case: the arglist is available as a string that we can
- ;;; `read'.
-
- (defun read-arglist (fn)
- "Parse the arglist-string of the function object FN."
- (let ((string (kernel:%function-arglist
- (kernel:%function-self fn)))
- (package (find-package
- (c::compiled-debug-info-package
- (kernel:%code-debug-info
- (vm::find-code-object fn))))))
- (with-standard-io-syntax
- (let ((*package* (or package *package*)))
- (read-from-string string)))))
-
- ;;; A harder case: an approximate arglist is derived from available
- ;;; debugging information.
-
- (defun debug-function-arglist (debug-function)
- "Derive the argument list of DEBUG-FUNCTION from debug info."
- (let ((args (di::debug-function-lambda-list debug-function))
- (required '())
- (optional '())
- (rest '())
- (key '()))
- ;; collect the names of debug-vars
- (dolist (arg args)
- (etypecase arg
- (di::debug-variable
- (push (di::debug-variable-symbol arg) required))
- ((member :deleted)
- (push ':deleted required))
- (cons
- (ecase (car arg)
- (:keyword
- (push (second arg) key))
- (:optional
- (push (debug-variable-symbol-or-deleted (second arg)) optional))
- (:rest
- (push (debug-variable-symbol-or-deleted (second arg)) rest))))))
- ;; intersperse lambda keywords as needed
- (append (nreverse required)
- (if optional (cons '&optional (nreverse optional)))
- (if rest (cons '&rest (nreverse rest)))
- (if key (cons '&key (nreverse key))))))
-
- (defun debug-variable-symbol-or-deleted (var)
- (etypecase var
- (di:debug-variable
- (di::debug-variable-symbol var))
- ((member :deleted)
- '#:deleted)))
-
- (defun symbol-debug-function-arglist (fname)
- "Return FNAME's debug-function-arglist and %function-arglist.
- A utility for debugging DEBUG-FUNCTION-ARGLIST."
- (let ((fn (fdefinition fname)))
- (values (debug-function-arglist (di::function-debug-function fn))
- (kernel:%function-arglist (kernel:%function-self fn)))))
-
- ;;; Deriving arglists for byte-compiled functions:
- ;;;
- (defun byte-code-function-arglist (fn)
- ;; There doesn't seem to be much arglist information around for
- ;; byte-code functions. Use the arg-count and return something like
- ;; (arg0 arg1 ...)
- (etypecase fn
- (c::simple-byte-function
- (loop for i from 0 below (c::simple-byte-function-num-args fn)
- collect (make-arg-symbol i)))
- (c::hairy-byte-function
- (hairy-byte-function-arglist fn))
- (c::byte-closure
- (byte-code-function-arglist (c::byte-closure-function fn)))))
-
- (defun make-arg-symbol (i)
- (make-symbol (format nil "~A~D" (string 'arg) i)))
-
- ;;; A "hairy" byte-function is one that takes a variable number of
- ;;; arguments. `hairy-byte-function' is a type from the bytecode
- ;;; interpreter.
- ;;;
- (defun hairy-byte-function-arglist (fn)
- (let ((counter -1))
- (flet ((next-arg () (make-arg-symbol (incf counter))))
- (with-struct (c::hairy-byte-function- min-args max-args rest-arg-p
- keywords-p keywords) fn
- (let ((arglist '())
- (optional (- max-args min-args)))
- ;; XXX isn't there a better way to write this?
- ;; (Looks fine to me. -luke)
- (dotimes (i min-args)
- (push (next-arg) arglist))
- (when (plusp optional)
- (push '&optional arglist)
- (dotimes (i optional)
- (push (next-arg) arglist)))
- (when rest-arg-p
- (push '&rest arglist)
- (push (next-arg) arglist))
- (when keywords-p
- (push '&key arglist)
- (loop for (key _ __) in keywords
- do (push key arglist))
- (when (eq keywords-p :allow-others)
- (push '&allow-other-keys arglist)))
- (nreverse arglist))))))
-
- ;;;; Miscellaneous.
-
- (defimplementation macroexpand-all (form &optional env)
- (walker:macroexpand-all form env))
-
- (defimplementation compiler-macroexpand-1 (form &optional env)
- (ext:compiler-macroexpand-1 form env))
-
- (defimplementation compiler-macroexpand (form &optional env)
- (ext:compiler-macroexpand form env))
-
- (defimplementation set-default-directory (directory)
- (setf (ext:default-directory) (namestring directory))
- ;; Setting *default-pathname-defaults* to an absolute directory
- ;; makes the behavior of MERGE-PATHNAMES a bit more intuitive.
- (setf *default-pathname-defaults* (pathname (ext:default-directory)))
- (default-directory))
-
- (defimplementation default-directory ()
- (namestring (ext:default-directory)))
-
- (defimplementation getpid ()
- (unix:unix-getpid))
-
- (defimplementation lisp-implementation-type-name ()
- "cmucl")
-
- (defimplementation quit-lisp ()
- (ext::quit))
-
- ;;; source-path-{stream,file,string,etc}-position moved into
- ;;; source-path-parser
-
- ;;;; Debugging
-
- (defvar *sldb-stack-top*)
-
- (defimplementation call-with-debugging-environment (debugger-loop-fn)
- (unix:unix-sigsetmask 0)
- (let* ((*sldb-stack-top* (or debug:*stack-top-hint* (di:top-frame)))
- (debug:*stack-top-hint* nil)
- (kernel:*current-level* 0))
- (handler-bind ((di::unhandled-condition
- (lambda (condition)
- (error 'sldb-condition
- :original-condition condition))))
- (unwind-protect
- (progn
- #+(or)(sys:scrub-control-stack)
- (funcall debugger-loop-fn))
- #+(or)(sys:scrub-control-stack)
- ))))
-
- (defun frame-down (frame)
- (handler-case (di:frame-down frame)
- (di:no-debug-info () nil)))
-
- (defun nth-frame (index)
- (do ((frame *sldb-stack-top* (frame-down frame))
- (i index (1- i)))
- ((zerop i) frame)))
-
- (defimplementation compute-backtrace (start end)
- (let ((end (or end most-positive-fixnum)))
- (loop for f = (nth-frame start) then (frame-down f)
- for i from start below end
- while f collect f)))
-
- (defimplementation print-frame (frame stream)
- (let ((*standard-output* stream))
- (handler-case
- (debug::print-frame-call frame :verbosity 1 :number nil)
- (error (e)
- (ignore-errors (princ e stream))))))
-
- (defimplementation frame-source-location (index)
- (let ((frame (nth-frame index)))
- (cond ((foreign-frame-p frame) (foreign-frame-source-location frame))
- ((code-location-source-location (di:frame-code-location frame))))))
-
- (defimplementation eval-in-frame (form index)
- (di:eval-in-frame (nth-frame index) form))
-
- (defun frame-debug-vars (frame)
- "Return a vector of debug-variables in frame."
- (let ((loc (di:frame-code-location frame)))
- (remove-if
- (lambda (v)
- (not (eq (di:debug-variable-validity v loc) :valid)))
- (di::debug-function-debug-variables (di:frame-debug-function frame)))))
-
- (defun debug-var-value (var frame)
- (let* ((loc (di:frame-code-location frame))
- (validity (di:debug-variable-validity var loc)))
- (ecase validity
- (:valid (di:debug-variable-value var frame))
- ((:invalid :unknown) (make-symbol (string validity))))))
-
- (defimplementation frame-locals (index)
- (let ((frame (nth-frame index)))
- (loop for v across (frame-debug-vars frame)
- collect (list :name (di:debug-variable-symbol v)
- :id (di:debug-variable-id v)
- :value (debug-var-value v frame)))))
-
- (defimplementation frame-var-value (frame var)
- (let* ((frame (nth-frame frame))
- (dvar (aref (frame-debug-vars frame) var)))
- (debug-var-value dvar frame)))
-
- (defimplementation frame-catch-tags (index)
- (mapcar #'car (di:frame-catches (nth-frame index))))
-
- (defimplementation frame-package (frame-number)
- (let* ((frame (nth-frame frame-number))
- (dbg-fun (di:frame-debug-function frame)))
- (typecase dbg-fun
- (di::compiled-debug-function
- (let* ((comp (di::compiled-debug-function-component dbg-fun))
- (dbg-info (kernel:%code-debug-info comp)))
- (typecase dbg-info
- (c::compiled-debug-info
- (find-package (c::compiled-debug-info-package dbg-info)))))))))
-
- (defimplementation return-from-frame (index form)
- (let ((sym (find-symbol (string 'find-debug-tag-for-frame)
- :debug-internals)))
- (if sym
- (let* ((frame (nth-frame index))
- (probe (funcall sym frame)))
- (cond (probe (throw (car probe) (eval-in-frame form index)))
- (t (format nil "Cannot return from frame: ~S" frame))))
- "return-from-frame is not implemented in this version of CMUCL.")))
-
- (defimplementation activate-stepping (frame)
- (set-step-breakpoints (nth-frame frame)))
-
- (defimplementation sldb-break-on-return (frame)
- (break-on-return (nth-frame frame)))
-
- ;;; We set the breakpoint in the caller which might be a bit confusing.
- ;;;
- (defun break-on-return (frame)
- (let* ((caller (di:frame-down frame))
- (cl (di:frame-code-location caller)))
- (flet ((hook (frame bp)
- (when (frame-pointer= frame caller)
- (di:delete-breakpoint bp)
- (signal-breakpoint bp frame))))
- (let* ((info (ecase (di:code-location-kind cl)
- ((:single-value-return :unknown-return) nil)
- (:known-return (debug-function-returns
- (di:frame-debug-function frame)))))
- (bp (di:make-breakpoint #'hook cl :kind :code-location
- :info info)))
- (di:activate-breakpoint bp)
- `(:ok ,(format nil "Set breakpoint in ~A" caller))))))
-
- (defun frame-pointer= (frame1 frame2)
- "Return true if the frame pointers of FRAME1 and FRAME2 are the same."
- (sys:sap= (di::frame-pointer frame1) (di::frame-pointer frame2)))
-
- ;;; The PC in escaped frames at a single-return-value point is
- ;;; actually vm:single-value-return-byte-offset bytes after the
- ;;; position given in the debug info. Here we try to recognize such
- ;;; cases.
- ;;;
- (defun next-code-locations (frame code-location)
- "Like `debug::next-code-locations' but be careful in escaped frames."
- (let ((next (debug::next-code-locations code-location)))
- (flet ((adjust-pc ()
- (let ((cl (di::copy-compiled-code-location code-location)))
- (incf (di::compiled-code-location-pc cl)
- vm:single-value-return-byte-offset)
- cl)))
- (cond ((and (di::compiled-frame-escaped frame)
- (eq (di:code-location-kind code-location)
- :single-value-return)
- (= (length next) 1)
- (di:code-location= (car next) (adjust-pc)))
- (debug::next-code-locations (car next)))
- (t
- next)))))
-
- (defun set-step-breakpoints (frame)
- (let ((cl (di:frame-code-location frame)))
- (when (di:debug-block-elsewhere-p (di:code-location-debug-block cl))
- (error "Cannot step in elsewhere code"))
- (let* ((debug::*bad-code-location-types*
- (remove :call-site debug::*bad-code-location-types*))
- (next (next-code-locations frame cl)))
- (cond (next
- (let ((steppoints '()))
- (flet ((hook (bp-frame bp)
- (signal-breakpoint bp bp-frame)
- (mapc #'di:delete-breakpoint steppoints)))
- (dolist (code-location next)
- (let ((bp (di:make-breakpoint #'hook code-location
- :kind :code-location)))
- (di:activate-breakpoint bp)
- (push bp steppoints))))))
- (t
- (break-on-return frame))))))
-
-
- ;; XXX the return values at return breakpoints should be passed to the
- ;; user hooks. debug-int.lisp should be changed to do this cleanly.
-
- ;;; The sigcontext and the PC for a breakpoint invocation are not
- ;;; passed to user hook functions, but we need them to extract return
- ;;; values. So we advice di::handle-breakpoint and bind the values to
- ;;; special variables.
- ;;;
- (defvar *breakpoint-sigcontext*)
- (defvar *breakpoint-pc*)
-
- (define-fwrapper bind-breakpoint-sigcontext (offset c sigcontext)
- (let ((*breakpoint-sigcontext* sigcontext)
- (*breakpoint-pc* offset))
- (call-next-function)))
- (set-fwrappers 'di::handle-breakpoint '())
- (fwrap 'di::handle-breakpoint #'bind-breakpoint-sigcontext)
-
- (defun sigcontext-object (sc index)
- "Extract the lisp object in sigcontext SC at offset INDEX."
- (kernel:make-lisp-obj (vm:sigcontext-register sc index)))
-
- (defun known-return-point-values (sigcontext sc-offsets)
- (let ((fp (system:int-sap (vm:sigcontext-register sigcontext
- vm::cfp-offset))))
- (system:without-gcing
- (loop for sc-offset across sc-offsets
- collect (di::sub-access-debug-var-slot fp sc-offset sigcontext)))))
-
- ;;; CMUCL returns the first few values in registers and the rest on
- ;;; the stack. In the multiple value case, the number of values is
- ;;; stored in a dedicated register. The values of the registers can be
- ;;; accessed in the sigcontext for the breakpoint. There are 3 kinds
- ;;; of return conventions: :single-value-return, :unknown-return, and
- ;;; :known-return.
- ;;;
- ;;; The :single-value-return convention returns the value in a
- ;;; register without setting the nargs registers.
- ;;;
- ;;; The :unknown-return variant is used for multiple values. A
- ;;; :unknown-return point consists actually of 2 breakpoints: one for
- ;;; the single value case and one for the general case. The single
- ;;; value breakpoint comes vm:single-value-return-byte-offset after
- ;;; the multiple value breakpoint.
- ;;;
- ;;; The :known-return convention is used by local functions.
- ;;; :known-return is currently not supported because we don't know
- ;;; where the values are passed.
- ;;;
- (defun breakpoint-values (breakpoint)
- "Return the list of return values for a return point."
- (flet ((1st (sc) (sigcontext-object sc (car vm::register-arg-offsets))))
- (let ((sc (locally (declare (optimize (speed 0)))
- (alien:sap-alien *breakpoint-sigcontext* (* unix:sigcontext))))
- (cl (di:breakpoint-what breakpoint)))
- (ecase (di:code-location-kind cl)
- (:single-value-return
- (list (1st sc)))
- (:known-return
- (let ((info (di:breakpoint-info breakpoint)))
- (if (vectorp info)
- (known-return-point-values sc info)
- (progn
- ;;(break)
- (list "<<known-return convention not supported>>" info)))))
- (:unknown-return
- (let ((mv-return-pc (di::compiled-code-location-pc cl)))
- (if (= mv-return-pc *breakpoint-pc*)
- (mv-function-end-breakpoint-values sc)
- (list (1st sc)))))))))
-
- ;; XXX: di::get-function-end-breakpoint-values takes 2 arguments in
- ;; newer versions of CMUCL (after ~March 2005).
- (defun mv-function-end-breakpoint-values (sigcontext)
- (let ((sym (find-symbol "FUNCTION-END-BREAKPOINT-VALUES/STANDARD" :di)))
- (cond (sym (funcall sym sigcontext))
- (t (funcall 'di::get-function-end-breakpoint-values sigcontext)))))
-
- (defun debug-function-returns (debug-fun)
- "Return the return style of DEBUG-FUN."
- (let* ((cdfun (di::compiled-debug-function-compiler-debug-fun debug-fun)))
- (c::compiled-debug-function-returns cdfun)))
-
- (define-condition breakpoint (simple-condition)
- ((message :initarg :message :reader breakpoint.message)
- (values :initarg :values :reader breakpoint.values))
- (:report (lambda (c stream) (princ (breakpoint.message c) stream))))
-
- (defimplementation condition-extras (condition)
- (typecase condition
- (breakpoint
- ;; pop up the source buffer
- `((:show-frame-source 0)))
- (t '())))
-
- (defun signal-breakpoint (breakpoint frame)
- "Signal a breakpoint condition for BREAKPOINT in FRAME.
- Try to create a informative message."
- (flet ((brk (values fstring &rest args)
- (let ((msg (apply #'format nil fstring args))
- (debug:*stack-top-hint* frame))
- (break 'breakpoint :message msg :values values))))
- (with-struct (di::breakpoint- kind what) breakpoint
- (case kind
- (:code-location
- (case (di:code-location-kind what)
- ((:single-value-return :known-return :unknown-return)
- (let ((values (breakpoint-values breakpoint)))
- (brk values "Return value: ~{~S ~}" values)))
- (t
- #+(or)
- (when (eq (di:code-location-kind what) :call-site)
- (call-site-function breakpoint frame))
- (brk nil "Breakpoint: ~S ~S"
- (di:code-location-kind what)
- (di::compiled-code-location-pc what)))))
- (:function-start
- (brk nil "Function start breakpoint"))
- (t (brk nil "Breakpoint: ~A in ~A" breakpoint frame))))))
-
- (defimplementation sldb-break-at-start (fname)
- (let ((debug-fun (di:function-debug-function (coerce fname 'function))))
- (cond ((not debug-fun)
- `(:error ,(format nil "~S has no debug-function" fname)))
- (t
- (flet ((hook (frame bp &optional args cookie)
- (declare (ignore args cookie))
- (signal-breakpoint bp frame)))
- (let ((bp (di:make-breakpoint #'hook debug-fun
- :kind :function-start)))
- (di:activate-breakpoint bp)
- `(:ok ,(format nil "Set breakpoint in ~S" fname))))))))
-
- (defun frame-cfp (frame)
- "Return the Control-Stack-Frame-Pointer for FRAME."
- (etypecase frame
- (di::compiled-frame (di::frame-pointer frame))
- ((or di::interpreted-frame null) -1)))
-
- (defun frame-ip (frame)
- "Return the (absolute) instruction pointer and the relative pc of FRAME."
- (if (not frame)
- -1
- (let ((debug-fun (di::frame-debug-function frame)))
- (etypecase debug-fun
- (di::compiled-debug-function
- (let* ((code-loc (di:frame-code-location frame))
- (component (di::compiled-debug-function-component debug-fun))
- (pc (di::compiled-code-location-pc code-loc))
- (ip (sys:without-gcing
- (sys:sap-int
- (sys:sap+ (kernel:code-instructions component) pc)))))
- (values ip pc)))
- (di::interpreted-debug-function -1)
- (di::bogus-debug-function
- #-x86
- (let* ((real (di::frame-real-frame (di::frame-up frame)))
- (fp (di::frame-pointer real)))
- ;;#+(or)
- (progn
- (format *debug-io* "Frame-real-frame = ~S~%" real)
- (format *debug-io* "fp = ~S~%" fp)
- (format *debug-io* "lra = ~S~%"
- (kernel:stack-ref fp vm::lra-save-offset)))
- (values
- (sys:int-sap
- (- (kernel:get-lisp-obj-address
- (kernel:stack-ref fp vm::lra-save-offset))
- (- (ash vm:function-code-offset vm:word-shift)
- vm:function-pointer-type)))
- 0))
- #+x86
- (let ((fp (di::frame-pointer (di:frame-up frame))))
- (multiple-value-bind (ra ofp) (di::x86-call-context fp)
- (declare (ignore ofp))
- (values ra 0))))))))
-
- (defun frame-registers (frame)
- "Return the lisp registers CSP, CFP, IP, OCFP, LRA for FRAME-NUMBER."
- (let* ((cfp (frame-cfp frame))
- (csp (frame-cfp (di::frame-up frame)))
- (ip (frame-ip frame))
- (ocfp (frame-cfp (di::frame-down frame)))
- (lra (frame-ip (di::frame-down frame))))
- (values csp cfp ip ocfp lra)))
-
- (defun print-frame-registers (frame-number)
- (let ((frame (di::frame-real-frame (nth-frame frame-number))))
- (flet ((fixnum (p) (etypecase p
- (integer p)
- (sys:system-area-pointer (sys:sap-int p)))))
- (apply #'format t "~
- ~8X Stack Pointer
- ~8X Frame Pointer
- ~8X Instruction Pointer
- ~8X Saved Frame Pointer
- ~8X Saved Instruction Pointer~%" (mapcar #'fixnum
- (multiple-value-list (frame-registers frame)))))))
-
- (defvar *gdb-program-name*
- (ext:enumerate-search-list (p "path:gdb")
- (when (probe-file p)
- (return p))))
-
- (defimplementation disassemble-frame (frame-number)
- (print-frame-registers frame-number)
- (terpri)
- (let* ((frame (di::frame-real-frame (nth-frame frame-number)))
- (debug-fun (di::frame-debug-function frame)))
- (etypecase debug-fun
- (di::compiled-debug-function
- (let* ((component (di::compiled-debug-function-component debug-fun))
- (fun (di:debug-function-function debug-fun)))
- (if fun
- (disassemble fun)
- (disassem:disassemble-code-component component))))
- (di::bogus-debug-function
- (cond ((probe-file *gdb-program-name*)
- (let ((ip (sys:sap-int (frame-ip frame))))
- (princ (gdb-command "disas 0x~x" ip))))
- (t
- (format t "~%[Disassembling bogus frames not implemented]")))))))
-
- (defmacro with-temporary-file ((stream filename) &body body)
- `(call/temporary-file (lambda (,stream ,filename) . ,body)))
-
- (defun call/temporary-file (fun)
- (let ((name (system::pick-temporary-file-name)))
- (unwind-protect
- (with-open-file (stream name :direction :output :if-exists :supersede)
- (funcall fun stream name))
- (delete-file name))))
-
- (defun gdb-command (format-string &rest args)
- (let ((str (gdb-exec (format nil
- "interpreter-exec mi2 \"attach ~d\"~%~
- interpreter-exec console ~s~%detach"
- (getpid)
- (apply #'format nil format-string args))))
- (prompt (format nil
- #-(and darwin x86) "~%^done~%(gdb) ~%"
- #+(and darwin x86)
- "~%^done,thread-id=\"1\"~%(gdb) ~%")))
- (subseq str (+ (or (search prompt str) 0) (length prompt)))))
-
- (defun gdb-exec (cmd)
- (with-temporary-file (file filename)
- (write-string cmd file)
- (force-output file)
- (let* ((output (make-string-output-stream))
- ;; gdb on sparc needs to know the executable to find the
- ;; symbols. Without this, gdb can't disassemble anything.
- ;; NOTE: We assume that the first entry in
- ;; lisp::*cmucl-lib* is the bin directory where lisp is
- ;; located. If this is not true, we'll have to do
- ;; something better to find the lisp executable.
- (lisp-path
- #+sparc
- (list
- (namestring
- (probe-file
- (merge-pathnames "lisp" (car (lisp::parse-unix-search-path
- lisp::*cmucl-lib*))))))
- #-sparc
- nil)
- (proc (ext:run-program *gdb-program-name*
- `(,@lisp-path "-batch" "-x" ,filename)
- :wait t
- :output output)))
- (assert (eq (ext:process-status proc) :exited))
- (assert (eq (ext:process-exit-code proc) 0))
- (get-output-stream-string output))))
-
- (defun foreign-frame-p (frame)
- #-x86
- (let ((ip (frame-ip frame)))
- (and (sys:system-area-pointer-p ip)
- (typep (di::frame-debug-function frame) 'di::bogus-debug-function)))
- #+x86
- (let ((ip (frame-ip frame)))
- (and (sys:system-area-pointer-p ip)
- (multiple-value-bind (pc code)
- (di::compute-lra-data-from-pc ip)
- (declare (ignore pc))
- (not code)))))
-
- (defun foreign-frame-source-location (frame)
- (let ((ip (sys:sap-int (frame-ip frame))))
- (cond ((probe-file *gdb-program-name*)
- (parse-gdb-line-info (gdb-command "info line *0x~x" ip)))
- (t `(:error "no srcloc available for ~a" frame)))))
-
- ;; The output of gdb looks like:
- ;; Line 215 of "../../src/lisp/x86-assem.S"
- ;; starts at address 0x805318c <Ldone+11>
- ;; and ends at 0x805318e <Ldone+13>.
- ;; The ../../ are fixed up with the "target:" search list which might
- ;; be wrong sometimes.
- (defun parse-gdb-line-info (string)
- (with-input-from-string (*standard-input* string)
- (let ((w1 (read-word)))
- (cond ((equal w1 "Line")
- (let ((line (read-word)))
- (assert (equal (read-word) "of"))
- (let* ((file (read-from-string (read-word)))
- (pathname
- (or (probe-file file)
- (probe-file (format nil "target:lisp/~a" file))
- file)))
- (make-location (list :file (unix-truename pathname))
- (list :line (parse-integer line))))))
- (t
- `(:error ,string))))))
-
- (defun read-word (&optional (stream *standard-input*))
- (peek-char t stream)
- (concatenate 'string (loop until (whitespacep (peek-char nil stream))
- collect (read-char stream))))
-
- (defun whitespacep (char)
- (member char '(#\space #\newline)))
-
- ;;;; Inspecting
-
- (defconstant +lowtag-symbols+
- '(vm:even-fixnum-type
- vm:function-pointer-type
- vm:other-immediate-0-type
- vm:list-pointer-type
- vm:odd-fixnum-type
- vm:instance-pointer-type
- vm:other-immediate-1-type
- vm:other-pointer-type)
- "Names of the constants that specify type tags.
- The `symbol-value' of each element is a type tag.")
-
- (defconstant +header-type-symbols+
- (labels ((suffixp (suffix string)
- (and (>= (length string) (length suffix))
- (string= string suffix :start1 (- (length string)
- (length suffix)))))
- (header-type-symbol-p (x)
- (and (suffixp "-TYPE" (symbol-name x))
- (not (member x +lowtag-symbols+))
- (boundp x)
- (typep (symbol-value x) 'fixnum))))
- (remove-if-not #'header-type-symbol-p
- (append (apropos-list "-TYPE" "VM")
- (apropos-list "-TYPE" "BIGNUM"))))
- "A list of names of the type codes in boxed objects.")
-
- (defimplementation describe-primitive-type (object)
- (with-output-to-string (*standard-output*)
- (let* ((lowtag (kernel:get-lowtag object))
- (lowtag-symbol (find lowtag +lowtag-symbols+ :key #'symbol-value)))
- (format t "lowtag: ~A" lowtag-symbol)
- (when (member lowtag (list vm:other-pointer-type
- vm:function-pointer-type
- vm:other-immediate-0-type
- vm:other-immediate-1-type
- ))
- (let* ((type (kernel:get-type object))
- (type-symbol (find type +header-type-symbols+
- :key #'symbol-value)))
- (format t ", type: ~A" type-symbol))))))
-
- (defmethod emacs-inspect ((o t))
- (cond ((di::indirect-value-cell-p o)
- `("Value: " (:value ,(c:value-cell-ref o))))
- ((alien::alien-value-p o)
- (inspect-alien-value o))
- (t
- (cmucl-inspect o))))
-
- (defun cmucl-inspect (o)
- (destructuring-bind (text labeledp . parts) (inspect::describe-parts o)
- (list* (format nil "~A~%" text)
- (if labeledp
- (loop for (label . value) in parts
- append (label-value-line label value))
- (loop for value in parts for i from 0
- append (label-value-line i value))))))
-
- (defmethod emacs-inspect ((o function))
- (let ((header (kernel:get-type o)))
- (cond ((= header vm:function-header-type)
- (append (label-value-line*
- ("Self" (kernel:%function-self o))
- ("Next" (kernel:%function-next o))
- ("Name" (kernel:%function-name o))
- ("Arglist" (kernel:%function-arglist o))
- ("Type" (kernel:%function-type o))
- ("Code" (kernel:function-code-header o)))
- (list
- (with-output-to-string (s)
- (disassem:disassemble-function o :stream s)))))
- ((= header vm:closure-header-type)
- (list* (format nil "~A is a closure.~%" o)
- (append
- (label-value-line "Function" (kernel:%closure-function o))
- `("Environment:" (:newline))
- (loop for i from 0 below (1- (kernel:get-closure-length o))
- append (label-value-line
- i (kernel:%closure-index-ref o i))))))
- ((eval::interpreted-function-p o)
- (cmucl-inspect o))
- (t
- (call-next-method)))))
-
- (defmethod emacs-inspect ((o kernel:funcallable-instance))
- (append (label-value-line*
- (:function (kernel:%funcallable-instance-function o))
- (:lexenv (kernel:%funcallable-instance-lexenv o))
- (:layout (kernel:%funcallable-instance-layout o)))
- (cmucl-inspect o)))
-
- (defmethod emacs-inspect ((o kernel:code-component))
- (append
- (label-value-line*
- ("code-size" (kernel:%code-code-size o))
- ("entry-points" (kernel:%code-entry-points o))
- ("debug-info" (kernel:%code-debug-info o))
- ("trace-table-offset" (kernel:code-header-ref
- o vm:code-trace-table-offset-slot)))
- `("Constants:" (:newline))
- (loop for i from vm:code-constants-offset
- below (kernel:get-header-data o)
- append (label-value-line i (kernel:code-header-ref o i)))
- `("Code:"
- (:newline)
- , (with-output-to-string (*standard-output*)
- (cond ((c::compiled-debug-info-p (kernel:%code-debug-info o))
- (disassem:disassemble-code-component o))
- ((or
- (c::debug-info-p (kernel:%code-debug-info o))
- (consp (kernel:code-header-ref
- o vm:code-trace-table-offset-slot)))
- (c:disassem-byte-component o))
- (t
- (disassem:disassemble-memory
- (disassem::align
- (+ (logandc2 (kernel:get-lisp-obj-address o)
- vm:lowtag-mask)
- (* vm:code-constants-offset vm:word-bytes))
- (ash 1 vm:lowtag-bits))
- (ash (kernel:%code-code-size o) vm:word-shift))))))))
-
- (defmethod emacs-inspect ((o kernel:fdefn))
- (label-value-line*
- ("name" (kernel:fdefn-name o))
- ("function" (kernel:fdefn-function o))
- ("raw-addr" (sys:sap-ref-32
- (sys:int-sap (kernel:get-lisp-obj-address o))
- (* vm:fdefn-raw-addr-slot vm:word-bytes)))))
-
- #+(or)
- (defmethod emacs-inspect ((o array))
- (if (typep o 'simple-array)
- (call-next-method)
- (label-value-line*
- (:header (describe-primitive-type o))
- (:rank (array-rank o))
- (:fill-pointer (kernel:%array-fill-pointer o))
- (:fill-pointer-p (kernel:%array-fill-pointer-p o))
- (:elements (kernel:%array-available-elements o))
- (:data (kernel:%array-data-vector o))
- (:displacement (kernel:%array-displacement o))
- (:displaced-p (kernel:%array-displaced-p o))
- (:dimensions (array-dimensions o)))))
-
- (defmethod emacs-inspect ((o simple-vector))
- (append
- (label-value-line*
- (:header (describe-primitive-type o))
- (:length (c::vector-length o)))
- (loop for i below (length o)
- append (label-value-line i (aref o i)))))
-
- (defun inspect-alien-record (alien)
- (with-struct (alien::alien-value- sap type) alien
- (with-struct (alien::alien-record-type- kind name fields) type
- (append
- (label-value-line*
- (:sap sap)
- (:kind kind)
- (:name name))
- (loop for field in fields
- append (let ((slot (alien::alien-record-field-name field)))
- (declare (optimize (speed 0)))
- (label-value-line slot (alien:slot alien slot))))))))
-
- (defun inspect-alien-pointer (alien)
- (with-struct (alien::alien-value- sap type) alien
- (label-value-line*
- (:sap sap)
- (:type type)
- (:to (alien::deref alien)))))
-
- (defun inspect-alien-value (alien)
- (typecase (alien::alien-value-type alien)
- (alien::alien-record-type (inspect-alien-record alien))
- (alien::alien-pointer-type (inspect-alien-pointer alien))
- (t (cmucl-inspect alien))))
-
- (defimplementation eval-context (obj)
- (cond ((typep (class-of obj) 'structure-class)
- (let* ((dd (kernel:layout-info (kernel:layout-of obj)))
- (slots (kernel:dd-slots dd)))
- (list* (cons '*package*
- (symbol-package (if slots
- (kernel:dsd-name (car slots))
- (kernel:dd-name dd))))
- (loop for slot in slots collect
- (cons (kernel:dsd-name slot)
- (funcall (kernel:dsd-accessor slot) obj))))))))
-
- ;;;; Profiling
- (defimplementation profile (fname)
- (eval `(profile:profile ,fname)))
-
- (defimplementation unprofile (fname)
- (eval `(profile:unprofile ,fname)))
-
- (defimplementation unprofile-all ()
- (eval `(profile:unprofile))
- "All functions unprofiled.")
-
- (defimplementation profile-report ()
- (eval `(profile:report-time)))
-
- (defimplementation profile-reset ()
- (eval `(profile:reset-time))
- "Reset profiling counters.")
-
- (defimplementation profiled-functions ()
- profile:*timed-functions*)
-
- (defimplementation profile-package (package callers methods)
- (profile:profile-all :package package
- :callers-p callers
- :methods methods))
-
- ;;;; Multiprocessing
-
- #+mp
- (progn
- (defimplementation initialize-multiprocessing (continuation)
- (mp::init-multi-processing)
- (mp:make-process continuation :name "swank")
- ;; Threads magic: this never returns! But top-level becomes
- ;; available again.
- (unless mp::*idle-process*
- (mp::startup-idle-and-top-level-loops)))
-
- (defimplementation spawn (fn &key name)
- (mp:make-process fn :name (or name "Anonymous")))
-
- (defvar *thread-id-counter* 0)
-
- (defimplementation thread-id (thread)
- (or (getf (mp:process-property-list thread) 'id)
- (setf (getf (mp:process-property-list thread) 'id)
- (incf *thread-id-counter*))))
-
- (defimplementation find-thread (id)
- (find id (all-threads)
- :key (lambda (p) (getf (mp:process-property-list p) 'id))))
-
- (defimplementation thread-name (thread)
- (mp:process-name thread))
-
- (defimplementation thread-status (thread)
- (mp:process-whostate thread))
-
- (defimplementation current-thread ()
- mp:*current-process*)
-
- (defimplementation all-threads ()
- (copy-list mp:*all-processes*))
-
- (defimplementation interrupt-thread (thread fn)
- (mp:process-interrupt thread fn))
-
- (defimplementation kill-thread (thread)
- (mp:destroy-process thread))
-
- (defvar *mailbox-lock* (mp:make-lock "mailbox lock"))
-
- (defstruct (mailbox (:conc-name mailbox.))
- (mutex (mp:make-lock "process mailbox"))
- (queue '() :type list))
-
- (defun mailbox (thread)
- "Return THREAD's mailbox."
- (mp:with-lock-held (*mailbox-lock*)
- (or (getf (mp:process-property-list thread) 'mailbox)
- (setf (getf (mp:process-property-list thread) 'mailbox)
- (make-mailbox)))))
-
- (defimplementation send (thread message)
- (check-slime-interrupts)
- (let* ((mbox (mailbox thread)))
- (mp:with-lock-held ((mailbox.mutex mbox))
- (setf (mailbox.queue mbox)
- (nconc (mailbox.queue mbox) (list message))))))
-
- (defimplementation receive-if (test &optional timeout)
- (let ((mbox (mailbox mp:*current-process*)))
- (assert (or (not timeout) (eq timeout t)))
- (loop
- (check-slime-interrupts)
- (mp:with-lock-held ((mailbox.mutex mbox))
- (let* ((q (mailbox.queue mbox))
- (tail (member-if test q)))
- (when tail
- (setf (mailbox.queue mbox)
- (nconc (ldiff q tail) (cdr tail)))
- (return (car tail)))))
- (when (eq timeout t) (return (values nil t)))
- (mp:process-wait-with-timeout
- "receive-if" 0.5
- (lambda () (some test (mailbox.queue mbox)))))))
-
-
- ) ;; #+mp
-
-
- ;;;; GC hooks
- ;;;
- ;;; Display GC messages in the echo area to avoid cluttering the
- ;;; normal output.
- ;;;
-
- ;; this should probably not be here, but where else?
- (defun background-message (message)
- (swank::background-message message))
-
- (defun print-bytes (nbytes &optional stream)
- "Print the number NBYTES to STREAM in KB, MB, or GB units."
- (let ((names '((0 bytes) (10 kb) (20 mb) (30 gb) (40 tb) (50 eb))))
- (multiple-value-bind (power name)
- (loop for ((p1 n1) (p2 n2)) on names
- while n2 do
- (when (<= (expt 2 p1) nbytes (1- (expt 2 p2)))
- (return (values p1 n1))))
- (cond (name
- (format stream "~,1F ~A" (/ nbytes (expt 2 power)) name))
- (t
- (format stream "~:D bytes" nbytes))))))
-
- (defconstant gc-generations 6)
-
- #+gencgc
- (defun generation-stats ()
- "Return a string describing the size distribution among the generations."
- (let* ((alloc (loop for i below gc-generations
- collect (lisp::gencgc-stats i)))
- (sum (coerce (reduce #'+ alloc) 'float)))
- (format nil "~{~3F~^/~}"
- (mapcar (lambda (size) (/ size sum))
- alloc))))
-
- (defvar *gc-start-time* 0)
-
- (defun pre-gc-hook (bytes-in-use)
- (setq *gc-start-time* (get-internal-real-time))
- (let ((msg (format nil "[Commencing GC with ~A in use.]"
- (print-bytes bytes-in-use))))
- (background-message msg)))
-
- (defun post-gc-hook (bytes-retained bytes-freed trigger)
- (declare (ignore trigger))
- (let* ((seconds (/ (- (get-internal-real-time) *gc-start-time*)
- internal-time-units-per-second))
- (msg (format nil "[GC done. ~A freed ~A retained ~A ~4F sec]"
- (print-bytes bytes-freed)
- (print-bytes bytes-retained)
- #+gencgc(generation-stats)
- #-gencgc""
- seconds)))
- (background-message msg)))
-
- (defun install-gc-hooks ()
- (setq ext:*gc-notify-before* #'pre-gc-hook)
- (setq ext:*gc-notify-after* #'post-gc-hook))
-
- (defun remove-gc-hooks ()
- (setq ext:*gc-notify-before* #'lisp::default-gc-notify-before)
- (setq ext:*gc-notify-after* #'lisp::default-gc-notify-after))
-
- (defvar *install-gc-hooks* t
- "If non-nil install GC hooks")
-
- (defimplementation emacs-connected ()
- (when *install-gc-hooks*
- (install-gc-hooks)))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;Trace implementations
- ;;In CMUCL, we have:
- ;; (trace <name>)
- ;; (trace (method <name> <qualifier>? (<specializer>+)))
- ;; (trace :methods t '<name>) ;;to trace all methods of the gf <name>
- ;; <name> can be a normal name or a (setf name)
-
- (defun tracedp (spec)
- (member spec (eval '(trace)) :test #'equal))
-
- (defun toggle-trace-aux (spec &rest options)
- (cond ((tracedp spec)
- (eval `(untrace ,spec))
- (format nil "~S is now untraced." spec))
- (t
- (eval `(trace ,spec ,@options))
- (format nil "~S is now traced." spec))))
-
- (defimplementation toggle-trace (spec)
- (ecase (car spec)
- ((setf)
- (toggle-trace-aux spec))
- ((:defgeneric)
- (let ((name (second spec)))
- (toggle-trace-aux name :methods name)))
- ((:defmethod)
- (cond ((fboundp `(method ,@(cdr spec)))
- (toggle-trace-aux `(method ,(cdr spec))))
- ;; Man, is this ugly
- ((fboundp `(pcl::fast-method ,@(cdr spec)))
- (toggle-trace-aux `(pcl::fast-method ,@(cdr spec))))
- (t
- (error 'undefined-function :name (cdr spec)))))
- ((:call)
- (destructuring-bind (caller callee) (cdr spec)
- (toggle-trace-aux (process-fspec callee)
- :wherein (list (process-fspec caller)))))
- ;; doesn't work properly
- ;; ((:labels :flet) (toggle-trace-aux (process-fspec spec)))
- ))
-
- (defun process-fspec (fspec)
- (cond ((consp fspec)
- (ecase (first fspec)
- ((:defun :defgeneric) (second fspec))
- ((:defmethod)
- `(method ,(second fspec) ,@(third fspec) ,(fourth fspec)))
- ((:labels) `(labels ,(third fspec) ,(process-fspec (second fspec))))
- ((:flet) `(flet ,(third fspec) ,(process-fspec (second fspec))))))
- (t
- fspec)))
-
- ;;; Weak datastructures
-
- (defimplementation make-weak-key-hash-table (&rest args)
- (apply #'make-hash-table :weak-p t args))
-
-
- ;;; Save image
-
- (defimplementation save-image (filename &optional restart-function)
- (multiple-value-bind (pid error) (unix:unix-fork)
- (when (not pid) (error "fork: ~A" (unix:get-unix-error-msg error)))
- (cond ((= pid 0)
- (apply #'ext:save-lisp
- filename
- (if restart-function
- `(:init-function ,restart-function))))
- (t
- (let ((status (waitpid pid)))
- (destructuring-bind (&key exited? status &allow-other-keys) status
- (assert (and exited? (equal status 0)) ()
- "Invalid exit status: ~a" status)))))))
-
- (defun waitpid (pid)
- (alien:with-alien ((status c-call:int))
- (let ((code (alien:alien-funcall
- (alien:extern-alien
- waitpid (alien:function c-call:int c-call:int
- (* c-call:int) c-call:int))
- pid (alien:addr status) 0)))
- (cond ((= code -1) (error "waitpid: ~A" (unix:get-unix-error-msg)))
- (t (assert (= code pid))
- (decode-wait-status status))))))
-
- (defun decode-wait-status (status)
- (let ((output (with-output-to-string (s)
- (call-program (list (process-status-program)
- (format nil "~d" status))
- :output s))))
- (read-from-string output)))
-
- (defun call-program (args &key output)
- (destructuring-bind (program &rest args) args
- (let ((process (ext:run-program program args :output output)))
- (when (not program) (error "fork failed"))
- (unless (and (eq (ext:process-status process) :exited)
- (= (ext:process-exit-code process) 0))
- (error "Non-zero exit status")))))
-
- (defvar *process-status-program* nil)
-
- (defun process-status-program ()
- (or *process-status-program*
- (setq *process-status-program*
- (compile-process-status-program))))
-
- (defun compile-process-status-program ()
- (let ((infile (system::pick-temporary-file-name
- "/tmp/process-status~d~c.c")))
- (with-open-file (stream infile :direction :output :if-exists :supersede)
- (format stream "
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <assert.h>
-
- #define FLAG(value) (value ? \"t\" : \"nil\")
-
- int main (int argc, char** argv) {
- assert (argc == 2);
- {
- char* endptr = NULL;
- char* arg = argv[1];
- long int status = strtol (arg, &endptr, 10);
- assert (endptr != arg && *endptr == '\\0');
- printf (\"(:exited? %s :status %d :signal? %s :signal %d :coredump? %s\"
- \" :stopped? %s :stopsig %d)\\n\",
- FLAG(WIFEXITED(status)), WEXITSTATUS(status),
- FLAG(WIFSIGNALED(status)), WTERMSIG(status),
- FLAG(WCOREDUMP(status)),
- FLAG(WIFSTOPPED(status)), WSTOPSIG(status));
- fflush (NULL);
- return 0;
- }
- }
- ")
- (finish-output stream))
- (let* ((outfile (system::pick-temporary-file-name))
- (args (list "cc" "-o" outfile infile)))
- (warn "Running cc: ~{~a ~}~%" args)
- (call-program args :output t)
- (delete-file infile)
- outfile)))
-
- ;; FIXME: lisp:unicode-complete introduced in version 20d.
- #+#.(swank/backend:with-symbol 'unicode-complete 'lisp)
- (defun match-semi-standard (prefix matchp)
- ;; Handle the CMUCL's short character names.
- (loop for name in lisp::char-name-alist
- when (funcall matchp prefix (car name))
- collect (car name)))
-
- #+#.(swank/backend:with-symbol 'unicode-complete 'lisp)
- (defimplementation character-completion-set (prefix matchp)
- (let ((names (lisp::unicode-complete prefix)))
- ;; Match prefix against semistandard names. If there's a match,
- ;; add it to our list of matches.
- (let ((semi-standard (match-semi-standard prefix matchp)))
- (when semi-standard
- (setf names (append semi-standard names))))
- (setf names (mapcar #'string-capitalize names))
- (loop for n in names
- when (funcall matchp prefix n)
- collect n)))
|