]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 2008, 2013 Apple Inc. All rights reserved. | |
3 | * | |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions | |
6 | * are met: | |
7 | * | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of Apple Inc. ("Apple") nor the names of | |
14 | * its contributors may be used to endorse or promote products derived | |
15 | * from this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | |
18 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
19 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
20 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | |
21 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
22 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
23 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
24 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
27 | */ | |
28 | ||
29 | #ifndef SamplingTool_h | |
30 | #define SamplingTool_h | |
31 | ||
32 | #include "Strong.h" | |
33 | #include "Opcode.h" | |
34 | #include "SamplingCounter.h" | |
35 | #include <wtf/Assertions.h> | |
36 | #include <wtf/Atomics.h> | |
37 | #include <wtf/HashMap.h> | |
38 | #include <wtf/MainThread.h> | |
39 | #include <wtf/Spectrum.h> | |
40 | #include <wtf/Threading.h> | |
41 | ||
42 | namespace JSC { | |
43 | ||
44 | class ScriptExecutable; | |
45 | ||
46 | class SamplingFlags { | |
47 | public: | |
48 | JS_EXPORT_PRIVATE static void start(); | |
49 | JS_EXPORT_PRIVATE static void stop(); | |
50 | ||
51 | #if ENABLE(SAMPLING_FLAGS) | |
52 | static void setFlag(unsigned flag) | |
53 | { | |
54 | ASSERT(flag >= 1); | |
55 | ASSERT(flag <= 32); | |
56 | s_flags |= 1u << (flag - 1); | |
57 | } | |
58 | ||
59 | static void clearFlag(unsigned flag) | |
60 | { | |
61 | ASSERT(flag >= 1); | |
62 | ASSERT(flag <= 32); | |
63 | s_flags &= ~(1u << (flag - 1)); | |
64 | } | |
65 | ||
66 | static void sample(); | |
67 | ||
68 | class ScopedFlag { | |
69 | public: | |
70 | ScopedFlag(int flag) | |
71 | : m_flag(flag) | |
72 | { | |
73 | setFlag(flag); | |
74 | } | |
75 | ||
76 | ~ScopedFlag() | |
77 | { | |
78 | clearFlag(m_flag); | |
79 | } | |
80 | ||
81 | private: | |
82 | int m_flag; | |
83 | }; | |
84 | ||
85 | static const void* addressOfFlags() | |
86 | { | |
87 | return &s_flags; | |
88 | } | |
89 | ||
90 | #endif | |
91 | private: | |
92 | JS_EXPORTDATA static uint32_t s_flags; | |
93 | #if ENABLE(SAMPLING_FLAGS) | |
94 | static uint64_t s_flagCounts[33]; | |
95 | #endif | |
96 | }; | |
97 | ||
98 | #if ENABLE(SAMPLING_REGIONS) | |
99 | class SamplingRegion { | |
100 | public: | |
101 | // Create a scoped sampling region using a C string constant name that describes | |
102 | // what you are doing. This must be a string constant that persists for the | |
103 | // lifetime of the process and is immutable. | |
104 | SamplingRegion(const char* name) | |
105 | { | |
106 | if (!isMainThread()) { | |
107 | m_name = 0; | |
108 | return; | |
109 | } | |
110 | ||
111 | m_name = name; | |
112 | exchangeCurrent(this, &m_previous); | |
113 | ASSERT(!m_previous || m_previous > this); | |
114 | } | |
115 | ||
116 | ~SamplingRegion() | |
117 | { | |
118 | if (!m_name) | |
119 | return; | |
120 | ||
121 | ASSERT(bitwise_cast<SamplingRegion*>(s_currentOrReserved & ~1) == this); | |
122 | exchangeCurrent(m_previous); | |
123 | } | |
124 | ||
125 | static void sample(); | |
126 | ||
127 | JS_EXPORT_PRIVATE static void dump(); | |
128 | ||
129 | private: | |
130 | const char* m_name; | |
131 | SamplingRegion* m_previous; | |
132 | ||
133 | static void exchangeCurrent(SamplingRegion* current, SamplingRegion** previousPtr = 0) | |
134 | { | |
135 | uintptr_t previous; | |
136 | while (true) { | |
137 | previous = s_currentOrReserved; | |
138 | ||
139 | // If it's reserved (i.e. sampling thread is reading it), loop around. | |
140 | if (previous & 1) { | |
141 | #if OS(UNIX) | |
142 | sched_yield(); | |
143 | #endif | |
144 | continue; | |
145 | } | |
146 | ||
147 | // If we're going to CAS, then make sure previous is set. | |
148 | if (previousPtr) | |
149 | *previousPtr = bitwise_cast<SamplingRegion*>(previous); | |
150 | ||
151 | if (WTF::weakCompareAndSwapUIntPtr(&s_currentOrReserved, previous, bitwise_cast<uintptr_t>(current))) | |
152 | break; | |
153 | } | |
154 | } | |
155 | ||
156 | static void dumpInternal(); | |
157 | ||
158 | class Locker { | |
159 | public: | |
160 | Locker(); | |
161 | ~Locker(); | |
162 | }; | |
163 | ||
164 | static volatile uintptr_t s_currentOrReserved; | |
165 | ||
166 | // rely on identity hashing of string constants | |
167 | static Spectrum<const char*>* s_spectrum; | |
168 | ||
169 | static unsigned long s_noneOfTheAbove; | |
170 | ||
171 | static unsigned s_numberOfSamplesSinceDump; | |
172 | }; | |
173 | #else // ENABLE(SAMPLING_REGIONS) | |
174 | class SamplingRegion { | |
175 | public: | |
176 | SamplingRegion(const char*) { } | |
177 | JS_EXPORT_PRIVATE void dump(); | |
178 | }; | |
179 | #endif // ENABLE(SAMPLING_REGIONS) | |
180 | ||
181 | class CodeBlock; | |
182 | class ExecState; | |
183 | class Interpreter; | |
184 | class ScopeNode; | |
185 | struct Instruction; | |
186 | ||
187 | struct ScriptSampleRecord { | |
188 | ScriptSampleRecord(VM& vm, ScriptExecutable* executable) | |
189 | : m_executable(vm, executable) | |
190 | , m_codeBlock(0) | |
191 | , m_sampleCount(0) | |
192 | , m_opcodeSampleCount(0) | |
193 | , m_samples(0) | |
194 | , m_size(0) | |
195 | { | |
196 | } | |
197 | ||
198 | ~ScriptSampleRecord() | |
199 | { | |
200 | if (m_samples) | |
201 | free(m_samples); | |
202 | } | |
203 | ||
204 | void sample(CodeBlock*, Instruction*); | |
205 | ||
206 | Strong<ScriptExecutable> m_executable; | |
207 | CodeBlock* m_codeBlock; | |
208 | int m_sampleCount; | |
209 | int m_opcodeSampleCount; | |
210 | int* m_samples; | |
211 | unsigned m_size; | |
212 | }; | |
213 | ||
214 | typedef HashMap<ScriptExecutable*, std::unique_ptr<ScriptSampleRecord>> ScriptSampleRecordMap; | |
215 | ||
216 | class SamplingThread { | |
217 | public: | |
218 | // Sampling thread state. | |
219 | static bool s_running; | |
220 | static unsigned s_hertz; | |
221 | static ThreadIdentifier s_samplingThread; | |
222 | ||
223 | JS_EXPORT_PRIVATE static void start(unsigned hertz=10000); | |
224 | JS_EXPORT_PRIVATE static void stop(); | |
225 | ||
226 | static void threadStartFunc(void*); | |
227 | }; | |
228 | ||
229 | class SamplingTool { | |
230 | public: | |
231 | friend struct CallRecord; | |
232 | ||
233 | #if ENABLE(OPCODE_SAMPLING) | |
234 | class CallRecord { | |
235 | WTF_MAKE_NONCOPYABLE(CallRecord); | |
236 | public: | |
237 | CallRecord(SamplingTool* samplingTool, bool isHostCall = false) | |
238 | : m_samplingTool(samplingTool) | |
239 | , m_savedSample(samplingTool->m_sample) | |
240 | , m_savedCodeBlock(samplingTool->m_codeBlock) | |
241 | { | |
242 | if (isHostcall) | |
243 | samplingTool->m_sample |= 0x1; | |
244 | } | |
245 | ||
246 | ~CallRecord() | |
247 | { | |
248 | m_samplingTool->m_sample = m_savedSample; | |
249 | m_samplingTool->m_codeBlock = m_savedCodeBlock; | |
250 | } | |
251 | ||
252 | private: | |
253 | SamplingTool* m_samplingTool; | |
254 | intptr_t m_savedSample; | |
255 | CodeBlock* m_savedCodeBlock; | |
256 | }; | |
257 | #else | |
258 | class CallRecord { | |
259 | WTF_MAKE_NONCOPYABLE(CallRecord); | |
260 | public: | |
261 | CallRecord(SamplingTool*, bool = false) | |
262 | { | |
263 | } | |
264 | }; | |
265 | #endif | |
266 | ||
267 | SamplingTool(Interpreter* interpreter) | |
268 | : m_interpreter(interpreter) | |
269 | , m_codeBlock(0) | |
270 | , m_sample(0) | |
271 | , m_sampleCount(0) | |
272 | , m_opcodeSampleCount(0) | |
273 | #if ENABLE(CODEBLOCK_SAMPLING) | |
274 | , m_scopeSampleMap(adoptPtr(new ScriptSampleRecordMap)) | |
275 | #endif | |
276 | { | |
277 | memset(m_opcodeSamples, 0, sizeof(m_opcodeSamples)); | |
278 | memset(m_opcodeSamplesInCTIFunctions, 0, sizeof(m_opcodeSamplesInCTIFunctions)); | |
279 | } | |
280 | ||
281 | JS_EXPORT_PRIVATE void setup(); | |
282 | void dump(ExecState*); | |
283 | ||
284 | void notifyOfScope(VM&, ScriptExecutable* scope); | |
285 | ||
286 | void sample(CodeBlock* codeBlock, Instruction* vPC) | |
287 | { | |
288 | ASSERT(!(reinterpret_cast<intptr_t>(vPC) & 0x3)); | |
289 | m_codeBlock = codeBlock; | |
290 | m_sample = reinterpret_cast<intptr_t>(vPC); | |
291 | } | |
292 | ||
293 | CodeBlock** codeBlockSlot() { return &m_codeBlock; } | |
294 | intptr_t* sampleSlot() { return &m_sample; } | |
295 | ||
296 | void* encodeSample(Instruction* vPC, bool inCTIFunction = false, bool inHostFunction = false) | |
297 | { | |
298 | ASSERT(!(reinterpret_cast<intptr_t>(vPC) & 0x3)); | |
299 | return reinterpret_cast<void*>(reinterpret_cast<intptr_t>(vPC) | (static_cast<intptr_t>(inCTIFunction) << 1) | static_cast<intptr_t>(inHostFunction)); | |
300 | } | |
301 | ||
302 | static void sample(); | |
303 | ||
304 | private: | |
305 | class Sample { | |
306 | public: | |
307 | Sample(volatile intptr_t sample, CodeBlock* volatile codeBlock) | |
308 | : m_sample(sample) | |
309 | , m_codeBlock(codeBlock) | |
310 | { | |
311 | } | |
312 | ||
313 | bool isNull() { return !m_sample; } | |
314 | CodeBlock* codeBlock() { return m_codeBlock; } | |
315 | Instruction* vPC() { return reinterpret_cast<Instruction*>(m_sample & ~0x3); } | |
316 | bool inHostFunction() { return m_sample & 0x1; } | |
317 | bool inCTIFunction() { return m_sample & 0x2; } | |
318 | ||
319 | private: | |
320 | intptr_t m_sample; | |
321 | CodeBlock* m_codeBlock; | |
322 | }; | |
323 | ||
324 | void doRun(); | |
325 | static SamplingTool* s_samplingTool; | |
326 | ||
327 | Interpreter* m_interpreter; | |
328 | ||
329 | // State tracked by the main thread, used by the sampling thread. | |
330 | CodeBlock* m_codeBlock; | |
331 | intptr_t m_sample; | |
332 | ||
333 | // Gathered sample data. | |
334 | long long m_sampleCount; | |
335 | long long m_opcodeSampleCount; | |
336 | unsigned m_opcodeSamples[numOpcodeIDs]; | |
337 | unsigned m_opcodeSamplesInCTIFunctions[numOpcodeIDs]; | |
338 | ||
339 | #if ENABLE(CODEBLOCK_SAMPLING) | |
340 | Mutex m_scriptSampleMapMutex; | |
341 | OwnPtr<ScriptSampleRecordMap> m_scopeSampleMap; | |
342 | #endif | |
343 | }; | |
344 | ||
345 | } // namespace JSC | |
346 | ||
347 | #endif // SamplingTool_h |