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