Skip to content

Commit

Permalink
Allow installing watchpoints at less than 8-byte alligned addresses f…
Browse files Browse the repository at this point in the history
…or AArch64 targets

This patch allows LLDB for AArch64 to watch all bytes, words or double words individually on non 8-byte alligned addresses.

This patch also adds tests to verify this functionality.

Differential revision: http://reviews.llvm.org/D21280

llvm-svn: 272916
  • Loading branch information
omjavaid committed Jun 16, 2016
1 parent d089a43 commit 43507f5
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 4 deletions.
@@ -0,0 +1,5 @@
LEVEL = ../../../make

C_SOURCES := main.c

include $(LEVEL)/Makefile.rules
@@ -0,0 +1,117 @@
"""
Test watchpoint size cases (1-byte, 2-byte, 4-byte).
Make sure we can watch all bytes, words or double words individually
when they are packed in a 8-byte region.
"""

from __future__ import print_function

import os, time
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil

class WatchpointSizeTestCase(TestBase):
NO_DEBUG_INFO_TESTCASE = True

mydir = TestBase.compute_mydir(__file__)

def setUp(self):
# Call super's setUp().
TestBase.setUp(self)

# Source filename.
self.source = 'main.c'

# Output filename.
self.exe_name = 'a.out'
self.d = {'C_SOURCES': self.source, 'EXE': self.exe_name}

@expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24446: WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows")
@expectedFailureAll(archs=['s390x']) # Read-write watchpoints not supported on SystemZ
def test_byte_size_watchpoints_with_byte_selection(self):
"""Test to selectively watch different bytes in a 8-byte array."""
self.run_watchpoint_size_test('byteArray', 8, '1')

@expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24446: WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows")
@expectedFailureAll(archs=['s390x']) # Read-write watchpoints not supported on SystemZ
def test_two_byte_watchpoints_with_word_selection(self):
"""Test to selectively watch different words in an 8-byte word array."""
self.run_watchpoint_size_test('wordArray', 4, '2')

@expectedFailureAndroid(archs=['arm', 'aarch64']) # Watchpoints not supported
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24446: WINDOWS XFAIL TRIAGE - Watchpoints not supported on Windows")
@expectedFailureAll(archs=['s390x']) # Read-write watchpoints not supported on SystemZ
def test_four_byte_watchpoints_with_dword_selection(self):
"""Test to selectively watch two double words in an 8-byte dword array."""
self.run_watchpoint_size_test('dwordArray', 2, '4')

def run_watchpoint_size_test(self, arrayName, array_size, watchsize):
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)

exe = os.path.join(os.getcwd(), self.exe_name)
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)

# Detect line number after which we are going to increment arrayName.
loc_line = line_number('main.c', '// About to write ' + arrayName)

# Set a breakpoint on the line detected above.
lldbutil.run_break_set_by_file_and_line (self, "main.c",loc_line,
num_expected_locations=1, loc_exact=True)

# Run the program.
self.runCmd("run", RUN_SUCCEEDED)

for i in range(array_size):
# 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'])

# Set a read_write type watchpoint arrayName
watch_loc=arrayName+"[" + str(i) + "]"
self.expect("watchpoint set variable -w read_write " + watch_loc,
WATCHPOINT_CREATED,
substrs = ['Watchpoint created', 'size = ' + watchsize, 'type = rw'])

# 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 due to the watchpoint.
# The stop reason of the thread should be watchpoint.
self.expect("thread list", STOPPED_DUE_TO_WATCHPOINT,
substrs = ['stopped', 'stop reason = watchpoint'])

# 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("process continue")

# We should be stopped due to the watchpoint.
# The stop reason of the thread should be watchpoint.
self.expect("thread list", STOPPED_DUE_TO_WATCHPOINT,
substrs = ['stopped', 'stop reason = watchpoint'])

# Use the '-v' option to do verbose listing of the watchpoint.
# The hit count should now be 1.
# Verify hit_count has been updated after value has been read.
self.expect("watchpoint list -v",
substrs = ['hit_count = 2'])

# Delete the watchpoint immediately, but set auto-confirm to true first.
self.runCmd("settings set auto-confirm true")
self.expect("watchpoint delete", substrs = ['All watchpoints removed.'])
# Restore the original setting of auto-confirm.
self.runCmd("settings clear auto-confirm")

self.runCmd("process continue")
@@ -0,0 +1,66 @@
//===-- 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 <stdio.h>
#include <stdint.h>

uint64_t pad0 = 0;
uint8_t byteArray[8] = {0};
uint64_t pad1 = 0;
uint16_t wordArray[4] = {0};
uint64_t pad2 = 0;
uint32_t dwordArray[2] = {0};

int main(int argc, char** argv) {

int i;
uint8_t localByte;
uint16_t localWord;
uint32_t localDword;

for (i = 0; i < 8; i++)
{
printf("About to write byteArray[%d] ...\n", i); // About to write byteArray
pad0++;
byteArray[i]++;
pad1++;
localByte = byteArray[i]; // Here onwards we should'nt be stopped in loop
byteArray[i]++;
localByte = byteArray[i];
}

pad0 = 0;
pad1 = 0;

for (i = 0; i < 4; i++)
{
printf("About to write wordArray[%d] ...\n", i); // About to write wordArray
pad0++;
wordArray[i]++;
pad1++;
localWord = wordArray[i]; // Here onwards we should'nt be stopped in loop
wordArray[i]++;
localWord = wordArray[i];
}

pad0 = 0;
pad1 = 0;

for (i = 0; i < 2; i++)
{
printf("About to write dwordArray[%d] ...\n", i); // About to write dwordArray
pad0++;
dwordArray[i]++;
pad1++;
localDword = dwordArray[i]; // Here onwards we shouldn't be stopped in loop
dwordArray[i]++;
localDword = dwordArray[i];
}

return 0;
}
Expand Up @@ -566,6 +566,7 @@ NativeRegisterContextLinux_arm64::SetHardwareWatchpoint (lldb::addr_t addr, size
return LLDB_INVALID_INDEX32;

uint32_t control_value = 0, wp_index = 0;
lldb::addr_t real_addr = addr;

// Check if we are setting watchpoint other than read/write/access
// Also update watchpoint flag to match AArch64 write-read bit configuration.
Expand All @@ -588,9 +589,23 @@ NativeRegisterContextLinux_arm64::SetHardwareWatchpoint (lldb::addr_t addr, size
return LLDB_INVALID_INDEX32;

// Check 8-byte alignment for hardware watchpoint target address.
// TODO: Add support for watching un-aligned addresses
// Below is a hack to recalculate address and size in order to
// make sure we can watch non 8-byte alligned addresses as well.
if (addr & 0x07)
return LLDB_INVALID_INDEX32;
{
uint8_t watch_mask = (addr & 0x07) + size;

if (watch_mask > 0x08)
return LLDB_INVALID_INDEX32;
else if (watch_mask <= 0x02)
size = 2;
else if (watch_mask <= 0x04)
size = 4;
else
size = 8;

addr = addr & (~0x07);
}

// Setup control value
control_value = watch_flags << 3;
Expand Down Expand Up @@ -620,6 +635,7 @@ NativeRegisterContextLinux_arm64::SetHardwareWatchpoint (lldb::addr_t addr, size
if ((m_hwp_regs[wp_index].control & 1) == 0)
{
// Update watchpoint in local cache
m_hwp_regs[wp_index].real_addr = real_addr;
m_hwp_regs[wp_index].address = addr;
m_hwp_regs[wp_index].control = control_value;
m_hwp_regs[wp_index].refcount = 1;
Expand Down Expand Up @@ -801,6 +817,7 @@ NativeRegisterContextLinux_arm64::GetWatchpointHitIndex(uint32_t &wp_index, lldb
if (m_hwp_regs[wp_index].refcount >= 1 && WatchpointIsEnabled(wp_index)
&& trap_addr >= watch_addr && trap_addr < watch_addr + watch_size)
{
m_hwp_regs[wp_index].hit_addr = trap_addr;
return Error();
}
}
Expand All @@ -821,7 +838,24 @@ NativeRegisterContextLinux_arm64::GetWatchpointAddress (uint32_t wp_index)
return LLDB_INVALID_ADDRESS;

if (WatchpointIsEnabled(wp_index))
return m_hwp_regs[wp_index].address;
return m_hwp_regs[wp_index].real_addr;
else
return LLDB_INVALID_ADDRESS;
}

lldb::addr_t
NativeRegisterContextLinux_arm64::GetWatchpointHitAddress (uint32_t wp_index)
{
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));

if (log)
log->Printf ("NativeRegisterContextLinux_arm64::%s()", __FUNCTION__);

if (wp_index >= m_max_hwp_supported)
return LLDB_INVALID_ADDRESS;

if (WatchpointIsEnabled(wp_index))
return m_hwp_regs[wp_index].hit_addr;
else
return LLDB_INVALID_ADDRESS;
}
Expand Down
Expand Up @@ -73,6 +73,9 @@ namespace process_linux {
Error
GetWatchpointHitIndex(uint32_t &wp_index, lldb::addr_t trap_addr) override;

lldb::addr_t
GetWatchpointHitAddress (uint32_t wp_index) override;

lldb::addr_t
GetWatchpointAddress (uint32_t wp_index) override;

Expand Down Expand Up @@ -161,6 +164,8 @@ namespace process_linux {
struct DREG
{
lldb::addr_t address; // Breakpoint/watchpoint address value.
lldb::addr_t hit_addr; // Address at which last watchpoint trigger exception occurred.
lldb::addr_t real_addr; // Address value that should cause target to stop.
uint32_t control; // Breakpoint/watchpoint control value.
uint32_t refcount; // Serves as enable/disable and refernce counter.
};
Expand Down
3 changes: 2 additions & 1 deletion lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
Expand Up @@ -2059,7 +2059,8 @@ ProcessGDBRemote::SetThreadStopInfo (lldb::tid_t tid,
{
WatchpointSP wp_sp;
ArchSpec::Core core = GetTarget().GetArchitecture().GetCore();
if (core >= ArchSpec::kCore_mips_first && core <= ArchSpec::kCore_mips_last)
if ((core >= ArchSpec::kCore_mips_first && core <= ArchSpec::kCore_mips_last) ||
(core >= ArchSpec::eCore_arm_arm64 && core <= ArchSpec::eCore_arm_aarch64))
wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_hit_addr);
if (!wp_sp)
wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr);
Expand Down

0 comments on commit 43507f5

Please sign in to comment.