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