diff options
Diffstat (limited to 'js/public/ProfilingFrameIterator.h')
-rw-r--r-- | js/public/ProfilingFrameIterator.h | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/js/public/ProfilingFrameIterator.h b/js/public/ProfilingFrameIterator.h new file mode 100644 index 000000000..52621adb4 --- /dev/null +++ b/js/public/ProfilingFrameIterator.h @@ -0,0 +1,206 @@ +/* -*- 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 */ |