mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 12:34:43 +00:00
Compare commits
86 Commits
efbd9add96
...
gdb-14.1-r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6bda1c19bc | ||
|
|
dfd845d276 | ||
|
|
64be8e424a | ||
|
|
fc1974557a | ||
|
|
3e1fd5afc4 | ||
|
|
385e95de46 | ||
|
|
403e9cfa01 | ||
|
|
823c11ef48 | ||
|
|
fb2d9542d0 | ||
|
|
5a37e5855c | ||
|
|
e447ef22ff | ||
|
|
1d688670c0 | ||
|
|
f61c9af603 | ||
|
|
ec05816a66 | ||
|
|
6eb20c0cce | ||
|
|
0bf1de7bb4 | ||
|
|
643b97a37d | ||
|
|
ab1d96b02b | ||
|
|
e6cad0cfd3 | ||
|
|
17f2cab5f8 | ||
|
|
fa2cc08fdd | ||
|
|
c558c8d5f6 | ||
|
|
de5b5fcb89 | ||
|
|
45b2be6618 | ||
|
|
aad1b7a5f6 | ||
|
|
6ab1f30817 | ||
|
|
aa5cef35e0 | ||
|
|
5458a19397 | ||
|
|
1df83e9a59 | ||
|
|
add31fcb87 | ||
|
|
d31566acb7 | ||
|
|
a3ca9693ff | ||
|
|
f703d35ad8 | ||
|
|
43f2540a8e | ||
|
|
18a2a7470f | ||
|
|
8015944464 | ||
|
|
e7e8d3e6c8 | ||
|
|
68c2310748 | ||
|
|
45e4611f07 | ||
|
|
35dfe80b27 | ||
|
|
bdb2b760e4 | ||
|
|
56c91b27cf | ||
|
|
f8acd77d79 | ||
|
|
30ba1331de | ||
|
|
f5f044c6d9 | ||
|
|
89a64d71b1 | ||
|
|
dfa27703ba | ||
|
|
42f8113b09 | ||
|
|
b81c6d0cc5 | ||
|
|
618bc9302e | ||
|
|
90e0e073f0 | ||
|
|
e8d0a795d9 | ||
|
|
e2f96589e0 | ||
|
|
934a14fd6b | ||
|
|
d273f35dbc | ||
|
|
d24f7fe446 | ||
|
|
445f4eb995 | ||
|
|
e7362a5e29 | ||
|
|
8d1b8bcb7f | ||
|
|
c8de56a239 | ||
|
|
f77f816268 | ||
|
|
7af94459a4 | ||
|
|
f07873df4d | ||
|
|
c7a4aa64e7 | ||
|
|
5584da0691 | ||
|
|
ea4174c96a | ||
|
|
2aabf131da | ||
|
|
ba0725dfa0 | ||
|
|
8836926927 | ||
|
|
c1edbcf2ba | ||
|
|
c9d954f934 | ||
|
|
53f160095d | ||
|
|
5eb4a57727 | ||
|
|
490af0931a | ||
|
|
6418429b8c | ||
|
|
a0939697c3 | ||
|
|
16ad65ea6c | ||
|
|
e2c1272009 | ||
|
|
78e450ac1d | ||
|
|
d06cabec12 | ||
|
|
20ce117bd1 | ||
|
|
8252d5f410 | ||
|
|
e51955483b | ||
|
|
b07cf62258 | ||
|
|
01d4f264bd | ||
|
|
d95407ee06 |
@@ -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
|
||||
|
||||
@@ -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@
|
||||
|
||||
7
gdb/NEWS
7
gdb/NEWS
@@ -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.
|
||||
|
||||
13
gdb/block.c
13
gdb/block.c
@@ -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)
|
||||
{
|
||||
|
||||
21
gdb/block.h
21
gdb/block.h
@@ -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
119
gdb/configure
vendored
@@ -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 \
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
40
gdb/frame.c
40
gdb/frame.c
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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. */
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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])
|
||||
|
||||
|
||||
|
||||
@@ -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 */
|
||||
};
|
||||
|
||||
|
||||
@@ -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*/
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
21
gdb/testsuite/gdb.base/displaced-step-closure.c
Normal file
21
gdb/testsuite/gdb.base/displaced-step-closure.c
Normal 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;
|
||||
}
|
||||
39
gdb/testsuite/gdb.base/displaced-step-closure.exp
Normal file
39
gdb/testsuite/gdb.base/displaced-step-closure.exp
Normal 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"
|
||||
95
gdb/testsuite/gdb.dap/ada-nested.exp
Normal file
95
gdb/testsuite/gdb.dap/ada-nested.exp
Normal 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
|
||||
32
gdb/testsuite/gdb.dap/ada-nested/prog.adb
Normal file
32
gdb/testsuite/gdb.dap/ada-nested/prog.adb
Normal 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;
|
||||
35
gdb/testsuite/gdb.dap/assign.c
Normal file
35
gdb/testsuite/gdb.dap/assign.c
Normal 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 */
|
||||
}
|
||||
122
gdb/testsuite/gdb.dap/assign.exp
Normal file
122
gdb/testsuite/gdb.dap/assign.exp
Normal 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
|
||||
54
gdb/testsuite/gdb.dap/assign.py
Normal file
54
gdb/testsuite/gdb.dap/assign.py
Normal 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)
|
||||
118
gdb/testsuite/gdb.dap/children.exp
Normal file
118
gdb/testsuite/gdb.dap/children.exp
Normal 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
|
||||
41
gdb/testsuite/gdb.dap/children.py
Normal file
41
gdb/testsuite/gdb.dap/children.py
Normal 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)
|
||||
23
gdb/testsuite/gdb.dap/lazy-string.c
Normal file
23
gdb/testsuite/gdb.dap/lazy-string.c
Normal 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 */
|
||||
}
|
||||
80
gdb/testsuite/gdb.dap/lazy-string.exp
Normal file
80
gdb/testsuite/gdb.dap/lazy-string.exp
Normal 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
|
||||
37
gdb/testsuite/gdb.dap/lazy-string.py
Normal file
37
gdb/testsuite/gdb.dap/lazy-string.py
Normal 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)
|
||||
44
gdb/testsuite/gdb.dap/pause.c
Normal file
44
gdb/testsuite/gdb.dap/pause.c
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
41
gdb/testsuite/gdb.dap/stack-format.c
Normal file
41
gdb/testsuite/gdb.dap/stack-format.c
Normal 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');
|
||||
}
|
||||
133
gdb/testsuite/gdb.dap/stack-format.exp
Normal file
133
gdb/testsuite/gdb.dap/stack-format.exp
Normal 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
|
||||
64
gdb/testsuite/gdb.dap/stack-format.py
Normal file
64
gdb/testsuite/gdb.dap/stack-format.py
Normal 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()
|
||||
@@ -1 +1 @@
|
||||
14.0.50.DATE-git
|
||||
14.1
|
||||
|
||||
57
gdbserver/configure
vendored
57
gdbserver/configure
vendored
@@ -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 \
|
||||
|
||||
@@ -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
57
gdbsupport/configure
vendored
@@ -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 \
|
||||
|
||||
Reference in New Issue
Block a user