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