forked from OSchip/llvm-project
rdar://problem/12007576
Record the snapshot of our watched value when the watchpoint is set or hit. And report the old/new values when watchpoint is triggered. Add some test scenarios. llvm-svn: 161785
This commit is contained in:
parent
d0af1d9657
commit
209bd65ea4
|
@ -53,10 +53,25 @@ public:
|
|||
uint32_t GetIgnoreCount () const;
|
||||
void SetIgnoreCount (uint32_t n);
|
||||
void SetWatchpointType (uint32_t type);
|
||||
void SetDeclInfo (std::string &str);
|
||||
void SetWatchSpec (std::string &str);
|
||||
void SetDeclInfo (const std::string &str);
|
||||
std::string GetWatchSpec();
|
||||
void SetWatchSpec (const std::string &str);
|
||||
|
||||
// Snapshot management interface.
|
||||
bool IsWatchVariable() const;
|
||||
void SetWatchVariable(bool val);
|
||||
std::string GetOldSnapshot() const;
|
||||
void SetOldSnapshot (const std::string &str);
|
||||
std::string GetNewSnapshot() const;
|
||||
void SetNewSnapshot (const std::string &str);
|
||||
uint64_t GetOldSnapshotVal() const;
|
||||
void SetOldSnapshotVal (uint64_t val);
|
||||
uint64_t GetNewSnapshotVal() const;
|
||||
void SetNewSnapshotVal (uint64_t val);
|
||||
|
||||
void GetDescription (Stream *s, lldb::DescriptionLevel level);
|
||||
void Dump (Stream *s) const;
|
||||
void DumpSnapshots (const char * prefix, Stream *s) const;
|
||||
void DumpWithLevel (Stream *s, lldb::DescriptionLevel description_level) const;
|
||||
Target &GetTarget() { return *m_target; }
|
||||
const Error &GetError() { return m_error; }
|
||||
|
@ -136,19 +151,23 @@ private:
|
|||
friend class WatchpointList;
|
||||
|
||||
void SetTarget(Target *target_ptr) { m_target = target_ptr; }
|
||||
std::string GetWatchSpec() { return m_watch_spec_str; }
|
||||
void ResetHitCount() { m_hit_count = 0; }
|
||||
|
||||
Target *m_target;
|
||||
bool m_enabled; // Is this watchpoint enabled
|
||||
bool m_is_hardware; // Is this a hardware watchpoint
|
||||
bool m_is_watch_variable; // True if set via 'watchpoint set variable'.
|
||||
uint32_t m_watch_read:1, // 1 if we stop when the watched data is read from
|
||||
m_watch_write:1, // 1 if we stop when the watched data is written to
|
||||
m_watch_was_read:1, // Set to 1 when watchpoint is hit for a read access
|
||||
m_watch_was_written:1; // Set to 1 when watchpoint is hit for a write access
|
||||
uint32_t m_ignore_count; // Number of times to ignore this breakpoint
|
||||
std::string m_decl_str; // Declaration information, if any.
|
||||
std::string m_watch_spec_str; // Spec for the watchpoint (for future use).
|
||||
std::string m_watch_spec_str; // Spec for the watchpoint.
|
||||
std::string m_snapshot_old_str; // Old snapshot for the watchpoint value as by ValueObject::DumpValueObject().
|
||||
std::string m_snapshot_new_str; // New Snapshot for the watchpoint value as by ValueObject::DumpValueObject().
|
||||
uint64_t m_snapshot_old_val; // Old snapshot for the watchpoint bytes.
|
||||
uint64_t m_snapshot_new_val; // New Snapshot for the watchpoint bytes.
|
||||
Error m_error; // An error object describing errors associated with this watchpoint.
|
||||
WatchpointOptions m_options; // Settable watchpoint options, which is a delegate to handle
|
||||
// the callback machinery.
|
||||
|
|
|
@ -4407,7 +4407,7 @@
|
|||
HEADER_SEARCH_PATHS = /usr/include/libxml2;
|
||||
LD_DYLIB_INSTALL_NAME = "$(DEVELOPER_DIR)/Library/PrivateFrameworks/LLDB.framework/Resources/lldb-core.a";
|
||||
MACH_O_TYPE = staticlib;
|
||||
MACOSX_DEPLOYMENT_TARGET = "10.7";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
||||
OTHER_CPLUSPLUSFLAGS = (
|
||||
"-fno-rtti",
|
||||
"-Wglobal-constructors",
|
||||
|
@ -4438,7 +4438,7 @@
|
|||
HEADER_SEARCH_PATHS = /usr/include/libxml2;
|
||||
LD_DYLIB_INSTALL_NAME = "$(DEVELOPER_DIR)/Library/PrivateFrameworks/LLDB.framework/Resources/lldb-core.a";
|
||||
MACH_O_TYPE = staticlib;
|
||||
MACOSX_DEPLOYMENT_TARGET = "10.7";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
||||
OTHER_CPLUSPLUSFLAGS = (
|
||||
"-fno-rtti",
|
||||
"-Wglobal-constructors",
|
||||
|
@ -4469,7 +4469,7 @@
|
|||
HEADER_SEARCH_PATHS = /usr/include/libxml2;
|
||||
LD_DYLIB_INSTALL_NAME = "$(DEVELOPER_DIR)/Library/PrivateFrameworks/LLDB.framework/Resources/lldb-core.a";
|
||||
MACH_O_TYPE = staticlib;
|
||||
MACOSX_DEPLOYMENT_TARGET = "10.7";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
||||
OTHER_CPLUSPLUSFLAGS = (
|
||||
"-fno-rtti",
|
||||
"-Wglobal-constructors",
|
||||
|
|
|
@ -28,6 +28,7 @@ Watchpoint::Watchpoint (lldb::addr_t addr, size_t size, bool hardware) :
|
|||
m_target(NULL),
|
||||
m_enabled(false),
|
||||
m_is_hardware(hardware),
|
||||
m_is_watch_variable(false),
|
||||
m_watch_read(0),
|
||||
m_watch_write(0),
|
||||
m_watch_was_read(0),
|
||||
|
@ -35,6 +36,10 @@ Watchpoint::Watchpoint (lldb::addr_t addr, size_t size, bool hardware) :
|
|||
m_ignore_count(0),
|
||||
m_decl_str(),
|
||||
m_watch_spec_str(),
|
||||
m_snapshot_old_str(),
|
||||
m_snapshot_new_str(),
|
||||
m_snapshot_old_val(0),
|
||||
m_snapshot_new_val(0),
|
||||
m_error(),
|
||||
m_options ()
|
||||
{
|
||||
|
@ -70,19 +75,79 @@ Watchpoint::ClearCallback ()
|
|||
}
|
||||
|
||||
void
|
||||
Watchpoint::SetDeclInfo (std::string &str)
|
||||
Watchpoint::SetDeclInfo (const std::string &str)
|
||||
{
|
||||
m_decl_str = str;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string
|
||||
Watchpoint::GetWatchSpec()
|
||||
{
|
||||
return m_watch_spec_str;
|
||||
}
|
||||
|
||||
void
|
||||
Watchpoint::SetWatchSpec (std::string &str)
|
||||
Watchpoint::SetWatchSpec (const std::string &str)
|
||||
{
|
||||
m_watch_spec_str = str;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string
|
||||
Watchpoint::GetOldSnapshot() const
|
||||
{
|
||||
return m_snapshot_old_str;
|
||||
}
|
||||
|
||||
void
|
||||
Watchpoint::SetOldSnapshot (const std::string &str)
|
||||
{
|
||||
m_snapshot_old_str = str;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string
|
||||
Watchpoint::GetNewSnapshot() const
|
||||
{
|
||||
return m_snapshot_new_str;
|
||||
}
|
||||
|
||||
void
|
||||
Watchpoint::SetNewSnapshot (const std::string &str)
|
||||
{
|
||||
m_snapshot_old_str = m_snapshot_new_str;
|
||||
m_snapshot_new_str = str;
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
Watchpoint::GetOldSnapshotVal() const
|
||||
{
|
||||
return m_snapshot_old_val;
|
||||
}
|
||||
|
||||
void
|
||||
Watchpoint::SetOldSnapshotVal (uint64_t val)
|
||||
{
|
||||
m_snapshot_old_val = val;
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
Watchpoint::GetNewSnapshotVal() const
|
||||
{
|
||||
return m_snapshot_new_val;
|
||||
}
|
||||
|
||||
void
|
||||
Watchpoint::SetNewSnapshotVal (uint64_t val)
|
||||
{
|
||||
m_snapshot_old_val = m_snapshot_new_val;
|
||||
m_snapshot_new_val = val;
|
||||
return;
|
||||
}
|
||||
|
||||
// Override default impl of StoppointLocation::IsHardware() since m_is_hardware
|
||||
// member field is more accurate.
|
||||
bool
|
||||
|
@ -91,6 +156,18 @@ Watchpoint::IsHardware () const
|
|||
return m_is_hardware;
|
||||
}
|
||||
|
||||
bool
|
||||
Watchpoint::IsWatchVariable() const
|
||||
{
|
||||
return m_is_watch_variable;
|
||||
}
|
||||
|
||||
void
|
||||
Watchpoint::SetWatchVariable(bool val)
|
||||
{
|
||||
m_is_watch_variable = val;
|
||||
}
|
||||
|
||||
// RETURNS - true if we should stop at this breakpoint, false if we
|
||||
// should continue.
|
||||
|
||||
|
@ -121,6 +198,24 @@ Watchpoint::Dump(Stream *s) const
|
|||
DumpWithLevel(s, lldb::eDescriptionLevelBrief);
|
||||
}
|
||||
|
||||
void
|
||||
Watchpoint::DumpSnapshots(const char *prefix, Stream *s) const
|
||||
{
|
||||
if (IsWatchVariable())
|
||||
{
|
||||
if (!m_snapshot_old_str.empty())
|
||||
s->Printf("\n%swatchpoint old value:\n\t%s", prefix, m_snapshot_old_str.c_str());
|
||||
if (!m_snapshot_new_str.empty())
|
||||
s->Printf("\n%swatchpoint new value:\n\t%s", prefix, m_snapshot_new_str.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t num_hex_digits = GetByteSize() * 2;
|
||||
s->Printf("\n%swatchpoint old value:0x%0*.*llx", prefix, num_hex_digits, num_hex_digits, m_snapshot_old_val);
|
||||
s->Printf("\n%swatchpoint new value:0x%0*.*llx", prefix, num_hex_digits, num_hex_digits, m_snapshot_new_val);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Watchpoint::DumpWithLevel(Stream *s, lldb::DescriptionLevel description_level) const
|
||||
{
|
||||
|
@ -142,7 +237,11 @@ Watchpoint::DumpWithLevel(Stream *s, lldb::DescriptionLevel description_level) c
|
|||
if (!m_decl_str.empty())
|
||||
s->Printf("\n declare @ '%s'", m_decl_str.c_str());
|
||||
if (!m_watch_spec_str.empty())
|
||||
s->Printf("\n static watchpoint spec = '%s'", m_watch_spec_str.c_str());
|
||||
s->Printf("\n watchpoint spec = '%s'", m_watch_spec_str.c_str());
|
||||
|
||||
// Dump the snapshots we have taken.
|
||||
DumpSnapshots(" ", s);
|
||||
|
||||
if (GetConditionText())
|
||||
s->Printf("\n condition = '%s'", GetConditionText());
|
||||
m_options.GetCallbackDescription(s, description_level);
|
||||
|
|
|
@ -168,11 +168,8 @@ WatchpointOptions::GetCallbackDescription (Stream *s, lldb::DescriptionLevel lev
|
|||
{
|
||||
if (m_callback_baton_sp.get())
|
||||
{
|
||||
if (level != eDescriptionLevelBrief)
|
||||
{
|
||||
s->EOL();
|
||||
m_callback_baton_sp->GetDescription (s, level);
|
||||
}
|
||||
s->EOL();
|
||||
m_callback_baton_sp->GetDescription (s, level);
|
||||
}
|
||||
}
|
||||
void
|
||||
|
@ -222,7 +219,7 @@ WatchpointOptions::CommandBaton::GetDescription (Stream *s, lldb::DescriptionLev
|
|||
}
|
||||
|
||||
s->IndentMore ();
|
||||
s->Indent("Watchpoint commands:\n");
|
||||
s->Indent("watchpoint commands:\n");
|
||||
|
||||
s->IndentMore ();
|
||||
if (data && data->user_source.GetSize() > 0)
|
||||
|
|
|
@ -1047,6 +1047,8 @@ protected:
|
|||
error.Clear();
|
||||
Watchpoint *wp = target->CreateWatchpoint(addr, size, watch_type, error).get();
|
||||
if (wp) {
|
||||
wp->SetWatchSpec(command.GetArgumentAtIndex(0));
|
||||
wp->SetWatchVariable(true);
|
||||
if (var_sp && var_sp->GetDeclaration().GetFile()) {
|
||||
StreamString ss;
|
||||
// True to show fullpath for declaration file.
|
||||
|
@ -1054,13 +1056,15 @@ protected:
|
|||
wp->SetDeclInfo(ss.GetString());
|
||||
}
|
||||
StreamString ss;
|
||||
ValueObject::DumpValueObject(ss, valobj_sp.get());
|
||||
wp->SetNewSnapshot(ss.GetString());
|
||||
output_stream.Printf("Watchpoint created: ");
|
||||
wp->GetDescription(&output_stream, lldb::eDescriptionLevelFull);
|
||||
output_stream.EOL();
|
||||
result.SetStatus(eReturnStatusSuccessFinishResult);
|
||||
} else {
|
||||
result.AppendErrorWithFormat("Watchpoint creation failed (addr=0x%llx, size=%lu).\n",
|
||||
addr, size);
|
||||
result.AppendErrorWithFormat("Watchpoint creation failed (addr=0x%llx, size=%lu, variable expression='%s').\n",
|
||||
addr, size, command.GetArgumentAtIndex(0));
|
||||
if (error.AsCString(NULL))
|
||||
result.AppendError(error.AsCString());
|
||||
result.SetStatus(eReturnStatusFailed);
|
||||
|
@ -1238,8 +1242,12 @@ protected:
|
|||
var_sp->GetDeclaration().DumpStopContext(&ss, true);
|
||||
wp->SetDeclInfo(ss.GetString());
|
||||
}
|
||||
StreamString ss;
|
||||
output_stream.Printf("Watchpoint created: ");
|
||||
uint64_t val = target->GetProcessSP()->ReadUnsignedIntegerFromMemory(addr, size, 0, error);
|
||||
if (error.Success())
|
||||
wp->SetNewSnapshotVal(val);
|
||||
else
|
||||
output_stream.Printf("watchpoint snapshot failed: %s", error.AsCString());
|
||||
wp->GetDescription(&output_stream, lldb::eDescriptionLevelFull);
|
||||
output_stream.EOL();
|
||||
result.SetStatus(eReturnStatusSuccessFinishResult);
|
||||
|
|
|
@ -451,7 +451,7 @@ public:
|
|||
virtual void
|
||||
PerformAction (Event *event_ptr)
|
||||
{
|
||||
LogSP log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS);
|
||||
LogSP log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS);
|
||||
// We're going to calculate if we should stop or not in some way during the course of
|
||||
// this code. Also by default we're going to stop, so set that here.
|
||||
m_should_stop = true;
|
||||
|
@ -461,11 +461,11 @@ public:
|
|||
if (wp_sp)
|
||||
{
|
||||
ExecutionContext exe_ctx (m_thread.GetStackFrameAtIndex(0));
|
||||
Process* process = exe_ctx.GetProcessPtr();
|
||||
{
|
||||
// check if this process is running on an architecture where watchpoints trigger
|
||||
// before the associated instruction runs. if so, disable the WP, single-step and then
|
||||
// re-enable the watchpoint
|
||||
Process* process = exe_ctx.GetProcessPtr();
|
||||
if (process)
|
||||
{
|
||||
uint32_t num; bool wp_triggers_after;
|
||||
|
@ -491,7 +491,62 @@ public:
|
|||
}
|
||||
}
|
||||
}
|
||||
StoppointCallbackContext context (event_ptr, exe_ctx, false);
|
||||
|
||||
// Record the snapshot of our watchpoint.
|
||||
VariableSP var_sp;
|
||||
ValueObjectSP valobj_sp;
|
||||
StackFrame *frame = exe_ctx.GetFramePtr();
|
||||
if (frame)
|
||||
{
|
||||
if (!wp_sp->IsWatchVariable())
|
||||
{
|
||||
assert (process);
|
||||
Error error;
|
||||
uint64_t val = process->ReadUnsignedIntegerFromMemory(wp_sp->GetLoadAddress(),
|
||||
wp_sp->GetByteSize(),
|
||||
0,
|
||||
error);
|
||||
if (log)
|
||||
{
|
||||
if (error.Success())
|
||||
log->Printf("Watchpoint snapshot val taken: 0x%llx\n", val);
|
||||
else
|
||||
log->Printf("Watchpoint snapshot val taking failed.\n");
|
||||
}
|
||||
wp_sp->SetNewSnapshotVal(val);
|
||||
}
|
||||
else if (!wp_sp->GetWatchSpec().empty())
|
||||
{
|
||||
// Things have checked out ok...
|
||||
Error error;
|
||||
uint32_t expr_path_options = StackFrame::eExpressionPathOptionCheckPtrVsMember |
|
||||
StackFrame::eExpressionPathOptionsAllowDirectIVarAccess;
|
||||
valobj_sp = frame->GetValueForVariableExpressionPath (wp_sp->GetWatchSpec().c_str(),
|
||||
eNoDynamicValues,
|
||||
expr_path_options,
|
||||
var_sp,
|
||||
error);
|
||||
if (valobj_sp)
|
||||
{
|
||||
// We're in business.
|
||||
StreamString ss;
|
||||
ValueObject::DumpValueObject(ss, valobj_sp.get());
|
||||
wp_sp->SetNewSnapshot(ss.GetString());
|
||||
}
|
||||
else
|
||||
wp_sp->SetNewSnapshot("snapshot attempt failed.");
|
||||
|
||||
if (log)
|
||||
log->Printf("Watchpoint snapshot taken: '%s'\n", wp_sp->GetNewSnapshot().c_str());
|
||||
}
|
||||
|
||||
// Now dump the snapshots we have taken.
|
||||
Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
|
||||
StreamSP output_sp = debugger.GetAsyncOutputStream ();
|
||||
wp_sp->DumpSnapshots("!!! ", output_sp.get());
|
||||
//output_sp->EOL();
|
||||
output_sp->Flush();
|
||||
}
|
||||
|
||||
if (m_should_stop && wp_sp->GetConditionText() != NULL)
|
||||
{
|
||||
|
@ -564,6 +619,7 @@ public:
|
|||
// If the condition says to stop, we run the callback to further decide whether to stop.
|
||||
if (m_should_stop)
|
||||
{
|
||||
StoppointCallbackContext context (event_ptr, exe_ctx, false);
|
||||
bool stop_requested = wp_sp->InvokeCallback (&context);
|
||||
// Also make sure that the callback hasn't continued the target.
|
||||
// If it did, when we'll set m_should_stop to false and get out of here.
|
||||
|
|
|
@ -63,11 +63,11 @@ class WatchpointLLDBCommandTestCase(TestBase):
|
|||
substrs = ['Watchpoint created', 'size = 4', 'type = w',
|
||||
'%s:%d' % (self.source, self.decl)])
|
||||
|
||||
self.runCmd('watchpoint command add 1 -o "expr -- global = 777"')
|
||||
self.runCmd('watchpoint command add 1 -o "expr -- cookie = 777"')
|
||||
|
||||
# List the watchpoint command we just added.
|
||||
self.expect("watchpoint command list 1",
|
||||
substrs = ['expr -- global = 777'])
|
||||
substrs = ['expr -- cookie = 777'])
|
||||
|
||||
# Use the '-v' option to do verbose listing of the watchpoint.
|
||||
# The hit count should be 0 initially.
|
||||
|
@ -81,9 +81,14 @@ class WatchpointLLDBCommandTestCase(TestBase):
|
|||
self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT,
|
||||
substrs = ['stop reason = watchpoint'])
|
||||
|
||||
# The watchpoint command "forced" our global variable to become 777.
|
||||
self.expect("frame variable -g global",
|
||||
substrs = ['(int32_t)', 'global = 777'])
|
||||
# Check that the watchpoint snapshoting mechanism is working.
|
||||
self.expect("watchpoint list -v",
|
||||
substrs = ['watchpoint old value:', 'global = 0',
|
||||
'watchpoint new value:', 'global = 1'])
|
||||
|
||||
# The watchpoint command "forced" our global variable 'cookie' to become 777.
|
||||
self.expect("frame variable -g cookie",
|
||||
substrs = ['(int32_t)', 'cookie = 777'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -63,11 +63,11 @@ class WatchpointPythonCommandTestCase(TestBase):
|
|||
substrs = ['Watchpoint created', 'size = 4', 'type = w',
|
||||
'%s:%d' % (self.source, self.decl)])
|
||||
|
||||
self.runCmd('watchpoint command add -s python 1 -o \'frame.EvaluateExpression("global = 777")\'')
|
||||
self.runCmd('watchpoint command add -s python 1 -o \'frame.EvaluateExpression("cookie = 777")\'')
|
||||
|
||||
# List the watchpoint command we just added.
|
||||
self.expect("watchpoint command list 1",
|
||||
substrs = ['frame.EvaluateExpression', 'global = 777'])
|
||||
substrs = ['frame.EvaluateExpression', 'cookie = 777'])
|
||||
|
||||
# Use the '-v' option to do verbose listing of the watchpoint.
|
||||
# The hit count should be 0 initially.
|
||||
|
@ -81,9 +81,14 @@ class WatchpointPythonCommandTestCase(TestBase):
|
|||
self.expect("thread backtrace", STOPPED_DUE_TO_WATCHPOINT,
|
||||
substrs = ['stop reason = watchpoint'])
|
||||
|
||||
# The watchpoint command "forced" our global variable to become 777.
|
||||
self.expect("frame variable -g global",
|
||||
substrs = ['(int32_t)', 'global = 777'])
|
||||
# Check that the watchpoint snapshoting mechanism is working.
|
||||
self.expect("watchpoint list -v",
|
||||
substrs = ['watchpoint old value:', 'global = 0',
|
||||
'watchpoint new value:', 'global = 1'])
|
||||
|
||||
# The watchpoint command "forced" our global variable 'cookie' to become 777.
|
||||
self.expect("frame variable -g cookie",
|
||||
substrs = ['(int32_t)', 'cookie = 777'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <stdint.h>
|
||||
|
||||
int32_t global = 0; // Watchpoint variable declaration.
|
||||
int32_t cookie = 0;
|
||||
|
||||
static void modify(int32_t &var) {
|
||||
++var;
|
||||
|
@ -19,10 +20,9 @@ int main(int argc, char** argv) {
|
|||
int local = 0;
|
||||
printf("&global=%p\n", &global);
|
||||
printf("about to write to 'global'...\n"); // Set break point at this line.
|
||||
// When stopped, watch 'global',
|
||||
// for the condition "global == 5".
|
||||
for (int i = 0; i < 10; ++i)
|
||||
modify(global);
|
||||
|
||||
printf("global=%d\n", global);
|
||||
printf("cookie=%d\n", cookie);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue