Add an accompanying option to the 'frame variable -w' command to, instead of watching the variable,

watch the location pointed to by the variable.  An example,

(lldb) frame variable -w write -x 1 -g g_char_ptr
(char *) g_char_ptr = 0x0000000100100860 ""...
Watchpoint created: WatchpointLocation 1: addr = 0x100100860 size = 1 state = enabled type = w
    declare @ '/Volumes/data/lldb/svn/trunk/test/functionalities/watchpoint/hello_watchlocation/main.cpp:21'

...

(lldb) c
Process 3936 resuming

...

rocess 3936 stopped
* thread #2: tid = 0x3403, 0x00000001000009b7 a.out`do_bad_thing_with_location(char*, char) + 23 at main.cpp:27, stop reason = watchpoint 1
    frame #0: 0x00000001000009b7 a.out`do_bad_thing_with_location(char*, char) + 23 at main.cpp:27
   24  	do_bad_thing_with_location(char *char_ptr, char new_val)
   25  	{
   26  	    *char_ptr = new_val;
-> 27  	}
   28  	
   29  	uint32_t access_pool (uint32_t flag = 0);
   30  	
(lldb) 

Also add TestWatchLocation.py test to exercise this functionality.

llvm-svn: 140836
This commit is contained in:
Johnny Chen 2011-09-30 01:08:48 +00:00
parent 2af4db5835
commit b62a3be1a2
6 changed files with 231 additions and 8 deletions

View File

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

View File

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

View File

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

View File

@ -0,0 +1,5 @@
LEVEL = ../../../make
CXX_SOURCES := main.cpp
include $(LEVEL)/Makefile.rules

View File

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

View File

@ -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 <pthread.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
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;
}