This is the mail archive of the
sid@sourceware.org
mailing list for the SID project.
[patch][commit] Hardware Watchpoint Support
- From: Dave Brolley <brolley at redhat dot com>
- To: sid at sources dot redhat dot com
- Date: Mon, 14 Nov 2005 15:14:23 -0500
- Subject: [patch][commit] Hardware Watchpoint Support
Hi,
I've committed the attached patch which adds support for hardware
watchpoints to SID's GDB component. The Z2 and z2 packets are now supported.
Tested on xstormy16-elf, but the work is target independent.
Dave
sid/component/gdb/ChangeLog:
2005-11-14 Dave Brolley <brolley@redhat.com>
* sidcpuutil.h (basic_cpu::pin_factory): Parse, recognize and add
watchable register and virtual pin for names representing hardware
watchpoints.
(read_data_memory_*): Make accessible as virtual methods from basic_cpu.
(read_watchpoint_memory): New method of basic_cpu.
sid/include/ChangeLog:
2005-11-14 Dave Brolley <brolley@redhat.com>
* gdb.h (hw_watchpoints_t): New typedef in gdb.
(hw_watchpoints): New member of gdb.
(remove_all_hw_watchpoints, remove_hw_watchpoint): New methods
of gdb.
(add_hw_watchpoint): New method of gdb.
* gdb.cxx (remove_breakpoint): Handle GDBSERV_TARGET_BP_WRITE.
(set_breakpoint): Likewise.
(remove_all_hw_watchpoints, remove_hw_watchpoint): New methods
of gdb.
(add_hw_watchpoint): New method of gdb.
(process_detach): Call remove_all_hw_watchpoints.
Index: sid/component/gdb/gdb.cxx
===================================================================
RCS file: /cvs/src/src/sid/component/gdb/gdb.cxx,v
retrieving revision 1.15
diff -c -p -r1.15 gdb.cxx
*** sid/component/gdb/gdb.cxx 19 Aug 2005 19:43:50 -0000 1.15
--- sid/component/gdb/gdb.cxx 14 Nov 2005 19:56:57 -0000
*************** gdb::remove_breakpoint (unsigned long ty
*** 1007,1023 ****
if (! enable_Z_packet) return 1;
if (this->cpu == 0) return -1;
! bool ok = false;
if ((type == GDBSERV_TARGET_BP_HARDWARE) ||
(type == GDBSERV_TARGET_BP_SOFTWARE && force_Z_sw_to_hw))
! ok = this->remove_hw_breakpoint (watch_pc, bp_length);
else if ((type == GDBSERV_TARGET_BP_SOFTWARE) ||
(type == GDBSERV_TARGET_BP_HARDWARE && force_Z_hw_to_sw))
! ok = this->remove_sw_breakpoint (watch_pc, bp_length);
// Fail on uses of other breakpoint types (WRITE, READ, ACCESS, UNKNOWN)
! return ok ? 0 : -1;
}
--- 1007,1025 ----
if (! enable_Z_packet) return 1;
if (this->cpu == 0) return -1;
! int rc = 1; // Not supported
if ((type == GDBSERV_TARGET_BP_HARDWARE) ||
(type == GDBSERV_TARGET_BP_SOFTWARE && force_Z_sw_to_hw))
! rc = this->remove_hw_breakpoint (watch_pc, bp_length) ? 0 : -1;
else if ((type == GDBSERV_TARGET_BP_SOFTWARE) ||
(type == GDBSERV_TARGET_BP_HARDWARE && force_Z_hw_to_sw))
! rc = this->remove_sw_breakpoint (watch_pc, bp_length) ? 0 : -1;
! else if (type == GDBSERV_TARGET_BP_WRITE)
! rc = this->remove_hw_watchpoint (watch_pc, bp_length) ? 0 : -1;
// Fail on uses of other breakpoint types (WRITE, READ, ACCESS, UNKNOWN)
! return rc;
}
*************** gdb::remove_sw_breakpoint (host_int_8 ad
*** 1146,1151 ****
--- 1148,1211 ----
}
+ bool
+ gdb::remove_all_hw_watchpoints ()
+ {
+ while (true)
+ {
+ hw_watchpoints_t::iterator it = this->hw_watchpoints.begin();
+ if (it == this->hw_watchpoints.end()) break;
+
+ // clean up carcass with refcount=0
+ if (it->second == 0)
+ {
+ this->hw_watchpoints.erase(it);
+ continue;
+ }
+
+ // decrement refcount
+ string watcher_name = it->first;
+ bool ok = this->remove_hw_watchpoint (watcher_name);
+ if (!ok) return ok;
+ }
+ return true;
+ }
+
+
+ bool
+ gdb::remove_hw_watchpoint (const string &watcher_name)
+ {
+ if (this->hw_watchpoints[watcher_name] <= 0)
+ {
+ cerr << "sw-debug-gdb: duplicate watchpoint count underflow!" << endl;
+ return false;
+ }
+
+ this->hw_watchpoints[watcher_name] --;
+ if (this->hw_watchpoints[watcher_name] == 0)
+ {
+ component::status s = this->cpu->disconnect_pin (watcher_name, & this->trapstop_pin);
+ return (s == component::ok);
+ }
+ else
+ return true;
+ }
+
+
+ bool
+ gdb::remove_hw_watchpoint (host_int_8 address, host_int_4 length)
+ {
+ string watcher_name = string ("watch:")
+ + map_watchable_name ("gdb-watchpoint-"
+ + make_numeric_attribute (address)
+ + "-"
+ + make_numeric_attribute (length))
+ + ":change";
+
+ return remove_hw_watchpoint (watcher_name);
+ }
+
+
int
gdb::set_breakpoint (unsigned long type, struct gdbserv_reg *addr, struct gdbserv_reg *len)
{
*************** gdb::set_breakpoint (unsigned long type,
*** 1165,1181 ****
if (! enable_Z_packet) return 1;
if (this->cpu == 0) return -1;
! bool ok = false;
if ((type == GDBSERV_TARGET_BP_HARDWARE) ||
(type == GDBSERV_TARGET_BP_SOFTWARE && force_Z_sw_to_hw))
! ok = this->add_hw_breakpoint (watch_pc, bp_length);
else if ((type == GDBSERV_TARGET_BP_SOFTWARE) ||
(type == GDBSERV_TARGET_BP_HARDWARE && force_Z_hw_to_sw))
! ok = this->add_sw_breakpoint (watch_pc, bp_length);
! // Fail on uses of other breakpoint types (WRITE, READ, ACCESS, UNKNOWN)
! return ok ? 0 : -1;
}
--- 1225,1244 ----
if (! enable_Z_packet) return 1;
if (this->cpu == 0) return -1;
!
! int rc = 1; // Not supported
if ((type == GDBSERV_TARGET_BP_HARDWARE) ||
(type == GDBSERV_TARGET_BP_SOFTWARE && force_Z_sw_to_hw))
! rc = this->add_hw_breakpoint (watch_pc, bp_length) ? 0 : -1;
else if ((type == GDBSERV_TARGET_BP_SOFTWARE) ||
(type == GDBSERV_TARGET_BP_HARDWARE && force_Z_hw_to_sw))
! rc = this->add_sw_breakpoint (watch_pc, bp_length) ? 0 : -1;
! else if (type == GDBSERV_TARGET_BP_WRITE)
! rc = this->add_hw_watchpoint (watch_pc, bp_length) ? 0 : -1;
! // Fail on uses of other breakpoint types (READ, ACCESS, UNKNOWN)
! return rc;
}
*************** gdb::add_sw_breakpoint (host_int_8 addre
*** 1266,1271 ****
--- 1329,1359 ----
}
+ bool
+ gdb::add_hw_watchpoint (host_int_8 address, host_int_4 length)
+ {
+ // XXX: be sensitive to length
+
+ string watcher_name = string ("watch:")
+ + map_watchable_name ("gdb-watchpoint-"
+ + make_numeric_attribute (address)
+ + "-"
+ + make_numeric_attribute (length))
+ + ":change";
+
+ // see also ::remove_hw_watchpoint()
+
+ this->hw_watchpoints[watcher_name] ++;
+ if (this->hw_watchpoints[watcher_name] == 1)
+ {
+ component::status s = this->cpu->connect_pin (watcher_name, & this->trapstop_pin);
+ return (s == component::ok);
+ }
+ else
+ return true;
+ }
+
+
void
gdb::process_detach ()
{
*************** gdb::process_detach ()
*** 1274,1280 ****
bool ok =
this->remove_all_hw_breakpoints () &&
! this->remove_all_sw_breakpoints ();
if (!ok)
{
cerr << "sw-debug-gdb: cannot clean up breakpoints" << endl;
--- 1362,1369 ----
bool ok =
this->remove_all_hw_breakpoints () &&
! this->remove_all_sw_breakpoints () &&
! this->remove_all_hw_watchpoints ();
if (!ok)
{
cerr << "sw-debug-gdb: cannot clean up breakpoints" << endl;
Index: sid/component/gdb/gdb.h
===================================================================
RCS file: /cvs/src/src/sid/component/gdb/gdb.h,v
retrieving revision 1.10
diff -c -p -r1.10 gdb.h
*** sid/component/gdb/gdb.h 19 Aug 2005 19:43:50 -0000 1.10
--- sid/component/gdb/gdb.h 14 Nov 2005 19:56:57 -0000
*************** private:
*** 143,148 ****
--- 143,156 ----
bool remove_sw_breakpoint (host_int_8, host_int_4);
bool remove_all_sw_breakpoints ();
+ // hw watchpoint tracking
+ typedef map<string,int> hw_watchpoints_t;
+ hw_watchpoints_t hw_watchpoints; // watcher name -> insertion-count
+ bool add_hw_watchpoint (host_int_8, host_int_4);
+ bool remove_hw_watchpoint (host_int_8, host_int_4);
+ bool remove_hw_watchpoint (const string &);
+ bool remove_all_hw_watchpoints ();
+
// pending signal tracking
typedef map<int,int> pending_signal_counts_t;
pending_signal_counts_t pending_signal_counts;
Index: sid/include/sidcpuutil.h
===================================================================
RCS file: /cvs/src/src/sid/include/sidcpuutil.h,v
retrieving revision 1.32
diff -c -p -r1.32 sidcpuutil.h
*** sid/include/sidcpuutil.h 23 Aug 2005 21:09:24 -0000 1.32
--- sid/include/sidcpuutil.h 14 Nov 2005 19:56:57 -0000
*************** namespace sidutil
*** 141,147 ****
// Virtual pin interfaces between self_watcher and fixed_pin_map_component
sid::component::status pin_factory (const std::string& name)
{
! return this->triggerpoint_manager.create_virtual_pin (name);
}
void pin_junkyard (const std::string& name)
{
--- 141,187 ----
// Virtual pin interfaces between self_watcher and fixed_pin_map_component
sid::component::status pin_factory (const std::string& name)
{
! sid::component::status s = this->triggerpoint_manager.create_virtual_pin (name);
! if (s == sid::component::not_found)
! {
! // If this is not a watchpoint, then return now.
! // N.B. The name has been mapped by map_watchable_name and is of the form
! // watch:gdb%xxwatchpoint%xx<addr>%xx<len>:change
! std::vector<std::string> tokens = sidutil::tokenize (name, ":");
! if (tokens.size () != 3 || tokens[0] != "watch" || tokens[2] != "change")
! return s;
!
! // gdb%xxwatchpoint%xx<addr>%xx<len>
! if (tokens[1].size () < 3 + 3 + 10 + 3 + 1 + 3 + 1)
! return s;
!
! std::string watchable_prefix = map_watchable_name ("gdb-watchpoint-");
! unsigned watchable_prefix_length = watchable_prefix.size ();
! if (tokens[1].substr (0, watchable_prefix_length) != watchable_prefix)
! return s;
!
! tokens = sidutil::tokenize (tokens[1].substr (watchable_prefix_length), tokens[1].substr (3, 1));
! if (tokens.size () != 2)
! return s;
!
! // This is a watch point, add the necessary watchable now.
! std::pair<sid::host_int_4,sid::host_int_4> addr_and_len;
! s = sidutil::parse_attribute (tokens[0], addr_and_len.first);
! assert (s == sid::component::ok);
!
! tokens[1] = tokens[1].substr (2); // get past hex digits xx
! s = sidutil::parse_attribute (tokens[1], addr_and_len.second);
! assert (s == sid::component::ok);
!
! add_watchable_register ("gdb-watchpoint-" + tokens[0] + "-" + tokens[1], addr_and_len, this,
! & basic_cpu::read_watchpoint_memory,
! & basic_cpu::write_watchpoint_memory);
!
! // Now try to create the virtual pin again. It should succeed.
! s = this->triggerpoint_manager.create_virtual_pin (name);
! assert (s == sid::component::ok);
! }
! return s;
}
void pin_junkyard (const std::string& name)
{
*************** namespace sidutil
*** 720,725 ****
--- 760,770 ----
template <typename BigOrLittleInt>
BigOrLittleInt write_data_memory (sid::host_int_4 pc, sid::host_int_4 address, BigOrLittleInt value);
+ virtual sid::host_int_1 read_data_memory_1 (sid::host_int_4 pc, sid::host_int_4 address) = 0;
+ virtual sid::host_int_2 read_data_memory_2 (sid::host_int_4 pc, sid::host_int_4 address) = 0;
+ virtual sid::host_int_4 read_data_memory_4 (sid::host_int_4 pc, sid::host_int_4 address) = 0;
+ virtual sid::host_int_8 read_data_memory_8 (sid::host_int_4 pc, sid::host_int_4 address) = 0;
+
virtual bool handle_insn_memory_read_error (sid::bus::status s, sid::host_int_4 & address) { return false; }
virtual bool handle_insn_memory_write_error (sid::bus::status s, sid::host_int_4 & address) { return false; }
virtual bool handle_data_memory_read_error (sid::bus::status s, sid::host_int_4 & address) { return false; }
*************** namespace sidutil
*** 730,735 ****
--- 775,826 ----
virtual void record_data_memory_read_latency (sid::bus::status s) { total_latency += s.latency; }
virtual void record_data_memory_write_latency (sid::bus::status s) { total_latency += s.latency; }
+ virtual std::string basic_cpu::read_watchpoint_memory (std::pair<sid::host_int_4,sid::host_int_4> addr_and_length)
+ {
+ // Extract the address and length from the argument.
+ sid::host_int_4 address = addr_and_length.first;
+ sid::host_int_4 length = addr_and_length.second;
+
+ // We'll need the current pc.
+ std::string pc_str = this->attribute_value ("pc");
+ sid::host_int_4 pc;
+ sid::component::status rc = sidutil::parse_attribute (pc_str, pc);
+ assert (rc == sid::component::ok);
+
+ // Just read from insn memory by default
+ switch (length)
+ {
+ case 1:
+ {
+ sid::host_int_1 r1 = read_data_memory_1 (pc, address);
+ return sidutil::make_attribute (r1);
+ }
+ case 2:
+ {
+ sid::host_int_2 r2 = read_data_memory_2 (pc, address);
+ return sidutil::make_attribute (r2);
+ }
+ case 4:
+ {
+ sid::host_int_4 r4 = read_data_memory_4 (pc, address);
+ return sidutil::make_attribute (r4);
+ }
+ case 8:
+ {
+ sid::host_int_8 r8 = read_data_memory_8 (pc, address);
+ return sidutil::make_attribute (r8);
+ }
+ }
+
+ throw cpu_memory_fault (pc, address, sid::bus::unmapped, "watchpoint read");
+ return "";
+ }
+
+ virtual sid::component::status basic_cpu::write_watchpoint_memory (std::pair<sid::host_int_4,sid::host_int_4> addr_and_length, const std::string &value)
+ {
+ return sid::component::bad_value;
+ }
+
// ------------------------------------------------------------------------
public:
*************** public:
*** 947,954 ****
throw cpu_memory_fault (pc, address, s, "data write");
}
-
-
// ------------------------------------------------------------------------
// Derived classes for memory access functions of various endianness
--- 1038,1043 ----