source: webkit/trunk/JavaScriptCore/bytecode/SamplingTool.cpp@ 43395

Last change on this file since 43395 was 43395, checked in by [email protected], 16 years ago

2009-05-08 Gavin Barraclough <[email protected]>

Reviewed by Oliver "I see lots of ifdefs" Hunt.

Fix (kinda) for sampling tool breakage. The codeblock sampling tool has become
b0rked due to recent changes in native function calling. The initialization of
a ScopeNode appears to now occur before the sampling tool (or possibly the
interpreter has been brought into existence, wihich leads to crashyness).

This patch doesn't fix the problem. The crash occurs when tracking a Scope, but
we shouldn't need to track scopes when we're just sampling opcodes, not
codeblocks. Not retaining Scopes when just opcode sampling will reduce sampling
overhead reducing any instrumentation skew, which is a good thing. As a side
benefit this patch also gets the opcode sampling going again, albeit in a bit of
a lame way. Will come back later with a proper fix from codeblock sampling.

  • JavaScriptCore.exp:
  • bytecode/SamplingTool.cpp: (JSC::compareLineCountInfoSampling): (JSC::SamplingTool::dump):
  • bytecode/SamplingTool.h: (JSC::SamplingTool::SamplingTool):
  • parser/Nodes.cpp: (JSC::ScopeNode::ScopeNode):
File size: 12.8 KB
Line 
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 !PLATFORM(WIN_OS)
37#include <unistd.h>
38#endif
39
40namespace JSC {
41
42#if ENABLE(SAMPLING_FLAGS)
43
44void SamplingFlags::sample()
45{
46 uint32_t mask = 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
58void SamplingFlags::start()
59{
60 for (unsigned i = 0; i <= 32; ++i)
61 s_flagCounts[i] = 0;
62}
63void 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}
79uint64_t SamplingFlags::s_flagCounts[33];
80
81#else
82void SamplingFlags::start() {}
83void 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*/
91uint32_t SamplingFlags::s_flags = 1 << 15;
92
93
94#if PLATFORM(WIN_OS)
95
96static 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
106static void sleepForMicroseconds(unsigned us)
107{
108 usleep(us);
109}
110
111#endif
112
113static inline unsigned hertz2us(unsigned hertz)
114{
115 return 1000000 / hertz;
116}
117
118
119SamplingTool* SamplingTool::s_samplingTool = 0;
120
121
122bool SamplingThread::s_running = false;
123unsigned SamplingThread::s_hertz = 10000;
124ThreadIdentifier SamplingThread::s_samplingThread;
125
126void* 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
143void 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
152void SamplingThread::stop()
153{
154 ASSERT(s_running);
155 s_running = false;
156 waitForThreadCompletion(s_samplingThread, 0);
157}
158
159
160void ScopeSampleRecord::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
179void 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_scopeSampleMapMutex);
200 ScopeSampleRecord* record = m_scopeSampleMap->get(codeBlock->ownerNode());
201 ASSERT(record);
202 record->sample(codeBlock, sample.vPC());
203 }
204#endif
205}
206
207void SamplingTool::sample()
208{
209 s_samplingTool->doRun();
210}
211
212#if ENABLE(CODEBLOCK_SAMPLING)
213void SamplingTool::notifyOfScope(ScopeNode* scope)
214{
215 MutexLocker locker(m_scopeSampleMapMutex);
216 m_scopeSampleMap->set(scope, new ScopeSampleRecord(scope));
217}
218#endif
219
220void SamplingTool::setup()
221{
222 s_samplingTool = this;
223}
224
225#if ENABLE(OPCODE_SAMPLING)
226
227struct OpcodeSampleInfo {
228 OpcodeID opcode;
229 long long count;
230 long long countInCTIFunctions;
231};
232
233struct LineCountInfo {
234 unsigned line;
235 unsigned count;
236};
237
238static int compareOpcodeIndicesSampling(const void* left, const void* right)
239{
240 const OpcodeSampleInfo* leftSampleInfo = reinterpret_cast<const OpcodeSampleInfo*>(left);
241 const OpcodeSampleInfo* rightSampleInfo = reinterpret_cast<const OpcodeSampleInfo*>(right);
242
243 return (leftSampleInfo->count < rightSampleInfo->count) ? 1 : (leftSampleInfo->count > rightSampleInfo->count) ? -1 : 0;
244}
245
246#if ENABLE(CODEBLOCK_SAMPLING)
247static int compareLineCountInfoSampling(const void* left, const void* right)
248{
249 const LineCountInfo* leftLineCount = reinterpret_cast<const LineCountInfo*>(left);
250 const LineCountInfo* rightLineCount = reinterpret_cast<const LineCountInfo*>(right);
251
252 return (leftLineCount->line > rightLineCount->line) ? 1 : (leftLineCount->line < rightLineCount->line) ? -1 : 0;
253}
254
255static int compareScopeSampleRecords(const void* left, const void* right)
256{
257 const ScopeSampleRecord* const leftValue = *static_cast<const ScopeSampleRecord* const *>(left);
258 const ScopeSampleRecord* const rightValue = *static_cast<const ScopeSampleRecord* const *>(right);
259
260 return (leftValue->m_sampleCount < rightValue->m_sampleCount) ? 1 : (leftValue->m_sampleCount > rightValue->m_sampleCount) ? -1 : 0;
261}
262#endif
263
264void SamplingTool::dump(ExecState* exec)
265{
266 // Tidies up SunSpider output by removing short scripts - such a small number of samples would likely not be useful anyhow.
267 if (m_sampleCount < 10)
268 return;
269
270 // (1) Build and sort 'opcodeSampleInfo' array.
271
272 OpcodeSampleInfo opcodeSampleInfo[numOpcodeIDs];
273 for (int i = 0; i < numOpcodeIDs; ++i) {
274 opcodeSampleInfo[i].opcode = static_cast<OpcodeID>(i);
275 opcodeSampleInfo[i].count = m_opcodeSamples[i];
276 opcodeSampleInfo[i].countInCTIFunctions = m_opcodeSamplesInCTIFunctions[i];
277 }
278
279 qsort(opcodeSampleInfo, numOpcodeIDs, sizeof(OpcodeSampleInfo), compareOpcodeIndicesSampling);
280
281 // (2) Print Opcode sampling results.
282
283 printf("\nBytecode samples [*]\n");
284 printf(" sample %% of %% of | cti cti %%\n");
285 printf("opcode count VM total | count of self\n");
286 printf("------------------------------------------------------- | ----------------\n");
287
288 for (int i = 0; i < numOpcodeIDs; ++i) {
289 long long count = opcodeSampleInfo[i].count;
290 if (!count)
291 continue;
292
293 OpcodeID opcodeID = opcodeSampleInfo[i].opcode;
294
295 const char* opcodeName = opcodeNames[opcodeID];
296 const char* opcodePadding = padOpcodeName(opcodeID, 28);
297 double percentOfVM = (static_cast<double>(count) * 100) / m_opcodeSampleCount;
298 double percentOfTotal = (static_cast<double>(count) * 100) / m_sampleCount;
299 long long countInCTIFunctions = opcodeSampleInfo[i].countInCTIFunctions;
300 double percentInCTIFunctions = (static_cast<double>(countInCTIFunctions) * 100) / count;
301 fprintf(stdout, "%s:%s%-6lld %.3f%%\t%.3f%%\t | %-6lld %.3f%%\n", opcodeName, opcodePadding, count, percentOfVM, percentOfTotal, countInCTIFunctions, percentInCTIFunctions);
302 }
303
304 printf("\n[*] Samples inside host code are not charged to any Bytecode.\n\n");
305 printf("\tSamples inside VM:\t\t%lld / %lld (%.3f%%)\n", m_opcodeSampleCount, m_sampleCount, (static_cast<double>(m_opcodeSampleCount) * 100) / m_sampleCount);
306 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);
307 printf("\tsample count:\tsamples inside this opcode\n");
308 printf("\t%% of VM:\tsample count / all opcode samples\n");
309 printf("\t%% of total:\tsample count / all samples\n");
310 printf("\t--------------\n");
311 printf("\tcti count:\tsamples inside a CTI function called by this opcode\n");
312 printf("\tcti %% of self:\tcti count / sample count\n");
313
314#if ENABLE(CODEBLOCK_SAMPLING)
315
316 // (3) Build and sort 'codeBlockSamples' array.
317
318 int scopeCount = m_scopeSampleMap->size();
319 Vector<ScopeSampleRecord*> codeBlockSamples(scopeCount);
320 ScopeSampleRecordMap::iterator iter = m_scopeSampleMap->begin();
321 for (int i = 0; i < scopeCount; ++i, ++iter)
322 codeBlockSamples[i] = iter->second;
323
324 qsort(codeBlockSamples.begin(), scopeCount, sizeof(ScopeSampleRecord*), compareScopeSampleRecords);
325
326 // (4) Print data from 'codeBlockSamples' array.
327
328 printf("\nCodeBlock samples\n\n");
329
330 for (int i = 0; i < scopeCount; ++i) {
331 ScopeSampleRecord* record = codeBlockSamples[i];
332 CodeBlock* codeBlock = record->m_codeBlock;
333
334 double blockPercent = (record->m_sampleCount * 100.0) / m_sampleCount;
335
336 if (blockPercent >= 1) {
337 //Instruction* code = codeBlock->instructions().begin();
338 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);
339 if (i < 10) {
340 HashMap<unsigned,unsigned> lineCounts;
341 codeBlock->dump(exec);
342
343 printf(" Opcode and line number samples [*]\n\n");
344 for (unsigned op = 0; op < record->m_size; ++op) {
345 int count = record->m_samples[op];
346 if (count) {
347 printf(" [% 4d] has sample count: % 4d\n", op, count);
348 unsigned line = codeBlock->lineNumberForBytecodeOffset(exec, op);
349 lineCounts.set(line, (lineCounts.contains(line) ? lineCounts.get(line) : 0) + count);
350 }
351 }
352 printf("\n");
353
354 int linesCount = lineCounts.size();
355 Vector<LineCountInfo> lineCountInfo(linesCount);
356 int lineno = 0;
357 for (HashMap<unsigned,unsigned>::iterator iter = lineCounts.begin(); iter != lineCounts.end(); ++iter, ++lineno) {
358 lineCountInfo[lineno].line = iter->first;
359 lineCountInfo[lineno].count = iter->second;
360 }
361
362 qsort(lineCountInfo.begin(), linesCount, sizeof(LineCountInfo), compareLineCountInfoSampling);
363
364 for (lineno = 0; lineno < linesCount; ++lineno) {
365 printf(" Line #%d has sample count %d.\n", lineCountInfo[lineno].line, lineCountInfo[lineno].count);
366 }
367 printf("\n");
368 printf(" [*] Samples inside host code are charged to the calling Bytecode.\n");
369 printf(" Samples on a call / return boundary are not charged to a specific opcode or line.\n\n");
370 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);
371 }
372 }
373 }
374#else
375 UNUSED_PARAM(exec);
376#endif
377}
378
379#else
380
381void SamplingTool::dump(ExecState*)
382{
383}
384
385#endif
386
387} // namespace JSC
Note: See TracBrowser for help on using the repository browser.