/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef js_ProfilingFrameIterator_h #define js_ProfilingFrameIterator_h #include "mozilla/Alignment.h" #include "mozilla/Maybe.h" #include "jsbytecode.h" #include "js/GCAPI.h" #include "js/TypeDecls.h" #include "js/Utility.h" struct JSContext; struct JSRuntime; class JSScript; namespace js { class Activation; namespace jit { class JitActivation; class JitProfilingFrameIterator; class JitcodeGlobalEntry; } // namespace jit namespace wasm { class ProfilingFrameIterator; } // namespace wasm } // namespace js namespace JS { struct ForEachTrackedOptimizationAttemptOp; struct ForEachTrackedOptimizationTypeInfoOp; // This iterator can be used to walk the stack of a thread suspended at an // arbitrary pc. To provide acurate results, profiling must have been enabled // (via EnableRuntimeProfilingStack) before executing the callstack being // unwound. // // Note that the caller must not do anything that could cause GC to happen while // the iterator is alive, since this could invalidate Ion code and cause its // contents to become out of date. class JS_PUBLIC_API(ProfilingFrameIterator) { JSRuntime* rt_; uint32_t sampleBufferGen_; js::Activation* activation_; // When moving past a JitActivation, we need to save the prevJitTop // from it to use as the exit-frame pointer when the next caller jit // activation (if any) comes around. void* savedPrevJitTop_; JS::AutoCheckCannotGC nogc_; static const unsigned StorageSpace = 8 * sizeof(void*); mozilla::AlignedStorage<StorageSpace> storage_; js::wasm::ProfilingFrameIterator& wasmIter() { MOZ_ASSERT(!done()); MOZ_ASSERT(isWasm()); return *reinterpret_cast<js::wasm::ProfilingFrameIterator*>(storage_.addr()); } const js::wasm::ProfilingFrameIterator& wasmIter() const { MOZ_ASSERT(!done()); MOZ_ASSERT(isWasm()); return *reinterpret_cast<const js::wasm::ProfilingFrameIterator*>(storage_.addr()); } js::jit::JitProfilingFrameIterator& jitIter() { MOZ_ASSERT(!done()); MOZ_ASSERT(isJit()); return *reinterpret_cast<js::jit::JitProfilingFrameIterator*>(storage_.addr()); } const js::jit::JitProfilingFrameIterator& jitIter() const { MOZ_ASSERT(!done()); MOZ_ASSERT(isJit()); return *reinterpret_cast<const js::jit::JitProfilingFrameIterator*>(storage_.addr()); } void settle(); bool hasSampleBufferGen() const { return sampleBufferGen_ != UINT32_MAX; } public: struct RegisterState { RegisterState() : pc(nullptr), sp(nullptr), lr(nullptr) {} void* pc; void* sp; void* lr; }; ProfilingFrameIterator(JSContext* cx, const RegisterState& state, uint32_t sampleBufferGen = UINT32_MAX); ~ProfilingFrameIterator(); void operator++(); bool done() const { return !activation_; } // Assuming the stack grows down (we do), the return value: // - always points into the stack // - is weakly monotonically increasing (may be equal for successive frames) // - will compare greater than newer native and psuedo-stack frame addresses // and less than older native and psuedo-stack frame addresses void* stackAddress() const; enum FrameKind { Frame_Baseline, Frame_Ion, Frame_Wasm }; struct Frame { FrameKind kind; void* stackAddress; void* returnAddress; void* activation; UniqueChars label; }; bool isWasm() const; bool isJit() const; uint32_t extractStack(Frame* frames, uint32_t offset, uint32_t end) const; mozilla::Maybe<Frame> getPhysicalFrameWithoutLabel() const; private: mozilla::Maybe<Frame> getPhysicalFrameAndEntry(js::jit::JitcodeGlobalEntry* entry) const; void iteratorConstruct(const RegisterState& state); void iteratorConstruct(); void iteratorDestroy(); bool iteratorDone(); }; JS_FRIEND_API(bool) IsProfilingEnabledForContext(JSContext* cx); /** * After each sample run, this method should be called with the latest sample * buffer generation, and the lapCount. It will update corresponding fields on * JSRuntime. * * See fields |profilerSampleBufferGen|, |profilerSampleBufferLapCount| on * JSRuntime for documentation about what these values are used for. */ JS_FRIEND_API(void) UpdateJSContextProfilerSampleBufferGen(JSContext* cx, uint32_t generation, uint32_t lapCount); struct ForEachProfiledFrameOp { // A handle to the underlying JitcodeGlobalEntry, so as to avoid repeated // lookups on JitcodeGlobalTable. class MOZ_STACK_CLASS FrameHandle { friend JS_PUBLIC_API(void) ForEachProfiledFrame(JSContext* cx, void* addr, ForEachProfiledFrameOp& op); JSRuntime* rt_; js::jit::JitcodeGlobalEntry& entry_; void* addr_; void* canonicalAddr_; const char* label_; uint32_t depth_; mozilla::Maybe<uint8_t> optsIndex_; FrameHandle(JSRuntime* rt, js::jit::JitcodeGlobalEntry& entry, void* addr, const char* label, uint32_t depth); void updateHasTrackedOptimizations(); public: const char* label() const { return label_; } uint32_t depth() const { return depth_; } bool hasTrackedOptimizations() const { return optsIndex_.isSome(); } void* canonicalAddress() const { return canonicalAddr_; } JS_PUBLIC_API(ProfilingFrameIterator::FrameKind) frameKind() const; JS_PUBLIC_API(void) forEachOptimizationAttempt(ForEachTrackedOptimizationAttemptOp& op, JSScript** scriptOut, jsbytecode** pcOut) const; JS_PUBLIC_API(void) forEachOptimizationTypeInfo(ForEachTrackedOptimizationTypeInfoOp& op) const; }; // Called once per frame. virtual void operator()(const FrameHandle& frame) = 0; }; JS_PUBLIC_API(void) ForEachProfiledFrame(JSContext* cx, void* addr, ForEachProfiledFrameOp& op); } // namespace JS #endif /* js_ProfilingFrameIterator_h */