blob: fb0ddde79f45f4aeeaff2cb6366067be3a82f4fc [file] [log] [blame]
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001/*
mark.lam@apple.combe73dca2022-06-19 04:07:37 +00002 * Copyright (C) 2016-2022 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();
sbarati@apple.comf4eb0222021-12-12 00:39:57 +0000133#if ENABLE(JIT)
134 stackTrace[m_depth].wasmPCMap = Wasm::CalleeRegistry::singleton().codeOriginMap(wasmCallee);
135#endif
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000136 }
137 }
138#endif
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000139 m_depth++;
140 }
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000141
sbarati@apple.com30cd1162016-02-11 21:04:43 +0000142 SUPPRESS_ASAN
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000143 void advanceToParentFrame()
144 {
jfbastien@apple.comd9f999e2017-10-20 02:23:29 +0000145 m_callFrame = m_callFrame->unsafeCallerFrame(m_entryFrame);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000146 }
147
148 bool isAtTop() const
149 {
150 return !m_callFrame;
151 }
152
sbarati@apple.com30cd1162016-02-11 21:04:43 +0000153 SUPPRESS_ASAN
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000154 void resetAtMachineFrame()
155 {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000156 if (isAtTop())
157 return;
158
159 if (!isValidFramePointer(m_callFrame)) {
160 // Guard against pausing the process at weird program points.
161 m_bailingOut = true;
162 if (sReportStats)
163 sNumFailedWalks++;
164 return;
165 }
166
sbarati@apple.com88a5fd62016-02-16 22:01:37 +0000167 CodeBlock* codeBlock = m_callFrame->unsafeCodeBlock();
mark.lam@apple.comcadee712019-11-04 19:23:24 +0000168 if (!codeBlock || m_callFrame->unsafeCallee().isWasm())
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000169 return;
170
171 if (!isValidCodeBlock(codeBlock)) {
172 m_bailingOut = true;
173 if (sReportStats)
174 sNumFailedWalks++;
175 return;
176 }
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000177 }
178
ysuzuki@apple.com52e98bb2019-10-22 09:24:48 +0000179 bool isValidFramePointer(void* callFrame)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000180 {
ysuzuki@apple.com52e98bb2019-10-22 09:24:48 +0000181 uint8_t* fpCast = bitwise_cast<uint8_t*>(callFrame);
utatane.tea@gmail.com71111912017-07-19 08:43:57 +0000182 for (auto& thread : m_vm.heap.machineThreads().threads(m_machineThreadsLocker)) {
183 uint8_t* stackBase = static_cast<uint8_t*>(thread->stack().origin());
184 uint8_t* stackLimit = static_cast<uint8_t*>(thread->stack().end());
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000185 RELEASE_ASSERT(stackBase);
186 RELEASE_ASSERT(stackLimit);
mark.lam@apple.com8a3e3372018-12-18 01:21:07 +0000187 RELEASE_ASSERT(stackLimit <= stackBase);
188 if (fpCast < stackBase && fpCast >= stackLimit)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000189 return true;
190 }
191 return false;
192 }
193
194 bool isValidCodeBlock(CodeBlock* codeBlock)
195 {
196 if (!codeBlock)
197 return false;
198 bool result = m_vm.heap.codeBlockSet().contains(m_codeBlockSetLocker, codeBlock);
199 return result;
200 }
201
202 VM& m_vm;
ysuzuki@apple.com52e98bb2019-10-22 09:24:48 +0000203 CallFrame* m_callFrame;
jfbastien@apple.comd9f999e2017-10-20 02:23:29 +0000204 EntryFrame* m_entryFrame;
mark.lam@apple.coma2668e02017-03-09 21:05:41 +0000205 const AbstractLocker& m_codeBlockSetLocker;
206 const AbstractLocker& m_machineThreadsLocker;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000207 bool m_bailingOut { false };
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000208 size_t m_depth { 0 };
209};
210
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000211class CFrameWalker : public FrameWalker {
212public:
213 typedef FrameWalker Base;
214
cdumez@apple.comb261d322021-05-29 21:08:38 +0000215 CFrameWalker(VM& vm, void* machineFrame, CallFrame* callFrame, const AbstractLocker& codeBlockSetLocker, const AbstractLocker& machineThreadsLocker)
216 : Base(vm, callFrame, codeBlockSetLocker, machineThreadsLocker)
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000217 , m_machineFrame(machineFrame)
218 {
219 }
220
221 size_t walk(Vector<UnprocessedStackFrame>& stackTrace, bool& didRunOutOfSpace)
222 {
223 if (sReportStats)
224 sNumTotalWalks++;
225 resetAtMachineFrame();
226 size_t maxStackTraceSize = stackTrace.size();
227 // The way the C walker decides if a frame it is about to trace is C or JS is by
228 // ensuring m_callFrame points to some frame above the machineFrame.
229 if (!isAtTop() && !m_bailingOut && m_machineFrame == m_callFrame) {
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000230 recordJITFrame(stackTrace);
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000231 Base::advanceToParentFrame();
232 resetAtMachineFrame();
233 }
234
235 while (!isAtTop() && !m_bailingOut && m_depth < maxStackTraceSize) {
236 if (m_machineFrame >= m_callFrame) {
237 // If we get to this state we probably have an invalid trace.
238 m_bailingOut = true;
239 break;
240 }
241
242 if (isCFrame()) {
243 RELEASE_ASSERT(!LLInt::isLLIntPC(frame()->callerFrame));
keith_miller@apple.come8709172018-10-16 07:19:13 +0000244 stackTrace[m_depth] = UnprocessedStackFrame(frame()->returnPC);
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000245 m_depth++;
246 } else
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000247 recordJITFrame(stackTrace);
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000248 advanceToParentFrame();
249 resetAtMachineFrame();
250 }
251 didRunOutOfSpace = m_depth >= maxStackTraceSize && !isAtTop();
252 reportStats();
253 return m_depth;
254 }
255
256private:
257
258 bool isCFrame()
259 {
260 return frame()->callerFrame != m_callFrame;
261 }
262
263 void advanceToParentFrame()
264 {
265 if (!isCFrame())
266 Base::advanceToParentFrame();
267 m_machineFrame = frame()->callerFrame;
268 }
269
270 void resetAtMachineFrame()
271 {
272 if (!isValidFramePointer(m_machineFrame)) {
273 // Guard against pausing the process at weird program points.
274 m_bailingOut = true;
275 if (sReportStats)
276 sNumFailedWalks++;
277 return;
278 }
279 Base::resetAtMachineFrame();
280 }
281
282 CallerFrameAndPC* frame()
283 {
284 return reinterpret_cast<CallerFrameAndPC*>(m_machineFrame);
285 }
286
287 void* m_machineFrame;
288};
289
drousso@apple.com1cd50532020-05-06 16:15:16 +0000290SamplingProfiler::SamplingProfiler(VM& vm, Ref<Stopwatch>&& stopwatch)
rmorisset@apple.com4ccbcf12019-03-12 19:07:04 +0000291 : m_isPaused(false)
292 , m_isShutDown(false)
293 , m_vm(vm)
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000294 , m_weakRandom()
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000295 , m_stopwatch(WTFMove(stopwatch))
utatane.tea@gmail.com5c7039d2017-12-27 00:33:11 +0000296 , m_timingInterval(Seconds::fromMicroseconds(Options::sampleInterval()))
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000297{
298 if (sReportStats) {
299 sNumTotalWalks = 0;
300 sNumFailedWalks = 0;
301 }
302
303 m_currentFrames.grow(256);
ysuzuki@apple.comb4399152019-11-09 03:45:18 +0000304 vm.heap.objectSpace().enablePreciseAllocationTracking();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000305}
306
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000307SamplingProfiler::~SamplingProfiler()
308{
309}
310
cdumez@apple.com2f481692021-05-29 21:14:39 +0000311void SamplingProfiler::createThreadIfNecessary()
sbarati@apple.com207a6832016-04-06 03:55:11 +0000312{
313 ASSERT(m_lock.isLocked());
314
utatane.tea@gmail.com1e94a262017-04-12 12:08:29 +0000315 if (m_thread)
sbarati@apple.com207a6832016-04-06 03:55:11 +0000316 return;
317
318 RefPtr<SamplingProfiler> profiler = this;
utatane.tea@gmail.com1e94a262017-04-12 12:08:29 +0000319 m_thread = Thread::create("jsc.sampling-profiler.thread", [profiler] {
sbarati@apple.com207a6832016-04-06 03:55:11 +0000320 profiler->timerLoop();
321 });
322}
323
324void SamplingProfiler::timerLoop()
325{
326 while (true) {
utatane.tea@gmail.com5c7039d2017-12-27 00:33:11 +0000327 Seconds stackTraceProcessingTime = 0_s;
sbarati@apple.com207a6832016-04-06 03:55:11 +0000328 {
cdumez@apple.com3e035a82021-05-22 16:49:42 +0000329 Locker locker { m_lock };
sbarati@apple.com207a6832016-04-06 03:55:11 +0000330 if (UNLIKELY(m_isShutDown))
331 return;
332
333 if (!m_isPaused && m_jscExecutionThread)
cdumez@apple.com2f481692021-05-29 21:14:39 +0000334 takeSample(stackTraceProcessingTime);
sbarati@apple.com207a6832016-04-06 03:55:11 +0000335
336 m_lastTime = m_stopwatch->elapsedTime();
337 }
338
sbarati@apple.com51d80072016-06-14 02:29:26 +0000339 // Read section 6.2 of this paper for more elaboration of why we add a random
340 // fluctuation here. The main idea is to prevent our timer from being in sync
341 // with some system process such as a scheduled context switch.
342 // http://plv.colorado.edu/papers/mytkowicz-pldi10.pdf
utatane.tea@gmail.com0f6fd7b2017-03-24 05:31:51 +0000343 double randomSignedNumber = (m_weakRandom.get() * 2.0) - 1.0; // A random number between [-1, 1).
utatane.tea@gmail.com5c7039d2017-12-27 00:33:11 +0000344 Seconds randomFluctuation = m_timingInterval * 0.2 * randomSignedNumber;
345 WTF::sleep(m_timingInterval - std::min(m_timingInterval, stackTraceProcessingTime) + randomFluctuation);
sbarati@apple.com207a6832016-04-06 03:55:11 +0000346 }
347}
348
cdumez@apple.com2f481692021-05-29 21:14:39 +0000349void SamplingProfiler::takeSample(Seconds& stackTraceProcessingTime)
sbarati@apple.com207a6832016-04-06 03:55:11 +0000350{
351 ASSERT(m_lock.isLocked());
352 if (m_vm.entryScope) {
utatane.tea@gmail.comf0130682018-03-02 17:13:32 +0000353 Seconds nowTime = m_stopwatch->elapsedTime();
sbarati@apple.com207a6832016-04-06 03:55:11 +0000354
cdumez@apple.com6c3db6c2021-05-22 03:13:17 +0000355 Locker machineThreadsLocker { m_vm.heap.machineThreads().getLock() };
356 Locker codeBlockSetLocker { m_vm.heap.codeBlockSet().getLock() };
357 Locker executableAllocatorLocker { ExecutableAllocator::singleton().getLock() };
darin@apple.coma4ddc782021-05-30 16:11:40 +0000358 std::optional<LockHolder> wasmCalleesLocker;
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000359#if ENABLE(WEBASSEMBLY)
mark.lam@apple.com9c025812020-05-26 21:32:50 +0000360 if (Wasm::isSupported())
cdumez@apple.comb261d322021-05-29 21:08:38 +0000361 wasmCalleesLocker.emplace(Wasm::CalleeRegistry::singleton().getLock());
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000362#endif
sbarati@apple.com207a6832016-04-06 03:55:11 +0000363
ysuzuki@apple.comb2f59452022-02-05 18:06:02 +0000364 ThreadSuspendLocker threadSuspendLocker;
365 auto didSuspend = m_jscExecutionThread->suspend(threadSuspendLocker);
sbarati@apple.com207a6832016-04-06 03:55:11 +0000366 if (didSuspend) {
367 // While the JSC thread is suspended, we can't do things like malloc because the JSC thread
368 // may be holding the malloc lock.
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000369 void* machineFrame;
ysuzuki@apple.com52e98bb2019-10-22 09:24:48 +0000370 CallFrame* callFrame;
sbarati@apple.com207a6832016-04-06 03:55:11 +0000371 void* machinePC;
372 bool topFrameIsLLInt = false;
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000373 RegExp* regExp = nullptr;
sbarati@apple.com207a6832016-04-06 03:55:11 +0000374 void* llintPC;
375 {
utatane.tea@gmail.com71111912017-07-19 08:43:57 +0000376 PlatformRegisters registers;
ysuzuki@apple.comb2f59452022-02-05 18:06:02 +0000377 m_jscExecutionThread->getRegisters(threadSuspendLocker, registers);
utatane.tea@gmail.com71111912017-07-19 08:43:57 +0000378 machineFrame = MachineContext::framePointer(registers);
ysuzuki@apple.com52e98bb2019-10-22 09:24:48 +0000379 callFrame = static_cast<CallFrame*>(machineFrame);
sbarati@apple.com4b798292018-08-03 00:14:11 +0000380 auto instructionPointer = MachineContext::instructionPointer(registers);
381 if (instructionPointer)
382 machinePC = instructionPointer->untaggedExecutableAddress();
383 else
384 machinePC = nullptr;
mark.lam@apple.com18b4eab2018-03-31 07:04:00 +0000385 llintPC = removeCodePtrTag(MachineContext::llintInstructionPointer(registers));
386 assertIsNotTagged(machinePC);
sbarati@apple.com207a6832016-04-06 03:55:11 +0000387 }
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000388
389 bool shouldAppendTopFrameAsCCode = false;
sbarati@apple.com207a6832016-04-06 03:55:11 +0000390 // FIXME: Lets have a way of detecting when we're parsing code.
391 // https://bugs.webkit.org/show_bug.cgi?id=152761
sbarati@apple.com893729f2017-03-29 22:55:53 +0000392 if (ExecutableAllocator::singleton().isValidExecutableMemory(executableAllocatorLocker, machinePC)) {
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000393 regExp = m_vm.m_executingRegExp;
394 if (regExp)
sbarati@apple.com207a6832016-04-06 03:55:11 +0000395 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 +0000396 } else if (LLInt::isLLIntPC(machinePC)) {
397 topFrameIsLLInt = true;
398 // We're okay to take a normal stack trace when the PC
399 // is in LLInt code.
400 } else {
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000401 // RegExp evaluation is leaf. So if RegExp evaluation exists, we can say it is RegExp evaluation is the top user-visible frame.
402 regExp = m_vm.m_executingRegExp;
sbarati@apple.com207a6832016-04-06 03:55:11 +0000403 // We resort to topCallFrame to see if we can get anything
404 // useful. We usually get here when we're executing C code.
405 callFrame = m_vm.topCallFrame;
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000406 if (Options::collectExtraSamplingProfilerData() && !Options::sampleCCode())
407 shouldAppendTopFrameAsCCode = true;
sbarati@apple.com207a6832016-04-06 03:55:11 +0000408 }
409
410 size_t walkSize;
411 bool wasValidWalk;
412 bool didRunOutOfVectorSpace;
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000413 if (Options::sampleCCode()) {
cdumez@apple.comb261d322021-05-29 21:08:38 +0000414 CFrameWalker walker(m_vm, machineFrame, callFrame, codeBlockSetLocker, machineThreadsLocker);
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000415 walkSize = walker.walk(m_currentFrames, didRunOutOfVectorSpace);
416 wasValidWalk = walker.wasValidWalk();
417 } else {
cdumez@apple.comb261d322021-05-29 21:08:38 +0000418 FrameWalker walker(m_vm, callFrame, codeBlockSetLocker, machineThreadsLocker);
sbarati@apple.com207a6832016-04-06 03:55:11 +0000419 walkSize = walker.walk(m_currentFrames, didRunOutOfVectorSpace);
420 wasValidWalk = walker.wasValidWalk();
421 }
422
ysuzuki@apple.comb2f59452022-02-05 18:06:02 +0000423 m_jscExecutionThread->resume(threadSuspendLocker);
sbarati@apple.com207a6832016-04-06 03:55:11 +0000424
utatane.tea@gmail.com5c7039d2017-12-27 00:33:11 +0000425 auto startTime = MonotonicTime::now();
sbarati@apple.com207a6832016-04-06 03:55:11 +0000426 // We can now use data structures that malloc, and do other interesting things, again.
427
428 // FIXME: It'd be interesting to take data about the program's state when
429 // we fail to take a stack trace: https://bugs.webkit.org/show_bug.cgi?id=152758
430 if (wasValidWalk && walkSize) {
431 if (sReportStats)
432 sNumTotalStackTraces++;
433 Vector<UnprocessedStackFrame> stackTrace;
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000434 stackTrace.reserveInitialCapacity(walkSize + !!shouldAppendTopFrameAsCCode);
435 if (shouldAppendTopFrameAsCCode)
436 stackTrace.uncheckedAppend(UnprocessedStackFrame { machinePC });
sbarati@apple.com207a6832016-04-06 03:55:11 +0000437 for (size_t i = 0; i < walkSize; i++) {
438 UnprocessedStackFrame frame = m_currentFrames[i];
439 stackTrace.uncheckedAppend(frame);
440 }
441
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000442 m_unprocessedStackTraces.append(UnprocessedStackTrace { nowTime, machinePC, topFrameIsLLInt, llintPC, regExp, WTFMove(stackTrace) });
sbarati@apple.com207a6832016-04-06 03:55:11 +0000443
444 if (didRunOutOfVectorSpace)
445 m_currentFrames.grow(m_currentFrames.size() * 1.25);
446 }
447
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000448 auto endTime = MonotonicTime::now();
449 stackTraceProcessingTime = endTime - startTime;
sbarati@apple.com207a6832016-04-06 03:55:11 +0000450 }
451 }
452}
453
keith_miller@apple.com0f985ec2019-10-23 00:55:38 +0000454static ALWAYS_INLINE BytecodeIndex tryGetBytecodeIndex(unsigned llintPC, CodeBlock* codeBlock)
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000455{
ossy@webkit.orgc8cea7b2016-02-18 16:45:26 +0000456#if ENABLE(DFG_JIT)
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000457 RELEASE_ASSERT(!codeBlock->hasCodeOrigins());
ossy@webkit.orgc8cea7b2016-02-18 16:45:26 +0000458#endif
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000459
keith_miller@apple.com0f985ec2019-10-23 00:55:38 +0000460 unsigned bytecodeOffset = llintPC;
461 if (bytecodeOffset < codeBlock->instructionsSize())
462 return BytecodeIndex(bytecodeOffset);
463 return BytecodeIndex();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000464}
465
cdumez@apple.com2f481692021-05-29 21:14:39 +0000466void SamplingProfiler::processUnverifiedStackTraces()
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000467{
468 // This function needs to be called from the JSC execution thread.
469 RELEASE_ASSERT(m_lock.isLocked());
470
yijia_huang@apple.com271f9d12022-05-29 03:23:06 +0000471 TinyBloomFilter<uintptr_t> filter = m_vm.heap.objectSpace().blocks().filter();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000472
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000473 for (UnprocessedStackTrace& unprocessedStackTrace : m_unprocessedStackTraces) {
474 m_stackTraces.append(StackTrace());
475 StackTrace& stackTrace = m_stackTraces.last();
476 stackTrace.timestamp = unprocessedStackTrace.timestamp;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000477
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000478 auto populateCodeLocation = [] (CodeBlock* codeBlock, JITType jitType, BytecodeIndex bytecodeIndex, StackFrame::CodeLocation& location) {
keith_miller@apple.com0f985ec2019-10-23 00:55:38 +0000479 if (bytecodeIndex.offset() < codeBlock->instructionsSize()) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000480 int divot;
481 int startOffset;
482 int endOffset;
keith_miller@apple.com0f985ec2019-10-23 00:55:38 +0000483 codeBlock->expressionRangeForBytecodeIndex(bytecodeIndex, divot, startOffset, endOffset,
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000484 location.lineNumber, location.columnNumber);
485 location.bytecodeIndex = bytecodeIndex;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000486 }
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000487 if (codeBlock->hasHash())
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000488 location.codeBlockHash = codeBlock->hash();
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000489 location.jitType = jitType;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000490 };
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000491
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000492 auto appendCodeBlock = [&] (CodeBlock* codeBlock, JITType jitType, BytecodeIndex bytecodeIndex) {
cdumez@apple.com2f481692021-05-29 21:14:39 +0000493 assertIsHeld(m_lock);
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000494 stackTrace.frames.append(StackFrame(codeBlock->ownerExecutable()));
495 m_liveCellPointers.add(codeBlock->ownerExecutable());
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000496 populateCodeLocation(codeBlock, jitType, bytecodeIndex, stackTrace.frames.last().semanticLocation);
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000497 };
498
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000499 auto appendEmptyFrame = [&] {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000500 stackTrace.frames.append(StackFrame());
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000501 };
502
sbarati@apple.comf4eb0222021-12-12 00:39:57 +0000503#if ENABLE(WEBASSEMBLY)
504 auto storeWasmCalleeIntoLastFrame = [&] (UnprocessedStackFrame& unprocessedStackFrame, void* pc) {
505 CalleeBits calleeBits = unprocessedStackFrame.unverifiedCallee;
506 ASSERT_UNUSED(calleeBits, calleeBits.isWasm());
507 StackFrame& stackFrame = stackTrace.frames.last();
508 stackFrame.frameType = FrameType::Wasm;
509 stackFrame.wasmIndexOrName = unprocessedStackFrame.wasmIndexOrName;
510 stackFrame.wasmCompilationMode = unprocessedStackFrame.wasmCompilationMode;
511#if ENABLE(JIT)
512 if (pc && unprocessedStackFrame.wasmPCMap) {
513 if (std::optional<CodeOrigin> codeOrigin = unprocessedStackFrame.wasmPCMap->findPC(pc))
514 stackFrame.wasmOffset = codeOrigin->bytecodeIndex();
515 }
516#else
517 UNUSED_PARAM(pc);
518#endif
519 };
520#endif
521
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000522 auto storeCalleeIntoLastFrame = [&] (UnprocessedStackFrame& unprocessedStackFrame) {
cdumez@apple.com2f481692021-05-29 21:14:39 +0000523 assertIsHeld(m_lock);
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000524 // Set the callee if it's a valid GC object.
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000525 CalleeBits calleeBits = unprocessedStackFrame.unverifiedCallee;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000526 StackFrame& stackFrame = stackTrace.frames.last();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000527 bool alreadyHasExecutable = !!stackFrame.executable;
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000528#if ENABLE(WEBASSEMBLY)
sbarati@apple.com5db42f82017-04-04 22:23:37 +0000529 if (calleeBits.isWasm()) {
sbarati@apple.comf4eb0222021-12-12 00:39:57 +0000530 storeWasmCalleeIntoLastFrame(unprocessedStackFrame, nullptr);
sbarati@apple.com5db42f82017-04-04 22:23:37 +0000531 return;
532 }
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000533#endif
sbarati@apple.com5db42f82017-04-04 22:23:37 +0000534
535 JSValue callee = calleeBits.asCell();
fpizlo@apple.combc16ddb2016-09-06 01:02:22 +0000536 if (!HeapUtil::isValueGCObject(m_vm.heap, filter, callee)) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000537 if (!alreadyHasExecutable)
538 stackFrame.frameType = FrameType::Unknown;
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000539 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000540 }
541
542 JSCell* calleeCell = callee.asCell();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000543 auto setFallbackFrameType = [&] {
544 ASSERT(!alreadyHasExecutable);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000545 FrameType result = FrameType::Unknown;
ysuzuki@apple.com984775d2022-04-16 00:00:35 +0000546 auto callData = JSC::getCallData(calleeCell);
ross.kirsling@sony.com924e7502020-04-27 09:09:32 +0000547 if (callData.type == CallData::Type::Native)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000548 result = FrameType::Host;
549
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000550 stackFrame.frameType = result;
551 };
552
553 auto addCallee = [&] (JSObject* callee) {
cdumez@apple.com2f481692021-05-29 21:14:39 +0000554 assertIsHeld(m_lock);
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000555 stackFrame.callee = callee;
556 m_liveCellPointers.add(callee);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000557 };
558
559 if (calleeCell->type() != JSFunctionType) {
ysuzuki@apple.com984775d2022-04-16 00:00:35 +0000560 if (JSObject* object = jsDynamicCast<JSObject*>(calleeCell))
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000561 addCallee(object);
562
563 if (!alreadyHasExecutable)
564 setFallbackFrameType();
565
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000566 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000567 }
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000568
569 addCallee(jsCast<JSFunction*>(calleeCell));
570
571 if (alreadyHasExecutable)
572 return;
573
574 ExecutableBase* executable = jsCast<JSFunction*>(calleeCell)->executable();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000575 if (!executable) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000576 setFallbackFrameType();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000577 return;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000578 }
579
fpizlo@apple.combc16ddb2016-09-06 01:02:22 +0000580 RELEASE_ASSERT(HeapUtil::isPointerGCObjectJSCell(m_vm.heap, filter, executable));
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000581 stackFrame.frameType = FrameType::Executable;
582 stackFrame.executable = executable;
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000583 m_liveCellPointers.add(executable);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000584 };
585
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000586 auto appendCodeOrigin = [&] (CodeBlock* machineCodeBlock, CodeOrigin origin) {
cdumez@apple.com2f481692021-05-29 21:14:39 +0000587 assertIsHeld(m_lock);
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000588 size_t startIndex = stackTrace.frames.size(); // We want to change stack traces that we're about to append.
589
590 CodeOrigin machineOrigin;
591 origin.walkUpInlineStack([&] (const CodeOrigin& codeOrigin) {
592 machineOrigin = codeOrigin;
rmorisset@apple.comb4811e72019-03-20 20:24:36 +0000593 auto* inlineCallFrame = codeOrigin.inlineCallFrame();
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000594 appendCodeBlock(inlineCallFrame ? inlineCallFrame->baselineCodeBlock.get() : machineCodeBlock, machineCodeBlock->jitType(), codeOrigin.bytecodeIndex());
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000595 });
596
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000597 if (Options::collectExtraSamplingProfilerData()) {
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000598 RELEASE_ASSERT(machineOrigin.isSet());
rmorisset@apple.comb4811e72019-03-20 20:24:36 +0000599 RELEASE_ASSERT(!machineOrigin.inlineCallFrame());
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000600
601 StackFrame::CodeLocation machineLocation = stackTrace.frames.last().semanticLocation;
602
603 // We want to tell each inlined frame about the machine frame
604 // they were inlined into. Currently, we only use this for dumping
605 // output on the command line, but we could extend it to the web
606 // inspector in the future if we find a need for it there.
607 RELEASE_ASSERT(stackTrace.frames.size());
sbarati@apple.com23c01192018-09-22 07:28:39 +0000608 m_liveCellPointers.add(machineCodeBlock);
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000609 for (size_t i = startIndex; i < stackTrace.frames.size() - 1; i++)
sbarati@apple.com23c01192018-09-22 07:28:39 +0000610 stackTrace.frames[i].machineLocation = std::make_pair(machineLocation, machineCodeBlock);
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000611 }
612 };
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000613
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000614 // Prepend the top-most inlined frame if needed and gather
615 // location information about where the top frame is executing.
616 size_t startIndex = 0;
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000617 if (unprocessedStackTrace.regExp) {
618 // If the stack-trace is annotated with RegExp, the top-frame must be RegExp since RegExp evaluation is leaf function.
619 appendEmptyFrame();
620 stackTrace.frames.last().regExp = unprocessedStackTrace.regExp;
621 stackTrace.frames.last().frameType = FrameType::RegExp;
622 stackTrace.frames.last().semanticLocation.isRegExp = true;
623 m_liveCellPointers.add(unprocessedStackTrace.regExp);
624 } else if (!unprocessedStackTrace.frames.isEmpty() && !!unprocessedStackTrace.frames[0].verifiedCodeBlock) {
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000625 CodeBlock* topCodeBlock = unprocessedStackTrace.frames[0].verifiedCodeBlock;
626 if (unprocessedStackTrace.topFrameIsLLInt) {
627 // We reuse LLInt CodeBlocks for the baseline JIT, so we need to check for both jit types.
628 // This might also be false for various reasons (known and unknown), even though
629 // it's super unlikely. One reason that this can be false is when we throw from a DFG frame,
630 // and we end up having to unwind past an EntryFrame, we will end up executing
ysuzuki@apple.com9549c072020-10-06 22:04:36 +0000631 // inside the LLInt's llint_handle_ucaught_exception. So we just protect against this
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000632 // by ignoring it.
keith_miller@apple.com0f985ec2019-10-23 00:55:38 +0000633 BytecodeIndex bytecodeIndex = BytecodeIndex(0);
sbarati@apple.com9777f7c2019-04-30 03:27:39 +0000634 if (topCodeBlock->jitType() == JITType::InterpreterThunk || topCodeBlock->jitType() == JITType::BaselineJIT) {
ysuzuki@apple.com5eb59652020-01-22 20:31:33 +0000635 unsigned bits = static_cast<unsigned>(bitwise_cast<uintptr_t>(unprocessedStackTrace.llintPC));
keith_miller@apple.com0f985ec2019-10-23 00:55:38 +0000636 bytecodeIndex = tryGetBytecodeIndex(bits, topCodeBlock);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000637
keith_miller@apple.com0f985ec2019-10-23 00:55:38 +0000638 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 +0000639
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000640 appendCodeBlock(topCodeBlock, topCodeBlock->jitType(), bytecodeIndex);
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000641 storeCalleeIntoLastFrame(unprocessedStackTrace.frames[0]);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000642 startIndex = 1;
643 }
yusukesuzuki@slowstart.org48bd0a02018-09-22 05:26:44 +0000644 } else {
645#if ENABLE(JIT)
darin@apple.coma4ddc782021-05-30 16:11:40 +0000646 if (std::optional<CodeOrigin> codeOrigin = topCodeBlock->findPC(unprocessedStackTrace.topPC)) {
yusukesuzuki@slowstart.org48bd0a02018-09-22 05:26:44 +0000647 appendCodeOrigin(topCodeBlock, *codeOrigin);
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000648 storeCalleeIntoLastFrame(unprocessedStackTrace.frames[0]);
yusukesuzuki@slowstart.org48bd0a02018-09-22 05:26:44 +0000649 startIndex = 1;
650 }
651#endif
652 UNUSED_PARAM(appendCodeOrigin);
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000653 }
654 }
sbarati@apple.comf4eb0222021-12-12 00:39:57 +0000655#if ENABLE(WEBASSEMBLY)
656 else if (!unprocessedStackTrace.frames.isEmpty() && unprocessedStackTrace.frames[0].unverifiedCallee.isWasm()) {
657 appendEmptyFrame();
658 storeWasmCalleeIntoLastFrame(unprocessedStackTrace.frames[0], unprocessedStackTrace.topPC);
659 startIndex = 1;
660 }
661#endif
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000662
663 for (size_t i = startIndex; i < unprocessedStackTrace.frames.size(); i++) {
664 UnprocessedStackFrame& unprocessedStackFrame = unprocessedStackTrace.frames[i];
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000665 if (CodeBlock* codeBlock = unprocessedStackFrame.verifiedCodeBlock) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000666 CallSiteIndex callSiteIndex = unprocessedStackFrame.callSiteIndex;
667
668 auto appendCodeBlockNoInlining = [&] {
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000669 appendCodeBlock(codeBlock, codeBlock->jitType(), tryGetBytecodeIndex(callSiteIndex.bits(), codeBlock));
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000670 };
671
672#if ENABLE(DFG_JIT)
673 if (codeBlock->hasCodeOrigins()) {
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +0000674 if (codeBlock->canGetCodeOrigin(callSiteIndex))
675 appendCodeOrigin(codeBlock, codeBlock->codeOrigin(callSiteIndex));
676 else
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000677 appendCodeBlock(codeBlock, codeBlock->jitType(), BytecodeIndex());
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000678 } else
679 appendCodeBlockNoInlining();
680#else
681 appendCodeBlockNoInlining();
682#endif
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000683 } else if (unprocessedStackFrame.cCodePC) {
684 appendEmptyFrame();
685 stackTrace.frames.last().cCodePC = unprocessedStackFrame.cCodePC;
686 stackTrace.frames.last().frameType = FrameType::C;
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000687 } else
688 appendEmptyFrame();
689
690 // Note that this is okay to do if we walked the inline stack because
691 // the machine frame will be at the top of the processed stack trace.
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000692 if (!unprocessedStackFrame.cCodePC)
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000693 storeCalleeIntoLastFrame(unprocessedStackFrame);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000694 }
695 }
696
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000697 m_unprocessedStackTraces.clear();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000698}
699
mark.lam@apple.com17ae4902021-02-19 15:51:15 +0000700template<typename Visitor>
701void SamplingProfiler::visit(Visitor& visitor)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000702{
703 RELEASE_ASSERT(m_lock.isLocked());
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000704 for (JSCell* cell : m_liveCellPointers)
mark.lam@apple.com15f620c2021-02-13 05:30:58 +0000705 visitor.appendUnbarriered(cell);
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000706}
707
mark.lam@apple.com17ae4902021-02-19 15:51:15 +0000708template void SamplingProfiler::visit(AbstractSlotVisitor&);
709template void SamplingProfiler::visit(SlotVisitor&);
710
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000711void SamplingProfiler::shutdown()
712{
cdumez@apple.com3e035a82021-05-22 16:49:42 +0000713 Locker locker { m_lock };
sbarati@apple.com207a6832016-04-06 03:55:11 +0000714 m_isShutDown = true;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000715}
716
717void SamplingProfiler::start()
718{
cdumez@apple.com3e035a82021-05-22 16:49:42 +0000719 Locker locker { m_lock };
cdumez@apple.com2f481692021-05-29 21:14:39 +0000720 startWithLock();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000721}
722
cdumez@apple.com2f481692021-05-29 21:14:39 +0000723void SamplingProfiler::startWithLock()
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000724{
725 ASSERT(m_lock.isLocked());
sbarati@apple.com207a6832016-04-06 03:55:11 +0000726 m_isPaused = false;
cdumez@apple.com2f481692021-05-29 21:14:39 +0000727 createThreadIfNecessary();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000728}
729
cdumez@apple.com2f481692021-05-29 21:14:39 +0000730void SamplingProfiler::pause()
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000731{
732 ASSERT(m_lock.isLocked());
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000733 m_isPaused = true;
734 reportStats();
735}
736
cdumez@apple.com2f481692021-05-29 21:14:39 +0000737void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThreadWithLock()
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000738{
739 ASSERT(m_lock.isLocked());
utatane.tea@gmail.com71111912017-07-19 08:43:57 +0000740 m_jscExecutionThread = &Thread::current();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000741}
742
743void SamplingProfiler::noticeCurrentThreadAsJSCExecutionThread()
744{
cdumez@apple.com3e035a82021-05-22 16:49:42 +0000745 Locker locker { m_lock };
cdumez@apple.com2f481692021-05-29 21:14:39 +0000746 noticeCurrentThreadAsJSCExecutionThreadWithLock();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000747}
748
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000749void SamplingProfiler::noticeJSLockAcquisition()
750{
cdumez@apple.com3e035a82021-05-22 16:49:42 +0000751 Locker locker { m_lock };
cdumez@apple.com2f481692021-05-29 21:14:39 +0000752 noticeCurrentThreadAsJSCExecutionThreadWithLock();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000753}
754
755void SamplingProfiler::noticeVMEntry()
756{
cdumez@apple.com3e035a82021-05-22 16:49:42 +0000757 Locker locker { m_lock };
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000758 ASSERT(m_vm.entryScope);
cdumez@apple.com2f481692021-05-29 21:14:39 +0000759 noticeCurrentThreadAsJSCExecutionThreadWithLock();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000760 m_lastTime = m_stopwatch->elapsedTime();
cdumez@apple.com2f481692021-05-29 21:14:39 +0000761 createThreadIfNecessary();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000762}
763
cdumez@apple.com2f481692021-05-29 21:14:39 +0000764void SamplingProfiler::clearData()
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000765{
766 ASSERT(m_lock.isLocked());
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000767 m_stackTraces.clear();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000768 m_liveCellPointers.clear();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000769 m_unprocessedStackTraces.clear();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000770}
771
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000772String SamplingProfiler::StackFrame::nameFromCallee(VM& vm)
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000773{
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000774 if (!callee) {
775 if (regExp)
776 return regExp->toSourceString();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000777 return String();
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000778 }
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000779
mark.lam@apple.come2166a82021-04-28 17:13:40 +0000780 DeferTermination deferScope(vm);
mark.lam@apple.com09f92b82016-09-27 20:32:13 +0000781 auto scope = DECLARE_CATCH_SCOPE(vm);
ysuzuki@apple.com984775d2022-04-16 00:00:35 +0000782 JSGlobalObject* globalObject = callee->globalObject();
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000783 auto getPropertyIfPureOperation = [&] (const Identifier& ident) -> String {
mark.lam@apple.com97021a82020-07-23 00:08:50 +0000784 PropertySlot slot(callee, PropertySlot::InternalMethodType::VMInquiry, &vm);
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000785 PropertyName propertyName(ident);
ysuzuki@apple.com52e98bb2019-10-22 09:24:48 +0000786 bool hasProperty = callee->getPropertySlot(globalObject, propertyName, slot);
mark.lam@apple.comcce76562017-05-08 16:56:32 +0000787 scope.assertNoException();
mark.lam@apple.com09f92b82016-09-27 20:32:13 +0000788 if (hasProperty) {
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000789 if (slot.isValue()) {
ysuzuki@apple.com52e98bb2019-10-22 09:24:48 +0000790 JSValue nameValue = slot.getValue(globalObject, propertyName);
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000791 if (isJSString(nameValue))
792 return asString(nameValue)->tryGetValue();
793 }
794 }
795 return String();
796 };
797
798 String name = getPropertyIfPureOperation(vm.propertyNames->displayName);
799 if (!name.isEmpty())
800 return name;
801
802 return getPropertyIfPureOperation(vm.propertyNames->name);
803}
804
805String SamplingProfiler::StackFrame::displayName(VM& vm)
806{
807 {
808 String name = nameFromCallee(vm);
809 if (!name.isEmpty())
810 return name;
811 }
812
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000813 switch (frameType) {
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000814 case FrameType::C:
utatane.tea@gmail.com57950612017-04-25 02:53:49 +0000815#if HAVE(DLADDR)
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000816 if (frameType == FrameType::C) {
tzagallo@apple.com3474dd02018-10-29 13:16:03 +0000817 auto demangled = WTF::StackTrace::demangle(const_cast<void*>(cCodePC));
utatane.tea@gmail.com57950612017-04-25 02:53:49 +0000818 if (demangled)
cdumez@apple.com78ea99a2022-04-06 19:05:23 +0000819 return String::fromLatin1(demangled->demangledName() ? demangled->demangledName() : demangled->mangledName());
keith_miller@apple.comb4a86442017-02-02 01:23:37 +0000820 WTF::dataLog("couldn't get a name");
821 }
822#endif
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000823 return "(unknown C PC)"_s;
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000824
sbarati@apple.com783a68c2021-04-29 17:42:13 +0000825 case FrameType::Unknown:
utatane.tea@gmail.com84077632018-06-23 08:39:34 +0000826 return "(unknown)"_s;
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000827
828 case FrameType::Host:
utatane.tea@gmail.com84077632018-06-23 08:39:34 +0000829 return "(host)"_s;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000830
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000831 case FrameType::RegExp:
832 return "(regexp)"_s;
833
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000834 case FrameType::Wasm:
835#if ENABLE(WEBASSEMBLY)
836 if (wasmIndexOrName)
837 return makeString(wasmIndexOrName.value());
838#endif
839 return "(wasm)"_s;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000840
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000841 case FrameType::Executable:
842 if (executable->isHostFunction())
843 return static_cast<NativeExecutable*>(executable)->name();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000844
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000845 if (executable->isFunctionExecutable())
846 return static_cast<FunctionExecutable*>(executable)->ecmaName().string();
847 if (executable->isProgramExecutable() || executable->isEvalExecutable())
848 return "(program)"_s;
849 if (executable->isModuleProgramExecutable())
850 return "(module)"_s;
851
852 RELEASE_ASSERT_NOT_REACHED();
853 return String();
854 }
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000855 RELEASE_ASSERT_NOT_REACHED();
856 return String();
857}
858
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000859String SamplingProfiler::StackFrame::displayNameForJSONTests(VM& vm)
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000860{
sbarati@apple.com8ec5c262016-02-04 21:51:40 +0000861 {
862 String name = nameFromCallee(vm);
863 if (!name.isEmpty())
864 return name;
865 }
866
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000867 switch (frameType) {
868 case FrameType::Unknown:
869 case FrameType::C:
utatane.tea@gmail.com84077632018-06-23 08:39:34 +0000870 return "(unknown)"_s;
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000871
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000872 case FrameType::RegExp:
873 return "(regexp)"_s;
874
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000875 case FrameType::Host:
utatane.tea@gmail.com84077632018-06-23 08:39:34 +0000876 return "(host)"_s;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000877
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000878 case FrameType::Wasm: {
879#if ENABLE(WEBASSEMBLY)
880 if (wasmIndexOrName)
881 return makeString(wasmIndexOrName.value());
882#endif
883 return "(wasm)"_s;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000884 }
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000885
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000886 case FrameType::Executable:
887 if (executable->isHostFunction())
888 return static_cast<NativeExecutable*>(executable)->name();
889
890 if (executable->isFunctionExecutable()) {
891 String result = static_cast<FunctionExecutable*>(executable)->ecmaName().string();
892 if (result.isEmpty())
893 return "(anonymous function)"_s;
894 return result;
895 }
896 if (executable->isEvalExecutable())
897 return "(eval)"_s;
898 if (executable->isProgramExecutable())
899 return "(program)"_s;
900 if (executable->isModuleProgramExecutable())
901 return "(module)"_s;
902
903 RELEASE_ASSERT_NOT_REACHED();
904 return String();
905 }
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000906 RELEASE_ASSERT_NOT_REACHED();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000907 return String();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000908}
909
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000910int SamplingProfiler::StackFrame::functionStartLine()
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +0000911{
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000912 switch (frameType) {
913 case FrameType::Unknown:
914 case FrameType::Host:
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000915 case FrameType::RegExp:
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000916 case FrameType::C:
917 case FrameType::Wasm:
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000918 return -1;
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000919
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000920 case FrameType::Executable:
921 if (executable->isHostFunction())
922 return -1;
923 return static_cast<ScriptExecutable*>(executable)->firstLine();
924 }
925 RELEASE_ASSERT_NOT_REACHED();
926 return -1;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000927}
928
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000929unsigned SamplingProfiler::StackFrame::functionStartColumn()
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000930{
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000931 switch (frameType) {
932 case FrameType::Unknown:
933 case FrameType::Host:
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000934 case FrameType::RegExp:
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000935 case FrameType::C:
936 case FrameType::Wasm:
peavo@outlook.coma83d48a2016-05-02 21:20:15 +0000937 return std::numeric_limits<unsigned>::max();
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000938
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000939 case FrameType::Executable:
940 if (executable->isHostFunction())
941 return std::numeric_limits<unsigned>::max();
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000942
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000943 return static_cast<ScriptExecutable*>(executable)->startColumn();
944 }
945 RELEASE_ASSERT_NOT_REACHED();
946 return std::numeric_limits<unsigned>::max();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000947}
948
keith_miller@apple.comc493f8e02021-10-11 16:58:40 +0000949SourceID SamplingProfiler::StackFrame::sourceID()
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000950{
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000951 switch (frameType) {
952 case FrameType::Unknown:
953 case FrameType::Host:
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000954 case FrameType::RegExp:
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000955 case FrameType::C:
956 case FrameType::Wasm:
ysuzuki@apple.com9f19c432021-07-15 18:29:47 +0000957 return internalSourceID;
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000958
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000959 case FrameType::Executable:
960 if (executable->isHostFunction())
ysuzuki@apple.com9f19c432021-07-15 18:29:47 +0000961 return internalSourceID;
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000962
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000963 return static_cast<ScriptExecutable*>(executable)->sourceID();
964 }
965 RELEASE_ASSERT_NOT_REACHED();
ysuzuki@apple.com9f19c432021-07-15 18:29:47 +0000966 return internalSourceID;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000967}
968
969String SamplingProfiler::StackFrame::url()
970{
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000971 switch (frameType) {
972 case FrameType::Unknown:
973 case FrameType::Host:
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +0000974 case FrameType::RegExp:
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000975 case FrameType::C:
976 case FrameType::Wasm:
utatane.tea@gmail.com988a3752018-01-29 10:43:13 +0000977 return emptyString();
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000978 case FrameType::Executable:
979 if (executable->isHostFunction())
980 return emptyString();
jlewis3@apple.com5d712de2018-01-29 17:47:30 +0000981
ysuzuki@apple.com32e43052019-08-02 22:58:09 +0000982 String url = static_cast<ScriptExecutable*>(executable)->sourceURL();
983 if (url.isEmpty())
984 return static_cast<ScriptExecutable*>(executable)->source().provider()->sourceURLDirective(); // Fall back to sourceURL directive.
985 return url;
986 }
987 RELEASE_ASSERT_NOT_REACHED();
988 return String();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000989}
990
cdumez@apple.com2f481692021-05-29 21:14:39 +0000991Vector<SamplingProfiler::StackTrace> SamplingProfiler::releaseStackTraces()
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000992{
993 ASSERT(m_lock.isLocked());
994 {
995 HeapIterationScope heapIterationScope(m_vm.heap);
cdumez@apple.com2f481692021-05-29 21:14:39 +0000996 processUnverifiedStackTraces();
sbarati@apple.com806ca4a2016-01-20 21:51:00 +0000997 }
998
sbarati@apple.comd3d0c002016-01-30 01:11:05 +0000999 Vector<StackTrace> result(WTFMove(m_stackTraces));
cdumez@apple.com2f481692021-05-29 21:14:39 +00001000 clearData();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +00001001 return result;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +00001002}
1003
1004String SamplingProfiler::stackTracesAsJSON()
1005{
mark.lam@apple.com6b810292021-11-11 17:31:37 +00001006 DeferGC deferGC(m_vm);
cdumez@apple.com6c3db6c2021-05-22 03:13:17 +00001007 Locker locker { m_lock };
sbarati@apple.com806ca4a2016-01-20 21:51:00 +00001008
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001009 {
1010 HeapIterationScope heapIterationScope(m_vm.heap);
cdumez@apple.com2f481692021-05-29 21:14:39 +00001011 processUnverifiedStackTraces();
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001012 }
1013
1014 StringBuilder json;
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +00001015 json.append('[');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001016
1017 bool loopedOnce = false;
1018 auto comma = [&] {
1019 if (loopedOnce)
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +00001020 json.append(',');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001021 };
sbarati@apple.com806ca4a2016-01-20 21:51:00 +00001022 for (StackTrace& stackTrace : m_stackTraces) {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001023 comma();
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +00001024 json.append('[');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001025 loopedOnce = false;
sbarati@apple.com806ca4a2016-01-20 21:51:00 +00001026 for (StackFrame& stackFrame : stackTrace.frames) {
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001027 comma();
mark.lam@apple.comf0515572019-02-15 21:41:15 +00001028 json.appendQuotedJSONString(stackFrame.displayNameForJSONTests(m_vm));
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001029 loopedOnce = true;
1030 }
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +00001031 json.append(']');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001032 loopedOnce = true;
1033 }
1034
commit-queue@webkit.org13c7bcc2016-03-02 04:30:45 +00001035 json.append(']');
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001036
cdumez@apple.com2f481692021-05-29 21:14:39 +00001037 clearData();
sbarati@apple.comd3d0c002016-01-30 01:11:05 +00001038
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001039 return json.toString();
1040}
1041
keith_miller@apple.com3517aa42016-05-24 23:03:09 +00001042void SamplingProfiler::registerForReportAtExit()
1043{
utatane.tea@gmail.come71a8722018-04-05 17:22:21 +00001044 static Lock registrationLock;
keith_miller@apple.com3517aa42016-05-24 23:03:09 +00001045 static HashSet<RefPtr<SamplingProfiler>>* profilesToReport;
1046
cdumez@apple.com3e035a82021-05-22 16:49:42 +00001047 Locker locker { registrationLock };
keith_miller@apple.com3517aa42016-05-24 23:03:09 +00001048
1049 if (!profilesToReport) {
1050 profilesToReport = new HashSet<RefPtr<SamplingProfiler>>();
1051 atexit([]() {
ddkilzer@apple.comfa7f50c2019-09-21 11:39:57 +00001052 for (const auto& profile : *profilesToReport)
keith_miller@apple.com3517aa42016-05-24 23:03:09 +00001053 profile->reportDataToOptionFile();
1054 });
1055 }
1056
1057 profilesToReport->add(adoptRef(this));
1058 m_needsReportAtExit = true;
1059}
1060
1061void SamplingProfiler::reportDataToOptionFile()
1062{
1063 if (m_needsReportAtExit) {
1064 m_needsReportAtExit = false;
tzagallo@apple.coma4c49222019-09-16 20:48:37 +00001065 JSLockHolder holder(m_vm);
keith_miller@apple.com3517aa42016-05-24 23:03:09 +00001066 const char* path = Options::samplingProfilerPath();
1067 StringPrintStream pathOut;
1068 pathOut.print(path, "/");
1069 pathOut.print("JSCSampilingProfile-", reinterpret_cast<uintptr_t>(this), ".txt");
1070 auto out = FilePrintStream::open(pathOut.toCString().data(), "w");
1071 reportTopFunctions(*out);
1072 reportTopBytecodes(*out);
1073 }
1074}
1075
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001076void SamplingProfiler::reportTopFunctions()
1077{
keith_miller@apple.com3517aa42016-05-24 23:03:09 +00001078 reportTopFunctions(WTF::dataFile());
1079}
1080
1081void SamplingProfiler::reportTopFunctions(PrintStream& out)
1082{
cdumez@apple.com6c3db6c2021-05-22 03:13:17 +00001083 Locker locker { m_lock };
mark.lam@apple.com6b810292021-11-11 17:31:37 +00001084 DeferGCForAWhile deferGC(m_vm);
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001085
1086 {
1087 HeapIterationScope heapIterationScope(m_vm.heap);
cdumez@apple.com2f481692021-05-29 21:14:39 +00001088 processUnverifiedStackTraces();
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001089 }
1090
keith_miller@apple.com5eaa04d2020-11-09 21:18:08 +00001091 size_t totalSamples = 0;
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001092 HashMap<String, size_t> functionCounts;
1093 for (StackTrace& stackTrace : m_stackTraces) {
1094 if (!stackTrace.frames.size())
1095 continue;
1096
1097 StackFrame& frame = stackTrace.frames.first();
sbarati@apple.comc7ad3d12020-09-03 22:47:33 +00001098 String hash = ""_s;
1099 if (frame.semanticLocation.hasCodeBlockHash()) {
1100 StringPrintStream stream;
1101 frame.semanticLocation.codeBlockHash.dump(stream);
1102 hash = stream.toString();
1103 } else
1104 hash = "<nil>"_s;
ysuzuki@apple.com319fc4c2021-10-17 04:52:41 +00001105 SourceID sourceID = frame.sourceID();
ysuzuki@apple.com9f19c432021-07-15 18:29:47 +00001106 if (Options::samplingProfilerIgnoreExternalSourceID()) {
1107 if (sourceID != internalSourceID)
1108 sourceID = aggregatedExternalSourceID;
1109 }
1110 auto frameDescription = makeString(frame.displayName(m_vm), '#', hash, ':', sourceID);
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001111 functionCounts.add(frameDescription, 0).iterator->value++;
keith_miller@apple.com5eaa04d2020-11-09 21:18:08 +00001112 totalSamples++;
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001113 }
1114
1115 auto takeMax = [&] () -> std::pair<String, size_t> {
1116 String maxFrameDescription;
1117 size_t maxFrameCount = 0;
ddkilzer@apple.comfa7f50c2019-09-21 11:39:57 +00001118 for (const auto& entry : functionCounts) {
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001119 if (entry.value > maxFrameCount) {
1120 maxFrameCount = entry.value;
1121 maxFrameDescription = entry.key;
1122 }
1123 }
1124 if (!maxFrameDescription.isEmpty())
1125 functionCounts.remove(maxFrameDescription);
1126 return std::make_pair(maxFrameDescription, maxFrameCount);
1127 };
1128
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001129 if (Options::samplingProfilerTopFunctionsCount()) {
keith_miller@apple.com5eaa04d2020-11-09 21:18:08 +00001130 out.println("\n\nSampling rate: ", m_timingInterval.microseconds(), " microseconds. Total samples: ", totalSamples);
1131 out.println("Top functions as <numSamples 'functionName#hash:sourceID'>");
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001132 for (size_t i = 0; i < Options::samplingProfilerTopFunctionsCount(); i++) {
1133 auto pair = takeMax();
1134 if (pair.first.isEmpty())
1135 break;
1136 out.printf("%6zu ", pair.second);
keith_miller@apple.com5eaa04d2020-11-09 21:18:08 +00001137 out.println(" '", pair.first, "'");
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001138 }
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001139 }
1140}
1141
1142void SamplingProfiler::reportTopBytecodes()
1143{
keith_miller@apple.com3517aa42016-05-24 23:03:09 +00001144 reportTopBytecodes(WTF::dataFile());
1145}
1146
1147void SamplingProfiler::reportTopBytecodes(PrintStream& out)
1148{
cdumez@apple.com6c3db6c2021-05-22 03:13:17 +00001149 Locker locker { m_lock };
mark.lam@apple.com6b810292021-11-11 17:31:37 +00001150 DeferGCForAWhile deferGC(m_vm);
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001151
1152 {
1153 HeapIterationScope heapIterationScope(m_vm.heap);
cdumez@apple.com2f481692021-05-29 21:14:39 +00001154 processUnverifiedStackTraces();
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001155 }
1156
keith_miller@apple.com5eaa04d2020-11-09 21:18:08 +00001157 size_t totalSamples = 0;
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001158 HashMap<String, size_t> bytecodeCounts;
sbarati@apple.com783a68c2021-04-29 17:42:13 +00001159 HashMap<String, size_t> tierCounts;
1160
1161 String llint = "LLInt"_s;
1162 String baseline = "Baseline"_s;
1163 String dfg = "DFG"_s;
1164 String ftl = "FTL"_s;
1165 String builtin = "js builtin"_s;
1166 String wasm = "Wasm"_s;
1167 String host = "Host"_s;
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +00001168 String regexp = "RegExp"_s;
sbarati@apple.com783a68c2021-04-29 17:42:13 +00001169 String cpp = "C/C++"_s;
1170 String unknownFrame = "Unknown Frame"_s;
1171 String unknownExecutable = "Unknown Executable"_s;
1172
1173 auto forEachTier = [&] (auto func) {
1174 func(llint);
1175 func(baseline);
1176 func(dfg);
1177 func(ftl);
1178 func(builtin);
1179 func(wasm);
1180 func(host);
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +00001181 func(regexp);
sbarati@apple.com783a68c2021-04-29 17:42:13 +00001182 func(cpp);
1183 func(unknownFrame);
1184 func(unknownExecutable);
1185 };
1186
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001187 for (StackTrace& stackTrace : m_stackTraces) {
1188 if (!stackTrace.frames.size())
1189 continue;
1190
sbarati@apple.comf4eb0222021-12-12 00:39:57 +00001191 auto descriptionForLocation = [&] (StackFrame::CodeLocation location, std::optional<Wasm::CompilationMode> wasmCompilationMode, BytecodeIndex wasmOffset) -> String {
1192 if (wasmCompilationMode) {
1193 StringPrintStream description;
1194 description.print(":");
1195 description.print(Wasm::makeString(wasmCompilationMode.value()));
1196 description.print(":");
1197 if (wasmOffset) {
1198 uintptr_t offset = wasmOffset.offset();
mark.lam@apple.combe73dca2022-06-19 04:07:37 +00001199 description.print(RawHex(offset));
sbarati@apple.comf4eb0222021-12-12 00:39:57 +00001200 } else
1201 description.print("nil");
1202 return description.toString();
1203 }
1204
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001205 String bytecodeIndex;
1206 String codeBlockHash;
ysuzuki@apple.com32e43052019-08-02 22:58:09 +00001207 String jitType;
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001208 if (location.hasBytecodeIndex())
keith_miller@apple.com0f985ec2019-10-23 00:55:38 +00001209 bytecodeIndex = toString(location.bytecodeIndex);
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001210 else
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +00001211 bytecodeIndex = "<nil>"_s;
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001212
1213 if (location.hasCodeBlockHash()) {
1214 StringPrintStream stream;
1215 location.codeBlockHash.dump(stream);
1216 codeBlockHash = stream.toString();
1217 } else
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +00001218 codeBlockHash = "<nil>"_s;
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001219
sbarati@apple.comf4eb0222021-12-12 00:39:57 +00001220 if (location.isRegExp)
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +00001221 jitType = "RegExp"_s;
ysuzuki@apple.com32e43052019-08-02 22:58:09 +00001222 else
1223 jitType = JITCode::typeName(location.jitType);
1224
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +00001225 return makeString('#', codeBlockHash, ':', jitType, ':', bytecodeIndex);
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001226 };
1227
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001228 StackFrame& frame = stackTrace.frames.first();
sbarati@apple.comf4eb0222021-12-12 00:39:57 +00001229 auto frameDescription = makeString(frame.displayName(m_vm), descriptionForLocation(frame.semanticLocation, frame.wasmCompilationMode, frame.wasmOffset));
darin@apple.coma4ddc782021-05-30 16:11:40 +00001230 if (std::optional<std::pair<StackFrame::CodeLocation, CodeBlock*>> machineLocation = frame.machineLocation) {
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001231 frameDescription = makeString(frameDescription, " <-- ",
sbarati@apple.comf4eb0222021-12-12 00:39:57 +00001232 machineLocation->second->inferredName().data(), descriptionForLocation(machineLocation->first, std::nullopt, BytecodeIndex()));
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001233 }
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001234 bytecodeCounts.add(frameDescription, 0).iterator->value++;
sbarati@apple.com783a68c2021-04-29 17:42:13 +00001235
1236 {
1237 String tierName;
1238 switch (frame.frameType) {
1239 case SamplingProfiler::FrameType::Executable:
1240 switch (frame.semanticLocation.jitType) {
1241 case JITType::HostCallThunk:
1242 tierName = host;
1243 break;
1244 case JITType::InterpreterThunk:
1245 tierName = llint;
1246 break;
1247 case JITType::BaselineJIT:
1248 tierName = baseline;
1249 break;
1250 case JITType::DFGJIT:
1251 tierName = dfg;
1252 break;
1253 case JITType::FTLJIT:
1254 tierName = ftl;
1255 break;
1256 default:
1257 tierName = unknownExecutable;
1258 break;
1259 }
1260
1261 if (frame.executable) {
ysuzuki@apple.com984775d2022-04-16 00:00:35 +00001262 if (auto* executable = jsDynamicCast<FunctionExecutable*>(frame.executable)) {
sbarati@apple.com783a68c2021-04-29 17:42:13 +00001263 if (executable->isBuiltinFunction())
1264 tierCounts.add(builtin, 0).iterator->value++;
1265 }
1266 }
1267
1268 break;
1269 case SamplingProfiler::FrameType::Wasm:
1270 tierName = wasm;
1271 break;
1272 case SamplingProfiler::FrameType::Host:
1273 tierName = host;
1274 break;
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +00001275 case SamplingProfiler::FrameType::RegExp:
1276 tierName = regexp;
1277 break;
sbarati@apple.com783a68c2021-04-29 17:42:13 +00001278 case SamplingProfiler::FrameType::C:
1279 tierName = cpp;
1280 break;
1281 case SamplingProfiler::FrameType::Unknown:
1282 tierName = unknownFrame;
1283 break;
1284 }
1285
1286 tierCounts.add(tierName, 0).iterator->value++;
1287 }
1288
keith_miller@apple.com5eaa04d2020-11-09 21:18:08 +00001289 totalSamples++;
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001290 }
1291
1292 auto takeMax = [&] () -> std::pair<String, size_t> {
1293 String maxFrameDescription;
1294 size_t maxFrameCount = 0;
ddkilzer@apple.comfa7f50c2019-09-21 11:39:57 +00001295 for (const auto& entry : bytecodeCounts) {
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001296 if (entry.value > maxFrameCount) {
1297 maxFrameCount = entry.value;
1298 maxFrameDescription = entry.key;
1299 }
1300 }
1301 if (!maxFrameDescription.isEmpty())
1302 bytecodeCounts.remove(maxFrameDescription);
1303 return std::make_pair(maxFrameDescription, maxFrameCount);
1304 };
1305
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001306 if (Options::samplingProfilerTopBytecodesCount()) {
sbarati@apple.com783a68c2021-04-29 17:42:13 +00001307 out.println("\n\nSampling rate: ", m_timingInterval.microseconds(), " microseconds. Total samples: ", totalSamples, "\n");
1308
1309 out.println("Tier breakdown:");
1310 out.println("-----------------------------------");
1311 unsigned maxTierNameLength = 0;
1312 forEachTier([&] (String tier) {
1313 maxTierNameLength = std::max(maxTierNameLength, tier.length());
1314 });
1315 auto printTier = [&] (String tier) {
1316 size_t count = tierCounts.get(tier);
1317 if (!count && (tier == unknownFrame || tier == unknownExecutable))
1318 return;
1319 out.print(tier, ": ");
1320 for (unsigned i = 0; i < maxTierNameLength + 2 - tier.length(); ++i)
1321 out.print(" ");
1322 out.printf("%6zu ", count);
1323 out.println(" (", (static_cast<double>(count) / static_cast<double>(totalSamples)) * 100, "%)");
1324 };
1325 forEachTier(printTier);
1326 out.println("\n");
1327
keith_miller@apple.com5eaa04d2020-11-09 21:18:08 +00001328 out.println("Hottest bytecodes as <numSamples 'functionName#hash:JITType:bytecodeIndex'>");
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001329 for (size_t i = 0; i < Options::samplingProfilerTopBytecodesCount(); i++) {
1330 auto pair = takeMax();
1331 if (pair.first.isEmpty())
1332 break;
1333 out.printf("%6zu ", pair.second);
keith_miller@apple.com5eaa04d2020-11-09 21:18:08 +00001334 out.println(" '", pair.first, "'");
sbarati@apple.com65f8e6e2017-01-28 01:04:06 +00001335 }
sbarati@apple.com10ec78a2016-04-20 02:24:53 +00001336 }
1337}
1338
carlosgc@webkit.org12f14832020-09-01 08:55:06 +00001339Thread* SamplingProfiler::thread() const
commit-queue@webkit.orgb181ba92019-01-25 23:45:04 +00001340{
carlosgc@webkit.org12f14832020-09-01 08:55:06 +00001341 return m_thread.get();
commit-queue@webkit.orgb181ba92019-01-25 23:45:04 +00001342}
commit-queue@webkit.orgb181ba92019-01-25 23:45:04 +00001343
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001344} // namespace JSC
1345
1346namespace WTF {
1347
1348using namespace JSC;
1349
1350void printInternal(PrintStream& out, SamplingProfiler::FrameType frameType)
1351{
1352 switch (frameType) {
sbarati@apple.comd3d0c002016-01-30 01:11:05 +00001353 case SamplingProfiler::FrameType::Executable:
1354 out.print("Executable");
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001355 break;
ysuzuki@apple.com32e43052019-08-02 22:58:09 +00001356 case SamplingProfiler::FrameType::Wasm:
1357 out.print("Wasm");
1358 break;
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001359 case SamplingProfiler::FrameType::Host:
1360 out.print("Host");
1361 break;
ysuzuki@apple.com420d9a92021-07-16 04:10:49 +00001362 case SamplingProfiler::FrameType::RegExp:
1363 out.print("RegExp");
1364 break;
keith_miller@apple.comb4a86442017-02-02 01:23:37 +00001365 case SamplingProfiler::FrameType::C:
sbarati@apple.coma4ce86b2016-01-11 06:49:49 +00001366 case SamplingProfiler::FrameType::Unknown:
1367 out.print("Unknown");
1368 break;
1369 }
1370}
1371
1372} // namespace WTF
1373
1374#endif // ENABLE(SAMPLING_PROFILER)