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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
namespace {
|
||||
|
||||
class MyException extends Exception { }
|
||||
class NotAnException { }
|
||||
|
||||
function throw_exception() {
|
||||
throw new Exception();
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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,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.
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário