/* * Copyright (C) 2008, 2013, 2015 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 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 #include #include #include #include #include #include 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(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(previous); if (WTF::weakCompareAndSwapUIntPtr(&s_currentOrReserved, previous, bitwise_cast(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* 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 m_executable; CodeBlock* m_codeBlock; int m_sampleCount; int m_opcodeSampleCount; int* m_samples; unsigned m_size; }; typedef HashMap> 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 { WTF_MAKE_FAST_ALLOCATED; 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(std::make_unique) #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(vPC) & 0x3)); m_codeBlock = codeBlock; m_sample = reinterpret_cast(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(vPC) & 0x3)); return reinterpret_cast(reinterpret_cast(vPC) | (static_cast(inCTIFunction) << 1) | static_cast(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(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) Lock m_scriptSampleMapMutex; std::unique_ptr m_scopeSampleMap; #endif }; } // namespace JSC #endif // SamplingTool_h