| /* -*- 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; |
| |
| 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 lineOrPc; |
| |
| // 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 { |
| // 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/global.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; |
| lineOrPc = 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; |
| } |
| JSScript* script() const volatile { |
| MOZ_ASSERT(isJs()); |
| return (JSScript*)spOrScript; |
| } |
| uint32_t line() const volatile { |
| MOZ_ASSERT(!isJs()); |
| return static_cast<uint32_t>(lineOrPc); |
| } |
| |
| // 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; |
| |
| // 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 offsetOfLineOrPc() { return offsetof(ProfileEntry, lineOrPc); } |
| static size_t offsetOfFlags() { return offsetof(ProfileEntry, flags_); } |
| }; |
| |
| JS_FRIEND_API(void) |
| SetRuntimeProfilingStack(JSRuntime* rt, ProfileEntry* stack, uint32_t* size, |
| uint32_t max); |
| |
| JS_FRIEND_API(void) |
| EnableRuntimeProfilingStack(JSRuntime* rt, bool enabled); |
| |
| JS_FRIEND_API(void) |
| RegisterRuntimeProfilingEventMarker(JSRuntime* rt, void (*fn)(const char*)); |
| |
| JS_FRIEND_API(jsbytecode*) |
| ProfilingGetPC(JSRuntime* rt, JSScript* script, void* ip); |
| |
| } // namespace js |
| |
| #endif /* js_ProfilingStack_h */ |