diff options
Diffstat (limited to 'js/public/ProfilingStack.h')
-rw-r--r-- | js/public/ProfilingStack.h | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/js/public/ProfilingStack.h b/js/public/ProfilingStack.h new file mode 100644 index 000000000..aeed349e8 --- /dev/null +++ b/js/public/ProfilingStack.h @@ -0,0 +1,208 @@ +/* -*- 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_ProfilingStack_h +#define js_ProfilingStack_h + +#include "jsbytecode.h" +#include "jstypes.h" +#include "js/TypeDecls.h" + +#include "js/Utility.h" + +struct JSRuntime; +class JSTracer; + +namespace js { + +// A call stack can be specified to the JS engine such that all JS entry/exits +// to functions push/pop an entry to/from the specified stack. +// +// For more detailed information, see vm/SPSProfiler.h. +// +class ProfileEntry +{ + // All fields are marked volatile to prevent the compiler from re-ordering + // instructions. Namely this sequence: + // + // entry[size] = ...; + // size++; + // + // If the size modification were somehow reordered before the stores, then + // if a sample were taken it would be examining bogus information. + // + // A ProfileEntry represents both a C++ profile entry and a JS one. + + // Descriptive string of this entry. + const char * volatile string; + + // Stack pointer for non-JS entries, the script pointer otherwise. + void * volatile spOrScript; + + // Line number for non-JS entries, the bytecode offset otherwise. + int32_t volatile lineOrPcOffset; + + // General purpose storage describing this frame. + uint32_t volatile flags_; + + public: + // These traits are bit masks. Make sure they're powers of 2. + enum Flags : uint32_t { + // Indicate whether a profile entry represents a CPP frame. If not set, + // a JS frame is assumed by default. You're not allowed to publicly + // change the frame type. Instead, initialize the ProfileEntry as either + // a JS or CPP frame with `initJsFrame` or `initCppFrame` respectively. + IS_CPP_ENTRY = 0x01, + + // Indicate that copying the frame label is not necessary when taking a + // sample of the pseudostack. + FRAME_LABEL_COPY = 0x02, + + // This ProfileEntry is a dummy entry indicating the start of a run + // of JS pseudostack entries. + BEGIN_PSEUDO_JS = 0x04, + + // This flag is used to indicate that an interpreter JS entry has OSR-ed + // into baseline. + OSR = 0x08, + + // Union of all flags. + ALL = IS_CPP_ENTRY|FRAME_LABEL_COPY|BEGIN_PSEUDO_JS|OSR, + + // Mask for removing all flags except the category information. + CATEGORY_MASK = ~ALL + }; + + // Keep these in sync with devtools/client/performance/modules/categories.js + enum class Category : uint32_t { + OTHER = 0x10, + CSS = 0x20, + JS = 0x40, + GC = 0x80, + CC = 0x100, + NETWORK = 0x200, + GRAPHICS = 0x400, + STORAGE = 0x800, + EVENTS = 0x1000, + + FIRST = OTHER, + LAST = EVENTS + }; + + static_assert((static_cast<int>(Category::FIRST) & Flags::ALL) == 0, + "The category bitflags should not intersect with the other flags!"); + + // All of these methods are marked with the 'volatile' keyword because SPS's + // representation of the stack is stored such that all ProfileEntry + // instances are volatile. These methods would not be available unless they + // were marked as volatile as well. + + bool isCpp() const volatile { return hasFlag(IS_CPP_ENTRY); } + bool isJs() const volatile { return !isCpp(); } + + bool isCopyLabel() const volatile { return hasFlag(FRAME_LABEL_COPY); } + + void setLabel(const char* aString) volatile { string = aString; } + const char* label() const volatile { return string; } + + void initJsFrame(JSScript* aScript, jsbytecode* aPc) volatile { + flags_ = 0; + spOrScript = aScript; + setPC(aPc); + } + void initCppFrame(void* aSp, uint32_t aLine) volatile { + flags_ = IS_CPP_ENTRY; + spOrScript = aSp; + lineOrPcOffset = static_cast<int32_t>(aLine); + } + + void setFlag(uint32_t flag) volatile { + MOZ_ASSERT(flag != IS_CPP_ENTRY); + flags_ |= flag; + } + void unsetFlag(uint32_t flag) volatile { + MOZ_ASSERT(flag != IS_CPP_ENTRY); + flags_ &= ~flag; + } + bool hasFlag(uint32_t flag) const volatile { + return bool(flags_ & flag); + } + + uint32_t flags() const volatile { + return flags_; + } + + uint32_t category() const volatile { + return flags_ & CATEGORY_MASK; + } + void setCategory(Category c) volatile { + MOZ_ASSERT(c >= Category::FIRST); + MOZ_ASSERT(c <= Category::LAST); + flags_ &= ~CATEGORY_MASK; + setFlag(static_cast<uint32_t>(c)); + } + + void setOSR() volatile { + MOZ_ASSERT(isJs()); + setFlag(OSR); + } + void unsetOSR() volatile { + MOZ_ASSERT(isJs()); + unsetFlag(OSR); + } + bool isOSR() const volatile { + return hasFlag(OSR); + } + + void* stackAddress() const volatile { + MOZ_ASSERT(!isJs()); + return spOrScript; + } + JS_PUBLIC_API(JSScript*) script() const volatile; + uint32_t line() const volatile { + MOZ_ASSERT(!isJs()); + return static_cast<uint32_t>(lineOrPcOffset); + } + + // Note that the pointer returned might be invalid. + JSScript* rawScript() const volatile { + MOZ_ASSERT(isJs()); + return (JSScript*)spOrScript; + } + + // We can't know the layout of JSScript, so look in vm/SPSProfiler.cpp. + JS_FRIEND_API(jsbytecode*) pc() const volatile; + JS_FRIEND_API(void) setPC(jsbytecode* pc) volatile; + + void trace(JSTracer* trc); + + // The offset of a pc into a script's code can actually be 0, so to + // signify a nullptr pc, use a -1 index. This is checked against in + // pc() and setPC() to set/get the right pc. + static const int32_t NullPCOffset = -1; + + static size_t offsetOfLabel() { return offsetof(ProfileEntry, string); } + static size_t offsetOfSpOrScript() { return offsetof(ProfileEntry, spOrScript); } + static size_t offsetOfLineOrPcOffset() { return offsetof(ProfileEntry, lineOrPcOffset); } + static size_t offsetOfFlags() { return offsetof(ProfileEntry, flags_); } +}; + +JS_FRIEND_API(void) +SetContextProfilingStack(JSContext* cx, ProfileEntry* stack, uint32_t* size, + uint32_t max); + +JS_FRIEND_API(void) +EnableContextProfilingStack(JSContext* cx, bool enabled); + +JS_FRIEND_API(void) +RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*)); + +JS_FRIEND_API(jsbytecode*) +ProfilingGetPC(JSContext* cx, JSScript* script, void* ip); + +} // namespace js + +#endif /* js_ProfilingStack_h */ |