[PATCH] Introduce tids cache for dwfl_getthreads

Masatake YAMATO yamato at redhat.com
Mon Dec 16 06:07:22 UTC 2013


dwfl_getthreads can be called once for the same Dwfl object.  In
dwfl_getthreads, directory entries under /proc/$pid/tasks are
traversed and the directory descriptor points the end of
/proc/$pid/tasks. Even if dwfl_getthreads called again, no
more directory entry can be read from the descriptor.

(1)rewinding the directory or (2) caching the threads at the initial
traversing can permit calling dwfl_getthreads repeatedly.
This patch implements (2). (2) is faster than (1) because
(2) can avoid overheads of readdir.

Following patch for stack.c is used for testing.
This patch makes stack command calling dwfl_getthreads twice.

  diff --git a/src/stack.c b/src/stack.c
  index f428ed0..3b7a319 100644
  --- a/src/stack.c
  +++ b/src/stack.c
  @@ -183,6 +183,16 @@ Only real user processes are supported, no kernel or process maps."),
       default:
         abort ();
       }
  +  switch (dwfl_getthreads (dwfl, thread_callback, NULL))
  +    {
  +    case DWARF_CB_OK:
  +      break;
  +    case -1:
  +      error (0, 0, "dwfl_getthreads: %s", dwfl_errmsg (-1));
  +      break;
  +    default:
  +      abort ();
  +    }
     dwfl_end (dwfl);

     return 0;

Signed-off-by: Masatake YAMATO <yamato at redhat.com>
---
 libdwfl/ChangeLog          | 13 ++++++++
 libdwfl/linux-pid-attach.c | 80 ++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 90 insertions(+), 3 deletions(-)

diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index ff38108..1b134e1 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,3 +1,16 @@
+2013-12-16  Masatake YAMATO  <yamato at redhat.com>
+
+	Introduce tids cache.
+	* linux-pid-attach.c (tids_vector): New struct type.
+	(pid_arg::vec): New field.
+	(pid_next_thread_in_vec): New function.
+	(pid_next_thread_in_dir): New function.
+	(pid_next_thread): Use above two functions.
+	(pid_detach): free pid_arg::vec field. closedir only
+	dir pid_arg::vec is not NULL.
+	(__libdwfl_attach_state_for_pid): Initialize pid_arg::vec
+	related field.
+
 2013-12-15  Jan Kratochvil  <jan.kratochvil at redhat.com>
 
 	unwinder: ppc and ppc64
diff --git a/libdwfl/linux-pid-attach.c b/libdwfl/linux-pid-attach.c
index 45a0732..fadcedb 100644
--- a/libdwfl/linux-pid-attach.c
+++ b/libdwfl/linux-pid-attach.c
@@ -37,9 +37,20 @@
 # define MAX(a, b) ((a) > (b) ? (a) : (b))
 #endif
 
+struct tids_vector
+{
+  int n_used;
+  int n_allocated;
+  int pos;			/* Used when reading tids cache */
+  pid_t *tids;
+};
+
 struct pid_arg
 {
+  /* vec acts as cache for the result of dir reading.
+     dir can be NULL after all dirents are stored to vec. */
   DIR *dir;
+  struct tids_vector vec;
   /* It is 0 if not used.  */
   pid_t tid_attached;
   /* Valid only if TID_ATTACHED is not zero.  */
@@ -159,8 +170,8 @@ pid_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg)
 }
 
 static pid_t
-pid_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
-		 void **thread_argp)
+pid_next_thread_in_dir (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
+			void **thread_argp)
 {
   struct pid_arg *pid_arg = dwfl_arg;
   struct dirent *dirent;
@@ -175,6 +186,10 @@ pid_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
 	      __libdwfl_seterrno (DWFL_E_ERRNO);
 	      return -1;
 	    }
+	  /* All dirents are read.
+	     These are now in pid_arg->vec. */
+	  closedir (pid_arg->dir);
+	  pid_arg->dir = NULL;
 	  return 0;
 	}
     }
@@ -194,10 +209,58 @@ pid_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
       __libdwfl_seterrno (DWFL_E_PARSE_PROC);
       return -1;
     }
+  if (pid_arg->vec.n_used == pid_arg->vec.n_allocated)
+    {
+      pid_t *tmp;
+
+      pid_arg->vec.n_allocated *= 2;
+      tmp = realloc(pid_arg->vec.tids, sizeof(pid_t) * pid_arg->vec.n_allocated);
+      if (tmp)
+	pid_arg->vec.tids = tmp;
+      else
+	{
+	  __libdwfl_seterrno (DWFL_E_NOMEM);
+	  return -1;
+	}
+    }
+  pid_arg->vec.tids[pid_arg->vec.n_used++] = tid;
+
   *thread_argp = dwfl_arg;
   return tid;
 }
 
+static pid_t
+pid_next_thread_in_vec (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
+			void **thread_argp)
+{
+  struct pid_arg *pid_arg = dwfl_arg;
+  pid_t r;
+
+  if (pid_arg->vec.pos < pid_arg->vec.n_used)
+    {
+      r = pid_arg->vec.tids[pid_arg->vec.pos++];
+      *thread_argp = dwfl_arg;
+    }
+  else
+    {
+      /* Reached the end of array. Reset the position. */
+      r = 0;
+      pid_arg->vec.pos = 0;
+    }
+
+  return r;
+}
+
+static pid_t
+pid_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
+		 void **thread_argp)
+{
+  struct pid_arg *pid_arg = dwfl_arg;
+  if (pid_arg->dir)
+    return pid_next_thread_in_dir(dwfl, dwfl_arg, thread_argp);
+  else
+    return pid_next_thread_in_vec(dwfl, dwfl_arg, thread_argp);
+}
 /* Implement the ebl_set_initial_registers_tid setfunc callback.  */
 
 static bool
@@ -235,7 +298,9 @@ static void
 pid_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
 {
   struct pid_arg *pid_arg = dwfl_arg;
-  closedir (pid_arg->dir);
+  if (pid_arg->dir)
+    closedir (pid_arg->dir);
+  free(pid_arg->vec.tids);
   free (pid_arg);
 }
 
@@ -279,16 +344,25 @@ __libdwfl_attach_state_for_pid (Dwfl *dwfl, pid_t pid)
   struct pid_arg *pid_arg = malloc (sizeof *pid_arg);
   if (pid_arg == NULL)
     {
+    error:
       closedir (dir);
       __libdwfl_seterrno (DWFL_E_NOMEM);
       return false;
     }
   pid_arg->dir = dir;
+  pid_arg->vec.n_used = 0;
+#define INITIAL_VEC_ALLOCATION 2
+  pid_arg->vec.n_allocated = INITIAL_VEC_ALLOCATION;
+  pid_arg->vec.pos = 0;
+  pid_arg->vec.tids = malloc(sizeof(pid_t) * INITIAL_VEC_ALLOCATION);
+  if (pid_arg->vec.tids == NULL)
+    goto error;
   pid_arg->tid_attached = 0;
   if (! INTUSE(dwfl_attach_state) (dwfl, NULL, pid, &pid_thread_callbacks,
 				   pid_arg))
     {
       closedir (dir);
+      free(pid_arg->vec.tids);
       free (pid_arg);
       return false;
     }
-- 
1.8.3.1



More information about the elfutils-devel mailing list