|
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 |