Re: [RFA]: Watchpoints per thread patch

Jeff Johnston
2004-11-04T18:25:44Z

[-- Attachment #1: Type: text/plain, Size: 6455 bytes --]

Eli Zaretskii wrote:
>>Date: Wed, 27 Oct 2004 18:36:14 -0400
>>From: Jeff Johnston <jjohnstn@redhat.com>
>>Cc: Andrew Cagney <cagney@gnu.org>, gdb-patches@sources.redhat.com
>>
>>The attached patch is the rework of my original attempt. It no longer uses
>>configuration or magic defines. Per Mark's suggestion, it uses an observer to
>>handle inserting watchpoints on a new thread and only the low-level code knows
>>about inserting/removing watchpoints on all threads.
>>
>>Ok to commit?
>
>
> A few comments:
>
>
>>+/* External function to insert all existing watchpoints on a newly
>>+ attached thread. IWPFN is a callback function to perform
>>+ the target insert watchpoint. This function is used to support
>>+ platforms whereby a watchpoint must be inserted/removed on each
>>+ individual thread (e.g. ia64-linux and s390-linux). For
>>+ ia64 and s390 linux, this function is called via a new thread
>>+ observer. */
>
>
> In this comment, the word "whereby" should be replaced by "where", I
> think.
>

Ok, done.

>
>>--- target.h 8 Oct 2004 20:29:55 -0000 1.65
>>+++ target.h 27 Oct 2004 21:43:51 -0000
>>@@ -178,6 +178,15 @@ extern char *target_signal_to_name (enum
>> /* Given a name (SIGHUP, etc.), return its signal. */
>> enum target_signal target_signal_from_name (char *);
>> \f
>>+
>>+/* Watchpoint specification. */
>>+struct target_watchpoint
>>+ {
>>+ CORE_ADDR addr;
>>+ int len;
>>+ int type;
>>+ };
>>+
>
>
> Why do we put on target.h, which is a general header, a definition of
> a struct used only on certain platforms?
>

I have moved this to linux-nat.h and renamed it struct linux_watchpoint.

>
>>--- doc/observer.texi 1 Sep 2004 17:59:37 -0000 1.8
>>+++ doc/observer.texi 27 Oct 2004 21:43:51 -0000
>>@@ -95,3 +95,7 @@ inferior, and before any information on
>> The specified shared library has been discovered to be unloaded.
>> @end deftypefun
>>
>>+@deftypefun void new_thread (ptid_t @var{ptid})
>>+A new thread has been attached to.
>
>
> "A new thread has been attached" to what? The description of the
> observer should at least reference the argument @var{ptid}.
>

I have changed the definition to be more meaningful now that it is being used
generically by all.

On Thu, Oct 28, 2004 at 03:47:22PM -0400, Jeff Johnston wrote:

>> Daniel Jacobowitz wrote:
>
>>> >On Wed, Oct 27, 2004 at 07:17:13PM -0400, Jeff Johnston wrote:
>>> >
>>
>>>> >>Were you thinking of add_thread()? If so, we would have to move the
>>>> >>calls to add_thread so they never occur before an attach because the
>>>> >>low-level observers will need the thread already attached.
>>
>>> >
>>> >
>>> >Oh, that's a good point. Do you think that's a reasonable change to
>>> >make?
>>> >
>
>>

Daniel, I have moved the observer notification to add_thread as suggested. To
do this, I had to move a few things in attach_thread. As well, I had to add
another part of my full patch in because the ptid that add_thread knows about is
in the wrong format (pid, 0, tid). The low-level insert watchpoint code needs
the lwp so I have added in my target_get_lwp change. I realize you have plans
to change how the ptid is kept, but until that is fleshed out, this change is
required. I used a call-back to allow breakpoint.c which knows how to handle
watchpoints to communicate with the low-level linux code that knows how to
insert/remove a watchpoint.

I have tested on ia64 and s390 linux. The ia64 requires another patch to make
it pass the watchthreads.exp test. S390 linux can't pass that test without
additional hardware support, however, I will note that s390 is properly
recognizing hardware watchpoints on threads which is a major step forward.

Ok to commit?

2004-11-04 Jeff Johnston <jjohnstn@redhat.com>

* breakpoint.c (insert_watchpoints_for_new_thread): New function.
(print_it_typical): Do not issue an error for bp_thread_event
if a subsequent event is on the chain.
* breakpoint.h (insert_watchpoints_for_new_thread): New prototype.
* ia64-linux-nat.c (ia64_linux_insert_one_watchpoint): New function.
(ia64_linux_insert_watchpoint_callback): Ditto.
(ia64_linux_insert_watchpoint): Change to iterate through lwps
and insert the specified watchpoint per thread.
(ia64_stopped_data_address): Call target_get_lwp to ensure
the ptid has its lwp field filled in.
(ia64_linux_remove_one_watchpoint): New function.
(ia64_linux_remove_watchpoint_callback): Ditto.
(ia64_linux_remove_watchpoint): Change to iterate through lwps and
remove the specified watchpoint for each thread.
(ia64_linux_new_thread): New thread observer.
(_initialize_ia64_linux_nat): New function. Initialize
new thread observer.
* linux-nat.h (struct linux_watchpoint): New structure.
* thread-db.c (attach_thread): Add the thread after attaching
to it. Before allocating the thread private info area, check
to see if it has already been allocated.
(thread_db_get_info): Allocate a thread private info area if
one doesn't already exist.
(init_thread_db_ops): Point to_get_lwp to lwp_from_thread function.
* thread.c (add_thread): Notify any observers of a new thread event.
* s390-nat.c (s390_tid): New function.
(s390_inferior_tid): Change to call s390_tid.
(s390_remove_one_watchpoint): New function.
(s390_remove_watchpoint_callback): Ditto.
(s390_remove_watchpoint): Change to iterate through lwps and
remove the specified watchpoint for each thread.
(s390_insert_one_watchpoint): New function.
(s390_insert_watchpoint_callback): Ditto.
(s390_insert_watchpoint): Change to iterate through lwps and
insert the specified watchpoint on each thread.
(s390_new_thread): New thread observer.
(_initialize_s390_nat): New function. Initialize
new thread observer.
* target.c (return_ptid): New static function.
(update_current_target): Add support for new to_get_lwp.
(init_dummy_target): Ditto.
* target.h (struct target_ops): Add to_get_lwp.
(target_get_lwp): New macro.

doc/ChangeLog:

2004-11-04 Jeff Johnston <jjohnstn@redhat.com>

* observer.texi (new_thread): New observer.

[-- Attachment #2: newwatchthreads3.patch --]
[-- Type: text/plain, Size: 20750 bytes --]

Index: breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.184
diff -u -p -r1.184 breakpoint.c
--- breakpoint.c 29 Oct 2004 20:23:04 -0000 1.184
+++ breakpoint.c 4 Nov 2004 18:19:11 -0000
@@ -748,6 +748,91 @@ insert_catchpoint (struct ui_out *uo, vo
return 0;
}

+/* External function to insert all existing watchpoints on a newly
+ attached thread. IWPFN is a callback function to perform
+ the target insert watchpoint. This function is used to support
+ platforms where a watchpoint must be inserted/removed on each
+ individual thread (e.g. ia64-linux and s390-linux). For
+ ia64 and s390 linux, this function is called via a new thread
+ observer. */
+int
+insert_watchpoints_for_new_thread (ptid_t new_thread,
+ insert_watchpoint_ftype *iwpfn)
+{
+ struct bp_location *b;
+ int val = 0;
+ int return_val = 0;
+ struct ui_file *tmp_error_stream = mem_fileopen ();
+ make_cleanup_ui_file_delete (tmp_error_stream);
+
+ /* Explicitly mark the warning -- this will only be printed if
+ there was an error. */
+ fprintf_unfiltered (tmp_error_stream, "Warning:\n");
+
+ ALL_BP_LOCATIONS (b)
+ {
+ /* Skip disabled breakpoints. */
+ if (!breakpoint_enabled (b->owner))
+ continue;
+
+ /* For every active watchpoint, we need to insert the watchpoint on
+ the new thread. */
+ if ((b->loc_type == bp_loc_hardware_watchpoint
+ || b->owner->type == bp_watchpoint))
+ {
+ struct value *v = b->owner->val_chain;
+
+ /* Look at each value on the value chain. */
+ for (; v; v = v->next)
+ {
+ /* If it's a memory location, and GDB actually needed
+ its contents to evaluate the expression, then we
+ must watch it. */
+ if (VALUE_LVAL (v) == lval_memory
+ && ! VALUE_LAZY (v))
+ {
+ struct type *vtype = check_typedef (VALUE_TYPE (v));
+
+ /* We only watch structs and arrays if user asked
+ for it explicitly, never if they just happen to
+ appear in the middle of some value chain. */
+ if (v == b->owner->val_chain
+ || (TYPE_CODE (vtype) != TYPE_CODE_STRUCT
+ && TYPE_CODE (vtype) != TYPE_CODE_ARRAY))
+ {
+ CORE_ADDR addr;
+ int len, type;
+
+ addr = VALUE_ADDRESS (v) + VALUE_OFFSET (v);
+ len = TYPE_LENGTH (VALUE_TYPE (v));
+ type = hw_write;
+ if (b->owner->type == bp_read_watchpoint)
+ type = hw_read;
+ else if (b->owner->type == bp_access_watchpoint)
+ type = hw_access;
+ val = (*iwpfn) (new_thread, addr, len, type);
+ }
+ }
+ }
+ }
+
+ if (val)
+ return_val = val;
+ }
+
+ /* Failure to insert a watchpoint on any memory value in the
+ value chain brings us here. */
+ if (return_val)
+ {
+ fprintf_unfiltered (tmp_error_stream,
+ "%s\n",
+ "Could not insert hardware watchpoints on new thread.");
+ target_terminal_ours_for_output ();
+ error_stream (tmp_error_stream);
+ }
+ return return_val;
+}
+
/* Helper routine: free the value chain for a breakpoint (watchpoint). */

static void free_valchain (struct bp_location *b)
@@ -2122,8 +2207,13 @@ print_it_typical (bpstat bs)
break;

case bp_thread_event:
- /* Not sure how we will get here.
- GDB should not stop for these breakpoints. */
+ /* We can only get here legitimately if something further on the bs
+ list has caused the stop status to be noisy. A valid example
+ of this is a new thread event and a software watchpoint have
+ both occurred. */
+ if (bs->next)
+ return PRINT_UNKNOWN;
+
printf_filtered ("Thread Event Breakpoint: gdb should not stop!\n");
return PRINT_NOTHING;
break;
Index: breakpoint.h
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.h,v
retrieving revision 1.34
diff -u -p -r1.34 breakpoint.h
--- breakpoint.h 13 May 2004 16:39:11 -0000 1.34
+++ breakpoint.h 4 Nov 2004 18:19:11 -0000
@@ -663,6 +663,12 @@ extern void tbreak_command (char *, int)

extern int insert_breakpoints (void);

+/* The following provides a callback mechanism to insert watchpoints
+ for a new thread. This is needed, for example, on ia64 linux. */
+typedef int (insert_watchpoint_ftype) (ptid_t, CORE_ADDR, int, int);
+extern int insert_watchpoints_for_new_thread (ptid_t ptid,
+ insert_watchpoint_ftype *fn);
+
extern int remove_breakpoints (void);

/* This function can be used to physically insert eventpoints from the
Index: ia64-linux-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/ia64-linux-nat.c,v
retrieving revision 1.26
diff -u -p -r1.26 ia64-linux-nat.c
--- ia64-linux-nat.c 13 Oct 2004 21:40:41 -0000 1.26
+++ ia64-linux-nat.c 4 Nov 2004 18:19:11 -0000
@@ -39,6 +39,8 @@

#include <asm/ptrace_offsets.h>
#include <sys/procfs.h>
+#include "observer.h"
+#include "linux-nat.h"

/* Prototypes for supply_gregset etc. */
#include "gregset.h"
@@ -559,8 +561,9 @@ is_power_of_2 (int val)
return onecount <= 1;
}

-int
-ia64_linux_insert_watchpoint (ptid_t ptid, CORE_ADDR addr, int len, int rw)
+/* Internal routine to insert one watchpoint for a specified thread. */
+static int
+ia64_linux_insert_one_watchpoint (ptid_t ptid, CORE_ADDR addr, int len, int rw)
{
int idx;
long dbr_addr, dbr_mask;
@@ -606,8 +609,38 @@ ia64_linux_insert_watchpoint (ptid_t pti
return 0;
}

+/* Internal callback routine which can be used via iterate_over_lwps
+ to insert a specific watchpoint from all active threads. */
+static int
+ia64_linux_insert_watchpoint_callback (struct lwp_info *lwp, void *data)
+{
+ struct linux_watchpoint *args = (struct linux_watchpoint *)data;
+
+ return ia64_linux_insert_one_watchpoint (lwp->ptid, args->addr,
+ args->len, args->type);
+}
+
+/* Insert a watchpoint for all threads. */
int
-ia64_linux_remove_watchpoint (ptid_t ptid, CORE_ADDR addr, int len)
+ia64_linux_insert_watchpoint (ptid_t ptid, CORE_ADDR addr, int len, int rw)
+{
+ struct linux_watchpoint args;
+
+ args.addr = addr;
+ args.len = len;
+ args.type = rw;
+
+ /* For ia64, watchpoints must be inserted/removed on each thread so
+ we iterate over the lwp list. */
+ if (iterate_over_lwps (&ia64_linux_insert_watchpoint_callback, &args))
+ return -1;
+
+ return 0;
+}
+
+/* Internal routine to remove one watchpoint for a specified thread. */
+static int
+ia64_linux_remove_one_watchpoint (ptid_t ptid, CORE_ADDR addr, int len)
{
int idx;
long dbr_addr, dbr_mask;
@@ -630,13 +663,41 @@ ia64_linux_remove_watchpoint (ptid_t pti
return -1;
}

+/* Internal callback routine which can be used via iterate_over_lwps
+ to remove a specific watchpoint from all active threads. */
+static int
+ia64_linux_remove_watchpoint_callback (struct lwp_info *lwp, void *data)
+{
+ struct linux_watchpoint *args = (struct linux_watchpoint *)data;
+
+ return ia64_linux_remove_one_watchpoint (lwp->ptid, args->addr,
+ args->len);
+}
+
+/* Remove a watchpoint for all threads. */
+int
+ia64_linux_remove_watchpoint (ptid_t ptid, CORE_ADDR addr, int len)
+{
+ struct linux_watchpoint args;
+
+ args.addr = addr;
+ args.len = len;
+
+ /* For ia64, watchpoints must be inserted/removed on each thread so
+ we iterate over the lwp list. */
+ if (iterate_over_lwps (&ia64_linux_remove_watchpoint_callback, &args))
+ return -1;
+
+ return 0;
+}
+
int
ia64_linux_stopped_data_address (CORE_ADDR *addr_p)
{
CORE_ADDR psr;
int tid;
struct siginfo siginfo;
- ptid_t ptid = inferior_ptid;
+ ptid_t ptid = target_get_lwp (inferior_ptid);

tid = TIDGET(ptid);
if (tid == 0)
@@ -674,3 +735,31 @@ ia64_linux_xfer_unwind_table (struct tar
{
return syscall (__NR_getunwind, readbuf, len);
}
+
+/* Internal callback for insert_watchpoints_for_new_thread to call.
+ The PTID will be in the thread-level format and must be
+ translated to the lwp-level format before calling
+ ia64_linux_insert_one_watchpoint to insert any watchpoint. */
+static int
+ia64_linux_insert_watchpoint_for_thread (ptid_t ptid, CORE_ADDR addr,
+ int len, int rw)
+{
+ ptid_t lwp_ptid = target_get_lwp (ptid);
+ return ia64_linux_insert_one_watchpoint (lwp_ptid, addr, len, rw);
+}
+
+/* Observer function for a new thread attach. We need to insert
+ existing watchpoints on the new thread. */
+static void
+ia64_linux_new_thread (ptid_t ptid)
+{
+ insert_watchpoints_for_new_thread (ptid,
+ &ia64_linux_insert_watchpoint_for_thread);
+}
+
+void
+_initialize_ia64_linux_nat (void)
+{
+ observer_attach_new_thread (ia64_linux_new_thread);
+}
+
Index: linux-nat.h
===================================================================
RCS file: /cvs/src/src/gdb/linux-nat.h,v
retrieving revision 1.6
diff -u -p -r1.6 linux-nat.h
--- linux-nat.h 29 Mar 2004 18:07:14 -0000 1.6
+++ linux-nat.h 4 Nov 2004 18:19:11 -0000
@@ -63,6 +63,14 @@ struct lwp_info
struct lwp_info *next;
};

+/* Watchpoint description. */
+struct linux_watchpoint
+{
+ CORE_ADDR addr;
+ int len;
+ int type;
+};
+
/* Read/write to target memory via the Linux kernel's "proc file
system". */
struct mem_attrib;
Index: s390-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/s390-nat.c,v
retrieving revision 1.12
diff -u -p -r1.12 s390-nat.c
--- s390-nat.c 18 Feb 2004 04:17:35 -0000 1.12
+++ s390-nat.c 4 Nov 2004 18:19:12 -0000
@@ -27,6 +27,8 @@
#include "inferior.h"

#include "s390-tdep.h"
+#include "linux-nat.h"
+#include "observer.h"

#include <asm/ptrace.h>
#include <sys/ptrace.h>
@@ -112,18 +114,32 @@ fill_fpregset (fpregset_t *regp, int reg
((char *)regp) + regmap_fpregset[i]);
}

-/* Find the TID for the current inferior thread to use with ptrace. */
+/* Find the TID for use with ptrace. */
static int
-s390_inferior_tid (void)
+s390_tid (ptid_t ptid)
{
/* GNU/Linux LWP ID's are process ID's. */
- int tid = TIDGET (inferior_ptid);
+ int tid = TIDGET (ptid);
if (tid == 0)
- tid = PIDGET (inferior_ptid); /* Not a threaded program. */
+ {
+ /* In some cases, we have a thread-level format ptid which
+ must be converted to an lwp-level format ptid. */
+ ptid_t lwp_ptid = target_get_lwp (ptid);
+ tid = TIDGET (lwp_ptid);
+ if (tid == 0)
+ tid = PIDGET (lwp_ptid); /* Not a threaded program. */
+ }

return tid;
}

+/* Find the TID for the current inferior thread to use with ptrace. */
+static int
+s390_inferior_tid (void)
+{
+ return s390_tid (inferior_ptid);
+}
+
/* Fetch all general-purpose registers from process/thread TID and
store their values in GDB's register cache. */
static void
@@ -269,9 +285,9 @@ s390_stopped_by_watchpoint (void)
}

static void
-s390_fix_watch_points (void)
+s390_fix_watch_points (ptid_t ptid)
{
- int tid = s390_inferior_tid ();
+ int tid = s390_tid (ptid);

per_struct per_info;
ptrace_area parea;
@@ -308,8 +324,9 @@ s390_fix_watch_points (void)
perror_with_name ("Couldn't modify watchpoint status");
}

-int
-s390_insert_watchpoint (CORE_ADDR addr, int len)
+/* Insert a specified watchpoint on a specified thread. */
+static int
+s390_insert_one_watchpoint (ptid_t ptid, CORE_ADDR addr, int len, int type)
{
struct watch_area *area = xmalloc (sizeof (struct watch_area));
if (!area)
@@ -321,12 +338,36 @@ s390_insert_watchpoint (CORE_ADDR addr,
area->next = watch_base;
watch_base = area;

- s390_fix_watch_points ();
+ s390_fix_watch_points (ptid);
return 0;
}

+/* Callback routine to use with iterate_over_lwps to insert a specified
+ watchpoint on all threads. */
+static int
+s390_insert_watchpoint_callback (struct lwp_info *lwp, void *data)
+{
+ struct linux_watchpoint *args = (struct linux_watchpoint *)data;
+
+ return s390_insert_one_watchpoint (lwp->ptid, args->addr, args->len, 0);
+}
+
+/* Insert a specified watchpoint on all threads. */
int
-s390_remove_watchpoint (CORE_ADDR addr, int len)
+s390_insert_watchpoint (CORE_ADDR addr, int len)
+{
+ struct linux_watchpoint args;
+
+ args.addr = addr;
+ args.len = len;
+ /* For the S390, a watchpoint must be inserted/removed for each
+ thread so we iterate over the list of existing lwps. */
+ return iterate_over_lwps (&s390_insert_watchpoint_callback, &args);
+}
+
+/* Remove a specified watchpoint from a specified thread. */
+static int
+s390_remove_one_watchpoint (ptid_t ptid, CORE_ADDR addr, int len)
{
struct watch_area *area, **parea;

@@ -346,10 +387,32 @@ s390_remove_watchpoint (CORE_ADDR addr,
*parea = area->next;
xfree (area);

- s390_fix_watch_points ();
+ s390_fix_watch_points (ptid);
return 0;
}

+/* Callback routine to use with iterate_over_lwps to remove a specified
+ watchpoint from all threads. */
+static int
+s390_remove_watchpoint_callback (struct lwp_info *lwp, void *data)
+{
+ struct linux_watchpoint *args = (struct linux_watchpoint *)data;
+
+ return s390_remove_one_watchpoint (lwp->ptid, args->addr, args->len);
+}
+
+/* Remove a specified watchpoint from all threads. */
+int
+s390_remove_watchpoint (CORE_ADDR addr, int len)
+{
+ struct linux_watchpoint args;
+
+ args.addr = addr;
+ args.len = len;
+ /* For the S390, a watchpoint must be inserted/removed for each
+ thread so we iterate over the list of existing lwps. */
+ return iterate_over_lwps (&s390_remove_watchpoint_callback, &args);
+}

int
kernel_u_size (void)
@@ -357,3 +420,18 @@ kernel_u_size (void)
return sizeof (struct user);
}

+/* New thread observer that inserts all existing watchpoints on the
+ new thread. */
+static void
+s390_new_thread (ptid_t ptid)
+{
+ insert_watchpoints_for_new_thread (ptid, &s390_insert_one_watchpoint);
+}
+
+void
+_initialize_s390_nat (void)
+{
+ observer_attach_new_thread (s390_new_thread);
+}
+
+
Index: target.c
===================================================================
RCS file: /cvs/src/src/gdb/target.c,v
retrieving revision 1.90
diff -u -p -r1.90 target.c
--- target.c 8 Oct 2004 20:29:55 -0000 1.90
+++ target.c 4 Nov 2004 18:19:12 -0000
@@ -61,6 +61,8 @@ static int return_one (void);

static int return_minus_one (void);

+static ptid_t return_ptid (ptid_t ptid);
+
void target_ignore (void);

static void target_command (char *, int);
@@ -430,6 +432,7 @@ update_current_target (void)
INHERIT (to_notice_signals, t);
INHERIT (to_thread_alive, t);
INHERIT (to_find_new_threads, t);
+ INHERIT (to_get_lwp, t);
INHERIT (to_pid_to_str, t);
INHERIT (to_extra_thread_info, t);
INHERIT (to_stop, t);
@@ -606,6 +609,9 @@ update_current_target (void)
de_fault (to_find_new_threads,
(void (*) (void))
target_ignore);
+ de_fault (to_get_lwp,
+ (ptid_t (*) (ptid_t))
+ return_ptid);
de_fault (to_extra_thread_info,
(char *(*) (struct thread_info *))
return_zero);
@@ -1541,6 +1547,12 @@ return_minus_one (void)
return -1;
}

+static ptid_t
+return_ptid (ptid_t ptid)
+{
+ return ptid;
+}
+
/*
* Resize the to_sections pointer. Also make sure that anyone that
* was holding on to an old value of it gets updated.
@@ -1793,6 +1805,7 @@ init_dummy_target (void)
dummy_target.to_find_memory_regions = dummy_find_memory_regions;
dummy_target.to_make_corefile_notes = dummy_make_corefile_notes;
dummy_target.to_xfer_partial = default_xfer_partial;
+ dummy_target.to_get_lwp = return_ptid;
dummy_target.to_magic = OPS_MAGIC;
}
\f
Index: target.h
===================================================================
RCS file: /cvs/src/src/gdb/target.h,v
retrieving revision 1.65
diff -u -p -r1.65 target.h
--- target.h 8 Oct 2004 20:29:55 -0000 1.65
+++ target.h 4 Nov 2004 18:19:12 -0000
@@ -372,6 +372,7 @@ struct target_ops
void (*to_notice_signals) (ptid_t ptid);
int (*to_thread_alive) (ptid_t ptid);
void (*to_find_new_threads) (void);
+ ptid_t (*to_get_lwp) (ptid_t ptid);
char *(*to_pid_to_str) (ptid_t);
char *(*to_extra_thread_info) (struct thread_info *);
void (*to_stop) (void);
@@ -802,6 +803,10 @@ extern void target_load (char *arg, int
#define target_find_new_threads() \
(*current_target.to_find_new_threads) (); \

+/* Get the lwp for a thread. */
+#define target_get_lwp(ptid) \
+ (*current_target.to_get_lwp) (ptid); \
+
/* Make target stop in a continuable fashion. (For instance, under
Unix, this should act like SIGSTOP). This function is normally
used by GUIs to implement a stop button. */
Index: thread-db.c
===================================================================
RCS file: /cvs/src/src/gdb/thread-db.c,v
retrieving revision 1.46
diff -u -p -r1.46 thread-db.c
--- thread-db.c 8 Oct 2004 20:29:56 -0000 1.46
+++ thread-db.c 4 Nov 2004 18:19:12 -0000
@@ -34,6 +34,7 @@
#include "target.h"
#include "regcache.h"
#include "solib-svr4.h"
+#include "observer.h"

#ifdef HAVE_GNU_LIBC_VERSION_H
#include <gnu/libc-version.h>
@@ -333,6 +334,17 @@ thread_db_get_info (struct thread_info *
{
td_err_e err;

+ /* Allocate a private area if needed. This can occur for ia64 or
+ s390 linux which must insert breakpoints on newly found threads.
+ The observer for a new thread event will be called on an add_thread
+ call before the private area has been set up and will need to
+ get the lwp for the new ptid using this function. */
+ if (!thread_info->private)
+ {
+ thread_info->private = xmalloc (sizeof (struct private_thread_info));
+ memset (thread_info->private, 0, sizeof (struct private_thread_info));
+ }
+
if (thread_info->private->ti_valid)
return &thread_info->private->ti;

@@ -746,14 +758,6 @@ attach_thread (ptid_t ptid, const td_thr

check_thread_signals ();

- /* Add the thread to GDB's thread list. */
- tp = add_thread (ptid);
- tp->private = xmalloc (sizeof (struct private_thread_info));
- memset (tp->private, 0, sizeof (struct private_thread_info));
-
- if (verbose)
- printf_unfiltered ("[New %s]\n", target_pid_to_str (ptid));
-
if (ti_p->ti_state == TD_THR_UNKNOWN || ti_p->ti_state == TD_THR_ZOMBIE)
return; /* A zombie thread -- do not attach. */

@@ -762,6 +766,21 @@ attach_thread (ptid_t ptid, const td_thr
ATTACH_LWP (BUILD_LWP (ti_p->ti_lid, GET_PID (ptid)), 0);
#endif

+ /* Add the thread to GDB's thread list.
+ We do this after attaching so any observers of a new
+ thread event can perform PTRACE operations on the thread
+ if needed. An observer may end up allocating the
+ private info area, so check first. */
+ tp = add_thread (ptid);
+ if (!tp->private)
+ {
+ tp->private = xmalloc (sizeof (struct private_thread_info));
+ memset (tp->private, 0, sizeof (struct private_thread_info));
+ }
+
+ if (verbose)
+ printf_unfiltered ("[New %s]\n", target_pid_to_str (ptid));
+
/* Enable thread event reporting for this thread. */
err = td_thr_event_enable_p (th_p, 1);
if (err != TD_OK)
@@ -1346,6 +1365,7 @@ init_thread_db_ops (void)
thread_db_ops.to_mourn_inferior = thread_db_mourn_inferior;
thread_db_ops.to_thread_alive = thread_db_thread_alive;
thread_db_ops.to_find_new_threads = thread_db_find_new_threads;
+ thread_db_ops.to_get_lwp = lwp_from_thread;
thread_db_ops.to_pid_to_str = thread_db_pid_to_str;
thread_db_ops.to_stratum = thread_stratum;
thread_db_ops.to_has_thread_control = tc_schedlock;
Index: thread.c
===================================================================
RCS file: /cvs/src/src/gdb/thread.c,v
retrieving revision 1.39
diff -u -p -r1.39 thread.c
--- thread.c 29 Oct 2004 20:23:13 -0000 1.39
+++ thread.c 4 Nov 2004 18:19:12 -0000
@@ -130,6 +130,10 @@ add_thread (ptid_t ptid)
tp->num = ++highest_thread_num;
tp->next = thread_list;
thread_list = tp;
+
+ /* Inform any observers of the new thread. */
+ observer_notify_new_thread (ptid);
+
return tp;
}

Index: doc/observer.texi
===================================================================
RCS file: /cvs/src/src/gdb/doc/observer.texi,v
retrieving revision 1.8
diff -u -p -r1.8 observer.texi
--- doc/observer.texi 1 Sep 2004 17:59:37 -0000 1.8
+++ doc/observer.texi 4 Nov 2004 18:19:12 -0000
@@ -95,3 +95,7 @@ inferior, and before any information on
The specified shared library has been discovered to be unloaded.
@end deftypefun

+@deftypefun void new_thread (ptid_t @var{ptid})
+A new thread described by @var{ptid} has been detected by gdb.
+@end deftypefun
+