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 18667 : 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 18666 : Probes::removeAllJITWatchers(JSRuntime *rt)
95 : {
96 18666 : if (rt) {
97 0 : for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
98 0 : rt->delete_(*p);
99 : } else {
100 18666 : for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
101 0 : Foreground::delete_(*p);
102 : }
103 18666 : jitWatchers.clear();
104 18666 : }
105 :
106 : Probes::JITReportGranularity
107 93731 : Probes::JITGranularityRequested()
108 : {
109 93731 : JITReportGranularity want = JITREPORT_GRANULARITY_NONE;
110 93731 : 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 93731 : 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 93652 : 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 93652 : 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 93652 : }
232 :
233 : void
234 93652 : Probes::discardMJITCode(FreeOp *fop, mjit::JITScript *jscr, JSScript *script, void* address)
235 : {
236 93652 : for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
237 0 : (*p)->discardMJITCode(fop, jscr, script, address);
238 93652 : }
239 :
240 : void
241 67597 : Probes::registerICCode(JSContext *cx,
242 : mjit::JITScript *jscr, JSScript *script, jsbytecode* pc,
243 : void *start, size_t size)
244 : {
245 67597 : for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
246 0 : (*p)->registerICCode(cx, jscr, script, pc, start, size);
247 67597 : }
248 : #endif
249 :
250 : /* ICs are unregistered in a batch */
251 : void
252 5646 : Probes::discardExecutableRegion(void *start, size_t size)
253 : {
254 5646 : for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
255 0 : (*p)->discardExecutableRegion(start, size);
256 5646 : }
257 :
258 : static JSRuntime *initRuntime;
259 :
260 : JSBool
261 18667 : Probes::startEngine()
262 : {
263 18667 : bool ok = true;
264 :
265 18667 : return ok;
266 : }
267 :
268 : bool
269 18761 : Probes::createRuntime(JSRuntime *rt)
270 : {
271 18761 : bool ok = true;
272 :
273 : static JSCallOnceType once = { 0 };
274 18761 : initRuntime = rt;
275 18761 : 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 18761 : return ok;
284 : }
285 :
286 : bool
287 18761 : Probes::destroyRuntime(JSRuntime *rt)
288 : {
289 18761 : bool ok = true;
290 : #ifdef MOZ_ETW
291 : if (!ETWDestroyRuntime(rt))
292 : ok = false;
293 : #endif
294 :
295 18761 : return ok;
296 : }
297 :
298 : bool
299 18666 : Probes::shutdown()
300 : {
301 18666 : bool ok = true;
302 : #ifdef MOZ_ETW
303 : if (!ETWShutdown())
304 : ok = false;
305 : #endif
306 :
307 18666 : Probes::removeAllJITWatchers(NULL);
308 :
309 18666 : return ok;
310 56001 : }
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 : if (!fun->atom)
329 : return Probes::anonymousName;
330 : return bytes->encode(cx, fun->atom) ? bytes->ptr() : Probes::nullName;
331 : }
332 :
333 : /*
334 : * These functions call the DTrace macros for the JavaScript USDT probes.
335 : * Originally this code was inlined in the JavaScript code; however since
336 : * a number of operations are called, these have been placed into functions
337 : * to reduce any negative compiler optimization effect that the addition of
338 : * a number of usually unused lines of code would cause.
339 : */
340 : void
341 : Probes::DTraceEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script)
342 : {
343 : JSAutoByteString funNameBytes;
344 : JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(script), Probes::nullName,
345 : FunctionName(cx, fun, &funNameBytes));
346 : }
347 :
348 : void
349 : Probes::DTraceExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script)
350 : {
351 : JSAutoByteString funNameBytes;
352 : JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(script), Probes::nullName,
353 : FunctionName(cx, fun, &funNameBytes));
354 : }
355 : #endif
356 :
357 : #ifdef MOZ_ETW
358 : static void
359 : current_location(JSContext *cx, int* lineno, char const **filename)
360 : {
361 : JSScript *script = js_GetCurrentScript(cx);
362 : if (! script) {
363 : *lineno = -1;
364 : *filename = "(uninitialized)";
365 : return;
366 : }
367 : *lineno = js_PCToLineNumber(cx, script, js_GetCurrentBytecodePC(cx));
368 : *filename = ScriptFilename(script);
369 : }
370 :
371 : /*
372 : * ETW (Event Tracing for Windows)
373 : *
374 : * These are here rather than in the .h file to avoid having to include
375 : * windows.h in a header.
376 : */
377 : bool
378 : Probes::ETWCallTrackingActive(JSContext *cx)
379 : {
380 : return MCGEN_ENABLE_CHECK(MozillaSpiderMonkey_Context, EvtFunctionEntry);
381 : }
382 :
383 : bool
384 : Probes::ETWCreateRuntime(JSRuntime *rt)
385 : {
386 : static bool registered = false;
387 : if (!registered) {
388 : EventRegisterMozillaSpiderMonkey();
389 : registered = true;
390 : }
391 : return true;
392 : }
393 :
394 : bool
395 : Probes::ETWDestroyRuntime(JSRuntime *rt)
396 : {
397 : return true;
398 : }
399 :
400 : bool
401 : Probes::ETWShutdown()
402 : {
403 : EventUnregisterMozillaSpiderMonkey();
404 : return true;
405 : }
406 :
407 : bool
408 : Probes::ETWEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter)
409 : {
410 : int lineno = script ? script->lineno : -1;
411 : JSAutoByteString bytes;
412 : return (EventWriteEvtFunctionEntry(ScriptFilename(script), lineno,
413 : ObjectClassname((JSObject *)fun),
414 : FunctionName(cx, fun, &bytes)) == ERROR_SUCCESS);
415 : }
416 :
417 : bool
418 : Probes::ETWExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter)
419 : {
420 : int lineno = script ? script->lineno : -1;
421 : JSAutoByteString bytes;
422 : return (EventWriteEvtFunctionExit(ScriptFilename(script), lineno,
423 : ObjectClassname((JSObject *)fun),
424 : FunctionName(cx, fun, &bytes)) == ERROR_SUCCESS);
425 : }
426 :
427 : bool
428 : Probes::ETWCreateObject(JSContext *cx, JSObject *obj)
429 : {
430 : int lineno;
431 : const char * script_filename;
432 : current_location(cx, &lineno, &script_filename);
433 :
434 : return EventWriteEvtObjectCreate(script_filename, lineno,
435 : ObjectClassname(obj), reinterpret_cast<uint64_t_t>(obj),
436 : obj ? obj->computedSizeOfIncludingThis() : 0) == ERROR_SUCCESS;
437 : }
438 :
439 : bool
440 : Probes::ETWFinalizeObject(JSObject *obj)
441 : {
442 : return EventWriteEvtObjectFinalize(ObjectClassname(obj),
443 : reinterpret_cast<uint64_t_t>(obj)) == ERROR_SUCCESS;
444 : }
445 :
446 : bool
447 : Probes::ETWResizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize)
448 : {
449 : int lineno;
450 : const char *script_filename;
451 : current_location(cx, &lineno, &script_filename);
452 :
453 : return EventWriteEvtObjectResize(script_filename, lineno,
454 : ObjectClassname(obj), reinterpret_cast<uint64_t_t>(obj),
455 : oldSize, newSize) == ERROR_SUCCESS;
456 : }
457 :
458 : bool
459 : Probes::ETWCreateString(JSContext *cx, JSString *string, size_t length)
460 : {
461 : int lineno;
462 : const char *script_filename;
463 : current_location(cx, &lineno, &script_filename);
464 :
465 : return EventWriteEvtStringCreate(script_filename, lineno,
466 : reinterpret_cast<uint64_t_t>(string), length) ==
467 : ERROR_SUCCESS;
468 : }
469 :
470 : bool
471 : Probes::ETWFinalizeString(JSString *string)
472 : {
473 : return EventWriteEvtStringFinalize(reinterpret_cast<uint64_t>(string),
474 : string->length()) == ERROR_SUCCESS;
475 : }
476 :
477 : bool
478 : Probes::ETWCompileScriptBegin(const char *filename, int lineno)
479 : {
480 : return EventWriteEvtScriptCompileBegin(filename, lineno) == ERROR_SUCCESS;
481 : }
482 :
483 : bool
484 : Probes::ETWCompileScriptEnd(const char *filename, int lineno)
485 : {
486 : return EventWriteEvtScriptCompileEnd(filename, lineno) == ERROR_SUCCESS;
487 : }
488 :
489 : bool
490 : Probes::ETWCalloutBegin(JSContext *cx, JSFunction *fun)
491 : {
492 : const char *script_filename;
493 : int lineno;
494 : JSAutoByteString bytes;
495 : current_location(cx, &lineno, &script_filename);
496 :
497 : return EventWriteEvtCalloutBegin(script_filename,
498 : lineno,
499 : ObjectClassname((JSObject *)fun),
500 : FunctionName(cx, fun, &bytes)) == ERROR_SUCCESS;
501 : }
502 :
503 : bool
504 : Probes::ETWCalloutEnd(JSContext *cx, JSFunction *fun)
505 : {
506 : const char *script_filename;
507 : int lineno;
508 : JSAutoByteString bytes;
509 : current_location(cx, &lineno, &script_filename);
510 :
511 : return EventWriteEvtCalloutEnd(script_filename,
512 : lineno,
513 : ObjectClassname((JSObject *)fun),
514 : FunctionName(cx, fun, &bytes)) == ERROR_SUCCESS;
515 : }
516 :
517 : bool
518 : Probes::ETWAcquireMemory(JSContext *cx, void *address, size_t nbytes)
519 : {
520 : return EventWriteEvtMemoryAcquire(reinterpret_cast<uint64_t>(cx->compartment),
521 : reinterpret_cast<uint64_t>(address),
522 : nbytes) == ERROR_SUCCESS;
523 : }
524 :
525 : bool
526 : Probes::ETWReleaseMemory(JSContext *cx, void *address, size_t nbytes)
527 : {
528 : return EventWriteEvtMemoryRelease(reinterpret_cast<uint64_t>(cx->compartment),
529 : reinterpret_cast<uint64_t>(address),
530 : nbytes) == ERROR_SUCCESS;
531 : }
532 :
533 : bool
534 : Probes::ETWGCStart(JSCompartment *compartment)
535 : {
536 : return EventWriteEvtGCStart(reinterpret_cast<uint64_t>(compartment)) == ERROR_SUCCESS;
537 : }
538 :
539 : bool
540 : Probes::ETWGCEnd(JSCompartment *compartment)
541 : {
542 : return EventWriteEvtGCEnd(reinterpret_cast<uint64_t>(compartment)) == ERROR_SUCCESS;
543 : }
544 :
545 : bool
546 : Probes::ETWGCStartMarkPhase(JSCompartment *compartment)
547 : {
548 : return EventWriteEvtGCStartMarkPhase(reinterpret_cast<uint64_t>(compartment)) == ERROR_SUCCESS;
549 : }
550 :
551 : bool
552 : Probes::ETWGCEndMarkPhase(JSCompartment *compartment)
553 : {
554 : return EventWriteEvtGCEndMarkPhase(reinterpret_cast<uint64_t>(compartment)) == ERROR_SUCCESS;
555 : }
556 :
557 : bool
558 : Probes::ETWGCStartSweepPhase(JSCompartment *compartment)
559 : {
560 : return EventWriteEvtGCStartSweepPhase(reinterpret_cast<uint64_t>(compartment)) ==
561 : ERROR_SUCCESS;
562 : }
563 :
564 : bool
565 : Probes::ETWGCEndSweepPhase(JSCompartment *compartment)
566 : {
567 : return EventWriteEvtGCEndSweepPhase(reinterpret_cast<uint64_t>(compartment)) == ERROR_SUCCESS;
568 : }
569 :
570 : bool
571 : Probes::ETWCustomMark(JSString *string)
572 : {
573 : const jschar *chars = string->getCharsZ(NULL);
574 : return !chars || EventWriteEvtCustomString(chars) == ERROR_SUCCESS;
575 : }
576 :
577 : bool
578 : Probes::ETWCustomMark(const char *string)
579 : {
580 : return EventWriteEvtCustomANSIString(string) == ERROR_SUCCESS;
581 : }
582 :
583 : bool
584 : Probes::ETWCustomMark(int marker)
585 : {
586 : return EventWriteEvtCustomInt(marker) == ERROR_SUCCESS;
587 : }
588 :
589 : bool
590 : Probes::ETWStartExecution(JSContext *cx, JSScript *script)
591 : {
592 : int lineno = script ? script->lineno : -1;
593 : return EventWriteEvtExecuteStart(ScriptFilename(script), lineno) == ERROR_SUCCESS;
594 : }
595 :
596 : bool
597 : Probes::ETWStopExecution(JSContext *cx, JSScript *script)
598 : {
599 : int lineno = script ? script->lineno : -1;
600 : return EventWriteEvtExecuteDone(ScriptFilename(script), lineno) == ERROR_SUCCESS;
601 : }
602 :
603 : bool
604 : Probes::ETWResizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize)
605 : {
606 : return EventWriteEvtHeapResize(reinterpret_cast<uint64_t>(compartment),
607 : oldSize, newSize) == ERROR_SUCCESS;
608 : }
609 :
610 : #endif
|