blob: 94332e4d801e4b8da5e5f330df58318869478433 [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"
33#include "Debugger.h"
34#include "Executable.h"
35#include "HeapInlines.h"
36#include "HeapIterationScope.h"
37#include "InlineCallFrame.h"
38#include "Interpreter.h"
39#include "JSCJSValueInlines.h"
40#include "JSFunction.h"
41#include "LLIntPCRanges.h"
42#include "MarkedBlock.h"
43#include "MarkedBlockSet.h"
sbarati@apple.comd3d0c002016-01-30 01:11:05 +000044#include "PCToCodeOriginMap.h"
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000045#include "SlotVisitor.h"
46#include "SlotVisitorInlines.h"
sbarati@apple.com8ec5c262016-02-04 21:51:40 +000047#include "StructureInlines.h"
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000048#include "VM.h"
49#include "VMEntryScope.h"
50
51namespace JSC {
52
53static double sNumTotalStackTraces = 0;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000054static double sNumTotalWalks = 0;
55static double sNumFailedWalks = 0;
56static const uint32_t sNumWalkReportingFrequency = 50;
57static const double sWalkErrorPercentage = .05;
58static const bool sReportStatsOnlyWhenTheyreAboveThreshold = false;
59static const bool sReportStats = false;
60
61using FrameType = SamplingProfiler::FrameType;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +000062using UnprocessedStackFrame = SamplingProfiler::UnprocessedStackFrame;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000063
64ALWAYS_INLINE static void reportStats()
65{
66 if (sReportStats && sNumTotalWalks && static_cast<uint64_t>(sNumTotalWalks) % sNumWalkReportingFrequency == 0) {
67 if (!sReportStatsOnlyWhenTheyreAboveThreshold || (sNumFailedWalks / sNumTotalWalks > sWalkErrorPercentage)) {
68 dataLogF("Num total walks: %llu. Failed walks percent: %lf\n",
utatane.tea@gmail.com0cf8d742016-01-30 12:53:59 +000069 static_cast<unsigned long long>(sNumTotalWalks), sNumFailedWalks / sNumTotalWalks);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000070 }
71 }
72}
73
74class FrameWalker {
75public:
76 FrameWalker(ExecState* callFrame, VM& vm, const LockHolder& codeBlockSetLocker, const LockHolder& machineThreadsLocker)
77 : m_vm(vm)
78 , m_callFrame(callFrame)
79 , m_vmEntryFrame(vm.topVMEntryFrame)
80 , m_codeBlockSetLocker(codeBlockSetLocker)
81 , m_machineThreadsLocker(machineThreadsLocker)
82 {
83 }
84
sbarati@apple.com30cd1162016-02-11 21:04:43 +000085 SUPPRESS_ASAN
sbarati@apple.comd3d0c002016-01-30 01:11:05 +000086 size_t walk(Vector<UnprocessedStackFrame>& stackTrace, bool& didRunOutOfSpace)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000087 {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000088 if (sReportStats)
89 sNumTotalWalks++;
90 resetAtMachineFrame();
91 size_t maxStackTraceSize = stackTrace.size();
92 while (!isAtTop() && !m_bailingOut && m_depth < maxStackTraceSize) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +000093 CallSiteIndex callSiteIndex;
94 JSValue unsafeCallee = m_callFrame->unsafeCallee();
sbarati@apple.com88a5fd62016-02-16 22:01:37 +000095 CodeBlock* codeBlock = m_callFrame->unsafeCodeBlock();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +000096 if (codeBlock) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +000097 ASSERT(isValidCodeBlock(codeBlock));
sbarati@apple.com88a5fd62016-02-16 22:01:37 +000098 callSiteIndex = m_callFrame->unsafeCallSiteIndex();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000099 }
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000100 stackTrace[m_depth] = UnprocessedStackFrame(codeBlock, JSValue::encode(unsafeCallee), callSiteIndex);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000101 m_depth++;
102 advanceToParentFrame();
103 resetAtMachineFrame();
104 }
105 didRunOutOfSpace = m_depth >= maxStackTraceSize && !isAtTop();
106 reportStats();
107 return m_depth;
108 }
109
110 bool wasValidWalk() const
111 {
112 return !m_bailingOut;
113 }
114
115private:
116
sbarati@apple.com30cd1162016-02-11 21:04:43 +0000117 SUPPRESS_ASAN
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000118 void advanceToParentFrame()
119 {
sbarati@apple.com88a5fd62016-02-16 22:01:37 +0000120 m_callFrame = m_callFrame->unsafeCallerFrame(m_vmEntryFrame);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000121 }
122
123 bool isAtTop() const
124 {
125 return !m_callFrame;
126 }
127
sbarati@apple.com30cd1162016-02-11 21:04:43 +0000128 SUPPRESS_ASAN
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000129 void resetAtMachineFrame()
130 {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000131 if (isAtTop())
132 return;
133
134 if (!isValidFramePointer(m_callFrame)) {
135 // Guard against pausing the process at weird program points.
136 m_bailingOut = true;
137 if (sReportStats)
138 sNumFailedWalks++;
139 return;
140 }
141
sbarati@apple.com88a5fd62016-02-16 22:01:37 +0000142 CodeBlock* codeBlock = m_callFrame->unsafeCodeBlock();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000143 if (!codeBlock)
144 return;
145
146 if (!isValidCodeBlock(codeBlock)) {
147 m_bailingOut = true;
148 if (sReportStats)
149 sNumFailedWalks++;
150 return;
151 }
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000152 }
153
154 bool isValidFramePointer(ExecState* exec)
155 {
156 uint8_t* fpCast = bitwise_cast<uint8_t*>(exec);
157 for (MachineThreads::Thread* thread = m_vm.heap.machineThreads().threadsListHead(m_machineThreadsLocker); thread; thread = thread->next) {
158 uint8_t* stackBase = static_cast<uint8_t*>(thread->stackBase);
159 uint8_t* stackLimit = static_cast<uint8_t*>(thread->stackEnd);
160 RELEASE_ASSERT(stackBase);
161 RELEASE_ASSERT(stackLimit);
162 if (fpCast <= stackBase && fpCast >= stackLimit)
163 return true;
164 }
165 return false;
166 }
167
168 bool isValidCodeBlock(CodeBlock* codeBlock)
169 {
170 if (!codeBlock)
171 return false;
172 bool result = m_vm.heap.codeBlockSet().contains(m_codeBlockSetLocker, codeBlock);
173 return result;
174 }
175
176 VM& m_vm;
177 ExecState* m_callFrame;
178 VMEntryFrame* m_vmEntryFrame;
179 const LockHolder& m_codeBlockSetLocker;
180 const LockHolder& m_machineThreadsLocker;
181 bool m_bailingOut { false };
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000182 size_t m_depth { 0 };
183};
184
185SamplingProfiler::SamplingProfiler(VM& vm, RefPtr<Stopwatch>&& stopwatch)
186 : m_vm(vm)
187 , m_stopwatch(WTFMove(stopwatch))
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000188 , m_timingInterval(std::chrono::microseconds(1000))
sbarati@apple.com207a6832016-04-06 03:55:11 +0000189 , m_threadIdentifier(0)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000190 , m_jscExecutionThread(nullptr)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000191 , m_isPaused(false)
sbarati@apple.com207a6832016-04-06 03:55:11 +0000192 , m_isShutDown(false)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000193{
194 if (sReportStats) {
195 sNumTotalWalks = 0;
196 sNumFailedWalks = 0;
197 }
198
199 m_currentFrames.grow(256);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000200}
201
202SamplingProfiler::~SamplingProfiler()
203{
204}
205
sbarati@apple.com207a6832016-04-06 03:55:11 +0000206void SamplingProfiler::createThreadIfNecessary(const LockHolder&)
207{
208 ASSERT(m_lock.isLocked());
209
210 if (m_threadIdentifier)
211 return;
212
213 RefPtr<SamplingProfiler> profiler = this;
214 m_threadIdentifier = createThread("jsc.sampling-profiler.thread", [profiler] {
215 profiler->timerLoop();
216 });
217}
218
219void SamplingProfiler::timerLoop()
220{
221 while (true) {
222 std::chrono::microseconds stackTraceProcessingTime = std::chrono::microseconds(0);
223 {
224 LockHolder locker(m_lock);
225 if (UNLIKELY(m_isShutDown))
226 return;
227
228 if (!m_isPaused && m_jscExecutionThread)
229 takeSample(locker, stackTraceProcessingTime);
230
231 m_lastTime = m_stopwatch->elapsedTime();
232 }
233
234 std::this_thread::sleep_for(m_timingInterval - std::min(m_timingInterval, stackTraceProcessingTime));
235 }
236}
237
238void SamplingProfiler::takeSample(const LockHolder&, std::chrono::microseconds& stackTraceProcessingTime)
239{
240 ASSERT(m_lock.isLocked());
241 if (m_vm.entryScope) {
242 double nowTime = m_stopwatch->elapsedTime();
243
244 LockHolder machineThreadsLocker(m_vm.heap.machineThreads().getLock());
245 LockHolder codeBlockSetLocker(m_vm.heap.codeBlockSet().getLock());
246 LockHolder executableAllocatorLocker(m_vm.executableAllocator.getLock());
247
248 bool didSuspend = m_jscExecutionThread->suspend();
249 if (didSuspend) {
250 // While the JSC thread is suspended, we can't do things like malloc because the JSC thread
251 // may be holding the malloc lock.
252 ExecState* callFrame;
253 void* machinePC;
254 bool topFrameIsLLInt = false;
255 void* llintPC;
256 {
257 MachineThreads::Thread::Registers registers;
258 m_jscExecutionThread->getRegisters(registers);
259 callFrame = static_cast<ExecState*>(registers.framePointer());
260 machinePC = registers.instructionPointer();
261 llintPC = registers.llintPC();
262 m_jscExecutionThread->freeRegisters(registers);
263 }
264 // FIXME: Lets have a way of detecting when we're parsing code.
265 // https://bugs.webkit.org/show_bug.cgi?id=152761
266 if (m_vm.executableAllocator.isValidExecutableMemory(executableAllocatorLocker, machinePC)) {
267 if (m_vm.isExecutingInRegExpJIT) {
268 // FIXME: We're executing a regexp. Lets gather more intersting data.
269 // https://bugs.webkit.org/show_bug.cgi?id=152729
270 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.
271 }
272 } else if (LLInt::isLLIntPC(machinePC)) {
273 topFrameIsLLInt = true;
274 // We're okay to take a normal stack trace when the PC
275 // is in LLInt code.
276 } else {
277 // We resort to topCallFrame to see if we can get anything
278 // useful. We usually get here when we're executing C code.
279 callFrame = m_vm.topCallFrame;
280 }
281
282 size_t walkSize;
283 bool wasValidWalk;
284 bool didRunOutOfVectorSpace;
285 {
286 FrameWalker walker(callFrame, m_vm, codeBlockSetLocker, machineThreadsLocker);
287 walkSize = walker.walk(m_currentFrames, didRunOutOfVectorSpace);
288 wasValidWalk = walker.wasValidWalk();
289 }
290
291 m_jscExecutionThread->resume();
292
293 auto startTime = std::chrono::steady_clock::now();
294 // We can now use data structures that malloc, and do other interesting things, again.
295
296 // FIXME: It'd be interesting to take data about the program's state when
297 // we fail to take a stack trace: https://bugs.webkit.org/show_bug.cgi?id=152758
298 if (wasValidWalk && walkSize) {
299 if (sReportStats)
300 sNumTotalStackTraces++;
301 Vector<UnprocessedStackFrame> stackTrace;
302 stackTrace.reserveInitialCapacity(walkSize);
303 for (size_t i = 0; i < walkSize; i++) {
304 UnprocessedStackFrame frame = m_currentFrames[i];
305 stackTrace.uncheckedAppend(frame);
306 }
307
308 m_unprocessedStackTraces.append(UnprocessedStackTrace { nowTime, machinePC, topFrameIsLLInt, llintPC, WTFMove(stackTrace) });
309
310 if (didRunOutOfVectorSpace)
311 m_currentFrames.grow(m_currentFrames.size() * 1.25);
312 }
313
314 auto endTime = std::chrono::steady_clock::now();
315 stackTraceProcessingTime = std::chrono::duration_cast<std::chrono::microseconds>(endTime - startTime);
316 }
317 }
318}
319
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000320static ALWAYS_INLINE unsigned tryGetBytecodeIndex(unsigned llintPC, CodeBlock* codeBlock, bool& isValid)
321{
ossy@webkit.orgc8cea7b2016-02-18 16:45:26 +0000322#if ENABLE(DFG_JIT)
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000323 RELEASE_ASSERT(!codeBlock->hasCodeOrigins());
ossy@webkit.orgc8cea7b2016-02-18 16:45:26 +0000324#endif
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000325
326#if USE(JSVALUE64)
327 unsigned bytecodeIndex = llintPC;
328 if (bytecodeIndex < codeBlock->instructionCount()) {
329 isValid = true;
330 return bytecodeIndex;
331 }
332 isValid = false;
333 return 0;
334#else
335 Instruction* instruction = bitwise_cast<Instruction*>(llintPC);
336 if (instruction >= codeBlock->instructions().begin() && instruction < codeBlock->instructions().begin() + codeBlock->instructionCount()) {
337 isValid = true;
338 unsigned bytecodeIndex = instruction - codeBlock->instructions().begin();
339 return bytecodeIndex;
340 }
341 isValid = false;
342 return 0;
343#endif
344}
345
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000346void SamplingProfiler::processUnverifiedStackTraces()
347{
348 // This function needs to be called from the JSC execution thread.
349 RELEASE_ASSERT(m_lock.isLocked());
350
351 TinyBloomFilter filter = m_vm.heap.objectSpace().blocks().filter();
352 MarkedBlockSet& markedBlockSet = m_vm.heap.objectSpace().blocks();
353
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000354 for (UnprocessedStackTrace& unprocessedStackTrace : m_unprocessedStackTraces) {
355 m_stackTraces.append(StackTrace());
356 StackTrace& stackTrace = m_stackTraces.last();
357 stackTrace.timestamp = unprocessedStackTrace.timestamp;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000358
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000359 auto appendCodeBlock = [&] (CodeBlock* codeBlock, unsigned bytecodeIndex) {
360 stackTrace.frames.append(StackFrame(codeBlock->ownerExecutable()));
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000361 m_liveCellPointers.add(codeBlock->ownerExecutable());
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000362
363 if (bytecodeIndex < codeBlock->instructionCount()) {
364 int divot;
365 int startOffset;
366 int endOffset;
367 codeBlock->expressionRangeForBytecodeOffset(bytecodeIndex, divot, startOffset, endOffset,
368 stackTrace.frames.last().lineNumber, stackTrace.frames.last().columnNumber);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000369 }
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000370 };
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000371
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000372 auto appendEmptyFrame = [&] {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000373 stackTrace.frames.append(StackFrame());
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000374 };
375
376 auto storeCalleeIntoTopFrame = [&] (EncodedJSValue encodedCallee) {
377 // Set the callee if it's a valid GC object.
378 JSValue callee = JSValue::decode(encodedCallee);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000379 StackFrame& stackFrame = stackTrace.frames.last();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000380 bool alreadyHasExecutable = !!stackFrame.executable;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000381 if (!Heap::isValueGCObject(filter, markedBlockSet, callee)) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000382 if (!alreadyHasExecutable)
383 stackFrame.frameType = FrameType::Unknown;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000384 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000385 }
386
387 JSCell* calleeCell = callee.asCell();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000388 auto setFallbackFrameType = [&] {
389 ASSERT(!alreadyHasExecutable);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000390 FrameType result = FrameType::Unknown;
391 CallData callData;
392 CallType callType;
393 callType = getCallData(calleeCell, callData);
utatane.tea@gmail.comf76f1b42016-03-05 17:01:04 +0000394 if (callType == CallType::Host)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000395 result = FrameType::Host;
396
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000397 stackFrame.frameType = result;
398 };
399
400 auto addCallee = [&] (JSObject* callee) {
401 stackFrame.callee = callee;
402 m_liveCellPointers.add(callee);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000403 };
404
405 if (calleeCell->type() != JSFunctionType) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000406 if (JSObject* object = jsDynamicCast<JSObject*>(calleeCell))
407 addCallee(object);
408
409 if (!alreadyHasExecutable)
410 setFallbackFrameType();
411
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000412 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000413 }
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000414
415 addCallee(jsCast<JSFunction*>(calleeCell));
416
417 if (alreadyHasExecutable)
418 return;
419
420 ExecutableBase* executable = jsCast<JSFunction*>(calleeCell)->executable();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000421 if (!executable) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000422 setFallbackFrameType();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000423 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000424 }
425
426 RELEASE_ASSERT(Heap::isPointerGCObject(filter, markedBlockSet, executable));
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000427 stackFrame.frameType = FrameType::Executable;
428 stackFrame.executable = executable;
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000429 m_liveCellPointers.add(executable);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000430 };
431
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000432
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000433 // Prepend the top-most inlined frame if needed and gather
434 // location information about where the top frame is executing.
435 size_t startIndex = 0;
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000436 if (unprocessedStackTrace.frames.size() && !!unprocessedStackTrace.frames[0].verifiedCodeBlock) {
437 CodeBlock* topCodeBlock = unprocessedStackTrace.frames[0].verifiedCodeBlock;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000438 if (unprocessedStackTrace.topFrameIsLLInt) {
439 // We reuse LLInt CodeBlocks for the baseline JIT, so we need to check for both jit types.
440 // This might also be false for various reasons (known and unknown), even though
441 // it's super unlikely. One reason that this can be false is when we throw from a DFG frame,
442 // and we end up having to unwind past a VMEntryFrame, we will end up executing
443 // inside the LLInt's handleUncaughtException. So we just protect against this
444 // by ignoring it.
445 unsigned bytecodeIndex = 0;
446 if (topCodeBlock->jitType() == JITCode::InterpreterThunk || topCodeBlock->jitType() == JITCode::BaselineJIT) {
447 bool isValidPC;
448 unsigned bits;
449#if USE(JSVALUE64)
450 bits = static_cast<unsigned>(bitwise_cast<uintptr_t>(unprocessedStackTrace.llintPC));
451#else
452 bits = bitwise_cast<unsigned>(unprocessedStackTrace.llintPC);
453#endif
454 bytecodeIndex = tryGetBytecodeIndex(bits, topCodeBlock, isValidPC);
455
456 UNUSED_PARAM(isValidPC); // FIXME: do something with this info for the web inspector: https://bugs.webkit.org/show_bug.cgi?id=153455
457
458 appendCodeBlock(topCodeBlock, bytecodeIndex);
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000459 storeCalleeIntoTopFrame(unprocessedStackTrace.frames[0].unverifiedCallee);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000460 startIndex = 1;
461 }
462 } else if (Optional<CodeOrigin> codeOrigin = topCodeBlock->findPC(unprocessedStackTrace.topPC)) {
463 codeOrigin->walkUpInlineStack([&] (const CodeOrigin& codeOrigin) {
464 appendCodeBlock(codeOrigin.inlineCallFrame ? codeOrigin.inlineCallFrame->baselineCodeBlock.get() : topCodeBlock, codeOrigin.bytecodeIndex);
465 });
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000466 storeCalleeIntoTopFrame(unprocessedStackTrace.frames[0].unverifiedCallee);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000467 startIndex = 1;
468 }
469 }
470
471 for (size_t i = startIndex; i < unprocessedStackTrace.frames.size(); i++) {
472 UnprocessedStackFrame& unprocessedStackFrame = unprocessedStackTrace.frames[i];
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000473 if (CodeBlock* codeBlock = unprocessedStackFrame.verifiedCodeBlock) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000474 CallSiteIndex callSiteIndex = unprocessedStackFrame.callSiteIndex;
475
476 auto appendCodeBlockNoInlining = [&] {
477 bool isValidPC;
478 appendCodeBlock(codeBlock, tryGetBytecodeIndex(callSiteIndex.bits(), codeBlock, isValidPC));
479 };
480
481#if ENABLE(DFG_JIT)
482 if (codeBlock->hasCodeOrigins()) {
483 if (codeBlock->canGetCodeOrigin(callSiteIndex)) {
484 codeBlock->codeOrigin(callSiteIndex).walkUpInlineStack([&] (const CodeOrigin& codeOrigin) {
485 appendCodeBlock(codeOrigin.inlineCallFrame ? codeOrigin.inlineCallFrame->baselineCodeBlock.get() : codeBlock, codeOrigin.bytecodeIndex);
486 });
487 } else
488 appendCodeBlock(codeBlock, std::numeric_limits<unsigned>::max());
489 } else
490 appendCodeBlockNoInlining();
491#else
492 appendCodeBlockNoInlining();
493#endif
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000494 } else
495 appendEmptyFrame();
496
497 // Note that this is okay to do if we walked the inline stack because
498 // the machine frame will be at the top of the processed stack trace.
499 storeCalleeIntoTopFrame(unprocessedStackFrame.unverifiedCallee);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000500 }
501 }
502
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000503 m_unprocessedStackTraces.clear();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000504}
505
506void SamplingProfiler::visit(SlotVisitor& slotVisitor)
507{
508 RELEASE_ASSERT(m_lock.isLocked());
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000509 for (JSCell* cell : m_liveCellPointers)
510 slotVisitor.appendUnbarrieredReadOnlyPointer(cell);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000511}
512
513void SamplingProfiler::shutdown()
514{
sbarati@apple.com207a6832016-04-06 03:55:11 +0000515 LockHolder locker(m_lock);
516 m_isShutDown = true;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000517}
518
519void SamplingProfiler::start()
520{
521 LockHolder locker(m_lock);
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000522 start(locker);
523}
524
525void SamplingProfiler::start(const LockHolder& locker)
526{
527 ASSERT(m_lock.isLocked());
sbarati@apple.com207a6832016-04-06 03:55:11 +0000528 m_isPaused = false;
529 createThreadIfNecessary(locker);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000530}
531
sbarati@apple.com207a6832016-04-06 03:55:11 +0000532void SamplingProfiler::pause(const LockHolder&)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000533{
534 ASSERT(m_lock.isLocked());
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000535 m_isPaused = true;
536 reportStats();
537}
538
539void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread(const LockHolder&)
540{
541 ASSERT(m_lock.isLocked());
542 m_jscExecutionThread = m_vm.heap.machineThreads().machineThreadForCurrentThread();
543}
544
545void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread()
546{
547 LockHolder locker(m_lock);
548 noticeCurrentThreadAsJSCExecutionThread(locker);
549}
550
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000551void SamplingProfiler::noticeJSLockAcquisition()
552{
553 LockHolder locker(m_lock);
554 noticeCurrentThreadAsJSCExecutionThread(locker);
555}
556
557void SamplingProfiler::noticeVMEntry()
558{
559 LockHolder locker(m_lock);
560 ASSERT(m_vm.entryScope);
561 noticeCurrentThreadAsJSCExecutionThread(locker);
562 m_lastTime = m_stopwatch->elapsedTime();
sbarati@apple.com207a6832016-04-06 03:55:11 +0000563 createThreadIfNecessary(locker);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000564}
565
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000566void SamplingProfiler::clearData(const LockHolder&)
567{
568 ASSERT(m_lock.isLocked());
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000569 m_stackTraces.clear();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000570 m_liveCellPointers.clear();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000571 m_unprocessedStackTraces.clear();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000572}
573
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000574String SamplingProfiler::StackFrame::nameFromCallee(VM& vm)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000575{
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000576 if (!callee)
577 return String();
578
579 ExecState* exec = callee->globalObject()->globalExec();
580 auto getPropertyIfPureOperation = [&] (const Identifier& ident) -> String {
sbarati@apple.comea974132016-02-17 22:11:39 +0000581 PropertySlot slot(callee, PropertySlot::InternalMethodType::VMInquiry);
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000582 PropertyName propertyName(ident);
583 if (callee->getPropertySlot(exec, propertyName, slot)) {
584 if (slot.isValue()) {
585 JSValue nameValue = slot.getValue(exec, propertyName);
586 if (isJSString(nameValue))
587 return asString(nameValue)->tryGetValue();
588 }
589 }
590 return String();
591 };
592
593 String name = getPropertyIfPureOperation(vm.propertyNames->displayName);
594 if (!name.isEmpty())
595 return name;
596
597 return getPropertyIfPureOperation(vm.propertyNames->name);
598}
599
600String SamplingProfiler::StackFrame::displayName(VM& vm)
601{
602 {
603 String name = nameFromCallee(vm);
604 if (!name.isEmpty())
605 return name;
606 }
607
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000608 if (frameType == FrameType::Unknown)
609 return ASCIILiteral("(unknown)");
610 if (frameType == FrameType::Host)
611 return ASCIILiteral("(host)");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000612
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000613 if (executable->isHostFunction())
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000614 return static_cast<NativeExecutable*>(executable)->name();
615
616 if (executable->isFunctionExecutable())
617 return static_cast<FunctionExecutable*>(executable)->inferredName().string();
618 if (executable->isProgramExecutable() || executable->isEvalExecutable())
619 return ASCIILiteral("(program)");
620 if (executable->isModuleProgramExecutable())
621 return ASCIILiteral("(module)");
622
623 RELEASE_ASSERT_NOT_REACHED();
624 return String();
625}
626
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000627String SamplingProfiler::StackFrame::displayNameForJSONTests(VM& vm)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000628{
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000629 {
630 String name = nameFromCallee(vm);
631 if (!name.isEmpty())
632 return name;
633 }
634
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000635 if (frameType == FrameType::Unknown)
636 return ASCIILiteral("(unknown)");
637 if (frameType == FrameType::Host)
638 return ASCIILiteral("(host)");
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000639
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000640 if (executable->isHostFunction())
641 return static_cast<NativeExecutable*>(executable)->name();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000642
643 if (executable->isFunctionExecutable()) {
644 String result = static_cast<FunctionExecutable*>(executable)->inferredName().string();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000645 if (result.isEmpty())
646 return ASCIILiteral("(anonymous function)");
647 return result;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000648 }
649 if (executable->isEvalExecutable())
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000650 return ASCIILiteral("(eval)");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000651 if (executable->isProgramExecutable())
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000652 return ASCIILiteral("(program)");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000653 if (executable->isModuleProgramExecutable())
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000654 return ASCIILiteral("(module)");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000655
656 RELEASE_ASSERT_NOT_REACHED();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000657 return String();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000658}
659
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000660int SamplingProfiler::StackFrame::functionStartLine()
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000661{
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000662 if (frameType == FrameType::Unknown || frameType == FrameType::Host)
663 return -1;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000664
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000665 if (executable->isHostFunction())
666 return -1;
667 return static_cast<ScriptExecutable*>(executable)->firstLine();
668}
669
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000670unsigned SamplingProfiler::StackFrame::functionStartColumn()
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000671{
672 if (frameType == FrameType::Unknown || frameType == FrameType::Host)
673 return -1;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000674
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000675 if (executable->isHostFunction())
676 return -1;
677
678 return static_cast<ScriptExecutable*>(executable)->startColumn();
679}
680
681intptr_t SamplingProfiler::StackFrame::sourceID()
682{
683 if (frameType == FrameType::Unknown || frameType == FrameType::Host)
684 return -1;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000685
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000686 if (executable->isHostFunction())
687 return -1;
688
689 return static_cast<ScriptExecutable*>(executable)->sourceID();
690}
691
692String SamplingProfiler::StackFrame::url()
693{
694 if (frameType == FrameType::Unknown || frameType == FrameType::Host)
695 return emptyString();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000696
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000697 if (executable->isHostFunction())
698 return emptyString();
699
700 String url = static_cast<ScriptExecutable*>(executable)->sourceURL();
701 if (url.isEmpty())
702 return static_cast<ScriptExecutable*>(executable)->source().provider()->sourceURL(); // Fall back to sourceURL directive.
703 return url;
704}
705
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000706Vector<SamplingProfiler::StackTrace> SamplingProfiler::releaseStackTraces(const LockHolder& locker)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000707{
708 ASSERT(m_lock.isLocked());
709 {
710 HeapIterationScope heapIterationScope(m_vm.heap);
711 processUnverifiedStackTraces();
712 }
713
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000714 Vector<StackTrace> result(WTFMove(m_stackTraces));
715 clearData(locker);
716 return result;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000717}
718
719String SamplingProfiler::stackTracesAsJSON()
720{
721 LockHolder locker(m_lock);
722
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000723 {
724 HeapIterationScope heapIterationScope(m_vm.heap);
725 processUnverifiedStackTraces();
726 }
727
728 StringBuilder json;
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000729 json.append('[');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000730
731 bool loopedOnce = false;
732 auto comma = [&] {
733 if (loopedOnce)
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000734 json.append(',');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000735 };
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000736 for (StackTrace& stackTrace : m_stackTraces) {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000737 comma();
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000738 json.append('[');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000739 loopedOnce = false;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000740 for (StackFrame& stackFrame : stackTrace.frames) {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000741 comma();
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000742 json.append('"');
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000743 json.append(stackFrame.displayNameForJSONTests(m_vm));
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000744 json.append('"');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000745 loopedOnce = true;
746 }
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000747 json.append(']');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000748 loopedOnce = true;
749 }
750
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000751 json.append(']');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000752
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000753 clearData(locker);
754
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000755 return json.toString();
756}
757
758} // namespace JSC
759
760namespace WTF {
761
762using namespace JSC;
763
764void printInternal(PrintStream& out, SamplingProfiler::FrameType frameType)
765{
766 switch (frameType) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000767 case SamplingProfiler::FrameType::Executable:
768 out.print("Executable");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000769 break;
770 case SamplingProfiler::FrameType::Host:
771 out.print("Host");
772 break;
773 case SamplingProfiler::FrameType::Unknown:
774 out.print("Unknown");
775 break;
776 }
777}
778
779} // namespace WTF
780
781#endif // ENABLE(SAMPLING_PROFILER)