diff mbox

[RFA] Fix leaks in all the linux osdata annex transfers + code factorization.

Message ID 20181208180754.2050-1-philippe.waroquiers@skynet.be
State New
Headers show

Commit Message

Philippe Waroquiers Dec. 8, 2018, 6:07 p.m. UTC
Valgrind reports leaks in all linux osdata annex transfers of linux-osdata.c.

A typical leak (this one is of gdb.base/info-os) is:
==10592== VALGRIND_GDB_ERROR_BEGIN
==10592== 65,536 bytes in 1 blocks are definitely lost in loss record 3,175 of 3,208
==10592==    at 0x4C2E273: realloc (vg_replace_malloc.c:826)
==10592==    by 0x409B0C: xrealloc (common-utils.c:62)
==10592==    by 0x408BC3: buffer_grow(buffer*, char const*, unsigned long) [clone .part.1] (buffer.c:40)
==10592==    by 0x5263DF: linux_xfer_osdata_processes(unsigned char*, unsigned long, unsigned long) (linux-osdata.c:370)
==10592==    by 0x520875: linux_nat_xfer_osdata (linux-nat.c:4214)
...

The leaks are created because the linux_xfer_osdata_* functions
transfer the ownership of their 'static struct buffer' memory
to their 'static char *buf' local var, but then call buffer_free
instead of xfree-ing buf.

I see no reason why the ownership of the memory has to be transferred
from a local var to another local var, so the fix consists in dropping
the 'static char *buf' and accessing the struct buffer memory where needed.

Also, because this bug was replicated in all functions, and there was
a non neglectible amount of duplicated code, the setup and usage
of the 'static struct buffer' is factorized in a new function
common_getter.  The buffer for a specific annex is now a member
of the struct osdata_type instead of being a static var of each
linux_xfer_osdata_* function.

Thanks to this, all the linux_xfer_osdata_* do not have
anymore any logic related to the partial transfer of data: they now
only build the xml data in a struct buffer.
This all removes about 300 SLOC.

Note: git diff/git format-patch shows a lot of differences only due
to space changes/indentation changes.
So, git diff -w helps to look only at the relevant differences.

gdb/ChangeLog
2018-12-08  Philippe Waroquiers  <philippe.waroquiers@skynet.be>

	* nat/linux-osdata.c : Removed various trailing spaces.
	(common_getter): New function.
	(struct osdata_type): Change getter to take_snapshot.
	Add LONGEST len_avail and struct buffer buffer.
	Change all elements in the initializer.
	Add an element for the list of types.
	(linux_xfer_osdata_info_os_types): New function.
	(linux_common_xfer_osdata): Use common_getter for the list of types.
	Replace getter call by common_getter.
	(linux_xfer_osdata_cpus): Remove args READBUF, OFFSET, LEN.
	Add arg BUFFER.  Only keep the code that adds data in BUFFER.
	(linux_xfer_osdata_fds): Likewise.
	(linux_xfer_osdata_modules): Likewise.
	(linux_xfer_osdata_msg): Likewise.
	(linux_xfer_osdata_processes): Likewise.
	(linux_xfer_osdata_processgroups): Likewise.
	(linux_xfer_osdata_sem): Likewise.
	(linux_xfer_osdata_shm): Likewise.
	(linux_xfer_osdata_isockets): Likewise.
	(linux_xfer_osdata_threads): Likewise.
---
 gdb/nat/linux-osdata.c | 1607 ++++++++++++++++------------------------
 1 file changed, 659 insertions(+), 948 deletions(-)

Comments

Simon Marchi Dec. 11, 2018, 4:55 a.m. UTC | #1
On 2018-12-08 13:07, Philippe Waroquiers wrote:
> Valgrind reports leaks in all linux osdata annex transfers of 
> linux-osdata.c.
> 
> A typical leak (this one is of gdb.base/info-os) is:
> ==10592== VALGRIND_GDB_ERROR_BEGIN
> ==10592== 65,536 bytes in 1 blocks are definitely lost in loss record
> 3,175 of 3,208
> ==10592==    at 0x4C2E273: realloc (vg_replace_malloc.c:826)
> ==10592==    by 0x409B0C: xrealloc (common-utils.c:62)
> ==10592==    by 0x408BC3: buffer_grow(buffer*, char const*, unsigned
> long) [clone .part.1] (buffer.c:40)
> ==10592==    by 0x5263DF: linux_xfer_osdata_processes(unsigned char*,
> unsigned long, unsigned long) (linux-osdata.c:370)
> ==10592==    by 0x520875: linux_nat_xfer_osdata (linux-nat.c:4214)
> ...
> 
> The leaks are created because the linux_xfer_osdata_* functions
> transfer the ownership of their 'static struct buffer' memory
> to their 'static char *buf' local var, but then call buffer_free
> instead of xfree-ing buf.
> 
> I see no reason why the ownership of the memory has to be transferred
> from a local var to another local var, so the fix consists in dropping
> the 'static char *buf' and accessing the struct buffer memory where 
> needed.
> 
> Also, because this bug was replicated in all functions, and there was
> a non neglectible amount of duplicated code, the setup and usage
> of the 'static struct buffer' is factorized in a new function
> common_getter.  The buffer for a specific annex is now a member
> of the struct osdata_type instead of being a static var of each
> linux_xfer_osdata_* function.
> 
> Thanks to this, all the linux_xfer_osdata_* do not have
> anymore any logic related to the partial transfer of data: they now
> only build the xml data in a struct buffer.
> This all removes about 300 SLOC.
> 
> Note: git diff/git format-patch shows a lot of differences only due
> to space changes/indentation changes.
> So, git diff -w helps to look only at the relevant differences.
> 
> gdb/ChangeLog
> 2018-12-08  Philippe Waroquiers  <philippe.waroquiers@skynet.be>
> 
> 	* nat/linux-osdata.c : Removed various trailing spaces.
> 	(common_getter): New function.
> 	(struct osdata_type): Change getter to take_snapshot.
> 	Add LONGEST len_avail and struct buffer buffer.
> 	Change all elements in the initializer.
> 	Add an element for the list of types.
> 	(linux_xfer_osdata_info_os_types): New function.
> 	(linux_common_xfer_osdata): Use common_getter for the list of types.
> 	Replace getter call by common_getter.
> 	(linux_xfer_osdata_cpus): Remove args READBUF, OFFSET, LEN.
> 	Add arg BUFFER.  Only keep the code that adds data in BUFFER.
> 	(linux_xfer_osdata_fds): Likewise.
> 	(linux_xfer_osdata_modules): Likewise.
> 	(linux_xfer_osdata_msg): Likewise.
> 	(linux_xfer_osdata_processes): Likewise.
> 	(linux_xfer_osdata_processgroups): Likewise.
> 	(linux_xfer_osdata_sem): Likewise.
> 	(linux_xfer_osdata_shm): Likewise.
> 	(linux_xfer_osdata_isockets): Likewise.
> 	(linux_xfer_osdata_threads): Likewise.

Thanks, this is a nice cleanup, this LGTM.

Would it be possible to split the changes that remove the trailing 
spaces in a separate (obvious) patch?

Simon
Philippe Waroquiers Dec. 11, 2018, 10:15 p.m. UTC | #2
On Mon, 2018-12-10 at 23:55 -0500, Simon Marchi wrote:
> 
> Thanks, this is a nice cleanup, this LGTM.
> 
> Would it be possible to split the changes that remove the trailing 
> spaces in a separate (obvious) patch?
I pushed the white space changes as an obvious
patch, and then the real fix as another patch.
Thanks for the review
Philippe
diff mbox

Patch

diff --git a/gdb/nat/linux-osdata.c b/gdb/nat/linux-osdata.c
index 98bded06ba..6f6724ca8e 100644
--- a/gdb/nat/linux-osdata.c
+++ b/gdb/nat/linux-osdata.c
@@ -1,5 +1,5 @@ 
 /* Linux-specific functions to retrieve OS data.
-   
+
    Copyright (C) 2009-2018 Free Software Foundation, Inc.
 
    This file is part of GDB.
@@ -55,7 +55,7 @@  typedef long long TIME_T;
 #define MAX_PID_T_STRLEN  (sizeof ("-9223372036854775808") - 1)
 
 /* Returns the CPU core that thread PTID is currently running on.  */
-					  
+
 /* Compute and return the processor core of a given thread.  */
 
 int
@@ -117,9 +117,9 @@  command_from_pid (char *command, int maxlen, PID_T pid)
 {
   std::string stat_path = string_printf ("/proc/%lld/stat", pid);
   gdb_file_up fp = gdb_fopen_cloexec (stat_path, "r");
-  
+
   command[0] = '\0';
- 
+
   if (fp)
     {
       /* sizeof (cmd) should be greater or equal to TASK_COMM_LEN (in
@@ -128,7 +128,7 @@  command_from_pid (char *command, int maxlen, PID_T pid)
       char cmd[18];
       PID_T stat_pid;
       int items_read = fscanf (fp.get (), "%lld %17s", &stat_pid, cmd);
-	  
+
       if (items_read == 2 && pid == stat_pid)
 	{
 	  cmd[strlen (cmd) - 1] = '\0'; /* Remove trailing parenthesis.  */
@@ -162,7 +162,7 @@  commandline_from_pid (PID_T pid)
 	{
 	  char buf[1024];
 	  size_t read_bytes = fread (buf, 1, sizeof (buf), f.get ());
-     
+
 	  if (read_bytes)
 	    {
 	      commandline = (char *) xrealloc (commandline, len + read_bytes + 1);
@@ -206,7 +206,7 @@  static void
 user_from_uid (char *user, int maxlen, uid_t uid)
 {
   struct passwd *pwentry = getpwuid (uid);
-  
+
   if (pwentry)
     {
       strncpy (user, pwentry->pw_name, maxlen);
@@ -227,7 +227,7 @@  get_process_owner (uid_t *owner, PID_T pid)
   char procentry[sizeof ("/proc/") + MAX_PID_T_STRLEN];
 
   sprintf (procentry, "/proc/%lld", pid);
-  
+
   if (stat (procentry, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
     {
       *owner = statbuf.st_uid;
@@ -278,114 +278,83 @@  get_cores_used_by_process (PID_T pid, int *cores, const int num_cores)
   return task_count;
 }
 
-static LONGEST
-linux_xfer_osdata_processes (gdb_byte *readbuf,
-			     ULONGEST offset, ULONGEST len)
+static void
+linux_xfer_osdata_processes (struct buffer *buffer)
 {
-  /* We make the process list snapshot when the object starts to be read.  */
-  static const char *buf;
-  static LONGEST len_avail = -1;
-  static struct buffer buffer;
+  DIR *dirp;
 
-  if (offset == 0)
-    {
-      DIR *dirp;
+  buffer_grow_str (buffer, "<osdata type=\"processes\">\n");
 
-      if (len_avail != -1 && len_avail != 0)
-	buffer_free (&buffer);
-      len_avail = 0;
-      buf = NULL;
-      buffer_init (&buffer);
-      buffer_grow_str (&buffer, "<osdata type=\"processes\">\n");
+  dirp = opendir ("/proc");
+  if (dirp)
+    {
+      const int num_cores = sysconf (_SC_NPROCESSORS_ONLN);
+      struct dirent *dp;
 
-      dirp = opendir ("/proc");
-      if (dirp)
+      while ((dp = readdir (dirp)) != NULL)
 	{
-	  const int num_cores = sysconf (_SC_NPROCESSORS_ONLN);
-	  struct dirent *dp;
+	  PID_T pid;
+	  uid_t owner;
+	  char user[UT_NAMESIZE];
+	  char *command_line;
+	  int *cores;
+	  int task_count;
+	  char *cores_str;
+	  int i;
 
-	  while ((dp = readdir (dirp)) != NULL)
-	    {
-	      PID_T pid;
-	      uid_t owner;
-	      char user[UT_NAMESIZE];
-	      char *command_line;
-	      int *cores;
-	      int task_count;
-	      char *cores_str;
-	      int i;
-
-	      if (!isdigit (dp->d_name[0])
-		  || NAMELEN (dp) > MAX_PID_T_STRLEN)
-		continue;
+	  if (!isdigit (dp->d_name[0])
+	      || NAMELEN (dp) > MAX_PID_T_STRLEN)
+	    continue;
 
-	      sscanf (dp->d_name, "%lld", &pid);
-	      command_line = commandline_from_pid (pid);
-
-	      if (get_process_owner (&owner, pid) == 0)
-		user_from_uid (user, sizeof (user), owner);
-	      else
-		strcpy (user, "?");
-
-	      /* Find CPU cores used by the process.  */
-	      cores = XCNEWVEC (int, num_cores);
-	      task_count = get_cores_used_by_process (pid, cores, num_cores);
-	      cores_str = (char *) xcalloc (task_count, sizeof ("4294967295") + 1);
-
-	      for (i = 0; i < num_cores && task_count > 0; ++i)
-		if (cores[i])
-		  {
-		    char core_str[sizeof ("4294967295")];
-
-		    sprintf (core_str, "%d", i);
-		    strcat (cores_str, core_str);
-
-		    task_count -= cores[i];
-		    if (task_count > 0)
-		      strcat (cores_str, ",");
-		  }
-
-	      xfree (cores);
-	      
-	      buffer_xml_printf (
-		  &buffer,
-		  "<item>"
-		  "<column name=\"pid\">%lld</column>"
-		  "<column name=\"user\">%s</column>"
-		  "<column name=\"command\">%s</column>"
-		  "<column name=\"cores\">%s</column>"
-		  "</item>",
-		  pid,
-		  user,
-		  command_line ? command_line : "",
-		  cores_str);
-
-	      xfree (command_line);     
-	      xfree (cores_str);
-	    }
-	  
-	  closedir (dirp);
+	  sscanf (dp->d_name, "%lld", &pid);
+	  command_line = commandline_from_pid (pid);
+
+	  if (get_process_owner (&owner, pid) == 0)
+	    user_from_uid (user, sizeof (user), owner);
+	  else
+	    strcpy (user, "?");
+
+	  /* Find CPU cores used by the process.  */
+	  cores = XCNEWVEC (int, num_cores);
+	  task_count = get_cores_used_by_process (pid, cores, num_cores);
+	  cores_str = (char *) xcalloc (task_count, sizeof ("4294967295") + 1);
+
+	  for (i = 0; i < num_cores && task_count > 0; ++i)
+	    if (cores[i])
+	      {
+		char core_str[sizeof ("4294967295")];
+
+		sprintf (core_str, "%d", i);
+		strcat (cores_str, core_str);
+
+		task_count -= cores[i];
+		if (task_count > 0)
+		  strcat (cores_str, ",");
+	      }
+
+	  xfree (cores);
+
+	  buffer_xml_printf
+	    (buffer,
+	     "<item>"
+	     "<column name=\"pid\">%lld</column>"
+	     "<column name=\"user\">%s</column>"
+	     "<column name=\"command\">%s</column>"
+	     "<column name=\"cores\">%s</column>"
+	     "</item>",
+	     pid,
+	     user,
+	     command_line ? command_line : "",
+	     cores_str);
+
+	  xfree (command_line);
+	  xfree (cores_str);
 	}
 
-      buffer_grow_str0 (&buffer, "</osdata>\n");
-      buf = buffer_finish (&buffer);
-      len_avail = strlen (buf);
+      closedir (dirp);
     }
 
-  if (offset >= len_avail)
-    {
-      /* Done.  Get rid of the buffer.  */
-      buffer_free (&buffer);
-      buf = NULL;
-      len_avail = 0;
-      return 0;
-    }
-
-  if (len > len_avail - offset)
-    len = len_avail - offset;
-  memcpy (readbuf, buf + offset, len);
-
-  return len;
+  buffer_grow_str0 (buffer, "</osdata>\n");
 }
 
 /* A simple PID/PGID pair.  */
@@ -425,428 +394,305 @@  struct pid_pgid_entry
   PID_T pid, pgid;
 };
 
-/* Collect all process groups from /proc.  */
+/* Collect all process groups from /proc in BUFFER.  */
 
-static LONGEST
-linux_xfer_osdata_processgroups (gdb_byte *readbuf,
-				 ULONGEST offset, ULONGEST len)
+static void
+linux_xfer_osdata_processgroups (struct buffer *buffer)
 {
-  /* We make the process list snapshot when the object starts to be read.  */
-  static const char *buf;
-  static LONGEST len_avail = -1;
-  static struct buffer buffer;
+  DIR *dirp;
 
-  if (offset == 0)
+  buffer_grow_str (buffer, "<osdata type=\"process groups\">\n");
+
+  dirp = opendir ("/proc");
+  if (dirp)
     {
-      DIR *dirp;
+      std::vector<pid_pgid_entry> process_list;
+      struct dirent *dp;
 
-      if (len_avail != -1 && len_avail != 0)
-	buffer_free (&buffer);
-      len_avail = 0;
-      buf = NULL;
-      buffer_init (&buffer);
-      buffer_grow_str (&buffer, "<osdata type=\"process groups\">\n");
+      process_list.reserve (512);
 
-      dirp = opendir ("/proc");
-      if (dirp)
+      /* Build list consisting of PIDs followed by their
+	 associated PGID.  */
+      while ((dp = readdir (dirp)) != NULL)
 	{
-	  std::vector<pid_pgid_entry> process_list;
-	  struct dirent *dp;
+	  PID_T pid, pgid;
 
-	  process_list.reserve (512);
-
-	  /* Build list consisting of PIDs followed by their
-	     associated PGID.  */
-	  while ((dp = readdir (dirp)) != NULL)
-	    {
-	      PID_T pid, pgid;
-
-	      if (!isdigit (dp->d_name[0])
-		  || NAMELEN (dp) > MAX_PID_T_STRLEN)
-		continue;
-
-	      sscanf (dp->d_name, "%lld", &pid);
-	      pgid = getpgid (pid);
-
-	      if (pgid > 0)
-		process_list.emplace_back (pid, pgid);
-	    }
+	  if (!isdigit (dp->d_name[0])
+	      || NAMELEN (dp) > MAX_PID_T_STRLEN)
+	    continue;
 
-	  closedir (dirp);
+	  sscanf (dp->d_name, "%lld", &pid);
+	  pgid = getpgid (pid);
 
-	  /* Sort the process list.  */
-	  std::sort (process_list.begin (), process_list.end ());
+	  if (pgid > 0)
+	    process_list.emplace_back (pid, pgid);
+	}
 
-	  for (const pid_pgid_entry &entry : process_list)
-	    {
-	      PID_T pid = entry.pid;
-	      PID_T pgid = entry.pgid;
-	      char leader_command[32];
-	      char *command_line;
-
-	      command_from_pid (leader_command, sizeof (leader_command), pgid);
-	      command_line = commandline_from_pid (pid);
-
-	      buffer_xml_printf (
-		  &buffer,
-		  "<item>"
-		  "<column name=\"pgid\">%lld</column>"
-		  "<column name=\"leader command\">%s</column>"
-		  "<column name=\"pid\">%lld</column>"
-		  "<column name=\"command line\">%s</column>"
-		  "</item>",
-		  pgid,
-		  leader_command,
-		  pid,
-		  command_line ? command_line : "");
-
-	      xfree (command_line);
-	    }
-	}   
+      closedir (dirp);
 
-      buffer_grow_str0 (&buffer, "</osdata>\n");
-      buf = buffer_finish (&buffer);
-      len_avail = strlen (buf);
-    }
+      /* Sort the process list.  */
+      std::sort (process_list.begin (), process_list.end ());
 
-  if (offset >= len_avail)
-    {
-      /* Done.  Get rid of the buffer.  */
-      buffer_free (&buffer);
-      buf = NULL;
-      len_avail = 0;
-      return 0;
+      for (const pid_pgid_entry &entry : process_list)
+	{
+	  PID_T pid = entry.pid;
+	  PID_T pgid = entry.pgid;
+	  char leader_command[32];
+	  char *command_line;
+
+	  command_from_pid (leader_command, sizeof (leader_command), pgid);
+	  command_line = commandline_from_pid (pid);
+
+	  buffer_xml_printf
+	    (buffer,
+	     "<item>"
+	     "<column name=\"pgid\">%lld</column>"
+	     "<column name=\"leader command\">%s</column>"
+	     "<column name=\"pid\">%lld</column>"
+	     "<column name=\"command line\">%s</column>"
+	     "</item>",
+	     pgid,
+	     leader_command,
+	     pid,
+	     command_line ? command_line : "");
+
+	  xfree (command_line);
+	}
     }
 
-  if (len > len_avail - offset)
-    len = len_avail - offset;
-  memcpy (readbuf, buf + offset, len);
-
-  return len;
+  buffer_grow_str0 (buffer, "</osdata>\n");
 }
 
 /* Collect all the threads in /proc by iterating through processes and
-   then tasks within each process.  */
+   then tasks within each process in BUFFER.  */
 
-static LONGEST
-linux_xfer_osdata_threads (gdb_byte *readbuf,
-			   ULONGEST offset, ULONGEST len)
+static void
+linux_xfer_osdata_threads (struct buffer *buffer)
 {
-  /* We make the process list snapshot when the object starts to be read.  */
-  static const char *buf;
-  static LONGEST len_avail = -1;
-  static struct buffer buffer;
+  DIR *dirp;
 
-  if (offset == 0)
-    {
-      DIR *dirp;
+  buffer_grow_str (buffer, "<osdata type=\"threads\">\n");
 
-      if (len_avail != -1 && len_avail != 0)
-	buffer_free (&buffer);
-      len_avail = 0;
-      buf = NULL;
-      buffer_init (&buffer);
-      buffer_grow_str (&buffer, "<osdata type=\"threads\">\n");
+  dirp = opendir ("/proc");
+  if (dirp)
+    {
+      struct dirent *dp;
 
-      dirp = opendir ("/proc");
-      if (dirp)
+      while ((dp = readdir (dirp)) != NULL)
 	{
-	  struct dirent *dp;
+	  struct stat statbuf;
+	  char procentry[sizeof ("/proc/4294967295")];
 
-	  while ((dp = readdir (dirp)) != NULL)
+	  if (!isdigit (dp->d_name[0])
+	      || NAMELEN (dp) > sizeof ("4294967295") - 1)
+	    continue;
+
+	  xsnprintf (procentry, sizeof (procentry), "/proc/%s",
+		     dp->d_name);
+	  if (stat (procentry, &statbuf) == 0
+	      && S_ISDIR (statbuf.st_mode))
 	    {
-	      struct stat statbuf;
-	      char procentry[sizeof ("/proc/4294967295")];
+	      DIR *dirp2;
+	      PID_T pid;
+	      char command[32];
 
-	      if (!isdigit (dp->d_name[0])
-		  || NAMELEN (dp) > sizeof ("4294967295") - 1)
-		continue;
+	      std::string pathname
+		= string_printf ("/proc/%s/task", dp->d_name);
 
-	      xsnprintf (procentry, sizeof (procentry), "/proc/%s",
-			 dp->d_name);
-	      if (stat (procentry, &statbuf) == 0
-		  && S_ISDIR (statbuf.st_mode))
-		{
-		  DIR *dirp2;
-		  PID_T pid;
-		  char command[32];
+	      pid = atoi (dp->d_name);
+	      command_from_pid (command, sizeof (command), pid);
 
-		  std::string pathname
-		    = string_printf ("/proc/%s/task", dp->d_name);
-		  
-		  pid = atoi (dp->d_name);
-		  command_from_pid (command, sizeof (command), pid);
+	      dirp2 = opendir (pathname.c_str ());
 
-		  dirp2 = opendir (pathname.c_str ());
+	      if (dirp2)
+		{
+		  struct dirent *dp2;
 
-		  if (dirp2)
+		  while ((dp2 = readdir (dirp2)) != NULL)
 		    {
-		      struct dirent *dp2;
-
-		      while ((dp2 = readdir (dirp2)) != NULL)
-			{
-			  PID_T tid;
-			  int core;
-
-			  if (!isdigit (dp2->d_name[0])
-			      || NAMELEN (dp2) > sizeof ("4294967295") - 1)
-			    continue;
-
-			  tid = atoi (dp2->d_name);
-			  core = linux_common_core_of_thread (ptid_t (pid, tid, 0));
-
-			  buffer_xml_printf (
-			    &buffer,
-			    "<item>"
-			    "<column name=\"pid\">%lld</column>"
-			    "<column name=\"command\">%s</column>"
-			    "<column name=\"tid\">%lld</column>"
-			    "<column name=\"core\">%d</column>"
-			    "</item>",
-			    pid,
-			    command,
-			    tid,
-			    core);
-			}
-
-		      closedir (dirp2);
+		      PID_T tid;
+		      int core;
+
+		      if (!isdigit (dp2->d_name[0])
+			  || NAMELEN (dp2) > sizeof ("4294967295") - 1)
+			continue;
+
+		      tid = atoi (dp2->d_name);
+		      core = linux_common_core_of_thread (ptid_t (pid, tid, 0));
+
+		      buffer_xml_printf
+			(buffer,
+			 "<item>"
+			 "<column name=\"pid\">%lld</column>"
+			 "<column name=\"command\">%s</column>"
+			 "<column name=\"tid\">%lld</column>"
+			 "<column name=\"core\">%d</column>"
+			 "</item>",
+			 pid,
+			 command,
+			 tid,
+			 core);
 		    }
+
+		  closedir (dirp2);
 		}
 	    }
-
-	  closedir (dirp);
 	}
 
-      buffer_grow_str0 (&buffer, "</osdata>\n");
-      buf = buffer_finish (&buffer);
-      len_avail = strlen (buf);
-    }
-
-  if (offset >= len_avail)
-    {
-      /* Done.  Get rid of the buffer.  */
-      buffer_free (&buffer);
-      buf = NULL;
-      len_avail = 0;
-      return 0;
+      closedir (dirp);
     }
 
-  if (len > len_avail - offset)
-    len = len_avail - offset;
-  memcpy (readbuf, buf + offset, len);
-
-  return len;
+  buffer_grow_str0 (buffer, "</osdata>\n");
 }
 
-/* Collect data about the cpus/cores on the system */
+/* Collect data about the cpus/cores on the system in BUFFER.  */
 
-static LONGEST
-linux_xfer_osdata_cpus (gdb_byte *readbuf,
-			   ULONGEST offset, ULONGEST len)
+static void
+linux_xfer_osdata_cpus (struct buffer *buffer)
 {
-  static const char *saved_buf;
-  static LONGEST len_avail = -1;
-  static struct buffer buffer;
+  int first_item = 1;
 
-  if (offset == 0)
-    {
-      int first_item = 1;
+  buffer_grow_str (buffer, "<osdata type=\"cpus\">\n");
 
-      if (len_avail != -1 && len_avail != 0)
-	buffer_free (&buffer);
-      len_avail = 0;
-      saved_buf = NULL;
-      buffer_init (&buffer);
-      buffer_grow_str (&buffer, "<osdata type=\"cpus\">\n");
+  gdb_file_up fp = gdb_fopen_cloexec ("/proc/cpuinfo", "r");
+  if (fp != NULL)
+    {
+      char buf[8192];
 
-      gdb_file_up fp = gdb_fopen_cloexec ("/proc/cpuinfo", "r");
-      if (fp != NULL)
+      do
 	{
-	  char buf[8192];
-
-	  do
+	  if (fgets (buf, sizeof (buf), fp.get ()))
 	    {
-	      if (fgets (buf, sizeof (buf), fp.get ()))
-		{
-		  char *key, *value;
-		  int i = 0;
+	      char *key, *value;
+	      int i = 0;
 
-		  key = strtok (buf, ":");
-		  if (key == NULL)
-		    continue;
-
-		  value = strtok (NULL, ":");
-		  if (value == NULL)
-		    continue;
+	      key = strtok (buf, ":");
+	      if (key == NULL)
+		continue;
 
-		  while (key[i] != '\t' && key[i] != '\0')
-		    i++;
+	      value = strtok (NULL, ":");
+	      if (value == NULL)
+		continue;
 
-		  key[i] = '\0';
+	      while (key[i] != '\t' && key[i] != '\0')
+		i++;
 
-		  i = 0;
-		  while (value[i] != '\t' && value[i] != '\0')
-		    i++;
+	      key[i] = '\0';
 
-		  value[i] = '\0';
+	      i = 0;
+	      while (value[i] != '\t' && value[i] != '\0')
+		i++;
 
-		  if (strcmp (key, "processor") == 0)
-		    {
-		      if (first_item)
-			buffer_grow_str (&buffer, "<item>");
-		      else
-			buffer_grow_str (&buffer, "</item><item>");
+	      value[i] = '\0';
 
-		      first_item = 0;
-		    }
+	      if (strcmp (key, "processor") == 0)
+		{
+		  if (first_item)
+		    buffer_grow_str (buffer, "<item>");
+		  else
+		    buffer_grow_str (buffer, "</item><item>");
 
-		  buffer_xml_printf (&buffer,
-				     "<column name=\"%s\">%s</column>",
-				     key,
-				     value);
+		  first_item = 0;
 		}
-	    }
-	  while (!feof (fp.get ()));
 
-	  if (first_item == 0)
-	    buffer_grow_str (&buffer, "</item>");
+	      buffer_xml_printf (buffer,
+				 "<column name=\"%s\">%s</column>",
+				 key,
+				 value);
+	    }
 	}
+      while (!feof (fp.get ()));
 
-      buffer_grow_str0 (&buffer, "</osdata>\n");
-      saved_buf = buffer_finish (&buffer);
-      len_avail = strlen (saved_buf);
-    }
-
-  if (offset >= len_avail)
-    {
-      /* Done.  Get rid of the buffer.  */
-      buffer_free (&buffer);
-      saved_buf = NULL;
-      len_avail = 0;
-      return 0;
+      if (first_item == 0)
+	buffer_grow_str (buffer, "</item>");
     }
 
-  if (len > len_avail - offset)
-    len = len_avail - offset;
-  memcpy (readbuf, saved_buf + offset, len);
-
-  return len;
+  buffer_grow_str0 (buffer, "</osdata>\n");
 }
 
 /* Collect all the open file descriptors found in /proc and put the details
-   found about them into READBUF.  */
+   found about them into BUFFER.  */
 
-static LONGEST
-linux_xfer_osdata_fds (gdb_byte *readbuf,
-		       ULONGEST offset, ULONGEST len)
+static void
+linux_xfer_osdata_fds (struct buffer *buffer)
 {
-  /* We make the process list snapshot when the object starts to be read.  */
-  static const char *saved_buf;
-  static LONGEST len_avail = -1;
-  static struct buffer buffer;
+  DIR *dirp;
 
-  if (offset == 0)
-    {
-      DIR *dirp;
+  buffer_grow_str (buffer, "<osdata type=\"files\">\n");
 
-      if (len_avail != -1 && len_avail != 0)
-	buffer_free (&buffer);
-      len_avail = 0;
-      saved_buf = NULL;
-      buffer_init (&buffer);
-      buffer_grow_str (&buffer, "<osdata type=\"files\">\n");
+  dirp = opendir ("/proc");
+  if (dirp)
+    {
+      struct dirent *dp;
 
-      dirp = opendir ("/proc");
-      if (dirp)
+      while ((dp = readdir (dirp)) != NULL)
 	{
-	  struct dirent *dp;
+	  struct stat statbuf;
+	  char procentry[sizeof ("/proc/4294967295")];
 
-	  while ((dp = readdir (dirp)) != NULL)
-	    {
-	      struct stat statbuf;
-	      char procentry[sizeof ("/proc/4294967295")];
+	  if (!isdigit (dp->d_name[0])
+	      || NAMELEN (dp) > sizeof ("4294967295") - 1)
+	    continue;
 
-	      if (!isdigit (dp->d_name[0])
-		  || NAMELEN (dp) > sizeof ("4294967295") - 1)
-		continue;
+	  xsnprintf (procentry, sizeof (procentry), "/proc/%s",
+		     dp->d_name);
+	  if (stat (procentry, &statbuf) == 0
+	      && S_ISDIR (statbuf.st_mode))
+	    {
+	      DIR *dirp2;
+	      PID_T pid;
+	      char command[32];
 
-	      xsnprintf (procentry, sizeof (procentry), "/proc/%s",
-			 dp->d_name);
-	      if (stat (procentry, &statbuf) == 0
-		  && S_ISDIR (statbuf.st_mode))
-		{
-		  DIR *dirp2;
-		  PID_T pid;
-		  char command[32];
+	      pid = atoi (dp->d_name);
+	      command_from_pid (command, sizeof (command), pid);
 
-		  pid = atoi (dp->d_name);
-		  command_from_pid (command, sizeof (command), pid);
+	      std::string pathname
+		= string_printf ("/proc/%s/fd", dp->d_name);
+	      dirp2 = opendir (pathname.c_str ());
 
-		  std::string pathname
-		    = string_printf ("/proc/%s/fd", dp->d_name);
-		  dirp2 = opendir (pathname.c_str ());
+	      if (dirp2)
+		{
+		  struct dirent *dp2;
 
-		  if (dirp2)
+		  while ((dp2 = readdir (dirp2)) != NULL)
 		    {
-		      struct dirent *dp2;
-
-		      while ((dp2 = readdir (dirp2)) != NULL)
-			{
-			  char buf[1000];
-			  ssize_t rslt;
-
-			  if (!isdigit (dp2->d_name[0]))
-			    continue;
-
-			  std::string fdname
-			    = string_printf ("%s/%s", pathname.c_str (),
-					     dp2->d_name);
-			  rslt = readlink (fdname.c_str (), buf,
-					   sizeof (buf) - 1);
-			  if (rslt >= 0)
-			    buf[rslt] = '\0';
-
-			  buffer_xml_printf (
-			    &buffer,
-			    "<item>"
-			    "<column name=\"pid\">%s</column>"
-			    "<column name=\"command\">%s</column>"
-			    "<column name=\"file descriptor\">%s</column>"
-			    "<column name=\"name\">%s</column>"
-			    "</item>",
-			    dp->d_name,
-			    command,
-			    dp2->d_name,
-			    (rslt >= 0 ? buf : dp2->d_name));
-			}
-
-		      closedir (dirp2);
+		      char buf[1000];
+		      ssize_t rslt;
+
+		      if (!isdigit (dp2->d_name[0]))
+			continue;
+
+		      std::string fdname
+			= string_printf ("%s/%s", pathname.c_str (),
+					 dp2->d_name);
+		      rslt = readlink (fdname.c_str (), buf,
+				       sizeof (buf) - 1);
+		      if (rslt >= 0)
+			buf[rslt] = '\0';
+
+		      buffer_xml_printf
+			(buffer,
+			 "<item>"
+			 "<column name=\"pid\">%s</column>"
+			 "<column name=\"command\">%s</column>"
+			 "<column name=\"file descriptor\">%s</column>"
+			 "<column name=\"name\">%s</column>"
+			 "</item>",
+			 dp->d_name,
+			 command,
+			 dp2->d_name,
+			 (rslt >= 0 ? buf : dp2->d_name));
 		    }
+
+		  closedir (dirp2);
 		}
 	    }
-
-	  closedir (dirp);
 	}
 
-      buffer_grow_str0 (&buffer, "</osdata>\n");
-      saved_buf = buffer_finish (&buffer);
-      len_avail = strlen (saved_buf);
+      closedir (dirp);
     }
 
-  if (offset >= len_avail)
-    {
-      /* Done.  Get rid of the buffer.  */
-      buffer_free (&buffer);
-      saved_buf = NULL;
-      len_avail = 0;
-      return 0;
-    }
-
-  if (len > len_avail - offset)
-    len = len_avail - offset;
-  memcpy (readbuf, saved_buf + offset, len);
-
-  return len;
+  buffer_grow_str0 (buffer, "</osdata>\n");
 }
 
 /* Returns the socket state STATE in textual form.  */
@@ -946,7 +792,7 @@  print_sockets (unsigned short family, int tcp, struct buffer *buffer)
 			       remote_address, &remote_port,
 			       &state,
 			       &uid);
-	      
+
 	      if (result == 6)
 		{
 		  union socket_addr locaddr, remaddr;
@@ -960,7 +806,7 @@  print_sockets (unsigned short family, int tcp, struct buffer *buffer)
 			      &locaddr.sin.sin_addr.s_addr);
 		      sscanf (remote_address, "%X",
 			      &remaddr.sin.sin_addr.s_addr);
-		      
+
 		      locaddr.sin.sin_port = htons (local_port);
 		      remaddr.sin.sin_port = htons (remote_port);
 
@@ -981,7 +827,7 @@  print_sockets (unsigned short family, int tcp, struct buffer *buffer)
 
 		      locaddr.sin6.sin6_port = htons (local_port);
 		      remaddr.sin6.sin6_port = htons (remote_port);
-		      
+
 		      locaddr.sin6.sin6_flowinfo = 0;
 		      remaddr.sin6.sin6_flowinfo = 0;
 		      locaddr.sin6.sin6_scope_id = 0;
@@ -989,9 +835,9 @@  print_sockets (unsigned short family, int tcp, struct buffer *buffer)
 
 		      addr_size = sizeof (struct sockaddr_in6);
 		    }
-	      
+
 		  locaddr.sa.sa_family = remaddr.sa.sa_family = family;
-		      
+
 		  result = getnameinfo (&locaddr.sa, addr_size,
 					local_address, sizeof (local_address),
 					local_service, sizeof (local_service),
@@ -999,7 +845,7 @@  print_sockets (unsigned short family, int tcp, struct buffer *buffer)
 					| (tcp ? 0 : NI_DGRAM));
 		  if (result)
 		    continue;
-		  
+
 		  result = getnameinfo (&remaddr.sa, addr_size,
 					remote_address,
 					sizeof (remote_address),
@@ -1009,9 +855,9 @@  print_sockets (unsigned short family, int tcp, struct buffer *buffer)
 					| (tcp ? 0 : NI_DGRAM));
 		  if (result)
 		    continue;
-		  
+
 		  user_from_uid (user, sizeof (user), uid);
-		  
+
 		  buffer_xml_printf (
 		      buffer,
 		      "<item>"
@@ -1039,49 +885,19 @@  print_sockets (unsigned short family, int tcp, struct buffer *buffer)
     }
 }
 
-/* Collect data about internet sockets and write it into READBUF.  */
+/* Collect data about internet sockets and write it into BUFFER.  */
 
-static LONGEST
-linux_xfer_osdata_isockets (gdb_byte *readbuf,
-			    ULONGEST offset, ULONGEST len)
+static void
+linux_xfer_osdata_isockets (struct buffer *buffer)
 {
-  static const char *buf;
-  static LONGEST len_avail = -1;
-  static struct buffer buffer;
-
-  if (offset == 0)
-    {
-      if (len_avail != -1 && len_avail != 0)
-	buffer_free (&buffer);
-      len_avail = 0;
-      buf = NULL;
-      buffer_init (&buffer);
-      buffer_grow_str (&buffer, "<osdata type=\"I sockets\">\n");
-
-      print_sockets (AF_INET, 1, &buffer);
-      print_sockets (AF_INET, 0, &buffer);
-      print_sockets (AF_INET6, 1, &buffer);
-      print_sockets (AF_INET6, 0, &buffer);
-
-      buffer_grow_str0 (&buffer, "</osdata>\n");
-      buf = buffer_finish (&buffer);
-      len_avail = strlen (buf);
-    }
-
-  if (offset >= len_avail)
-    {
-      /* Done.  Get rid of the buffer.  */
-      buffer_free (&buffer);
-      buf = NULL;
-      len_avail = 0;
-      return 0;
-    }
+  buffer_grow_str (buffer, "<osdata type=\"I sockets\">\n");
 
-  if (len > len_avail - offset)
-    len = len_avail - offset;
-  memcpy (readbuf, buf + offset, len);
+  print_sockets (AF_INET, 1, buffer);
+  print_sockets (AF_INET, 0, buffer);
+  print_sockets (AF_INET6, 1, buffer);
+  print_sockets (AF_INET6, 0, buffer);
 
-  return len;
+  buffer_grow_str0 (buffer, "</osdata>\n");
 }
 
 /* Converts the time SECONDS into textual form and copies it into a
@@ -1095,7 +911,7 @@  time_from_time_t (char *time, int maxlen, TIME_T seconds)
   else
     {
       time_t t = (time_t) seconds;
-      
+
       strncpy (time, ctime (&t), maxlen);
       time[maxlen - 1] = '\0';
     }
@@ -1108,7 +924,7 @@  static void
 group_from_gid (char *group, int maxlen, gid_t gid)
 {
   struct group *grentry = getgrgid (gid);
-  
+
   if (grentry)
     {
       strncpy (group, grentry->gr_name, maxlen);
@@ -1120,546 +936,444 @@  group_from_gid (char *group, int maxlen, gid_t gid)
 }
 
 /* Collect data about shared memory recorded in /proc and write it
-   into READBUF.  */
+   into BUFFER.  */
 
-static LONGEST
-linux_xfer_osdata_shm (gdb_byte *readbuf,
-		       ULONGEST offset, ULONGEST len)
+static void
+linux_xfer_osdata_shm (struct buffer *buffer)
 {
-  static const char *saved_buf;
-  static LONGEST len_avail = -1;
-  static struct buffer buffer;
+  buffer_grow_str (buffer, "<osdata type=\"shared memory\">\n");
 
-  if (offset == 0)
+  gdb_file_up fp = gdb_fopen_cloexec ("/proc/sysvipc/shm", "r");
+  if (fp)
     {
-      if (len_avail != -1 && len_avail != 0)
-	buffer_free (&buffer);
-      len_avail = 0;
-      saved_buf = NULL;
-      buffer_init (&buffer);
-      buffer_grow_str (&buffer, "<osdata type=\"shared memory\">\n");
-
-      gdb_file_up fp = gdb_fopen_cloexec ("/proc/sysvipc/shm", "r");
-      if (fp)
-	{
-	  char buf[8192];
+      char buf[8192];
 
-	  do
+      do
+	{
+	  if (fgets (buf, sizeof (buf), fp.get ()))
 	    {
-	      if (fgets (buf, sizeof (buf), fp.get ()))
+	      key_t key;
+	      uid_t uid, cuid;
+	      gid_t gid, cgid;
+	      PID_T cpid, lpid;
+	      int shmid, size, nattch;
+	      TIME_T atime, dtime, ctime;
+	      unsigned int perms;
+	      int items_read;
+
+	      items_read = sscanf (buf,
+				   "%d %d %o %d %lld %lld %d %u %u %u %u %lld %lld %lld",
+				   &key, &shmid, &perms, &size,
+				   &cpid, &lpid,
+				   &nattch,
+				   &uid, &gid, &cuid, &cgid,
+				   &atime, &dtime, &ctime);
+
+	      if (items_read == 14)
 		{
-		  key_t key;
-		  uid_t uid, cuid;
-		  gid_t gid, cgid;
-		  PID_T cpid, lpid;
-		  int shmid, size, nattch;
-		  TIME_T atime, dtime, ctime;
-		  unsigned int perms;
-		  int items_read;
-				  
-		  items_read = sscanf (buf,
-				       "%d %d %o %d %lld %lld %d %u %u %u %u %lld %lld %lld",
-				       &key, &shmid, &perms, &size,
-				       &cpid, &lpid,
-				       &nattch,
-				       &uid, &gid, &cuid, &cgid,
-				       &atime, &dtime, &ctime);
-
-		  if (items_read == 14)
-		    {
-		      char user[UT_NAMESIZE], group[UT_NAMESIZE];
-		      char cuser[UT_NAMESIZE], cgroup[UT_NAMESIZE];
-		      char ccmd[32], lcmd[32];
-		      char atime_str[32], dtime_str[32], ctime_str[32];
-		      
-		      user_from_uid (user, sizeof (user), uid);
-		      group_from_gid (group, sizeof (group), gid);
-		      user_from_uid (cuser, sizeof (cuser), cuid);
-		      group_from_gid (cgroup, sizeof (cgroup), cgid);
-		      
-		      command_from_pid (ccmd, sizeof (ccmd), cpid);
-		      command_from_pid (lcmd, sizeof (lcmd), lpid);
-		      
-		      time_from_time_t (atime_str, sizeof (atime_str), atime);
-		      time_from_time_t (dtime_str, sizeof (dtime_str), dtime);
-		      time_from_time_t (ctime_str, sizeof (ctime_str), ctime);
-		      
-		      buffer_xml_printf (
-		          &buffer,
-			  "<item>"
-			  "<column name=\"key\">%d</column>"
-			  "<column name=\"shmid\">%d</column>"
-			  "<column name=\"permissions\">%o</column>"
-			  "<column name=\"size\">%d</column>"
-			  "<column name=\"creator command\">%s</column>"
-			  "<column name=\"last op. command\">%s</column>"
-			  "<column name=\"num attached\">%d</column>"
-			  "<column name=\"user\">%s</column>"
-			  "<column name=\"group\">%s</column>"
-			  "<column name=\"creator user\">%s</column>"
-			  "<column name=\"creator group\">%s</column>"
-			  "<column name=\"last shmat() time\">%s</column>"
-			  "<column name=\"last shmdt() time\">%s</column>"
-			  "<column name=\"last shmctl() time\">%s</column>"
-			  "</item>",
-			  key,
-			  shmid,
-			  perms,
-			  size,
-			  ccmd,
-			  lcmd,
-			  nattch,
-			  user,
-			  group,
-			  cuser,
-			  cgroup,
-			  atime_str,
-			  dtime_str,
-			  ctime_str);
-		    }
+		  char user[UT_NAMESIZE], group[UT_NAMESIZE];
+		  char cuser[UT_NAMESIZE], cgroup[UT_NAMESIZE];
+		  char ccmd[32], lcmd[32];
+		  char atime_str[32], dtime_str[32], ctime_str[32];
+
+		  user_from_uid (user, sizeof (user), uid);
+		  group_from_gid (group, sizeof (group), gid);
+		  user_from_uid (cuser, sizeof (cuser), cuid);
+		  group_from_gid (cgroup, sizeof (cgroup), cgid);
+
+		  command_from_pid (ccmd, sizeof (ccmd), cpid);
+		  command_from_pid (lcmd, sizeof (lcmd), lpid);
+
+		  time_from_time_t (atime_str, sizeof (atime_str), atime);
+		  time_from_time_t (dtime_str, sizeof (dtime_str), dtime);
+		  time_from_time_t (ctime_str, sizeof (ctime_str), ctime);
+
+		  buffer_xml_printf
+		    (buffer,
+		     "<item>"
+		     "<column name=\"key\">%d</column>"
+		     "<column name=\"shmid\">%d</column>"
+		     "<column name=\"permissions\">%o</column>"
+		     "<column name=\"size\">%d</column>"
+		     "<column name=\"creator command\">%s</column>"
+		     "<column name=\"last op. command\">%s</column>"
+		     "<column name=\"num attached\">%d</column>"
+		     "<column name=\"user\">%s</column>"
+		     "<column name=\"group\">%s</column>"
+		     "<column name=\"creator user\">%s</column>"
+		     "<column name=\"creator group\">%s</column>"
+		     "<column name=\"last shmat() time\">%s</column>"
+		     "<column name=\"last shmdt() time\">%s</column>"
+		     "<column name=\"last shmctl() time\">%s</column>"
+		     "</item>",
+		     key,
+		     shmid,
+		     perms,
+		     size,
+		     ccmd,
+		     lcmd,
+		     nattch,
+		     user,
+		     group,
+		     cuser,
+		     cgroup,
+		     atime_str,
+		     dtime_str,
+		     ctime_str);
 		}
 	    }
-	  while (!feof (fp.get ()));
 	}
-      
-      buffer_grow_str0 (&buffer, "</osdata>\n");
-      saved_buf = buffer_finish (&buffer);
-      len_avail = strlen (saved_buf);
-    }
-
-  if (offset >= len_avail)
-    {
-      /* Done.  Get rid of the buffer.  */
-      buffer_free (&buffer);
-      saved_buf = NULL;
-      len_avail = 0;
-      return 0;
+      while (!feof (fp.get ()));
     }
 
-  if (len > len_avail - offset)
-    len = len_avail - offset;
-  memcpy (readbuf, saved_buf + offset, len);
-
-  return len;
+  buffer_grow_str0 (buffer, "</osdata>\n");
 }
 
 /* Collect data about semaphores recorded in /proc and write it
-   into READBUF.  */
+   into BUFFER.  */
 
-static LONGEST
-linux_xfer_osdata_sem (gdb_byte *readbuf,
-		       ULONGEST offset, ULONGEST len)
+static void
+linux_xfer_osdata_sem (struct buffer *buffer)
 {
-  static const char *saved_buf;
-  static LONGEST len_avail = -1;
-  static struct buffer buffer;
+  buffer_grow_str (buffer, "<osdata type=\"semaphores\">\n");
 
-  if (offset == 0)
+  gdb_file_up fp = gdb_fopen_cloexec ("/proc/sysvipc/sem", "r");
+  if (fp)
     {
-      if (len_avail != -1 && len_avail != 0)
-	buffer_free (&buffer);
-      len_avail = 0;
-      saved_buf = NULL;
-      buffer_init (&buffer);
-      buffer_grow_str (&buffer, "<osdata type=\"semaphores\">\n");
-
-      gdb_file_up fp = gdb_fopen_cloexec ("/proc/sysvipc/sem", "r");
-      if (fp)
+      char buf[8192];
+
+      do
 	{
-	  char buf[8192];
-	  
-	  do
+	  if (fgets (buf, sizeof (buf), fp.get ()))
 	    {
-	      if (fgets (buf, sizeof (buf), fp.get ()))
+	      key_t key;
+	      uid_t uid, cuid;
+	      gid_t gid, cgid;
+	      unsigned int perms, nsems;
+	      int semid;
+	      TIME_T otime, ctime;
+	      int items_read;
+
+	      items_read = sscanf (buf,
+				   "%d %d %o %u %d %d %d %d %lld %lld",
+				   &key, &semid, &perms, &nsems,
+				   &uid, &gid, &cuid, &cgid,
+				   &otime, &ctime);
+
+	      if (items_read == 10)
 		{
-		  key_t key;
-		  uid_t uid, cuid;
-		  gid_t gid, cgid;
-		  unsigned int perms, nsems;
-		  int semid;
-		  TIME_T otime, ctime;
-		  int items_read;
-		  
-		  items_read = sscanf (buf,
-				       "%d %d %o %u %d %d %d %d %lld %lld",
-				       &key, &semid, &perms, &nsems,
-				       &uid, &gid, &cuid, &cgid,
-				       &otime, &ctime);
-		  
-		  if (items_read == 10)
-		    {
-		      char user[UT_NAMESIZE], group[UT_NAMESIZE];
-		      char cuser[UT_NAMESIZE], cgroup[UT_NAMESIZE];
-		      char otime_str[32], ctime_str[32];
-		      
-		      user_from_uid (user, sizeof (user), uid);
-		      group_from_gid (group, sizeof (group), gid);
-		      user_from_uid (cuser, sizeof (cuser), cuid);
-		      group_from_gid (cgroup, sizeof (cgroup), cgid);
-		      
-		      time_from_time_t (otime_str, sizeof (otime_str), otime);
-		      time_from_time_t (ctime_str, sizeof (ctime_str), ctime);
-		      
-		      buffer_xml_printf (
-			  &buffer,
-			  "<item>"
-			  "<column name=\"key\">%d</column>"
-			  "<column name=\"semid\">%d</column>"
-			  "<column name=\"permissions\">%o</column>"
-			  "<column name=\"num semaphores\">%u</column>"
-			  "<column name=\"user\">%s</column>"
-			  "<column name=\"group\">%s</column>"
-			  "<column name=\"creator user\">%s</column>"
-			  "<column name=\"creator group\">%s</column>"
-			  "<column name=\"last semop() time\">%s</column>"
-			  "<column name=\"last semctl() time\">%s</column>"
-			  "</item>",
-			  key,
-			  semid,
-			  perms,
-			  nsems,
-			  user,
-			  group,
-			  cuser,
-			  cgroup,
-			  otime_str,
-			  ctime_str);
-		    }
+		  char user[UT_NAMESIZE], group[UT_NAMESIZE];
+		  char cuser[UT_NAMESIZE], cgroup[UT_NAMESIZE];
+		  char otime_str[32], ctime_str[32];
+
+		  user_from_uid (user, sizeof (user), uid);
+		  group_from_gid (group, sizeof (group), gid);
+		  user_from_uid (cuser, sizeof (cuser), cuid);
+		  group_from_gid (cgroup, sizeof (cgroup), cgid);
+
+		  time_from_time_t (otime_str, sizeof (otime_str), otime);
+		  time_from_time_t (ctime_str, sizeof (ctime_str), ctime);
+
+		  buffer_xml_printf 
+		    (buffer,
+		     "<item>"
+		     "<column name=\"key\">%d</column>"
+		     "<column name=\"semid\">%d</column>"
+		     "<column name=\"permissions\">%o</column>"
+		     "<column name=\"num semaphores\">%u</column>"
+		     "<column name=\"user\">%s</column>"
+		     "<column name=\"group\">%s</column>"
+		     "<column name=\"creator user\">%s</column>"
+		     "<column name=\"creator group\">%s</column>"
+		     "<column name=\"last semop() time\">%s</column>"
+		     "<column name=\"last semctl() time\">%s</column>"
+		     "</item>",
+		     key,
+		     semid,
+		     perms,
+		     nsems,
+		     user,
+		     group,
+		     cuser,
+		     cgroup,
+		     otime_str,
+		     ctime_str);
 		}
 	    }
-	  while (!feof (fp.get ()));
 	}
-
-      buffer_grow_str0 (&buffer, "</osdata>\n");
-      saved_buf = buffer_finish (&buffer);
-      len_avail = strlen (saved_buf);
-    }
-
-  if (offset >= len_avail)
-    {
-      /* Done.  Get rid of the buffer.  */
-      buffer_free (&buffer);
-      saved_buf = NULL;
-      len_avail = 0;
-      return 0;
+      while (!feof (fp.get ()));
     }
 
-  if (len > len_avail - offset)
-    len = len_avail - offset;
-  memcpy (readbuf, saved_buf + offset, len);
-
-  return len;
+  buffer_grow_str0 (buffer, "</osdata>\n");
 }
 
 /* Collect data about message queues recorded in /proc and write it
-   into READBUF.  */
+   into BUFFER.  */
 
-static LONGEST
-linux_xfer_osdata_msg (gdb_byte *readbuf,
-		       ULONGEST offset, ULONGEST len)
+static void
+linux_xfer_osdata_msg (struct buffer *buffer)
 {
-  static const char *saved_buf;
-  static LONGEST len_avail = -1;
-  static struct buffer buffer;
+  buffer_grow_str (buffer, "<osdata type=\"message queues\">\n");
 
-  if (offset == 0)
+  gdb_file_up fp = gdb_fopen_cloexec ("/proc/sysvipc/msg", "r");
+  if (fp)
     {
-      if (len_avail != -1 && len_avail != 0)
-	buffer_free (&buffer);
-      len_avail = 0;
-      saved_buf = NULL;
-      buffer_init (&buffer);
-      buffer_grow_str (&buffer, "<osdata type=\"message queues\">\n");
-      
-      gdb_file_up fp = gdb_fopen_cloexec ("/proc/sysvipc/msg", "r");
-      if (fp)
+      char buf[8192];
+
+      do
 	{
-	  char buf[8192];
-	  
-	  do
+	  if (fgets (buf, sizeof (buf), fp.get ()))
 	    {
-	      if (fgets (buf, sizeof (buf), fp.get ()))
+	      key_t key;
+	      PID_T lspid, lrpid;
+	      uid_t uid, cuid;
+	      gid_t gid, cgid;
+	      unsigned int perms, cbytes, qnum;
+	      int msqid;
+	      TIME_T stime, rtime, ctime;
+	      int items_read;
+
+	      items_read = sscanf (buf,
+				   "%d %d %o %u %u %lld %lld %d %d %d %d %lld %lld %lld",
+				   &key, &msqid, &perms, &cbytes, &qnum,
+				   &lspid, &lrpid, &uid, &gid, &cuid, &cgid,
+				   &stime, &rtime, &ctime);
+
+	      if (items_read == 14)
 		{
-		  key_t key;
-		  PID_T lspid, lrpid;
-		  uid_t uid, cuid;
-		  gid_t gid, cgid;
-		  unsigned int perms, cbytes, qnum;
-		  int msqid;
-		  TIME_T stime, rtime, ctime;
-		  int items_read;
-		  
-		  items_read = sscanf (buf,
-				       "%d %d %o %u %u %lld %lld %d %d %d %d %lld %lld %lld",
-				       &key, &msqid, &perms, &cbytes, &qnum,
-				       &lspid, &lrpid, &uid, &gid, &cuid, &cgid,
-				       &stime, &rtime, &ctime);
-		  
-		  if (items_read == 14)
-		    {
-		      char user[UT_NAMESIZE], group[UT_NAMESIZE];
-		      char cuser[UT_NAMESIZE], cgroup[UT_NAMESIZE];
-		      char lscmd[32], lrcmd[32];
-		      char stime_str[32], rtime_str[32], ctime_str[32];
-		      
-		      user_from_uid (user, sizeof (user), uid);
-		      group_from_gid (group, sizeof (group), gid);
-		      user_from_uid (cuser, sizeof (cuser), cuid);
-		      group_from_gid (cgroup, sizeof (cgroup), cgid);
-		      
-		      command_from_pid (lscmd, sizeof (lscmd), lspid);
-		      command_from_pid (lrcmd, sizeof (lrcmd), lrpid);
-		      
-		      time_from_time_t (stime_str, sizeof (stime_str), stime);
-		      time_from_time_t (rtime_str, sizeof (rtime_str), rtime);
-		      time_from_time_t (ctime_str, sizeof (ctime_str), ctime);
-		      
-		      buffer_xml_printf (
-			  &buffer,
-			  "<item>"
-			  "<column name=\"key\">%d</column>"
-			  "<column name=\"msqid\">%d</column>"
-			  "<column name=\"permissions\">%o</column>"
-			  "<column name=\"num used bytes\">%u</column>"
-			  "<column name=\"num messages\">%u</column>"
-			  "<column name=\"last msgsnd() command\">%s</column>"
-			  "<column name=\"last msgrcv() command\">%s</column>"
-			  "<column name=\"user\">%s</column>"
-			  "<column name=\"group\">%s</column>"
-			  "<column name=\"creator user\">%s</column>"
-			  "<column name=\"creator group\">%s</column>"
-			  "<column name=\"last msgsnd() time\">%s</column>"
-			  "<column name=\"last msgrcv() time\">%s</column>"
-			  "<column name=\"last msgctl() time\">%s</column>"
-			  "</item>",
-			  key,
-			  msqid,
-			  perms,
-			  cbytes,
-			  qnum,
-			  lscmd,
-			  lrcmd,
-			  user,
-			  group,
-			  cuser,
-			  cgroup,
-			  stime_str,
-			  rtime_str,
-			  ctime_str);
-		    }
+		  char user[UT_NAMESIZE], group[UT_NAMESIZE];
+		  char cuser[UT_NAMESIZE], cgroup[UT_NAMESIZE];
+		  char lscmd[32], lrcmd[32];
+		  char stime_str[32], rtime_str[32], ctime_str[32];
+
+		  user_from_uid (user, sizeof (user), uid);
+		  group_from_gid (group, sizeof (group), gid);
+		  user_from_uid (cuser, sizeof (cuser), cuid);
+		  group_from_gid (cgroup, sizeof (cgroup), cgid);
+
+		  command_from_pid (lscmd, sizeof (lscmd), lspid);
+		  command_from_pid (lrcmd, sizeof (lrcmd), lrpid);
+
+		  time_from_time_t (stime_str, sizeof (stime_str), stime);
+		  time_from_time_t (rtime_str, sizeof (rtime_str), rtime);
+		  time_from_time_t (ctime_str, sizeof (ctime_str), ctime);
+
+		  buffer_xml_printf 
+		    (buffer,
+		     "<item>"
+		     "<column name=\"key\">%d</column>"
+		     "<column name=\"msqid\">%d</column>"
+		     "<column name=\"permissions\">%o</column>"
+		     "<column name=\"num used bytes\">%u</column>"
+		     "<column name=\"num messages\">%u</column>"
+		     "<column name=\"last msgsnd() command\">%s</column>"
+		     "<column name=\"last msgrcv() command\">%s</column>"
+		     "<column name=\"user\">%s</column>"
+		     "<column name=\"group\">%s</column>"
+		     "<column name=\"creator user\">%s</column>"
+		     "<column name=\"creator group\">%s</column>"
+		     "<column name=\"last msgsnd() time\">%s</column>"
+		     "<column name=\"last msgrcv() time\">%s</column>"
+		     "<column name=\"last msgctl() time\">%s</column>"
+		     "</item>",
+		     key,
+		     msqid,
+		     perms,
+		     cbytes,
+		     qnum,
+		     lscmd,
+		     lrcmd,
+		     user,
+		     group,
+		     cuser,
+		     cgroup,
+		     stime_str,
+		     rtime_str,
+		     ctime_str);
 		}
 	    }
-	  while (!feof (fp.get ()));
 	}
-
-      buffer_grow_str0 (&buffer, "</osdata>\n");
-      saved_buf = buffer_finish (&buffer);
-      len_avail = strlen (saved_buf);
-    }
-
-  if (offset >= len_avail)
-    {
-      /* Done.  Get rid of the buffer.  */
-      buffer_free (&buffer);
-      saved_buf = NULL;
-      len_avail = 0;
-      return 0;
+      while (!feof (fp.get ()));
     }
 
-  if (len > len_avail - offset)
-    len = len_avail - offset;
-  memcpy (readbuf, saved_buf + offset, len);
-
-  return len;
+  buffer_grow_str0 (buffer, "</osdata>\n");
 }
 
 /* Collect data about loaded kernel modules and write it into
-   READBUF.  */
+   BUFFER.  */
 
-static LONGEST
-linux_xfer_osdata_modules (gdb_byte *readbuf,
-			   ULONGEST offset, ULONGEST len)
+static void
+linux_xfer_osdata_modules (struct buffer *buffer)
 {
-  static const char *saved_buf;
-  static LONGEST len_avail = -1;
-  static struct buffer buffer;
+  buffer_grow_str (buffer, "<osdata type=\"modules\">\n");
 
-  if (offset == 0)
+  gdb_file_up fp = gdb_fopen_cloexec ("/proc/modules", "r");
+  if (fp)
     {
-      if (len_avail != -1 && len_avail != 0)
-	buffer_free (&buffer);
-      len_avail = 0;
-      saved_buf = NULL;
-      buffer_init (&buffer);
-      buffer_grow_str (&buffer, "<osdata type=\"modules\">\n");
-
-      gdb_file_up fp = gdb_fopen_cloexec ("/proc/modules", "r");
-      if (fp)
+      char buf[8192];
+
+      do
 	{
-	  char buf[8192];
-	  
-	  do
+	  if (fgets (buf, sizeof (buf), fp.get ()))
 	    {
-	      if (fgets (buf, sizeof (buf), fp.get ()))
-		{
-		  char *name, *dependencies, *status, *tmp;
-		  unsigned int size;
-		  unsigned long long address;
-		  int uses;
+	      char *name, *dependencies, *status, *tmp;
+	      unsigned int size;
+	      unsigned long long address;
+	      int uses;
 
-		  name = strtok (buf, " ");
-		  if (name == NULL)
-		    continue;
+	      name = strtok (buf, " ");
+	      if (name == NULL)
+		continue;
 
-		  tmp = strtok (NULL, " ");
-		  if (tmp == NULL)
-		    continue;
-		  if (sscanf (tmp, "%u", &size) != 1)
-		    continue;
+	      tmp = strtok (NULL, " ");
+	      if (tmp == NULL)
+		continue;
+	      if (sscanf (tmp, "%u", &size) != 1)
+		continue;
 
-		  tmp = strtok (NULL, " ");
-		  if (tmp == NULL)
-		    continue;
-		  if (sscanf (tmp, "%d", &uses) != 1)
-		    continue;
+	      tmp = strtok (NULL, " ");
+	      if (tmp == NULL)
+		continue;
+	      if (sscanf (tmp, "%d", &uses) != 1)
+		continue;
 
-		  dependencies = strtok (NULL, " ");
-		  if (dependencies == NULL)
-		    continue;
+	      dependencies = strtok (NULL, " ");
+	      if (dependencies == NULL)
+		continue;
 
-		  status = strtok (NULL, " ");
-		  if (status == NULL)
-		    continue;
+	      status = strtok (NULL, " ");
+	      if (status == NULL)
+		continue;
 
-		  tmp = strtok (NULL, "\n");
-		  if (tmp == NULL)
-		    continue;
-		  if (sscanf (tmp, "%llx", &address) != 1)
-		    continue;
+	      tmp = strtok (NULL, "\n");
+	      if (tmp == NULL)
+		continue;
+	      if (sscanf (tmp, "%llx", &address) != 1)
+		continue;
 
-		  buffer_xml_printf (
-			&buffer,
-			"<item>"
-			"<column name=\"name\">%s</column>"
-			"<column name=\"size\">%u</column>"
-			"<column name=\"num uses\">%d</column>"
-			"<column name=\"dependencies\">%s</column>"
-			"<column name=\"status\">%s</column>"
-			"<column name=\"address\">%llx</column>"
-			"</item>",
-			name,
-			size,
-			uses,
-			dependencies,
-			status,
-			address);
-		}
+	      buffer_xml_printf (buffer,
+				 "<item>"
+				 "<column name=\"name\">%s</column>"
+				 "<column name=\"size\">%u</column>"
+				 "<column name=\"num uses\">%d</column>"
+				 "<column name=\"dependencies\">%s</column>"
+				 "<column name=\"status\">%s</column>"
+				 "<column name=\"address\">%llx</column>"
+				 "</item>",
+				 name,
+				 size,
+				 uses,
+				 dependencies,
+				 status,
+				 address);
 	    }
-	  while (!feof (fp.get ()));
 	}
-
-      buffer_grow_str0 (&buffer, "</osdata>\n");
-      saved_buf = buffer_finish (&buffer);
-      len_avail = strlen (saved_buf);
-    }
-
-  if (offset >= len_avail)
-    {
-      /* Done.  Get rid of the buffer.  */
-      buffer_free (&buffer);
-      saved_buf = NULL;
-      len_avail = 0;
-      return 0;
+      while (!feof (fp.get ()));
     }
 
-  if (len > len_avail - offset)
-    len = len_avail - offset;
-  memcpy (readbuf, saved_buf + offset, len);
-
-  return len;
+  buffer_grow_str0 (buffer, "</osdata>\n");
 }
 
+static void
+linux_xfer_osdata_info_os_types (struct buffer *buffer);
+
 struct osdata_type {
   const char *type;
   const char *title;
   const char *description;
-  LONGEST (*getter) (gdb_byte *readbuf, ULONGEST offset, ULONGEST len);
+  void (*take_snapshot) (struct buffer *buffer);
+  LONGEST len_avail;
+  struct buffer buffer;
 } osdata_table[] = {
+  { "types", "Types", "Listing of info os types you can list",
+    linux_xfer_osdata_info_os_types, -1 },
   { "cpus", "CPUs", "Listing of all cpus/cores on the system",
-    linux_xfer_osdata_cpus },
+    linux_xfer_osdata_cpus, -1 },
   { "files", "File descriptors", "Listing of all file descriptors",
-    linux_xfer_osdata_fds },
+    linux_xfer_osdata_fds, -1 },
   { "modules", "Kernel modules", "Listing of all loaded kernel modules",
-    linux_xfer_osdata_modules },
+    linux_xfer_osdata_modules, -1 },
   { "msg", "Message queues", "Listing of all message queues",
-    linux_xfer_osdata_msg },
+    linux_xfer_osdata_msg, -1 },
   { "processes", "Processes", "Listing of all processes",
-    linux_xfer_osdata_processes },
+    linux_xfer_osdata_processes, -1 },
   { "procgroups", "Process groups", "Listing of all process groups",
-    linux_xfer_osdata_processgroups },
+    linux_xfer_osdata_processgroups, -1 },
   { "semaphores", "Semaphores", "Listing of all semaphores",
-    linux_xfer_osdata_sem },
+    linux_xfer_osdata_sem, -1 },
   { "shm", "Shared-memory regions", "Listing of all shared-memory regions",
-    linux_xfer_osdata_shm },
+    linux_xfer_osdata_shm, -1 },
   { "sockets", "Sockets", "Listing of all internet-domain sockets",
-    linux_xfer_osdata_isockets },
+    linux_xfer_osdata_isockets, -1 },
   { "threads", "Threads", "Listing of all threads",
-    linux_xfer_osdata_threads },
+  linux_xfer_osdata_threads, -1 },
   { NULL, NULL, NULL }
 };
 
-LONGEST
-linux_common_xfer_osdata (const char *annex, gdb_byte *readbuf,
-			  ULONGEST offset, ULONGEST len)
+/* Collect data about all types info os can show in BUFFER.  */
+
+static void
+linux_xfer_osdata_info_os_types (struct buffer *buffer)
 {
-  if (!annex || *annex == '\0')
-    {
-      static const char *buf;
-      static LONGEST len_avail = -1;
-      static struct buffer buffer;
+  buffer_grow_str (buffer, "<osdata type=\"types\">\n");
+
+  /* Start the below loop at 1, as we do not want to list ourselves.  */
+  for (int i = 1; osdata_table[i].type; ++i)
+    buffer_xml_printf (buffer,
+		       "<item>"
+		       "<column name=\"Type\">%s</column>"
+		       "<column name=\"Description\">%s</column>"
+		       "<column name=\"Title\">%s</column>"
+		       "</item>",
+		       osdata_table[i].type,
+		       osdata_table[i].description,
+		       osdata_table[i].title);
+
+  buffer_grow_str0 (buffer, "</osdata>\n");
+}
 
-      if (offset == 0)
-	{
-	  int i;
 
-	  if (len_avail != -1 && len_avail != 0)
-	    buffer_free (&buffer);
-	  len_avail = 0;
-	  buf = NULL;
-	  buffer_init (&buffer);
-	  buffer_grow_str (&buffer, "<osdata type=\"types\">\n");
-
-	  for (i = 0; osdata_table[i].type; ++i)
-	    buffer_xml_printf (
-			       &buffer,
-			       "<item>"
-			       "<column name=\"Type\">%s</column>"
-			       "<column name=\"Description\">%s</column>"
-			       "<column name=\"Title\">%s</column>"
-			       "</item>",
-			       osdata_table[i].type,
-			       osdata_table[i].description,
-			       osdata_table[i].title);
-
-	  buffer_grow_str0 (&buffer, "</osdata>\n");
-	  buf = buffer_finish (&buffer);
-	  len_avail = strlen (buf);
-	}
+/*  Copies up to LEN bytes in READBUF from offset OFFSET in OSD->BUFFER.
+    If OFFSET is zero, first calls OSD->TAKE_SNAPSHOT.  */
 
-      if (offset >= len_avail)
-	{
-	  /* Done.  Get rid of the buffer.  */
-	  buffer_free (&buffer);
-	  buf = NULL;
-	  len_avail = 0;
-	  return 0;
-	}
+static LONGEST
+common_getter (struct osdata_type *osd,
+	       gdb_byte *readbuf, ULONGEST offset, ULONGEST len)
+{
+  gdb_assert (readbuf);
 
-      if (len > len_avail - offset)
-	len = len_avail - offset;
-      memcpy (readbuf, buf + offset, len);
+  if (offset == 0)
+    {
+      if (osd->len_avail != -1 && osd->len_avail != 0)
+	buffer_free (&osd->buffer);
+      osd->len_avail = 0;
+      buffer_init (&osd->buffer);
+      (osd->take_snapshot) (&osd->buffer);
+      osd->len_avail = strlen (osd->buffer.buffer);
+    }
+  if (offset >= osd->len_avail)
+    {
+      /* Done.  Get rid of the buffer.  */
+      buffer_free (&osd->buffer);
+      osd->len_avail = 0;
+      return 0;
+    }
+  if (len > osd->len_avail - offset)
+    len = osd->len_avail - offset;
+  memcpy (readbuf, osd->buffer.buffer + offset, len);
+
+  return len;
+
+}
 
-      return len;
+LONGEST
+linux_common_xfer_osdata (const char *annex, gdb_byte *readbuf,
+			  ULONGEST offset, ULONGEST len)
+{
+  if (!annex || *annex == '\0')
+    {
+      return common_getter (&osdata_table[0],
+			    readbuf, offset, len);
     }
   else
     {
@@ -1668,11 +1382,8 @@  linux_common_xfer_osdata (const char *annex, gdb_byte *readbuf,
       for (i = 0; osdata_table[i].type; ++i)
 	{
 	  if (strcmp (annex, osdata_table[i].type) == 0)
-	    {
-	      gdb_assert (readbuf);
-	      
-	      return (osdata_table[i].getter) (readbuf, offset, len);
-	    }
+	    return common_getter (&osdata_table[i],
+				  readbuf, offset, len);
 	}
 
       return 0;