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