2 +----------------------------------------------------------------------+
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 #include "hphp/runtime/debugger/debugger_hook_handler.h"
19 namespace HPHP::Eval
{
21 //////////////////////////////////////////////////////////////////////////
23 TRACE_SET_MOD(debuggerflow
);
25 // See if the given name matches the function's name.
26 static bool matchFunctionName(std::string name
, const Func
* f
) {
27 return name
== f
->name()->data();
30 static void addBreakPointInUnit(BreakPointInfoPtr bp
, Unit
* unit
) {
31 OffsetFuncRangeVec offsets
;
32 if (!unit
->getOffsetRanges(bp
->m_line1
, offsets
) || offsets
.size() == 0) {
33 bp
->m_bindState
= BreakPointInfo::KnownToBeInvalid
;
36 bp
->m_bindState
= BreakPointInfo::KnownToBeValid
;
37 TRACE(3, "Add to breakpoint filter for %s:%d, unit %p:\n",
38 unit
->filepath()->data(), bp
->m_line1
, unit
);
40 assertx(offsets
.size() > 0);
41 assertx(offsets
[0].second
.size() > 0);
42 auto func
= offsets
[0].first
;
43 auto bpOffset
= offsets
[0].second
[0].base
;
44 phpAddBreakPoint(func
, bpOffset
);
47 void proxySetBreakPoints(DebuggerProxy
* proxy
) {
48 std::vector
<BreakPointInfoPtr
> bps
;
49 proxy
->getBreakPoints(bps
);
50 for (unsigned int i
= 0; i
< bps
.size(); i
++) {
51 BreakPointInfoPtr bp
= bps
[i
];
52 bp
->m_bindState
= BreakPointInfo::Unknown
;
53 auto className
= bp
->getClass();
54 if (!className
.empty()) {
55 auto clsName
= makeStaticString(className
);
56 auto cls
= Class::lookup(clsName
);
57 if (cls
== nullptr) continue;
58 bp
->m_bindState
= BreakPointInfo::KnownToBeInvalid
;
59 size_t numFuncs
= cls
->numMethods();
60 if (numFuncs
== 0) continue;
61 auto methodName
= bp
->getFunction();
62 for (size_t i2
= 0; i2
< numFuncs
; ++i2
) {
63 auto f
= cls
->getMethod(i2
);
64 if (!matchFunctionName(methodName
, f
)) continue;
65 bp
->m_bindState
= BreakPointInfo::KnownToBeValid
;
66 phpAddBreakPointFuncEntry(f
);
71 auto funcName
= bp
->getFuncName();
72 if (!funcName
.empty()) {
73 auto fName
= makeStaticString(funcName
);
74 Func
* f
= Func::lookup(fName
);
75 if (f
== nullptr) continue;
76 bp
->m_bindState
= BreakPointInfo::KnownToBeValid
;
77 phpAddBreakPointFuncEntry(f
);
80 auto fileName
= bp
->m_file
;
81 if (!fileName
.empty()) {
82 for (auto& kv
: g_context
->m_evaledFiles
) {
83 auto const unit
= kv
.second
.unit
;
84 if (!BreakPointInfo::MatchFile(fileName
,
85 unit
->filepath()->toCppString())) {
88 addBreakPointInUnit(bp
, unit
);
93 auto exceptionClassName
= bp
->getExceptionClass();
94 if (exceptionClassName
== "@") {
95 bp
->m_bindState
= BreakPointInfo::KnownToBeValid
;
97 } else if (!exceptionClassName
.empty()) {
98 auto expClsName
= makeStaticString(exceptionClassName
);
99 auto cls
= Class::lookup(expClsName
);
100 if (cls
!= nullptr) {
101 static auto baseClsName
= makeStaticString("Exception");
102 auto baseCls
= Class::lookup(baseClsName
);
103 if (baseCls
!= nullptr) {
104 if (cls
->classof(baseCls
)) {
105 bp
->m_bindState
= BreakPointInfo::KnownToBeValid
;
107 bp
->m_bindState
= BreakPointInfo::KnownToBeInvalid
;
115 // If we get here, the break point is of a type that does
116 // not need to be explicitly enabled in the VM. For example
117 // a break point that get's triggered when the server starts
118 // to process a page request.
119 bp
->m_bindState
= BreakPointInfo::KnownToBeValid
;
123 DebuggerHook
* HphpdHook::GetInstance() {
124 static DebuggerHook
* instance
= new HphpdHook();
128 void HphpdHook::onFileLoad(Unit
* unit
) {
129 DebuggerProxy
* proxy
= Debugger::GetProxy().get();
130 if (proxy
== nullptr) return;
132 // Look up the proxy's breakpoints and add needed breakpoints to the passed
134 std::vector
<BreakPointInfoPtr
> bps
;
135 proxy
->getBreakPoints(bps
);
136 for (unsigned int i
= 0; i
< bps
.size(); i
++) {
137 BreakPointInfoPtr bp
= bps
[i
];
138 if (BreakPointInfo::MatchFile(bp
->m_file
,
139 unit
->filepath()->toCppString())) {
140 addBreakPointInUnit(bp
, unit
);
145 void HphpdHook::onDefClass(const Class
* cls
) {
146 // Make sure we have a proxy
147 DebuggerProxy
* proxy
= Debugger::GetProxy().get();
148 if (proxy
== nullptr) return;
150 // If the proxy has enabled breakpoints that match entry into methods of
151 // the given class, arrange for the VM to stop execution and notify the
152 // debugger whenever execution enters one of these matched method.
153 // This function is called once, when a class is first loaded, so it is not
154 // performance critical.
155 size_t numFuncs
= cls
->numMethods();
156 if (numFuncs
== 0) return;
157 auto clsName
= cls
->name();
158 std::vector
<BreakPointInfoPtr
> bps
;
159 proxy
->getBreakPoints(bps
);
160 for (unsigned int i
= 0; i
< bps
.size(); i
++) {
161 BreakPointInfoPtr bp
= bps
[i
];
162 if (bp
->m_state
== BreakPointInfo::Disabled
) continue;
163 // TODO: check name space separately
164 if (bp
->getClass() != clsName
->data()) continue;
165 bp
->m_bindState
= BreakPointInfo::KnownToBeInvalid
;
166 for (size_t i2
= 0; i2
< numFuncs
; ++i2
) {
167 auto f
= cls
->getMethod(i2
);
168 if (!matchFunctionName(bp
->getFunction(), f
)) continue;
169 bp
->m_bindState
= BreakPointInfo::KnownToBeValid
;
170 phpAddBreakPointFuncEntry(f
);
175 void HphpdHook::onDefFunc(const Func
* f
) {
176 // Make sure we have a proxy
177 DebuggerProxyPtr proxy
= Debugger::GetProxy();
178 if (proxy
== nullptr) return;
180 // If the proxy has an enabled breakpoint that matches entry into the given
181 // function, arrange for the VM to stop execution and notify the debugger
182 // whenever execution enters the given function.
183 std::vector
<BreakPointInfoPtr
> bps
;
184 proxy
->getBreakPoints(bps
);
185 for (unsigned int i
= 0; i
< bps
.size(); i
++) {
186 BreakPointInfoPtr bp
= bps
[i
];
187 if (bp
->m_state
== BreakPointInfo::Disabled
) continue;
188 if (!matchFunctionName(bp
->getFuncName(), f
)) continue;
189 bp
->m_bindState
= BreakPointInfo::KnownToBeValid
;
190 phpAddBreakPointFuncEntry(f
);
195 //////////////////////////////////////////////////////////////////////////