|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /* |
|
8 * JavaScript Debugging support - Stepping support |
|
9 */ |
|
10 |
|
11 #include "jsd.h" |
|
12 |
|
13 /* |
|
14 * #define JSD_TRACE 1 |
|
15 */ |
|
16 |
|
17 #ifdef JSD_TRACE |
|
18 |
|
19 static char* |
|
20 _indentSpaces(int i) |
|
21 { |
|
22 #define MAX_INDENT 63 |
|
23 static char* p = nullptr; |
|
24 if(!p) |
|
25 { |
|
26 p = calloc(1, MAX_INDENT+1); |
|
27 if(!p) return ""; |
|
28 memset(p, ' ', MAX_INDENT); |
|
29 } |
|
30 if(i > MAX_INDENT) return p; |
|
31 return p + MAX_INDENT-i; |
|
32 } |
|
33 |
|
34 static void |
|
35 _interpreterTrace(JSDContext* jsdc, JSContext *cx, JSAbstractFramePtr frame, |
|
36 bool isConstructing, bool before) |
|
37 { |
|
38 JSDScript* jsdscript = nullptr; |
|
39 JSScript * script; |
|
40 static indent = 0; |
|
41 JSString* funName = nullptr; |
|
42 |
|
43 script = frame.script(); |
|
44 if(script) |
|
45 { |
|
46 JSD_LOCK_SCRIPTS(jsdc); |
|
47 jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, frame); |
|
48 JSD_UNLOCK_SCRIPTS(jsdc); |
|
49 if(jsdscript) |
|
50 funName = JSD_GetScriptFunctionId(jsdc, jsdscript); |
|
51 } |
|
52 |
|
53 if(before) |
|
54 printf("%sentering ", _indentSpaces(indent++)); |
|
55 else |
|
56 printf("%sleaving ", _indentSpaces(--indent)); |
|
57 |
|
58 if (!funName) |
|
59 printf("TOP_LEVEL"); |
|
60 else |
|
61 JS_FileEscapedString(stdout, funName, 0); |
|
62 |
|
63 if(before) |
|
64 { |
|
65 jsval thisVal; |
|
66 |
|
67 printf("%s this: ", isConstructing ? "constructing":""); |
|
68 |
|
69 if (JS_GetFrameThis(cx, frame, &thisVal)) |
|
70 printf("0x%0llx", (uintptr_t) thisVal); |
|
71 else |
|
72 puts("<unavailable>"); |
|
73 } |
|
74 printf("\n"); |
|
75 MOZ_ASSERT(indent >= 0); |
|
76 } |
|
77 #endif |
|
78 |
|
79 bool |
|
80 _callHook(JSDContext *jsdc, JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, |
|
81 bool before, unsigned type, JSD_CallHookProc hook, void *hookData) |
|
82 { |
|
83 JSDScript* jsdscript; |
|
84 JSScript* jsscript; |
|
85 bool hookresult = true; |
|
86 |
|
87 if (!jsdc || !jsdc->inited) |
|
88 return false; |
|
89 |
|
90 if (!hook && !(jsdc->flags & JSD_COLLECT_PROFILE_DATA)) |
|
91 { |
|
92 /* no hook to call, no profile data needs to be collected, |
|
93 * so there is nothing to do here. |
|
94 */ |
|
95 return hookresult; |
|
96 } |
|
97 |
|
98 if (before && isConstructing) { |
|
99 JS::RootedValue newObj(cx); |
|
100 if (!frame.getThisValue(cx, &newObj)) |
|
101 return false; |
|
102 jsd_Constructing(jsdc, cx, JSVAL_TO_OBJECT(newObj), frame); |
|
103 } |
|
104 |
|
105 jsscript = frame.script(); |
|
106 if (jsscript) |
|
107 { |
|
108 JSD_LOCK_SCRIPTS(jsdc); |
|
109 jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, jsscript, frame); |
|
110 JSD_UNLOCK_SCRIPTS(jsdc); |
|
111 |
|
112 if (jsdscript) |
|
113 { |
|
114 if (JSD_IS_PROFILE_ENABLED(jsdc, jsdscript)) |
|
115 { |
|
116 JSDProfileData *pdata; |
|
117 pdata = jsd_GetScriptProfileData (jsdc, jsdscript); |
|
118 if (pdata) |
|
119 { |
|
120 if (before) |
|
121 { |
|
122 if (!pdata->lastCallStart) |
|
123 { |
|
124 int64_t now; |
|
125 JSDProfileData *callerpdata; |
|
126 |
|
127 /* Get the time just the once, for consistency. */ |
|
128 now = JS_Now(); |
|
129 /* This contains a pointer to the profile data for |
|
130 * the caller of this function. */ |
|
131 callerpdata = jsdc->callingFunctionPData; |
|
132 if (callerpdata) |
|
133 { |
|
134 int64_t ll_delta; |
|
135 pdata->caller = callerpdata; |
|
136 /* We need to 'stop' the timer for the caller. |
|
137 * Use time since last return if appropriate. */ |
|
138 ll_delta = jsdc->lastReturnTime |
|
139 ? now - jsdc->lastReturnTime |
|
140 : now - callerpdata->lastCallStart; |
|
141 callerpdata->runningTime += ll_delta; |
|
142 } |
|
143 /* We're the new current function, and no return |
|
144 * has happened yet. */ |
|
145 jsdc->callingFunctionPData = pdata; |
|
146 jsdc->lastReturnTime = 0; |
|
147 /* This function has no running time (just been |
|
148 * called!), and we'll need the call start time. */ |
|
149 pdata->runningTime = 0; |
|
150 pdata->lastCallStart = now; |
|
151 } else { |
|
152 if (++pdata->recurseDepth > pdata->maxRecurseDepth) |
|
153 pdata->maxRecurseDepth = pdata->recurseDepth; |
|
154 } |
|
155 /* make sure we're called for the return too. */ |
|
156 hookresult = true; |
|
157 } else if (!pdata->recurseDepth && pdata->lastCallStart) { |
|
158 int64_t now, ll_delta; |
|
159 double delta; |
|
160 now = JS_Now(); |
|
161 ll_delta = now - pdata->lastCallStart; |
|
162 delta = ll_delta; |
|
163 delta /= 1000.0; |
|
164 pdata->totalExecutionTime += delta; |
|
165 /* minExecutionTime starts as 0, so we need to overwrite |
|
166 * it on the first call always. */ |
|
167 if ((0 == pdata->callCount) || |
|
168 delta < pdata->minExecutionTime) |
|
169 { |
|
170 pdata->minExecutionTime = delta; |
|
171 } |
|
172 if (delta > pdata->maxExecutionTime) |
|
173 pdata->maxExecutionTime = delta; |
|
174 |
|
175 /* If we last returned from a function (as opposed to |
|
176 * having last entered this function), we need to inc. |
|
177 * the running total by the time delta since the last |
|
178 * return, and use the running total instead of the |
|
179 * delta calculated above. */ |
|
180 if (jsdc->lastReturnTime) |
|
181 { |
|
182 /* Add last chunk to running time, and use total |
|
183 * running time as 'delta'. */ |
|
184 ll_delta = now - jsdc->lastReturnTime; |
|
185 pdata->runningTime += ll_delta; |
|
186 delta = pdata->runningTime; |
|
187 delta /= 1000.0; |
|
188 } |
|
189 |
|
190 pdata->totalOwnExecutionTime += delta; |
|
191 /* See minExecutionTime comment above. */ |
|
192 if ((0 == pdata->callCount) || |
|
193 delta < pdata->minOwnExecutionTime) |
|
194 { |
|
195 pdata->minOwnExecutionTime = delta; |
|
196 } |
|
197 if (delta > pdata->maxOwnExecutionTime) |
|
198 pdata->maxOwnExecutionTime = delta; |
|
199 |
|
200 /* Current function is now our caller. */ |
|
201 jsdc->callingFunctionPData = pdata->caller; |
|
202 /* No hanging pointers, please. */ |
|
203 pdata->caller = nullptr; |
|
204 /* Mark the time we returned, and indicate this |
|
205 * function is no longer running. */ |
|
206 jsdc->lastReturnTime = now; |
|
207 pdata->lastCallStart = 0; |
|
208 ++pdata->callCount; |
|
209 } else if (pdata->recurseDepth) { |
|
210 --pdata->recurseDepth; |
|
211 ++pdata->callCount; |
|
212 } |
|
213 } |
|
214 if (hook) |
|
215 jsd_CallCallHook (jsdc, cx, type, hook, hookData); |
|
216 } else { |
|
217 if (hook) |
|
218 hookresult = |
|
219 jsd_CallCallHook (jsdc, cx, type, hook, hookData); |
|
220 else |
|
221 hookresult = true; |
|
222 } |
|
223 } |
|
224 } |
|
225 |
|
226 #ifdef JSD_TRACE |
|
227 _interpreterTrace(jsdc, cx, frame, isConstructing, before); |
|
228 return true; |
|
229 #else |
|
230 return hookresult; |
|
231 #endif |
|
232 |
|
233 } |
|
234 |
|
235 void * |
|
236 jsd_FunctionCallHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, |
|
237 bool before, bool *ok, void *closure) |
|
238 { |
|
239 JSDContext* jsdc; |
|
240 JSD_CallHookProc hook; |
|
241 void* hookData; |
|
242 |
|
243 jsdc = (JSDContext*) closure; |
|
244 |
|
245 /* local in case jsdc->functionHook gets cleared on another thread */ |
|
246 JSD_LOCK(); |
|
247 hook = jsdc->functionHook; |
|
248 hookData = jsdc->functionHookData; |
|
249 JSD_UNLOCK(); |
|
250 |
|
251 if (_callHook (jsdc, cx, frame, isConstructing, before, |
|
252 (before) ? JSD_HOOK_FUNCTION_CALL : JSD_HOOK_FUNCTION_RETURN, |
|
253 hook, hookData)) |
|
254 { |
|
255 return closure; |
|
256 } |
|
257 |
|
258 return nullptr; |
|
259 } |
|
260 |
|
261 void * |
|
262 jsd_TopLevelCallHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, |
|
263 bool before, bool *ok, void *closure) |
|
264 { |
|
265 JSDContext* jsdc; |
|
266 JSD_CallHookProc hook; |
|
267 void* hookData; |
|
268 |
|
269 jsdc = (JSDContext*) closure; |
|
270 |
|
271 /* local in case jsdc->toplevelHook gets cleared on another thread */ |
|
272 JSD_LOCK(); |
|
273 hook = jsdc->toplevelHook; |
|
274 hookData = jsdc->toplevelHookData; |
|
275 JSD_UNLOCK(); |
|
276 |
|
277 if (_callHook (jsdc, cx, frame, isConstructing, before, |
|
278 (before) ? JSD_HOOK_TOPLEVEL_START : JSD_HOOK_TOPLEVEL_END, |
|
279 hook, hookData)) |
|
280 { |
|
281 return closure; |
|
282 } |
|
283 |
|
284 return nullptr; |
|
285 |
|
286 } |