This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: performance of multithreading gets gradually worse under gdb
- From: Tom Tromey <tromey at redhat dot com>
- To: "Ulrich Weigand" <uweigand at de dot ibm dot com>
- Cc: markus at hyperion-imrt dot org (Markus Alber), msnyder at vmware dot com (Michael Snyder), gdb-patches at sourceware dot org, pedro at codesourcery dot com
- Date: Fri, 04 Feb 2011 07:49:49 -0700
- Subject: Re: performance of multithreading gets gradually worse under gdb
- References: <201102041349.p14DnCeY025641@d06av02.portsmouth.uk.ibm.com>
Moving to gdb-patches.
Ulrich> Yes, I think so. Note that we still need to (potentially) keep
Ulrich> more than one regcache per thread for multi-arch support. We
Ulrich> may also need to reset the regcache's address space before
Ulrich> reusing it ...
In that case it seemed simpler to free the caches.
Here is what I am testing. Let me know what you think.
Tom
2011-02-04 Tom Tromey <tromey@redhat.com>
* thread.c (free_thread): Call free_thread_regcache.
* regcache.h (free_thread_regcache): Declare.
* regcache.c (current_regcache): Remove.
(get_thread_arch_regcache): Use thread's regcache.
(free_thread_regcache): New function.
(regcache_thread_ptid_changed): Use thread's regcache.
(invalidate_registers_maybe): New function.
(registers_changed_ptid): Use iterate_over_threads and
invalidate_registers_maybe.
* gdbthread.h (struct thread_info) <regcache>: New field.
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index ddb7b0f..cfe1bea 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -23,6 +23,7 @@
#define GDBTHREAD_H
struct symtab;
+struct regcache_list;
#include "breakpoint.h"
#include "frame.h"
@@ -224,6 +225,10 @@ struct thread_info
/* Function that is called to free PRIVATE. If this is NULL, then
xfree will be called on PRIVATE. */
void (*private_dtor) (struct private_thread_info *);
+
+ /* The register caches associated with this thread. Note that this
+ type is opaque; it is entirely managed by the regcache code. */
+ struct regcache_list *regcache;
};
/* Create an empty thread list, or empty the existing one. */
diff --git a/gdb/regcache.c b/gdb/regcache.c
index 53e0c59..55f39fe 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -29,6 +29,7 @@
#include "gdb_string.h"
#include "gdbcmd.h" /* For maintenanceprintlist. */
#include "observer.h"
+#include "gdbthread.h"
/*
* DATA STRUCTURE
@@ -433,9 +434,6 @@ regcache_invalidate (struct regcache *regcache, int regnum)
regcache->register_status[regnum] = REG_UNKNOWN;
}
-
-/* Global structure containing the current regcache. */
-
/* NOTE: this is a write-through cache. There is no "dirty" bit for
recording if the register values have been changed (eg. by the
user). Therefore all registers must be written back to the
@@ -447,17 +445,15 @@ struct regcache_list
struct regcache_list *next;
};
-static struct regcache_list *current_regcache;
-
struct regcache *
get_thread_arch_regcache (ptid_t ptid, struct gdbarch *gdbarch)
{
struct regcache_list *list;
struct regcache *new_regcache;
+ struct thread_info *tp = find_thread_ptid (ptid);
- for (list = current_regcache; list; list = list->next)
- if (ptid_equal (list->regcache->ptid, ptid)
- && get_regcache_arch (list->regcache) == gdbarch)
+ for (list = tp->regcache; list; list = list->next)
+ if (get_regcache_arch (list->regcache) == gdbarch)
return list->regcache;
new_regcache = regcache_xmalloc_1 (gdbarch,
@@ -467,8 +463,8 @@ get_thread_arch_regcache (ptid_t ptid, struct gdbarch *gdbarch)
list = xmalloc (sizeof (struct regcache_list));
list->regcache = new_regcache;
- list->next = current_regcache;
- current_regcache = list;
+ list->next = tp->regcache;
+ tp->regcache = list;
return new_regcache;
}
@@ -494,6 +490,21 @@ get_current_regcache (void)
return get_thread_regcache (inferior_ptid);
}
+void
+free_thread_regcache (struct thread_info *tp)
+{
+ struct regcache_list *iter, *next;
+
+ for (iter = tp->regcache; iter; iter = next)
+ {
+ next = iter->next;
+ regcache_xfree (iter->regcache);
+ xfree (iter);
+ }
+
+ tp->regcache = NULL;
+}
+
/* Observer for the target_changed event. */
@@ -508,11 +519,14 @@ regcache_observer_target_changed (struct target_ops *target)
static void
regcache_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid)
{
+ struct thread_info *tp = find_thread_ptid (new_ptid);
struct regcache_list *list;
- for (list = current_regcache; list; list = list->next)
- if (ptid_equal (list->regcache->ptid, old_ptid))
+ for (list = tp->regcache; list; list = list->next)
+ {
+ gdb_assert (ptid_equal (list->regcache->ptid, old_ptid));
list->regcache->ptid = new_ptid;
+ }
}
/* Low level examining and depositing of registers.
@@ -522,6 +536,21 @@ regcache_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid)
garbage. (a change from GDB version 3, in which the caller got the
value from the last stop). */
+/* Helper for registers_changed_ptid. This is used as a callback to
+ iterate_over_threads. */
+
+static int
+invalidate_registers_maybe (struct thread_info *tp, void *data)
+{
+ ptid_t *filter = data;
+
+ if (ptid_match (tp->ptid, *filter))
+ free_thread_regcache (tp);
+
+ /* Keep going. */
+ return 0;
+}
+
/* REGISTERS_CHANGED ()
Indicate that registers may have changed, so invalidate the cache. */
@@ -529,28 +558,7 @@ regcache_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid)
void
registers_changed_ptid (ptid_t ptid)
{
- struct regcache_list *list, **list_link;
-
- list = current_regcache;
- list_link = ¤t_regcache;
- while (list)
- {
- if (ptid_match (list->regcache->ptid, ptid))
- {
- struct regcache_list *dead = list;
-
- *list_link = list->next;
- regcache_xfree (list->regcache);
- list = *list_link;
- xfree (dead);
- continue;
- }
-
- list_link = &list->next;
- list = *list_link;
- }
-
- current_regcache = NULL;
+ iterate_over_threads (invalidate_registers_maybe, &ptid);
current_thread_ptid = null_ptid;
current_thread_arch = NULL;
diff --git a/gdb/regcache.h b/gdb/regcache.h
index 7ae585a..be71f90 100644
--- a/gdb/regcache.h
+++ b/gdb/regcache.h
@@ -180,4 +180,8 @@ extern void regcache_cpy_no_passthrough (struct regcache *dest,
extern void registers_changed (void);
extern void registers_changed_ptid (ptid_t);
+/* Free the regcaches associated with the thread TP. */
+
+extern void free_thread_regcache (struct thread_info *tp);
+
#endif /* REGCACHE_H */
diff --git a/gdb/thread.c b/gdb/thread.c
index 62455c2..6717dd4 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -141,6 +141,8 @@ free_thread (struct thread_info *tp)
xfree (tp->private);
}
+ free_thread_regcache (tp);
+
xfree (tp->name);
xfree (tp);
}