traps.scm 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. ;;; Traps: stepping, breakpoints, and such.
  2. ;; Copyright (C) 2010 Free Software Foundation, Inc.
  3. ;;; This library is free software; you can redistribute it and/or
  4. ;;; modify it under the terms of the GNU Lesser General Public
  5. ;;; License as published by the Free Software Foundation; either
  6. ;;; version 3 of the License, or (at your option) any later version.
  7. ;;;
  8. ;;; This library is distributed in the hope that it will be useful,
  9. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. ;;; Lesser General Public License for more details.
  12. ;;;
  13. ;;; You should have received a copy of the GNU Lesser General Public
  14. ;;; License along with this library; if not, write to the Free Software
  15. ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  16. ;;; Commentary:
  17. ;;;
  18. ;;; Guile's debugging capabilities come from the hooks that its VM
  19. ;;; provides. For example, there is a hook that is fired when a function
  20. ;;; is called, and even a hook that gets fired at every retired
  21. ;;; instruction.
  22. ;;;
  23. ;;; But as the firing of these hooks is interleaved with the program
  24. ;;; execution, if we want to debug a program, we have to write an
  25. ;;; imperative program that mutates the state of these hooks, and to
  26. ;;; dispatch the hooks to a more semantic context.
  27. ;;;
  28. ;;; For example if we have placed a breakpoint at foo.scm:38, and
  29. ;;; determined that that location maps to the 18th instruction in
  30. ;;; procedure `bar', then we will need per-instruction hooks within
  31. ;;; `bar' -- but when running other procedures, we can have the
  32. ;;; per-instruction hooks off.
  33. ;;;
  34. ;;; Our approach is to define "traps". The behavior of a trap is
  35. ;;; specified when the trap is created. After creation, traps expose a
  36. ;;; limited, uniform interface: they are either on or off.
  37. ;;;
  38. ;;; To take our foo.scm:38 example again, we can define a trap that
  39. ;;; calls a function when control transfers to that source line --
  40. ;;; trap-at-source-location below. Calling the trap-at-source-location
  41. ;;; function adds to the VM hooks in such at way that it can do its job.
  42. ;;; The result of calling the function is a "disable-hook" closure that,
  43. ;;; when called, will turn off that trap.
  44. ;;;
  45. ;;; The result of calling the "disable-hook" closure, in turn, is an
  46. ;;; "enable-hook" closure, which when called turns the hook back on, and
  47. ;;; returns a "disable-hook" closure.
  48. ;;;
  49. ;;; It's a little confusing. The summary is, call these functions to add
  50. ;;; a trap; and call their return value to disable the trap.
  51. ;;;
  52. ;;; Code:
  53. (define-module (system vm traps)
  54. #:use-module (system base pmatch)
  55. #:use-module (system vm vm)
  56. #:use-module (system vm frame)
  57. #:use-module (system vm program)
  58. #:use-module (system vm objcode)
  59. #:use-module (system vm instruction)
  60. #:use-module (system xref)
  61. #:use-module (rnrs bytevectors)
  62. #:export (trap-at-procedure-call
  63. trap-in-procedure
  64. trap-instructions-in-procedure
  65. trap-at-procedure-ip-in-range
  66. trap-at-source-location
  67. trap-frame-finish
  68. trap-in-dynamic-extent
  69. trap-calls-in-dynamic-extent
  70. trap-instructions-in-dynamic-extent
  71. trap-calls-to-procedure
  72. trap-matching-instructions))
  73. (define-syntax arg-check
  74. (syntax-rules ()
  75. ((_ arg predicate? message)
  76. (if (not (predicate? arg))
  77. (error "bad argument ~a: ~a" 'arg message)))
  78. ((_ arg predicate?)
  79. (if (not (predicate? arg))
  80. (error "bad argument ~a: expected ~a" 'arg 'predicate?)))))
  81. (define (new-disabled-trap vm enable disable)
  82. (let ((enabled? #f))
  83. (define-syntax disabled?
  84. (identifier-syntax
  85. (disabled? (not enabled?))
  86. ((set! disabled? val) (set! enabled? (not val)))))
  87. (define* (enable-trap #:optional frame)
  88. (if enabled? (error "trap already enabled"))
  89. (enable frame)
  90. (set! enabled? #t)
  91. disable-trap)
  92. (define* (disable-trap #:optional frame)
  93. (if disabled? (error "trap already disabled"))
  94. (disable frame)
  95. (set! disabled? #t)
  96. enable-trap)
  97. enable-trap))
  98. (define (new-enabled-trap vm frame enable disable)
  99. ((new-disabled-trap vm enable disable) frame))
  100. (define (frame-matcher proc match-objcode?)
  101. (if match-objcode?
  102. (lambda (frame)
  103. (let ((frame-proc (frame-procedure frame)))
  104. (or (eq? frame-proc proc)
  105. (and (program? frame-proc)
  106. (eq? (program-objcode frame-proc)
  107. (program-objcode proc))))))
  108. (lambda (frame)
  109. (eq? (frame-procedure frame) proc))))
  110. ;; A basic trap, fires when a procedure is called.
  111. ;;
  112. (define* (trap-at-procedure-call proc handler #:key (vm (the-vm))
  113. (closure? #f)
  114. (our-frame? (frame-matcher proc closure?)))
  115. (arg-check proc procedure?)
  116. (arg-check handler procedure?)
  117. (let ()
  118. (define (apply-hook frame)
  119. (if (our-frame? frame)
  120. (handler frame)))
  121. (new-enabled-trap
  122. vm #f
  123. (lambda (frame)
  124. (add-hook! (vm-apply-hook vm) apply-hook))
  125. (lambda (frame)
  126. (remove-hook! (vm-apply-hook vm) apply-hook)))))
  127. ;; A more complicated trap, traps when control enters a procedure.
  128. ;;
  129. ;; Control can enter a procedure via:
  130. ;; * A procedure call.
  131. ;; * A return to a procedure's frame on the stack.
  132. ;; * A continuation returning directly to an application of this
  133. ;; procedure.
  134. ;;
  135. ;; Control can leave a procedure via:
  136. ;; * A normal return from the procedure.
  137. ;; * An application of another procedure.
  138. ;; * An invocation of a continuation.
  139. ;; * An abort.
  140. ;;
  141. (define* (trap-in-procedure proc enter-handler exit-handler
  142. #:key current-frame (vm (the-vm))
  143. (closure? #f)
  144. (our-frame? (frame-matcher proc closure?)))
  145. (arg-check proc procedure?)
  146. (arg-check enter-handler procedure?)
  147. (arg-check exit-handler procedure?)
  148. (let ((in-proc? #f))
  149. (define (enter-proc frame)
  150. (if in-proc?
  151. (warn "already in proc" frame)
  152. (begin
  153. (enter-handler frame)
  154. (set! in-proc? #t))))
  155. (define (exit-proc frame)
  156. (if in-proc?
  157. (begin
  158. (exit-handler frame)
  159. (set! in-proc? #f))
  160. (warn "not in proc" frame)))
  161. (define (apply-hook frame)
  162. (if in-proc?
  163. (exit-proc frame))
  164. (if (our-frame? frame)
  165. (enter-proc frame)))
  166. (define (push-cont-hook frame)
  167. (if in-proc?
  168. (exit-proc frame)))
  169. (define (pop-cont-hook frame)
  170. (if in-proc?
  171. (exit-proc frame))
  172. (if (our-frame? (frame-previous frame))
  173. (enter-proc (frame-previous frame))))
  174. (define (abort-hook frame)
  175. (if in-proc?
  176. (exit-proc frame))
  177. (if (our-frame? frame)
  178. (enter-proc frame)))
  179. (define (restore-hook frame)
  180. (if in-proc?
  181. (exit-proc frame))
  182. (if (our-frame? frame)
  183. (enter-proc frame)))
  184. (new-enabled-trap
  185. vm current-frame
  186. (lambda (frame)
  187. (add-hook! (vm-apply-hook vm) apply-hook)
  188. (add-hook! (vm-push-continuation-hook vm) push-cont-hook)
  189. (add-hook! (vm-pop-continuation-hook vm) pop-cont-hook)
  190. (add-hook! (vm-abort-continuation-hook vm) abort-hook)
  191. (add-hook! (vm-restore-continuation-hook vm) restore-hook)
  192. (if (and frame (our-frame? frame))
  193. (enter-proc frame)))
  194. (lambda (frame)
  195. (if in-proc?
  196. (exit-proc frame))
  197. (remove-hook! (vm-apply-hook vm) apply-hook)
  198. (remove-hook! (vm-push-continuation-hook vm) push-cont-hook)
  199. (remove-hook! (vm-pop-continuation-hook vm) pop-cont-hook)
  200. (remove-hook! (vm-abort-continuation-hook vm) abort-hook)
  201. (remove-hook! (vm-restore-continuation-hook vm) restore-hook)))))
  202. ;; Building on trap-in-procedure, we have trap-instructions-in-procedure
  203. ;;
  204. (define* (trap-instructions-in-procedure proc next-handler exit-handler
  205. #:key current-frame (vm (the-vm))
  206. (closure? #f)
  207. (our-frame?
  208. (frame-matcher proc closure?)))
  209. (arg-check proc procedure?)
  210. (arg-check next-handler procedure?)
  211. (arg-check exit-handler procedure?)
  212. (let ()
  213. (define (next-hook frame)
  214. (if (our-frame? frame)
  215. (next-handler frame)))
  216. (define (enter frame)
  217. (add-hook! (vm-next-hook vm) next-hook)
  218. (if frame (next-hook frame)))
  219. (define (exit frame)
  220. (exit-handler frame)
  221. (remove-hook! (vm-next-hook vm) next-hook))
  222. (trap-in-procedure proc enter exit
  223. #:current-frame current-frame #:vm vm
  224. #:our-frame? our-frame?)))
  225. (define (non-negative-integer? x)
  226. (and (number? x) (integer? x) (exact? x) (not (negative? x))))
  227. (define (positive-integer? x)
  228. (and (number? x) (integer? x) (exact? x) (positive? x)))
  229. (define (range? x)
  230. (and (list? x)
  231. (and-map (lambda (x)
  232. (and (pair? x)
  233. (non-negative-integer? (car x))
  234. (non-negative-integer? (cdr x))))
  235. x)))
  236. (define (in-range? range i)
  237. (or-map (lambda (bounds)
  238. (and (<= (car bounds) i)
  239. (< i (cdr bounds))))
  240. range))
  241. ;; Building on trap-instructions-in-procedure, we have
  242. ;; trap-at-procedure-ip-in-range.
  243. ;;
  244. (define* (trap-at-procedure-ip-in-range proc range handler
  245. #:key current-frame (vm (the-vm))
  246. (closure? #f)
  247. (our-frame?
  248. (frame-matcher proc closure?)))
  249. (arg-check proc procedure?)
  250. (arg-check range range?)
  251. (arg-check handler procedure?)
  252. (let ((fp-stack '()))
  253. (define (cull-frames! fp)
  254. (let lp ((frames fp-stack))
  255. (if (and (pair? frames) (< (car frames) fp))
  256. (lp (cdr frames))
  257. (set! fp-stack frames))))
  258. (define (next-handler frame)
  259. (let ((fp (frame-address frame))
  260. (ip (frame-instruction-pointer frame)))
  261. (cull-frames! fp)
  262. (let ((now-in-range? (in-range? range ip))
  263. (was-in-range? (and (pair? fp-stack) (= (car fp-stack) fp))))
  264. (cond
  265. (was-in-range?
  266. (if (not now-in-range?)
  267. (set! fp-stack (cdr fp-stack))))
  268. (now-in-range?
  269. (set! fp-stack (cons fp fp-stack))
  270. (handler frame))))))
  271. (define (exit-handler frame)
  272. (if (and (pair? fp-stack)
  273. (= (car fp-stack) (frame-address frame)))
  274. (set! fp-stack (cdr fp-stack))))
  275. (trap-instructions-in-procedure proc next-handler exit-handler
  276. #:current-frame current-frame #:vm vm
  277. #:our-frame? our-frame?)))
  278. ;; FIXME: define this in objcode somehow. We are reffing the first
  279. ;; uint32 in the objcode, which is the length of the program (without
  280. ;; the meta).
  281. (define (program-last-ip prog)
  282. (bytevector-u32-native-ref (objcode->bytecode (program-objcode prog)) 0))
  283. (define (program-sources-by-line proc file)
  284. (let lp ((sources (program-sources-pre-retire proc))
  285. (out '()))
  286. (if (pair? sources)
  287. (lp (cdr sources)
  288. (pmatch (car sources)
  289. ((,start-ip ,start-file ,start-line . ,start-col)
  290. (if (equal? start-file file)
  291. (cons (cons start-line
  292. (if (pair? (cdr sources))
  293. (pmatch (cadr sources)
  294. ((,end-ip . _)
  295. (cons start-ip end-ip))
  296. (else (error "unexpected")))
  297. (cons start-ip (program-last-ip proc))))
  298. out)
  299. out))
  300. (else (error "unexpected"))))
  301. (let ((alist '()))
  302. (for-each
  303. (lambda (pair)
  304. (set! alist
  305. (assv-set! alist (car pair)
  306. (cons (cdr pair)
  307. (or (assv-ref alist (car pair))
  308. '())))))
  309. out)
  310. (sort! alist (lambda (x y) (< (car x) (car y))))
  311. alist))))
  312. (define (source->ip-range proc file line)
  313. (or (or-map (lambda (line-and-ranges)
  314. (cond
  315. ((= (car line-and-ranges) line)
  316. (cdr line-and-ranges))
  317. ((> (car line-and-ranges) line)
  318. (warn "no instructions found at" file ":" line
  319. "; using line" (car line-and-ranges) "instead")
  320. (cdr line-and-ranges))
  321. (else #f)))
  322. (program-sources-by-line proc file))
  323. (begin
  324. (warn "no instructions found for" file ":" line)
  325. '())))
  326. (define (source-closures-or-procedures file line)
  327. (let ((closures (source-closures file line)))
  328. (if (pair? closures)
  329. (values closures #t)
  330. (values (source-procedures file line) #f))))
  331. ;; Building on trap-on-instructions-in-procedure, we have
  332. ;; trap-at-source-location. The parameter `user-line' is one-indexed, as
  333. ;; a user counts lines, instead of zero-indexed, as Guile counts lines.
  334. ;;
  335. (define* (trap-at-source-location file user-line handler
  336. #:key current-frame (vm (the-vm)))
  337. (arg-check file string?)
  338. (arg-check user-line positive-integer?)
  339. (arg-check handler procedure?)
  340. (let ((traps #f))
  341. (call-with-values
  342. (lambda () (source-closures-or-procedures file (1- user-line)))
  343. (lambda (procs closures?)
  344. (new-enabled-trap
  345. vm current-frame
  346. (lambda (frame)
  347. (set! traps
  348. (map
  349. (lambda (proc)
  350. (let ((range (source->ip-range proc file (1- user-line))))
  351. (trap-at-procedure-ip-in-range proc range handler
  352. #:current-frame current-frame
  353. #:vm vm
  354. #:closure? closures?)))
  355. procs))
  356. (if (null? traps)
  357. (error "No procedures found at ~a:~a." file user-line)))
  358. (lambda (frame)
  359. (for-each (lambda (trap) (trap frame)) traps)
  360. (set! traps #f)))))))
  361. ;; On a different tack, now we're going to build up a set of traps that
  362. ;; do useful things during the dynamic extent of a procedure's
  363. ;; application. First, a trap for when a frame returns.
  364. ;;
  365. (define* (trap-frame-finish frame return-handler abort-handler
  366. #:key (vm (the-vm)))
  367. (arg-check frame frame?)
  368. (arg-check return-handler procedure?)
  369. (arg-check abort-handler procedure?)
  370. (let ((fp (frame-address frame)))
  371. (define (pop-cont-hook frame)
  372. (if (and fp (eq? (frame-address frame) fp))
  373. (begin
  374. (set! fp #f)
  375. (return-handler frame))))
  376. (define (abort-hook frame)
  377. (if (and fp (< (frame-address frame) fp))
  378. (begin
  379. (set! fp #f)
  380. (abort-handler frame))))
  381. (new-enabled-trap
  382. vm frame
  383. (lambda (frame)
  384. (if (not fp)
  385. (error "return-or-abort traps may only be enabled once"))
  386. (add-hook! (vm-pop-continuation-hook vm) pop-cont-hook)
  387. (add-hook! (vm-abort-continuation-hook vm) abort-hook)
  388. (add-hook! (vm-restore-continuation-hook vm) abort-hook))
  389. (lambda (frame)
  390. (set! fp #f)
  391. (remove-hook! (vm-pop-continuation-hook vm) pop-cont-hook)
  392. (remove-hook! (vm-abort-continuation-hook vm) abort-hook)
  393. (remove-hook! (vm-restore-continuation-hook vm) abort-hook)))))
  394. ;; A more traditional dynamic-wind trap. Perhaps this should not be
  395. ;; based on the above trap-frame-finish?
  396. ;;
  397. (define* (trap-in-dynamic-extent proc enter-handler return-handler abort-handler
  398. #:key current-frame (vm (the-vm))
  399. (closure? #f)
  400. (our-frame? (frame-matcher proc closure?)))
  401. (arg-check proc procedure?)
  402. (arg-check enter-handler procedure?)
  403. (arg-check return-handler procedure?)
  404. (arg-check abort-handler procedure?)
  405. (let ((exit-trap #f))
  406. (define (return-hook frame)
  407. (exit-trap frame) ; disable the return/abort trap.
  408. (set! exit-trap #f)
  409. (return-handler frame))
  410. (define (abort-hook frame)
  411. (exit-trap frame) ; disable the return/abort trap.
  412. (set! exit-trap #f)
  413. (abort-handler frame))
  414. (define (apply-hook frame)
  415. (if (and (not exit-trap) (our-frame? frame))
  416. (begin
  417. (enter-handler frame)
  418. (set! exit-trap
  419. (trap-frame-finish frame return-hook abort-hook
  420. #:vm vm)))))
  421. (new-enabled-trap
  422. vm current-frame
  423. (lambda (frame)
  424. (add-hook! (vm-apply-hook vm) apply-hook))
  425. (lambda (frame)
  426. (if exit-trap
  427. (abort-hook frame))
  428. (set! exit-trap #f)
  429. (remove-hook! (vm-apply-hook vm) apply-hook)))))
  430. ;; Trapping all procedure calls within a dynamic extent, recording the
  431. ;; depth of the call stack relative to the original procedure.
  432. ;;
  433. (define* (trap-calls-in-dynamic-extent proc apply-handler return-handler
  434. #:key current-frame (vm (the-vm))
  435. (closure? #f)
  436. (our-frame?
  437. (frame-matcher proc closure?)))
  438. (arg-check proc procedure?)
  439. (arg-check apply-handler procedure?)
  440. (arg-check return-handler procedure?)
  441. (let ((*call-depth* 0))
  442. (define (trace-push frame)
  443. (set! *call-depth* (1+ *call-depth*)))
  444. (define (trace-pop frame)
  445. (return-handler frame *call-depth*)
  446. (set! *call-depth* (1- *call-depth*)))
  447. (define (trace-apply frame)
  448. (apply-handler frame *call-depth*))
  449. ;; FIXME: recalc depth on abort
  450. (define (enter frame)
  451. (add-hook! (vm-push-continuation-hook vm) trace-push)
  452. (add-hook! (vm-pop-continuation-hook vm) trace-pop)
  453. (add-hook! (vm-apply-hook vm) trace-apply))
  454. (define (leave frame)
  455. (remove-hook! (vm-push-continuation-hook vm) trace-push)
  456. (remove-hook! (vm-pop-continuation-hook vm) trace-pop)
  457. (remove-hook! (vm-apply-hook vm) trace-apply))
  458. (define (return frame)
  459. (leave frame))
  460. (define (abort frame)
  461. (leave frame))
  462. (trap-in-dynamic-extent proc enter return abort
  463. #:current-frame current-frame #:vm vm
  464. #:our-frame? our-frame?)))
  465. ;; Trapping all retired intructions within a dynamic extent.
  466. ;;
  467. (define* (trap-instructions-in-dynamic-extent proc next-handler
  468. #:key current-frame (vm (the-vm))
  469. (closure? #f)
  470. (our-frame?
  471. (frame-matcher proc closure?)))
  472. (arg-check proc procedure?)
  473. (arg-check next-handler procedure?)
  474. (let ()
  475. (define (trace-next frame)
  476. (next-handler frame))
  477. (define (enter frame)
  478. (add-hook! (vm-next-hook vm) trace-next))
  479. (define (leave frame)
  480. (remove-hook! (vm-next-hook vm) trace-next))
  481. (define (return frame)
  482. (leave frame))
  483. (define (abort frame)
  484. (leave frame))
  485. (trap-in-dynamic-extent proc enter return abort
  486. #:current-frame current-frame #:vm vm
  487. #:our-frame? our-frame?)))
  488. ;; Traps calls and returns for a given procedure, keeping track of the call depth.
  489. ;;
  490. (define* (trap-calls-to-procedure proc apply-handler return-handler
  491. #:key (vm (the-vm)))
  492. (arg-check proc procedure?)
  493. (arg-check apply-handler procedure?)
  494. (arg-check return-handler procedure?)
  495. (let ((pending-finish-traps '())
  496. (last-fp #f))
  497. (define (apply-hook frame)
  498. (let ((depth (length pending-finish-traps)))
  499. (apply-handler frame depth)
  500. (if (not (eq? (frame-address frame) last-fp))
  501. (let ((finish-trap #f))
  502. (define (frame-finished frame)
  503. (finish-trap frame) ;; disables the trap.
  504. (set! pending-finish-traps
  505. (delq finish-trap pending-finish-traps))
  506. (set! finish-trap #f))
  507. (define (return-hook frame)
  508. (frame-finished frame)
  509. (return-handler frame depth))
  510. ;; FIXME: abort handler?
  511. (define (abort-hook frame)
  512. (frame-finished frame))
  513. (set! finish-trap
  514. (trap-frame-finish frame return-hook abort-hook #:vm vm))
  515. (set! pending-finish-traps
  516. (cons finish-trap pending-finish-traps))))))
  517. ;; The basic idea is that we install one trap that fires for calls,
  518. ;; but that each call installs its own finish trap. Those finish
  519. ;; traps remove themselves as their frames finish or abort.
  520. ;;
  521. ;; However since to the outside world we present the interface of
  522. ;; just being one trap, disabling this calls-to-procedure trap
  523. ;; should take care of disabling all of the pending finish traps. We
  524. ;; keep track of pending traps through the pending-finish-traps
  525. ;; list.
  526. ;;
  527. ;; So since we know that the trap-at-procedure will be enabled, and
  528. ;; thus returning a disable closure, we make sure to wrap that
  529. ;; closure in something that will disable pending finish traps.
  530. (define (with-pending-finish-disablers trap)
  531. (define (with-pending-finish-enablers trap)
  532. (lambda* (#:optional frame)
  533. (with-pending-finish-disablers (trap frame))))
  534. (lambda* (#:optional frame)
  535. (for-each (lambda (disable) (disable frame))
  536. pending-finish-traps)
  537. (set! pending-finish-traps '())
  538. (with-pending-finish-enablers (trap frame))))
  539. (with-pending-finish-disablers
  540. (trap-at-procedure-call proc apply-hook #:vm vm))))
  541. ;; Trap when the source location changes.
  542. ;;
  543. (define* (trap-matching-instructions frame-pred handler
  544. #:key (vm (the-vm)))
  545. (arg-check frame-pred procedure?)
  546. (arg-check handler procedure?)
  547. (let ()
  548. (define (next-hook frame)
  549. (if (frame-pred frame)
  550. (handler frame)))
  551. (new-enabled-trap
  552. vm #f
  553. (lambda (frame)
  554. (add-hook! (vm-next-hook vm) next-hook))
  555. (lambda (frame)
  556. (remove-hook! (vm-next-hook vm) next-hook)))))