debug.scm 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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 rdelim)
  26. #:use-module (ice-9 pretty-print)
  27. #:use-module (ice-9 format)
  28. #:use-module ((system vm inspect) #:select ((inspect . %inspect)))
  29. #:use-module (system vm program)
  30. #:export (<debug>
  31. make-debug debug?
  32. debug-frames debug-index debug-error-message
  33. terminal-width
  34. print-registers print-locals print-frame print-frames
  35. stack->vector narrow-stack->vector
  36. frame->stack-vector))
  37. ;; TODO:
  38. ;;
  39. ;; eval expression in context of frame
  40. ;; set local variable in frame
  41. ;; step until greater source line
  42. ;; watch expression
  43. ;; set printing width
  44. ;; disassemble the current function
  45. ;; inspect any object
  46. ;;;
  47. ;;; Debugger
  48. ;;;
  49. ;;; The actual interaction loop of the debugger is run by the repl. This module
  50. ;;; simply exports a data structure to hold the debugger state, along with its
  51. ;;; accessors, and provides some helper functions.
  52. ;;;
  53. (define-record <debug> frames index error-message)
  54. ;; A fluid, because terminals are usually implicitly associated with
  55. ;; threads.
  56. ;;
  57. (define terminal-width
  58. (let ((set-width (make-fluid)))
  59. (case-lambda
  60. (()
  61. (or (fluid-ref set-width)
  62. (let ((w (false-if-exception (string->number (getenv "COLUMNS")))))
  63. (and (integer? w) (exact? w) (> w 0) w))
  64. 72))
  65. ((w)
  66. (if (or (not w) (and (integer? w) (exact? w) (> w 0)))
  67. (fluid-set! set-width w)
  68. (error "Expected a column number (a positive integer)" w))))))
  69. (define (reverse-hashq h)
  70. (let ((ret (make-hash-table)))
  71. (hash-for-each
  72. (lambda (k v)
  73. (hashq-set! ret v (cons k (hashq-ref ret v '()))))
  74. h)
  75. ret))
  76. (define* (print-registers frame #:optional (port (current-output-port))
  77. #:key (per-line-prefix " "))
  78. (define (print fmt val)
  79. (display per-line-prefix port)
  80. (run-hook before-print-hook val)
  81. (format port fmt val))
  82. (format port "~aRegisters:~%" per-line-prefix)
  83. (let ((ip (frame-instruction-pointer frame)))
  84. (print "ip = #x~x" ip)
  85. (let ((info (find-program-debug-info ip)))
  86. (when info
  87. (let ((addr (program-debug-info-addr info)))
  88. (format port " (#x~x + ~d * 4)" addr (/ (- ip addr) 4)))))
  89. (newline port))
  90. (print "sp = ~a\n" (frame-stack-pointer frame))
  91. (print "fp = ~a\n" (frame-address frame)))
  92. (define* (print-locals frame #:optional (port (current-output-port))
  93. #:key (width (terminal-width)) (per-line-prefix " "))
  94. (let ((bindings (frame-bindings frame)))
  95. (cond
  96. ((null? bindings)
  97. (format port "~aNo local variables.~%" per-line-prefix))
  98. (else
  99. (format port "~aLocal variables:~%" per-line-prefix)
  100. (for-each
  101. (lambda (binding)
  102. (let ((v (binding-ref binding)))
  103. (display per-line-prefix port)
  104. (run-hook before-print-hook v)
  105. (format port "~a = ~v:@y\n" (binding-name binding) width v)))
  106. (frame-bindings frame))))))
  107. (define* (print-frame frame #:optional (port (current-output-port))
  108. #:key index (width (terminal-width)) (full? #f)
  109. (last-source #f) next-source?)
  110. (define (source:pretty-file source)
  111. (if source
  112. (or (source:file source) "current input")
  113. "unknown file"))
  114. (let* ((source (frame-source frame))
  115. (file (source:pretty-file source))
  116. (line (and=> source source:line-for-user))
  117. (col (and=> source source:column)))
  118. (if (and file (not (equal? file (source:pretty-file last-source))))
  119. (format port "~&In ~a:~&" file))
  120. (format port "~9@a~:[~*~3_~;~3d~] ~v:@y~%"
  121. (if line (format #f "~a:~a" line col) "")
  122. index index width
  123. (frame-call-representation frame #:top-frame? (zero? index)))
  124. (if full?
  125. (print-locals frame #:width width
  126. #:per-line-prefix " "))))
  127. (define* (print-frames frames
  128. #:optional (port (current-output-port))
  129. #:key (width (terminal-width)) (full? #f)
  130. (forward? #f) count)
  131. (let* ((len (vector-length frames))
  132. (lower-idx (if (or (not count) (positive? count))
  133. 0
  134. (max 0 (+ len count))))
  135. (upper-idx (if (and count (negative? count))
  136. (1- len)
  137. (1- (if count (min count len) len))))
  138. (inc (if forward? 1 -1)))
  139. (let lp ((i (if forward? lower-idx upper-idx))
  140. (last-source #f))
  141. (if (<= lower-idx i upper-idx)
  142. (let* ((frame (vector-ref frames i)))
  143. (print-frame frame port #:index i #:width width #:full? full?
  144. #:last-source last-source)
  145. (lp (+ i inc)
  146. (frame-source frame)))))))
  147. (define (stack->vector stack)
  148. (let* ((len (stack-length stack))
  149. (v (make-vector len)))
  150. (if (positive? len)
  151. (let lp ((i 0) (frame (stack-ref stack 0)))
  152. (if (< i len)
  153. (begin
  154. (vector-set! v i frame)
  155. (lp (1+ i) (frame-previous frame))))))
  156. v))
  157. (define (narrow-stack->vector stack . args)
  158. (let ((narrowed (apply make-stack (stack-ref stack 0) args)))
  159. (if narrowed
  160. (stack->vector narrowed)
  161. #()))) ; ? Can be the case for a tail-call to `throw' tho
  162. (define (frame->stack-vector frame)
  163. (let ((tag (and (pair? (fluid-ref %stacks))
  164. (cdar (fluid-ref %stacks)))))
  165. (narrow-stack->vector
  166. (make-stack frame)
  167. ;; Take the stack from the given frame, cutting 0
  168. ;; frames.
  169. 0
  170. ;; Narrow the end of the stack to the most recent
  171. ;; start-stack.
  172. tag
  173. ;; And one more frame, because %start-stack
  174. ;; invoking the start-stack thunk has its own frame
  175. ;; too.
  176. 0 (and tag 1))))
  177. ;; (define (debug)
  178. ;; (run-debugger
  179. ;; (narrow-stack->vector
  180. ;; (make-stack #t)
  181. ;; ;; Narrow the `make-stack' frame and the `debug' frame
  182. ;; 2
  183. ;; ;; Narrow the end of the stack to the most recent start-stack.
  184. ;; (and (pair? (fluid-ref %stacks))
  185. ;; (cdar (fluid-ref %stacks))))))