-
Notifications
You must be signed in to change notification settings - Fork 0
/
breakpoints.lisp
153 lines (129 loc) · 5.31 KB
/
breakpoints.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
(defpackage :breakpoints
(:use :cl)
(:export
#:break-on-entry
#:step-on-entry
#:toggle-breakpoint
#:remove-breakpoint
#:remove-all-breakpoints
#:reinstall-breakpoint
#:reinstall-all-breakpoints
#:disable-breakpoint
#:disable-all-breakpoints
#:breakpoint-installed-p
#:find-breakpoint
#:*breakpoints*))
(in-package :breakpoints)
(defvar *breakpoints* (make-hash-table))
(defvar *save-definitions* t
"When enabled, the original functions definitions is saved in the breakpoint, to give the SLIME interface the possibility to follow the correct source locations of the function with a breakpoint installed.
Making this optional, since it can have a performance penalty when installing breakpionts.")
(defun find-breakpoint (function-name)
(check-type function-name symbol)
(gethash function-name *breakpoints*))
(defun breakpoint-installed-p (function-name)
"Wether a breakpoint is installed on FUNCTION-NAME."
(check-type function-name symbol)
(let ((breakpoint (gethash function-name *breakpoints*)))
(when breakpoint
(destructuring-bind (&key type replaced break definitions) breakpoint
(declare (ignore type replaced definitions))
(when (eq (symbol-function function-name) break)
(return-from breakpoint-installed-p t))))))
(defun break-on-entry (function-name)
"Setup a breakpoint on entry on FUNCTION-NAME."
(check-type function-name symbol)
;; First remove any breakpoints installed on FUNCTION-NAME, if any
(remove-breakpoint function-name)
(let* ((definitions (when *save-definitions*
(swank:find-definitions-for-emacs (prin1-to-string function-name))))
(original-function (symbol-function function-name))
(function-with-break
(lambda (&rest args)
(break)
(apply original-function args))))
(setf (symbol-function function-name) function-with-break)
(setf (gethash function-name *breakpoints*)
(list :type :break-on-entry
:replaced original-function
:break function-with-break
:definitions definitions)))
t)
(defun step-on-entry (function-name)
"Start stepping when function named FUNCTION-NAME is invoked."
(check-type function-name symbol)
;; First remove any breakpoints installed on FUNCTION-NAME, if any
(remove-breakpoint function-name)
(let* ((definitions (when *save-definitions*
(swank:find-definitions-for-emacs (prin1-to-string function-name))))
(original-function (symbol-function function-name))
(function-with-step
(lambda (&rest args)
(step
(apply original-function args)))))
(setf (symbol-function function-name) function-with-step)
(setf (gethash function-name *breakpoints*)
(list :type :step-on-entry
:replaced original-function
:break function-with-step
:definitions definitions)))
t)
(defun remove-breakpoint (function-name)
"Remove breakpoint on FUNCTION-NAME."
(check-type function-name symbol)
(when (not (breakpoint-installed-p function-name))
(return-from remove-breakpoint nil))
(let ((breakpoint (gethash function-name *breakpoints*)))
(destructuring-bind (&key type replaced break definitions) breakpoint
(declare (ignore type definitions))
(when (eq (symbol-function function-name) break)
(setf (symbol-function function-name) replaced)))
(remhash function-name *breakpoints*)
t))
(defun disable-breakpoint (function-name)
"Disable breakpoint on FUNCTION-NAME.
The breakpoint remains in the list of breakpoints."
(check-type function-name symbol)
(when (not (breakpoint-installed-p function-name))
(return-from disable-breakpoint nil))
(let ((breakpoint (gethash function-name *breakpoints*)))
(destructuring-bind (&key type replaced break definitions) breakpoint
(declare (ignore type definitions))
(when (eq (symbol-function function-name) break)
(setf (symbol-function function-name) replaced)))
t))
(defun toggle-breakpoint (function-name)
"Toggle breakpoint on FUNCTION-NAME."
(check-type function-name symbol)
(if (breakpoint-installed-p function-name)
(progn
(remove-breakpoint function-name)
nil)
(progn
(break-on-entry function-name)
t)))
(defun remove-all-breakpoints ()
"Remove all installed breakpoints."
(loop for k being each hash-key of *breakpoints*
do (remove-breakpoint k))
(setf *breakpoints* (make-hash-table))
t)
(defun disable-all-breakpoints ()
"Disable all installed breakpoints."
(loop for k being each hash-key of *breakpoints*
do (disable-breakpoint k))
t)
(defun reinstall-breakpoint (function-name)
"Reinstall breakpoint on FUNCTION-NAME.
When a function is recompiled, the breakpoint is lost. A call to this function reinstalls the breakpoint."
(let ((breakpoint (gethash function-name *breakpoints*)))
(when breakpoint
(let ((break (getf breakpoint :break)))
(when (not (eq (symbol-function function-name) break))
(break-on-entry function-name))))))
(defun reinstall-all-breakpoints ()
"Reinstall all breakpoints.
When a function is recompiled, the breakpoint is lost. A call to this function reintalls all breakpoints."
(loop for k being each hash-key of *breakpoints*
do (reinstall-breakpoint k)))
(provide :breakpoints)