public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 1/2] gdbsupport, gdb: add read_text_file_to_string, use it in linux_common_core_of_thread
@ 2022-11-04 15:51 Simon Marchi
  2022-11-04 15:51 ` [PATCH 2/2] gdb/linux-nat: get core count using /sys/devices/system/cpu/possible Simon Marchi
  2022-11-08 17:13 ` [PATCH 1/2] gdbsupport, gdb: add read_text_file_to_string, use it in linux_common_core_of_thread Tom Tromey
  0 siblings, 2 replies; 7+ messages in thread
From: Simon Marchi @ 2022-11-04 15:51 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

I would like to add more code to nat/linux-osdata.c that reads an entire
file from /proc or /sys and processes it as a string afterwards.  I
would like to avoid duplicating the somewhat error-prone code that reads
an entire file to a buffer.  I think we should have a utility function
that does that.

Add read_file_to_string to gdbsupport/filestuff.{c,h}, and make
linux_common_core_of_thread use it.  I want to make the new function
return an std::string, and because strtok doesn't play well with
std::string (it requires a `char *`, std::string::c_str returns a `const
char *`), change linux_common_core_of_thread to use std::string methods
instead.

Change-Id: I1793fda72a82969c28b944a84acb953f74c9230a
---
 gdb/nat/linux-osdata.c  | 52 +++++++++++++++++------------------------
 gdbsupport/filestuff.cc | 35 +++++++++++++++++++++++++++
 gdbsupport/filestuff.h  |  2 ++
 3 files changed, 58 insertions(+), 31 deletions(-)

diff --git a/gdb/nat/linux-osdata.c b/gdb/nat/linux-osdata.c
index 3c31ee6fe50..f9c43f6691e 100644
--- a/gdb/nat/linux-osdata.c
+++ b/gdb/nat/linux-osdata.c
@@ -62,49 +62,39 @@ int
 linux_common_core_of_thread (ptid_t ptid)
 {
   char filename[sizeof ("/proc//task//stat") + 2 * MAX_PID_T_STRLEN];
-  char *content = NULL;
-  char *p;
-  char *ts = 0;
-  int content_read = 0;
-  int i;
   int core;
 
   sprintf (filename, "/proc/%lld/task/%lld/stat",
 	   (PID_T) ptid.pid (), (PID_T) ptid.lwp ());
-  gdb_file_up f = gdb_fopen_cloexec (filename, "r");
-  if (!f)
-    return -1;
 
-  for (;;)
-    {
-      int n;
-      content = (char *) xrealloc (content, content_read + 1024);
-      n = fread (content + content_read, 1, 1024, f.get ());
-      content_read += n;
-      if (n < 1024)
-	{
-	  content[content_read] = '\0';
-	  break;
-	}
-    }
+  gdb::optional<std::string> content = read_text_file_to_string (filename);
+  if (!content.has_value ())
+    return -1;
 
   /* ps command also relies on no trailing fields ever contain ')'.  */
-  p = strrchr (content, ')');
-  if (p != NULL)
-    p++;
+  std::string::size_type pos = content->find_last_of (')');
+  if (pos == std::string::npos)
+    return -1;
 
   /* 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);
+     the field with index 36 (so, the 37th).  There's no constant for that
+     anywhere.  */
+  for (int i = 0; i < 37; ++i)
+    {
+      /* Find separator.  */
+      pos = content->find_first_of (' ', pos);
+      if (pos == std::string::npos)
+	return {};
+
+      /* Find beginning of field.  */
+      pos = content->find_first_not_of (' ', pos);
+      if (pos == std::string::npos)
+	return {};
+    }
 
-  if (p == NULL || sscanf (p, "%d", &core) == 0)
+  if (sscanf (&(*content)[pos], "%d", &core) == 0)
     core = -1;
 
-  xfree (content);
-
   return core;
 }
 
diff --git a/gdbsupport/filestuff.cc b/gdbsupport/filestuff.cc
index 2dfae5a48c5..d003a7f39c4 100644
--- a/gdbsupport/filestuff.cc
+++ b/gdbsupport/filestuff.cc
@@ -501,3 +501,38 @@ mkdir_recursive (const char *dir)
       component_start = component_end;
     }
 }
+
+gdb::optional<std::string>
+read_text_file_to_string (const char *path)
+{
+  gdb_file_up file = gdb_fopen_cloexec (path, "r");
+  if (file == nullptr)
+    return {};
+
+  std::string res;
+  for (;;)
+    {
+      std::string::size_type start_size = res.size ();
+      constexpr int chunk_size = 1024;
+
+      /* Resize to accomodate CHUNK_SIZE bytes.  */
+      res.resize (start_size + chunk_size);
+
+      int n = fread (&res[start_size], 1, chunk_size, file.get ());
+      if (n == chunk_size)
+	continue;
+
+      gdb_assert (n < chunk_size);
+
+      /* Less than CHUNK means EOF or error.  If it's an error, return
+         no value.  */
+      if (ferror (file.get ()))
+	return {};
+
+      /* Resize the string according to the data we read.  */
+      res.resize (start_size + n);
+      break;
+    }
+
+  return res;
+}
diff --git a/gdbsupport/filestuff.h b/gdbsupport/filestuff.h
index 4bc9249dcbf..3932710b5dc 100644
--- a/gdbsupport/filestuff.h
+++ b/gdbsupport/filestuff.h
@@ -129,4 +129,6 @@ extern bool is_regular_file (const char *name, int *errno_ptr);
 
 extern bool mkdir_recursive (const char *dir);
 
+extern gdb::optional<std::string> read_text_file_to_string (const char *path);
+
 #endif /* COMMON_FILESTUFF_H */
-- 
2.37.3


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

* [PATCH 2/2] gdb/linux-nat: get core count using /sys/devices/system/cpu/possible
  2022-11-04 15:51 [PATCH 1/2] gdbsupport, gdb: add read_text_file_to_string, use it in linux_common_core_of_thread Simon Marchi
@ 2022-11-04 15:51 ` Simon Marchi
  2022-11-08 17:17   ` Tom Tromey
  2022-11-08 17:13 ` [PATCH 1/2] gdbsupport, gdb: add read_text_file_to_string, use it in linux_common_core_of_thread Tom Tromey
  1 sibling, 1 reply; 7+ messages in thread
From: Simon Marchi @ 2022-11-04 15:51 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

I get this test failure on my CI;

  FAIL: gdb.base/info-os.exp: get process list

The particularity of this setup is that builds are done in containers
who are allocated 4 CPUs on a machine that has 40.  The code in
nat/linux-osdata.c fails to properly fetch the core number for each
task.

linux_xfer_osdata_processes uses `sysconf (_SC_NPROCESSORS_ONLN)`, which
returns 4, so it allocates an array of 4 integers.  However, the core
numbers read from /proc/pid/task/tid/stat, by function
linux_common_core_of_thread, returns a value anywhere between 0 and 39.
The core numbers above 3 are therefore ignored, many processes end up
with no core value, and the regexp in the test doesn't match (it
requires an integer as the core field).

The way this the CPUs are exposed to the container is that the container
sees 40 CPUs "present" and "possible", but only 4 arbitrary CPUs
actually online:

    root@ci-node-jammy-amd64-04-08:~# cat /sys/devices/system/cpu/present
    0-39
    root@ci-node-jammy-amd64-04-08:~# cat /sys/devices/system/cpu/online
    5,11,24,31
    root@ci-node-jammy-amd64-04-08:~# cat /sys/devices/system/cpu/possible
    0-39

The solution proposed in this patch is to find out the number of
possible CPUs using /sys/devices/system/cpu/possible.  In practice, this
will probably always contain `0-N`, where N is the number of CPUs, minus
one.  But the documentation [1] doesn't such guarantee, so I'll assume
that it can contain a more complex range list such as `2,4-31,32-63`,
like the other files in that directory can have.  The solution is to
iterate over these numbers to find the highest possible CPU id, and
use that that value plus one as the size of the array to allocate.

[1] https://www.kernel.org/doc/Documentation/admin-guide/cputopology.rst

Change-Id: I7abce2e43b000c1327fa94cd7b99d46e49d7ccf3
---
 gdb/nat/linux-osdata.c | 70 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 66 insertions(+), 4 deletions(-)

diff --git a/gdb/nat/linux-osdata.c b/gdb/nat/linux-osdata.c
index f9c43f6691e..8639f090910 100644
--- a/gdb/nat/linux-osdata.c
+++ b/gdb/nat/linux-osdata.c
@@ -271,6 +271,68 @@ get_cores_used_by_process (PID_T pid, int *cores, const int num_cores)
   return task_count;
 }
 
+/* get_core_array_size helper that uses /sys/devices/system/cpu/possible.  */
+
+static gdb::optional<size_t>
+get_core_array_size_using_sys_possible ()
+{
+  gdb::optional<std::string> possible
+    = read_text_file_to_string ("/sys/devices/system/cpu/possible");
+
+  if (!possible.has_value ())
+    return {};
+
+  /* The format is documented here:
+
+       https://www.kernel.org/doc/Documentation/admin-guide/cputopology.rst
+
+     For the purpose of this function, we assume the file can contain a complex
+     set of ranges, like `2,4-31,32-63`.  Read all number, disregarding commands
+     and dashes, in order to find the largest possible core number.  The size
+     of the array to allocate is that plus one.  */
+
+  unsigned long max_id = 0;
+  for (std::string::size_type start = 0; start < possible->size ();)
+    {
+      const char *start_p = &(*possible)[start];
+      char *end_p;
+
+      /* Parse one number.  */
+      errno = 0;
+      unsigned long id = strtoul (start_p, &end_p, 10);
+      if (errno != 0)
+	return {};
+
+      max_id = std::max (max_id, id);
+
+      start += end_p - start_p;
+      gdb_assert (start <= possible->size ());
+
+      /* Skip comma, dash, or new line (if we are at the end).  */
+      ++start;
+    }
+
+  return max_id + 1;
+}
+
+/* Return the array size to allocate in order to be able to index it using
+   CPU core numbers.  This may be more than the actual number of cores if
+   the core numbers are not contiguous.  */
+
+static size_t
+get_core_array_size ()
+{
+  /* Using /sys/.../possible is prefered, because it handles the case where
+     we are in a container that has access to a subset of the host's cores.
+     It will return a size that considers all the CPU cores available to the
+     host.  If that fials for some reason, fall back to sysconf.  */
+  gdb::optional<size_t> count = get_core_array_size_using_sys_possible ();
+  if (count.has_value ())
+    return *count;
+
+  return sysconf (_SC_NPROCESSORS_ONLN);
+}
+
 static void
 linux_xfer_osdata_processes (struct buffer *buffer)
 {
@@ -281,7 +343,7 @@ linux_xfer_osdata_processes (struct buffer *buffer)
   dirp = opendir ("/proc");
   if (dirp)
     {
-      const int num_cores = sysconf (_SC_NPROCESSORS_ONLN);
+      const int core_array_size = get_core_array_size ();
       struct dirent *dp;
 
       while ((dp = readdir (dirp)) != NULL)
@@ -308,10 +370,10 @@ linux_xfer_osdata_processes (struct buffer *buffer)
 	    strcpy (user, "?");
 
 	  /* Find CPU cores used by the process.  */
-	  cores = XCNEWVEC (int, num_cores);
-	  task_count = get_cores_used_by_process (pid, cores, num_cores);
+	  cores = XCNEWVEC (int, core_array_size);
+	  task_count = get_cores_used_by_process (pid, cores, core_array_size);
 
-	  for (i = 0; i < num_cores && task_count > 0; ++i)
+	  for (i = 0; i < core_array_size && task_count > 0; ++i)
 	    if (cores[i])
 	      {
 		string_appendf (cores_str, "%d", i);
-- 
2.37.3


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

* Re: [PATCH 1/2] gdbsupport, gdb: add read_text_file_to_string, use it in linux_common_core_of_thread
  2022-11-04 15:51 [PATCH 1/2] gdbsupport, gdb: add read_text_file_to_string, use it in linux_common_core_of_thread Simon Marchi
  2022-11-04 15:51 ` [PATCH 2/2] gdb/linux-nat: get core count using /sys/devices/system/cpu/possible Simon Marchi
@ 2022-11-08 17:13 ` Tom Tromey
  2022-11-08 20:36   ` Simon Marchi
  1 sibling, 1 reply; 7+ messages in thread
From: Tom Tromey @ 2022-11-08 17:13 UTC (permalink / raw)
  To: Simon Marchi via Gdb-patches; +Cc: Simon Marchi

>>>>> "Simon" == Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:

Simon> I would like to add more code to nat/linux-osdata.c that reads an entire
Simon> file from /proc or /sys and processes it as a string afterwards.  I
Simon> would like to avoid duplicating the somewhat error-prone code that reads
Simon> an entire file to a buffer.  I think we should have a utility function
Simon> that does that.

Makes sense.  source_cache::get_plain_source_lines could use this btw,
though it would need some change for error reporting.

Simon> +
Simon> +gdb::optional<std::string>
Simon> +read_text_file_to_string (const char *path)

Should have a comment.

 
Simon> +extern gdb::optional<std::string> read_text_file_to_string (const char *path);

Here too.


Tom

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

* Re: [PATCH 2/2] gdb/linux-nat: get core count using /sys/devices/system/cpu/possible
  2022-11-04 15:51 ` [PATCH 2/2] gdb/linux-nat: get core count using /sys/devices/system/cpu/possible Simon Marchi
@ 2022-11-08 17:17   ` Tom Tromey
  0 siblings, 0 replies; 7+ messages in thread
From: Tom Tromey @ 2022-11-08 17:17 UTC (permalink / raw)
  To: Simon Marchi via Gdb-patches; +Cc: Simon Marchi

>>>>> "Simon" == Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:

Simon> The solution proposed in this patch is to find out the number of
Simon> possible CPUs using /sys/devices/system/cpu/possible.  [...]

FWIW this looks good to me.

Tom

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

* Re: [PATCH 1/2] gdbsupport, gdb: add read_text_file_to_string, use it in linux_common_core_of_thread
  2022-11-08 17:13 ` [PATCH 1/2] gdbsupport, gdb: add read_text_file_to_string, use it in linux_common_core_of_thread Tom Tromey
@ 2022-11-08 20:36   ` Simon Marchi
  2022-11-08 21:48     ` Tom Tromey
  0 siblings, 1 reply; 7+ messages in thread
From: Simon Marchi @ 2022-11-08 20:36 UTC (permalink / raw)
  To: Tom Tromey, Simon Marchi via Gdb-patches; +Cc: Simon Marchi

On 11/8/22 12:13, Tom Tromey wrote:
>>>>>> "Simon" == Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:
> 
> Simon> I would like to add more code to nat/linux-osdata.c that reads an entire
> Simon> file from /proc or /sys and processes it as a string afterwards.  I
> Simon> would like to avoid duplicating the somewhat error-prone code that reads
> Simon> an entire file to a buffer.  I think we should have a utility function
> Simon> that does that.
> 
> Makes sense.  source_cache::get_plain_source_lines could use this btw,
> though it would need some change for error reporting.

Ok, although it would need a bit of refactoring, because that uses
open_source_file, which returns a scoped_fd.

I also spotted xml_fetch_content_from_file.  The problem here is that it
returns a gdb::char_vector, not an std::string.  To change it, we would
have to change to std::string all the way to target_read_stralloc.  I
think it would be nice (it's nicer to use std::string for strings than
gdb::char_vector), but a bit too much just for this patch.

> 
> Simon> +
> Simon> +gdb::optional<std::string>
> Simon> +read_text_file_to_string (const char *path)
> 
> Should have a comment.
> 
>  
> Simon> +extern gdb::optional<std::string> read_text_file_to_string (const char *path);
> 
> Here too.

Oops, forgot.  Does that look good to you?

diff --git a/gdbsupport/filestuff.cc b/gdbsupport/filestuff.cc
index d003a7f39c4..cf5fb13bd0c 100644
--- a/gdbsupport/filestuff.cc
+++ b/gdbsupport/filestuff.cc
@@ -502,6 +502,8 @@ mkdir_recursive (const char *dir)
     }
 }

+/* See gdbsupport/filestuff.h.  */
+
 gdb::optional<std::string>
 read_text_file_to_string (const char *path)
 {
diff --git a/gdbsupport/filestuff.h b/gdbsupport/filestuff.h
index 3932710b5dc..33362901ab8 100644
--- a/gdbsupport/filestuff.h
+++ b/gdbsupport/filestuff.h
@@ -129,6 +129,8 @@ extern bool is_regular_file (const char *name, int *errno_ptr);

 extern bool mkdir_recursive (const char *dir);

+/* Read the entire content of file PATH into an std::string.  */
+
 extern gdb::optional<std::string> read_text_file_to_string (const char *path);

 #endif /* COMMON_FILESTUFF_H */

Simon

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

* Re: [PATCH 1/2] gdbsupport, gdb: add read_text_file_to_string, use it in linux_common_core_of_thread
  2022-11-08 20:36   ` Simon Marchi
@ 2022-11-08 21:48     ` Tom Tromey
  2022-11-08 21:52       ` Simon Marchi
  0 siblings, 1 reply; 7+ messages in thread
From: Tom Tromey @ 2022-11-08 21:48 UTC (permalink / raw)
  To: Simon Marchi; +Cc: Tom Tromey, Simon Marchi via Gdb-patches, Simon Marchi

Simon> Oops, forgot.  Does that look good to you?

Yes, thank you.

Tom

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

* Re: [PATCH 1/2] gdbsupport, gdb: add read_text_file_to_string, use it in linux_common_core_of_thread
  2022-11-08 21:48     ` Tom Tromey
@ 2022-11-08 21:52       ` Simon Marchi
  0 siblings, 0 replies; 7+ messages in thread
From: Simon Marchi @ 2022-11-08 21:52 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Simon Marchi via Gdb-patches, Simon Marchi

On 11/8/22 16:48, Tom Tromey wrote:
> Simon> Oops, forgot.  Does that look good to you?
> 
> Yes, thank you.
> 
> Tom

Thanks, pushed both patches.

Simon

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

end of thread, other threads:[~2022-11-08 21:52 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-04 15:51 [PATCH 1/2] gdbsupport, gdb: add read_text_file_to_string, use it in linux_common_core_of_thread Simon Marchi
2022-11-04 15:51 ` [PATCH 2/2] gdb/linux-nat: get core count using /sys/devices/system/cpu/possible Simon Marchi
2022-11-08 17:17   ` Tom Tromey
2022-11-08 17:13 ` [PATCH 1/2] gdbsupport, gdb: add read_text_file_to_string, use it in linux_common_core_of_thread Tom Tromey
2022-11-08 20:36   ` Simon Marchi
2022-11-08 21:48     ` Tom Tromey
2022-11-08 21:52       ` Simon Marchi

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).