| 1 | ;;; -*- Mode: Lisp; Package: ED; Log: hemlock.log -*-
|
|---|
| 2 | ;;;
|
|---|
| 3 | ;;; **********************************************************************
|
|---|
| 4 | ;;; This code was written as part of the CMU Common Lisp project at
|
|---|
| 5 | ;;; Carnegie Mellon University, and has been placed in the public domain.
|
|---|
| 6 | ;;;
|
|---|
| 7 | #+CMU (ext:file-comment
|
|---|
| 8 | "$Header$")
|
|---|
| 9 | ;;;
|
|---|
| 10 | ;;; **********************************************************************
|
|---|
| 11 | ;;;
|
|---|
| 12 | ;;; This contains commands for sending debugger commands to slaves in the
|
|---|
| 13 | ;;; debugger.
|
|---|
| 14 | ;;;
|
|---|
| 15 | ;;; Written by Bill Chiles.
|
|---|
| 16 | ;;;
|
|---|
| 17 |
|
|---|
| 18 | (in-package :hemlock)
|
|---|
| 19 |
|
|---|
| 20 |
|
|---|
| 21 | |
|---|
| 22 |
|
|---|
| 23 | ;;;; DEFINE-DEBUGGER-COMMAND.
|
|---|
| 24 |
|
|---|
| 25 | (defmacro define-debugger-command (name doc cmd &key uses-argument)
|
|---|
| 26 | `(defcommand ,(concatenate 'simple-string "Debug " name) (p)
|
|---|
| 27 | ,doc ,doc
|
|---|
| 28 | ,@(if uses-argument
|
|---|
| 29 | nil
|
|---|
| 30 | '((declare (ignore p))))
|
|---|
| 31 | (let* ((server-info (get-current-eval-server t))
|
|---|
| 32 | (wire (server-info-wire server-info)))
|
|---|
| 33 | (wire:remote wire
|
|---|
| 34 | (ts-stream-accept-input
|
|---|
| 35 | (ts-data-stream (server-info-slave-info server-info))
|
|---|
| 36 | ,(if uses-argument
|
|---|
| 37 | `(list ,cmd p)
|
|---|
| 38 | cmd)))
|
|---|
| 39 | (wire:wire-force-output wire))))
|
|---|
| 40 |
|
|---|
| 41 |
|
|---|
| 42 | |
|---|
| 43 |
|
|---|
| 44 | ;;;; Frame changing commands.
|
|---|
| 45 |
|
|---|
| 46 | (define-debugger-command "Up"
|
|---|
| 47 | "Moves the \"Current Eval Server\" up one debugger frame."
|
|---|
| 48 | :up)
|
|---|
| 49 |
|
|---|
| 50 | (define-debugger-command "Down"
|
|---|
| 51 | "Moves the \"Current Eval Server\" down one debugger frame."
|
|---|
| 52 | :down)
|
|---|
| 53 |
|
|---|
| 54 | (define-debugger-command "Top"
|
|---|
| 55 | "Moves the \"Current Eval Server\" to the top of the debugging stack."
|
|---|
| 56 | :top)
|
|---|
| 57 |
|
|---|
| 58 | (define-debugger-command "Bottom"
|
|---|
| 59 | "Moves the \"Current Eval Server\" to the bottom of the debugging stack."
|
|---|
| 60 | :bottom)
|
|---|
| 61 |
|
|---|
| 62 | (define-debugger-command "Frame"
|
|---|
| 63 | "Moves the \"Current Eval Server\" to the absolute debugger frame number
|
|---|
| 64 | indicated by the prefix argument."
|
|---|
| 65 | :frame
|
|---|
| 66 | :uses-argument t)
|
|---|
| 67 |
|
|---|
| 68 |
|
|---|
| 69 | |
|---|
| 70 |
|
|---|
| 71 | ;;;; In and Out commands.
|
|---|
| 72 |
|
|---|
| 73 | (define-debugger-command "Quit"
|
|---|
| 74 | "In the \"Current Eval Server\", throws to top level out of the debugger."
|
|---|
| 75 | :quit)
|
|---|
| 76 |
|
|---|
| 77 | (define-debugger-command "Go"
|
|---|
| 78 | "In the \"Current Eval Server\", tries the CONTINUE restart."
|
|---|
| 79 | :go)
|
|---|
| 80 |
|
|---|
| 81 | (define-debugger-command "Abort"
|
|---|
| 82 | "In the \"Current Eval Server\", execute the previous ABORT restart."
|
|---|
| 83 | :abort)
|
|---|
| 84 |
|
|---|
| 85 | (define-debugger-command "Restart"
|
|---|
| 86 | "In the \"Current Eval Server\", executes the restart indicated by the
|
|---|
| 87 | prefix argument."
|
|---|
| 88 | :restart
|
|---|
| 89 | :uses-argument t)
|
|---|
| 90 |
|
|---|
| 91 |
|
|---|
| 92 | |
|---|
| 93 |
|
|---|
| 94 | ;;;; Information commands.
|
|---|
| 95 |
|
|---|
| 96 | (define-debugger-command "Help"
|
|---|
| 97 | "In the \"Current Eval Server\", prints the debugger's help text."
|
|---|
| 98 | :help)
|
|---|
| 99 |
|
|---|
| 100 | (define-debugger-command "Error"
|
|---|
| 101 | "In the \"Current Eval Server\", print the error condition and restart cases
|
|---|
| 102 | upon entering the debugger."
|
|---|
| 103 | :error)
|
|---|
| 104 |
|
|---|
| 105 | (define-debugger-command "Backtrace"
|
|---|
| 106 | "Executes the debugger's BACKTRACE command."
|
|---|
| 107 | :backtrace)
|
|---|
| 108 |
|
|---|
| 109 | (define-debugger-command "Print"
|
|---|
| 110 | "In the \"Current Eval Server\", prints a representation of the debugger's
|
|---|
| 111 | current frame."
|
|---|
| 112 | :print)
|
|---|
| 113 |
|
|---|
| 114 | (define-debugger-command "Verbose Print"
|
|---|
| 115 | "In the \"Current Eval Server\", prints a representation of the debugger's
|
|---|
| 116 | current frame without elipsis."
|
|---|
| 117 | :vprint)
|
|---|
| 118 |
|
|---|
| 119 | (define-debugger-command "List Locals"
|
|---|
| 120 | "In the \"Current Eval Server\", prints the local variables for the debugger's
|
|---|
| 121 | current frame."
|
|---|
| 122 | :list-locals)
|
|---|
| 123 |
|
|---|
| 124 | (define-debugger-command "Source"
|
|---|
| 125 | "In the \"Current Eval Server\", prints the source form for the debugger's
|
|---|
| 126 | current frame."
|
|---|
| 127 | :source)
|
|---|
| 128 |
|
|---|
| 129 | (define-debugger-command "Verbose Source"
|
|---|
| 130 | "In the \"Current Eval Server\", prints the source form for the debugger's
|
|---|
| 131 | current frame with surrounding forms for context."
|
|---|
| 132 | :vsource)
|
|---|
| 133 |
|
|---|
| 134 |
|
|---|
| 135 | |
|---|
| 136 |
|
|---|
| 137 | ;;;; Source editing.
|
|---|
| 138 |
|
|---|
| 139 | ;;; "Debug Edit Source" -- Command.
|
|---|
| 140 | ;;;
|
|---|
| 141 | ;;; The :edit-source command in the slave debugger initiates a synchronous RPC
|
|---|
| 142 | ;;; into the editor via the wire in *terminal-io*, a typescript stream. This
|
|---|
| 143 | ;;; routine takes the necessary values, a file and source-path, and changes the
|
|---|
| 144 | ;;; editor's state to display that location.
|
|---|
| 145 | ;;;
|
|---|
| 146 | ;;; This command has to wait on SERVE-EVENT until some special is set by the
|
|---|
| 147 | ;;; RPC routine saying it is okay to return to the editor's top level.
|
|---|
| 148 | ;;;
|
|---|
| 149 | (defvar *debug-editor-source-data* nil)
|
|---|
| 150 | (defvar *in-debug-edit-source* nil)
|
|---|
| 151 |
|
|---|
| 152 | (defcommand "Debug Edit Source" (p)
|
|---|
| 153 | "Given the \"Current Eval Server\"'s current debugger frame, place the user
|
|---|
| 154 | at the location's source in the editor."
|
|---|
| 155 | "Given the \"Current Eval Server\"'s current debugger frame, place the user
|
|---|
| 156 | at the location's source in the editor."
|
|---|
| 157 | (declare (ignore p))
|
|---|
| 158 | (let* ((server-info (get-current-eval-server t))
|
|---|
| 159 | (wire (server-info-wire server-info)))
|
|---|
| 160 | ;;
|
|---|
| 161 | ;; Tell the slave to tell the editor some source info.
|
|---|
| 162 | (wire:remote wire
|
|---|
| 163 | (ts-stream-accept-input
|
|---|
| 164 | (ts-data-stream (server-info-slave-info server-info))
|
|---|
| 165 | :edit-source))
|
|---|
| 166 | (wire:wire-force-output wire)
|
|---|
| 167 | ;;
|
|---|
| 168 | ;; Wait for the source info.
|
|---|
| 169 | (let ((*debug-editor-source-data* nil)
|
|---|
| 170 | (*in-debug-edit-source* t))
|
|---|
| 171 | (catch 'blow-debug-edit-source
|
|---|
| 172 | (loop
|
|---|
| 173 | (system:serve-event)
|
|---|
| 174 | (when *debug-editor-source-data* (return)))))))
|
|---|
| 175 |
|
|---|
| 176 | ;;; EDIT-SOURCE-LOCATION -- Internal Interface.
|
|---|
| 177 | ;;;
|
|---|
| 178 | ;;; The slave calls this in the editor when the debugger gets an :edit-source
|
|---|
| 179 | ;;; command. This receives the information necessary to take the user in
|
|---|
| 180 | ;;; Hemlock to the source location, and does it.
|
|---|
| 181 | ;;;
|
|---|
| 182 | (defun edit-source-location (name source-created-date tlf-offset
|
|---|
| 183 | local-tlf-offset char-offset form-number)
|
|---|
| 184 | (let ((pn (pathname name)))
|
|---|
| 185 | (unless (probe-file pn)
|
|---|
| 186 | (editor-error "Source file no longer exists: ~A." name))
|
|---|
| 187 | (multiple-value-bind (buffer newp) (find-file-buffer pn)
|
|---|
| 188 | (let ((date (buffer-write-date buffer))
|
|---|
| 189 | (point (buffer-point buffer)))
|
|---|
| 190 | (when newp (push-buffer-mark (copy-mark point) nil))
|
|---|
| 191 | (buffer-start point)
|
|---|
| 192 | ;;
|
|---|
| 193 | ;; Get to the top-level form in the buffer.
|
|---|
| 194 | (cond ((buffer-modified buffer)
|
|---|
| 195 | (loud-message "Buffer has been modified. Using form offset ~
|
|---|
| 196 | instead of character position.")
|
|---|
| 197 | (dotimes (i local-tlf-offset)
|
|---|
| 198 | (pre-command-parse-check point)
|
|---|
| 199 | (form-offset point 1)))
|
|---|
| 200 | ((not date)
|
|---|
| 201 | (loud-message "Cannot compare write dates. Assuming source ~
|
|---|
| 202 | has not been modified -- ~A."
|
|---|
| 203 | name)
|
|---|
| 204 | (character-offset point char-offset))
|
|---|
| 205 | ((= source-created-date date)
|
|---|
| 206 | (character-offset point char-offset))
|
|---|
| 207 | (t
|
|---|
| 208 | (loud-message "File has been modified since reading the source. ~
|
|---|
| 209 | Using form offset instead of character position.")
|
|---|
| 210 | (dotimes (i local-tlf-offset)
|
|---|
| 211 | (pre-command-parse-check point)
|
|---|
| 212 | (form-offset point 1))))
|
|---|
| 213 | ;;
|
|---|
| 214 | ;; Read our form, get form-number translations, get the source-path,
|
|---|
| 215 | ;; and make it usable.
|
|---|
| 216 | ;;
|
|---|
| 217 | ;; NOTE: Here READ is used in the editor lisp to look at a form
|
|---|
| 218 | ;; that the compiler has digested in the slave lisp. The editor
|
|---|
| 219 | ;; does not have the same environment at the slave so bad things
|
|---|
| 220 | ;; can happen if READ hits a #. reader macro (like unknown package
|
|---|
| 221 | ;; or undefined function errors) which can break the editor. This
|
|---|
| 222 | ;; code basically inhibits the read-time eval. This doesn't always
|
|---|
| 223 | ;; work right as the compiler may be seeing a different form structure
|
|---|
| 224 | ;; and the compiler's version of PATH may not match the editor's.
|
|---|
| 225 | ;; The main trouble seen in testing is that the 'form-number'
|
|---|
| 226 | ;; supplied by the compiler was one more than what the vector
|
|---|
| 227 | ;; returned by form-number-translations contained. For lack of a
|
|---|
| 228 | ;; better solution, I (pw) just limit the form-number to legal range.
|
|---|
| 229 | ;; This has worked ok on test code but may be off for some
|
|---|
| 230 | ;; forms. At least the editor won't break.
|
|---|
| 231 |
|
|---|
| 232 | (let* ((vector (di:form-number-translations
|
|---|
| 233 | (with-input-from-region
|
|---|
| 234 | (s (region point (buffer-end-mark buffer)))
|
|---|
| 235 | (let ((*read-suppress* t))
|
|---|
| 236 | (read s)))
|
|---|
| 237 | tlf-offset))
|
|---|
| 238 | ;; Don't signal error on index overrun.It may be due
|
|---|
| 239 | ;; to read-time eval getting form editing blind to
|
|---|
| 240 | ;; editor
|
|---|
| 241 | (index (min form-number (1- (length vector))))
|
|---|
| 242 | (path (nreverse (butlast (cdr (svref vector index))))))
|
|---|
| 243 | ;;
|
|---|
| 244 | ;; Walk down to the form. Change to buffer in case we get an error
|
|---|
| 245 | ;; while finding the form.
|
|---|
| 246 | (change-to-buffer buffer)
|
|---|
| 247 | (mark-to-debug-source-path point path)))))
|
|---|
| 248 | (setf *debug-editor-source-data* t)
|
|---|
| 249 | ;;
|
|---|
| 250 | ;; While Hemlock was setting up the source edit, the user could have typed
|
|---|
| 251 | ;; while looking at a buffer no longer current when the commands execute.
|
|---|
| 252 | (clear-editor-input *editor-input*))
|
|---|
| 253 |
|
|---|
| 254 | ;;; CANNOT-EDIT-SOURCE-LOCATION -- Interface.
|
|---|
| 255 | ;;;
|
|---|
| 256 | ;;; The slave calls this when the debugger command "EDIT-SOURCE" runs, and the
|
|---|
| 257 | ;;; slave cannot give the editor source information.
|
|---|
| 258 | ;;;
|
|---|
| 259 | (defun cannot-edit-source-location ()
|
|---|
| 260 | (loud-message "Can't edit source.")
|
|---|
| 261 | (when *in-debug-edit-source*
|
|---|
| 262 | (throw 'blow-debug-edit-source nil)))
|
|---|
| 263 |
|
|---|
| 264 | |
|---|
| 265 |
|
|---|
| 266 | ;;;; Breakpoints.
|
|---|
| 267 |
|
|---|
| 268 | ;;;
|
|---|
| 269 | ;;; Breakpoint information for editor management.
|
|---|
| 270 | ;;;
|
|---|
| 271 |
|
|---|
| 272 | ;;; This holds all the stuff we might want to know about a breakpoint in some
|
|---|
| 273 | ;;; slave.
|
|---|
| 274 | ;;;
|
|---|
| 275 | (defstruct (breakpoint-info (:print-function print-breakpoint-info)
|
|---|
| 276 | (:constructor make-breakpoint-info
|
|---|
| 277 | (slave buffer remote-object name)))
|
|---|
| 278 | (slave nil :type server-info)
|
|---|
| 279 | (buffer nil :type buffer)
|
|---|
| 280 | (remote-object nil :type wire:remote-object)
|
|---|
| 281 | (name nil :type simple-string))
|
|---|
| 282 | ;;;
|
|---|
| 283 | (defun print-breakpoint-info (obj str n)
|
|---|
| 284 | (declare (ignore n))
|
|---|
| 285 | (format str "#<Breakpoint-Info for ~S>" (breakpoint-info-name obj)))
|
|---|
| 286 |
|
|---|
| 287 | (defvar *breakpoints* nil)
|
|---|
| 288 |
|
|---|
| 289 | (macrolet ((frob (name accessor)
|
|---|
| 290 | `(defun ,name (key)
|
|---|
| 291 | (let ((res nil))
|
|---|
| 292 | (dolist (bpt-info *breakpoints* res)
|
|---|
| 293 | (when (eq (,accessor bpt-info) key)
|
|---|
| 294 | (push bpt-info res)))))))
|
|---|
| 295 | (frob slave-breakpoints breakpoint-info-slave)
|
|---|
| 296 | (frob buffer-breakpoints breakpoint-info-buffer))
|
|---|
| 297 |
|
|---|
| 298 | (defun delete-breakpoints-buffer-hook (buffer)
|
|---|
| 299 | (let ((server-info (value current-eval-server)))
|
|---|
| 300 | (when server-info
|
|---|
| 301 | (let ((bpts (buffer-breakpoints buffer))
|
|---|
| 302 | (wire (server-info-wire server-info)))
|
|---|
| 303 | (dolist (b bpts)
|
|---|
| 304 | (setf *breakpoints* (delete b *breakpoints*))
|
|---|
| 305 | (when wire
|
|---|
| 306 | (wire:remote wire
|
|---|
| 307 | (di:delete-breakpoint (breakpoint-info-remote-object b))))
|
|---|
| 308 | (when wire
|
|---|
| 309 | (wire:wire-force-output wire)))))))
|
|---|
| 310 | ;;;
|
|---|
| 311 | (add-hook delete-buffer-hook 'delete-breakpoints-buffer-hook)
|
|---|
| 312 |
|
|---|
| 313 | ;;;
|
|---|
| 314 | ;;; Setting breakpoints.
|
|---|
| 315 | ;;;
|
|---|
| 316 |
|
|---|
| 317 | ;;; "Debug Breakpoint" uses this to prompt for :function-end and
|
|---|
| 318 | ;;; :function-start breakpoints.
|
|---|
| 319 | ;;;
|
|---|
| 320 | (defvar *function-breakpoint-strings*
|
|---|
| 321 | (make-string-table :initial-contents
|
|---|
| 322 | '(("Start" . :function-start) ("End" . :function-end))))
|
|---|
| 323 | ;;;
|
|---|
| 324 | ;;; Maybe this should use the wire level directly and hold onto remote-objects
|
|---|
| 325 | ;;; identifying the breakpoints. Then we could write commands to show where
|
|---|
| 326 | ;;; the breakpoints were and to individually deactivate or delete them. As it
|
|---|
| 327 | ;;; is now we probably have to delete all for a given function. What about
|
|---|
| 328 | ;;; setting user supplied breakpoint hook-functions, or Hemlock supplying a
|
|---|
| 329 | ;;; nice set such as something to simply print all locals at a certain
|
|---|
| 330 | ;;; location.
|
|---|
| 331 | ;;;
|
|---|
| 332 | (defcommand "Debug Breakpoint" (p)
|
|---|
| 333 | "This tries to set a breakpoint in the \"Current Eval Server\" at the
|
|---|
| 334 | location designated by the current point. If there is no known code
|
|---|
| 335 | location at the point, then this moves the point to the closest location
|
|---|
| 336 | before the point. With an argument, this sets a breakpoint at the start
|
|---|
| 337 | or end of the function, prompting the user for which one to use."
|
|---|
| 338 | "This tries to set a breakpoint in the \"Current Eval Server\" at the
|
|---|
| 339 | location designated by the current point. If there is no known code
|
|---|
| 340 | location at the point, then this moves the point to the closest location
|
|---|
| 341 | before the point. With an argument, this sets a breakpoint at the start
|
|---|
| 342 | or end of the function, prompting the user for which one to use."
|
|---|
| 343 | (let ((point (current-point)))
|
|---|
| 344 | (pre-command-parse-check point)
|
|---|
| 345 | (let ((name (find-defun-for-breakpoint point)))
|
|---|
| 346 | (if p
|
|---|
| 347 | (multiple-value-bind (str place)
|
|---|
| 348 | (prompt-for-keyword
|
|---|
| 349 | (list *function-breakpoint-strings*)
|
|---|
| 350 | :prompt "Set breakpoint at function: "
|
|---|
| 351 | :default :start :default-string "Start")
|
|---|
| 352 | (declare (ignore str))
|
|---|
| 353 | (set-breakpoint-in-slave (get-current-eval-server t) name place))
|
|---|
| 354 | (let* ((path (find-path-for-breakpoint point))
|
|---|
| 355 | (server-info (get-current-eval-server t))
|
|---|
| 356 | (res (set-breakpoint-in-slave server-info name path)))
|
|---|
| 357 | (cond ((not res)
|
|---|
| 358 | (message "No code locations correspond with point."))
|
|---|
| 359 | ((wire:remote-object-p res)
|
|---|
| 360 | (push (make-breakpoint-info server-info (current-buffer)
|
|---|
| 361 | res name)
|
|---|
| 362 | *breakpoints*)
|
|---|
| 363 | (message "Breakpoint set."))
|
|---|
| 364 | (t
|
|---|
| 365 | (resolve-ambiguous-breakpoint-location server-info
|
|---|
| 366 | name res))))))))
|
|---|
| 367 |
|
|---|
| 368 | ;;; FIND-PATH-FOR-BREAKPOINT -- Internal.
|
|---|
| 369 | ;;;
|
|---|
| 370 | ;;; This walks up from point to the beginning of its containing DEFUN to return
|
|---|
| 371 | ;;; the pseudo source-path (no form-number, no top-level form offset, and in
|
|---|
| 372 | ;;; descent order from start of the DEFUN).
|
|---|
| 373 | ;;;
|
|---|
| 374 | (defun find-path-for-breakpoint (point)
|
|---|
| 375 | (with-mark ((m point)
|
|---|
| 376 | (end point))
|
|---|
| 377 | (let ((path nil))
|
|---|
| 378 | (top-level-offset end -1)
|
|---|
| 379 | (with-mark ((containing-form m))
|
|---|
| 380 | (loop
|
|---|
| 381 | (when (mark= m end) (return))
|
|---|
| 382 | (backward-up-list containing-form)
|
|---|
| 383 | (do ((count 0 (1+ count)))
|
|---|
| 384 | ((mark= m containing-form)
|
|---|
| 385 | ;; Count includes moving from the first form inside the
|
|---|
| 386 | ;; containing-form paren to the outside of the containing-form
|
|---|
| 387 | ;; paren -- one too many.
|
|---|
| 388 | (push (1- count) path))
|
|---|
| 389 | (form-offset m -1))))
|
|---|
| 390 | path)))
|
|---|
| 391 |
|
|---|
| 392 | ;;; SET-BREAKPOINT-IN-SLAVE -- Internal.
|
|---|
| 393 | ;;;
|
|---|
| 394 | ;;; This tells the slave to set a breakpoint for name. Path is a modified
|
|---|
| 395 | ;;; source-path (with no form-number or top-level-form offset) or a symbol
|
|---|
| 396 | ;;; (:function-start or :function-end). If the server dies while evaluating
|
|---|
| 397 | ;;; form, then this signals an editor-error.
|
|---|
| 398 | ;;;
|
|---|
| 399 | (defun set-breakpoint-in-slave (server-info name path)
|
|---|
| 400 | (when (server-info-notes server-info)
|
|---|
| 401 | (editor-error "Server ~S is currently busy. See \"List Operations\"."
|
|---|
| 402 | (server-info-name server-info)))
|
|---|
| 403 | (multiple-value-bind (res error)
|
|---|
| 404 | (wire:remote-value (server-info-wire server-info)
|
|---|
| 405 | (di:set-breakpoint-for-editor (value current-package)
|
|---|
| 406 | name path))
|
|---|
| 407 | (when error (editor-error "The server died before finishing."))
|
|---|
| 408 | res))
|
|---|
| 409 |
|
|---|
| 410 | ;;; RESOLVE-AMBIGUOUS-BREAKPOINT-LOCATION -- Internal.
|
|---|
| 411 | ;;;
|
|---|
| 412 | ;;; This helps the user select an ambiguous code location for "Debug
|
|---|
| 413 | ;;; Breakpoint".
|
|---|
| 414 | ;;;
|
|---|
| 415 | (defun resolve-ambiguous-breakpoint-location (server-info name locs)
|
|---|
| 416 | (declare (list locs))
|
|---|
| 417 | (let ((point (current-point))
|
|---|
| 418 | (loc-num (length locs))
|
|---|
| 419 | (count 1)
|
|---|
| 420 | (cur-loc locs))
|
|---|
| 421 | (flet ((show-loc ()
|
|---|
| 422 | (top-level-offset point -1)
|
|---|
| 423 | (mark-to-debug-source-path point (cdar cur-loc))))
|
|---|
| 424 | (show-loc)
|
|---|
| 425 | (command-case (:prompt `("Ambiguous location ~D of ~D: " ,count ,loc-num)
|
|---|
| 426 | :help "Pick a location to set a breakpoint."
|
|---|
| 427 | :change-window nil)
|
|---|
| 428 | (#\space "Move point to next possible location."
|
|---|
| 429 | (setf cur-loc (cdr cur-loc))
|
|---|
| 430 | (cond (cur-loc
|
|---|
| 431 | (incf count))
|
|---|
| 432 | (t
|
|---|
| 433 | (setf cur-loc locs)
|
|---|
| 434 | (setf count 1)))
|
|---|
| 435 | (show-loc)
|
|---|
| 436 | (reprompt))
|
|---|
| 437 | (:confirm "Choose the current location."
|
|---|
| 438 | (let ((res (wire:remote-value (server-info-wire server-info)
|
|---|
| 439 | (di:set-location-breakpoint-for-editor (caar cur-loc)))))
|
|---|
| 440 | (unless (wire:remote-object-p res)
|
|---|
| 441 | (editor-error "Couldn't set breakpoint from location?"))
|
|---|
| 442 | (push (make-breakpoint-info server-info (current-buffer) res name)
|
|---|
| 443 | *breakpoints*))
|
|---|
| 444 | (message "Breakpoint set."))))))
|
|---|
| 445 |
|
|---|
| 446 | ;;; MARK-TO-DEBUG-SOURCE-PATH -- Internal.
|
|---|
| 447 | ;;;
|
|---|
| 448 | ;;; This takes a mark at the beginning of a top-level form and modified debugger
|
|---|
| 449 | ;;; source-path. Path has no form number or top-level-form offset element, and
|
|---|
| 450 | ;;; it has been reversed to actually be usable.
|
|---|
| 451 | ;;;
|
|---|
| 452 | (defun mark-to-debug-source-path (mark path)
|
|---|
| 453 | (let ((quote-or-function nil))
|
|---|
| 454 | (pre-command-parse-check mark)
|
|---|
| 455 | (dolist (n path)
|
|---|
| 456 | (when quote-or-function
|
|---|
| 457 | (editor-error
|
|---|
| 458 | "Apparently settled on the symbol QUOTE or FUNCTION via their ~
|
|---|
| 459 | read macros, which is odd, but furthermore there seems to be ~
|
|---|
| 460 | more source-path left."))
|
|---|
| 461 | (unless (form-offset mark 1)
|
|---|
| 462 | ;; Want to use the following and delete the next FORM-OFFSET -1.
|
|---|
| 463 | ;; (scan-direction-valid mark t (or :open-paren :prefix))
|
|---|
| 464 | (editor-error
|
|---|
| 465 | "Ran out of text in buffer with more source-path remaining."))
|
|---|
| 466 | (form-offset mark -1)
|
|---|
| 467 | (ecase (next-character mark)
|
|---|
| 468 | (#\(
|
|---|
| 469 | (mark-after mark)
|
|---|
| 470 | (form-offset mark n))
|
|---|
| 471 | (#\'
|
|---|
| 472 | (case n
|
|---|
| 473 | (0 (setf quote-or-function t))
|
|---|
| 474 | (1 (mark-after mark))
|
|---|
| 475 | (t (editor-error "Next form is QUOTE, but source-path index ~
|
|---|
| 476 | is other than zero or one."))))
|
|---|
| 477 | (#\#
|
|---|
| 478 | (case (next-character (mark-after mark))
|
|---|
| 479 | (#\'
|
|---|
| 480 | (case n
|
|---|
| 481 | (0 (setf quote-or-function t))
|
|---|
| 482 | (1 (mark-after mark))
|
|---|
| 483 | (t (editor-error "Next form is FUNCTION, but source-path ~
|
|---|
| 484 | index is other than zero or one."))))
|
|---|
| 485 | (t (editor-error
|
|---|
| 486 | "Can only parse ' and #' read macros."))))))
|
|---|
| 487 | ;; Get to the beginning of the form.
|
|---|
| 488 | (form-offset mark 1)
|
|---|
| 489 | (form-offset mark -1)))
|
|---|
| 490 |
|
|---|
| 491 | ;;;
|
|---|
| 492 | ;;; Deleting breakpoints.
|
|---|
| 493 | ;;;
|
|---|
| 494 |
|
|---|
| 495 | (defhvar "Delete Breakpoints Confirm"
|
|---|
| 496 | "This determines whether \"Debug Delete Breakpoints\" should ask for
|
|---|
| 497 | confirmation before deleting breakpoints."
|
|---|
| 498 | :value t)
|
|---|
| 499 |
|
|---|
| 500 | (defcommand "Debug Delete Breakpoints" (p)
|
|---|
| 501 | "This deletes all breakpoints for the named DEFUN containing the point.
|
|---|
| 502 | This affects the \"Current Eval Server\"."
|
|---|
| 503 | "This deletes all breakpoints for the named DEFUN containing the point.
|
|---|
| 504 | This affects the \"Current Eval Server\"."
|
|---|
| 505 | (declare (ignore p))
|
|---|
| 506 | (let* ((server-info (get-current-eval-server t))
|
|---|
| 507 | (wire (server-info-wire server-info))
|
|---|
| 508 | (name (find-defun-for-breakpoint (current-point)))
|
|---|
| 509 | (bpts (slave-breakpoints server-info)))
|
|---|
| 510 | (cond ((not bpts)
|
|---|
| 511 | (message "No breakpoints recorded for ~A." name))
|
|---|
| 512 | ((or (not (value delete-breakpoints-confirm))
|
|---|
| 513 | (prompt-for-y-or-n :prompt `("Delete breakpoints for ~A? " ,name)
|
|---|
| 514 | :default t
|
|---|
| 515 | :default-string "Y"))
|
|---|
| 516 | (dolist (b bpts)
|
|---|
| 517 | (when (string= name (breakpoint-info-name b))
|
|---|
| 518 | (setf *breakpoints* (delete b *breakpoints*))
|
|---|
| 519 | (wire:remote wire
|
|---|
| 520 | (di:delete-breakpoint-for-editor
|
|---|
| 521 | (breakpoint-info-remote-object b)))))
|
|---|
| 522 | (wire:wire-force-output wire)))))
|
|---|
| 523 |
|
|---|
| 524 | ;;;
|
|---|
| 525 | ;;; Breakpoint utilities.
|
|---|
| 526 | ;;;
|
|---|
| 527 |
|
|---|
| 528 | ;;; FIND-DEFUN-FOR-BREAKPOINT -- Internal.
|
|---|
| 529 | ;;;
|
|---|
| 530 | ;;; This returns as a string the name of the DEFUN containing point. It
|
|---|
| 531 | ;;; signals any errors necessary to ensure "we are in good form".
|
|---|
| 532 | ;;;
|
|---|
| 533 | (defun find-defun-for-breakpoint (point)
|
|---|
| 534 | (with-mark ((m1 point)
|
|---|
| 535 | (m2 point))
|
|---|
| 536 | (unless (top-level-offset m2 -1)
|
|---|
| 537 | (editor-error "Must be inside a DEFUN."))
|
|---|
| 538 | ;;
|
|---|
| 539 | ;; Check for DEFUN.
|
|---|
| 540 | (mark-after (move-mark m1 m2))
|
|---|
| 541 | (unless (find-attribute m1 :whitespace #'zerop)
|
|---|
| 542 | (editor-error "Must be inside a DEFUN."))
|
|---|
| 543 | (word-offset (move-mark m2 m1) 1)
|
|---|
| 544 | (unless (string-equal (region-to-string (region m1 m2)) "defun")
|
|---|
| 545 | (editor-error "Must be inside a DEFUN."))
|
|---|
| 546 | ;;
|
|---|
| 547 | ;; Find name.
|
|---|
| 548 | (unless (find-attribute m2 :whitespace #'zerop)
|
|---|
| 549 | (editor-error "Function unnamed?"))
|
|---|
| 550 | (form-offset (move-mark m1 m2) 1)
|
|---|
| 551 | (region-to-string (region m2 m1))))
|
|---|
| 552 |
|
|---|
| 553 |
|
|---|
| 554 | |
|---|
| 555 |
|
|---|
| 556 | ;;;; Miscellaneous commands.
|
|---|
| 557 |
|
|---|
| 558 | (define-debugger-command "Flush Errors"
|
|---|
| 559 | "In the \"Current Eval Server\", toggles whether the debugger ignores errors
|
|---|
| 560 | or recursively enters itself."
|
|---|
| 561 | :flush)
|
|---|