mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 04:24:43 +00:00
Compare commits
3 Commits
1ae9fa5c60
...
users/abur
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
889bdea181 | ||
|
|
6cb39b064b | ||
|
|
a99db75d46 |
214
gdb/corelow.c
214
gdb/corelow.c
@@ -351,40 +351,24 @@ core_target::close ()
|
||||
/* Look for sections whose names start with `.reg/' so that we can
|
||||
extract the list of threads in a core file. */
|
||||
|
||||
static void
|
||||
add_to_thread_list (asection *asect, asection *reg_sect)
|
||||
{
|
||||
int core_tid;
|
||||
int pid, lwpid;
|
||||
bool fake_pid_p = false;
|
||||
struct inferior *inf;
|
||||
/* If ASECT is a section whose name begins with '.reg/' then extract the
|
||||
lwpid after the '/' and create a new thread in INF.
|
||||
|
||||
If REG_SECT is not nullptr, and the both ASECT and REG_SECT point at the
|
||||
same position in the parent bfd object then switch to the newly created
|
||||
thread, otherwise, the selected thread is left unchanged. */
|
||||
|
||||
static void
|
||||
add_to_thread_list (asection *asect, asection *reg_sect, inferior *inf)
|
||||
{
|
||||
if (!startswith (bfd_section_name (asect), ".reg/"))
|
||||
return;
|
||||
|
||||
core_tid = atoi (bfd_section_name (asect) + 5);
|
||||
|
||||
pid = bfd_core_file_pid (core_bfd);
|
||||
if (pid == 0)
|
||||
{
|
||||
fake_pid_p = true;
|
||||
pid = CORELOW_PID;
|
||||
}
|
||||
|
||||
lwpid = core_tid;
|
||||
|
||||
inf = current_inferior ();
|
||||
if (inf->pid == 0)
|
||||
{
|
||||
inferior_appeared (inf, pid);
|
||||
inf->fake_pid_p = fake_pid_p;
|
||||
}
|
||||
|
||||
ptid_t ptid (pid, lwpid);
|
||||
|
||||
int lwpid = atoi (bfd_section_name (asect) + 5);
|
||||
ptid_t ptid (inf->pid, lwpid);
|
||||
thread_info *thr = add_thread (inf->process_target (), ptid);
|
||||
|
||||
/* Warning, Will Robinson, looking at BFD private data! */
|
||||
/* Warning, Will Robinson, looking at BFD private data! */
|
||||
|
||||
if (reg_sect != NULL
|
||||
&& asect->filepos == reg_sect->filepos) /* Did we find .reg? */
|
||||
@@ -421,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 §_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. */
|
||||
|
||||
@@ -541,12 +672,30 @@ core_target_open (const char *arg, int from_tty)
|
||||
previous session, and the frame cache being stale. */
|
||||
registers_changed ();
|
||||
|
||||
/* Find (or fake) the pid for the process in this core file, and
|
||||
initialise the current inferior with that pid. */
|
||||
bool fake_pid_p = false;
|
||||
int pid = bfd_core_file_pid (core_bfd);
|
||||
if (pid == 0)
|
||||
{
|
||||
fake_pid_p = true;
|
||||
pid = CORELOW_PID;
|
||||
}
|
||||
|
||||
inferior *inf = current_inferior ();
|
||||
gdb_assert (inf->pid == 0);
|
||||
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. */
|
||||
asection *reg_sect = bfd_get_section_by_name (core_bfd, ".reg");
|
||||
for (asection *sect : gdb_bfd_sections (core_bfd))
|
||||
add_to_thread_list (sect, reg_sect);
|
||||
add_to_thread_list (sect, reg_sect, inf);
|
||||
|
||||
if (inferior_ptid == null_ptid)
|
||||
{
|
||||
@@ -556,13 +705,10 @@ core_target_open (const char *arg, int from_tty)
|
||||
which was the "main" thread. The latter case shouldn't
|
||||
usually happen, but we're dealing with input here, which can
|
||||
always be broken in different ways. */
|
||||
thread_info *thread = first_thread_of_inferior (current_inferior ());
|
||||
thread_info *thread = first_thread_of_inferior (inf);
|
||||
|
||||
if (thread == NULL)
|
||||
{
|
||||
inferior_appeared (current_inferior (), CORELOW_PID);
|
||||
thread = add_thread_silent (target, ptid_t (CORELOW_PID));
|
||||
}
|
||||
thread = add_thread_silent (target, ptid_t (CORELOW_PID));
|
||||
|
||||
switch_to_thread (thread);
|
||||
}
|
||||
|
||||
73
gdb/testsuite/gdb.arch/core-file-pid0.exp
Normal file
73
gdb/testsuite/gdb.arch/core-file-pid0.exp
Normal file
@@ -0,0 +1,73 @@
|
||||
# This testcase is part of GDB, the GNU debugger.
|
||||
#
|
||||
# Copyright 2023 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 2 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, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
# Some kernel core files have PID 0 (for the idle task), check that
|
||||
# GDB can handle such a core file.
|
||||
|
||||
standard_testfile
|
||||
|
||||
# Set CF_NAME, the name of the compressed core file within the source
|
||||
# tree, and CF_SIZE, the size (in bytes) of the uncompressed core
|
||||
# file.
|
||||
if {[istarget "x86_64-*-linux*"]} {
|
||||
set cf_name ${testfile}.x86-64.core.bz2
|
||||
set cf_size 8757248
|
||||
} else {
|
||||
unsupported "no pre-generated core file for this target"
|
||||
}
|
||||
|
||||
# Decompress the core file.
|
||||
set corebz2file ${srcdir}/${subdir}/${cf_name}
|
||||
set corefile [decompress_bz2 $corebz2file]
|
||||
if { $corefile eq "" } {
|
||||
untested "failed to bunzip2 the core file"
|
||||
return -1
|
||||
}
|
||||
|
||||
# Check the size of the decompressed core file. Just for sanity.
|
||||
file stat ${corefile} corestat
|
||||
if { $corestat(size) != ${cf_size} } {
|
||||
untested "uncompressed core file is the wrong size"
|
||||
return -1
|
||||
}
|
||||
|
||||
# Copy over the corefile if we are remote testing.
|
||||
set corefile [gdb_remote_download host $corefile]
|
||||
|
||||
clean_restart
|
||||
|
||||
# Load the core file. At one point GDB would assert, complaining that
|
||||
# the inferior was nullptr. For now we see a message about the
|
||||
# current thread having terminated, this is because GDB gets confused
|
||||
# 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\\." \
|
||||
"#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"
|
||||
BIN
gdb/testsuite/gdb.arch/core-file-pid0.x86-64.core.bz2
Normal file
BIN
gdb/testsuite/gdb.arch/core-file-pid0.x86-64.core.bz2
Normal file
Binary file not shown.
Reference in New Issue
Block a user