1/*
2 * Copyright (C) 2016 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#if ENABLE(SAMPLING_PROFILER)
29
30#include "CallFrame.h"
31#include "CodeBlockHash.h"
32#include "JITCode.h"
33#include "MachineStackMarker.h"
34#include "WasmCompilationMode.h"
35#include "WasmIndexOrName.h"
36#include <wtf/HashSet.h>
37#include <wtf/Lock.h>
38#include <wtf/Stopwatch.h>
39#include <wtf/Vector.h>
40#include <wtf/WeakRandom.h>
41
42namespace JSC {
43
44class VM;
45class ExecutableBase;
46
47class SamplingProfiler : public ThreadSafeRefCounted<SamplingProfiler> {
48 WTF_MAKE_FAST_ALLOCATED;
49public:
50
51 struct UnprocessedStackFrame {
52 UnprocessedStackFrame(CodeBlock* codeBlock, CalleeBits callee, CallSiteIndex callSiteIndex)
53 : unverifiedCallee(callee)
54 , verifiedCodeBlock(codeBlock)
55 , callSiteIndex(callSiteIndex)
56 { }
57
58 UnprocessedStackFrame(const void* pc)
59 : cCodePC(pc)
60 { }
61
62 UnprocessedStackFrame() = default;
63
64 const void* cCodePC { nullptr };
65 CalleeBits unverifiedCallee;
66 CodeBlock* verifiedCodeBlock { nullptr };
67 CallSiteIndex callSiteIndex;
68#if ENABLE(WEBASSEMBLY)
69 Optional<Wasm::IndexOrName> wasmIndexOrName;
70#endif
71 Optional<Wasm::CompilationMode> wasmCompilationMode;
72 };
73
74 enum class FrameType {
75 Executable,
76 Wasm,
77 Host,
78 C,
79 Unknown,
80 };
81
82 struct StackFrame {
83 StackFrame(ExecutableBase* executable)
84 : frameType(FrameType::Executable)
85 , executable(executable)
86 { }
87
88 StackFrame()
89 { }
90
91 FrameType frameType { FrameType::Unknown };
92 const void* cCodePC { nullptr };
93 ExecutableBase* executable { nullptr };
94 JSObject* callee { nullptr };
95#if ENABLE(WEBASSEMBLY)
96 Optional<Wasm::IndexOrName> wasmIndexOrName;
97#endif
98 Optional<Wasm::CompilationMode> wasmCompilationMode;
99
100 struct CodeLocation {
101 bool hasCodeBlockHash() const
102 {
103 return codeBlockHash.isSet();
104 }
105
106 bool hasBytecodeIndex() const
107 {
108 return !!bytecodeIndex;
109 }
110
111 bool hasExpressionInfo() const
112 {
113 return lineNumber != std::numeric_limits<unsigned>::max()
114 && columnNumber != std::numeric_limits<unsigned>::max();
115 }
116
117 // These attempt to be expression-level line and column number.
118 unsigned lineNumber { std::numeric_limits<unsigned>::max() };
119 unsigned columnNumber { std::numeric_limits<unsigned>::max() };
120 BytecodeIndex bytecodeIndex;
121 CodeBlockHash codeBlockHash;
122 JITType jitType { JITType::None };
123 };
124
125 CodeLocation semanticLocation;
126 Optional<std::pair<CodeLocation, CodeBlock*>> machineLocation; // This is non-null if we were inlined. It represents the machine frame we were inlined into.
127
128 bool hasExpressionInfo() const { return semanticLocation.hasExpressionInfo(); }
129 unsigned lineNumber() const
130 {
131 ASSERT(hasExpressionInfo());
132 return semanticLocation.lineNumber;
133 }
134 unsigned columnNumber() const
135 {
136 ASSERT(hasExpressionInfo());
137 return semanticLocation.columnNumber;
138 }
139
140 // These are function-level data.
141 String nameFromCallee(VM&);
142 String displayName(VM&);
143 String displayNameForJSONTests(VM&); // Used for JSC stress tests because they want the "(anonymous function)" string for anonymous functions and they want "(eval)" for eval'd code.
144 int functionStartLine();
145 unsigned functionStartColumn();
146 intptr_t sourceID();
147 String url();
148 };
149
150 struct UnprocessedStackTrace {
151 Seconds timestamp;
152 void* topPC;
153 bool topFrameIsLLInt;
154 void* llintPC;
155 Vector<UnprocessedStackFrame> frames;
156 };
157
158 struct StackTrace {
159 Seconds timestamp;
160 Vector<StackFrame> frames;
161 StackTrace()
162 { }
163 StackTrace(StackTrace&& other)
164 : timestamp(other.timestamp)
165 , frames(WTFMove(other.frames))
166 { }
167 };
168
169 SamplingProfiler(VM&, RefPtr<Stopwatch>&&);
170 ~SamplingProfiler();
171 void noticeJSLockAcquisition();
172 void noticeVMEntry();
173 void shutdown();
174 void visit(SlotVisitor&);
175 Lock& getLock() { return m_lock; }
176 void setTimingInterval(Seconds interval) { m_timingInterval = interval; }
177 JS_EXPORT_PRIVATE void start();
178 void start(const AbstractLocker&);
179 Vector<StackTrace> releaseStackTraces(const AbstractLocker&);
180 JS_EXPORT_PRIVATE String stackTracesAsJSON();
181 JS_EXPORT_PRIVATE void noticeCurrentThreadAsJSCExecutionThread();
182 void noticeCurrentThreadAsJSCExecutionThread(const AbstractLocker&);
183 void processUnverifiedStackTraces(const AbstractLocker&);
184 void setStopWatch(const AbstractLocker&, Ref<Stopwatch>&& stopwatch) { m_stopwatch = WTFMove(stopwatch); }
185 void pause(const AbstractLocker&);
186 void clearData(const AbstractLocker&);
187
188 // Used for debugging in the JSC shell/DRT.
189 void registerForReportAtExit();
190 void reportDataToOptionFile();
191 JS_EXPORT_PRIVATE void reportTopFunctions();
192 JS_EXPORT_PRIVATE void reportTopFunctions(PrintStream&);
193 JS_EXPORT_PRIVATE void reportTopBytecodes();
194 JS_EXPORT_PRIVATE void reportTopBytecodes(PrintStream&);
195
196#if OS(DARWIN)
197 JS_EXPORT_PRIVATE mach_port_t machThread();
198#endif
199
200private:
201 void createThreadIfNecessary(const AbstractLocker&);
202 void timerLoop();
203 void takeSample(const AbstractLocker&, Seconds& stackTraceProcessingTime);
204
205 Lock m_lock;
206 bool m_isPaused;
207 bool m_isShutDown;
208 bool m_needsReportAtExit { false };
209 VM& m_vm;
210 WeakRandom m_weakRandom;
211 RefPtr<Stopwatch> m_stopwatch;
212 Vector<StackTrace> m_stackTraces;
213 Vector<UnprocessedStackTrace> m_unprocessedStackTraces;
214 Seconds m_timingInterval;
215 Seconds m_lastTime;
216 RefPtr<Thread> m_thread;
217 RefPtr<Thread> m_jscExecutionThread;
218 HashSet<JSCell*> m_liveCellPointers;
219 Vector<UnprocessedStackFrame> m_currentFrames;
220};
221
222} // namespace JSC
223
224namespace WTF {
225
226void printInternal(PrintStream&, JSC::SamplingProfiler::FrameType);
227
228} // namespace WTF
229
230#endif // ENABLE(SAMPLING_PROFILER)
231