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