forked from OSchip/llvm-project
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:
parent
2af4db5835
commit
b62a3be1a2
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
LEVEL = ../../../make
|
||||
|
||||
CXX_SOURCES := main.cpp
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
|
@ -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()
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue