debug.scm 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. ;;; Guile VM debugging facilities
  2. ;;; Copyright (C) 2001, 2009, 2010, 2011, 2013, 2014, 2015 Free Software Foundation, Inc.
  3. ;;;
  4. ;;; This library is free software; you can redistribute it and/or
  5. ;;; modify it under the terms of the GNU Lesser General Public
  6. ;;; License as published by the Free Software Foundation; either
  7. ;;; version 3 of the License, or (at your option) any later version.
  8. ;;;
  9. ;;; This library is distributed in the hope that it will be useful,
  10. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;;; Lesser General Public License for more details.
  13. ;;;
  14. ;;; You should have received a copy of the GNU Lesser General Public
  15. ;;; License along with this library; if not, write to the Free Software
  16. ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. ;;; Code:
  18. (define-module (system repl debug)
  19. #:use-module (system base pmatch)
  20. #:use-module (system base syntax)
  21. #:use-module (system base language)
  22. #:use-module (system vm vm)
  23. #:use-module (system vm frame)
  24. #:use-module (system vm debug)
  25. #:use-module (ice-9 format)
  26. #:use-module (ice-9 match)
  27. #:use-module (ice-9 rdelim)
  28. #:use-module (ice-9 pretty-print)
  29. #:use-module ((system vm inspect) #:select ((inspect . %inspect)))
  30. #:use-module (system vm program)
  31. #:export (<debug>
  32. make-debug debug?
  33. debug-frames debug-index debug-error-message
  34. terminal-width
  35. print-registers print-locals print-frame print-frames
  36. stack->vector narrow-stack->vector
  37. frame->stack-vector))
  38. ;; TODO:
  39. ;;
  40. ;; eval expression in context of frame
  41. ;; set local variable in frame
  42. ;; step until greater source line
  43. ;; watch expression
  44. ;; set printing width
  45. ;; disassemble the current function
  46. ;; inspect any object
  47. ;;;
  48. ;;; Debugger
  49. ;;;
  50. ;;; The actual interaction loop of the debugger is run by the repl. This module
  51. ;;; simply exports a data structure to hold the debugger state, along with its
  52. ;;; accessors, and provides some helper functions.
  53. ;;;
  54. (define-record <debug> frames index error-message)
  55. ;; A fluid, because terminals are usually implicitly associated with
  56. ;; threads.
  57. ;;
  58. (define terminal-width
  59. (let ((set-width (make-fluid)))
  60. (case-lambda
  61. (()
  62. (or (fluid-ref set-width)
  63. (let ((w (false-if-exception (string->number (getenv "COLUMNS")))))
  64. (and (integer? w) (exact? w) (> w 0) w))
  65. 72))
  66. ((w)
  67. (if (or (not w) (and (integer? w) (exact? w) (> w 0)))
  68. (fluid-set! set-width w)
  69. (error "Expected a column number (a positive integer)" w))))))
  70. (define (reverse-hashq h)
  71. (let ((ret (make-hash-table)))
  72. (hash-for-each
  73. (lambda (k v)
  74. (hashq-set! ret v (cons k (hashq-ref ret v '()))))
  75. h)
  76. ret))
  77. (define* (print-registers frame #:optional (port (current-output-port))
  78. #:key (per-line-prefix " "))
  79. (define (print fmt val)
  80. (display per-line-prefix port)
  81. (run-hook before-print-hook val)
  82. (format port fmt val))
  83. (format port "~aRegisters:~%" per-line-prefix)
  84. (let ((ip (frame-instruction-pointer frame)))
  85. (print "ip = #x~x" ip)
  86. (let ((info (find-program-debug-info ip)))
  87. (when info
  88. (let ((addr (program-debug-info-addr info)))
  89. (format port " (#x~x + ~d * 4)" addr (/ (- ip addr) 4)))))
  90. (newline port))
  91. (print "sp = ~a\n" (frame-stack-pointer frame))
  92. (print "fp = ~a\n" (frame-address frame)))
  93. (define* (print-locals frame #:optional (port (current-output-port))
  94. #:key (width (terminal-width)) (per-line-prefix " "))
  95. (let ((bindings (frame-bindings frame)))
  96. (cond
  97. ((null? bindings)
  98. (format port "~aNo local variables.~%" per-line-prefix))
  99. (else
  100. (format port "~aLocal variables:~%" per-line-prefix)
  101. (for-each
  102. (lambda (binding)
  103. (let ((v (binding-ref binding)))
  104. (display per-line-prefix port)
  105. (run-hook before-print-hook v)
  106. (format port "~a = ~v:@y\n" (binding-name binding) width v)))
  107. (frame-bindings frame))))))
  108. (define* (print-frame frame #:optional (port (current-output-port))
  109. #:key index (width (terminal-width)) (full? #f)
  110. (last-source #f) next-source?)
  111. (define (source:pretty-file source)
  112. (if source
  113. (or (source:file source) "current input")
  114. "unknown file"))
  115. (let* ((source (frame-source frame))
  116. (file (source:pretty-file source))
  117. (line (and=> source source:line-for-user))
  118. (col (and=> source source:column)))
  119. (if (and file (not (equal? file (source:pretty-file last-source))))
  120. (format port "~&In ~a:~&" file))
  121. (format port "~9@a~:[~*~3_~;~3d~] ~v:@y~%"
  122. (if line (format #f "~a:~a" line col) "")
  123. index index width
  124. (frame-call-representation frame #:top-frame? (zero? index)))
  125. (if full?
  126. (print-locals frame #:width width
  127. #:per-line-prefix " "))))
  128. (define* (print-frames frames
  129. #:optional (port (current-output-port))
  130. #:key (width (terminal-width)) (full? #f)
  131. (forward? #f) count)
  132. (let* ((len (vector-length frames))
  133. (lower-idx (if (or (not count) (positive? count))
  134. 0
  135. (max 0 (+ len count))))
  136. (upper-idx (if (and count (negative? count))
  137. (1- len)
  138. (1- (if count (min count len) len))))
  139. (inc (if forward? 1 -1)))
  140. (let lp ((i (if forward? lower-idx upper-idx))
  141. (last-source #f))
  142. (if (<= lower-idx i upper-idx)
  143. (let* ((frame (vector-ref frames i)))
  144. (print-frame frame port #:index i #:width width #:full? full?
  145. #:last-source last-source)
  146. (lp (+ i inc)
  147. (frame-source frame)))))))
  148. (define (stack->vector stack)
  149. (let* ((len (stack-length stack))
  150. (v (make-vector len)))
  151. (if (positive? len)
  152. (let lp ((i 0) (frame (stack-ref stack 0)))
  153. (if (< i len)
  154. (begin
  155. (vector-set! v i frame)
  156. (lp (1+ i) (frame-previous frame))))))
  157. v))
  158. (define (narrow-stack->vector stack . args)
  159. (let ((narrowed (apply make-stack (stack-ref stack 0) args)))
  160. (if narrowed
  161. (stack->vector narrowed)
  162. #()))) ; ? Can be the case for a tail-call to `throw' tho
  163. (define (frame->stack-vector frame)
  164. (let ((stack (make-stack frame)))
  165. (match (fluid-ref %stacks)
  166. ((stack-tag . prompt-tag)
  167. (narrow-stack->vector
  168. stack
  169. ;; Take the stack from the given frame, cutting 0 frames.
  170. 0
  171. ;; Narrow the end of the stack to the most recent start-stack.
  172. prompt-tag
  173. ;; And one more frame, because %start-stack invoking the
  174. ;; start-stack thunk has its own frame too.
  175. 0 (and prompt-tag 1)))
  176. (_
  177. ;; Otherwise take the whole stack.
  178. (stack->vector stack)))))
  179. ;; (define (debug)
  180. ;; (run-debugger
  181. ;; (narrow-stack->vector
  182. ;; (make-stack #t)
  183. ;; ;; Narrow the `make-stack' frame and the `debug' frame
  184. ;; 2
  185. ;; ;; Narrow the end of the stack to the most recent start-stack.
  186. ;; (and (pair? (fluid-ref %stacks))
  187. ;; (cdr (fluid-ref %stacks))))))