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 void ScopeSampleRecord::sample(CodeBlock
* codeBlock
, Instruction
* vPC
)
45 m_size
= codeBlock
->instructions().size();
46 m_samples
= static_cast<int*>(calloc(m_size
, sizeof(int)));
47 m_codeBlock
= codeBlock
;
52 unsigned offest
= vPC
- codeBlock
->instructions().begin();
53 // Since we don't read and write codeBlock and vPC atomically, this check
54 // can fail if we sample mid op_call / op_ret.
55 if (offest
< m_size
) {
57 m_opcodeSampleCount
++;
63 static void sleepForMicroseconds(unsigned us
)
65 unsigned ms
= us
/ 1000;
73 static void sleepForMicroseconds(unsigned us
)
80 static inline unsigned hertz2us(unsigned hertz
)
82 return 1000000 / hertz
;
85 void SamplingTool::run()
88 sleepForMicroseconds(hertz2us(m_hertz
));
90 Sample
sample(m_sample
, m_codeBlock
);
96 if (!sample
.inHostFunction()) {
97 unsigned opcodeID
= m_interpreter
->getOpcodeID(sample
.vPC()[0].u
.opcode
);
99 ++m_opcodeSampleCount
;
100 ++m_opcodeSamples
[opcodeID
];
102 if (sample
.inCTIFunction())
103 m_opcodeSamplesInCTIFunctions
[opcodeID
]++;
106 #if ENABLE(CODEBLOCK_SAMPLING)
107 MutexLocker
locker(m_scopeSampleMapMutex
);
108 ScopeSampleRecord
* record
= m_scopeSampleMap
->get(sample
.codeBlock()->ownerNode());
110 record
->sample(sample
.codeBlock(), sample
.vPC());
115 void* SamplingTool::threadStartFunc(void* samplingTool
)
117 reinterpret_cast<SamplingTool
*>(samplingTool
)->run();
121 void SamplingTool::notifyOfScope(ScopeNode
* scope
)
123 MutexLocker
locker(m_scopeSampleMapMutex
);
124 m_scopeSampleMap
->set(scope
, new ScopeSampleRecord(scope
));
127 void SamplingTool::start(unsigned hertz
)
133 m_samplingThread
= createThread(threadStartFunc
, this, "JavaScriptCore::Sampler");
136 void SamplingTool::stop()
140 waitForThreadCompletion(m_samplingThread
, 0);
143 #if ENABLE(OPCODE_SAMPLING)
145 struct OpcodeSampleInfo
{
148 long long countInCTIFunctions
;
151 struct LineCountInfo
{
156 static int compareLineCountInfoSampling(const void* left
, const void* right
)
158 const LineCountInfo
* leftLineCount
= reinterpret_cast<const LineCountInfo
*>(left
);
159 const LineCountInfo
* rightLineCount
= reinterpret_cast<const LineCountInfo
*>(right
);
161 return (leftLineCount
->line
> rightLineCount
->line
) ? 1 : (leftLineCount
->line
< rightLineCount
->line
) ? -1 : 0;
164 static int compareOpcodeIndicesSampling(const void* left
, const void* right
)
166 const OpcodeSampleInfo
* leftSampleInfo
= reinterpret_cast<const OpcodeSampleInfo
*>(left
);
167 const OpcodeSampleInfo
* rightSampleInfo
= reinterpret_cast<const OpcodeSampleInfo
*>(right
);
169 return (leftSampleInfo
->count
< rightSampleInfo
->count
) ? 1 : (leftSampleInfo
->count
> rightSampleInfo
->count
) ? -1 : 0;
172 static int compareScopeSampleRecords(const void* left
, const void* right
)
174 const ScopeSampleRecord
* const leftValue
= *static_cast<const ScopeSampleRecord
* const *>(left
);
175 const ScopeSampleRecord
* const rightValue
= *static_cast<const ScopeSampleRecord
* const *>(right
);
177 return (leftValue
->m_sampleCount
< rightValue
->m_sampleCount
) ? 1 : (leftValue
->m_sampleCount
> rightValue
->m_sampleCount
) ? -1 : 0;
180 void SamplingTool::dump(ExecState
* exec
)
182 // Tidies up SunSpider output by removing short scripts - such a small number of samples would likely not be useful anyhow.
183 if (m_sampleCount
< 10)
186 // (1) Build and sort 'opcodeSampleInfo' array.
188 OpcodeSampleInfo opcodeSampleInfo
[numOpcodeIDs
];
189 for (int i
= 0; i
< numOpcodeIDs
; ++i
) {
190 opcodeSampleInfo
[i
].opcode
= static_cast<OpcodeID
>(i
);
191 opcodeSampleInfo
[i
].count
= m_opcodeSamples
[i
];
192 opcodeSampleInfo
[i
].countInCTIFunctions
= m_opcodeSamplesInCTIFunctions
[i
];
195 qsort(opcodeSampleInfo
, numOpcodeIDs
, sizeof(OpcodeSampleInfo
), compareOpcodeIndicesSampling
);
197 // (2) Print Opcode sampling results.
199 printf("\nBytecode samples [*]\n");
200 printf(" sample %% of %% of | cti cti %%\n");
201 printf("opcode count VM total | count of self\n");
202 printf("------------------------------------------------------- | ----------------\n");
204 for (int i
= 0; i
< numOpcodeIDs
; ++i
) {
205 long long count
= opcodeSampleInfo
[i
].count
;
209 OpcodeID opcodeID
= opcodeSampleInfo
[i
].opcode
;
211 const char* opcodeName
= opcodeNames
[opcodeID
];
212 const char* opcodePadding
= padOpcodeName(opcodeID
, 28);
213 double percentOfVM
= (static_cast<double>(count
) * 100) / m_opcodeSampleCount
;
214 double percentOfTotal
= (static_cast<double>(count
) * 100) / m_sampleCount
;
215 long long countInCTIFunctions
= opcodeSampleInfo
[i
].countInCTIFunctions
;
216 double percentInCTIFunctions
= (static_cast<double>(countInCTIFunctions
) * 100) / count
;
217 fprintf(stdout
, "%s:%s%-6lld %.3f%%\t%.3f%%\t | %-6lld %.3f%%\n", opcodeName
, opcodePadding
, count
, percentOfVM
, percentOfTotal
, countInCTIFunctions
, percentInCTIFunctions
);
220 printf("\n[*] Samples inside host code are not charged to any Bytecode.\n\n");
221 printf("\tSamples inside VM:\t\t%lld / %lld (%.3f%%)\n", m_opcodeSampleCount
, m_sampleCount
, (static_cast<double>(m_opcodeSampleCount
) * 100) / m_sampleCount
);
222 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
);
223 printf("\tsample count:\tsamples inside this opcode\n");
224 printf("\t%% of VM:\tsample count / all opcode samples\n");
225 printf("\t%% of total:\tsample count / all samples\n");
226 printf("\t--------------\n");
227 printf("\tcti count:\tsamples inside a CTI function called by this opcode\n");
228 printf("\tcti %% of self:\tcti count / sample count\n");
230 // (3) Build and sort 'codeBlockSamples' array.
232 int scopeCount
= m_scopeSampleMap
->size();
233 Vector
<ScopeSampleRecord
*> codeBlockSamples(scopeCount
);
234 ScopeSampleRecordMap::iterator iter
= m_scopeSampleMap
->begin();
235 for (int i
= 0; i
< scopeCount
; ++i
, ++iter
)
236 codeBlockSamples
[i
] = iter
->second
;
238 qsort(codeBlockSamples
.begin(), scopeCount
, sizeof(ScopeSampleRecord
*), compareScopeSampleRecords
);
240 // (4) Print data from 'codeBlockSamples' array.
242 printf("\nCodeBlock samples\n\n");
244 for (int i
= 0; i
< scopeCount
; ++i
) {
245 ScopeSampleRecord
* record
= codeBlockSamples
[i
];
246 CodeBlock
* codeBlock
= record
->m_codeBlock
;
248 double blockPercent
= (record
->m_sampleCount
* 100.0) / m_sampleCount
;
250 if (blockPercent
>= 1) {
251 //Instruction* code = codeBlock->instructions().begin();
252 printf("#%d: %s:%d: %d / %lld (%.3f%%)\n", i
+ 1, record
->m_scope
->sourceURL().UTF8String().c_str(), codeBlock
->lineNumberForBytecodeOffset(exec
, 0), record
->m_sampleCount
, m_sampleCount
, blockPercent
);
254 HashMap
<unsigned,unsigned> lineCounts
;
255 codeBlock
->dump(exec
);
257 printf(" Opcode and line number samples [*]\n\n");
258 for (unsigned op
= 0; op
< record
->m_size
; ++op
) {
259 int count
= record
->m_samples
[op
];
261 printf(" [% 4d] has sample count: % 4d\n", op
, count
);
262 unsigned line
= codeBlock
->lineNumberForBytecodeOffset(exec
, op
);
263 lineCounts
.set(line
, (lineCounts
.contains(line
) ? lineCounts
.get(line
) : 0) + count
);
268 int linesCount
= lineCounts
.size();
269 Vector
<LineCountInfo
> lineCountInfo(linesCount
);
271 for (HashMap
<unsigned,unsigned>::iterator iter
= lineCounts
.begin(); iter
!= lineCounts
.end(); ++iter
, ++lineno
) {
272 lineCountInfo
[lineno
].line
= iter
->first
;
273 lineCountInfo
[lineno
].count
= iter
->second
;
276 qsort(lineCountInfo
.begin(), linesCount
, sizeof(LineCountInfo
), compareLineCountInfoSampling
);
278 for (lineno
= 0; lineno
< linesCount
; ++lineno
) {
279 printf(" Line #%d has sample count %d.\n", lineCountInfo
[lineno
].line
, lineCountInfo
[lineno
].count
);
282 printf(" [*] Samples inside host code are charged to the calling Bytecode.\n");
283 printf(" Samples on a call / return boundary are not charged to a specific opcode or line.\n\n");
284 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
);
292 void SamplingTool::dump(ExecState
*)