aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/corelow.c150
-rw-r--r--gdb/testsuite/gdb.arch/core-file-pid0.exp12
2 files changed, 161 insertions, 1 deletions
diff --git a/gdb/corelow.c b/gdb/corelow.c
index 7312d40..321b245 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -405,6 +405,153 @@ core_file_command (const char *filename, int from_tty)
core_target_open (filename, from_tty);
}
+/* A vmcore file is a core file created by the Linux kernel at the point of
+ a crash. Each thread in the core file represents a real CPU core, and
+ the lwpid for each thread is the pid of the process that was running on
+ that core at the moment of the crash.
+
+ However, not every CPU core will have been running a process, some cores
+ will be idle. For these idle cores the CPU writes an lwpid of 0. And
+ of course, multiple cores might be idle, so there could be multiple
+ threads with an lwpid of 0.
+
+ The problem is GDB doesn't really like threads with an lwpid of 0; GDB
+ presents such a thread as a process rather than a thread. And GDB
+ certainly doesn't like multiple threads having the same lwpid, each time
+ a new thread is seen with the same lwpid the earlier thread (with the
+ same lwpid) will be deleted.
+
+ This function addresses both of these problems by assigning a fake lwpid
+ to any thread with an lwpid of 0.
+
+ GDB finds the lwpid information by looking at the bfd section names
+ which include the lwpid, e.g. .reg/NN where NN is the lwpid. This
+ function looks though all the section names looking for sections named
+ .reg/NN. If any sections are found where NN == 0, then we assign a new
+ unique value of NN. Then, in a second pass, any sections ending /0 are
+ assigned their new number.
+
+ Remember, a core file may contain multiple register sections for
+ different register sets, but the sets are always grouped by thread, so
+ we can figure out which registers should be assigned the same new
+ lwpid. For example, consider a core file containing:
+
+ .reg/0, .reg2/0, .reg/0, .reg2/0
+
+ This represents two threads, each thread contains a .reg and .reg2
+ register set. The .reg represents the start of each thread. After
+ renaming the sections will now look like this:
+
+ .reg/1, .reg2/1, .reg/2, .reg2/2
+
+ After calling this function the rest of the core file handling code can
+ treat this core file just like any other core file. */
+
+static void
+rename_vmcore_idle_reg_sections (bfd *abfd, inferior *inf)
+{
+ /* Map from the bfd section to its lwpid (the /NN number). */
+ std::vector<std::pair<asection *, int>> sections_and_lwpids;
+
+ /* The set of all /NN numbers found. Needed so we can easily find unused
+ numbers in the case that we need to rename some sections. */
+ std::unordered_set<int> all_lwpids;
+
+ /* A count of how many sections called .reg/0 we have found. */
+ unsigned zero_lwpid_count = 0;
+
+ /* Look for all the .reg sections. Record the section object and the
+ lwpid which is extracted from the section name. Spot if any have an
+ lwpid of zero. */
+ for (asection *sect : gdb_bfd_sections (core_bfd))
+ {
+ if (startswith (bfd_section_name (sect), ".reg/"))
+ {
+ int lwpid = atoi (bfd_section_name (sect) + 5);
+ sections_and_lwpids.emplace_back (sect, lwpid);
+ all_lwpids.insert (lwpid);
+ if (lwpid == 0)
+ zero_lwpid_count++;
+ }
+ }
+
+ /* If every ".reg/NN" section has a non-zero lwpid then we don't need to
+ do any renaming. */
+ if (zero_lwpid_count == 0)
+ return;
+
+ /* Assign a new number to any .reg sections with an lwpid of 0. */
+ int new_lwpid = 1;
+ for (auto &sect_and_lwpid : sections_and_lwpids)
+ if (sect_and_lwpid.second == 0)
+ {
+ while (all_lwpids.find (new_lwpid) != all_lwpids.end ())
+ new_lwpid++;
+ sect_and_lwpid.second = new_lwpid;
+ new_lwpid++;
+ }
+
+ /* Now update the names of any sections with an lwpid of 0. This is
+ more than just the .reg sections we originally found. */
+ std::string replacement_lwpid_str;
+ auto iter = sections_and_lwpids.begin ();
+ int replacement_lwpid = 0;
+ for (asection *sect : gdb_bfd_sections (core_bfd))
+ {
+ if (iter != sections_and_lwpids.end () && sect == iter->first)
+ {
+ gdb_assert (startswith (bfd_section_name (sect), ".reg/"));
+
+ int lwpid = atoi (bfd_section_name (sect) + 5);
+ if (lwpid == iter->second)
+ {
+ /* This section was not given a new number. */
+ gdb_assert (lwpid != 0);
+ replacement_lwpid = 0;
+ }
+ else
+ {
+ replacement_lwpid = iter->second;
+ ptid_t ptid (inf->pid, replacement_lwpid);
+ if (!replacement_lwpid_str.empty ())
+ replacement_lwpid_str += ", ";
+ replacement_lwpid_str += target_pid_to_str (ptid);
+ }
+
+ iter++;
+ }
+
+ if (replacement_lwpid != 0)
+ {
+ const char *name = bfd_section_name (sect);
+ size_t len = strlen (name);
+
+ if (strncmp (name + len - 2, "/0", 2) == 0)
+ {
+ /* This section needs a new name. */
+ std::string name_str
+ = string_printf ("%.*s/%d",
+ static_cast<int> (len - 2),
+ name, replacement_lwpid);
+ char *name_buf
+ = static_cast<char *> (bfd_alloc (abfd, name_str.size () + 1));
+ if (name_buf == nullptr)
+ error (_("failed to allocate space for section name '%s'"),
+ name_str.c_str ());
+ memcpy (name_buf, name_str.c_str(), name_str.size () + 1);
+ bfd_rename_section (sect, name_buf);
+ }
+ }
+ }
+
+ if (zero_lwpid_count == 1)
+ warning (_("found thread with pid 0, assigned replacement Target Id: %s"),
+ replacement_lwpid_str.c_str ());
+ else
+ warning (_("found threads with pid 0, assigned replacement Target Ids: %s"),
+ replacement_lwpid_str.c_str ());
+}
+
/* Locate (and load) an executable file (and symbols) given the core file
BFD ABFD. */
@@ -540,6 +687,9 @@ core_target_open (const char *arg, int from_tty)
inferior_appeared (inf, pid);
inf->fake_pid_p = fake_pid_p;
+ /* Rename any .reg/0 sections, giving them each a fake lwpid. */
+ rename_vmcore_idle_reg_sections (core_bfd, inf);
+
/* Build up thread list from BFD sections, and possibly set the
current thread to the .reg/NN section matching the .reg
section. */
diff --git a/gdb/testsuite/gdb.arch/core-file-pid0.exp b/gdb/testsuite/gdb.arch/core-file-pid0.exp
index b960dfe..6e91111 100644
--- a/gdb/testsuite/gdb.arch/core-file-pid0.exp
+++ b/gdb/testsuite/gdb.arch/core-file-pid0.exp
@@ -57,7 +57,17 @@ clean_restart
# and incorrectly deletes what should be the current thread.
gdb_test "core-file ${corefile}" \
[multi_line \
+ "warning: found threads with pid 0, assigned replacement Target Ids: LWP 1, LWP 2" \
+ ".*" \
"Core was generated by \[^\r\n\]+\\." \
"Program terminated with signal (?:11|SIGSEGV), Segmentation fault\\." \
- "The current thread has terminated"] \
+ "#0\\s+$hex in \[^\r\n\]+" \
+ "\\\[Current thread is 1 \\(LWP 1\\)\\\]"] \
"check core file termination reason"
+
+# And check GDB has found both threads.
+gdb_test "info threads" \
+ [multi_line \
+ "\\* 1\\s+LWP 1\\s+$hex in \[^\r\n\]+" \
+ " 2\\s+LWP 2\\s+$hex in \[^\r\n\]+"] \
+ "check both threads are visible"