blob: de264355022b5f6ecb2fe1baf98c90f586917b15 [file] [log] [blame]
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001/*
2 * Copyright (C) 2016 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 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "SamplingProfiler.h"
28
29#if ENABLE(SAMPLING_PROFILER)
30
31#include "CallFrame.h"
32#include "CodeBlock.h"
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000033#include "Executable.h"
34#include "HeapInlines.h"
35#include "HeapIterationScope.h"
fpizlo@apple.combc16ddb2016-09-06 01:02:22 +000036#include "HeapUtil.h"
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000037#include "InlineCallFrame.h"
38#include "Interpreter.h"
39#include "JSCJSValueInlines.h"
40#include "JSFunction.h"
mark.lam@apple.comd7755682016-09-01 22:34:27 +000041#include "JSObjectInlines.h"
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000042#include "LLIntPCRanges.h"
43#include "MarkedBlock.h"
44#include "MarkedBlockSet.h"
fpizlo@apple.com96352992016-09-20 18:12:18 +000045#include "MarkedSpaceInlines.h"
sbarati@apple.comd3d0c002016-01-30 01:11:05 +000046#include "PCToCodeOriginMap.h"
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000047#include "SlotVisitor.h"
48#include "SlotVisitorInlines.h"
sbarati@apple.com8ec5c262016-02-04 21:51:40 +000049#include "StructureInlines.h"
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000050#include "VM.h"
keith_miller@apple.com3517aa42016-05-24 23:03:09 +000051#include <wtf/HashSet.h>
sbarati@apple.com51d80072016-06-14 02:29:26 +000052#include <wtf/RandomNumber.h>
keith_miller@apple.com3517aa42016-05-24 23:03:09 +000053#include <wtf/RefPtr.h>
joepeck@webkit.org7e07f392016-09-22 18:59:47 +000054#include <wtf/text/StringBuilder.h>
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000055
56namespace JSC {
57
58static double sNumTotalStackTraces = 0;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000059static double sNumTotalWalks = 0;
60static double sNumFailedWalks = 0;
61static const uint32_t sNumWalkReportingFrequency = 50;
62static const double sWalkErrorPercentage = .05;
63static const bool sReportStatsOnlyWhenTheyreAboveThreshold = false;
64static const bool sReportStats = false;
65
66using FrameType = SamplingProfiler::FrameType;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +000067using UnprocessedStackFrame = SamplingProfiler::UnprocessedStackFrame;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000068
69ALWAYS_INLINE static void reportStats()
70{
71 if (sReportStats && sNumTotalWalks && static_cast<uint64_t>(sNumTotalWalks) % sNumWalkReportingFrequency == 0) {
72 if (!sReportStatsOnlyWhenTheyreAboveThreshold || (sNumFailedWalks / sNumTotalWalks > sWalkErrorPercentage)) {
73 dataLogF("Num total walks: %llu. Failed walks percent: %lf\n",
utatane.tea@gmail.com0cf8d742016-01-30 12:53:59 +000074 static_cast<unsigned long long>(sNumTotalWalks), sNumFailedWalks / sNumTotalWalks);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000075 }
76 }
77}
78
79class FrameWalker {
80public:
81 FrameWalker(ExecState* callFrame, VM& vm, const LockHolder& codeBlockSetLocker, const LockHolder& machineThreadsLocker)
82 : m_vm(vm)
83 , m_callFrame(callFrame)
84 , m_vmEntryFrame(vm.topVMEntryFrame)
85 , m_codeBlockSetLocker(codeBlockSetLocker)
86 , m_machineThreadsLocker(machineThreadsLocker)
87 {
88 }
89
sbarati@apple.com30cd1162016-02-11 21:04:43 +000090 SUPPRESS_ASAN
sbarati@apple.comd3d0c002016-01-30 01:11:05 +000091 size_t walk(Vector<UnprocessedStackFrame>& stackTrace, bool& didRunOutOfSpace)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000092 {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000093 if (sReportStats)
94 sNumTotalWalks++;
95 resetAtMachineFrame();
96 size_t maxStackTraceSize = stackTrace.size();
97 while (!isAtTop() && !m_bailingOut && m_depth < maxStackTraceSize) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +000098 CallSiteIndex callSiteIndex;
99 JSValue unsafeCallee = m_callFrame->unsafeCallee();
sbarati@apple.com88a5fd62016-02-16 22:01:37 +0000100 CodeBlock* codeBlock = m_callFrame->unsafeCodeBlock();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000101 if (codeBlock) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000102 ASSERT(isValidCodeBlock(codeBlock));
sbarati@apple.com88a5fd62016-02-16 22:01:37 +0000103 callSiteIndex = m_callFrame->unsafeCallSiteIndex();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000104 }
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000105 stackTrace[m_depth] = UnprocessedStackFrame(codeBlock, JSValue::encode(unsafeCallee), callSiteIndex);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000106 m_depth++;
107 advanceToParentFrame();
108 resetAtMachineFrame();
109 }
110 didRunOutOfSpace = m_depth >= maxStackTraceSize && !isAtTop();
111 reportStats();
112 return m_depth;
113 }
114
115 bool wasValidWalk() const
116 {
117 return !m_bailingOut;
118 }
119
120private:
121
sbarati@apple.com30cd1162016-02-11 21:04:43 +0000122 SUPPRESS_ASAN
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000123 void advanceToParentFrame()
124 {
sbarati@apple.com88a5fd62016-02-16 22:01:37 +0000125 m_callFrame = m_callFrame->unsafeCallerFrame(m_vmEntryFrame);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000126 }
127
128 bool isAtTop() const
129 {
130 return !m_callFrame;
131 }
132
sbarati@apple.com30cd1162016-02-11 21:04:43 +0000133 SUPPRESS_ASAN
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000134 void resetAtMachineFrame()
135 {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000136 if (isAtTop())
137 return;
138
139 if (!isValidFramePointer(m_callFrame)) {
140 // Guard against pausing the process at weird program points.
141 m_bailingOut = true;
142 if (sReportStats)
143 sNumFailedWalks++;
144 return;
145 }
146
sbarati@apple.com88a5fd62016-02-16 22:01:37 +0000147 CodeBlock* codeBlock = m_callFrame->unsafeCodeBlock();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000148 if (!codeBlock)
149 return;
150
151 if (!isValidCodeBlock(codeBlock)) {
152 m_bailingOut = true;
153 if (sReportStats)
154 sNumFailedWalks++;
155 return;
156 }
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000157 }
158
159 bool isValidFramePointer(ExecState* exec)
160 {
161 uint8_t* fpCast = bitwise_cast<uint8_t*>(exec);
162 for (MachineThreads::Thread* thread = m_vm.heap.machineThreads().threadsListHead(m_machineThreadsLocker); thread; thread = thread->next) {
163 uint8_t* stackBase = static_cast<uint8_t*>(thread->stackBase);
164 uint8_t* stackLimit = static_cast<uint8_t*>(thread->stackEnd);
165 RELEASE_ASSERT(stackBase);
166 RELEASE_ASSERT(stackLimit);
167 if (fpCast <= stackBase && fpCast >= stackLimit)
168 return true;
169 }
170 return false;
171 }
172
173 bool isValidCodeBlock(CodeBlock* codeBlock)
174 {
175 if (!codeBlock)
176 return false;
177 bool result = m_vm.heap.codeBlockSet().contains(m_codeBlockSetLocker, codeBlock);
178 return result;
179 }
180
181 VM& m_vm;
182 ExecState* m_callFrame;
183 VMEntryFrame* m_vmEntryFrame;
184 const LockHolder& m_codeBlockSetLocker;
185 const LockHolder& m_machineThreadsLocker;
186 bool m_bailingOut { false };
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000187 size_t m_depth { 0 };
188};
189
190SamplingProfiler::SamplingProfiler(VM& vm, RefPtr<Stopwatch>&& stopwatch)
191 : m_vm(vm)
192 , m_stopwatch(WTFMove(stopwatch))
commit-queue@webkit.org18b95832016-04-26 16:47:33 +0000193 , m_timingInterval(std::chrono::microseconds(Options::sampleInterval()))
sbarati@apple.com207a6832016-04-06 03:55:11 +0000194 , m_threadIdentifier(0)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000195 , m_jscExecutionThread(nullptr)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000196 , m_isPaused(false)
sbarati@apple.com207a6832016-04-06 03:55:11 +0000197 , m_isShutDown(false)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000198{
199 if (sReportStats) {
200 sNumTotalWalks = 0;
201 sNumFailedWalks = 0;
202 }
203
204 m_currentFrames.grow(256);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000205}
206
207SamplingProfiler::~SamplingProfiler()
208{
209}
210
sbarati@apple.com207a6832016-04-06 03:55:11 +0000211void SamplingProfiler::createThreadIfNecessary(const LockHolder&)
212{
213 ASSERT(m_lock.isLocked());
214
215 if (m_threadIdentifier)
216 return;
217
218 RefPtr<SamplingProfiler> profiler = this;
219 m_threadIdentifier = createThread("jsc.sampling-profiler.thread", [profiler] {
220 profiler->timerLoop();
221 });
222}
223
224void SamplingProfiler::timerLoop()
225{
226 while (true) {
227 std::chrono::microseconds stackTraceProcessingTime = std::chrono::microseconds(0);
228 {
229 LockHolder locker(m_lock);
230 if (UNLIKELY(m_isShutDown))
231 return;
232
233 if (!m_isPaused && m_jscExecutionThread)
234 takeSample(locker, stackTraceProcessingTime);
235
236 m_lastTime = m_stopwatch->elapsedTime();
237 }
238
sbarati@apple.com51d80072016-06-14 02:29:26 +0000239 // Read section 6.2 of this paper for more elaboration of why we add a random
240 // fluctuation here. The main idea is to prevent our timer from being in sync
241 // with some system process such as a scheduled context switch.
242 // http://plv.colorado.edu/papers/mytkowicz-pldi10.pdf
243 double randomSignedNumber = (randomNumber() * 2.0) - 1.0; // A random number between [-1, 1).
244 std::chrono::microseconds randomFluctuation = std::chrono::microseconds(static_cast<uint64_t>(randomSignedNumber * static_cast<double>(m_timingInterval.count()) * 0.20l));
245 std::this_thread::sleep_for(m_timingInterval - std::min(m_timingInterval, stackTraceProcessingTime) + randomFluctuation);
sbarati@apple.com207a6832016-04-06 03:55:11 +0000246 }
247}
248
249void SamplingProfiler::takeSample(const LockHolder&, std::chrono::microseconds& stackTraceProcessingTime)
250{
251 ASSERT(m_lock.isLocked());
252 if (m_vm.entryScope) {
253 double nowTime = m_stopwatch->elapsedTime();
254
255 LockHolder machineThreadsLocker(m_vm.heap.machineThreads().getLock());
256 LockHolder codeBlockSetLocker(m_vm.heap.codeBlockSet().getLock());
257 LockHolder executableAllocatorLocker(m_vm.executableAllocator.getLock());
258
259 bool didSuspend = m_jscExecutionThread->suspend();
260 if (didSuspend) {
261 // While the JSC thread is suspended, we can't do things like malloc because the JSC thread
262 // may be holding the malloc lock.
263 ExecState* callFrame;
264 void* machinePC;
265 bool topFrameIsLLInt = false;
266 void* llintPC;
267 {
268 MachineThreads::Thread::Registers registers;
269 m_jscExecutionThread->getRegisters(registers);
270 callFrame = static_cast<ExecState*>(registers.framePointer());
271 machinePC = registers.instructionPointer();
272 llintPC = registers.llintPC();
273 m_jscExecutionThread->freeRegisters(registers);
274 }
275 // FIXME: Lets have a way of detecting when we're parsing code.
276 // https://bugs.webkit.org/show_bug.cgi?id=152761
277 if (m_vm.executableAllocator.isValidExecutableMemory(executableAllocatorLocker, machinePC)) {
278 if (m_vm.isExecutingInRegExpJIT) {
279 // FIXME: We're executing a regexp. Lets gather more intersting data.
280 // https://bugs.webkit.org/show_bug.cgi?id=152729
281 callFrame = m_vm.topCallFrame; // We need to do this or else we'd fail our backtrace validation b/c this isn't a JS frame.
282 }
283 } else if (LLInt::isLLIntPC(machinePC)) {
284 topFrameIsLLInt = true;
285 // We're okay to take a normal stack trace when the PC
286 // is in LLInt code.
287 } else {
288 // We resort to topCallFrame to see if we can get anything
289 // useful. We usually get here when we're executing C code.
290 callFrame = m_vm.topCallFrame;
291 }
292
293 size_t walkSize;
294 bool wasValidWalk;
295 bool didRunOutOfVectorSpace;
296 {
297 FrameWalker walker(callFrame, m_vm, codeBlockSetLocker, machineThreadsLocker);
298 walkSize = walker.walk(m_currentFrames, didRunOutOfVectorSpace);
299 wasValidWalk = walker.wasValidWalk();
300 }
301
302 m_jscExecutionThread->resume();
303
304 auto startTime = std::chrono::steady_clock::now();
305 // We can now use data structures that malloc, and do other interesting things, again.
306
307 // FIXME: It'd be interesting to take data about the program's state when
308 // we fail to take a stack trace: https://bugs.webkit.org/show_bug.cgi?id=152758
309 if (wasValidWalk && walkSize) {
310 if (sReportStats)
311 sNumTotalStackTraces++;
312 Vector<UnprocessedStackFrame> stackTrace;
313 stackTrace.reserveInitialCapacity(walkSize);
314 for (size_t i = 0; i < walkSize; i++) {
315 UnprocessedStackFrame frame = m_currentFrames[i];
316 stackTrace.uncheckedAppend(frame);
317 }
318
319 m_unprocessedStackTraces.append(UnprocessedStackTrace { nowTime, machinePC, topFrameIsLLInt, llintPC, WTFMove(stackTrace) });
320
321 if (didRunOutOfVectorSpace)
322 m_currentFrames.grow(m_currentFrames.size() * 1.25);
323 }
324
325 auto endTime = std::chrono::steady_clock::now();
326 stackTraceProcessingTime = std::chrono::duration_cast<std::chrono::microseconds>(endTime - startTime);
327 }
328 }
329}
330
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000331static ALWAYS_INLINE unsigned tryGetBytecodeIndex(unsigned llintPC, CodeBlock* codeBlock, bool& isValid)
332{
ossy@webkit.orgc8cea7b2016-02-18 16:45:26 +0000333#if ENABLE(DFG_JIT)
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000334 RELEASE_ASSERT(!codeBlock->hasCodeOrigins());
ossy@webkit.orgc8cea7b2016-02-18 16:45:26 +0000335#endif
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000336
337#if USE(JSVALUE64)
338 unsigned bytecodeIndex = llintPC;
339 if (bytecodeIndex < codeBlock->instructionCount()) {
340 isValid = true;
341 return bytecodeIndex;
342 }
343 isValid = false;
344 return 0;
345#else
346 Instruction* instruction = bitwise_cast<Instruction*>(llintPC);
347 if (instruction >= codeBlock->instructions().begin() && instruction < codeBlock->instructions().begin() + codeBlock->instructionCount()) {
348 isValid = true;
349 unsigned bytecodeIndex = instruction - codeBlock->instructions().begin();
350 return bytecodeIndex;
351 }
352 isValid = false;
353 return 0;
354#endif
355}
356
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000357void SamplingProfiler::processUnverifiedStackTraces()
358{
359 // This function needs to be called from the JSC execution thread.
360 RELEASE_ASSERT(m_lock.isLocked());
361
362 TinyBloomFilter filter = m_vm.heap.objectSpace().blocks().filter();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000363
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000364 for (UnprocessedStackTrace& unprocessedStackTrace : m_unprocessedStackTraces) {
365 m_stackTraces.append(StackTrace());
366 StackTrace& stackTrace = m_stackTraces.last();
367 stackTrace.timestamp = unprocessedStackTrace.timestamp;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000368
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000369 auto appendCodeBlock = [&] (CodeBlock* codeBlock, unsigned bytecodeIndex) {
370 stackTrace.frames.append(StackFrame(codeBlock->ownerExecutable()));
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000371 m_liveCellPointers.add(codeBlock->ownerExecutable());
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000372
373 if (bytecodeIndex < codeBlock->instructionCount()) {
374 int divot;
375 int startOffset;
376 int endOffset;
377 codeBlock->expressionRangeForBytecodeOffset(bytecodeIndex, divot, startOffset, endOffset,
378 stackTrace.frames.last().lineNumber, stackTrace.frames.last().columnNumber);
sbarati@apple.com10ec78a2016-04-20 02:24:53 +0000379 stackTrace.frames.last().bytecodeIndex = bytecodeIndex;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000380 }
sbarati@apple.com38496ee2016-04-21 00:55:03 +0000381 if (Options::collectSamplingProfilerDataForJSCShell()) {
sbarati@apple.com10ec78a2016-04-20 02:24:53 +0000382 stackTrace.frames.last().codeBlockHash = codeBlock->hash();
sbarati@apple.com38496ee2016-04-21 00:55:03 +0000383 stackTrace.frames.last().jitType = codeBlock->jitType();
384 }
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000385 };
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000386
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000387 auto appendEmptyFrame = [&] {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000388 stackTrace.frames.append(StackFrame());
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000389 };
390
391 auto storeCalleeIntoTopFrame = [&] (EncodedJSValue encodedCallee) {
392 // Set the callee if it's a valid GC object.
393 JSValue callee = JSValue::decode(encodedCallee);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000394 StackFrame& stackFrame = stackTrace.frames.last();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000395 bool alreadyHasExecutable = !!stackFrame.executable;
fpizlo@apple.combc16ddb2016-09-06 01:02:22 +0000396 if (!HeapUtil::isValueGCObject(m_vm.heap, filter, callee)) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000397 if (!alreadyHasExecutable)
398 stackFrame.frameType = FrameType::Unknown;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000399 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000400 }
401
402 JSCell* calleeCell = callee.asCell();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000403 auto setFallbackFrameType = [&] {
404 ASSERT(!alreadyHasExecutable);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000405 FrameType result = FrameType::Unknown;
406 CallData callData;
407 CallType callType;
408 callType = getCallData(calleeCell, callData);
utatane.tea@gmail.comf76f1b42016-03-05 17:01:04 +0000409 if (callType == CallType::Host)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000410 result = FrameType::Host;
411
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000412 stackFrame.frameType = result;
413 };
414
415 auto addCallee = [&] (JSObject* callee) {
416 stackFrame.callee = callee;
417 m_liveCellPointers.add(callee);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000418 };
419
420 if (calleeCell->type() != JSFunctionType) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000421 if (JSObject* object = jsDynamicCast<JSObject*>(calleeCell))
422 addCallee(object);
423
424 if (!alreadyHasExecutable)
425 setFallbackFrameType();
426
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000427 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000428 }
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000429
430 addCallee(jsCast<JSFunction*>(calleeCell));
431
432 if (alreadyHasExecutable)
433 return;
434
435 ExecutableBase* executable = jsCast<JSFunction*>(calleeCell)->executable();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000436 if (!executable) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000437 setFallbackFrameType();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000438 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000439 }
440
fpizlo@apple.combc16ddb2016-09-06 01:02:22 +0000441 RELEASE_ASSERT(HeapUtil::isPointerGCObjectJSCell(m_vm.heap, filter, executable));
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000442 stackFrame.frameType = FrameType::Executable;
443 stackFrame.executable = executable;
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000444 m_liveCellPointers.add(executable);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000445 };
446
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000447
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000448 // Prepend the top-most inlined frame if needed and gather
449 // location information about where the top frame is executing.
450 size_t startIndex = 0;
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000451 if (unprocessedStackTrace.frames.size() && !!unprocessedStackTrace.frames[0].verifiedCodeBlock) {
452 CodeBlock* topCodeBlock = unprocessedStackTrace.frames[0].verifiedCodeBlock;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000453 if (unprocessedStackTrace.topFrameIsLLInt) {
454 // We reuse LLInt CodeBlocks for the baseline JIT, so we need to check for both jit types.
455 // This might also be false for various reasons (known and unknown), even though
456 // it's super unlikely. One reason that this can be false is when we throw from a DFG frame,
457 // and we end up having to unwind past a VMEntryFrame, we will end up executing
458 // inside the LLInt's handleUncaughtException. So we just protect against this
459 // by ignoring it.
460 unsigned bytecodeIndex = 0;
461 if (topCodeBlock->jitType() == JITCode::InterpreterThunk || topCodeBlock->jitType() == JITCode::BaselineJIT) {
462 bool isValidPC;
463 unsigned bits;
464#if USE(JSVALUE64)
465 bits = static_cast<unsigned>(bitwise_cast<uintptr_t>(unprocessedStackTrace.llintPC));
466#else
467 bits = bitwise_cast<unsigned>(unprocessedStackTrace.llintPC);
468#endif
469 bytecodeIndex = tryGetBytecodeIndex(bits, topCodeBlock, isValidPC);
470
471 UNUSED_PARAM(isValidPC); // FIXME: do something with this info for the web inspector: https://bugs.webkit.org/show_bug.cgi?id=153455
472
473 appendCodeBlock(topCodeBlock, bytecodeIndex);
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000474 storeCalleeIntoTopFrame(unprocessedStackTrace.frames[0].unverifiedCallee);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000475 startIndex = 1;
476 }
477 } else if (Optional<CodeOrigin> codeOrigin = topCodeBlock->findPC(unprocessedStackTrace.topPC)) {
478 codeOrigin->walkUpInlineStack([&] (const CodeOrigin& codeOrigin) {
479 appendCodeBlock(codeOrigin.inlineCallFrame ? codeOrigin.inlineCallFrame->baselineCodeBlock.get() : topCodeBlock, codeOrigin.bytecodeIndex);
480 });
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000481 storeCalleeIntoTopFrame(unprocessedStackTrace.frames[0].unverifiedCallee);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000482 startIndex = 1;
483 }
484 }
485
486 for (size_t i = startIndex; i < unprocessedStackTrace.frames.size(); i++) {
487 UnprocessedStackFrame& unprocessedStackFrame = unprocessedStackTrace.frames[i];
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000488 if (CodeBlock* codeBlock = unprocessedStackFrame.verifiedCodeBlock) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000489 CallSiteIndex callSiteIndex = unprocessedStackFrame.callSiteIndex;
490
491 auto appendCodeBlockNoInlining = [&] {
492 bool isValidPC;
493 appendCodeBlock(codeBlock, tryGetBytecodeIndex(callSiteIndex.bits(), codeBlock, isValidPC));
494 };
495
496#if ENABLE(DFG_JIT)
497 if (codeBlock->hasCodeOrigins()) {
498 if (codeBlock->canGetCodeOrigin(callSiteIndex)) {
499 codeBlock->codeOrigin(callSiteIndex).walkUpInlineStack([&] (const CodeOrigin& codeOrigin) {
500 appendCodeBlock(codeOrigin.inlineCallFrame ? codeOrigin.inlineCallFrame->baselineCodeBlock.get() : codeBlock, codeOrigin.bytecodeIndex);
501 });
502 } else
503 appendCodeBlock(codeBlock, std::numeric_limits<unsigned>::max());
504 } else
505 appendCodeBlockNoInlining();
506#else
507 appendCodeBlockNoInlining();
508#endif
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000509 } else
510 appendEmptyFrame();
511
512 // Note that this is okay to do if we walked the inline stack because
513 // the machine frame will be at the top of the processed stack trace.
514 storeCalleeIntoTopFrame(unprocessedStackFrame.unverifiedCallee);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000515 }
516 }
517
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000518 m_unprocessedStackTraces.clear();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000519}
520
521void SamplingProfiler::visit(SlotVisitor& slotVisitor)
522{
523 RELEASE_ASSERT(m_lock.isLocked());
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000524 for (JSCell* cell : m_liveCellPointers)
525 slotVisitor.appendUnbarrieredReadOnlyPointer(cell);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000526}
527
528void SamplingProfiler::shutdown()
529{
sbarati@apple.com207a6832016-04-06 03:55:11 +0000530 LockHolder locker(m_lock);
531 m_isShutDown = true;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000532}
533
534void SamplingProfiler::start()
535{
536 LockHolder locker(m_lock);
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000537 start(locker);
538}
539
540void SamplingProfiler::start(const LockHolder& locker)
541{
542 ASSERT(m_lock.isLocked());
sbarati@apple.com207a6832016-04-06 03:55:11 +0000543 m_isPaused = false;
544 createThreadIfNecessary(locker);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000545}
546
sbarati@apple.com207a6832016-04-06 03:55:11 +0000547void SamplingProfiler::pause(const LockHolder&)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000548{
549 ASSERT(m_lock.isLocked());
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000550 m_isPaused = true;
551 reportStats();
552}
553
554void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread(const LockHolder&)
555{
556 ASSERT(m_lock.isLocked());
557 m_jscExecutionThread = m_vm.heap.machineThreads().machineThreadForCurrentThread();
558}
559
560void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread()
561{
562 LockHolder locker(m_lock);
563 noticeCurrentThreadAsJSCExecutionThread(locker);
564}
565
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000566void SamplingProfiler::noticeJSLockAcquisition()
567{
568 LockHolder locker(m_lock);
569 noticeCurrentThreadAsJSCExecutionThread(locker);
570}
571
572void SamplingProfiler::noticeVMEntry()
573{
574 LockHolder locker(m_lock);
575 ASSERT(m_vm.entryScope);
576 noticeCurrentThreadAsJSCExecutionThread(locker);
577 m_lastTime = m_stopwatch->elapsedTime();
sbarati@apple.com207a6832016-04-06 03:55:11 +0000578 createThreadIfNecessary(locker);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000579}
580
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000581void SamplingProfiler::clearData(const LockHolder&)
582{
583 ASSERT(m_lock.isLocked());
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000584 m_stackTraces.clear();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000585 m_liveCellPointers.clear();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000586 m_unprocessedStackTraces.clear();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000587}
588
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000589String SamplingProfiler::StackFrame::nameFromCallee(VM& vm)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000590{
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000591 if (!callee)
592 return String();
593
mark.lam@apple.com09f92b82016-09-27 20:32:13 +0000594 auto scope = DECLARE_CATCH_SCOPE(vm);
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000595 ExecState* exec = callee->globalObject()->globalExec();
596 auto getPropertyIfPureOperation = [&] (const Identifier& ident) -> String {
sbarati@apple.comea974132016-02-17 22:11:39 +0000597 PropertySlot slot(callee, PropertySlot::InternalMethodType::VMInquiry);
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000598 PropertyName propertyName(ident);
mark.lam@apple.com09f92b82016-09-27 20:32:13 +0000599 bool hasProperty = callee->getPropertySlot(exec, propertyName, slot);
600 ASSERT_UNUSED(scope, !scope.exception());
601 if (hasProperty) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000602 if (slot.isValue()) {
603 JSValue nameValue = slot.getValue(exec, propertyName);
604 if (isJSString(nameValue))
605 return asString(nameValue)->tryGetValue();
606 }
607 }
608 return String();
609 };
610
611 String name = getPropertyIfPureOperation(vm.propertyNames->displayName);
612 if (!name.isEmpty())
613 return name;
614
615 return getPropertyIfPureOperation(vm.propertyNames->name);
616}
617
618String SamplingProfiler::StackFrame::displayName(VM& vm)
619{
620 {
621 String name = nameFromCallee(vm);
622 if (!name.isEmpty())
623 return name;
624 }
625
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000626 if (frameType == FrameType::Unknown)
627 return ASCIILiteral("(unknown)");
628 if (frameType == FrameType::Host)
629 return ASCIILiteral("(host)");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000630
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000631 if (executable->isHostFunction())
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000632 return static_cast<NativeExecutable*>(executable)->name();
633
634 if (executable->isFunctionExecutable())
635 return static_cast<FunctionExecutable*>(executable)->inferredName().string();
636 if (executable->isProgramExecutable() || executable->isEvalExecutable())
637 return ASCIILiteral("(program)");
638 if (executable->isModuleProgramExecutable())
639 return ASCIILiteral("(module)");
640
641 RELEASE_ASSERT_NOT_REACHED();
642 return String();
643}
644
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000645String SamplingProfiler::StackFrame::displayNameForJSONTests(VM& vm)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000646{
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000647 {
648 String name = nameFromCallee(vm);
649 if (!name.isEmpty())
650 return name;
651 }
652
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000653 if (frameType == FrameType::Unknown)
654 return ASCIILiteral("(unknown)");
655 if (frameType == FrameType::Host)
656 return ASCIILiteral("(host)");
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000657
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000658 if (executable->isHostFunction())
659 return static_cast<NativeExecutable*>(executable)->name();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000660
661 if (executable->isFunctionExecutable()) {
662 String result = static_cast<FunctionExecutable*>(executable)->inferredName().string();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000663 if (result.isEmpty())
664 return ASCIILiteral("(anonymous function)");
665 return result;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000666 }
667 if (executable->isEvalExecutable())
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000668 return ASCIILiteral("(eval)");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000669 if (executable->isProgramExecutable())
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000670 return ASCIILiteral("(program)");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000671 if (executable->isModuleProgramExecutable())
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000672 return ASCIILiteral("(module)");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000673
674 RELEASE_ASSERT_NOT_REACHED();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000675 return String();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000676}
677
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000678int SamplingProfiler::StackFrame::functionStartLine()
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000679{
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000680 if (frameType == FrameType::Unknown || frameType == FrameType::Host)
681 return -1;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000682
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000683 if (executable->isHostFunction())
684 return -1;
685 return static_cast<ScriptExecutable*>(executable)->firstLine();
686}
687
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000688unsigned SamplingProfiler::StackFrame::functionStartColumn()
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000689{
690 if (frameType == FrameType::Unknown || frameType == FrameType::Host)
peavo@outlook.coma83d48a2016-05-02 21:20:15 +0000691 return std::numeric_limits<unsigned>::max();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000692
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000693 if (executable->isHostFunction())
peavo@outlook.coma83d48a2016-05-02 21:20:15 +0000694 return std::numeric_limits<unsigned>::max();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000695
696 return static_cast<ScriptExecutable*>(executable)->startColumn();
697}
698
699intptr_t SamplingProfiler::StackFrame::sourceID()
700{
701 if (frameType == FrameType::Unknown || frameType == FrameType::Host)
702 return -1;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000703
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000704 if (executable->isHostFunction())
705 return -1;
706
707 return static_cast<ScriptExecutable*>(executable)->sourceID();
708}
709
710String SamplingProfiler::StackFrame::url()
711{
712 if (frameType == FrameType::Unknown || frameType == FrameType::Host)
713 return emptyString();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000714
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000715 if (executable->isHostFunction())
716 return emptyString();
717
718 String url = static_cast<ScriptExecutable*>(executable)->sourceURL();
719 if (url.isEmpty())
720 return static_cast<ScriptExecutable*>(executable)->source().provider()->sourceURL(); // Fall back to sourceURL directive.
721 return url;
722}
723
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000724Vector<SamplingProfiler::StackTrace> SamplingProfiler::releaseStackTraces(const LockHolder& locker)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000725{
726 ASSERT(m_lock.isLocked());
727 {
728 HeapIterationScope heapIterationScope(m_vm.heap);
729 processUnverifiedStackTraces();
730 }
731
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000732 Vector<StackTrace> result(WTFMove(m_stackTraces));
733 clearData(locker);
734 return result;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000735}
736
737String SamplingProfiler::stackTracesAsJSON()
738{
739 LockHolder locker(m_lock);
740
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000741 {
742 HeapIterationScope heapIterationScope(m_vm.heap);
743 processUnverifiedStackTraces();
744 }
745
746 StringBuilder json;
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000747 json.append('[');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000748
749 bool loopedOnce = false;
750 auto comma = [&] {
751 if (loopedOnce)
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000752 json.append(',');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000753 };
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000754 for (StackTrace& stackTrace : m_stackTraces) {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000755 comma();
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000756 json.append('[');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000757 loopedOnce = false;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000758 for (StackFrame& stackFrame : stackTrace.frames) {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000759 comma();
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000760 json.append('"');
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000761 json.append(stackFrame.displayNameForJSONTests(m_vm));
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000762 json.append('"');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000763 loopedOnce = true;
764 }
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000765 json.append(']');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000766 loopedOnce = true;
767 }
768
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000769 json.append(']');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000770
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000771 clearData(locker);
772
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000773 return json.toString();
774}
775
keith_miller@apple.com3517aa42016-05-24 23:03:09 +0000776void SamplingProfiler::registerForReportAtExit()
777{
778 static StaticLock registrationLock;
779 static HashSet<RefPtr<SamplingProfiler>>* profilesToReport;
780
781 LockHolder holder(registrationLock);
782
783 if (!profilesToReport) {
784 profilesToReport = new HashSet<RefPtr<SamplingProfiler>>();
785 atexit([]() {
786 for (auto profile : *profilesToReport)
787 profile->reportDataToOptionFile();
788 });
789 }
790
791 profilesToReport->add(adoptRef(this));
792 m_needsReportAtExit = true;
793}
794
795void SamplingProfiler::reportDataToOptionFile()
796{
797 if (m_needsReportAtExit) {
798 m_needsReportAtExit = false;
799 const char* path = Options::samplingProfilerPath();
800 StringPrintStream pathOut;
801 pathOut.print(path, "/");
802 pathOut.print("JSCSampilingProfile-", reinterpret_cast<uintptr_t>(this), ".txt");
803 auto out = FilePrintStream::open(pathOut.toCString().data(), "w");
804 reportTopFunctions(*out);
805 reportTopBytecodes(*out);
806 }
807}
808
sbarati@apple.com10ec78a2016-04-20 02:24:53 +0000809void SamplingProfiler::reportTopFunctions()
810{
keith_miller@apple.com3517aa42016-05-24 23:03:09 +0000811 reportTopFunctions(WTF::dataFile());
812}
813
814void SamplingProfiler::reportTopFunctions(PrintStream& out)
815{
sbarati@apple.com10ec78a2016-04-20 02:24:53 +0000816 LockHolder locker(m_lock);
817
818 {
819 HeapIterationScope heapIterationScope(m_vm.heap);
820 processUnverifiedStackTraces();
821 }
822
823
824 HashMap<String, size_t> functionCounts;
825 for (StackTrace& stackTrace : m_stackTraces) {
826 if (!stackTrace.frames.size())
827 continue;
828
829 StackFrame& frame = stackTrace.frames.first();
830 String frameDescription = makeString(frame.displayName(m_vm), ":", String::number(frame.sourceID()));
831 functionCounts.add(frameDescription, 0).iterator->value++;
832 }
833
834 auto takeMax = [&] () -> std::pair<String, size_t> {
835 String maxFrameDescription;
836 size_t maxFrameCount = 0;
837 for (auto entry : functionCounts) {
838 if (entry.value > maxFrameCount) {
839 maxFrameCount = entry.value;
840 maxFrameDescription = entry.key;
841 }
842 }
843 if (!maxFrameDescription.isEmpty())
844 functionCounts.remove(maxFrameDescription);
845 return std::make_pair(maxFrameDescription, maxFrameCount);
846 };
847
keith_miller@apple.com3517aa42016-05-24 23:03:09 +0000848 out.print("\n\nSampling rate: ", m_timingInterval.count(), " microseconds\n");
849 out.print("Hottest functions as <numSamples 'functionName:sourceID'>\n");
sbarati@apple.com10ec78a2016-04-20 02:24:53 +0000850 for (size_t i = 0; i < 40; i++) {
851 auto pair = takeMax();
852 if (pair.first.isEmpty())
853 break;
keith_miller@apple.com3517aa42016-05-24 23:03:09 +0000854 out.printf("%6zu ", pair.second);
855 out.print(" '", pair.first, "'\n");
sbarati@apple.com10ec78a2016-04-20 02:24:53 +0000856 }
857}
858
859void SamplingProfiler::reportTopBytecodes()
860{
keith_miller@apple.com3517aa42016-05-24 23:03:09 +0000861 reportTopBytecodes(WTF::dataFile());
862}
863
864void SamplingProfiler::reportTopBytecodes(PrintStream& out)
865{
sbarati@apple.com10ec78a2016-04-20 02:24:53 +0000866 LockHolder locker(m_lock);
867
868 {
869 HeapIterationScope heapIterationScope(m_vm.heap);
870 processUnverifiedStackTraces();
871 }
872
873 HashMap<String, size_t> bytecodeCounts;
874 for (StackTrace& stackTrace : m_stackTraces) {
875 if (!stackTrace.frames.size())
876 continue;
877
878 StackFrame& frame = stackTrace.frames.first();
879 String bytecodeIndex;
880 String codeBlockHash;
881 if (frame.hasBytecodeIndex())
882 bytecodeIndex = String::number(frame.bytecodeIndex);
883 else
884 bytecodeIndex = "<nil>";
885
886 if (frame.hasCodeBlockHash()) {
887 StringPrintStream stream;
888 frame.codeBlockHash.dump(stream);
889 codeBlockHash = stream.toString();
890 } else
891 codeBlockHash = "<nil>";
892
sbarati@apple.com38496ee2016-04-21 00:55:03 +0000893 String frameDescription = makeString(frame.displayName(m_vm), "#", codeBlockHash, ":", JITCode::typeName(frame.jitType), ":", bytecodeIndex);
sbarati@apple.com10ec78a2016-04-20 02:24:53 +0000894 bytecodeCounts.add(frameDescription, 0).iterator->value++;
895 }
896
897 auto takeMax = [&] () -> std::pair<String, size_t> {
898 String maxFrameDescription;
899 size_t maxFrameCount = 0;
900 for (auto entry : bytecodeCounts) {
901 if (entry.value > maxFrameCount) {
902 maxFrameCount = entry.value;
903 maxFrameDescription = entry.key;
904 }
905 }
906 if (!maxFrameDescription.isEmpty())
907 bytecodeCounts.remove(maxFrameDescription);
908 return std::make_pair(maxFrameDescription, maxFrameCount);
909 };
910
keith_miller@apple.com3517aa42016-05-24 23:03:09 +0000911 out.print("\n\nSampling rate: ", m_timingInterval.count(), " microseconds\n");
912 out.print("Hottest bytecodes as <numSamples 'functionName#hash:JITType:bytecodeIndex'>\n");
sbarati@apple.com10ec78a2016-04-20 02:24:53 +0000913 for (size_t i = 0; i < 80; i++) {
914 auto pair = takeMax();
915 if (pair.first.isEmpty())
916 break;
keith_miller@apple.com3517aa42016-05-24 23:03:09 +0000917 out.printf("%6zu ", pair.second);
918 out.print(" '", pair.first, "'\n");
sbarati@apple.com10ec78a2016-04-20 02:24:53 +0000919 }
920}
921
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000922} // namespace JSC
923
924namespace WTF {
925
926using namespace JSC;
927
928void printInternal(PrintStream& out, SamplingProfiler::FrameType frameType)
929{
930 switch (frameType) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000931 case SamplingProfiler::FrameType::Executable:
932 out.print("Executable");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000933 break;
934 case SamplingProfiler::FrameType::Host:
935 out.print("Host");
936 break;
937 case SamplingProfiler::FrameType::Unknown:
938 out.print("Unknown");
939 break;
940 }
941}
942
943} // namespace WTF
944
945#endif // ENABLE(SAMPLING_PROFILER)