• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         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:
CallRecord(SamplingTool * samplingTool)147             CallRecord(SamplingTool* samplingTool)
148                 : m_samplingTool(samplingTool)
149                 , m_savedSample(samplingTool->m_sample)
150                 , m_savedCodeBlock(samplingTool->m_codeBlock)
151             {
152             }
153 
~CallRecord()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:
HostCallRecord(SamplingTool * samplingTool)168             HostCallRecord(SamplingTool* samplingTool)
169                 : CallRecord(samplingTool)
170             {
171                 samplingTool->m_sample |= 0x1;
172             }
173         };
174 #else
175         class CallRecord : public Noncopyable {
176         public:
CallRecord(SamplingTool *)177             CallRecord(SamplingTool*)
178             {
179             }
180         };
181 
182         class HostCallRecord : public CallRecord {
183         public:
HostCallRecord(SamplingTool * samplingTool)184             HostCallRecord(SamplingTool* samplingTool)
185                 : CallRecord(samplingTool)
186             {
187             }
188         };
189 #endif
190 
SamplingTool(Interpreter * interpreter)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 
~SamplingTool()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 
sample(CodeBlock * codeBlock,Instruction * vPC)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 
codeBlockSlot()224         CodeBlock** codeBlockSlot() { return &m_codeBlock; }
sampleSlot()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:
Sample(volatile intptr_t sample,CodeBlock * volatile codeBlock)238             Sample(volatile intptr_t sample, CodeBlock* volatile codeBlock)
239                 : m_sample(sample)
240                 , m_codeBlock(codeBlock)
241             {
242             }
243 
isNull()244             bool isNull() { return !m_sample; }
codeBlock()245             CodeBlock* codeBlock() { return m_codeBlock; }
vPC()246             Instruction* vPC() { return reinterpret_cast<Instruction*>(m_sample & ~0x3); }
inHostFunction()247             bool inHostFunction() { return m_sample & 0x1; }
inCTIFunction()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.
init(const char * name)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:
SamplingCounter(const char * name)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:
name(const char * name)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:
DeletableSamplingCounter(const char * name)395         DeletableSamplingCounter(const char* name) { init(name); }
396 
~DeletableSamplingCounter()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
415