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:
Johnny Chen 2012-08-13 21:09:54 +00:00
parent d0af1d9657
commit 209bd65ea4
9 changed files with 223 additions and 34 deletions

View File

@ -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.

View File

@ -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",

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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.

View File

@ -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__':

View File

@ -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__':

View File

@ -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);
}