diff options
author | Kwok Yeung <kcy@sourceware.org> | 2011-07-21 23:46:12 +0000 |
---|---|---|
committer | Kwok Yeung <kcy@sourceware.org> | 2011-07-21 23:46:12 +0000 |
commit | d26e3629bbf1122feb8400bdeb8da32931c24d61 (patch) | |
tree | aeb70412b9df767983a9ff2e99a05537ad96e18e /gdb/common/linux-osdata.c | |
parent | edc849905405c3c77b7655f669f874e8b0d227e0 (diff) | |
download | binutils-d26e3629bbf1122feb8400bdeb8da32931c24d61.zip binutils-d26e3629bbf1122feb8400bdeb8da32931c24d61.tar.gz binutils-d26e3629bbf1122feb8400bdeb8da32931c24d61.tar.bz2 |
2011-07-22 Kwok Cheung Yeung <kcy@codesourcery.com>
gdb/
* defs.h: Add guard against inclusion in gdbserver.
(struct ptid, ptid_t): Move to common/ptid.h.
(xfree, xzalloc, xasprintf, xvasprintf, xstrprintf, xstrvprintf,
xsnprintf, internal_error): Move to common/common-utils.h.
(nomem): Delete.
* gdb_assert.h: Move into common/ sub-directory.
* gdb_locale.h: Ditto.
* gdb_dirent.h: Ditto.
* inferior.h (minus_one_ptid, null_ptid, ptid_build, pid_to_ptid,
ptid_get_pid, ptid_get_lwp, ptid_get_tid, ptid_equal, ptid_is_pid):
Move into common/ptid.h.
* xml-support.c (xml_escape_text): Move into common/xml-utils.c.
(gdb_xml_create_parser_and_cleanup_1, xml_fetch_context_from_file):
Change nomem to malloc_failure.
* xml-support.h (xml_escape_text): Move into common/xml-utils.h.
* utils.c (nomem): Rename to malloc_failure.
(xmalloc, xzalloc, xrealloc, xcalloc, xfree, xstrprintf, xasprintf,
xvasprintf, xstrvprintf, xsnprintf): Move to common/common-utils.c.
(gdb_buildargv): Change nomem to malloc_failure.
* infrun.c (null_ptid, minus_one_ptid, ptid_build, pid_to_ptid,
ptid_get_pid, ptid_get_lwp, ptid_get_tid, ptid_equal,
ptid_is_pid): Move into common/ptid.c.
(initialize_infrun): Delete initialization of null_ptid and
minus_one_ptid.
* linux-nat.c (linux_nat_xfer_osdata): Defer to
linux_common_xfer_osdata.
* Makefile.in (SFILES): Add common/common-utils.c, common/xml-utils.c,
common/ptid.c and common/buffer.c.
(HFILES_NO_SRCDIR): Add common/common-utils.h, common/xml-utils.h,
common/ptid.h, common/buffer.h and common/linux-osdata.h.
(COMMON_OBS): Add xml-utils.o, common-utils.o, buffer.o and ptid.o.
(common-utils.o, xml-utils.o, ptid.o, buffer.o, linux-osdata.o): New
rules.
* common/gdb_assert.h: New.
* common/gdb_dirent.h: New.
* common/gdb_locale.h: New.
* common/buffer.c: New.
* common/buffer.h: New.
* common/ptid.c: New.
* common/ptid.h: New.
* common/xml-utils.c: New.
* common/xml-utils.h: New.
* common/common-utils.c: New.
* common/common-utils.h: New.
* common/linux-osdata.c: New.
* common/linux-osdata.h: New.
* config/alpha/alpha-linux.mh (NATDEPFILES): Add linux-osdata.o.
* config/arm/linux.mh (NATDEPFILES): Ditto.
* config/i386/linux.mh (NATDEPFILES): Ditto.
* config/i386/linux64.mh (NATDEPFILES): Ditto.
* config/ia64/linux.mh (NATDEPFILES): Ditto.
* config/m32r/linux.mh (NATDEPFILES): Ditto.
* config/m68k/linux.mh (NATDEPFILES): Ditto.
* config/mips/linux.mh (NATDEPFILES): Ditto.
* config/pa/linux.mh (NATDEPFILES): Ditto.
* config/powerpc/linux.mh (NATDEPFILES): Ditto.
* config/powerpc/ppc64-linux.mh (NATDEPFILES): Ditto.
* config/s390/s390.mh (NATDEPFILES): Ditto.
* config/sparc/linux.mh (NATDEPFILES): Ditto.
* config/sparc/linux64.mh (NATDEPFILES): Ditto.
* config/xtensa/linux.mh (NATDEPFILES): Ditto.
gdbserver/
* linux-low.c (compare_ints, unique, list_threads, show_process,
linux_core_of_thread): Delete.
(linux_target_ops): Change linux_core_of_thread to
linux_common_core_of_thread.
(linux_qxfer_osdata): Defer to linux_common_xfer_osdata.
* utils.c (malloc_failure): Change type of argument.
(xmalloc, xrealloc, xcalloc, xsnprintf): Delete.
* Makefile.in (SFILES): Add common/common-utils.c, common/xml-utils.c,
common/linux-osdata.c, common/ptid.c and common/buffer.c.
(OBS): Add xml-utils.o, common-utils.o, ptid.o and buffer.o.
(IPA_OBJS): Add common-utils-ipa.o.
(ptid_h, linux_osdata_h): New macros.
(server_h): Add common/common-utils.h, common/xml-utils.h,
common/buffer.h, common/gdb_assert.h, common/gdb_locale.h and
common/ptid.h.
(common-utils-ipa.o, common-utils.o, xml-utils.o, linux-osdata.o,
ptid.o, buffer.o): New rules.
(linux-low.o): Add common/linux-osdata.h as a dependency.
* configure.srv (srv_tgtobj): Add linux-osdata.o to Linux targets.
* configure.ac: Add AC_HEADER_DIRENT check.
* config.in: Regenerate.
* configure: Regenerate.
* remote-utils.c (xml_escape_text): Delete.
(buffer_grow, buffer_free, buffer_init, buffer_finish,
buffer_xml_printf): Move to common/buffer.c.
* server.c (main): Remove call to initialize_inferiors.
* server.h (struct ptid, ptid_t, minus_one_ptid, null_ptid,
ptid_build, pid_to_ptid, ptid_get_pid, ptid_get_lwp, ptid_get_tid,
ptid_equal, ptid_is_pid, initialize_inferiors, xml_escape_text,
internal_error, gdb_assert, gdb_assert_fail): Delete.
(struct buffer, buffer_grow, buffer_free, buffer_init, buffer_finish,
buffer_xml_printf, buffer_grow_str, buffer_grow_str0): Move to
common/buffer.h.
* inferiors.c (null_ptid, minus_one_ptid, ptid_build, pid_to_ptid,
ptid_get_pid, ptid_get_lwp, ptid_get_tid, ptid_equal, ptid_is_pid,
initialize_inferiors): Delete.
Diffstat (limited to 'gdb/common/linux-osdata.c')
-rw-r--r-- | gdb/common/linux-osdata.c | 586 |
1 files changed, 586 insertions, 0 deletions
diff --git a/gdb/common/linux-osdata.c b/gdb/common/linux-osdata.c new file mode 100644 index 0000000..78659d4 --- /dev/null +++ b/gdb/common/linux-osdata.c @@ -0,0 +1,586 @@ +/* Linux-specific functions to retrieve OS data. + + Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc. + + This file is part of GDB. + + 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/>. */ + +#ifdef GDBSERVER +#include "server.h" +#else +#include "defs.h" +#endif + +#include "linux-osdata.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <utmp.h> +#include <time.h> +#include <unistd.h> +#include <pwd.h> +#include <grp.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "xml-utils.h" +#include "buffer.h" +#include "gdb_assert.h" +#include "gdb_dirent.h" + +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]; + FILE *f; + char *content = NULL; + char *p; + char *ts = 0; + int content_read = 0; + int i; + int core; + + sprintf (filename, "/proc/%d/task/%ld/stat", + ptid_get_pid (ptid), ptid_get_lwp (ptid)); + f = fopen (filename, "r"); + if (!f) + return -1; + + for (;;) + { + int n; + content = xrealloc (content, content_read + 1024); + n = fread (content + content_read, 1, 1024, f); + content_read += n; + if (n < 1024) + { + content[content_read] = '\0'; + break; + } + } + + p = strchr (content, '('); + + /* Skip ")". */ + if (p != NULL) + p = strchr (p, ')'); + if (p != NULL) + p++; + + /* If the first field after program name has index 0, then core number is + the field with index 36. There's no constant for that anywhere. */ + if (p != NULL) + p = strtok_r (p, " ", &ts); + for (i = 0; p != NULL && i != 36; ++i) + p = strtok_r (NULL, " ", &ts); + + if (p == NULL || sscanf (p, "%d", &core) == 0) + core = -1; + + xfree (content); + fclose (f); + + return core; +} + +static void +command_from_pid (char *command, int maxlen, pid_t pid) +{ + char *stat_path = xstrprintf ("/proc/%d/stat", pid); + FILE *fp = fopen (stat_path, "r"); + + command[0] = '\0'; + + if (fp) + { + /* sizeof (cmd) should be greater or equal to TASK_COMM_LEN (in + 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); + + if (items_read == 2 && pid == stat_pid) + { + cmd[strlen (cmd) - 1] = '\0'; /* Remove trailing parenthesis. */ + strncpy (command, cmd + 1, maxlen); /* Ignore leading parenthesis. */ + } + + fclose (fp); + } + else + { + /* Return the PID if a /proc entry for the process cannot be found. */ + snprintf (command, maxlen, "%d", pid); + } + + command[maxlen - 1] = '\0'; /* Ensure string is null-terminated. */ + + 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. */ + +static char * +commandline_from_pid (pid_t pid) +{ + char *pathname = xstrprintf ("/proc/%d/cmdline", pid); + char *commandline = NULL; + FILE *f = fopen (pathname, "r"); + + if (f) + { + size_t len = 0; + + while (!feof (f)) + { + char buf[1024]; + size_t read_bytes = fread (buf, 1, sizeof (buf), f); + + if (read_bytes) + { + commandline = (char *) xrealloc (commandline, len + read_bytes + 1); + memcpy (commandline + len, buf, read_bytes); + len += read_bytes; + } + } + + fclose (f); + + if (commandline) + { + size_t i; + + /* Replace null characters with spaces. */ + for (i = 0; i < len; ++i) + if (commandline[i] == '\0') + commandline[i] = ' '; + + commandline[len] = '\0'; + } + else + { + /* 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); + + len = strlen (commandline); + if (len < 31) + strcat (commandline, "]"); + } + } + + xfree (pathname); + + return commandline; +} + +static void +user_from_uid (char *user, int maxlen, uid_t uid) +{ + struct passwd *pwentry = getpwuid (uid); + + if (pwentry) + { + strncpy (user, pwentry->pw_name, maxlen); + user[maxlen - 1] = '\0'; /* Ensure that the user name is null-terminated. */ + } + else + user[0] = '\0'; +} + +static int +get_process_owner (uid_t *owner, pid_t pid) +{ + struct stat statbuf; + char procentry[sizeof ("/proc/4294967295")]; + + sprintf (procentry, "/proc/%d", pid); + + if (stat (procentry, &statbuf) == 0 && S_ISDIR (statbuf.st_mode)) + { + *owner = statbuf.st_uid; + return 0; + } + else + return -1; +} + +static int +get_number_of_cpu_cores (void) +{ + int cores = 0; + FILE *f = fopen ("/proc/cpuinfo", "r"); + + while (!feof (f)) + { + char buf[512]; + char *p = fgets (buf, sizeof (buf), f); + + if (p && strncmp (buf, "processor", 9) == 0) + ++cores; + } + + fclose (f); + + return 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) +{ + char taskdir[sizeof ("/proc/4294967295/task")]; + DIR *dir; + struct dirent *dp; + int task_count = 0; + + sprintf (taskdir, "/proc/%d/task", pid); + dir = opendir (taskdir); + + while ((dp = readdir (dir)) != NULL) + { + pid_t tid; + int core; + + if (!isdigit (dp->d_name[0]) + || NAMELEN (dp) > sizeof ("4294967295") - 1) + continue; + + tid = atoi (dp->d_name); + core = linux_common_core_of_thread (ptid_build (pid, tid, 0)); + + if (core >= 0) + { + ++cores[core]; + ++task_count; + } + } + + closedir (dir); + + return task_count; +} + +static LONGEST +linux_xfer_osdata_processes (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=\"processes\">\n"); + + dirp = opendir ("/proc"); + if (dirp) + { + const int num_cores = get_number_of_cpu_cores (); + struct dirent *dp; + + while ((dp = readdir (dirp)) != NULL) + { + pid_t pid; + uid_t owner; + char user[UT_NAMESIZE]; + char *command_line; + int *cores; + int task_count; + char *cores_str; + int i; + + if (!isdigit (dp->d_name[0]) + || NAMELEN (dp) > sizeof ("4294967295") - 1) + continue; + + sscanf (dp->d_name, "%d", &pid); + command_line = commandline_from_pid (pid); + + if (get_process_owner (&owner, pid) == 0) + user_from_uid (user, sizeof (user), owner); + else + strcpy (user, "?"); + + /* Find CPU cores used by the process. */ + cores = (int *) xcalloc (num_cores, sizeof (int)); + task_count = get_cores_used_by_process (pid, cores); + cores_str = (char *) xcalloc (task_count, sizeof ("4294967295") + 1); + + for (i = 0; i < num_cores && task_count > 0; ++i) + if (cores[i]) + { + char core_str[sizeof ("4294967205")]; + + sprintf (core_str, "%d", i); + strcat (cores_str, core_str); + + task_count -= cores[i]; + if (task_count > 0) + strcat (cores_str, ","); + } + + xfree (cores); + + buffer_xml_printf ( + &buffer, + "<item>" + "<column name=\"pid\">%d</column>" + "<column name=\"user\">%s</column>" + "<column name=\"command\">%s</column>" + "<column name=\"cores\">%s</column>" + "</item>", + pid, + user, + command_line ? command_line : "", + cores_str); + + xfree (command_line); + xfree (cores_str); + } + + closedir (dirp); + } + + 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) +{ + /* 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=\"threads\">\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)) + { + DIR *dirp2; + char *pathname; + pid_t pid; + char command[32]; + + pathname = xstrprintf ("/proc/%s/task", dp->d_name); + + pid = atoi (dp->d_name); + command_from_pid (command, sizeof (command), pid); + + dirp2 = opendir (pathname); + + if (dirp2) + { + struct dirent *dp2; + + while ((dp2 = readdir (dirp2)) != NULL) + { + pid_t tid; + int core; + + if (!isdigit (dp2->d_name[0]) + || NAMELEN (dp2) > sizeof ("4294967295") - 1) + continue; + + tid = atoi (dp2->d_name); + core = linux_common_core_of_thread (ptid_build (pid, tid, 0)); + + buffer_xml_printf ( + &buffer, + "<item>" + "<column name=\"pid\">%d</column>" + "<column name=\"command\">%s</column>" + "<column name=\"tid\">%d</column>" + "<column name=\"core\">%d</column>" + "</item>", + pid, + command, + tid, + core); + } + + 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; +} + +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 }, + { NULL, NULL, NULL } +}; + +LONGEST +linux_common_xfer_osdata (const char *annex, gdb_byte *readbuf, + ULONGEST offset, LONGEST len) +{ + if (!annex || *annex == '\0') + { + static const char *buf; + static LONGEST len_avail = -1; + static struct buffer buffer; + + if (offset == 0) + { + int i; + + if (len_avail != -1 && len_avail != 0) + buffer_free (&buffer); + len_avail = 0; + buf = NULL; + buffer_init (&buffer); + buffer_grow_str (&buffer, "<osdata type=\"types\">\n"); + + for (i = 0; osdata_table[i].type; ++i) + buffer_xml_printf ( + &buffer, + "<item>" + "<column name=\"Type\">%s</column>" + "<column name=\"Description\">%s</column>" + "</item>", + osdata_table[i].type, + osdata_table[i].description); + + 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; + } + else + { + int i; + + for (i = 0; osdata_table[i].type; ++i) + { + if (strcmp (annex, osdata_table[i].type) == 0) + { + gdb_assert (readbuf); + + return (osdata_table[i].getter) (readbuf, offset, len); + } + } + + return 0; + } +} + |