Give feedback if breakpoints won't hit because a file is not loaded, a class is not loaded, or a function is not loaded

The debugger client now accepts feedback from the the debugger server about whether a breakpoint can be hit, absent further loads of files, classes or functions.
Esse commit está contido em:
Herman Venter
2013-06-19 18:21:27 -07:00
commit de Sara Golemon
commit 8d25c0f6f2
27 arquivos alterados com 375 adições e 200 exclusões
+10 -6
Ver Arquivo
@@ -189,9 +189,10 @@ BreakPointInfo::~BreakPointInfo() {
}
}
void BreakPointInfo::sendImpl(DebuggerThriftBuffer &thrift) {
void BreakPointInfo::sendImpl(int version, DebuggerThriftBuffer &thrift) {
TRACE(2, "BreakPointInfo::sendImpl\n");
thrift.write((int8_t)m_state);
if (version >= 1) thrift.write((int8_t)m_bindState);
thrift.write((int8_t)m_interruptType);
thrift.write(m_file);
thrift.write(m_line1);
@@ -208,10 +209,13 @@ void BreakPointInfo::sendImpl(DebuggerThriftBuffer &thrift) {
thrift.write(m_exceptionObject);
}
void BreakPointInfo::recvImpl(DebuggerThriftBuffer &thrift) {
void BreakPointInfo::recvImpl(int version, DebuggerThriftBuffer &thrift) {
TRACE(2, "BreakPointInfo::recvImpl\n");
int8_t tmp;
thrift.read(tmp); m_state = (State)tmp;
if (version >= 1) {
thrift.read(tmp); m_bindState = (BindState)tmp;
}
thrift.read(tmp); m_interruptType = (InterruptType)tmp;
thrift.read(m_file);
thrift.read(m_line1);
@@ -970,17 +974,17 @@ bool BreakPointInfo::checkClause() {
///////////////////////////////////////////////////////////////////////////////
void BreakPointInfo::SendImpl(const BreakPointInfoPtrVec &bps,
void BreakPointInfo::SendImpl(int version, const BreakPointInfoPtrVec &bps,
DebuggerThriftBuffer &thrift) {
TRACE(2, "BreakPointInfo::SendImpl\n");
int16_t size = bps.size();
thrift.write(size);
for (int i = 0; i < size; i++) {
bps[i]->sendImpl(thrift);
bps[i]->sendImpl(version, thrift);
}
}
void BreakPointInfo::RecvImpl(BreakPointInfoPtrVec &bps,
void BreakPointInfo::RecvImpl(int version, BreakPointInfoPtrVec &bps,
DebuggerThriftBuffer &thrift) {
TRACE(2, "BreakPointInfo::RecvImpl\n");
int16_t size;
@@ -988,7 +992,7 @@ void BreakPointInfo::RecvImpl(BreakPointInfoPtrVec &bps,
bps.resize(size);
for (int i = 0; i < size; i++) {
BreakPointInfoPtr bpi(new BreakPointInfo());
bpi->recvImpl(thrift);
bpi->recvImpl(version, thrift);
bps[i] = bpi;
}
}
+13 -5
Ver Arquivo
@@ -115,13 +115,20 @@ DECLARE_BOOST_TYPES(DFunctionInfo);
DECLARE_BOOST_TYPES(BreakPointInfo);
class BreakPointInfo {
public:
// The state of break point
// The state of the break point
enum State : int8_t {
Always = -1, // always break when reaching this break point
Once = 1, // break the first time, then disable
Disabled = 0, // carry on with execution when reaching this break point
};
// Does the break point correspond to a known executable location?
enum BindState : int8_t {
KnownToBeValid, // Breakpoint refers to valid location or member
KnownToBeInvalid, // Breakpoint cannot be bound (no such class or line)
Unknown, // The file or class referenced by breakpoint is not loaded
};
static const char *ErrorClassName;
static const char *GetInterruptName(InterruptType interrupt);
@@ -154,12 +161,12 @@ public:
std::string site() const;
std::string regex(const std::string &name) const;
void sendImpl(DebuggerThriftBuffer &thrift);
void recvImpl(DebuggerThriftBuffer &thrift);
void sendImpl(int version, DebuggerThriftBuffer &thrift);
void recvImpl(int version, DebuggerThriftBuffer &thrift);
static void SendImpl(const BreakPointInfoPtrVec &bps,
static void SendImpl(int version, const BreakPointInfoPtrVec &bps,
DebuggerThriftBuffer &thrift);
static void RecvImpl(BreakPointInfoPtrVec &bps,
static void RecvImpl(int version, BreakPointInfoPtrVec &bps,
DebuggerThriftBuffer &thrift);
bool breakable(int stackDepth) const;
@@ -170,6 +177,7 @@ public:
int16_t m_index; // client side index number
State m_state; // Always, Once, Disabled
BindState m_bindState; // KnownToBeValid, KnownToBeInvalid, Unknown
bool m_valid; // false if syntactically invalid
InterruptType m_interruptType; // Why this break point was reached
+77 -11
Ver Arquivo
@@ -28,13 +28,22 @@ void CmdBreak::sendImpl(DebuggerThriftBuffer &thrift) {
// via Thrift, m_breakpoints points to a copy that is placed in m_bps.
assert(m_breakpoints);
DebuggerCommand::sendImpl(thrift);
BreakPointInfo::SendImpl(*m_breakpoints, thrift);
BreakPointInfo::SendImpl(this->m_version, *m_breakpoints, thrift);
}
// Deserializes a CmdBreak from the given Thrift buffer.
void CmdBreak::recvImpl(DebuggerThriftBuffer &thrift) {
DebuggerCommand::recvImpl(thrift);
BreakPointInfo::RecvImpl(m_bps, thrift);
BreakPointInfo::RecvImpl(this->m_version, m_bps, thrift);
m_breakpoints = &m_bps;
// Old senders will set version to 0. A new sender sets it to 1
// and then expects an answer using version 2.
// Note that version 1 is the same format as version 0, so old
// receivers will not break when receiving a version 1 message.
// This code ensures that version 2 messages are received only
// by receivers that previously sent a version 1 message (thus
// indicating their ability to deal with version 2 messages).
if (this->m_version == 1) this->m_version = 2;
}
// Informs the client of all strings that may follow a break command.
@@ -179,10 +188,13 @@ void CmdBreak::help(DebuggerClient &client) {
// Carries out the "break list" command.
void CmdBreak::processList(DebuggerClient &client) {
m_breakpoints = client.getBreakPoints();
updateServer(client);
for (int i = 0; i < (int)m_breakpoints->size(); i++) {
BreakPointInfoPtr bpi = m_breakpoints->at(i);
client.print(" %d\t%s %s", bpi->index(), bpi->state(true).c_str(),
bpi->desc().c_str());
const char* boundStr =
bpi->m_bindState == BreakPointInfo::Unknown ? " (unbound)" : "";
client.print(" %d\t%s %s%s", bpi->index(), bpi->state(true).c_str(),
bpi->desc().c_str(), boundStr);
}
if (m_breakpoints->empty()) {
client.tutorial(
@@ -330,19 +342,70 @@ void CmdBreak::processStatusChange(DebuggerClient &client) {
// Uses the client to send this command to the server, which
// will update its breakpoint list with the one in this command.
// The client will block until the server echoes
// this command back to it. The echoed command is discarded.
bool CmdBreak::updateServer(DebuggerClient &client) {
// this command back to it. The echoed command is discarded.
// If the server checked the validity of the breakpoints, the values
// of the m_bindState flags are copied to the client's breakpoint list.
void CmdBreak::updateServer(DebuggerClient &client) {
m_body = "update";
client.xend<CmdBreak>(this);
return true;
auto serverReply = client.xend<CmdBreak>(this);
if (serverReply->m_version == 2) {
// The server will have checked the breakpoint list for validity.
// Transfer the results to the local breakpoint list.
auto cbreakpoints = *client.getBreakPoints();
auto sbreakpoints = *serverReply->m_breakpoints;
int csize = cbreakpoints.size();
int ssize = sbreakpoints.size();
assert(csize == ssize);
if (csize > ssize) csize = ssize;
for (int i = 0; i < csize; i++) {
cbreakpoints[i]->m_bindState = sbreakpoints[i]->m_bindState;
}
}
}
// Creates a new CmdBreak instance, sets its breakpoints to the client's
// list, sends the command to the server and waits for a response.
bool CmdBreak::SendClientBreakpointListToServer(DebuggerClient &client) {
void CmdBreak::SendClientBreakpointListToServer(DebuggerClient &client) {
auto cmd = CmdBreak();
cmd.m_breakpoints = client.getBreakPoints();
return cmd.updateServer(client);
cmd.updateServer(client);
}
void ReportBreakpointBindState(DebuggerClient &client, BreakPointInfoPtr bpi) {
switch (bpi->m_bindState) {
case BreakPointInfo::KnownToBeValid:
client.info("Breakpoint %d set %s", bpi->index(), bpi->desc().c_str());
break;
case BreakPointInfo::KnownToBeInvalid:
client.info("Breakpoint %d not set %s", bpi->index(), bpi->desc().c_str());
if (!bpi->getClass().empty()) {
client.info("Because method %s does not exist.",
bpi->getFuncName().c_str());
} else if (!bpi->getExceptionClass().empty()) {
client.info("Because class %s is not an exception.",
bpi->getExceptionClass().c_str());
} else {
client.info("Because the line does not exist or is not executable code.");
}
break;
case BreakPointInfo::Unknown:
client.info("Breakpoint %d set %s", bpi->index(), bpi->desc().c_str());
if (!bpi->getClass().empty()) {
client.info("But wont break until class %s has been loaded.",
bpi->getClass().c_str());
} else if (!bpi->getFuncName().empty()) {
client.info("But wont break until function %s has been loaded.",
bpi->getFuncName().c_str());
} else if (!bpi->getExceptionClass().empty()) {
client.info("But note that class %s has yet been loaded.",
bpi->getExceptionClass().c_str());
} else {
client.info("But wont break until file %s has been loaded.",
bpi->m_file.c_str());
}
break;
}
}
// Adds conditional or watch clause to the breakpoint info if needed.
@@ -368,7 +431,10 @@ bool CmdBreak::addToBreakpointListAndUpdateServer(
}
m_breakpoints->push_back(bpi);
updateServer(client);
client.info("Breakpoint %d set %s", bpi->index(), bpi->desc().c_str());
ReportBreakpointBindState(client, bpi);
if (bpi->m_bindState == BreakPointInfo::KnownToBeInvalid) {
m_breakpoints->pop_back();
}
return true;
}
+5 -3
Ver Arquivo
@@ -25,7 +25,9 @@ namespace HPHP { namespace Eval {
DECLARE_BOOST_TYPES(CmdBreak);
class CmdBreak : public DebuggerCommand {
public:
CmdBreak() : DebuggerCommand(KindOfBreak), m_breakpoints(nullptr) {}
CmdBreak() : DebuggerCommand(KindOfBreak), m_breakpoints(nullptr) {
m_version = 1;
}
// Informs the client of all strings that may follow a break command.
// Used for auto completion. The client uses the prefix of the argument
@@ -48,7 +50,7 @@ public:
// Creates a new CmdBreak instance, sets its breakpoints to the client's
// list, sends the command to the server and waits for a response.
static bool SendClientBreakpointListToServer(DebuggerClient &client);
static void SendClientBreakpointListToServer(DebuggerClient &client);
protected:
// Carries out the Break command. This always involves an action on the
@@ -84,7 +86,7 @@ private:
// will update its breakpoint list with the one in this command.
// The client will block until the server echoes
// this command back to it. The echoed command is discarded.
bool updateServer(DebuggerClient &client);
void updateServer(DebuggerClient &client);
// Carries out the "break list" command.
void processList(DebuggerClient &client);
+2 -2
Ver Arquivo
@@ -56,7 +56,7 @@ void CmdInterrupt::sendImpl(DebuggerThriftBuffer &thrift) {
} else {
thrift.write(false);
}
BreakPointInfo::SendImpl(m_matched, thrift);
BreakPointInfo::SendImpl(0, m_matched, thrift);
}
void CmdInterrupt::recvImpl(DebuggerThriftBuffer &thrift) {
@@ -85,7 +85,7 @@ void CmdInterrupt::recvImpl(DebuggerThriftBuffer &thrift) {
thrift.read(m_bpi->m_exceptionClass);
thrift.read(m_bpi->m_exceptionObject);
}
BreakPointInfo::RecvImpl(m_matched, thrift);
BreakPointInfo::RecvImpl(0, m_matched, thrift);
}
std::string CmdInterrupt::desc() const {
+1 -59
Ver Arquivo
@@ -155,8 +155,6 @@ void DebuggerProxy::notifyDummySandbox() {
m_dummySandbox->notifySignal(CmdSignal::SignalBreak);
}
// Hold the entire set of breakpoints, and sift breakpoints by function and
// class name into separate containers for later.
void DebuggerProxy::setBreakPoints(BreakPointInfoPtrVec &breakpoints) {
TRACE(2, "DebuggerProxy::setBreakPoints\n");
// Hold the break mutex while we update the proxy's state. There's no need
@@ -165,28 +163,8 @@ void DebuggerProxy::setBreakPoints(BreakPointInfoPtrVec &breakpoints) {
WriteLock lock(m_breakMutex);
m_breakpoints = breakpoints;
m_hasBreakPoints = !m_breakpoints.empty();
m_breaksEnterFunc.clear();
m_breaksEnterClsMethod.clear();
for (unsigned int i = 0; i < m_breakpoints.size(); i++) {
BreakPointInfoPtr bp = m_breakpoints[i];
std::string funcFullName = bp->getFuncName();
if (funcFullName.empty()) {
continue;
}
{
StringDataMap::accessor acc;
const StringData* sd = StringData::GetStaticString(funcFullName);
m_breaksEnterFunc.insert(acc, sd);
}
std::string clsName = bp->getClass();
if (!clsName.empty()) {
StringDataMap::accessor acc;
const StringData* sd = StringData::GetStaticString(clsName);
m_breaksEnterClsMethod.insert(acc, sd);
}
}
}
phpSetBreakPointsInAllFiles(this); // Apply breakpoints to the code.
phpSetBreakPoints(this);
}
void DebuggerProxy::getBreakPoints(BreakPointInfoPtrVec &breakpoints) {
@@ -195,42 +173,6 @@ void DebuggerProxy::getBreakPoints(BreakPointInfoPtrVec &breakpoints) {
breakpoints = m_breakpoints;
}
bool DebuggerProxy::couldBreakEnterClsMethod(const StringData* className) {
TRACE(5, "DebuggerProxy::couldBreakEnterClsMethod\n");
ReadLock lock(m_breakMutex);
StringDataMap::const_accessor acc;
return m_breaksEnterClsMethod.find(acc, className);
}
bool DebuggerProxy::couldBreakEnterFunc(const StringData* funcFullName) {
TRACE(5, "DebuggerProxy::couldBreakEnterFunc\n");
ReadLock lock(m_breakMutex);
StringDataMap::const_accessor acc;
return m_breaksEnterFunc.find(acc, funcFullName);
}
void DebuggerProxy::getBreakClsMethods(
std::vector<const StringData*>& classNames) {
TRACE(2, "DebuggerProxy::getBreakClsMethods\n");
classNames.clear();
WriteLock lock(m_breakMutex); // Write lock in case iteration causes a re-hash
for (StringDataMap::const_iterator iter = m_breaksEnterClsMethod.begin();
iter != m_breaksEnterClsMethod.end(); ++iter) {
classNames.push_back(iter->first);
}
}
void DebuggerProxy::getBreakFuncs(
std::vector<const StringData*>& funcFullNames) {
TRACE(2, "DebuggerProxy::getBreakFuncs\n");
funcFullNames.clear();
WriteLock lock(m_breakMutex); // Write lock in case iteration causes a re-hash
for (StringDataMap::const_iterator iter = m_breaksEnterFunc.begin();
iter != m_breaksEnterFunc.end(); ++iter) {
funcFullNames.push_back(iter->first);
}
}
// Proxy needs to be interrupted because it has something setup which may need
// processing; breakpoints, flow control commands, a signal.
bool DebuggerProxy::needInterrupt() {
-9
Ver Arquivo
@@ -83,10 +83,6 @@ public:
void setBreakPoints(BreakPointInfoPtrVec &breakpoints);
void getBreakPoints(BreakPointInfoPtrVec &breakpoints);
bool couldBreakEnterClsMethod(const StringData* className);
bool couldBreakEnterFunc(const StringData* funcFullName);
void getBreakClsMethods(std::vector<const StringData*>& classNames);
void getBreakFuncs(std::vector<const StringData*>& funcFullNames);
BreakPointInfoPtr getBreakPointAtCmd(CmdInterrupt& cmd);
bool needInterrupt();
@@ -134,11 +130,6 @@ private:
DThreadInfoPtr m_newThread;
std::map<int64_t, DThreadInfoPtr> m_threads;
typedef tbb::concurrent_hash_map<const StringData*, void*,
StringDataHashCompare> StringDataMap;
StringDataMap m_breaksEnterClsMethod;
StringDataMap m_breaksEnterFunc;
CmdFlowControlPtr m_flow; // c, s, n, o commands that can skip breakpoints
Mutex m_signalMutex; // who can talk to client
+144 -58
Ver Arquivo
@@ -139,30 +139,35 @@ static PCFilter *getBreakPointFilter() {
return g_vmContext->m_breakPointFilter;
}
// Looks up the offset range in the given unit, of the given breakpoint.
// If the offset cannot be found, the breakpoint is marked as invalid.
// Otherwise it is marked as valid and the offset is added to the
// breakpoint filter and the offset range is black listed for the JIT.
static void addBreakPointInUnit(Eval::BreakPointInfoPtr bp, Unit* unit) {
OffsetRangeVec offsets;
if (!unit->getOffsetRanges(bp->m_line1, offsets) || offsets.size() == 0) {
bp->m_bindState = Eval::BreakPointInfo::KnownToBeInvalid;
return;
}
bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid;
TRACE(3, "Add to breakpoint filter for %s:%d, unit %p:\n",
unit->filepath()->data(), bp->m_line1, unit);
getBreakPointFilter()->addRanges(unit, offsets);
if (RuntimeOption::EvalJit) {
blacklistRangesInJit(unit, offsets);
}
}
static void addBreakPointsInFile(Eval::DebuggerProxy* proxy,
Eval::PhpFile* efile) {
Eval::BreakPointInfoPtrVec bps;
proxy->getBreakPoints(bps);
for(unsigned int i = 0; i < bps.size(); i++) {
for (unsigned int i = 0; i < bps.size(); i++) {
Eval::BreakPointInfoPtr bp = bps[i];
if (bp->m_line1 == 0 || bp->m_file.empty()) {
// invalid breakpoint for file:line
continue;
}
if (!Eval::BreakPointInfo::MatchFile(bp->m_file, efile->getFileName(),
if (Eval::BreakPointInfo::MatchFile(bp->m_file, efile->getFileName(),
efile->getRelPath())) {
continue;
}
Unit* unit = efile->unit();
OffsetRangeVec offsets;
if (!unit->getOffsetRanges(bp->m_line1, offsets)) {
continue;
}
TRACE(3, "Add to breakpoint filter for %s:%d, unit %p:\n",
efile->getFileName().c_str(), bp->m_line1, unit);
getBreakPointFilter()->addRanges(unit, offsets);
if (RuntimeOption::EvalJit) {
blacklistRangesInJit(unit, offsets);
addBreakPointInUnit(bp, efile->unit());
break;
}
}
}
@@ -182,13 +187,45 @@ static void addBreakPointFuncEntry(const Func* f) {
}
}
static void addBreakPointsClass(Eval::DebuggerProxy* proxy,
const Class* cls) {
// If the proxy has an enabled breakpoint that matches entry into the given
// function, arrange for the VM to stop execution and notify the debugger
// whenever execution enters the given function.
static void addBreakPointFuncEntry(Eval::DebuggerProxy* proxy, const Func* f) {
Eval::BreakPointInfoPtrVec bps;
proxy->getBreakPoints(bps);
for (unsigned int i = 0; i < bps.size(); i++) {
Eval::BreakPointInfoPtr bp = bps[i];
if (bp->m_state == Eval::BreakPointInfo::Disabled) continue;
if (bp->getFuncName() != f->fullName()->data()) continue;
bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid;
addBreakPointFuncEntry(f);
return;
}
}
// If the proxy has enabled breakpoints that match entry into methods of
// the given class, arrange for the VM to stop execution and notify the debugger
// whenever execution enters one of these matched method.
// This function is called once, when a class is first loaded, so it is not
// performance critical.
static void addBreakPointsClass(Eval::DebuggerProxy* proxy, const Class* cls) {
size_t numFuncs = cls->numMethods();
Func* const* funcs = cls->methods();
for (size_t i = 0; i < numFuncs; ++i) {
if (proxy->couldBreakEnterFunc(funcs[i]->fullName())) {
addBreakPointFuncEntry(funcs[i]);
if (numFuncs == 0) return;
auto clsName = cls->name();
auto funcs = cls->methods();
Eval::BreakPointInfoPtrVec bps;
proxy->getBreakPoints(bps);
for (unsigned int i = 0; i < bps.size(); i++) {
Eval::BreakPointInfoPtr bp = bps[i];
if (bp->m_state == Eval::BreakPointInfo::Disabled) continue;
// TODO: check name space separately
if (bp->getClass() != clsName->data()) continue;
bp->m_bindState = Eval::BreakPointInfo::KnownToBeInvalid;
for (size_t i = 0; i < numFuncs; ++i) {
auto f = funcs[i];
if (bp->getFunction() != f->name()->data()) continue;
bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid;
addBreakPointFuncEntry(f);
}
}
}
@@ -223,57 +260,106 @@ void phpDebuggerEvalHook(const Func* f) {
}
}
// Hook called by the VM when a file is loaded. Gives the debugger a chance
// to apply any pending breakpoints that might be in the file.
// Called by the VM when a file is loaded.
void phpDebuggerFileLoadHook(Eval::PhpFile* efile) {
Eval::DebuggerProxyPtr proxy = Eval::Debugger::GetProxy();
if (!proxy) {
return;
}
if (proxy == nullptr) return;
addBreakPointsInFile(proxy.get(), efile);
}
// Called by the VM when a class definition is loaded.
void phpDebuggerDefClassHook(const Class* cls) {
Eval::DebuggerProxyPtr proxy = Eval::Debugger::GetProxy();
if (!proxy) {
return;
}
if (proxy == nullptr) return;
addBreakPointsClass(proxy.get(), cls);
}
// Called by the VM when a function definition is loaded.
void phpDebuggerDefFuncHook(const Func* func) {
Eval::DebuggerProxyPtr proxy = Eval::Debugger::GetProxy();
if (proxy && proxy->couldBreakEnterFunc(func->fullName())) {
addBreakPointFuncEntry(func);
}
if (proxy == nullptr) return;
addBreakPointFuncEntry(proxy.get(), func);
}
// Helper which will look at every loaded file and attempt to see if any
// existing file:line breakpoints should be set.
void phpSetBreakPointsInAllFiles(Eval::DebuggerProxy* proxy) {
for (EvaledFilesMap::const_iterator it =
g_vmContext->m_evaledFiles.begin();
it != g_vmContext->m_evaledFiles.end(); ++it) {
addBreakPointsInFile(proxy, it->second);
}
std::vector<const StringData*> clsNames;
proxy->getBreakClsMethods(clsNames);
for (unsigned int i = 0; i < clsNames.size(); i++) {
Class* cls = Unit::lookupClass(clsNames[i]);
if (cls) {
addBreakPointsClass(proxy, cls);
// Called by the proxy whenever its breakpoint list is updated.
// Since this intended to be called when user input is received, it is not
// performance critical. Also, in typical scenarios, the list is short.
void phpSetBreakPoints(Eval::DebuggerProxy* proxy) {
Eval::BreakPointInfoPtrVec bps;
proxy->getBreakPoints(bps);
for (unsigned int i = 0; i < bps.size(); i++) {
Eval::BreakPointInfoPtr bp = bps[i];
bp->m_bindState = Eval::BreakPointInfo::Unknown;
auto className = bp->getClass();
if (!className.empty()) {
auto clsName = StringData::GetStaticString(className);
auto cls = Unit::lookupClass(clsName);
if (cls == nullptr) continue;
bp->m_bindState = Eval::BreakPointInfo::KnownToBeInvalid;
size_t numFuncs = cls->numMethods();
if (numFuncs == 0) continue;
auto methodName = bp->getFunction();
Func* const* funcs = cls->methods();
for (size_t i = 0; i < numFuncs; ++i) {
auto f = funcs[i];
if (methodName != f->name()->data()) continue;
bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid;
addBreakPointFuncEntry(f);
break;
}
//TODO: what about superclass methods accessed via the derived class?
//Task 2527229.
continue;
}
}
std::vector<const StringData*> funcFullNames;
proxy->getBreakFuncs(funcFullNames);
for (unsigned int i = 0; i < funcFullNames.size(); i++) {
// This list contains class method as well but they shouldn't hit anything
Func* f = Unit::lookupFunc(funcFullNames[i]);
if (f) {
auto funcName = bp->getFuncName();
if (!funcName.empty()) {
auto fName = StringData::GetStaticString(funcName);
Func* f = Unit::lookupFunc(fName);
if (f == nullptr) continue;
bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid;
addBreakPointFuncEntry(f);
continue;
}
auto fileName = bp->m_file;
if (!fileName.empty()) {
for (EvaledFilesMap::const_iterator it =
g_vmContext->m_evaledFiles.begin();
it != g_vmContext->m_evaledFiles.end(); ++it) {
auto efile = it->second;
if (!Eval::BreakPointInfo::MatchFile(fileName, efile->getFileName(),
efile->getRelPath())) continue;
addBreakPointInUnit(bp, efile->unit());
break;
}
continue;
}
auto exceptionClassName = bp->getExceptionClass();
if (exceptionClassName == "@") {
bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid;
continue;
} else if (!exceptionClassName.empty()) {
auto expClsName = StringData::GetStaticString(exceptionClassName);
auto cls = Unit::lookupClass(expClsName);
if (cls != nullptr) {
auto baseClsName = StringData::GetStaticString("Exception");
auto baseCls = Unit::lookupClass(baseClsName);
if (baseCls != nullptr) {
if (cls->classof(baseCls)) {
bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid;
} else {
bp->m_bindState = Eval::BreakPointInfo::KnownToBeInvalid;
}
}
}
continue;
} else {
continue;
}
// If we get here, the break point is of a type that does
// not need to be explicitly enabled in the VM. For example
// a break point that get's triggered when the server starts
// to process a page request.
bp->m_bindState = Eval::BreakPointInfo::KnownToBeValid;
}
}
+1 -2
Ver Arquivo
@@ -49,8 +49,7 @@ class Func;
void phpDebuggerDefClassHook(const Class* cls);
void phpDebuggerDefFuncHook(const Func* func);
// Helper to apply pending breakpoints to all files.
void phpSetBreakPointsInAllFiles(Eval::DebuggerProxy* proxy);
void phpSetBreakPoints(Eval::DebuggerProxy* proxy);
// Add/remove breakpoints at a specific offset.
void phpAddBreakPoint(const Unit* unit, Offset offset);
+18
Ver Arquivo
@@ -4,6 +4,11 @@ break1.php loaded
Program %s/break1.php exited normally.
break break1.php:7
Breakpoint 1 set on line 7 of break1.php
break break1.php:77
Breakpoint 2 not set on line 77 of break1.php
Because the line does not exist or is not executable code.
break list
1 ALWAYS on line 7 of break1.php
@ foo('test_break1')
Breakpoint 1 reached at foo() on line 7 of %s/break1.php
6 $y = $x.'_suffix';
@@ -13,6 +18,8 @@ Breakpoint 1 reached at foo() on line 7 of %s/break1.php
variable
$x = "test_break1"
$y = "test_break1_suffix"
break list
1 ALWAYS on line 7 of break1.php
break clear all
All breakpoints are cleared.
continue
@@ -77,4 +84,15 @@ Break at cls::pubHardBreak() on line 19 of %s/break1.php
continue
pubHardBreak:done
break clear all
There is no breakpoint to clear or toggle.
break cls::nosuchMethod()
Breakpoint 1 not set upon entering cls::nosuchMethod()
Because method cls::nosuchMethod does not exist.
break list
break noSuchFunction()
Breakpoint 1 set upon entering noSuchFunction()
But wont break until function noSuchFunction has been loaded.
break list
1 ALWAYS upon entering noSuchFunction() (unbound)
quit
+8
Ver Arquivo
@@ -1,7 +1,10 @@
run
break break1.php:7
break break1.php:77
break list
@ foo('test_break1')
variable
break list
break clear all
continue
break foo()
@@ -23,4 +26,9 @@ continue
@ $break5=new cls()
@ $break5->pubHardBreak('test_break5')
continue
break clear all
break cls::nosuchMethod()
break list
break noSuchFunction()
break list
quit
+14
Ver Arquivo
@@ -1,12 +1,21 @@
Program %s/break2.php loaded. Type '[r]un' or '[c]ontinue' to go.
break break2.php:7
Breakpoint 1 set on line 7 of break2.php
But wont break until file break2.php has been loaded.
break foo2()
Breakpoint 2 set upon entering foo2()
But wont break until function foo2 has been loaded.
break cls2::pubObj()
Breakpoint 3 set upon entering cls2::pubObj()
But wont break until class cls2 has been loaded.
break cls2::pubCls()
Breakpoint 4 set upon entering cls2::pubCls()
But wont break until class cls2 has been loaded.
break list
1 ALWAYS on line 7 of break2.php (unbound)
2 ALWAYS upon entering foo2() (unbound)
3 ALWAYS upon entering cls2::pubObj() (unbound)
4 ALWAYS upon entering cls2::pubCls() (unbound)
run
break2.php loaded
Program %s/break2.php exited normally.
@@ -16,6 +25,11 @@ Breakpoint 2 reached at foo2() on line 6 of %s/break2.php
6 $y = $x.'_suffix';
7 error_log($y);
break list
1 ALWAYS on line 7 of break2.php
2 ALWAYS upon entering foo2()
3 ALWAYS upon entering cls2::pubObj()
4 ALWAYS upon entering cls2::pubCls()
continue
Breakpoint 1 reached at foo2() on line 7 of %s/break2.php
6 $y = $x.'_suffix';
+2
Ver Arquivo
@@ -2,8 +2,10 @@ break break2.php:7
break foo2()
break cls2::pubObj()
break cls2::pubCls()
break list
run
@ foo2('test_break6')
break list
continue
@ $break6=new cls2()
@ $break6->pubObj('test_break6')
@@ -1,6 +1,7 @@
Program %s/break4.php loaded. Type '[r]un' or '[c]ontinue' to go.
break :fb:my:thing::doIt()
Breakpoint 1 set upon entering xhp_fb__my__thing::doIt()
But wont break until class xhp_fb__my__thing has been loaded.
continue
break4.php loaded
Program %s/break4.php exited normally.
@@ -1,6 +1,7 @@
Program %s/error_bad_cmd_type_receive.php loaded. Type '[r]un' or '[c]ontinue' to go.
break error_bad_cmd_type_receive.php:3
Breakpoint 1 set on line 3 of error_bad_cmd_type_receive.php
But wont break until file error_bad_cmd_type_receive.php has been loaded.
run
Breakpoint 1 reached on line 3 of %s/error_bad_cmd_type_receive.php
2
@@ -1,6 +1,7 @@
Program %s/error_bad_cmd_type_send.php loaded. Type '[r]un' or '[c]ontinue' to go.
break error_bad_cmd_type_send.php:3
Breakpoint 1 set on line 3 of error_bad_cmd_type_send.php
But wont break until file error_bad_cmd_type_send.php has been loaded.
run
Breakpoint 1 reached on line 3 of %s/error_bad_cmd_type_send.php
2
@@ -1,6 +1,7 @@
Program %s/error_short_cmd_receive.php loaded. Type '[r]un' or '[c]ontinue' to go.
break error_short_cmd_receive.php:3
Breakpoint 1 set on line 3 of error_short_cmd_receive.php
But wont break until file error_short_cmd_receive.php has been loaded.
run
Breakpoint 1 reached on line 3 of %s/error_short_cmd_receive.php
2
@@ -1,6 +1,7 @@
Program %s/error_short_cmd_send.php loaded. Type '[r]un' or '[c]ontinue' to go.
break error_short_cmd_send.php:3
Breakpoint 1 set on line 3 of error_short_cmd_send.php
But wont break until file error_short_cmd_send.php has been loaded.
run
Breakpoint 1 reached on line 3 of %s/error_short_cmd_send.php
2
+1
Ver Arquivo
@@ -5,6 +5,7 @@
namespace {
class MyException extends Exception { }
class NotAnException { }
function throw_exception() {
throw new Exception();
+59 -45
Ver Arquivo
@@ -1,18 +1,32 @@
Program %s/exception1.php loaded. Type '[r]un' or '[c]ontinue' to go.
exception MyException
Breakpoint 1 set right before throwing MyException
But note that class MyException has yet been loaded.
break list
1 ALWAYS right before throwing MyException (unbound)
run
Program %s/exception1.php exited normally.
exception NotAnException
Breakpoint 2 not set right before throwing NotAnException
Because class NotAnException is not an exception.
break list
1 ALWAYS right before throwing MyException
break clear all
All breakpoints are cleared.
exception Exception
Breakpoint 1 set right before throwing Exception
break list
1 ALWAYS right before throwing Exception
@ throw_exception()
Breakpoint 1 reached: Throwing Exception at throw_exception() on line 10 of %s/exception1.php
9 function throw_exception() {
10 throw new Exception();
11 }
Breakpoint 1 reached: Throwing Exception at throw_exception() on line 11 of %s/exception1.php
10 function throw_exception() {
11 throw new Exception();
12 }
break clear all
All breakpoints are cleared.
continue
Hit a php exception : exception 'Exception' with message '' in %s/exception1.php:10
Hit a php exception : exception 'Exception' with message '' in %s/exception1.php:11
Stack trace:
#0 (1): throw_exception()
#1 (1): include()
@@ -20,15 +34,15 @@ Stack trace:
exception MyException
Breakpoint 1 set right before throwing MyException
@ throw_myexception()
Breakpoint 1 reached: Throwing MyException at throw_myexception() on line 14 of %s/exception1.php
13 function throw_myexception() {
14 throw new MyException();
15 }
Breakpoint 1 reached: Throwing MyException at throw_myexception() on line 15 of %s/exception1.php
14 function throw_myexception() {
15 throw new MyException();
16 }
break clear all
All breakpoints are cleared.
continue
Hit a php exception : exception 'MyException' with message '' in %s/exception1.php:14
Hit a php exception : exception 'MyException' with message '' in %s/exception1.php:15
Stack trace:
#0 (1): throw_myexception()
#1 (1): include()
@@ -36,32 +50,32 @@ Stack trace:
exception error
Breakpoint 1 set right after an error
@ error_undefined_class()
Breakpoint 1 reached: An error occurred at error_undefined_class() on line 18 of %s/exception1.php
17 function error_undefined_class() {
18 $x = new NoSuchClass();
19 }
Breakpoint 1 reached: An error occurred at error_undefined_class() on line 19 of %s/exception1.php
18 function error_undefined_class() {
19 $x = new NoSuchClass();
20 }
Error Message: Class undefined: NoSuchClass
break clear all
All breakpoints are cleared.
continue
Hit fatal : Class undefined: NoSuchClass
#0 at %s/exception1.php:18]
#0 at %s/exception1.php:19]
#1 error_undefined_class(), called at [:1]
#2 include(), called at [:1]
exception Exception
Breakpoint 1 set right before throwing Exception
@ \Outer\throw_exception()
Breakpoint 1 reached: Throwing Exception at Outer\throw_exception() on line 28 of %s/exception1.php
27 function throw_exception() {
28 throw new \Exception();
29 }
Breakpoint 1 reached: Throwing Exception at Outer\throw_exception() on line 29 of %s/exception1.php
28 function throw_exception() {
29 throw new \Exception();
30 }
break clear all
All breakpoints are cleared.
continue
Hit a php exception : exception 'Exception' with message '' in %s/exception1.php:28
Hit a php exception : exception 'Exception' with message '' in %s/exception1.php:29
Stack trace:
#0 (1): Outer\throw_exception()
#1 (1): include()
@@ -69,15 +83,15 @@ Stack trace:
exception \Outer\MyException
Breakpoint 1 set right before throwing Outer\MyException
@ Outer\throw_myexception()
Breakpoint 1 reached: Throwing Outer\MyException at Outer\throw_myexception() on line 32 of %s/exception1.php
31 function throw_myexception() {
32 throw new MyException();
33 }
Breakpoint 1 reached: Throwing Outer\MyException at Outer\throw_myexception() on line 33 of %s/exception1.php
32 function throw_myexception() {
33 throw new MyException();
34 }
break clear all
All breakpoints are cleared.
continue
Hit a php exception : exception 'Outer\MyException' with message '' in %s/exception1.php:32
Hit a php exception : exception 'Outer\MyException' with message '' in %s/exception1.php:33
Stack trace:
#0 (1): Outer\throw_myexception()
#1 (1): include()
@@ -85,32 +99,32 @@ Stack trace:
exception error
Breakpoint 1 set right after an error
@ \Outer\error_undefined_class()
Breakpoint 1 reached: An error occurred at Outer\error_undefined_class() on line 36 of %s/exception1.php
35 function error_undefined_class() {
36 $x = new NoSuchClass();
37 }
Breakpoint 1 reached: An error occurred at Outer\error_undefined_class() on line 37 of %s/exception1.php
36 function error_undefined_class() {
37 $x = new NoSuchClass();
38 }
Error Message: Class undefined: Outer\NoSuchClass
break clear all
All breakpoints are cleared.
continue
Hit fatal : Class undefined: Outer\NoSuchClass
#0 at %s/exception1.php:36]
#0 at %s/exception1.php:37]
#1 Outer\error_undefined_class(), called at [:1]
#2 include(), called at [:1]
exception Exception
Breakpoint 1 set right before throwing Exception
@ \Outer\Inner\throw_exception()
Breakpoint 1 reached: Throwing Exception at Outer\Inner\throw_exception() on line 46 of %s/exception1.php
45 function throw_exception() {
46 throw new \Exception();
47 }
Breakpoint 1 reached: Throwing Exception at Outer\Inner\throw_exception() on line 47 of %s/exception1.php
46 function throw_exception() {
47 throw new \Exception();
48 }
break clear all
All breakpoints are cleared.
continue
Hit a php exception : exception 'Exception' with message '' in %s/exception1.php:46
Hit a php exception : exception 'Exception' with message '' in %s/exception1.php:47
Stack trace:
#0 (1): Outer\Inner\throw_exception()
#1 (1): include()
@@ -118,15 +132,15 @@ Stack trace:
exception Outer\Inner\MyException
Breakpoint 1 set right before throwing Outer\Inner\MyException
@ Outer\Inner\throw_myexception()
Breakpoint 1 reached: Throwing Outer\Inner\MyException at Outer\Inner\throw_myexception() on line 50 of %s/exception1.php
49 function throw_myexception() {
50 throw new MyException();
51 }
Breakpoint 1 reached: Throwing Outer\Inner\MyException at Outer\Inner\throw_myexception() on line 51 of %s/exception1.php
50 function throw_myexception() {
51 throw new MyException();
52 }
break clear all
All breakpoints are cleared.
continue
Hit a php exception : exception 'Outer\Inner\MyException' with message '' in %s/exception1.php:50
Hit a php exception : exception 'Outer\Inner\MyException' with message '' in %s/exception1.php:51
Stack trace:
#0 (1): Outer\Inner\throw_myexception()
#1 (1): include()
@@ -134,17 +148,17 @@ Stack trace:
exception error
Breakpoint 1 set right after an error
@ \Outer\Inner\error_undefined_class()
Breakpoint 1 reached: An error occurred at Outer\Inner\error_undefined_class() on line 54 of %s/exception1.php
53 function error_undefined_class() {
54 $x = new NoSuchClass();
55 }
Breakpoint 1 reached: An error occurred at Outer\Inner\error_undefined_class() on line 55 of %s/exception1.php
54 function error_undefined_class() {
55 $x = new NoSuchClass();
56 }
Error Message: Class undefined: Outer\Inner\NoSuchClass
break clear all
All breakpoints are cleared.
continue
Hit fatal : Class undefined: Outer\Inner\NoSuchClass
#0 at %s/exception1.php:54]
#0 at %s/exception1.php:55]
#1 Outer\Inner\error_undefined_class(), called at [:1]
#2 include(), called at [:1]
+6
Ver Arquivo
@@ -1,5 +1,11 @@
exception MyException
break list
run
exception NotAnException
break list
break clear all
exception Exception
break list
@ throw_exception()
break clear all
continue
+1
Ver Arquivo
@@ -1,6 +1,7 @@
Program %s/flow.php loaded. Type '[r]un' or '[c]ontinue' to go.
%Sbreak cls::pub()
Breakpoint 1 set upon entering cls::pub()
But wont break until class cls has been loaded.
run
flow_t.php loaded
Program %s/flow.php exited normally.
@@ -1,6 +1,7 @@
Program %s/flow_excep.php loaded. Type '[r]un' or '[c]ontinue' to go.
break main()
Breakpoint 1 set upon entering main()
But wont break until function main has been loaded.
run
Breakpoint 1 reached at main() on line 55 of %s/flow_excep.php
54 function main() {
@@ -1,6 +1,7 @@
Program %s/flow_multistep.php loaded. Type '[r]un' or '[c]ontinue' to go.
%Sbreak flow_multistep.php:58
Breakpoint 1 set on line 58 of flow_multistep.php
But wont break until file flow_multistep.php has been loaded.
run
C1 oh hai
C2 oh hai
@@ -4,6 +4,7 @@ SmallStep(ss) set to on.
break cls::pub()
Breakpoint 1 set upon entering cls::pub()
But wont break until class cls has been loaded.
run
flow_small.php loaded
Program %s/flow_small.php exited normally.
+4
Ver Arquivo
@@ -64,5 +64,9 @@ Color {
}
Tutorial = 0
Tutorial {
Visited {
0 = 376d06037a5af010a610dd885c6817c1
1 = d497d02bce18120e9ae2b91992a9ce60
}
}
MaxCodeLines = -1
@@ -1,6 +1,7 @@
Program %s/printThis.php loaded. Type '[r]un' or '[c]ontinue' to go.
%Sbreak printThis.php:5
Breakpoint 1 set on line 5 of printThis.php
But wont break until file printThis.php has been loaded.
run
Breakpoint 1 reached at Foo::method() on line 5 of %s/printThis.php
4 function method() {