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