2 * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
30 #include "SamplingTool.h"
32 #include "CodeBlock.h"
33 #include "Interpreter.h"
42 #if ENABLE(SAMPLING_FLAGS)
44 void SamplingFlags::sample()
46 uint32_t mask
= 1 << 31;
49 for (index
= 0; index
< 32; ++index
) {
55 s_flagCounts
[32 - index
]++;
58 void SamplingFlags::start()
60 for (unsigned i
= 0; i
<= 32; ++i
)
63 void SamplingFlags::stop()
66 for (unsigned i
= 0; i
<= 32; ++i
)
67 total
+= s_flagCounts
[i
];
70 printf("\nSamplingFlags: sample counts with flags set: (%lld total)\n", total
);
71 for (unsigned i
= 0; i
<= 32; ++i
) {
73 printf(" [ %02d ] : %lld\t\t(%03.2f%%)\n", i
, s_flagCounts
[i
], (100.0 * s_flagCounts
[i
]) / total
);
77 printf("\nSamplingFlags: no samples.\n\n");
79 uint64_t SamplingFlags::s_flagCounts
[33];
82 void SamplingFlags::start() {}
83 void SamplingFlags::stop() {}
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.
91 uint32_t SamplingFlags::s_flags
= 1 << 15;
96 static void sleepForMicroseconds(unsigned us
)
98 unsigned ms
= us
/ 1000;
106 static void sleepForMicroseconds(unsigned us
)
113 static inline unsigned hertz2us(unsigned hertz
)
115 return 1000000 / hertz
;
119 SamplingTool
* SamplingTool::s_samplingTool
= 0;
122 bool SamplingThread::s_running
= false;
123 unsigned SamplingThread::s_hertz
= 10000;
124 ThreadIdentifier
SamplingThread::s_samplingThread
;
126 void* SamplingThread::threadStartFunc(void*)
129 sleepForMicroseconds(hertz2us(s_hertz
));
131 #if ENABLE(SAMPLING_FLAGS)
132 SamplingFlags::sample();
134 #if ENABLE(OPCODE_SAMPLING)
135 SamplingTool::sample();
143 void SamplingThread::start(unsigned hertz
)
149 s_samplingThread
= createThread(threadStartFunc
, 0, "JavaScriptCore::Sampler");
152 void SamplingThread::stop()
156 waitForThreadCompletion(s_samplingThread
, 0);
160 void ScriptSampleRecord::sample(CodeBlock
* codeBlock
, Instruction
* vPC
)
163 m_size
= codeBlock
->instructions().size();
164 m_samples
= static_cast<int*>(calloc(m_size
, sizeof(int)));
165 m_codeBlock
= codeBlock
;
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
) {
175 m_opcodeSampleCount
++;
179 void SamplingTool::doRun()
181 Sample
sample(m_sample
, m_codeBlock
);
187 if (!sample
.inHostFunction()) {
188 unsigned opcodeID
= m_interpreter
->getOpcodeID(sample
.vPC()[0].u
.opcode
);
190 ++m_opcodeSampleCount
;
191 ++m_opcodeSamples
[opcodeID
];
193 if (sample
.inCTIFunction())
194 m_opcodeSamplesInCTIFunctions
[opcodeID
]++;
197 #if ENABLE(CODEBLOCK_SAMPLING)
198 if (CodeBlock
* codeBlock
= sample
.codeBlock()) {
199 MutexLocker
locker(m_scriptSampleMapMutex
);
200 ScriptSampleRecord
* record
= m_scopeSampleMap
->get(codeBlock
->ownerExecutable());
202 record
->sample(codeBlock
, sample
.vPC());
207 void SamplingTool::sample()
209 s_samplingTool
->doRun();
212 void SamplingTool::notifyOfScope(ScriptExecutable
* script
)
214 #if ENABLE(CODEBLOCK_SAMPLING)
215 MutexLocker
locker(m_scriptSampleMapMutex
);
216 m_scopeSampleMap
->set(script
, new ScriptSampleRecord(script
));
218 UNUSED_PARAM(script
);
222 void SamplingTool::setup()
224 s_samplingTool
= this;
227 #if ENABLE(OPCODE_SAMPLING)
229 struct OpcodeSampleInfo
{
232 long long countInCTIFunctions
;
235 struct LineCountInfo
{
240 static int compareOpcodeIndicesSampling(const void* left
, const void* right
)
242 const OpcodeSampleInfo
* leftSampleInfo
= reinterpret_cast<const OpcodeSampleInfo
*>(left
);
243 const OpcodeSampleInfo
* rightSampleInfo
= reinterpret_cast<const OpcodeSampleInfo
*>(right
);
245 return (leftSampleInfo
->count
< rightSampleInfo
->count
) ? 1 : (leftSampleInfo
->count
> rightSampleInfo
->count
) ? -1 : 0;
248 #if ENABLE(CODEBLOCK_SAMPLING)
249 static int compareLineCountInfoSampling(const void* left
, const void* right
)
251 const LineCountInfo
* leftLineCount
= reinterpret_cast<const LineCountInfo
*>(left
);
252 const LineCountInfo
* rightLineCount
= reinterpret_cast<const LineCountInfo
*>(right
);
254 return (leftLineCount
->line
> rightLineCount
->line
) ? 1 : (leftLineCount
->line
< rightLineCount
->line
) ? -1 : 0;
257 static int compareScriptSampleRecords(const void* left
, const void* right
)
259 const ScriptSampleRecord
* const leftValue
= *static_cast<const ScriptSampleRecord
* const *>(left
);
260 const ScriptSampleRecord
* const rightValue
= *static_cast<const ScriptSampleRecord
* const *>(right
);
262 return (leftValue
->m_sampleCount
< rightValue
->m_sampleCount
) ? 1 : (leftValue
->m_sampleCount
> rightValue
->m_sampleCount
) ? -1 : 0;
266 void SamplingTool::dump(ExecState
* exec
)
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)
272 // (1) Build and sort 'opcodeSampleInfo' array.
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
];
281 qsort(opcodeSampleInfo
, numOpcodeIDs
, sizeof(OpcodeSampleInfo
), compareOpcodeIndicesSampling
);
283 // (2) Print Opcode sampling results.
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");
290 for (int i
= 0; i
< numOpcodeIDs
; ++i
) {
291 long long count
= opcodeSampleInfo
[i
].count
;
295 OpcodeID opcodeID
= opcodeSampleInfo
[i
].opcode
;
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
);
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");
316 #if ENABLE(CODEBLOCK_SAMPLING)
318 // (3) Build and sort 'codeBlockSamples' array.
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
;
326 qsort(codeBlockSamples
.begin(), scopeCount
, sizeof(ScriptSampleRecord
*), compareScriptSampleRecords
);
328 // (4) Print data from 'codeBlockSamples' array.
330 printf("\nCodeBlock samples\n\n");
332 for (int i
= 0; i
< scopeCount
; ++i
) {
333 ScriptSampleRecord
* record
= codeBlockSamples
[i
];
334 CodeBlock
* codeBlock
= record
->m_codeBlock
;
336 double blockPercent
= (record
->m_sampleCount
* 100.0) / m_sampleCount
;
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().UTF8String().c_str(), codeBlock
->lineNumberForBytecodeOffset(exec
, 0), record
->m_sampleCount
, m_sampleCount
, blockPercent
);
342 HashMap
<unsigned,unsigned> lineCounts
;
343 codeBlock
->dump(exec
);
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
];
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
);
356 int linesCount
= lineCounts
.size();
357 Vector
<LineCountInfo
> lineCountInfo(linesCount
);
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
;
364 qsort(lineCountInfo
.begin(), linesCount
, sizeof(LineCountInfo
), compareLineCountInfoSampling
);
366 for (lineno
= 0; lineno
< linesCount
; ++lineno
) {
367 printf(" Line #%d has sample count %d.\n", lineCountInfo
[lineno
].line
, lineCountInfo
[lineno
].count
);
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
);
383 void SamplingTool::dump(ExecState
*)
389 void AbstractSamplingCounter::dump()
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
);
402 AbstractSamplingCounter
AbstractSamplingCounter::s_abstractSamplingCounterChainEnd
;
403 AbstractSamplingCounter
* AbstractSamplingCounter::s_abstractSamplingCounterChain
= &s_abstractSamplingCounterChainEnd
;
404 bool AbstractSamplingCounter::s_completed
= false;