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