public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH]  Add extra 'info os' information types for Linux
@ 2011-10-12 18:29 Kwok Cheung Yeung
  2011-10-21 23:38 ` Tom Tromey
  0 siblings, 1 reply; 19+ messages in thread
From: Kwok Cheung Yeung @ 2011-10-12 18:29 UTC (permalink / raw)
  To: gdb-patches

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

This patch greatly extends the OS information types available to the user using 
the 'info os' command in Linux. Since the OS info backend is now unified, this 
information can be obtained from both local and remote (gdbserver) targets.

The new information types are:

procgroups - Process groups
files - File descriptors
sockets - Internet-domain sockets
shm - Shared memory regions
semaphores - Semaphores
msg - Message queues
modules - Loaded kernel modules

They all work in the same manner as the existing 'processes' and 'threads' 
information types - by pulling the information from the /proc filesystem and 
formatting it into a table.

This facility is tested using a program which creates file descriptors, sockets 
and IPC structures with specific port numbers/targets, and the test script then 
looks for these in the output of 'info os'.

The documentation is also updated regarding these information types.

Kwok Cheung Yeung


ChangeLog:

gdb/
	* common/linux-osdata.c (compare_processes): Auxiliary function for
	linux_xfer_osdata_processgroups.
	(linux_xfer_osdata_processgroups): New function to
	look up process groups.
	(linux_xfer_osdata_fds): New function to look up file descriptors.
	(format_socket_state, print_sockets): Auxiliary functions for
	linux_xfer_osdata_isockets.
	(union socket_addr): New union used to avoid strict-aliasing problems.
	(linux_xfer_osdata_isockets): New function to look up internet sockets.
	(time_from_int, group_from_gid): New convenience functions for
	converting between data types.	
	(linux_xfer_osdata_shm): New function to look up shared memory for IPC.
	(linux_xfer_osdata_sem): New function to look up semaphores for IPC.
	(linux_xfer_osdata_msg): New function to look up message queues for
	IPC.
	(linux_xfer_osdata_modules): New function to look up loaded kernel
	modules.
	(osdata_table): Add new entries.

gdb/doc/
	* gdb.texinfo (Operating System Auxilliary Information): Document new
	'info os' commands.

gdb/testsuite/
	* gdb.base/info-os.exp: New test to exercise the 'info os *' commands.
	* gdb.base/info-os.c: New test program used by info-os.exp.


[-- Attachment #2: os-awareness.diff --]
[-- Type: text/plain, Size: 39244 bytes --]

Index: gdb/common/linux-osdata.c
===================================================================
RCS file: /cvs/src/src/gdb/common/linux-osdata.c,v
retrieving revision 1.2
diff -u -p -r1.2 linux-osdata.c
--- gdb/common/linux-osdata.c	26 Aug 2011 18:58:04 -0000	1.2
+++ gdb/common/linux-osdata.c	12 Oct 2011 18:19:25 -0000
@@ -396,6 +396,146 @@ linux_xfer_osdata_processes (gdb_byte *r
   return len;
 }
 
+static int
+compare_processes (const void *process1, const void *process2)
+{
+  pid_t pid1 = *((pid_t *) process1);
+  pid_t pid2 = *((pid_t *) process2);
+  pid_t pgid1 = *((pid_t *) process1 + 1);
+  pid_t pgid2 = *((pid_t *) process2 + 1);
+
+  /* Sort by PGID.  */
+  if (pgid1 < pgid2)
+    return -1;
+  else if (pgid1 > pgid2)
+    return 1;
+  else
+    {
+      /* Process group leaders always come first, else sort by PID.  */
+      if (pid1 == pgid1)
+	return -1;
+      else if (pid2 == pgid2)
+	return 1;
+      else if (pid1 < pid2)
+	return -1;
+      else if (pid1 > pid2)
+	return 1;
+      else
+	return 0;
+    }
+}
+
+static LONGEST
+linux_xfer_osdata_processgroups (gdb_byte *readbuf,
+				 ULONGEST offset, LONGEST len)
+{
+  /* 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;
+
+  if (offset == 0)
+    {
+      DIR *dirp;
+
+      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");
+
+      dirp = opendir ("/proc");
+      if (dirp)
+	{
+	  struct dirent *dp;
+	  const size_t list_block_size = 512;
+	  pid_t *process_list = (pid_t *) xmalloc (list_block_size * 2 * sizeof (pid_t));
+	  size_t process_count = 0;
+	  size_t i;
+
+	  /* 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) > sizeof ("4294967295") - 1)
+		continue;
+
+	      sscanf (dp->d_name, "%d", &pid);
+	      pgid = getpgid (pid);
+
+	      if (pgid > 0)
+		{
+		  process_list[2 * process_count] = pid;
+		  process_list[2 * process_count + 1] = pgid;
+		  ++process_count;
+
+		  /* Increase the size of the list if necessary.  */
+		  if (process_count % list_block_size == 0)
+		    process_list = (pid_t *) xrealloc (
+			process_list,
+			(process_count + list_block_size) * 2 * sizeof (pid_t));
+		}
+	    }
+
+	  closedir (dirp);
+
+	  /* Sort the process list.  */
+	  qsort (process_list, process_count, 2 * sizeof (pid_t), compare_processes);
+
+	  for (i = 0; i < process_count; ++i)
+	    {
+	      pid_t pid = process_list[2 * i];
+	      pid_t pgid = process_list[2 * i + 1];
+	      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\">%d</column>"
+		  "<column name=\"leader command\">%s</column>"
+		  "<column name=\"pid\">%d</column>"
+		  "<column name=\"command line\">%s</column>"
+		  "</item>",
+		  pgid,
+		  leader_command,
+		  pid,
+		  command_line ? command_line : "");
+
+	      xfree (command_line);
+	    }
+
+	  xfree (process_list);
+	}   
+      
+
+      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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
 static LONGEST
 linux_xfer_osdata_threads (gdb_byte *readbuf,
 			   ULONGEST offset, LONGEST len)
@@ -507,13 +647,840 @@ linux_xfer_osdata_threads (gdb_byte *rea
   return len;
 }
 
+static LONGEST
+linux_xfer_osdata_fds (gdb_byte *readbuf,
+		       ULONGEST offset, LONGEST len)
+{
+  /* 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;
+
+  if (offset == 0)
+    {
+      DIR *dirp;
+
+      if (len_avail != -1 && len_avail != 0)
+	buffer_free (&buffer);
+      len_avail = 0;
+      buf = NULL;
+      buffer_init (&buffer);
+      buffer_grow_str (&buffer, "<osdata type=\"files\">\n");
+
+      dirp = opendir ("/proc");
+      if (dirp)
+	{
+	  struct dirent *dp;
+
+	  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;
+
+	      sprintf (procentry, "/proc/%s", dp->d_name);
+	      if (stat (procentry, &statbuf) == 0
+		  && S_ISDIR (statbuf.st_mode))
+		{
+		  char *pathname;
+		  DIR *dirp2;
+		  pid_t pid;
+		  char command[32];
+
+		  pid = atoi (dp->d_name);
+		  command_from_pid (command, sizeof (command), pid);
+
+		  pathname = xstrprintf ("/proc/%s/fd", dp->d_name);
+		  dirp2 = opendir (pathname);
+
+		  if (dirp2)
+		    {
+		      struct dirent *dp2;
+
+		      while ((dp2 = readdir (dirp2)) != NULL)
+			{
+#if 0
+			  struct stat statbuf;
+			  stat (dp2->d_name, &statbuf);
+#endif
+			  char *fdname;
+			  char buf[1000];
+			  ssize_t rslt;
+
+			  if (!isdigit (dp2->d_name[0]))
+			    continue;
+
+			  fdname = xstrprintf ("%s/%s", pathname, dp2->d_name);
+			  rslt = readlink (fdname, buf, 1000);
+			  if (rslt >= 0)
+			    buf[rslt] = '\0';
+
+#if 0
+			  printf_unfiltered ("readlink returned %d, %s\n", rslt, buf);
+#endif
+			  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);
+		    }
+
+		  xfree (pathname);
+		}
+	    }
+
+	  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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
+static const char *
+format_socket_state (unsigned char state)
+{
+  /* Copied from include/net/tcp_states.h in the Linux kernel sources.  */
+  enum {
+    TCP_ESTABLISHED = 1,
+    TCP_SYN_SENT,
+    TCP_SYN_RECV,
+    TCP_FIN_WAIT1,
+    TCP_FIN_WAIT2,
+    TCP_TIME_WAIT,
+    TCP_CLOSE,
+    TCP_CLOSE_WAIT,
+    TCP_LAST_ACK,
+    TCP_LISTEN,
+    TCP_CLOSING
+  };
+
+  switch (state)
+    {
+    case TCP_ESTABLISHED:
+      return "ESTABLISHED";
+    case TCP_SYN_SENT:
+      return "SYN_SENT";
+    case TCP_SYN_RECV:
+      return "SYN_RECV";
+    case TCP_FIN_WAIT1:
+      return "FIN_WAIT1";
+    case TCP_FIN_WAIT2:
+      return "FIN_WAIT2";
+    case TCP_TIME_WAIT:
+      return "TIME_WAIT";
+    case TCP_CLOSE:
+      return "CLOSE";
+    case TCP_CLOSE_WAIT:
+      return "CLOSE_WAIT";
+    case TCP_LAST_ACK:
+      return "LAST_ACK";
+    case TCP_LISTEN:
+      return "LISTEN";
+    case TCP_CLOSING:
+      return "CLOSING";
+    default:
+      return "(unknown)";
+    }
+}
+
+union socket_addr
+  {
+    struct sockaddr sa;
+    struct sockaddr_in sin;
+    struct sockaddr_in6 sin6;
+  };
+
+static void
+print_sockets (unsigned short family, int tcp, struct buffer *buffer)
+{
+  const char *proc_file;
+  FILE *fp;
+
+  if (family == AF_INET)
+    proc_file = tcp ? "/proc/net/tcp" : "/proc/net/udp";
+  else if (family == AF_INET6)
+    proc_file = tcp ? "/proc/net/tcp6" : "/proc/net/udp6";
+  else
+    return;
+
+  fp = fopen (proc_file, "r");
+  if (fp)
+    {
+      char buf[8192];
+
+      do
+	{
+	  if (fgets (buf, sizeof (buf), fp))
+	    {
+	      uid_t uid;
+	      unsigned long tlen, inode;
+	      int sl, timeout;
+	      unsigned int local_port, remote_port, state, txq, rxq, trun, retn;
+	      char local_address[NI_MAXHOST], remote_address[NI_MAXHOST], extra[512];
+	      int result;
+
+	      result = sscanf (buf,
+			       "%d: %33[0-9A-F]:%X %33[0-9A-F]:%X %X %X:%X %X:%lX %X %d %d %lu %512s\n",
+			       &sl,
+			       local_address, &local_port,
+			       remote_address, &remote_port,
+			       &state,
+			       &txq, &rxq,
+			       &trun, &tlen,
+			       &retn,
+			       &uid,
+			       &timeout,
+			       &inode,
+			       extra);
+	      
+	      if (result == 15)
+		{
+		  union socket_addr locaddr, remaddr;
+		  size_t addr_size;
+		  char user[UT_NAMESIZE];
+		  char local_service[NI_MAXSERV], remote_service[NI_MAXSERV];
+
+		  if (family == AF_INET)
+		    {
+		      sscanf (local_address, "%X", &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);
+
+		      addr_size = sizeof (struct sockaddr_in);
+		    }
+		  else
+		    {
+		      sscanf (local_address, "%8X%8X%8X%8X",
+			      locaddr.sin6.sin6_addr.s6_addr32,
+			      locaddr.sin6.sin6_addr.s6_addr32 + 1,
+			      locaddr.sin6.sin6_addr.s6_addr32 + 2,
+			      locaddr.sin6.sin6_addr.s6_addr32 + 3);
+		      sscanf (remote_address, "%8X%8X%8X%8X",
+			      remaddr.sin6.sin6_addr.s6_addr32,
+			      remaddr.sin6.sin6_addr.s6_addr32 + 1,
+			      remaddr.sin6.sin6_addr.s6_addr32 + 2,
+			      remaddr.sin6.sin6_addr.s6_addr32 + 3);
+
+		      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;
+		      remaddr.sin6.sin6_scope_id = 0;
+
+		      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),
+					NI_NUMERICHOST | NI_NUMERICSERV
+					| (tcp ? 0 : NI_DGRAM));
+		  if (result)
+		    continue;
+		  
+		  result = getnameinfo (&remaddr.sa, addr_size,
+					remote_address, sizeof (remote_address),
+					remote_service, sizeof (remote_service),
+					NI_NUMERICHOST | NI_NUMERICSERV
+					| (tcp ? 0 : NI_DGRAM));
+		  if (result)
+		    continue;
+		  
+		  user_from_uid (user, sizeof (user), uid);
+		  
+		  buffer_xml_printf (
+		      buffer,
+		      "<item>"
+		      "<column name=\"local address\">%s</column>"
+		      "<column name=\"local port\">%s</column>"
+		      "<column name=\"remote address\">%s</column>"
+		      "<column name=\"remote port\">%s</column>"
+		      "<column name=\"state\">%s</column>"
+		      "<column name=\"user\">%s</column>"
+		      "<column name=\"family\">%s</column>" 
+		      "<column name=\"protocol\">%s</column>"
+		      "</item>",
+		      local_address,
+		      local_service,
+		      remote_address,
+		      remote_service,
+		      format_socket_state (state),
+		      user,
+		      (family == AF_INET) ? "INET" : "INET6",
+		      tcp ? "STREAM" : "DGRAM");
+		}
+	    }
+	}
+      while (!feof (fp));
+
+      fclose (fp);
+    }
+}
+
+static LONGEST
+linux_xfer_osdata_isockets (gdb_byte *readbuf,
+			    ULONGEST offset, LONGEST len)
+{
+  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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
+static void
+time_from_int (char *time, int maxlen, int seconds)
+{
+  if (!seconds)
+    time[0] = '\0';
+  else
+    {
+      time_t t = (time_t) seconds;
+      
+      strncpy (time, ctime (&t), maxlen);
+      time[maxlen - 1] = '\0';
+    }
+}
+
+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);
+      group[maxlen - 1] = '\0'; /* Ensure that the group name is null-terminated.  */
+    }
+  else
+    group[0] = '\0';
+}
+
+static LONGEST
+linux_xfer_osdata_shm (gdb_byte *readbuf,
+		       ULONGEST offset, LONGEST len)
+{
+  static const char *buf;
+  static LONGEST len_avail = -1;
+  static struct buffer buffer;
+
+  if (offset == 0)
+    {
+      FILE *fp;
+
+      if (len_avail != -1 && len_avail != 0)
+	buffer_free (&buffer);
+      len_avail = 0;
+      buf = NULL;
+      buffer_init (&buffer);
+      buffer_grow_str (&buffer, "<osdata type=\"shared memory\">\n");
+
+      fp = fopen ("/proc/sysvipc/shm", "r");
+      if (fp)
+	{
+	  char buf[8192];
+
+	  do
+	    {
+	      if (fgets (buf, sizeof (buf), fp))
+		{
+		  key_t key;
+		  uid_t uid, cuid;
+		  gid_t gid, cgid;
+		  pid_t cpid, lpid;
+		  int shmid, size, nattch, atime, dtime, ctime;
+		  unsigned int perms;
+		  int items_read;
+				  
+		  items_read = sscanf (buf,
+				       "%d %d %o %d %d %d %d %u %u %u %u %d %d %d",
+				       &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_int (atime_str, sizeof (atime_str), atime);
+		      time_from_int (dtime_str, sizeof (dtime_str), dtime);
+		      time_from_int (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));
+
+	  fclose (fp);
+	}
+      
+      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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
+static LONGEST
+linux_xfer_osdata_sem (gdb_byte *readbuf,
+		       ULONGEST offset, LONGEST len)
+{
+  static const char *buf;
+  static LONGEST len_avail = -1;
+  static struct buffer buffer;
+
+  if (offset == 0)
+    {
+      FILE *fp;
+      
+      if (len_avail != -1 && len_avail != 0)
+	buffer_free (&buffer);
+      len_avail = 0;
+      buf = NULL;
+      buffer_init (&buffer);
+      buffer_grow_str (&buffer, "<osdata type=\"semaphores\">\n");
+
+      fp = fopen ("/proc/sysvipc/sem", "r");
+      if (fp)
+	{
+	  char buf[8192];
+	  
+	  do
+	    {
+	      if (fgets (buf, sizeof (buf), fp))
+		{
+		  key_t key;
+		  uid_t uid, cuid;
+		  gid_t gid, cgid;
+		  unsigned int perms, nsems;
+		  int semid, otime, ctime;
+		  int items_read;
+		  
+		  items_read = sscanf (buf,
+				       "%d %d %o %u %d %d %d %d %d %d",
+				       &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_int (otime_str, sizeof (otime_str), otime);
+		      time_from_int (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));
+
+	  fclose (fp);
+	}
+
+      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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
+static LONGEST
+linux_xfer_osdata_msg (gdb_byte *readbuf,
+		       ULONGEST offset, LONGEST len)
+{
+  static const char *buf;
+  static LONGEST len_avail = -1;
+  static struct buffer buffer;
+
+  if (offset == 0)
+    {
+      FILE *fp;
+      
+      if (len_avail != -1 && len_avail != 0)
+	buffer_free (&buffer);
+      len_avail = 0;
+      buf = NULL;
+      buffer_init (&buffer);
+      buffer_grow_str (&buffer, "<osdata type=\"message queues\">\n");
+      
+      fp = fopen ("/proc/sysvipc/msg", "r");
+      if (fp)
+	{
+	  char buf[8192];
+	  
+	  do
+	    {
+	      if (fgets (buf, sizeof (buf), fp))
+		{
+		  key_t key;
+		  pid_t lspid, lrpid;
+		  uid_t uid, cuid;
+		  gid_t gid, cgid;
+		  unsigned int perms, cbytes, qnum;
+		  int msqid, stime, rtime, ctime;
+		  int items_read;
+		  
+		  items_read = sscanf (buf,
+				       "%d %d %o %u %u %d %d %d %d %d %d %d %d %d",
+				       &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_int (stime_str, sizeof (stime_str), stime);
+		      time_from_int (rtime_str, sizeof (rtime_str), rtime);
+		      time_from_int (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));
+
+	  fclose (fp);
+	}
+
+      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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
+static LONGEST
+linux_xfer_osdata_modules (gdb_byte *readbuf,
+			   ULONGEST offset, LONGEST len)
+{
+  static const char *buf;
+  static LONGEST len_avail = -1;
+  static struct buffer buffer;
+
+  if (offset == 0)
+    {
+      FILE *fp;
+
+      if (len_avail != -1 && len_avail != 0)
+	buffer_free (&buffer);
+      len_avail = 0;
+      buf = NULL;
+      buffer_init (&buffer);
+      buffer_grow_str (&buffer, "<osdata type=\"modules\">\n");
+
+      fp = fopen ("/proc/modules", "r");
+      if (fp)
+	{
+	  char buf[8192];
+	  
+	  do
+	    {
+	      if (fgets (buf, sizeof (buf), fp))
+		{
+		  char name[64], dependencies[256], status[16];
+		  unsigned int size;
+		  unsigned long long address;
+		  int uses;
+		  int items_read;
+		  
+		  items_read = sscanf (buf,
+				       "%64s %d %d %256s %16s 0x%llx",
+				       name, &size, &uses,
+				       dependencies, status, &address);
+
+		  if (items_read == 6)
+		    {
+		      char address_str[20];
+		      
+		      snprintf (address_str, 20, "%llx", 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\">%s</column>"
+			  "</item>",
+			  name,
+			  size,
+			  uses,
+			  dependencies,
+			  status,
+			  address_str);
+		    }
+		}
+	    }
+	  while (!feof (fp));
+
+	  fclose (fp);
+	}
+
+      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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
 struct osdata_type {
   char *type;
   char *description;
   LONGEST (*getter) (gdb_byte *readbuf, ULONGEST offset, LONGEST len);
 } osdata_table[] = {
   { "processes", "Listing of all processes", linux_xfer_osdata_processes },
+  { "procgroups", "Listing of all process groups", linux_xfer_osdata_processgroups },
   { "threads", "Listing of all threads", linux_xfer_osdata_threads },
+  { "files", "Listing of all file descriptors", linux_xfer_osdata_fds },
+  { "sockets", "Listing of all internet-domain sockets", linux_xfer_osdata_isockets },
+  { "shm", "Listing of all shared-memory regions", linux_xfer_osdata_shm },
+  { "semaphores", "Listing of all semaphores", linux_xfer_osdata_sem },
+  { "msg", "Listing of all message queues", linux_xfer_osdata_msg },
+  { "modules", "Listing of all loaded kernel modules", linux_xfer_osdata_modules },
   { NULL, NULL, NULL }
 };
 
Index: gdb/doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.877
diff -u -p -r1.877 gdb.texinfo
--- gdb/doc/gdb.texinfo	12 Oct 2011 15:55:04 -0000	1.877
+++ gdb/doc/gdb.texinfo	12 Oct 2011 18:19:31 -0000
@@ -8916,22 +8916,105 @@ an unrecognized tag.
 @end table
 
 On some targets, @value{GDBN} can access operating-system-specific information
-and display it to user, without interpretation.  For remote targets,
-this functionality depends on the remote stub's support of the 
+and display it to user.  The types of information available will differ
+depending on the type of operating system running on the target.  The
+mechanism used to fetch the data is described in
+@ref{Operating System Information}.  For remote targets, this
+functionality depends on the remote stub's support of the 
 @samp{qXfer:osdata:read} packet, see @ref{qXfer osdata read}.
 
 @table @code
 @kindex info os
-@item info os
-List the types of OS information available for the target.  If the
-target does not return a list of possible types, this command will
-report an error.
+@item info os INFOTYPE
+
+Display OS information of the requested type.
+
+On @sc{gnu}/Linux, the following values of INFOTYPE are valid:
 
+@anchor{linux info os infotypes}
+@table @code
 @kindex info os processes
-@item info os processes
+@item processes
 Display the list of processes on the target.  For each process,
-@value{GDBN} prints the process identifier, the name of the user, and
-the command corresponding to the process.
+@value{GDBN} prints the process identifier, the name of the user, the
+command corresponding to the process, and the list of processor cores
+that the process is currently running on.
+
+@kindex info os procgroups
+@item procgroups
+Display the list of process groups on the target.  For each process,
+@value{GDBN} prints the identifier of the process group that it belongs
+to, the command corresponding to the process group leader, the process
+identifier, and the command line of the process.  The list is sorted
+first by the process group identifier, then by the process identifier,
+so that processes belonging to the same process group are grouped together
+and the process group leader is listed first.
+
+@kindex info os threads
+@item threads
+Display the list of threads running on the target.  For each thread,
+@value{GDBN} prints the identifier of the process that the thread
+belongs to, the command of the process, the thread identifier, and the
+processor core that it is currently running on.  The main thread of a
+process is not listed.
+
+@kindex info os files
+@item files
+Display the list of open file descriptors on the target.  For each
+file descriptor, @value{GDBN} prints the identifier of the process
+owning the descriptor, the command of the owning process, the value
+of the descriptor, and the target of the descriptor.
+
+@kindex info os sockets
+@item sockets
+Display the list of Internet-domain sockets on the target.  For each
+socket, @value{GDBN} prints the address and port of the local and
+remote endpoints, the current state of the connection, the creator of
+the socket, the IP address family of the socket, and the type of the
+connection.
+
+@kindex info os shm
+@item shm
+Display the list of all System V shared-memory regions on the target.
+For each shared-memory region, @value{GDBN} prints the region key,
+the shared-memory identifier, the access permissions, the size of the
+region, the process that created the region, the process that last
+attached to or detached from the region, the current number of live
+attaches to the region, and the times at which the region was last
+attached to, detach from, and changed.
+
+@kindex info os semaphores
+@item semaphores
+Display the list of all System V semaphore sets on the target.  For each
+semaphore set, @value{GDBN} prints the semaphore set key, the semaphore
+set identifier, the access permissions, the number of semaphores in the
+set, the user and group of the owner and creator of the semaphore set,
+and the times at which the semaphore set was operated upon and changed.
+
+@kindex info os msg
+@item msg
+Display the list of all System V message queues on the target.  For each
+message queue, @value{GDBN} prints the message queue key, the message
+queue identifier, the access permissions, the current number of bytes
+on the queue, the current number of messages on the queue, the processes
+that last sent and received a message on the queue, the user and group
+of the owner and creator of the message queue, the times at which a
+message was last sent and received on the queue, and the time at which
+the message queue was last changed.
+
+@kindex info os modules
+@item modules
+Display the list of all loaded kernel modules on the target.  For each
+module, @value{GDBN} prints the module name, the size of the module in
+bytes, the number of times the module is used, the dependencies of the
+module, the status of the module, and the address of the loaded module
+in memory.
+@end table
+
+@item info os
+If INFOTYPE is omitted, then list the types of OS information available
+for the target.  If the target does not return a list of possible types,
+this command will report an error.
 @end table
 
 @node Memory Region Attributes
Index: gdb/testsuite/gdb.base/info-os.c
===================================================================
RCS file: gdb/testsuite/gdb.base/info-os.c
diff -N gdb/testsuite/gdb.base/info-os.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gdb/testsuite/gdb.base/info-os.c	12 Oct 2011 18:19:31 -0000
@@ -0,0 +1,115 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2011 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <sys/shm.h>
+#include <sys/sem.h>
+#include <sys/msg.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void *
+thread_proc (void *args)
+{
+  pthread_mutex_lock (&mutex);
+  pthread_mutex_unlock (&mutex);
+}
+
+int
+main (void)
+{
+  const int flags = IPC_CREAT | 0666;
+  int shmid, semid, msqid;
+  FILE *fd;
+  pthread_t thread;
+  struct sockaddr_in sock_addr;
+  int sock;
+  unsigned short port;
+  socklen_t size;
+  int status;
+
+  if ((shmid = shmget (3925, 4096, flags | IPC_EXCL)) < 0)
+    {
+      /* Attempt to delete the existing shared-memory region, then
+	 recreate it.  */
+      shmctl (shmget (3925, 4096, flags), IPC_RMID, NULL);
+      if ((shmid = shmget (3925, 4096, flags | IPC_EXCL)) < 0)
+	{
+	  printf ("Cannot create shared-memory region.\n");
+	  return 1;
+	}	  
+    }
+
+  semid = semget (7428, 1, flags);
+  msqid = msgget (5294, flags);
+  fd = fopen ("/dev/null", "r");
+
+  /* Lock the mutex to prevent the new thread from finishing immediately.  */
+  pthread_mutex_lock (&mutex);
+  pthread_create (&thread, NULL, thread_proc, 0);
+ 
+  sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+  if (sock < 0)
+    {
+      printf ("Cannot create socket.\n");
+      return 1;
+    }
+ 
+  sock_addr.sin_family = AF_INET;
+  sock_addr.sin_port = 0; /* Bind to a free port.  */
+  sock_addr.sin_addr.s_addr = htonl (INADDR_ANY);
+
+  status = bind (sock, (struct sockaddr *) &sock_addr, sizeof (sock_addr));
+  if (status < 0)
+    {
+      printf ("Cannot bind socket.\n");
+      return 1;
+    }
+
+  /* Find the assigned port number of the socket.  */
+  size = sizeof (sock_addr);
+  status = getsockname (sock, (struct sockaddr *) &sock_addr, &size);
+  if (status < 0)
+    {
+      printf ("Cannot find name of socket.\n");
+      return 1;
+    }
+  port = ntohs (sock_addr.sin_port);
+
+  status = listen (sock, 1);
+  if (status < 0)
+    {
+      printf ("Cannot listen on socket.\n");
+      return 1;
+    }
+
+  /* Set breakpoint here.  */
+
+  shmctl (shmid, IPC_RMID, NULL);
+  semctl (semid, 0, IPC_RMID, NULL);
+  msgctl (msqid, IPC_RMID, NULL);
+  fclose (fd);
+  close (sock);
+
+  pthread_mutex_unlock (&mutex);
+  pthread_join (thread, NULL);
+
+  return 0;
+}
Index: gdb/testsuite/gdb.base/info-os.exp
===================================================================
RCS file: gdb/testsuite/gdb.base/info-os.exp
diff -N gdb/testsuite/gdb.base/info-os.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gdb/testsuite/gdb.base/info-os.exp	12 Oct 2011 18:19:31 -0000
@@ -0,0 +1,110 @@
+# Copyright 2011 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+set testfile "info-os"
+set srcfile ${testfile}.c
+
+# This test is Linux-only.
+if ![istarget *-*-linux*] then {
+    unsupported "info-os.exp"
+    return -1
+}
+
+# Support for XML-output is needed to run this test.
+if [gdb_skip_xml_test] then {
+    unsupported "info-os.exp"
+    return -1
+}
+
+# Compile test program.
+if { [prepare_for_testing ${testfile}.exp $testfile $srcfile {debug additional_flags=-lpthread}] } {
+    fail "cannot compile test program"
+    return -1
+}
+
+if ![runto_main] then {
+    fail "cannot run to main"
+    return -1;
+}
+
+# Get PID of test program.
+set inferior_pid -1
+set test "get inferior process ID"
+gdb_test_multiple "call getpid()" $test {
+    -re ".* = ($decimal).*$gdb_prompt $" {
+	set inferior_pid $expect_out(1,string)
+	pass $test
+    }
+}
+
+gdb_breakpoint ${srcfile}:[gdb_get_line_number "Set breakpoint here"]
+gdb_continue_to_breakpoint "Set breakpoint here"
+
+# Get IDs of the IPC object instances.
+set shmid -1
+set test "get shared memory ID"
+gdb_test_multiple "print shmid" $test {
+    -re ".* = ($decimal).*$gdb_prompt $" {
+	set shmid $expect_out(1,string)
+	pass $test
+    }
+}
+
+set semid -1
+set test "get semaphore ID"
+gdb_test_multiple "print semid" $test {
+    -re ".* = ($decimal).*$gdb_prompt $" {
+	set semid $expect_out(1,string)
+	pass $test
+    }
+}
+
+set msqid -1
+set test "get message queue ID"
+gdb_test_multiple "print msqid" $test {
+    -re ".* = ($decimal).*$gdb_prompt $" {
+	set msqid $expect_out(1,string)
+	pass $test
+    }
+}
+
+# Get port number of test socket.
+set port -1
+set test "get socket port number"
+gdb_test_multiple "print port" $test {
+    -re ".* = ($decimal).*$gdb_prompt $" {
+	set port $expect_out(1,string)
+	pass $test
+    }
+}
+
+# Test output of the 'info os' commands against the expected results.
+gdb_test "info os processes" ".*pid +user +command +cores.*$inferior_pid +\\S+ +\\S*info-os +\[0-9\]+.*" "get process list"
+gdb_test "info os procgroups" ".*pgid +leader command +pid +command line.*$inferior_pid +info-os +$inferior_pid +\\S*info-os.*" "get process groups"
+gdb_test "info os threads" ".*pid +command +tid +core.*$inferior_pid +info-os +\\d+ +\\d+.*" "get threads"
+gdb_test "info os files" ".*pid +command +file descriptor +name.*$inferior_pid +info-os +\\d+ +/dev/null.*" "get file descriptors"
+gdb_test "info os sockets" ".*local address +local port +remote address +remote port +state +user +family +protocol.*0\\.0\\.0\\.0 +$port +0\\.0\\.0\\.0 +0 +LISTEN +\\S+ +INET +STREAM.*" "get internet-domain sockets"
+gdb_test "info os shm" ".*key +shmid +permissions +size +creator command +last op\\. command +num attached +user +group +creator user +creator group +last shmat\\(\\) time +last shmdt\\(\\) time +last shmctl\\(\\) time.*3925 +$shmid +666 +4096 +info-os +.*" "get shared-memory regions"
+gdb_test "info os semaphores" ".*key +semid +permissions +num semaphores +user +group +creator user +creator group +last semop\\(\\) time +last semctl\\(\\) time.*7428 +$semid +666 +1 +.*" "get semaphores"
+gdb_test "info os msg" ".*key +msqid +permissions +num used bytes +num messages +last msgsnd\\(\\) command +last msgrcv\\(\\) command +user +group +creator user +creator group +last msgsnd\\(\\) time +last msgrcv\\(\\) time +last msgctl\\(\\) time.*5294 +$msqid +666 +.*" "get message queues"
+
+# The SysV IPC primitives linger on after the creating process is killed
+# unless they are destroyed explicitly, so allow the test program to tidy
+# up after itself.  Note that the test program attempts to delete and
+# recreate the shared-memory region if it already exists, in case a
+# previous run failed before having a chance to clean up.  The tests for
+# semaphores and message queues should still work with primitives from
+# previous runs.
+send_gdb "continue\n"

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH]  Add extra 'info os' information types for Linux
  2011-10-12 18:29 [PATCH] Add extra 'info os' information types for Linux Kwok Cheung Yeung
@ 2011-10-21 23:38 ` Tom Tromey
  2011-11-23 18:00   ` Kwok Cheung Yeung
  0 siblings, 1 reply; 19+ messages in thread
From: Tom Tromey @ 2011-10-21 23:38 UTC (permalink / raw)
  To: Kwok Cheung Yeung; +Cc: gdb-patches

>>>>> ">" == Kwok Cheung Yeung <kcy@codesourcery.com> writes:

>> This patch greatly extends the OS information types available to the
>> user using the 'info os' command in Linux. Since the OS info backend
>> is now unified, this information can be obtained from both local and
>> remote (gdbserver) targets.

I think the idea is good.

>> +static int
>> +compare_processes (const void *process1, const void *process2)

Most of the new functions need introductory comments.

>> +  static struct buffer buffer;

Wow, another growable buffer type.  I didn't know about this one.
Let's see.. VEC, dynstr, obstack, buffer... can we have a 5th?  :)

>> +	  pid_t *process_list = (pid_t *) xmalloc (list_block_size * 2 * sizeof (pid_t));

Too bad VEC isn't available here.

>> +	      char procentry[sizeof ("/proc/4294967295")];

One occasionally hears talk of 64 bit PIDs.

>> +#if 0
>> +			  struct stat statbuf;
>> +			  stat (dp2->d_name, &statbuf);
>> +#endif

No new #if 0 code.

>> +static void
>> +time_from_int (char *time, int maxlen, int seconds)

Why int and not just time_t?
...

>> +		  items_read = sscanf (buf,
>> +				       "%d %d %o %d %d %d %d %u %u %u %u %d %d %d",
>> +				       &key, &shmid, &perms, &size,
>> +				       &cpid, &lpid,
>> +				       &nattch,
>> +				       &uid, &gid, &cuid, &cgid,
>> +				       &atime, &dtime, &ctime);
[...]
>> +		      char atime_str[32], dtime_str[32], ctime_str[32];
[...]
>> +		      time_from_int (atime_str, sizeof (atime_str), atime);
>> +		      time_from_int (dtime_str, sizeof (dtime_str), dtime);
>> +		      time_from_int (ctime_str, sizeof (ctime_str), ctime);

I think it is probably better to use long at least.

Tom

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH]  Add extra 'info os' information types for Linux
  2011-10-21 23:38 ` Tom Tromey
@ 2011-11-23 18:00   ` Kwok Cheung Yeung
  2011-12-27  4:56     ` [PATCH] Add extra 'info os' information types for Linux (trunk and 7.4) Stan Shebs
  0 siblings, 1 reply; 19+ messages in thread
From: Kwok Cheung Yeung @ 2011-11-23 18:00 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

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

Sorry for the late reply - I was busy on another project.

On 21/10/2011 9:57 PM, Tom Tromey wrote:
>>> +static int
>>> +compare_processes (const void *process1, const void *process2)
>
> Most of the new functions need introductory comments.
>

I've now commented all the auxilliary functions used by the 
linux_xfer_osdata_<data type> functions.

>>> +  static struct buffer buffer;
>
> Wow, another growable buffer type.  I didn't know about this one.
> Let's see.. VEC, dynstr, obstack, buffer... can we have a 5th?  :)
>

buffer was used in the gdbserver implementation of Linux osdata, while obstacks 
were used in the gdb implementation. When combining the two, I went with buffer 
since it was self-contained in GDB (it is implemented in gdb/common/buffer.[hc]).

>>> +	      char procentry[sizeof ("/proc/4294967295")];
>
> One occasionally hears talk of 64 bit PIDs.
>
>>> +static void
>>> +time_from_int (char *time, int maxlen, int seconds)
>
> Why int and not just time_t?
> ...
>
>>> +		  items_read = sscanf (buf,
>>> +				       "%d %d %o %d %d %d %d %u %u %u %u %d %d %d",
>>> +				&key,&shmid,&perms,&size,
>>> +				&cpid,&lpid,
>>> +				&nattch,
>>> +				&uid,&gid,&cuid,&cgid,
>>> +				&atime,&dtime,&ctime);
> [...]
>>> +		      char atime_str[32], dtime_str[32], ctime_str[32];
> [...]
>>> +		      time_from_int (atime_str, sizeof (atime_str), atime);
>>> +		      time_from_int (dtime_str, sizeof (dtime_str), dtime);
>>> +		      time_from_int (ctime_str, sizeof (ctime_str), ctime);
>
> I think it is probably better to use long at least.
>

I have worked around the issue of the sizes of time_t and pid_t by defining my 
own local versions (TIME_T and PID_T) which are of a known type. The code in 
linux-osdata.c works with these types and cast to/from time_t/pid_t when calling 
external functions. I have also changed buffer_xml_printf to be able to handle 
long and long long format specifiers in the format string.

 >>> +#if 0
 >>> +			  struct stat statbuf;
 >>> +			  stat (dp2->d_name,&statbuf);
 >>> +#endif
 >
 > No new #if 0 code.
 >

Now removed.

Kwok


ChangeLog:

gdb/
	* common/linux-osdata.c (PID_T, TIME_T): Add local versions of pid_t and
	time_t to avoid problems with the size of the native versions.
	(MAX_PID_T_STRLEN): New.
	(linux_common_core_of_thread): Add comment.  Change to use PID_T and
	MAX_PID_T_STRLEN.
	(command_from_pid): Add comment.  Change to use PID_T.
	(commandline_from_pid):  Change to use PID_T.
	(user_from_pid): Add comment.
	(get_process_owner): Add comment. Change to use PID_T and
	MAX_PID_T_STRLEN.
	(get_number_of_cpu_cores): Add comment.
	(get_cores_used_by_process): Add comment.  Change to use PID_T and
	MAX_PID_T_STRLEN.
	(linux_xfer_osdata_processes): Change to use PID_T and MAX_PID_T_STRLEN.
	(compare_processes): Auxiliary function for
	linux_xfer_osdata_processgroups.
	(linux_xfer_osdata_processgroups): New function to
	look up process groups.
	(linux_xfer_osdata_threads): Change to use PID_T.
	(linux_xfer_osdata_fds): New function to look up file descriptors.
	(format_socket_state, print_sockets): Auxiliary functions for
	linux_xfer_osdata_isockets.
	(union socket_addr): New union used to avoid strict-aliasing problems.
	(linux_xfer_osdata_isockets): New function to look up internet sockets.
	(time_from_time_t, group_from_gid): New convenience functions for
	converting between data types.	
	(linux_xfer_osdata_shm): New function to look up shared memory for IPC.
	(linux_xfer_osdata_sem): New function to look up semaphores for IPC.
	(linux_xfer_osdata_msg): New function to look up message queues for
	IPC.
	(linux_xfer_osdata_modules): New function to look up loaded kernel
	modules.
	(osdata_table): Add new entries.
	* common/buffer.c (buffer_xml_printf): Add support for long and
	long long format specifiers.

[-- Attachment #2: os-awareness_2.patch --]
[-- Type: text/plain, Size: 35502 bytes --]

Index: gdb/common/linux-osdata.c
===================================================================
RCS file: /cvs/src/src/gdb/common/linux-osdata.c,v
retrieving revision 1.2
diff -u -p -r1.2 linux-osdata.c
--- gdb/common/linux-osdata.c	26 Aug 2011 18:58:04 -0000	1.2
+++ gdb/common/linux-osdata.c	23 Nov 2011 17:55:53 -0000
@@ -45,12 +45,19 @@
 #include "gdb_assert.h"
 #include "gdb_dirent.h"
 
+/* PID_T/TIME_T are used in place of pid_t/time_t to avoid making
+   assumptions about the size of the native versions.  */
+typedef long long  PID_T;
+typedef long long  TIME_T;
+
+#define MAX_PID_T_STRLEN  (sizeof ("-9223372036854775808") - 1)
+
+/* Returns the CPU core that thread PTID is currently running on.  */
+					  
 int
 linux_common_core_of_thread (ptid_t ptid)
 {
-  char filename[sizeof ("/proc//task//stat")
-		 + 2 * 20 /* decimal digits for 2 numbers, max 2^64 bit each */
-		 + 1];
+  char filename[sizeof ("/proc//task//stat") + 2 * MAX_PID_T_STRLEN];
   FILE *f;
   char *content = NULL;
   char *p;
@@ -59,8 +66,8 @@ linux_common_core_of_thread (ptid_t ptid
   int i;
   int core;
 
-  sprintf (filename, "/proc/%d/task/%ld/stat",
-	   ptid_get_pid (ptid), ptid_get_lwp (ptid));
+  sprintf (filename, "/proc/%lld/task/%lld/stat",
+	   (PID_T) ptid_get_pid (ptid), (PID_T) ptid_get_lwp (ptid));
   f = fopen (filename, "r");
   if (!f)
     return -1;
@@ -102,10 +109,14 @@ linux_common_core_of_thread (ptid_t ptid
   return core;
 }
 
+/* Finds the command-line of process PID and copies it into COMMAND.  At most MAXLEN
+   characters are copied.  If the command-line cannot be found, PID is copied into
+   command in text-form.  */
+
 static void
-command_from_pid (char *command, int maxlen, pid_t pid)
+command_from_pid (char *command, int maxlen, PID_T pid)
 {
-  char *stat_path = xstrprintf ("/proc/%d/stat", pid); 
+  char *stat_path = xstrprintf ("/proc/%lld/stat", pid); 
   FILE *fp = fopen (stat_path, "r");
   
   command[0] = '\0';
@@ -116,8 +127,8 @@ command_from_pid (char *command, int max
 	 include/linux/sched.h in the Linux kernel sources) plus two
 	 (for the brackets).  */
       char cmd[32]; 
-      pid_t stat_pid;
-      int items_read = fscanf (fp, "%d %32s", &stat_pid, cmd);
+      PID_T stat_pid;
+      int items_read = fscanf (fp, "%lld %32s", &stat_pid, cmd);
 	  
       if (items_read == 2 && pid == stat_pid)
 	{
@@ -130,7 +141,7 @@ command_from_pid (char *command, int max
   else
     {
       /* Return the PID if a /proc entry for the process cannot be found.  */
-      snprintf (command, maxlen, "%d", pid);
+      snprintf (command, maxlen, "%lld", pid);
     }
 
   command[maxlen - 1] = '\0'; /* Ensure string is null-terminated.  */
@@ -142,9 +153,9 @@ command_from_pid (char *command, int max
    string needs to be freed using xfree after use.  */
 
 static char *
-commandline_from_pid (pid_t pid)
+commandline_from_pid (PID_T pid)
 {
-  char *pathname = xstrprintf ("/proc/%d/cmdline", pid);
+  char *pathname = xstrprintf ("/proc/%lld/cmdline", pid);
   char *commandline = NULL;
   FILE *f = fopen (pathname, "r");
 
@@ -196,6 +207,9 @@ commandline_from_pid (pid_t pid)
   return commandline;
 }
 
+/* Finds the user name for the user UID and copies it into USER.  At most MAXLEN
+   characters are copied.  */
+
 static void
 user_from_uid (char *user, int maxlen, uid_t uid)
 {
@@ -210,13 +224,16 @@ user_from_uid (char *user, int maxlen, u
     user[0] = '\0';
 }
 
+/*  Finds the owner of process PID and returns the user id in OWNER.
+    Returns 0 if the owner was found, -1 otherwise.  */
+
 static int
-get_process_owner (uid_t *owner, pid_t pid)
+get_process_owner (uid_t *owner, PID_T pid)
 {
   struct stat statbuf;
-  char procentry[sizeof ("/proc/4294967295")];
+  char procentry[sizeof ("/proc/") + MAX_PID_T_STRLEN];
 
-  sprintf (procentry, "/proc/%d", pid);
+  sprintf (procentry, "/proc/%lld", pid);
   
   if (stat (procentry, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
     {
@@ -227,6 +244,8 @@ get_process_owner (uid_t *owner, pid_t p
     return -1;
 }
 
+/* Returns the number of CPU cores found on the system.  */
+
 static int
 get_number_of_cpu_cores (void)
 {
@@ -247,31 +266,32 @@ get_number_of_cpu_cores (void)
   return cores;
 }
 
-/* CORES points to an array of at least get_number_of_cpu_cores () elements.  */
+/* Find the CPU cores used by process PID and return them in CORES.  CORES points to an
+   array of at least get_number_of_cpu_cores () elements.  */
 
 static int
-get_cores_used_by_process (pid_t pid, int *cores)
+get_cores_used_by_process (PID_T pid, int *cores)
 {
-  char taskdir[sizeof ("/proc/4294967295/task")];
+  char taskdir[sizeof ("/proc/") + MAX_PID_T_STRLEN + sizeof ("/task") - 1];
   DIR *dir;
   struct dirent *dp;
   int task_count = 0;
 
-  sprintf (taskdir, "/proc/%d/task", pid);
+  sprintf (taskdir, "/proc/%lld/task", pid);
   dir = opendir (taskdir);
   if (dir)
     {
       while ((dp = readdir (dir)) != NULL)
 	{
-	  pid_t tid;
+	  PID_T tid;
 	  int core;
 
 	  if (!isdigit (dp->d_name[0])
-	      || NAMELEN (dp) > sizeof ("4294967295") - 1)
+	      || NAMELEN (dp) > MAX_PID_T_STRLEN)
 	    continue;
 
-	  tid = atoi (dp->d_name);
-	  core = linux_common_core_of_thread (ptid_build (pid, tid, 0));
+	  sscanf (dp->d_name, "%lld", &tid);
+	  core = linux_common_core_of_thread (ptid_build ((pid_t) pid, (pid_t) tid, 0));
 
 	  if (core >= 0)
 	    {
@@ -314,7 +334,7 @@ linux_xfer_osdata_processes (gdb_byte *r
 
 	  while ((dp = readdir (dirp)) != NULL)
 	    {
-	      pid_t pid;
+	      PID_T pid;
 	      uid_t owner;
 	      char user[UT_NAMESIZE];
 	      char *command_line;
@@ -324,10 +344,10 @@ linux_xfer_osdata_processes (gdb_byte *r
 	      int i;
 
 	      if (!isdigit (dp->d_name[0])
-		  || NAMELEN (dp) > sizeof ("4294967295") - 1)
+		  || NAMELEN (dp) > MAX_PID_T_STRLEN)
 		continue;
 
-	      sscanf (dp->d_name, "%d", &pid);
+	      sscanf (dp->d_name, "%lld", &pid);
 	      command_line = commandline_from_pid (pid);
 
 	      if (get_process_owner (&owner, pid) == 0)
@@ -343,7 +363,7 @@ linux_xfer_osdata_processes (gdb_byte *r
 	      for (i = 0; i < num_cores && task_count > 0; ++i)
 		if (cores[i])
 		  {
-		    char core_str[sizeof ("4294967205")];
+		    char core_str[sizeof ("4294967295")];
 
 		    sprintf (core_str, "%d", i);
 		    strcat (cores_str, core_str);
@@ -358,7 +378,7 @@ linux_xfer_osdata_processes (gdb_byte *r
 	      buffer_xml_printf (
 		  &buffer,
 		  "<item>"
-		  "<column name=\"pid\">%d</column>"
+		  "<column name=\"pid\">%lld</column>"
 		  "<column name=\"user\">%s</column>"
 		  "<column name=\"command\">%s</column>"
 		  "<column name=\"cores\">%s</column>"
@@ -396,6 +416,151 @@ linux_xfer_osdata_processes (gdb_byte *r
   return len;
 }
 
+/* Auxilliary function used by qsort to sort processes by process group.  Compares two
+   processes with ids PROCESS1 and PROCESS2.  PROCESS1 comes before PROCESS2 if it has
+   a lower process group id.  If they belong to the same process group, PROCESS1 comes
+   before PROCESS2 if it has a lower process id or is the process group leader.  */
+
+static int
+compare_processes (const void *process1, const void *process2)
+{
+  PID_T pid1 = *((PID_T *) process1);
+  PID_T pid2 = *((PID_T *) process2);
+  PID_T pgid1 = *((PID_T *) process1 + 1);
+  PID_T pgid2 = *((PID_T *) process2 + 1);
+
+  /* Sort by PGID.  */
+  if (pgid1 < pgid2)
+    return -1;
+  else if (pgid1 > pgid2)
+    return 1;
+  else
+    {
+      /* Process group leaders always come first, else sort by PID.  */
+      if (pid1 == pgid1)
+	return -1;
+      else if (pid2 == pgid2)
+	return 1;
+      else if (pid1 < pid2)
+	return -1;
+      else if (pid1 > pid2)
+	return 1;
+      else
+	return 0;
+    }
+}
+
+static LONGEST
+linux_xfer_osdata_processgroups (gdb_byte *readbuf,
+				 ULONGEST offset, LONGEST len)
+{
+  /* 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;
+
+  if (offset == 0)
+    {
+      DIR *dirp;
+
+      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");
+
+      dirp = opendir ("/proc");
+      if (dirp)
+	{
+	  struct dirent *dp;
+	  const size_t list_block_size = 512;
+	  PID_T *process_list = (PID_T *) xmalloc (list_block_size * 2 * sizeof (PID_T));
+	  size_t process_count = 0;
+	  size_t i;
+
+	  /* 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[2 * process_count] = pid;
+		  process_list[2 * process_count + 1] = pgid;
+		  ++process_count;
+
+		  /* Increase the size of the list if necessary.  */
+		  if (process_count % list_block_size == 0)
+		    process_list = (PID_T *) xrealloc (
+			process_list,
+			(process_count + list_block_size) * 2 * sizeof (PID_T));
+		}
+	    }
+
+	  closedir (dirp);
+
+	  /* Sort the process list.  */
+	  qsort (process_list, process_count, 2 * sizeof (PID_T), compare_processes);
+
+	  for (i = 0; i < process_count; ++i)
+	    {
+	      PID_T pid = process_list[2 * i];
+	      PID_T pgid = process_list[2 * i + 1];
+	      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);
+	    }
+
+	  xfree (process_list);
+	}   
+      
+
+      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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
 static LONGEST
 linux_xfer_osdata_threads (gdb_byte *readbuf,
 			   ULONGEST offset, LONGEST len)
@@ -436,7 +601,7 @@ linux_xfer_osdata_threads (gdb_byte *rea
 		{
 		  DIR *dirp2;
 		  char *pathname;
-		  pid_t pid;
+		  PID_T pid;
 		  char command[32];
 
 		  pathname = xstrprintf ("/proc/%s/task", dp->d_name);
@@ -452,7 +617,7 @@ linux_xfer_osdata_threads (gdb_byte *rea
 
 		      while ((dp2 = readdir (dirp2)) != NULL)
 			{
-			  pid_t tid;
+			  PID_T tid;
 			  int core;
 
 			  if (!isdigit (dp2->d_name[0])
@@ -465,9 +630,9 @@ linux_xfer_osdata_threads (gdb_byte *rea
 			  buffer_xml_printf (
 			    &buffer,
 			    "<item>"
-			    "<column name=\"pid\">%d</column>"
+			    "<column name=\"pid\">%lld</column>"
 			    "<column name=\"command\">%s</column>"
-			    "<column name=\"tid\">%d</column>"
+			    "<column name=\"tid\">%lld</column>"
 			    "<column name=\"core\">%d</column>"
 			    "</item>",
 			    pid,
@@ -507,13 +672,843 @@ linux_xfer_osdata_threads (gdb_byte *rea
   return len;
 }
 
+static LONGEST
+linux_xfer_osdata_fds (gdb_byte *readbuf,
+		       ULONGEST offset, LONGEST len)
+{
+  /* 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;
+
+  if (offset == 0)
+    {
+      DIR *dirp;
+
+      if (len_avail != -1 && len_avail != 0)
+	buffer_free (&buffer);
+      len_avail = 0;
+      buf = NULL;
+      buffer_init (&buffer);
+      buffer_grow_str (&buffer, "<osdata type=\"files\">\n");
+
+      dirp = opendir ("/proc");
+      if (dirp)
+	{
+	  struct dirent *dp;
+
+	  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;
+
+	      sprintf (procentry, "/proc/%s", dp->d_name);
+	      if (stat (procentry, &statbuf) == 0
+		  && S_ISDIR (statbuf.st_mode))
+		{
+		  char *pathname;
+		  DIR *dirp2;
+		  PID_T pid;
+		  char command[32];
+
+		  pid = atoi (dp->d_name);
+		  command_from_pid (command, sizeof (command), pid);
+
+		  pathname = xstrprintf ("/proc/%s/fd", dp->d_name);
+		  dirp2 = opendir (pathname);
+
+		  if (dirp2)
+		    {
+		      struct dirent *dp2;
+
+		      while ((dp2 = readdir (dirp2)) != NULL)
+			{
+			  char *fdname;
+			  char buf[1000];
+			  ssize_t rslt;
+
+			  if (!isdigit (dp2->d_name[0]))
+			    continue;
+
+			  fdname = xstrprintf ("%s/%s", pathname, dp2->d_name);
+			  rslt = readlink (fdname, buf, 1000);
+			  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);
+		    }
+
+		  xfree (pathname);
+		}
+	    }
+
+	  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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
+/* Returns the socket state STATE in textual form.  */
+
+static const char *
+format_socket_state (unsigned char state)
+{
+  /* Copied from include/net/tcp_states.h in the Linux kernel sources.  */
+  enum {
+    TCP_ESTABLISHED = 1,
+    TCP_SYN_SENT,
+    TCP_SYN_RECV,
+    TCP_FIN_WAIT1,
+    TCP_FIN_WAIT2,
+    TCP_TIME_WAIT,
+    TCP_CLOSE,
+    TCP_CLOSE_WAIT,
+    TCP_LAST_ACK,
+    TCP_LISTEN,
+    TCP_CLOSING
+  };
+
+  switch (state)
+    {
+    case TCP_ESTABLISHED:
+      return "ESTABLISHED";
+    case TCP_SYN_SENT:
+      return "SYN_SENT";
+    case TCP_SYN_RECV:
+      return "SYN_RECV";
+    case TCP_FIN_WAIT1:
+      return "FIN_WAIT1";
+    case TCP_FIN_WAIT2:
+      return "FIN_WAIT2";
+    case TCP_TIME_WAIT:
+      return "TIME_WAIT";
+    case TCP_CLOSE:
+      return "CLOSE";
+    case TCP_CLOSE_WAIT:
+      return "CLOSE_WAIT";
+    case TCP_LAST_ACK:
+      return "LAST_ACK";
+    case TCP_LISTEN:
+      return "LISTEN";
+    case TCP_CLOSING:
+      return "CLOSING";
+    default:
+      return "(unknown)";
+    }
+}
+
+union socket_addr
+  {
+    struct sockaddr sa;
+    struct sockaddr_in sin;
+    struct sockaddr_in6 sin6;
+  };
+
+/* Auxilliary function used by linux_xfer_osdata_isocket.  Formats information for
+   all open internet sockets of type FAMILY on the system into BUFFER.  If TCP is
+   set, only TCP sockets are processed, otherwise only UDP sockets are processed.  */
+
+static void
+print_sockets (unsigned short family, int tcp, struct buffer *buffer)
+{
+  const char *proc_file;
+  FILE *fp;
+
+  if (family == AF_INET)
+    proc_file = tcp ? "/proc/net/tcp" : "/proc/net/udp";
+  else if (family == AF_INET6)
+    proc_file = tcp ? "/proc/net/tcp6" : "/proc/net/udp6";
+  else
+    return;
+
+  fp = fopen (proc_file, "r");
+  if (fp)
+    {
+      char buf[8192];
+
+      do
+	{
+	  if (fgets (buf, sizeof (buf), fp))
+	    {
+	      uid_t uid;
+	      unsigned long tlen, inode;
+	      int sl, timeout;
+	      unsigned int local_port, remote_port, state, txq, rxq, trun, retn;
+	      char local_address[NI_MAXHOST], remote_address[NI_MAXHOST], extra[512];
+	      int result;
+
+	      result = sscanf (buf,
+			       "%d: %33[0-9A-F]:%X %33[0-9A-F]:%X %X %X:%X %X:%lX %X %d %d %lu %512s\n",
+			       &sl,
+			       local_address, &local_port,
+			       remote_address, &remote_port,
+			       &state,
+			       &txq, &rxq,
+			       &trun, &tlen,
+			       &retn,
+			       &uid,
+			       &timeout,
+			       &inode,
+			       extra);
+	      
+	      if (result == 15)
+		{
+		  union socket_addr locaddr, remaddr;
+		  size_t addr_size;
+		  char user[UT_NAMESIZE];
+		  char local_service[NI_MAXSERV], remote_service[NI_MAXSERV];
+
+		  if (family == AF_INET)
+		    {
+		      sscanf (local_address, "%X", &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);
+
+		      addr_size = sizeof (struct sockaddr_in);
+		    }
+		  else
+		    {
+		      sscanf (local_address, "%8X%8X%8X%8X",
+			      locaddr.sin6.sin6_addr.s6_addr32,
+			      locaddr.sin6.sin6_addr.s6_addr32 + 1,
+			      locaddr.sin6.sin6_addr.s6_addr32 + 2,
+			      locaddr.sin6.sin6_addr.s6_addr32 + 3);
+		      sscanf (remote_address, "%8X%8X%8X%8X",
+			      remaddr.sin6.sin6_addr.s6_addr32,
+			      remaddr.sin6.sin6_addr.s6_addr32 + 1,
+			      remaddr.sin6.sin6_addr.s6_addr32 + 2,
+			      remaddr.sin6.sin6_addr.s6_addr32 + 3);
+
+		      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;
+		      remaddr.sin6.sin6_scope_id = 0;
+
+		      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),
+					NI_NUMERICHOST | NI_NUMERICSERV
+					| (tcp ? 0 : NI_DGRAM));
+		  if (result)
+		    continue;
+		  
+		  result = getnameinfo (&remaddr.sa, addr_size,
+					remote_address, sizeof (remote_address),
+					remote_service, sizeof (remote_service),
+					NI_NUMERICHOST | NI_NUMERICSERV
+					| (tcp ? 0 : NI_DGRAM));
+		  if (result)
+		    continue;
+		  
+		  user_from_uid (user, sizeof (user), uid);
+		  
+		  buffer_xml_printf (
+		      buffer,
+		      "<item>"
+		      "<column name=\"local address\">%s</column>"
+		      "<column name=\"local port\">%s</column>"
+		      "<column name=\"remote address\">%s</column>"
+		      "<column name=\"remote port\">%s</column>"
+		      "<column name=\"state\">%s</column>"
+		      "<column name=\"user\">%s</column>"
+		      "<column name=\"family\">%s</column>" 
+		      "<column name=\"protocol\">%s</column>"
+		      "</item>",
+		      local_address,
+		      local_service,
+		      remote_address,
+		      remote_service,
+		      format_socket_state (state),
+		      user,
+		      (family == AF_INET) ? "INET" : "INET6",
+		      tcp ? "STREAM" : "DGRAM");
+		}
+	    }
+	}
+      while (!feof (fp));
+
+      fclose (fp);
+    }
+}
+
+static LONGEST
+linux_xfer_osdata_isockets (gdb_byte *readbuf,
+			    ULONGEST offset, LONGEST len)
+{
+  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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
+/* Converts the time SECONDS into textual form and copies it
+   into a buffer TIME, with at most MAXLEN characters copied.  */
+
+static void
+time_from_time_t (char *time, int maxlen, TIME_T seconds)
+{
+  if (!seconds)
+    time[0] = '\0';
+  else
+    {
+      time_t t = (time_t) seconds;
+      
+      strncpy (time, ctime (&t), maxlen);
+      time[maxlen - 1] = '\0';
+    }
+}
+
+/* Finds the group name for the group GID and copies it into GROUP.  At most MAXLEN
+   characters are copied.  */
+
+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);
+      group[maxlen - 1] = '\0'; /* Ensure that the group name is null-terminated.  */
+    }
+  else
+    group[0] = '\0';
+}
+
+static LONGEST
+linux_xfer_osdata_shm (gdb_byte *readbuf,
+		       ULONGEST offset, LONGEST len)
+{
+  static const char *buf;
+  static LONGEST len_avail = -1;
+  static struct buffer buffer;
+
+  if (offset == 0)
+    {
+      FILE *fp;
+
+      if (len_avail != -1 && len_avail != 0)
+	buffer_free (&buffer);
+      len_avail = 0;
+      buf = NULL;
+      buffer_init (&buffer);
+      buffer_grow_str (&buffer, "<osdata type=\"shared memory\">\n");
+
+      fp = fopen ("/proc/sysvipc/shm", "r");
+      if (fp)
+	{
+	  char buf[8192];
+
+	  do
+	    {
+	      if (fgets (buf, sizeof (buf), fp))
+		{
+		  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);
+		    }
+		}
+	    }
+	  while (!feof (fp));
+
+	  fclose (fp);
+	}
+      
+      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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
+static LONGEST
+linux_xfer_osdata_sem (gdb_byte *readbuf,
+		       ULONGEST offset, LONGEST len)
+{
+  static const char *buf;
+  static LONGEST len_avail = -1;
+  static struct buffer buffer;
+
+  if (offset == 0)
+    {
+      FILE *fp;
+      
+      if (len_avail != -1 && len_avail != 0)
+	buffer_free (&buffer);
+      len_avail = 0;
+      buf = NULL;
+      buffer_init (&buffer);
+      buffer_grow_str (&buffer, "<osdata type=\"semaphores\">\n");
+
+      fp = fopen ("/proc/sysvipc/sem", "r");
+      if (fp)
+	{
+	  char buf[8192];
+	  
+	  do
+	    {
+	      if (fgets (buf, sizeof (buf), fp))
+		{
+		  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);
+		    }
+		}
+	    }
+	  while (!feof (fp));
+
+	  fclose (fp);
+	}
+
+      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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
+static LONGEST
+linux_xfer_osdata_msg (gdb_byte *readbuf,
+		       ULONGEST offset, LONGEST len)
+{
+  static const char *buf;
+  static LONGEST len_avail = -1;
+  static struct buffer buffer;
+
+  if (offset == 0)
+    {
+      FILE *fp;
+      
+      if (len_avail != -1 && len_avail != 0)
+	buffer_free (&buffer);
+      len_avail = 0;
+      buf = NULL;
+      buffer_init (&buffer);
+      buffer_grow_str (&buffer, "<osdata type=\"message queues\">\n");
+      
+      fp = fopen ("/proc/sysvipc/msg", "r");
+      if (fp)
+	{
+	  char buf[8192];
+	  
+	  do
+	    {
+	      if (fgets (buf, sizeof (buf), fp))
+		{
+		  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);
+		    }
+		}
+	    }
+	  while (!feof (fp));
+
+	  fclose (fp);
+	}
+
+      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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
+static LONGEST
+linux_xfer_osdata_modules (gdb_byte *readbuf,
+			   ULONGEST offset, LONGEST len)
+{
+  static const char *buf;
+  static LONGEST len_avail = -1;
+  static struct buffer buffer;
+
+  if (offset == 0)
+    {
+      FILE *fp;
+
+      if (len_avail != -1 && len_avail != 0)
+	buffer_free (&buffer);
+      len_avail = 0;
+      buf = NULL;
+      buffer_init (&buffer);
+      buffer_grow_str (&buffer, "<osdata type=\"modules\">\n");
+
+      fp = fopen ("/proc/modules", "r");
+      if (fp)
+	{
+	  char buf[8192];
+	  
+	  do
+	    {
+	      if (fgets (buf, sizeof (buf), fp))
+		{
+		  char name[64], dependencies[256], status[16];
+		  unsigned int size;
+		  unsigned long long address;
+		  int uses;
+		  int items_read;
+		  
+		  items_read = sscanf (buf,
+				       "%64s %d %d %256s %16s 0x%llx",
+				       name, &size, &uses,
+				       dependencies, status, &address);
+
+		  if (items_read == 6)
+		    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));
+
+	  fclose (fp);
+	}
+
+      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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
 struct osdata_type {
   char *type;
   char *description;
   LONGEST (*getter) (gdb_byte *readbuf, ULONGEST offset, LONGEST len);
 } osdata_table[] = {
   { "processes", "Listing of all processes", linux_xfer_osdata_processes },
+  { "procgroups", "Listing of all process groups", linux_xfer_osdata_processgroups },
   { "threads", "Listing of all threads", linux_xfer_osdata_threads },
+  { "files", "Listing of all file descriptors", linux_xfer_osdata_fds },
+  { "sockets", "Listing of all internet-domain sockets", linux_xfer_osdata_isockets },
+  { "shm", "Listing of all shared-memory regions", linux_xfer_osdata_shm },
+  { "semaphores", "Listing of all semaphores", linux_xfer_osdata_sem },
+  { "msg", "Listing of all message queues", linux_xfer_osdata_msg },
+  { "modules", "Listing of all loaded kernel modules", linux_xfer_osdata_modules },
   { NULL, NULL, NULL }
 };
 
Index: gdb/common/buffer.c
===================================================================
RCS file: /cvs/src/src/gdb/common/buffer.c,v
retrieving revision 1.1
diff -u -p -r1.1 buffer.c
--- gdb/common/buffer.c	21 Jul 2011 23:46:09 -0000	1.1
+++ gdb/common/buffer.c	23 Nov 2011 17:55:53 -0000
@@ -101,6 +101,7 @@ buffer_xml_printf (struct buffer *buffer
 	  char buf[32];
 	  char *p;
 	  char *str = buf;
+	  const char *f_old = f;
 	  
 	  switch (*f)
 	    {
@@ -119,14 +120,56 @@ buffer_xml_printf (struct buffer *buffer
 	    case 'o':
 	      sprintf (str, "%o", va_arg (ap, unsigned int));
 	      break;
+	    case 'l':
+	      f++;
+	      switch (*f)
+		{
+		case 'd':
+		  sprintf (str, "%ld", va_arg (ap, long));
+		  break;
+		case 'u':
+		  sprintf (str, "%lu", va_arg (ap, unsigned long));
+		  break;
+		case 'x':
+		  sprintf (str, "%lx", va_arg (ap, unsigned long));
+		  break;
+		case 'o':
+		  sprintf (str, "%lo", va_arg (ap, unsigned long));
+		  break;
+		case 'l':
+		  f++;
+		  switch (*f)
+		    {
+		    case 'd':
+		      sprintf (str, "%lld", va_arg (ap, long long));
+		      break;
+		    case 'u':
+		      sprintf (str, "%llu", va_arg (ap, unsigned long long));
+		      break;
+		    case 'x':
+		      sprintf (str, "%llx", va_arg (ap, unsigned long long));
+		      break;
+		    case 'o':
+		      sprintf (str, "%llo", va_arg (ap, unsigned long long));
+		      break;
+		    default:
+		      str = 0;
+		      break;
+		    }
+		  break;
+		default:
+		  str = 0;
+		  break;
+		}
+	      break;
 	    default:
 	      str = 0;
 	      break;
 	    }
-	  
+  
 	  if (str)
 	    {
-	      buffer_grow (buffer, prev, f - prev - 1);
+	      buffer_grow (buffer, prev, f_old - prev - 1);
 	      p = xml_escape_text (str);
 	      buffer_grow_str (buffer, p);
 	      xfree (p);
@@ -137,7 +180,7 @@ buffer_xml_printf (struct buffer *buffer
       else if (*f == '%')
 	percent = 1;
     }
-
+  
   buffer_grow_str (buffer, prev);
   va_end (ap);
 }

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH]  Add extra 'info os' information types for Linux (trunk and 7.4)
  2011-11-23 18:00   ` Kwok Cheung Yeung
@ 2011-12-27  4:56     ` Stan Shebs
  2011-12-27  9:32       ` Eli Zaretskii
  2011-12-29 20:34       ` Doug Evans
  0 siblings, 2 replies; 19+ messages in thread
From: Stan Shebs @ 2011-12-27  4:56 UTC (permalink / raw)
  To: gdb-patches

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

Here is a third revision of the 'info os' additions for Linux; it rolls 
up Kwok's original patch plus requested edits, plus a few more comments 
and tweaks.  I plan to commit this in a day or so, if there are no 
objections.

I'd also like to get this pushed into 7.4; although it's getting late in 
the release cycle, this code is purely Linux-native and does not have 
any effect outside of 'info os'.  The urgency is partly due to snafu on 
my part - Kwok originally posted this patch in October - and partly due 
to interdependency with Eclipse, who is doing GUI for these commands.  
(And yes, there is an approved-but-uncommitted MI patch connected with 
this, that I'll be pushing soon.)

Stan

2011-12-26  Stan Shebs <stan@codesourcery.com>
         Kwok Cheung Yeung <kcy@codesourcery.com>

     * NEWS: Describe new info os commands.
     * common/linux-osdata.c (PID_T, TIME_T): Define.
     (MAX_PID_T_STRLEN): New.
     (linux_common_core_of_thread): Add comment.  Change to use PID_T and
     MAX_PID_T_STRLEN.
     (command_from_pid): Add comment.  Change to use PID_T.
     (commandline_from_pid):  Change to use PID_T.
     (user_from_pid): Add comment.
     (get_process_owner): Add comment. Change to use PID_T and
     MAX_PID_T_STRLEN.
     (get_number_of_cpu_cores): Add comment.
     (get_cores_used_by_process): Add comment.  Change to use PID_T and
     MAX_PID_T_STRLEN.
     (linux_xfer_osdata_processes): Change to use PID_T and
     MAX_PID_T_STRLEN.
     (compare_processes): New function.
     (linux_xfer_osdata_processgroups): New function.
     (linux_xfer_osdata_threads): Change to use PID_T.
     (linux_xfer_osdata_fds): New function.
     (format_socket_state, print_sockets): New functions.
     (union socket_addr): New union.
     (linux_xfer_osdata_isockets): New function.
     (time_from_time_t, group_from_gid): New functions.
     (linux_xfer_osdata_shm): New function.
     (linux_xfer_osdata_sem): New function.
     (linux_xfer_osdata_msg): New function.
     (linux_xfer_osdata_modules): New function.
     (osdata_table): Add new entries.
     * common/buffer.c (buffer_xml_printf): Add support for long and
     long long format specifiers.

     * gdb.texinfo (Operating System Auxiliary Information): Document new
     'info os' subcommands.

     * gdb.base/info-os.exp: New file.
     * gdb.base/info-os.c: New file.



[-- Attachment #2: osaware-patch-3 --]
[-- Type: text/plain, Size: 51937 bytes --]

Index: NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.477
diff -u -p -r1.477 NEWS
--- NEWS	23 Dec 2011 17:06:10 -0000	1.477
+++ NEWS	27 Dec 2011 04:11:17 -0000
@@ -6,6 +6,19 @@
 * GDBserver now supports stdio connections.
   E.g. (gdb) target remote | ssh myhost gdbserver - hello
 
+* New commands
+
+info os procgroups
+info os threads
+info os files
+info os sockets
+info os shm
+info os semaphores
+info os msg
+info os modules
+     Display detailed information about GNU/Linux OS objects of
+     various sorts.
+
 *** Changes in GDB 7.4
 
 * GDB now handles ambiguous linespecs more consistently; the existing
Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.908
diff -u -p -r1.908 gdb.texinfo
--- doc/gdb.texinfo	23 Dec 2011 17:06:13 -0000	1.908
+++ doc/gdb.texinfo	27 Dec 2011 04:11:18 -0000
@@ -9025,23 +9025,109 @@ most appropriate form for a recognized t
 an unrecognized tag.
 @end table
 
-On some targets, @value{GDBN} can access operating-system-specific information
-and display it to user, without interpretation.  For remote targets,
-this functionality depends on the remote stub's support of the 
+On some targets, @value{GDBN} can access operating system-specific
+information and show it to you.  The types of information available
+will differ depending on the type of operating system running on the
+target.  The mechanism used to fetch the data is described in
+@ref{Operating System Information}.  For remote targets, this
+functionality depends on the remote stub's support of the
 @samp{qXfer:osdata:read} packet, see @ref{qXfer osdata read}.
 
 @table @code
 @kindex info os
-@item info os
-List the types of OS information available for the target.  If the
-target does not return a list of possible types, this command will
-report an error.
+@item info os @var{infotype}
+
+Display OS information of the requested type.
+
+On @sc{gnu}/Linux, the following values of @var{infotype} are valid:
 
+@anchor{linux info os infotypes}
+@table @code
 @kindex info os processes
-@item info os processes
+@item processes
 Display the list of processes on the target.  For each process,
-@value{GDBN} prints the process identifier, the name of the user, and
-the command corresponding to the process.
+@value{GDBN} prints the process identifier, the name of the user, the
+command corresponding to the process, and the list of processor cores
+that the process is currently running on.  (To understand what these
+properties mean, for this and the following info types, please consult
+the general @sc{gnu}/Linux documentation.)
+
+@kindex info os procgroups
+@item procgroups
+Display the list of process groups on the target.  For each process,
+@value{GDBN} prints the identifier of the process group that it belongs
+to, the command corresponding to the process group leader, the process
+identifier, and the command line of the process.  The list is sorted
+first by the process group identifier, then by the process identifier,
+so that processes belonging to the same process group are grouped together
+and the process group leader is listed first.
+
+@kindex info os threads
+@item threads
+Display the list of threads running on the target.  For each thread,
+@value{GDBN} prints the identifier of the process that the thread
+belongs to, the command of the process, the thread identifier, and the
+processor core that it is currently running on.  The main thread of a
+process is not listed.
+
+@kindex info os files
+@item files
+Display the list of open file descriptors on the target.  For each
+file descriptor, @value{GDBN} prints the identifier of the process
+owning the descriptor, the command of the owning process, the value
+of the descriptor, and the target of the descriptor.
+
+@kindex info os sockets
+@item sockets
+Display the list of Internet-domain sockets on the target.  For each
+socket, @value{GDBN} prints the address and port of the local and
+remote endpoints, the current state of the connection, the creator of
+the socket, the IP address family of the socket, and the type of the
+connection.
+
+@kindex info os shm
+@item shm
+Display the list of all System V shared-memory regions on the target.
+For each shared-memory region, @value{GDBN} prints the region key,
+the shared-memory identifier, the access permissions, the size of the
+region, the process that created the region, the process that last
+attached to or detached from the region, the current number of live
+attaches to the region, and the times at which the region was last
+attached to, detach from, and changed.
+
+@kindex info os semaphores
+@item semaphores
+Display the list of all System V semaphore sets on the target.  For each
+semaphore set, @value{GDBN} prints the semaphore set key, the semaphore
+set identifier, the access permissions, the number of semaphores in the
+set, the user and group of the owner and creator of the semaphore set,
+and the times at which the semaphore set was operated upon and changed.
+
+@kindex info os msg
+@item msg
+Display the list of all System V message queues on the target.  For each
+message queue, @value{GDBN} prints the message queue key, the message
+queue identifier, the access permissions, the current number of bytes
+on the queue, the current number of messages on the queue, the processes
+that last sent and received a message on the queue, the user and group
+of the owner and creator of the message queue, the times at which a
+message was last sent and received on the queue, and the time at which
+the message queue was last changed.
+
+@kindex info os modules
+@item modules
+Display the list of all loaded kernel modules on the target.  For each
+module, @value{GDBN} prints the module name, the size of the module in
+bytes, the number of times the module is used, the dependencies of the
+module, the status of the module, and the address of the loaded module
+in memory.
+@end table
+
+@item info os
+If @var{infotype} is omitted, then list the possible values for
+@var{infotype} and the kind of OS information available for each
+@var{infotype}.  If the target does not return a list of possible
+types, this command will report an error.
 @end table
 
 @node Memory Region Attributes
Index: common/buffer.c
===================================================================
RCS file: /cvs/src/src/gdb/common/buffer.c,v
retrieving revision 1.1
diff -u -p -r1.1 buffer.c
--- common/buffer.c	21 Jul 2011 23:46:09 -0000	1.1
+++ common/buffer.c	27 Dec 2011 04:11:18 -0000
@@ -101,6 +101,7 @@ buffer_xml_printf (struct buffer *buffer
 	  char buf[32];
 	  char *p;
 	  char *str = buf;
+	  const char *f_old = f;
 	  
 	  switch (*f)
 	    {
@@ -119,14 +120,56 @@ buffer_xml_printf (struct buffer *buffer
 	    case 'o':
 	      sprintf (str, "%o", va_arg (ap, unsigned int));
 	      break;
+	    case 'l':
+	      f++;
+	      switch (*f)
+		{
+		case 'd':
+		  sprintf (str, "%ld", va_arg (ap, long));
+		  break;
+		case 'u':
+		  sprintf (str, "%lu", va_arg (ap, unsigned long));
+		  break;
+		case 'x':
+		  sprintf (str, "%lx", va_arg (ap, unsigned long));
+		  break;
+		case 'o':
+		  sprintf (str, "%lo", va_arg (ap, unsigned long));
+		  break;
+		case 'l':
+		  f++;
+		  switch (*f)
+		    {
+		    case 'd':
+		      sprintf (str, "%lld", va_arg (ap, long long));
+		      break;
+		    case 'u':
+		      sprintf (str, "%llu", va_arg (ap, unsigned long long));
+		      break;
+		    case 'x':
+		      sprintf (str, "%llx", va_arg (ap, unsigned long long));
+		      break;
+		    case 'o':
+		      sprintf (str, "%llo", va_arg (ap, unsigned long long));
+		      break;
+		    default:
+		      str = 0;
+		      break;
+		    }
+		  break;
+		default:
+		  str = 0;
+		  break;
+		}
+	      break;
 	    default:
 	      str = 0;
 	      break;
 	    }
-	  
+
 	  if (str)
 	    {
-	      buffer_grow (buffer, prev, f - prev - 1);
+	      buffer_grow (buffer, prev, f_old - prev - 1);
 	      p = xml_escape_text (str);
 	      buffer_grow_str (buffer, p);
 	      xfree (p);
Index: common/linux-osdata.c
===================================================================
RCS file: /cvs/src/src/gdb/common/linux-osdata.c,v
retrieving revision 1.2
diff -u -p -r1.2 linux-osdata.c
--- common/linux-osdata.c	26 Aug 2011 18:58:04 -0000	1.2
+++ common/linux-osdata.c	27 Dec 2011 04:11:18 -0000
@@ -45,12 +45,25 @@
 #include "gdb_assert.h"
 #include "gdb_dirent.h"
 
+/* Define PID_T to be a fixed size that is at least as large as pid_t,
+   so that reading pid values embedded in /proc works
+   consistently.  */
+
+typedef long long  PID_T;
+
+/* Define TIME_T to be at least as large as time_t, so that reading
+   time values embedded in /proc works consistently.  */
+
+typedef long long TIME_T;
+
+#define MAX_PID_T_STRLEN  (sizeof ("-9223372036854775808") - 1)
+
+/* Returns the CPU core that thread PTID is currently running on.  */
+					  
 int
 linux_common_core_of_thread (ptid_t ptid)
 {
-  char filename[sizeof ("/proc//task//stat")
-		 + 2 * 20 /* decimal digits for 2 numbers, max 2^64 bit each */
-		 + 1];
+  char filename[sizeof ("/proc//task//stat") + 2 * MAX_PID_T_STRLEN];
   FILE *f;
   char *content = NULL;
   char *p;
@@ -59,8 +72,8 @@ linux_common_core_of_thread (ptid_t ptid
   int i;
   int core;
 
-  sprintf (filename, "/proc/%d/task/%ld/stat",
-	   ptid_get_pid (ptid), ptid_get_lwp (ptid));
+  sprintf (filename, "/proc/%lld/task/%lld/stat",
+	   (PID_T) ptid_get_pid (ptid), (PID_T) ptid_get_lwp (ptid));
   f = fopen (filename, "r");
   if (!f)
     return -1;
@@ -102,10 +115,14 @@ linux_common_core_of_thread (ptid_t ptid
   return core;
 }
 
+/* Finds the command-line of process PID and copies it into COMMAND.
+   At most MAXLEN characters are copied.  If the command-line cannot
+   be found, PID is copied into command in text-form.  */
+
 static void
-command_from_pid (char *command, int maxlen, pid_t pid)
+command_from_pid (char *command, int maxlen, PID_T pid)
 {
-  char *stat_path = xstrprintf ("/proc/%d/stat", pid); 
+  char *stat_path = xstrprintf ("/proc/%lld/stat", pid); 
   FILE *fp = fopen (stat_path, "r");
   
   command[0] = '\0';
@@ -116,8 +133,8 @@ command_from_pid (char *command, int max
 	 include/linux/sched.h in the Linux kernel sources) plus two
 	 (for the brackets).  */
       char cmd[32]; 
-      pid_t stat_pid;
-      int items_read = fscanf (fp, "%d %32s", &stat_pid, cmd);
+      PID_T stat_pid;
+      int items_read = fscanf (fp, "%lld %32s", &stat_pid, cmd);
 	  
       if (items_read == 2 && pid == stat_pid)
 	{
@@ -130,7 +147,7 @@ command_from_pid (char *command, int max
   else
     {
       /* Return the PID if a /proc entry for the process cannot be found.  */
-      snprintf (command, maxlen, "%d", pid);
+      snprintf (command, maxlen, "%lld", pid);
     }
 
   command[maxlen - 1] = '\0'; /* Ensure string is null-terminated.  */
@@ -138,13 +155,13 @@ command_from_pid (char *command, int max
   xfree (stat_path);
 }
 
-/* Returns the command-line of the process with the given PID. The returned
-   string needs to be freed using xfree after use.  */
+/* Returns the command-line of the process with the given PID. The
+   returned string needs to be freed using xfree after use.  */
 
 static char *
-commandline_from_pid (pid_t pid)
+commandline_from_pid (PID_T pid)
 {
-  char *pathname = xstrprintf ("/proc/%d/cmdline", pid);
+  char *pathname = xstrprintf ("/proc/%lld/cmdline", pid);
   char *commandline = NULL;
   FILE *f = fopen (pathname, "r");
 
@@ -180,7 +197,8 @@ commandline_from_pid (pid_t pid)
 	}
       else
 	{
-	  /* Return the command in square brackets if the command-line is empty.  */
+	  /* Return the command in square brackets if the command-line
+	     is empty.  */
 	  commandline = (char *) xmalloc (32);
 	  commandline[0] = '[';
 	  command_from_pid (commandline + 1, 31, pid);
@@ -196,6 +214,9 @@ commandline_from_pid (pid_t pid)
   return commandline;
 }
 
+/* Finds the user name for the user UID and copies it into USER.  At
+   most MAXLEN characters are copied.  */
+
 static void
 user_from_uid (char *user, int maxlen, uid_t uid)
 {
@@ -204,19 +225,23 @@ user_from_uid (char *user, int maxlen, u
   if (pwentry)
     {
       strncpy (user, pwentry->pw_name, maxlen);
-      user[maxlen - 1] = '\0'; /* Ensure that the user name is null-terminated.  */
+      /* Ensure that the user name is null-terminated.  */
+      user[maxlen - 1] = '\0';
     }
   else
     user[0] = '\0';
 }
 
+/* Finds the owner of process PID and returns the user id in OWNER.
+   Returns 0 if the owner was found, -1 otherwise.  */
+
 static int
-get_process_owner (uid_t *owner, pid_t pid)
+get_process_owner (uid_t *owner, PID_T pid)
 {
   struct stat statbuf;
-  char procentry[sizeof ("/proc/4294967295")];
+  char procentry[sizeof ("/proc/") + MAX_PID_T_STRLEN];
 
-  sprintf (procentry, "/proc/%d", pid);
+  sprintf (procentry, "/proc/%lld", pid);
   
   if (stat (procentry, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
     {
@@ -227,6 +252,8 @@ get_process_owner (uid_t *owner, pid_t p
     return -1;
 }
 
+/* Returns the number of CPU cores found on the system.  */
+
 static int
 get_number_of_cpu_cores (void)
 {
@@ -247,31 +274,34 @@ get_number_of_cpu_cores (void)
   return cores;
 }
 
-/* CORES points to an array of at least get_number_of_cpu_cores () elements.  */
+/* Find the CPU cores used by process PID and return them in CORES.
+   CORES points to an array of at least get_number_of_cpu_cores ()
+   elements.  */
 
 static int
-get_cores_used_by_process (pid_t pid, int *cores)
+get_cores_used_by_process (PID_T pid, int *cores)
 {
-  char taskdir[sizeof ("/proc/4294967295/task")];
+  char taskdir[sizeof ("/proc/") + MAX_PID_T_STRLEN + sizeof ("/task") - 1];
   DIR *dir;
   struct dirent *dp;
   int task_count = 0;
 
-  sprintf (taskdir, "/proc/%d/task", pid);
+  sprintf (taskdir, "/proc/%lld/task", pid);
   dir = opendir (taskdir);
   if (dir)
     {
       while ((dp = readdir (dir)) != NULL)
 	{
-	  pid_t tid;
+	  PID_T tid;
 	  int core;
 
 	  if (!isdigit (dp->d_name[0])
-	      || NAMELEN (dp) > sizeof ("4294967295") - 1)
+	      || NAMELEN (dp) > MAX_PID_T_STRLEN)
 	    continue;
 
-	  tid = atoi (dp->d_name);
-	  core = linux_common_core_of_thread (ptid_build (pid, tid, 0));
+	  sscanf (dp->d_name, "%lld", &tid);
+	  core = linux_common_core_of_thread (ptid_build ((pid_t) pid,
+							  (pid_t) tid, 0));
 
 	  if (core >= 0)
 	    {
@@ -314,7 +344,7 @@ linux_xfer_osdata_processes (gdb_byte *r
 
 	  while ((dp = readdir (dirp)) != NULL)
 	    {
-	      pid_t pid;
+	      PID_T pid;
 	      uid_t owner;
 	      char user[UT_NAMESIZE];
 	      char *command_line;
@@ -324,10 +354,10 @@ linux_xfer_osdata_processes (gdb_byte *r
 	      int i;
 
 	      if (!isdigit (dp->d_name[0])
-		  || NAMELEN (dp) > sizeof ("4294967295") - 1)
+		  || NAMELEN (dp) > MAX_PID_T_STRLEN)
 		continue;
 
-	      sscanf (dp->d_name, "%d", &pid);
+	      sscanf (dp->d_name, "%lld", &pid);
 	      command_line = commandline_from_pid (pid);
 
 	      if (get_process_owner (&owner, pid) == 0)
@@ -343,7 +373,7 @@ linux_xfer_osdata_processes (gdb_byte *r
 	      for (i = 0; i < num_cores && task_count > 0; ++i)
 		if (cores[i])
 		  {
-		    char core_str[sizeof ("4294967205")];
+		    char core_str[sizeof ("4294967295")];
 
 		    sprintf (core_str, "%d", i);
 		    strcat (cores_str, core_str);
@@ -358,7 +388,7 @@ linux_xfer_osdata_processes (gdb_byte *r
 	      buffer_xml_printf (
 		  &buffer,
 		  "<item>"
-		  "<column name=\"pid\">%d</column>"
+		  "<column name=\"pid\">%lld</column>"
 		  "<column name=\"user\">%s</column>"
 		  "<column name=\"command\">%s</column>"
 		  "<column name=\"cores\">%s</column>"
@@ -396,6 +426,160 @@ linux_xfer_osdata_processes (gdb_byte *r
   return len;
 }
 
+/* Auxiliary function used by qsort to sort processes by process
+   group.  Compares two processes with ids PROCESS1 and PROCESS2.
+   PROCESS1 comes before PROCESS2 if it has a lower process group id.
+   If they belong to the same process group, PROCESS1 comes before
+   PROCESS2 if it has a lower process id or is the process group
+   leader.  */
+
+static int
+compare_processes (const void *process1, const void *process2)
+{
+  PID_T pid1 = *((PID_T *) process1);
+  PID_T pid2 = *((PID_T *) process2);
+  PID_T pgid1 = *((PID_T *) process1 + 1);
+  PID_T pgid2 = *((PID_T *) process2 + 1);
+
+  /* Sort by PGID.  */
+  if (pgid1 < pgid2)
+    return -1;
+  else if (pgid1 > pgid2)
+    return 1;
+  else
+    {
+      /* Process group leaders always come first, else sort by PID.  */
+      if (pid1 == pgid1)
+	return -1;
+      else if (pid2 == pgid2)
+	return 1;
+      else if (pid1 < pid2)
+	return -1;
+      else if (pid1 > pid2)
+	return 1;
+      else
+	return 0;
+    }
+}
+
+/* Collect all process groups from /proc.  */
+
+static LONGEST
+linux_xfer_osdata_processgroups (gdb_byte *readbuf,
+				 ULONGEST offset, LONGEST len)
+{
+  /* 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;
+
+  if (offset == 0)
+    {
+      DIR *dirp;
+
+      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");
+
+      dirp = opendir ("/proc");
+      if (dirp)
+	{
+	  struct dirent *dp;
+	  const size_t list_block_size = 512;
+	  PID_T *process_list = (PID_T *) xmalloc (list_block_size * 2 * sizeof (PID_T));
+	  size_t process_count = 0;
+	  size_t i;
+
+	  /* 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[2 * process_count] = pid;
+		  process_list[2 * process_count + 1] = pgid;
+		  ++process_count;
+
+		  /* Increase the size of the list if necessary.  */
+		  if (process_count % list_block_size == 0)
+		    process_list = (PID_T *) xrealloc (
+			process_list,
+			(process_count + list_block_size)
+			* 2 * sizeof (PID_T));
+		}
+	    }
+
+	  closedir (dirp);
+
+	  /* Sort the process list.  */
+	  qsort (process_list, process_count, 2 * sizeof (PID_T),
+		 compare_processes);
+
+	  for (i = 0; i < process_count; ++i)
+	    {
+	      PID_T pid = process_list[2 * i];
+	      PID_T pgid = process_list[2 * i + 1];
+	      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);
+	    }
+
+	  xfree (process_list);
+	}   
+
+      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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
+/* Collect all the threads in /proc by iterating through processes and
+   then tasks within each process.  */
+
 static LONGEST
 linux_xfer_osdata_threads (gdb_byte *readbuf,
 			   ULONGEST offset, LONGEST len)
@@ -436,7 +620,7 @@ linux_xfer_osdata_threads (gdb_byte *rea
 		{
 		  DIR *dirp2;
 		  char *pathname;
-		  pid_t pid;
+		  PID_T pid;
 		  char command[32];
 
 		  pathname = xstrprintf ("/proc/%s/task", dp->d_name);
@@ -452,7 +636,7 @@ linux_xfer_osdata_threads (gdb_byte *rea
 
 		      while ((dp2 = readdir (dirp2)) != NULL)
 			{
-			  pid_t tid;
+			  PID_T tid;
 			  int core;
 
 			  if (!isdigit (dp2->d_name[0])
@@ -465,9 +649,9 @@ linux_xfer_osdata_threads (gdb_byte *rea
 			  buffer_xml_printf (
 			    &buffer,
 			    "<item>"
-			    "<column name=\"pid\">%d</column>"
+			    "<column name=\"pid\">%lld</column>"
 			    "<column name=\"command\">%s</column>"
-			    "<column name=\"tid\">%d</column>"
+			    "<column name=\"tid\">%lld</column>"
 			    "<column name=\"core\">%d</column>"
 			    "</item>",
 			    pid,
@@ -507,13 +691,877 @@ linux_xfer_osdata_threads (gdb_byte *rea
   return len;
 }
 
+/* Collect all the open file descriptors found in /proc and put the details
+   found about them into READBUF.  */
+
+static LONGEST
+linux_xfer_osdata_fds (gdb_byte *readbuf,
+		       ULONGEST offset, LONGEST len)
+{
+  /* 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;
+
+  if (offset == 0)
+    {
+      DIR *dirp;
+
+      if (len_avail != -1 && len_avail != 0)
+	buffer_free (&buffer);
+      len_avail = 0;
+      buf = NULL;
+      buffer_init (&buffer);
+      buffer_grow_str (&buffer, "<osdata type=\"files\">\n");
+
+      dirp = opendir ("/proc");
+      if (dirp)
+	{
+	  struct dirent *dp;
+
+	  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;
+
+	      sprintf (procentry, "/proc/%s", dp->d_name);
+	      if (stat (procentry, &statbuf) == 0
+		  && S_ISDIR (statbuf.st_mode))
+		{
+		  char *pathname;
+		  DIR *dirp2;
+		  PID_T pid;
+		  char command[32];
+
+		  pid = atoi (dp->d_name);
+		  command_from_pid (command, sizeof (command), pid);
+
+		  pathname = xstrprintf ("/proc/%s/fd", dp->d_name);
+		  dirp2 = opendir (pathname);
+
+		  if (dirp2)
+		    {
+		      struct dirent *dp2;
+
+		      while ((dp2 = readdir (dirp2)) != NULL)
+			{
+			  char *fdname;
+			  char buf[1000];
+			  ssize_t rslt;
+
+			  if (!isdigit (dp2->d_name[0]))
+			    continue;
+
+			  fdname = xstrprintf ("%s/%s", pathname, dp2->d_name);
+			  rslt = readlink (fdname, buf, 1000);
+			  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);
+		    }
+
+		  xfree (pathname);
+		}
+	    }
+
+	  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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
+/* Returns the socket state STATE in textual form.  */
+
+static const char *
+format_socket_state (unsigned char state)
+{
+  /* Copied from include/net/tcp_states.h in the Linux kernel sources.  */
+  enum {
+    TCP_ESTABLISHED = 1,
+    TCP_SYN_SENT,
+    TCP_SYN_RECV,
+    TCP_FIN_WAIT1,
+    TCP_FIN_WAIT2,
+    TCP_TIME_WAIT,
+    TCP_CLOSE,
+    TCP_CLOSE_WAIT,
+    TCP_LAST_ACK,
+    TCP_LISTEN,
+    TCP_CLOSING
+  };
+
+  switch (state)
+    {
+    case TCP_ESTABLISHED:
+      return "ESTABLISHED";
+    case TCP_SYN_SENT:
+      return "SYN_SENT";
+    case TCP_SYN_RECV:
+      return "SYN_RECV";
+    case TCP_FIN_WAIT1:
+      return "FIN_WAIT1";
+    case TCP_FIN_WAIT2:
+      return "FIN_WAIT2";
+    case TCP_TIME_WAIT:
+      return "TIME_WAIT";
+    case TCP_CLOSE:
+      return "CLOSE";
+    case TCP_CLOSE_WAIT:
+      return "CLOSE_WAIT";
+    case TCP_LAST_ACK:
+      return "LAST_ACK";
+    case TCP_LISTEN:
+      return "LISTEN";
+    case TCP_CLOSING:
+      return "CLOSING";
+    default:
+      return "(unknown)";
+    }
+}
+
+union socket_addr
+  {
+    struct sockaddr sa;
+    struct sockaddr_in sin;
+    struct sockaddr_in6 sin6;
+  };
+
+/* Auxiliary function used by linux_xfer_osdata_isocket.  Formats
+   information for all open internet sockets of type FAMILY on the
+   system into BUFFER.  If TCP is set, only TCP sockets are processed,
+   otherwise only UDP sockets are processed.  */
+
+static void
+print_sockets (unsigned short family, int tcp, struct buffer *buffer)
+{
+  const char *proc_file;
+  FILE *fp;
+
+  if (family == AF_INET)
+    proc_file = tcp ? "/proc/net/tcp" : "/proc/net/udp";
+  else if (family == AF_INET6)
+    proc_file = tcp ? "/proc/net/tcp6" : "/proc/net/udp6";
+  else
+    return;
+
+  fp = fopen (proc_file, "r");
+  if (fp)
+    {
+      char buf[8192];
+
+      do
+	{
+	  if (fgets (buf, sizeof (buf), fp))
+	    {
+	      uid_t uid;
+	      unsigned long tlen, inode;
+	      int sl, timeout;
+	      unsigned int local_port, remote_port, state;
+	      unsigned int txq, rxq, trun, retn;
+	      char local_address[NI_MAXHOST], remote_address[NI_MAXHOST];
+	      char extra[512];
+	      int result;
+
+	      result = sscanf (buf,
+			       "%d: %33[0-9A-F]:%X %33[0-9A-F]:%X %X %X:%X %X:%lX %X %d %d %lu %512s\n",
+			       &sl,
+			       local_address, &local_port,
+			       remote_address, &remote_port,
+			       &state,
+			       &txq, &rxq,
+			       &trun, &tlen,
+			       &retn,
+			       &uid,
+			       &timeout,
+			       &inode,
+			       extra);
+	      
+	      if (result == 15)
+		{
+		  union socket_addr locaddr, remaddr;
+		  size_t addr_size;
+		  char user[UT_NAMESIZE];
+		  char local_service[NI_MAXSERV], remote_service[NI_MAXSERV];
+
+		  if (family == AF_INET)
+		    {
+		      sscanf (local_address, "%X",
+			      &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);
+
+		      addr_size = sizeof (struct sockaddr_in);
+		    }
+		  else
+		    {
+		      sscanf (local_address, "%8X%8X%8X%8X",
+			      locaddr.sin6.sin6_addr.s6_addr32,
+			      locaddr.sin6.sin6_addr.s6_addr32 + 1,
+			      locaddr.sin6.sin6_addr.s6_addr32 + 2,
+			      locaddr.sin6.sin6_addr.s6_addr32 + 3);
+		      sscanf (remote_address, "%8X%8X%8X%8X",
+			      remaddr.sin6.sin6_addr.s6_addr32,
+			      remaddr.sin6.sin6_addr.s6_addr32 + 1,
+			      remaddr.sin6.sin6_addr.s6_addr32 + 2,
+			      remaddr.sin6.sin6_addr.s6_addr32 + 3);
+
+		      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;
+		      remaddr.sin6.sin6_scope_id = 0;
+
+		      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),
+					NI_NUMERICHOST | NI_NUMERICSERV
+					| (tcp ? 0 : NI_DGRAM));
+		  if (result)
+		    continue;
+		  
+		  result = getnameinfo (&remaddr.sa, addr_size,
+					remote_address,
+					sizeof (remote_address),
+					remote_service,
+					sizeof (remote_service),
+					NI_NUMERICHOST | NI_NUMERICSERV
+					| (tcp ? 0 : NI_DGRAM));
+		  if (result)
+		    continue;
+		  
+		  user_from_uid (user, sizeof (user), uid);
+		  
+		  buffer_xml_printf (
+		      buffer,
+		      "<item>"
+		      "<column name=\"local address\">%s</column>"
+		      "<column name=\"local port\">%s</column>"
+		      "<column name=\"remote address\">%s</column>"
+		      "<column name=\"remote port\">%s</column>"
+		      "<column name=\"state\">%s</column>"
+		      "<column name=\"user\">%s</column>"
+		      "<column name=\"family\">%s</column>" 
+		      "<column name=\"protocol\">%s</column>"
+		      "</item>",
+		      local_address,
+		      local_service,
+		      remote_address,
+		      remote_service,
+		      format_socket_state (state),
+		      user,
+		      (family == AF_INET) ? "INET" : "INET6",
+		      tcp ? "STREAM" : "DGRAM");
+		}
+	    }
+	}
+      while (!feof (fp));
+
+      fclose (fp);
+    }
+}
+
+/* Collect data about internet sockets and write it into READBUF.  */
+
+static LONGEST
+linux_xfer_osdata_isockets (gdb_byte *readbuf,
+			    ULONGEST offset, LONGEST len)
+{
+  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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
+/* Converts the time SECONDS into textual form and copies it into a
+   buffer TIME, with at most MAXLEN characters copied.  */
+
+static void
+time_from_time_t (char *time, int maxlen, TIME_T seconds)
+{
+  if (!seconds)
+    time[0] = '\0';
+  else
+    {
+      time_t t = (time_t) seconds;
+      
+      strncpy (time, ctime (&t), maxlen);
+      time[maxlen - 1] = '\0';
+    }
+}
+
+/* Finds the group name for the group GID and copies it into GROUP.
+   At most MAXLEN characters are copied.  */
+
+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);
+      /* Ensure that the group name is null-terminated.  */
+      group[maxlen - 1] = '\0';
+    }
+  else
+    group[0] = '\0';
+}
+
+/* Collect data about shared memory recorded in /proc and write it
+   into READBUF.  */
+
+static LONGEST
+linux_xfer_osdata_shm (gdb_byte *readbuf,
+		       ULONGEST offset, LONGEST len)
+{
+  static const char *buf;
+  static LONGEST len_avail = -1;
+  static struct buffer buffer;
+
+  if (offset == 0)
+    {
+      FILE *fp;
+
+      if (len_avail != -1 && len_avail != 0)
+	buffer_free (&buffer);
+      len_avail = 0;
+      buf = NULL;
+      buffer_init (&buffer);
+      buffer_grow_str (&buffer, "<osdata type=\"shared memory\">\n");
+
+      fp = fopen ("/proc/sysvipc/shm", "r");
+      if (fp)
+	{
+	  char buf[8192];
+
+	  do
+	    {
+	      if (fgets (buf, sizeof (buf), fp))
+		{
+		  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);
+		    }
+		}
+	    }
+	  while (!feof (fp));
+
+	  fclose (fp);
+	}
+      
+      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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
+/* Collect data about semaphores recorded in /proc and write it
+   into READBUF.  */
+
+static LONGEST
+linux_xfer_osdata_sem (gdb_byte *readbuf,
+		       ULONGEST offset, LONGEST len)
+{
+  static const char *buf;
+  static LONGEST len_avail = -1;
+  static struct buffer buffer;
+
+  if (offset == 0)
+    {
+      FILE *fp;
+      
+      if (len_avail != -1 && len_avail != 0)
+	buffer_free (&buffer);
+      len_avail = 0;
+      buf = NULL;
+      buffer_init (&buffer);
+      buffer_grow_str (&buffer, "<osdata type=\"semaphores\">\n");
+
+      fp = fopen ("/proc/sysvipc/sem", "r");
+      if (fp)
+	{
+	  char buf[8192];
+	  
+	  do
+	    {
+	      if (fgets (buf, sizeof (buf), fp))
+		{
+		  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);
+		    }
+		}
+	    }
+	  while (!feof (fp));
+
+	  fclose (fp);
+	}
+
+      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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
+/* Collect data about message queues recorded in /proc and write it
+   into READBUF.  */
+
+static LONGEST
+linux_xfer_osdata_msg (gdb_byte *readbuf,
+		       ULONGEST offset, LONGEST len)
+{
+  static const char *buf;
+  static LONGEST len_avail = -1;
+  static struct buffer buffer;
+
+  if (offset == 0)
+    {
+      FILE *fp;
+      
+      if (len_avail != -1 && len_avail != 0)
+	buffer_free (&buffer);
+      len_avail = 0;
+      buf = NULL;
+      buffer_init (&buffer);
+      buffer_grow_str (&buffer, "<osdata type=\"message queues\">\n");
+      
+      fp = fopen ("/proc/sysvipc/msg", "r");
+      if (fp)
+	{
+	  char buf[8192];
+	  
+	  do
+	    {
+	      if (fgets (buf, sizeof (buf), fp))
+		{
+		  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);
+		    }
+		}
+	    }
+	  while (!feof (fp));
+
+	  fclose (fp);
+	}
+
+      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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
+/* Collect data about loaded kernel modules and write it into
+   READBUF.  */
+
+static LONGEST
+linux_xfer_osdata_modules (gdb_byte *readbuf,
+			   ULONGEST offset, LONGEST len)
+{
+  static const char *buf;
+  static LONGEST len_avail = -1;
+  static struct buffer buffer;
+
+  if (offset == 0)
+    {
+      FILE *fp;
+
+      if (len_avail != -1 && len_avail != 0)
+	buffer_free (&buffer);
+      len_avail = 0;
+      buf = NULL;
+      buffer_init (&buffer);
+      buffer_grow_str (&buffer, "<osdata type=\"modules\">\n");
+
+      fp = fopen ("/proc/modules", "r");
+      if (fp)
+	{
+	  char buf[8192];
+	  
+	  do
+	    {
+	      if (fgets (buf, sizeof (buf), fp))
+		{
+		  char name[64], dependencies[256], status[16];
+		  unsigned int size;
+		  unsigned long long address;
+		  int uses;
+		  int items_read;
+		  
+		  items_read = sscanf (buf,
+				       "%64s %d %d %256s %16s 0x%llx",
+				       name, &size, &uses,
+				       dependencies, status, &address);
+
+		  if (items_read == 6)
+		    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));
+
+	  fclose (fp);
+	}
+
+      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;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
 struct osdata_type {
   char *type;
   char *description;
   LONGEST (*getter) (gdb_byte *readbuf, ULONGEST offset, LONGEST len);
 } osdata_table[] = {
-  { "processes", "Listing of all processes", linux_xfer_osdata_processes },
-  { "threads", "Listing of all threads", linux_xfer_osdata_threads },
+  { "processes", "Listing of all processes",
+    linux_xfer_osdata_processes },
+  { "procgroups", "Listing of all process groups",
+    linux_xfer_osdata_processgroups },
+  { "threads", "Listing of all threads",
+    linux_xfer_osdata_threads },
+  { "files", "Listing of all file descriptors",
+    linux_xfer_osdata_fds },
+  { "sockets", "Listing of all internet-domain sockets",
+    linux_xfer_osdata_isockets },
+  { "shm", "Listing of all shared-memory regions",
+    linux_xfer_osdata_shm },
+  { "semaphores", "Listing of all semaphores",
+    linux_xfer_osdata_sem },
+  { "msg", "Listing of all message queues",
+    linux_xfer_osdata_msg },
+  { "modules", "Listing of all loaded kernel modules",
+    linux_xfer_osdata_modules },
   { NULL, NULL, NULL }
 };
 
Index: testsuite/gdb.base/info-os.c
===================================================================
RCS file: testsuite/gdb.base/info-os.c
diff -N testsuite/gdb.base/info-os.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.base/info-os.c	27 Dec 2011 04:11:18 -0000
@@ -0,0 +1,115 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2011 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <sys/shm.h>
+#include <sys/sem.h>
+#include <sys/msg.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void *
+thread_proc (void *args)
+{
+  pthread_mutex_lock (&mutex);
+  pthread_mutex_unlock (&mutex);
+}
+
+int
+main (void)
+{
+  const int flags = IPC_CREAT | 0666;
+  int shmid, semid, msqid;
+  FILE *fd;
+  pthread_t thread;
+  struct sockaddr_in sock_addr;
+  int sock;
+  unsigned short port;
+  socklen_t size;
+  int status;
+
+  if ((shmid = shmget (3925, 4096, flags | IPC_EXCL)) < 0)
+    {
+      /* Attempt to delete the existing shared-memory region, then
+	 recreate it.  */
+      shmctl (shmget (3925, 4096, flags), IPC_RMID, NULL);
+      if ((shmid = shmget (3925, 4096, flags | IPC_EXCL)) < 0)
+	{
+	  printf ("Cannot create shared-memory region.\n");
+	  return 1;
+	}	  
+    }
+
+  semid = semget (7428, 1, flags);
+  msqid = msgget (5294, flags);
+  fd = fopen ("/dev/null", "r");
+
+  /* Lock the mutex to prevent the new thread from finishing immediately.  */
+  pthread_mutex_lock (&mutex);
+  pthread_create (&thread, NULL, thread_proc, 0);
+ 
+  sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+  if (sock < 0)
+    {
+      printf ("Cannot create socket.\n");
+      return 1;
+    }
+ 
+  sock_addr.sin_family = AF_INET;
+  sock_addr.sin_port = 0; /* Bind to a free port.  */
+  sock_addr.sin_addr.s_addr = htonl (INADDR_ANY);
+
+  status = bind (sock, (struct sockaddr *) &sock_addr, sizeof (sock_addr));
+  if (status < 0)
+    {
+      printf ("Cannot bind socket.\n");
+      return 1;
+    }
+
+  /* Find the assigned port number of the socket.  */
+  size = sizeof (sock_addr);
+  status = getsockname (sock, (struct sockaddr *) &sock_addr, &size);
+  if (status < 0)
+    {
+      printf ("Cannot find name of socket.\n");
+      return 1;
+    }
+  port = ntohs (sock_addr.sin_port);
+
+  status = listen (sock, 1);
+  if (status < 0)
+    {
+      printf ("Cannot listen on socket.\n");
+      return 1;
+    }
+
+  /* Set breakpoint here.  */
+
+  shmctl (shmid, IPC_RMID, NULL);
+  semctl (semid, 0, IPC_RMID, NULL);
+  msgctl (msqid, IPC_RMID, NULL);
+  fclose (fd);
+  close (sock);
+
+  pthread_mutex_unlock (&mutex);
+  pthread_join (thread, NULL);
+
+  return 0;
+}
Index: testsuite/gdb.base/info-os.exp
===================================================================
RCS file: testsuite/gdb.base/info-os.exp
diff -N testsuite/gdb.base/info-os.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.base/info-os.exp	27 Dec 2011 04:11:18 -0000
@@ -0,0 +1,110 @@
+# Copyright 2011 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+set testfile "info-os"
+set srcfile ${testfile}.c
+
+# This test is Linux-only.
+if ![istarget *-*-linux*] then {
+    unsupported "info-os.exp"
+    return -1
+}
+
+# Support for XML-output is needed to run this test.
+if [gdb_skip_xml_test] then {
+    unsupported "info-os.exp"
+    return -1
+}
+
+# Compile test program.
+if { [prepare_for_testing ${testfile}.exp $testfile $srcfile {debug additional_flags=-lpthread}] } {
+    fail "cannot compile test program"
+    return -1
+}
+
+if ![runto_main] then {
+    fail "cannot run to main"
+    return -1;
+}
+
+# Get PID of test program.
+set inferior_pid -1
+set test "get inferior process ID"
+gdb_test_multiple "call getpid()" $test {
+    -re ".* = ($decimal).*$gdb_prompt $" {
+	set inferior_pid $expect_out(1,string)
+	pass $test
+    }
+}
+
+gdb_breakpoint ${srcfile}:[gdb_get_line_number "Set breakpoint here"]
+gdb_continue_to_breakpoint "Set breakpoint here"
+
+# Get IDs of the IPC object instances.
+set shmid -1
+set test "get shared memory ID"
+gdb_test_multiple "print shmid" $test {
+    -re ".* = ($decimal).*$gdb_prompt $" {
+	set shmid $expect_out(1,string)
+	pass $test
+    }
+}
+
+set semid -1
+set test "get semaphore ID"
+gdb_test_multiple "print semid" $test {
+    -re ".* = ($decimal).*$gdb_prompt $" {
+	set semid $expect_out(1,string)
+	pass $test
+    }
+}
+
+set msqid -1
+set test "get message queue ID"
+gdb_test_multiple "print msqid" $test {
+    -re ".* = ($decimal).*$gdb_prompt $" {
+	set msqid $expect_out(1,string)
+	pass $test
+    }
+}
+
+# Get port number of test socket.
+set port -1
+set test "get socket port number"
+gdb_test_multiple "print port" $test {
+    -re ".* = ($decimal).*$gdb_prompt $" {
+	set port $expect_out(1,string)
+	pass $test
+    }
+}
+
+# Test output of the 'info os' commands against the expected results.
+gdb_test "info os processes" ".*pid +user +command +cores.*$inferior_pid +\\S+ +\\S*info-os +\[0-9\]+.*" "get process list"
+gdb_test "info os procgroups" ".*pgid +leader command +pid +command line.*$inferior_pid +info-os +$inferior_pid +\\S*info-os.*" "get process groups"
+gdb_test "info os threads" ".*pid +command +tid +core.*$inferior_pid +info-os +\\d+ +\\d+.*" "get threads"
+gdb_test "info os files" ".*pid +command +file descriptor +name.*$inferior_pid +info-os +\\d+ +/dev/null.*" "get file descriptors"
+gdb_test "info os sockets" ".*local address +local port +remote address +remote port +state +user +family +protocol.*0\\.0\\.0\\.0 +$port +0\\.0\\.0\\.0 +0 +LISTEN +\\S+ +INET +STREAM.*" "get internet-domain sockets"
+gdb_test "info os shm" ".*key +shmid +permissions +size +creator command +last op\\. command +num attached +user +group +creator user +creator group +last shmat\\(\\) time +last shmdt\\(\\) time +last shmctl\\(\\) time.*3925 +$shmid +666 +4096 +info-os +.*" "get shared-memory regions"
+gdb_test "info os semaphores" ".*key +semid +permissions +num semaphores +user +group +creator user +creator group +last semop\\(\\) time +last semctl\\(\\) time.*7428 +$semid +666 +1 +.*" "get semaphores"
+gdb_test "info os msg" ".*key +msqid +permissions +num used bytes +num messages +last msgsnd\\(\\) command +last msgrcv\\(\\) command +user +group +creator user +creator group +last msgsnd\\(\\) time +last msgrcv\\(\\) time +last msgctl\\(\\) time.*5294 +$msqid +666 +.*" "get message queues"
+
+# The SysV IPC primitives linger on after the creating process is killed
+# unless they are destroyed explicitly, so allow the test program to tidy
+# up after itself.  Note that the test program attempts to delete and
+# recreate the shared-memory region if it already exists, in case a
+# previous run failed before having a chance to clean up.  The tests for
+# semaphores and message queues should still work with primitives from
+# previous runs.
+send_gdb "continue\n"

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH]  Add extra 'info os' information types for Linux (trunk and 7.4)
  2011-12-27  4:56     ` [PATCH] Add extra 'info os' information types for Linux (trunk and 7.4) Stan Shebs
@ 2011-12-27  9:32       ` Eli Zaretskii
  2011-12-27 21:30         ` Mark Kettenis
  2011-12-28  0:05         ` Stan Shebs
  2011-12-29 20:34       ` Doug Evans
  1 sibling, 2 replies; 19+ messages in thread
From: Eli Zaretskii @ 2011-12-27  9:32 UTC (permalink / raw)
  To: Stan Shebs; +Cc: gdb-patches

> Date: Mon, 26 Dec 2011 20:28:43 -0800
> From: Stan Shebs <stanshebs@earthlink.net>
> 
> Here is a third revision of the 'info os' additions for Linux; it rolls 
> up Kwok's original patch plus requested edits, plus a few more comments 
> and tweaks.  I plan to commit this in a day or so, if there are no 
> objections.

I already voiced an objection the first time: I think Linux-specific
OS information doesn't belong to "info os", which should be for
commands generally available on all supported systems.  I would
support an "info linux" command for what you want here.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH]  Add extra 'info os' information types for Linux (trunk and 7.4)
  2011-12-27  9:32       ` Eli Zaretskii
@ 2011-12-27 21:30         ` Mark Kettenis
  2011-12-27 23:23           ` Eli Zaretskii
  2011-12-28  0:05         ` Stan Shebs
  1 sibling, 1 reply; 19+ messages in thread
From: Mark Kettenis @ 2011-12-27 21:30 UTC (permalink / raw)
  To: eliz; +Cc: stanshebs, gdb-patches

> Date: Tue, 27 Dec 2011 01:29:19 -0500
> From: Eli Zaretskii <eliz@gnu.org>
> 
> > Date: Mon, 26 Dec 2011 20:28:43 -0800
> > From: Stan Shebs <stanshebs@earthlink.net>
> > 
> > Here is a third revision of the 'info os' additions for Linux; it rolls 
> > up Kwok's original patch plus requested edits, plus a few more comments 
> > and tweaks.  I plan to commit this in a day or so, if there are no 
> > objections.
> 
> I already voiced an objection the first time: I think Linux-specific
> OS information doesn't belong to "info os", which should be for
> commands generally available on all supported systems.  I would
> support an "info linux" command for what you want here.

I have largely ignored this discussion so far.  The operating system
of my choice comes with a fairly complete and consistent set of tools
to gather information from the OS, and I feel GDB should not try to
reimplement functionality that already exists.  But I realize that
things are a little bit different on Linux, especially when debugging
"embedded" systems remotely.  There is a clear need for this sort of
additional information about the system the process is running on,
given the fact that diffs adding this kind of functionality keep
popping up.

Eli, I don't think your objection makes a lot of sense.  The "info os"
is a generic command for displaying "osdata" that's made available by
the backend.  It is for the backend to decide what information is made
available.  This diff just adds a bit more "osdata" to the Linux
native backend and the Linux gdbserver remote backend.  I do think
though that the way Stan's diff changes the documentation of the "info
os" command isn't quite right.  The generic description of the "info
os" command should come first, followed by documentation of the
specific "osdata" types for the backends that are part of GDB.

I am a bit worried though that the "info proc" stuff that was recently
discussed seems to overlap with the "info os" stuff implemented here.
I also feel that new functionality like this doesn't really belong on
the 7.4 branch.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH]  Add extra 'info os' information types for Linux (trunk and 7.4)
  2011-12-27 21:30         ` Mark Kettenis
@ 2011-12-27 23:23           ` Eli Zaretskii
  2011-12-28 20:48             ` Mark Kettenis
  0 siblings, 1 reply; 19+ messages in thread
From: Eli Zaretskii @ 2011-12-27 23:23 UTC (permalink / raw)
  To: Mark Kettenis; +Cc: stanshebs, gdb-patches

> Date: Tue, 27 Dec 2011 22:24:39 +0100 (CET)
> From: Mark Kettenis <mark.kettenis@xs4all.nl>
> CC: stanshebs@earthlink.net, gdb-patches@sourceware.org
> 
> Eli, I don't think your objection makes a lot of sense.

That kind of argument is not an efficient way of making me change my
mind.

>  The "info os"
> is a generic command for displaying "osdata" that's made available by
> the backend.  It is for the backend to decide what information is made
> available.  This diff just adds a bit more "osdata" to the Linux
> native backend and the Linux gdbserver remote backend.

Then let's lump there also the DOS- and Windows-specific "info"
commands for a good measure.

IOW, either "info os" is a hodgepodge of every OS-specific information
we provide about the process being debugged, or we have OS-specific
"info FOO" ("info dos", "info w32", "info linux", etc.) commands.
Having some of this and some of that is just inconsistent.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH]  Add extra 'info os' information types for Linux (trunk and 7.4)
  2011-12-27  9:32       ` Eli Zaretskii
  2011-12-27 21:30         ` Mark Kettenis
@ 2011-12-28  0:05         ` Stan Shebs
  2011-12-28  3:51           ` Joel Brobecker
                             ` (2 more replies)
  1 sibling, 3 replies; 19+ messages in thread
From: Stan Shebs @ 2011-12-28  0:05 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

On 12/26/11 10:29 PM, Eli Zaretskii wrote:
>> Date: Mon, 26 Dec 2011 20:28:43 -0800
>> From: Stan Shebs<stanshebs@earthlink.net>
>>
>> Here is a third revision of the 'info os' additions for Linux; it rolls
>> up Kwok's original patch plus requested edits, plus a few more comments
>> and tweaks.  I plan to commit this in a day or so, if there are no
>> objections.
> I already voiced an objection the first time: I think Linux-specific
> OS information doesn't belong to "info os", which should be for
> commands generally available on all supported systems.  I would
> support an "info linux" command for what you want here.
>

Yeah, I see that went by without comment at the time, but it's a fair point.

I think the answer is that there would be few if any "info os" 
subcommands that would be genuinely common to all operating systems that 
GDB supports; embedded OSes may not even have a well-defined concept of 
processes.  On the other hand, one could argue that anything that is not 
totally general should be given a OS-specific subcommand, a la "info dos".

For my part, I would tend to favor "info os" for those kinds of data 
that are generic enough to be found on more than one target OS.  Things 
like processes, semaphores, and sockets are found across a broad range 
of systems large and small, and it seems unduly pedantic to require 
users to do "info linux semaphores" when targeting Linux, but "info bsd 
sem" for BSD - or worse, "info freebsd sem" vs "info openbsd sem" - and 
which flavor of BSD is Darwin most like, again? :-)  Putting things 
under "info os" means less detail for users to remember.

And although the patch at hand consists of implementations for Linux, I 
don't think any of the types of data are truly Linux-only; the IPC types 
are common to all System V inheritors for instance, and even the 
seemingly-Linux concept of loadable kernel modules now has a BSD 
equivalent.  By comparison, "info dos" has subcommands like "gdt" 
(global descriptor table) that are not meaningful for any other kind of OS.

Stan


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH]  Add extra 'info os' information types for Linux (trunk and 7.4)
  2011-12-28  0:05         ` Stan Shebs
@ 2011-12-28  3:51           ` Joel Brobecker
  2011-12-28  6:02           ` Eli Zaretskii
  2012-01-02 12:08           ` Pedro Alves
  2 siblings, 0 replies; 19+ messages in thread
From: Joel Brobecker @ 2011-12-28  3:51 UTC (permalink / raw)
  To: Stan Shebs; +Cc: Eli Zaretskii, gdb-patches

> For my part, I would tend to favor "info os" for those kinds of data
> that are generic enough to be found on more than one target OS.
> Things like processes, semaphores, and sockets are found across a
> broad range of systems large and small, and it seems unduly pedantic
> to require users to do "info linux semaphores" when targeting Linux,
> but "info bsd sem" for BSD - or worse, "info freebsd sem" vs "info
> openbsd sem" - and which flavor of BSD is Darwin most like, again?
> :-)  Putting things under "info os" means less detail for users to
> remember.

I tend to agree with that. Otherwise, we end up with exactly what
you explained, inconsistent interface across platforms.

The discussion we are having is also an indicator that we should
not put this in the 7.4 branch as well, I am afraid.

-- 
Joel

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH]  Add extra 'info os' information types for Linux (trunk and 7.4)
  2011-12-28  0:05         ` Stan Shebs
  2011-12-28  3:51           ` Joel Brobecker
@ 2011-12-28  6:02           ` Eli Zaretskii
  2012-01-02 12:08           ` Pedro Alves
  2 siblings, 0 replies; 19+ messages in thread
From: Eli Zaretskii @ 2011-12-28  6:02 UTC (permalink / raw)
  To: Stan Shebs; +Cc: gdb-patches

> Date: Tue, 27 Dec 2011 15:30:07 -0800
> From: Stan Shebs <stanshebs@earthlink.net>
> CC: gdb-patches@sourceware.org
> 
> I think the answer is that there would be few if any "info os" 
> subcommands that would be genuinely common to all operating systems that 
> GDB supports; embedded OSes may not even have a well-defined concept of 
> processes.  On the other hand, one could argue that anything that is not 
> totally general should be given a OS-specific subcommand, a la "info dos".

But "info dos" is not more OS-specific than the commands suggested
here.

> And although the patch at hand consists of implementations for Linux, I 
> don't think any of the types of data are truly Linux-only; the IPC types 
> are common to all System V inheritors for instance, and even the 
> seemingly-Linux concept of loadable kernel modules now has a BSD 
> equivalent.  By comparison, "info dos" has subcommands like "gdt" 
> (global descriptor table) that are not meaningful for any other kind of OS.

Any modern system that runs on x86 will have a GDT, it's just that
most don't let you access it easily.  So I really don't see a
fundamental difference here.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH]  Add extra 'info os' information types for Linux (trunk and 7.4)
  2011-12-27 23:23           ` Eli Zaretskii
@ 2011-12-28 20:48             ` Mark Kettenis
  0 siblings, 0 replies; 19+ messages in thread
From: Mark Kettenis @ 2011-12-28 20:48 UTC (permalink / raw)
  To: eliz; +Cc: stanshebs, gdb-patches

> Date: Wed, 28 Dec 2011 00:02:57 +0200
> From: Eli Zaretskii <eliz@gnu.org>
> 
> > Date: Tue, 27 Dec 2011 22:24:39 +0100 (CET)
> > From: Mark Kettenis <mark.kettenis@xs4all.nl>
> > CC: stanshebs@earthlink.net, gdb-patches@sourceware.org
> > 
> > Eli, I don't think your objection makes a lot of sense.
> 
> That kind of argument is not an efficient way of making me change my
> mind.

Apologies.  That came out a bit stronger than intended.  I'm not even
sure I'm trying to change your mind here.

> >  The "info os"
> > is a generic command for displaying "osdata" that's made available by
> > the backend.  It is for the backend to decide what information is made
> > available.  This diff just adds a bit more "osdata" to the Linux
> > native backend and the Linux gdbserver remote backend.
> 
> Then let's lump there also the DOS- and Windows-specific "info"
> commands for a good measure.
> 
> IOW, either "info os" is a hodgepodge of every OS-specific information
> we provide about the process being debugged, or we have OS-specific
> "info FOO" ("info dos", "info w32", "info linux", etc.) commands.
> Having some of this and some of that is just inconsistent.

I'm not disagreeing here.  It's just that the mechanism behind "info
os" is totally different so unification here is not trivial.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Add extra 'info os' information types for Linux (trunk and 7.4)
  2011-12-27  4:56     ` [PATCH] Add extra 'info os' information types for Linux (trunk and 7.4) Stan Shebs
  2011-12-27  9:32       ` Eli Zaretskii
@ 2011-12-29 20:34       ` Doug Evans
  1 sibling, 0 replies; 19+ messages in thread
From: Doug Evans @ 2011-12-29 20:34 UTC (permalink / raw)
  To: Stan Shebs; +Cc: gdb-patches

On Mon, Dec 26, 2011 at 8:28 PM, Stan Shebs <stanshebs@earthlink.net> wrote:
> Here is a third revision of the 'info os' additions for Linux; it rolls up
> Kwok's original patch plus requested edits, plus a few more comments and
> tweaks.  I plan to commit this in a day or so, if there are no objections.
>
> I'd also like to get this pushed into 7.4; although it's getting late in the
> release cycle, this code is purely Linux-native and does not have any effect
> outside of 'info os'.  The urgency is partly due to snafu on my part - Kwok
> originally posted this patch in October - and partly due to interdependency
> with Eclipse, who is doing GUI for these commands.  (And yes, there is an
> approved-but-uncommitted MI patch connected with this, that I'll be pushing
> soon.)
>
> Stan
>
> 2011-12-26  Stan Shebs <stan@codesourcery.com>
>        Kwok Cheung Yeung <kcy@codesourcery.com>
>
>    * NEWS: Describe new info os commands.
>    * common/linux-osdata.c (PID_T, TIME_T): Define.
>    (MAX_PID_T_STRLEN): New.
>    (linux_common_core_of_thread): Add comment.  Change to use PID_T and
>    MAX_PID_T_STRLEN.
>    (command_from_pid): Add comment.  Change to use PID_T.
>    (commandline_from_pid):  Change to use PID_T.
>    (user_from_pid): Add comment.
>    (get_process_owner): Add comment. Change to use PID_T and
>    MAX_PID_T_STRLEN.
>    (get_number_of_cpu_cores): Add comment.
>    (get_cores_used_by_process): Add comment.  Change to use PID_T and
>    MAX_PID_T_STRLEN.
>    (linux_xfer_osdata_processes): Change to use PID_T and
>    MAX_PID_T_STRLEN.
>    (compare_processes): New function.
>    (linux_xfer_osdata_processgroups): New function.
>    (linux_xfer_osdata_threads): Change to use PID_T.
>    (linux_xfer_osdata_fds): New function.
>    (format_socket_state, print_sockets): New functions.
>    (union socket_addr): New union.
>    (linux_xfer_osdata_isockets): New function.
>    (time_from_time_t, group_from_gid): New functions.
>    (linux_xfer_osdata_shm): New function.
>    (linux_xfer_osdata_sem): New function.
>    (linux_xfer_osdata_msg): New function.
>    (linux_xfer_osdata_modules): New function.
>    (osdata_table): Add new entries.
>    * common/buffer.c (buffer_xml_printf): Add support for long and
>    long long format specifiers.
>
>    * gdb.texinfo (Operating System Auxiliary Information): Document new
>    'info os' subcommands.
>
>    * gdb.base/info-os.exp: New file.
>    * gdb.base/info-os.c: New file.

One nit:

In info-os.exp, at the end:

+# The SysV IPC primitives linger on after the creating process is killed
+# unless they are destroyed explicitly, so allow the test program to tidy
+# up after itself.  Note that the test program attempts to delete and
+# recreate the shared-memory region if it already exists, in case a
+# previous run failed before having a chance to clean up.  The tests for
+# semaphores and message queues should still work with primitives from
+# previous runs.
+send_gdb "continue\n"

Is that robust enough?
Seems like you should also wait for the program to exit.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH]  Add extra 'info os' information types for Linux (trunk and 7.4)
  2011-12-28  0:05         ` Stan Shebs
  2011-12-28  3:51           ` Joel Brobecker
  2011-12-28  6:02           ` Eli Zaretskii
@ 2012-01-02 12:08           ` Pedro Alves
  2012-01-02 12:35             ` Eli Zaretskii
  2012-01-02 18:15             ` Doug Evans
  2 siblings, 2 replies; 19+ messages in thread
From: Pedro Alves @ 2012-01-02 12:08 UTC (permalink / raw)
  To: Stan Shebs; +Cc: Eli Zaretskii, gdb-patches

On 12/27/2011 11:30 PM, Stan Shebs wrote:
>  On 12/26/11 10:29 PM, Eli Zaretskii wrote:
> >> Date: Mon, 26 Dec 2011 20:28:43 -0800 From: Stan
> >> Shebs<stanshebs@earthlink.net>
> >>
> >> Here is a third revision of the 'info os' additions for Linux;
> >> it rolls up Kwok's original patch plus requested edits, plus a
> >> few more comments and tweaks. I plan to commit this in a day or
> >> so, if there are no objections.
> > I already voiced an objection the first time: I think
> > Linux-specific OS information doesn't belong to "info os", which
> > should be for commands generally available on all supported
> > systems. I would support an "info linux" command for what you
> > want here.
> >
>
>  Yeah, I see that went by without comment at the time, but it's a
>  fair point.
>
>  I think the answer is that there would be few if any "info os"
>  subcommands that would be genuinely common to all operating systems
>  that GDB supports; embedded OSes may not even have a well-defined
>  concept of processes. On the other hand, one could argue that
>  anything that is not totally general should be given a OS-specific
>  subcommand, a la "info dos".
>
>  For my part, I would tend to favor "info os" for those kinds of data
>  that are generic enough to be found on more than one target OS.
>  Things like processes, semaphores, and sockets are found across a
>  broad range of systems large and small, and it seems unduly pedantic
>  to require users to do "info linux semaphores" when targeting Linux,
>  but "info bsd sem" for BSD - or worse, "info freebsd sem" vs "info
>  openbsd sem" - and which flavor of BSD is Darwin most like, again?
>  :-) Putting things under "info os" means less detail for users to
>  remember.

The idea of "info os" is to leave GDB completely agnostic of what is it the
backend decides to present to the user/frontend.  GDB only knows that it is
being given a table with columns and lines.  We should not assume that 
"info os FOO"
means the same thing on different OSs.  FOO in "info os FOO" is 
completely not standardized.
We're already suffering somewhat from one bit in gdb (MI) that is 
assuming it is
(Mentor is working on a target where "info os processes" would make much 
more sense to
display its own concept of "processes", but MI uses "info os processes"
for -list-thread-groups.)  If we want to have standard classes of 
objects, and assume
some concepts and fields are present, we should put those objects in some
namespace, so that GDB can give them special treatment, like with target 
description
features.  E.g., something like "org.gdb.mutex", "org.gdb.sem", etc.

It may be important to this discussion to consider that in its present form,
"info os" is more useful in its MI variant, where the frontend queries
GDB for what tables does the backend expose (with "info os"), and then
presents them in spreadsheet-like format, all without any hardcoding.  
Exposing
more bits in the GNU/Linux backends serves the purpose of being the
reference implementation / proof-of-concept.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH]  Add extra 'info os' information types for Linux (trunk and 7.4)
  2012-01-02 12:08           ` Pedro Alves
@ 2012-01-02 12:35             ` Eli Zaretskii
  2012-01-02 19:31               ` Pedro Alves
  2012-01-03  3:05               ` Joel Brobecker
  2012-01-02 18:15             ` Doug Evans
  1 sibling, 2 replies; 19+ messages in thread
From: Eli Zaretskii @ 2012-01-02 12:35 UTC (permalink / raw)
  To: Pedro Alves; +Cc: stanshebs, gdb-patches

> Date: Mon, 02 Jan 2012 12:08:37 +0000
> From: Pedro Alves <alves.ped@gmail.com>
> CC: Eli Zaretskii <eliz@gnu.org>, gdb-patches@sourceware.org
> 
> The idea of "info os" is to leave GDB completely agnostic of what is
> it the backend decides to present to the user/frontend.  GDB only
> knows that it is being given a table with columns and lines.  We
> should not assume that "info os FOO" means the same thing on
> different OSs.  FOO in "info os FOO" is completely not standardized.

As I already wrote, I have absolutely no problems with that, provided
that we apply this logic consistently.  Doing so would mean that we
should gather all the OS-specific "info MyOS SOMETHING" under the
single "info os" roof, and remove "info dos", "info w32", etc.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Add extra 'info os' information types for Linux (trunk and 7.4)
  2012-01-02 12:08           ` Pedro Alves
  2012-01-02 12:35             ` Eli Zaretskii
@ 2012-01-02 18:15             ` Doug Evans
  2012-01-02 19:19               ` Pedro Alves
  1 sibling, 1 reply; 19+ messages in thread
From: Doug Evans @ 2012-01-02 18:15 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Stan Shebs, Eli Zaretskii, gdb-patches

On Mon, Jan 2, 2012 at 4:08 AM, Pedro Alves <alves.ped@gmail.com> wrote:
> It may be important to this discussion to consider that in its present form,
> "info os" is more useful in its MI variant, where the frontend queries
> GDB for what tables does the backend expose (with "info os"), and then
> presents them in spreadsheet-like format, all without any hardcoding.
>  Exposing
> more bits in the GNU/Linux backends serves the purpose of being the
> reference implementation / proof-of-concept.

IWBN to expose such functionality to python without python having to
parse the CLI output (i.e. funnel the xml to python).

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Add extra 'info os' information types for Linux (trunk and 7.4)
  2012-01-02 18:15             ` Doug Evans
@ 2012-01-02 19:19               ` Pedro Alves
  2012-01-02 19:41                 ` Tom Tromey
  0 siblings, 1 reply; 19+ messages in thread
From: Pedro Alves @ 2012-01-02 19:19 UTC (permalink / raw)
  To: Doug Evans; +Cc: Stan Shebs, Eli Zaretskii, gdb-patches

On 01/02/2012 06:15 PM, Doug Evans wrote:
> On Mon, Jan 2, 2012 at 4:08 AM, Pedro Alves<alves.ped@gmail.com>  wrote:
>> It may be important to this discussion to consider that in its present form,
>> "info os" is more useful in its MI variant, where the frontend queries
>> GDB for what tables does the backend expose (with "info os"), and then
>> presents them in spreadsheet-like format, all without any hardcoding.
>>   Exposing
>> more bits in the GNU/Linux backends serves the purpose of being the
>> reference implementation / proof-of-concept.
>
> IWBN to expose such functionality to python without python having to
> parse the CLI output (i.e. funnel the xml to python).

Certainly.  "info os" is all about structured data.  Whether exposed as
raw xml or as some other pre-parsed structured form is arguable; it
could be said to fit fit in the general theme of exposing mi
structures to python as well?  Not sure.

-- 
Pedro Alves

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH]  Add extra 'info os' information types for Linux (trunk and 7.4)
  2012-01-02 12:35             ` Eli Zaretskii
@ 2012-01-02 19:31               ` Pedro Alves
  2012-01-03  3:05               ` Joel Brobecker
  1 sibling, 0 replies; 19+ messages in thread
From: Pedro Alves @ 2012-01-02 19:31 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: stanshebs, gdb-patches

On 01/02/2012 12:35 PM, Eli Zaretskii wrote:
>> Date: Mon, 02 Jan 2012 12:08:37 +0000
>> From: Pedro Alves<alves.ped@gmail.com>
>> CC: Eli Zaretskii<eliz@gnu.org>, gdb-patches@sourceware.org
>>
>> The idea of "info os" is to leave GDB completely agnostic of what is
>> it the backend decides to present to the user/frontend.  GDB only
>> knows that it is being given a table with columns and lines.  We
>> should not assume that "info os FOO" means the same thing on
>> different OSs.  FOO in "info os FOO" is completely not standardized.
>
> As I already wrote, I have absolutely no problems with that, provided
> that we apply this logic consistently.  Doing so would mean that we
> should gather all the OS-specific "info MyOS SOMETHING" under the
> single "info os" roof, and remove "info dos", "info w32", etc.

Provided those commands output tabular form data, and, have no
dependency on current inferior context (e.g., apply to the
current inferior only), then it would work.  I don't think at
least "info w32" is not a good fit for that reason.  "info os"
really lists info about everything (all processes, threads, etc.) 
running on the target.  E.g., on GNU/Linux, "info os sem" lists
all semaphores in the system, not just the current inferior's.

Also, "info os" is not a full replacement for a hard coded command
today.  E.g., we'd lose the specific online help for those
commands' and their sub-fields, given the generality of "info os".

-- 
Pedro Alves

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] Add extra 'info os' information types for Linux (trunk and 7.4)
  2012-01-02 19:19               ` Pedro Alves
@ 2012-01-02 19:41                 ` Tom Tromey
  0 siblings, 0 replies; 19+ messages in thread
From: Tom Tromey @ 2012-01-02 19:41 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Doug Evans, Stan Shebs, Eli Zaretskii, gdb-patches

>>>>> "Pedro" == Pedro Alves <alves.ped@gmail.com> writes:

Pedro> Certainly.  "info os" is all about structured data.  Whether exposed as
Pedro> raw xml or as some other pre-parsed structured form is arguable; it
Pedro> could be said to fit fit in the general theme of exposing mi
Pedro> structures to python as well?  Not sure.

Recently I have been thinking that emit_stop_event (emitting normal
stops as Python events) should probably call bpstat_print and expose the
resulting information as fields of the event.  This in turn would need a
new ui-out that can create Python objects.

If there is sufficient need we can generalize this idea.
There's a PR about this already.

Tom

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH]  Add extra 'info os' information types for Linux (trunk and 7.4)
  2012-01-02 12:35             ` Eli Zaretskii
  2012-01-02 19:31               ` Pedro Alves
@ 2012-01-03  3:05               ` Joel Brobecker
  1 sibling, 0 replies; 19+ messages in thread
From: Joel Brobecker @ 2012-01-03  3:05 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Pedro Alves, stanshebs, gdb-patches

> As I already wrote, I have absolutely no problems with that, provided
> that we apply this logic consistently.  Doing so would mean that we
> should gather all the OS-specific "info MyOS SOMETHING" under the
> single "info os" roof, and remove "info dos", "info w32", etc.

Generally speaking, I tend to agree - it is best to have a general
command that each platform can implement rather than each having
their own command . People like myself who work on so many different
platforms tend to lose out on these little commands.  I've seen this
sort of transition with the command used to list shared libraries on
Windows, for instance.  But, as Pedro shows, the devil is in the
details. I think we need to look at each command and deal with it
individually.

-- 
Joel

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2012-01-03  3:05 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-10-12 18:29 [PATCH] Add extra 'info os' information types for Linux Kwok Cheung Yeung
2011-10-21 23:38 ` Tom Tromey
2011-11-23 18:00   ` Kwok Cheung Yeung
2011-12-27  4:56     ` [PATCH] Add extra 'info os' information types for Linux (trunk and 7.4) Stan Shebs
2011-12-27  9:32       ` Eli Zaretskii
2011-12-27 21:30         ` Mark Kettenis
2011-12-27 23:23           ` Eli Zaretskii
2011-12-28 20:48             ` Mark Kettenis
2011-12-28  0:05         ` Stan Shebs
2011-12-28  3:51           ` Joel Brobecker
2011-12-28  6:02           ` Eli Zaretskii
2012-01-02 12:08           ` Pedro Alves
2012-01-02 12:35             ` Eli Zaretskii
2012-01-02 19:31               ` Pedro Alves
2012-01-03  3:05               ` Joel Brobecker
2012-01-02 18:15             ` Doug Evans
2012-01-02 19:19               ` Pedro Alves
2012-01-02 19:41                 ` Tom Tromey
2011-12-29 20:34       ` Doug Evans

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).