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 "vm/Stack-inl.h"
8
9 #include "mozilla/ArrayUtils.h" // mozilla::ArrayLength
10 #include "mozilla/Maybe.h" // mozilla::Maybe
11
12 #include <algorithm> // std::max
13 #include <stddef.h> // size_t
14 #include <stdint.h> // uint8_t, uint32_t
15 #include <utility> // std::move
16
17 #include "debugger/DebugAPI.h"
18 #include "gc/Marking.h"
19 #include "gc/Tracer.h" // js::TraceRoot
20 #include "jit/JitcodeMap.h"
21 #include "js/Value.h" // JS::Value
22 #include "vm/FrameIter.h" // js::FrameIter
23 #include "vm/JSContext.h"
24 #include "vm/Opcodes.h"
25 #include "wasm/WasmInstance.h"
26
27 #include "jit/JSJitFrameIter-inl.h"
28 #include "vm/Compartment-inl.h"
29 #include "vm/EnvironmentObject-inl.h"
30 #include "vm/Interpreter-inl.h"
31 #include "vm/Probes-inl.h"
32
33 using namespace js;
34
35 using mozilla::ArrayLength;
36 using mozilla::Maybe;
37
38 using JS::Value;
39
40 /*****************************************************************************/
41
initExecuteFrame(JSContext * cx,HandleScript script,AbstractFramePtr evalInFramePrev,HandleValue newTargetValue,HandleObject envChain)42 void InterpreterFrame::initExecuteFrame(JSContext* cx, HandleScript script,
43 AbstractFramePtr evalInFramePrev,
44 HandleValue newTargetValue,
45 HandleObject envChain) {
46 flags_ = 0;
47 script_ = script;
48
49 Value* dstvp = (Value*)this - 1;
50 dstvp[0] = newTargetValue;
51
52 envChain_ = envChain.get();
53 prev_ = nullptr;
54 prevpc_ = nullptr;
55 prevsp_ = nullptr;
56
57 evalInFramePrev_ = evalInFramePrev;
58 MOZ_ASSERT_IF(evalInFramePrev, isDebuggerEvalFrame());
59
60 if (script->isDebuggee()) {
61 setIsDebuggee();
62 }
63
64 #ifdef DEBUG
65 Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
66 #endif
67 }
68
createRestParameter(JSContext * cx)69 ArrayObject* InterpreterFrame::createRestParameter(JSContext* cx) {
70 MOZ_ASSERT(script()->hasRest());
71 unsigned nformal = callee().nargs() - 1, nactual = numActualArgs();
72 unsigned nrest = (nactual > nformal) ? nactual - nformal : 0;
73 Value* restvp = argv() + nformal;
74 return ObjectGroup::newArrayObject(cx, restvp, nrest, GenericObject,
75 ObjectGroup::NewArrayKind::UnknownIndex);
76 }
77
AssertScopeMatchesEnvironment(Scope * scope,JSObject * originalEnv)78 static inline void AssertScopeMatchesEnvironment(Scope* scope,
79 JSObject* originalEnv) {
80 #ifdef DEBUG
81 JSObject* env = originalEnv;
82 for (ScopeIter si(scope); si; si++) {
83 if (si.kind() == ScopeKind::NonSyntactic) {
84 while (env->is<WithEnvironmentObject>() ||
85 env->is<NonSyntacticVariablesObject>() ||
86 (env->is<LexicalEnvironmentObject>() &&
87 !env->as<LexicalEnvironmentObject>().isSyntactic())) {
88 MOZ_ASSERT(!IsSyntacticEnvironment(env));
89 env = &env->as<EnvironmentObject>().enclosingEnvironment();
90 }
91 } else if (si.hasSyntacticEnvironment()) {
92 switch (si.kind()) {
93 case ScopeKind::Function:
94 MOZ_ASSERT(env->as<CallObject>()
95 .callee()
96 .maybeCanonicalFunction()
97 ->nonLazyScript() ==
98 si.scope()->as<FunctionScope>().script());
99 env = &env->as<CallObject>().enclosingEnvironment();
100 break;
101
102 case ScopeKind::FunctionBodyVar:
103 MOZ_ASSERT(&env->as<VarEnvironmentObject>().scope() == si.scope());
104 env = &env->as<VarEnvironmentObject>().enclosingEnvironment();
105 break;
106
107 case ScopeKind::Lexical:
108 case ScopeKind::SimpleCatch:
109 case ScopeKind::Catch:
110 case ScopeKind::NamedLambda:
111 case ScopeKind::StrictNamedLambda:
112 case ScopeKind::FunctionLexical:
113 MOZ_ASSERT(&env->as<LexicalEnvironmentObject>().scope() ==
114 si.scope());
115 env = &env->as<LexicalEnvironmentObject>().enclosingEnvironment();
116 break;
117
118 case ScopeKind::With:
119 MOZ_ASSERT(&env->as<WithEnvironmentObject>().scope() == si.scope());
120 env = &env->as<WithEnvironmentObject>().enclosingEnvironment();
121 break;
122
123 case ScopeKind::Eval:
124 case ScopeKind::StrictEval:
125 env = &env->as<VarEnvironmentObject>().enclosingEnvironment();
126 break;
127
128 case ScopeKind::Global:
129 MOZ_ASSERT(env->as<LexicalEnvironmentObject>().isGlobal());
130 env = &env->as<LexicalEnvironmentObject>().enclosingEnvironment();
131 MOZ_ASSERT(env->is<GlobalObject>());
132 break;
133
134 case ScopeKind::NonSyntactic:
135 MOZ_CRASH("NonSyntactic should not have a syntactic environment");
136 break;
137
138 case ScopeKind::Module:
139 MOZ_ASSERT(&env->as<ModuleEnvironmentObject>().module() ==
140 si.scope()->as<ModuleScope>().module());
141 env = &env->as<ModuleEnvironmentObject>().enclosingEnvironment();
142 break;
143
144 case ScopeKind::WasmInstance:
145 env =
146 &env->as<WasmInstanceEnvironmentObject>().enclosingEnvironment();
147 break;
148
149 case ScopeKind::WasmFunction:
150 env = &env->as<WasmFunctionCallObject>().enclosingEnvironment();
151 break;
152 }
153 }
154 }
155
156 // In the case of a non-syntactic env chain, the immediate parent of the
157 // outermost non-syntactic env may be the global lexical env, or, if
158 // called from Debugger, a DebugEnvironmentProxy.
159 //
160 // In the case of a syntactic env chain, the outermost env is always a
161 // GlobalObject.
162 MOZ_ASSERT(env->is<GlobalObject>() || IsGlobalLexicalEnvironment(env) ||
163 env->is<DebugEnvironmentProxy>());
164 #endif
165 }
166
AssertScopeMatchesEnvironment(InterpreterFrame * fp,jsbytecode * pc)167 static inline void AssertScopeMatchesEnvironment(InterpreterFrame* fp,
168 jsbytecode* pc) {
169 #ifdef DEBUG
170 // If we OOMed before fully initializing the environment chain, the scope
171 // and environment will definitely mismatch.
172 if (fp->script()->initialEnvironmentShape() && fp->hasInitialEnvironment()) {
173 AssertScopeMatchesEnvironment(fp->script()->innermostScope(pc),
174 fp->environmentChain());
175 }
176 #endif
177 }
178
initFunctionEnvironmentObjects(JSContext * cx)179 bool InterpreterFrame::initFunctionEnvironmentObjects(JSContext* cx) {
180 return js::InitFunctionEnvironmentObjects(cx, this);
181 }
182
prologue(JSContext * cx)183 bool InterpreterFrame::prologue(JSContext* cx) {
184 RootedScript script(cx, this->script());
185
186 MOZ_ASSERT(cx->interpreterRegs().pc == script->code());
187 MOZ_ASSERT(cx->realm() == script->realm());
188
189 if (!isFunctionFrame()) {
190 return probes::EnterScript(cx, script, nullptr, this);
191 }
192
193 // At this point, we've yet to push any environments. Check that they
194 // match the enclosing scope.
195 AssertScopeMatchesEnvironment(script->enclosingScope(), environmentChain());
196
197 if (callee().needsFunctionEnvironmentObjects() &&
198 !initFunctionEnvironmentObjects(cx)) {
199 return false;
200 }
201
202 MOZ_ASSERT_IF(isConstructing(),
203 thisArgument().isObject() ||
204 thisArgument().isMagic(JS_UNINITIALIZED_LEXICAL));
205
206 return probes::EnterScript(cx, script, script->function(), this);
207 }
208
epilogue(JSContext * cx,jsbytecode * pc)209 void InterpreterFrame::epilogue(JSContext* cx, jsbytecode* pc) {
210 RootedScript script(cx, this->script());
211 MOZ_ASSERT(cx->realm() == script->realm());
212 probes::ExitScript(cx, script, script->function(),
213 hasPushedGeckoProfilerFrame());
214
215 // Check that the scope matches the environment at the point of leaving
216 // the frame.
217 AssertScopeMatchesEnvironment(this, pc);
218
219 EnvironmentIter ei(cx, this, pc);
220 UnwindAllEnvironmentsInFrame(cx, ei);
221
222 if (isFunctionFrame()) {
223 if (!callee().isGenerator() && !callee().isAsync() && isConstructing() &&
224 thisArgument().isObject() && returnValue().isPrimitive()) {
225 setReturnValue(thisArgument());
226 }
227
228 return;
229 }
230
231 MOZ_ASSERT(isEvalFrame() || isGlobalFrame() || isModuleFrame());
232 }
233
checkReturn(JSContext * cx,HandleValue thisv)234 bool InterpreterFrame::checkReturn(JSContext* cx, HandleValue thisv) {
235 MOZ_ASSERT(script()->isDerivedClassConstructor());
236 MOZ_ASSERT(isFunctionFrame());
237 MOZ_ASSERT(callee().isClassConstructor());
238
239 HandleValue retVal = returnValue();
240 if (retVal.isObject()) {
241 return true;
242 }
243
244 if (!retVal.isUndefined()) {
245 ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, retVal,
246 nullptr);
247 return false;
248 }
249
250 if (thisv.isMagic(JS_UNINITIALIZED_LEXICAL)) {
251 return ThrowUninitializedThis(cx);
252 }
253
254 setReturnValue(thisv);
255 return true;
256 }
257
pushVarEnvironment(JSContext * cx,HandleScope scope)258 bool InterpreterFrame::pushVarEnvironment(JSContext* cx, HandleScope scope) {
259 return js::PushVarEnvironmentObject(cx, scope, this);
260 }
261
pushLexicalEnvironment(JSContext * cx,Handle<LexicalScope * > scope)262 bool InterpreterFrame::pushLexicalEnvironment(JSContext* cx,
263 Handle<LexicalScope*> scope) {
264 LexicalEnvironmentObject* env =
265 LexicalEnvironmentObject::createForFrame(cx, scope, this);
266 if (!env) {
267 return false;
268 }
269
270 pushOnEnvironmentChain(*env);
271 return true;
272 }
273
freshenLexicalEnvironment(JSContext * cx)274 bool InterpreterFrame::freshenLexicalEnvironment(JSContext* cx) {
275 Rooted<LexicalEnvironmentObject*> env(
276 cx, &envChain_->as<LexicalEnvironmentObject>());
277 LexicalEnvironmentObject* fresh = LexicalEnvironmentObject::clone(cx, env);
278 if (!fresh) {
279 return false;
280 }
281
282 replaceInnermostEnvironment(*fresh);
283 return true;
284 }
285
recreateLexicalEnvironment(JSContext * cx)286 bool InterpreterFrame::recreateLexicalEnvironment(JSContext* cx) {
287 Rooted<LexicalEnvironmentObject*> env(
288 cx, &envChain_->as<LexicalEnvironmentObject>());
289 LexicalEnvironmentObject* fresh = LexicalEnvironmentObject::recreate(cx, env);
290 if (!fresh) {
291 return false;
292 }
293
294 replaceInnermostEnvironment(*fresh);
295 return true;
296 }
297
trace(JSTracer * trc,Value * sp,jsbytecode * pc)298 void InterpreterFrame::trace(JSTracer* trc, Value* sp, jsbytecode* pc) {
299 TraceRoot(trc, &envChain_, "env chain");
300 TraceRoot(trc, &script_, "script");
301
302 if (flags_ & HAS_ARGS_OBJ) {
303 TraceRoot(trc, &argsObj_, "arguments");
304 }
305
306 if (hasReturnValue()) {
307 TraceRoot(trc, &rval_, "rval");
308 }
309
310 MOZ_ASSERT(sp >= slots());
311
312 if (hasArgs()) {
313 // Trace the callee and |this|. When we're doing a moving GC, we
314 // need to fix up the callee pointer before we use it below, under
315 // numFormalArgs() and script().
316 TraceRootRange(trc, 2, argv_ - 2, "fp callee and this");
317
318 // Trace arguments.
319 unsigned argc = std::max(numActualArgs(), numFormalArgs());
320 TraceRootRange(trc, argc + isConstructing(), argv_, "fp argv");
321 } else {
322 // Trace newTarget.
323 TraceRoot(trc, ((Value*)this) - 1, "stack newTarget");
324 }
325
326 JSScript* script = this->script();
327 size_t nfixed = script->nfixed();
328 size_t nlivefixed = script->calculateLiveFixed(pc);
329
330 if (nfixed == nlivefixed) {
331 // All locals are live.
332 traceValues(trc, 0, sp - slots());
333 } else {
334 // Trace operand stack.
335 traceValues(trc, nfixed, sp - slots());
336
337 // Clear dead block-scoped locals.
338 while (nfixed > nlivefixed) {
339 unaliasedLocal(--nfixed).setUndefined();
340 }
341
342 // Trace live locals.
343 traceValues(trc, 0, nlivefixed);
344 }
345
346 if (auto* debugEnvs = script->realm()->debugEnvs()) {
347 debugEnvs->traceLiveFrame(trc, this);
348 }
349 }
350
traceValues(JSTracer * trc,unsigned start,unsigned end)351 void InterpreterFrame::traceValues(JSTracer* trc, unsigned start,
352 unsigned end) {
353 if (start < end) {
354 TraceRootRange(trc, end - start, slots() + start, "vm_stack");
355 }
356 }
357
TraceInterpreterActivation(JSTracer * trc,InterpreterActivation * act)358 static void TraceInterpreterActivation(JSTracer* trc,
359 InterpreterActivation* act) {
360 for (InterpreterFrameIterator frames(act); !frames.done(); ++frames) {
361 InterpreterFrame* fp = frames.frame();
362 fp->trace(trc, frames.sp(), frames.pc());
363 }
364 }
365
TraceInterpreterActivations(JSContext * cx,JSTracer * trc)366 void js::TraceInterpreterActivations(JSContext* cx, JSTracer* trc) {
367 for (ActivationIterator iter(cx); !iter.done(); ++iter) {
368 Activation* act = iter.activation();
369 if (act->isInterpreter()) {
370 TraceInterpreterActivation(trc, act->asInterpreter());
371 }
372 }
373 }
374
375 /*****************************************************************************/
376
377 // Unlike the other methods of this class, this method is defined here so that
378 // we don't have to #include jsautooplen.h in vm/Stack.h.
setToEndOfScript()379 void InterpreterRegs::setToEndOfScript() { sp = fp()->base(); }
380
381 /*****************************************************************************/
382
pushInvokeFrame(JSContext * cx,const CallArgs & args,MaybeConstruct constructing)383 InterpreterFrame* InterpreterStack::pushInvokeFrame(
384 JSContext* cx, const CallArgs& args, MaybeConstruct constructing) {
385 LifoAlloc::Mark mark = allocator_.mark();
386
387 RootedFunction fun(cx, &args.callee().as<JSFunction>());
388 RootedScript script(cx, fun->nonLazyScript());
389
390 Value* argv;
391 InterpreterFrame* fp = getCallFrame(cx, args, script, constructing, &argv);
392 if (!fp) {
393 return nullptr;
394 }
395
396 fp->mark_ = mark;
397 fp->initCallFrame(nullptr, nullptr, nullptr, *fun, script, argv,
398 args.length(), constructing);
399 return fp;
400 }
401
pushExecuteFrame(JSContext * cx,HandleScript script,HandleValue newTargetValue,HandleObject envChain,AbstractFramePtr evalInFrame)402 InterpreterFrame* InterpreterStack::pushExecuteFrame(
403 JSContext* cx, HandleScript script, HandleValue newTargetValue,
404 HandleObject envChain, AbstractFramePtr evalInFrame) {
405 LifoAlloc::Mark mark = allocator_.mark();
406
407 unsigned nvars = 1 /* newTarget */ + script->nslots();
408 uint8_t* buffer =
409 allocateFrame(cx, sizeof(InterpreterFrame) + nvars * sizeof(Value));
410 if (!buffer) {
411 return nullptr;
412 }
413
414 InterpreterFrame* fp =
415 reinterpret_cast<InterpreterFrame*>(buffer + 1 * sizeof(Value));
416 fp->mark_ = mark;
417 fp->initExecuteFrame(cx, script, evalInFrame, newTargetValue, envChain);
418 fp->initLocals();
419
420 return fp;
421 }
422
423 /*****************************************************************************/
424
operator ++()425 InterpreterFrameIterator& InterpreterFrameIterator::operator++() {
426 MOZ_ASSERT(!done());
427 if (fp_ != activation_->entryFrame_) {
428 pc_ = fp_->prevpc();
429 sp_ = fp_->prevsp();
430 fp_ = fp_->prev();
431 } else {
432 pc_ = nullptr;
433 sp_ = nullptr;
434 fp_ = nullptr;
435 }
436 return *this;
437 }
438
ProfilingFrameIterator(JSContext * cx,const RegisterState & state,const Maybe<uint64_t> & samplePositionInProfilerBuffer)439 JS::ProfilingFrameIterator::ProfilingFrameIterator(
440 JSContext* cx, const RegisterState& state,
441 const Maybe<uint64_t>& samplePositionInProfilerBuffer)
442 : cx_(cx),
443 samplePositionInProfilerBuffer_(samplePositionInProfilerBuffer),
444 activation_(nullptr) {
445 if (!cx->runtime()->geckoProfiler().enabled()) {
446 MOZ_CRASH(
447 "ProfilingFrameIterator called when geckoProfiler not enabled for "
448 "runtime.");
449 }
450
451 if (!cx->profilingActivation()) {
452 return;
453 }
454
455 // If profiler sampling is not enabled, skip.
456 if (!cx->isProfilerSamplingEnabled()) {
457 return;
458 }
459
460 activation_ = cx->profilingActivation();
461
462 MOZ_ASSERT(activation_->isProfiling());
463
464 static_assert(sizeof(wasm::ProfilingFrameIterator) <= StorageSpace &&
465 sizeof(jit::JSJitProfilingFrameIterator) <= StorageSpace,
466 "ProfilingFrameIterator::storage_ is too small");
467 static_assert(alignof(void*) >= alignof(wasm::ProfilingFrameIterator) &&
468 alignof(void*) >= alignof(jit::JSJitProfilingFrameIterator),
469 "ProfilingFrameIterator::storage_ is too weakly aligned");
470
471 iteratorConstruct(state);
472 settle();
473 }
474
~ProfilingFrameIterator()475 JS::ProfilingFrameIterator::~ProfilingFrameIterator() {
476 if (!done()) {
477 MOZ_ASSERT(activation_->isProfiling());
478 iteratorDestroy();
479 }
480 }
481
operator ++()482 void JS::ProfilingFrameIterator::operator++() {
483 MOZ_ASSERT(!done());
484 MOZ_ASSERT(activation_->isJit());
485 if (isWasm()) {
486 ++wasmIter();
487 } else {
488 ++jsJitIter();
489 }
490 settle();
491 }
492
settleFrames()493 void JS::ProfilingFrameIterator::settleFrames() {
494 // Handle transition frames (see comment in JitFrameIter::operator++).
495 if (isJSJit() && !jsJitIter().done() &&
496 jsJitIter().frameType() == jit::FrameType::WasmToJSJit) {
497 wasm::Frame* fp = (wasm::Frame*)jsJitIter().fp();
498 iteratorDestroy();
499 new (storage()) wasm::ProfilingFrameIterator(fp);
500 kind_ = Kind::Wasm;
501 MOZ_ASSERT(!wasmIter().done());
502 return;
503 }
504
505 if (isWasm() && wasmIter().done() && wasmIter().unwoundIonCallerFP()) {
506 uint8_t* fp = wasmIter().unwoundIonCallerFP();
507 iteratorDestroy();
508 // Using this ctor will skip the first ion->wasm frame, which is
509 // needed because the profiling iterator doesn't know how to unwind
510 // when the callee has no script.
511 new (storage())
512 jit::JSJitProfilingFrameIterator((jit::CommonFrameLayout*)fp);
513 kind_ = Kind::JSJit;
514 MOZ_ASSERT(!jsJitIter().done());
515 return;
516 }
517 }
518
settle()519 void JS::ProfilingFrameIterator::settle() {
520 settleFrames();
521 while (iteratorDone()) {
522 iteratorDestroy();
523 activation_ = activation_->prevProfiling();
524 if (!activation_) {
525 return;
526 }
527 iteratorConstruct();
528 settleFrames();
529 }
530 }
531
iteratorConstruct(const RegisterState & state)532 void JS::ProfilingFrameIterator::iteratorConstruct(const RegisterState& state) {
533 MOZ_ASSERT(!done());
534 MOZ_ASSERT(activation_->isJit());
535
536 jit::JitActivation* activation = activation_->asJit();
537
538 // We want to know if we should start with a wasm profiling frame iterator
539 // or not. To determine this, there are three possibilities:
540 // - we've exited to C++ from wasm, in which case the activation
541 // exitFP low bit is tagged and we can test hasWasmExitFP().
542 // - we're in wasm code, so we can do a lookup on PC.
543 // - in all the other cases, we're not in wasm or we haven't exited from
544 // wasm.
545 if (activation->hasWasmExitFP() || wasm::InCompiledCode(state.pc)) {
546 new (storage()) wasm::ProfilingFrameIterator(*activation, state);
547 kind_ = Kind::Wasm;
548 return;
549 }
550
551 new (storage()) jit::JSJitProfilingFrameIterator(cx_, state.pc);
552 kind_ = Kind::JSJit;
553 }
554
iteratorConstruct()555 void JS::ProfilingFrameIterator::iteratorConstruct() {
556 MOZ_ASSERT(!done());
557 MOZ_ASSERT(activation_->isJit());
558
559 jit::JitActivation* activation = activation_->asJit();
560
561 // The same reasoning as in the above iteratorConstruct variant applies
562 // here, except that it's even simpler: since this activation is higher up
563 // on the stack, it can only have exited to C++, through wasm or ion.
564 if (activation->hasWasmExitFP()) {
565 new (storage()) wasm::ProfilingFrameIterator(*activation);
566 kind_ = Kind::Wasm;
567 return;
568 }
569
570 auto* fp = (jit::ExitFrameLayout*)activation->jsExitFP();
571 new (storage()) jit::JSJitProfilingFrameIterator(fp);
572 kind_ = Kind::JSJit;
573 }
574
iteratorDestroy()575 void JS::ProfilingFrameIterator::iteratorDestroy() {
576 MOZ_ASSERT(!done());
577 MOZ_ASSERT(activation_->isJit());
578
579 if (isWasm()) {
580 wasmIter().~ProfilingFrameIterator();
581 return;
582 }
583
584 jsJitIter().~JSJitProfilingFrameIterator();
585 }
586
iteratorDone()587 bool JS::ProfilingFrameIterator::iteratorDone() {
588 MOZ_ASSERT(!done());
589 MOZ_ASSERT(activation_->isJit());
590
591 if (isWasm()) {
592 return wasmIter().done();
593 }
594
595 return jsJitIter().done();
596 }
597
stackAddress() const598 void* JS::ProfilingFrameIterator::stackAddress() const {
599 MOZ_ASSERT(!done());
600 MOZ_ASSERT(activation_->isJit());
601
602 if (isWasm()) {
603 return wasmIter().stackAddress();
604 }
605
606 return jsJitIter().stackAddress();
607 }
608
609 Maybe<JS::ProfilingFrameIterator::Frame>
getPhysicalFrameAndEntry(jit::JitcodeGlobalEntry * entry) const610 JS::ProfilingFrameIterator::getPhysicalFrameAndEntry(
611 jit::JitcodeGlobalEntry* entry) const {
612 void* stackAddr = stackAddress();
613
614 if (isWasm()) {
615 Frame frame;
616 frame.kind = Frame_Wasm;
617 frame.stackAddress = stackAddr;
618 frame.returnAddress_ = nullptr;
619 frame.activation = activation_;
620 frame.label = nullptr;
621 frame.endStackAddress = activation_->asJit()->jsOrWasmExitFP();
622 frame.interpreterScript = nullptr;
623 // TODO: get the realm ID of wasm frames. Bug 1596235.
624 frame.realmID = 0;
625 return mozilla::Some(frame);
626 }
627
628 MOZ_ASSERT(isJSJit());
629
630 // Look up an entry for the return address.
631 void* returnAddr = jsJitIter().resumePCinCurrentFrame();
632 jit::JitcodeGlobalTable* table =
633 cx_->runtime()->jitRuntime()->getJitcodeGlobalTable();
634
635 // NB:
636 // The following lookups should be infallible, but the ad-hoc stackwalking
637 // code rots easily and corner cases where frames can't be looked up
638 // occur too often (e.g. once every day).
639 //
640 // The calls to `lookup*` below have been changed from infallible ones to
641 // fallible ones. The proper solution to this problem is to fix all
642 // the jitcode to use frame-pointers and reliably walk the stack with those.
643 const jit::JitcodeGlobalEntry* lookedUpEntry = nullptr;
644 if (samplePositionInProfilerBuffer_) {
645 lookedUpEntry = table->lookupForSampler(returnAddr, cx_->runtime(),
646 *samplePositionInProfilerBuffer_);
647 } else {
648 lookedUpEntry = table->lookup(returnAddr);
649 }
650
651 // Failed to look up a jitcode entry for the given address, ignore.
652 if (!lookedUpEntry) {
653 return mozilla::Nothing();
654 }
655 *entry = *lookedUpEntry;
656
657 MOZ_ASSERT(entry->isIon() || entry->isBaseline() ||
658 entry->isBaselineInterpreter() || entry->isDummy());
659
660 // Dummy frames produce no stack frames.
661 if (entry->isDummy()) {
662 return mozilla::Nothing();
663 }
664
665 Frame frame;
666 if (entry->isBaselineInterpreter()) {
667 frame.kind = Frame_BaselineInterpreter;
668 } else if (entry->isBaseline()) {
669 frame.kind = Frame_Baseline;
670 } else {
671 frame.kind = Frame_Ion;
672 }
673 frame.stackAddress = stackAddr;
674 if (entry->isBaselineInterpreter()) {
675 frame.label = jsJitIter().baselineInterpreterLabel();
676 jsJitIter().baselineInterpreterScriptPC(
677 &frame.interpreterScript, &frame.interpreterPC_, &frame.realmID);
678 MOZ_ASSERT(frame.interpreterScript);
679 MOZ_ASSERT(frame.interpreterPC_);
680 } else {
681 frame.interpreterScript = nullptr;
682 frame.returnAddress_ = returnAddr;
683 frame.label = nullptr;
684 frame.realmID = 0;
685 }
686 frame.activation = activation_;
687 frame.endStackAddress = activation_->asJit()->jsOrWasmExitFP();
688 return mozilla::Some(frame);
689 }
690
extractStack(Frame * frames,uint32_t offset,uint32_t end) const691 uint32_t JS::ProfilingFrameIterator::extractStack(Frame* frames,
692 uint32_t offset,
693 uint32_t end) const {
694 if (offset >= end) {
695 return 0;
696 }
697
698 jit::JitcodeGlobalEntry entry;
699 Maybe<Frame> physicalFrame = getPhysicalFrameAndEntry(&entry);
700
701 // Dummy frames produce no stack frames.
702 if (physicalFrame.isNothing()) {
703 return 0;
704 }
705
706 if (isWasm()) {
707 frames[offset] = physicalFrame.value();
708 frames[offset].label = wasmIter().label();
709 return 1;
710 }
711
712 if (physicalFrame->kind == Frame_BaselineInterpreter) {
713 frames[offset] = physicalFrame.value();
714 return 1;
715 }
716
717 // Extract the stack for the entry. Assume maximum inlining depth is <64
718 const char* labels[64];
719 uint32_t depth = entry.callStackAtAddr(cx_->runtime(),
720 jsJitIter().resumePCinCurrentFrame(),
721 labels, ArrayLength(labels));
722 MOZ_ASSERT(depth < ArrayLength(labels));
723 for (uint32_t i = 0; i < depth; i++) {
724 if (offset + i >= end) {
725 return i;
726 }
727 frames[offset + i] = physicalFrame.value();
728 frames[offset + i].label = labels[i];
729 }
730
731 return depth;
732 }
733
734 Maybe<JS::ProfilingFrameIterator::Frame>
getPhysicalFrameWithoutLabel() const735 JS::ProfilingFrameIterator::getPhysicalFrameWithoutLabel() const {
736 jit::JitcodeGlobalEntry unused;
737 return getPhysicalFrameAndEntry(&unused);
738 }
739
isWasm() const740 bool JS::ProfilingFrameIterator::isWasm() const {
741 MOZ_ASSERT(!done());
742 return kind_ == Kind::Wasm;
743 }
744
isJSJit() const745 bool JS::ProfilingFrameIterator::isJSJit() const {
746 return kind_ == Kind::JSJit;
747 }
748