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

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

2010-04-20 Oliver Hunt <[email protected]>

Reviewed by Gavin Barraclough.

Autogenerate yarr character tables
https://bugs.webkit.org/show_bug.cgi?id=37877

Use a python script to automatically generate character tables
for the builtin YARR character classes. This allows us to generate
actual tables as well, by using these tables we can both increase
performance of the check (for complex builtins) and reduce the actual
code size.

4-8% win on string-unpack-code, but lots of noise on other tests so
i'm only confident saying its a 1% win overall.

  • DerivedSources.make:
  • JavaScriptCore.xcodeproj/project.pbxproj:
  • assembler/AbstractMacroAssembler.h: (JSC::AbstractMacroAssembler::ExtendedAddress::ExtendedAddress):
  • assembler/MacroAssembler.h: (JSC::MacroAssembler::branchTest8):
  • assembler/MacroAssemblerX86Common.h: (JSC::MacroAssemblerX86Common::branchTest8):
  • assembler/MacroAssemblerX86_64.h: (JSC::MacroAssemblerX86_64::branchTest8):
  • assembler/X86Assembler.h: (JSC::X86Assembler::cmpb_im): (JSC::X86Assembler::testb_im):
  • bytecode/SamplingTool.cpp: (JSC::SamplingTool::dump):
  • create_regex_tables: Added.
  • yarr/RegexCompiler.cpp: (JSC::Yarr::CharacterClassConstructor::charClass):
  • yarr/RegexJIT.cpp: (JSC::Yarr::RegexGenerator::matchCharacterClass): (JSC::Yarr::RegexGenerator::generatePatternCharacterGreedy): (JSC::Yarr::RegexGenerator::generatePatternCharacterNonGreedy): (JSC::Yarr::RegexGenerator::generateCharacterClassGreedy):
  • yarr/RegexPattern.h: (JSC::Yarr::CharacterClassTable::create): (JSC::Yarr::CharacterClassTable::CharacterClassTable): (JSC::Yarr::CharacterClass::CharacterClass):
File size: 13.6 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 !OS(WINDOWS)
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 OS(WINDOWS)
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 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
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_scriptSampleMapMutex);
200 ScriptSampleRecord* record = m_scopeSampleMap->get(codeBlock->ownerExecutable());
201 ASSERT(record);
202 record->sample(codeBlock, sample.vPC());
203 }
204#endif
205}
206
207void SamplingTool::sample()
208{
209 s_samplingTool->doRun();
210}
211
212void 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
222void SamplingTool::setup()
223{
224 s_samplingTool = this;
225}
226
227#if ENABLE(OPCODE_SAMPLING)
228
229struct OpcodeSampleInfo {
230 OpcodeID opcode;
231 long long count;
232 long long countInCTIFunctions;
233};
234
235struct LineCountInfo {
236 unsigned line;
237 unsigned count;
238};
239
240static 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)
249static 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
257static 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
266void 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
383void SamplingTool::dump(ExecState*)
384{
385}
386
387#endif
388
389void 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
402AbstractSamplingCounter AbstractSamplingCounter::s_abstractSamplingCounterChainEnd;
403AbstractSamplingCounter* AbstractSamplingCounter::s_abstractSamplingCounterChain = &s_abstractSamplingCounterChainEnd;
404bool AbstractSamplingCounter::s_completed = false;
405
406} // namespace JSC
Note: See TracBrowser for help on using the repository browser.