|
1 /* |
|
2 * Copyright (C) 2008 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 Computer, 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 <wtf/Assertions.h> |
|
33 #include <wtf/HashMap.h> |
|
34 #include <wtf/Threading.h> |
|
35 |
|
36 #include "Nodes.h" |
|
37 #include "Opcode.h" |
|
38 |
|
39 namespace JSC { |
|
40 |
|
41 class ScriptExecutable; |
|
42 |
|
43 class SamplingFlags { |
|
44 friend class JIT; |
|
45 public: |
|
46 static void start(); |
|
47 static void stop(); |
|
48 |
|
49 #if ENABLE(SAMPLING_FLAGS) |
|
50 static void setFlag(unsigned flag) |
|
51 { |
|
52 ASSERT(flag >= 1); |
|
53 ASSERT(flag <= 32); |
|
54 s_flags |= 1u << (flag - 1); |
|
55 } |
|
56 |
|
57 static void clearFlag(unsigned flag) |
|
58 { |
|
59 ASSERT(flag >= 1); |
|
60 ASSERT(flag <= 32); |
|
61 s_flags &= ~(1u << (flag - 1)); |
|
62 } |
|
63 |
|
64 static void sample(); |
|
65 |
|
66 class ScopedFlag { |
|
67 public: |
|
68 ScopedFlag(int flag) |
|
69 : m_flag(flag) |
|
70 { |
|
71 setFlag(flag); |
|
72 } |
|
73 |
|
74 ~ScopedFlag() |
|
75 { |
|
76 clearFlag(m_flag); |
|
77 } |
|
78 |
|
79 private: |
|
80 int m_flag; |
|
81 }; |
|
82 |
|
83 #endif |
|
84 private: |
|
85 static uint32_t s_flags; |
|
86 #if ENABLE(SAMPLING_FLAGS) |
|
87 static uint64_t s_flagCounts[33]; |
|
88 #endif |
|
89 }; |
|
90 |
|
91 class CodeBlock; |
|
92 class ExecState; |
|
93 class Interpreter; |
|
94 class ScopeNode; |
|
95 struct Instruction; |
|
96 |
|
97 struct ScriptSampleRecord { |
|
98 ScriptSampleRecord(ScriptExecutable* executable) |
|
99 : m_executable(executable) |
|
100 , m_codeBlock(0) |
|
101 , m_sampleCount(0) |
|
102 , m_opcodeSampleCount(0) |
|
103 , m_samples(0) |
|
104 , m_size(0) |
|
105 { |
|
106 } |
|
107 |
|
108 ~ScriptSampleRecord() |
|
109 { |
|
110 if (m_samples) |
|
111 free(m_samples); |
|
112 } |
|
113 |
|
114 void sample(CodeBlock*, Instruction*); |
|
115 |
|
116 RefPtr<ScriptExecutable> m_executable; |
|
117 CodeBlock* m_codeBlock; |
|
118 int m_sampleCount; |
|
119 int m_opcodeSampleCount; |
|
120 int* m_samples; |
|
121 unsigned m_size; |
|
122 }; |
|
123 |
|
124 typedef WTF::HashMap<ScriptExecutable*, ScriptSampleRecord*> ScriptSampleRecordMap; |
|
125 |
|
126 class SamplingThread { |
|
127 public: |
|
128 // Sampling thread state. |
|
129 static bool s_running; |
|
130 static unsigned s_hertz; |
|
131 static ThreadIdentifier s_samplingThread; |
|
132 |
|
133 static void start(unsigned hertz=10000); |
|
134 static void stop(); |
|
135 |
|
136 static void* threadStartFunc(void*); |
|
137 }; |
|
138 |
|
139 class SamplingTool { |
|
140 public: |
|
141 friend struct CallRecord; |
|
142 friend class HostCallRecord; |
|
143 |
|
144 #if ENABLE(OPCODE_SAMPLING) |
|
145 class CallRecord : public Noncopyable { |
|
146 public: |
|
147 CallRecord(SamplingTool* samplingTool) |
|
148 : m_samplingTool(samplingTool) |
|
149 , m_savedSample(samplingTool->m_sample) |
|
150 , m_savedCodeBlock(samplingTool->m_codeBlock) |
|
151 { |
|
152 } |
|
153 |
|
154 ~CallRecord() |
|
155 { |
|
156 m_samplingTool->m_sample = m_savedSample; |
|
157 m_samplingTool->m_codeBlock = m_savedCodeBlock; |
|
158 } |
|
159 |
|
160 private: |
|
161 SamplingTool* m_samplingTool; |
|
162 intptr_t m_savedSample; |
|
163 CodeBlock* m_savedCodeBlock; |
|
164 }; |
|
165 |
|
166 class HostCallRecord : public CallRecord { |
|
167 public: |
|
168 HostCallRecord(SamplingTool* samplingTool) |
|
169 : CallRecord(samplingTool) |
|
170 { |
|
171 samplingTool->m_sample |= 0x1; |
|
172 } |
|
173 }; |
|
174 #else |
|
175 class CallRecord : public Noncopyable { |
|
176 public: |
|
177 CallRecord(SamplingTool*) |
|
178 { |
|
179 } |
|
180 }; |
|
181 |
|
182 class HostCallRecord : public CallRecord { |
|
183 public: |
|
184 HostCallRecord(SamplingTool* samplingTool) |
|
185 : CallRecord(samplingTool) |
|
186 { |
|
187 } |
|
188 }; |
|
189 #endif |
|
190 |
|
191 SamplingTool(Interpreter* interpreter) |
|
192 : m_interpreter(interpreter) |
|
193 , m_codeBlock(0) |
|
194 , m_sample(0) |
|
195 , m_sampleCount(0) |
|
196 , m_opcodeSampleCount(0) |
|
197 #if ENABLE(CODEBLOCK_SAMPLING) |
|
198 , m_scopeSampleMap(new ScriptSampleRecordMap()) |
|
199 #endif |
|
200 { |
|
201 memset(m_opcodeSamples, 0, sizeof(m_opcodeSamples)); |
|
202 memset(m_opcodeSamplesInCTIFunctions, 0, sizeof(m_opcodeSamplesInCTIFunctions)); |
|
203 } |
|
204 |
|
205 ~SamplingTool() |
|
206 { |
|
207 #if ENABLE(CODEBLOCK_SAMPLING) |
|
208 deleteAllValues(*m_scopeSampleMap); |
|
209 #endif |
|
210 } |
|
211 |
|
212 void setup(); |
|
213 void dump(ExecState*); |
|
214 |
|
215 void notifyOfScope(ScriptExecutable* scope); |
|
216 |
|
217 void sample(CodeBlock* codeBlock, Instruction* vPC) |
|
218 { |
|
219 ASSERT(!(reinterpret_cast<intptr_t>(vPC) & 0x3)); |
|
220 m_codeBlock = codeBlock; |
|
221 m_sample = reinterpret_cast<intptr_t>(vPC); |
|
222 } |
|
223 |
|
224 CodeBlock** codeBlockSlot() { return &m_codeBlock; } |
|
225 intptr_t* sampleSlot() { return &m_sample; } |
|
226 |
|
227 void* encodeSample(Instruction* vPC, bool inCTIFunction = false, bool inHostFunction = false) |
|
228 { |
|
229 ASSERT(!(reinterpret_cast<intptr_t>(vPC) & 0x3)); |
|
230 return reinterpret_cast<void*>(reinterpret_cast<intptr_t>(vPC) | (static_cast<intptr_t>(inCTIFunction) << 1) | static_cast<intptr_t>(inHostFunction)); |
|
231 } |
|
232 |
|
233 static void sample(); |
|
234 |
|
235 private: |
|
236 class Sample { |
|
237 public: |
|
238 Sample(volatile intptr_t sample, CodeBlock* volatile codeBlock) |
|
239 : m_sample(sample) |
|
240 , m_codeBlock(codeBlock) |
|
241 { |
|
242 } |
|
243 |
|
244 bool isNull() { return !m_sample; } |
|
245 CodeBlock* codeBlock() { return m_codeBlock; } |
|
246 Instruction* vPC() { return reinterpret_cast<Instruction*>(m_sample & ~0x3); } |
|
247 bool inHostFunction() { return m_sample & 0x1; } |
|
248 bool inCTIFunction() { return m_sample & 0x2; } |
|
249 |
|
250 private: |
|
251 intptr_t m_sample; |
|
252 CodeBlock* m_codeBlock; |
|
253 }; |
|
254 |
|
255 void doRun(); |
|
256 static SamplingTool* s_samplingTool; |
|
257 |
|
258 Interpreter* m_interpreter; |
|
259 |
|
260 // State tracked by the main thread, used by the sampling thread. |
|
261 CodeBlock* m_codeBlock; |
|
262 intptr_t m_sample; |
|
263 |
|
264 // Gathered sample data. |
|
265 long long m_sampleCount; |
|
266 long long m_opcodeSampleCount; |
|
267 unsigned m_opcodeSamples[numOpcodeIDs]; |
|
268 unsigned m_opcodeSamplesInCTIFunctions[numOpcodeIDs]; |
|
269 |
|
270 #if ENABLE(CODEBLOCK_SAMPLING) |
|
271 Mutex m_scriptSampleMapMutex; |
|
272 OwnPtr<ScriptSampleRecordMap> m_scopeSampleMap; |
|
273 #endif |
|
274 }; |
|
275 |
|
276 // AbstractSamplingCounter: |
|
277 // |
|
278 // Implements a named set of counters, printed on exit if ENABLE(SAMPLING_COUNTERS). |
|
279 // See subclasses below, SamplingCounter, GlobalSamplingCounter and DeletableSamplingCounter. |
|
280 class AbstractSamplingCounter { |
|
281 friend class JIT; |
|
282 friend class DeletableSamplingCounter; |
|
283 public: |
|
284 void count(uint32_t count = 1) |
|
285 { |
|
286 m_counter += count; |
|
287 } |
|
288 |
|
289 static void dump(); |
|
290 |
|
291 protected: |
|
292 // Effectively the contructor, however called lazily in the case of GlobalSamplingCounter. |
|
293 void init(const char* name) |
|
294 { |
|
295 m_counter = 0; |
|
296 m_name = name; |
|
297 |
|
298 // Set m_next to point to the head of the chain, and inform whatever is |
|
299 // currently at the head that this node will now hold the pointer to it. |
|
300 m_next = s_abstractSamplingCounterChain; |
|
301 s_abstractSamplingCounterChain->m_referer = &m_next; |
|
302 // Add this node to the head of the list. |
|
303 s_abstractSamplingCounterChain = this; |
|
304 m_referer = &s_abstractSamplingCounterChain; |
|
305 } |
|
306 |
|
307 int64_t m_counter; |
|
308 const char* m_name; |
|
309 AbstractSamplingCounter* m_next; |
|
310 // This is a pointer to the pointer to this node in the chain; used to |
|
311 // allow fast linked list deletion. |
|
312 AbstractSamplingCounter** m_referer; |
|
313 // Null object used to detect end of static chain. |
|
314 static AbstractSamplingCounter s_abstractSamplingCounterChainEnd; |
|
315 static AbstractSamplingCounter* s_abstractSamplingCounterChain; |
|
316 static bool s_completed; |
|
317 }; |
|
318 |
|
319 #if ENABLE(SAMPLING_COUNTERS) |
|
320 // SamplingCounter: |
|
321 // |
|
322 // This class is suitable and (hopefully!) convenient for cases where a counter is |
|
323 // required within the scope of a single function. It can be instantiated as a |
|
324 // static variable since it contains a constructor but not a destructor (static |
|
325 // variables in WebKit cannot have destructors). |
|
326 // |
|
327 // For example: |
|
328 // |
|
329 // void someFunction() |
|
330 // { |
|
331 // static SamplingCounter countMe("This is my counter. There are many like it, but this one is mine."); |
|
332 // countMe.count(); |
|
333 // // ... |
|
334 // } |
|
335 // |
|
336 class SamplingCounter : public AbstractSamplingCounter { |
|
337 public: |
|
338 SamplingCounter(const char* name) { init(name); } |
|
339 }; |
|
340 |
|
341 // GlobalSamplingCounter: |
|
342 // |
|
343 // This class is suitable for use where a counter is to be declared globally, |
|
344 // since it contains neither a constructor nor destructor. Instead, ensure |
|
345 // that 'name()' is called to provide the counter with a name (and also to |
|
346 // allow it to be printed out on exit). |
|
347 // |
|
348 // GlobalSamplingCounter globalCounter; |
|
349 // |
|
350 // void firstFunction() |
|
351 // { |
|
352 // // Put this within a function that is definitely called! |
|
353 // // (Or alternatively alongside all calls to 'count()'). |
|
354 // globalCounter.name("I Name You Destroyer."); |
|
355 // globalCounter.count(); |
|
356 // // ... |
|
357 // } |
|
358 // |
|
359 // void secondFunction() |
|
360 // { |
|
361 // globalCounter.count(); |
|
362 // // ... |
|
363 // } |
|
364 // |
|
365 class GlobalSamplingCounter : public AbstractSamplingCounter { |
|
366 public: |
|
367 void name(const char* name) |
|
368 { |
|
369 // Global objects should be mapped in zero filled memory, so this should |
|
370 // be a safe (albeit not necessarily threadsafe) check for 'first call'. |
|
371 if (!m_next) |
|
372 init(name); |
|
373 } |
|
374 }; |
|
375 |
|
376 // DeletableSamplingCounter: |
|
377 // |
|
378 // The above classes (SamplingCounter, GlobalSamplingCounter), are intended for |
|
379 // use within a global or static scope, and as such cannot have a destructor. |
|
380 // This means there is no convenient way for them to remove themselves from the |
|
381 // static list of counters, and should an instance of either class be freed |
|
382 // before 'dump()' has walked over the list it will potentially walk over an |
|
383 // invalid pointer. |
|
384 // |
|
385 // This class is intended for use where the counter may possibly be deleted before |
|
386 // the program exits. Should this occur, the counter will print it's value to |
|
387 // stderr, and remove itself from the static list. Example: |
|
388 // |
|
389 // DeletableSamplingCounter* counter = new DeletableSamplingCounter("The Counter With No Name"); |
|
390 // counter->count(); |
|
391 // delete counter; |
|
392 // |
|
393 class DeletableSamplingCounter : public AbstractSamplingCounter { |
|
394 public: |
|
395 DeletableSamplingCounter(const char* name) { init(name); } |
|
396 |
|
397 ~DeletableSamplingCounter() |
|
398 { |
|
399 if (!s_completed) |
|
400 fprintf(stderr, "DeletableSamplingCounter \"%s\" deleted early (with count %lld)\n", m_name, m_counter); |
|
401 // Our m_referer pointer should know where the pointer to this node is, |
|
402 // and m_next should know that this node is the previous node in the list. |
|
403 ASSERT(*m_referer == this); |
|
404 ASSERT(m_next->m_referer == &m_next); |
|
405 // Remove this node from the list, and inform m_next that we have done so. |
|
406 m_next->m_referer = m_referer; |
|
407 *m_referer = m_next; |
|
408 } |
|
409 }; |
|
410 #endif |
|
411 |
|
412 } // namespace JSC |
|
413 |
|
414 #endif // SamplingTool_h |