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 <wtf/HashSet.h>
35#include <wtf/Lock.h>
36#include <wtf/Stopwatch.h>
37#include <wtf/Vector.h>
38#include <wtf/WeakRandom.h>
39
40namespace JSC {
41
42class VM;
43class ExecutableBase;
44
45class SamplingProfiler : public ThreadSafeRefCounted<SamplingProfiler> {
46 WTF_MAKE_FAST_ALLOCATED;
47public:
48
49 struct UnprocessedStackFrame {
50 UnprocessedStackFrame(CodeBlock* codeBlock, CalleeBits callee, CallSiteIndex callSiteIndex)
51 : unverifiedCallee(callee)
52 , verifiedCodeBlock(codeBlock)
53 , callSiteIndex(callSiteIndex)
54 { }
55
56 UnprocessedStackFrame(const void* pc)
57 : cCodePC(pc)
58 { }
59
60 UnprocessedStackFrame() = default;
61
62 const void* cCodePC { nullptr };
63 CalleeBits unverifiedCallee;
64 CodeBlock* verifiedCodeBlock { nullptr };
65 CallSiteIndex callSiteIndex;
66 };
67
68 enum class FrameType {
69 Executable,
70 Host,
71 C,
72 Unknown
73 };
74
75 struct StackFrame {
76 StackFrame(ExecutableBase* executable)
77 : frameType(FrameType::Executable)
78 , executable(executable)
79 { }
80
81 StackFrame()
82 { }
83
84 FrameType frameType { FrameType::Unknown };
85 const void* cCodePC { nullptr };
86 ExecutableBase* executable { nullptr };
87 JSObject* callee { nullptr };
88
89 struct CodeLocation {
90 bool hasCodeBlockHash() const
91 {
92 return codeBlockHash.isSet();
93 }
94
95 bool hasBytecodeIndex() const
96 {
97 return bytecodeIndex != std::numeric_limits<unsigned>::max();
98 }
99
100 bool hasExpressionInfo() const
101 {
102 return lineNumber != std::numeric_limits<unsigned>::max()
103 && columnNumber != std::numeric_limits<unsigned>::max();
104 }
105
106 // These attempt to be expression-level line and column number.
107 unsigned lineNumber { std::numeric_limits<unsigned>::max() };
108 unsigned columnNumber { std::numeric_limits<unsigned>::max() };
109 unsigned bytecodeIndex { std::numeric_limits<unsigned>::max() };
110 CodeBlockHash codeBlockHash;
111 JITType jitType { JITType::None };
112 };
113
114 CodeLocation semanticLocation;
115 Optional<std::pair<CodeLocation, CodeBlock*>> machineLocation; // This is non-null if we were inlined. It represents the machine frame we were inlined into.
116
117 bool hasExpressionInfo() const { return semanticLocation.hasExpressionInfo(); }
118 unsigned lineNumber() const
119 {
120 ASSERT(hasExpressionInfo());
121 return semanticLocation.lineNumber;
122 }
123 unsigned columnNumber() const
124 {
125 ASSERT(hasExpressionInfo());
126 return semanticLocation.columnNumber;
127 }
128
129 // These are function-level data.
130 String nameFromCallee(VM&);
131 String displayName(VM&);
132 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.
133 int functionStartLine();
134 unsigned functionStartColumn();
135 intptr_t sourceID();
136 String url();
137 };
138
139 struct UnprocessedStackTrace {
140 Seconds timestamp;
141 void* topPC;
142 bool topFrameIsLLInt;
143 void* llintPC;
144 Vector<UnprocessedStackFrame> frames;
145 };
146
147 struct StackTrace {
148 Seconds timestamp;
149 Vector<StackFrame> frames;
150 StackTrace()
151 { }
152 StackTrace(StackTrace&& other)
153 : timestamp(other.timestamp)
154 , frames(WTFMove(other.frames))
155 { }
156 };
157
158 SamplingProfiler(VM&, RefPtr<Stopwatch>&&);
159 ~SamplingProfiler();
160 void noticeJSLockAcquisition();
161 void noticeVMEntry();
162 void shutdown();
163 void visit(SlotVisitor&);
164 Lock& getLock() { return m_lock; }
165 void setTimingInterval(Seconds interval) { m_timingInterval = interval; }
166 JS_EXPORT_PRIVATE void start();
167 void start(const AbstractLocker&);
168 Vector<StackTrace> releaseStackTraces(const AbstractLocker&);
169 JS_EXPORT_PRIVATE String stackTracesAsJSON();
170 JS_EXPORT_PRIVATE void noticeCurrentThreadAsJSCExecutionThread();
171 void noticeCurrentThreadAsJSCExecutionThread(const AbstractLocker&);
172 void processUnverifiedStackTraces(); // You should call this only after acquiring the lock.
173 void setStopWatch(const AbstractLocker&, Ref<Stopwatch>&& stopwatch) { m_stopwatch = WTFMove(stopwatch); }
174 void pause(const AbstractLocker&);
175 void clearData(const AbstractLocker&);
176
177 // Used for debugging in the JSC shell/DRT.
178 void registerForReportAtExit();
179 void reportDataToOptionFile();
180 JS_EXPORT_PRIVATE void reportTopFunctions();
181 JS_EXPORT_PRIVATE void reportTopFunctions(PrintStream&);
182 JS_EXPORT_PRIVATE void reportTopBytecodes();
183 JS_EXPORT_PRIVATE void reportTopBytecodes(PrintStream&);
184
185#if OS(DARWIN)
186 JS_EXPORT_PRIVATE mach_port_t machThread();
187#endif
188
189private:
190 void createThreadIfNecessary(const AbstractLocker&);
191 void timerLoop();
192 void takeSample(const AbstractLocker&, Seconds& stackTraceProcessingTime);
193
194 Lock m_lock;
195 bool m_isPaused;
196 bool m_isShutDown;
197 bool m_needsReportAtExit { false };
198 VM& m_vm;
199 WeakRandom m_weakRandom;
200 RefPtr<Stopwatch> m_stopwatch;
201 Vector<StackTrace> m_stackTraces;
202 Vector<UnprocessedStackTrace> m_unprocessedStackTraces;
203 Seconds m_timingInterval;
204 Seconds m_lastTime;
205 RefPtr<Thread> m_thread;
206 RefPtr<Thread> m_jscExecutionThread;
207 HashSet<JSCell*> m_liveCellPointers;
208 Vector<UnprocessedStackFrame> m_currentFrames;
209};
210
211} // namespace JSC
212
213namespace WTF {
214
215void printInternal(PrintStream&, JSC::SamplingProfiler::FrameType);
216
217} // namespace WTF
218
219#endif // ENABLE(SAMPLING_PROFILER)
220