1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * vim: set ts=8 sts=4 et sw=4 tw=99: 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 #ifndef js_ProfilingStack_h 8 #define js_ProfilingStack_h 9 10 #include "jsbytecode.h" 11 #include "jstypes.h" 12 #include "js/TypeDecls.h" 13 14 #include "js/Utility.h" 15 16 struct JSRuntime; 17 18 namespace js { 19 20 // A call stack can be specified to the JS engine such that all JS entry/exits 21 // to functions push/pop an entry to/from the specified stack. 22 // 23 // For more detailed information, see vm/SPSProfiler.h. 24 // 25 class ProfileEntry 26 { 27 // All fields are marked volatile to prevent the compiler from re-ordering 28 // instructions. Namely this sequence: 29 // 30 // entry[size] = ...; 31 // size++; 32 // 33 // If the size modification were somehow reordered before the stores, then 34 // if a sample were taken it would be examining bogus information. 35 // 36 // A ProfileEntry represents both a C++ profile entry and a JS one. 37 38 // Descriptive string of this entry. 39 const char * volatile string; 40 41 // Stack pointer for non-JS entries, the script pointer otherwise. 42 void * volatile spOrScript; 43 44 // Line number for non-JS entries, the bytecode offset otherwise. 45 int32_t volatile lineOrPc; 46 47 // General purpose storage describing this frame. 48 uint32_t volatile flags_; 49 50 public: 51 // These traits are bit masks. Make sure they're powers of 2. 52 enum Flags { 53 // Indicate whether a profile entry represents a CPP frame. If not set, 54 // a JS frame is assumed by default. You're not allowed to publicly 55 // change the frame type. Instead, initialize the ProfileEntry as either 56 // a JS or CPP frame with `initJsFrame` or `initCppFrame` respectively. 57 IS_CPP_ENTRY = 0x01, 58 59 // Indicate that copying the frame label is not necessary when taking a 60 // sample of the pseudostack. 61 FRAME_LABEL_COPY = 0x02, 62 63 // This ProfileEntry is a dummy entry indicating the start of a run 64 // of JS pseudostack entries. 65 BEGIN_PSEUDO_JS = 0x04, 66 67 // This flag is used to indicate that an interpreter JS entry has OSR-ed 68 // into baseline. 69 OSR = 0x08, 70 71 // Union of all flags. 72 ALL = IS_CPP_ENTRY|FRAME_LABEL_COPY|BEGIN_PSEUDO_JS|OSR, 73 74 // Mask for removing all flags except the category information. 75 CATEGORY_MASK = ~ALL 76 }; 77 78 // Keep these in sync with devtools/client/performance/modules/global.js 79 enum class Category : uint32_t { 80 OTHER = 0x10, 81 CSS = 0x20, 82 JS = 0x40, 83 GC = 0x80, 84 CC = 0x100, 85 NETWORK = 0x200, 86 GRAPHICS = 0x400, 87 STORAGE = 0x800, 88 EVENTS = 0x1000, 89 90 FIRST = OTHER, 91 LAST = EVENTS 92 }; 93 94 static_assert((static_cast<int>(Category::FIRST) & Flags::ALL) == 0, 95 "The category bitflags should not intersect with the other flags!"); 96 97 // All of these methods are marked with the 'volatile' keyword because SPS's 98 // representation of the stack is stored such that all ProfileEntry 99 // instances are volatile. These methods would not be available unless they 100 // were marked as volatile as well. 101 isCpp()102 bool isCpp() const volatile { return hasFlag(IS_CPP_ENTRY); } isJs()103 bool isJs() const volatile { return !isCpp(); } 104 isCopyLabel()105 bool isCopyLabel() const volatile { return hasFlag(FRAME_LABEL_COPY); } 106 setLabel(const char * aString)107 void setLabel(const char* aString) volatile { string = aString; } label()108 const char* label() const volatile { return string; } 109 initJsFrame(JSScript * aScript,jsbytecode * aPc)110 void initJsFrame(JSScript* aScript, jsbytecode* aPc) volatile { 111 flags_ = 0; 112 spOrScript = aScript; 113 setPC(aPc); 114 } initCppFrame(void * aSp,uint32_t aLine)115 void initCppFrame(void* aSp, uint32_t aLine) volatile { 116 flags_ = IS_CPP_ENTRY; 117 spOrScript = aSp; 118 lineOrPc = static_cast<int32_t>(aLine); 119 } 120 setFlag(uint32_t flag)121 void setFlag(uint32_t flag) volatile { 122 MOZ_ASSERT(flag != IS_CPP_ENTRY); 123 flags_ |= flag; 124 } unsetFlag(uint32_t flag)125 void unsetFlag(uint32_t flag) volatile { 126 MOZ_ASSERT(flag != IS_CPP_ENTRY); 127 flags_ &= ~flag; 128 } hasFlag(uint32_t flag)129 bool hasFlag(uint32_t flag) const volatile { 130 return bool(flags_ & flag); 131 } 132 flags()133 uint32_t flags() const volatile { 134 return flags_; 135 } 136 category()137 uint32_t category() const volatile { 138 return flags_ & CATEGORY_MASK; 139 } setCategory(Category c)140 void setCategory(Category c) volatile { 141 MOZ_ASSERT(c >= Category::FIRST); 142 MOZ_ASSERT(c <= Category::LAST); 143 flags_ &= ~CATEGORY_MASK; 144 setFlag(static_cast<uint32_t>(c)); 145 } 146 setOSR()147 void setOSR() volatile { 148 MOZ_ASSERT(isJs()); 149 setFlag(OSR); 150 } unsetOSR()151 void unsetOSR() volatile { 152 MOZ_ASSERT(isJs()); 153 unsetFlag(OSR); 154 } isOSR()155 bool isOSR() const volatile { 156 return hasFlag(OSR); 157 } 158 stackAddress()159 void* stackAddress() const volatile { 160 MOZ_ASSERT(!isJs()); 161 return spOrScript; 162 } script()163 JSScript* script() const volatile { 164 MOZ_ASSERT(isJs()); 165 return (JSScript*)spOrScript; 166 } line()167 uint32_t line() const volatile { 168 MOZ_ASSERT(!isJs()); 169 return static_cast<uint32_t>(lineOrPc); 170 } 171 172 // We can't know the layout of JSScript, so look in vm/SPSProfiler.cpp. 173 JS_FRIEND_API(jsbytecode*) pc() const volatile; 174 JS_FRIEND_API(void) setPC(jsbytecode* pc) volatile; 175 176 // The offset of a pc into a script's code can actually be 0, so to 177 // signify a nullptr pc, use a -1 index. This is checked against in 178 // pc() and setPC() to set/get the right pc. 179 static const int32_t NullPCOffset = -1; 180 offsetOfLabel()181 static size_t offsetOfLabel() { return offsetof(ProfileEntry, string); } offsetOfSpOrScript()182 static size_t offsetOfSpOrScript() { return offsetof(ProfileEntry, spOrScript); } offsetOfLineOrPc()183 static size_t offsetOfLineOrPc() { return offsetof(ProfileEntry, lineOrPc); } offsetOfFlags()184 static size_t offsetOfFlags() { return offsetof(ProfileEntry, flags_); } 185 }; 186 187 JS_FRIEND_API(void) 188 SetRuntimeProfilingStack(JSRuntime* rt, ProfileEntry* stack, uint32_t* size, 189 uint32_t max); 190 191 JS_FRIEND_API(void) 192 EnableRuntimeProfilingStack(JSRuntime* rt, bool enabled); 193 194 JS_FRIEND_API(void) 195 RegisterRuntimeProfilingEventMarker(JSRuntime* rt, void (*fn)(const char*)); 196 197 JS_FRIEND_API(jsbytecode*) 198 ProfilingGetPC(JSRuntime* rt, JSScript* script, void* ip); 199 200 } // namespace js 201 202 #endif /* js_ProfilingStack_h */ 203