Compare commits

...

86 Commits

Author SHA1 Message Date
Joel Brobecker
6bda1c19bc Set GDB version number to 14.1.
This commit changes gdb/version.in to 14.1.
2023-12-03 09:18:15 +04:00
GDB Administrator
dfd845d276 Automatic date update in version.in 2023-12-03 00:00:36 +00:00
GDB Administrator
64be8e424a Automatic date update in version.in 2023-12-02 00:00:29 +00:00
GDB Administrator
fc1974557a Automatic date update in version.in 2023-12-01 00:00:26 +00:00
GDB Administrator
3e1fd5afc4 Automatic date update in version.in 2023-11-30 00:00:48 +00:00
GDB Administrator
385e95de46 Automatic date update in version.in 2023-11-29 00:00:39 +00:00
GDB Administrator
403e9cfa01 Automatic date update in version.in 2023-11-28 00:00:43 +00:00
John Baldwin
823c11ef48 i386: Use a fallback XSAVE layout for remote targets
If a target provides a target description including registers from the
XSAVE extended region, but does not provide an XSAVE layout, use a
fallback XSAVE layout based on the included registers.  This fallback
layout matches GDB's behavior in earlier releases which assumes the
layout from Intel CPUs.

This fallback layout is currently only used for remote targets since
native targets which support XSAVE provide an explicit layout derived
from CPUID.

PR gdb/30912
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30912
Approved-By: Simon Marchi <simon.marchi@efficios.com>

(cherry picked from commit 66637e209c)
2023-11-27 13:54:50 -08:00
Tom Tromey
fb2d9542d0 Fix bug in DAP handling of 'pause' requests
While working on cancellation, I noticed that a DAP 'pause' request
would set the "do not emit the continue" flag.  This meant that a
subsequent request that should provoke a 'continue' event would
instead suppress the event.

I then tried writing a more obvious test case for this, involving an
inferior call -- and discovered that gdb.events.cont does not fire for
an inferior call.

This patch installs a new event listener for gdb.events.inferior_call
and arranges for this to emit continue and stop events when
appropriate.  It also fixes the original bug, by adding a check to
exec_and_expect_stop.

(cherry picked from commit c618a1c548)
2023-11-27 09:16:46 -07:00
GDB Administrator
5a37e5855c Automatic date update in version.in 2023-11-27 00:00:49 +00:00
GDB Administrator
e447ef22ff Automatic date update in version.in 2023-11-26 00:00:28 +00:00
GDB Administrator
1d688670c0 Automatic date update in version.in 2023-11-25 00:00:48 +00:00
GDB Administrator
f61c9af603 Automatic date update in version.in 2023-11-24 00:00:55 +00:00
GDB Administrator
ec05816a66 Automatic date update in version.in 2023-11-23 00:01:05 +00:00
GDB Administrator
6eb20c0cce Automatic date update in version.in 2023-11-22 00:00:47 +00:00
GDB Administrator
0bf1de7bb4 Automatic date update in version.in 2023-11-21 00:00:36 +00:00
GDB Administrator
643b97a37d Automatic date update in version.in 2023-11-20 00:00:23 +00:00
GDB Administrator
ab1d96b02b Automatic date update in version.in 2023-11-19 00:00:24 +00:00
GDB Administrator
e6cad0cfd3 Automatic date update in version.in 2023-11-18 00:00:34 +00:00
Tom Tromey
17f2cab5f8 Ignore static members in NoOpStructPrinter
Hannes' patch to show local variables in the TUI pointed out that
NoOpStructPrinter should ignore static members.  This patch implements
this.

(cherry picked from commit 4a1b9a4bad)
2023-11-17 08:43:04 -07:00
Tom Tromey
fa2cc08fdd Implement the notStopped DAP response
DAP specifies that a request can fail with the "notStopped" message if
the inferior is running but the request requires that it first be
stopped.

This patch implements this for gdb.  Most requests are assumed to
require a stopped inferior, and the exceptions are noted by a new
'request' parameter.

You may notice that the implementation is a bit racy.  I think this is
inherent -- unless the client waits for a stop event before sending a
request, the request may be processed at any time relative to a stop.

https://sourceware.org/bugzilla/show_bug.cgi?id=31037

Reviewed-by: Kévin Le Gouguec <legouguec@adacore.com>

(cherry picked from commit cfd00e8050)
2023-11-17 08:43:04 -07:00
Tom Tromey
c558c8d5f6 Remove ExecutionInvoker
ExecutionInvoker is no longer really needed, due to the previous DAP
refactoring.  This patch removes it in favor of an ordinary function.
One spot (the 'continue' request) could still have used it, but is
more succinctly expressed as a lambda.

Reviewed-by: Kévin Le Gouguec <legouguec@adacore.com>

(cherry picked from commit 68caad9d0b)
2023-11-17 08:43:04 -07:00
Tom Tromey
de5b5fcb89 Automatically run (most) DAP requests in gdb thread
Nearly every DAP request implementation forwards its work to the gdb
thread, using send_gdb_with_response.  This patch refactors the
'request' decorator to make this automatic, and to provide some
parameters so that the unusual requests can express their needs as
well.

In a few spots this simplifies the code by removing an unnecessary
helper function.  This could be done in more places as well if we
wanted.

The main motivation for this patch is that I thought it would be
helpful for cancellation.  I am still working on that, but meanwhile
the parameterization of 'request' makes it easy to handle the
'notStopped' response as well.

Reviewed-by: Kévin Le Gouguec <legouguec@adacore.com>
(cherry picked from commit c98921b258)
2023-11-17 08:43:04 -07:00
Tom Tromey
45b2be6618 Handle StackFrameFormat in DAP
DAP specifies a StackFrameFormat object that can be used to change how
the "name" part of a stack frame is constructed.  While this output
can already be done in a nicer way (and also letting the client choose
the formatting), nevertheless it is in the spec, so I figured I'd
implement it.

While implementing this, I discovered that the current code does not
correctly preserve frame IDs across requests.  I rewrote frame
iteration to preserve this, and it turned out to be simpler to combine
these patches.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30475

(cherry picked from commit 1920148904)
2023-11-17 07:16:39 -07:00
GDB Administrator
aad1b7a5f6 Automatic date update in version.in 2023-11-17 00:00:55 +00:00
GDB Administrator
6ab1f30817 Automatic date update in version.in 2023-11-16 00:00:50 +00:00
Tom Tromey
aa5cef35e0 Minor cleanups in ada-nested.exp
This changes ada-nested.exp to fix a test name (the test expects three
variables but is named "two"), and to iterate over all the variables
that are found.  It also adds a workaround to a problem Tom de Vries
found with an older version of GNAT -- it emits a duplicate "x".

(cherry picked from commit e1ccbfffb5)
2023-11-15 09:23:53 -07:00
GDB Administrator
5458a19397 Automatic date update in version.in 2023-11-15 00:00:40 +00:00
Tom Tromey
1df83e9a59 Update gdb.Symbol.is_variable documentation
Kévin found a bug in an earlier version of this series that was based
on a misconception I had about Symbol.is_variable.  This patch fixes
the documentation to explain the method a bit better.

Approved-By: Eli Zaretskii <eliz@gnu.org>
(cherry picked from commit 5006ea556d)
2023-11-14 08:49:55 -07:00
Tom Tromey
add31fcb87 Handle the static link in FrameDecorator
A co-worker requested that the DAP scope for a nested function's frame
also show the variables from outer frames.  DAP doesn't directly
support this notion, so this patch arranges to put these variables
into the inner frames "Locals" scope.

I chose to do this only for DAP.  For CLI and MI, gdb currently does
not do this, so this preserves the behavior.

Note that an earlier patch (see commit 4a1311ba) removed some code
that seemed to do something similar.  However, that code did not
actually work.

(cherry picked from commit ebea770b19)
2023-11-14 08:49:55 -07:00
Tom Tromey
d31566acb7 Fix a bug in DAP scopes code
While working on static links, I noticed that the DAP scopes code does
not handle the scenario where a frame decorator returns None.  This
situation should be handled identically to a frame decorator returning
an empty iterator.

(cherry picked from commit e9dacb1d6c)
2023-11-14 08:49:55 -07:00
Tom Tromey
a3ca9693ff Add gdb.Frame.static_link method
This adds a new gdb.Frame.static_link method to the gdb Python layer.
This can be used to find the static link frame for a given frame.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
(cherry picked from commit 4ead09a294)
2023-11-14 08:49:55 -07:00
Tom Tromey
f703d35ad8 Move follow_static_link to frame.c
This moves the follow_static_link function to frame.c and exports it
for use elsewhere.  The API is changed slightly to make it more
generically useful.

(cherry picked from commit 19b83d5c9b)
2023-11-14 08:49:55 -07:00
Tom Tromey
43f2540a8e Add block::function_block
This adds the method block::function_block, to easily access a block's
enclosing function block.

(cherry picked from commit ba707cadae)
2023-11-14 08:49:55 -07:00
Tom Tromey
18a2a7470f Add two convenience methods to block
This adds a couple of convenience methods, block::is_static_block and
block::is_global_block.

(cherry picked from commit edf1b9640b)
2023-11-14 08:49:55 -07:00
GDB Administrator
8015944464 Automatic date update in version.in 2023-11-14 00:00:41 +00:00
GDB Administrator
e7e8d3e6c8 Automatic date update in version.in 2023-11-13 00:00:46 +00:00
GDB Administrator
68c2310748 Automatic date update in version.in 2023-11-12 00:00:26 +00:00
GDB Administrator
45e4611f07 Automatic date update in version.in 2023-11-11 00:00:44 +00:00
GDB Administrator
35dfe80b27 Automatic date update in version.in 2023-11-10 00:00:22 +00:00
GDB Administrator
bdb2b760e4 Automatic date update in version.in 2023-11-09 00:01:01 +00:00
GDB Administrator
56c91b27cf Automatic date update in version.in 2023-11-08 00:00:45 +00:00
GDB Administrator
f8acd77d79 Automatic date update in version.in 2023-11-07 00:00:37 +00:00
GDB Administrator
30ba1331de Automatic date update in version.in 2023-11-06 00:00:38 +00:00
GDB Administrator
f5f044c6d9 Automatic date update in version.in 2023-11-05 00:00:30 +00:00
GDB Administrator
89a64d71b1 Automatic date update in version.in 2023-11-04 00:00:33 +00:00
GDB Administrator
dfa27703ba Automatic date update in version.in 2023-11-03 00:00:33 +00:00
GDB Administrator
42f8113b09 Automatic date update in version.in 2023-11-02 00:00:33 +00:00
GDB Administrator
b81c6d0cc5 Automatic date update in version.in 2023-11-01 00:01:05 +00:00
Tom Tromey
618bc9302e Implement DAP setVariable request
This patch implements the DAP setVariable request.

setVariable is a bit odd in that it specifies the variable to modify
by passing in the variable's container and the name of the variable.
This approach can't handle variable shadowing (there are a couple of
open DAP bugs on this topic), so this patch renames duplicates to
avoid the problem.

(cherry picked from commit 87e3cc466e)
2023-10-31 11:54:48 -06:00
GDB Administrator
90e0e073f0 Automatic date update in version.in 2023-10-31 00:00:44 +00:00
GDB Administrator
e8d0a795d9 Automatic date update in version.in 2023-10-30 00:00:38 +00:00
GDB Administrator
e2f96589e0 Automatic date update in version.in 2023-10-29 00:00:36 +00:00
GDB Administrator
934a14fd6b Automatic date update in version.in 2023-10-28 00:00:42 +00:00
GDB Administrator
d273f35dbc Automatic date update in version.in 2023-10-27 00:00:43 +00:00
Thiago Jung Bauermann
d24f7fe446 gdb/nat/aarch64-scalable-linux-ptrace.h: Don't include itself
GCC doesn't complain, but it's still wrong.
2023-10-26 10:20:53 -03:00
GDB Administrator
445f4eb995 Automatic date update in version.in 2023-10-26 00:00:54 +00:00
GDB Administrator
e7362a5e29 Automatic date update in version.in 2023-10-25 00:00:41 +00:00
GDB Administrator
8d1b8bcb7f Automatic date update in version.in 2023-10-24 00:01:16 +00:00
GDB Administrator
c8de56a239 Automatic date update in version.in 2023-10-23 00:00:44 +00:00
GDB Administrator
f77f816268 Automatic date update in version.in 2023-10-22 00:00:59 +00:00
GDB Administrator
7af94459a4 Automatic date update in version.in 2023-10-21 00:00:44 +00:00
GDB Administrator
f07873df4d Automatic date update in version.in 2023-10-20 00:00:28 +00:00
GDB Administrator
c7a4aa64e7 Automatic date update in version.in 2023-10-19 00:00:50 +00:00
GDB Administrator
5584da0691 Automatic date update in version.in 2023-10-18 00:00:31 +00:00
GDB Administrator
ea4174c96a Automatic date update in version.in 2023-10-17 00:01:02 +00:00
Tom Tromey
2aabf131da Have DAP handle non-Value results from 'children'
A pretty-printer's 'children' method may return values other than a
gdb.Value -- it may return any value that can be converted to a
gdb.Value.

I noticed that this case did not work for DAP.  This patch fixes the
problem.
2023-10-16 09:49:02 -06:00
Tom Tromey
ba0725dfa0 Handle gdb.LazyString in DAP
Andry pointed out that the DAP code did not properly handle
gdb.LazyString results from a pretty-printer, yielding:

    TypeError: Object of type LazyString is not JSON serializable

This patch fixes the problem, partly with a small patch in varref.py,
but mainly by implementing tp_str for LazyString.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2023-10-16 09:28:22 -06:00
Tom Tromey
8836926927 Fix register-setting response from DAP
Andry noticed that given a DAP setExpression request, where the
expression to set is a register, DAP will return the wrong value -- it
will return the old value, not the updated one.

This happens because gdb.Value.assign (which was recently added for
DAP) does not update the value.

In this patch, I chose to have the assign method update the Value
in-place.  It's also possible to have it return a new value, but this
didn't seem very useful to me.
2023-10-16 09:28:22 -06:00
Tom Tromey
c1edbcf2ba Add DAP scope cache
Andry Ogorodnik, a co-worker, noticed that multiple "scopes" requests
with the same frame would yield different variableReference values in
the response.

This patch adds a regression test for this, and adds a scope cache in
scopes.py, ensuring that multiple identical requests will get the same
response.

Tested-By: Alexandra Petlanova Hajkova <ahajkova@redhat.com>
2023-10-16 09:06:17 -06:00
Luis Machado
c9d954f934 Only allow closure lookup by address if there are threads displaced-stepping
Since commit 1e5ccb9c5f, we have an assertion in
displaced_step_buffers::copy_insn_closure_by_addr that makes sure a closure
is available whenever we have a match between the provided address argument and
the buffer address.

That is fine, but the report in PR30872 shows this assertion triggering when
it really shouldn't. After some investigation, here's what I found out.

The 32-bit Arm architecture is the only one that calls
gdbarch_displaced_step_copy_insn_closure_by_addr directly, and that's because
32-bit Arm needs to figure out the thumb state of the original instruction
that we displaced-stepped through the displaced-step buffer.

Before the assertion was put in place by commit
1e5ccb9c5f, there was the possibility of
getting nullptr back, which meant we were not doing a displaced-stepping
operation.

Now, with the assertion in place, this is running into issues.

It looks like displaced_step_buffers::copy_insn_closure_by_addr is
being used to return a couple different answers depending on the
state we're in:

1 - If we are actively displaced-stepping, then copy_insn_closure_by_addr
is supposed to return a valid closure for us, so we can determine the
thumb mode.

2 - If we are not actively displaced-stepping, then copy_insn_closure_by_addr
should return nullptr to signal that there isn't any displaced-step buffers
in use, because we don't have a valid closure (but we should always have
this).

Since the displaced-step buffers are always allocated, but not always used,
that means the buffers will always contain data. In particular, the buffer
addr field cannot be used to determine if the buffer is active or not.

For instance, we cannot set the buffer addr field to 0x0, as that can be a
valid PC in some cases.

My understanding is that the current_thread field should be a good candidate
to signal that a particular displaced-step buffer is active or not. If it is
nullptr, we have no threads using that buffer to displaced-step.  Otherwise,
it is an active buffer in use by a particular thread.

The following fix modifies the displaced_step_buffers::copy_insn_closure_by_addr
function so we only attempt to return a closure if the buffer has an assigned
current_thread and if the buffer address matches the address argument.

Alternatively, I think we could use a function to answer the question of
whether we're actively displaced-stepping (so we have an active buffer) or
not.

I've also added a testcase that exercises the problem. It should reproduce
reliably on Arm, as that is the only architecture that faces this problem
at the moment.

Regression-tested on Ubuntu 20.04. OK?

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30872
Approved-By: Simon Marchi <simon.marchi@efficios.com>
2023-10-16 11:58:26 +01:00
GDB Administrator
53f160095d Automatic date update in version.in 2023-10-16 00:00:57 +00:00
GDB Administrator
5eb4a57727 Automatic date update in version.in 2023-10-15 00:01:01 +00:00
GDB Administrator
490af0931a Automatic date update in version.in 2023-10-14 00:02:06 +00:00
Luis Machado
6418429b8c Use SVE_VQ_BYTES instead of __SVE_VQ_BYTES
__SVE_VQ_BYTES is only available if SVE definitions are available in
the system's headers, and this is not true for all systems.

For this purpose, we define SVE_VQ_BYTES.  This patch fixes the
name of the constant being used.
2023-10-13 16:35:17 +01:00
Tom Tromey
a0939697c3 Move -lsocket check to common.m4
A user pointed out that the -lsocket check in gdb should also apply to
gdbserver -- otherwise it can't find the Solaris socketpair.  This
patch makes the change.  It also removes a couple of redundant
function checks from gdb's configure.ac.

This was tested by the person who reported the bug.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30927
Approved-By: Pedro Alves <pedro@palves.net>
2023-10-12 18:22:19 -06:00
GDB Administrator
16ad65ea6c Automatic date update in version.in 2023-10-13 00:00:50 +00:00
GDB Administrator
e2c1272009 Automatic date update in version.in 2023-10-12 00:01:37 +00:00
GDB Administrator
78e450ac1d Automatic date update in version.in 2023-10-11 00:01:31 +00:00
GDB Administrator
d06cabec12 Automatic date update in version.in 2023-10-10 00:01:40 +00:00
GDB Administrator
20ce117bd1 Automatic date update in version.in 2023-10-09 00:00:53 +00:00
Joel Brobecker
8252d5f410 Bump GDB's version number to 14.0.91.DATE-git.
This commit changes gdb/version.in to 14.0.91.DATE-git.
2023-10-08 10:35:29 +02:00
Joel Brobecker
e51955483b Set GDB version number to 14.0.91.
This commit changes gdb/version.in to 14.0.91.
2023-10-08 10:18:45 +02:00
Joel Brobecker
b07cf62258 gdb/NEWS: Change "since GDB 13" to "in GDB 14"
This commit updates the NEWS files for the upcoming GDB 14 release.
2023-10-08 10:13:18 +02:00
Joel Brobecker
01d4f264bd Set development mode to "off" by default.
This is done by setting the "development" variable to "false"
in bfd/development.sh.
2023-10-08 09:52:40 +02:00
Joel Brobecker
d95407ee06 Bump version to 14.0.90.DATE-git.
Now that the GDB 14 branch has been created,
this commit bumps the version number in gdb/version.in to
14.0.90.DATE-git

For the record, the GDB 14 branch was created
from commit 8f12a1a841.
2023-10-08 09:52:20 +02:00
63 changed files with 2147 additions and 385 deletions

View File

@@ -16,7 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Controls whether to enable development-mode features by default.
development=true
development=false
# Indicate whether this is a release branch.
experimental=true

View File

@@ -16,7 +16,7 @@
In releases, the date is not included in either version strings or
sonames. */
#define BFD_VERSION_DATE 20231008
#define BFD_VERSION_DATE 20231203
#define BFD_VERSION @bfd_version@
#define BFD_VERSION_STRING @bfd_version_package@ @bfd_version_string@
#define REPORT_BUGS_TO @report_bugs_to@

View File

@@ -1,7 +1,7 @@
What has changed in GDB?
(Organized release by release)
*** Changes since GDB 13
*** Changes in GDB 14
* GDB now supports the AArch64 Scalable Matrix Extension 2 (SME2), which
includes a new 512 bit lookup table register named ZT0.
@@ -348,6 +348,11 @@ show tui mouse-events
gdb.Progspace that is either being added to GDB, or removed from
GDB.
** gdb.LazyString now implements the __str__ method.
** New method gdb.Frame.static_link that returns the outer frame
of a nested function frame.
*** Changes in GDB 13
* MI version 1 is deprecated, and will be removed in GDB 14.

View File

@@ -378,6 +378,19 @@ block::global_block () const
/* See block.h. */
const struct block *
block::function_block () const
{
const block *block = this;
while (block != nullptr && block->function () == nullptr)
block = block->superblock ();
return block;
}
/* See block.h. */
void
block::set_compunit_symtab (struct compunit_symtab *cu)
{

View File

@@ -254,10 +254,31 @@ struct block : public allocate_on_obstack
const struct block *static_block () const;
/* Return true if this block is a static block. */
bool is_static_block () const
{
const block *sup = superblock ();
if (sup == nullptr)
return false;
return sup->is_global_block ();
}
/* Return the static block associated with block. */
const struct block *global_block () const;
/* Return true if this block is a global block. */
bool is_global_block () const
{ return superblock () == nullptr; }
/* Return the function block for this block. Returns nullptr if
there is no enclosing function, i.e., if this block is a static
or global block. */
const struct block *function_block () const;
/* Set the compunit of this block, which must be a global block. */
void set_compunit_symtab (struct compunit_symtab *);

119
gdb/configure vendored
View File

@@ -19704,64 +19704,6 @@ if test "$ac_res" != no; then :
fi
# Some systems (e.g. Solaris) have `socketpair' in libsocket.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing socketpair" >&5
$as_echo_n "checking for library containing socketpair... " >&6; }
if ${ac_cv_search_socketpair+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char socketpair ();
int
main ()
{
return socketpair ();
;
return 0;
}
_ACEOF
for ac_lib in '' socket; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_socketpair=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_socketpair+:} false; then :
break
fi
done
if ${ac_cv_search_socketpair+:} false; then :
else
ac_cv_search_socketpair=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socketpair" >&5
$as_echo "$ac_cv_search_socketpair" >&6; }
ac_res=$ac_cv_search_socketpair
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
# Link in zlib/zstd if we can. This allows us to read compressed debug
# sections.
@@ -23626,9 +23568,9 @@ $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
# ------------------------------ #
for ac_func in getuid getgid \
pipe pread pread64 pwrite resize_term \
pread pread64 pwrite resize_term \
getpgid setsid \
sigaction sigsetmask socketpair \
sigsetmask \
ttrace wresize setlocale iconvlist libiconvlist btowc \
setrlimit getrlimit posix_madvise waitpid \
use_default_colors
@@ -24473,6 +24415,63 @@ if test "x$ac_cv_func_fork_works" = xyes; then
$as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h
fi
# Some systems (e.g. Solaris) have `socketpair' in libsocket.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing socketpair" >&5
$as_echo_n "checking for library containing socketpair... " >&6; }
if ${ac_cv_search_socketpair+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char socketpair ();
int
main ()
{
return socketpair ();
;
return 0;
}
_ACEOF
for ac_lib in '' socket; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_socketpair=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_socketpair+:} false; then :
break
fi
done
if ${ac_cv_search_socketpair+:} false; then :
else
ac_cv_search_socketpair=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socketpair" >&5
$as_echo "$ac_cv_search_socketpair" >&6; }
ac_res=$ac_cv_search_socketpair
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
for ac_func in fdwalk getrlimit pipe pipe2 poll socketpair sigaction \

View File

@@ -511,9 +511,6 @@ AC_CHECK_LIB(m, main)
# Some systems (e.g. Solaris) have `gethostbyname' in libnsl.
AC_SEARCH_LIBS(gethostbyname, nsl)
# Some systems (e.g. Solaris) have `socketpair' in libsocket.
AC_SEARCH_LIBS(socketpair, socket)
# Link in zlib/zstd if we can. This allows us to read compressed debug
# sections.
AM_ZLIB
@@ -1349,9 +1346,9 @@ AC_C_BIGENDIAN
# ------------------------------ #
AC_CHECK_FUNCS([getuid getgid \
pipe pread pread64 pwrite resize_term \
pread pread64 pwrite resize_term \
getpgid setsid \
sigaction sigsetmask socketpair \
sigsetmask \
ttrace wresize setlocale iconvlist libiconvlist btowc \
setrlimit getrlimit posix_madvise waitpid \
use_default_colors])

View File

@@ -277,7 +277,8 @@ displaced_step_buffers::copy_insn_closure_by_addr (CORE_ADDR addr)
{
for (const displaced_step_buffer &buffer : m_buffers)
{
if (addr == buffer.addr)
/* Make sure we have active buffers to compare to. */
if (buffer.current_thread != nullptr && addr == buffer.addr)
{
/* The closure information should always be available. */
gdb_assert (buffer.copy_insn_closure.get () != nullptr);

View File

@@ -5671,6 +5671,16 @@ Set this frame to be the selected frame. @xref{Stack, ,Examining the
Stack}.
@end defun
@defun Frame.static_link ()
In some languages (e.g., Ada, but also a GNU C extension), a nested
function can access the variables in the outer scope. This is done
via a ``static link'', which is a reference from the nested frame to
the appropriate outer frame.
This method returns this frame's static link frame, if one exists. If
there is no static link, this method returns @code{None}.
@end defun
@defun Frame.level ()
Return an integer, the stack frame level for this frame. @xref{Frames, ,Stack Frames}.
@end defun
@@ -5974,7 +5984,9 @@ local variables will require a frame, but other symbols will not.
@end defvar
@defvar Symbol.is_variable
@code{True} if the symbol is a variable.
@code{True} if the symbol is a variable, as opposed to something like
a function or type. Note that this also returns @code{False} for
arguments.
@end defvar
A @code{gdb.Symbol} object has the following methods:

View File

@@ -31,7 +31,6 @@
#include "block.h"
#include "objfiles.h"
#include "language.h"
#include "dwarf2/loc.h"
#include "gdbsupport/selftest.h"
/* Basic byte-swapping routines. All 'extract' functions return a
@@ -391,41 +390,6 @@ symbol_read_needs_frame (struct symbol *sym)
return symbol_read_needs (sym) == SYMBOL_NEEDS_FRAME;
}
/* Given static link expression and the frame it lives in, look for the frame
the static links points to and return it. Return NULL if we could not find
such a frame. */
static frame_info_ptr
follow_static_link (frame_info_ptr frame,
const struct dynamic_prop *static_link)
{
CORE_ADDR upper_frame_base;
if (!dwarf2_evaluate_property (static_link, frame, NULL, &upper_frame_base))
return NULL;
/* Now climb up the stack frame until we reach the frame we are interested
in. */
for (; frame != NULL; frame = get_prev_frame (frame))
{
struct symbol *framefunc = get_frame_function (frame);
/* Stacks can be quite deep: give the user a chance to stop this. */
QUIT;
/* If we don't know how to compute FRAME's base address, don't give up:
maybe the frame we are looking for is upper in the stack frame. */
if (framefunc != NULL
&& SYMBOL_BLOCK_OPS (framefunc) != NULL
&& SYMBOL_BLOCK_OPS (framefunc)->get_frame_base != NULL
&& (SYMBOL_BLOCK_OPS (framefunc)->get_frame_base (framefunc, frame)
== upper_frame_base))
break;
}
return frame;
}
/* Assuming VAR is a symbol that can be reached from FRAME thanks to lexical
rules, look for the frame that is actually hosting VAR and return it. If,
for some reason, we found no such frame, return NULL.
@@ -460,8 +424,7 @@ get_hosting_frame (struct symbol *var, const struct block *var_block,
tests that embed global/static symbols with null location lists.
We want to get <optimized out> instead of <frame required> when evaluating
them so return a frame instead of raising an error. */
else if (var_block == var_block->global_block ()
|| var_block == var_block->static_block ())
else if (var_block->is_global_block () || var_block->is_static_block ())
return frame;
/* We have to handle the "my_func::my_local_var" notation. This requires us
@@ -486,7 +449,7 @@ get_hosting_frame (struct symbol *var, const struct block *var_block,
/* If we failed to find the proper frame, fallback to the heuristic
method below. */
else if (frame_block == frame_block->global_block ())
else if (frame_block->is_global_block ())
{
frame = NULL;
break;
@@ -495,25 +458,14 @@ get_hosting_frame (struct symbol *var, const struct block *var_block,
/* Assuming we have a block for this frame: if we are at the function
level, the immediate upper lexical block is in an outer function:
follow the static link. */
else if (frame_block->function ())
else if (frame_block->function () != nullptr)
{
const struct dynamic_prop *static_link
= frame_block->static_link ();
int could_climb_up = 0;
if (static_link != NULL)
frame = frame_follow_static_link (frame);
if (frame != nullptr)
{
frame = follow_static_link (frame, static_link);
if (frame != NULL)
{
frame_block = get_frame_block (frame, NULL);
could_climb_up = frame_block != NULL;
}
}
if (!could_climb_up)
{
frame = NULL;
break;
frame_block = get_frame_block (frame, nullptr);
if (frame_block == nullptr)
frame = nullptr;
}
}

View File

@@ -43,6 +43,7 @@
#include "hashtab.h"
#include "valprint.h"
#include "cli/cli-option.h"
#include "dwarf2/loc.h"
/* The sentinel frame terminates the innermost end of the frame chain.
If unwound, it returns the information needed to construct an
@@ -3120,6 +3121,45 @@ get_frame_sp (frame_info_ptr this_frame)
return gdbarch_unwind_sp (gdbarch, frame_info_ptr (this_frame->next));
}
/* See frame.h. */
frame_info_ptr
frame_follow_static_link (frame_info_ptr frame)
{
const block *frame_block = get_frame_block (frame, nullptr);
frame_block = frame_block->function_block ();
const struct dynamic_prop *static_link = frame_block->static_link ();
if (static_link == nullptr)
return {};
CORE_ADDR upper_frame_base;
if (!dwarf2_evaluate_property (static_link, frame, NULL, &upper_frame_base))
return {};
/* Now climb up the stack frame until we reach the frame we are interested
in. */
for (; frame != nullptr; frame = get_prev_frame (frame))
{
struct symbol *framefunc = get_frame_function (frame);
/* Stacks can be quite deep: give the user a chance to stop this. */
QUIT;
/* If we don't know how to compute FRAME's base address, don't give up:
maybe the frame we are looking for is upper in the stack frame. */
if (framefunc != NULL
&& SYMBOL_BLOCK_OPS (framefunc) != NULL
&& SYMBOL_BLOCK_OPS (framefunc)->get_frame_base != NULL
&& (SYMBOL_BLOCK_OPS (framefunc)->get_frame_base (framefunc, frame)
== upper_frame_base))
break;
}
return frame;
}
/* Return the reason why we can't unwind past FRAME. */
enum unwind_stop_reason

View File

@@ -640,6 +640,13 @@ class address_space;
/* Return the frame's address space. */
extern const address_space *get_frame_address_space (frame_info_ptr);
/* A frame may have a "static link". That is, in some languages, a
nested function may have access to variables from the enclosing
block and frame. This function looks for a frame's static link.
If found, returns the corresponding frame; otherwise, returns a
null frame_info_ptr. */
extern frame_info_ptr frame_follow_static_link (frame_info_ptr frame);
/* For frames where we can not unwind further, describe why. */
enum unwind_stop_reason

View File

@@ -8295,6 +8295,72 @@ i386_floatformat_for_type (struct gdbarch *gdbarch,
return default_floatformat_for_type (gdbarch, name, len);
}
/* Compute an XCR0 mask based on a target description. */
static uint64_t
i386_xcr0_from_tdesc (const struct target_desc *tdesc)
{
if (! tdesc_has_registers (tdesc))
return 0;
const struct tdesc_feature *feature_core;
const struct tdesc_feature *feature_sse, *feature_avx, *feature_mpx,
*feature_avx512, *feature_pkeys;
/* Get core registers. */
feature_core = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.core");
if (feature_core == NULL)
return 0;
/* Get SSE registers. */
feature_sse = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.sse");
/* Try AVX registers. */
feature_avx = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.avx");
/* Try MPX registers. */
feature_mpx = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.mpx");
/* Try AVX512 registers. */
feature_avx512 = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.avx512");
/* Try PKEYS */
feature_pkeys = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.pkeys");
/* The XCR0 bits. */
uint64_t xcr0 = X86_XSTATE_X87;
if (feature_sse)
xcr0 |= X86_XSTATE_SSE;
if (feature_avx)
{
/* AVX register description requires SSE register description. */
if (!feature_sse)
return 0;
xcr0 |= X86_XSTATE_AVX;
}
if (feature_mpx)
xcr0 |= X86_XSTATE_MPX_MASK;
if (feature_avx512)
{
/* AVX512 register description requires AVX register description. */
if (!feature_avx)
return 0;
xcr0 |= X86_XSTATE_AVX512;
}
if (feature_pkeys)
xcr0 |= X86_XSTATE_PKRU;
return xcr0;
}
static int
i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
struct tdesc_arch_data *tdesc_data)
@@ -8506,6 +8572,15 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
x86_xsave_layout xsave_layout = target_fetch_x86_xsave_layout ();
/* If the target did not provide an XSAVE layout but the target
description includes registers from the XSAVE extended region,
use a fallback XSAVE layout. Specifically, this fallback layout
is used when writing out a local core dump for a remote
target. */
if (xsave_layout.sizeof_xsave == 0)
xsave_layout
= i387_fallback_xsave_layout (i386_xcr0_from_tdesc (info.target_desc));
/* If there is already a candidate, use it. */
for (arches = gdbarch_list_lookup_by_info (arches, &info);
arches != NULL;

View File

@@ -987,6 +987,54 @@ i387_guess_xsave_layout (uint64_t xcr0, size_t xsave_size,
return true;
}
/* See i387-tdep.h. */
x86_xsave_layout
i387_fallback_xsave_layout (uint64_t xcr0)
{
x86_xsave_layout layout;
if (HAS_PKRU (xcr0))
{
/* Intel CPUs supporting PKRU. */
layout.avx_offset = 576;
layout.bndregs_offset = 960;
layout.bndcfg_offset = 1024;
layout.k_offset = 1088;
layout.zmm_h_offset = 1152;
layout.zmm_offset = 1664;
layout.pkru_offset = 2688;
layout.sizeof_xsave = 2696;
}
else if (HAS_AVX512 (xcr0))
{
/* Intel CPUs supporting AVX512. */
layout.avx_offset = 576;
layout.bndregs_offset = 960;
layout.bndcfg_offset = 1024;
layout.k_offset = 1088;
layout.zmm_h_offset = 1152;
layout.zmm_offset = 1664;
layout.sizeof_xsave = 2688;
}
else if (HAS_MPX (xcr0))
{
/* Intel CPUs supporting MPX. */
layout.avx_offset = 576;
layout.bndregs_offset = 960;
layout.bndcfg_offset = 1024;
layout.sizeof_xsave = 1088;
}
else if (HAS_AVX (xcr0))
{
/* Intel and AMD CPUs supporting AVX. */
layout.avx_offset = 576;
layout.sizeof_xsave = 832;
}
return layout;
}
/* Extract from XSAVE a bitset of the features that are available on the
target, but which have not yet been enabled. */

View File

@@ -147,6 +147,11 @@ extern void i387_supply_fxsave (struct regcache *regcache, int regnum,
extern bool i387_guess_xsave_layout (uint64_t xcr0, size_t xsave_size,
x86_xsave_layout &layout);
/* Compute an XSAVE layout based on the XCR0 bitmask. This is used
as a fallback if a target does not provide an XSAVE layout. */
extern x86_xsave_layout i387_fallback_xsave_layout (uint64_t xcr0);
/* Similar to i387_supply_fxsave, but use XSAVE extended state. */
extern void i387_supply_xsave (struct regcache *regcache, int regnum,

View File

@@ -32,7 +32,6 @@
#include <sys/ptrace.h>
#include <asm/ptrace.h>
#include <stdarg.h>
#include "aarch64-scalable-linux-ptrace.h"
#include "aarch64-scalable-linux-sigcontext.h"
/* Indicates whether a SVE ptrace header is followed by SVE registers or a

View File

@@ -301,8 +301,8 @@ struct user_za_header {
/* Offset from the start of struct user_za_header to the register data */
#define ZA_PT_ZA_OFFSET \
((sizeof (struct user_za_header) + (__SVE_VQ_BYTES - 1)) \
/ __SVE_VQ_BYTES * __SVE_VQ_BYTES)
((sizeof (struct user_za_header) + (SVE_VQ_BYTES - 1)) \
/ SVE_VQ_BYTES * SVE_VQ_BYTES)
/* The payload starts at offset ZA_PT_ZA_OFFSET, and is of size
ZA_PT_ZA_SIZE(vq, flags).
@@ -317,9 +317,9 @@ struct user_za_header {
systems: see sigcontext.h for more explanation. */
#define ZA_PT_ZAV_OFFSET(vq, n) \
(ZA_PT_ZA_OFFSET + ((vq * __SVE_VQ_BYTES) * n))
(ZA_PT_ZA_OFFSET + ((vq * SVE_VQ_BYTES) * n))
#define ZA_PT_ZA_SIZE(vq) ((vq * __SVE_VQ_BYTES) * (vq * __SVE_VQ_BYTES))
#define ZA_PT_ZA_SIZE(vq) ((vq * SVE_VQ_BYTES) * (vq * SVE_VQ_BYTES))
#define ZA_PT_SIZE(vq) \
(ZA_PT_ZA_OFFSET + ZA_PT_ZA_SIZE(vq))

View File

@@ -213,18 +213,36 @@ class DAPFrameDecorator(_FrameDecoratorBase):
return sal.symtab.fullname()
return None
def frame_locals(self):
"""Return an iterable of local variables for this frame, if
any. The iterable object contains objects conforming with the
Symbol/Value interface. If there are no frame locals, or if
this frame is deemed to be a special case, return None."""
if hasattr(self._base, "frame_locals"):
return self._base.frame_locals()
frame = self.inferior_frame()
args = FrameVars(frame)
return args.fetch_frame_locals(True)
class SymValueWrapper(object):
"""A container class conforming to the Symbol/Value interface
which holds frame locals or frame arguments."""
def __init__(self, symbol, value):
# The FRAME argument is needed here because gdb.Symbol doesn't
# carry the block with it, and so read_var can't find symbols from
# outer (static link) frames.
def __init__(self, frame, symbol):
self.frame = frame
self.sym = symbol
self.val = value
def value(self):
"""Return the value associated with this symbol, or None"""
return self.val
if self.frame is None:
return None
return self.frame.read_var(self.sym)
def symbol(self):
"""Return the symbol, or Python text, associated with this
@@ -240,32 +258,50 @@ class FrameVars(object):
def __init__(self, frame):
self.frame = frame
def fetch_frame_locals(self):
def fetch_frame_locals(self, follow_link=False):
"""Public utility method to fetch frame local variables for
the stored frame. Frame arguments are not fetched. If there
are no frame local variables, return an empty list."""
lvars = []
frame = self.frame
try:
block = self.frame.block()
block = frame.block()
except RuntimeError:
block = None
traversed_link = False
while block is not None:
if block.is_global or block.is_static:
break
for sym in block:
# Exclude arguments from the innermost function, but
# if we found and traversed a static link, just treat
# all such variables as "local".
if sym.is_argument:
if not traversed_link:
continue
elif not sym.is_variable:
# We use an 'elif' here because is_variable
# returns False for arguments as well. Anyway,
# don't include non-variables here.
continue
if sym.is_variable:
lvars.append(SymValueWrapper(sym, None))
lvars.append(SymValueWrapper(frame, sym))
# Stop when the function itself is seen, to avoid showing
# variables from outer functions in a nested function.
if block.function is not None:
break
block = block.superblock
if not follow_link:
break
# If the frame has a static link, follow it here.
traversed_link = True
frame = frame.static_link()
if frame is None:
break
try:
block = frame.block()
except RuntimeError:
block = None
else:
block = block.superblock
return lvars
@@ -287,10 +323,12 @@ class FrameVars(object):
for sym in block:
if not sym.is_argument:
continue
args.append(SymValueWrapper(sym, None))
args.append(SymValueWrapper(None, sym))
# Stop when the function itself is seen, to avoid showing
# variables from outer functions in a nested function.
# Note that we don't traverse the static link for
# arguments, only for locals.
if block.function is not None:
break

View File

@@ -24,7 +24,7 @@ from typing import Optional, Sequence
from .server import request, capability, send_event
from .sources import make_source
from .startup import send_gdb_with_response, in_gdb_thread, log_stack
from .startup import in_gdb_thread, log_stack
from .typecheck import type_check
@@ -287,7 +287,7 @@ def set_breakpoint(*, source, breakpoints: Sequence = (), **args):
# Be sure to include the path in the key, so that we only
# clear out breakpoints coming from this same source.
key = "source:" + source["path"]
result = send_gdb_with_response(lambda: _set_breakpoints(key, specs))
result = _set_breakpoints(key, specs)
return {
"breakpoints": result,
}
@@ -315,9 +315,8 @@ def _rewrite_fn_breakpoint(
@capability("supportsFunctionBreakpoints")
def set_fn_breakpoint(*, breakpoints: Sequence, **args):
specs = [_rewrite_fn_breakpoint(**bp) for bp in breakpoints]
result = send_gdb_with_response(lambda: _set_breakpoints("function", specs))
return {
"breakpoints": result,
"breakpoints": _set_breakpoints("function", specs),
}
@@ -351,9 +350,8 @@ def set_insn_breakpoints(
*, breakpoints: Sequence, offset: Optional[int] = None, **args
):
specs = [_rewrite_insn_breakpoint(**bp) for bp in breakpoints]
result = send_gdb_with_response(lambda: _set_breakpoints("instruction", specs))
return {
"breakpoints": result,
"breakpoints": _set_breakpoints("instruction", specs),
}
@@ -432,7 +430,6 @@ def set_exception_breakpoints(
options = [{"filterId": filter} for filter in filters]
options.extend(filterOptions)
options = [_rewrite_exception_breakpoint(**bp) for bp in options]
result = send_gdb_with_response(lambda: _set_exception_catchpoints(options))
return {
"breakpoints": result,
"breakpoints": _set_exception_catchpoints(options),
}

View File

@@ -16,37 +16,68 @@
import gdb
import os
from gdb.frames import frame_iterator
from .frames import frame_id
# This is deprecated in 3.9, but required in older versions.
from typing import Optional
from .frames import dap_frame_generator
from .modules import module_id
from .scopes import symbol_value
from .server import request, capability
from .sources import make_source
from .startup import send_gdb_with_response, in_gdb_thread
from .startup import in_gdb_thread
from .state import set_thread
from .typecheck import type_check
from .varref import apply_format
# Helper function to compute parameter information for a stack frame.
@in_gdb_thread
def _compute_parameters(frame, stack_format):
arg_iter = frame.frame_args()
if arg_iter is None:
return ""
result = []
for arg in arg_iter:
desc = []
name, val = symbol_value(arg, frame)
# We don't try to use any particular language's syntax for the
# output here.
if stack_format["parameterTypes"]:
desc.append("[" + str(val.type) + "]")
if stack_format["parameterNames"]:
desc.append(name)
# If both the name and the value are requested, insert an
# '=' for clarity.
if stack_format["parameterValues"]:
desc.append("=")
if stack_format["parameterValues"]:
desc.append(val.format_string(summary=True))
result.append(" ".join(desc))
return ", ".join(result)
# Helper function to compute a stack trace.
@in_gdb_thread
def _backtrace(thread_id, levels, startFrame, value_format):
with apply_format(value_format):
def _backtrace(thread_id, levels, startFrame, stack_format):
with apply_format(stack_format):
set_thread(thread_id)
frames = []
if levels == 0:
# Zero means all remaining frames.
high = -1
else:
# frame_iterator uses an inclusive range, so subtract one.
high = startFrame + levels - 1
try:
frame_iter = frame_iterator(gdb.newest_frame(), startFrame, high)
except gdb.error:
frame_iter = ()
for current_frame in frame_iter:
frame_iter = dap_frame_generator(startFrame, levels, stack_format["includeAll"])
for frame_id, current_frame in frame_iter:
pc = current_frame.address()
# The stack frame format affects the name, so we build it up
# piecemeal and assign it at the end.
name = current_frame.function()
# The meaning of StackFrameFormat.parameters was clarified
# in https://github.com/microsoft/debug-adapter-protocol/issues/411.
if stack_format["parameters"] and (
stack_format["parameterTypes"]
or stack_format["parameterNames"]
or stack_format["parameterValues"]
):
name += "(" + _compute_parameters(current_frame, stack_format) + ")"
newframe = {
"id": frame_id(current_frame),
"name": current_frame.function(),
"id": frame_id,
# This must always be supplied, but we will set it
# correctly later if that is possible.
"line": 0,
@@ -54,15 +85,20 @@ def _backtrace(thread_id, levels, startFrame, value_format):
"column": 0,
"instructionPointerReference": hex(pc),
}
objfile = gdb.current_progspace().objfile_for_address(pc)
if objfile is not None:
newframe["moduleId"] = module_id(objfile)
line = current_frame.line()
if line is not None:
newframe["line"] = line
if stack_format["line"]:
name += ", line " + str(line)
objfile = gdb.current_progspace().objfile_for_address(pc)
if objfile is not None:
newframe["moduleId"] = module_id(objfile)
if stack_format["module"]:
name += ", module " + objfile.username
filename = current_frame.filename()
if filename is not None:
newframe["source"] = make_source(filename, os.path.basename(filename))
newframe["name"] = name
frames.append(newframe)
# Note that we do not calculate totalFrames here. Its absence
# tells the client that it may simply ask for frames until a
@@ -72,11 +108,43 @@ def _backtrace(thread_id, levels, startFrame, value_format):
}
# A helper function that checks the types of the elements of a
# StackFrameFormat, and converts this to a dict where all the members
# are set. This simplifies the implementation code a bit.
@type_check
def check_stack_frame(
*,
# Note that StackFrameFormat extends ValueFormat, which is why
# "hex" appears here.
hex: Optional[bool] = False,
parameters: Optional[bool] = False,
parameterTypes: Optional[bool] = False,
parameterNames: Optional[bool] = False,
parameterValues: Optional[bool] = False,
line: Optional[bool] = False,
module: Optional[bool] = False,
includeAll: Optional[bool] = False,
**rest
):
return {
"hex": hex,
"parameters": parameters,
"parameterTypes": parameterTypes,
"parameterNames": parameterNames,
"parameterValues": parameterValues,
"line": line,
"module": module,
"includeAll": includeAll,
}
@request("stackTrace")
@capability("supportsDelayedStackTraceLoading")
def stacktrace(
*, levels: int = 0, startFrame: int = 0, threadId: int, format=None, **extra
):
return send_gdb_with_response(
lambda: _backtrace(threadId, levels, startFrame, format)
)
# It's simpler if the format is always set.
if format is None:
format = {}
format = check_stack_frame(**format)
return _backtrace(threadId, levels, startFrame, format)

View File

@@ -16,7 +16,7 @@
import gdb
from .server import request, capability
from .startup import send_gdb_with_response, in_gdb_thread
from .startup import in_gdb_thread
@in_gdb_thread
@@ -54,6 +54,4 @@ def disassemble(
**extra
):
pc = int(memoryReference, 0) + offset
return send_gdb_with_response(
lambda: _disassemble(pc, instructionOffset, instructionCount)
)
return _disassemble(pc, instructionOffset, instructionCount)

View File

@@ -20,7 +20,7 @@ from typing import Optional
from .frames import select_frame
from .server import capability, request, client_bool_capability
from .startup import send_gdb_with_response, in_gdb_thread
from .startup import in_gdb_thread
from .varref import find_variable, VariableReference, apply_format
@@ -96,14 +96,12 @@ def eval_request(
):
if context in ("watch", "variables"):
# These seem to be expression-like.
return send_gdb_with_response(lambda: _evaluate(expression, frameId, format))
return _evaluate(expression, frameId, format)
elif context == "hover":
return send_gdb_with_response(
lambda: _eval_for_hover(expression, frameId, format)
)
return _eval_for_hover(expression, frameId, format)
elif context == "repl":
# Ignore the format for repl evaluation.
return send_gdb_with_response(lambda: _repl(expression, frameId))
return _repl(expression, frameId)
else:
raise Exception('unknown evaluate context "' + context + '"')
@@ -127,10 +125,7 @@ def variables(
if not client_bool_capability("supportsVariablePaging"):
start = 0
count = 0
result = send_gdb_with_response(
lambda: _variables(variablesReference, start, count, format)
)
return {"variables": result}
return {"variables": _variables(variablesReference, start, count, format)}
@capability("supportsSetExpression")
@@ -138,6 +133,23 @@ def variables(
def set_expression(
*, expression: str, value: str, frameId: Optional[int] = None, format=None, **args
):
return send_gdb_with_response(
lambda: _set_expression(expression, value, frameId, format)
)
return _set_expression(expression, value, frameId, format)
# Helper function to perform an assignment.
@in_gdb_thread
def _set_variable(ref, name, value, value_format):
with apply_format(value_format):
var = find_variable(ref)
lhs = var.find_child_by_name(name)
rhs = gdb.parse_and_eval(value)
lhs.assign(rhs)
return lhs.to_object()
@capability("supportsSetVariable")
@request("setVariable")
def set_variable(
*, variablesReference: int, name: str, value: str, format=None, **args
):
return _set_variable(variablesReference, name, value, format)

View File

@@ -17,12 +17,21 @@ import enum
import gdb
from .server import send_event
from .startup import in_gdb_thread, Invoker, log
from .startup import exec_and_log, in_gdb_thread, log
from .modules import is_module, make_module
# True when the inferior is thought to be running, False otherwise.
# This may be accessed from any thread, which can be racy. However,
# this unimportant because this global is only used for the
# 'notStopped' response, which itself is inherently racy.
inferior_running = False
@in_gdb_thread
def _on_exit(event):
global inferior_running
inferior_running = False
code = 0
if hasattr(event, "exit_code"):
code = event.exit_code
@@ -48,6 +57,8 @@ def thread_event(event, reason):
@in_gdb_thread
def _new_thread(event):
global inferior_running
inferior_running = True
thread_event(event, "started")
@@ -85,6 +96,8 @@ _suppress_cont = False
@in_gdb_thread
def _cont(event):
global inferior_running
inferior_running = True
global _suppress_cont
if _suppress_cont:
log("_suppress_cont case")
@@ -111,33 +124,21 @@ _expected_stop = None
@in_gdb_thread
def expect_stop(reason):
"""Indicate that a stop is expected, for the reason given."""
def exec_and_expect_stop(cmd, reason):
"""Indicate that a stop is expected, then execute CMD"""
global _expected_stop
_expected_stop = reason
# A wrapper for Invoker that also sets the expected stop.
class ExecutionInvoker(Invoker):
"""A subclass of Invoker that sets the expected stop.
Note that this assumes that the command will restart the inferior,
so it will also cause ContinuedEvents to be suppressed."""
def __init__(self, cmd, expected):
super().__init__(cmd)
self.expected = expected
@in_gdb_thread
def __call__(self):
expect_stop(self.expected)
if reason != StopKinds.PAUSE:
global _suppress_cont
_suppress_cont = True
# FIXME if the call fails should we clear _suppress_cont?
super().__call__()
# FIXME if the call fails should we clear _suppress_cont?
exec_and_log(cmd)
@in_gdb_thread
def _on_stop(event):
global inferior_running
inferior_running = False
log("entering _on_stop: " + repr(event))
global _expected_stop
obj = {
@@ -156,6 +157,26 @@ def _on_stop(event):
send_event("stopped", obj)
# This keeps a bit of state between the start of an inferior call and
# the end. If the inferior was already running when the call started
# (as can happen if a breakpoint condition calls a function), then we
# do not want to emit 'continued' or 'stop' events for the call. Note
# that, for some reason, gdb.events.cont does not fire for an infcall.
_infcall_was_running = False
@in_gdb_thread
def _on_inferior_call(event):
global _infcall_was_running
if isinstance(event, gdb.InferiorCallPreEvent):
_infcall_was_running = inferior_running
if not _infcall_was_running:
_cont(None)
else:
if not _infcall_was_running:
_on_stop(None)
gdb.events.stop.connect(_on_stop)
gdb.events.exited.connect(_on_exit)
gdb.events.new_thread.connect(_new_thread)
@@ -163,3 +184,4 @@ gdb.events.thread_exited.connect(_thread_exited)
gdb.events.cont.connect(_cont)
gdb.events.new_objfile.connect(_new_objfile)
gdb.events.free_objfile.connect(_objfile_removed)
gdb.events.inferior_call.connect(_on_inferior_call)

View File

@@ -14,6 +14,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import gdb
import itertools
from gdb.frames import frame_iterator
from .startup import in_gdb_thread
@@ -24,29 +27,23 @@ from .startup import in_gdb_thread
_all_frames = []
# Map from a global thread ID to a memoizing frame iterator.
_iter_map = {}
# Clear all the frame IDs.
@in_gdb_thread
def _clear_frame_ids(evt):
global _all_frames
_all_frames = []
global _iter_map
_iter_map = {}
# Clear the frame ID map whenever the inferior runs.
gdb.events.cont.connect(_clear_frame_ids)
@in_gdb_thread
def frame_id(frame):
"""Return the frame identifier for FRAME."""
global _all_frames
for i in range(0, len(_all_frames)):
if _all_frames[i] == frame:
return i
result = len(_all_frames)
_all_frames.append(frame)
return result
@in_gdb_thread
def frame_for_id(id):
"""Given a frame identifier ID, return the corresponding frame."""
@@ -59,3 +56,95 @@ def select_frame(id):
"""Given a frame identifier ID, select the corresponding frame."""
frame = frame_for_id(id)
frame.inferior_frame().select()
# A simple memoizing iterator. Note that this is not very robust.
# For example, you can't start two copies of the iterator and then
# alternate fetching items from each. Instead, it implements just
# what is needed for the current callers.
class _MemoizingIterator:
def __init__(self, iterator):
self.iterator = iterator
self.seen = []
def __iter__(self):
# First the memoized items.
for item in self.seen:
yield item
# Now memoize new items.
for item in self.iterator:
self.seen.append(item)
yield item
# A generator that fetches frames and pairs them with a frame ID. It
# yields tuples of the form (ID, ELIDED, FRAME), where ID is the
# generated ID, ELIDED is a boolean indicating if the frame should be
# elided, and FRAME is the frame itself. This approach lets us
# memoize the result and assign consistent IDs, independent of how
# "includeAll" is set in the request.
@in_gdb_thread
def _frame_id_generator():
try:
base_iterator = frame_iterator(gdb.newest_frame(), 0, -1)
except gdb.error:
base_iterator = ()
# Helper function to assign an ID to a frame.
def get_id(frame):
global _all_frames
num = len(_all_frames)
_all_frames.append(frame)
return num
def yield_frames(iterator, for_elided):
for frame in iterator:
# Unfortunately the frame filter docs don't describe
# whether the elided frames conceptually come before or
# after the eliding frame. Here we choose after.
yield (get_id(frame), for_elided, frame)
elided = frame.elided()
if elided is not None:
yield from yield_frames(frame.elided(), True)
yield from yield_frames(base_iterator, False)
# Return the memoizing frame iterator for the selected thread.
@in_gdb_thread
def _get_frame_iterator():
thread_id = gdb.selected_thread().global_num
global _iter_map
if thread_id not in _iter_map:
_iter_map[thread_id] = _MemoizingIterator(_frame_id_generator())
return _iter_map[thread_id]
# A helper function that creates an iterable that returns (ID, FRAME)
# pairs. It uses the memoizing frame iterator, but also handles the
# "includeAll" member of StackFrameFormat.
@in_gdb_thread
def dap_frame_generator(frame_low, levels, include_all):
"""A generator that yields identifiers and frames.
Each element is a pair of the form (ID, FRAME).
ID is the internally-assigned frame ID.
FRAME is a FrameDecorator of some kind.
Arguments are as to the stackTrace request."""
base_iterator = _get_frame_iterator()
if not include_all:
base_iterator = itertools.filterfalse(lambda item: item[1], base_iterator)
if levels == 0:
# Zero means all remaining frames.
frame_high = None
else:
frame_high = frame_low + levels
base_iterator = itertools.islice(base_iterator, frame_low, frame_high)
for ident, _, frame in base_iterator:
yield (ident, frame)

View File

@@ -18,13 +18,13 @@ import gdb
# These are deprecated in 3.9, but required in older versions.
from typing import Mapping, Optional, Sequence
from .events import ExecutionInvoker
from .events import exec_and_expect_stop
from .server import request, capability
from .startup import send_gdb, send_gdb_with_response, in_gdb_thread, exec_and_log
from .startup import in_gdb_thread, exec_and_log
# The program being launched, or None. This should only be access
# from the DAP thread.
# The program being launched, or None. This should only be accessed
# from the gdb thread.
_program = None
@@ -49,7 +49,7 @@ def _launch_setup(program, cwd, args, env, stopAtBeginningOfMainSubprogram):
# Any parameters here are necessarily extensions -- DAP requires this
# from implementations. Any additions or changes here should be
# documented in the gdb manual.
@request("launch")
@request("launch", response=False)
def launch(
*,
program: Optional[str] = None,
@@ -61,9 +61,7 @@ def launch(
):
global _program
_program = program
send_gdb(
lambda: _launch_setup(program, cwd, args, env, stopAtBeginningOfMainSubprogram)
)
_launch_setup(program, cwd, args, env, stopAtBeginningOfMainSubprogram)
@request("attach")
@@ -77,17 +75,14 @@ def attach(*, pid: Optional[int] = None, target: Optional[str] = None, **args):
cmd = "target remote " + target
else:
raise Exception("attach requires either 'pid' or 'target'")
# Use send_gdb_with_response to ensure we get an error if the
# attach fails.
send_gdb_with_response(cmd)
return None
exec_and_log(cmd)
@capability("supportsConfigurationDoneRequest")
@request("configurationDone")
@request("configurationDone", response=False)
def config_done(**args):
global _program
if _program is not None:
# Suppress the continue event, but don't set any particular
# expected stop.
send_gdb(ExecutionInvoker("run", None))
exec_and_expect_stop("run", None)

View File

@@ -20,7 +20,7 @@ from typing import Optional
from .server import capability, request
from .sources import decode_source
from .startup import in_gdb_thread, send_gdb_with_response
from .startup import in_gdb_thread
@in_gdb_thread
@@ -46,4 +46,4 @@ def _find_lines(source, start_line, end_line):
def breakpoint_locations(*, source, line: int, endLine: Optional[int] = None, **extra):
if endLine is None:
endLine = line
return send_gdb_with_response(lambda: _find_lines(source, line, endLine))
return _find_lines(source, line, endLine)

View File

@@ -17,35 +17,22 @@ import base64
import gdb
from .server import request, capability
from .startup import send_gdb_with_response, in_gdb_thread
@in_gdb_thread
def _read_memory(addr, count):
buf = gdb.selected_inferior().read_memory(addr, count)
return base64.b64encode(buf).decode("ASCII")
@request("readMemory")
@capability("supportsReadMemoryRequest")
def read_memory(*, memoryReference: str, offset: int = 0, count: int, **extra):
addr = int(memoryReference, 0) + offset
buf = send_gdb_with_response(lambda: _read_memory(addr, count))
buf = gdb.selected_inferior().read_memory(addr, count)
return {
"address": hex(addr),
"data": buf,
"data": base64.b64encode(buf).decode("ASCII"),
}
@in_gdb_thread
def _write_memory(addr, contents):
buf = base64.b64decode(contents)
gdb.selected_inferior().write_memory(addr, buf)
@request("writeMemory")
@capability("supportsWriteMemoryRequest")
def write_memory(*, memoryReference: str, offset: int = 0, data: str, **extra):
addr = int(memoryReference, 0) + offset
send_gdb_with_response(lambda: _write_memory(addr, data))
return {}
buf = base64.b64decode(data)
gdb.selected_inferior().write_memory(addr, buf)

View File

@@ -16,7 +16,7 @@
import gdb
from .server import capability, request
from .startup import in_gdb_thread, send_gdb_with_response
from .startup import in_gdb_thread
@in_gdb_thread
@@ -63,4 +63,4 @@ def _modules(start, count):
@capability("supportsModulesRequest")
@request("modules")
def modules(*, startModule: int = 0, moduleCount: int = 0, **args):
return send_gdb_with_response(lambda: _modules(startModule, moduleCount))
return _modules(startModule, moduleCount)

View File

@@ -15,7 +15,7 @@
import gdb
from .events import StopKinds, ExecutionInvoker
from .events import StopKinds, exec_and_expect_stop
from .server import capability, request
from .startup import in_gdb_thread, send_gdb, send_gdb_with_response
from .state import set_thread
@@ -49,38 +49,43 @@ def _handle_thread_step(thread_id, single_thread, select=False):
return result
@request("next")
@request("next", response=False)
def next(
*, threadId: int, singleThread: bool = False, granularity: str = "statement", **args
):
send_gdb(lambda: _handle_thread_step(threadId, singleThread))
_handle_thread_step(threadId, singleThread)
cmd = "next"
if granularity == "instruction":
cmd += "i"
send_gdb(ExecutionInvoker(cmd, StopKinds.STEP))
exec_and_expect_stop(cmd, StopKinds.STEP)
@capability("supportsSteppingGranularity")
@capability("supportsSingleThreadExecutionRequests")
@request("stepIn")
@request("stepIn", response=False)
def step_in(
*, threadId: int, singleThread: bool = False, granularity: str = "statement", **args
):
send_gdb(lambda: _handle_thread_step(threadId, singleThread))
_handle_thread_step(threadId, singleThread)
cmd = "step"
if granularity == "instruction":
cmd += "i"
send_gdb(ExecutionInvoker(cmd, StopKinds.STEP))
exec_and_expect_stop(cmd, StopKinds.STEP)
@request("stepOut")
@request("stepOut", response=False)
def step_out(*, threadId: int, singleThread: bool = False, **args):
send_gdb(lambda: _handle_thread_step(threadId, singleThread, True))
send_gdb(ExecutionInvoker("finish", StopKinds.STEP))
_handle_thread_step(threadId, singleThread, True)
exec_and_expect_stop("finish", StopKinds.STEP)
@request("continue")
# This is a server-side request because it is funny: it wants to
# 'continue' but also return a result, which precludes using
# response=False. Using 'continue &' would mostly work ok, but this
# yields races when a stop occurs before the response is sent back to
# the client.
@request("continue", on_dap_thread=True)
def continue_request(*, threadId: int, singleThread: bool = False, **args):
locked = send_gdb_with_response(lambda: _handle_thread_step(threadId, singleThread))
send_gdb(ExecutionInvoker("continue", None))
send_gdb(lambda: exec_and_expect_stop("continue", None))
return {"allThreadsContinued": not locked}

View File

@@ -13,11 +13,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .events import StopKinds, ExecutionInvoker
from .events import StopKinds, exec_and_expect_stop
from .server import request
from .startup import send_gdb
@request("pause")
@request("pause", response=False, expect_stopped=False)
def pause(**args):
send_gdb(ExecutionInvoker("interrupt -a", StopKinds.PAUSE))
exec_and_expect_stop("interrupt -a", StopKinds.PAUSE)

View File

@@ -16,11 +16,49 @@
import gdb
from .frames import frame_for_id
from .startup import send_gdb_with_response, in_gdb_thread
from .startup import in_gdb_thread
from .server import request
from .varref import BaseReference
# Map DAP frame IDs to scopes. This ensures that scopes are re-used.
frame_to_scope = {}
# When the inferior is re-started, we erase all scope references. See
# the section "Lifetime of Objects References" in the spec.
@in_gdb_thread
def clear_scopes(event):
global frame_to_scope
frame_to_scope = {}
gdb.events.cont.connect(clear_scopes)
# A helper function to compute the value of a symbol. SYM is either a
# gdb.Symbol, or an object implementing the SymValueWrapper interface.
# FRAME is a frame wrapper, as produced by a frame filter. Returns a
# tuple of the form (NAME, VALUE), where NAME is the symbol's name and
# VALUE is a gdb.Value.
@in_gdb_thread
def symbol_value(sym, frame):
inf_frame = frame.inferior_frame()
# Make sure to select the frame first. Ideally this would not
# be needed, but this is a way to set the current language
# properly so that language-dependent APIs will work.
inf_frame.select()
name = str(sym.symbol())
val = sym.value()
if val is None:
# No synthetic value, so must read the symbol value
# ourselves.
val = sym.symbol().value(inf_frame)
elif not isinstance(val, gdb.Value):
val = gdb.Value(val)
return (name, val)
class _ScopeReference(BaseReference):
def __init__(self, name, hint, frame, var_list):
super().__init__(name)
@@ -44,26 +82,15 @@ class _ScopeReference(BaseReference):
# FIXME construct a Source object
return result
def has_children(self):
return True
def child_count(self):
return len(self.var_list)
@in_gdb_thread
def fetch_one_child(self, idx):
# Make sure to select the frame first. Ideally this would not
# be needed, but this is a way to set the current language
# properly so that language-dependent APIs will work.
self.inf_frame.select()
# Here SYM will conform to the SymValueWrapper interface.
sym = self.var_list[idx]
name = str(sym.symbol())
val = sym.value()
if val is None:
# No synthetic value, so must read the symbol value
# ourselves.
val = sym.symbol().value(self.inf_frame)
elif not isinstance(val, gdb.Value):
val = gdb.Value(val)
return (name, val)
return symbol_value(self.var_list[idx], self.frame)
class _RegisterReference(_ScopeReference):
@@ -83,19 +110,27 @@ class _RegisterReference(_ScopeReference):
# Helper function to create a DAP scopes for a given frame ID.
@in_gdb_thread
def _get_scope(id):
frame = frame_for_id(id)
scopes = []
args = frame.frame_args()
if args:
scopes.append(_ScopeReference("Arguments", "arguments", frame, args))
locs = frame.frame_locals()
if locs:
scopes.append(_ScopeReference("Locals", "locals", frame, locs))
scopes.append(_RegisterReference("Registers", frame))
global frame_to_scope
if id in frame_to_scope:
scopes = frame_to_scope[id]
else:
frame = frame_for_id(id)
scopes = []
# Make sure to handle the None case as well as the empty
# iterator case.
args = tuple(frame.frame_args() or ())
if args:
scopes.append(_ScopeReference("Arguments", "arguments", frame, args))
# Make sure to handle the None case as well as the empty
# iterator case.
locs = tuple(frame.frame_locals() or ())
if locs:
scopes.append(_ScopeReference("Locals", "locals", frame, locs))
scopes.append(_RegisterReference("Registers", frame))
frame_to_scope[id] = scopes
return [x.to_object() for x in scopes]
@request("scopes")
def scopes(*, frameId: int, **extra):
scopes = send_gdb_with_response(lambda: _get_scope(frameId))
return {"scopes": scopes}
return {"scopes": _get_scope(frameId)}

View File

@@ -13,6 +13,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import functools
import inspect
import json
import queue
@@ -20,11 +21,14 @@ import sys
from .io import start_json_writer, read_json
from .startup import (
exec_and_log,
in_dap_thread,
in_gdb_thread,
send_gdb,
send_gdb_with_response,
start_thread,
log,
log_stack,
send_gdb_with_response,
)
from .typecheck import type_check
@@ -160,12 +164,53 @@ def send_event(event, body=None):
_server.send_event(event, body)
def request(name):
"""A decorator that indicates that the wrapper function implements
the DAP request NAME."""
# A helper decorator that checks whether the inferior is running.
def _check_not_running(func):
@functools.wraps(func)
def check(*args, **kwargs):
# Import this as late as possible. This is done to avoid
# circular imports.
from .events import inferior_running
if inferior_running:
raise Exception("notStopped")
return func(*args, **kwargs)
return check
def request(
name: str,
*,
response: bool = True,
on_dap_thread: bool = False,
expect_stopped: bool = True
):
"""A decorator for DAP requests.
This registers the function as the implementation of the DAP
request NAME. By default, the function is invoked in the gdb
thread, and its result is returned as the 'body' of the DAP
response.
Some keyword arguments are provided as well:
If RESPONSE is False, the result of the function will not be
waited for and no 'body' will be in the response.
If ON_DAP_THREAD is True, the function will be invoked in the DAP
thread. When ON_DAP_THREAD is True, RESPONSE may not be False.
If EXPECT_STOPPED is True (the default), then the request will
fail with the 'notStopped' reason if it is processed while the
inferior is running. When EXPECT_STOPPED is False, the request
will proceed regardless of the inferior's state.
"""
# Validate the parameters.
assert not on_dap_thread or response
def wrap(func):
global _commands
code = func.__code__
# We don't permit requests to have positional arguments.
try:
@@ -176,11 +221,38 @@ def request(name):
assert code.co_argcount == 0
# A request must have a **args parameter.
assert code.co_flags & inspect.CO_VARKEYWORDS
# All requests must run in the DAP thread.
# Also type-check the calls.
func = in_dap_thread(type_check(func))
_commands[name] = func
return func
# Type-check the calls.
func = type_check(func)
# Verify that the function is run on the correct thread.
if on_dap_thread:
cmd = in_dap_thread(func)
else:
func = in_gdb_thread(func)
if response:
def sync_call(**args):
return send_gdb_with_response(lambda: func(**args))
cmd = sync_call
else:
def non_sync_call(**args):
return send_gdb(lambda: func(**args))
cmd = non_sync_call
# If needed, check that the inferior is not running. This
# wrapping is done last, so the check is done first, before
# trying to dispatch the request to another thread.
if expect_stopped:
cmd = _check_not_running(cmd)
global _commands
_commands[name] = cmd
return cmd
return wrap
@@ -208,7 +280,7 @@ def client_bool_capability(name):
return False
@request("initialize")
@request("initialize", on_dap_thread=True)
def initialize(**args):
global _server, _capabilities
_server.config = args
@@ -216,17 +288,15 @@ def initialize(**args):
return _capabilities.copy()
@request("terminate")
@request("terminate", expect_stopped=False)
@capability("supportsTerminateRequest")
def terminate(**args):
# We can ignore the result here, because we only really need to
# synchronize.
send_gdb_with_response("kill")
exec_and_log("kill")
@request("disconnect")
@request("disconnect", on_dap_thread=True, expect_stopped=False)
@capability("supportTerminateDebuggee")
def disconnect(*, terminateDebuggee: bool = False, **args):
if terminateDebuggee:
terminate()
send_gdb_with_response("kill")
_server.shutdown()

View File

@@ -18,7 +18,7 @@ import os
import gdb
from .server import request, capability
from .startup import send_gdb_with_response, in_gdb_thread
from .startup import in_gdb_thread
# The next available source reference ID. Must be greater than 0.
@@ -76,8 +76,9 @@ def decode_source(source):
return _id_map[ref]["path"]
@in_gdb_thread
def _sources():
@request("loadedSources")
@capability("supportsLoadedSourcesRequest")
def loaded_sources(**extra):
result = []
for elt in gdb.execute_mi("-file-list-exec-source-files")["files"]:
result.append(make_source(elt["fullname"], elt["file"]))
@@ -86,24 +87,6 @@ def _sources():
}
@request("loadedSources")
@capability("supportsLoadedSourcesRequest")
def loaded_sources(**extra):
return send_gdb_with_response(_sources)
# This helper is needed because we must only access the globals here
# from the gdb thread.
@in_gdb_thread
def _get_source(source):
filename = decode_source(source)
with open(filename) as f:
content = f.read()
return {
"content": content,
}
@request("source")
def source(*, source=None, sourceReference: int, **extra):
# The 'sourceReference' parameter is required by the spec, but is
@@ -111,4 +94,9 @@ def source(*, source=None, sourceReference: int, **extra):
# 'source' is preferred.
if source is None:
source = {"sourceReference": sourceReference}
return send_gdb_with_response(lambda: _get_source(source))
filename = decode_source(source)
with open(filename) as f:
content = f.read()
return {
"content": content,
}

View File

@@ -16,7 +16,6 @@
import gdb
from .server import request
from .startup import send_gdb_with_response, in_gdb_thread
def _thread_name(thr):
@@ -27,9 +26,8 @@ def _thread_name(thr):
return None
# A helper function to construct the list of threads.
@in_gdb_thread
def _get_threads():
@request("threads")
def threads(**args):
result = []
for thr in gdb.selected_inferior().threads():
one_result = {
@@ -39,12 +37,6 @@ def _get_threads():
if name is not None:
one_result["name"] = name
result.append(one_result)
return result
@request("threads")
def threads(**args):
result = send_gdb_with_response(_get_threads)
return {
"threads": result,
}

View File

@@ -16,7 +16,8 @@
import gdb
from .startup import in_gdb_thread
from .server import client_bool_capability
from abc import abstractmethod
from abc import ABC, abstractmethod
from collections import defaultdict
from contextlib import contextmanager
@@ -52,7 +53,7 @@ def apply_format(value_format):
return _null()
class BaseReference:
class BaseReference(ABC):
"""Represent a variable or a scope.
This class is just a base class, some methods must be implemented in
@@ -72,7 +73,7 @@ class BaseReference:
all_variables.append(self)
self.ref = len(all_variables)
self.name = name
self.children = None
self.reset_children()
@in_gdb_thread
def to_object(self):
@@ -80,16 +81,27 @@ class BaseReference:
The resulting object is a starting point that can be filled in
further. See the Scope or Variable types in the spec"""
result = {
"variablesReference": self.ref,
}
result = {"variablesReference": self.ref if self.has_children() else 0}
if self.name is not None:
result["name"] = str(self.name)
return result
def no_children(self):
"""Call this to declare that this variable or scope has no children."""
self.ref = 0
@abstractmethod
def has_children(self):
"""Return True if this object has children."""
return False
def reset_children(self):
"""Reset any cached information about the children of this object."""
# A list of all the children. Each child is a BaseReference
# of some kind.
self.children = None
# Map from the name of a child to a BaseReference.
self.by_name = {}
# Keep track of how many duplicates there are of a given name,
# so that unique names can be generated. Map from base name
# to a count.
self.name_counts = defaultdict(lambda: 1)
@abstractmethod
def fetch_one_child(self, index):
@@ -105,23 +117,55 @@ class BaseReference:
"""Return the number of children of this variable."""
return
# Helper method to compute the final name for a child whose base
# name is given. Updates the name_counts map. This is used to
# handle shadowing -- in DAP, the adapter is responsible for
# making sure that all the variables in a a given container have
# unique names. See
# https://github.com/microsoft/debug-adapter-protocol/issues/141
# and
# https://github.com/microsoft/debug-adapter-protocol/issues/149
def _compute_name(self, name):
if name in self.by_name:
self.name_counts[name] += 1
# In theory there's no safe way to compute a name, because
# a pretty-printer might already be generating names of
# that form. In practice I think we should not worry too
# much.
name = name + " #" + str(self.name_counts[name])
return name
@in_gdb_thread
def fetch_children(self, start, count):
"""Fetch children of this variable.
START is the starting index.
COUNT is the number to return, with 0 meaning return all."""
COUNT is the number to return, with 0 meaning return all.
Returns an iterable of some kind."""
if count == 0:
count = self.child_count()
if self.children is None:
self.children = [None] * self.child_count()
result = []
for idx in range(start, start + count):
if self.children[idx] is None:
(name, value) = self.fetch_one_child(idx)
self.children[idx] = VariableReference(name, value)
result.append(self.children[idx])
return result
name = self._compute_name(name)
var = VariableReference(name, value)
self.children[idx] = var
self.by_name[name] = var
yield self.children[idx]
@in_gdb_thread
def find_child_by_name(self, name):
"""Find a child of this variable, given its name.
Returns the value of the child, or throws if not found."""
# A lookup by name can only be done using names previously
# provided to the client, so we can simply rely on the by-name
# map here.
if name in self.by_name:
return self.by_name[name]
raise Exception("no variable named '" + name + "'")
class VariableReference(BaseReference):
@@ -135,16 +179,27 @@ class VariableReference(BaseReference):
RESULT_NAME can be used to change how the simple string result
is emitted in the result dictionary."""
super().__init__(name)
self.value = value
self.printer = gdb.printing.make_visualizer(value)
self.result_name = result_name
# We cache all the children we create.
self.value = value
self._update_value()
# Internal method to update local data when the value changes.
def _update_value(self):
self.reset_children()
self.printer = gdb.printing.make_visualizer(self.value)
self.child_cache = None
if not hasattr(self.printer, "children"):
self.no_children()
self.count = None
else:
if self.has_children():
self.count = -1
else:
self.count = None
def assign(self, value):
"""Assign VALUE to this object and update."""
self.value.assign(value)
self._update_value()
def has_children(self):
return hasattr(self.printer, "children")
def cache_children(self):
if self.child_cache is None:
@@ -173,7 +228,7 @@ class VariableReference(BaseReference):
def to_object(self):
result = super().to_object()
result[self.result_name] = self.printer.to_string()
result[self.result_name] = str(self.printer.to_string())
num_children = self.child_count()
if num_children is not None:
if (
@@ -199,9 +254,14 @@ class VariableReference(BaseReference):
if isinstance(self.printer, gdb.ValuePrinter) and hasattr(
self.printer, "child"
):
return self.printer.child(idx)
(name, val) = self.printer.child(idx)
else:
return self.cache_children()[idx]
(name, val) = self.cache_children()[idx]
# A pretty-printer can return something other than a
# gdb.Value, but it must be convertible.
if not isinstance(val, gdb.Value):
val = gdb.Value(val)
return (name, val)
@in_gdb_thread

View File

@@ -350,7 +350,7 @@ class NoOpStructPrinter(gdb.ValuePrinter):
def children(self):
for field in self.__ty.fields():
if field.name is not None:
if hasattr(field, "bitpos") and field.name is not None:
yield (field.name, self.__value[field])

View File

@@ -622,6 +622,30 @@ frapy_language (PyObject *self, PyObject *args)
Py_RETURN_NONE;
}
/* The static link for this frame. */
static PyObject *
frapy_static_link (PyObject *self, PyObject *args)
{
frame_info_ptr link;
try
{
FRAPY_REQUIRE_VALID (self, link);
link = frame_follow_static_link (link);
}
catch (const gdb_exception &except)
{
GDB_PY_HANDLE_EXCEPTION (except);
}
if (link == nullptr)
Py_RETURN_NONE;
return frame_info_to_frame_object (link);
}
/* Implementation of gdb.newest_frame () -> gdb.Frame.
Returns the newest frame object. */
@@ -800,6 +824,8 @@ Return the value of the variable in this frame." },
"The stack level of this frame." },
{ "language", frapy_language, METH_NOARGS,
"The language of this frame." },
{ "static_link", frapy_static_link, METH_NOARGS,
"The static link of this frame, or None." },
{NULL} /* Sentinel */
};

View File

@@ -296,6 +296,32 @@ gdbpy_extract_lazy_string (PyObject *string, CORE_ADDR *addr,
encoding->reset (lazy->encoding ? xstrdup (lazy->encoding) : NULL);
}
/* __str__ for LazyString. */
static PyObject *
stpy_str (PyObject *self)
{
lazy_string_object *str = (lazy_string_object *) self;
struct value_print_options opts;
get_user_print_options (&opts);
opts.addressprint = false;
string_file stream;
try
{
struct type *type = stpy_lazy_string_elt_type (str);
val_print_string (type, str->encoding, str->address, str->length,
&stream, &opts);
}
catch (const gdb_exception &exc)
{
GDB_PY_HANDLE_EXCEPTION (exc);
}
return host_string_to_python_string (stream.c_str ()).release ();
}
GDBPY_INITIALIZE_FILE (gdbpy_initialize_lazy_string);
@@ -331,7 +357,7 @@ PyTypeObject lazy_string_object_type = {
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
stpy_str, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/

View File

@@ -905,7 +905,13 @@ valpy_assign (PyObject *self_obj, PyObject *args)
try
{
value_object *self = (value_object *) self_obj;
value_assign (self->value, val);
value *new_value = value_assign (self->value, val);
/* value_as_address returns a new value with the same location
as the old one. Ensure that this gdb.Value is updated to
reflect the new value. */
new_value->incref ();
self->value->decref ();
self->value = new_value;
}
catch (const gdb_exception &except)
{

View File

@@ -0,0 +1,21 @@
/* 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 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/>. */
int main (int argc, char **argv)
{
return 0;
}

View File

@@ -0,0 +1,39 @@
# 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 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/>.
#
# This file is part of the gdb testsuite.
#
# Test a displaced stepping closure management bug, where a closure lookup
# by address returns a match even if no displaced stepping is currently
# taking place.
standard_testfile
if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
return -1
}
if ![runto_main] {
return -1
}
# We have a breakpoint at the current pc (from stopping at main). Step over
# the breakpoint.
gdb_test "stepi" ".*" "step-over breakpoint"
# Now attempt to disassemble the entry point function, where the displaced
# stepping buffer is. With the bug, gdb will crash when we attempt to list
# the PC that was used to displaced-step the previous instruction.
gdb_test "disassemble _start" ".*End of assembler dump\." \
"disassemble through displaced-step buffer"

View File

@@ -0,0 +1,95 @@
# 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 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/>.
# Check the scope of a nested function.
load_lib ada.exp
load_lib dap-support.exp
require allow_ada_tests allow_dap_tests
standard_ada_testfile prog
if {[gdb_compile_ada "${srcfile}" "${binfile}" executable \
{debug additional_flags=-gnata}] != ""} {
return -1
}
if {[dap_launch $binfile] == ""} {
return
}
set line [gdb_get_line_number "STOP"]
set obj [dap_check_request_and_response "set breakpoint" \
setBreakpoints \
[format {o source [o path [%s]] \
breakpoints [a [o line [i %d]]]} \
[list s $srcfile] $line]]
set fn_bpno [dap_get_breakpoint_number $obj]
dap_check_request_and_response "start inferior" configurationDone
dap_wait_for_event_and_check "stopped at breakpoint" stopped \
"body reason" breakpoint \
"body hitBreakpointIds" $fn_bpno
set bt [lindex [dap_check_request_and_response "backtrace" stackTrace \
{o threadId [i 1]}] \
0]
set frame_id [dict get [lindex [dict get $bt body stackFrames] 0] id]
set scopes [dap_check_request_and_response "get scopes" scopes \
[format {o frameId [i %d]} $frame_id]]
set scopes [dict get [lindex $scopes 0] body scopes]
# This is what the implementation does, so we can assume it, but check
# just in case something changes.
lassign $scopes args locals _ignore
gdb_assert {[dict get $args name] == "Arguments"} "argument scope"
gdb_assert {[dict get $locals name] == "Locals"} "local scope"
set num_vars [dict get $locals namedVariables]
# Some versions of GNAT emit an extra 'x' in scope.
gdb_assert {$num_vars == 3 || $num_vars == 4} "correct number of locals"
set num [dict get $locals variablesReference]
set refs [lindex [dap_check_request_and_response "fetch variables" \
"variables" \
[format {o variablesReference [i %d] count [i %d]} \
$num $num_vars]] \
0]
foreach var [dict get $refs body variables] {
set name [dict get $var name]
switch $name {
"i" {
gdb_assert {[dict get $var value] == "1"} "check value of i"
}
"x" - "x #2" {
# Some versions of GNAT erroneously emit a duplicate 'x'.
# Work around it here.
gdb_assert {[dict get $var value] == "12"} "check value of $name"
}
"outer_arg" {
gdb_assert {[dict get $var value] == "1"} "check value of outer_arg"
}
default {
fail "unknown variable $name"
}
}
}
dap_shutdown

View File

@@ -0,0 +1,32 @@
-- 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 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/>.
procedure Foo is
X : Integer := 12;
procedure Outer (Outer_Arg : Integer) is
procedure Bump (Stride : Integer) is
begin
X := X + Stride; -- STOP
end;
begin
Bump (Outer_Arg);
end;
begin
for I in 1 .. 20 loop
Outer (1);
end loop;
end Foo;

View File

@@ -0,0 +1,35 @@
/* Copyright 2023 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/>. */
struct special_type
{
/* Discriminator used by the pretty-printer. When zero, no fields
are shown; when non-zero, shows the datum. */
int disc;
int datum;
};
struct special_type empty = { 0, 23 };
struct special_type full = { 1, 23 };
int
main ()
{
struct special_type value = full;
return 0; /* STOP */
}

View File

@@ -0,0 +1,122 @@
# 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 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/>.
# Test the setVariable request.
require allow_dap_tests
load_lib dap-support.exp
standard_testfile
if {[build_executable ${testfile}.exp $testfile] == -1} {
return
}
set remote_python_file [gdb_remote_download host \
${srcdir}/${subdir}/${testfile}.py]
save_vars GDBFLAGS {
append GDBFLAGS " -iex \"source $remote_python_file\""
if {[dap_launch $testfile] == ""} {
return
}
}
set line [gdb_get_line_number "STOP"]
set obj [dap_check_request_and_response "set breakpoint by line number" \
setBreakpoints \
[format {o source [o path [%s]] breakpoints [a [o line [i %d]]]} \
[list s $srcfile] $line]]
set line_bpno [dap_get_breakpoint_number $obj]
dap_check_request_and_response "start inferior" configurationDone
dap_wait_for_event_and_check "stopped at line breakpoint" stopped \
"body reason" breakpoint \
"body hitBreakpointIds" $line_bpno
set bt [lindex [dap_check_request_and_response "backtrace" stackTrace \
{o threadId [i 1]}] \
0]
set frame_id [dict get [lindex [dict get $bt body stackFrames] 0] id]
set scopes [dap_check_request_and_response "get scopes" scopes \
[format {o frameId [i %d]} $frame_id]]
set scopes [dict get [lindex $scopes 0] body scopes]
lassign $scopes scope reg_scope
gdb_assert {[dict get $scope name] == "Locals"} "scope is locals"
gdb_assert {[dict get $scope namedVariables] == 1} "one var in scope"
set num [dict get $scope variablesReference]
set refs [lindex [dap_check_request_and_response "fetch variable" \
"variables" \
[format {o variablesReference [i %d] count [i 1]} \
$num]] \
0]
set desc [dict get $refs body variables]
gdb_assert {[llength $desc] == 1} "only one variable fetched"
set desc [lindex $desc 0]
set saved_ref [dict get $desc variablesReference]
proc check_vars {prefix var varref summary} {
with_test_prefix $prefix {
gdb_assert {[dict get $var name] == "value"} "variable name"
gdb_assert {[dict get $var variablesReference] == $varref} \
"variable reference"
gdb_assert {[dict get $var value] == $summary} \
"variable value"
}
}
check_vars initial $desc $saved_ref full
set refs [lindex [dap_check_request_and_response "assign empty to variable" \
"setVariable" \
[format {o variablesReference [i %d] name [s value] \
value [s empty]} \
$num]] \
0]
check_vars "assign empty" [dict get $refs body] 0 empty
set refs [lindex [dap_check_request_and_response "assign full to variable" \
"setVariable" \
[format {o variablesReference [i %d] name [s value] \
value [s full]} \
$num]] \
0]
check_vars "assign full" [dict get $refs body] $saved_ref full
# Now fetch the children of the variable, to check that the shadowing
# workaround works.
gdb_assert {[dict get $refs body namedVariables] == 2} \
"two children of variable"
set num [dict get $refs body variablesReference]
set refs [lindex [dap_check_request_and_response \
"fetch children of variable" "variables" \
[format {o variablesReference [i %d] count [i 2]} \
$num]] \
0]
lassign [dict get $refs body variables] one two
gdb_assert {[dict get $one name] != [dict get $two name]} \
"children have different names"
dap_shutdown

View File

@@ -0,0 +1,54 @@
# Copyright (C) 2022-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 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/>.
import gdb
class EmptyPrinter(gdb.ValuePrinter):
"""Pretty print a structure"""
def __init__(self, val):
self._val = val
def to_string(self):
return "empty"
class FullPrinter(gdb.ValuePrinter):
"""Pretty print a structure"""
def __init__(self, val):
self._val = val
def to_string(self):
return "full"
def children(self):
# This is used to test the renaming code.
yield "datum", self._val["datum"]
yield "datum", self._val["datum"]
def lookup_function(val):
if val.type.tag == "special_type":
if val["disc"] == 0:
return EmptyPrinter(val)
else:
return FullPrinter(val)
return None
gdb.pretty_printers.append(lookup_function)

View File

@@ -0,0 +1,118 @@
# 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 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/>.
# Test the handling of non-Value responses from 'children' method.
require allow_dap_tests
load_lib dap-support.exp
standard_testfile lazy-string.c
if {[build_executable ${testfile}.exp $testfile $srcfile] == -1} {
return
}
set remote_python_file [gdb_remote_download host \
${srcdir}/${subdir}/${testfile}.py]
save_vars GDBFLAGS {
append GDBFLAGS " -iex \"source $remote_python_file\""
if {[dap_launch $testfile] == ""} {
return
}
}
set line [gdb_get_line_number "STOP"]
set obj [dap_check_request_and_response "set breakpoint by line number" \
setBreakpoints \
[format {o source [o path [%s]] breakpoints [a [o line [i %d]]]} \
[list s $srcfile] $line]]
set line_bpno [dap_get_breakpoint_number $obj]
dap_check_request_and_response "start inferior" configurationDone
dap_wait_for_event_and_check "stopped at line breakpoint" stopped \
"body reason" breakpoint \
"body hitBreakpointIds" $line_bpno
set bt [lindex [dap_check_request_and_response "backtrace" stackTrace \
{o threadId [i 1]}] \
0]
set frame_id [dict get [lindex [dict get $bt body stackFrames] 0] id]
set scopes [dap_check_request_and_response "get scopes" scopes \
[format {o frameId [i %d]} $frame_id]]
set scopes [dict get [lindex $scopes 0] body scopes]
lassign $scopes scope reg_scope
gdb_assert {[dict get $scope name] == "Locals"} "scope is locals"
gdb_assert {[dict get $scope namedVariables] == 1} "one var in scope"
set num [dict get $scope variablesReference]
set refs [lindex [dap_check_request_and_response "fetch variable" \
"variables" \
[format {o variablesReference [i %d] count [i 1]} \
$num]] \
0]
set vars [dict get $refs body variables]
gdb_assert {[llength $vars] == 1} "just one variable"
set var [lindex $vars 0]
gdb_assert {[dict get $var name] == "the_string"} "variable name"
gdb_assert {[dict get $var value] == "contents"} "variable value"
gdb_assert {[dict get $var namedVariables] == 2} "variable has two children"
set num [dict get $var variablesReference]
set refs [lindex [dap_check_request_and_response "fetch children of variable" \
"variables" \
[format {o variablesReference [i %d] count [i 2]} \
$num]] \
0]
set vars [dict get $refs body variables]
gdb_assert {[llength $vars] == 2} "got two children of variable"
set saw_first 0
set saw_second 0
foreach var [dict get $refs body variables] {
set name [dict get $var name]
switch $name {
"first" {
gdb_assert {[dict get $var value] == 23} "value of first"
incr saw_first
}
"second" {
# The result looks strange here, but only because TON does
# not handle the backslash-quote sequence properly when
# decoding the JSON. The actual JSON is: "value":
# "\"DEI\"".
gdb_assert {[dict get $var value] == "\\\"DEI\\\""} \
"value of second"
incr saw_second
}
default {
fail "unknown variable $name"
}
}
}
gdb_assert {$saw_first == 1 && $saw_second == 1} "saw both children"
dap_shutdown

View File

@@ -0,0 +1,41 @@
# Copyright (C) 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 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/>.
import gdb
class Printer(gdb.ValuePrinter):
"""Pretty print test class."""
def __init__(self, val):
self._val = val
def to_string(self):
return "contents"
def children(self):
yield "first", 23
yield "second", "DEI"
def lookup_function(val):
typ = val.type
if typ.code == gdb.TYPE_CODE_PTR:
return Printer(val)
return None
gdb.pretty_printers.append(lookup_function)

View File

@@ -0,0 +1,23 @@
/* Copyright 2023 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/>. */
int
main ()
{
const char *the_string = "DEI";
return 0; /* STOP */
}

View File

@@ -0,0 +1,80 @@
# 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 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/>.
# Test printing of gdb.LazyString objects.
require allow_dap_tests
load_lib dap-support.exp
standard_testfile
if {[build_executable ${testfile}.exp $testfile] == -1} {
return
}
set remote_python_file [gdb_remote_download host \
${srcdir}/${subdir}/${testfile}.py]
save_vars GDBFLAGS {
append GDBFLAGS " -iex \"source $remote_python_file\""
if {[dap_launch $testfile] == ""} {
return
}
}
set line [gdb_get_line_number "STOP"]
set obj [dap_check_request_and_response "set breakpoint by line number" \
setBreakpoints \
[format {o source [o path [%s]] breakpoints [a [o line [i %d]]]} \
[list s $srcfile] $line]]
set line_bpno [dap_get_breakpoint_number $obj]
dap_check_request_and_response "start inferior" configurationDone
dap_wait_for_event_and_check "stopped at line breakpoint" stopped \
"body reason" breakpoint \
"body hitBreakpointIds" $line_bpno
set bt [lindex [dap_check_request_and_response "backtrace" stackTrace \
{o threadId [i 1]}] \
0]
set frame_id [dict get [lindex [dict get $bt body stackFrames] 0] id]
set scopes [dap_check_request_and_response "get scopes" scopes \
[format {o frameId [i %d]} $frame_id]]
set scopes [dict get [lindex $scopes 0] body scopes]
lassign $scopes scope reg_scope
gdb_assert {[dict get $scope name] == "Locals"} "scope is locals"
gdb_assert {[dict get $scope namedVariables] == 1} "one var in scope"
set num [dict get $scope variablesReference]
set refs [lindex [dap_check_request_and_response "fetch variable" \
"variables" \
[format {o variablesReference [i %d] count [i 1]} \
$num]] \
0]
foreach var [dict get $refs body variables] {
gdb_assert {[dict get $var name] == "the_string"} "variable name"
# The result looks strange here, but only because TON does not
# handle the backslash-quote sequence properly when decoding the
# JSON. The actual JSON is: "value": "\"DEI\"".
gdb_assert {[dict get $var value] == "\\\"DEI\\\""} "variable value"
}
dap_shutdown

View File

@@ -0,0 +1,37 @@
# Copyright (C) 2022-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 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/>.
import gdb
class Printer(gdb.ValuePrinter):
"""Pretty print a string"""
def __init__(self, val):
self._val = val
def to_string(self):
return self._val.lazy_string()
def lookup_function(val):
typ = val.type
if typ.code == gdb.TYPE_CODE_PTR:
return Printer(val)
return None
gdb.pretty_printers.append(lookup_function)

View File

@@ -0,0 +1,44 @@
/* This testcase is part of GDB, the GNU debugger.
Copyright 2011-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 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 <unistd.h>
int
do_nothing ()
{
return 91;
}
int
return_false ()
{
return 0;
}
void
sleep_a_bit ()
{
sleep (1); /* STOP */
}
int
main ()
{
while (1)
sleep_a_bit ();
return 0;
}

View File

@@ -19,7 +19,7 @@ require allow_dap_tests
load_lib dap-support.exp
standard_testfile attach.c
standard_testfile
if {[build_executable ${testfile}.exp $testfile $srcfile] == -1} {
return
@@ -29,13 +29,48 @@ if {[dap_launch $testfile] == ""} {
return
}
# Set a conditional breakpoint that will never fire. This is done to
# test the state-tracking in events -- an inferior call from a
# breakpoint condition should not cause any sort of stop or continue
# events.
set line [gdb_get_line_number "STOP"]
dap_check_request_and_response "set conditional breakpoint" \
setBreakpoints \
[format {o source [o path [%s]] \
breakpoints [a [o line [i %d] \
condition [s "return_false()"]]]} \
[list s $srcfile] $line]
dap_check_request_and_response "start inferior" configurationDone
dap_wait_for_event_and_check "inferior started" thread "body reason" started
set resp [lindex [dap_request_and_response evaluate {o expression [s 23]}] \
0]
gdb_assert {[dict get $resp success] == "false"} \
"evaluate failed while inferior executing"
gdb_assert {[dict get $resp message] == "notStopped"} \
"evaluate issued notStopped"
dap_check_request_and_response pause pause \
{o threadId [i 1]}
dap_wait_for_event_and_check "stopped by pause" stopped \
"body reason" pause
set result [dap_request_and_response evaluate {o expression [s do_nothing()]}]
gdb_assert {[dict get [lindex $result 0] body result] == 91} \
"check result of evaluation"
set seen fail
foreach event [lindex $result 1] {
if {[dict get $event type] != "event"} {
continue
}
if {[dict get $event event] == "continued"} {
set seen pass
break
}
}
gdb_assert {$seen == "pass"} "continue event from inferior call"
dap_shutdown

View File

@@ -15,9 +15,21 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
struct some_struct
{
int x;
int y;
static int z;
};
int some_struct::z = 37;
void
func ()
{
some_struct aggregate { 91, 87 };
int value = 23;
int *ptr = &value;

View File

@@ -55,7 +55,7 @@ gdb_assert {[llength $scopes] == 2} "two scopes"
lassign $scopes scope reg_scope
gdb_assert {[dict get $scope name] == "Locals"} "scope is locals"
gdb_assert {[dict get $scope namedVariables] == 3} "three vars in scope"
gdb_assert {[dict get $scope namedVariables] == 4} "three vars in scope"
set num [dict get $scope variablesReference]
set refs [lindex [dap_check_request_and_response "fetch variables" \
@@ -97,6 +97,13 @@ foreach var [dict get $refs body variables] {
"$name has exactly one child"
fetch_pointer $name $var
}
"aggregate" {
gdb_assert {[dict get $var variablesReference] != 0} \
"$name has children"
# This should omit the static field.
gdb_assert {[dict get $var namedVariables] == 2} \
"$name has exactly 2 children"
}
}
}

View File

@@ -52,6 +52,13 @@ set scopes [dap_check_request_and_response "get scopes" scopes \
[format {o frameId [i %d]} $frame_id]]
set scopes [dict get [lindex $scopes 0] body scopes]
# Request the scopes twice, and verify that the results are identical.
# GDB previously had a bug where it would return new scopes each time.
set scopes2 [dap_check_request_and_response "get scopes again" scopes \
[format {o frameId [i %d]} $frame_id]]
set scopes2 [dict get [lindex $scopes2 0] body scopes]
gdb_assert {$scopes2 == $scopes} "identical scopes requests yield same body"
gdb_assert {[llength $scopes] == 2} "two scopes"
lassign $scopes scope reg_scope
@@ -118,8 +125,27 @@ gdb_assert {[llength $deivals] == 2} "dei has two members"
set num [dict get $reg_scope variablesReference]
# The request succeeding is sufficient.
dap_check_request_and_response "fetch first register" \
"variables" \
[format {o variablesReference [i %d] count [i 1]} $num]
set val [dap_check_request_and_response "fetch first register" \
"variables" \
[format {o variablesReference [i %d] count [i 1]} $num]]
# Try setting the value to something else.
set val [dict get [lindex $val 0] body variables]
set name [dict get [lindex $val 0] name]
set val [dict get [lindex $val 0] value]
# Just make sure it is different from the original value.
set val [expr {$val ^ 7}]
# setVariable isn't implemented yet, so use the register name. Note
# that we sneak the "$" into the name, written in a slightly funny way
# to work around apparent TON limitations.
set response [dap_check_request_and_response "set first register" \
setExpression \
[format {o expression [s \$%s] value [s %d] frameId [i %d]} \
$name $val $frame_id]]
set response [lindex $response 0]
gdb_assert {[dict get $response body value] == $val} \
"setting register yields updated value"
dap_shutdown

View File

@@ -0,0 +1,41 @@
/* Copyright 2023 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/>. */
int function (int x, char y)
{
return y - x - 1; /* BREAK */
}
int z_1 (int x, char y)
{
return function (x, y);
}
int z_2 (int x, char y)
{
return z_1 (x, y);
}
int z_3 (int x, char y)
{
return z_2 (x, y);
}
int main ()
{
return z_3 (64, 'A');
}

View File

@@ -0,0 +1,133 @@
# 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 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/>.
# Test DAP stack format options.
require allow_dap_tests
load_lib dap-support.exp
standard_testfile
if {[build_executable ${testfile}.exp $testfile] == -1} {
return
}
set remote_python_file [gdb_remote_download host \
${srcdir}/${subdir}/${testfile}.py]
save_vars GDBFLAGS {
append GDBFLAGS " -iex \"source $remote_python_file\""
if {[dap_launch $testfile] == ""} {
return
}
}
set line [gdb_get_line_number "BREAK"]
set obj [dap_check_request_and_response "set breakpoint by line number" \
setBreakpoints \
[format {o source [o path [%s]] breakpoints [a [o line [i %d]]]} \
[list s $srcfile] $line]]
set line_bpno [dap_get_breakpoint_number $obj]
dap_check_request_and_response "start inferior" configurationDone
dap_wait_for_event_and_check "stopped at line breakpoint" stopped \
"body reason" breakpoint \
"body hitBreakpointIds" $line_bpno
# Each request should return the same frame ID. Store it here,
# indexed by requested frame.
array set frame_id {}
# Request a single frame of the stack trace and check it. NAME is the
# name of the test. FRAME is the number of the frame to request.
# FORMAT is contents of the StackFrameFormat object to use. It is in
# TON form. EXPECTED is the expected 'name' value of the resulting
# frame. If REGEXP is set, then EXPECTED is a regular expression;
# otherwise it is treated as the exact string result.
proc check_stack_frame {name frame format expected {regexp 0}} {
with_test_prefix $name {
set args [format {o startFrame [i %d] levels [i 1] threadId [i 1]} \
$frame]
if {$format != ""} {
append args " format \[o $format\]"
}
set bt [lindex [dap_check_request_and_response "backtrace" \
stackTrace $args] \
0]
set frame_info [lindex [dict get $bt body stackFrames] 0]
# Each request at this level should return the same frame ID.
set this_id [dict get $frame_info id]
global frame_id
if {[info exists frame_id($frame)]} {
gdb_assert {$frame_id($frame) == $this_id} "unchanging frame id"
} else {
set frame_id($frame) $this_id
}
if {$regexp} {
gdb_assert {[regexp $expected [dict get $frame_info name]]} \
"got expected name"
} else {
gdb_assert {[dict get $frame_info name] == $expected} \
"got expected name"
}
}
}
check_stack_frame empty 0 {} "function"
check_stack_frame parameters 0 {parameters [l true] \
parameterTypes [l true] \
parameterNames [l true] \
parameterValues [l true]} \
{function([int] x = 64, [char] y = 65 'A')}
# When 'parameters' is false, it disables the other parameter*
# options. This was clarified in
# https://github.com/microsoft/debug-adapter-protocol/issues/411
check_stack_frame noparams 0 {parameters [l false] \
parameterTypes [l true] \
parameterNames [l true] \
parameterValues [l true]} \
"function"
check_stack_frame line 0 {line [l true] module [l true]} \
"function, line $line, module .*stack-format" \
1
check_stack_frame hex 0 \
{parameters [l true] parameterValues [l true] hex [l true]} \
"function(0x40, 0x41)"
check_stack_frame elided-main 1 {} "main"
# The next requests will ask for all frames, so the old frame 1 will
# be the new frame 4. Update the map to check this.
set frame_id(4) $frame_id(1)
unset frame_id(1)
check_stack_frame no-elide 0 {includeAll [l true]} "function"
check_stack_frame z-frame-1 1 {includeAll [l true]} "z_1"
check_stack_frame z-frame-2 2 {includeAll [l true]} "z_2"
check_stack_frame z-frame-3 3 {includeAll [l true]} "z_3"
check_stack_frame main-include-all 4 {includeAll [l true]} "main"
dap_shutdown

View File

@@ -0,0 +1,64 @@
# Copyright (C) 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 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/>.
import gdb
import itertools
from gdb.FrameDecorator import DAPFrameDecorator
class ElidingFrameDecorator(DAPFrameDecorator):
def __init__(self, frame, elided_frames):
super(ElidingFrameDecorator, self).__init__(frame)
self.elided_frames = elided_frames
def elided(self):
return iter(self.elided_frames)
class ElidingIterator:
def __init__(self, ii):
self.input_iterator = ii
def __iter__(self):
return self
def __next__(self):
frame = next(self.input_iterator)
if str(frame.function()) != "function":
return frame
# Elide the next three frames.
elided = []
elided.append(next(self.input_iterator))
elided.append(next(self.input_iterator))
elided.append(next(self.input_iterator))
return ElidingFrameDecorator(frame, elided)
class FrameElider:
def __init__(self):
self.name = "Elider"
self.priority = 900
self.enabled = True
gdb.frame_filters[self.name] = self
def filter(self, frame_iter):
return ElidingIterator(frame_iter)
FrameElider()

View File

@@ -1 +1 @@
14.0.50.DATE-git
14.1

57
gdbserver/configure vendored
View File

@@ -7135,6 +7135,63 @@ if test "x$ac_cv_func_fork_works" = xyes; then
$as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h
fi
# Some systems (e.g. Solaris) have `socketpair' in libsocket.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing socketpair" >&5
$as_echo_n "checking for library containing socketpair... " >&6; }
if ${ac_cv_search_socketpair+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char socketpair ();
int
main ()
{
return socketpair ();
;
return 0;
}
_ACEOF
for ac_lib in '' socket; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_socketpair=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_socketpair+:} false; then :
break
fi
done
if ${ac_cv_search_socketpair+:} false; then :
else
ac_cv_search_socketpair=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socketpair" >&5
$as_echo "$ac_cv_search_socketpair" >&6; }
ac_res=$ac_cv_search_socketpair
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
for ac_func in fdwalk getrlimit pipe pipe2 poll socketpair sigaction \

View File

@@ -51,6 +51,8 @@ AC_DEFUN([GDB_AC_COMMON], [
AC_FUNC_MMAP
AC_FUNC_FORK
# Some systems (e.g. Solaris) have `socketpair' in libsocket.
AC_SEARCH_LIBS(socketpair, socket)
AC_CHECK_FUNCS([fdwalk getrlimit pipe pipe2 poll socketpair sigaction \
ptrace64 sbrk setns sigaltstack sigprocmask \
setpgid setpgrp getrusage getauxval sigtimedwait])

57
gdbsupport/configure vendored
View File

@@ -8151,6 +8151,63 @@ if test "x$ac_cv_func_fork_works" = xyes; then
$as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h
fi
# Some systems (e.g. Solaris) have `socketpair' in libsocket.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing socketpair" >&5
$as_echo_n "checking for library containing socketpair... " >&6; }
if ${ac_cv_search_socketpair+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char socketpair ();
int
main ()
{
return socketpair ();
;
return 0;
}
_ACEOF
for ac_lib in '' socket; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_socketpair=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_socketpair+:} false; then :
break
fi
done
if ${ac_cv_search_socketpair+:} false; then :
else
ac_cv_search_socketpair=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socketpair" >&5
$as_echo "$ac_cv_search_socketpair" >&6; }
ac_res=$ac_cv_search_socketpair
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
for ac_func in fdwalk getrlimit pipe pipe2 poll socketpair sigaction \