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