JavaScriptCore/bytecode/SamplingTool.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     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().ascii(), codeBlock->lineNumberForBytecodeOffset(exec, 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(exec, 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