From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 20753 invoked by alias); 28 Aug 2008 05:40:31 -0000 Received: (qmail 19488 invoked by uid 22791); 28 Aug 2008 05:40:28 -0000 X-Spam-Check-By: sourceware.org Received: from smtp1.dnsmadeeasy.com (HELO smtp1.dnsmadeeasy.com) (205.234.170.134) by sourceware.org (qpsmtpd/0.31) with ESMTP; Thu, 28 Aug 2008 05:39:38 +0000 Received: from smtp1.dnsmadeeasy.com (localhost [127.0.0.1]) by smtp1.dnsmadeeasy.com (Postfix) with ESMTP id B80CB3208E2 for ; Thu, 28 Aug 2008 05:39:37 +0000 (UTC) X-Authenticated-Name: js.dnsmadeeasy X-Transit-System: In case of SPAM please contact abuse@dnsmadeeasy.com Received: from avtrex.com (unknown [173.8.135.205]) by smtp1.dnsmadeeasy.com (Postfix) with ESMTP for ; Thu, 28 Aug 2008 05:39:37 +0000 (UTC) Received: from silver64.hq2.avtrex.com ([192.168.7.223]) by avtrex.com with Microsoft SMTPSVC(6.0.3790.1830); Wed, 27 Aug 2008 22:39:34 -0700 Message-ID: <48B63A15.1010107@avtrex.com> Date: Thu, 28 Aug 2008 05:40:00 -0000 From: David Daney User-Agent: Thunderbird 2.0.0.16 (X11/20080723) MIME-Version: 1.0 To: gdb-patches@sourceware.org Subject: [Patch] MIPS: Preliminary linux hardware watch register support. Content-Type: multipart/mixed; boundary="------------040200090709010601030105" X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2008-08/txt/msg00650.txt.bz2 This is a multi-part message in MIME format. --------------040200090709010601030105 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-length: 1543 This is a slight modification of the patch I posted last April. It is close to the minimal amount of code needed to verify that the Linux kernel patch (still under development) is working, and will need more work before it would be considered for committing. The main change from the previous version is that mips_linux_stopped_data_address() now always returns false as we cannot accurately determine the location. Why post it again you ask? To have something to point people at. Any comments or criticisms (about the patch) are welcome. Note that I am still aware of Daniel's earlier suggestions, and hope to act on them soon. 2008-08-27 David Daney * config/mips/linux.mh (NAT_FILE): Change to nm-linux.h. * config/mips/nm-linux.h: New file. * infrun.c (handle_inferior_event): Use software single step when target requires it. * mips-linux-nat.c: Include command.h and gdbcmd.h. (maint_show_dr): New variable. (PTRACE_GET_WATCH_REGS): New macro. (PTRACE_SET_WATCH_REGS): Same. (enum pt_watch_style): Define. (struct mips32_watch_regs): Define. (struct pt_watch_regs): Define. (MAX_DEBUG_REGISTER): New macro. (watch_mirror): New variable. (mips_show_dr, mips_linux_can_use_hardware_watchpoint, mips_linux_stopped_data_address, mips_linux_stopped_by_watchpoint, mips_linux_region_ok_for_watchpoint, watchlo_val, write_watchpoint_regs, mips_linux_insert_watchpoint, mips_linux_remove_watchpoint): New functions. (_initialize_mips_linux_nat): Register show-debug-regs maintenance command. --------------040200090709010601030105 Content-Type: text/plain; name="gdb.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="gdb.patch" Content-length: 9011 diff -rcp ../gdb-clean/gdb-6.8/gdb/config/mips/linux.mh gdb/config/mips/linux.mh *** ../gdb-clean/gdb-6.8/gdb/config/mips/linux.mh 2007-10-15 12:19:17.000000000 -0700 --- gdb/config/mips/linux.mh 2008-04-15 14:25:17.000000000 -0700 *************** *** 1,5 **** # Host: Linux/MIPS ! NAT_FILE= config/nm-linux.h NATDEPFILES= inf-ptrace.o fork-child.o mips-linux-nat.o \ linux-thread-db.o proc-service.o gcore.o \ linux-nat.o linux-fork.o --- 1,5 ---- # Host: Linux/MIPS ! NAT_FILE= nm-linux.h NATDEPFILES= inf-ptrace.o fork-child.o mips-linux-nat.o \ linux-thread-db.o proc-service.o gcore.o \ linux-nat.o linux-fork.o diff -rcp ../gdb-clean/gdb-6.8/gdb/infrun.c gdb/infrun.c *** ../gdb-clean/gdb-6.8/gdb/infrun.c 2008-01-29 14:47:19.000000000 -0800 --- gdb/infrun.c 2008-04-21 11:01:07.000000000 -0700 *************** handle_inferior_event (struct execution_ *** 1830,1835 **** --- 1830,1836 ---- && (HAVE_STEPPABLE_WATCHPOINT || gdbarch_have_nonsteppable_watchpoint (current_gdbarch))) { + int step_over_watchpoint = 1; if (debug_infrun) fprintf_unfiltered (gdb_stdlog, "infrun: STOPPED_BY_WATCHPOINT\n"); *************** handle_inferior_event (struct execution_ *** 1857,1863 **** if (!HAVE_STEPPABLE_WATCHPOINT) remove_breakpoints (); registers_changed (); ! target_resume (ecs->ptid, 1, TARGET_SIGNAL_0); /* Single step */ ecs->waiton_ptid = ecs->ptid; if (HAVE_STEPPABLE_WATCHPOINT) ecs->infwait_state = infwait_step_watch_state; --- 1858,1880 ---- if (!HAVE_STEPPABLE_WATCHPOINT) remove_breakpoints (); registers_changed (); ! ! if (gdbarch_software_single_step_p (current_gdbarch)) ! { ! /* Do it the hard way, w/temp breakpoints */ ! if (gdbarch_software_single_step (current_gdbarch, get_current_frame ())) ! { ! /* ...and don't ask hardware to do it. */ ! step_over_watchpoint = 0; ! /* and do not pull these breakpoints until after a `wait' in ! `wait_for_inferior' */ ! singlestep_breakpoints_inserted_p = 1; ! singlestep_ptid = inferior_ptid; ! singlestep_pc = read_pc (); ! } ! } ! ! target_resume (ecs->ptid, step_over_watchpoint, TARGET_SIGNAL_0); /* Single step */ ecs->waiton_ptid = ecs->ptid; if (HAVE_STEPPABLE_WATCHPOINT) ecs->infwait_state = infwait_step_watch_state; diff -rcp ../gdb-clean/gdb-6.8/gdb/mips-linux-nat.c gdb/mips-linux-nat.c *** ../gdb-clean/gdb-6.8/gdb/mips-linux-nat.c 2008-01-01 14:53:12.000000000 -0800 --- gdb/mips-linux-nat.c 2008-08-27 22:23:34.000000000 -0700 *************** *** 19,24 **** --- 19,26 ---- along with this program. If not, see . */ #include "defs.h" + #include "command.h" + #include "gdbcmd.h" #include "inferior.h" #include "mips-tdep.h" #include "target.h" *************** *** 44,49 **** --- 46,54 ---- we'll clear this and use PTRACE_PEEKUSER instead. */ static int have_ptrace_regsets = 1; + /* Whether or not to print the mirrored debug registers. */ + static int maint_show_dr; + /* Saved function pointers to fetch and store a single register using PTRACE_PEEKUSER and PTRACE_POKEUSER. */ *************** mips_linux_read_description (struct targ *** 355,366 **** return tdesc_mips64_linux; } void _initialize_mips_linux_nat (void); void _initialize_mips_linux_nat (void) { ! struct target_ops *t = linux_trad_target (mips_linux_register_u_offset); super_fetch_registers = t->to_fetch_registers; super_store_registers = t->to_store_registers; --- 360,584 ---- return tdesc_mips64_linux; } + #ifndef PTRACE_GET_WATCH_REGS + # define PTRACE_GET_WATCH_REGS 0xd0 + #endif + + #ifndef PTRACE_SET_WATCH_REGS + # define PTRACE_SET_WATCH_REGS 0xd1 + #endif + + enum pt_watch_style { + pt_watch_style_mips32, + pt_watch_style_mips64 + }; + struct mips32_watch_regs { + unsigned long watchlo[8]; + unsigned int watchhi[8]; + unsigned int num_valid; + unsigned int reg_mask; + unsigned int irw_mask; + }; + + struct pt_watch_regs { + enum pt_watch_style style; + union { + struct mips32_watch_regs mips32; + }; + }; + + /* A value of zero in a register indicates that it is available. */ + #define MAX_DEBUG_REGISTER 4 + static struct pt_watch_regs watch_mirror; + + static void + mips_show_dr (const char *func, CORE_ADDR addr, + int len, enum target_hw_bp_type type) + { + int i; + + puts_unfiltered (func); + if (addr || len) + printf_unfiltered (" (addr=%lx, len=%d, type=%s)", + /* This code is for mips, so casting CORE_ADDR + to unsigned long should be okay. */ + (unsigned long)addr, len, + type == hw_write ? "data-write" + : (type == hw_read ? "data-read" + : (type == hw_access ? "data-read/write" + : (type == hw_execute ? "instruction-execute" + /* FIXME: if/when I/O read/write + watchpoints are supported, add them + here. */ + : "??unknown??")))); + puts_unfiltered (":\n"); + + for (i = 0; i < MAX_DEBUG_REGISTER; i++) + { + printf_unfiltered ("\ + \tDR%d: lo=0x%s, hi=0x%s\n", + i, paddr(watch_mirror.mips32.watchlo[i]), + paddr(watch_mirror.mips32.watchhi[i])); + } + } + + int mips_linux_can_use_hardware_watchpoint (int type, int cnt, + int ot) + { + switch (type) + { + case bp_hardware_watchpoint: + case bp_read_watchpoint: + case bp_access_watchpoint: + return 1; + default: + return 0; + } + } + + int mips_linux_stopped_by_watchpoint (void) + { + int i; + struct pt_watch_regs watch_readback; + int tid = ptid_get_lwp (inferior_ptid); + + if (ptrace (PTRACE_GET_WATCH_REGS, tid, &watch_readback) == -1) + { + perror_with_name (_("Couldn't read debug register")); + return 0; + } + for (i = 0; + i < MAX_DEBUG_REGISTER && i < watch_readback.mips32.num_valid; i++) + { + if (watch_readback.mips32.watchhi[i] & 3) + { + return 1; + } + } + return 0; + } + int mips_linux_stopped_data_address (CORE_ADDR *paddr) + { + /* On mips we don't know the low order 3 bits of the data address, + so we must return false. */ + return 0; + } + + int mips_linux_region_ok_for_watchpoint (CORE_ADDR addr, int len) + { + return 1; + } + + static unsigned long watchlo_val(CORE_ADDR addr, int type) + { + unsigned long irw; + + switch (type) + { + case hw_write: + irw = 1; + break; + case hw_read: + irw = 2; + break; + case hw_access: + irw = 3; + break; + default: + return 0; + } + + return irw | (addr & ~7UL); + } + + static int write_watchpoint_regs(void) + { + struct lwp_info *lp; + ptid_t ptid; + int tid; + + ALL_LWPS (lp, ptid) + { + tid = ptid_get_lwp (ptid); + if (ptrace (PTRACE_SET_WATCH_REGS, tid, &watch_mirror) == -1) + { + perror_with_name (_("Couldn't write debug register")); + return -1; + } + } + return 0; + } + + + int mips_linux_insert_watchpoint (CORE_ADDR addr, int len, int type) + { + int i; + int retval = -1; + unsigned long lo_val; + struct lwp_info *lp; + ptid_t ptid; + + lo_val = watchlo_val(addr, type); + + for (i = 0; i < MAX_DEBUG_REGISTER; i++) + { + if (watch_mirror.mips32.watchlo[i] == 0) + { + watch_mirror.mips32.watchlo[i] = lo_val; + watch_mirror.mips32.watchhi[i] = 0; + retval = write_watchpoint_regs(); + break; + } + } + + if (maint_show_dr) + mips_show_dr ("insert_watchpoint", addr, len, type); + + return retval; + } + + int mips_linux_remove_watchpoint (CORE_ADDR addr, int len, int type) + { + int i; + int retval = -1; + unsigned long lo_val; + + lo_val = watchlo_val(addr, type); + + for (i = 0; i < MAX_DEBUG_REGISTER; i++) + { + if (watch_mirror.mips32.watchlo[i] == lo_val) + { + watch_mirror.mips32.watchlo[i] = 0; + retval = write_watchpoint_regs(); + } + } + + if (maint_show_dr) + mips_show_dr ("remove_watchpoint", addr, len, type); + + return retval; + } + + void _initialize_mips_linux_nat (void); void _initialize_mips_linux_nat (void) { ! struct target_ops *t; ! ! deprecated_add_set_cmd ("show-debug-regs", class_maintenance, ! var_boolean, (char *) &maint_show_dr, _("\ ! Set whether to show variables that mirror the mips debug registers.\n\ ! Use \"on\" to enable, \"off\" to disable.\n\ ! If enabled, the debug registers values are shown when GDB inserts\n\ ! or removes a hardware breakpoint or watchpoint, and when the inferior\n\ ! triggers a breakpoint or watchpoint."), ! &maintenancelist); ! ! ! t = linux_trad_target (mips_linux_register_u_offset); super_fetch_registers = t->to_fetch_registers; super_store_registers = t->to_store_registers; --------------040200090709010601030105--