Optimize struct element initialization
[hiphop-php.git] / hphp / runtime / vm / debugger-hook.h
blob190fd01b036f36bb316fa46591c9f5f5a1b8d9f4
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #pragma once
19 #include <functional>
21 #include "hphp/runtime/base/request-info.h"
22 #include "hphp/runtime/base/request-injection-data.h"
23 #include "hphp/runtime/base/surprise-flags.h"
24 #include "hphp/runtime/server/cli-server.h"
25 #include "hphp/runtime/vm/event-hook.h"
26 #include "hphp/runtime/vm/hhbc.h"
27 #include "hphp/runtime/vm/unit.h" // OffsetRangeVec
29 ///////////////////////////////////////////////////////////////////////////////
30 // This is a set of functions which are primarily called from the VM to notify
31 // the debugger about various events. Some of the implementations also interact
32 // with the VM to setup further notifications, though this is not the only place
33 // the debugger interacts directly with the VM.
35 namespace HPHP {
37 namespace Eval { struct HphpdHook; }
39 struct Class;
40 struct DebuggerHook;
41 struct Func;
42 struct ObjectData;
44 // Is this thread being debugged?
45 inline bool isDebuggerAttached(RequestInfo* ti = nullptr) {
46 ti = (ti != nullptr) ? ti : &RI();
47 return ti->m_reqInjectionData.getDebuggerAttached();
50 inline bool requestHasBreakpoints(RequestInjectionData& rid) {
51 return !rid.m_breakPointFilter.isNull() ||
52 !rid.m_lineBreakPointFilter.isNull() ||
53 !rid.m_callBreakPointFilter.isNull() ||
54 !rid.m_retBreakPointFilter.isNull();
57 // Executes the passed code only if there is a debugger attached to the current
58 // thread.
59 #define DEBUGGER_ATTACHED_ONLY(code) do { \
60 if (isDebuggerAttached()) { \
61 code; \
62 } \
63 } while(0) \
65 // This flag ensures two things: first, that we stay in the interpreter and
66 // out of JIT code. Second, that phpDebuggerOpcodeHook will continue to allow
67 // debugger interrupts for every opcode executed (modulo filters.)
68 #define DEBUGGER_FORCE_INTR (RID().getDebuggerForceIntr())
70 enum class StackDepthDisposition {
71 Equal, // Same.
72 Shallower, // Less than baseline.
73 Deeper, // Greater than baseline.
76 ////////////////////////////////////////////////////////////////////////////////
77 // DebuggerHook
78 // A hook for debugger events. Any extension can subclass this class and
79 // attach it to the thread in order to receive debugging events.
80 struct DebuggerHook {
81 DebuggerHook() {}
82 virtual ~DebuggerHook() {}
84 // Attempts to attach an instance of the given debugger hook to the passed
85 // thread. If no thread info is passed, the current one is used. The
86 // template parameter should be an instance of DebuggerHook. Returns true on
87 // success, false on failure (for instance, if another debugger hook is
88 // already attached).
89 template<class HookClass>
90 static bool attach(RequestInfo* ti = nullptr) {
91 ti = (ti != nullptr) ? ti : &RI();
93 if (isDebuggerAttached(ti)) {
94 // Check if this debugger hook is already attached.
95 // TODO: Ideally this wouldn't be necessary here, debuggers should
96 // be well behaved and only attach once per thread. There is at least
97 // one instance where hphpd still needs this. Remove once that's
98 // cleaned up.
99 return dynamic_cast<HookClass*>(ti->m_debuggerHook) != nullptr;
102 // Increment the number of attached hooks.
104 Lock lock(s_lock);
105 auto instance = HookClass::GetInstance();
107 // Once a debugger has attached to any request, it is the only debugger
108 // allowed to attach to subsequent requests until it has detached from
109 // all requests.
110 if (s_activeHook == nullptr) {
111 s_activeHook = instance;
112 } else if (s_activeHook != instance) {
113 return false;
116 // Attach to the thread
117 ti->m_debuggerHook = instance;
119 s_numAttached++;
120 ti->m_reqInjectionData.setDebuggerAttached(true);
121 if (requestHasBreakpoints(ti->m_reqInjectionData) ||
122 !RuntimeOption::ServerExecutionMode() || is_cli_server_mode()) {
123 ti->m_reqInjectionData.setJittingDisabled(true);
124 rl_typeProfileLocals->forceInterpret = true;
125 ti->m_reqInjectionData.updateJit();
130 // Event hooks need to be enabled to receive function entry and exit events.
131 // This comes at the cost of a small bit of performance, however, it makes
132 // the code dealing with line breakpoints much easier. Essentially the
133 // problem is ensuring we only break on a line breakpoint once. We can't
134 // just disable the breakpoint until we leave the site because some opcode
135 // in the site could recurse to the site. So a disable must be attached to
136 // a stack depth. This will be disabled on call to detach().
137 ti->m_reqInjectionData.setFlag(DebuggerHookFlag);
139 return true;
142 // Attempts to set or remove the specified debugger hook as the "active" hook.
143 // The active hook is the only hook that is permitted to attach to any request
144 static bool setActiveDebuggerInstance(DebuggerHook* hook, bool attach) {
145 Lock lock(s_lock);
146 if (attach) {
147 if (s_activeHook != nullptr) {
148 return s_activeHook == hook;
151 s_activeHook = hook;
152 return true;
153 } else {
154 if (s_activeHook != hook) {
155 return false;
158 s_activeHook = nullptr;
159 return true;
163 // If a hook is attached to the thread, detaches it.
164 static void detach(RequestInfo* ti = nullptr);
166 // Debugger events. Subclasses can override these methods to receive
167 // events.
168 virtual void onExceptionThrown(ObjectData* /*exception*/) {}
169 virtual void onExceptionHandle() {}
170 virtual void onError(const ExtendedException& /*ee*/, int /*errnum*/,
171 const std::string& /*message*/) {}
172 virtual void onEval(const Func* /*f*/) {}
173 virtual void onFileLoad(Unit* /*efile*/) {}
174 virtual void onDefClass(const Class* /*cls*/) {}
175 virtual void onDefFunc(const Func* /*func*/) {}
176 virtual void onRegisterFuncIntercept(const String& /*name*/) {}
178 // Called whenever the program counter is at a location that could be
179 // interesting to a debugger. Such as when have hit a registered breakpoint
180 // (regardless of type), when interrupt forcing is enabled, or when the pc is
181 // over an active line breakpoint
182 virtual void onOpcode(const unsigned char* /*pc*/) {}
184 // Called right before top-level pseudo-main enters. This may be useful for
185 // debuggers to initialize separate from an extension as they can assume
186 // other extensions are initialized.
187 virtual void onRequestInit() {}
189 // Called whenever we are breaking due to completion of a step in or step out
190 virtual void onStepInBreak(const Unit* /*unit*/, int /*line*/) {}
191 virtual void onStepOutBreak(const Unit* /*unit*/, int /*line*/) {}
192 virtual void onNextBreak(const Unit* /*unit*/, int /*line*/) {}
194 // Called when we have hit a registered function entry breakpoint
195 virtual void onFuncEntryBreak(const Func* /*f*/) {}
197 // Called when we have hit a registered function exit breakpoint
198 virtual void onFuncExitBreak(const Func* /*f*/) {}
200 // Called when we have hit a registered line breakpoint. Even though a line
201 // spans multiple opcodes, this will only be called once per hit.
202 virtual void onLineBreak(const Unit* /*unit*/, int /*line*/) {}
204 // The number of DebuggerHooks that are currently attached to the process.
205 // The mutex is needed because we need to perform work when we are sure there
206 // are no hooks attached.
207 static Mutex s_lock;
208 static int s_numAttached;
209 static DebuggerHook* s_activeHook;
212 // Returns the current hook.
213 inline DebuggerHook* getDebuggerHook() {
214 return RI().m_debuggerHook;
217 // Is this process being debugged? Since this is across all threads, this cannot
218 // be counted on to be accurate.
219 inline bool isDebuggerAttachedProcess() {
220 return DebuggerHook::s_numAttached > 0;
223 ////////////////////////////////////////////////////////////////////////////////
224 // Hooks
225 // Called by the VM at various points during program execution while
226 // debugging to give the debugger a chance to act. The debugger may block
227 // execution indefinitely within one of these hooks.
228 void phpDebuggerOpcodeHook(const unsigned char* pc);
229 void phpDebuggerRequestInitHook();
230 void phpDebuggerFuncEntryHook(const ActRec* ar);
231 void phpDebuggerFuncExitHook(const ActRec* ar);
232 void phpDebuggerExceptionThrownHook(ObjectData* exception);
233 void phpDebuggerExceptionHandlerHook() noexcept;
234 void phpDebuggerErrorHook(const ExtendedException& ee,
235 int errnum,
236 const std::string& message);
237 void phpDebuggerEvalHook(const Func* f);
238 void phpDebuggerFileLoadHook(Unit* efile);
239 void phpDebuggerDefClassHook(const Class* cls);
240 void phpDebuggerDefFuncHook(const Func* func);
241 void phpDebuggerInterceptRegisterHook(const String& name);
243 ////////////////////////////////////////////////////////////////////////////////
244 // Flow commands
245 // Commands manipulating control flow. Calling any one of these short-cicruits
246 // the others.
248 // Continues execution until the next breakpoint or program exit
249 void phpDebuggerContinue();
251 // Steps a single line, stepping into functions if necessary. If the current
252 // site is invalid, the break will occur on the next valid opcode.
253 void phpDebuggerStepIn();
255 // Steps until the current function returns. Breaks on the opcode following the
256 // return site.
257 void phpDebuggerStepOut();
259 // Steps a single line, stepping over functions if necessary. If the current
260 // site is invalid, the break will occur on the next valid opcode.
261 void phpDebuggerNext();
263 ////////////////////////////////////////////////////////////////////////////////
264 // Breakpoint manipulation
266 // Add breakpoints of various types
267 void phpAddBreakPoint(const Func* f, Offset offset);
268 void phpAddBreakPointFuncEntry(const Func* f);
269 void phpAddBreakPointFuncExit(const Func* f);
270 // Returns false if the line is invalid
271 bool phpAddBreakPointLine(const Unit* u, int line);
273 // Breakpoint removal functions.
274 // FIXME Note that internally there is a global PCFilter for all breakpoints.
275 // This is checked against every opcode to determine if we should break. While
276 // good for performance, this allows no distinction between the types of
277 // breakpoints. That is, we can never remove breakpoints from the
278 // global filter because there could be overlap.
280 // If the new style of breakpoints is used (function breakpoints and lines), we
281 // can at least prevent the appropriate breakpoint hooks from being called.
282 // This means once added, we will always interrupt on that breakpoint until the
283 // hook is detached. This isn't a huge deal as it just means we can't jit those
284 // opcodes, but it should be fixed since it's just a design issue.
285 void phpRemoveBreakPoint(const Func* f, Offset offset);
286 void phpRemoveBreakPointFuncEntry(const Func* f);
287 void phpRemoveBreakPointFuncExit(const Func* f);
288 void phpRemoveBreakPointLine(const Unit* u, int line);
290 bool phpHasBreakpoint(const Func* f, Offset offset);
292 StackDepthDisposition getStackDisposition(int baseline);
294 PCFilter* getBreakPointFilter();
295 PCFilter* getFlowFilter();
297 String getCurrentFilePath(int* pLine);