1 /* 2 * Copyright (C) 2008, 2009 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 #include "config.h" 30 #include "SamplingTool.h" 31 32 #include "CodeBlock.h" 33 #include "Interpreter.h" 34 #include "Opcode.h" 35 36 #if !OS(WINDOWS) 37 #include <unistd.h> 38 #endif 39 40 namespace JSC { 41 42 #if ENABLE(SAMPLING_FLAGS) 43 44 void SamplingFlags::sample() 45 { 46 uint32_t mask = static_cast<uint32_t>(1 << 31); 47 unsigned index; 48 49 for (index = 0; index < 32; ++index) { 50 if (mask & s_flags) 51 break; 52 mask >>= 1; 53 } 54 55 s_flagCounts[32 - index]++; 56 } 57 58 void SamplingFlags::start() 59 { 60 for (unsigned i = 0; i <= 32; ++i) 61 s_flagCounts[i] = 0; 62 } 63 void SamplingFlags::stop() 64 { 65 uint64_t total = 0; 66 for (unsigned i = 0; i <= 32; ++i) 67 total += s_flagCounts[i]; 68 69 if (total) { 70 printf("\nSamplingFlags: sample counts with flags set: (%lld total)\n", total); 71 for (unsigned i = 0; i <= 32; ++i) { 72 if (s_flagCounts[i]) 73 printf(" [ %02d ] : %lld\t\t(%03.2f%%)\n", i, s_flagCounts[i], (100.0 * s_flagCounts[i]) / total); 74 } 75 printf("\n"); 76 } else 77 printf("\nSamplingFlags: no samples.\n\n"); 78 } 79 uint64_t SamplingFlags::s_flagCounts[33]; 80 81 #else 82 void SamplingFlags::start() {} 83 void SamplingFlags::stop() {} 84 #endif 85 86 /* 87 Start with flag 16 set. 88 By doing this the monitoring of lower valued flags will be masked out 89 until flag 16 is explictly cleared. 90 */ 91 uint32_t SamplingFlags::s_flags = 1 << 15; 92 93 94 #if OS(WINDOWS) 95 96 static void sleepForMicroseconds(unsigned us) 97 { 98 unsigned ms = us / 1000; 99 if (us && !ms) 100 ms = 1; 101 Sleep(ms); 102 } 103 104 #else 105 106 static void sleepForMicroseconds(unsigned us) 107 { 108 usleep(us); 109 } 110 111 #endif 112 113 static inline unsigned hertz2us(unsigned hertz) 114 { 115 return 1000000 / hertz; 116 } 117 118 119 SamplingTool* SamplingTool::s_samplingTool = 0; 120 121 122 bool SamplingThread::s_running = false; 123 unsigned SamplingThread::s_hertz = 10000; 124 ThreadIdentifier SamplingThread::s_samplingThread; 125 126 void* SamplingThread::threadStartFunc(void*) 127 { 128 while (s_running) { 129 sleepForMicroseconds(hertz2us(s_hertz)); 130 131 #if ENABLE(SAMPLING_FLAGS) 132 SamplingFlags::sample(); 133 #endif 134 #if ENABLE(OPCODE_SAMPLING) 135 SamplingTool::sample(); 136 #endif 137 } 138 139 return 0; 140 } 141 142 143 void SamplingThread::start(unsigned hertz) 144 { 145 ASSERT(!s_running); 146 s_running = true; 147 s_hertz = hertz; 148 149 s_samplingThread = createThread(threadStartFunc, 0, "JavaScriptCore::Sampler"); 150 } 151 152 void SamplingThread::stop() 153 { 154 ASSERT(s_running); 155 s_running = false; 156 waitForThreadCompletion(s_samplingThread, 0); 157 } 158 159 160 void ScriptSampleRecord::sample(CodeBlock* codeBlock, Instruction* vPC) 161 { 162 if (!m_samples) { 163 m_size = codeBlock->instructions().size(); 164 m_samples = static_cast<int*>(calloc(m_size, sizeof(int))); 165 m_codeBlock = codeBlock; 166 } 167 168 ++m_sampleCount; 169 170 unsigned offest = vPC - codeBlock->instructions().begin(); 171 // Since we don't read and write codeBlock and vPC atomically, this check 172 // can fail if we sample mid op_call / op_ret. 173 if (offest < m_size) { 174 m_samples[offest]++; 175 m_opcodeSampleCount++; 176 } 177 } 178 179 void SamplingTool::doRun() 180 { 181 Sample sample(m_sample, m_codeBlock); 182 ++m_sampleCount; 183 184 if (sample.isNull()) 185 return; 186 187 if (!sample.inHostFunction()) { 188 unsigned opcodeID = m_interpreter->getOpcodeID(sample.vPC()[0].u.opcode); 189 190 ++m_opcodeSampleCount; 191 ++m_opcodeSamples[opcodeID]; 192 193 if (sample.inCTIFunction()) 194 m_opcodeSamplesInCTIFunctions[opcodeID]++; 195 } 196 197 #if ENABLE(CODEBLOCK_SAMPLING) 198 if (CodeBlock* codeBlock = sample.codeBlock()) { 199 MutexLocker locker(m_scriptSampleMapMutex); 200 ScriptSampleRecord* record = m_scopeSampleMap->get(codeBlock->ownerExecutable()); 201 ASSERT(record); 202 record->sample(codeBlock, sample.vPC()); 203 } 204 #endif 205 } 206 207 void SamplingTool::sample() 208 { 209 s_samplingTool->doRun(); 210 } 211 212 void SamplingTool::notifyOfScope(ScriptExecutable* script) 213 { 214 #if ENABLE(CODEBLOCK_SAMPLING) 215 MutexLocker locker(m_scriptSampleMapMutex); 216 m_scopeSampleMap->set(script, new ScriptSampleRecord(script)); 217 #else 218 UNUSED_PARAM(script); 219 #endif 220 } 221 222 void SamplingTool::setup() 223 { 224 s_samplingTool = this; 225 } 226 227 #if ENABLE(OPCODE_SAMPLING) 228 229 struct OpcodeSampleInfo { 230 OpcodeID opcode; 231 long long count; 232 long long countInCTIFunctions; 233 }; 234 235 struct LineCountInfo { 236 unsigned line; 237 unsigned count; 238 }; 239 240 static int compareOpcodeIndicesSampling(const void* left, const void* right) 241 { 242 const OpcodeSampleInfo* leftSampleInfo = reinterpret_cast<const OpcodeSampleInfo*>(left); 243 const OpcodeSampleInfo* rightSampleInfo = reinterpret_cast<const OpcodeSampleInfo*>(right); 244 245 return (leftSampleInfo->count < rightSampleInfo->count) ? 1 : (leftSampleInfo->count > rightSampleInfo->count) ? -1 : 0; 246 } 247 248 #if ENABLE(CODEBLOCK_SAMPLING) 249 static int compareLineCountInfoSampling(const void* left, const void* right) 250 { 251 const LineCountInfo* leftLineCount = reinterpret_cast<const LineCountInfo*>(left); 252 const LineCountInfo* rightLineCount = reinterpret_cast<const LineCountInfo*>(right); 253 254 return (leftLineCount->line > rightLineCount->line) ? 1 : (leftLineCount->line < rightLineCount->line) ? -1 : 0; 255 } 256 257 static int compareScriptSampleRecords(const void* left, const void* right) 258 { 259 const ScriptSampleRecord* const leftValue = *static_cast<const ScriptSampleRecord* const *>(left); 260 const ScriptSampleRecord* const rightValue = *static_cast<const ScriptSampleRecord* const *>(right); 261 262 return (leftValue->m_sampleCount < rightValue->m_sampleCount) ? 1 : (leftValue->m_sampleCount > rightValue->m_sampleCount) ? -1 : 0; 263 } 264 #endif 265 266 void SamplingTool::dump(ExecState* exec) 267 { 268 // Tidies up SunSpider output by removing short scripts - such a small number of samples would likely not be useful anyhow. 269 if (m_sampleCount < 10) 270 return; 271 272 // (1) Build and sort 'opcodeSampleInfo' array. 273 274 OpcodeSampleInfo opcodeSampleInfo[numOpcodeIDs]; 275 for (int i = 0; i < numOpcodeIDs; ++i) { 276 opcodeSampleInfo[i].opcode = static_cast<OpcodeID>(i); 277 opcodeSampleInfo[i].count = m_opcodeSamples[i]; 278 opcodeSampleInfo[i].countInCTIFunctions = m_opcodeSamplesInCTIFunctions[i]; 279 } 280 281 qsort(opcodeSampleInfo, numOpcodeIDs, sizeof(OpcodeSampleInfo), compareOpcodeIndicesSampling); 282 283 // (2) Print Opcode sampling results. 284 285 printf("\nBytecode samples [*]\n"); 286 printf(" sample %% of %% of | cti cti %%\n"); 287 printf("opcode count VM total | count of self\n"); 288 printf("------------------------------------------------------- | ----------------\n"); 289 290 for (int i = 0; i < numOpcodeIDs; ++i) { 291 long long count = opcodeSampleInfo[i].count; 292 if (!count) 293 continue; 294 295 OpcodeID opcodeID = opcodeSampleInfo[i].opcode; 296 297 const char* opcodeName = opcodeNames[opcodeID]; 298 const char* opcodePadding = padOpcodeName(opcodeID, 28); 299 double percentOfVM = (static_cast<double>(count) * 100) / m_opcodeSampleCount; 300 double percentOfTotal = (static_cast<double>(count) * 100) / m_sampleCount; 301 long long countInCTIFunctions = opcodeSampleInfo[i].countInCTIFunctions; 302 double percentInCTIFunctions = (static_cast<double>(countInCTIFunctions) * 100) / count; 303 fprintf(stdout, "%s:%s%-6lld %.3f%%\t%.3f%%\t | %-6lld %.3f%%\n", opcodeName, opcodePadding, count, percentOfVM, percentOfTotal, countInCTIFunctions, percentInCTIFunctions); 304 } 305 306 printf("\n[*] Samples inside host code are not charged to any Bytecode.\n\n"); 307 printf("\tSamples inside VM:\t\t%lld / %lld (%.3f%%)\n", m_opcodeSampleCount, m_sampleCount, (static_cast<double>(m_opcodeSampleCount) * 100) / m_sampleCount); 308 printf("\tSamples inside host code:\t%lld / %lld (%.3f%%)\n\n", m_sampleCount - m_opcodeSampleCount, m_sampleCount, (static_cast<double>(m_sampleCount - m_opcodeSampleCount) * 100) / m_sampleCount); 309 printf("\tsample count:\tsamples inside this opcode\n"); 310 printf("\t%% of VM:\tsample count / all opcode samples\n"); 311 printf("\t%% of total:\tsample count / all samples\n"); 312 printf("\t--------------\n"); 313 printf("\tcti count:\tsamples inside a CTI function called by this opcode\n"); 314 printf("\tcti %% of self:\tcti count / sample count\n"); 315 316 #if ENABLE(CODEBLOCK_SAMPLING) 317 318 // (3) Build and sort 'codeBlockSamples' array. 319 320 int scopeCount = m_scopeSampleMap->size(); 321 Vector<ScriptSampleRecord*> codeBlockSamples(scopeCount); 322 ScriptSampleRecordMap::iterator iter = m_scopeSampleMap->begin(); 323 for (int i = 0; i < scopeCount; ++i, ++iter) 324 codeBlockSamples[i] = iter->second; 325 326 qsort(codeBlockSamples.begin(), scopeCount, sizeof(ScriptSampleRecord*), compareScriptSampleRecords); 327 328 // (4) Print data from 'codeBlockSamples' array. 329 330 printf("\nCodeBlock samples\n\n"); 331 332 for (int i = 0; i < scopeCount; ++i) { 333 ScriptSampleRecord* record = codeBlockSamples[i]; 334 CodeBlock* codeBlock = record->m_codeBlock; 335 336 double blockPercent = (record->m_sampleCount * 100.0) / m_sampleCount; 337 338 if (blockPercent >= 1) { 339 //Instruction* code = codeBlock->instructions().begin(); 340 printf("#%d: %s:%d: %d / %lld (%.3f%%)\n", i + 1, record->m_executable->sourceURL().utf8().data(), codeBlock->lineNumberForBytecodeOffset(0), record->m_sampleCount, m_sampleCount, blockPercent); 341 if (i < 10) { 342 HashMap<unsigned,unsigned> lineCounts; 343 codeBlock->dump(exec); 344 345 printf(" Opcode and line number samples [*]\n\n"); 346 for (unsigned op = 0; op < record->m_size; ++op) { 347 int count = record->m_samples[op]; 348 if (count) { 349 printf(" [% 4d] has sample count: % 4d\n", op, count); 350 unsigned line = codeBlock->lineNumberForBytecodeOffset(op); 351 lineCounts.set(line, (lineCounts.contains(line) ? lineCounts.get(line) : 0) + count); 352 } 353 } 354 printf("\n"); 355 356 int linesCount = lineCounts.size(); 357 Vector<LineCountInfo> lineCountInfo(linesCount); 358 int lineno = 0; 359 for (HashMap<unsigned,unsigned>::iterator iter = lineCounts.begin(); iter != lineCounts.end(); ++iter, ++lineno) { 360 lineCountInfo[lineno].line = iter->first; 361 lineCountInfo[lineno].count = iter->second; 362 } 363 364 qsort(lineCountInfo.begin(), linesCount, sizeof(LineCountInfo), compareLineCountInfoSampling); 365 366 for (lineno = 0; lineno < linesCount; ++lineno) { 367 printf(" Line #%d has sample count %d.\n", lineCountInfo[lineno].line, lineCountInfo[lineno].count); 368 } 369 printf("\n"); 370 printf(" [*] Samples inside host code are charged to the calling Bytecode.\n"); 371 printf(" Samples on a call / return boundary are not charged to a specific opcode or line.\n\n"); 372 printf(" Samples on a call / return boundary: %d / %d (%.3f%%)\n\n", record->m_sampleCount - record->m_opcodeSampleCount, record->m_sampleCount, (static_cast<double>(record->m_sampleCount - record->m_opcodeSampleCount) * 100) / record->m_sampleCount); 373 } 374 } 375 } 376 #else 377 UNUSED_PARAM(exec); 378 #endif 379 } 380 381 #else 382 383 void SamplingTool::dump(ExecState*) 384 { 385 } 386 387 #endif 388 389 void AbstractSamplingCounter::dump() 390 { 391 #if ENABLE(SAMPLING_COUNTERS) 392 if (s_abstractSamplingCounterChain != &s_abstractSamplingCounterChainEnd) { 393 printf("\nSampling Counter Values:\n"); 394 for (AbstractSamplingCounter* currCounter = s_abstractSamplingCounterChain; (currCounter != &s_abstractSamplingCounterChainEnd); currCounter = currCounter->m_next) 395 printf("\t%s\t: %lld\n", currCounter->m_name, currCounter->m_counter); 396 printf("\n\n"); 397 } 398 s_completed = true; 399 #endif 400 } 401 402 AbstractSamplingCounter AbstractSamplingCounter::s_abstractSamplingCounterChainEnd; 403 AbstractSamplingCounter* AbstractSamplingCounter::s_abstractSamplingCounterChain = &s_abstractSamplingCounterChainEnd; 404 bool AbstractSamplingCounter::s_completed = false; 405 406 } // namespace JSC 407