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