blob: 7cd78c6925e2a2a0253d60db56e3987fe2f8873e [file] [log] [blame]
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001/*
mark.lam@apple.com15f620c2021-02-13 05:30:58 +00002 * Copyright (C) 2016-2021 Apple Inc. All rights reserved.
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00003 *
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
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000031#include "CodeBlock.h"
fpizlo@apple.com7b231642016-10-11 23:52:02 +000032#include "CodeBlockSet.h"
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000033#include "HeapIterationScope.h"
fpizlo@apple.combc16ddb2016-09-06 01:02:22 +000034#include "HeapUtil.h"
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000035#include "InlineCallFrame.h"
ross.kirsling@sony.come257a3b2020-05-19 23:56:00 +000036#include "JSCInlines.h"
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000037#include "LLIntPCRanges.h"
utatane.tea@gmail.com71111912017-07-19 08:43:57 +000038#include "MachineContext.h"
ross.kirsling@sony.com0ebe33c2020-05-15 19:39:36 +000039#include "MarkedBlockInlines.h"
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000040#include "MarkedBlockSet.h"
ggaren@apple.comc9a6ef42016-10-28 20:04:56 +000041#include "NativeExecutable.h"
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000042#include "VM.h"
mark.lam@apple.come2166a82021-04-28 17:13:40 +000043#include "VMTrapsInlines.h"
ysuzuki@apple.com32e43052019-08-02 22:58:09 +000044#include "WasmCallee.h"
45#include "WasmCalleeRegistry.h"
mark.lam@apple.com9c025812020-05-26 21:32:50 +000046#include "WasmCapabilities.h"
annulen@yandex.ru9c4fab52017-06-20 16:32:41 +000047#include <wtf/FilePrintStream.h>
keith_miller@apple.com3517aa42016-05-24 23:03:09 +000048#include <wtf/HashSet.h>
49#include <wtf/RefPtr.h>
utatane.tea@gmail.com57950612017-04-25 02:53:49 +000050#include <wtf/StackTrace.h>
joepeck@webkit.org7e07f392016-09-22 18:59:47 +000051#include <wtf/text/StringBuilder.h>
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000052
53namespace JSC {
54
55static double sNumTotalStackTraces = 0;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000056static double sNumTotalWalks = 0;
57static double sNumFailedWalks = 0;
58static const uint32_t sNumWalkReportingFrequency = 50;
59static const double sWalkErrorPercentage = .05;
mark.lam@apple.comd27553f2019-09-18 00:36:19 +000060static constexpr bool sReportStatsOnlyWhenTheyreAboveThreshold = false;
61static constexpr bool sReportStats = false;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000062
63using FrameType = SamplingProfiler::FrameType;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +000064using UnprocessedStackFrame = SamplingProfiler::UnprocessedStackFrame;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000065
66ALWAYS_INLINE static void reportStats()
67{
68 if (sReportStats && sNumTotalWalks && static_cast<uint64_t>(sNumTotalWalks) % sNumWalkReportingFrequency == 0) {
69 if (!sReportStatsOnlyWhenTheyreAboveThreshold || (sNumFailedWalks / sNumTotalWalks > sWalkErrorPercentage)) {
70 dataLogF("Num total walks: %llu. Failed walks percent: %lf\n",
utatane.tea@gmail.com0cf8d742016-01-30 12:53:59 +000071 static_cast<unsigned long long>(sNumTotalWalks), sNumFailedWalks / sNumTotalWalks);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000072 }
73 }
74}
75
76class FrameWalker {
77public:
cdumez@apple.comb261d322021-05-29 21:08:38 +000078 FrameWalker(VM& vm, CallFrame* callFrame, const AbstractLocker& codeBlockSetLocker, const AbstractLocker& machineThreadsLocker)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000079 : m_vm(vm)
80 , m_callFrame(callFrame)
jfbastien@apple.comd9f999e2017-10-20 02:23:29 +000081 , m_entryFrame(vm.topEntryFrame)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000082 , m_codeBlockSetLocker(codeBlockSetLocker)
83 , m_machineThreadsLocker(machineThreadsLocker)
84 {
85 }
86
sbarati@apple.com30cd1162016-02-11 21:04:43 +000087 SUPPRESS_ASAN
sbarati@apple.comd3d0c002016-01-30 01:11:05 +000088 size_t walk(Vector<UnprocessedStackFrame>& stackTrace, bool& didRunOutOfSpace)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000089 {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000090 if (sReportStats)
91 sNumTotalWalks++;
92 resetAtMachineFrame();
93 size_t maxStackTraceSize = stackTrace.size();
94 while (!isAtTop() && !m_bailingOut && m_depth < maxStackTraceSize) {
ysuzuki@apple.com32e43052019-08-02 22:58:09 +000095 recordJITFrame(stackTrace);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +000096 advanceToParentFrame();
97 resetAtMachineFrame();
98 }
99 didRunOutOfSpace = m_depth >= maxStackTraceSize && !isAtTop();
100 reportStats();
101 return m_depth;
102 }
103
104 bool wasValidWalk() const
105 {
106 return !m_bailingOut;
107 }
108
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000109protected:
110
111 SUPPRESS_ASAN
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000112 void recordJITFrame(Vector<UnprocessedStackFrame>& stackTrace)
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000113 {
114 CallSiteIndex callSiteIndex;
sbarati@apple.com5db42f82017-04-04 22:23:37 +0000115 CalleeBits unsafeCallee = m_callFrame->unsafeCallee();
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000116 CodeBlock* codeBlock = m_callFrame->unsafeCodeBlock();
tzagallo@apple.combf01be22019-10-31 22:32:52 +0000117 if (unsafeCallee.isWasm())
118 codeBlock = nullptr;
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000119 if (codeBlock) {
120 ASSERT(isValidCodeBlock(codeBlock));
121 callSiteIndex = m_callFrame->unsafeCallSiteIndex();
122 }
sbarati@apple.com5db42f82017-04-04 22:23:37 +0000123 stackTrace[m_depth] = UnprocessedStackFrame(codeBlock, unsafeCallee, callSiteIndex);
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000124#if ENABLE(WEBASSEMBLY)
mark.lam@apple.com9c025812020-05-26 21:32:50 +0000125 if (Wasm::isSupported() && unsafeCallee.isWasm()) {
cdumez@apple.comb261d322021-05-29 21:08:38 +0000126 assertIsHeld(Wasm::CalleeRegistry::singleton().getLock());
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000127 auto* wasmCallee = unsafeCallee.asWasmCallee();
cdumez@apple.comb261d322021-05-29 21:08:38 +0000128 if (Wasm::CalleeRegistry::singleton().isValidCallee(wasmCallee)) {
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000129 // At this point, Wasm::Callee would be dying (ref count is 0), but its fields are still live.
130 // And we can safely copy Wasm::IndexOrName even when any lock is held by suspended threads.
131 stackTrace[m_depth].wasmIndexOrName = wasmCallee->indexOrName();
132 stackTrace[m_depth].wasmCompilationMode = wasmCallee->compilationMode();
133 }
134 }
135#endif
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000136 m_depth++;
137 }
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000138
sbarati@apple.com30cd1162016-02-11 21:04:43 +0000139 SUPPRESS_ASAN
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000140 void advanceToParentFrame()
141 {
jfbastien@apple.comd9f999e2017-10-20 02:23:29 +0000142 m_callFrame = m_callFrame->unsafeCallerFrame(m_entryFrame);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000143 }
144
145 bool isAtTop() const
146 {
147 return !m_callFrame;
148 }
149
sbarati@apple.com30cd1162016-02-11 21:04:43 +0000150 SUPPRESS_ASAN
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000151 void resetAtMachineFrame()
152 {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000153 if (isAtTop())
154 return;
155
156 if (!isValidFramePointer(m_callFrame)) {
157 // Guard against pausing the process at weird program points.
158 m_bailingOut = true;
159 if (sReportStats)
160 sNumFailedWalks++;
161 return;
162 }
163
sbarati@apple.com88a5fd62016-02-16 22:01:37 +0000164 CodeBlock* codeBlock = m_callFrame->unsafeCodeBlock();
mark.lam@apple.comcadee712019-11-04 19:23:24 +0000165 if (!codeBlock || m_callFrame->unsafeCallee().isWasm())
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000166 return;
167
168 if (!isValidCodeBlock(codeBlock)) {
169 m_bailingOut = true;
170 if (sReportStats)
171 sNumFailedWalks++;
172 return;
173 }
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000174 }
175
ysuzuki@apple.com52e98bb2019-10-22 09:24:48 +0000176 bool isValidFramePointer(void* callFrame)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000177 {
ysuzuki@apple.com52e98bb2019-10-22 09:24:48 +0000178 uint8_t* fpCast = bitwise_cast<uint8_t*>(callFrame);
utatane.tea@gmail.com71111912017-07-19 08:43:57 +0000179 for (auto& thread : m_vm.heap.machineThreads().threads(m_machineThreadsLocker)) {
180 uint8_t* stackBase = static_cast<uint8_t*>(thread->stack().origin());
181 uint8_t* stackLimit = static_cast<uint8_t*>(thread->stack().end());
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000182 RELEASE_ASSERT(stackBase);
183 RELEASE_ASSERT(stackLimit);
mark.lam@apple.com8a3e3372018-12-18 01:21:07 +0000184 RELEASE_ASSERT(stackLimit <= stackBase);
185 if (fpCast < stackBase && fpCast >= stackLimit)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000186 return true;
187 }
188 return false;
189 }
190
191 bool isValidCodeBlock(CodeBlock* codeBlock)
192 {
193 if (!codeBlock)
194 return false;
195 bool result = m_vm.heap.codeBlockSet().contains(m_codeBlockSetLocker, codeBlock);
196 return result;
197 }
198
199 VM& m_vm;
ysuzuki@apple.com52e98bb2019-10-22 09:24:48 +0000200 CallFrame* m_callFrame;
jfbastien@apple.comd9f999e2017-10-20 02:23:29 +0000201 EntryFrame* m_entryFrame;
mark.lam@apple.coma2668e02017-03-09 21:05:41 +0000202 const AbstractLocker& m_codeBlockSetLocker;
203 const AbstractLocker& m_machineThreadsLocker;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000204 bool m_bailingOut { false };
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000205 size_t m_depth { 0 };
206};
207
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000208class CFrameWalker : public FrameWalker {
209public:
210 typedef FrameWalker Base;
211
cdumez@apple.comb261d322021-05-29 21:08:38 +0000212 CFrameWalker(VM& vm, void* machineFrame, CallFrame* callFrame, const AbstractLocker& codeBlockSetLocker, const AbstractLocker& machineThreadsLocker)
213 : Base(vm, callFrame, codeBlockSetLocker, machineThreadsLocker)
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000214 , m_machineFrame(machineFrame)
215 {
216 }
217
218 size_t walk(Vector<UnprocessedStackFrame>& stackTrace, bool& didRunOutOfSpace)
219 {
220 if (sReportStats)
221 sNumTotalWalks++;
222 resetAtMachineFrame();
223 size_t maxStackTraceSize = stackTrace.size();
224 // The way the C walker decides if a frame it is about to trace is C or JS is by
225 // ensuring m_callFrame points to some frame above the machineFrame.
226 if (!isAtTop() && !m_bailingOut && m_machineFrame == m_callFrame) {
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000227 recordJITFrame(stackTrace);
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000228 Base::advanceToParentFrame();
229 resetAtMachineFrame();
230 }
231
232 while (!isAtTop() && !m_bailingOut && m_depth < maxStackTraceSize) {
233 if (m_machineFrame >= m_callFrame) {
234 // If we get to this state we probably have an invalid trace.
235 m_bailingOut = true;
236 break;
237 }
238
239 if (isCFrame()) {
240 RELEASE_ASSERT(!LLInt::isLLIntPC(frame()->callerFrame));
keith_miller@apple.come8709172018-10-16 07:19:13 +0000241 stackTrace[m_depth] = UnprocessedStackFrame(frame()->returnPC);
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000242 m_depth++;
243 } else
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000244 recordJITFrame(stackTrace);
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000245 advanceToParentFrame();
246 resetAtMachineFrame();
247 }
248 didRunOutOfSpace = m_depth >= maxStackTraceSize && !isAtTop();
249 reportStats();
250 return m_depth;
251 }
252
253private:
254
255 bool isCFrame()
256 {
257 return frame()->callerFrame != m_callFrame;
258 }
259
260 void advanceToParentFrame()
261 {
262 if (!isCFrame())
263 Base::advanceToParentFrame();
264 m_machineFrame = frame()->callerFrame;
265 }
266
267 void resetAtMachineFrame()
268 {
269 if (!isValidFramePointer(m_machineFrame)) {
270 // Guard against pausing the process at weird program points.
271 m_bailingOut = true;
272 if (sReportStats)
273 sNumFailedWalks++;
274 return;
275 }
276 Base::resetAtMachineFrame();
277 }
278
279 CallerFrameAndPC* frame()
280 {
281 return reinterpret_cast<CallerFrameAndPC*>(m_machineFrame);
282 }
283
284 void* m_machineFrame;
285};
286
drousso@apple.com1cd50532020-05-06 16:15:16 +0000287SamplingProfiler::SamplingProfiler(VM& vm, Ref<Stopwatch>&& stopwatch)
rmorisset@apple.com4ccbcf12019-03-12 19:07:04 +0000288 : m_isPaused(false)
289 , m_isShutDown(false)
290 , m_vm(vm)
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000291 , m_weakRandom()
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000292 , m_stopwatch(WTFMove(stopwatch))
utatane.tea@gmail.com5c7039d2017-12-27 00:33:11 +0000293 , m_timingInterval(Seconds::fromMicroseconds(Options::sampleInterval()))
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000294{
295 if (sReportStats) {
296 sNumTotalWalks = 0;
297 sNumFailedWalks = 0;
298 }
299
300 m_currentFrames.grow(256);
ysuzuki@apple.comb4399152019-11-09 03:45:18 +0000301 vm.heap.objectSpace().enablePreciseAllocationTracking();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000302}
303
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000304SamplingProfiler::~SamplingProfiler()
305{
306}
307
cdumez@apple.com2f481692021-05-29 21:14:39 +0000308void SamplingProfiler::createThreadIfNecessary()
sbarati@apple.com207a6832016-04-06 03:55:11 +0000309{
310 ASSERT(m_lock.isLocked());
311
utatane.tea@gmail.com1e94a262017-04-12 12:08:29 +0000312 if (m_thread)
sbarati@apple.com207a6832016-04-06 03:55:11 +0000313 return;
314
315 RefPtr<SamplingProfiler> profiler = this;
utatane.tea@gmail.com1e94a262017-04-12 12:08:29 +0000316 m_thread = Thread::create("jsc.sampling-profiler.thread", [profiler] {
sbarati@apple.com207a6832016-04-06 03:55:11 +0000317 profiler->timerLoop();
318 });
319}
320
321void SamplingProfiler::timerLoop()
322{
323 while (true) {
utatane.tea@gmail.com5c7039d2017-12-27 00:33:11 +0000324 Seconds stackTraceProcessingTime = 0_s;
sbarati@apple.com207a6832016-04-06 03:55:11 +0000325 {
cdumez@apple.com3e035a82021-05-22 16:49:42 +0000326 Locker locker { m_lock };
sbarati@apple.com207a6832016-04-06 03:55:11 +0000327 if (UNLIKELY(m_isShutDown))
328 return;
329
330 if (!m_isPaused && m_jscExecutionThread)
cdumez@apple.com2f481692021-05-29 21:14:39 +0000331 takeSample(stackTraceProcessingTime);
sbarati@apple.com207a6832016-04-06 03:55:11 +0000332
333 m_lastTime = m_stopwatch->elapsedTime();
334 }
335
sbarati@apple.com51d80072016-06-14 02:29:26 +0000336 // Read section 6.2 of this paper for more elaboration of why we add a random
337 // fluctuation here. The main idea is to prevent our timer from being in sync
338 // with some system process such as a scheduled context switch.
339 // http://plv.colorado.edu/papers/mytkowicz-pldi10.pdf
utatane.tea@gmail.com0f6fd7b2017-03-24 05:31:51 +0000340 double randomSignedNumber = (m_weakRandom.get() * 2.0) - 1.0; // A random number between [-1, 1).
utatane.tea@gmail.com5c7039d2017-12-27 00:33:11 +0000341 Seconds randomFluctuation = m_timingInterval * 0.2 * randomSignedNumber;
342 WTF::sleep(m_timingInterval - std::min(m_timingInterval, stackTraceProcessingTime) + randomFluctuation);
sbarati@apple.com207a6832016-04-06 03:55:11 +0000343 }
344}
345
cdumez@apple.com2f481692021-05-29 21:14:39 +0000346void SamplingProfiler::takeSample(Seconds& stackTraceProcessingTime)
sbarati@apple.com207a6832016-04-06 03:55:11 +0000347{
348 ASSERT(m_lock.isLocked());
349 if (m_vm.entryScope) {
utatane.tea@gmail.comf0130682018-03-02 17:13:32 +0000350 Seconds nowTime = m_stopwatch->elapsedTime();
sbarati@apple.com207a6832016-04-06 03:55:11 +0000351
cdumez@apple.com6c3db6c2021-05-22 03:13:17 +0000352 Locker machineThreadsLocker { m_vm.heap.machineThreads().getLock() };
353 Locker codeBlockSetLocker { m_vm.heap.codeBlockSet().getLock() };
354 Locker executableAllocatorLocker { ExecutableAllocator::singleton().getLock() };
darin@apple.coma4ddc782021-05-30 16:11:40 +0000355 std::optional<LockHolder> wasmCalleesLocker;
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000356#if ENABLE(WEBASSEMBLY)
mark.lam@apple.com9c025812020-05-26 21:32:50 +0000357 if (Wasm::isSupported())
cdumez@apple.comb261d322021-05-29 21:08:38 +0000358 wasmCalleesLocker.emplace(Wasm::CalleeRegistry::singleton().getLock());
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000359#endif
sbarati@apple.com207a6832016-04-06 03:55:11 +0000360
utatane.tea@gmail.com1e94a262017-04-12 12:08:29 +0000361 auto didSuspend = m_jscExecutionThread->suspend();
sbarati@apple.com207a6832016-04-06 03:55:11 +0000362 if (didSuspend) {
363 // While the JSC thread is suspended, we can't do things like malloc because the JSC thread
364 // may be holding the malloc lock.
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000365 void* machineFrame;
ysuzuki@apple.com52e98bb2019-10-22 09:24:48 +0000366 CallFrame* callFrame;
sbarati@apple.com207a6832016-04-06 03:55:11 +0000367 void* machinePC;
368 bool topFrameIsLLInt = false;
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000369 RegExp* regExp = nullptr;
sbarati@apple.com207a6832016-04-06 03:55:11 +0000370 void* llintPC;
371 {
utatane.tea@gmail.com71111912017-07-19 08:43:57 +0000372 PlatformRegisters registers;
sbarati@apple.com207a6832016-04-06 03:55:11 +0000373 m_jscExecutionThread->getRegisters(registers);
utatane.tea@gmail.com71111912017-07-19 08:43:57 +0000374 machineFrame = MachineContext::framePointer(registers);
ysuzuki@apple.com52e98bb2019-10-22 09:24:48 +0000375 callFrame = static_cast<CallFrame*>(machineFrame);
sbarati@apple.com4b798292018-08-03 00:14:11 +0000376 auto instructionPointer = MachineContext::instructionPointer(registers);
377 if (instructionPointer)
378 machinePC = instructionPointer->untaggedExecutableAddress();
379 else
380 machinePC = nullptr;
mark.lam@apple.com18b4eab2018-03-31 07:04:00 +0000381 llintPC = removeCodePtrTag(MachineContext::llintInstructionPointer(registers));
382 assertIsNotTagged(machinePC);
sbarati@apple.com207a6832016-04-06 03:55:11 +0000383 }
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000384
385 bool shouldAppendTopFrameAsCCode = false;
sbarati@apple.com207a6832016-04-06 03:55:11 +0000386 // FIXME: Lets have a way of detecting when we're parsing code.
387 // https://bugs.webkit.org/show_bug.cgi?id=152761
sbarati@apple.com893729f2017-03-29 22:55:53 +0000388 if (ExecutableAllocator::singleton().isValidExecutableMemory(executableAllocatorLocker, machinePC)) {
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000389 regExp = m_vm.m_executingRegExp;
390 if (regExp)
sbarati@apple.com207a6832016-04-06 03:55:11 +0000391 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.
sbarati@apple.com207a6832016-04-06 03:55:11 +0000392 } else if (LLInt::isLLIntPC(machinePC)) {
393 topFrameIsLLInt = true;
394 // We're okay to take a normal stack trace when the PC
395 // is in LLInt code.
396 } else {
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000397 // RegExp evaluation is leaf. So if RegExp evaluation exists, we can say it is RegExp evaluation is the top user-visible frame.
398 regExp = m_vm.m_executingRegExp;
sbarati@apple.com207a6832016-04-06 03:55:11 +0000399 // We resort to topCallFrame to see if we can get anything
400 // useful. We usually get here when we're executing C code.
401 callFrame = m_vm.topCallFrame;
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000402 if (Options::collectExtraSamplingProfilerData() && !Options::sampleCCode())
403 shouldAppendTopFrameAsCCode = true;
sbarati@apple.com207a6832016-04-06 03:55:11 +0000404 }
405
406 size_t walkSize;
407 bool wasValidWalk;
408 bool didRunOutOfVectorSpace;
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000409 if (Options::sampleCCode()) {
cdumez@apple.comb261d322021-05-29 21:08:38 +0000410 CFrameWalker walker(m_vm, machineFrame, callFrame, codeBlockSetLocker, machineThreadsLocker);
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000411 walkSize = walker.walk(m_currentFrames, didRunOutOfVectorSpace);
412 wasValidWalk = walker.wasValidWalk();
413 } else {
cdumez@apple.comb261d322021-05-29 21:08:38 +0000414 FrameWalker walker(m_vm, callFrame, codeBlockSetLocker, machineThreadsLocker);
sbarati@apple.com207a6832016-04-06 03:55:11 +0000415 walkSize = walker.walk(m_currentFrames, didRunOutOfVectorSpace);
416 wasValidWalk = walker.wasValidWalk();
417 }
418
419 m_jscExecutionThread->resume();
420
utatane.tea@gmail.com5c7039d2017-12-27 00:33:11 +0000421 auto startTime = MonotonicTime::now();
sbarati@apple.com207a6832016-04-06 03:55:11 +0000422 // We can now use data structures that malloc, and do other interesting things, again.
423
424 // FIXME: It'd be interesting to take data about the program's state when
425 // we fail to take a stack trace: https://bugs.webkit.org/show_bug.cgi?id=152758
426 if (wasValidWalk && walkSize) {
427 if (sReportStats)
428 sNumTotalStackTraces++;
429 Vector<UnprocessedStackFrame> stackTrace;
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000430 stackTrace.reserveInitialCapacity(walkSize + !!shouldAppendTopFrameAsCCode);
431 if (shouldAppendTopFrameAsCCode)
432 stackTrace.uncheckedAppend(UnprocessedStackFrame { machinePC });
sbarati@apple.com207a6832016-04-06 03:55:11 +0000433 for (size_t i = 0; i < walkSize; i++) {
434 UnprocessedStackFrame frame = m_currentFrames[i];
435 stackTrace.uncheckedAppend(frame);
436 }
437
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000438 m_unprocessedStackTraces.append(UnprocessedStackTrace { nowTime, machinePC, topFrameIsLLInt, llintPC, regExp, WTFMove(stackTrace) });
sbarati@apple.com207a6832016-04-06 03:55:11 +0000439
440 if (didRunOutOfVectorSpace)
441 m_currentFrames.grow(m_currentFrames.size() * 1.25);
442 }
443
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000444 auto endTime = MonotonicTime::now();
445 stackTraceProcessingTime = endTime - startTime;
sbarati@apple.com207a6832016-04-06 03:55:11 +0000446 }
447 }
448}
449
keith_miller@apple.com0f985ec2019-10-23 00:55:38 +0000450static ALWAYS_INLINE BytecodeIndex tryGetBytecodeIndex(unsigned llintPC, CodeBlock* codeBlock)
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000451{
ossy@webkit.orgc8cea7b2016-02-18 16:45:26 +0000452#if ENABLE(DFG_JIT)
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000453 RELEASE_ASSERT(!codeBlock->hasCodeOrigins());
ossy@webkit.orgc8cea7b2016-02-18 16:45:26 +0000454#endif
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000455
keith_miller@apple.com0f985ec2019-10-23 00:55:38 +0000456 unsigned bytecodeOffset = llintPC;
457 if (bytecodeOffset < codeBlock->instructionsSize())
458 return BytecodeIndex(bytecodeOffset);
459 return BytecodeIndex();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000460}
461
cdumez@apple.com2f481692021-05-29 21:14:39 +0000462void SamplingProfiler::processUnverifiedStackTraces()
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000463{
464 // This function needs to be called from the JSC execution thread.
465 RELEASE_ASSERT(m_lock.isLocked());
466
467 TinyBloomFilter filter = m_vm.heap.objectSpace().blocks().filter();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000468
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000469 for (UnprocessedStackTrace& unprocessedStackTrace : m_unprocessedStackTraces) {
470 m_stackTraces.append(StackTrace());
471 StackTrace& stackTrace = m_stackTraces.last();
472 stackTrace.timestamp = unprocessedStackTrace.timestamp;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000473
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000474 auto populateCodeLocation = [] (CodeBlock* codeBlock, JITType jitType, BytecodeIndex bytecodeIndex, StackFrame::CodeLocation& location) {
keith_miller@apple.com0f985ec2019-10-23 00:55:38 +0000475 if (bytecodeIndex.offset() < codeBlock->instructionsSize()) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000476 int divot;
477 int startOffset;
478 int endOffset;
keith_miller@apple.com0f985ec2019-10-23 00:55:38 +0000479 codeBlock->expressionRangeForBytecodeIndex(bytecodeIndex, divot, startOffset, endOffset,
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000480 location.lineNumber, location.columnNumber);
481 location.bytecodeIndex = bytecodeIndex;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000482 }
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000483 if (codeBlock->hasHash())
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000484 location.codeBlockHash = codeBlock->hash();
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000485 location.jitType = jitType;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000486 };
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000487
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000488 auto appendCodeBlock = [&] (CodeBlock* codeBlock, JITType jitType, BytecodeIndex bytecodeIndex) {
cdumez@apple.com2f481692021-05-29 21:14:39 +0000489 assertIsHeld(m_lock);
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000490 stackTrace.frames.append(StackFrame(codeBlock->ownerExecutable()));
491 m_liveCellPointers.add(codeBlock->ownerExecutable());
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000492 populateCodeLocation(codeBlock, jitType, bytecodeIndex, stackTrace.frames.last().semanticLocation);
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000493 };
494
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000495 auto appendEmptyFrame = [&] {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000496 stackTrace.frames.append(StackFrame());
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000497 };
498
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000499 auto storeCalleeIntoLastFrame = [&] (UnprocessedStackFrame& unprocessedStackFrame) {
cdumez@apple.com2f481692021-05-29 21:14:39 +0000500 assertIsHeld(m_lock);
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000501 // Set the callee if it's a valid GC object.
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000502 CalleeBits calleeBits = unprocessedStackFrame.unverifiedCallee;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000503 StackFrame& stackFrame = stackTrace.frames.last();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000504 bool alreadyHasExecutable = !!stackFrame.executable;
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000505#if ENABLE(WEBASSEMBLY)
sbarati@apple.com5db42f82017-04-04 22:23:37 +0000506 if (calleeBits.isWasm()) {
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000507 stackFrame.frameType = FrameType::Wasm;
508 stackFrame.wasmIndexOrName = unprocessedStackFrame.wasmIndexOrName;
509 stackFrame.wasmCompilationMode = unprocessedStackFrame.wasmCompilationMode;
sbarati@apple.com5db42f82017-04-04 22:23:37 +0000510 return;
511 }
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000512#endif
sbarati@apple.com5db42f82017-04-04 22:23:37 +0000513
514 JSValue callee = calleeBits.asCell();
fpizlo@apple.combc16ddb2016-09-06 01:02:22 +0000515 if (!HeapUtil::isValueGCObject(m_vm.heap, filter, callee)) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000516 if (!alreadyHasExecutable)
517 stackFrame.frameType = FrameType::Unknown;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000518 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000519 }
520
521 JSCell* calleeCell = callee.asCell();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000522 auto setFallbackFrameType = [&] {
523 ASSERT(!alreadyHasExecutable);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000524 FrameType result = FrameType::Unknown;
ross.kirsling@sony.com924e7502020-04-27 09:09:32 +0000525 auto callData = getCallData(m_vm, calleeCell);
526 if (callData.type == CallData::Type::Native)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000527 result = FrameType::Host;
528
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000529 stackFrame.frameType = result;
530 };
531
532 auto addCallee = [&] (JSObject* callee) {
cdumez@apple.com2f481692021-05-29 21:14:39 +0000533 assertIsHeld(m_lock);
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000534 stackFrame.callee = callee;
535 m_liveCellPointers.add(callee);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000536 };
537
538 if (calleeCell->type() != JSFunctionType) {
mark.lam@apple.com5ba07792019-08-27 22:14:52 +0000539 if (JSObject* object = jsDynamicCast<JSObject*>(calleeCell->vm(), calleeCell))
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000540 addCallee(object);
541
542 if (!alreadyHasExecutable)
543 setFallbackFrameType();
544
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000545 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000546 }
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000547
548 addCallee(jsCast<JSFunction*>(calleeCell));
549
550 if (alreadyHasExecutable)
551 return;
552
553 ExecutableBase* executable = jsCast<JSFunction*>(calleeCell)->executable();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000554 if (!executable) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000555 setFallbackFrameType();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000556 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000557 }
558
fpizlo@apple.combc16ddb2016-09-06 01:02:22 +0000559 RELEASE_ASSERT(HeapUtil::isPointerGCObjectJSCell(m_vm.heap, filter, executable));
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000560 stackFrame.frameType = FrameType::Executable;
561 stackFrame.executable = executable;
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000562 m_liveCellPointers.add(executable);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000563 };
564
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000565 auto appendCodeOrigin = [&] (CodeBlock* machineCodeBlock, CodeOrigin origin) {
cdumez@apple.com2f481692021-05-29 21:14:39 +0000566 assertIsHeld(m_lock);
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000567 size_t startIndex = stackTrace.frames.size(); // We want to change stack traces that we're about to append.
568
569 CodeOrigin machineOrigin;
570 origin.walkUpInlineStack([&] (const CodeOrigin& codeOrigin) {
571 machineOrigin = codeOrigin;
rmorisset@apple.comb4811e72019-03-20 20:24:36 +0000572 auto* inlineCallFrame = codeOrigin.inlineCallFrame();
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000573 appendCodeBlock(inlineCallFrame ? inlineCallFrame->baselineCodeBlock.get() : machineCodeBlock, machineCodeBlock->jitType(), codeOrigin.bytecodeIndex());
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000574 });
575
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000576 if (Options::collectExtraSamplingProfilerData()) {
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000577 RELEASE_ASSERT(machineOrigin.isSet());
rmorisset@apple.comb4811e72019-03-20 20:24:36 +0000578 RELEASE_ASSERT(!machineOrigin.inlineCallFrame());
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000579
580 StackFrame::CodeLocation machineLocation = stackTrace.frames.last().semanticLocation;
581
582 // We want to tell each inlined frame about the machine frame
583 // they were inlined into. Currently, we only use this for dumping
584 // output on the command line, but we could extend it to the web
585 // inspector in the future if we find a need for it there.
586 RELEASE_ASSERT(stackTrace.frames.size());
sbarati@apple.com23c01192018-09-22 07:28:39 +0000587 m_liveCellPointers.add(machineCodeBlock);
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000588 for (size_t i = startIndex; i < stackTrace.frames.size() - 1; i++)
sbarati@apple.com23c01192018-09-22 07:28:39 +0000589 stackTrace.frames[i].machineLocation = std::make_pair(machineLocation, machineCodeBlock);
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000590 }
591 };
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000592
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000593 // Prepend the top-most inlined frame if needed and gather
594 // location information about where the top frame is executing.
595 size_t startIndex = 0;
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000596 if (unprocessedStackTrace.regExp) {
597 // If the stack-trace is annotated with RegExp, the top-frame must be RegExp since RegExp evaluation is leaf function.
598 appendEmptyFrame();
599 stackTrace.frames.last().regExp = unprocessedStackTrace.regExp;
600 stackTrace.frames.last().frameType = FrameType::RegExp;
601 stackTrace.frames.last().semanticLocation.isRegExp = true;
602 m_liveCellPointers.add(unprocessedStackTrace.regExp);
603 } else if (!unprocessedStackTrace.frames.isEmpty() && !!unprocessedStackTrace.frames[0].verifiedCodeBlock) {
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000604 CodeBlock* topCodeBlock = unprocessedStackTrace.frames[0].verifiedCodeBlock;
605 if (unprocessedStackTrace.topFrameIsLLInt) {
606 // We reuse LLInt CodeBlocks for the baseline JIT, so we need to check for both jit types.
607 // This might also be false for various reasons (known and unknown), even though
608 // it's super unlikely. One reason that this can be false is when we throw from a DFG frame,
609 // and we end up having to unwind past an EntryFrame, we will end up executing
ysuzuki@apple.com9549c072020-10-06 22:04:36 +0000610 // inside the LLInt's llint_handle_ucaught_exception. So we just protect against this
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000611 // by ignoring it.
keith_miller@apple.com0f985ec2019-10-23 00:55:38 +0000612 BytecodeIndex bytecodeIndex = BytecodeIndex(0);
sbarati@apple.com9777f7c2019-04-30 03:27:39 +0000613 if (topCodeBlock->jitType() == JITType::InterpreterThunk || topCodeBlock->jitType() == JITType::BaselineJIT) {
ysuzuki@apple.com5eb59652020-01-22 20:31:33 +0000614 unsigned bits = static_cast<unsigned>(bitwise_cast<uintptr_t>(unprocessedStackTrace.llintPC));
keith_miller@apple.com0f985ec2019-10-23 00:55:38 +0000615 bytecodeIndex = tryGetBytecodeIndex(bits, topCodeBlock);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000616
keith_miller@apple.com0f985ec2019-10-23 00:55:38 +0000617 UNUSED_PARAM(bytecodeIndex); // FIXME: do something with this info for the web inspector: https://bugs.webkit.org/show_bug.cgi?id=153455
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000618
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000619 appendCodeBlock(topCodeBlock, topCodeBlock->jitType(), bytecodeIndex);
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000620 storeCalleeIntoLastFrame(unprocessedStackTrace.frames[0]);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000621 startIndex = 1;
622 }
yusukesuzuki@slowstart.org48bd0a02018-09-22 05:26:44 +0000623 } else {
624#if ENABLE(JIT)
darin@apple.coma4ddc782021-05-30 16:11:40 +0000625 if (std::optional<CodeOrigin> codeOrigin = topCodeBlock->findPC(unprocessedStackTrace.topPC)) {
yusukesuzuki@slowstart.org48bd0a02018-09-22 05:26:44 +0000626 appendCodeOrigin(topCodeBlock, *codeOrigin);
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000627 storeCalleeIntoLastFrame(unprocessedStackTrace.frames[0]);
yusukesuzuki@slowstart.org48bd0a02018-09-22 05:26:44 +0000628 startIndex = 1;
629 }
630#endif
631 UNUSED_PARAM(appendCodeOrigin);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000632 }
633 }
634
635 for (size_t i = startIndex; i < unprocessedStackTrace.frames.size(); i++) {
636 UnprocessedStackFrame& unprocessedStackFrame = unprocessedStackTrace.frames[i];
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000637 if (CodeBlock* codeBlock = unprocessedStackFrame.verifiedCodeBlock) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000638 CallSiteIndex callSiteIndex = unprocessedStackFrame.callSiteIndex;
639
640 auto appendCodeBlockNoInlining = [&] {
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000641 appendCodeBlock(codeBlock, codeBlock->jitType(), tryGetBytecodeIndex(callSiteIndex.bits(), codeBlock));
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000642 };
643
644#if ENABLE(DFG_JIT)
645 if (codeBlock->hasCodeOrigins()) {
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000646 if (codeBlock->canGetCodeOrigin(callSiteIndex))
647 appendCodeOrigin(codeBlock, codeBlock->codeOrigin(callSiteIndex));
648 else
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000649 appendCodeBlock(codeBlock, codeBlock->jitType(), BytecodeIndex());
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000650 } else
651 appendCodeBlockNoInlining();
652#else
653 appendCodeBlockNoInlining();
654#endif
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000655 } else if (unprocessedStackFrame.cCodePC) {
656 appendEmptyFrame();
657 stackTrace.frames.last().cCodePC = unprocessedStackFrame.cCodePC;
658 stackTrace.frames.last().frameType = FrameType::C;
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000659 } else
660 appendEmptyFrame();
661
662 // Note that this is okay to do if we walked the inline stack because
663 // the machine frame will be at the top of the processed stack trace.
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000664 if (!unprocessedStackFrame.cCodePC)
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000665 storeCalleeIntoLastFrame(unprocessedStackFrame);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000666 }
667 }
668
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000669 m_unprocessedStackTraces.clear();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000670}
671
mark.lam@apple.com17ae4902021-02-19 15:51:15 +0000672template<typename Visitor>
673void SamplingProfiler::visit(Visitor& visitor)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000674{
675 RELEASE_ASSERT(m_lock.isLocked());
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000676 for (JSCell* cell : m_liveCellPointers)
mark.lam@apple.com15f620c2021-02-13 05:30:58 +0000677 visitor.appendUnbarriered(cell);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000678}
679
mark.lam@apple.com17ae4902021-02-19 15:51:15 +0000680template void SamplingProfiler::visit(AbstractSlotVisitor&);
681template void SamplingProfiler::visit(SlotVisitor&);
682
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000683void SamplingProfiler::shutdown()
684{
cdumez@apple.com3e035a82021-05-22 16:49:42 +0000685 Locker locker { m_lock };
sbarati@apple.com207a6832016-04-06 03:55:11 +0000686 m_isShutDown = true;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000687}
688
689void SamplingProfiler::start()
690{
cdumez@apple.com3e035a82021-05-22 16:49:42 +0000691 Locker locker { m_lock };
cdumez@apple.com2f481692021-05-29 21:14:39 +0000692 startWithLock();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000693}
694
cdumez@apple.com2f481692021-05-29 21:14:39 +0000695void SamplingProfiler::startWithLock()
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000696{
697 ASSERT(m_lock.isLocked());
sbarati@apple.com207a6832016-04-06 03:55:11 +0000698 m_isPaused = false;
cdumez@apple.com2f481692021-05-29 21:14:39 +0000699 createThreadIfNecessary();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000700}
701
cdumez@apple.com2f481692021-05-29 21:14:39 +0000702void SamplingProfiler::pause()
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000703{
704 ASSERT(m_lock.isLocked());
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000705 m_isPaused = true;
706 reportStats();
707}
708
cdumez@apple.com2f481692021-05-29 21:14:39 +0000709void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThreadWithLock()
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000710{
711 ASSERT(m_lock.isLocked());
utatane.tea@gmail.com71111912017-07-19 08:43:57 +0000712 m_jscExecutionThread = &Thread::current();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000713}
714
715void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread()
716{
cdumez@apple.com3e035a82021-05-22 16:49:42 +0000717 Locker locker { m_lock };
cdumez@apple.com2f481692021-05-29 21:14:39 +0000718 noticeCurrentThreadAsJSCExecutionThreadWithLock();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000719}
720
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000721void SamplingProfiler::noticeJSLockAcquisition()
722{
cdumez@apple.com3e035a82021-05-22 16:49:42 +0000723 Locker locker { m_lock };
cdumez@apple.com2f481692021-05-29 21:14:39 +0000724 noticeCurrentThreadAsJSCExecutionThreadWithLock();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000725}
726
727void SamplingProfiler::noticeVMEntry()
728{
cdumez@apple.com3e035a82021-05-22 16:49:42 +0000729 Locker locker { m_lock };
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000730 ASSERT(m_vm.entryScope);
cdumez@apple.com2f481692021-05-29 21:14:39 +0000731 noticeCurrentThreadAsJSCExecutionThreadWithLock();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000732 m_lastTime = m_stopwatch->elapsedTime();
cdumez@apple.com2f481692021-05-29 21:14:39 +0000733 createThreadIfNecessary();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000734}
735
cdumez@apple.com2f481692021-05-29 21:14:39 +0000736void SamplingProfiler::clearData()
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000737{
738 ASSERT(m_lock.isLocked());
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000739 m_stackTraces.clear();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000740 m_liveCellPointers.clear();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000741 m_unprocessedStackTraces.clear();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000742}
743
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000744String SamplingProfiler::StackFrame::nameFromCallee(VM& vm)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000745{
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000746 if (!callee) {
747 if (regExp)
748 return regExp->toSourceString();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000749 return String();
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000750 }
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000751
mark.lam@apple.come2166a82021-04-28 17:13:40 +0000752 DeferTermination deferScope(vm);
mark.lam@apple.com09f92b82016-09-27 20:32:13 +0000753 auto scope = DECLARE_CATCH_SCOPE(vm);
ysuzuki@apple.com52e98bb2019-10-22 09:24:48 +0000754 JSGlobalObject* globalObject = callee->globalObject(vm);
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000755 auto getPropertyIfPureOperation = [&] (const Identifier& ident) -> String {
mark.lam@apple.com97021a82020-07-23 00:08:50 +0000756 PropertySlot slot(callee, PropertySlot::InternalMethodType::VMInquiry, &vm);
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000757 PropertyName propertyName(ident);
ysuzuki@apple.com52e98bb2019-10-22 09:24:48 +0000758 bool hasProperty = callee->getPropertySlot(globalObject, propertyName, slot);
mark.lam@apple.comcce76562017-05-08 16:56:32 +0000759 scope.assertNoException();
mark.lam@apple.com09f92b82016-09-27 20:32:13 +0000760 if (hasProperty) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000761 if (slot.isValue()) {
ysuzuki@apple.com52e98bb2019-10-22 09:24:48 +0000762 JSValue nameValue = slot.getValue(globalObject, propertyName);
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000763 if (isJSString(nameValue))
764 return asString(nameValue)->tryGetValue();
765 }
766 }
767 return String();
768 };
769
770 String name = getPropertyIfPureOperation(vm.propertyNames->displayName);
771 if (!name.isEmpty())
772 return name;
773
774 return getPropertyIfPureOperation(vm.propertyNames->name);
775}
776
777String SamplingProfiler::StackFrame::displayName(VM& vm)
778{
779 {
780 String name = nameFromCallee(vm);
781 if (!name.isEmpty())
782 return name;
783 }
784
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000785 switch (frameType) {
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000786 case FrameType::C:
utatane.tea@gmail.com57950612017-04-25 02:53:49 +0000787#if HAVE(DLADDR)
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000788 if (frameType == FrameType::C) {
tzagallo@apple.com3474dd02018-10-29 13:16:03 +0000789 auto demangled = WTF::StackTrace::demangle(const_cast<void*>(cCodePC));
utatane.tea@gmail.com57950612017-04-25 02:53:49 +0000790 if (demangled)
791 return String(demangled->demangledName() ? demangled->demangledName() : demangled->mangledName());
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000792 WTF::dataLog("couldn't get a name");
793 }
794#endif
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000795 return "(unknown C PC)"_s;
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000796
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000797 case FrameType::Unknown:
utatane.tea@gmail.com84077632018-06-23 08:39:34 +0000798 return "(unknown)"_s;
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000799
800 case FrameType::Host:
utatane.tea@gmail.com84077632018-06-23 08:39:34 +0000801 return "(host)"_s;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000802
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000803 case FrameType::RegExp:
804 return "(regexp)"_s;
805
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000806 case FrameType::Wasm:
807#if ENABLE(WEBASSEMBLY)
808 if (wasmIndexOrName)
809 return makeString(wasmIndexOrName.value());
810#endif
811 return "(wasm)"_s;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000812
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000813 case FrameType::Executable:
814 if (executable->isHostFunction())
815 return static_cast<NativeExecutable*>(executable)->name();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000816
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000817 if (executable->isFunctionExecutable())
818 return static_cast<FunctionExecutable*>(executable)->ecmaName().string();
819 if (executable->isProgramExecutable() || executable->isEvalExecutable())
820 return "(program)"_s;
821 if (executable->isModuleProgramExecutable())
822 return "(module)"_s;
823
824 RELEASE_ASSERT_NOT_REACHED();
825 return String();
826 }
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000827 RELEASE_ASSERT_NOT_REACHED();
828 return String();
829}
830
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000831String SamplingProfiler::StackFrame::displayNameForJSONTests(VM& vm)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000832{
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000833 {
834 String name = nameFromCallee(vm);
835 if (!name.isEmpty())
836 return name;
837 }
838
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000839 switch (frameType) {
840 case FrameType::Unknown:
841 case FrameType::C:
utatane.tea@gmail.com84077632018-06-23 08:39:34 +0000842 return "(unknown)"_s;
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000843
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000844 case FrameType::RegExp:
845 return "(regexp)"_s;
846
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000847 case FrameType::Host:
utatane.tea@gmail.com84077632018-06-23 08:39:34 +0000848 return "(host)"_s;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000849
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000850 case FrameType::Wasm: {
851#if ENABLE(WEBASSEMBLY)
852 if (wasmIndexOrName)
853 return makeString(wasmIndexOrName.value());
854#endif
855 return "(wasm)"_s;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000856 }
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000857
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000858 case FrameType::Executable:
859 if (executable->isHostFunction())
860 return static_cast<NativeExecutable*>(executable)->name();
861
862 if (executable->isFunctionExecutable()) {
863 String result = static_cast<FunctionExecutable*>(executable)->ecmaName().string();
864 if (result.isEmpty())
865 return "(anonymous function)"_s;
866 return result;
867 }
868 if (executable->isEvalExecutable())
869 return "(eval)"_s;
870 if (executable->isProgramExecutable())
871 return "(program)"_s;
872 if (executable->isModuleProgramExecutable())
873 return "(module)"_s;
874
875 RELEASE_ASSERT_NOT_REACHED();
876 return String();
877 }
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000878 RELEASE_ASSERT_NOT_REACHED();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000879 return String();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000880}
881
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000882int SamplingProfiler::StackFrame::functionStartLine()
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000883{
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000884 switch (frameType) {
885 case FrameType::Unknown:
886 case FrameType::Host:
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000887 case FrameType::RegExp:
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000888 case FrameType::C:
889 case FrameType::Wasm:
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000890 return -1;
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000891
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000892 case FrameType::Executable:
893 if (executable->isHostFunction())
894 return -1;
895 return static_cast<ScriptExecutable*>(executable)->firstLine();
896 }
897 RELEASE_ASSERT_NOT_REACHED();
898 return -1;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000899}
900
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000901unsigned SamplingProfiler::StackFrame::functionStartColumn()
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000902{
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000903 switch (frameType) {
904 case FrameType::Unknown:
905 case FrameType::Host:
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000906 case FrameType::RegExp:
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000907 case FrameType::C:
908 case FrameType::Wasm:
peavo@outlook.coma83d48a2016-05-02 21:20:15 +0000909 return std::numeric_limits<unsigned>::max();
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000910
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000911 case FrameType::Executable:
912 if (executable->isHostFunction())
913 return std::numeric_limits<unsigned>::max();
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000914
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000915 return static_cast<ScriptExecutable*>(executable)->startColumn();
916 }
917 RELEASE_ASSERT_NOT_REACHED();
918 return std::numeric_limits<unsigned>::max();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000919}
920
keith_miller@apple.comc493f8e02021-10-11 16:58:40 +0000921SourceID SamplingProfiler::StackFrame::sourceID()
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000922{
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000923 switch (frameType) {
924 case FrameType::Unknown:
925 case FrameType::Host:
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000926 case FrameType::RegExp:
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000927 case FrameType::C:
928 case FrameType::Wasm:
ysuzuki@apple.com9f19c432021-07-15 18:29:47 +0000929 return internalSourceID;
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000930
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000931 case FrameType::Executable:
932 if (executable->isHostFunction())
ysuzuki@apple.com9f19c432021-07-15 18:29:47 +0000933 return internalSourceID;
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000934
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000935 return static_cast<ScriptExecutable*>(executable)->sourceID();
936 }
937 RELEASE_ASSERT_NOT_REACHED();
ysuzuki@apple.com9f19c432021-07-15 18:29:47 +0000938 return internalSourceID;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000939}
940
941String SamplingProfiler::StackFrame::url()
942{
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000943 switch (frameType) {
944 case FrameType::Unknown:
945 case FrameType::Host:
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000946 case FrameType::RegExp:
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000947 case FrameType::C:
948 case FrameType::Wasm:
utatane.tea@gmail.com988a3752018-01-29 10:43:13 +0000949 return emptyString();
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000950 case FrameType::Executable:
951 if (executable->isHostFunction())
952 return emptyString();
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000953
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000954 String url = static_cast<ScriptExecutable*>(executable)->sourceURL();
955 if (url.isEmpty())
956 return static_cast<ScriptExecutable*>(executable)->source().provider()->sourceURLDirective(); // Fall back to sourceURL directive.
957 return url;
958 }
959 RELEASE_ASSERT_NOT_REACHED();
960 return String();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000961}
962
cdumez@apple.com2f481692021-05-29 21:14:39 +0000963Vector<SamplingProfiler::StackTrace> SamplingProfiler::releaseStackTraces()
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000964{
965 ASSERT(m_lock.isLocked());
966 {
967 HeapIterationScope heapIterationScope(m_vm.heap);
cdumez@apple.com2f481692021-05-29 21:14:39 +0000968 processUnverifiedStackTraces();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000969 }
970
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000971 Vector<StackTrace> result(WTFMove(m_stackTraces));
cdumez@apple.com2f481692021-05-29 21:14:39 +0000972 clearData();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000973 return result;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000974}
975
976String SamplingProfiler::stackTracesAsJSON()
977{
fpizlo@apple.com0ef43952016-12-08 22:14:50 +0000978 DeferGC deferGC(m_vm.heap);
cdumez@apple.com6c3db6c2021-05-22 03:13:17 +0000979 Locker locker { m_lock };
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000980
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000981 {
982 HeapIterationScope heapIterationScope(m_vm.heap);
cdumez@apple.com2f481692021-05-29 21:14:39 +0000983 processUnverifiedStackTraces();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000984 }
985
986 StringBuilder json;
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000987 json.append('[');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000988
989 bool loopedOnce = false;
990 auto comma = [&] {
991 if (loopedOnce)
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000992 json.append(',');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000993 };
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000994 for (StackTrace& stackTrace : m_stackTraces) {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000995 comma();
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +0000996 json.append('[');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000997 loopedOnce = false;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000998 for (StackFrame& stackFrame : stackTrace.frames) {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000999 comma();
mark.lam@apple.comf0515572019-02-15 21:41:15 +00001000 json.appendQuotedJSONString(stackFrame.displayNameForJSONTests(m_vm));
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001001 loopedOnce = true;
1002 }
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +00001003 json.append(']');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001004 loopedOnce = true;
1005 }
1006
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +00001007 json.append(']');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001008
cdumez@apple.com2f481692021-05-29 21:14:39 +00001009 clearData();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +00001010
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001011 return json.toString();
1012}
1013
keith_miller@apple.com3517aa42016-05-24 23:03:09 +00001014void SamplingProfiler::registerForReportAtExit()
1015{
utatane.tea@gmail.come71a8722018-04-05 17:22:21 +00001016 static Lock registrationLock;
keith_miller@apple.com3517aa42016-05-24 23:03:09 +00001017 static HashSet<RefPtr<SamplingProfiler>>* profilesToReport;
1018
cdumez@apple.com3e035a82021-05-22 16:49:42 +00001019 Locker locker { registrationLock };
keith_miller@apple.com3517aa42016-05-24 23:03:09 +00001020
1021 if (!profilesToReport) {
1022 profilesToReport = new HashSet<RefPtr<SamplingProfiler>>();
1023 atexit([]() {
ddkilzer@apple.comfa7f50c2019-09-21 11:39:57 +00001024 for (const auto& profile : *profilesToReport)
keith_miller@apple.com3517aa42016-05-24 23:03:09 +00001025 profile->reportDataToOptionFile();
1026 });
1027 }
1028
1029 profilesToReport->add(adoptRef(this));
1030 m_needsReportAtExit = true;
1031}
1032
1033void SamplingProfiler::reportDataToOptionFile()
1034{
1035 if (m_needsReportAtExit) {
1036 m_needsReportAtExit = false;
tzagallo@apple.coma4c49222019-09-16 20:48:37 +00001037 JSLockHolder holder(m_vm);
keith_miller@apple.com3517aa42016-05-24 23:03:09 +00001038 const char* path = Options::samplingProfilerPath();
1039 StringPrintStream pathOut;
1040 pathOut.print(path, "/");
1041 pathOut.print("JSCSampilingProfile-", reinterpret_cast<uintptr_t>(this), ".txt");
1042 auto out = FilePrintStream::open(pathOut.toCString().data(), "w");
1043 reportTopFunctions(*out);
1044 reportTopBytecodes(*out);
1045 }
1046}
1047
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001048void SamplingProfiler::reportTopFunctions()
1049{
keith_miller@apple.com3517aa42016-05-24 23:03:09 +00001050 reportTopFunctions(WTF::dataFile());
1051}
1052
1053void SamplingProfiler::reportTopFunctions(PrintStream& out)
1054{
cdumez@apple.com6c3db6c2021-05-22 03:13:17 +00001055 Locker locker { m_lock };
sbarati@apple.com23c01192018-09-22 07:28:39 +00001056 DeferGCForAWhile deferGC(m_vm.heap);
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001057
1058 {
1059 HeapIterationScope heapIterationScope(m_vm.heap);
cdumez@apple.com2f481692021-05-29 21:14:39 +00001060 processUnverifiedStackTraces();
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001061 }
1062
keith_miller@apple.com5eaa04d2020-11-09 21:18:08 +00001063 size_t totalSamples = 0;
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001064 HashMap<String, size_t> functionCounts;
1065 for (StackTrace& stackTrace : m_stackTraces) {
1066 if (!stackTrace.frames.size())
1067 continue;
1068
1069 StackFrame& frame = stackTrace.frames.first();
sbarati@apple.comc7ad3d12020-09-03 22:47:33 +00001070 String hash = ""_s;
1071 if (frame.semanticLocation.hasCodeBlockHash()) {
1072 StringPrintStream stream;
1073 frame.semanticLocation.codeBlockHash.dump(stream);
1074 hash = stream.toString();
1075 } else
1076 hash = "<nil>"_s;
ysuzuki@apple.com9f19c432021-07-15 18:29:47 +00001077 intptr_t sourceID = frame.sourceID();
1078 if (Options::samplingProfilerIgnoreExternalSourceID()) {
1079 if (sourceID != internalSourceID)
1080 sourceID = aggregatedExternalSourceID;
1081 }
1082 auto frameDescription = makeString(frame.displayName(m_vm), '#', hash, ':', sourceID);
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001083 functionCounts.add(frameDescription, 0).iterator->value++;
keith_miller@apple.com5eaa04d2020-11-09 21:18:08 +00001084 totalSamples++;
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001085 }
1086
1087 auto takeMax = [&] () -> std::pair<String, size_t> {
1088 String maxFrameDescription;
1089 size_t maxFrameCount = 0;
ddkilzer@apple.comfa7f50c2019-09-21 11:39:57 +00001090 for (const auto& entry : functionCounts) {
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001091 if (entry.value > maxFrameCount) {
1092 maxFrameCount = entry.value;
1093 maxFrameDescription = entry.key;
1094 }
1095 }
1096 if (!maxFrameDescription.isEmpty())
1097 functionCounts.remove(maxFrameDescription);
1098 return std::make_pair(maxFrameDescription, maxFrameCount);
1099 };
1100
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001101 if (Options::samplingProfilerTopFunctionsCount()) {
keith_miller@apple.com5eaa04d2020-11-09 21:18:08 +00001102 out.println("\n\nSampling rate: ", m_timingInterval.microseconds(), " microseconds. Total samples: ", totalSamples);
1103 out.println("Top functions as <numSamples 'functionName#hash:sourceID'>");
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001104 for (size_t i = 0; i < Options::samplingProfilerTopFunctionsCount(); i++) {
1105 auto pair = takeMax();
1106 if (pair.first.isEmpty())
1107 break;
1108 out.printf("%6zu ", pair.second);
keith_miller@apple.com5eaa04d2020-11-09 21:18:08 +00001109 out.println(" '", pair.first, "'");
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001110 }
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001111 }
1112}
1113
1114void SamplingProfiler::reportTopBytecodes()
1115{
keith_miller@apple.com3517aa42016-05-24 23:03:09 +00001116 reportTopBytecodes(WTF::dataFile());
1117}
1118
1119void SamplingProfiler::reportTopBytecodes(PrintStream& out)
1120{
cdumez@apple.com6c3db6c2021-05-22 03:13:17 +00001121 Locker locker { m_lock };
sbarati@apple.com23c01192018-09-22 07:28:39 +00001122 DeferGCForAWhile deferGC(m_vm.heap);
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001123
1124 {
1125 HeapIterationScope heapIterationScope(m_vm.heap);
cdumez@apple.com2f481692021-05-29 21:14:39 +00001126 processUnverifiedStackTraces();
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001127 }
1128
keith_miller@apple.com5eaa04d2020-11-09 21:18:08 +00001129 size_t totalSamples = 0;
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001130 HashMap<String, size_t> bytecodeCounts;
sbarati@apple.com783a68c2021-04-29 17:42:13 +00001131 HashMap<String, size_t> tierCounts;
1132
1133 String llint = "LLInt"_s;
1134 String baseline = "Baseline"_s;
1135 String dfg = "DFG"_s;
1136 String ftl = "FTL"_s;
1137 String builtin = "js builtin"_s;
1138 String wasm = "Wasm"_s;
1139 String host = "Host"_s;
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +00001140 String regexp = "RegExp"_s;
sbarati@apple.com783a68c2021-04-29 17:42:13 +00001141 String cpp = "C/C++"_s;
1142 String unknownFrame = "Unknown Frame"_s;
1143 String unknownExecutable = "Unknown Executable"_s;
1144
1145 auto forEachTier = [&] (auto func) {
1146 func(llint);
1147 func(baseline);
1148 func(dfg);
1149 func(ftl);
1150 func(builtin);
1151 func(wasm);
1152 func(host);
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +00001153 func(regexp);
sbarati@apple.com783a68c2021-04-29 17:42:13 +00001154 func(cpp);
1155 func(unknownFrame);
1156 func(unknownExecutable);
1157 };
1158
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001159 for (StackTrace& stackTrace : m_stackTraces) {
1160 if (!stackTrace.frames.size())
1161 continue;
1162
darin@apple.coma4ddc782021-05-30 16:11:40 +00001163 auto descriptionForLocation = [&] (StackFrame::CodeLocation location, std::optional<Wasm::CompilationMode> wasmCompilationMode) -> String {
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001164 String bytecodeIndex;
1165 String codeBlockHash;
ysuzuki@apple.com32e43052019-08-02 22:58:09 +00001166 String jitType;
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001167 if (location.hasBytecodeIndex())
keith_miller@apple.com0f985ec2019-10-23 00:55:38 +00001168 bytecodeIndex = toString(location.bytecodeIndex);
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001169 else
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +00001170 bytecodeIndex = "<nil>"_s;
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001171
1172 if (location.hasCodeBlockHash()) {
1173 StringPrintStream stream;
1174 location.codeBlockHash.dump(stream);
1175 codeBlockHash = stream.toString();
1176 } else
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +00001177 codeBlockHash = "<nil>"_s;
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001178
ysuzuki@apple.com32e43052019-08-02 22:58:09 +00001179 if (wasmCompilationMode)
1180 jitType = Wasm::makeString(wasmCompilationMode.value());
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +00001181 else if (location.isRegExp)
1182 jitType = "RegExp"_s;
ysuzuki@apple.com32e43052019-08-02 22:58:09 +00001183 else
1184 jitType = JITCode::typeName(location.jitType);
1185
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +00001186 return makeString('#', codeBlockHash, ':', jitType, ':', bytecodeIndex);
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001187 };
1188
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001189 StackFrame& frame = stackTrace.frames.first();
darin@apple.come1c00f72021-05-24 21:51:47 +00001190 auto frameDescription = makeString(frame.displayName(m_vm), descriptionForLocation(frame.semanticLocation, frame.wasmCompilationMode));
darin@apple.coma4ddc782021-05-30 16:11:40 +00001191 if (std::optional<std::pair<StackFrame::CodeLocation, CodeBlock*>> machineLocation = frame.machineLocation) {
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001192 frameDescription = makeString(frameDescription, " <-- ",
darin@apple.com7c840b62021-05-28 01:26:23 +00001193 machineLocation->second->inferredName().data(), descriptionForLocation(machineLocation->first, std::nullopt));
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001194 }
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001195 bytecodeCounts.add(frameDescription, 0).iterator->value++;
sbarati@apple.com783a68c2021-04-29 17:42:13 +00001196
1197 {
1198 String tierName;
1199 switch (frame.frameType) {
1200 case SamplingProfiler::FrameType::Executable:
1201 switch (frame.semanticLocation.jitType) {
1202 case JITType::HostCallThunk:
1203 tierName = host;
1204 break;
1205 case JITType::InterpreterThunk:
1206 tierName = llint;
1207 break;
1208 case JITType::BaselineJIT:
1209 tierName = baseline;
1210 break;
1211 case JITType::DFGJIT:
1212 tierName = dfg;
1213 break;
1214 case JITType::FTLJIT:
1215 tierName = ftl;
1216 break;
1217 default:
1218 tierName = unknownExecutable;
1219 break;
1220 }
1221
1222 if (frame.executable) {
1223 if (auto* executable = jsDynamicCast<FunctionExecutable*>(m_vm, frame.executable)) {
1224 if (executable->isBuiltinFunction())
1225 tierCounts.add(builtin, 0).iterator->value++;
1226 }
1227 }
1228
1229 break;
1230 case SamplingProfiler::FrameType::Wasm:
1231 tierName = wasm;
1232 break;
1233 case SamplingProfiler::FrameType::Host:
1234 tierName = host;
1235 break;
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +00001236 case SamplingProfiler::FrameType::RegExp:
1237 tierName = regexp;
1238 break;
sbarati@apple.com783a68c2021-04-29 17:42:13 +00001239 case SamplingProfiler::FrameType::C:
1240 tierName = cpp;
1241 break;
1242 case SamplingProfiler::FrameType::Unknown:
1243 tierName = unknownFrame;
1244 break;
1245 }
1246
1247 tierCounts.add(tierName, 0).iterator->value++;
1248 }
1249
keith_miller@apple.com5eaa04d2020-11-09 21:18:08 +00001250 totalSamples++;
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001251 }
1252
1253 auto takeMax = [&] () -> std::pair<String, size_t> {
1254 String maxFrameDescription;
1255 size_t maxFrameCount = 0;
ddkilzer@apple.comfa7f50c2019-09-21 11:39:57 +00001256 for (const auto& entry : bytecodeCounts) {
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001257 if (entry.value > maxFrameCount) {
1258 maxFrameCount = entry.value;
1259 maxFrameDescription = entry.key;
1260 }
1261 }
1262 if (!maxFrameDescription.isEmpty())
1263 bytecodeCounts.remove(maxFrameDescription);
1264 return std::make_pair(maxFrameDescription, maxFrameCount);
1265 };
1266
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001267 if (Options::samplingProfilerTopBytecodesCount()) {
sbarati@apple.com783a68c2021-04-29 17:42:13 +00001268 out.println("\n\nSampling rate: ", m_timingInterval.microseconds(), " microseconds. Total samples: ", totalSamples, "\n");
1269
1270 out.println("Tier breakdown:");
1271 out.println("-----------------------------------");
1272 unsigned maxTierNameLength = 0;
1273 forEachTier([&] (String tier) {
1274 maxTierNameLength = std::max(maxTierNameLength, tier.length());
1275 });
1276 auto printTier = [&] (String tier) {
1277 size_t count = tierCounts.get(tier);
1278 if (!count && (tier == unknownFrame || tier == unknownExecutable))
1279 return;
1280 out.print(tier, ": ");
1281 for (unsigned i = 0; i < maxTierNameLength + 2 - tier.length(); ++i)
1282 out.print(" ");
1283 out.printf("%6zu ", count);
1284 out.println(" (", (static_cast<double>(count) / static_cast<double>(totalSamples)) * 100, "%)");
1285 };
1286 forEachTier(printTier);
1287 out.println("\n");
1288
keith_miller@apple.com5eaa04d2020-11-09 21:18:08 +00001289 out.println("Hottest bytecodes as <numSamples 'functionName#hash:JITType:bytecodeIndex'>");
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001290 for (size_t i = 0; i < Options::samplingProfilerTopBytecodesCount(); i++) {
1291 auto pair = takeMax();
1292 if (pair.first.isEmpty())
1293 break;
1294 out.printf("%6zu ", pair.second);
keith_miller@apple.com5eaa04d2020-11-09 21:18:08 +00001295 out.println(" '", pair.first, "'");
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001296 }
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001297 }
1298}
1299
carlosgc@webkit.org12f14832020-09-01 08:55:06 +00001300Thread* SamplingProfiler::thread() const
commit-queue@webkit.orgb181ba92019-01-25 23:45:04 +00001301{
carlosgc@webkit.org12f14832020-09-01 08:55:06 +00001302 return m_thread.get();
commit-queue@webkit.orgb181ba92019-01-25 23:45:04 +00001303}
commit-queue@webkit.orgb181ba92019-01-25 23:45:04 +00001304
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001305} // namespace JSC
1306
1307namespace WTF {
1308
1309using namespace JSC;
1310
1311void printInternal(PrintStream& out, SamplingProfiler::FrameType frameType)
1312{
1313 switch (frameType) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +00001314 case SamplingProfiler::FrameType::Executable:
1315 out.print("Executable");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001316 break;
ysuzuki@apple.com32e43052019-08-02 22:58:09 +00001317 case SamplingProfiler::FrameType::Wasm:
1318 out.print("Wasm");
1319 break;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001320 case SamplingProfiler::FrameType::Host:
1321 out.print("Host");
1322 break;
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +00001323 case SamplingProfiler::FrameType::RegExp:
1324 out.print("RegExp");
1325 break;
keith_miller@apple.comb4a86442017-02-02 01:23:37 +00001326 case SamplingProfiler::FrameType::C:
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001327 case SamplingProfiler::FrameType::Unknown:
1328 out.print("Unknown");
1329 break;
1330 }
1331}
1332
1333} // namespace WTF
1334
1335#endif // ENABLE(SAMPLING_PROFILER)