File Coverage

probe.xs
Criterion Covered Total %
statement 141 154 91.5
branch 57 108 52.7
condition n/a
subroutine n/a
pod n/a
total 198 262 75.5


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT /* we want efficiency */
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5             #include "ppport.h"
6              
7             #define PROBE_TYPE_NONE 0
8             #define PROBE_TYPE_ONCE 1
9             #define PROBE_TYPE_PERMANENT 2
10              
11             #define PROBE_ACTION_LOOKUP 0
12             #define PROBE_ACTION_CREATE 1
13             #define PROBE_ACTION_REMOVE 2
14              
15             /*
16             * Use preprocessor macros for time-sensitive operations.
17             */
18             #define probe_is_enabled() !!probe_enabled
19              
20             static Perl_ppaddr_t probe_nextstate_orig = 0;
21             static int probe_installed = 0;
22             static int probe_enabled = 0;
23             static HV* probe_hash = 0;
24             static SV* probe_trigger_cb = 0;
25              
26             static void probe_enable(void);
27             static void probe_disable(void);
28             static int probe_is_installed(void);
29             static void probe_install(void);
30             static void probe_remove(void);
31              
32             #define DEBUG 0
33              
34             #define INFO(x) do { if (DEBUG > 0) dbg_printf x; } while (0)
35             #define TRACE(x) do { if (DEBUG > 1) dbg_printf x; } while (0)
36              
37 0           void dbg_printf(const char *fmt, ...)
38             {
39             va_list args;
40 0           va_start(args, fmt);
41 0           vfprintf(stderr, fmt, args);
42 0           va_end(args);
43 0           }
44              
45 5           static inline void probe_invoke_callback(const char* file, int line, SV* callback)
46             {
47             int count;
48              
49 5           dSP;
50              
51 5           ENTER;
52 5           SAVETMPS;
53              
54 5 50         PUSHMARK (SP);
55 5 50         EXTEND(SP, 2);
56 5 50         XPUSHs(sv_2mortal(newSVpv(file, 0)));
57 5 50         XPUSHs(sv_2mortal(newSViv(line)));
58 5           PUTBACK;
59              
60 5           count = call_sv(callback, G_VOID|G_DISCARD);
61 5 50         if (count != 0) {
62 0           croak("probe trigger should have zero return values");
63             }
64              
65 5 50         FREETMPS;
66 5           LEAVE;
67 5           }
68              
69 126           static int probe_lookup(const char* file, int line, int type, int action)
70             {
71 126           U32 klen = strlen(file);
72             char kstr[20];
73 126           SV** rlines = 0;
74 126           SV** rflag = 0;
75 126           HV* lines = 0;
76 126           SV* flag = 0;
77              
78 126           rlines = hv_fetch(probe_hash, file, klen, 0);
79 126 100         if (rlines) {
80 101           lines = (HV*) SvRV(*rlines);
81             TRACE(("PROBE found entry for file [%s]: %p\n", file, lines));
82 25 100         } else if (action == PROBE_ACTION_CREATE) {
83 1           SV* slines = 0;
84 1           lines = newHV();
85 1           slines = (SV*) newRV((SV*) lines);
86 1           hv_store(probe_hash, file, klen, slines, 0);
87             INFO(("PROBE created entry for file [%s]: %p\n", file, lines));
88             } else {
89 24           return PROBE_TYPE_NONE;
90             }
91              
92 102           klen = sprintf(kstr, "%d", line);
93 102           rflag = hv_fetch(lines, kstr, klen, 0);
94 102 100         if (rflag) {
95 7           int ret = 0;
96 7           flag = *rflag;
97 7 50         ret = SvIV(flag);
98 7 100         if (action == PROBE_ACTION_REMOVE) {
99             /* TODO: remove file name when last line for file was removed? */
100 2           hv_delete(lines, kstr, klen, G_DISCARD);
101             INFO(("PROBE removed entry for line [%s] => %d\n", kstr, ret));
102             }
103 7           return ret;
104 95 100         } else if (action == PROBE_ACTION_CREATE) {
105 3           flag = newSViv(type);
106 3           hv_store(lines, kstr, klen, flag, 0);
107             INFO(("PROBE created entry for line [%s] => %d\n", kstr, type));
108 3           return type;
109             } else {
110 126           return PROBE_TYPE_NONE;
111             }
112              
113             /* catch any mistakes */
114             return PROBE_TYPE_NONE;
115             }
116              
117             /*
118             * This function will run for every single line in your Perl code.
119             * You would do well to make it as cheap as possible.
120             */
121 1786           static OP* probe_nextstate(pTHX)
122             {
123 1786           OP* ret = probe_nextstate_orig(aTHX);
124              
125             do {
126 1786           const char* file = 0;
127 1786           int line = 0;
128 1786           int type = PROBE_TYPE_NONE;
129              
130 1786 100         if (!probe_is_enabled()) {
131 1665           break;
132             }
133              
134 121 50         file = CopFILE(PL_curcop);
135 121           line = CopLINE(PL_curcop);
136             TRACE(("PROBE check [%s] [%d]\n", file, line));
137 121           type = probe_lookup(file, line, PROBE_TYPE_NONE, PROBE_ACTION_LOOKUP);
138 121 100         if (type == PROBE_TYPE_NONE) {
139 116           break;
140             }
141              
142             INFO(("PROBE triggered [%s] [%d] [%d]\n", file, line, type));
143 5 50         if (probe_trigger_cb) {
144 5           probe_invoke_callback(file, line, probe_trigger_cb);
145             }
146              
147 5 100         if (type == PROBE_TYPE_ONCE) {
148 2           probe_lookup(file, line, type, PROBE_ACTION_REMOVE);
149             }
150             } while (0);
151              
152 1786           return ret;
153             }
154              
155 1           static void probe_dump(void)
156             {
157 1           hv_iterinit(probe_hash);
158             while (1) {
159 2           SV* key = 0;
160 2           SV* value = 0;
161 2           char* kstr = 0;
162 2           STRLEN klen = 0;
163 2           HV* lines = 0;
164 2           HE* entry = hv_iternext(probe_hash);
165 2 100         if (!entry) {
166 1           break; /* no more hash keys */
167             }
168 1           key = hv_iterkeysv(entry);
169 1 50         if (!key) {
170 0           continue; /* invalid key */
171             }
172 1 50         kstr = SvPV(key, klen);
173 1 50         if (!kstr) {
174 0           continue; /* invalid key */
175             }
176 1           fprintf(stderr, "PROBE dump file [%s]\n", kstr);
177              
178 1           value = hv_iterval(probe_hash, entry);
179 1 50         if (!value) {
180 0           continue; /* invalid value */
181             }
182 1           lines = (HV*) SvRV(value);
183 1           hv_iterinit(lines);
184             while (1) {
185 4           SV* key = 0;
186 4           SV* value = 0;
187 4           char* kstr = 0;
188 4           STRLEN klen = 0;
189 4           HE* entry = hv_iternext(lines);
190 4 100         if (!entry) {
191 1           break; /* no more hash keys */
192             }
193 3           key = hv_iterkeysv(entry);
194 3 50         if (!key) {
195 0           continue; /* invalid key */
196             }
197 3 50         kstr = SvPV(key, klen);
198 3 50         if (!kstr) {
199 0           continue; /* invalid key */
200             }
201 3           value = hv_iterval(lines, entry);
202 3 50         if (!value || !SvTRUE(value)) {
    50          
    50          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    50          
    0          
203 0           continue;
204             }
205 3           fprintf(stderr, "PROBE dump line [%s]\n", kstr);
206 3           }
207 1           }
208 1           }
209              
210 3           static void probe_enable(void)
211             {
212 3 100         if (probe_is_enabled()) {
213 1           return;
214             }
215             INFO(("PROBE enabling\n"));
216 2           probe_enabled = 1;
217             }
218              
219 6           static void probe_reset(int installed)
220             {
221 6           probe_installed = installed;
222 6           probe_enabled = 0;
223 6           probe_hash = 0;
224 6           probe_trigger_cb = 0;
225 6           }
226              
227 7           static void probe_clear(void)
228             {
229 7           probe_hash = newHV();
230             INFO(("PROBE cleared\n"));
231 7           }
232              
233 6           static void probe_disable(void)
234             {
235 6 100         if (!probe_is_enabled()) {
236 5           return;
237             }
238 1           probe_enabled = 0;
239             INFO(("PROBE disabled\n"));
240             }
241              
242 15           static int probe_is_installed(void)
243             {
244 15           return probe_installed;
245             }
246              
247 7           static void probe_install(void)
248             {
249 7 100         if (probe_is_installed()) {
250 2           return;
251             }
252              
253             INFO(("PROBE installed, [%p] => [%p]\n", PL_ppaddr[OP_NEXTSTATE], probe_nextstate));
254              
255 5 100         if (!probe_nextstate_orig) {
256 4           probe_nextstate_orig = PL_ppaddr[OP_NEXTSTATE];
257             }
258 5           PL_ppaddr[OP_NEXTSTATE] = probe_nextstate;
259 5           probe_reset(1);
260 5           probe_clear();
261             }
262              
263 2           static void probe_remove(void)
264             {
265 2 100         if (!probe_is_installed()) {
266 1           return;
267             }
268             INFO(("PROBE removed, [%p] => [%p]\n", PL_ppaddr[OP_NEXTSTATE], probe_nextstate_orig));
269 1 50         if (probe_nextstate_orig) {
270 1           PL_ppaddr[OP_NEXTSTATE] = probe_nextstate_orig;
271             }
272 1           probe_reset(0);
273             }
274              
275             MODULE = Devel::Probe PACKAGE = Devel::Probe
276             PROTOTYPES: DISABLE
277              
278             #################################################################
279              
280             void
281             install()
282             CODE:
283 7           probe_install();
284              
285             void
286             remove()
287             CODE:
288 2           probe_remove();
289              
290             int
291             is_installed()
292             CODE:
293 6           RETVAL = probe_is_installed();
294             OUTPUT: RETVAL
295              
296             void
297             enable()
298             CODE:
299 3           probe_enable();
300              
301             void
302             disable()
303             CODE:
304 4           probe_disable();
305              
306             int
307             is_enabled()
308             CODE:
309 5           RETVAL = probe_is_enabled();
310             OUTPUT: RETVAL
311              
312             void
313             clear()
314             CODE:
315 2           probe_disable();
316 2           probe_clear();
317              
318             void
319             dump()
320             CODE:
321 1           probe_dump();
322              
323             void
324             add_probe(const char* file, int line, int type)
325             CODE:
326 3           probe_lookup(file, line, type, PROBE_ACTION_CREATE);
327              
328             void
329             trigger(SV* callback)
330             CODE:
331 1 50         if (probe_trigger_cb == (SV*)NULL) {
332 1           probe_trigger_cb = newSVsv(callback);
333             } else {
334 0 0         SvSetSV(probe_trigger_cb, callback);
335             }