From 6027c94d2f7f31917226e0eaa544d22cdf90a713 Mon Sep 17 00:00:00 2001 From: Johnny Chen Date: Sat, 24 Sep 2011 00:50:33 +0000 Subject: [PATCH] Add an SB API SBFrame::WatchValue() and exported to the Python interface to set a watchpoint Pythonically. If the find-and-watch-a-variable operation fails, an invalid SBValue is returned, instead. Example Python usage: value = frame0.WatchValue('global', lldb.eValueTypeVariableGlobal, lldb.LLDB_WATCH_TYPE_READ|lldb.LLDB_WATCH_TYPE_WRITE) Add TestSetWatchpoint.py to exercise this API. We have 400 test cases now. llvm-svn: 140436 --- lldb/include/lldb/API/SBFrame.h | 7 ++ lldb/scripts/Python/interface/SBFrame.i | 9 ++ lldb/source/API/SBFrame.cpp | 43 ++++++++ lldb/test/python_api/watchpoint/Makefile | 5 + .../watchpoint/TestSetWatchpoint.py | 97 +++++++++++++++++++ lldb/test/python_api/watchpoint/main.c | 24 +++++ 6 files changed, 185 insertions(+) create mode 100644 lldb/test/python_api/watchpoint/Makefile create mode 100644 lldb/test/python_api/watchpoint/TestSetWatchpoint.py create mode 100644 lldb/test/python_api/watchpoint/main.c diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h index 47c59a908cef..3bfa14f43760 100644 --- a/lldb/include/lldb/API/SBFrame.h +++ b/lldb/include/lldb/API/SBFrame.h @@ -176,6 +176,13 @@ public: lldb::SBValue FindValue (const char *name, ValueType value_type, lldb::DynamicValueType use_dynamic); + /// Find and watch a variable using the frame as the scope. + /// It returns an SBValue, similar to FindValue() method, if find-and-watch + /// operation succeeds. Otherwise, an invalid SBValue is returned. + /// You can use LLDB_WATCH_TYPE_READ | LLDB_WATCH_TYPE_WRITE for 'rw' watch. + lldb::SBValue + WatchValue (const char *name, ValueType value_type, uint32_t watch_type); + bool GetDescription (lldb::SBStream &description); diff --git a/lldb/scripts/Python/interface/SBFrame.i b/lldb/scripts/Python/interface/SBFrame.i index 28526ab7478a..19bccc7a0325 100644 --- a/lldb/scripts/Python/interface/SBFrame.i +++ b/lldb/scripts/Python/interface/SBFrame.i @@ -215,6 +215,15 @@ public: lldb::SBValue FindValue (const char *name, ValueType value_type, lldb::DynamicValueType use_dynamic); + %feature("docstring", " + /// Find and watch a variable using the frame as the scope. + /// It returns an SBValue, similar to FindValue() method, if find-and-watch + /// operation succeeds. Otherwise, an invalid SBValue is returned. + /// You can use LLDB_WATCH_TYPE_READ | LLDB_WATCH_TYPE_WRITE for 'rw' watch. + ") FindValue; + lldb::SBValue + WatchValue (const char *name, ValueType value_type, uint32_t watch_type); + bool GetDescription (lldb::SBStream &description); diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp index f72f8bfacf4b..c4fc392b98d4 100644 --- a/lldb/source/API/SBFrame.cpp +++ b/lldb/source/API/SBFrame.cpp @@ -14,6 +14,7 @@ #include "lldb/lldb-types.h" +#include "lldb/Breakpoint/WatchpointLocation.h" #include "lldb/Core/Address.h" #include "lldb/Core/ConstString.h" #include "lldb/Core/Log.h" @@ -405,6 +406,48 @@ SBFrame::FindValue (const char *name, ValueType value_type) return value; } +/// Find and watch a variable using the frame as the scope. +/// You can use LLDB_WATCH_TYPE_READ | LLDB_WATCH_TYPE_WRITE for 'rw' watch. +SBValue +SBFrame::WatchValue (const char *name, ValueType value_type, uint32_t watch_type) +{ + SBValue sb_value_empty; + + if (!IsValid()) + return sb_value_empty; + + // Acquire the API locker, to be released at the end of the method call. + Mutex::Locker api_locker (m_opaque_sp->GetThread().GetProcess().GetTarget().GetAPIMutex()); + + switch (value_type) { + case eValueTypeVariableGlobal: // global variable + case eValueTypeVariableStatic: // static variable + case eValueTypeVariableArgument: // function argument variables + case eValueTypeVariableLocal: // function local variables + break; + default: + return sb_value_empty; // these are not eligible for watching + } + + SBValue sb_value = FindValue(name, value_type); + // If the SBValue is not valid, there's no point in even trying to watch it. + if (!sb_value.IsValid()) + return sb_value; + + addr_t addr = sb_value.GetLoadAddress(); + size_t size = sb_value.GetByteSize(); + + WatchpointLocationSP wp_loc_sp = m_opaque_sp->GetThread().GetProcess().GetTarget(). + CreateWatchpointLocation(addr, size, watch_type); + + LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBFrame(%p)::WatchValue (name=\"%s\", value_type=%i, watch_type=%i) => SBValue(%p) & wp_loc(%p)", + m_opaque_sp.get(), name, value_type, watch_type, sb_value.get(), wp_loc_sp.get()); + + return wp_loc_sp ? sb_value : sb_value_empty; +} + SBValue SBFrame::FindValue (const char *name, ValueType value_type, lldb::DynamicValueType use_dynamic) { diff --git a/lldb/test/python_api/watchpoint/Makefile b/lldb/test/python_api/watchpoint/Makefile new file mode 100644 index 000000000000..0d70f2595019 --- /dev/null +++ b/lldb/test/python_api/watchpoint/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/python_api/watchpoint/TestSetWatchpoint.py b/lldb/test/python_api/watchpoint/TestSetWatchpoint.py new file mode 100644 index 000000000000..8618962b23ee --- /dev/null +++ b/lldb/test/python_api/watchpoint/TestSetWatchpoint.py @@ -0,0 +1,97 @@ +""" +Use lldb Python SBFrame API to create a watchpoint for read_write of 'globl' var +""" + +import os, time +import re +import unittest2 +import lldb, lldbutil +from lldbtest import * + +class SetWatchpointAPITestCase(TestBase): + + mydir = os.path.join("python_api", "watchpoint") + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.c' + # Find the line number to break inside main(). + self.line = line_number(self.source, '// Set break point at this line.') + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @python_api_test + def test_watch_val_with_dsym(self): + """Exercise SBFrame.WatchValue() API to set a watchpoint.""" + self.buildDsym() + self.do_set_watchpoint() + + @python_api_test + def test_watch_val_with_dwarf(self): + """Exercise SBFrame.WatchValue() API to set a watchpoint.""" + self.buildDwarf() + self.do_set_watchpoint() + + def do_set_watchpoint(self): + """Use SBFrame.WatchValue() to set a watchpoint and verify that the program stops later due to the watchpoint.""" + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now create a breakpoint on main.c. + breakpoint = target.BreakpointCreateByLocation(self.source, self.line) + self.assertTrue(breakpoint and + breakpoint.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + process = target.LaunchSimple(None, None, os.getcwd()) + + # We should be stopped due to the breakpoint. Get frame #0. + process = target.GetProcess() + self.assertTrue(process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + frame0 = thread.GetFrameAtIndex(0) + + value = frame0.WatchValue('global', + lldb.eValueTypeVariableGlobal, + lldb.LLDB_WATCH_TYPE_READ|lldb.LLDB_WATCH_TYPE_WRITE) + self.assertTrue(value, "Successfully found the variable and set a watchpoint") + self.DebugSBValue(value) + + # Continue. Expect the program to stop due to the variable being written to. + process.Continue() + + if (self.TraceOn()): + lldbutil.print_stacktraces(process) + + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonWatchpoint) + self.assertTrue(thread, "The thread stopped due to watchpoint") + self.DebugSBValue(value) + + # Continue. Expect the program to stop due to the variable being read from. + process.Continue() + + if (self.TraceOn()): + lldbutil.print_stacktraces(process) + + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonWatchpoint) + self.assertTrue(thread, "The thread stopped due to watchpoint") + self.DebugSBValue(value) + + # Continue the process. We don't expect the program to be stopped again. + process.Continue() + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) + + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/python_api/watchpoint/main.c b/lldb/test/python_api/watchpoint/main.c new file mode 100644 index 000000000000..622e74679299 --- /dev/null +++ b/lldb/test/python_api/watchpoint/main.c @@ -0,0 +1,24 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include +#include + +int32_t global = 10; // Watchpoint variable declaration. + +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 write. + global = 20; + local += argc; + ++local; + printf("local: %d\n", local); + printf("global=%d\n", global); +}