0751f9b4fc7ecab7f86617412d4300c3be5ab6cc
[WebKit-https.git] / Source / JavaScriptCore / runtime / SamplingProfiler.cpp
1 /*
2  * Copyright (C) 2016-2019 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 "CatchScope.h"
33 #include "CodeBlock.h"
34 #include "CodeBlockSet.h"
35 #include "HeapIterationScope.h"
36 #include "HeapUtil.h"
37 #include "InlineCallFrame.h"
38 #include "Interpreter.h"
39 #include "JSCInlines.h"
40 #include "JSFunction.h"
41 #include "LLIntPCRanges.h"
42 #include "MachineContext.h"
43 #include "MarkedBlock.h"
44 #include "MarkedBlockSet.h"
45 #include "MarkedSpaceInlines.h"
46 #include "NativeExecutable.h"
47 #include "PCToCodeOriginMap.h"
48 #include "SlotVisitor.h"
49 #include "StrongInlines.h"
50 #include "VM.h"
51 #include "WasmCallee.h"
52 #include "WasmCalleeRegistry.h"
53 #include <thread>
54 #include <wtf/FilePrintStream.h>
55 #include <wtf/HashSet.h>
56 #include <wtf/RefPtr.h>
57 #include <wtf/StackTrace.h>
58 #include <wtf/text/StringBuilder.h>
59 #include <wtf/text/StringConcatenateNumbers.h>
60
61 namespace JSC {
62
63 static double sNumTotalStackTraces = 0;
64 static double sNumTotalWalks = 0;
65 static double sNumFailedWalks = 0;
66 static const uint32_t sNumWalkReportingFrequency = 50;
67 static const double sWalkErrorPercentage = .05;
68 static constexpr bool sReportStatsOnlyWhenTheyreAboveThreshold = false;
69 static constexpr bool sReportStats = false;
70
71 using FrameType = SamplingProfiler::FrameType;
72 using UnprocessedStackFrame = SamplingProfiler::UnprocessedStackFrame;
73
74 ALWAYS_INLINE static void reportStats()
75 {
76     if (sReportStats && sNumTotalWalks && static_cast<uint64_t>(sNumTotalWalks) % sNumWalkReportingFrequency == 0) {
77         if (!sReportStatsOnlyWhenTheyreAboveThreshold || (sNumFailedWalks / sNumTotalWalks > sWalkErrorPercentage)) {
78             dataLogF("Num total walks: %llu. Failed walks percent: %lf\n",
79                 static_cast<unsigned long long>(sNumTotalWalks), sNumFailedWalks / sNumTotalWalks);
80         }
81     }
82 }
83
84 class FrameWalker {
85 public:
86     FrameWalker(VM& vm, CallFrame* callFrame, const AbstractLocker& codeBlockSetLocker, const AbstractLocker& machineThreadsLocker, const AbstractLocker& wasmCalleeLocker)
87         : m_vm(vm)
88         , m_callFrame(callFrame)
89         , m_entryFrame(vm.topEntryFrame)
90         , m_codeBlockSetLocker(codeBlockSetLocker)
91         , m_machineThreadsLocker(machineThreadsLocker)
92         , m_wasmCalleeLocker(wasmCalleeLocker)
93     {
94     }
95
96     SUPPRESS_ASAN
97     size_t walk(Vector<UnprocessedStackFrame>& stackTrace, bool& didRunOutOfSpace)
98     {
99         if (sReportStats)
100             sNumTotalWalks++;
101         resetAtMachineFrame();
102         size_t maxStackTraceSize = stackTrace.size();
103         while (!isAtTop() && !m_bailingOut && m_depth < maxStackTraceSize) {
104             recordJITFrame(stackTrace);
105             advanceToParentFrame();
106             resetAtMachineFrame();
107         }
108         didRunOutOfSpace = m_depth >= maxStackTraceSize && !isAtTop();
109         reportStats();
110         return m_depth;
111     }
112
113     bool wasValidWalk() const
114     {
115         return !m_bailingOut;
116     }
117
118 protected:
119
120     SUPPRESS_ASAN
121     void recordJITFrame(Vector<UnprocessedStackFrame>& stackTrace)
122     {
123         CallSiteIndex callSiteIndex;
124         CalleeBits unsafeCallee = m_callFrame->unsafeCallee();
125         CodeBlock* codeBlock = m_callFrame->unsafeCodeBlock();
126         if (unsafeCallee.isWasm())
127             codeBlock = nullptr;
128         if (codeBlock) {
129             ASSERT(isValidCodeBlock(codeBlock));
130             callSiteIndex = m_callFrame->unsafeCallSiteIndex();
131         }
132         stackTrace[m_depth] = UnprocessedStackFrame(codeBlock, unsafeCallee, callSiteIndex);
133 #if ENABLE(WEBASSEMBLY)
134         if (unsafeCallee.isWasm()) {
135             auto* wasmCallee = unsafeCallee.asWasmCallee();
136             if (Wasm::CalleeRegistry::singleton().isValidCallee(m_wasmCalleeLocker, wasmCallee)) {
137                 // At this point, Wasm::Callee would be dying (ref count is 0), but its fields are still live.
138                 // And we can safely copy Wasm::IndexOrName even when any lock is held by suspended threads.
139                 stackTrace[m_depth].wasmIndexOrName = wasmCallee->indexOrName();
140                 stackTrace[m_depth].wasmCompilationMode = wasmCallee->compilationMode();
141             }
142         }
143 #endif
144         m_depth++;
145     }
146
147     SUPPRESS_ASAN
148     void advanceToParentFrame()
149     {
150         m_callFrame = m_callFrame->unsafeCallerFrame(m_entryFrame);
151     }
152
153     bool isAtTop() const
154     {
155         return !m_callFrame;
156     }
157
158     SUPPRESS_ASAN
159     void resetAtMachineFrame()
160     {
161         if (isAtTop())
162             return;
163
164         if (!isValidFramePointer(m_callFrame)) {
165             // Guard against pausing the process at weird program points.
166             m_bailingOut = true;
167             if (sReportStats)
168                 sNumFailedWalks++;
169             return;
170         }
171
172         CodeBlock* codeBlock = m_callFrame->unsafeCodeBlock();
173         if (!codeBlock || m_callFrame->unsafeCallee().isWasm())
174             return;
175
176         if (!isValidCodeBlock(codeBlock)) {
177             m_bailingOut = true;
178             if (sReportStats)
179                 sNumFailedWalks++;
180             return;
181         }
182     }
183
184     bool isValidFramePointer(void* callFrame)
185     {
186         uint8_t* fpCast = bitwise_cast<uint8_t*>(callFrame);
187         for (auto& thread : m_vm.heap.machineThreads().threads(m_machineThreadsLocker)) {
188             uint8_t* stackBase = static_cast<uint8_t*>(thread->stack().origin());
189             uint8_t* stackLimit = static_cast<uint8_t*>(thread->stack().end());
190             RELEASE_ASSERT(stackBase);
191             RELEASE_ASSERT(stackLimit);
192             RELEASE_ASSERT(stackLimit <= stackBase);
193             if (fpCast < stackBase && fpCast >= stackLimit)
194                 return true;
195         }
196         return false;
197     }
198
199     bool isValidCodeBlock(CodeBlock* codeBlock)
200     {
201         if (!codeBlock)
202             return false;
203         bool result = m_vm.heap.codeBlockSet().contains(m_codeBlockSetLocker, codeBlock);
204         return result;
205     }
206
207     VM& m_vm;
208     CallFrame* m_callFrame;
209     EntryFrame* m_entryFrame;
210     const AbstractLocker& m_codeBlockSetLocker;
211     const AbstractLocker& m_machineThreadsLocker;
212     const AbstractLocker& m_wasmCalleeLocker;
213     bool m_bailingOut { false };
214     size_t m_depth { 0 };
215 };
216
217 class CFrameWalker : public FrameWalker {
218 public:
219     typedef FrameWalker Base;
220
221     CFrameWalker(VM& vm, void* machineFrame, CallFrame* callFrame, const AbstractLocker& codeBlockSetLocker, const AbstractLocker& machineThreadsLocker, const AbstractLocker& wasmCalleeLocker)
222         : Base(vm, callFrame, codeBlockSetLocker, machineThreadsLocker, wasmCalleeLocker)
223         , m_machineFrame(machineFrame)
224     {
225     }
226
227     size_t walk(Vector<UnprocessedStackFrame>& stackTrace, bool& didRunOutOfSpace)
228     {
229         if (sReportStats)
230             sNumTotalWalks++;
231         resetAtMachineFrame();
232         size_t maxStackTraceSize = stackTrace.size();
233         // The way the C walker decides if a frame it is about to trace is C or JS is by
234         // ensuring m_callFrame points to some frame above the machineFrame.
235         if (!isAtTop() && !m_bailingOut && m_machineFrame == m_callFrame) {
236             recordJITFrame(stackTrace);
237             Base::advanceToParentFrame();
238             resetAtMachineFrame();
239         }
240
241         while (!isAtTop() && !m_bailingOut && m_depth < maxStackTraceSize) {
242             if (m_machineFrame >= m_callFrame) {
243                 // If we get to this state we probably have an invalid trace.
244                 m_bailingOut = true;
245                 break;
246             }
247
248             if (isCFrame()) {
249                 RELEASE_ASSERT(!LLInt::isLLIntPC(frame()->callerFrame));
250                 stackTrace[m_depth] = UnprocessedStackFrame(frame()->returnPC);
251                 m_depth++;
252             } else
253                 recordJITFrame(stackTrace);
254             advanceToParentFrame();
255             resetAtMachineFrame();
256         }
257         didRunOutOfSpace = m_depth >= maxStackTraceSize && !isAtTop();
258         reportStats();
259         return m_depth;
260     }
261
262 private:
263
264     bool isCFrame()
265     {
266         return frame()->callerFrame != m_callFrame;
267     }
268
269     void advanceToParentFrame()
270     {
271         if (!isCFrame())
272             Base::advanceToParentFrame();
273         m_machineFrame = frame()->callerFrame;
274     }
275
276     void resetAtMachineFrame()
277     {
278         if (!isValidFramePointer(m_machineFrame)) {
279             // Guard against pausing the process at weird program points.
280             m_bailingOut = true;
281             if (sReportStats)
282                 sNumFailedWalks++;
283             return;
284         }
285         Base::resetAtMachineFrame();
286     }
287
288     CallerFrameAndPC* frame()
289     {
290         return reinterpret_cast<CallerFrameAndPC*>(m_machineFrame);
291     }
292
293     void* m_machineFrame;
294 };
295
296 SamplingProfiler::SamplingProfiler(VM& vm, RefPtr<Stopwatch>&& stopwatch)
297     : m_isPaused(false)
298     , m_isShutDown(false)
299     , m_vm(vm)
300     , m_weakRandom()
301     , m_stopwatch(WTFMove(stopwatch))
302     , m_timingInterval(Seconds::fromMicroseconds(Options::sampleInterval()))
303 {
304     if (sReportStats) {
305         sNumTotalWalks = 0;
306         sNumFailedWalks = 0;
307     }
308
309     m_currentFrames.grow(256);
310     vm.heap.objectSpace().enableLargeAllocationTracking();
311 }
312
313 SamplingProfiler::~SamplingProfiler()
314 {
315 }
316
317 void SamplingProfiler::createThreadIfNecessary(const AbstractLocker&)
318 {
319     ASSERT(m_lock.isLocked());
320
321     if (m_thread)
322         return;
323
324     RefPtr<SamplingProfiler> profiler = this;
325     m_thread = Thread::create("jsc.sampling-profiler.thread", [profiler] {
326         profiler->timerLoop();
327     });
328 }
329
330 void SamplingProfiler::timerLoop()
331 {
332     while (true) {
333         Seconds stackTraceProcessingTime = 0_s;
334         {
335             LockHolder locker(m_lock);
336             if (UNLIKELY(m_isShutDown))
337                 return;
338
339             if (!m_isPaused && m_jscExecutionThread)
340                 takeSample(locker, stackTraceProcessingTime);
341
342             m_lastTime = m_stopwatch->elapsedTime();
343         }
344
345         // Read section 6.2 of this paper for more elaboration of why we add a random
346         // fluctuation here. The main idea is to prevent our timer from being in sync
347         // with some system process such as a scheduled context switch.
348         // http://plv.colorado.edu/papers/mytkowicz-pldi10.pdf
349         double randomSignedNumber = (m_weakRandom.get() * 2.0) - 1.0; // A random number between [-1, 1).
350         Seconds randomFluctuation = m_timingInterval * 0.2 * randomSignedNumber;
351         WTF::sleep(m_timingInterval - std::min(m_timingInterval, stackTraceProcessingTime) + randomFluctuation);
352     }
353 }
354
355 void SamplingProfiler::takeSample(const AbstractLocker&, Seconds& stackTraceProcessingTime)
356 {
357     ASSERT(m_lock.isLocked());
358     if (m_vm.entryScope) {
359         Seconds nowTime = m_stopwatch->elapsedTime();
360
361         auto machineThreadsLocker = holdLock(m_vm.heap.machineThreads().getLock());
362         auto codeBlockSetLocker = holdLock(m_vm.heap.codeBlockSet().getLock());
363         auto executableAllocatorLocker = holdLock(ExecutableAllocator::singleton().getLock());
364 #if ENABLE(WEBASSEMBLY)
365         auto wasmCalleesLocker = holdLock(Wasm::CalleeRegistry::singleton().getLock());
366 #else
367         LockHolder wasmCalleesLocker(NoLockingNecessary);
368 #endif
369
370         auto didSuspend = m_jscExecutionThread->suspend();
371         if (didSuspend) {
372             // While the JSC thread is suspended, we can't do things like malloc because the JSC thread
373             // may be holding the malloc lock.
374             void* machineFrame;
375             CallFrame* callFrame;
376             void* machinePC;
377             bool topFrameIsLLInt = false;
378             void* llintPC;
379             {
380                 PlatformRegisters registers;
381                 m_jscExecutionThread->getRegisters(registers);
382                 machineFrame = MachineContext::framePointer(registers);
383                 callFrame = static_cast<CallFrame*>(machineFrame);
384                 auto instructionPointer = MachineContext::instructionPointer(registers);
385                 if (instructionPointer)
386                     machinePC = instructionPointer->untaggedExecutableAddress();
387                 else
388                     machinePC = nullptr;
389                 llintPC = removeCodePtrTag(MachineContext::llintInstructionPointer(registers));
390                 assertIsNotTagged(machinePC);
391             }
392             // FIXME: Lets have a way of detecting when we're parsing code.
393             // https://bugs.webkit.org/show_bug.cgi?id=152761
394             if (ExecutableAllocator::singleton().isValidExecutableMemory(executableAllocatorLocker, machinePC)) {
395                 if (m_vm.isExecutingInRegExpJIT) {
396                     // FIXME: We're executing a regexp. Lets gather more intersting data.
397                     // https://bugs.webkit.org/show_bug.cgi?id=152729
398                     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.
399                 }
400             } else if (LLInt::isLLIntPC(machinePC)) {
401                 topFrameIsLLInt = true;
402                 // We're okay to take a normal stack trace when the PC
403                 // is in LLInt code.
404             } else {
405                 // We resort to topCallFrame to see if we can get anything
406                 // useful. We usually get here when we're executing C code.
407                 callFrame = m_vm.topCallFrame;
408             }
409
410             size_t walkSize;
411             bool wasValidWalk;
412             bool didRunOutOfVectorSpace;
413             if (Options::sampleCCode()) {
414                 CFrameWalker walker(m_vm, machineFrame, callFrame, codeBlockSetLocker, machineThreadsLocker, wasmCalleesLocker);
415                 walkSize = walker.walk(m_currentFrames, didRunOutOfVectorSpace);
416                 wasValidWalk = walker.wasValidWalk();
417             } else {
418                 FrameWalker walker(m_vm, callFrame, codeBlockSetLocker, machineThreadsLocker, wasmCalleesLocker);
419                 walkSize = walker.walk(m_currentFrames, didRunOutOfVectorSpace);
420                 wasValidWalk = walker.wasValidWalk();
421             }
422
423             m_jscExecutionThread->resume();
424
425             auto startTime = MonotonicTime::now();
426             // We can now use data structures that malloc, and do other interesting things, again.
427
428             // FIXME: It'd be interesting to take data about the program's state when
429             // we fail to take a stack trace: https://bugs.webkit.org/show_bug.cgi?id=152758
430             if (wasValidWalk && walkSize) {
431                 if (sReportStats)
432                     sNumTotalStackTraces++;
433                 Vector<UnprocessedStackFrame> stackTrace;
434                 stackTrace.reserveInitialCapacity(walkSize);
435                 for (size_t i = 0; i < walkSize; i++) {
436                     UnprocessedStackFrame frame = m_currentFrames[i];
437                     stackTrace.uncheckedAppend(frame);
438                 }
439
440                 m_unprocessedStackTraces.append(UnprocessedStackTrace { nowTime, machinePC, topFrameIsLLInt, llintPC, WTFMove(stackTrace) });
441
442                 if (didRunOutOfVectorSpace)
443                     m_currentFrames.grow(m_currentFrames.size() * 1.25);
444             }
445
446             auto endTime = MonotonicTime::now();
447             stackTraceProcessingTime = endTime - startTime;
448         }
449     }
450 }
451
452 static ALWAYS_INLINE BytecodeIndex tryGetBytecodeIndex(unsigned llintPC, CodeBlock* codeBlock)
453 {
454 #if ENABLE(DFG_JIT)
455     RELEASE_ASSERT(!codeBlock->hasCodeOrigins());
456 #endif
457
458 #if USE(JSVALUE64)
459     unsigned bytecodeOffset = llintPC;
460     if (bytecodeOffset < codeBlock->instructionsSize())
461         return BytecodeIndex(bytecodeOffset);
462     return BytecodeIndex();
463 #else
464     Instruction* instruction = bitwise_cast<Instruction*>(llintPC);
465
466     if (codeBlock->instructions().contains(instruction))
467         return BytecodeIndex(codeBlock->bytecodeOffset(instruction));
468     return BytecodeIndex();
469 #endif
470 }
471
472 void SamplingProfiler::processUnverifiedStackTraces(const AbstractLocker&)
473 {
474     // This function needs to be called from the JSC execution thread.
475     RELEASE_ASSERT(m_lock.isLocked());
476
477     TinyBloomFilter filter = m_vm.heap.objectSpace().blocks().filter();
478
479     for (UnprocessedStackTrace& unprocessedStackTrace : m_unprocessedStackTraces) {
480         m_stackTraces.append(StackTrace());
481         StackTrace& stackTrace = m_stackTraces.last();
482         stackTrace.timestamp = unprocessedStackTrace.timestamp;
483
484         auto populateCodeLocation = [] (CodeBlock* codeBlock, BytecodeIndex bytecodeIndex, StackFrame::CodeLocation& location) {
485             if (bytecodeIndex.offset() < codeBlock->instructionsSize()) {
486                 int divot;
487                 int startOffset;
488                 int endOffset;
489                 codeBlock->expressionRangeForBytecodeIndex(bytecodeIndex, divot, startOffset, endOffset,
490                     location.lineNumber, location.columnNumber);
491                 location.bytecodeIndex = bytecodeIndex;
492             }
493             if (Options::collectSamplingProfilerDataForJSCShell()) {
494                 location.codeBlockHash = codeBlock->hash();
495                 location.jitType = codeBlock->jitType();
496             }
497         };
498
499         auto appendCodeBlock = [&] (CodeBlock* codeBlock, BytecodeIndex bytecodeIndex) {
500             stackTrace.frames.append(StackFrame(codeBlock->ownerExecutable()));
501             m_liveCellPointers.add(codeBlock->ownerExecutable());
502             populateCodeLocation(codeBlock, bytecodeIndex, stackTrace.frames.last().semanticLocation);
503         };
504
505         auto appendEmptyFrame = [&] {
506             stackTrace.frames.append(StackFrame());
507         };
508
509         auto storeCalleeIntoLastFrame = [&] (UnprocessedStackFrame& unprocessedStackFrame) {
510             // Set the callee if it's a valid GC object.
511             CalleeBits calleeBits = unprocessedStackFrame.unverifiedCallee;
512             StackFrame& stackFrame = stackTrace.frames.last();
513             bool alreadyHasExecutable = !!stackFrame.executable;
514 #if ENABLE(WEBASSEMBLY)
515             if (calleeBits.isWasm()) {
516                 stackFrame.frameType = FrameType::Wasm;
517                 stackFrame.wasmIndexOrName = unprocessedStackFrame.wasmIndexOrName;
518                 stackFrame.wasmCompilationMode = unprocessedStackFrame.wasmCompilationMode;
519                 return;
520             }
521 #endif
522
523             JSValue callee = calleeBits.asCell();
524             if (!HeapUtil::isValueGCObject(m_vm.heap, filter, callee)) {
525                 if (!alreadyHasExecutable)
526                     stackFrame.frameType = FrameType::Unknown;
527                 return;
528             }
529
530             JSCell* calleeCell = callee.asCell();
531             auto setFallbackFrameType = [&] {
532                 ASSERT(!alreadyHasExecutable);
533                 FrameType result = FrameType::Unknown;
534                 CallData callData;
535                 CallType callType;
536                 callType = getCallData(m_vm, calleeCell, callData);
537                 if (callType == CallType::Host)
538                     result = FrameType::Host;
539
540                 stackFrame.frameType = result;
541             };
542
543             auto addCallee = [&] (JSObject* callee) {
544                 stackFrame.callee = callee;
545                 m_liveCellPointers.add(callee);
546             };
547
548             if (calleeCell->type() != JSFunctionType) {
549                 if (JSObject* object = jsDynamicCast<JSObject*>(calleeCell->vm(), calleeCell))
550                     addCallee(object);
551
552                 if (!alreadyHasExecutable)
553                     setFallbackFrameType();
554
555                 return;
556             }
557
558             addCallee(jsCast<JSFunction*>(calleeCell));
559
560             if (alreadyHasExecutable)
561                 return;
562
563             ExecutableBase* executable = jsCast<JSFunction*>(calleeCell)->executable();
564             if (!executable) {
565                 setFallbackFrameType();
566                 return;
567             }
568
569             RELEASE_ASSERT(HeapUtil::isPointerGCObjectJSCell(m_vm.heap, filter, executable));
570             stackFrame.frameType = FrameType::Executable;
571             stackFrame.executable = executable;
572             m_liveCellPointers.add(executable);
573         };
574
575         auto appendCodeOrigin = [&] (CodeBlock* machineCodeBlock, CodeOrigin origin) {
576             size_t startIndex = stackTrace.frames.size(); // We want to change stack traces that we're about to append.
577
578             CodeOrigin machineOrigin;
579             origin.walkUpInlineStack([&] (const CodeOrigin& codeOrigin) {
580                 machineOrigin = codeOrigin;
581                 auto* inlineCallFrame = codeOrigin.inlineCallFrame();
582                 appendCodeBlock(inlineCallFrame ? inlineCallFrame->baselineCodeBlock.get() : machineCodeBlock, codeOrigin.bytecodeIndex());
583             });
584
585             if (Options::collectSamplingProfilerDataForJSCShell()) {
586                 RELEASE_ASSERT(machineOrigin.isSet());
587                 RELEASE_ASSERT(!machineOrigin.inlineCallFrame());
588
589                 StackFrame::CodeLocation machineLocation = stackTrace.frames.last().semanticLocation;
590
591                 // We want to tell each inlined frame about the machine frame
592                 // they were inlined into. Currently, we only use this for dumping
593                 // output on the command line, but we could extend it to the web
594                 // inspector in the future if we find a need for it there.
595                 RELEASE_ASSERT(stackTrace.frames.size());
596                 m_liveCellPointers.add(machineCodeBlock);
597                 for (size_t i = startIndex; i < stackTrace.frames.size() - 1; i++)
598                     stackTrace.frames[i].machineLocation = std::make_pair(machineLocation, machineCodeBlock);
599             }
600         };
601
602         // Prepend the top-most inlined frame if needed and gather
603         // location information about where the top frame is executing.
604         size_t startIndex = 0;
605         if (unprocessedStackTrace.frames.size() && !!unprocessedStackTrace.frames[0].verifiedCodeBlock) {
606             CodeBlock* topCodeBlock = unprocessedStackTrace.frames[0].verifiedCodeBlock;
607             if (unprocessedStackTrace.topFrameIsLLInt) {
608                 // We reuse LLInt CodeBlocks for the baseline JIT, so we need to check for both jit types.
609                 // This might also be false for various reasons (known and unknown), even though
610                 // it's super unlikely. One reason that this can be false is when we throw from a DFG frame,
611                 // and we end up having to unwind past an EntryFrame, we will end up executing
612                 // inside the LLInt's handleUncaughtException. So we just protect against this
613                 // by ignoring it.
614                 BytecodeIndex bytecodeIndex = BytecodeIndex(0);
615                 if (topCodeBlock->jitType() == JITType::InterpreterThunk || topCodeBlock->jitType() == JITType::BaselineJIT) {
616                     unsigned bits;
617 #if USE(JSVALUE64)
618                     bits = static_cast<unsigned>(bitwise_cast<uintptr_t>(unprocessedStackTrace.llintPC));
619 #else
620                     bits = bitwise_cast<unsigned>(unprocessedStackTrace.llintPC);
621 #endif
622                     bytecodeIndex = tryGetBytecodeIndex(bits, topCodeBlock);
623
624                     UNUSED_PARAM(bytecodeIndex); // FIXME: do something with this info for the web inspector: https://bugs.webkit.org/show_bug.cgi?id=153455
625
626                     appendCodeBlock(topCodeBlock, bytecodeIndex);
627                     storeCalleeIntoLastFrame(unprocessedStackTrace.frames[0]);
628                     startIndex = 1;
629                 }
630             } else {
631 #if ENABLE(JIT)
632                 if (Optional<CodeOrigin> codeOrigin = topCodeBlock->findPC(unprocessedStackTrace.topPC)) {
633                     appendCodeOrigin(topCodeBlock, *codeOrigin);
634                     storeCalleeIntoLastFrame(unprocessedStackTrace.frames[0]);
635                     startIndex = 1;
636                 }
637 #endif
638                 UNUSED_PARAM(appendCodeOrigin);
639             }
640         }
641
642         for (size_t i = startIndex; i < unprocessedStackTrace.frames.size(); i++) {
643             UnprocessedStackFrame& unprocessedStackFrame = unprocessedStackTrace.frames[i];
644             if (CodeBlock* codeBlock = unprocessedStackFrame.verifiedCodeBlock) {
645                 CallSiteIndex callSiteIndex = unprocessedStackFrame.callSiteIndex;
646
647                 auto appendCodeBlockNoInlining = [&] {
648                     appendCodeBlock(codeBlock, tryGetBytecodeIndex(callSiteIndex.bits(), codeBlock));
649                 };
650
651 #if ENABLE(DFG_JIT)
652                 if (codeBlock->hasCodeOrigins()) {
653                     if (codeBlock->canGetCodeOrigin(callSiteIndex))
654                         appendCodeOrigin(codeBlock, codeBlock->codeOrigin(callSiteIndex));
655                     else
656                         appendCodeBlock(codeBlock, BytecodeIndex());
657                 } else
658                     appendCodeBlockNoInlining();
659 #else
660                 appendCodeBlockNoInlining();
661 #endif
662             } else if (unprocessedStackFrame.cCodePC) {
663                 appendEmptyFrame();
664                 stackTrace.frames.last().cCodePC = unprocessedStackFrame.cCodePC;
665                 stackTrace.frames.last().frameType = FrameType::C;
666             } else
667                 appendEmptyFrame();
668
669             // Note that this is okay to do if we walked the inline stack because
670             // the machine frame will be at the top of the processed stack trace.
671             if (!unprocessedStackFrame.cCodePC)
672                 storeCalleeIntoLastFrame(unprocessedStackFrame);
673         }
674     }
675
676     m_unprocessedStackTraces.clear();
677 }
678
679 void SamplingProfiler::visit(SlotVisitor& slotVisitor)
680 {
681     RELEASE_ASSERT(m_lock.isLocked());
682     for (JSCell* cell : m_liveCellPointers)
683         slotVisitor.appendUnbarriered(cell);
684 }
685
686 void SamplingProfiler::shutdown()
687 {
688     LockHolder locker(m_lock);
689     m_isShutDown = true;
690 }
691
692 void SamplingProfiler::start()
693 {
694     LockHolder locker(m_lock);
695     start(locker);
696 }
697
698 void SamplingProfiler::start(const AbstractLocker& locker)
699 {
700     ASSERT(m_lock.isLocked());
701     m_isPaused = false;
702     createThreadIfNecessary(locker);
703 }
704
705 void SamplingProfiler::pause(const AbstractLocker&)
706 {
707     ASSERT(m_lock.isLocked());
708     m_isPaused = true;
709     reportStats();
710 }
711
712 void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread(const AbstractLocker&)
713 {
714     ASSERT(m_lock.isLocked());
715     m_jscExecutionThread = &Thread::current();
716 }
717
718 void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread()
719 {
720     LockHolder locker(m_lock);
721     noticeCurrentThreadAsJSCExecutionThread(locker);
722 }
723
724 void SamplingProfiler::noticeJSLockAcquisition()
725 {
726     LockHolder locker(m_lock);
727     noticeCurrentThreadAsJSCExecutionThread(locker);
728 }
729
730 void SamplingProfiler::noticeVMEntry()
731 {
732     LockHolder locker(m_lock);
733     ASSERT(m_vm.entryScope);
734     noticeCurrentThreadAsJSCExecutionThread(locker);
735     m_lastTime = m_stopwatch->elapsedTime();
736     createThreadIfNecessary(locker);
737 }
738
739 void SamplingProfiler::clearData(const AbstractLocker&)
740 {
741     ASSERT(m_lock.isLocked());
742     m_stackTraces.clear();
743     m_liveCellPointers.clear();
744     m_unprocessedStackTraces.clear();
745 }
746
747 String SamplingProfiler::StackFrame::nameFromCallee(VM& vm)
748 {
749     if (!callee)
750         return String();
751
752     auto scope = DECLARE_CATCH_SCOPE(vm);
753     JSGlobalObject* globalObject = callee->globalObject(vm);
754     auto getPropertyIfPureOperation = [&] (const Identifier& ident) -> String {
755         PropertySlot slot(callee, PropertySlot::InternalMethodType::VMInquiry);
756         PropertyName propertyName(ident);
757         bool hasProperty = callee->getPropertySlot(globalObject, propertyName, slot);
758         scope.assertNoException();
759         if (hasProperty) {
760             if (slot.isValue()) {
761                 JSValue nameValue = slot.getValue(globalObject, propertyName);
762                 if (isJSString(nameValue))
763                     return asString(nameValue)->tryGetValue();
764             }
765         }
766         return String();
767     };
768
769     String name = getPropertyIfPureOperation(vm.propertyNames->displayName);
770     if (!name.isEmpty())
771         return name;
772
773     return getPropertyIfPureOperation(vm.propertyNames->name);
774 }
775
776 String SamplingProfiler::StackFrame::displayName(VM& vm)
777 {
778     {
779         String name = nameFromCallee(vm);
780         if (!name.isEmpty())
781             return name;
782     }
783
784     switch (frameType) {
785     case FrameType::Unknown:
786     case FrameType::C:
787 #if HAVE(DLADDR)
788         if (frameType == FrameType::C) {
789             auto demangled = WTF::StackTrace::demangle(const_cast<void*>(cCodePC));
790             if (demangled)
791                 return String(demangled->demangledName() ? demangled->demangledName() : demangled->mangledName());
792             WTF::dataLog("couldn't get a name");
793         }
794 #endif
795         return "(unknown)"_s;
796
797     case FrameType::Host:
798         return "(host)"_s;
799
800     case FrameType::Wasm:
801 #if ENABLE(WEBASSEMBLY)
802         if (wasmIndexOrName)
803             return makeString(wasmIndexOrName.value());
804 #endif
805         return "(wasm)"_s;
806
807     case FrameType::Executable:
808         if (executable->isHostFunction())
809             return static_cast<NativeExecutable*>(executable)->name();
810
811         if (executable->isFunctionExecutable())
812             return static_cast<FunctionExecutable*>(executable)->ecmaName().string();
813         if (executable->isProgramExecutable() || executable->isEvalExecutable())
814             return "(program)"_s;
815         if (executable->isModuleProgramExecutable())
816             return "(module)"_s;
817
818         RELEASE_ASSERT_NOT_REACHED();
819         return String();
820     }
821     RELEASE_ASSERT_NOT_REACHED();
822     return String();
823 }
824
825 String SamplingProfiler::StackFrame::displayNameForJSONTests(VM& vm)
826 {
827     {
828         String name = nameFromCallee(vm);
829         if (!name.isEmpty())
830             return name;
831     }
832
833     switch (frameType) {
834     case FrameType::Unknown:
835     case FrameType::C:
836         return "(unknown)"_s;
837
838     case FrameType::Host:
839         return "(host)"_s;
840
841     case FrameType::Wasm: {
842 #if ENABLE(WEBASSEMBLY)
843         if (wasmIndexOrName)
844             return makeString(wasmIndexOrName.value());
845 #endif
846         return "(wasm)"_s;
847     }
848
849     case FrameType::Executable:
850         if (executable->isHostFunction())
851             return static_cast<NativeExecutable*>(executable)->name();
852
853         if (executable->isFunctionExecutable()) {
854             String result = static_cast<FunctionExecutable*>(executable)->ecmaName().string();
855             if (result.isEmpty())
856                 return "(anonymous function)"_s;
857             return result;
858         }
859         if (executable->isEvalExecutable())
860             return "(eval)"_s;
861         if (executable->isProgramExecutable())
862             return "(program)"_s;
863         if (executable->isModuleProgramExecutable())
864             return "(module)"_s;
865
866         RELEASE_ASSERT_NOT_REACHED();
867         return String();
868     }
869     RELEASE_ASSERT_NOT_REACHED();
870     return String();
871 }
872
873 int SamplingProfiler::StackFrame::functionStartLine()
874 {
875     switch (frameType) {
876     case FrameType::Unknown:
877     case FrameType::Host:
878     case FrameType::C:
879     case FrameType::Wasm:
880         return -1;
881
882     case FrameType::Executable:
883         if (executable->isHostFunction())
884             return -1;
885         return static_cast<ScriptExecutable*>(executable)->firstLine();
886     }
887     RELEASE_ASSERT_NOT_REACHED();
888     return -1;
889 }
890
891 unsigned SamplingProfiler::StackFrame::functionStartColumn()
892 {
893     switch (frameType) {
894     case FrameType::Unknown:
895     case FrameType::Host:
896     case FrameType::C:
897     case FrameType::Wasm:
898         return std::numeric_limits<unsigned>::max();
899
900     case FrameType::Executable:
901         if (executable->isHostFunction())
902             return std::numeric_limits<unsigned>::max();
903
904         return static_cast<ScriptExecutable*>(executable)->startColumn();
905     }
906     RELEASE_ASSERT_NOT_REACHED();
907     return std::numeric_limits<unsigned>::max();
908 }
909
910 intptr_t SamplingProfiler::StackFrame::sourceID()
911 {
912     switch (frameType) {
913     case FrameType::Unknown:
914     case FrameType::Host:
915     case FrameType::C:
916     case FrameType::Wasm:
917         return -1;
918
919     case FrameType::Executable:
920         if (executable->isHostFunction())
921             return -1;
922
923         return static_cast<ScriptExecutable*>(executable)->sourceID();
924     }
925     RELEASE_ASSERT_NOT_REACHED();
926     return -1;
927 }
928
929 String SamplingProfiler::StackFrame::url()
930 {
931     switch (frameType) {
932     case FrameType::Unknown:
933     case FrameType::Host:
934     case FrameType::C:
935     case FrameType::Wasm:
936         return emptyString();
937     case FrameType::Executable:
938         if (executable->isHostFunction())
939             return emptyString();
940
941         String url = static_cast<ScriptExecutable*>(executable)->sourceURL();
942         if (url.isEmpty())
943             return static_cast<ScriptExecutable*>(executable)->source().provider()->sourceURLDirective(); // Fall back to sourceURL directive.
944         return url;
945     }
946     RELEASE_ASSERT_NOT_REACHED();
947     return String();
948 }
949
950 Vector<SamplingProfiler::StackTrace> SamplingProfiler::releaseStackTraces(const AbstractLocker& locker)
951 {
952     ASSERT(m_lock.isLocked());
953     {
954         HeapIterationScope heapIterationScope(m_vm.heap);
955         processUnverifiedStackTraces(locker);
956     }
957
958     Vector<StackTrace> result(WTFMove(m_stackTraces));
959     clearData(locker);
960     return result;
961 }
962
963 String SamplingProfiler::stackTracesAsJSON()
964 {
965     DeferGC deferGC(m_vm.heap);
966     auto locker = holdLock(m_lock);
967
968     {
969         HeapIterationScope heapIterationScope(m_vm.heap);
970         processUnverifiedStackTraces(locker);
971     }
972
973     StringBuilder json;
974     json.append('[');
975
976     bool loopedOnce = false;
977     auto comma = [&] {
978         if (loopedOnce)
979             json.append(',');
980     };
981     for (StackTrace& stackTrace : m_stackTraces) {
982         comma();
983         json.append('[');
984         loopedOnce = false;
985         for (StackFrame& stackFrame : stackTrace.frames) {
986             comma();
987             json.appendQuotedJSONString(stackFrame.displayNameForJSONTests(m_vm));
988             loopedOnce = true;
989         }
990         json.append(']');
991         loopedOnce = true;
992     }
993
994     json.append(']');
995
996     clearData(locker);
997
998     return json.toString();
999 }
1000
1001 void SamplingProfiler::registerForReportAtExit()
1002 {
1003     static Lock registrationLock;
1004     static HashSet<RefPtr<SamplingProfiler>>* profilesToReport;
1005
1006     LockHolder holder(registrationLock);
1007
1008     if (!profilesToReport) {
1009         profilesToReport = new HashSet<RefPtr<SamplingProfiler>>();
1010         atexit([]() {
1011             for (const auto& profile : *profilesToReport)
1012                 profile->reportDataToOptionFile();
1013         });
1014     }
1015
1016     profilesToReport->add(adoptRef(this));
1017     m_needsReportAtExit = true;
1018 }
1019
1020 void SamplingProfiler::reportDataToOptionFile()
1021 {
1022     if (m_needsReportAtExit) {
1023         m_needsReportAtExit = false;
1024         JSLockHolder holder(m_vm);
1025         const char* path = Options::samplingProfilerPath();
1026         StringPrintStream pathOut;
1027         pathOut.print(path, "/");
1028         pathOut.print("JSCSampilingProfile-", reinterpret_cast<uintptr_t>(this), ".txt");
1029         auto out = FilePrintStream::open(pathOut.toCString().data(), "w");
1030         reportTopFunctions(*out);
1031         reportTopBytecodes(*out);
1032     }
1033 }
1034
1035 void SamplingProfiler::reportTopFunctions()
1036 {
1037     reportTopFunctions(WTF::dataFile());
1038 }
1039
1040 void SamplingProfiler::reportTopFunctions(PrintStream& out)
1041 {
1042     auto locker = holdLock(m_lock);
1043     DeferGCForAWhile deferGC(m_vm.heap);
1044
1045     {
1046         HeapIterationScope heapIterationScope(m_vm.heap);
1047         processUnverifiedStackTraces(locker);
1048     }
1049
1050
1051     HashMap<String, size_t> functionCounts;
1052     for (StackTrace& stackTrace : m_stackTraces) {
1053         if (!stackTrace.frames.size())
1054             continue;
1055
1056         StackFrame& frame = stackTrace.frames.first();
1057         String frameDescription = makeString(frame.displayName(m_vm), ':', frame.sourceID());
1058         functionCounts.add(frameDescription, 0).iterator->value++;
1059     }
1060
1061     auto takeMax = [&] () -> std::pair<String, size_t> {
1062         String maxFrameDescription;
1063         size_t maxFrameCount = 0;
1064         for (const auto& entry : functionCounts) {
1065             if (entry.value > maxFrameCount) {
1066                 maxFrameCount = entry.value;
1067                 maxFrameDescription = entry.key;
1068             }
1069         }
1070         if (!maxFrameDescription.isEmpty())
1071             functionCounts.remove(maxFrameDescription);
1072         return std::make_pair(maxFrameDescription, maxFrameCount);
1073     };
1074
1075     if (Options::samplingProfilerTopFunctionsCount()) {
1076         out.print("\n\nSampling rate: ", m_timingInterval.microseconds(), " microseconds\n");
1077         out.print("Top functions as <numSamples  'functionName:sourceID'>\n");
1078         for (size_t i = 0; i < Options::samplingProfilerTopFunctionsCount(); i++) {
1079             auto pair = takeMax();
1080             if (pair.first.isEmpty())
1081                 break;
1082             out.printf("%6zu ", pair.second);
1083             out.print("   '", pair.first, "'\n");
1084         }
1085     }
1086 }
1087
1088 void SamplingProfiler::reportTopBytecodes()
1089 {
1090     reportTopBytecodes(WTF::dataFile());
1091 }
1092
1093 void SamplingProfiler::reportTopBytecodes(PrintStream& out)
1094 {
1095     auto locker = holdLock(m_lock);
1096     DeferGCForAWhile deferGC(m_vm.heap);
1097
1098     {
1099         HeapIterationScope heapIterationScope(m_vm.heap);
1100         processUnverifiedStackTraces(locker);
1101     }
1102
1103     HashMap<String, size_t> bytecodeCounts;
1104     for (StackTrace& stackTrace : m_stackTraces) {
1105         if (!stackTrace.frames.size())
1106             continue;
1107
1108         auto descriptionForLocation = [&] (StackFrame::CodeLocation location, Optional<Wasm::CompilationMode> wasmCompilationMode) -> String {
1109             String bytecodeIndex;
1110             String codeBlockHash;
1111             String jitType;
1112             if (location.hasBytecodeIndex())
1113                 bytecodeIndex = toString(location.bytecodeIndex);
1114             else
1115                 bytecodeIndex = "<nil>";
1116
1117             if (location.hasCodeBlockHash()) {
1118                 StringPrintStream stream;
1119                 location.codeBlockHash.dump(stream);
1120                 codeBlockHash = stream.toString();
1121             } else
1122                 codeBlockHash = "<nil>";
1123
1124             if (wasmCompilationMode)
1125                 jitType = Wasm::makeString(wasmCompilationMode.value());
1126             else
1127                 jitType = JITCode::typeName(location.jitType);
1128
1129             return makeString("#", codeBlockHash, ":", jitType, ":", bytecodeIndex);
1130         };
1131
1132         StackFrame& frame = stackTrace.frames.first();
1133         String frameDescription = makeString(frame.displayName(m_vm), descriptionForLocation(frame.semanticLocation, frame.wasmCompilationMode));
1134         if (Optional<std::pair<StackFrame::CodeLocation, CodeBlock*>> machineLocation = frame.machineLocation) {
1135             frameDescription = makeString(frameDescription, " <-- ",
1136                 machineLocation->second->inferredName().data(), descriptionForLocation(machineLocation->first, WTF::nullopt));
1137         }
1138         bytecodeCounts.add(frameDescription, 0).iterator->value++;
1139     }
1140
1141     auto takeMax = [&] () -> std::pair<String, size_t> {
1142         String maxFrameDescription;
1143         size_t maxFrameCount = 0;
1144         for (const auto& entry : bytecodeCounts) {
1145             if (entry.value > maxFrameCount) {
1146                 maxFrameCount = entry.value;
1147                 maxFrameDescription = entry.key;
1148             }
1149         }
1150         if (!maxFrameDescription.isEmpty())
1151             bytecodeCounts.remove(maxFrameDescription);
1152         return std::make_pair(maxFrameDescription, maxFrameCount);
1153     };
1154
1155     if (Options::samplingProfilerTopBytecodesCount()) {
1156         out.print("\n\nSampling rate: ", m_timingInterval.microseconds(), " microseconds\n");
1157         out.print("Hottest bytecodes as <numSamples   'functionName#hash:JITType:bytecodeIndex'>\n");
1158         for (size_t i = 0; i < Options::samplingProfilerTopBytecodesCount(); i++) {
1159             auto pair = takeMax();
1160             if (pair.first.isEmpty())
1161                 break;
1162             out.printf("%6zu ", pair.second);
1163             out.print("   '", pair.first, "'\n");
1164         }
1165     }
1166 }
1167
1168 #if OS(DARWIN)
1169 mach_port_t SamplingProfiler::machThread()
1170 {
1171     if (!m_thread)
1172         return MACH_PORT_NULL;
1173
1174     return m_thread->machThread();
1175 }
1176 #endif
1177
1178 } // namespace JSC
1179
1180 namespace WTF {
1181
1182 using namespace JSC;
1183
1184 void printInternal(PrintStream& out, SamplingProfiler::FrameType frameType)
1185 {
1186     switch (frameType) {
1187     case SamplingProfiler::FrameType::Executable:
1188         out.print("Executable");
1189         break;
1190     case SamplingProfiler::FrameType::Wasm:
1191         out.print("Wasm");
1192         break;
1193     case SamplingProfiler::FrameType::Host:
1194         out.print("Host");
1195         break;
1196     case SamplingProfiler::FrameType::C:
1197     case SamplingProfiler::FrameType::Unknown:
1198         out.print("Unknown");
1199         break;
1200     }
1201 }
1202
1203 } // namespace WTF
1204
1205 #endif // ENABLE(SAMPLING_PROFILER)