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