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

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

JavaScriptCore:

2009-05-11 Dmitry Titov <[email protected]>

Reviewed by Alexey Proskuryakov and Adam Roben.

https://bugs.webkit.org/show_bug.cgi?id=25348
Change WTF::ThreadIdentifier to be an actual (but wrapped) thread id, remove ThreadMap.

  • wtf/Threading.h: (WTF::ThreadIdentifier::ThreadIdentifier): (WTF::ThreadIdentifier::isValid): (WTF::ThreadIdentifier::invalidate): (WTF::ThreadIdentifier::platformId): ThreadIdentifier is now a class, containing a PlatformThreadIdentifier and methods that are used across the code on thread ids: construction, comparisons, check for 'valid' state etc. '0' is used as invalid id, which happens to just work with all platform-specific thread id implementations.

All the following files repeatedly reflect the new ThreadIdentifier for each platform.
We remove ThreadMap and threadMapMutex from all of them, remove the functions that
populated/searched/cleared the map and add platform-specific comparison operators
for ThreadIdentifier.

There are specific temporary workarounds for Safari 4 beta on OSX and Win32 since the
public build uses WTF threading functions with old type of ThreadingIdentifier.
The next time Safari 4 is rebuilt, it will 'automatically' pick up the new type and new
functions so the deprecated ones can be removed.

  • wtf/gtk/ThreadingGtk.cpp: (WTF::ThreadIdentifier::operator==): (WTF::ThreadIdentifier::operator!=): (WTF::initializeThreading): (WTF::createThreadInternal): (WTF::waitForThreadCompletion): (WTF::currentThread):
  • wtf/ThreadingNone.cpp: (WTF::ThreadIdentifier::operator==): (WTF::ThreadIdentifier::operator!=):
  • wtf/ThreadingPthreads.cpp: (WTF::ThreadIdentifier::operator==): (WTF::ThreadIdentifier::operator!=): (WTF::initializeThreading): (WTF::createThreadInternal): (WTF::waitForThreadCompletion): (WTF::detachThread): (WTF::currentThread): (WTF::waitForThreadCompletion): This is a workaround for Safari 4 beta on Mac. Safari 4 is linked against old definition of ThreadIdentifier so it treats it as uint32_t. This 'old' variant of waitForThreadCompletion takes uint32_t and has the old decorated name, so Safari can load it from JavaScriptCore library. The other functions (CurrentThread() etc) happen to match their previous decorated names and, while they return pthread_t now, it is a pointer which round-trips through a uint32_t. This function will be removed as soon as Safari 4 will release next public build.
  • wtf/qt/ThreadingQt.cpp: (WTF::ThreadIdentifier::operator==): (WTF::ThreadIdentifier::operator!=): (WTF::initializeThreading): (WTF::createThreadInternal): (WTF::waitForThreadCompletion): (WTF::currentThread):
  • wtf/ThreadingWin.cpp: (WTF::ThreadIdentifier::operator==): (WTF::ThreadIdentifier::operator!=): (WTF::initializeThreading): (WTF::createThreadInternal): All the platforms (except Windows) used a sequential counter as a thread ID and mapped it into platform ID. Windows was using native thread id and mapped it into thread handle. Since we can always obtain a thread handle by thread id, createThread now closes the handle. (WTF::waitForThreadCompletion): obtains another one using OpenThread(id) API. If can not obtain a handle, it means the thread already exited. (WTF::detachThread): (WTF::currentThread): (WTF::detachThreadDeprecated): old function, renamed (for Win Safari 4 beta which uses it for now). (WTF::waitForThreadCompletionDeprecated): same. (WTF::currentThreadDeprecated): same. (WTF::createThreadDeprecated): same.
  • bytecode/SamplingTool.h:
  • bytecode/SamplingTool.cpp: Use DEFINE_STATIC_LOCAL for a static ThreadIdentifier variable, to avoid static constructor.
  • JavaScriptCore.exp: export lists - updated decorated names of the WTF threading functions since they now take a different type as a parameter.
  • JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: ditto for Windows, plus added "deprecated" functions that take old parameter type - turns out public beta of Safari 4 uses those, so they need to be kept along for a while.
  • JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore_debug.def: ditto.

WebCore:

2009-05-11 Dmitry Titov <[email protected]>

Reviewed by Alexey Proskuryakov and Adam Roben.

https://bugs.webkit.org/show_bug.cgi?id=25348
Change WTF::ThreadIdentifier to be an actual (but wrapped) thread id, remove ThreadMap.

Most of the change is in WTF.
Unless noted, all the following files changed to use the new ThreadIdentifier::isValid()
method instead of just doing 'if(m_threadID)' kind of checks, since ThreadIdentifier
is now a class rather then an integer.
Also, there is no need to initialize threadID in constructors to 0 now.

  • dom/XMLTokenizerLibxml2.cpp: (WebCore::libxmlLoaderThread): use DEFINE_STATIC_LOCAL and accessor function for static thread id, since now ThreadIdentifier needs construction and we avoid having global initializers. (WebCore::matchFunc): use the new accessor function. (WebCore::openFunc): ditto. (WebCore::createStringParser): ditto. (WebCore::createMemoryParser): ditto.
  • loader/icon/IconDatabase.cpp: (WebCore::IconDatabase::open):
  • platform/sql/SQLiteDatabase.cpp: (WebCore::SQLiteDatabase::SQLiteDatabase): (WebCore::SQLiteDatabase::close):
  • storage/DatabaseThread.cpp: (WebCore::DatabaseThread::start): (WebCore::DatabaseThread::databaseThread): remove m_threadID from debug output.
  • storage/LocalStorageThread.cpp: (WebCore::LocalStorageThread::start): (WebCore::LocalStorageThread::scheduleImport): (WebCore::LocalStorageThread::scheduleSync): (WebCore::LocalStorageThread::terminate):
  • workers/WorkerThread.cpp: (WebCore::WorkerThread::start): (WebCore::WorkerThread::WorkerThread): (WebCore::WorkerThread::start):

WebKit/win:

2009-05-11 Dmitry Titov <[email protected]>

Reviewed by Alexey Proskuryakov and Adam Roben.

https://bugs.webkit.org/show_bug.cgi?id=25348
Change WTF::ThreadIdentifier to be an actual (but wrapped) thread id, remove ThreadMap.

Most of the change is in WTF and WebCore.

  • WebKit.vcproj/WebKit.def: replaced decorated names of WTF threading functions with new ones. Also, aliased the old implementations so the public Safari 4 beta can load the old WTF functions which it uses. Next time Safari 4 builds, it will pick up new functions and the deprecated ones can be removed.
  • WebKit.vcproj/WebKit_debug.def: same.
File size: 12.9 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;
124
125ThreadIdentifier& SamplingThread::samplingThread() {
126 DEFINE_STATIC_LOCAL(ThreadIdentifier, staticSamplingThread, ());
127 return staticSamplingThread;
128}
129
130void* SamplingThread::threadStartFunc(void*)
131{
132 while (s_running) {
133 sleepForMicroseconds(hertz2us(s_hertz));
134
135#if ENABLE(SAMPLING_FLAGS)
136 SamplingFlags::sample();
137#endif
138#if ENABLE(OPCODE_SAMPLING)
139 SamplingTool::sample();
140#endif
141 }
142
143 return 0;
144}
145
146
147void SamplingThread::start(unsigned hertz)
148{
149 ASSERT(!s_running);
150 s_running = true;
151 s_hertz = hertz;
152
153 samplingThread() = createThread(threadStartFunc, 0, "JavaScriptCore::Sampler");
154}
155
156void SamplingThread::stop()
157{
158 ASSERT(s_running);
159 s_running = false;
160 waitForThreadCompletion(samplingThread(), 0);
161}
162
163
164void ScopeSampleRecord::sample(CodeBlock* codeBlock, Instruction* vPC)
165{
166 if (!m_samples) {
167 m_size = codeBlock->instructions().size();
168 m_samples = static_cast<int*>(calloc(m_size, sizeof(int)));
169 m_codeBlock = codeBlock;
170 }
171
172 ++m_sampleCount;
173
174 unsigned offest = vPC - codeBlock->instructions().begin();
175 // Since we don't read and write codeBlock and vPC atomically, this check
176 // can fail if we sample mid op_call / op_ret.
177 if (offest < m_size) {
178 m_samples[offest]++;
179 m_opcodeSampleCount++;
180 }
181}
182
183void SamplingTool::doRun()
184{
185 Sample sample(m_sample, m_codeBlock);
186 ++m_sampleCount;
187
188 if (sample.isNull())
189 return;
190
191 if (!sample.inHostFunction()) {
192 unsigned opcodeID = m_interpreter->getOpcodeID(sample.vPC()[0].u.opcode);
193
194 ++m_opcodeSampleCount;
195 ++m_opcodeSamples[opcodeID];
196
197 if (sample.inCTIFunction())
198 m_opcodeSamplesInCTIFunctions[opcodeID]++;
199 }
200
201#if ENABLE(CODEBLOCK_SAMPLING)
202 if (CodeBlock* codeBlock = sample.codeBlock()) {
203 MutexLocker locker(m_scopeSampleMapMutex);
204 ScopeSampleRecord* record = m_scopeSampleMap->get(codeBlock->ownerNode());
205 ASSERT(record);
206 record->sample(codeBlock, sample.vPC());
207 }
208#endif
209}
210
211void SamplingTool::sample()
212{
213 s_samplingTool->doRun();
214}
215
216void SamplingTool::notifyOfScope(ScopeNode* scope)
217{
218#if ENABLE(CODEBLOCK_SAMPLING)
219 MutexLocker locker(m_scopeSampleMapMutex);
220 m_scopeSampleMap->set(scope, new ScopeSampleRecord(scope));
221#else
222 UNUSED_PARAM(scope);
223#endif
224}
225
226void SamplingTool::setup()
227{
228 s_samplingTool = this;
229}
230
231#if ENABLE(OPCODE_SAMPLING)
232
233struct OpcodeSampleInfo {
234 OpcodeID opcode;
235 long long count;
236 long long countInCTIFunctions;
237};
238
239struct LineCountInfo {
240 unsigned line;
241 unsigned count;
242};
243
244static int compareOpcodeIndicesSampling(const void* left, const void* right)
245{
246 const OpcodeSampleInfo* leftSampleInfo = reinterpret_cast<const OpcodeSampleInfo*>(left);
247 const OpcodeSampleInfo* rightSampleInfo = reinterpret_cast<const OpcodeSampleInfo*>(right);
248
249 return (leftSampleInfo->count < rightSampleInfo->count) ? 1 : (leftSampleInfo->count > rightSampleInfo->count) ? -1 : 0;
250}
251
252#if ENABLE(CODEBLOCK_SAMPLING)
253static int compareLineCountInfoSampling(const void* left, const void* right)
254{
255 const LineCountInfo* leftLineCount = reinterpret_cast<const LineCountInfo*>(left);
256 const LineCountInfo* rightLineCount = reinterpret_cast<const LineCountInfo*>(right);
257
258 return (leftLineCount->line > rightLineCount->line) ? 1 : (leftLineCount->line < rightLineCount->line) ? -1 : 0;
259}
260
261static int compareScopeSampleRecords(const void* left, const void* right)
262{
263 const ScopeSampleRecord* const leftValue = *static_cast<const ScopeSampleRecord* const *>(left);
264 const ScopeSampleRecord* const rightValue = *static_cast<const ScopeSampleRecord* const *>(right);
265
266 return (leftValue->m_sampleCount < rightValue->m_sampleCount) ? 1 : (leftValue->m_sampleCount > rightValue->m_sampleCount) ? -1 : 0;
267}
268#endif
269
270void SamplingTool::dump(ExecState* exec)
271{
272 // Tidies up SunSpider output by removing short scripts - such a small number of samples would likely not be useful anyhow.
273 if (m_sampleCount < 10)
274 return;
275
276 // (1) Build and sort 'opcodeSampleInfo' array.
277
278 OpcodeSampleInfo opcodeSampleInfo[numOpcodeIDs];
279 for (int i = 0; i < numOpcodeIDs; ++i) {
280 opcodeSampleInfo[i].opcode = static_cast<OpcodeID>(i);
281 opcodeSampleInfo[i].count = m_opcodeSamples[i];
282 opcodeSampleInfo[i].countInCTIFunctions = m_opcodeSamplesInCTIFunctions[i];
283 }
284
285 qsort(opcodeSampleInfo, numOpcodeIDs, sizeof(OpcodeSampleInfo), compareOpcodeIndicesSampling);
286
287 // (2) Print Opcode sampling results.
288
289 printf("\nBytecode samples [*]\n");
290 printf(" sample %% of %% of | cti cti %%\n");
291 printf("opcode count VM total | count of self\n");
292 printf("------------------------------------------------------- | ----------------\n");
293
294 for (int i = 0; i < numOpcodeIDs; ++i) {
295 long long count = opcodeSampleInfo[i].count;
296 if (!count)
297 continue;
298
299 OpcodeID opcodeID = opcodeSampleInfo[i].opcode;
300
301 const char* opcodeName = opcodeNames[opcodeID];
302 const char* opcodePadding = padOpcodeName(opcodeID, 28);
303 double percentOfVM = (static_cast<double>(count) * 100) / m_opcodeSampleCount;
304 double percentOfTotal = (static_cast<double>(count) * 100) / m_sampleCount;
305 long long countInCTIFunctions = opcodeSampleInfo[i].countInCTIFunctions;
306 double percentInCTIFunctions = (static_cast<double>(countInCTIFunctions) * 100) / count;
307 fprintf(stdout, "%s:%s%-6lld %.3f%%\t%.3f%%\t | %-6lld %.3f%%\n", opcodeName, opcodePadding, count, percentOfVM, percentOfTotal, countInCTIFunctions, percentInCTIFunctions);
308 }
309
310 printf("\n[*] Samples inside host code are not charged to any Bytecode.\n\n");
311 printf("\tSamples inside VM:\t\t%lld / %lld (%.3f%%)\n", m_opcodeSampleCount, m_sampleCount, (static_cast<double>(m_opcodeSampleCount) * 100) / m_sampleCount);
312 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);
313 printf("\tsample count:\tsamples inside this opcode\n");
314 printf("\t%% of VM:\tsample count / all opcode samples\n");
315 printf("\t%% of total:\tsample count / all samples\n");
316 printf("\t--------------\n");
317 printf("\tcti count:\tsamples inside a CTI function called by this opcode\n");
318 printf("\tcti %% of self:\tcti count / sample count\n");
319
320#if ENABLE(CODEBLOCK_SAMPLING)
321
322 // (3) Build and sort 'codeBlockSamples' array.
323
324 int scopeCount = m_scopeSampleMap->size();
325 Vector<ScopeSampleRecord*> codeBlockSamples(scopeCount);
326 ScopeSampleRecordMap::iterator iter = m_scopeSampleMap->begin();
327 for (int i = 0; i < scopeCount; ++i, ++iter)
328 codeBlockSamples[i] = iter->second;
329
330 qsort(codeBlockSamples.begin(), scopeCount, sizeof(ScopeSampleRecord*), compareScopeSampleRecords);
331
332 // (4) Print data from 'codeBlockSamples' array.
333
334 printf("\nCodeBlock samples\n\n");
335
336 for (int i = 0; i < scopeCount; ++i) {
337 ScopeSampleRecord* record = codeBlockSamples[i];
338 CodeBlock* codeBlock = record->m_codeBlock;
339
340 double blockPercent = (record->m_sampleCount * 100.0) / m_sampleCount;
341
342 if (blockPercent >= 1) {
343 //Instruction* code = codeBlock->instructions().begin();
344 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);
345 if (i < 10) {
346 HashMap<unsigned,unsigned> lineCounts;
347 codeBlock->dump(exec);
348
349 printf(" Opcode and line number samples [*]\n\n");
350 for (unsigned op = 0; op < record->m_size; ++op) {
351 int count = record->m_samples[op];
352 if (count) {
353 printf(" [% 4d] has sample count: % 4d\n", op, count);
354 unsigned line = codeBlock->lineNumberForBytecodeOffset(exec, op);
355 lineCounts.set(line, (lineCounts.contains(line) ? lineCounts.get(line) : 0) + count);
356 }
357 }
358 printf("\n");
359
360 int linesCount = lineCounts.size();
361 Vector<LineCountInfo> lineCountInfo(linesCount);
362 int lineno = 0;
363 for (HashMap<unsigned,unsigned>::iterator iter = lineCounts.begin(); iter != lineCounts.end(); ++iter, ++lineno) {
364 lineCountInfo[lineno].line = iter->first;
365 lineCountInfo[lineno].count = iter->second;
366 }
367
368 qsort(lineCountInfo.begin(), linesCount, sizeof(LineCountInfo), compareLineCountInfoSampling);
369
370 for (lineno = 0; lineno < linesCount; ++lineno) {
371 printf(" Line #%d has sample count %d.\n", lineCountInfo[lineno].line, lineCountInfo[lineno].count);
372 }
373 printf("\n");
374 printf(" [*] Samples inside host code are charged to the calling Bytecode.\n");
375 printf(" Samples on a call / return boundary are not charged to a specific opcode or line.\n\n");
376 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);
377 }
378 }
379 }
380#else
381 UNUSED_PARAM(exec);
382#endif
383}
384
385#else
386
387void SamplingTool::dump(ExecState*)
388{
389}
390
391#endif
392
393} // namespace JSC
Note: See TracBrowser for help on using the repository browser.