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