a0696b134a726cb6ecc4f9e7394cadb2c9517372
[WebKit-https.git] / Source / JavaScriptCore / bytecode / SamplingTool.h
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(JSGlobalData& globalData, ScriptExecutable* executable)
99             : m_executable(globalData, 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         Global<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 {
146             WTF_MAKE_NONCOPYABLE(CallRecord);
147         public:
148             CallRecord(SamplingTool* samplingTool)
149                 : m_samplingTool(samplingTool)
150                 , m_savedSample(samplingTool->m_sample)
151                 , m_savedCodeBlock(samplingTool->m_codeBlock)
152             {
153             }
154
155             ~CallRecord()
156             {
157                 m_samplingTool->m_sample = m_savedSample;
158                 m_samplingTool->m_codeBlock = m_savedCodeBlock;
159             }
160
161         private:
162             SamplingTool* m_samplingTool;
163             intptr_t m_savedSample;
164             CodeBlock* m_savedCodeBlock;
165         };
166         
167         class HostCallRecord : public CallRecord {
168         public:
169             HostCallRecord(SamplingTool* samplingTool)
170                 : CallRecord(samplingTool)
171             {
172                 samplingTool->m_sample |= 0x1;
173             }
174         };
175 #else
176         class CallRecord {
177             WTF_MAKE_NONCOPYABLE(CallRecord);
178         public:
179             CallRecord(SamplingTool*)
180             {
181             }
182         };
183
184         class HostCallRecord : public CallRecord {
185         public:
186             HostCallRecord(SamplingTool* samplingTool)
187                 : CallRecord(samplingTool)
188             {
189             }
190         };
191 #endif        
192
193         SamplingTool(Interpreter* interpreter)
194             : m_interpreter(interpreter)
195             , m_codeBlock(0)
196             , m_sample(0)
197             , m_sampleCount(0)
198             , m_opcodeSampleCount(0)
199 #if ENABLE(CODEBLOCK_SAMPLING)
200             , m_scopeSampleMap(new ScriptSampleRecordMap())
201 #endif
202         {
203             memset(m_opcodeSamples, 0, sizeof(m_opcodeSamples));
204             memset(m_opcodeSamplesInCTIFunctions, 0, sizeof(m_opcodeSamplesInCTIFunctions));
205         }
206
207         ~SamplingTool()
208         {
209 #if ENABLE(CODEBLOCK_SAMPLING)
210             deleteAllValues(*m_scopeSampleMap);
211 #endif
212         }
213
214         void setup();
215         void dump(ExecState*);
216
217         void notifyOfScope(ScriptExecutable* scope);
218
219         void sample(CodeBlock* codeBlock, Instruction* vPC)
220         {
221             ASSERT(!(reinterpret_cast<intptr_t>(vPC) & 0x3));
222             m_codeBlock = codeBlock;
223             m_sample = reinterpret_cast<intptr_t>(vPC);
224         }
225
226         CodeBlock** codeBlockSlot() { return &m_codeBlock; }
227         intptr_t* sampleSlot() { return &m_sample; }
228
229         void* encodeSample(Instruction* vPC, bool inCTIFunction = false, bool inHostFunction = false)
230         {
231             ASSERT(!(reinterpret_cast<intptr_t>(vPC) & 0x3));
232             return reinterpret_cast<void*>(reinterpret_cast<intptr_t>(vPC) | (static_cast<intptr_t>(inCTIFunction) << 1) | static_cast<intptr_t>(inHostFunction));
233         }
234
235         static void sample();
236
237     private:
238         class Sample {
239         public:
240             Sample(volatile intptr_t sample, CodeBlock* volatile codeBlock)
241                 : m_sample(sample)
242                 , m_codeBlock(codeBlock)
243             {
244             }
245             
246             bool isNull() { return !m_sample; }
247             CodeBlock* codeBlock() { return m_codeBlock; }
248             Instruction* vPC() { return reinterpret_cast<Instruction*>(m_sample & ~0x3); }
249             bool inHostFunction() { return m_sample & 0x1; }
250             bool inCTIFunction() { return m_sample & 0x2; }
251
252         private:
253             intptr_t m_sample;
254             CodeBlock* m_codeBlock;
255         };
256
257         void doRun();
258         static SamplingTool* s_samplingTool;
259         
260         Interpreter* m_interpreter;
261         
262         // State tracked by the main thread, used by the sampling thread.
263         CodeBlock* m_codeBlock;
264         intptr_t m_sample;
265
266         // Gathered sample data.
267         long long m_sampleCount;
268         long long m_opcodeSampleCount;
269         unsigned m_opcodeSamples[numOpcodeIDs];
270         unsigned m_opcodeSamplesInCTIFunctions[numOpcodeIDs];
271         
272 #if ENABLE(CODEBLOCK_SAMPLING)
273         Mutex m_scriptSampleMapMutex;
274         OwnPtr<ScriptSampleRecordMap> m_scopeSampleMap;
275 #endif
276     };
277
278     // AbstractSamplingCounter:
279     //
280     // Implements a named set of counters, printed on exit if ENABLE(SAMPLING_COUNTERS).
281     // See subclasses below, SamplingCounter, GlobalSamplingCounter and DeletableSamplingCounter.
282     class AbstractSamplingCounter {
283         friend class DeletableSamplingCounter;
284     public:
285         void count(uint32_t count = 1)
286         {
287             m_counter += count;
288         }
289
290         static void dump();
291
292         int64_t* addressOfCounter() { return &m_counter; }
293
294     protected:
295         // Effectively the contructor, however called lazily in the case of GlobalSamplingCounter.
296         void init(const char* name)
297         {
298             m_counter = 0;
299             m_name = name;
300
301             // Set m_next to point to the head of the chain, and inform whatever is
302             // currently at the head that this node will now hold the pointer to it.
303             m_next = s_abstractSamplingCounterChain;
304             s_abstractSamplingCounterChain->m_referer = &m_next;
305             // Add this node to the head of the list.
306             s_abstractSamplingCounterChain = this;
307             m_referer = &s_abstractSamplingCounterChain;
308         }
309
310         int64_t m_counter;
311         const char* m_name;
312         AbstractSamplingCounter* m_next;
313         // This is a pointer to the pointer to this node in the chain; used to
314         // allow fast linked list deletion.
315         AbstractSamplingCounter** m_referer;
316         // Null object used to detect end of static chain.
317         static AbstractSamplingCounter s_abstractSamplingCounterChainEnd;
318         static AbstractSamplingCounter* s_abstractSamplingCounterChain;
319         static bool s_completed;
320     };
321
322 #if ENABLE(SAMPLING_COUNTERS)
323     // SamplingCounter:
324     //
325     // This class is suitable and (hopefully!) convenient for cases where a counter is
326     // required within the scope of a single function.  It can be instantiated as a
327     // static variable since it contains a constructor but not a destructor (static
328     // variables in WebKit cannot have destructors).
329     //
330     // For example:
331     //
332     // void someFunction()
333     // {
334     //     static SamplingCounter countMe("This is my counter.  There are many like it, but this one is mine.");
335     //     countMe.count();
336     //     // ...
337     // }
338     //
339     class SamplingCounter : public AbstractSamplingCounter {
340     public:
341         SamplingCounter(const char* name) { init(name); }
342     };
343
344     // GlobalSamplingCounter:
345     //
346     // This class is suitable for use where a counter is to be declared globally,
347     // since it contains neither a constructor nor destructor.  Instead, ensure
348     // that 'name()' is called to provide the counter with a name (and also to
349     // allow it to be printed out on exit).
350     //
351     // GlobalSamplingCounter globalCounter;
352     //
353     // void firstFunction()
354     // {
355     //     // Put this within a function that is definitely called!
356     //     // (Or alternatively alongside all calls to 'count()').
357     //     globalCounter.name("I Name You Destroyer.");
358     //     globalCounter.count();
359     //     // ...
360     // }
361     //
362     // void secondFunction()
363     // {
364     //     globalCounter.count();
365     //     // ...
366     // }
367     //
368     class GlobalSamplingCounter : public AbstractSamplingCounter {
369     public:
370         void name(const char* name)
371         {
372             // Global objects should be mapped in zero filled memory, so this should
373             // be a safe (albeit not necessarily threadsafe) check for 'first call'.
374             if (!m_next)
375                 init(name);
376         }
377     };
378
379     // DeletableSamplingCounter:
380     //
381     // The above classes (SamplingCounter, GlobalSamplingCounter), are intended for
382     // use within a global or static scope, and as such cannot have a destructor.
383     // This means there is no convenient way for them to remove themselves from the
384     // static list of counters, and should an instance of either class be freed
385     // before 'dump()' has walked over the list it will potentially walk over an
386     // invalid pointer.
387     //
388     // This class is intended for use where the counter may possibly be deleted before
389     // the program exits.  Should this occur, the counter will print it's value to
390     // stderr, and remove itself from the static list.  Example:
391     //
392     // DeletableSamplingCounter* counter = new DeletableSamplingCounter("The Counter With No Name");
393     // counter->count();
394     // delete counter;
395     //
396     class DeletableSamplingCounter : public AbstractSamplingCounter {
397     public:
398         DeletableSamplingCounter(const char* name) { init(name); }
399
400         ~DeletableSamplingCounter()
401         {
402             if (!s_completed)
403                 fprintf(stderr, "DeletableSamplingCounter \"%s\" deleted early (with count %lld)\n", m_name, m_counter);
404             // Our m_referer pointer should know where the pointer to this node is,
405             // and m_next should know that this node is the previous node in the list.
406             ASSERT(*m_referer == this);
407             ASSERT(m_next->m_referer == &m_next);
408             // Remove this node from the list, and inform m_next that we have done so.
409             m_next->m_referer = m_referer;
410             *m_referer = m_next;
411         }
412     };
413 #endif
414
415 } // namespace JSC
416
417 #endif // SamplingTool_h