1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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 #include "jit/JSJitFrameIter-inl.h"
8
9 #include "jit/CalleeToken.h"
10 #include "jit/IonScript.h"
11 #include "jit/JitcodeMap.h"
12 #include "jit/JitFrames.h"
13 #include "jit/JitRuntime.h"
14 #include "jit/JitScript.h"
15 #include "jit/MacroAssembler.h" // js::jit::Assembler::GetPointer
16 #include "jit/SafepointIndex.h"
17 #include "jit/Safepoints.h"
18 #include "jit/ScriptFromCalleeToken.h"
19 #include "jit/VMFunctions.h"
20 #include "js/friend/DumpFunctions.h" // js::DumpObject, js::DumpValue
21
22 #include "vm/JSScript-inl.h"
23
24 using namespace js;
25 using namespace js::jit;
26
JSJitFrameIter(const JitActivation * activation)27 JSJitFrameIter::JSJitFrameIter(const JitActivation* activation)
28 : JSJitFrameIter(activation, FrameType::Exit, activation->jsExitFP()) {}
29
JSJitFrameIter(const JitActivation * activation,FrameType frameType,uint8_t * fp)30 JSJitFrameIter::JSJitFrameIter(const JitActivation* activation,
31 FrameType frameType, uint8_t* fp)
32 : current_(fp),
33 type_(frameType),
34 resumePCinCurrentFrame_(nullptr),
35 frameSize_(0),
36 cachedSafepointIndex_(nullptr),
37 activation_(activation) {
38 MOZ_ASSERT(type_ == FrameType::JSJitToWasm || type_ == FrameType::Exit);
39 if (activation_->bailoutData()) {
40 current_ = activation_->bailoutData()->fp();
41 frameSize_ = activation_->bailoutData()->topFrameSize();
42 type_ = FrameType::Bailout;
43 } else {
44 MOZ_ASSERT(!TlsContext.get()->inUnsafeCallWithABI);
45 }
46 }
47
checkInvalidation() const48 bool JSJitFrameIter::checkInvalidation() const {
49 IonScript* dummy;
50 return checkInvalidation(&dummy);
51 }
52
checkInvalidation(IonScript ** ionScriptOut) const53 bool JSJitFrameIter::checkInvalidation(IonScript** ionScriptOut) const {
54 JSScript* script = this->script();
55 if (isBailoutJS()) {
56 *ionScriptOut = activation_->bailoutData()->ionScript();
57 return !script->hasIonScript() || script->ionScript() != *ionScriptOut;
58 }
59
60 uint8_t* returnAddr = resumePCinCurrentFrame();
61 // N.B. the current IonScript is not the same as the frame's
62 // IonScript if the frame has since been invalidated.
63 bool invalidated = !script->hasIonScript() ||
64 !script->ionScript()->containsReturnAddress(returnAddr);
65 if (!invalidated) {
66 return false;
67 }
68
69 int32_t invalidationDataOffset = ((int32_t*)returnAddr)[-1];
70 uint8_t* ionScriptDataOffset = returnAddr + invalidationDataOffset;
71 IonScript* ionScript = (IonScript*)Assembler::GetPointer(ionScriptDataOffset);
72 MOZ_ASSERT(ionScript->containsReturnAddress(returnAddr));
73 *ionScriptOut = ionScript;
74 return true;
75 }
76
calleeToken() const77 CalleeToken JSJitFrameIter::calleeToken() const {
78 return ((JitFrameLayout*)current_)->calleeToken();
79 }
80
callee() const81 JSFunction* JSJitFrameIter::callee() const {
82 MOZ_ASSERT(isScripted());
83 MOZ_ASSERT(isFunctionFrame());
84 return CalleeTokenToFunction(calleeToken());
85 }
86
maybeCallee() const87 JSFunction* JSJitFrameIter::maybeCallee() const {
88 if (isScripted() && isFunctionFrame()) {
89 return callee();
90 }
91 return nullptr;
92 }
93
isBareExit() const94 bool JSJitFrameIter::isBareExit() const {
95 if (type_ != FrameType::Exit) {
96 return false;
97 }
98 return exitFrame()->isBareExit();
99 }
100
isFunctionFrame() const101 bool JSJitFrameIter::isFunctionFrame() const {
102 return CalleeTokenIsFunction(calleeToken());
103 }
104
script() const105 JSScript* JSJitFrameIter::script() const {
106 MOZ_ASSERT(isScripted());
107 if (isBaselineJS()) {
108 return baselineFrame()->script();
109 }
110 JSScript* script = ScriptFromCalleeToken(calleeToken());
111 MOZ_ASSERT(script);
112 return script;
113 }
114
maybeForwardedScript() const115 JSScript* JSJitFrameIter::maybeForwardedScript() const {
116 MOZ_ASSERT(isScripted());
117 if (isBaselineJS()) {
118 return MaybeForwardedScriptFromCalleeToken(baselineFrame()->calleeToken());
119 }
120 JSScript* script = MaybeForwardedScriptFromCalleeToken(calleeToken());
121 MOZ_ASSERT(script);
122 return script;
123 }
124
baselineScriptAndPc(JSScript ** scriptRes,jsbytecode ** pcRes) const125 void JSJitFrameIter::baselineScriptAndPc(JSScript** scriptRes,
126 jsbytecode** pcRes) const {
127 MOZ_ASSERT(isBaselineJS());
128 JSScript* script = this->script();
129 if (scriptRes) {
130 *scriptRes = script;
131 }
132
133 MOZ_ASSERT(pcRes);
134
135 // The Baseline Interpreter stores the bytecode pc in the frame.
136 if (baselineFrame()->runningInInterpreter()) {
137 MOZ_ASSERT(baselineFrame()->interpreterScript() == script);
138 *pcRes = baselineFrame()->interpreterPC();
139 return;
140 }
141
142 // There must be a BaselineScript with a RetAddrEntry for the current return
143 // address.
144 uint8_t* retAddr = resumePCinCurrentFrame();
145 const RetAddrEntry& entry =
146 script->baselineScript()->retAddrEntryFromReturnAddress(retAddr);
147 *pcRes = entry.pc(script);
148 }
149
actualArgs() const150 Value* JSJitFrameIter::actualArgs() const { return jsFrame()->argv() + 1; }
151
prevFp() const152 uint8_t* JSJitFrameIter::prevFp() const {
153 return current_ + current()->prevFrameLocalSize() + current()->headerSize();
154 }
155
operator ++()156 void JSJitFrameIter::operator++() {
157 MOZ_ASSERT(!isEntry());
158
159 // Compute BaselineFrame size, the size stored in the descriptor excluding
160 // VMFunction arguments pushed for VM calls.
161 //
162 // In debug builds this is equivalent to BaselineFrame::debugFrameSize_. This
163 // is asserted at the end of this method.
164 if (current()->prevType() == FrameType::BaselineJS) {
165 uint32_t frameSize = prevFrameLocalSize();
166 if (isExitFrame() && exitFrame()->isWrapperExit()) {
167 const VMFunctionData* data = exitFrame()->footer()->function();
168 frameSize -= data->explicitStackSlots() * sizeof(void*);
169 }
170 baselineFrameSize_ = mozilla::Some(frameSize);
171 } else {
172 baselineFrameSize_ = mozilla::Nothing();
173 }
174
175 frameSize_ = prevFrameLocalSize();
176 cachedSafepointIndex_ = nullptr;
177
178 // If the next frame is the entry frame, just exit. Don't update current_,
179 // since the entry and first frames overlap.
180 if (isEntry(current()->prevType())) {
181 type_ = current()->prevType();
182 return;
183 }
184
185 type_ = current()->prevType();
186 resumePCinCurrentFrame_ = current()->returnAddress();
187 current_ = prevFp();
188
189 MOZ_ASSERT_IF(isBaselineJS(),
190 baselineFrame()->debugFrameSize() == *baselineFrameSize_);
191 }
192
spillBase() const193 uintptr_t* JSJitFrameIter::spillBase() const {
194 MOZ_ASSERT(isIonJS());
195
196 // Get the base address to where safepoint registers are spilled.
197 // Out-of-line calls do not unwind the extra padding space used to
198 // aggregate bailout tables, so we use frameSize instead of frameLocals,
199 // which would only account for local stack slots.
200 return reinterpret_cast<uintptr_t*>(fp() - ionScript()->frameSize());
201 }
202
machineState() const203 MachineState JSJitFrameIter::machineState() const {
204 MOZ_ASSERT(isIonScripted());
205
206 // The MachineState is used by GCs for tracing call-sites.
207 if (MOZ_UNLIKELY(isBailoutJS())) {
208 return *activation_->bailoutData()->machineState();
209 }
210
211 SafepointReader reader(ionScript(), safepoint());
212 uintptr_t* spill = spillBase();
213 MachineState machine;
214
215 for (GeneralRegisterBackwardIterator iter(reader.allGprSpills()); iter.more();
216 ++iter) {
217 machine.setRegisterLocation(*iter, --spill);
218 }
219
220 uint8_t* spillAlign = alignDoubleSpill(reinterpret_cast<uint8_t*>(spill));
221
222 char* floatSpill = reinterpret_cast<char*>(spillAlign);
223 FloatRegisterSet fregs = reader.allFloatSpills().set();
224 fregs = fregs.reduceSetForPush();
225 for (FloatRegisterBackwardIterator iter(fregs); iter.more(); ++iter) {
226 floatSpill -= (*iter).size();
227 for (uint32_t a = 0; a < (*iter).numAlignedAliased(); a++) {
228 // Only say that registers that actually start here start here.
229 // e.g. d0 should not start at s1, only at s0.
230 FloatRegister ftmp = (*iter).alignedAliased(a);
231 machine.setRegisterLocation(ftmp, (double*)floatSpill);
232 }
233 }
234
235 return machine;
236 }
237
jsFrame() const238 JitFrameLayout* JSJitFrameIter::jsFrame() const {
239 MOZ_ASSERT(isScripted());
240 if (isBailoutJS()) {
241 return (JitFrameLayout*)activation_->bailoutData()->fp();
242 }
243
244 return (JitFrameLayout*)fp();
245 }
246
ionScript() const247 IonScript* JSJitFrameIter::ionScript() const {
248 MOZ_ASSERT(isIonScripted());
249 if (isBailoutJS()) {
250 return activation_->bailoutData()->ionScript();
251 }
252
253 IonScript* ionScript = nullptr;
254 if (checkInvalidation(&ionScript)) {
255 return ionScript;
256 }
257 return ionScriptFromCalleeToken();
258 }
259
ionScriptFromCalleeToken() const260 IonScript* JSJitFrameIter::ionScriptFromCalleeToken() const {
261 MOZ_ASSERT(isIonJS());
262 MOZ_ASSERT(!checkInvalidation());
263 return script()->ionScript();
264 }
265
safepoint() const266 const SafepointIndex* JSJitFrameIter::safepoint() const {
267 MOZ_ASSERT(isIonJS());
268 if (!cachedSafepointIndex_) {
269 cachedSafepointIndex_ =
270 ionScript()->getSafepointIndex(resumePCinCurrentFrame());
271 }
272 return cachedSafepointIndex_;
273 }
274
snapshotOffset() const275 SnapshotOffset JSJitFrameIter::snapshotOffset() const {
276 MOZ_ASSERT(isIonScripted());
277 if (isBailoutJS()) {
278 return activation_->bailoutData()->snapshotOffset();
279 }
280 return osiIndex()->snapshotOffset();
281 }
282
osiIndex() const283 const OsiIndex* JSJitFrameIter::osiIndex() const {
284 MOZ_ASSERT(isIonJS());
285 SafepointReader reader(ionScript(), safepoint());
286 return ionScript()->getOsiIndex(reader.osiReturnPointOffset());
287 }
288
isConstructing() const289 bool JSJitFrameIter::isConstructing() const {
290 return CalleeTokenIsConstructing(calleeToken());
291 }
292
numActualArgs() const293 unsigned JSJitFrameIter::numActualArgs() const {
294 if (isScripted()) {
295 return jsFrame()->numActualArgs();
296 }
297
298 MOZ_ASSERT(isExitFrameLayout<NativeExitFrameLayout>());
299 return exitFrame()->as<NativeExitFrameLayout>()->argc();
300 }
301
dumpBaseline() const302 void JSJitFrameIter::dumpBaseline() const {
303 MOZ_ASSERT(isBaselineJS());
304
305 fprintf(stderr, " JS Baseline frame\n");
306 if (isFunctionFrame()) {
307 fprintf(stderr, " callee fun: ");
308 #if defined(DEBUG) || defined(JS_JITSPEW)
309 DumpObject(callee());
310 #else
311 fprintf(stderr, "?\n");
312 #endif
313 } else {
314 fprintf(stderr, " global frame, no callee\n");
315 }
316
317 fprintf(stderr, " file %s line %u\n", script()->filename(),
318 script()->lineno());
319
320 JSContext* cx = TlsContext.get();
321 RootedScript script(cx);
322 jsbytecode* pc;
323 baselineScriptAndPc(script.address(), &pc);
324
325 fprintf(stderr, " script = %p, pc = %p (offset %u)\n", (void*)script, pc,
326 uint32_t(script->pcToOffset(pc)));
327 fprintf(stderr, " current op: %s\n", CodeName(JSOp(*pc)));
328
329 fprintf(stderr, " actual args: %u\n", numActualArgs());
330
331 for (unsigned i = 0; i < baselineFrameNumValueSlots(); i++) {
332 fprintf(stderr, " slot %u: ", i);
333 #if defined(DEBUG) || defined(JS_JITSPEW)
334 Value* v = baselineFrame()->valueSlot(i);
335 DumpValue(*v);
336 #else
337 fprintf(stderr, "?\n");
338 #endif
339 }
340 }
341
dump() const342 void JSJitFrameIter::dump() const {
343 switch (type_) {
344 case FrameType::CppToJSJit:
345 fprintf(stderr, " Entry frame\n");
346 fprintf(stderr, " Frame size: %u\n",
347 unsigned(current()->prevFrameLocalSize()));
348 break;
349 case FrameType::BaselineJS:
350 dumpBaseline();
351 break;
352 case FrameType::BaselineStub:
353 fprintf(stderr, " Baseline stub frame\n");
354 fprintf(stderr, " Frame size: %u\n",
355 unsigned(current()->prevFrameLocalSize()));
356 break;
357 case FrameType::Bailout:
358 case FrameType::IonJS: {
359 InlineFrameIterator frames(TlsContext.get(), this);
360 for (;;) {
361 frames.dump();
362 if (!frames.more()) {
363 break;
364 }
365 ++frames;
366 }
367 break;
368 }
369 case FrameType::Rectifier:
370 fprintf(stderr, " Rectifier frame\n");
371 fprintf(stderr, " Frame size: %u\n",
372 unsigned(current()->prevFrameLocalSize()));
373 break;
374 case FrameType::IonICCall:
375 fprintf(stderr, " Ion IC call\n");
376 fprintf(stderr, " Frame size: %u\n",
377 unsigned(current()->prevFrameLocalSize()));
378 break;
379 case FrameType::WasmToJSJit:
380 fprintf(stderr, " Fast wasm-to-JS entry frame\n");
381 fprintf(stderr, " Frame size: %u\n",
382 unsigned(current()->prevFrameLocalSize()));
383 break;
384 case FrameType::Exit:
385 fprintf(stderr, " Exit frame\n");
386 break;
387 case FrameType::JSJitToWasm:
388 fprintf(stderr, " Wasm exit frame\n");
389 break;
390 };
391 fputc('\n', stderr);
392 }
393
394 #ifdef DEBUG
verifyReturnAddressUsingNativeToBytecodeMap()395 bool JSJitFrameIter::verifyReturnAddressUsingNativeToBytecodeMap() {
396 MOZ_ASSERT(resumePCinCurrentFrame_ != nullptr);
397
398 // Only handle Ion frames for now.
399 if (type_ != FrameType::IonJS && type_ != FrameType::BaselineJS) {
400 return true;
401 }
402
403 JSRuntime* rt = TlsContext.get()->runtime();
404
405 // Don't verify while off thread.
406 if (!CurrentThreadCanAccessRuntime(rt)) {
407 return true;
408 }
409
410 // Don't verify if sampling is being suppressed.
411 if (!TlsContext.get()->isProfilerSamplingEnabled()) {
412 return true;
413 }
414
415 if (JS::RuntimeHeapIsMinorCollecting()) {
416 return true;
417 }
418
419 JitRuntime* jitrt = rt->jitRuntime();
420
421 // Look up and print bytecode info for the native address.
422 const JitcodeGlobalEntry* entry =
423 jitrt->getJitcodeGlobalTable()->lookup(resumePCinCurrentFrame_);
424 if (!entry) {
425 return true;
426 }
427
428 JitSpew(JitSpew_Profiling, "Found nativeToBytecode entry for %p: %p - %p",
429 resumePCinCurrentFrame_, entry->nativeStartAddr(),
430 entry->nativeEndAddr());
431
432 JitcodeGlobalEntry::BytecodeLocationVector location;
433 uint32_t depth = UINT32_MAX;
434 if (!entry->callStackAtAddr(rt, resumePCinCurrentFrame_, location, &depth)) {
435 return false;
436 }
437 MOZ_ASSERT(depth > 0 && depth != UINT32_MAX);
438 MOZ_ASSERT(location.length() == depth);
439
440 JitSpew(JitSpew_Profiling, "Found bytecode location of depth %u:", depth);
441 for (size_t i = 0; i < location.length(); i++) {
442 JitSpew(JitSpew_Profiling, " %s:%u - %zu",
443 location[i].getDebugOnlyScript()->filename(),
444 location[i].getDebugOnlyScript()->lineno(),
445 size_t(location[i].toRawBytecode() -
446 location[i].getDebugOnlyScript()->code()));
447 }
448
449 if (type_ == FrameType::IonJS) {
450 // Create an InlineFrameIterator here and verify the mapped info against the
451 // iterator info.
452 InlineFrameIterator inlineFrames(TlsContext.get(), this);
453 for (size_t idx = 0; idx < location.length(); idx++) {
454 MOZ_ASSERT(idx < location.length());
455 MOZ_ASSERT_IF(idx < location.length() - 1, inlineFrames.more());
456
457 JitSpew(JitSpew_Profiling, "Match %d: ION %s:%u(%zu) vs N2B %s:%u(%zu)",
458 (int)idx, inlineFrames.script()->filename(),
459 inlineFrames.script()->lineno(),
460 size_t(inlineFrames.pc() - inlineFrames.script()->code()),
461 location[idx].getDebugOnlyScript()->filename(),
462 location[idx].getDebugOnlyScript()->lineno(),
463 size_t(location[idx].toRawBytecode() -
464 location[idx].getDebugOnlyScript()->code()));
465
466 MOZ_ASSERT(inlineFrames.script() == location[idx].getDebugOnlyScript());
467
468 if (inlineFrames.more()) {
469 ++inlineFrames;
470 }
471 }
472 }
473
474 return true;
475 }
476 #endif // DEBUG
477
JSJitProfilingFrameIterator(JSContext * cx,void * pc)478 JSJitProfilingFrameIterator::JSJitProfilingFrameIterator(JSContext* cx,
479 void* pc) {
480 // If no profilingActivation is live, initialize directly to
481 // end-of-iteration state.
482 if (!cx->profilingActivation()) {
483 type_ = FrameType::CppToJSJit;
484 fp_ = nullptr;
485 resumePCinCurrentFrame_ = nullptr;
486 return;
487 }
488
489 MOZ_ASSERT(cx->profilingActivation()->isJit());
490
491 JitActivation* act = cx->profilingActivation()->asJit();
492
493 // If the top JitActivation has a null lastProfilingFrame, assume that
494 // it's a trivially empty activation, and initialize directly
495 // to end-of-iteration state.
496 if (!act->lastProfilingFrame()) {
497 type_ = FrameType::CppToJSJit;
498 fp_ = nullptr;
499 resumePCinCurrentFrame_ = nullptr;
500 return;
501 }
502
503 // Get the fp from the current profilingActivation
504 fp_ = (uint8_t*)act->lastProfilingFrame();
505
506 // Profiler sampling must NOT be suppressed if we are here.
507 MOZ_ASSERT(cx->isProfilerSamplingEnabled());
508
509 // Try initializing with sampler pc
510 if (tryInitWithPC(pc)) {
511 return;
512 }
513
514 // Try initializing with sampler pc using native=>bytecode table.
515 JitcodeGlobalTable* table =
516 cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
517 if (tryInitWithTable(table, pc, /* forLastCallSite = */ false)) {
518 return;
519 }
520
521 // Try initializing with lastProfilingCallSite pc
522 void* lastCallSite = act->lastProfilingCallSite();
523 if (lastCallSite) {
524 if (tryInitWithPC(lastCallSite)) {
525 return;
526 }
527
528 // Try initializing with lastProfilingCallSite pc using native=>bytecode
529 // table.
530 if (tryInitWithTable(table, lastCallSite, /* forLastCallSite = */ true)) {
531 return;
532 }
533 }
534
535 // If nothing matches, for now just assume we are at the start of the last
536 // frame's baseline jit code or interpreter code.
537 type_ = FrameType::BaselineJS;
538 if (frameScript()->hasBaselineScript()) {
539 resumePCinCurrentFrame_ = frameScript()->baselineScript()->method()->raw();
540 } else {
541 MOZ_ASSERT(IsBaselineInterpreterEnabled());
542 resumePCinCurrentFrame_ =
543 cx->runtime()->jitRuntime()->baselineInterpreter().codeRaw();
544 }
545 }
546
547 template <typename ReturnType = CommonFrameLayout*>
GetPreviousRawFrame(CommonFrameLayout * frame)548 static inline ReturnType GetPreviousRawFrame(CommonFrameLayout* frame) {
549 size_t prevSize = frame->prevFrameLocalSize() + frame->headerSize();
550 return ReturnType((uint8_t*)frame + prevSize);
551 }
552
JSJitProfilingFrameIterator(CommonFrameLayout * fp)553 JSJitProfilingFrameIterator::JSJitProfilingFrameIterator(
554 CommonFrameLayout* fp) {
555 moveToNextFrame(fp);
556 }
557
tryInitWithPC(void * pc)558 bool JSJitProfilingFrameIterator::tryInitWithPC(void* pc) {
559 JSScript* callee = frameScript();
560
561 // Check for Ion first, since it's more likely for hot code.
562 if (callee->hasIonScript() &&
563 callee->ionScript()->method()->containsNativePC(pc)) {
564 type_ = FrameType::IonJS;
565 resumePCinCurrentFrame_ = pc;
566 return true;
567 }
568
569 // Check for containment in Baseline jitcode second.
570 if (callee->hasBaselineScript() &&
571 callee->baselineScript()->method()->containsNativePC(pc)) {
572 type_ = FrameType::BaselineJS;
573 resumePCinCurrentFrame_ = pc;
574 return true;
575 }
576
577 return false;
578 }
579
tryInitWithTable(JitcodeGlobalTable * table,void * pc,bool forLastCallSite)580 bool JSJitProfilingFrameIterator::tryInitWithTable(JitcodeGlobalTable* table,
581 void* pc,
582 bool forLastCallSite) {
583 if (!pc) {
584 return false;
585 }
586
587 const JitcodeGlobalEntry* entry = table->lookup(pc);
588 if (!entry) {
589 return false;
590 }
591
592 JSScript* callee = frameScript();
593
594 MOZ_ASSERT(entry->isIon() || entry->isBaseline() ||
595 entry->isBaselineInterpreter() || entry->isDummy());
596
597 // Treat dummy lookups as an empty frame sequence.
598 if (entry->isDummy()) {
599 type_ = FrameType::CppToJSJit;
600 fp_ = nullptr;
601 resumePCinCurrentFrame_ = nullptr;
602 return true;
603 }
604
605 if (entry->isIon()) {
606 // If looked-up callee doesn't match frame callee, don't accept
607 // lastProfilingCallSite
608 if (entry->ionEntry().getScript(0) != callee) {
609 return false;
610 }
611
612 type_ = FrameType::IonJS;
613 resumePCinCurrentFrame_ = pc;
614 return true;
615 }
616
617 if (entry->isBaseline()) {
618 // If looked-up callee doesn't match frame callee, don't accept
619 // lastProfilingCallSite
620 if (forLastCallSite && entry->baselineEntry().script() != callee) {
621 return false;
622 }
623
624 type_ = FrameType::BaselineJS;
625 resumePCinCurrentFrame_ = pc;
626 return true;
627 }
628
629 if (entry->isBaselineInterpreter()) {
630 type_ = FrameType::BaselineJS;
631 resumePCinCurrentFrame_ = pc;
632 return true;
633 }
634
635 return false;
636 }
637
baselineInterpreterLabel() const638 const char* JSJitProfilingFrameIterator::baselineInterpreterLabel() const {
639 MOZ_ASSERT(type_ == FrameType::BaselineJS);
640 return frameScript()->jitScript()->profileString();
641 }
642
baselineInterpreterScriptPC(JSScript ** script,jsbytecode ** pc,uint64_t * realmID) const643 void JSJitProfilingFrameIterator::baselineInterpreterScriptPC(
644 JSScript** script, jsbytecode** pc, uint64_t* realmID) const {
645 MOZ_ASSERT(type_ == FrameType::BaselineJS);
646 BaselineFrame* blFrame =
647 (BaselineFrame*)(fp_ - BaselineFrame::FramePointerOffset -
648 BaselineFrame::Size());
649 *script = frameScript();
650 *pc = (*script)->code();
651
652 if (blFrame->runningInInterpreter() &&
653 blFrame->interpreterScript() == *script) {
654 jsbytecode* interpPC = blFrame->interpreterPC();
655 if ((*script)->containsPC(interpPC)) {
656 *pc = interpPC;
657 }
658
659 *realmID = (*script)->realm()->creationOptions().profilerRealmID();
660 }
661 }
662
operator ++()663 void JSJitProfilingFrameIterator::operator++() {
664 JitFrameLayout* frame = framePtr();
665 moveToNextFrame(frame);
666 }
667
moveToWasmFrame(CommonFrameLayout * frame)668 void JSJitProfilingFrameIterator::moveToWasmFrame(CommonFrameLayout* frame) {
669 // No previous js jit frame, this is a transition frame, used to
670 // pass a wasm iterator the correct value of FP.
671 resumePCinCurrentFrame_ = nullptr;
672 fp_ = GetPreviousRawFrame<uint8_t*>(frame);
673 type_ = FrameType::WasmToJSJit;
674 MOZ_ASSERT(!done());
675 }
676
moveToCppEntryFrame()677 void JSJitProfilingFrameIterator::moveToCppEntryFrame() {
678 // No previous frame, set to nullptr to indicate that
679 // JSJitProfilingFrameIterator is done().
680 resumePCinCurrentFrame_ = nullptr;
681 fp_ = nullptr;
682 type_ = FrameType::CppToJSJit;
683 }
684
moveToNextFrame(CommonFrameLayout * frame)685 void JSJitProfilingFrameIterator::moveToNextFrame(CommonFrameLayout* frame) {
686 /*
687 * fp_ points to a Baseline or Ion frame. The possible call-stacks
688 * patterns occurring between this frame and a previous Ion or Baseline
689 * frame are as follows:
690 *
691 * <Baseline-Or-Ion>
692 * ^
693 * |
694 * ^--- Ion
695 * |
696 * ^--- Baseline Stub <---- Baseline
697 * |
698 * ^--- WasmToJSJit <---- (other wasm frames, not handled by this iterator)
699 * |
700 * ^--- Argument Rectifier
701 * | ^
702 * | |
703 * | ^--- Ion
704 * | |
705 * | ^--- Baseline Stub <---- Baseline
706 * | |
707 * | ^--- WasmToJSJit <--- (other wasm frames)
708 * | |
709 * | ^--- CppToJSJit
710 * |
711 * ^--- Entry Frame (From C++)
712 * Exit Frame (From previous JitActivation)
713 * ^
714 * |
715 * ^--- Ion
716 * |
717 * ^--- Baseline
718 * |
719 * ^--- Baseline Stub <---- Baseline
720 */
721 FrameType prevType = frame->prevType();
722
723 if (prevType == FrameType::IonJS) {
724 resumePCinCurrentFrame_ = frame->returnAddress();
725 fp_ = GetPreviousRawFrame<uint8_t*>(frame);
726 type_ = FrameType::IonJS;
727 return;
728 }
729
730 if (prevType == FrameType::BaselineJS) {
731 resumePCinCurrentFrame_ = frame->returnAddress();
732 fp_ = GetPreviousRawFrame<uint8_t*>(frame);
733 type_ = FrameType::BaselineJS;
734 return;
735 }
736
737 if (prevType == FrameType::BaselineStub) {
738 BaselineStubFrameLayout* stubFrame =
739 GetPreviousRawFrame<BaselineStubFrameLayout*>(frame);
740 MOZ_ASSERT(stubFrame->prevType() == FrameType::BaselineJS);
741
742 resumePCinCurrentFrame_ = stubFrame->returnAddress();
743 fp_ = ((uint8_t*)stubFrame->reverseSavedFramePtr()) +
744 jit::BaselineFrame::FramePointerOffset;
745 type_ = FrameType::BaselineJS;
746 return;
747 }
748
749 if (prevType == FrameType::Rectifier) {
750 RectifierFrameLayout* rectFrame =
751 GetPreviousRawFrame<RectifierFrameLayout*>(frame);
752 FrameType rectPrevType = rectFrame->prevType();
753
754 if (rectPrevType == FrameType::IonJS) {
755 resumePCinCurrentFrame_ = rectFrame->returnAddress();
756 fp_ = GetPreviousRawFrame<uint8_t*>(rectFrame);
757 type_ = FrameType::IonJS;
758 return;
759 }
760
761 if (rectPrevType == FrameType::BaselineStub) {
762 BaselineStubFrameLayout* stubFrame =
763 GetPreviousRawFrame<BaselineStubFrameLayout*>(rectFrame);
764 resumePCinCurrentFrame_ = stubFrame->returnAddress();
765 fp_ = ((uint8_t*)stubFrame->reverseSavedFramePtr()) +
766 jit::BaselineFrame::FramePointerOffset;
767 type_ = FrameType::BaselineJS;
768 return;
769 }
770
771 if (rectPrevType == FrameType::WasmToJSJit) {
772 moveToWasmFrame(rectFrame);
773 return;
774 }
775
776 if (rectPrevType == FrameType::CppToJSJit) {
777 moveToCppEntryFrame();
778 return;
779 }
780
781 MOZ_CRASH("Bad frame type prior to rectifier frame.");
782 }
783
784 if (prevType == FrameType::IonICCall) {
785 IonICCallFrameLayout* callFrame =
786 GetPreviousRawFrame<IonICCallFrameLayout*>(frame);
787
788 MOZ_ASSERT(callFrame->prevType() == FrameType::IonJS);
789
790 resumePCinCurrentFrame_ = callFrame->returnAddress();
791 fp_ = GetPreviousRawFrame<uint8_t*>(callFrame);
792 type_ = FrameType::IonJS;
793 return;
794 }
795
796 if (prevType == FrameType::WasmToJSJit) {
797 moveToWasmFrame(frame);
798 return;
799 }
800
801 if (prevType == FrameType::CppToJSJit) {
802 moveToCppEntryFrame();
803 return;
804 }
805
806 MOZ_CRASH("Bad frame type.");
807 }
808