blob: 799fe94b16ed844b660e00d2f4c94dad6c669425 [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"
fpizlo@apple.com7b231642016-10-11 23:52:02 +000033#include "CodeBlockSet.h"
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000034#include "HeapIterationScope.h"
fpizlo@apple.combc16ddb2016-09-06 01:02:22 +000035#include "HeapUtil.h"
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000036#include "InlineCallFrame.h"
37#include "Interpreter.h"
fpizlo@apple.com0ef43952016-12-08 22:14:50 +000038#include "JSCInlines.h"
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000039#include "JSFunction.h"
40#include "LLIntPCRanges.h"
41#include "MarkedBlock.h"
42#include "MarkedBlockSet.h"
fpizlo@apple.com96352992016-09-20 18:12:18 +000043#include "MarkedSpaceInlines.h"
ggaren@apple.comc9a6ef42016-10-28 20:04:56 +000044#include "NativeExecutable.h"
sbarati@apple.comd3d0c002016-01-30 01:11:05 +000045#include "PCToCodeOriginMap.h"
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000046#include "SlotVisitor.h"
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +000047#include "StrongInlines.h"
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000048#include "VM.h"
keith_miller@apple.com3517aa42016-05-24 23:03:09 +000049#include <wtf/HashSet.h>
sbarati@apple.com51d80072016-06-14 02:29:26 +000050#include <wtf/RandomNumber.h>
keith_miller@apple.com3517aa42016-05-24 23:03:09 +000051#include <wtf/RefPtr.h>
joepeck@webkit.org7e07f392016-09-22 18:59:47 +000052#include <wtf/text/StringBuilder.h>
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000053
keith_miller@apple.comb4a86442017-02-02 01:23:37 +000054#if OS(DARWIN)
55#include <cxxabi.h>
56#include <dlfcn.h>
57#endif
58
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000059namespace JSC {
60
61static double sNumTotalStackTraces = 0;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000062static double sNumTotalWalks = 0;
63static double sNumFailedWalks = 0;
64static const uint32_t sNumWalkReportingFrequency = 50;
65static const double sWalkErrorPercentage = .05;
66static const bool sReportStatsOnlyWhenTheyreAboveThreshold = false;
67static const bool sReportStats = false;
68
69using FrameType = SamplingProfiler::FrameType;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +000070using UnprocessedStackFrame = SamplingProfiler::UnprocessedStackFrame;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000071
72ALWAYS_INLINE static void reportStats()
73{
74 if (sReportStats && sNumTotalWalks && static_cast<uint64_t>(sNumTotalWalks) % sNumWalkReportingFrequency == 0) {
75 if (!sReportStatsOnlyWhenTheyreAboveThreshold || (sNumFailedWalks / sNumTotalWalks > sWalkErrorPercentage)) {
76 dataLogF("Num total walks: %llu. Failed walks percent: %lf\n",
utatane.tea@gmail.com0cf8d742016-01-30 12:53:59 +000077 static_cast<unsigned long long>(sNumTotalWalks), sNumFailedWalks / sNumTotalWalks);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000078 }
79 }
80}
81
82class FrameWalker {
83public:
mark.lam@apple.coma2668e02017-03-09 21:05:41 +000084 FrameWalker(VM& vm, ExecState* callFrame, const AbstractLocker& codeBlockSetLocker, const AbstractLocker& machineThreadsLocker)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000085 : m_vm(vm)
86 , m_callFrame(callFrame)
87 , m_vmEntryFrame(vm.topVMEntryFrame)
88 , m_codeBlockSetLocker(codeBlockSetLocker)
89 , m_machineThreadsLocker(machineThreadsLocker)
90 {
91 }
92
sbarati@apple.com30cd1162016-02-11 21:04:43 +000093 SUPPRESS_ASAN
sbarati@apple.comd3d0c002016-01-30 01:11:05 +000094 size_t walk(Vector<UnprocessedStackFrame>& stackTrace, bool& didRunOutOfSpace)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000095 {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000096 if (sReportStats)
97 sNumTotalWalks++;
98 resetAtMachineFrame();
99 size_t maxStackTraceSize = stackTrace.size();
100 while (!isAtTop() && !m_bailingOut && m_depth < maxStackTraceSize) {
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000101 recordJSFrame(stackTrace);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000102 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
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000115protected:
116
117 SUPPRESS_ASAN
118 void recordJSFrame(Vector<UnprocessedStackFrame>& stackTrace)
119 {
120 CallSiteIndex callSiteIndex;
121 JSValue unsafeCallee = m_callFrame->unsafeCallee();
122 CodeBlock* codeBlock = m_callFrame->unsafeCodeBlock();
123 if (codeBlock) {
124 ASSERT(isValidCodeBlock(codeBlock));
125 callSiteIndex = m_callFrame->unsafeCallSiteIndex();
126 }
127 stackTrace[m_depth] = UnprocessedStackFrame(codeBlock, JSValue::encode(unsafeCallee), callSiteIndex);
128 m_depth++;
129 }
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000130
sbarati@apple.com30cd1162016-02-11 21:04:43 +0000131 SUPPRESS_ASAN
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000132 void advanceToParentFrame()
133 {
sbarati@apple.com88a5fd62016-02-16 22:01:37 +0000134 m_callFrame = m_callFrame->unsafeCallerFrame(m_vmEntryFrame);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000135 }
136
137 bool isAtTop() const
138 {
139 return !m_callFrame;
140 }
141
sbarati@apple.com30cd1162016-02-11 21:04:43 +0000142 SUPPRESS_ASAN
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000143 void resetAtMachineFrame()
144 {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000145 if (isAtTop())
146 return;
147
148 if (!isValidFramePointer(m_callFrame)) {
149 // Guard against pausing the process at weird program points.
150 m_bailingOut = true;
151 if (sReportStats)
152 sNumFailedWalks++;
153 return;
154 }
155
sbarati@apple.com88a5fd62016-02-16 22:01:37 +0000156 CodeBlock* codeBlock = m_callFrame->unsafeCodeBlock();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000157 if (!codeBlock)
158 return;
159
160 if (!isValidCodeBlock(codeBlock)) {
161 m_bailingOut = true;
162 if (sReportStats)
163 sNumFailedWalks++;
164 return;
165 }
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000166 }
167
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000168 bool isValidFramePointer(void* exec)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000169 {
170 uint8_t* fpCast = bitwise_cast<uint8_t*>(exec);
171 for (MachineThreads::Thread* thread = m_vm.heap.machineThreads().threadsListHead(m_machineThreadsLocker); thread; thread = thread->next) {
172 uint8_t* stackBase = static_cast<uint8_t*>(thread->stackBase);
173 uint8_t* stackLimit = static_cast<uint8_t*>(thread->stackEnd);
174 RELEASE_ASSERT(stackBase);
175 RELEASE_ASSERT(stackLimit);
176 if (fpCast <= stackBase && fpCast >= stackLimit)
177 return true;
178 }
179 return false;
180 }
181
182 bool isValidCodeBlock(CodeBlock* codeBlock)
183 {
184 if (!codeBlock)
185 return false;
186 bool result = m_vm.heap.codeBlockSet().contains(m_codeBlockSetLocker, codeBlock);
187 return result;
188 }
189
190 VM& m_vm;
191 ExecState* m_callFrame;
192 VMEntryFrame* m_vmEntryFrame;
mark.lam@apple.coma2668e02017-03-09 21:05:41 +0000193 const AbstractLocker& m_codeBlockSetLocker;
194 const AbstractLocker& m_machineThreadsLocker;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000195 bool m_bailingOut { false };
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000196 size_t m_depth { 0 };
197};
198
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000199class CFrameWalker : public FrameWalker {
200public:
201 typedef FrameWalker Base;
202
mark.lam@apple.coma2668e02017-03-09 21:05:41 +0000203 CFrameWalker(VM& vm, void* machineFrame, ExecState* callFrame, const AbstractLocker& codeBlockSetLocker, const AbstractLocker& machineThreadsLocker)
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000204 : Base(vm, callFrame, codeBlockSetLocker, machineThreadsLocker)
205 , m_machineFrame(machineFrame)
206 {
207 }
208
209 size_t walk(Vector<UnprocessedStackFrame>& stackTrace, bool& didRunOutOfSpace)
210 {
211 if (sReportStats)
212 sNumTotalWalks++;
213 resetAtMachineFrame();
214 size_t maxStackTraceSize = stackTrace.size();
215 // The way the C walker decides if a frame it is about to trace is C or JS is by
216 // ensuring m_callFrame points to some frame above the machineFrame.
217 if (!isAtTop() && !m_bailingOut && m_machineFrame == m_callFrame) {
218 recordJSFrame(stackTrace);
219 Base::advanceToParentFrame();
220 resetAtMachineFrame();
221 }
222
223 while (!isAtTop() && !m_bailingOut && m_depth < maxStackTraceSize) {
224 if (m_machineFrame >= m_callFrame) {
225 // If we get to this state we probably have an invalid trace.
226 m_bailingOut = true;
227 break;
228 }
229
230 if (isCFrame()) {
231 RELEASE_ASSERT(!LLInt::isLLIntPC(frame()->callerFrame));
232 stackTrace[m_depth] = UnprocessedStackFrame(frame()->pc);
233 m_depth++;
234 } else
235 recordJSFrame(stackTrace);
236 advanceToParentFrame();
237 resetAtMachineFrame();
238 }
239 didRunOutOfSpace = m_depth >= maxStackTraceSize && !isAtTop();
240 reportStats();
241 return m_depth;
242 }
243
244private:
245
246 bool isCFrame()
247 {
248 return frame()->callerFrame != m_callFrame;
249 }
250
251 void advanceToParentFrame()
252 {
253 if (!isCFrame())
254 Base::advanceToParentFrame();
255 m_machineFrame = frame()->callerFrame;
256 }
257
258 void resetAtMachineFrame()
259 {
260 if (!isValidFramePointer(m_machineFrame)) {
261 // Guard against pausing the process at weird program points.
262 m_bailingOut = true;
263 if (sReportStats)
264 sNumFailedWalks++;
265 return;
266 }
267 Base::resetAtMachineFrame();
268 }
269
270 CallerFrameAndPC* frame()
271 {
272 return reinterpret_cast<CallerFrameAndPC*>(m_machineFrame);
273 }
274
275 void* m_machineFrame;
276};
277
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000278SamplingProfiler::SamplingProfiler(VM& vm, RefPtr<Stopwatch>&& stopwatch)
279 : m_vm(vm)
280 , m_stopwatch(WTFMove(stopwatch))
commit-queue@webkit.org18b95832016-04-26 16:47:33 +0000281 , m_timingInterval(std::chrono::microseconds(Options::sampleInterval()))
sbarati@apple.com207a6832016-04-06 03:55:11 +0000282 , m_threadIdentifier(0)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000283 , m_jscExecutionThread(nullptr)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000284 , m_isPaused(false)
sbarati@apple.com207a6832016-04-06 03:55:11 +0000285 , m_isShutDown(false)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000286{
287 if (sReportStats) {
288 sNumTotalWalks = 0;
289 sNumFailedWalks = 0;
290 }
291
292 m_currentFrames.grow(256);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000293}
294
295SamplingProfiler::~SamplingProfiler()
296{
297}
298
mark.lam@apple.coma2668e02017-03-09 21:05:41 +0000299void SamplingProfiler::createThreadIfNecessary(const AbstractLocker&)
sbarati@apple.com207a6832016-04-06 03:55:11 +0000300{
301 ASSERT(m_lock.isLocked());
302
303 if (m_threadIdentifier)
304 return;
305
306 RefPtr<SamplingProfiler> profiler = this;
307 m_threadIdentifier = createThread("jsc.sampling-profiler.thread", [profiler] {
308 profiler->timerLoop();
309 });
310}
311
312void SamplingProfiler::timerLoop()
313{
314 while (true) {
315 std::chrono::microseconds stackTraceProcessingTime = std::chrono::microseconds(0);
316 {
317 LockHolder locker(m_lock);
318 if (UNLIKELY(m_isShutDown))
319 return;
320
321 if (!m_isPaused && m_jscExecutionThread)
322 takeSample(locker, stackTraceProcessingTime);
323
324 m_lastTime = m_stopwatch->elapsedTime();
325 }
326
sbarati@apple.com51d80072016-06-14 02:29:26 +0000327 // Read section 6.2 of this paper for more elaboration of why we add a random
328 // fluctuation here. The main idea is to prevent our timer from being in sync
329 // with some system process such as a scheduled context switch.
330 // http://plv.colorado.edu/papers/mytkowicz-pldi10.pdf
331 double randomSignedNumber = (randomNumber() * 2.0) - 1.0; // A random number between [-1, 1).
332 std::chrono::microseconds randomFluctuation = std::chrono::microseconds(static_cast<uint64_t>(randomSignedNumber * static_cast<double>(m_timingInterval.count()) * 0.20l));
333 std::this_thread::sleep_for(m_timingInterval - std::min(m_timingInterval, stackTraceProcessingTime) + randomFluctuation);
sbarati@apple.com207a6832016-04-06 03:55:11 +0000334 }
335}
336
mark.lam@apple.coma2668e02017-03-09 21:05:41 +0000337void SamplingProfiler::takeSample(const AbstractLocker&, std::chrono::microseconds& stackTraceProcessingTime)
sbarati@apple.com207a6832016-04-06 03:55:11 +0000338{
339 ASSERT(m_lock.isLocked());
340 if (m_vm.entryScope) {
341 double nowTime = m_stopwatch->elapsedTime();
342
343 LockHolder machineThreadsLocker(m_vm.heap.machineThreads().getLock());
344 LockHolder codeBlockSetLocker(m_vm.heap.codeBlockSet().getLock());
345 LockHolder executableAllocatorLocker(m_vm.executableAllocator.getLock());
346
347 bool didSuspend = m_jscExecutionThread->suspend();
348 if (didSuspend) {
349 // While the JSC thread is suspended, we can't do things like malloc because the JSC thread
350 // may be holding the malloc lock.
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000351 void* machineFrame;
sbarati@apple.com207a6832016-04-06 03:55:11 +0000352 ExecState* callFrame;
353 void* machinePC;
354 bool topFrameIsLLInt = false;
355 void* llintPC;
356 {
357 MachineThreads::Thread::Registers registers;
358 m_jscExecutionThread->getRegisters(registers);
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000359 machineFrame = registers.framePointer();
360 callFrame = static_cast<ExecState*>(machineFrame);
sbarati@apple.com207a6832016-04-06 03:55:11 +0000361 machinePC = registers.instructionPointer();
362 llintPC = registers.llintPC();
363 m_jscExecutionThread->freeRegisters(registers);
364 }
365 // FIXME: Lets have a way of detecting when we're parsing code.
366 // https://bugs.webkit.org/show_bug.cgi?id=152761
367 if (m_vm.executableAllocator.isValidExecutableMemory(executableAllocatorLocker, machinePC)) {
368 if (m_vm.isExecutingInRegExpJIT) {
369 // FIXME: We're executing a regexp. Lets gather more intersting data.
370 // https://bugs.webkit.org/show_bug.cgi?id=152729
371 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.
372 }
373 } else if (LLInt::isLLIntPC(machinePC)) {
374 topFrameIsLLInt = true;
375 // We're okay to take a normal stack trace when the PC
376 // is in LLInt code.
377 } else {
378 // We resort to topCallFrame to see if we can get anything
379 // useful. We usually get here when we're executing C code.
380 callFrame = m_vm.topCallFrame;
381 }
382
383 size_t walkSize;
384 bool wasValidWalk;
385 bool didRunOutOfVectorSpace;
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000386 if (Options::sampleCCode()) {
387 CFrameWalker walker(m_vm, machineFrame, callFrame, codeBlockSetLocker, machineThreadsLocker);
388 walkSize = walker.walk(m_currentFrames, didRunOutOfVectorSpace);
389 wasValidWalk = walker.wasValidWalk();
390 } else {
391 FrameWalker walker(m_vm, callFrame, codeBlockSetLocker, machineThreadsLocker);
sbarati@apple.com207a6832016-04-06 03:55:11 +0000392 walkSize = walker.walk(m_currentFrames, didRunOutOfVectorSpace);
393 wasValidWalk = walker.wasValidWalk();
394 }
395
396 m_jscExecutionThread->resume();
397
398 auto startTime = std::chrono::steady_clock::now();
399 // We can now use data structures that malloc, and do other interesting things, again.
400
401 // FIXME: It'd be interesting to take data about the program's state when
402 // we fail to take a stack trace: https://bugs.webkit.org/show_bug.cgi?id=152758
403 if (wasValidWalk && walkSize) {
404 if (sReportStats)
405 sNumTotalStackTraces++;
406 Vector<UnprocessedStackFrame> stackTrace;
407 stackTrace.reserveInitialCapacity(walkSize);
408 for (size_t i = 0; i < walkSize; i++) {
409 UnprocessedStackFrame frame = m_currentFrames[i];
410 stackTrace.uncheckedAppend(frame);
411 }
412
413 m_unprocessedStackTraces.append(UnprocessedStackTrace { nowTime, machinePC, topFrameIsLLInt, llintPC, WTFMove(stackTrace) });
414
415 if (didRunOutOfVectorSpace)
416 m_currentFrames.grow(m_currentFrames.size() * 1.25);
417 }
418
419 auto endTime = std::chrono::steady_clock::now();
420 stackTraceProcessingTime = std::chrono::duration_cast<std::chrono::microseconds>(endTime - startTime);
421 }
422 }
423}
424
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000425static ALWAYS_INLINE unsigned tryGetBytecodeIndex(unsigned llintPC, CodeBlock* codeBlock, bool& isValid)
426{
ossy@webkit.orgc8cea7b2016-02-18 16:45:26 +0000427#if ENABLE(DFG_JIT)
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000428 RELEASE_ASSERT(!codeBlock->hasCodeOrigins());
ossy@webkit.orgc8cea7b2016-02-18 16:45:26 +0000429#endif
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000430
431#if USE(JSVALUE64)
432 unsigned bytecodeIndex = llintPC;
433 if (bytecodeIndex < codeBlock->instructionCount()) {
434 isValid = true;
435 return bytecodeIndex;
436 }
437 isValid = false;
438 return 0;
439#else
440 Instruction* instruction = bitwise_cast<Instruction*>(llintPC);
441 if (instruction >= codeBlock->instructions().begin() && instruction < codeBlock->instructions().begin() + codeBlock->instructionCount()) {
442 isValid = true;
443 unsigned bytecodeIndex = instruction - codeBlock->instructions().begin();
444 return bytecodeIndex;
445 }
446 isValid = false;
447 return 0;
448#endif
449}
450
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000451void SamplingProfiler::processUnverifiedStackTraces()
452{
453 // This function needs to be called from the JSC execution thread.
454 RELEASE_ASSERT(m_lock.isLocked());
455
456 TinyBloomFilter filter = m_vm.heap.objectSpace().blocks().filter();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000457
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000458 for (UnprocessedStackTrace& unprocessedStackTrace : m_unprocessedStackTraces) {
459 m_stackTraces.append(StackTrace());
460 StackTrace& stackTrace = m_stackTraces.last();
461 stackTrace.timestamp = unprocessedStackTrace.timestamp;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000462
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000463 auto populateCodeLocation = [] (CodeBlock* codeBlock, unsigned bytecodeIndex, StackFrame::CodeLocation& location) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000464 if (bytecodeIndex < codeBlock->instructionCount()) {
465 int divot;
466 int startOffset;
467 int endOffset;
468 codeBlock->expressionRangeForBytecodeOffset(bytecodeIndex, divot, startOffset, endOffset,
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000469 location.lineNumber, location.columnNumber);
470 location.bytecodeIndex = bytecodeIndex;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000471 }
sbarati@apple.com38496ee2016-04-21 00:55:03 +0000472 if (Options::collectSamplingProfilerDataForJSCShell()) {
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000473 location.codeBlockHash = codeBlock->hash();
474 location.jitType = codeBlock->jitType();
sbarati@apple.com38496ee2016-04-21 00:55:03 +0000475 }
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000476 };
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000477
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000478 auto appendCodeBlock = [&] (CodeBlock* codeBlock, unsigned bytecodeIndex) {
479 stackTrace.frames.append(StackFrame(codeBlock->ownerExecutable()));
480 m_liveCellPointers.add(codeBlock->ownerExecutable());
481 populateCodeLocation(codeBlock, bytecodeIndex, stackTrace.frames.last().semanticLocation);
482 };
483
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000484 auto appendEmptyFrame = [&] {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000485 stackTrace.frames.append(StackFrame());
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000486 };
487
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000488 auto storeCalleeIntoLastFrame = [&] (EncodedJSValue encodedCallee) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000489 // Set the callee if it's a valid GC object.
490 JSValue callee = JSValue::decode(encodedCallee);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000491 StackFrame& stackFrame = stackTrace.frames.last();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000492 bool alreadyHasExecutable = !!stackFrame.executable;
fpizlo@apple.combc16ddb2016-09-06 01:02:22 +0000493 if (!HeapUtil::isValueGCObject(m_vm.heap, filter, callee)) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000494 if (!alreadyHasExecutable)
495 stackFrame.frameType = FrameType::Unknown;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000496 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000497 }
498
499 JSCell* calleeCell = callee.asCell();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000500 auto setFallbackFrameType = [&] {
501 ASSERT(!alreadyHasExecutable);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000502 FrameType result = FrameType::Unknown;
503 CallData callData;
504 CallType callType;
505 callType = getCallData(calleeCell, callData);
utatane.tea@gmail.comf76f1b42016-03-05 17:01:04 +0000506 if (callType == CallType::Host)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000507 result = FrameType::Host;
508
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000509 stackFrame.frameType = result;
510 };
511
512 auto addCallee = [&] (JSObject* callee) {
513 stackFrame.callee = callee;
514 m_liveCellPointers.add(callee);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000515 };
516
517 if (calleeCell->type() != JSFunctionType) {
keith_miller@apple.com45da7602017-01-27 01:47:52 +0000518 if (JSObject* object = jsDynamicCast<JSObject*>(*calleeCell->vm(), calleeCell))
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000519 addCallee(object);
520
521 if (!alreadyHasExecutable)
522 setFallbackFrameType();
523
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000524 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000525 }
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000526
527 addCallee(jsCast<JSFunction*>(calleeCell));
528
529 if (alreadyHasExecutable)
530 return;
531
532 ExecutableBase* executable = jsCast<JSFunction*>(calleeCell)->executable();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000533 if (!executable) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000534 setFallbackFrameType();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000535 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000536 }
537
fpizlo@apple.combc16ddb2016-09-06 01:02:22 +0000538 RELEASE_ASSERT(HeapUtil::isPointerGCObjectJSCell(m_vm.heap, filter, executable));
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000539 stackFrame.frameType = FrameType::Executable;
540 stackFrame.executable = executable;
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000541 m_liveCellPointers.add(executable);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000542 };
543
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000544 auto appendCodeOrigin = [&] (CodeBlock* machineCodeBlock, CodeOrigin origin) {
545 size_t startIndex = stackTrace.frames.size(); // We want to change stack traces that we're about to append.
546
547 CodeOrigin machineOrigin;
548 origin.walkUpInlineStack([&] (const CodeOrigin& codeOrigin) {
549 machineOrigin = codeOrigin;
550 appendCodeBlock(codeOrigin.inlineCallFrame ? codeOrigin.inlineCallFrame->baselineCodeBlock.get() : machineCodeBlock, codeOrigin.bytecodeIndex);
551 });
552
553 if (Options::collectSamplingProfilerDataForJSCShell()) {
554 RELEASE_ASSERT(machineOrigin.isSet());
555 RELEASE_ASSERT(!machineOrigin.inlineCallFrame);
556
557 StackFrame::CodeLocation machineLocation = stackTrace.frames.last().semanticLocation;
558
559 // We want to tell each inlined frame about the machine frame
560 // they were inlined into. Currently, we only use this for dumping
561 // output on the command line, but we could extend it to the web
562 // inspector in the future if we find a need for it there.
563 RELEASE_ASSERT(stackTrace.frames.size());
564 for (size_t i = startIndex; i < stackTrace.frames.size() - 1; i++)
565 stackTrace.frames[i].machineLocation = std::make_pair(machineLocation, Strong<CodeBlock>(m_vm, machineCodeBlock));
566 }
567 };
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000568
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000569 // Prepend the top-most inlined frame if needed and gather
570 // location information about where the top frame is executing.
571 size_t startIndex = 0;
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000572 if (unprocessedStackTrace.frames.size() && !!unprocessedStackTrace.frames[0].verifiedCodeBlock) {
573 CodeBlock* topCodeBlock = unprocessedStackTrace.frames[0].verifiedCodeBlock;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000574 if (unprocessedStackTrace.topFrameIsLLInt) {
575 // We reuse LLInt CodeBlocks for the baseline JIT, so we need to check for both jit types.
576 // This might also be false for various reasons (known and unknown), even though
577 // it's super unlikely. One reason that this can be false is when we throw from a DFG frame,
578 // and we end up having to unwind past a VMEntryFrame, we will end up executing
579 // inside the LLInt's handleUncaughtException. So we just protect against this
580 // by ignoring it.
581 unsigned bytecodeIndex = 0;
582 if (topCodeBlock->jitType() == JITCode::InterpreterThunk || topCodeBlock->jitType() == JITCode::BaselineJIT) {
583 bool isValidPC;
584 unsigned bits;
585#if USE(JSVALUE64)
586 bits = static_cast<unsigned>(bitwise_cast<uintptr_t>(unprocessedStackTrace.llintPC));
587#else
588 bits = bitwise_cast<unsigned>(unprocessedStackTrace.llintPC);
589#endif
590 bytecodeIndex = tryGetBytecodeIndex(bits, topCodeBlock, isValidPC);
591
592 UNUSED_PARAM(isValidPC); // FIXME: do something with this info for the web inspector: https://bugs.webkit.org/show_bug.cgi?id=153455
593
594 appendCodeBlock(topCodeBlock, bytecodeIndex);
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000595 storeCalleeIntoLastFrame(unprocessedStackTrace.frames[0].unverifiedCallee);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000596 startIndex = 1;
597 }
utatane.tea@gmail.com43926962016-11-27 06:08:16 +0000598 } else if (std::optional<CodeOrigin> codeOrigin = topCodeBlock->findPC(unprocessedStackTrace.topPC)) {
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000599 appendCodeOrigin(topCodeBlock, *codeOrigin);
600 storeCalleeIntoLastFrame(unprocessedStackTrace.frames[0].unverifiedCallee);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000601 startIndex = 1;
602 }
603 }
604
605 for (size_t i = startIndex; i < unprocessedStackTrace.frames.size(); i++) {
606 UnprocessedStackFrame& unprocessedStackFrame = unprocessedStackTrace.frames[i];
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000607 if (CodeBlock* codeBlock = unprocessedStackFrame.verifiedCodeBlock) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000608 CallSiteIndex callSiteIndex = unprocessedStackFrame.callSiteIndex;
609
610 auto appendCodeBlockNoInlining = [&] {
611 bool isValidPC;
612 appendCodeBlock(codeBlock, tryGetBytecodeIndex(callSiteIndex.bits(), codeBlock, isValidPC));
613 };
614
615#if ENABLE(DFG_JIT)
616 if (codeBlock->hasCodeOrigins()) {
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000617 if (codeBlock->canGetCodeOrigin(callSiteIndex))
618 appendCodeOrigin(codeBlock, codeBlock->codeOrigin(callSiteIndex));
619 else
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000620 appendCodeBlock(codeBlock, std::numeric_limits<unsigned>::max());
621 } else
622 appendCodeBlockNoInlining();
623#else
624 appendCodeBlockNoInlining();
625#endif
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000626 } else if (unprocessedStackFrame.cCodePC) {
627 appendEmptyFrame();
628 stackTrace.frames.last().cCodePC = unprocessedStackFrame.cCodePC;
629 stackTrace.frames.last().frameType = FrameType::C;
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000630 } else
631 appendEmptyFrame();
632
633 // Note that this is okay to do if we walked the inline stack because
634 // the machine frame will be at the top of the processed stack trace.
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000635 if (!unprocessedStackFrame.cCodePC)
636 storeCalleeIntoLastFrame(unprocessedStackFrame.unverifiedCallee);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000637 }
638 }
639
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000640 m_unprocessedStackTraces.clear();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000641}
642
643void SamplingProfiler::visit(SlotVisitor& slotVisitor)
644{
645 RELEASE_ASSERT(m_lock.isLocked());
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000646 for (JSCell* cell : m_liveCellPointers)
fpizlo@apple.comf7240e02016-12-16 02:16:19 +0000647 slotVisitor.appendUnbarriered(cell);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000648}
649
650void SamplingProfiler::shutdown()
651{
sbarati@apple.com207a6832016-04-06 03:55:11 +0000652 LockHolder locker(m_lock);
653 m_isShutDown = true;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000654}
655
656void SamplingProfiler::start()
657{
658 LockHolder locker(m_lock);
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000659 start(locker);
660}
661
mark.lam@apple.coma2668e02017-03-09 21:05:41 +0000662void SamplingProfiler::start(const AbstractLocker& locker)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000663{
664 ASSERT(m_lock.isLocked());
sbarati@apple.com207a6832016-04-06 03:55:11 +0000665 m_isPaused = false;
666 createThreadIfNecessary(locker);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000667}
668
mark.lam@apple.coma2668e02017-03-09 21:05:41 +0000669void SamplingProfiler::pause(const AbstractLocker&)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000670{
671 ASSERT(m_lock.isLocked());
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000672 m_isPaused = true;
673 reportStats();
674}
675
mark.lam@apple.coma2668e02017-03-09 21:05:41 +0000676void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread(const AbstractLocker&)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000677{
678 ASSERT(m_lock.isLocked());
679 m_jscExecutionThread = m_vm.heap.machineThreads().machineThreadForCurrentThread();
680}
681
682void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread()
683{
684 LockHolder locker(m_lock);
685 noticeCurrentThreadAsJSCExecutionThread(locker);
686}
687
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000688void SamplingProfiler::noticeJSLockAcquisition()
689{
690 LockHolder locker(m_lock);
691 noticeCurrentThreadAsJSCExecutionThread(locker);
692}
693
694void SamplingProfiler::noticeVMEntry()
695{
696 LockHolder locker(m_lock);
697 ASSERT(m_vm.entryScope);
698 noticeCurrentThreadAsJSCExecutionThread(locker);
699 m_lastTime = m_stopwatch->elapsedTime();
sbarati@apple.com207a6832016-04-06 03:55:11 +0000700 createThreadIfNecessary(locker);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000701}
702
mark.lam@apple.coma2668e02017-03-09 21:05:41 +0000703void SamplingProfiler::clearData(const AbstractLocker&)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000704{
705 ASSERT(m_lock.isLocked());
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000706 m_stackTraces.clear();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000707 m_liveCellPointers.clear();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000708 m_unprocessedStackTraces.clear();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000709}
710
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000711String SamplingProfiler::StackFrame::nameFromCallee(VM& vm)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000712{
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000713 if (!callee)
714 return String();
715
mark.lam@apple.com09f92b82016-09-27 20:32:13 +0000716 auto scope = DECLARE_CATCH_SCOPE(vm);
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000717 ExecState* exec = callee->globalObject()->globalExec();
718 auto getPropertyIfPureOperation = [&] (const Identifier& ident) -> String {
sbarati@apple.comea974132016-02-17 22:11:39 +0000719 PropertySlot slot(callee, PropertySlot::InternalMethodType::VMInquiry);
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000720 PropertyName propertyName(ident);
mark.lam@apple.com09f92b82016-09-27 20:32:13 +0000721 bool hasProperty = callee->getPropertySlot(exec, propertyName, slot);
722 ASSERT_UNUSED(scope, !scope.exception());
723 if (hasProperty) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000724 if (slot.isValue()) {
725 JSValue nameValue = slot.getValue(exec, propertyName);
726 if (isJSString(nameValue))
727 return asString(nameValue)->tryGetValue();
728 }
729 }
730 return String();
731 };
732
733 String name = getPropertyIfPureOperation(vm.propertyNames->displayName);
734 if (!name.isEmpty())
735 return name;
736
737 return getPropertyIfPureOperation(vm.propertyNames->name);
738}
739
740String SamplingProfiler::StackFrame::displayName(VM& vm)
741{
742 {
743 String name = nameFromCallee(vm);
744 if (!name.isEmpty())
745 return name;
746 }
747
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000748 if (frameType == FrameType::Unknown || frameType == FrameType::C) {
749#if OS(DARWIN)
750 if (frameType == FrameType::C) {
751 const char* mangledName = nullptr;
752 const char* cxaDemangled = nullptr;
753 Dl_info info;
754 if (dladdr(cCodePC, &info) && info.dli_sname)
755 mangledName = info.dli_sname;
756 if (mangledName) {
757 cxaDemangled = abi::__cxa_demangle(mangledName, 0, 0, 0);
758 return String(cxaDemangled ? cxaDemangled : mangledName);
759 }
760 WTF::dataLog("couldn't get a name");
761 }
762#endif
keith_miller@apple.comac5c1c22017-02-02 01:49:20 +0000763 return ASCIILiteral("(unknown)");
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000764 }
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000765 if (frameType == FrameType::Host)
766 return ASCIILiteral("(host)");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000767
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000768 if (executable->isHostFunction())
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000769 return static_cast<NativeExecutable*>(executable)->name();
770
771 if (executable->isFunctionExecutable())
772 return static_cast<FunctionExecutable*>(executable)->inferredName().string();
773 if (executable->isProgramExecutable() || executable->isEvalExecutable())
774 return ASCIILiteral("(program)");
775 if (executable->isModuleProgramExecutable())
776 return ASCIILiteral("(module)");
777
778 RELEASE_ASSERT_NOT_REACHED();
779 return String();
780}
781
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000782String SamplingProfiler::StackFrame::displayNameForJSONTests(VM& vm)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000783{
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000784 {
785 String name = nameFromCallee(vm);
786 if (!name.isEmpty())
787 return name;
788 }
789
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000790 if (frameType == FrameType::Unknown || frameType == FrameType::C)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000791 return ASCIILiteral("(unknown)");
792 if (frameType == FrameType::Host)
793 return ASCIILiteral("(host)");
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000794
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000795 if (executable->isHostFunction())
796 return static_cast<NativeExecutable*>(executable)->name();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000797
798 if (executable->isFunctionExecutable()) {
799 String result = static_cast<FunctionExecutable*>(executable)->inferredName().string();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000800 if (result.isEmpty())
801 return ASCIILiteral("(anonymous function)");
802 return result;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000803 }
804 if (executable->isEvalExecutable())
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000805 return ASCIILiteral("(eval)");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000806 if (executable->isProgramExecutable())
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000807 return ASCIILiteral("(program)");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000808 if (executable->isModuleProgramExecutable())
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000809 return ASCIILiteral("(module)");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000810
811 RELEASE_ASSERT_NOT_REACHED();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000812 return String();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000813}
814
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000815int SamplingProfiler::StackFrame::functionStartLine()
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000816{
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000817 if (frameType == FrameType::Unknown || frameType == FrameType::Host || frameType == FrameType::C)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000818 return -1;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000819
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000820 if (executable->isHostFunction())
821 return -1;
822 return static_cast<ScriptExecutable*>(executable)->firstLine();
823}
824
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000825unsigned SamplingProfiler::StackFrame::functionStartColumn()
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000826{
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000827 if (frameType == FrameType::Unknown || frameType == FrameType::Host || frameType == FrameType::C)
peavo@outlook.coma83d48a2016-05-02 21:20:15 +0000828 return std::numeric_limits<unsigned>::max();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000829
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000830 if (executable->isHostFunction())
peavo@outlook.coma83d48a2016-05-02 21:20:15 +0000831 return std::numeric_limits<unsigned>::max();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000832
833 return static_cast<ScriptExecutable*>(executable)->startColumn();
834}
835
836intptr_t SamplingProfiler::StackFrame::sourceID()
837{
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000838 if (frameType == FrameType::Unknown || frameType == FrameType::Host || frameType == FrameType::C)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000839 return -1;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000840
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000841 if (executable->isHostFunction())
842 return -1;
843
844 return static_cast<ScriptExecutable*>(executable)->sourceID();
845}
846
847String SamplingProfiler::StackFrame::url()
848{
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000849 if (frameType == FrameType::Unknown || frameType == FrameType::Host || frameType == FrameType::C)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000850 return emptyString();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000851
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000852 if (executable->isHostFunction())
853 return emptyString();
854
855 String url = static_cast<ScriptExecutable*>(executable)->sourceURL();
856 if (url.isEmpty())
857 return static_cast<ScriptExecutable*>(executable)->source().provider()->sourceURL(); // Fall back to sourceURL directive.
858 return url;
859}
860
mark.lam@apple.coma2668e02017-03-09 21:05:41 +0000861Vector<SamplingProfiler::StackTrace> SamplingProfiler::releaseStackTraces(const AbstractLocker& locker)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000862{
863 ASSERT(m_lock.isLocked());
864 {
865 HeapIterationScope heapIterationScope(m_vm.heap);
866 processUnverifiedStackTraces();
867 }
868
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000869 Vector<StackTrace> result(WTFMove(m_stackTraces));
870 clearData(locker);
871 return result;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000872}
873
874String SamplingProfiler::stackTracesAsJSON()
875{
fpizlo@apple.com0ef43952016-12-08 22:14:50 +0000876 DeferGC deferGC(m_vm.heap);
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000877 LockHolder locker(m_lock);
878
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000879 {
880 HeapIterationScope heapIterationScope(m_vm.heap);
881 processUnverifiedStackTraces();
882 }
883
884 StringBuilder json;
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000885 json.append('[');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000886
887 bool loopedOnce = false;
888 auto comma = [&] {
889 if (loopedOnce)
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000890 json.append(',');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000891 };
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000892 for (StackTrace& stackTrace : m_stackTraces) {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000893 comma();
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000894 json.append('[');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000895 loopedOnce = false;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000896 for (StackFrame& stackFrame : stackTrace.frames) {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000897 comma();
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000898 json.append('"');
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000899 json.append(stackFrame.displayNameForJSONTests(m_vm));
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000900 json.append('"');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000901 loopedOnce = true;
902 }
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000903 json.append(']');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000904 loopedOnce = true;
905 }
906
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000907 json.append(']');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000908
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000909 clearData(locker);
910
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000911 return json.toString();
912}
913
keith_miller@apple.com3517aa42016-05-24 23:03:09 +0000914void SamplingProfiler::registerForReportAtExit()
915{
916 static StaticLock registrationLock;
917 static HashSet<RefPtr<SamplingProfiler>>* profilesToReport;
918
919 LockHolder holder(registrationLock);
920
921 if (!profilesToReport) {
922 profilesToReport = new HashSet<RefPtr<SamplingProfiler>>();
923 atexit([]() {
924 for (auto profile : *profilesToReport)
925 profile->reportDataToOptionFile();
926 });
927 }
928
929 profilesToReport->add(adoptRef(this));
930 m_needsReportAtExit = true;
931}
932
933void SamplingProfiler::reportDataToOptionFile()
934{
935 if (m_needsReportAtExit) {
936 m_needsReportAtExit = false;
937 const char* path = Options::samplingProfilerPath();
938 StringPrintStream pathOut;
939 pathOut.print(path, "/");
940 pathOut.print("JSCSampilingProfile-", reinterpret_cast<uintptr_t>(this), ".txt");
941 auto out = FilePrintStream::open(pathOut.toCString().data(), "w");
942 reportTopFunctions(*out);
943 reportTopBytecodes(*out);
944 }
945}
946
sbarati@apple.com10ec78a2016-04-20 02:24:53 +0000947void SamplingProfiler::reportTopFunctions()
948{
keith_miller@apple.com3517aa42016-05-24 23:03:09 +0000949 reportTopFunctions(WTF::dataFile());
950}
951
952void SamplingProfiler::reportTopFunctions(PrintStream& out)
953{
sbarati@apple.com10ec78a2016-04-20 02:24:53 +0000954 LockHolder locker(m_lock);
955
956 {
957 HeapIterationScope heapIterationScope(m_vm.heap);
958 processUnverifiedStackTraces();
959 }
960
961
962 HashMap<String, size_t> functionCounts;
963 for (StackTrace& stackTrace : m_stackTraces) {
964 if (!stackTrace.frames.size())
965 continue;
966
967 StackFrame& frame = stackTrace.frames.first();
968 String frameDescription = makeString(frame.displayName(m_vm), ":", String::number(frame.sourceID()));
969 functionCounts.add(frameDescription, 0).iterator->value++;
970 }
971
972 auto takeMax = [&] () -> std::pair<String, size_t> {
973 String maxFrameDescription;
974 size_t maxFrameCount = 0;
975 for (auto entry : functionCounts) {
976 if (entry.value > maxFrameCount) {
977 maxFrameCount = entry.value;
978 maxFrameDescription = entry.key;
979 }
980 }
981 if (!maxFrameDescription.isEmpty())
982 functionCounts.remove(maxFrameDescription);
983 return std::make_pair(maxFrameDescription, maxFrameCount);
984 };
985
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000986 if (Options::samplingProfilerTopFunctionsCount()) {
987 out.print("\n\nSampling rate: ", m_timingInterval.count(), " microseconds\n");
988 out.print("Top functions as <numSamples 'functionName:sourceID'>\n");
989 for (size_t i = 0; i < Options::samplingProfilerTopFunctionsCount(); i++) {
990 auto pair = takeMax();
991 if (pair.first.isEmpty())
992 break;
993 out.printf("%6zu ", pair.second);
994 out.print(" '", pair.first, "'\n");
995 }
sbarati@apple.com10ec78a2016-04-20 02:24:53 +0000996 }
997}
998
999void SamplingProfiler::reportTopBytecodes()
1000{
keith_miller@apple.com3517aa42016-05-24 23:03:09 +00001001 reportTopBytecodes(WTF::dataFile());
1002}
1003
1004void SamplingProfiler::reportTopBytecodes(PrintStream& out)
1005{
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001006 LockHolder locker(m_lock);
1007
1008 {
1009 HeapIterationScope heapIterationScope(m_vm.heap);
1010 processUnverifiedStackTraces();
1011 }
1012
1013 HashMap<String, size_t> bytecodeCounts;
1014 for (StackTrace& stackTrace : m_stackTraces) {
1015 if (!stackTrace.frames.size())
1016 continue;
1017
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001018 auto descriptionForLocation = [&] (StackFrame::CodeLocation location) -> String {
1019 String bytecodeIndex;
1020 String codeBlockHash;
1021 if (location.hasBytecodeIndex())
1022 bytecodeIndex = String::number(location.bytecodeIndex);
1023 else
1024 bytecodeIndex = "<nil>";
1025
1026 if (location.hasCodeBlockHash()) {
1027 StringPrintStream stream;
1028 location.codeBlockHash.dump(stream);
1029 codeBlockHash = stream.toString();
1030 } else
1031 codeBlockHash = "<nil>";
1032
1033 return makeString("#", codeBlockHash, ":", JITCode::typeName(location.jitType), ":", bytecodeIndex);
1034 };
1035
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001036 StackFrame& frame = stackTrace.frames.first();
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001037 String frameDescription = makeString(frame.displayName(m_vm), descriptionForLocation(frame.semanticLocation));
1038 if (std::optional<std::pair<StackFrame::CodeLocation, Strong<CodeBlock>>> machineLocation = frame.machineLocation) {
1039 frameDescription = makeString(frameDescription, " <-- ",
1040 machineLocation->second->inferredName().data(), descriptionForLocation(machineLocation->first));
1041 }
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001042 bytecodeCounts.add(frameDescription, 0).iterator->value++;
1043 }
1044
1045 auto takeMax = [&] () -> std::pair<String, size_t> {
1046 String maxFrameDescription;
1047 size_t maxFrameCount = 0;
1048 for (auto entry : bytecodeCounts) {
1049 if (entry.value > maxFrameCount) {
1050 maxFrameCount = entry.value;
1051 maxFrameDescription = entry.key;
1052 }
1053 }
1054 if (!maxFrameDescription.isEmpty())
1055 bytecodeCounts.remove(maxFrameDescription);
1056 return std::make_pair(maxFrameDescription, maxFrameCount);
1057 };
1058
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001059 if (Options::samplingProfilerTopBytecodesCount()) {
1060 out.print("\n\nSampling rate: ", m_timingInterval.count(), " microseconds\n");
1061 out.print("Hottest bytecodes as <numSamples 'functionName#hash:JITType:bytecodeIndex'>\n");
1062 for (size_t i = 0; i < Options::samplingProfilerTopBytecodesCount(); i++) {
1063 auto pair = takeMax();
1064 if (pair.first.isEmpty())
1065 break;
1066 out.printf("%6zu ", pair.second);
1067 out.print(" '", pair.first, "'\n");
1068 }
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001069 }
1070}
1071
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001072} // namespace JSC
1073
1074namespace WTF {
1075
1076using namespace JSC;
1077
1078void printInternal(PrintStream& out, SamplingProfiler::FrameType frameType)
1079{
1080 switch (frameType) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +00001081 case SamplingProfiler::FrameType::Executable:
1082 out.print("Executable");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001083 break;
1084 case SamplingProfiler::FrameType::Host:
1085 out.print("Host");
1086 break;
keith_miller@apple.comb4a86442017-02-02 01:23:37 +00001087 case SamplingProfiler::FrameType::C:
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001088 case SamplingProfiler::FrameType::Unknown:
1089 out.print("Unknown");
1090 break;
1091 }
1092}
1093
1094} // namespace WTF
1095
1096#endif // ENABLE(SAMPLING_PROFILER)