1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=80:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * Copyright (C) 2007 Sun Microsystems, Inc. All Rights Reserved.
18 : *
19 : * Contributor(s):
20 : * Brendan Eich <brendan@mozilla.org>
21 : *
22 : * Alternatively, the contents of this file may be used under the terms of
23 : * either of the GNU General Public License Version 2 or later (the "GPL"),
24 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
25 : * in which case the provisions of the GPL or the LGPL are applicable instead
26 : * of those above. If you wish to allow use of your version of this file only
27 : * under the terms of either the GPL or the LGPL, and not to allow others to
28 : * use your version of this file under the terms of the MPL, indicate your
29 : * decision by deleting the provisions above and replace them with the notice
30 : * and other provisions required by the GPL or the LGPL. If you do not delete
31 : * the provisions above, a recipient may use your version of this file under
32 : * the terms of any one of the MPL, the GPL or the LGPL.
33 : *
34 : * ***** END LICENSE BLOCK ***** */
35 :
36 : #ifdef MOZ_ETW
37 :
38 : #include "jswin.h"
39 : #include <evntprov.h>
40 : #include <sys/types.h>
41 :
42 : /* Generated from ETWProvider.man */
43 : #include "ETWProvider.h"
44 : #endif
45 :
46 : #include "jsapi.h"
47 : #include "jsutil.h"
48 : #include "jsatom.h"
49 : #include "jscntxt.h"
50 : #include "jsdbgapi.h"
51 : #include "jsfun.h"
52 : #include "jsinterp.h"
53 : #include "jsobj.h"
54 : #include "jsprobes.h"
55 : #include "jsscript.h"
56 : #include "jsstr.h"
57 :
58 : #include "methodjit/Compiler.h"
59 :
60 : #include "jsobjinlines.h"
61 :
62 : #define TYPEOF(cx,v) (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v))
63 :
64 : using namespace js;
65 :
66 : const char Probes::nullName[] = "(null)";
67 : const char Probes::anonymousName[] = "(anonymous)";
68 :
69 : bool Probes::ProfilingActive = true;
70 :
71 19870 : static Vector<Probes::JITWatcher*, 4, SystemAllocPolicy> jitWatchers;
72 :
73 : bool
74 0 : Probes::addJITWatcher(JITWatcher *watcher)
75 : {
76 0 : return jitWatchers.append(watcher);
77 : }
78 :
79 : bool
80 0 : Probes::removeJITWatcher(JSRuntime *rt, JITWatcher *watcher)
81 : {
82 0 : JITWatcher **place = Find(jitWatchers, watcher);
83 0 : if (!place)
84 0 : return false;
85 0 : if (rt)
86 0 : rt->delete_(*place);
87 : else
88 0 : Foreground::delete_(*place);
89 0 : jitWatchers.erase(place);
90 0 : return true;
91 : }
92 :
93 : void
94 19808 : Probes::removeAllJITWatchers(JSRuntime *rt)
95 : {
96 19808 : if (rt) {
97 0 : for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
98 0 : rt->delete_(*p);
99 : } else {
100 19808 : for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
101 0 : Foreground::delete_(*p);
102 : }
103 19808 : jitWatchers.clear();
104 19808 : }
105 :
106 : Probes::JITReportGranularity
107 128881 : Probes::JITGranularityRequested()
108 : {
109 128881 : JITReportGranularity want = JITREPORT_GRANULARITY_NONE;
110 128881 : for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p) {
111 0 : JITReportGranularity request = (*p)->granularityRequested();
112 0 : if (request > want)
113 0 : want = request;
114 : }
115 :
116 128881 : return want;
117 : }
118 :
119 : #ifdef JS_METHODJIT
120 : /*
121 : * Flatten the tree of inlined frames into a series of native code regions, one
122 : * for each contiguous section of native code that belongs to a single
123 : * ActiveFrame. (Note that some of these regions may be zero-length, for
124 : * example if two ActiveFrames end at the same place.)
125 : */
126 : typedef mjit::Compiler::ActiveFrame ActiveFrame;
127 :
128 : bool
129 0 : Probes::JITWatcher::CollectNativeRegions(RegionVector ®ions,
130 : JSRuntime *rt,
131 : mjit::JITChunk *jit,
132 : mjit::JSActiveFrame *outerFrame,
133 : mjit::JSActiveFrame **inlineFrames)
134 : {
135 0 : regions.resize(jit->nInlineFrames * 2 + 2);
136 :
137 : mjit::JSActiveFrame **stack =
138 0 : rt->array_new<mjit::JSActiveFrame*>(jit->nInlineFrames+2);
139 0 : if (!stack)
140 0 : return false;
141 0 : uint32_t depth = 0;
142 0 : uint32_t ip = 0;
143 :
144 0 : stack[depth++] = NULL;
145 0 : stack[depth++] = outerFrame;
146 0 : regions[0].frame = outerFrame;
147 0 : regions[0].script = outerFrame->script;
148 0 : regions[0].pc = outerFrame->script->code;
149 0 : regions[0].enter = true;
150 0 : ip++;
151 :
152 0 : for (uint32_t i = 0; i <= jit->nInlineFrames; i++) {
153 0 : mjit::JSActiveFrame *frame = (i < jit->nInlineFrames) ? inlineFrames[i] : outerFrame;
154 :
155 : // Not a down frame; pop the current frame, then pop until we reach
156 : // this frame's parent, recording subframe ends as we go
157 0 : while (stack[depth-1] != frame->parent) {
158 0 : depth--;
159 0 : JS_ASSERT(depth > 0);
160 : // Pop up from regions[ip-1].frame to top of the stack: start a
161 : // region in the destination frame and close off the source
162 : // (origin) frame at the end of its script
163 0 : mjit::JSActiveFrame *src = regions[ip-1].frame;
164 0 : mjit::JSActiveFrame *dst = stack[depth-1];
165 0 : JS_ASSERT_IF(!dst, i == jit->nInlineFrames);
166 0 : regions[ip].frame = dst;
167 0 : regions[ip].script = dst ? dst->script : NULL;
168 0 : regions[ip].pc = src->parentPC + 1;
169 0 : regions[ip-1].endpc = src->script->code + src->script->length;
170 0 : regions[ip].enter = false;
171 0 : ip++;
172 : }
173 :
174 0 : if (i < jit->nInlineFrames) {
175 : // Push a frame (enter an inlined function). Start a region at the
176 : // beginning of the new frame's script, and end the previous region
177 : // at parentPC.
178 0 : stack[depth++] = frame;
179 :
180 0 : regions[ip].frame = frame;
181 0 : regions[ip].script = frame->script;
182 0 : regions[ip].pc = frame->script->code;
183 0 : regions[ip-1].endpc = frame->parentPC;
184 0 : regions[ip].enter = true;
185 0 : ip++;
186 : }
187 : }
188 :
189 : // Final region is always zero-length and not particularly useful
190 0 : ip--;
191 0 : regions.popBack();
192 :
193 0 : mjit::JSActiveFrame *prev = NULL;
194 0 : for (NativeRegion *iter = regions.begin(); iter != regions.end(); ++iter) {
195 0 : mjit::JSActiveFrame *frame = iter->frame;
196 0 : if (iter->enter) {
197 : // Pushing down a frame, so region starts at the beginning of the
198 : // (destination) frame
199 0 : iter->mainOffset = frame->mainCodeStart;
200 0 : iter->stubOffset = frame->stubCodeStart;
201 : } else {
202 : // Popping up a level, so region starts at the end of the (source) frame
203 0 : iter->mainOffset = prev->mainCodeEnd;
204 0 : iter->stubOffset = prev->stubCodeEnd;
205 : }
206 0 : prev = frame;
207 : }
208 :
209 0 : JS_ASSERT(ip == 2 * jit->nInlineFrames + 1);
210 0 : rt->array_delete(stack);
211 :
212 : // All of the stub code comes immediately after the main code
213 0 : for (NativeRegion *iter = regions.begin(); iter != regions.end(); ++iter)
214 0 : iter->stubOffset += outerFrame->mainCodeEnd;
215 :
216 0 : return true;
217 : }
218 :
219 : void
220 128827 : Probes::registerMJITCode(JSContext *cx, js::mjit::JITScript *jscr,
221 : js::mjit::JSActiveFrame *outerFrame,
222 : js::mjit::JSActiveFrame **inlineFrames,
223 : void *mainCodeAddress, size_t mainCodeSize,
224 : void *stubCodeAddress, size_t stubCodeSize)
225 : {
226 128827 : for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
227 : (*p)->registerMJITCode(cx, jscr, outerFrame,
228 : inlineFrames,
229 : mainCodeAddress, mainCodeSize,
230 0 : stubCodeAddress, stubCodeSize);
231 128827 : }
232 :
233 : void
234 128827 : Probes::discardMJITCode(JSContext *cx, mjit::JITScript *jscr, JSScript *script, void* address)
235 : {
236 128827 : for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
237 0 : (*p)->discardMJITCode(cx, jscr, script, address);
238 128827 : }
239 :
240 : void
241 303936 : Probes::registerICCode(JSContext *cx,
242 : mjit::JITScript *jscr, JSScript *script, jsbytecode* pc,
243 : void *start, size_t size)
244 : {
245 303936 : for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
246 0 : (*p)->registerICCode(cx, jscr, script, pc, start, size);
247 303936 : }
248 : #endif
249 :
250 : /* ICs are unregistered in a batch */
251 : void
252 9875 : Probes::discardExecutableRegion(void *start, size_t size)
253 : {
254 9875 : for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
255 0 : (*p)->discardExecutableRegion(start, size);
256 9875 : }
257 :
258 : static JSRuntime *initRuntime;
259 :
260 : JSBool
261 19811 : Probes::startEngine()
262 : {
263 19811 : bool ok = true;
264 :
265 19811 : return ok;
266 : }
267 :
268 : bool
269 19910 : Probes::createRuntime(JSRuntime *rt)
270 : {
271 19910 : bool ok = true;
272 :
273 : static JSCallOnceType once = { 0 };
274 19910 : initRuntime = rt;
275 19910 : if (!JS_CallOnce(&once, Probes::startEngine))
276 0 : ok = false;
277 :
278 : #ifdef MOZ_ETW
279 : if (!ETWCreateRuntime(rt))
280 : ok = false;
281 : #endif
282 :
283 19910 : return ok;
284 : }
285 :
286 : bool
287 19908 : Probes::destroyRuntime(JSRuntime *rt)
288 : {
289 19908 : bool ok = true;
290 : #ifdef MOZ_ETW
291 : if (!ETWDestroyRuntime(rt))
292 : ok = false;
293 : #endif
294 :
295 19908 : return ok;
296 : }
297 :
298 : bool
299 19808 : Probes::shutdown()
300 : {
301 19808 : bool ok = true;
302 : #ifdef MOZ_ETW
303 : if (!ETWShutdown())
304 : ok = false;
305 : #endif
306 :
307 19808 : Probes::removeAllJITWatchers(NULL);
308 :
309 19808 : return ok;
310 59610 : }
311 :
312 : #ifdef INCLUDE_MOZILLA_DTRACE
313 : static const char *
314 : ScriptFilename(const JSScript *script)
315 : {
316 : if (!script)
317 : return Probes::nullName;
318 : if (!script->filename)
319 : return Probes::anonymousName;
320 : return script->filename;
321 : }
322 :
323 : static const char *
324 : FunctionName(JSContext *cx, const JSFunction *fun, JSAutoByteString* bytes)
325 : {
326 : if (!fun)
327 : return Probes::nullName;
328 : JSAtom *atom = const_cast<JSAtom*>(fun->atom);
329 : if (!atom)
330 : return Probes::anonymousName;
331 : return bytes->encode(cx, atom) ? bytes->ptr() : Probes::nullName;
332 : }
333 :
334 : static const char *
335 : FunctionClassname(const JSFunction *fun)
336 : {
337 : if (!fun || fun->isInterpreted())
338 : return Probes::nullName;
339 : if (fun->getConstructorClass())
340 : return fun->getConstructorClass()->name;
341 : return Probes::nullName;
342 : }
343 :
344 : /*
345 : * These functions call the DTrace macros for the JavaScript USDT probes.
346 : * Originally this code was inlined in the JavaScript code; however since
347 : * a number of operations are called, these have been placed into functions
348 : * to reduce any negative compiler optimization effect that the addition of
349 : * a number of usually unused lines of code would cause.
350 : */
351 : void
352 : Probes::DTraceEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script)
353 : {
354 : JSAutoByteString funNameBytes;
355 : JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(script), FunctionClassname(fun),
356 : FunctionName(cx, fun, &funNameBytes));
357 : }
358 :
359 : void
360 : Probes::DTraceExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script)
361 : {
362 : JSAutoByteString funNameBytes;
363 : JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(script), FunctionClassname(fun),
364 : FunctionName(cx, fun, &funNameBytes));
365 : }
366 : #endif
367 :
368 : #ifdef MOZ_ETW
369 : static void
370 : current_location(JSContext *cx, int* lineno, char const **filename)
371 : {
372 : JSScript *script = js_GetCurrentScript(cx);
373 : if (! script) {
374 : *lineno = -1;
375 : *filename = "(uninitialized)";
376 : return;
377 : }
378 : *lineno = js_PCToLineNumber(cx, script, js_GetCurrentBytecodePC(cx));
379 : *filename = ScriptFilename(script);
380 : }
381 :
382 : /*
383 : * ETW (Event Tracing for Windows)
384 : *
385 : * These are here rather than in the .h file to avoid having to include
386 : * windows.h in a header.
387 : */
388 : bool
389 : Probes::ETWCallTrackingActive(JSContext *cx)
390 : {
391 : return MCGEN_ENABLE_CHECK(MozillaSpiderMonkey_Context, EvtFunctionEntry);
392 : }
393 :
394 : bool
395 : Probes::ETWCreateRuntime(JSRuntime *rt)
396 : {
397 : static bool registered = false;
398 : if (!registered) {
399 : EventRegisterMozillaSpiderMonkey();
400 : registered = true;
401 : }
402 : return true;
403 : }
404 :
405 : bool
406 : Probes::ETWDestroyRuntime(JSRuntime *rt)
407 : {
408 : return true;
409 : }
410 :
411 : bool
412 : Probes::ETWShutdown()
413 : {
414 : EventUnregisterMozillaSpiderMonkey();
415 : return true;
416 : }
417 :
418 : bool
419 : Probes::ETWEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter)
420 : {
421 : int lineno = script ? script->lineno : -1;
422 : JSAutoByteString bytes;
423 : return (EventWriteEvtFunctionEntry(ScriptFilename(script), lineno,
424 : ObjectClassname((JSObject *)fun),
425 : FunctionName(cx, fun, &bytes)) == ERROR_SUCCESS);
426 : }
427 :
428 : bool
429 : Probes::ETWExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter)
430 : {
431 : int lineno = script ? script->lineno : -1;
432 : JSAutoByteString bytes;
433 : return (EventWriteEvtFunctionExit(ScriptFilename(script), lineno,
434 : ObjectClassname((JSObject *)fun),
435 : FunctionName(cx, fun, &bytes)) == ERROR_SUCCESS);
436 : }
437 :
438 : bool
439 : Probes::ETWCreateObject(JSContext *cx, JSObject *obj)
440 : {
441 : int lineno;
442 : const char * script_filename;
443 : current_location(cx, &lineno, &script_filename);
444 :
445 : return EventWriteEvtObjectCreate(script_filename, lineno,
446 : ObjectClassname(obj), reinterpret_cast<uint64_t_t>(obj),
447 : obj ? obj->computedSizeOfIncludingThis() : 0) == ERROR_SUCCESS;
448 : }
449 :
450 : bool
451 : Probes::ETWFinalizeObject(JSObject *obj)
452 : {
453 : return EventWriteEvtObjectFinalize(ObjectClassname(obj),
454 : reinterpret_cast<uint64_t_t>(obj)) == ERROR_SUCCESS;
455 : }
456 :
457 : bool
458 : Probes::ETWResizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize)
459 : {
460 : int lineno;
461 : const char *script_filename;
462 : current_location(cx, &lineno, &script_filename);
463 :
464 : return EventWriteEvtObjectResize(script_filename, lineno,
465 : ObjectClassname(obj), reinterpret_cast<uint64_t_t>(obj),
466 : oldSize, newSize) == ERROR_SUCCESS;
467 : }
468 :
469 : bool
470 : Probes::ETWCreateString(JSContext *cx, JSString *string, size_t length)
471 : {
472 : int lineno;
473 : const char *script_filename;
474 : current_location(cx, &lineno, &script_filename);
475 :
476 : return EventWriteEvtStringCreate(script_filename, lineno,
477 : reinterpret_cast<uint64_t_t>(string), length) ==
478 : ERROR_SUCCESS;
479 : }
480 :
481 : bool
482 : Probes::ETWFinalizeString(JSString *string)
483 : {
484 : return EventWriteEvtStringFinalize(reinterpret_cast<uint64_t>(string),
485 : string->length()) == ERROR_SUCCESS;
486 : }
487 :
488 : bool
489 : Probes::ETWCompileScriptBegin(const char *filename, int lineno)
490 : {
491 : return EventWriteEvtScriptCompileBegin(filename, lineno) == ERROR_SUCCESS;
492 : }
493 :
494 : bool
495 : Probes::ETWCompileScriptEnd(const char *filename, int lineno)
496 : {
497 : return EventWriteEvtScriptCompileEnd(filename, lineno) == ERROR_SUCCESS;
498 : }
499 :
500 : bool
501 : Probes::ETWCalloutBegin(JSContext *cx, JSFunction *fun)
502 : {
503 : const char *script_filename;
504 : int lineno;
505 : JSAutoByteString bytes;
506 : current_location(cx, &lineno, &script_filename);
507 :
508 : return EventWriteEvtCalloutBegin(script_filename,
509 : lineno,
510 : ObjectClassname((JSObject *)fun),
511 : FunctionName(cx, fun, &bytes)) == ERROR_SUCCESS;
512 : }
513 :
514 : bool
515 : Probes::ETWCalloutEnd(JSContext *cx, JSFunction *fun)
516 : {
517 : const char *script_filename;
518 : int lineno;
519 : JSAutoByteString bytes;
520 : current_location(cx, &lineno, &script_filename);
521 :
522 : return EventWriteEvtCalloutEnd(script_filename,
523 : lineno,
524 : ObjectClassname((JSObject *)fun),
525 : FunctionName(cx, fun, &bytes)) == ERROR_SUCCESS;
526 : }
527 :
528 : bool
529 : Probes::ETWAcquireMemory(JSContext *cx, void *address, size_t nbytes)
530 : {
531 : return EventWriteEvtMemoryAcquire(reinterpret_cast<uint64_t>(cx->compartment),
532 : reinterpret_cast<uint64_t>(address),
533 : nbytes) == ERROR_SUCCESS;
534 : }
535 :
536 : bool
537 : Probes::ETWReleaseMemory(JSContext *cx, void *address, size_t nbytes)
538 : {
539 : return EventWriteEvtMemoryRelease(reinterpret_cast<uint64_t>(cx->compartment),
540 : reinterpret_cast<uint64_t>(address),
541 : nbytes) == ERROR_SUCCESS;
542 : }
543 :
544 : bool
545 : Probes::ETWGCStart(JSCompartment *compartment)
546 : {
547 : return EventWriteEvtGCStart(reinterpret_cast<uint64_t>(compartment)) == ERROR_SUCCESS;
548 : }
549 :
550 : bool
551 : Probes::ETWGCEnd(JSCompartment *compartment)
552 : {
553 : return EventWriteEvtGCEnd(reinterpret_cast<uint64_t>(compartment)) == ERROR_SUCCESS;
554 : }
555 :
556 : bool
557 : Probes::ETWGCStartMarkPhase(JSCompartment *compartment)
558 : {
559 : return EventWriteEvtGCStartMarkPhase(reinterpret_cast<uint64_t>(compartment)) == ERROR_SUCCESS;
560 : }
561 :
562 : bool
563 : Probes::ETWGCEndMarkPhase(JSCompartment *compartment)
564 : {
565 : return EventWriteEvtGCEndMarkPhase(reinterpret_cast<uint64_t>(compartment)) == ERROR_SUCCESS;
566 : }
567 :
568 : bool
569 : Probes::ETWGCStartSweepPhase(JSCompartment *compartment)
570 : {
571 : return EventWriteEvtGCStartSweepPhase(reinterpret_cast<uint64_t>(compartment)) ==
572 : ERROR_SUCCESS;
573 : }
574 :
575 : bool
576 : Probes::ETWGCEndSweepPhase(JSCompartment *compartment)
577 : {
578 : return EventWriteEvtGCEndSweepPhase(reinterpret_cast<uint64_t>(compartment)) == ERROR_SUCCESS;
579 : }
580 :
581 : bool
582 : Probes::ETWCustomMark(JSString *string)
583 : {
584 : const jschar *chars = string->getCharsZ(NULL);
585 : return !chars || EventWriteEvtCustomString(chars) == ERROR_SUCCESS;
586 : }
587 :
588 : bool
589 : Probes::ETWCustomMark(const char *string)
590 : {
591 : return EventWriteEvtCustomANSIString(string) == ERROR_SUCCESS;
592 : }
593 :
594 : bool
595 : Probes::ETWCustomMark(int marker)
596 : {
597 : return EventWriteEvtCustomInt(marker) == ERROR_SUCCESS;
598 : }
599 :
600 : bool
601 : Probes::ETWStartExecution(JSContext *cx, JSScript *script)
602 : {
603 : int lineno = script ? script->lineno : -1;
604 : return EventWriteEvtExecuteStart(ScriptFilename(script), lineno) == ERROR_SUCCESS;
605 : }
606 :
607 : bool
608 : Probes::ETWStopExecution(JSContext *cx, JSScript *script)
609 : {
610 : int lineno = script ? script->lineno : -1;
611 : return EventWriteEvtExecuteDone(ScriptFilename(script), lineno) == ERROR_SUCCESS;
612 : }
613 :
614 : bool
615 : Probes::ETWResizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize)
616 : {
617 : return EventWriteEvtHeapResize(reinterpret_cast<uint64_t>(compartment),
618 : oldSize, newSize) == ERROR_SUCCESS;
619 : }
620 :
621 : #endif
|