gds-client.scm 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. (define-module (ice-9 gds-client)
  2. #:use-module (oop goops)
  3. #:use-module (oop goops describe)
  4. #:use-module (ice-9 debugging trace)
  5. #:use-module (ice-9 debugging traps)
  6. #:use-module (ice-9 debugging trc)
  7. #:use-module (ice-9 debugging steps)
  8. #:use-module (ice-9 pretty-print)
  9. #:use-module (ice-9 regex)
  10. #:use-module (ice-9 session)
  11. #:use-module (ice-9 string-fun)
  12. #:export (gds-debug-trap
  13. run-utility
  14. gds-accept-input))
  15. (cond ((string>=? (version) "1.7")
  16. (use-modules (ice-9 debugger utils)))
  17. (else
  18. (define the-ice-9-debugger-module (resolve-module '(ice-9 debugger)))
  19. (module-export! the-ice-9-debugger-module
  20. '(source-position
  21. write-frame-short/application
  22. write-frame-short/expression
  23. write-frame-args-long
  24. write-frame-long))))
  25. (use-modules (ice-9 debugger))
  26. (define gds-port #f)
  27. ;; Return an integer that somehow identifies the current thread.
  28. (define (get-thread-id)
  29. (let ((root (dynamic-root)))
  30. (cond ((integer? root)
  31. root)
  32. ((pair? root)
  33. (object-address root))
  34. (else
  35. (error "Unexpected dynamic root:" root)))))
  36. ;; gds-debug-read is a high-priority read. The (debug-thread-id ID)
  37. ;; form causes the frontend to dismiss any reads from threads whose id
  38. ;; is not ID, until it receives the (thread-id ...) form with the same
  39. ;; id as ID. Dismissing the reads of any other threads (by sending a
  40. ;; form that is otherwise ignored) causes those threads to release the
  41. ;; read mutex, which allows the (gds-read) here to proceed.
  42. (define (gds-debug-read)
  43. (write-form `(debug-thread-id ,(get-thread-id)))
  44. (gds-read))
  45. (define (gds-debug-trap trap-context)
  46. "Invoke the GDS debugger to explore the stack at the specified trap."
  47. (connect-to-gds)
  48. (start-stack 'debugger
  49. (let* ((stack (tc:stack trap-context))
  50. (flags1 (let ((trap-type (tc:type trap-context)))
  51. (case trap-type
  52. ((#:return #:error)
  53. (list trap-type
  54. (tc:return-value trap-context)))
  55. (else
  56. (list trap-type)))))
  57. (flags (if (tc:continuation trap-context)
  58. (cons #:continuable flags1)
  59. flags1))
  60. (fired-traps (tc:fired-traps trap-context))
  61. (special-index (and (= (length fired-traps) 1)
  62. (is-a? (car fired-traps) <exit-trap>)
  63. (eq? (tc:type trap-context) #:return)
  64. (- (tc:depth trap-context)
  65. (slot-ref (car fired-traps) 'depth)))))
  66. ;; Write current stack to the frontend.
  67. (write-form (list 'stack
  68. (if (and special-index (> special-index 0))
  69. special-index
  70. 0)
  71. (stack->emacs-readable stack)
  72. (append (flags->emacs-readable flags)
  73. (slot-ref trap-context
  74. 'handler-return-syms))))
  75. ;; Now wait for instruction.
  76. (let loop ((protocol (gds-debug-read)))
  77. ;; Act on it.
  78. (case (car protocol)
  79. ((tweak)
  80. ;; Request to tweak the handler return value.
  81. (let ((tweaking (catch #t
  82. (lambda ()
  83. (list (with-input-from-string
  84. (cadr protocol)
  85. read)))
  86. (lambda ignored #f))))
  87. (if tweaking
  88. (slot-set! trap-context
  89. 'handler-return-value
  90. (cons 'instead (car tweaking)))))
  91. (loop (gds-debug-read)))
  92. ((continue)
  93. ;; Continue (by exiting the debugger).
  94. *unspecified*)
  95. ((evaluate)
  96. ;; Evaluate expression in specified frame.
  97. (eval-in-frame stack (cadr protocol) (caddr protocol))
  98. (loop (gds-debug-read)))
  99. ((info-frame)
  100. ;; Return frame info.
  101. (let ((frame (stack-ref stack (cadr protocol))))
  102. (write-form (list 'info-result
  103. (with-output-to-string
  104. (lambda ()
  105. (write-frame-long frame))))))
  106. (loop (gds-debug-read)))
  107. ((info-args)
  108. ;; Return frame args.
  109. (let ((frame (stack-ref stack (cadr protocol))))
  110. (write-form (list 'info-result
  111. (with-output-to-string
  112. (lambda ()
  113. (write-frame-args-long frame))))))
  114. (loop (gds-debug-read)))
  115. ((proc-source)
  116. ;; Show source of application procedure.
  117. (let* ((frame (stack-ref stack (cadr protocol)))
  118. (proc (frame-procedure frame))
  119. (source (and proc (procedure-source proc))))
  120. (write-form (list 'info-result
  121. (if source
  122. (sans-surrounding-whitespace
  123. (with-output-to-string
  124. (lambda ()
  125. (pretty-print source))))
  126. (if proc
  127. "This procedure is coded in C"
  128. "This frame has no procedure")))))
  129. (loop (gds-debug-read)))
  130. ((traps-here)
  131. ;; Show the traps that fired here.
  132. (write-form (list 'info-result
  133. (with-output-to-string
  134. (lambda ()
  135. (for-each describe
  136. (tc:fired-traps trap-context))))))
  137. (loop (gds-debug-read)))
  138. ((step-into)
  139. ;; Set temporary breakpoint on next trap.
  140. (at-step gds-debug-trap
  141. 1
  142. #f
  143. (if (memq #:return flags)
  144. #f
  145. (- (stack-length stack)
  146. (cadr protocol)))))
  147. ((step-over)
  148. ;; Set temporary breakpoint on exit from
  149. ;; specified frame.
  150. (at-exit (- (stack-length stack) (cadr protocol))
  151. gds-debug-trap))
  152. ((step-file)
  153. ;; Set temporary breakpoint on next trap in same
  154. ;; source file.
  155. (at-step gds-debug-trap
  156. 1
  157. (frame-file-name (stack-ref stack
  158. (cadr protocol)))
  159. (if (memq #:return flags)
  160. #f
  161. (- (stack-length stack)
  162. (cadr protocol)))))
  163. (else
  164. (safely-handle-nondebug-protocol protocol)
  165. (loop (gds-debug-read))))))))
  166. (define (connect-to-gds . application-name)
  167. (or gds-port
  168. (begin
  169. (set! gds-port
  170. (or (let ((s (socket PF_INET SOCK_STREAM 0))
  171. (SOL_TCP 6)
  172. (TCP_NODELAY 1))
  173. (setsockopt s SOL_TCP TCP_NODELAY 1)
  174. (catch #t
  175. (lambda ()
  176. (connect s AF_INET (inet-aton "127.0.0.1") 8333)
  177. s)
  178. (lambda _ #f)))
  179. (let ((s (socket PF_UNIX SOCK_STREAM 0)))
  180. (catch #t
  181. (lambda ()
  182. (connect s AF_UNIX "/tmp/.gds_socket")
  183. s)
  184. (lambda _ #f)))
  185. (error "Couldn't connect to GDS by TCP or Unix domain socket")))
  186. (write-form (list 'name (getpid) (apply client-name application-name))))))
  187. (define (client-name . application-name)
  188. (let loop ((args (append application-name (program-arguments))))
  189. (if (null? args)
  190. (format #f "PID ~A" (getpid))
  191. (let ((arg (car args)))
  192. (cond ((string-match "^(.*[/\\])?guile(\\..*)?$" arg)
  193. (loop (cdr args)))
  194. ((string-match "^-" arg)
  195. (loop (cdr args)))
  196. (else
  197. (format #f "~A (PID ~A)" arg (getpid))))))))
  198. (if (not (defined? 'make-mutex))
  199. (begin
  200. (define (make-mutex) #f)
  201. (define lock-mutex noop)
  202. (define unlock-mutex noop)))
  203. (define write-mutex (make-mutex))
  204. (define (write-form form)
  205. ;; Write any form FORM to GDS.
  206. (lock-mutex write-mutex)
  207. (write form gds-port)
  208. (newline gds-port)
  209. (force-output gds-port)
  210. (unlock-mutex write-mutex))
  211. (define (stack->emacs-readable stack)
  212. ;; Return Emacs-readable representation of STACK.
  213. (map (lambda (index)
  214. (frame->emacs-readable (stack-ref stack index)))
  215. (iota (min (stack-length stack)
  216. (cadr (memq 'depth (debug-options)))))))
  217. (define (frame->emacs-readable frame)
  218. ;; Return Emacs-readable representation of FRAME.
  219. (if (frame-procedure? frame)
  220. (list 'application
  221. (with-output-to-string
  222. (lambda ()
  223. (display (if (frame-real? frame) " " "t "))
  224. (write-frame-short/application frame)))
  225. (source->emacs-readable frame))
  226. (list 'evaluation
  227. (with-output-to-string
  228. (lambda ()
  229. (display (if (frame-real? frame) " " "t "))
  230. (write-frame-short/expression frame)))
  231. (source->emacs-readable frame))))
  232. (define (source->emacs-readable frame)
  233. ;; Return Emacs-readable representation of the filename, line and
  234. ;; column source properties of SOURCE.
  235. (or (frame->source-position frame) 'nil))
  236. (define (flags->emacs-readable flags)
  237. ;; Return Emacs-readable representation of trap FLAGS.
  238. (let ((prev #f))
  239. (map (lambda (flag)
  240. (let ((erf (if (and (keyword? flag)
  241. (not (eq? prev #:return)))
  242. (keyword->symbol flag)
  243. (format #f "~S" flag))))
  244. (set! prev flag)
  245. erf))
  246. flags)))
  247. (define (eval-in-frame stack index expr)
  248. (write-form
  249. (list 'eval-result
  250. (format #f "~S"
  251. (catch #t
  252. (lambda ()
  253. (local-eval (with-input-from-string expr read)
  254. (memoized-environment
  255. (frame-source (stack-ref stack
  256. index)))))
  257. (lambda args
  258. (cons 'ERROR args)))))))
  259. (set! (behaviour-ordering gds-debug-trap) 100)
  260. ;;; Code below here adds support for interaction between the GDS
  261. ;;; client program and the Emacs frontend even when not stopped in the
  262. ;;; debugger.
  263. ;; A mutex to control attempts by multiple threads to read protocol
  264. ;; back from the frontend.
  265. (define gds-read-mutex (make-mutex))
  266. ;; Read a protocol instruction from the frontend.
  267. (define (gds-read)
  268. ;; Acquire the read mutex.
  269. (lock-mutex gds-read-mutex)
  270. ;; Tell the front end something that identifies us as a thread.
  271. (write-form `(thread-id ,(get-thread-id)))
  272. ;; Now read, then release the mutex and return what was read.
  273. (let ((x (catch #t
  274. (lambda () (read gds-port))
  275. (lambda ignored the-eof-object))))
  276. (unlock-mutex gds-read-mutex)
  277. x))
  278. (define (gds-accept-input exit-on-continue)
  279. ;; If reading from the GDS connection returns EOF, we will throw to
  280. ;; this catch.
  281. (catch 'server-eof
  282. (lambda ()
  283. (let loop ((protocol (gds-read)))
  284. (if (or (eof-object? protocol)
  285. (and exit-on-continue
  286. (eq? (car protocol) 'continue)))
  287. (throw 'server-eof))
  288. (safely-handle-nondebug-protocol protocol)
  289. (loop (gds-read))))
  290. (lambda ignored #f)))
  291. (define (safely-handle-nondebug-protocol protocol)
  292. ;; This catch covers any internal errors in the GDS code or
  293. ;; protocol.
  294. (catch #t
  295. (lambda ()
  296. (lazy-catch #t
  297. (lambda ()
  298. (handle-nondebug-protocol protocol))
  299. save-lazy-trap-context-and-rethrow))
  300. (lambda (key . args)
  301. (write-form
  302. `(eval-results (error . ,(format #f "~s" protocol))
  303. ,(if last-lazy-trap-context 't 'nil)
  304. "GDS Internal Error
  305. Please report this to <neil@ossau.uklinux.net>, ideally including:
  306. - a description of the scenario in which this error occurred
  307. - which versions of Guile and guile-debugging you are using
  308. - the error stack, which you can get by clicking on the link below,
  309. and then cut and paste into your report.
  310. Thanks!\n\n"
  311. ,(list (with-output-to-string
  312. (lambda ()
  313. (write key)
  314. (display ": ")
  315. (write args)
  316. (newline)))))))))
  317. ;; The key that is used to signal a read error changes from 1.6 to
  318. ;; 1.8; here we cover all eventualities by discovering the key
  319. ;; dynamically.
  320. (define read-error-key
  321. (catch #t
  322. (lambda ()
  323. (with-input-from-string "(+ 3 4" read))
  324. (lambda (key . args)
  325. key)))
  326. (define (handle-nondebug-protocol protocol)
  327. (case (car protocol)
  328. ((eval)
  329. (set! last-lazy-trap-context #f)
  330. (apply (lambda (correlator module port-name line column code flags)
  331. (with-input-from-string code
  332. (lambda ()
  333. (set-port-filename! (current-input-port) port-name)
  334. (set-port-line! (current-input-port) line)
  335. (set-port-column! (current-input-port) column)
  336. (let ((m (and module (resolve-module-from-root module))))
  337. (catch read-error-key
  338. (lambda ()
  339. (let loop ((exprs '()) (x (read)))
  340. (if (eof-object? x)
  341. ;; Expressions to be evaluated have all
  342. ;; been read. Now evaluate them.
  343. (let loop2 ((exprs (reverse! exprs))
  344. (results '())
  345. (n 1))
  346. (if (null? exprs)
  347. (write-form `(eval-results ,correlator
  348. ,(if last-lazy-trap-context 't 'nil)
  349. ,@results))
  350. (loop2 (cdr exprs)
  351. (append results (gds-eval (car exprs) m
  352. (if (and (null? (cdr exprs))
  353. (= n 1))
  354. #f n)))
  355. (+ n 1))))
  356. ;; Another complete expression read; add
  357. ;; it to the list.
  358. (begin
  359. (if (and (pair? x)
  360. (memq 'debug flags))
  361. (install-trap (make <source-trap>
  362. #:expression x
  363. #:behaviour gds-debug-trap)))
  364. (loop (cons x exprs) (read))))))
  365. (lambda (key . args)
  366. (write-form `(eval-results
  367. ,correlator
  368. ,(if last-lazy-trap-context 't 'nil)
  369. ,(with-output-to-string
  370. (lambda ()
  371. (display ";;; Reading expressions")
  372. (display " to evaluate\n")
  373. (apply display-error #f
  374. (current-output-port) args)))
  375. ("error-in-read")))))))))
  376. (cdr protocol)))
  377. ((complete)
  378. (let ((matches (apropos-internal
  379. (string-append "^" (regexp-quote (cadr protocol))))))
  380. (cond ((null? matches)
  381. (write-form '(completion-result nil)))
  382. (else
  383. ;;(write matches (current-error-port))
  384. ;;(newline (current-error-port))
  385. (let ((match
  386. (let loop ((match (symbol->string (car matches)))
  387. (matches (cdr matches)))
  388. ;;(write match (current-error-port))
  389. ;;(newline (current-error-port))
  390. ;;(write matches (current-error-port))
  391. ;;(newline (current-error-port))
  392. (if (null? matches)
  393. match
  394. (if (string-prefix=? match
  395. (symbol->string (car matches)))
  396. (loop match (cdr matches))
  397. (loop (substring match 0
  398. (- (string-length match) 1))
  399. matches))))))
  400. (if (string=? match (cadr protocol))
  401. (write-form `(completion-result
  402. ,(map symbol->string matches)))
  403. (write-form `(completion-result
  404. ,match))))))))
  405. ((debug-lazy-trap-context)
  406. (if last-lazy-trap-context
  407. (gds-debug-trap last-lazy-trap-context)
  408. (error "There is no stack available to show")))
  409. (else
  410. (error "Unexpected protocol:" protocol))))
  411. (define (resolve-module-from-root name)
  412. (save-module-excursion
  413. (lambda ()
  414. (set-current-module the-root-module)
  415. (resolve-module name))))
  416. (define (gds-eval x m part)
  417. ;; Consumer to accept possibly multiple values and present them for
  418. ;; Emacs as a list of strings.
  419. (define (value-consumer . values)
  420. (if (unspecified? (car values))
  421. '()
  422. (map (lambda (value)
  423. (with-output-to-string (lambda () (write value))))
  424. values)))
  425. ;; Now do evaluation.
  426. (let ((intro (if part
  427. (format #f ";;; Evaluating expression ~A" part)
  428. ";;; Evaluating"))
  429. (value #f))
  430. (let* ((do-eval (if m
  431. (lambda ()
  432. (display intro)
  433. (display " in module ")
  434. (write (module-name m))
  435. (newline)
  436. (set! value
  437. (call-with-values (lambda ()
  438. (start-stack 'gds-eval-stack
  439. (eval x m)))
  440. value-consumer)))
  441. (lambda ()
  442. (display intro)
  443. (display " in current module ")
  444. (write (module-name (current-module)))
  445. (newline)
  446. (set! value
  447. (call-with-values (lambda ()
  448. (start-stack 'gds-eval-stack
  449. (primitive-eval x)))
  450. value-consumer)))))
  451. (output
  452. (with-output-to-string
  453. (lambda ()
  454. (catch #t
  455. (lambda ()
  456. (lazy-catch #t
  457. do-eval
  458. save-lazy-trap-context-and-rethrow))
  459. (lambda (key . args)
  460. (case key
  461. ((misc-error signal unbound-variable numerical-overflow)
  462. (apply display-error #f
  463. (current-output-port) args)
  464. (set! value '("error-in-evaluation")))
  465. (else
  466. (display "EXCEPTION: ")
  467. (display key)
  468. (display " ")
  469. (write args)
  470. (newline)
  471. (set! value
  472. '("unhandled-exception-in-evaluation"))))))))))
  473. (list output value))))
  474. (define last-lazy-trap-context #f)
  475. (define (save-lazy-trap-context-and-rethrow key . args)
  476. (set! last-lazy-trap-context
  477. (throw->trap-context key args save-lazy-trap-context-and-rethrow))
  478. (apply throw key args))
  479. (define (run-utility)
  480. (connect-to-gds)
  481. (write (getpid))
  482. (newline)
  483. (force-output)
  484. (named-module-use! '(guile-user) '(ice-9 session))
  485. (gds-accept-input #f))
  486. (define-method (trap-description (trap <trap>))
  487. (let loop ((description (list (class-name (class-of trap))))
  488. (next 'installed?))
  489. (case next
  490. ((installed?)
  491. (loop (if (slot-ref trap 'installed)
  492. (cons 'installed description)
  493. description)
  494. 'conditional?))
  495. ((conditional?)
  496. (loop (if (slot-ref trap 'condition)
  497. (cons 'conditional description)
  498. description)
  499. 'skip-count))
  500. ((skip-count)
  501. (loop (let ((skip-count (slot-ref trap 'skip-count)))
  502. (if (zero? skip-count)
  503. description
  504. (cons* skip-count 'skip-count description)))
  505. 'single-shot?))
  506. ((single-shot?)
  507. (loop (if (slot-ref trap 'single-shot)
  508. (cons 'single-shot description)
  509. description)
  510. 'done))
  511. (else
  512. (reverse! description)))))
  513. (define-method (trap-description (trap <procedure-trap>))
  514. (let ((description (next-method)))
  515. (set-cdr! description
  516. (cons (procedure-name (slot-ref trap 'procedure))
  517. (cdr description)))
  518. description))
  519. (define-method (trap-description (trap <source-trap>))
  520. (let ((description (next-method)))
  521. (set-cdr! description
  522. (cons (format #f "~s" (slot-ref trap 'expression))
  523. (cdr description)))
  524. description))
  525. (define-method (trap-description (trap <location-trap>))
  526. (let ((description (next-method)))
  527. (set-cdr! description
  528. (cons* (slot-ref trap 'file-regexp)
  529. (slot-ref trap 'line)
  530. (slot-ref trap 'column)
  531. (cdr description)))
  532. description))
  533. (define (gds-trace-trap trap-context)
  534. (connect-to-gds)
  535. (gds-do-trace trap-context)
  536. (at-exit (tc:depth trap-context) gds-do-trace))
  537. (define (gds-do-trace trap-context)
  538. (write-form (list 'trace
  539. (format #f
  540. "~3@a: ~a"
  541. (trace/stack-real-depth trap-context)
  542. (trace/info trap-context)))))
  543. (define (gds-trace-subtree trap-context)
  544. (connect-to-gds)
  545. (gds-do-trace trap-context)
  546. (let ((step-trap (make <step-trap> #:behaviour gds-do-trace)))
  547. (install-trap step-trap)
  548. (at-exit (tc:depth trap-context)
  549. (lambda (trap-context)
  550. (uninstall-trap step-trap)))))
  551. ;;; (ice-9 gds-client) ends here.