diff --git a/lldb/include/lldb/Interpreter/OptionGroupWatchpoint.h b/lldb/include/lldb/Interpreter/OptionGroupWatchpoint.h index 695b3722d9f6..d813ec865c30 100644 --- a/lldb/include/lldb/Interpreter/OptionGroupWatchpoint.h +++ b/lldb/include/lldb/Interpreter/OptionGroupWatchpoint.h @@ -55,8 +55,9 @@ namespace lldb_private { eWatchReadWrite } WatchType; - bool watch_variable; WatchType watch_type; + uint32_t watch_size; + bool watch_variable; private: DISALLOW_COPY_AND_ASSIGN(OptionGroupWatchpoint); diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp index 4790eef4cde4..288a5ea1b7d6 100644 --- a/lldb/source/Commands/CommandObjectFrame.cpp +++ b/lldb/source/Commands/CommandObjectFrame.cpp @@ -436,7 +436,7 @@ public: } // Things have checked out ok... - // m_option_watchpoint.watch_mode specifies the mode for watching. + // m_option_watchpoint.watch_type specifies the type of watching. } if (command.GetArgumentCount() > 0) { @@ -532,12 +532,20 @@ public: if (m_option_watchpoint.watch_variable) { AddressType addr_type; - lldb::addr_t addr = valobj_sp->GetAddressOf(false, &addr_type); + lldb::addr_t addr = 0; size_t size = 0; - if (addr_type == eAddressTypeLoad) { - // We're in business. - // Find out the size of this variable. - size = valobj_sp->GetByteSize(); + if (m_option_watchpoint.watch_size == 0) { + addr = valobj_sp->GetAddressOf(false, &addr_type); + if (addr_type == eAddressTypeLoad) { + // We're in business. + // Find out the size of this variable. + size = valobj_sp->GetByteSize(); + } + } else { + // The '-xsize'/'-x' option means to treat the value object as + // a pointer and to watch the pointee with the specified size. + addr = valobj_sp->GetValueAsUnsigned(0); + size = m_option_watchpoint.watch_size; } uint32_t watch_type = m_option_watchpoint.watch_type; WatchpointLocation *wp_loc = exe_ctx.GetTargetRef().CreateWatchpointLocation(addr, size, watch_type).get(); diff --git a/lldb/source/Interpreter/OptionGroupWatchpoint.cpp b/lldb/source/Interpreter/OptionGroupWatchpoint.cpp index b0f381ef4b3c..076d010ae7ec 100644 --- a/lldb/source/Interpreter/OptionGroupWatchpoint.cpp +++ b/lldb/source/Interpreter/OptionGroupWatchpoint.cpp @@ -28,11 +28,21 @@ static OptionEnumValueElement g_watch_type[] = { 0, NULL, NULL } }; +static OptionEnumValueElement g_watch_size[] = +{ + { 1, "1", "Watch for byte size of 1"}, + { 2, "2", "Watch for byte size of 2"}, + { 4, "4", "Watch for byte size of 4"}, + { 8, "8", "Watch for byte size of 8"}, + { 0, NULL, NULL } +}; + // if you add any options here, remember to update the counters in OptionGroupWatchpoint::GetNumDefinitions() static OptionDefinition g_option_table[] = { - { LLDB_OPT_SET_1, false, "watch", 'w', required_argument, g_watch_type, 0, eArgTypeWatchType, "Determine how to watch a memory location (read, write, or read/write)."} + { LLDB_OPT_SET_1, false, "watch", 'w', required_argument, g_watch_type, 0, eArgTypeWatchType, "Determine how to watch a variable (read, write, or read/write)."}, + { LLDB_OPT_SET_1, false, "xsize", 'x', required_argument, g_watch_size, 0, eArgTypeByteSize, "Number of bytes to use to watch a location (1, 2, 4, or 8)."} }; @@ -61,6 +71,14 @@ OptionGroupWatchpoint::SetOptionValue (CommandInterpreter &interpreter, error.SetErrorStringWithFormat("Invalid option arg for '-w': '%s'.\n", option_arg); break; } + case 'x': { + bool success = false; + OptionEnumValueElement *enum_values = g_option_table[option_idx].enum_values; + watch_size = (WatchType) Args::StringToOptionEnum(option_arg, enum_values, 0, &success); + if (!success) + error.SetErrorStringWithFormat("Invalid option arg for '-x': '%s'.\n", option_arg); + break; + } default: error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option); break; @@ -74,6 +92,7 @@ OptionGroupWatchpoint::OptionParsingStarting (CommandInterpreter &interpreter) { watch_variable = false; watch_type = eWatchInvalid; + watch_size = 0; } diff --git a/lldb/test/functionalities/watchpoint/hello_watchlocation/Makefile b/lldb/test/functionalities/watchpoint/hello_watchlocation/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/lldb/test/functionalities/watchpoint/hello_watchlocation/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/watchpoint/hello_watchlocation/TestWatchLocation.py b/lldb/test/functionalities/watchpoint/hello_watchlocation/TestWatchLocation.py new file mode 100644 index 000000000000..73a0f29d64ad --- /dev/null +++ b/lldb/test/functionalities/watchpoint/hello_watchlocation/TestWatchLocation.py @@ -0,0 +1,92 @@ +""" +Test lldb watchpoint that uses '-x size' to watch a pointed location with size. +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * + +class HelloWatchLocationTestCase(TestBase): + + mydir = os.path.join("functionalities", "watchpoint", "hello_watchlocation") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_hello_watchlocation_with_dsym(self): + """Test watching a location with '-x size' option.""" + self.buildDsym(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + self.hello_watchlocation() + + def test_hello_watchlocation_with_dwarf(self): + """Test watching a location with '-x size' option.""" + self.buildDwarf(dictionary=self.d) + self.setTearDownCleanup(dictionary=self.d) + self.hello_watchlocation() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Our simple source filename. + self.source = 'main.cpp' + # Find the line number to break inside main(). + self.line = line_number(self.source, '// Set break point at this line.') + # This is for verifying that watch location works. + self.violating_func = "do_bad_thing_with_location"; + # Build dictionary to have unique executable names for each test method. + self.exe_name = self.testMethodName + self.d = {'CXX_SOURCES': self.source, 'EXE': self.exe_name} + + def hello_watchlocation(self): + """Test watching a location with '-x size' option.""" + exe = os.path.join(os.getcwd(), self.exe_name) + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Add a breakpoint to set a watchpoint when stopped on the breakpoint. + self.expect("breakpoint set -l %d" % self.line, BREAKPOINT_CREATED, + startstr = "Breakpoint created: 1: file ='%s', line = %d, locations = 1" % + (self.source, self.line)) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # We should be stopped again due to the breakpoint. + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Now let's set a write-type watchpoint pointed to by 'g_char_ptr'. + # The main.cpp, by design, misbehaves by not following the agreed upon + # protocol of using a mutex while accessing the global pool and by not + # incrmenting the global pool by 2. + self.expect("frame variable -w write -x 1 -g g_char_ptr", WATCHPOINT_CREATED, + substrs = ['Watchpoint created', 'size = 1', 'type = w']) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should be 0 initially. + self.expect("watchpoint list -v", + substrs = ['hit_count = 0']) + + self.runCmd("process continue") + + # We should be stopped again due to the watchpoint (write type), but + # only once. The stop reason of the thread should be watchpoint. + self.expect("thread list", STOPPED_DUE_TO_WATCHPOINT, + substrs = ['stopped', + 'stop reason = watchpoint', + self.violating_func]) + + # Use the '-v' option to do verbose listing of the watchpoint. + # The hit count should now be 1. + self.expect("watchpoint list -v", + substrs = ['hit_count = 1']) + + self.runCmd("thread backtrace all") + + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/watchpoint/hello_watchlocation/main.cpp b/lldb/test/functionalities/watchpoint/hello_watchlocation/main.cpp new file mode 100644 index 000000000000..8515ca0cca8c --- /dev/null +++ b/lldb/test/functionalities/watchpoint/hello_watchlocation/main.cpp @@ -0,0 +1,98 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C includes +#include +#include +#include +#include +#include + +pthread_t g_thread_1 = NULL; +pthread_t g_thread_2 = NULL; +pthread_t g_thread_3 = NULL; + +char *g_char_ptr = NULL; + +void +do_bad_thing_with_location(char *char_ptr, char new_val) +{ + *char_ptr = new_val; +} + +uint32_t access_pool (uint32_t flag = 0); + +uint32_t +access_pool (uint32_t flag) +{ + static pthread_mutex_t g_access_mutex = PTHREAD_MUTEX_INITIALIZER; + if (flag == 0) + ::pthread_mutex_lock (&g_access_mutex); + + char old_val = *g_char_ptr; + if (flag != 0) + do_bad_thing_with_location(g_char_ptr, old_val + 1); + + if (flag == 0) + ::pthread_mutex_unlock (&g_access_mutex); + return *g_char_ptr; +} + +void * +thread_func (void *arg) +{ + uint32_t thread_index = *((uint32_t *)arg); + printf ("%s (thread index = %u) startng...\n", __FUNCTION__, thread_index); + + uint32_t count = 0; + uint32_t val; + while (count++ < 15) + { + // random micro second sleep from zero to 3 seconds + int usec = ::rand() % 3000000; + printf ("%s (thread = %u) doing a usleep (%d)...\n", __FUNCTION__, thread_index, usec); + ::usleep (usec); + + if (count < 7) + val = access_pool (); + else + val = access_pool (1); + + printf ("%s (thread = %u) after usleep access_pool returns %d (count=%d)...\n", __FUNCTION__, thread_index, val, count); + } + printf ("%s (thread index = %u) exiting...\n", __FUNCTION__, thread_index); + return NULL; +} + + +int main (int argc, char const *argv[]) +{ + int err; + void *thread_result = NULL; + uint32_t thread_index_1 = 1; + uint32_t thread_index_2 = 2; + uint32_t thread_index_3 = 3; + + g_char_ptr = (char *)malloc (1); + *g_char_ptr = 0; + + // Create 3 threads + err = ::pthread_create (&g_thread_1, NULL, thread_func, &thread_index_1); + err = ::pthread_create (&g_thread_2, NULL, thread_func, &thread_index_2); + err = ::pthread_create (&g_thread_3, NULL, thread_func, &thread_index_3); + + printf ("Before turning all three threads loose...\n"); // Set break point at this line. + + // Join all of our threads + err = ::pthread_join (g_thread_1, &thread_result); + err = ::pthread_join (g_thread_2, &thread_result); + err = ::pthread_join (g_thread_3, &thread_result); + + return 0; +}