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