| /* | 
 |  * Copyright (C) 2008, 2013 Apple Inc. All rights reserved. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions | 
 |  * are met: | 
 |  * | 
 |  * 1.  Redistributions of source code must retain the above copyright | 
 |  *     notice, this list of conditions and the following disclaimer. | 
 |  * 2.  Redistributions in binary form must reproduce the above copyright | 
 |  *     notice, this list of conditions and the following disclaimer in the | 
 |  *     documentation and/or other materials provided with the distribution. | 
 |  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of | 
 |  *     its contributors may be used to endorse or promote products derived | 
 |  *     from this software without specific prior written permission. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | 
 |  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | 
 |  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
 |  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | 
 |  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | 
 |  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 
 |  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | 
 |  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
 |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
 |  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
 |  */ | 
 |  | 
 | #ifndef SamplingTool_h | 
 | #define SamplingTool_h | 
 |  | 
 | #include "Strong.h" | 
 | #include "Opcode.h" | 
 | #include "SamplingCounter.h" | 
 | #include <wtf/Assertions.h> | 
 | #include <wtf/Atomics.h> | 
 | #include <wtf/HashMap.h> | 
 | #include <wtf/MainThread.h> | 
 | #include <wtf/Spectrum.h> | 
 | #include <wtf/Threading.h> | 
 |  | 
 | namespace JSC { | 
 |  | 
 |     class ScriptExecutable; | 
 |  | 
 |     class SamplingFlags { | 
 |     public: | 
 |         JS_EXPORT_PRIVATE static void start(); | 
 |         JS_EXPORT_PRIVATE static void stop(); | 
 |  | 
 | #if ENABLE(SAMPLING_FLAGS) | 
 |         static void setFlag(unsigned flag) | 
 |         { | 
 |             ASSERT(flag >= 1); | 
 |             ASSERT(flag <= 32); | 
 |             s_flags |= 1u << (flag - 1); | 
 |         } | 
 |  | 
 |         static void clearFlag(unsigned flag) | 
 |         { | 
 |             ASSERT(flag >= 1); | 
 |             ASSERT(flag <= 32); | 
 |             s_flags &= ~(1u << (flag - 1)); | 
 |         } | 
 |  | 
 |         static void sample(); | 
 |  | 
 |         class ScopedFlag { | 
 |         public: | 
 |             ScopedFlag(int flag) | 
 |                 : m_flag(flag) | 
 |             { | 
 |                 setFlag(flag); | 
 |             } | 
 |  | 
 |             ~ScopedFlag() | 
 |             { | 
 |                 clearFlag(m_flag); | 
 |             } | 
 |  | 
 |         private: | 
 |             int m_flag; | 
 |         }; | 
 |      | 
 |         static const void* addressOfFlags() | 
 |         { | 
 |             return &s_flags; | 
 |         } | 
 |  | 
 | #endif | 
 |     private: | 
 |         JS_EXPORTDATA static uint32_t s_flags; | 
 | #if ENABLE(SAMPLING_FLAGS) | 
 |         static uint64_t s_flagCounts[33]; | 
 | #endif | 
 |     }; | 
 |  | 
 | #if ENABLE(SAMPLING_REGIONS) | 
 |     class SamplingRegion { | 
 |     public: | 
 |         // Create a scoped sampling region using a C string constant name that describes | 
 |         // what you are doing. This must be a string constant that persists for the | 
 |         // lifetime of the process and is immutable. | 
 |         SamplingRegion(const char* name) | 
 |         { | 
 |             if (!isMainThread()) { | 
 |                 m_name = 0; | 
 |                 return; | 
 |             } | 
 |              | 
 |             m_name = name; | 
 |             exchangeCurrent(this, &m_previous); | 
 |             ASSERT(!m_previous || m_previous > this); | 
 |         } | 
 |          | 
 |         ~SamplingRegion() | 
 |         { | 
 |             if (!m_name) | 
 |                 return; | 
 |              | 
 |             ASSERT(bitwise_cast<SamplingRegion*>(s_currentOrReserved & ~1) == this); | 
 |             exchangeCurrent(m_previous); | 
 |         } | 
 |          | 
 |         static void sample(); | 
 |          | 
 |         JS_EXPORT_PRIVATE static void dump(); | 
 |          | 
 |     private: | 
 |         const char* m_name; | 
 |         SamplingRegion* m_previous; | 
 |  | 
 |         static void exchangeCurrent(SamplingRegion* current, SamplingRegion** previousPtr = 0) | 
 |         { | 
 |             uintptr_t previous; | 
 |             while (true) { | 
 |                 previous = s_currentOrReserved; | 
 |                  | 
 |                 // If it's reserved (i.e. sampling thread is reading it), loop around. | 
 |                 if (previous & 1) { | 
 | #if OS(UNIX) | 
 |                     sched_yield(); | 
 | #endif | 
 |                     continue; | 
 |                 } | 
 |                  | 
 |                 // If we're going to CAS, then make sure previous is set. | 
 |                 if (previousPtr) | 
 |                     *previousPtr = bitwise_cast<SamplingRegion*>(previous); | 
 |                  | 
 |                 if (WTF::weakCompareAndSwapUIntPtr(&s_currentOrReserved, previous, bitwise_cast<uintptr_t>(current))) | 
 |                     break; | 
 |             } | 
 |         } | 
 |          | 
 |         static void dumpInternal(); | 
 |  | 
 |         class Locker { | 
 |         public: | 
 |             Locker(); | 
 |             ~Locker(); | 
 |         }; | 
 |  | 
 |         static volatile uintptr_t s_currentOrReserved; | 
 |          | 
 |         // rely on identity hashing of string constants | 
 |         static Spectrum<const char*>* s_spectrum; | 
 |          | 
 |         static unsigned long s_noneOfTheAbove; | 
 |          | 
 |         static unsigned s_numberOfSamplesSinceDump; | 
 |     }; | 
 | #else // ENABLE(SAMPLING_REGIONS) | 
 |     class SamplingRegion { | 
 |     public: | 
 |         SamplingRegion(const char*) { } | 
 |         JS_EXPORT_PRIVATE void dump(); | 
 |     }; | 
 | #endif // ENABLE(SAMPLING_REGIONS) | 
 |  | 
 |     class CodeBlock; | 
 |     class ExecState; | 
 |     class Interpreter; | 
 |     class ScopeNode; | 
 |     struct Instruction; | 
 |  | 
 |     struct ScriptSampleRecord { | 
 |         ScriptSampleRecord(VM& vm, ScriptExecutable* executable) | 
 |             : m_executable(vm, executable) | 
 |             , m_codeBlock(0) | 
 |             , m_sampleCount(0) | 
 |             , m_opcodeSampleCount(0) | 
 |             , m_samples(0) | 
 |             , m_size(0) | 
 |         { | 
 |         } | 
 |          | 
 |         ~ScriptSampleRecord() | 
 |         { | 
 |             if (m_samples) | 
 |                 free(m_samples); | 
 |         } | 
 |          | 
 |         void sample(CodeBlock*, Instruction*); | 
 |  | 
 |         Strong<ScriptExecutable> m_executable; | 
 |         CodeBlock* m_codeBlock; | 
 |         int m_sampleCount; | 
 |         int m_opcodeSampleCount; | 
 |         int* m_samples; | 
 |         unsigned m_size; | 
 |     }; | 
 |  | 
 |     typedef HashMap<ScriptExecutable*, OwnPtr<ScriptSampleRecord> > ScriptSampleRecordMap; | 
 |  | 
 |     class SamplingThread { | 
 |     public: | 
 |         // Sampling thread state. | 
 |         static bool s_running; | 
 |         static unsigned s_hertz; | 
 |         static ThreadIdentifier s_samplingThread; | 
 |  | 
 |         JS_EXPORT_PRIVATE static void start(unsigned hertz=10000); | 
 |         JS_EXPORT_PRIVATE static void stop(); | 
 |  | 
 |         static void threadStartFunc(void*); | 
 |     }; | 
 |  | 
 |     class SamplingTool { | 
 |     public: | 
 |         friend struct CallRecord; | 
 |          | 
 | #if ENABLE(OPCODE_SAMPLING) | 
 |         class CallRecord { | 
 |             WTF_MAKE_NONCOPYABLE(CallRecord); | 
 |         public: | 
 |             CallRecord(SamplingTool* samplingTool, bool isHostCall = false) | 
 |                 : m_samplingTool(samplingTool) | 
 |                 , m_savedSample(samplingTool->m_sample) | 
 |                 , m_savedCodeBlock(samplingTool->m_codeBlock) | 
 |             { | 
 |                 if (isHostcall) | 
 |                     samplingTool->m_sample |= 0x1; | 
 |             } | 
 |  | 
 |             ~CallRecord() | 
 |             { | 
 |                 m_samplingTool->m_sample = m_savedSample; | 
 |                 m_samplingTool->m_codeBlock = m_savedCodeBlock; | 
 |             } | 
 |  | 
 |         private: | 
 |             SamplingTool* m_samplingTool; | 
 |             intptr_t m_savedSample; | 
 |             CodeBlock* m_savedCodeBlock; | 
 |         }; | 
 | #else | 
 |         class CallRecord { | 
 |             WTF_MAKE_NONCOPYABLE(CallRecord); | 
 |         public: | 
 |             CallRecord(SamplingTool*, bool = false) | 
 |             { | 
 |             } | 
 |         }; | 
 | #endif | 
 |  | 
 |         SamplingTool(Interpreter* interpreter) | 
 |             : m_interpreter(interpreter) | 
 |             , m_codeBlock(0) | 
 |             , m_sample(0) | 
 |             , m_sampleCount(0) | 
 |             , m_opcodeSampleCount(0) | 
 | #if ENABLE(CODEBLOCK_SAMPLING) | 
 |             , m_scopeSampleMap(adoptPtr(new ScriptSampleRecordMap)) | 
 | #endif | 
 |         { | 
 |             memset(m_opcodeSamples, 0, sizeof(m_opcodeSamples)); | 
 |             memset(m_opcodeSamplesInCTIFunctions, 0, sizeof(m_opcodeSamplesInCTIFunctions)); | 
 |         } | 
 |  | 
 |         JS_EXPORT_PRIVATE void setup(); | 
 |         void dump(ExecState*); | 
 |  | 
 |         void notifyOfScope(VM&, ScriptExecutable* scope); | 
 |  | 
 |         void sample(CodeBlock* codeBlock, Instruction* vPC) | 
 |         { | 
 |             ASSERT(!(reinterpret_cast<intptr_t>(vPC) & 0x3)); | 
 |             m_codeBlock = codeBlock; | 
 |             m_sample = reinterpret_cast<intptr_t>(vPC); | 
 |         } | 
 |  | 
 |         CodeBlock** codeBlockSlot() { return &m_codeBlock; } | 
 |         intptr_t* sampleSlot() { return &m_sample; } | 
 |  | 
 |         void* encodeSample(Instruction* vPC, bool inCTIFunction = false, bool inHostFunction = false) | 
 |         { | 
 |             ASSERT(!(reinterpret_cast<intptr_t>(vPC) & 0x3)); | 
 |             return reinterpret_cast<void*>(reinterpret_cast<intptr_t>(vPC) | (static_cast<intptr_t>(inCTIFunction) << 1) | static_cast<intptr_t>(inHostFunction)); | 
 |         } | 
 |  | 
 |         static void sample(); | 
 |  | 
 |     private: | 
 |         class Sample { | 
 |         public: | 
 |             Sample(volatile intptr_t sample, CodeBlock* volatile codeBlock) | 
 |                 : m_sample(sample) | 
 |                 , m_codeBlock(codeBlock) | 
 |             { | 
 |             } | 
 |              | 
 |             bool isNull() { return !m_sample; } | 
 |             CodeBlock* codeBlock() { return m_codeBlock; } | 
 |             Instruction* vPC() { return reinterpret_cast<Instruction*>(m_sample & ~0x3); } | 
 |             bool inHostFunction() { return m_sample & 0x1; } | 
 |             bool inCTIFunction() { return m_sample & 0x2; } | 
 |  | 
 |         private: | 
 |             intptr_t m_sample; | 
 |             CodeBlock* m_codeBlock; | 
 |         }; | 
 |  | 
 |         void doRun(); | 
 |         static SamplingTool* s_samplingTool; | 
 |          | 
 |         Interpreter* m_interpreter; | 
 |          | 
 |         // State tracked by the main thread, used by the sampling thread. | 
 |         CodeBlock* m_codeBlock; | 
 |         intptr_t m_sample; | 
 |  | 
 |         // Gathered sample data. | 
 |         long long m_sampleCount; | 
 |         long long m_opcodeSampleCount; | 
 |         unsigned m_opcodeSamples[numOpcodeIDs]; | 
 |         unsigned m_opcodeSamplesInCTIFunctions[numOpcodeIDs]; | 
 |          | 
 | #if ENABLE(CODEBLOCK_SAMPLING) | 
 |         Mutex m_scriptSampleMapMutex; | 
 |         OwnPtr<ScriptSampleRecordMap> m_scopeSampleMap; | 
 | #endif | 
 |     }; | 
 |  | 
 | } // namespace JSC | 
 |  | 
 | #endif // SamplingTool_h |