blob: 296b54917994389ce9745cdec8972a4b38a49fd6 [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))
189 , m_totalTime(0)
190 , m_timerQueue(WorkQueue::create("jsc.sampling-profiler.queue", WorkQueue::Type::Serial, WorkQueue::QOS::UserInteractive))
191 , m_jscExecutionThread(nullptr)
192 , m_isActive(false)
193 , m_isPaused(false)
194 , m_hasDispatchedFunction(false)
195{
196 if (sReportStats) {
197 sNumTotalWalks = 0;
198 sNumFailedWalks = 0;
199 }
200
201 m_currentFrames.grow(256);
202
203 m_handler = [this] () {
204 LockHolder samplingProfilerLocker(m_lock);
205 if (!m_isActive || !m_jscExecutionThread || m_isPaused) {
206 m_hasDispatchedFunction = false;
207 deref();
208 return;
209 }
210
211 if (m_vm.entryScope) {
212 double nowTime = m_stopwatch->elapsedTime();
213
214 LockHolder machineThreadsLocker(m_vm.heap.machineThreads().getLock());
215 LockHolder codeBlockSetLocker(m_vm.heap.codeBlockSet().getLock());
216 LockHolder executableAllocatorLocker(m_vm.executableAllocator.getLock());
217
218 bool didSuspend = m_jscExecutionThread->suspend();
219 if (didSuspend) {
220 // While the JSC thread is suspended, we can't do things like malloc because the JSC thread
221 // may be holding the malloc lock.
222 ExecState* callFrame;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000223 void* machinePC;
224 bool topFrameIsLLInt = false;
225 void* llintPC;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000226 {
227 MachineThreads::Thread::Registers registers;
228 m_jscExecutionThread->getRegisters(registers);
229 callFrame = static_cast<ExecState*>(registers.framePointer());
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000230 machinePC = registers.instructionPointer();
231 llintPC = registers.llintPC();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000232 m_jscExecutionThread->freeRegisters(registers);
233 }
234 // FIXME: Lets have a way of detecting when we're parsing code.
235 // https://bugs.webkit.org/show_bug.cgi?id=152761
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000236 if (m_vm.executableAllocator.isValidExecutableMemory(executableAllocatorLocker, machinePC)) {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000237 if (m_vm.isExecutingInRegExpJIT) {
238 // FIXME: We're executing a regexp. Lets gather more intersting data.
239 // https://bugs.webkit.org/show_bug.cgi?id=152729
240 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.
241 }
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000242 } else if (LLInt::isLLIntPC(machinePC)) {
243 topFrameIsLLInt = true;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000244 // We're okay to take a normal stack trace when the PC
245 // is in LLInt code.
246 } else {
247 // We resort to topCallFrame to see if we can get anything
248 // useful. We usually get here when we're executing C code.
249 callFrame = m_vm.topCallFrame;
250 }
251
252 size_t walkSize;
253 bool wasValidWalk;
254 bool didRunOutOfVectorSpace;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000255 {
256 FrameWalker walker(callFrame, m_vm, codeBlockSetLocker, machineThreadsLocker);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000257 walkSize = walker.walk(m_currentFrames, didRunOutOfVectorSpace);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000258 wasValidWalk = walker.wasValidWalk();
259 }
260
261 m_jscExecutionThread->resume();
262
263 // We can now use data structures that malloc, and do other interesting things, again.
264
265 // FIXME: It'd be interesting to take data about the program's state when
266 // we fail to take a stack trace: https://bugs.webkit.org/show_bug.cgi?id=152758
267 if (wasValidWalk && walkSize) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000268 if (sReportStats)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000269 sNumTotalStackTraces++;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000270 Vector<UnprocessedStackFrame> stackTrace;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000271 stackTrace.reserveInitialCapacity(walkSize);
272 for (size_t i = 0; i < walkSize; i++) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000273 UnprocessedStackFrame frame = m_currentFrames[i];
274 stackTrace.uncheckedAppend(frame);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000275 }
276
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000277 m_unprocessedStackTraces.append(UnprocessedStackTrace { nowTime, machinePC, topFrameIsLLInt, llintPC, WTFMove(stackTrace) });
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000278
279 if (didRunOutOfVectorSpace)
280 m_currentFrames.grow(m_currentFrames.size() * 1.25);
281
282 m_totalTime += nowTime - m_lastTime;
283 }
284 }
285 }
286
287 m_lastTime = m_stopwatch->elapsedTime();
288
289 dispatchFunction(samplingProfilerLocker);
290 };
291}
292
293SamplingProfiler::~SamplingProfiler()
294{
295}
296
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000297static ALWAYS_INLINE unsigned tryGetBytecodeIndex(unsigned llintPC, CodeBlock* codeBlock, bool& isValid)
298{
299 RELEASE_ASSERT(!codeBlock->hasCodeOrigins());
300
301#if USE(JSVALUE64)
302 unsigned bytecodeIndex = llintPC;
303 if (bytecodeIndex < codeBlock->instructionCount()) {
304 isValid = true;
305 return bytecodeIndex;
306 }
307 isValid = false;
308 return 0;
309#else
310 Instruction* instruction = bitwise_cast<Instruction*>(llintPC);
311 if (instruction >= codeBlock->instructions().begin() && instruction < codeBlock->instructions().begin() + codeBlock->instructionCount()) {
312 isValid = true;
313 unsigned bytecodeIndex = instruction - codeBlock->instructions().begin();
314 return bytecodeIndex;
315 }
316 isValid = false;
317 return 0;
318#endif
319}
320
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000321void SamplingProfiler::processUnverifiedStackTraces()
322{
323 // This function needs to be called from the JSC execution thread.
324 RELEASE_ASSERT(m_lock.isLocked());
325
326 TinyBloomFilter filter = m_vm.heap.objectSpace().blocks().filter();
327 MarkedBlockSet& markedBlockSet = m_vm.heap.objectSpace().blocks();
328
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000329 for (UnprocessedStackTrace& unprocessedStackTrace : m_unprocessedStackTraces) {
330 m_stackTraces.append(StackTrace());
331 StackTrace& stackTrace = m_stackTraces.last();
332 stackTrace.timestamp = unprocessedStackTrace.timestamp;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000333
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000334 auto appendCodeBlock = [&] (CodeBlock* codeBlock, unsigned bytecodeIndex) {
335 stackTrace.frames.append(StackFrame(codeBlock->ownerExecutable()));
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000336 m_liveCellPointers.add(codeBlock->ownerExecutable());
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000337
338 if (bytecodeIndex < codeBlock->instructionCount()) {
339 int divot;
340 int startOffset;
341 int endOffset;
342 codeBlock->expressionRangeForBytecodeOffset(bytecodeIndex, divot, startOffset, endOffset,
343 stackTrace.frames.last().lineNumber, stackTrace.frames.last().columnNumber);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000344 }
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000345 };
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000346
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000347 auto appendEmptyFrame = [&] {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000348 stackTrace.frames.append(StackFrame());
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000349 };
350
351 auto storeCalleeIntoTopFrame = [&] (EncodedJSValue encodedCallee) {
352 // Set the callee if it's a valid GC object.
353 JSValue callee = JSValue::decode(encodedCallee);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000354 StackFrame& stackFrame = stackTrace.frames.last();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000355 bool alreadyHasExecutable = !!stackFrame.executable;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000356 if (!Heap::isValueGCObject(filter, markedBlockSet, callee)) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000357 if (!alreadyHasExecutable)
358 stackFrame.frameType = FrameType::Unknown;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000359 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000360 }
361
362 JSCell* calleeCell = callee.asCell();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000363 auto setFallbackFrameType = [&] {
364 ASSERT(!alreadyHasExecutable);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000365 FrameType result = FrameType::Unknown;
366 CallData callData;
367 CallType callType;
368 callType = getCallData(calleeCell, callData);
369 if (callType == CallTypeHost)
370 result = FrameType::Host;
371
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000372 stackFrame.frameType = result;
373 };
374
375 auto addCallee = [&] (JSObject* callee) {
376 stackFrame.callee = callee;
377 m_liveCellPointers.add(callee);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000378 };
379
380 if (calleeCell->type() != JSFunctionType) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000381 if (JSObject* object = jsDynamicCast<JSObject*>(calleeCell))
382 addCallee(object);
383
384 if (!alreadyHasExecutable)
385 setFallbackFrameType();
386
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000387 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000388 }
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000389
390 addCallee(jsCast<JSFunction*>(calleeCell));
391
392 if (alreadyHasExecutable)
393 return;
394
395 ExecutableBase* executable = jsCast<JSFunction*>(calleeCell)->executable();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000396 if (!executable) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000397 setFallbackFrameType();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000398 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000399 }
400
401 RELEASE_ASSERT(Heap::isPointerGCObject(filter, markedBlockSet, executable));
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000402 stackFrame.frameType = FrameType::Executable;
403 stackFrame.executable = executable;
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000404 m_liveCellPointers.add(executable);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000405 };
406
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000407
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000408 // Prepend the top-most inlined frame if needed and gather
409 // location information about where the top frame is executing.
410 size_t startIndex = 0;
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000411 if (unprocessedStackTrace.frames.size() && !!unprocessedStackTrace.frames[0].verifiedCodeBlock) {
412 CodeBlock* topCodeBlock = unprocessedStackTrace.frames[0].verifiedCodeBlock;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000413 if (unprocessedStackTrace.topFrameIsLLInt) {
414 // We reuse LLInt CodeBlocks for the baseline JIT, so we need to check for both jit types.
415 // This might also be false for various reasons (known and unknown), even though
416 // it's super unlikely. One reason that this can be false is when we throw from a DFG frame,
417 // and we end up having to unwind past a VMEntryFrame, we will end up executing
418 // inside the LLInt's handleUncaughtException. So we just protect against this
419 // by ignoring it.
420 unsigned bytecodeIndex = 0;
421 if (topCodeBlock->jitType() == JITCode::InterpreterThunk || topCodeBlock->jitType() == JITCode::BaselineJIT) {
422 bool isValidPC;
423 unsigned bits;
424#if USE(JSVALUE64)
425 bits = static_cast<unsigned>(bitwise_cast<uintptr_t>(unprocessedStackTrace.llintPC));
426#else
427 bits = bitwise_cast<unsigned>(unprocessedStackTrace.llintPC);
428#endif
429 bytecodeIndex = tryGetBytecodeIndex(bits, topCodeBlock, isValidPC);
430
431 UNUSED_PARAM(isValidPC); // FIXME: do something with this info for the web inspector: https://bugs.webkit.org/show_bug.cgi?id=153455
432
433 appendCodeBlock(topCodeBlock, bytecodeIndex);
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000434 storeCalleeIntoTopFrame(unprocessedStackTrace.frames[0].unverifiedCallee);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000435 startIndex = 1;
436 }
437 } else if (Optional<CodeOrigin> codeOrigin = topCodeBlock->findPC(unprocessedStackTrace.topPC)) {
438 codeOrigin->walkUpInlineStack([&] (const CodeOrigin& codeOrigin) {
439 appendCodeBlock(codeOrigin.inlineCallFrame ? codeOrigin.inlineCallFrame->baselineCodeBlock.get() : topCodeBlock, codeOrigin.bytecodeIndex);
440 });
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000441 storeCalleeIntoTopFrame(unprocessedStackTrace.frames[0].unverifiedCallee);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000442 startIndex = 1;
443 }
444 }
445
446 for (size_t i = startIndex; i < unprocessedStackTrace.frames.size(); i++) {
447 UnprocessedStackFrame& unprocessedStackFrame = unprocessedStackTrace.frames[i];
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000448 if (CodeBlock* codeBlock = unprocessedStackFrame.verifiedCodeBlock) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000449 CallSiteIndex callSiteIndex = unprocessedStackFrame.callSiteIndex;
450
451 auto appendCodeBlockNoInlining = [&] {
452 bool isValidPC;
453 appendCodeBlock(codeBlock, tryGetBytecodeIndex(callSiteIndex.bits(), codeBlock, isValidPC));
454 };
455
456#if ENABLE(DFG_JIT)
457 if (codeBlock->hasCodeOrigins()) {
458 if (codeBlock->canGetCodeOrigin(callSiteIndex)) {
459 codeBlock->codeOrigin(callSiteIndex).walkUpInlineStack([&] (const CodeOrigin& codeOrigin) {
460 appendCodeBlock(codeOrigin.inlineCallFrame ? codeOrigin.inlineCallFrame->baselineCodeBlock.get() : codeBlock, codeOrigin.bytecodeIndex);
461 });
462 } else
463 appendCodeBlock(codeBlock, std::numeric_limits<unsigned>::max());
464 } else
465 appendCodeBlockNoInlining();
466#else
467 appendCodeBlockNoInlining();
468#endif
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000469 } else
470 appendEmptyFrame();
471
472 // Note that this is okay to do if we walked the inline stack because
473 // the machine frame will be at the top of the processed stack trace.
474 storeCalleeIntoTopFrame(unprocessedStackFrame.unverifiedCallee);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000475 }
476 }
477
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000478 m_unprocessedStackTraces.clear();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000479}
480
481void SamplingProfiler::visit(SlotVisitor& slotVisitor)
482{
483 RELEASE_ASSERT(m_lock.isLocked());
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000484 for (JSCell* cell : m_liveCellPointers)
485 slotVisitor.appendUnbarrieredReadOnlyPointer(cell);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000486}
487
488void SamplingProfiler::shutdown()
489{
490 stop();
491}
492
493void SamplingProfiler::start()
494{
495 LockHolder locker(m_lock);
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000496 start(locker);
497}
498
499void SamplingProfiler::start(const LockHolder& locker)
500{
501 ASSERT(m_lock.isLocked());
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000502 m_isActive = true;
503 dispatchIfNecessary(locker);
504}
505
506void SamplingProfiler::stop()
507{
508 LockHolder locker(m_lock);
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000509 stop(locker);
510}
511
512void SamplingProfiler::stop(const LockHolder&)
513{
514 ASSERT(m_lock.isLocked());
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000515 m_isActive = false;
516 reportStats();
517}
518
519void SamplingProfiler::pause()
520{
521 LockHolder locker(m_lock);
522 m_isPaused = true;
523 reportStats();
524}
525
526void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread(const LockHolder&)
527{
528 ASSERT(m_lock.isLocked());
529 m_jscExecutionThread = m_vm.heap.machineThreads().machineThreadForCurrentThread();
530}
531
532void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread()
533{
534 LockHolder locker(m_lock);
535 noticeCurrentThreadAsJSCExecutionThread(locker);
536}
537
538void SamplingProfiler::dispatchIfNecessary(const LockHolder& locker)
539{
540 if (m_isActive && !m_hasDispatchedFunction && m_jscExecutionThread && m_vm.entryScope) {
541 ref(); // Matching deref() is inside m_handler when m_handler stops recursing.
542 dispatchFunction(locker);
543 }
544}
545
546void SamplingProfiler::dispatchFunction(const LockHolder&)
547{
548 m_hasDispatchedFunction = true;
549 m_isPaused = false;
550 m_lastTime = m_stopwatch->elapsedTime();
551 m_timerQueue->dispatchAfter(m_timingInterval, m_handler);
552}
553
554void SamplingProfiler::noticeJSLockAcquisition()
555{
556 LockHolder locker(m_lock);
557 noticeCurrentThreadAsJSCExecutionThread(locker);
558}
559
560void SamplingProfiler::noticeVMEntry()
561{
562 LockHolder locker(m_lock);
563 ASSERT(m_vm.entryScope);
564 noticeCurrentThreadAsJSCExecutionThread(locker);
565 m_lastTime = m_stopwatch->elapsedTime();
566 dispatchIfNecessary(locker);
567}
568
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000569void SamplingProfiler::clearData(const LockHolder&)
570{
571 ASSERT(m_lock.isLocked());
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000572 m_stackTraces.clear();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000573 m_liveCellPointers.clear();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000574 m_unprocessedStackTraces.clear();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000575}
576
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000577String SamplingProfiler::StackFrame::nameFromCallee(VM& vm)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000578{
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000579 if (!callee)
580 return String();
581
582 ExecState* exec = callee->globalObject()->globalExec();
583 auto getPropertyIfPureOperation = [&] (const Identifier& ident) -> String {
584 PropertySlot slot(callee);
585 PropertyName propertyName(ident);
586 if (callee->getPropertySlot(exec, propertyName, slot)) {
587 if (slot.isValue()) {
588 JSValue nameValue = slot.getValue(exec, propertyName);
589 if (isJSString(nameValue))
590 return asString(nameValue)->tryGetValue();
591 }
592 }
593 return String();
594 };
595
596 String name = getPropertyIfPureOperation(vm.propertyNames->displayName);
597 if (!name.isEmpty())
598 return name;
599
600 return getPropertyIfPureOperation(vm.propertyNames->name);
601}
602
603String SamplingProfiler::StackFrame::displayName(VM& vm)
604{
605 {
606 String name = nameFromCallee(vm);
607 if (!name.isEmpty())
608 return name;
609 }
610
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000611 if (frameType == FrameType::Unknown)
612 return ASCIILiteral("(unknown)");
613 if (frameType == FrameType::Host)
614 return ASCIILiteral("(host)");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000615
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000616 if (executable->isHostFunction())
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000617 return static_cast<NativeExecutable*>(executable)->name();
618
619 if (executable->isFunctionExecutable())
620 return static_cast<FunctionExecutable*>(executable)->inferredName().string();
621 if (executable->isProgramExecutable() || executable->isEvalExecutable())
622 return ASCIILiteral("(program)");
623 if (executable->isModuleProgramExecutable())
624 return ASCIILiteral("(module)");
625
626 RELEASE_ASSERT_NOT_REACHED();
627 return String();
628}
629
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000630String SamplingProfiler::StackFrame::displayNameForJSONTests(VM& vm)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000631{
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000632 {
633 String name = nameFromCallee(vm);
634 if (!name.isEmpty())
635 return name;
636 }
637
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000638 if (frameType == FrameType::Unknown)
639 return ASCIILiteral("(unknown)");
640 if (frameType == FrameType::Host)
641 return ASCIILiteral("(host)");
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000642
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000643 if (executable->isHostFunction())
644 return static_cast<NativeExecutable*>(executable)->name();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000645
646 if (executable->isFunctionExecutable()) {
647 String result = static_cast<FunctionExecutable*>(executable)->inferredName().string();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000648 if (result.isEmpty())
649 return ASCIILiteral("(anonymous function)");
650 return result;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000651 }
652 if (executable->isEvalExecutable())
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000653 return ASCIILiteral("(eval)");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000654 if (executable->isProgramExecutable())
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000655 return ASCIILiteral("(program)");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000656 if (executable->isModuleProgramExecutable())
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000657 return ASCIILiteral("(module)");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000658
659 RELEASE_ASSERT_NOT_REACHED();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000660 return String();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000661}
662
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000663int SamplingProfiler::StackFrame::functionStartLine()
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000664{
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000665 if (frameType == FrameType::Unknown || frameType == FrameType::Host)
666 return -1;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000667
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000668 if (executable->isHostFunction())
669 return -1;
670 return static_cast<ScriptExecutable*>(executable)->firstLine();
671}
672
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000673unsigned SamplingProfiler::StackFrame::functionStartColumn()
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000674{
675 if (frameType == FrameType::Unknown || frameType == FrameType::Host)
676 return -1;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000677
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000678 if (executable->isHostFunction())
679 return -1;
680
681 return static_cast<ScriptExecutable*>(executable)->startColumn();
682}
683
684intptr_t SamplingProfiler::StackFrame::sourceID()
685{
686 if (frameType == FrameType::Unknown || frameType == FrameType::Host)
687 return -1;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000688
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000689 if (executable->isHostFunction())
690 return -1;
691
692 return static_cast<ScriptExecutable*>(executable)->sourceID();
693}
694
695String SamplingProfiler::StackFrame::url()
696{
697 if (frameType == FrameType::Unknown || frameType == FrameType::Host)
698 return emptyString();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000699
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000700 if (executable->isHostFunction())
701 return emptyString();
702
703 String url = static_cast<ScriptExecutable*>(executable)->sourceURL();
704 if (url.isEmpty())
705 return static_cast<ScriptExecutable*>(executable)->source().provider()->sourceURL(); // Fall back to sourceURL directive.
706 return url;
707}
708
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000709Vector<SamplingProfiler::StackTrace> SamplingProfiler::releaseStackTraces(const LockHolder& locker)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000710{
711 ASSERT(m_lock.isLocked());
712 {
713 HeapIterationScope heapIterationScope(m_vm.heap);
714 processUnverifiedStackTraces();
715 }
716
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000717 Vector<StackTrace> result(WTFMove(m_stackTraces));
718 clearData(locker);
719 return result;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000720}
721
722String SamplingProfiler::stackTracesAsJSON()
723{
724 LockHolder locker(m_lock);
725
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000726 {
727 HeapIterationScope heapIterationScope(m_vm.heap);
728 processUnverifiedStackTraces();
729 }
730
731 StringBuilder json;
732 json.appendLiteral("[");
733
734 bool loopedOnce = false;
735 auto comma = [&] {
736 if (loopedOnce)
737 json.appendLiteral(",");
738 };
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000739 for (StackTrace& stackTrace : m_stackTraces) {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000740 comma();
741 json.appendLiteral("[");
742 loopedOnce = false;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000743 for (StackFrame& stackFrame : stackTrace.frames) {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000744 comma();
745 json.appendLiteral("\"");
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000746 json.append(stackFrame.displayNameForJSONTests(m_vm));
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000747 json.appendLiteral("\"");
748 loopedOnce = true;
749 }
750 json.appendLiteral("]");
751 loopedOnce = true;
752 }
753
754 json.appendLiteral("]");
755
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000756 clearData(locker);
757
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000758 return json.toString();
759}
760
761} // namespace JSC
762
763namespace WTF {
764
765using namespace JSC;
766
767void printInternal(PrintStream& out, SamplingProfiler::FrameType frameType)
768{
769 switch (frameType) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000770 case SamplingProfiler::FrameType::Executable:
771 out.print("Executable");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000772 break;
773 case SamplingProfiler::FrameType::Host:
774 out.print("Host");
775 break;
776 case SamplingProfiler::FrameType::Unknown:
777 out.print("Unknown");
778 break;
779 }
780}
781
782} // namespace WTF
783
784#endif // ENABLE(SAMPLING_PROFILER)