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
= static_cast<uint32_t>(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 dataLog("\nSamplingFlags: sample counts with flags set: (%lld total)\n", total
);
71 for (unsigned i
= 0; i
<= 32; ++i
) {
73 dataLog(" [ %02d ] : %lld\t\t(%03.2f%%)\n", i
, s_flagCounts
[i
], (100.0 * s_flagCounts
[i
]) / total
);
77 dataLog("\nSamplingFlags: no samples.\n\n");
79 uint64_t SamplingFlags::s_flagCounts
[33];
82 void SamplingFlags::start() {}
83 void SamplingFlags::stop() {}
86 #if ENABLE(SAMPLING_REGIONS)
87 volatile uintptr_t SamplingRegion::s_currentOrReserved
;
88 Spectrum
<const char*>* SamplingRegion::s_spectrum
;
89 unsigned long SamplingRegion::s_noneOfTheAbove
;
90 unsigned SamplingRegion::s_numberOfSamplesSinceDump
;
92 SamplingRegion::Locker::Locker()
96 previous
= s_currentOrReserved
;
103 if (WTF::weakCompareAndSwapUIntPtr(&s_currentOrReserved
, previous
, previous
| 1))
108 SamplingRegion::Locker::~Locker()
110 // We don't need the CAS, but we do it out of an
111 // abundance of caution (and because it gives us a memory fence, which is
115 previous
= s_currentOrReserved
;
116 } while (!WTF::weakCompareAndSwapUIntPtr(&s_currentOrReserved
, previous
, previous
& ~1));
119 void SamplingRegion::sample()
121 // Make sure we lock s_current.
124 // Create a spectrum if we don't have one already.
126 s_spectrum
= new Spectrum
<const char*>();
128 ASSERT(s_currentOrReserved
& 1);
130 // Walk the region stack, and record each region we see.
131 SamplingRegion
* region
= bitwise_cast
<SamplingRegion
*>(s_currentOrReserved
& ~1);
133 for (; region
; region
= region
->m_previous
)
134 s_spectrum
->add(region
->m_name
);
138 if (s_numberOfSamplesSinceDump
++ == SamplingThread::s_hertz
) {
139 s_numberOfSamplesSinceDump
= 0;
144 void SamplingRegion::dump()
151 void SamplingRegion::dumpInternal()
154 dataLog("\nSamplingRegion: was never sampled.\n\n");
158 Vector
<Spectrum
<const char*>::KeyAndCount
> list
= s_spectrum
->buildList();
160 unsigned long total
= s_noneOfTheAbove
;
161 for (unsigned i
= list
.size(); i
--;)
162 total
+= list
[i
].count
;
164 dataLog("\nSamplingRegion: sample counts for regions: (%lu samples)\n", total
);
166 for (unsigned i
= list
.size(); i
--;)
167 dataLog(" %3.2lf%% %s\n", (100.0 * list
[i
].count
) / total
, list
[i
].key
);
169 #else // ENABLE(SAMPLING_REGIONS)
170 void SamplingRegion::dump() { }
171 #endif // ENABLE(SAMPLING_REGIONS)
174 Start with flag 16 set.
175 By doing this the monitoring of lower valued flags will be masked out
176 until flag 16 is explictly cleared.
178 uint32_t SamplingFlags::s_flags
= 1 << 15;
183 static void sleepForMicroseconds(unsigned us
)
185 unsigned ms
= us
/ 1000;
193 static void sleepForMicroseconds(unsigned us
)
200 static inline unsigned hertz2us(unsigned hertz
)
202 return 1000000 / hertz
;
206 SamplingTool
* SamplingTool::s_samplingTool
= 0;
209 bool SamplingThread::s_running
= false;
210 unsigned SamplingThread::s_hertz
= 10000;
211 ThreadIdentifier
SamplingThread::s_samplingThread
;
213 void SamplingThread::threadStartFunc(void*)
216 sleepForMicroseconds(hertz2us(s_hertz
));
218 #if ENABLE(SAMPLING_FLAGS)
219 SamplingFlags::sample();
221 #if ENABLE(SAMPLING_REGIONS)
222 SamplingRegion::sample();
224 #if ENABLE(OPCODE_SAMPLING)
225 SamplingTool::sample();
231 void SamplingThread::start(unsigned hertz
)
237 s_samplingThread
= createThread(threadStartFunc
, 0, "JavaScriptCore::Sampler");
240 void SamplingThread::stop()
244 waitForThreadCompletion(s_samplingThread
);
248 void ScriptSampleRecord::sample(CodeBlock
* codeBlock
, Instruction
* vPC
)
251 m_size
= codeBlock
->instructions().size();
252 m_samples
= static_cast<int*>(calloc(m_size
, sizeof(int)));
253 m_codeBlock
= codeBlock
;
258 unsigned offest
= vPC
- codeBlock
->instructions().begin();
259 // Since we don't read and write codeBlock and vPC atomically, this check
260 // can fail if we sample mid op_call / op_ret.
261 if (offest
< m_size
) {
263 m_opcodeSampleCount
++;
267 void SamplingTool::doRun()
269 Sample
sample(m_sample
, m_codeBlock
);
275 if (!sample
.inHostFunction()) {
276 unsigned opcodeID
= m_interpreter
->getOpcodeID(sample
.vPC()[0].u
.opcode
);
278 ++m_opcodeSampleCount
;
279 ++m_opcodeSamples
[opcodeID
];
281 if (sample
.inCTIFunction())
282 m_opcodeSamplesInCTIFunctions
[opcodeID
]++;
285 #if ENABLE(CODEBLOCK_SAMPLING)
286 if (CodeBlock
* codeBlock
= sample
.codeBlock()) {
287 MutexLocker
locker(m_scriptSampleMapMutex
);
288 ScriptSampleRecord
* record
= m_scopeSampleMap
->get(codeBlock
->ownerExecutable());
290 record
->sample(codeBlock
, sample
.vPC());
295 void SamplingTool::sample()
297 s_samplingTool
->doRun();
300 void SamplingTool::notifyOfScope(JSGlobalData
& globalData
, ScriptExecutable
* script
)
302 #if ENABLE(CODEBLOCK_SAMPLING)
303 MutexLocker
locker(m_scriptSampleMapMutex
);
304 m_scopeSampleMap
->set(script
, adoptPtr(new ScriptSampleRecord(globalData
, script
)));
306 UNUSED_PARAM(globalData
);
307 UNUSED_PARAM(script
);
311 void SamplingTool::setup()
313 s_samplingTool
= this;
316 #if ENABLE(OPCODE_SAMPLING)
318 struct OpcodeSampleInfo
{
321 long long countInCTIFunctions
;
324 struct LineCountInfo
{
329 static int compareOpcodeIndicesSampling(const void* left
, const void* right
)
331 const OpcodeSampleInfo
* leftSampleInfo
= reinterpret_cast<const OpcodeSampleInfo
*>(left
);
332 const OpcodeSampleInfo
* rightSampleInfo
= reinterpret_cast<const OpcodeSampleInfo
*>(right
);
334 return (leftSampleInfo
->count
< rightSampleInfo
->count
) ? 1 : (leftSampleInfo
->count
> rightSampleInfo
->count
) ? -1 : 0;
337 #if ENABLE(CODEBLOCK_SAMPLING)
338 static int compareLineCountInfoSampling(const void* left
, const void* right
)
340 const LineCountInfo
* leftLineCount
= reinterpret_cast<const LineCountInfo
*>(left
);
341 const LineCountInfo
* rightLineCount
= reinterpret_cast<const LineCountInfo
*>(right
);
343 return (leftLineCount
->line
> rightLineCount
->line
) ? 1 : (leftLineCount
->line
< rightLineCount
->line
) ? -1 : 0;
346 static int compareScriptSampleRecords(const void* left
, const void* right
)
348 const ScriptSampleRecord
* const leftValue
= *static_cast<const ScriptSampleRecord
* const *>(left
);
349 const ScriptSampleRecord
* const rightValue
= *static_cast<const ScriptSampleRecord
* const *>(right
);
351 return (leftValue
->m_sampleCount
< rightValue
->m_sampleCount
) ? 1 : (leftValue
->m_sampleCount
> rightValue
->m_sampleCount
) ? -1 : 0;
355 void SamplingTool::dump(ExecState
* exec
)
357 // Tidies up SunSpider output by removing short scripts - such a small number of samples would likely not be useful anyhow.
358 if (m_sampleCount
< 10)
361 // (1) Build and sort 'opcodeSampleInfo' array.
363 OpcodeSampleInfo opcodeSampleInfo
[numOpcodeIDs
];
364 for (int i
= 0; i
< numOpcodeIDs
; ++i
) {
365 opcodeSampleInfo
[i
].opcode
= static_cast<OpcodeID
>(i
);
366 opcodeSampleInfo
[i
].count
= m_opcodeSamples
[i
];
367 opcodeSampleInfo
[i
].countInCTIFunctions
= m_opcodeSamplesInCTIFunctions
[i
];
370 qsort(opcodeSampleInfo
, numOpcodeIDs
, sizeof(OpcodeSampleInfo
), compareOpcodeIndicesSampling
);
372 // (2) Print Opcode sampling results.
374 dataLog("\nBytecode samples [*]\n");
375 dataLog(" sample %% of %% of | cti cti %%\n");
376 dataLog("opcode count VM total | count of self\n");
377 dataLog("------------------------------------------------------- | ----------------\n");
379 for (int i
= 0; i
< numOpcodeIDs
; ++i
) {
380 long long count
= opcodeSampleInfo
[i
].count
;
384 OpcodeID opcodeID
= opcodeSampleInfo
[i
].opcode
;
386 const char* opcodeName
= opcodeNames
[opcodeID
];
387 const char* opcodePadding
= padOpcodeName(opcodeID
, 28);
388 double percentOfVM
= (static_cast<double>(count
) * 100) / m_opcodeSampleCount
;
389 double percentOfTotal
= (static_cast<double>(count
) * 100) / m_sampleCount
;
390 long long countInCTIFunctions
= opcodeSampleInfo
[i
].countInCTIFunctions
;
391 double percentInCTIFunctions
= (static_cast<double>(countInCTIFunctions
) * 100) / count
;
392 debugDebugPrintf("%s:%s%-6lld %.3f%%\t%.3f%%\t | %-6lld %.3f%%\n", opcodeName
, opcodePadding
, count
, percentOfVM
, percentOfTotal
, countInCTIFunctions
, percentInCTIFunctions
);
395 dataLog("\n[*] Samples inside host code are not charged to any Bytecode.\n\n");
396 dataLog("\tSamples inside VM:\t\t%lld / %lld (%.3f%%)\n", m_opcodeSampleCount
, m_sampleCount
, (static_cast<double>(m_opcodeSampleCount
) * 100) / m_sampleCount
);
397 dataLog("\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
);
398 dataLog("\tsample count:\tsamples inside this opcode\n");
399 dataLog("\t%% of VM:\tsample count / all opcode samples\n");
400 dataLog("\t%% of total:\tsample count / all samples\n");
401 dataLog("\t--------------\n");
402 dataLog("\tcti count:\tsamples inside a CTI function called by this opcode\n");
403 dataLog("\tcti %% of self:\tcti count / sample count\n");
405 #if ENABLE(CODEBLOCK_SAMPLING)
407 // (3) Build and sort 'codeBlockSamples' array.
409 int scopeCount
= m_scopeSampleMap
->size();
410 Vector
<ScriptSampleRecord
*> codeBlockSamples(scopeCount
);
411 ScriptSampleRecordMap::iterator iter
= m_scopeSampleMap
->begin();
412 for (int i
= 0; i
< scopeCount
; ++i
, ++iter
)
413 codeBlockSamples
[i
] = iter
->second
.get();
415 qsort(codeBlockSamples
.begin(), scopeCount
, sizeof(ScriptSampleRecord
*), compareScriptSampleRecords
);
417 // (4) Print data from 'codeBlockSamples' array.
419 dataLog("\nCodeBlock samples\n\n");
421 for (int i
= 0; i
< scopeCount
; ++i
) {
422 ScriptSampleRecord
* record
= codeBlockSamples
[i
];
423 CodeBlock
* codeBlock
= record
->m_codeBlock
;
425 double blockPercent
= (record
->m_sampleCount
* 100.0) / m_sampleCount
;
427 if (blockPercent
>= 1) {
428 //Instruction* code = codeBlock->instructions().begin();
429 dataLog("#%d: %s:%d: %d / %lld (%.3f%%)\n", i
+ 1, record
->m_executable
->sourceURL().utf8().data(), codeBlock
->lineNumberForBytecodeOffset(0), record
->m_sampleCount
, m_sampleCount
, blockPercent
);
431 HashMap
<unsigned,unsigned> lineCounts
;
432 codeBlock
->dump(exec
);
434 dataLog(" Opcode and line number samples [*]\n\n");
435 for (unsigned op
= 0; op
< record
->m_size
; ++op
) {
436 int count
= record
->m_samples
[op
];
438 dataLog(" [% 4d] has sample count: % 4d\n", op
, count
);
439 unsigned line
= codeBlock
->lineNumberForBytecodeOffset(op
);
440 lineCounts
.set(line
, (lineCounts
.contains(line
) ? lineCounts
.get(line
) : 0) + count
);
445 int linesCount
= lineCounts
.size();
446 Vector
<LineCountInfo
> lineCountInfo(linesCount
);
448 for (HashMap
<unsigned,unsigned>::iterator iter
= lineCounts
.begin(); iter
!= lineCounts
.end(); ++iter
, ++lineno
) {
449 lineCountInfo
[lineno
].line
= iter
->first
;
450 lineCountInfo
[lineno
].count
= iter
->second
;
453 qsort(lineCountInfo
.begin(), linesCount
, sizeof(LineCountInfo
), compareLineCountInfoSampling
);
455 for (lineno
= 0; lineno
< linesCount
; ++lineno
) {
456 dataLog(" Line #%d has sample count %d.\n", lineCountInfo
[lineno
].line
, lineCountInfo
[lineno
].count
);
459 dataLog(" [*] Samples inside host code are charged to the calling Bytecode.\n");
460 dataLog(" Samples on a call / return boundary are not charged to a specific opcode or line.\n\n");
461 dataLog(" 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
);
472 void SamplingTool::dump(ExecState
*)