From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 20669 invoked by alias); 13 Sep 2011 19:44:19 -0000 Received: (qmail 20121 invoked by uid 22791); 13 Sep 2011 19:43:56 -0000 X-SWARE-Spam-Status: No, hits=-2.5 required=5.0 tests=AWL,BAYES_50,KAM_STOCKTIP,RCVD_IN_DNSWL_HI,RP_MATCHES_RCVD,SPF_HELO_PASS,TW_BJ,TW_CP,TW_DL,TW_EG,TW_XZ X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 13 Sep 2011 19:43:18 +0000 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id p8DJhIgm001845 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Tue, 13 Sep 2011 15:43:18 -0400 Received: from host1.jankratochvil.net (ovpn-116-38.ams2.redhat.com [10.36.116.38]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id p8DJhAlD021604 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Tue, 13 Sep 2011 15:43:12 -0400 Received: from host1.jankratochvil.net (localhost [127.0.0.1]) by host1.jankratochvil.net (8.14.4/8.14.4) with ESMTP id p8DJh9mv015541 for ; Tue, 13 Sep 2011 21:43:09 +0200 Received: (from jkratoch@localhost) by host1.jankratochvil.net (8.14.4/8.14.4/Submit) id p8DJh6UF015537 for gdb-patches@sourceware.org; Tue, 13 Sep 2011 21:43:06 +0200 Date: Tue, 13 Sep 2011 19:44:00 -0000 From: Jan Kratochvil To: gdb-patches@sourceware.org Subject: [patch 00/12] entryval#2: Fix x86_64 parameters, virtual tail call frames Message-ID: <20110913194306.GA12849@host1.jankratochvil.net> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.21 (2010-09-15) X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2011-09/txt/msg00223.txt.bz2 Hi, re-post of the series: [patch 00/12] entryval: Fix x86_64 parameters, virtual tail call frames http://sourceware.org/ml/gdb-patches/2011-07/msg00430.html particular excerpt: The patches are available (merged only) in GIT for more convenience at: http://sourceware.org/gdb/wiki/ArcherBranchManagement archer-jankratochvil-entryval Here is attached a diff against the previous patch series. The changes are: New `set print entry-values' setting. Printed now for any frame arguments printing (not just during `bt full'). $sp shift is simulated in the tail call frames. There is new NO_ENTRY_VALUE_ERROR (NOT_FOUND_ERROR is no longer reused). [patch 07/12] const/non-const dropped thanks to new lval_funcs->coerce_ref. Entry values are attempted even for non-loclist DWARF expressions. MI is supported now, no specific MI protocol changes were needed. Reviews were replied before. I will check it in if no new issues appear. Thanks, Jan --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -772,7 +772,7 @@ cli/cli-decode.h cli/cli-cmds.h cli/cli-dump.h cli/cli-utils.h \ cli/cli-script.h macrotab.h symtab.h version.h gnulib/wchar.in.h \ gnulib/string.in.h gnulib/str-two-way.h \ gnulib/stdint.in.h remote.h gdb.h sparc-nat.h \ -gdbthread.h dwarf2-frame.h nbsd-nat.h dcache.h \ +gdbthread.h dwarf2-frame.h dwarf2-frame-tailcall.h nbsd-nat.h dcache.h \ amd64-nat.h s390-tdep.h arm-linux-tdep.h exceptions.h macroscope.h \ gdbarch.h bsd-uthread.h gdb_stat.h memory-map.h memrange.h \ mdebugread.h m88k-tdep.h stabsread.h hppa-linux-offsets.h linux-fork.h \ --- a/gdb/NEWS +++ b/gdb/NEWS @@ -93,6 +93,19 @@ info auto-load-scripts [REGEXP] begin, assuming that tracepoints will be enabled as needed while the trace is running. +* New options + +set print entry-values (both|compact|default|if-needed|no|only|preferred) +show print entry-values + Set printing of frame arguments values at function entry. In some cases + GDB can determine the value of function argument which was passed by the + function caller, despite the argument value may be already modified. + +set debug tailcall +show debug tailcall + Control display of debugging info for determining virtual tail call frames, + present in inferior debug info together with the @entry values. + * New remote packets QTEnable --- a/gdb/ada-lang.h +++ b/gdb/ada-lang.h @@ -164,7 +164,8 @@ extern void ada_print_typedef (struct type *type, struct symbol *new_symbol, struct ui_file *stream); extern int ada_val_print (struct type *, const gdb_byte *, int, CORE_ADDR, - struct ui_file *, int, struct value *, + struct ui_file *, int, + const struct value *, const struct value_print_options *); extern int ada_value_print (struct value *, struct ui_file *, --- a/gdb/ada-valprint.c +++ b/gdb/ada-valprint.c @@ -37,18 +37,23 @@ #include "objfiles.h" static void print_record (struct type *, const gdb_byte *, int, - struct ui_file *, int, struct value *, + struct ui_file *, + int, + const struct value *, const struct value_print_options *); static int print_field_values (struct type *, const gdb_byte *, - int, struct ui_file *, int, struct value *, + int, + struct ui_file *, int, + const struct value *, const struct value_print_options *, int, struct type *, int); static void adjust_type_signedness (struct type *); static int ada_val_print_1 (struct type *, const gdb_byte *, int, CORE_ADDR, - struct ui_file *, int, struct value *, + struct ui_file *, int, + const struct value *, const struct value_print_options *); @@ -568,7 +573,8 @@ ada_printstr (struct ui_file *stream, struct type *type, int ada_val_print (struct type *type, const gdb_byte *valaddr, int embedded_offset, CORE_ADDR address, - struct ui_file *stream, int recurse, struct value *val, + struct ui_file *stream, int recurse, + const struct value *val, const struct value_print_options *options) { volatile struct gdb_exception except; @@ -595,7 +601,8 @@ ada_val_print (struct type *type, const gdb_byte *valaddr, static int ada_val_print_array (struct type *type, const gdb_byte *valaddr, int offset, CORE_ADDR address, - struct ui_file *stream, int recurse, struct value *val, + struct ui_file *stream, int recurse, + const struct value *val, const struct value_print_options *options) { int result = 0; @@ -663,7 +670,7 @@ static int ada_val_print_1 (struct type *type, const gdb_byte *valaddr, int offset, CORE_ADDR address, struct ui_file *stream, int recurse, - struct value *original_value, + const struct value *original_value, const struct value_print_options *options) { unsigned int len; @@ -892,23 +899,14 @@ ada_val_print_1 (struct type *type, const gdb_byte *valaddr, if (TYPE_CODE (elttype) != TYPE_CODE_UNDEF) { CORE_ADDR deref_val_int; + struct value *deref_val; - if (VALUE_LVAL (original_value) == lval_computed) + deref_val = coerce_ref_if_computed (original_value); + if (deref_val) { - const struct lval_funcs *funcs; - - funcs = value_computed_funcs (original_value); - if (funcs->indirect) - { - struct value *result = funcs->indirect (original_value); - - if (result) - { - common_val_print (result, stream, recurse, - options, current_language); - return 0; - } - } + common_val_print (deref_val, stream, recurse + 1, options, + current_language); + return 0; } deref_val_int = unpack_pointer (type, valaddr + offset_aligned); @@ -940,7 +938,8 @@ ada_val_print_1 (struct type *type, const gdb_byte *valaddr, static int print_variant_part (struct type *type, int field_num, const gdb_byte *valaddr, int offset, - struct ui_file *stream, int recurse, struct value *val, + struct ui_file *stream, int recurse, + const struct value *val, const struct value_print_options *options, int comma_needed, struct type *outer_type, int outer_offset) @@ -1014,7 +1013,8 @@ ada_value_print (struct value *val0, struct ui_file *stream, static void print_record (struct type *type, const gdb_byte *valaddr, int offset, - struct ui_file *stream, int recurse, struct value *val, + struct ui_file *stream, int recurse, + const struct value *val, const struct value_print_options *options) { type = ada_check_typedef (type); @@ -1050,7 +1050,7 @@ print_record (struct type *type, const gdb_byte *valaddr, static int print_field_values (struct type *type, const gdb_byte *valaddr, int offset, struct ui_file *stream, int recurse, - struct value *val, + const struct value *val, const struct value_print_options *options, int comma_needed, struct type *outer_type, int outer_offset) --- a/gdb/c-lang.h +++ b/gdb/c-lang.h @@ -73,7 +73,8 @@ extern void c_print_typedef (struct type *, extern int c_val_print (struct type *, const gdb_byte *, int, CORE_ADDR, - struct ui_file *, int, struct value *, + struct ui_file *, int, + const struct value *, const struct value_print_options *); extern int c_value_print (struct value *, struct ui_file *, @@ -118,13 +119,15 @@ extern void cp_print_class_member (const gdb_byte *, struct type *, extern void cp_print_value_fields (struct type *, struct type *, const gdb_byte *, int, CORE_ADDR, - struct ui_file *, int, struct value *, + struct ui_file *, int, + const struct value *, const struct value_print_options *, struct type **, int); extern void cp_print_value_fields_rtti (struct type *, const gdb_byte *, int, CORE_ADDR, - struct ui_file *, int, struct value *, + struct ui_file *, int, + const struct value *, const struct value_print_options *, struct type **, int); --- a/gdb/c-valprint.c +++ b/gdb/c-valprint.c @@ -149,7 +149,8 @@ c_textual_element_type (struct type *type, char format) int c_val_print (struct type *type, const gdb_byte *valaddr, int embedded_offset, CORE_ADDR address, - struct ui_file *stream, int recurse, struct value *original_value, + struct ui_file *stream, int recurse, + const struct value *original_value, const struct value_print_options *options) { struct gdbarch *gdbarch = get_type_arch (type); @@ -381,22 +382,12 @@ c_val_print (struct type *type, const gdb_byte *valaddr, { struct value *deref_val; - if (VALUE_LVAL (original_value) == lval_computed) + deref_val = coerce_ref_if_computed (original_value); + if (deref_val) { - const struct lval_funcs *funcs; - - funcs = value_computed_funcs (original_value); - if (funcs->indirect) - { - struct value *result = funcs->indirect (original_value); - - if (result) - { - common_val_print (result, stream, recurse, - options, current_language); - return 0; - } - } + common_val_print (deref_val, stream, recurse, options, + current_language); + return 0; } deref_val = value_at (TYPE_TARGET_TYPE (type), --- a/gdb/cp-valprint.c +++ b/gdb/cp-valprint.c @@ -85,7 +85,7 @@ static void cp_print_static_field (struct type *, struct value *, static void cp_print_value (struct type *, struct type *, const gdb_byte *, int, CORE_ADDR, struct ui_file *, - int, struct value *, + int, const struct value *, const struct value_print_options *, struct type **); @@ -159,7 +159,7 @@ void cp_print_value_fields (struct type *type, struct type *real_type, const gdb_byte *valaddr, int offset, CORE_ADDR address, struct ui_file *stream, - int recurse, struct value *val, + int recurse, const struct value *val, const struct value_print_options *options, struct type **dont_print_vb, int dont_print_statmem) @@ -432,7 +432,7 @@ cp_print_value_fields_rtti (struct type *type, const gdb_byte *valaddr, int offset, CORE_ADDR address, struct ui_file *stream, int recurse, - struct value *val, + const struct value *val, const struct value_print_options *options, struct type **dont_print_vb, int dont_print_statmem) @@ -471,7 +471,7 @@ static void cp_print_value (struct type *type, struct type *real_type, const gdb_byte *valaddr, int offset, CORE_ADDR address, struct ui_file *stream, - int recurse, struct value *val, + int recurse, const struct value *val, const struct value_print_options *options, struct type **dont_print_vb) { @@ -498,7 +498,7 @@ cp_print_value (struct type *type, struct type *real_type, struct type *baseclass = check_typedef (TYPE_BASECLASS (type, i)); char *basename = TYPE_NAME (baseclass); const gdb_byte *base_valaddr = NULL; - struct value *base_val = NULL; + const struct value *base_val = NULL; volatile struct gdb_exception ex; if (BASETYPE_VIA_VIRTUAL (type, i)) --- a/gdb/d-lang.h +++ b/gdb/d-lang.h @@ -27,7 +27,8 @@ extern char *d_demangle (const char *mangled, int options); extern int d_val_print (struct type *type, const gdb_byte *valaddr, int embedded_offset, CORE_ADDR address, - struct ui_file *stream, int recurse, struct value *val, + struct ui_file *stream, int recurse, + const struct value *val, const struct value_print_options *options); #endif /* !defined (D_LANG_H) */ --- a/gdb/d-valprint.c +++ b/gdb/d-valprint.c @@ -72,7 +72,8 @@ dynamic_array_type (struct type *type, const gdb_byte *valaddr, int d_val_print (struct type *type, const gdb_byte *valaddr, int embedded_offset, CORE_ADDR address, struct ui_file *stream, int recurse, - struct value *val, const struct value_print_options *options) + const struct value *val, + const struct value_print_options *options) { int ret; --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -5947,13 +5947,8 @@ Similar, but print only the outermost @var{n} frames. @itemx bt full @itemx bt full @var{n} @itemx bt full -@var{n} -Print the values of the local variables also. - -Names with appended @code{@@entry} show values of function parameters at the -time the function got called. The @code{@@entry} parameter line is not shown -when the entry value is not available. - -@var{n} specifies the number of frames to print, as described above. +Print the values of the local variables also. @var{n} specifies the +number of frames to print, as described above. @end table @kindex where @@ -6228,10 +6223,6 @@ architectures) that you specify in the @code{frame} command. @item info args Print the arguments of the selected frame, each on a separate line. -Names with appended @code{@@entry} show values of function parameters at the -time the function got called. The @code{@@entry} parameter line is not shown -when the entry value is not available. - @item info locals @kindex info locals Print the local variables of the selected frame, each on a separate @@ -7286,10 +7277,9 @@ If you ask to print an object whose contents are unknown to by the debug information, @value{GDBN} will say @samp{}. @xref{Symbols, incomplete type}, for more about this. -If you append @code{@@entry} string to a function parameter name you get its +If you append @kbd{@@entry} string to a function parameter name you get its value at the time the function got called. If the value is not available an -error message is printed. Entry values are available only since @value{NGCC} -version 4.7. +error message is printed. Entry values are available only with some compilers. @smallexample Breakpoint 1, d (i=30) at gdb.base/entry-value.c:29 @@ -7966,6 +7956,118 @@ thus speeding up the display of each Ada frame. @item show print frame-arguments Show how the value of arguments should be displayed when printing a frame. +@item set print entry-values @var{value} +@kindex set print entry-values +Set printing of frame arguments values at function entry. In some cases +@value{GDBN} can determine the value of function argument which was passed by +the function caller, despite the argument value may be already modified by the +current function and therefore different. For optimized code also the current +value may be possibly not available and the entry value may be still known, +which aids the debugging of production code. + +The default value is @code{default} (see below for its description). Older +@value{GDBN} behaved as with the setting @code{no}. Compilers not supporting +this feature will behave in the default @code{default} setting the same way as +with the @code{no} setting. + +This functionality is currently supported only by DWARF 2 debugging format and +the compiler has to produce @samp{DW_TAG_GNU_call_site} tags. For example for +@value{NGCC} additionally optimization and debugging compilation options must +be enabled (@option{-O -g}). Still even with the required debug info there +exist many reasons why the code path analysis by @value{GDBN} may fail in +specific cases. + +@table @code +@item no +Print only actual parameter values, never print values from function entry +point. +@smallexample +#0 equal (val=5) +#0 different (val=6) +#0 lost (val=) +#0 born (val=10) +#0 invalid (val=) +@end smallexample + +@item only +set print entry-values only +Print only parameter values from function entry point. The actual parameter +values are never printed. +@smallexample +#0 equal (val@@entry=5) +#0 different (val@@entry=5) +#0 lost (val@@entry=5) +#0 born (val@@entry=) +#0 invalid (val@@entry=) +@end smallexample + +@item preferred +Print only parameter values from function entry point. If value from function +entry point is not known while the actual value is known print at least the +actual value for such parameter. +@smallexample +#0 equal (val@@entry=5) +#0 different (val@@entry=5) +#0 lost (val@@entry=5) +#0 born (val@@entry=) +#0 invalid (val@@entry=) +@end smallexample + +@item if-needed +Print actual parameter values. If actual parameter value is not known while +value from function entry point is known print at least the entry point value +for such parameter. +@smallexample +#0 equal (val@@entry=5) +#0 different (val@@entry=5) +#0 lost (val@@entry=5) +#0 born (val=10) +#0 invalid (val@@entry=) +@end smallexample + +@item both +set print entry-values both +Always print both the actual parameter value and its value from function entry +point. Still print both even if one of them or both are @code{}. +@smallexample +#0 equal (val=5, val@@entry=5) +#0 different (val=6, val@@entry=5) +#0 lost (val=, val@@entry=5) +#0 born (val=10, val@@entry=) +#0 invalid (val=, val@@entry=) +@end smallexample + +@item compact +Print the actual parameter value if it is know and also its value from +function entry point if it is known. If neither is known print for the actual +value @code{}. If not in MI mode (@pxref{GDB/MI}) and if both +values are known and they are equal print the shortened +@code{param=param@@entry=VALUE} notation. +@smallexample +#0 equal (val=val@@entry=5) +#0 different (val=6, val@@entry=5) +#0 lost (val@@entry=5) +#0 born (val=10) +#0 invalid (val=) +@end smallexample + +@item default +Always print the actual parameter value. Print also its value from function +entry point but only if it is known. If not in MI mode (@pxref{GDB/MI}) and if +both values are known and they are equal print the shortened +@code{param=param@@entry=VALUE} notation. +@smallexample +#0 equal (val=val@@entry=5) +#0 different (val=6, val@@entry=5) +#0 lost (val=, val@@entry=5) +#0 born (val=10) +#0 invalid (val=) +@end smallexample +@end table + +@item show print entry-values +Show printing of frame arguments values at function entry. + @item set print repeats @cindex repeated array elements Set the threshold for suppressing display of repeated array @@ -9511,6 +9613,7 @@ please report it to us as a bug (including a test case!). @menu * Inline Functions:: How @value{GDBN} presents inlining +* Tail Call Frames:: @value{GDBN} analysis of jumps to functions @end menu @node Inline Functions @@ -9578,6 +9681,60 @@ and print a variable where your program stored the return value. @end itemize +@node Tail Call Frames +@section Tail Call Frames +@cindex tail call frames, debugging + +Function @code{B} can call function @code{C} by its very last statement. In +unoptimized compilation the call of @code{C} is immediately followed by return +instruction at the end of @code{B} code. Optimizing compiler may replace the +call and return in function @code{B} into one jump to function @code{C} +instead. Such use of a jump instruction is called tail call. + +During execution of function @code{C} there will remain no indication it has +been tail called from function @code{B}. If function @code{A} regularly calls +function @code{B} which tail calls function @code{C} then @value{GDBN} sees as +the caller of function @code{C} the function @code{A}. @value{GDBN} can in +some cases search all the possible code paths and if it determintes there +exists an unambiguous code path it will create call frames for it (in this case +a frame with @code{$pc} in function @code{B}). The virtual return address into +such tail call frame will be pointing pointing right after the jump instruction +(as if it would be a call instructions). + +This functionality is currently supported only by DWARF 2 debugging format and +the compiler has to produce @samp{DW_TAG_GNU_call_site} tags. For example for +@value{NGCC} additionally optimization and debugging compilation options must +be enabled (@option{-O -g}). Still even with the required debug info there +exist many reasons why the code path analysis by @value{GDBN} may fail in +specific cases. + +@kbd{info frame} command (@pxref{Frame Info}) will indicate the tail call frame +kind by text @code{tail call frame}. + +The detection of all the possible code path executions can find them ambiguous. +There is no execution history stored (possible @ref{Reverse Execution} is never +used for this purpose) and the last known caller could have reached the known +callee by multiple different jump sequences. In such case @value{GDBN} still +tries to show at least all the unambiguous top tail callers and all the +unambiguous bottom tail calees, if any. + +@kbd{set verbose} command (@pxref{Messages/Warnings, ,Optional Warnings and +Messages}.) can show some reasons why the complete code path analysis did not +succeed in a specific case. + +@table @code +@item set debug tailcall +@kindex set debug tailcall +When set to on, enables tail calls analysis messages printing. It will show +all the possible valid tail calls code paths it has considered. It will also +print the intersection of them with the final unambiguous (possibly partial or +even empty) code path result. + +@item show debug tailcall +@kindex show debug tailcall +Show the current state of tail calls analysis messages. +@end table + @node Macros @chapter C Preprocessor Macros @@ -23078,6 +23235,13 @@ inferior function call. A frame representing an inlined function. The function was inlined into a @code{gdb.NORMAL_FRAME} that is older than this one. +@item gdb.TAILCALL_FRAME +A frame representing a tail call. Tail calls are used if the last statement of +an inferior function is a call of a (usually different) function. Such call +immediately followed by return instruction is in optimized code converted to +just one jump instruction. @value{GDBN} can in some cases guess such jump has +been executed and it creates a @code{gdb.TAILCALL_FRAME} for it. + @item gdb.SIGTRAMP_FRAME A signal trampoline frame. This is the frame created by the OS when it calls into a signal handler. --- a/gdb/dwarf2-frame-tailcall.c +++ b/gdb/dwarf2-frame-tailcall.c @@ -27,13 +27,15 @@ #include "hashtab.h" #include "exceptions.h" #include "gdbtypes.h" - +#include "regcache.h" +#include "value.h" /* Contains struct tailcall_cache indexed by next_bottom_frame. */ static htab_t cache_htab; -/* Associated structure of the unwinder for call_site_chain. */ - +/* Associate structure of the unwinder to call_site_chain. Lifetime of this + structure is maintained by REFC decremented by dealloc_cache, all of them + get deleted during reinit_frame_cache. */ struct tailcall_cache { /* It must be the first one of this struct. It is the furthest callee. */ @@ -51,8 +53,15 @@ struct tailcall_cache /* Unwound PC from the top (caller) frame, as it is not contained in CHAIN. */ - CORE_ADDR prev_pc; + + /* Compensate SP in caller frames appropriately. prev_sp and + entry_cfa_sp_offset are valid only if PREV_SP_P. PREV_SP is SP at the top + (caller) frame. ENTRY_CFA_SP_OFFSET is shift of SP in tail call frames + against next_bottom_frame SP. */ + unsigned prev_sp_p : 1; + CORE_ADDR prev_sp; + LONGEST entry_cfa_sp_offset; }; /* hash_f for htab_create_alloc of cache_htab. */ @@ -86,7 +95,7 @@ cache_new_ref1 (struct frame_info *next_bottom_frame) struct tailcall_cache *cache; void **slot; - cache = xmalloc (sizeof (*cache)); + cache = xzalloc (sizeof (*cache)); cache->next_bottom_frame = next_bottom_frame; cache->refc = 1; @@ -205,16 +214,14 @@ tailcall_frame_this_id (struct frame_info *this_frame, void **this_cache, { struct tailcall_cache *cache = *this_cache; struct frame_info *next_frame; - CORE_ADDR this_frame_base; /* Tail call does not make sense for a sentinel frame. */ next_frame = get_next_frame (this_frame); gdb_assert (next_frame != NULL); - /* SP does not change during tail calls. */ - this_frame_base = get_frame_base (next_frame); - - (*this_id) = frame_id_build (this_frame_base, get_frame_pc (this_frame)); + *this_id = get_frame_id (next_frame); + (*this_id).code_addr = get_frame_pc (this_frame); + (*this_id).code_addr_p = 1; (*this_id).inline_depth = (cache->chain_levels - existing_next_levels (this_frame, cache)); gdb_assert ((*this_id).inline_depth > 0); @@ -251,20 +258,53 @@ pretend_pc (struct frame_info *this_frame, struct tailcall_cache *cache) return cache->prev_pc; } -/* Implementation of frame_prev_register_ftype. Register set of virtual tail - call frames is assumed to be the one of the top (caller) frame. Only PC - value can be different for virtual tail call frames. */ +/* Implementation of frame_prev_register_ftype. If no specific register + override is supplied NULL is returned (this is incompatible with + frame_prev_register_ftype semantics). next_bottom_frame and tail call + frames unwind the NULL case differently. */ + +struct value * +dwarf2_tailcall_prev_register_first (struct frame_info *this_frame, + void **tailcall_cachep, int regnum) +{ + struct gdbarch *this_gdbarch = get_frame_arch (this_frame); + struct tailcall_cache *cache = *tailcall_cachep; + CORE_ADDR addr; + + if (regnum == gdbarch_pc_regnum (this_gdbarch)) + addr = pretend_pc (this_frame, cache); + else if (cache->prev_sp_p && regnum == gdbarch_sp_regnum (this_gdbarch)) + { + int next_levels = existing_next_levels (this_frame, cache); + + if (next_levels == cache->chain_levels - 1) + addr = cache->prev_sp; + else + addr = get_frame_base (this_frame) - cache->entry_cfa_sp_offset; + } + else + return NULL; + + return frame_unwind_got_address (this_frame, regnum, addr); +} + +/* Implementation of frame_prev_register_ftype for tail call frames. Register + set of virtual tail call frames is assumed to be the one of the top (caller) + frame - assume unchanged register value for NULL from + dwarf2_tailcall_prev_register_first. */ static struct value * tailcall_frame_prev_register (struct frame_info *this_frame, void **this_cache, int regnum) { - struct gdbarch *this_gdbarch = get_frame_arch (this_frame); struct tailcall_cache *cache = *this_cache; + struct value *val; - if (regnum == gdbarch_pc_regnum (this_gdbarch)) - return frame_unwind_got_constant (this_frame, regnum, - pretend_pc (this_frame, cache)); + gdb_assert (this_frame != cache->next_bottom_frame); + + val = dwarf2_tailcall_prev_register_first (this_frame, this_cache, regnum); + if (val) + return val; return frame_unwind_got_register (this_frame, regnum, regnum); } @@ -312,19 +352,25 @@ tailcall_frame_sniffer (const struct frame_unwind *self, /* The initial "sniffer" whether THIS_FRAME is a bottom (callee) frame of a new chain to create. Keep TAILCALL_CACHEP NULL if it did not find any chain, initialize it otherwise. No tail call chain is created if there are no - unambiguous virtual tail call frames to report. */ + unambiguous virtual tail call frames to report. + + ENTRY_CFA_SP_OFFSETP is NULL if no special SP handling is possible, + otherwise *ENTRY_CFA_SP_OFFSETP is the number of bytes to subtract from tail + call frames frame base to get the SP value there - to simulate return + address pushed on the stack. */ void dwarf2_tailcall_sniffer_first (struct frame_info *this_frame, - void **tailcall_cachep) + void **tailcall_cachep, + const LONGEST *entry_cfa_sp_offsetp) { - CORE_ADDR prev_pc = 0; /* GCC warning. */ + CORE_ADDR prev_pc = 0, prev_sp = 0; /* GCC warning. */ + int prev_sp_p = 0; CORE_ADDR this_pc, pc; struct gdbarch *prev_gdbarch; struct call_site_chain *chain = NULL; struct frame_info *fi; struct tailcall_cache *cache; - int pc_regnum; volatile struct gdb_exception except; gdb_assert (*tailcall_cachep == NULL); @@ -334,6 +380,8 @@ dwarf2_tailcall_sniffer_first (struct frame_info *this_frame, /* Catch any unwinding errors. */ TRY_CATCH (except, RETURN_MASK_ERROR) { + int pc_regnum, sp_regnum; + prev_gdbarch = frame_unwind_arch (this_frame); pc_regnum = gdbarch_pc_regnum (prev_gdbarch); if (pc_regnum == -1) @@ -346,6 +394,14 @@ dwarf2_tailcall_sniffer_first (struct frame_info *this_frame, /* call_site_find_chain can throw an exception. */ chain = call_site_find_chain (prev_gdbarch, prev_pc, this_pc); + + if (entry_cfa_sp_offsetp == NULL) + break; + sp_regnum = gdbarch_sp_regnum (prev_gdbarch); + if (sp_regnum == -1) + break; + prev_sp = frame_unwind_register_unsigned (this_frame, sp_regnum); + prev_sp_p = 1; } if (except.reason < 0) return; @@ -362,6 +418,12 @@ dwarf2_tailcall_sniffer_first (struct frame_info *this_frame, cache->chain = chain; cache->prev_pc = prev_pc; cache->chain_levels = pretended_chain_levels (chain); + cache->prev_sp_p = prev_sp_p; + if (cache->prev_sp_p) + { + cache->prev_sp = prev_sp; + cache->entry_cfa_sp_offset = *entry_cfa_sp_offsetp; + } gdb_assert (cache->chain_levels > 0); } --- a/gdb/dwarf2-frame-tailcall.h +++ b/gdb/dwarf2-frame-tailcall.h @@ -25,8 +25,14 @@ struct frame_unwind; /* The tail call frame unwinder. */ -extern void dwarf2_tailcall_sniffer_first (struct frame_info *this_frame, - void **tailcall_cachep); +extern void + dwarf2_tailcall_sniffer_first (struct frame_info *this_frame, + void **tailcall_cachep, + const LONGEST *entry_cfa_sp_offsetp); + +extern struct value * + dwarf2_tailcall_prev_register_first (struct frame_info *this_frame, + void **tailcall_cachep, int regnum); extern const struct frame_unwind dwarf2_tailcall_frame_unwind; --- a/gdb/dwarf2-frame.c +++ b/gdb/dwarf2-frame.c @@ -313,16 +313,6 @@ read_mem (void *baton, gdb_byte *buf, CORE_ADDR addr, size_t len) read_memory (addr, buf, len); } -/* Helper function for execute_stack_op. */ - -static void -no_push_dwarf_reg_entry_value (struct dwarf_expr_context *ctx, int dwarf_reg, - CORE_ADDR fb_offset, CORE_ADDR deref_size) -{ - internal_error (__FILE__, __LINE__, - _("Support for DW_OP_GNU_entry_value is unimplemented")); -} - /* Execute the required actions for both the DW_CFA_restore and DW_CFA_restore_extended instructions. */ static void @@ -365,7 +355,7 @@ static const struct dwarf_expr_context_funcs dwarf2_frame_ctx_funcs = ctx_no_get_tls_address, ctx_no_dwarf_call, ctx_no_get_base_type, - no_push_dwarf_reg_entry_value + ctx_no_push_dwarf_reg_entry_value }; static CORE_ADDR @@ -409,7 +399,11 @@ Not implemented: computing unwound register using explicit value operator")); } -static void +/* Execute FDE program from INSN_PTR possibly up to INSN_END or up to inferior + PC. Modify FS state accordingly. Return current INSN_PTR where the + execution has stopped, one can resume it on the next call. */ + +static const gdb_byte * execute_cfa_program (struct dwarf2_fde *fde, const gdb_byte *insn_ptr, const gdb_byte *insn_end, struct gdbarch *gdbarch, CORE_ADDR pc, struct dwarf2_frame_state *fs) @@ -692,9 +686,14 @@ bad CFI data; mismatched DW_CFA_restore_state at %s"), } } - /* Don't allow remember/restore between CIE and FDE programs. */ - dwarf2_frame_state_free_regs (fs->regs.prev); - fs->regs.prev = NULL; + if (fs->initial.reg == NULL) + { + /* Don't allow remember/restore between CIE and FDE programs. */ + dwarf2_frame_state_free_regs (fs->regs.prev); + fs->regs.prev = NULL; + } + + return insn_ptr; } @@ -1006,6 +1005,10 @@ dwarf2_frame_cache (struct frame_info *this_frame, void **this_cache) struct dwarf2_frame_state *fs; struct dwarf2_fde *fde; volatile struct gdb_exception ex; + CORE_ADDR entry_pc; + LONGEST entry_cfa_sp_offset; + int entry_cfa_sp_offset_p = 0; + const gdb_byte *instr; if (*this_cache) return *this_cache; @@ -1057,8 +1060,25 @@ dwarf2_frame_cache (struct frame_info *this_frame, void **this_cache) fs->initial = fs->regs; fs->initial.reg = dwarf2_frame_state_copy_regs (&fs->regs); + if (get_frame_func_if_available (this_frame, &entry_pc)) + { + /* Decode the insns in the FDE up to the entry PC. */ + instr = execute_cfa_program (fde, fde->instructions, fde->end, gdbarch, + entry_pc, fs); + + if (fs->regs.cfa_how == CFA_REG_OFFSET + && (gdbarch_dwarf2_reg_to_regnum (gdbarch, fs->regs.cfa_reg) + == gdbarch_sp_regnum (gdbarch))) + { + entry_cfa_sp_offset = fs->regs.cfa_offset; + entry_cfa_sp_offset_p = 1; + } + } + else + instr = fde->instructions; + /* Then decode the insns in the FDE up to our target PC. */ - execute_cfa_program (fde, fde->instructions, fde->end, gdbarch, + execute_cfa_program (fde, instr, fde->end, gdbarch, get_frame_pc (this_frame), fs); TRY_CATCH (ex, RETURN_MASK_ERROR) @@ -1201,7 +1221,9 @@ incomplete CFI data; unspecified registers (e.g., %s) at %s"), /* Try to find a virtual tail call frames chain with bottom (callee) frame starting at THIS_FRAME. */ - dwarf2_tailcall_sniffer_first (this_frame, &cache->tailcall_cache); + dwarf2_tailcall_sniffer_first (this_frame, &cache->tailcall_cache, + (entry_cfa_sp_offset_p + ? &entry_cfa_sp_offset : NULL)); return cache; } @@ -1248,14 +1270,21 @@ dwarf2_frame_prev_register (struct frame_info *this_frame, void **this_cache, CORE_ADDR addr; int realnum; - /* Virtual tail call frames report different values only for PC. Non-bottom - frames of a virtual tail call frames chain use + /* Non-bottom frames of a virtual tail call frames chain use dwarf2_tailcall_frame_unwind unwinder so this code does not apply for - them. */ - if (cache->tailcall_cache && regnum == gdbarch_pc_regnum (gdbarch)) - return dwarf2_tailcall_frame_unwind.prev_register (this_frame, - &cache->tailcall_cache, - regnum); + them. If dwarf2_tailcall_prev_register_first does not have specific value + unwind the register, tail call frames are assumed to have the register set + of the top caller. */ + if (cache->tailcall_cache) + { + struct value *val; + + val = dwarf2_tailcall_prev_register_first (this_frame, + &cache->tailcall_cache, + regnum); + if (val) + return val; + } switch (cache->reg[regnum].how) { --- a/gdb/dwarf2expr.c +++ b/gdb/dwarf2expr.c @@ -371,7 +371,7 @@ dwarf_expr_eval (struct dwarf_expr_context *ctx, const gdb_byte *addr, /* Decode the unsigned LEB128 constant at BUF into the variable pointed to by R, and return the new value of BUF. Verify that it doesn't extend - past BUF_END. */ + past BUF_END. R can be NULL, the constant is then only skipped. */ const gdb_byte * read_uleb128 (const gdb_byte *buf, const gdb_byte *buf_end, ULONGEST * r) @@ -391,13 +391,14 @@ read_uleb128 (const gdb_byte *buf, const gdb_byte *buf_end, ULONGEST * r) break; shift += 7; } - *r = result; + if (r) + *r = result; return buf; } /* Decode the signed LEB128 constant at BUF into the variable pointed to by R, and return the new value of BUF. Verify that it doesn't extend - past BUF_END. */ + past BUF_END. R can be NULL, the constant is then only skipped. */ const gdb_byte * read_sleb128 (const gdb_byte *buf, const gdb_byte *buf_end, LONGEST * r) @@ -420,7 +421,8 @@ read_sleb128 (const gdb_byte *buf, const gdb_byte *buf_end, LONGEST * r) if (shift < (sizeof (*r) * 8) && (byte & 0x40) != 0) result |= -(((LONGEST) 1) << shift); - *r = result; + if (r) + *r = result; return buf; } @@ -498,10 +500,19 @@ dwarf_block_to_dwarf_reg (const gdb_byte *buf, const gdb_byte *buf_end) return *buf - DW_OP_reg0; } - if (*buf != DW_OP_regx) + if (*buf == DW_OP_GNU_regval_type) + { + buf++; + buf = read_uleb128 (buf, buf_end, &dwarf_reg); + buf = read_uleb128 (buf, buf_end, NULL); + } + else if (*buf == DW_OP_regx) + { + buf++; + buf = read_uleb128 (buf, buf_end, &dwarf_reg); + } + else return -1; - buf++; - buf = read_uleb128 (buf, buf_end, &dwarf_reg); if (buf != buf_end || (int) dwarf_reg != dwarf_reg) return -1; return dwarf_reg; @@ -531,7 +542,7 @@ dwarf_block_to_dwarf_reg_deref (const gdb_byte *buf, const gdb_byte *buf_end, buf++; buf = read_uleb128 (buf, buf_end, &dwarf_reg); if ((int) dwarf_reg != dwarf_reg) - return -1; + return -1; } else return -1; @@ -552,7 +563,7 @@ dwarf_block_to_dwarf_reg_deref (const gdb_byte *buf, const gdb_byte *buf_end, { buf++; if (buf >= buf_end) - return -1; + return -1; *deref_size_return = *buf++; } else @@ -564,6 +575,30 @@ dwarf_block_to_dwarf_reg_deref (const gdb_byte *buf, const gdb_byte *buf_end, return dwarf_reg; } +/* If funcs->push_dwarf_reg_entry_value (ctx, dwarf_reg, - 0 /* unused */, -1); + 0 /* unused */, + -1 /* deref_size */); goto no_push; } @@ -1511,6 +1523,18 @@ ctx_no_get_base_type (struct dwarf_expr_context *ctx, size_t die) error (_("Support for typed DWARF is not supported in this context")); } +/* Stub dwarf_expr_context_funcs.push_dwarf_block_entry_value + implementation. */ + +void +ctx_no_push_dwarf_reg_entry_value (struct dwarf_expr_context *ctx, + int dwarf_reg, CORE_ADDR fb_offset, + int deref_size) +{ + internal_error (__FILE__, __LINE__, + _("Support for DW_OP_GNU_entry_value is unimplemented")); +} + void _initialize_dwarf2expr (void) { --- a/gdb/dwarf2expr.h +++ b/gdb/dwarf2expr.h @@ -71,7 +71,7 @@ struct dwarf_expr_context_funcs DW_AT_GNU_call_site_data_value instead of DW_AT_GNU_call_site_value. */ void (*push_dwarf_reg_entry_value) (struct dwarf_expr_context *ctx, int dwarf_reg, CORE_ADDR fb_offset, - CORE_ADDR deref_size); + int deref_size); #if 0 /* Not yet implemented. */ @@ -274,18 +274,21 @@ CORE_ADDR ctx_no_get_frame_pc (void *baton); CORE_ADDR ctx_no_get_tls_address (void *baton, CORE_ADDR offset); void ctx_no_dwarf_call (struct dwarf_expr_context *ctx, size_t die_offset); struct type *ctx_no_get_base_type (struct dwarf_expr_context *ctx, size_t die); +void ctx_no_push_dwarf_reg_entry_value (struct dwarf_expr_context *ctx, + int dwarf_reg, CORE_ADDR fb_offset, + int deref_size); int dwarf_block_to_dwarf_reg (const gdb_byte *buf, const gdb_byte *buf_end); -int dwarf_block_to_sp_offset (struct gdbarch *gdbarch, const gdb_byte *buf, - const gdb_byte *buf_end, - CORE_ADDR *sp_offset_return); +int dwarf_block_to_dwarf_reg_deref (const gdb_byte *buf, + const gdb_byte *buf_end, + CORE_ADDR *deref_size_return); int dwarf_block_to_fb_offset (const gdb_byte *buf, const gdb_byte *buf_end, CORE_ADDR *fb_offset_return); -int dwarf_block_to_dwarf_reg_deref (const gdb_byte *buf, - const gdb_byte *buf_end, - CORE_ADDR *deref_size_return); +int dwarf_block_to_sp_offset (struct gdbarch *gdbarch, const gdb_byte *buf, + const gdb_byte *buf_end, + CORE_ADDR *sp_offset_return); #endif /* dwarf2expr.h */ --- a/gdb/dwarf2loc.c +++ b/gdb/dwarf2loc.c @@ -332,7 +332,7 @@ show_tailcall_debug (struct ui_file *file, int from_tty, /* Find DW_TAG_GNU_call_site's DW_AT_GNU_call_site_target address. CALLER_FRAME (for registers) can be NULL if it is not known. This function - always returns valid address or it throws NOT_FOUND_ERROR. */ + always returns valid address or it throws NO_ENTRY_VALUE_ERROR. */ static CORE_ADDR call_site_to_target_addr (struct call_site *call_site, @@ -349,10 +349,10 @@ call_site_to_target_addr (struct call_site *call_site, dwarf_block = FIELD_DWARF_BLOCK (call_site->target); if (dwarf_block == NULL) - throw_error (NOT_FOUND_ERROR, + throw_error (NO_ENTRY_VALUE_ERROR, _("DW_AT_GNU_call_site_target is not specified")); if (caller_frame == NULL) - throw_error (NOT_FOUND_ERROR, + throw_error (NO_ENTRY_VALUE_ERROR, _("DW_AT_GNU_call_site_target DWARF block resolving " "requires known frame which is currently not " "available")); @@ -377,7 +377,7 @@ call_site_to_target_addr (struct call_site *call_site, physname = FIELD_STATIC_PHYSNAME (call_site->target); msym = lookup_minimal_symbol_text (physname, NULL); if (msym == NULL) - throw_error (NOT_FOUND_ERROR, + throw_error (NO_ENTRY_VALUE_ERROR, _("Cannot find function \"%s\" for a call site target"), physname); return SYMBOL_VALUE_ADDRESS (msym); @@ -392,8 +392,8 @@ call_site_to_target_addr (struct call_site *call_site, } /* Convert function entry point exact address ADDR to the function which is - compliant with TAIL_CALL_LIST_COMPLETE condition. Throw NOT_FOUND_ERROR - otherwise. */ + compliant with TAIL_CALL_LIST_COMPLETE condition. Throw + NO_ENTRY_VALUE_ERROR otherwise. */ static struct symbol * func_addr_to_tail_call_list (struct gdbarch *gdbarch, CORE_ADDR addr) @@ -402,7 +402,7 @@ func_addr_to_tail_call_list (struct gdbarch *gdbarch, CORE_ADDR addr) struct type *type; if (sym == NULL || BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) != addr) - throw_error (NOT_FOUND_ERROR, + throw_error (NO_ENTRY_VALUE_ERROR, _("DW_TAG_GNU_call_site resolving failed to find function " "name for address %s"), paddress (gdbarch, addr)); @@ -417,23 +417,9 @@ func_addr_to_tail_call_list (struct gdbarch *gdbarch, CORE_ADDR addr) /* Define VEC (CORE_ADDR) functions. */ DEF_VEC_I (CORE_ADDR); -/* Cleanup helper to free VEC (CORE_ADDR) **. */ - -static void -free_addr_vecp (void *arg) -{ - VEC (CORE_ADDR) **vecp = arg; - - if (*vecp) - { - VEC_free (CORE_ADDR, *vecp); - *vecp = NULL; - } -} - /* Verify function with entry point exact address ADDR can never call itself - via its tail calls (incl. transitively). Throw NOT_FOUND_ERROR if it can - call itself via tail calls. + via its tail calls (incl. transitively). Throw NO_ENTRY_VALUE_ERROR if it + can call itself via tail calls. If a funtion can tail call itself its entry value based parameters are unreliable. There is no verification whether the value of some/all @@ -461,7 +447,7 @@ func_verify_no_selftailcall (struct gdbarch *gdbarch, CORE_ADDR verify_addr) NULL); make_cleanup_htab_delete (addr_hash); - make_cleanup (free_addr_vecp, &todo); + make_cleanup (VEC_cleanup (CORE_ADDR), &todo); VEC_safe_push (CORE_ADDR, todo, verify_addr); while (!VEC_empty (CORE_ADDR, todo)) @@ -488,9 +474,10 @@ func_verify_no_selftailcall (struct gdbarch *gdbarch, CORE_ADDR verify_addr) struct minimal_symbol *msym; msym = lookup_minimal_symbol_by_pc (verify_addr); - throw_error (NOT_FOUND_ERROR, _("DW_OP_GNU_entry_value resolving " - "has found function \"%s\" at %s " - "can call itself via tail calls"), + throw_error (NO_ENTRY_VALUE_ERROR, + _("DW_OP_GNU_entry_value resolving has found " + "function \"%s\" at %s can call itself via tail " + "calls"), msym == NULL ? "???" : SYMBOL_PRINT_NAME (msym), paddress (gdbarch, verify_addr)); } @@ -625,26 +612,12 @@ chain_candidate (struct gdbarch *gdbarch, struct call_site_chain **resultp, gdb_assert (result->callers + result->callees < result->length); } -/* Cleanup helper to free VEC (call_sitep) **. */ - -static void -free_call_sitep_vecp (void *arg) -{ - VEC (call_sitep) **vecp = arg; - - if (*vecp) - { - VEC_free (call_sitep, *vecp); - *vecp = NULL; - } -} - /* Create and return call_site_chain for CALLER_PC and CALLEE_PC. All the assumed frames between them use GDBARCH. Use depth first search so we can keep single CHAIN of call_site's back to CALLER_PC. Function recursion would have needless GDB stack overhead. Caller is responsible for xfree of the returned result. Any unreliability results in thrown - NOT_FOUND_ERROR. */ + NO_ENTRY_VALUE_ERROR. */ static struct call_site_chain * call_site_find_chain_1 (struct gdbarch *gdbarch, CORE_ADDR caller_pc, @@ -668,7 +641,7 @@ call_site_find_chain_1 (struct gdbarch *gdbarch, CORE_ADDR caller_pc, /* We are not interested in the specific PC inside the callee function. */ callee_pc = get_pc_function_start (callee_pc); if (callee_pc == 0) - throw_error (NOT_FOUND_ERROR, _("Unable to find function for PC %s"), + throw_error (NO_ENTRY_VALUE_ERROR, _("Unable to find function for PC %s"), paddress (gdbarch, callee_pc)); back_to_retval = make_cleanup (free_current_contents, &retval); @@ -680,7 +653,7 @@ call_site_find_chain_1 (struct gdbarch *gdbarch, CORE_ADDR caller_pc, NULL); make_cleanup_htab_delete (addr_hash); - make_cleanup (free_call_sitep_vecp, &chain); + make_cleanup (VEC_cleanup (call_sitep), &chain); /* Do not push CALL_SITE to CHAIN. Push there only the first tail call site at the target's function. All the possible tail call sites in the target's @@ -766,7 +739,7 @@ call_site_find_chain_1 (struct gdbarch *gdbarch, CORE_ADDR caller_pc, msym_caller = lookup_minimal_symbol_by_pc (caller_pc); msym_callee = lookup_minimal_symbol_by_pc (callee_pc); - throw_error (NOT_FOUND_ERROR, + throw_error (NO_ENTRY_VALUE_ERROR, _("There are no unambiguously determinable intermediate " "callers or callees between caller function \"%s\" at %s " "and callee function \"%s\" at %s"), @@ -801,7 +774,7 @@ call_site_find_chain (struct gdbarch *gdbarch, CORE_ADDR caller_pc, } if (e.reason < 0) { - if (e.error == NOT_FOUND_ERROR) + if (e.error == NO_ENTRY_VALUE_ERROR) { if (info_verbose) exception_print (gdb_stdout, e); @@ -818,15 +791,17 @@ call_site_find_chain (struct gdbarch *gdbarch, CORE_ADDR caller_pc, callee. See DWARF_REG and FB_OFFSET description at struct dwarf_expr_context_funcs->push_dwarf_reg_entry_value. - Function always returns non-NULL, it throws NOT_FOUND_ERROR otherwise. */ + Function always returns non-NULL, it throws NO_ENTRY_VALUE_ERROR + otherwise. */ static struct call_site_parameter * -dwarf_expr_dwarf_reg_entry_value (struct frame_info *frame, int dwarf_reg, - CORE_ADDR fb_offset) +dwarf_expr_reg_to_entry_parameter (struct frame_info *frame, int dwarf_reg, + CORE_ADDR fb_offset, + struct dwarf2_per_cu_data **per_cu_return) { CORE_ADDR func_addr = get_frame_func (frame); CORE_ADDR caller_pc; - struct gdbarch *caller_gdbarch = frame_unwind_arch (frame); + struct gdbarch *gdbarch = get_frame_arch (frame); struct frame_info *caller_frame = get_prev_frame (frame); struct call_site *call_site; int iparams; @@ -835,13 +810,27 @@ dwarf_expr_dwarf_reg_entry_value (struct frame_info *frame, int dwarf_reg, struct call_site_parameter *parameter; CORE_ADDR target_addr; + if (gdbarch != frame_unwind_arch (frame)) + { + struct minimal_symbol *msym = lookup_minimal_symbol_by_pc (func_addr); + struct gdbarch *caller_gdbarch = frame_unwind_arch (frame); + + throw_error (NO_ENTRY_VALUE_ERROR, + _("DW_OP_GNU_entry_value resolving callee gdbarch %s " + "(of %s (%s)) does not match caller gdbarch %s"), + gdbarch_bfd_arch_info (gdbarch)->printable_name, + paddress (gdbarch, func_addr), + msym == NULL ? "???" : SYMBOL_PRINT_NAME (msym), + gdbarch_bfd_arch_info (caller_gdbarch)->printable_name); + } + if (caller_frame == NULL) { struct minimal_symbol *msym = lookup_minimal_symbol_by_pc (func_addr); - throw_error (NOT_FOUND_ERROR, _("DW_OP_GNU_entry_value resolving " - "requires caller of %s (%s)"), - paddress (get_frame_arch (frame), func_addr), + throw_error (NO_ENTRY_VALUE_ERROR, _("DW_OP_GNU_entry_value resolving " + "requires caller of %s (%s)"), + paddress (gdbarch, func_addr), msym == NULL ? "???" : SYMBOL_PRINT_NAME (msym)); } caller_pc = get_frame_pc (caller_frame); @@ -852,11 +841,11 @@ dwarf_expr_dwarf_reg_entry_value (struct frame_info *frame, int dwarf_reg, struct minimal_symbol *msym = lookup_minimal_symbol_by_pc (caller_pc); /* DW_TAG_gnu_call_site will be missing just if GCC could not determine - the call target. So do not complain more than NOT_FOUND_ERROR. */ - throw_error (NOT_FOUND_ERROR, + the call target. */ + throw_error (NO_ENTRY_VALUE_ERROR, _("DW_OP_GNU_entry_value resolving cannot find " "DW_TAG_GNU_call_site %s in %s"), - paddress (caller_gdbarch, caller_pc), + paddress (gdbarch, caller_pc), msym == NULL ? "???" : SYMBOL_PRINT_NAME (msym)); } @@ -867,19 +856,19 @@ dwarf_expr_dwarf_reg_entry_value (struct frame_info *frame, int dwarf_reg, target_msym = lookup_minimal_symbol_by_pc (target_addr); func_msym = lookup_minimal_symbol_by_pc (func_addr); - throw_error (NOT_FOUND_ERROR, + throw_error (NO_ENTRY_VALUE_ERROR, _("DW_OP_GNU_entry_value resolving expects callee %s at %s " "but the called frame is for %s at %s"), (target_msym == NULL ? "???" : SYMBOL_PRINT_NAME (target_msym)), - paddress (caller_gdbarch, target_addr), + paddress (gdbarch, target_addr), func_msym == NULL ? "???" : SYMBOL_PRINT_NAME (func_msym), - paddress (caller_gdbarch, func_addr)); + paddress (gdbarch, func_addr)); } /* No entry value based parameters would be reliable if this function can call itself via tail calls. */ - func_verify_no_selftailcall (caller_gdbarch, func_addr); + func_verify_no_selftailcall (gdbarch, func_addr); for (iparams = 0; iparams < call_site->parameter_count; iparams++) { @@ -897,103 +886,94 @@ dwarf_expr_dwarf_reg_entry_value (struct frame_info *frame, int dwarf_reg, struct minimal_symbol *msym = lookup_minimal_symbol_by_pc (caller_pc); /* DW_TAG_GNU_call_site_parameter will be missing just if GCC could not - determine its value. So do not complain more than NOT_FOUND_ERROR. */ - throw_error (NOT_FOUND_ERROR, _("Cannot find DWARF reg%d/fbreg(%s) at " - "DW_TAG_GNU_call_site %s at %s"), - dwarf_reg, paddress (caller_gdbarch, fb_offset), - paddress (caller_gdbarch, caller_pc), + determine its value. */ + throw_error (NO_ENTRY_VALUE_ERROR, _("Cannot find matching parameter " + "at DW_TAG_GNU_call_site %s at %s"), + paddress (gdbarch, caller_pc), msym == NULL ? "???" : SYMBOL_PRINT_NAME (msym)); } + *per_cu_return = call_site->per_cu; return parameter; } -/* Return dwarf2_locexpr_baton for PARAMETER matching DEREF_SIZE. If - DEREF_SIZE is -1, return the normal DW_AT_GNU_call_site_value block. - Otherwise return the DW_AT_GNU_call_site_data_value (dereferenced) block. - - Function always returns non-NULL, it throws NOT_FOUND_ERROR if DEREF_SIZE - was not -1 and the DW_AT_GNU_call_site_data_value block is not defined by - PARAMETER. */ - -static struct dwarf2_locexpr_baton * -dwarf_entry_parameter_to_block (struct call_site_parameter *parameter, - CORE_ADDR deref_size) -{ - - if (deref_size == -1) - { - gdb_assert (parameter->call_site_value != NULL); - return parameter->call_site_value; - } - - /* DEREF_SIZE size is not verified here. */ - - if (parameter->call_site_data_value == NULL) - throw_error (NOT_FOUND_ERROR, - _("Cannot resolve DW_AT_GNU_call_site_data_value")); - - return parameter->call_site_data_value; -} - -/* Return value for PARAMETER matching DEREF_SIZE, see - dwarf_entry_parameter_to_block for the description of these parameters. +/* Return value for PARAMETER matching DEREF_SIZE. If DEREF_SIZE is -1, return + the normal DW_AT_GNU_call_site_value block. Otherwise return the + DW_AT_GNU_call_site_data_value (dereferenced) block. TYPE and CALLER_FRAME specify how to evaluate the DWARF block into returned struct value. Function always returns non-NULL, non-optimized out value. It throws - NOT_FOUND_ERROR if it cannot resolve the value for any reason. */ + NO_ENTRY_VALUE_ERROR if it cannot resolve the value for any reason. */ static struct value * dwarf_entry_parameter_to_value (struct call_site_parameter *parameter, CORE_ADDR deref_size, struct type *type, - struct frame_info *caller_frame) + struct frame_info *caller_frame, + struct dwarf2_per_cu_data *per_cu) { - struct dwarf2_locexpr_baton *dwarf_block; + const gdb_byte *data_src; gdb_byte *data; + size_t size; - dwarf_block = dwarf_entry_parameter_to_block (parameter, deref_size); + data_src = deref_size == -1 ? parameter->value : parameter->data_value; + size = deref_size == -1 ? parameter->value_size : parameter->data_value_size; + + /* DEREF_SIZE size is not verified here. */ + if (data_src == NULL) + throw_error (NO_ENTRY_VALUE_ERROR, + _("Cannot resolve DW_AT_GNU_call_site_data_value")); /* DW_AT_GNU_call_site_value is a DWARF expression, not a DWARF - location. */ - data = alloca (dwarf_block->size + 1); - memcpy (data, dwarf_block->data, dwarf_block->size); - data[dwarf_block->size] = DW_OP_stack_value; + location. Postprocessing of DWARF_VALUE_MEMORY would lose the type from + DWARF block. */ + data = alloca (size + 1); + memcpy (data, data_src, size); + data[size] = DW_OP_stack_value; - return dwarf2_evaluate_loc_desc (type, caller_frame, data, - dwarf_block->size + 1, dwarf_block->per_cu); + return dwarf2_evaluate_loc_desc (type, caller_frame, data, size + 1, per_cu); } -/* Execute DWARF_BLOCK for caller of the CTX's frame. CTX must be of - dwarf_expr_ctx_funcs kind. See DWARF_REG, FB_OFFSET and DEREF_SIZE - description at struct dwarf_expr_context_funcs->push_dwarf_reg_entry_value. +/* Execute call_site_parameter's DWARF block matching DEREF_SIZE for caller of + the CTX's frame. CTX must be of dwarf_expr_ctx_funcs kind. See DWARF_REG + and FB_OFFSET description at struct + dwarf_expr_context_funcs->push_dwarf_reg_entry_value. - The CTX caller can be from a different CU - per_cu_dwarf_call is simpler as - it does not support cross-CU DWARF executions. */ + The CTX caller can be from a different CU - per_cu_dwarf_call implementation + can be more simple as it does not support cross-CU DWARF executions. */ static void dwarf_expr_push_dwarf_reg_entry_value (struct dwarf_expr_context *ctx, int dwarf_reg, CORE_ADDR fb_offset, - CORE_ADDR deref_size) + int deref_size) { struct dwarf_expr_baton *debaton; struct frame_info *frame, *caller_frame; - struct dwarf2_locexpr_baton *dwarf_block; + struct dwarf2_per_cu_data *caller_per_cu; struct dwarf_expr_baton baton_local; struct dwarf_expr_context saved_ctx; struct call_site_parameter *parameter; + const gdb_byte *data_src; + size_t size; gdb_assert (ctx->funcs == &dwarf_expr_ctx_funcs); debaton = ctx->baton; frame = debaton->frame; caller_frame = get_prev_frame (frame); - parameter = dwarf_expr_dwarf_reg_entry_value (frame, dwarf_reg, fb_offset); - dwarf_block = dwarf_entry_parameter_to_block (parameter, deref_size); + parameter = dwarf_expr_reg_to_entry_parameter (frame, dwarf_reg, fb_offset, + &caller_per_cu); + data_src = deref_size == -1 ? parameter->value : parameter->data_value; + size = deref_size == -1 ? parameter->value_size : parameter->data_value_size; + + /* DEREF_SIZE size is not verified here. */ + if (data_src == NULL) + throw_error (NO_ENTRY_VALUE_ERROR, + _("Cannot resolve DW_AT_GNU_call_site_data_value")); baton_local.frame = caller_frame; - baton_local.per_cu = dwarf_block->per_cu; + baton_local.per_cu = caller_per_cu; saved_ctx.gdbarch = ctx->gdbarch; saved_ctx.addr_size = ctx->addr_size; @@ -1004,7 +984,7 @@ dwarf_expr_push_dwarf_reg_entry_value (struct dwarf_expr_context *ctx, ctx->offset = dwarf2_per_cu_text_offset (baton_local.per_cu); ctx->baton = &baton_local; - dwarf_expr_eval (ctx, dwarf_block->data, dwarf_block->size); + dwarf_expr_eval (ctx, data_src, size); ctx->gdbarch = saved_ctx.gdbarch; ctx->addr_size = saved_ctx.addr_size; @@ -1012,6 +992,151 @@ dwarf_expr_push_dwarf_reg_entry_value (struct dwarf_expr_context *ctx, ctx->baton = saved_ctx.baton; } +/* VALUE must be of type lval_computed with entry_data_value_funcs. Perform + the indirect method on it, that is use its stored target value, the sole + purpose of entry_data_value_funcs.. */ + +static struct value * +entry_data_value_coerce_ref (const struct value *value) +{ + struct type *checked_type = check_typedef (value_type (value)); + struct value *target_val; + + if (TYPE_CODE (checked_type) != TYPE_CODE_REF) + return NULL; + + target_val = value_computed_closure (value); + value_incref (target_val); + return target_val; +} + +/* Implement copy_closure. */ + +static void * +entry_data_value_copy_closure (const struct value *v) +{ + struct value *target_val = value_computed_closure (v); + + value_incref (target_val); + return target_val; +} + +/* Implement free_closure. */ + +static void +entry_data_value_free_closure (struct value *v) +{ + struct value *target_val = value_computed_closure (v); + + value_free (target_val); +} + +/* Vector for methods for an entry value reference where the referenced value + is stored in the caller. On the first dereference use + DW_AT_GNU_call_site_data_value in the caller. */ + +static const struct lval_funcs entry_data_value_funcs = +{ + NULL, /* read */ + NULL, /* write */ + NULL, /* check_validity */ + NULL, /* check_any_valid */ + NULL, /* indirect */ + entry_data_value_coerce_ref, + NULL, /* check_synthetic_pointer */ + entry_data_value_copy_closure, + entry_data_value_free_closure +}; + +/* Read parameter of TYPE at (callee) FRAME's function entry. DWARF_REG and + FB_OFFSET are used to match DW_AT_location at the caller's + DW_TAG_GNU_call_site_parameter. See DWARF_REG and FB_OFFSET description at + struct dwarf_expr_context_funcs->push_dwarf_reg_entry_value. + + Function always returns non-NULL value. It throws NO_ENTRY_VALUE_ERROR if it + cannot resolve the parameter for any reason. */ + +static struct value * +value_of_dwarf_reg_entry (struct type *type, struct frame_info *frame, + int dwarf_reg, CORE_ADDR fb_offset) +{ + struct type *checked_type = check_typedef (type); + struct type *target_type = TYPE_TARGET_TYPE (checked_type); + struct frame_info *caller_frame = get_prev_frame (frame); + struct value *outer_val, *target_val, *val; + struct call_site_parameter *parameter; + struct dwarf2_per_cu_data *caller_per_cu; + CORE_ADDR addr; + + parameter = dwarf_expr_reg_to_entry_parameter (frame, dwarf_reg, fb_offset, + &caller_per_cu); + + outer_val = dwarf_entry_parameter_to_value (parameter, -1 /* deref_size */, + type, caller_frame, + caller_per_cu); + + /* Check if DW_AT_GNU_call_site_data_value cannot be used. If it should be + used and it is not available do not fall back to OUTER_VAL - dereferencing + TYPE_CODE_REF with non-entry data value would give current value - not the + entry value. */ + + if (TYPE_CODE (checked_type) != TYPE_CODE_REF + || TYPE_TARGET_TYPE (checked_type) == NULL) + return outer_val; + + target_val = dwarf_entry_parameter_to_value (parameter, + TYPE_LENGTH (target_type), + target_type, caller_frame, + caller_per_cu); + + /* value_as_address dereferences TYPE_CODE_REF. */ + addr = extract_typed_address (value_contents (outer_val), checked_type); + + /* The target entry value has artificial address of the entry value + reference. */ + VALUE_LVAL (target_val) = lval_memory; + set_value_address (target_val, addr); + + release_value (target_val); + val = allocate_computed_value (type, &entry_data_value_funcs, + target_val /* closure */); + + /* Copy the referencing pointer to the new computed value. */ + memcpy (value_contents_raw (val), value_contents_raw (outer_val), TYPE_LENGTH (checked_type)); + set_value_lazy (val, 0); + + return val; +} + +/* Read parameter of TYPE at (callee) FRAME's function entry. DATA and + SIZE are DWARF block used to match DW_AT_location at the caller's + DW_TAG_GNU_call_site_parameter. + + Function always returns non-NULL value. It throws NO_ENTRY_VALUE_ERROR if it + cannot resolve the parameter for any reason. */ + +static struct value * +value_of_dwarf_block_entry (struct type *type, struct frame_info *frame, + const gdb_byte *block, size_t block_len) +{ + int dwarf_reg; + CORE_ADDR fb_offset; + + dwarf_reg = dwarf_block_to_dwarf_reg (block, block + block_len); + if (dwarf_reg != -1) + return value_of_dwarf_reg_entry (type, frame, dwarf_reg, 0 /* unused */); + + if (dwarf_block_to_fb_offset (block, block + block_len, &fb_offset)) + return value_of_dwarf_reg_entry (type, frame, -1, fb_offset); + + /* This can normally happen - throw NO_ENTRY_VALUE_ERROR to get the message + suppressed during normal operation. The expression can be arbitrary if + there is no caller-callee entry value binding expected. */ + throw_error (NO_ENTRY_VALUE_ERROR, + _("DWARF-2 expression error: DW_OP_GNU_entry_value is supported " + "only for single DW_OP_reg* or for DW_OP_fbreg(*)")); +} + struct piece_closure { /* Reference count. */ @@ -1780,6 +1905,7 @@ static const struct lval_funcs pieced_value_funcs = { check_pieced_value_validity, check_pieced_value_invalid, indirect_pieced_value, + NULL, /* coerce_ref */ check_pieced_synthetic_pointer, copy_pieced_value_closure, free_pieced_value_closure @@ -1853,19 +1979,18 @@ dwarf2_evaluate_loc_desc_full (struct type *type, struct frame_info *frame, } if (ex.reason < 0) { - do_cleanups (old_chain); - if (ex.error == NOT_AVAILABLE_ERROR) { + do_cleanups (old_chain); retval = allocate_value (type); mark_value_bytes_unavailable (retval, 0, TYPE_LENGTH (type)); return retval; } - else if (ex.error == NOT_FOUND_ERROR) + else if (ex.error == NO_ENTRY_VALUE_ERROR) { if (info_verbose) exception_print (gdb_stdout, ex); - + do_cleanups (old_chain); return allocate_optimized_out_value (type); } else @@ -2098,8 +2223,8 @@ needs_frame_dwarf_call (struct dwarf_expr_context *ctx, size_t die_offset) /* DW_OP_GNU_entry_value accesses require a caller, therefore a frame. */ static void -needs_dwarf_reg_entry_value (struct dwarf_expr_context *ctx, int dwarf_reg, - CORE_ADDR fb_offset, CORE_ADDR deref_size) +needs_dwarf_reg_entry_value (struct dwarf_expr_context *ctx, + int dwarf_reg, CORE_ADDR fb_offset, int deref_size) { struct needs_frame_baton *nf_baton = ctx->baton; @@ -2934,6 +3059,19 @@ locexpr_read_variable (struct symbol *symbol, struct frame_info *frame) return val; } +/* Return the value of SYMBOL in FRAME at (callee) FRAME's function + entry. SYMBOL should be a function parameter, otherwise NO_ENTRY_VALUE_ERROR + will be thrown. */ + +static struct value * +locexpr_read_variable_at_entry (struct symbol *symbol, struct frame_info *frame) +{ + struct dwarf2_locexpr_baton *dlbaton = SYMBOL_LOCATION_BATON (symbol); + + return value_of_dwarf_block_entry (SYMBOL_TYPE (symbol), frame, dlbaton->data, + dlbaton->size); +} + /* Return non-zero iff we need a frame to evaluate SYMBOL. */ static int locexpr_read_needs_frame (struct symbol *symbol) @@ -3585,7 +3723,7 @@ locexpr_tracepoint_var_ref (struct symbol *symbol, struct gdbarch *gdbarch, evaluator. */ const struct symbol_computed_ops dwarf2_locexpr_funcs = { locexpr_read_variable, - NULL, /* read_variable_at_entry */ + locexpr_read_variable_at_entry, locexpr_read_needs_frame, locexpr_describe_location, locexpr_tracepoint_var_ref @@ -3616,156 +3754,21 @@ loclist_read_variable (struct symbol *symbol, struct frame_info *frame) return val; } -/* VALUE must be of type lval_computed with entry_data_value_funcs. Perform - the indirect method on it, that is use its stored target value, the sole - purpose of entry_data_value_funcs.. */ - -struct value * -entry_data_value_indirect (struct value *value) -{ - struct type *checked_type = check_typedef (value_type (value)); - struct value *target_val = value_computed_closure (value); - - gdb_assert (TYPE_CODE (checked_type) == TYPE_CODE_PTR - || TYPE_CODE (checked_type) == TYPE_CODE_REF); - - value_incref (target_val); - return target_val; -} - -/* Implement copy_closure. */ - -static void * -entry_data_value_copy_closure (const struct value *v) -{ - struct value *target_val = value_computed_closure (v); - - value_incref (target_val); - return target_val; -} - -/* Implement free_closure. */ - -static void -entry_data_value_free_closure (struct value *v) -{ - struct value *target_val = value_computed_closure (v); - - value_free (target_val); -} - -/* Vector for methods for an entry value reference where the referenced value - is stored in the caller. On the first dereference use - DW_AT_GNU_call_site_data_value in the caller. */ - -static struct lval_funcs entry_data_value_funcs = -{ - NULL, /* read */ - NULL, /* write */ - NULL, /* check_validity */ - NULL, /* check_any_valid */ - entry_data_value_indirect, - NULL, /* check_synthetic_pointer */ - entry_data_value_copy_closure, - entry_data_value_free_closure -}; - -/* Return value of parameter of TYPE at (callee) FRAME which at function entry - point. Parameter has been passed in DWARF_REG or FB_OFFSET, see their - description at struct dwarf_expr_context_funcs->push_dwarf_reg_entry_value. - - Function always returns non-NULL, non-optimized out value. It throws - NOT_FOUND_ERROR if it cannot resolve the value for any reason. */ - -static struct value * -value_of_dwarf_reg_entry (struct type *type, struct frame_info *frame, - int dwarf_reg, CORE_ADDR fb_offset) -{ - struct type *checked_type = check_typedef (type); - struct frame_info *caller_frame = get_prev_frame (frame); - struct value *outer_val; - struct call_site_parameter *parameter; - - parameter = dwarf_expr_dwarf_reg_entry_value (frame, dwarf_reg, fb_offset); - - outer_val = dwarf_entry_parameter_to_value (parameter, -1 /* deref_size */, - type, caller_frame); - - /* Check if DW_AT_GNU_call_site_data_value cannot be used. */ - - if ((TYPE_CODE (checked_type) == TYPE_CODE_PTR - || TYPE_CODE (checked_type) == TYPE_CODE_REF) - && TYPE_TARGET_TYPE (checked_type) != NULL) - { - struct type *target_type = TYPE_TARGET_TYPE (checked_type); - volatile struct gdb_exception e; - struct value *target_val; - - TRY_CATCH (e, RETURN_MASK_ERROR) - { - int target_length = TYPE_LENGTH (target_type); - - target_val = dwarf_entry_parameter_to_value (parameter, target_length, - target_type, - caller_frame); - } - if (e.reason < 0) - { - if (e.error == NOT_FOUND_ERROR) - { - if (info_verbose) - exception_print (gdb_stdout, e); - } - else - throw_exception (e); - } - else - { - CORE_ADDR addr; - struct value *val; - - /* value_as_address dereferences TYPE_CODE_REF. */ - addr = extract_typed_address (value_contents (outer_val), - checked_type); - - /* The target entry value has artificial address of the entry value - reference. */ - VALUE_LVAL (target_val) = lval_memory; - set_value_address (target_val, addr); - - release_value (target_val); - val = allocate_computed_value (type, &entry_data_value_funcs, - target_val /* closure */); - - /* Copy the referencing pointer to the new computed value. */ - memcpy (value_contents_raw (val), value_contents_raw (outer_val), - TYPE_LENGTH (checked_type)); - set_value_lazy (val, 0); - - return val; - } - } - - return outer_val; -} - /* Read variable SYMBOL like loclist_read_variable at (callee) FRAME's function - entry. SYMBOL should be a function parameter, otherwise NOT_FOUND_ERROR + entry. SYMBOL should be a function parameter, otherwise NO_ENTRY_VALUE_ERROR will be thrown. Function always returns non-NULL value, it may be marked optimized out if - inferior frame information is not available. It throws NOT_FOUND_ERROR if - it cannot resolve the parameter for any reason. */ + inferior frame information is not available. It throws NO_ENTRY_VALUE_ERROR + if it cannot resolve the parameter for any reason. */ static struct value * loclist_read_variable_at_entry (struct symbol *symbol, struct frame_info *frame) { struct dwarf2_loclist_baton *dlbaton = SYMBOL_LOCATION_BATON (symbol); - struct value *val; const gdb_byte *data; size_t size; - int dwarf_reg; - CORE_ADDR deref_size, pc, fb_offset; + CORE_ADDR pc; if (frame == NULL || !get_frame_func_if_available (frame, &pc)) return allocate_optimized_out_value (SYMBOL_TYPE (symbol)); @@ -3774,17 +3777,7 @@ loclist_read_variable_at_entry (struct symbol *symbol, struct frame_info *frame) if (data == NULL) return allocate_optimized_out_value (SYMBOL_TYPE (symbol)); - dwarf_reg = dwarf_block_to_dwarf_reg (data, data + size); - if (dwarf_reg != -1) - return value_of_dwarf_reg_entry (SYMBOL_TYPE (symbol), frame, dwarf_reg, - 0 /* unused */); - - if (dwarf_block_to_fb_offset (data, data + size, &fb_offset)) - return value_of_dwarf_reg_entry (SYMBOL_TYPE (symbol), frame, -1, - fb_offset); - - error (_("DWARF-2 expression error: DW_OP_GNU_entry_value is supported only " - "for single DW_OP_reg* or for DW_OP_fbreg(*)")); + return value_of_dwarf_block_entry (SYMBOL_TYPE (symbol), frame, data, size); } /* Return non-zero iff we need a frame to evaluate SYMBOL. */ --- a/gdb/dwarf2read.c +++ b/gdb/dwarf2read.c @@ -6127,24 +6127,6 @@ read_lexical_block_scope (struct die_info *die, struct dwarf2_cu *cu) using_directives = new->using_directives; } -/* Allocate a copy of BLK on CU's objfile_obstack (not comp_unit_obstack), - including a copy of the BLK DWARF code. */ - -static struct dwarf2_locexpr_baton * -dlbaton_obstack_copy (const struct dwarf_block *blk, struct dwarf2_cu *cu) -{ - struct objfile *objfile = cu->objfile; - struct dwarf2_locexpr_baton *dlbaton; - - dlbaton = obstack_alloc (&objfile->objfile_obstack, sizeof (*dlbaton)); - dlbaton->data = obstack_copy (&objfile->objfile_obstack, blk->data, - blk->size); - dlbaton->size = blk->size; - dlbaton->per_cu = cu->per_cu; - - return dlbaton; -} - /* Read in DW_TAG_GNU_call_site and insert it to CU->call_site_htab. */ static void @@ -6267,8 +6249,16 @@ read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu) if (!attr || (attr_form_is_block (attr) && DW_BLOCK (attr)->size == 0)) /* Keep NULL DWARF_BLOCK. */; else if (attr_form_is_block (attr)) - SET_FIELD_DWARF_BLOCK (call_site->target, - dlbaton_obstack_copy (DW_BLOCK (attr), cu)); + { + struct dwarf2_locexpr_baton *dlbaton; + + dlbaton = obstack_alloc (&objfile->objfile_obstack, sizeof (*dlbaton)); + dlbaton->data = DW_BLOCK (attr)->data; + dlbaton->size = DW_BLOCK (attr)->size; + dlbaton->per_cu = cu->per_cu; + + SET_FIELD_DWARF_BLOCK (call_site->target, dlbaton); + } else if (is_ref_attr (attr)) { struct objfile *objfile = cu->objfile; @@ -6310,6 +6300,8 @@ read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu) "block nor reference, for DIE 0x%x [in module %s]"), die->offset, cu->objfile->name); + call_site->per_cu = cu->per_cu; + for (child_die = die->child; child_die && child_die->tag; child_die = sibling_die (child_die)) @@ -6346,9 +6338,9 @@ read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu) ¶meter->fb_offset)) { complaint (&symfile_complaints, - _("Only single DW_OP_reg is supported for DW_FORM_block* " - "DW_AT_location for DW_TAG_GNU_call_site " - "child DIE 0x%x [in module %s]"), + _("Only single DW_OP_reg or DW_OP_fbreg is supported " + "for DW_FORM_block* DW_AT_location for " + "DW_TAG_GNU_call_site child DIE 0x%x [in module %s]"), child_die->offset, cu->objfile->name); continue; } @@ -6362,10 +6354,12 @@ read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu) child_die->offset, cu->objfile->name); continue; } - parameter->call_site_value = dlbaton_obstack_copy (DW_BLOCK (attr), cu); + parameter->value = DW_BLOCK (attr)->data; + parameter->value_size = DW_BLOCK (attr)->size; /* Parameters are not pre-cleared by memset above. */ - parameter->call_site_data_value = NULL; + parameter->data_value = NULL; + parameter->data_value_size = 0; call_site->parameter_count++; attr = dwarf2_attr (child_die, DW_AT_GNU_call_site_data_value, cu); @@ -6377,8 +6371,10 @@ read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu) "DW_TAG_GNU_call_site child DIE 0x%x [in module %s]"), child_die->offset, cu->objfile->name); else - parameter->call_site_data_value - = dlbaton_obstack_copy (DW_BLOCK (attr), cu); + { + parameter->data_value = DW_BLOCK (attr)->data; + parameter->data_value_size = DW_BLOCK (attr)->size; + } } } } @@ -16599,14 +16595,6 @@ write_one_signatured_type (void **slot, void *d) return 1; } -/* A cleanup function for an htab_t. */ - -static void -cleanup_htab (void *arg) -{ - htab_delete (arg); -} - /* Create an index file for OBJFILE in the directory DIR. */ static void @@ -16663,7 +16651,7 @@ write_psymtabs_to_index (struct objfile *objfile, const char *dir) psyms_seen = htab_create_alloc (100, htab_hash_pointer, htab_eq_pointer, NULL, xcalloc, xfree); - make_cleanup (cleanup_htab, psyms_seen); + make_cleanup_htab_delete (psyms_seen); /* While we're scanning CU's create a table that maps a psymtab pointer (which is what addrmap records) to its index (which is what is recorded @@ -16673,7 +16661,7 @@ write_psymtabs_to_index (struct objfile *objfile, const char *dir) hash_psymtab_cu_index, eq_psymtab_cu_index, NULL, xcalloc, xfree); - make_cleanup (cleanup_htab, cu_index_htab); + make_cleanup_htab_delete (cu_index_htab); psymtab_cu_index_map = (struct psymtab_cu_index_map *) xmalloc (sizeof (struct psymtab_cu_index_map) * dwarf2_per_objfile->n_comp_units); --- a/gdb/exceptions.h +++ b/gdb/exceptions.h @@ -85,6 +85,9 @@ enum errors { traceframe. */ NOT_AVAILABLE_ERROR, + /* DW_OP_GNU_entry_value resolving failed. */ + NO_ENTRY_VALUE_ERROR, + /* Add more errors here. */ NR_ERRORS }; --- a/gdb/f-lang.h +++ b/gdb/f-lang.h @@ -29,7 +29,8 @@ extern void f_print_type (struct type *, const char *, struct ui_file *, int, int); extern int f_val_print (struct type *, const gdb_byte *, int, CORE_ADDR, - struct ui_file *, int, struct value *, + struct ui_file *, int, + const struct value *, const struct value_print_options *); /* Language-specific data structures */ --- a/gdb/f-valprint.c +++ b/gdb/f-valprint.c @@ -165,7 +165,8 @@ static void f77_print_array_1 (int nss, int ndimensions, struct type *type, const gdb_byte *valaddr, int embedded_offset, CORE_ADDR address, - struct ui_file *stream, int recurse, struct value *val, + struct ui_file *stream, int recurse, + const struct value *val, const struct value_print_options *options, int *elts) { @@ -216,7 +217,8 @@ static void f77_print_array (struct type *type, const gdb_byte *valaddr, int embedded_offset, CORE_ADDR address, struct ui_file *stream, - int recurse, struct value *val, + int recurse, + const struct value *val, const struct value_print_options *options) { int ndimensions; @@ -247,7 +249,7 @@ Type node corrupt! F77 arrays cannot have %d subscripts (%d Max)"), int f_val_print (struct type *type, const gdb_byte *valaddr, int embedded_offset, CORE_ADDR address, struct ui_file *stream, int recurse, - struct value *original_value, + const struct value *original_value, const struct value_print_options *options) { struct gdbarch *gdbarch = get_type_arch (type); @@ -346,22 +348,12 @@ f_val_print (struct type *type, const gdb_byte *valaddr, int embedded_offset, { struct value *deref_val; - if (VALUE_LVAL (original_value) == lval_computed) + deref_val = coerce_ref_if_computed (original_value); + if (deref_val) { - const struct lval_funcs *funcs; - - funcs = value_computed_funcs (original_value); - if (funcs->indirect) - { - struct value *result = funcs->indirect (original_value); - - if (result) - { - common_val_print (result, stream, recurse, - options, current_language); - return 0; - } - } + common_val_print (deref_val, stream, recurse, options, + current_language); + return 0; } deref_val = value_at (TYPE_TARGET_TYPE (type), @@ -627,8 +619,7 @@ info_common_command (char *comname, int from_tty) while (entry != NULL) { - print_variable_and_value (NULL, entry->symbol, fi, gdb_stdout, 0, - PVAVD_IS_NOT_ARGUMENT); + print_variable_and_value (NULL, entry->symbol, fi, gdb_stdout, 0); entry = entry->next; } } --- a/gdb/frame.h +++ b/gdb/frame.h @@ -711,6 +711,47 @@ extern int frame_register_read (struct frame_info *frame, int regnum, gdb_byte *buf); /* From stack.c. */ + +extern const char print_entry_values_no[]; +extern const char print_entry_values_only[]; +extern const char print_entry_values_preferred[]; +extern const char print_entry_values_if_needed[]; +extern const char print_entry_values_both[]; +extern const char print_entry_values_compact[]; +extern const char print_entry_values_default[]; +extern const char *print_entry_values; + +/* Inferior function parameter value read in from a frame. */ + +struct frame_arg +{ + /* Symbol for this parameter used for example for its name. */ + struct symbol *sym; + + /* Value of the parameter. It is NULL if ERROR is not NULL; if both VAL and + ERROR are NULL this parameter's value should not be printed. */ + struct value *val; + + /* String containing the error message, it is more usually NULL indicating no + error occured reading this parameter. */ + char *error; + + /* One of the print_entry_values_* entries as appropriate specifically for + this frame_arg. It will be different from print_entry_values. With + print_entry_values_no this frame_arg should be printed as a normal + parameter. print_entry_values_only says it should be printed as entry + value parameter. print_entry_values_compact says it should be printed as + both as a normal parameter and entry values parameter having the same + value - print_entry_values_compact is not permitted fi ui_out_is_mi_like_p + (in such case print_entry_values_no and print_entry_values_only is used + for each parameter kind specifically. */ + const char *entry_kind; +}; + +extern void read_frame_arg (struct symbol *sym, struct frame_info *frame, + struct frame_arg *argp, + struct frame_arg *entryargp); + extern void args_info (char *, int); extern void locals_info (char *, int); --- a/gdb/gdbtypes.h +++ b/gdb/gdbtypes.h @@ -911,7 +911,7 @@ struct func_type struct call_site *tail_call_list; }; -/* A place where some function gets called from, represented by +/* A place where a function gets called from, represented by DW_TAG_GNU_call_site. It can be looked up from symtab->call_site_htab. */ struct call_site @@ -937,6 +937,10 @@ struct call_site /* Size of the PARAMETER array. */ unsigned parameter_count; + /* CU of the function where the call is located. It gets used for DWARF + blocks execution in the parameter array below. */ + struct dwarf2_per_cu_data *per_cu; + /* Describe DW_TAG_GNU_call_site's DW_TAG_formal_parameter. */ struct call_site_parameter { @@ -952,11 +956,13 @@ struct call_site /* DW_TAG_formal_parameter's DW_AT_GNU_call_site_value. It is never NULL. */ - struct dwarf2_locexpr_baton *call_site_value; + const gdb_byte *value; + size_t value_size; /* DW_TAG_formal_parameter's DW_AT_GNU_call_site_data_value. It may be NULL if not provided by DWARF. */ - struct dwarf2_locexpr_baton *call_site_data_value; + const gdb_byte *data_value; + size_t data_value_size; } parameter[1]; }; --- a/gdb/jv-lang.h +++ b/gdb/jv-lang.h @@ -43,7 +43,8 @@ struct builtin_java_type extern const struct builtin_java_type *builtin_java_type (struct gdbarch *); extern int java_val_print (struct type *, const gdb_byte *, int, CORE_ADDR, - struct ui_file *, int, struct value *, + struct ui_file *, int, + const struct value *, const struct value_print_options *); extern int java_value_print (struct value *, struct ui_file *, --- a/gdb/jv-valprint.c +++ b/gdb/jv-valprint.c @@ -264,7 +264,8 @@ static void java_print_value_fields (struct type *type, const gdb_byte *valaddr, int offset, CORE_ADDR address, struct ui_file *stream, - int recurse, struct value *val, + int recurse, + const struct value *val, const struct value_print_options *options) { int i, len, n_baseclasses; @@ -481,7 +482,8 @@ java_print_value_fields (struct type *type, const gdb_byte *valaddr, int java_val_print (struct type *type, const gdb_byte *valaddr, int embedded_offset, CORE_ADDR address, - struct ui_file *stream, int recurse, struct value *val, + struct ui_file *stream, int recurse, + const struct value *val, const struct value_print_options *options) { struct gdbarch *gdbarch = get_type_arch (type); --- a/gdb/language.c +++ b/gdb/language.c @@ -1118,7 +1118,7 @@ static int unk_lang_val_print (struct type *type, const gdb_byte *valaddr, int embedded_offset, CORE_ADDR address, struct ui_file *stream, int recurse, - struct value *val, + const struct value *val, const struct value_print_options *options) { error (_("internal error - unimplemented " --- a/gdb/language.h +++ b/gdb/language.h @@ -234,7 +234,7 @@ struct language_defn const gdb_byte *contents, int embedded_offset, CORE_ADDR address, struct ui_file *stream, int recurse, - struct value *val, + const struct value *val, const struct value_print_options *options); /* Print a top-level value using syntax appropriate for this language. */ --- a/gdb/m2-lang.h +++ b/gdb/m2-lang.h @@ -33,7 +33,8 @@ extern int m2_is_long_set (struct type *type); extern int m2_is_unbounded_array (struct type *type); extern int m2_val_print (struct type *, const gdb_byte *, int, CORE_ADDR, - struct ui_file *, int, struct value *, + struct ui_file *, int, + const struct value *, const struct value_print_options *); extern int get_long_set_bounds (struct type *type, LONGEST *low, --- a/gdb/m2-valprint.c +++ b/gdb/m2-valprint.c @@ -38,7 +38,7 @@ static void m2_print_array_contents (struct type *type, const gdb_byte *valaddr, int embedded_offset, CORE_ADDR address, struct ui_file *stream, int recurse, - struct value *val, + const struct value *val, const struct value_print_options *options, int len); @@ -279,7 +279,7 @@ static void m2_print_array_contents (struct type *type, const gdb_byte *valaddr, int embedded_offset, CORE_ADDR address, struct ui_file *stream, int recurse, - struct value *val, + const struct value *val, const struct value_print_options *options, int len) { @@ -317,7 +317,7 @@ m2_print_array_contents (struct type *type, const gdb_byte *valaddr, int m2_val_print (struct type *type, const gdb_byte *valaddr, int embedded_offset, CORE_ADDR address, struct ui_file *stream, int recurse, - struct value *original_value, + const struct value *original_value, const struct value_print_options *options) { struct gdbarch *gdbarch = get_type_arch (type); --- a/gdb/mi/mi-cmd-stack.c +++ b/gdb/mi/mi-cmd-stack.c @@ -236,6 +236,78 @@ mi_cmd_stack_list_variables (char *command, char **argv, int argc) list_args_or_locals (all, parse_print_values (argv[0]), frame); } +/* Print single local or argument. ARG must be already read in. For WHAT and + VALUES see list_args_or_locals. + + Errors are printed as if they would be the parameter value. Use zeroed ARG + iff it should not be printed accoring to VALUES. */ + +static void +list_arg_or_local (const struct frame_arg *arg, enum what_to_list what, + enum print_values values) +{ + struct cleanup *cleanup_tuple = NULL; + struct ui_out *uiout = current_uiout; + struct ui_stream *stb = ui_out_stream_new (uiout); + + gdb_assert (!arg->val || !arg->error); + gdb_assert ((values == PRINT_NO_VALUES && arg->val == NULL + && arg->error == NULL) + || values == PRINT_SIMPLE_VALUES + || (values == PRINT_ALL_VALUES + && (arg->val != NULL || arg->error != NULL))); + gdb_assert (arg->entry_kind == print_entry_values_no + || (arg->entry_kind == print_entry_values_only + && (arg->val || arg->error))); + + if (values != PRINT_NO_VALUES || what == all) + cleanup_tuple = make_cleanup_ui_out_tuple_begin_end (uiout, NULL); + + fputs_filtered (SYMBOL_PRINT_NAME (arg->sym), stb->stream); + if (arg->entry_kind == print_entry_values_only) + fputs_filtered ("@entry", stb->stream); + ui_out_field_stream (uiout, "name", stb); + + if (what == all && SYMBOL_IS_ARGUMENT (arg->sym)) + ui_out_field_int (uiout, "arg", 1); + + if (values == PRINT_SIMPLE_VALUES) + { + check_typedef (arg->sym->type); + type_print (arg->sym->type, "", stb->stream, -1); + ui_out_field_stream (uiout, "type", stb); + } + + if (arg->val || arg->error) + { + volatile struct gdb_exception except; + + if (arg->error) + except.message = arg->error; + else + { + /* TRY_CATCH has two statements, wrap it in a block. */ + + TRY_CATCH (except, RETURN_MASK_ERROR) + { + struct value_print_options opts; + + get_raw_print_options (&opts); + opts.deref_ref = 1; + common_val_print (arg->val, stb->stream, 0, &opts, + language_def (SYMBOL_LANGUAGE (arg->sym))); + } + } + if (except.message) + fprintf_filtered (stb->stream, _(""), + except.message); + ui_out_field_stream (uiout, "value", stb); + } + + ui_out_stream_delete (stb); + if (values != PRINT_NO_VALUES || what == all) + do_cleanups (cleanup_tuple); +} /* Print a list of the locals or the arguments for the currently selected frame. If the argument passed is 0, printonly the names @@ -313,16 +385,8 @@ list_args_or_locals (enum what_to_list what, enum print_values values, } if (print_me) { - struct cleanup *cleanup_tuple = NULL; struct symbol *sym2; - struct value *val; - - if (values != PRINT_NO_VALUES || what == all) - cleanup_tuple = - make_cleanup_ui_out_tuple_begin_end (uiout, NULL); - ui_out_field_string (uiout, "name", SYMBOL_PRINT_NAME (sym)); - if (what == all && SYMBOL_IS_ARGUMENT (sym)) - ui_out_field_int (uiout, "arg", 1); + struct frame_arg arg, entryarg; if (SYMBOL_IS_ARGUMENT (sym)) sym2 = lookup_symbol (SYMBOL_NATURAL_NAME (sym), @@ -330,64 +394,34 @@ list_args_or_locals (enum what_to_list what, enum print_values values, (int *) NULL); else sym2 = sym; + + memset (&arg, 0, sizeof (arg)); + arg.sym = sym2; + arg.entry_kind = print_entry_values_no; + memset (&entryarg, 0, sizeof (entryarg)); + entryarg.sym = sym2; + entryarg.entry_kind = print_entry_values_no; + switch (values) { case PRINT_SIMPLE_VALUES: type = check_typedef (sym2->type); - type_print (sym2->type, "", stb->stream, -1); - ui_out_field_stream (uiout, "type", stb); if (TYPE_CODE (type) != TYPE_CODE_ARRAY && TYPE_CODE (type) != TYPE_CODE_STRUCT && TYPE_CODE (type) != TYPE_CODE_UNION) { - volatile struct gdb_exception except; - - TRY_CATCH (except, RETURN_MASK_ERROR) - { - struct value_print_options opts; - - val = read_var_value (sym2, fi); - get_raw_print_options (&opts); - opts.deref_ref = 1; - common_val_print - (val, stb->stream, 0, &opts, - language_def (SYMBOL_LANGUAGE (sym2))); - } - if (except.reason < 0) - fprintf_filtered (stb->stream, - _(""), - except.message); - - ui_out_field_stream (uiout, "value", stb); - } - break; case PRINT_ALL_VALUES: - { - volatile struct gdb_exception except; - - TRY_CATCH (except, RETURN_MASK_ERROR) - { - struct value_print_options opts; - - val = read_var_value (sym2, fi); - get_raw_print_options (&opts); - opts.deref_ref = 1; - common_val_print - (val, stb->stream, 0, &opts, - language_def (SYMBOL_LANGUAGE (sym2))); - } - if (except.reason < 0) - fprintf_filtered (stb->stream, - _(""), - except.message); - - ui_out_field_stream (uiout, "value", stb); - } + read_frame_arg (sym2, fi, &arg, &entryarg); + } break; } - if (values != PRINT_NO_VALUES || what == all) - do_cleanups (cleanup_tuple); + if (arg.entry_kind != print_entry_values_only) + list_arg_or_local (&arg, what, values); + if (entryarg.entry_kind != print_entry_values_no) + list_arg_or_local (&entryarg, what, values); + xfree (arg.error); + xfree (entryarg.error); } } if (BLOCK_FUNCTION (block)) --- a/gdb/opencl-lang.c +++ b/gdb/opencl-lang.c @@ -360,7 +360,8 @@ static const struct lval_funcs opencl_value_funcs = lval_func_write, lval_func_check_validity, lval_func_check_any_valid, - NULL, + NULL, /* indirect */ + NULL, /* coerce_ref */ lval_func_check_synthetic_pointer, lval_func_copy_closure, lval_func_free_closure --- a/gdb/p-lang.h +++ b/gdb/p-lang.h @@ -38,7 +38,7 @@ extern void pascal_print_typedef (struct type *, struct symbol *, extern int pascal_val_print (struct type *, const gdb_byte *, int, CORE_ADDR, struct ui_file *, int, - struct value *, + const struct value *, const struct value_print_options *); extern int pascal_value_print (struct value *, struct ui_file *, @@ -72,7 +72,8 @@ extern void extern void pascal_object_print_value_fields (struct type *, const gdb_byte *, int, CORE_ADDR, struct ui_file *, - int, struct value *, + int, + const struct value *, const struct value_print_options *, struct type **, int); --- a/gdb/p-valprint.c +++ b/gdb/p-valprint.c @@ -49,7 +49,7 @@ int pascal_val_print (struct type *type, const gdb_byte *valaddr, int embedded_offset, CORE_ADDR address, struct ui_file *stream, int recurse, - struct value *original_value, + const struct value *original_value, const struct value_print_options *options) { struct gdbarch *gdbarch = get_type_arch (type); @@ -274,22 +274,12 @@ pascal_val_print (struct type *type, const gdb_byte *valaddr, { struct value *deref_val; - if (VALUE_LVAL (original_value) == lval_computed) + deref_val = coerce_ref_if_computed (original_value); + if (deref_val) { - const struct lval_funcs *funcs; - - funcs = value_computed_funcs (original_value); - if (funcs->indirect) - { - struct value *result = funcs->indirect (original_value); - - if (result) - { - common_val_print (result, stream, recurse, - options, current_language); - return 0; - } - } + common_val_print (deref_val, stream, recurse + 1, options, + current_language); + return 0; } deref_val = value_at (TYPE_TARGET_TYPE (type), @@ -645,7 +635,7 @@ static void pascal_object_print_static_field (struct value *, static void pascal_object_print_value (struct type *, const gdb_byte *, int, CORE_ADDR, struct ui_file *, int, - struct value *, + const struct value *, const struct value_print_options *, struct type **); @@ -704,7 +694,8 @@ void pascal_object_print_value_fields (struct type *type, const gdb_byte *valaddr, int offset, CORE_ADDR address, struct ui_file *stream, - int recurse, struct value *val, + int recurse, + const struct value *val, const struct value_print_options *options, struct type **dont_print_vb, int dont_print_statmem) @@ -898,7 +889,8 @@ static void pascal_object_print_value (struct type *type, const gdb_byte *valaddr, int offset, CORE_ADDR address, struct ui_file *stream, - int recurse, struct value *val, + int recurse, + const struct value *val, const struct value_print_options *options, struct type **dont_print_vb) { --- a/gdb/printcmd.c +++ b/gdb/printcmd.c @@ -1956,114 +1956,33 @@ clear_dangling_display_expressions (struct so_list *solib) struct symbol. NAME is the name to print; if NULL then VAR's print name will be used. STREAM is the ui_file on which to print the value. INDENT specifies the number of indent levels to print - before printing the variable name. PRINT_ARGUMENT specifies whether @entry - kind of function parameters should be printed. */ + before printing the variable name. */ void print_variable_and_value (const char *name, struct symbol *var, struct frame_info *frame, - struct ui_file *stream, int indent, - enum print_argument print_argument) + struct ui_file *stream, int indent) { volatile struct gdb_exception except; if (!name) name = SYMBOL_PRINT_NAME (var); + fprintf_filtered (stream, "%s%s = ", n_spaces (2 * indent), name); TRY_CATCH (except, RETURN_MASK_ERROR) { - struct value *val, *entryval = NULL; + struct value *val; struct value_print_options opts; val = read_var_value (var, frame); get_user_print_options (&opts); opts.deref_ref = 1; - - if (print_argument != PVAVD_IS_NOT_ARGUMENT - && SYMBOL_CLASS (var) == LOC_COMPUTED - && SYMBOL_COMPUTED_OPS (var)->read_variable_at_entry != NULL) - { - const struct symbol_computed_ops *ops; - unsigned len = TYPE_LENGTH (value_type (val)); - volatile struct gdb_exception entryval_ex; - - ops = SYMBOL_COMPUTED_OPS (var); - - TRY_CATCH (entryval_ex, RETURN_MASK_ERROR) - { - entryval = ops->read_variable_at_entry (var, frame); - } - - if (entryval_ex.reason < 0 || value_optimized_out (entryval)) - entryval = NULL; - else - { - if (!value_optimized_out (val) && value_lazy (val)) - value_fetch_lazy (val); - if (!value_optimized_out (val) && value_lazy (entryval)) - value_fetch_lazy (entryval); - if (!value_optimized_out (val) - && value_available_contents_eq (val, 0, entryval, 0, len)) - { - volatile struct gdb_exception deref_ex; - struct value *val_deref, *entryval_deref; - - /* DW_AT_GNU_call_site_value does match with the current - value. If it is a reference still try to verify if - dereferenced DW_AT_GNU_call_site_data_value does not - differ. */ - - TRY_CATCH (deref_ex, RETURN_MASK_ERROR) - { - unsigned len_deref; - - val_deref = coerce_ref (val); - if (value_lazy (val_deref)) - value_fetch_lazy (val_deref); - len_deref = TYPE_LENGTH (value_type (val_deref)); - - entryval_deref = coerce_ref (entryval); - if (value_lazy (entryval_deref)) - value_fetch_lazy (entryval_deref); - - /* If the reference addresses match but dereferenced - content does not match print them. */ - if (val != val_deref - && value_available_contents_eq (val_deref, 0, - entryval_deref, 0, - len_deref)) - entryval = NULL; - } - - /* If the dereferenced content could not be fetch do not - display anything. */ - if (deref_ex.reason < 0) - entryval = NULL; - - /* Value was not a reference; and its content matches. */ - if (val == val_deref) - entryval = NULL; - } - } - } - - if (print_argument != PVAVD_ARGUMENT_PRINT_ENTRYVAL_ONLY) - { - fprintf_filtered (stream, "%s%s = ", n_spaces (2 * indent), name); - common_val_print (val, stream, indent, &opts, current_language); - fputc_filtered ('\n', stream); - } - if (entryval) - { - fprintf_filtered (stream, "%s%s@entry = ", n_spaces (2 * indent), - name); - common_val_print (entryval, stream, indent, &opts, current_language); - fputc_filtered ('\n', stream); - } + common_val_print (val, stream, indent, &opts, current_language); } if (except.reason < 0) - fprintf_filtered (stream, "%s%s = \n", - n_spaces (2 * indent), name, name, except.message); + fprintf_filtered(stream, "", name, + except.message); + fprintf_filtered (stream, "\n"); } /* printf "printf format string" ARG to STREAM. */ --- a/gdb/python/py-frame.c +++ b/gdb/python/py-frame.c @@ -595,6 +595,7 @@ gdbpy_initialize_frames (void) PyModule_AddIntConstant (gdb_module, "NORMAL_FRAME", NORMAL_FRAME); PyModule_AddIntConstant (gdb_module, "DUMMY_FRAME", DUMMY_FRAME); PyModule_AddIntConstant (gdb_module, "INLINE_FRAME", INLINE_FRAME); + PyModule_AddIntConstant (gdb_module, "TAILCALL_FRAME", TAILCALL_FRAME); PyModule_AddIntConstant (gdb_module, "SIGTRAMP_FRAME", SIGTRAMP_FRAME); PyModule_AddIntConstant (gdb_module, "ARCH_FRAME", ARCH_FRAME); PyModule_AddIntConstant (gdb_module, "SENTINEL_FRAME", SENTINEL_FRAME); --- a/gdb/python/py-type.c +++ b/gdb/python/py-type.c @@ -29,6 +29,7 @@ #include "language.h" #include "vec.h" #include "bcache.h" +#include "dwarf2loc.h" typedef struct pyty_type_object { @@ -822,6 +823,22 @@ check_types_equal (struct type *type1, struct type *type2, FIELD_STATIC_PHYSNAME (*field2))) return Py_NE; break; + case FIELD_LOC_KIND_DWARF_BLOCK: + { + struct dwarf2_locexpr_baton *block1, *block2; + + block1 = FIELD_DWARF_BLOCK (*field1); + block2 = FIELD_DWARF_BLOCK (*field2); + if (block1->per_cu != block2->per_cu + || block1->size != block2->size + || memcmp (block1->data, block2->data, block1->size) != 0) + return Py_NE; + } + break; + default: + internal_error (__FILE__, __LINE__, _("Unsupported field kind " + "%d by check_types_equal"), + FIELD_LOC_KIND (*field1)); } entry.type1 = FIELD_TYPE (*field1); --- a/gdb/stack.c +++ b/gdb/stack.c @@ -64,6 +64,29 @@ static const char *print_frame_arguments_choices[] = {"all", "scalars", "none", NULL}; static const char *print_frame_arguments = "scalars"; +/* The possible choices of "set print entry-values", and the value + of this setting. */ + +const char print_entry_values_no[] = "no"; +const char print_entry_values_only[] = "only"; +const char print_entry_values_preferred[] = "preferred"; +const char print_entry_values_if_needed[] = "if-needed"; +const char print_entry_values_both[] = "both"; +const char print_entry_values_compact[] = "compact"; +const char print_entry_values_default[] = "default"; +static const char *print_entry_values_choices[] = +{ + print_entry_values_no, + print_entry_values_only, + print_entry_values_preferred, + print_entry_values_if_needed, + print_entry_values_both, + print_entry_values_compact, + print_entry_values_default, + NULL +}; +const char *print_entry_values = print_entry_values_default; + /* Prototypes for local functions. */ static void print_frame_local_vars (struct frame_info *, int, @@ -162,6 +185,285 @@ print_frame_nameless_args (struct frame_info *frame, long start, int num, } } +/* Print single argument of inferior function. ARG must be already + read in. + + Errors are printed as if they would be the parameter value. Use zeroed ARG + iff it should not be printed accoring to user settings. */ + +static void +print_frame_arg (const struct frame_arg *arg) +{ + struct ui_out *uiout = current_uiout; + volatile struct gdb_exception except; + struct cleanup *old_chain; + struct ui_stream *stb; + + stb = ui_out_stream_new (uiout); + old_chain = make_cleanup_ui_out_stream_delete (stb); + + gdb_assert (!arg->val || !arg->error); + gdb_assert (arg->entry_kind == print_entry_values_no + || arg->entry_kind == print_entry_values_only + || (!ui_out_is_mi_like_p (uiout) + && arg->entry_kind == print_entry_values_compact)); + + annotate_arg_begin (); + + make_cleanup_ui_out_tuple_begin_end (uiout, NULL); + fprintf_symbol_filtered (stb->stream, SYMBOL_PRINT_NAME (arg->sym), + SYMBOL_LANGUAGE (arg->sym), DMGL_PARAMS | DMGL_ANSI); + if (arg->entry_kind == print_entry_values_compact) + { + /* It is OK to provide invalid MI-like stream as with + PRINT_ENTRY_VALUE_COMPACT we never use MI. */ + fputs_filtered ("=", stb->stream); + + fprintf_symbol_filtered (stb->stream, SYMBOL_PRINT_NAME (arg->sym), + SYMBOL_LANGUAGE (arg->sym), + DMGL_PARAMS | DMGL_ANSI); + } + if (arg->entry_kind == print_entry_values_only + || arg->entry_kind == print_entry_values_compact) + fputs_filtered ("@entry", stb->stream); + ui_out_field_stream (uiout, "name", stb); + annotate_arg_name_end (); + ui_out_text (uiout, "="); + + if (!arg->val && !arg->error) + ui_out_text (uiout, "..."); + else + { + if (arg->error) + except.message = arg->error; + else + { + /* TRY_CATCH has two statements, wrap it in a block. */ + + TRY_CATCH (except, RETURN_MASK_ERROR) + { + const struct language_defn *language; + struct value_print_options opts; + + /* Avoid value_print because it will deref ref parameters. We + just want to print their addresses. Print ??? for args whose + address we do not know. We pass 2 as "recurse" to val_print + because our standard indentation here is 4 spaces, and + val_print indents 2 for each recurse. */ + + annotate_arg_value (value_type (arg->val)); + + /* Use the appropriate language to display our symbol, unless the + user forced the language to a specific language. */ + if (language_mode == language_mode_auto) + language = language_def (SYMBOL_LANGUAGE (arg->sym)); + else + language = current_language; + + get_raw_print_options (&opts); + opts.deref_ref = 1; + + /* True in "summary" mode, false otherwise. */ + opts.summary = !strcmp (print_frame_arguments, "scalars"); + + common_val_print (arg->val, stb->stream, 2, &opts, language); + } + } + if (except.message) + fprintf_filtered (stb->stream, _(""), + except.message); + } + + ui_out_field_stream (uiout, "value", stb); + + /* Aleo invoke ui_out_tuple_end. */ + do_cleanups (old_chain); + + annotate_arg_end (); +} + +/* Read in inferior function parameter SYM at FRAME into ARGP. Caller is + responsible for xfree of ARGP->ERROR. This function never throws an + exception. */ + +void +read_frame_arg (struct symbol *sym, struct frame_info *frame, + struct frame_arg *argp, struct frame_arg *entryargp) +{ + struct value *val = NULL, *entryval = NULL; + char *val_error = NULL, *entryval_error = NULL; + int val_equal = 0; + volatile struct gdb_exception except; + + if (print_entry_values != print_entry_values_only + && print_entry_values != print_entry_values_preferred) + { + TRY_CATCH (except, RETURN_MASK_ERROR) + { + val = read_var_value (sym, frame); + } + if (!val) + { + val_error = alloca (strlen (except.message) + 1); + strcpy (val_error, except.message); + } + } + + if (SYMBOL_CLASS (sym) == LOC_COMPUTED + && print_entry_values != print_entry_values_no + && (print_entry_values != print_entry_values_if_needed + || !val || value_optimized_out (val))) + { + TRY_CATCH (except, RETURN_MASK_ERROR) + { + const struct symbol_computed_ops *ops; + + ops = SYMBOL_COMPUTED_OPS (sym); + entryval = ops->read_variable_at_entry (sym, frame); + } + if (!entryval) + { + entryval_error = alloca (strlen (except.message) + 1); + strcpy (entryval_error, except.message); + } + + if (except.error == NO_ENTRY_VALUE_ERROR + || (entryval && value_optimized_out (entryval))) + { + entryval = NULL; + entryval_error = NULL; + } + + if (print_entry_values == print_entry_values_compact + || print_entry_values == print_entry_values_default) + { + /* For MI do not try to use print_entry_values_compact for ARGP. */ + + if (val && entryval && !ui_out_is_mi_like_p (current_uiout)) + { + unsigned len = TYPE_LENGTH (value_type (val)); + + if (!value_optimized_out (val) && value_lazy (val)) + value_fetch_lazy (val); + if (!value_optimized_out (val) && value_lazy (entryval)) + value_fetch_lazy (entryval); + if (!value_optimized_out (val) + && value_available_contents_eq (val, 0, entryval, 0, len)) + { + struct value *val_deref, *entryval_deref; + + /* DW_AT_GNU_call_site_value does match with the current + value. If it is a reference still try to verify if + dereferenced DW_AT_GNU_call_site_data_value does not + differ. */ + + TRY_CATCH (except, RETURN_MASK_ERROR) + { + unsigned len_deref; + + val_deref = coerce_ref (val); + if (value_lazy (val_deref)) + value_fetch_lazy (val_deref); + len_deref = TYPE_LENGTH (value_type (val_deref)); + + entryval_deref = coerce_ref (entryval); + if (value_lazy (entryval_deref)) + value_fetch_lazy (entryval_deref); + + /* If the reference addresses match but dereferenced + content does not match print them. */ + if (val != val_deref + && value_available_contents_eq (val_deref, 0, + entryval_deref, 0, + len_deref)) + val_equal = 1; + } + + /* Value was not a reference; and its content matches. */ + if (val == val_deref) + val_equal = 1; + /* If the dereferenced content could not be fetched do not + display anything. */ + else if (except.error == NO_ENTRY_VALUE_ERROR) + val_equal = 1; + else if (except.message) + { + entryval_error = alloca (strlen (except.message) + 1); + strcpy (entryval_error, except.message); + } + + if (val_equal) + entryval = NULL; + } + } + + /* Try to remove possibly duplicate error message for ENTRYARGP even + in MI mode. */ + + if (val_error && entryval_error + && strcmp (val_error, entryval_error) == 0) + { + entryval_error = NULL; + + /* Do not se VAL_EQUAL as the same error message may be shown for + the entry value even if no entry values are present in the + inferior. */ + } + } + } + + if (entryval == NULL) + { + if (print_entry_values == print_entry_values_preferred) + { + TRY_CATCH (except, RETURN_MASK_ERROR) + { + val = read_var_value (sym, frame); + } + if (!val) + { + val_error = alloca (strlen (except.message) + 1); + strcpy (val_error, except.message); + } + } + if (print_entry_values == print_entry_values_only + || print_entry_values == print_entry_values_both + || (print_entry_values == print_entry_values_preferred + && (!val || value_optimized_out (val)))) + entryval = allocate_optimized_out_value (SYMBOL_TYPE (sym)); + } + if ((print_entry_values == print_entry_values_compact + || print_entry_values == print_entry_values_if_needed + || print_entry_values == print_entry_values_preferred) + && (!val || value_optimized_out (val)) && entryval != NULL) + { + val = NULL; + val_error = NULL; + } + + argp->sym = sym; + argp->val = val; + argp->error = val_error ? xstrdup (val_error) : NULL; + if (!val && !val_error) + argp->entry_kind = print_entry_values_only; + else if ((print_entry_values == print_entry_values_compact + || print_entry_values == print_entry_values_default) && val_equal) + { + argp->entry_kind = print_entry_values_compact; + gdb_assert (!ui_out_is_mi_like_p (current_uiout)); + } + else + argp->entry_kind = print_entry_values_no; + + entryargp->sym = sym; + entryargp->val = entryval; + entryargp->error = entryval_error ? xstrdup (entryval_error) : NULL; + if (!entryval && !entryval_error) + entryargp->entry_kind = print_entry_values_no; + else + entryargp->entry_kind = print_entry_values_only; +} + /* Print the arguments of frame FRAME on STREAM, given the function FUNC running in that frame (as a symbol), where NUM is the number of arguments according to the stack frame (or -1 if the number of @@ -198,10 +500,11 @@ print_frame_args (struct symbol *func, struct frame_info *frame, struct block *b = SYMBOL_BLOCK_VALUE (func); struct dict_iterator iter; struct symbol *sym; - struct value *val; ALL_BLOCK_SYMBOLS (b, iter, sym) { + struct frame_arg arg, entryarg; + QUIT; /* Keep track of the highest stack argument offset seen, and @@ -314,65 +617,34 @@ print_frame_args (struct symbol *func, struct frame_info *frame, ui_out_text (uiout, ", "); ui_out_wrap_hint (uiout, " "); - annotate_arg_begin (); - - list_chain = make_cleanup_ui_out_tuple_begin_end (uiout, NULL); - fprintf_symbol_filtered (stb->stream, SYMBOL_PRINT_NAME (sym), - SYMBOL_LANGUAGE (sym), - DMGL_PARAMS | DMGL_ANSI); - ui_out_field_stream (uiout, "name", stb); - annotate_arg_name_end (); - ui_out_text (uiout, "="); + if (!print_args) + { + memset (&arg, 0, sizeof (arg)); + arg.sym = sym; + arg.entry_kind = print_entry_values_no; + memset (&entryarg, 0, sizeof (entryarg)); + entryarg.sym = sym; + entryarg.entry_kind = print_entry_values_no; + } + else + read_frame_arg (sym, frame, &arg, &entryarg); - if (print_args) - { - volatile struct gdb_exception except; + if (arg.entry_kind != print_entry_values_only) + print_frame_arg (&arg); - TRY_CATCH (except, RETURN_MASK_ERROR) - { - const struct language_defn *language; - struct value_print_options opts; - - /* Avoid value_print because it will deref ref parameters. - We just want to print their addresses. Print ??? for - args whose address we do not know. We pass 2 as - "recurse" to val_print because our standard indentation - here is 4 spaces, and val_print indents 2 for each - recurse. */ - val = read_var_value (sym, frame); - - annotate_arg_value (value_type (val)); - - /* Use the appropriate language to display our symbol, - unless the user forced the language to a specific - language. */ - if (language_mode == language_mode_auto) - language = language_def (SYMBOL_LANGUAGE (sym)); - else - language = current_language; - - get_raw_print_options (&opts); - opts.deref_ref = 1; - opts.summary = summary; - common_val_print (val, stb->stream, 2, &opts, language); - ui_out_field_stream (uiout, "value", stb); - } - if (except.reason < 0) + if (entryarg.entry_kind != print_entry_values_no) + { + if (arg.entry_kind != print_entry_values_only) { - fprintf_filtered (stb->stream, - _(""), - except.message); - ui_out_field_stream (uiout, "value", stb); + ui_out_text (uiout, ", "); + ui_out_wrap_hint (uiout, " "); } - } - else - ui_out_text (uiout, "..."); - - /* Invoke ui_out_tuple_end. */ - do_cleanups (list_chain); + print_frame_arg (&entryarg); + } - annotate_arg_end (); + xfree (arg.error); + xfree (entryarg.error); first = 0; } @@ -1087,8 +1359,7 @@ frame_info (char *addr_exp, int from_tty) frame_stop_reason_string (reason)); } else if (get_frame_type (fi) == TAILCALL_FRAME) - printf_filtered (" tail call frame %d", - frame_relative_level (get_prev_frame (fi))); + puts_filtered (" tail call frame"); else if (get_frame_type (fi) == INLINE_FRAME) printf_filtered (" inlined into frame %d", frame_relative_level (get_prev_frame (fi))); @@ -1532,7 +1803,6 @@ struct print_variable_and_value_data int num_tabs; struct ui_file *stream; int values_printed; - enum print_argument print_argument; }; /* The callback for the locals and args iterators. */ @@ -1544,17 +1814,13 @@ do_print_variable_and_value (const char *print_name, { struct print_variable_and_value_data *p = cb_data; - print_variable_and_value (print_name, sym, p->frame, p->stream, p->num_tabs, - p->print_argument); + print_variable_and_value (print_name, sym, + p->frame, p->stream, p->num_tabs); p->values_printed = 1; } -/* Show function local variables at FRAME. FROM_FRAME is 1 if the local - variables are printed after the function frame (parameter values) have been - printed, it is 0 otherwise. Print them to STREAM. */ - static void -print_frame_local_vars (struct frame_info *frame, int from_frame, +print_frame_local_vars (struct frame_info *frame, int num_tabs, struct ui_file *stream) { struct print_variable_and_value_data cb_data; @@ -1576,24 +1842,10 @@ print_frame_local_vars (struct frame_info *frame, int from_frame, } cb_data.frame = frame; - cb_data.num_tabs = from_frame ? 4 : 0; + cb_data.num_tabs = 4 * num_tabs; cb_data.stream = stream; cb_data.values_printed = 0; - if (from_frame) - { - /* For the variables we display them from the innermost block. But for - parameters we need to fetch the outermost block still in the same - function. Stop at the first inlined function boundary, if any. */ - struct symbol *func = get_frame_function (frame); - - cb_data.print_argument = PVAVD_ARGUMENT_PRINT_ENTRYVAL_ONLY; - if (func) - iterate_over_block_arg_vars (SYMBOL_BLOCK_VALUE (func), - do_print_variable_and_value, &cb_data); - } - - cb_data.print_argument = PVAVD_IS_NOT_ARGUMENT; iterate_over_block_local_vars (block, do_print_variable_and_value, &cb_data); @@ -1753,7 +2005,6 @@ print_frame_arg_vars (struct frame_info *frame, struct ui_file *stream) cb_data.stream = gdb_stdout; cb_data.values_printed = 0; - cb_data.print_argument = PVAVD_ARGUMENT_PRINT_BOTH; iterate_over_block_arg_vars (SYMBOL_BLOCK_VALUE (func), do_print_variable_and_value, &cb_data); @@ -2274,4 +2525,17 @@ source line."), show_disassemble_next_line, &setlist, &showlist); disassemble_next_line = AUTO_BOOLEAN_FALSE; + + add_setshow_enum_cmd ("entry-values", class_stack, + print_entry_values_choices, &print_entry_values, + _("Set printing of frame arguments values at function " + "entry"), + _("Show printing of frame arguments values at function " + "entry"), + _("\ +GDB can print in some cases besides frame arguments values also the values\n\ +they had at function entry (marked as `NAME@entry'). The value itself and/or\n\ +the entry value may be . Which of this current or entry\n\ +values get printed in which case can be set by this option."), + NULL, NULL, &setprintlist, &showprintlist); } --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -534,8 +534,8 @@ struct symbol_computed_ops struct frame_info * frame); /* Read variable SYMBOL like read_variable at (callee) FRAME's function - entry. SYMBOL should be a function parameter, otherwise NOT_FOUND_ERROR - will be thrown. */ + entry. SYMBOL should be a function parameter, otherwise + NO_ENTRY_VALUE_ERROR will be thrown. */ struct value *(*read_variable_at_entry) (struct symbol *symbol, struct frame_info *frame); --- a/gdb/testsuite/gdb.arch/amd64-entry-value.cc +++ b/gdb/testsuite/gdb.arch/amd64-entry-value.cc @@ -18,43 +18,51 @@ static volatile int v; static void __attribute__((noinline, noclone)) -e (int i) +e (int i, double j) { v = 0; } static void __attribute__((noinline, noclone)) -d (int i) +d (int i, double j) { i++; - e (i); - e (v); + j++; + e (i, j); + e (v, v); asm ("breakhere:"); - e (v); + e (v, v); } static void __attribute__((noinline, noclone)) -c (int i) +locexpr (int i) { - d (i * 10); + i = i; +asm ("breakhere_locexpr:"); } static void __attribute__((noinline, noclone)) -a (int i) +c (int i, double j) { - c (i + 1); + d (i * 10, j * 10); } static void __attribute__((noinline, noclone)) -b (int i) +a (int i, double j) { - c (i + 2); + c (i + 1, j + 1); +} + +static void __attribute__((noinline, noclone)) +b (int i, double j) +{ + c (i + 2, j + 2); } static void __attribute__((noinline, noclone)) amb_z (int i) { - d (i + 7); + d (i + 7, i + 7.5); } static void __attribute__((noinline, noclone)) @@ -108,46 +116,117 @@ self (int i) } else { - e (v); - d (i + 2); + e (v, v); + d (i + 2, i + 2.5); } } static void __attribute__((noinline, noclone)) -stacktest (int r1, int r2, int r3, int r4, int r5, int r6, int s1, int s2) +stacktest (int r1, int r2, int r3, int r4, int r5, int r6, int s1, int s2, + double d1, double d2, double d3, double d4, double d5, double d6, + double d7, double d8, double d9, double da) { s1 = 3; s2 = 4; - e (v); + d9 = 3.5; + da = 4.5; + e (v, v); asm ("breakhere_stacktest:"); - e (v); + e (v, v); } +/* nodataparam has DW_AT_GNU_call_site_value but it does not have + DW_AT_GNU_call_site_data_value. GDB should not display dereferenced @entry + value for it. */ + static void __attribute__((noinline, noclone)) -reference (int &refparam) +reference (int ®param, int &nodataparam, int r3, int r4, int r5, int r6, + int &stackparam1, int &stackparam2) { - int refcopy = refparam; + int regcopy = regparam, nodatacopy = nodataparam; + int stackcopy1 = stackparam1, stackcopy2 = stackparam2; - refparam = 10; + regparam = 21; + nodataparam = 22; + stackparam1 = 31; + stackparam2 = 32; + e (v, v); asm ("breakhere_reference:"); - e (v); + e (v, v); +} + +static int *__attribute__((noinline, noclone)) +datap () +{ + static int two = 2; + + return &two; +} + +static void __attribute__((noinline, noclone)) +datap_input (int *datap) +{ + (*datap)++; +} + +static int __attribute__((noinline, noclone)) +data (void) +{ + return 10; +} + +static int __attribute__((noinline, noclone)) +data2 (void) +{ + return 20; +} + +static int __attribute__((noinline, noclone)) +different (int val) +{ + val++; + e (val, val); +asm ("breakhere_different:"); + return val; +} + +static int __attribute__((noinline, noclone)) +validity (int lost, int born) +{ + lost = data (); + e (0, 0.0); +asm ("breakhere_validity:"); + return born; +} + +static void __attribute__((noinline, noclone)) +invalid (int inv) +{ + e (0, 0.0); +asm ("breakhere_invalid:"); } int main () { - int refvar; - - d (30); - stacktest (1, 2, 3, 4, 5, 6, 11, 12); + d (30, 30.5); + locexpr (30); + stacktest (1, 2, 3, 4, 5, 6, 11, 12, + 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 11.5, 12.5); + different (5); + validity (5, data ()); + invalid (data2 ()); - refvar = 5; - reference (refvar); + { + int regvar = 1, *nodatavarp = datap (), stackvar1 = 11, stackvar2 = 12; + reference (regvar, *nodatavarp, 3, 4, 5, 6, stackvar1, stackvar2); + datap_input (nodatavarp); + } if (v) - a (1); + a (1, 1.25); else - b (5); + b (5, 5.25); amb_a (100); self (200); return 0; --- a/gdb/testsuite/gdb.arch/amd64-entry-value.exp +++ b/gdb/testsuite/gdb.arch/amd64-entry-value.exp @@ -35,8 +35,13 @@ if ![runto_main] { } gdb_breakpoint "breakhere" +gdb_breakpoint "breakhere_locexpr" gdb_breakpoint "stacktest" gdb_breakpoint "breakhere_stacktest" +gdb_breakpoint "different" +gdb_breakpoint "breakhere_different" +gdb_breakpoint "breakhere_validity" +gdb_breakpoint "breakhere_invalid" gdb_breakpoint "reference" gdb_breakpoint "breakhere_reference" @@ -45,112 +50,175 @@ gdb_breakpoint "breakhere_reference" gdb_continue_to_breakpoint "entry: breakhere" -# (gdb) bt full -# #0 d (i=31) at gdb.arch/amd64-entry-value.cc:33 -# i@entry = 30 -# #1 0x00000000004003da in main () at gdb.arch/amd64-entry-value.cc:56 -gdb_test "bt full" "^bt full\r\n#0 +d *\\(i=31\\) \[^\r\n\]*\r\n\[ \t\]*i@entry = 30\r\n#1 +0x\[0-9a-f\]+ in main .*" \ - "entry: bt full" +gdb_test "bt" "^bt\r\n#0 +d *\\(i=31, i@entry=30, j=31\\.5, j@entry=30\\.5\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in main .*" \ + "entry: bt" gdb_test "p i" " = 31" "entry: p i" gdb_test "p i@entry" " = 30" "entry: p i@entry" +gdb_test "p j" { = 31\.5} "entry: p j" +gdb_test "p j@entry" { = 30\.5} "entry: p j@entry" + + +# Test @entry values when parameter in function is locexpr (and not loclist). + +gdb_continue_to_breakpoint "entry_locexpr: breakhere_locexpr" +gdb_test "p i" " = 30" "entry_locexpr: p i" +gdb_test_no_output "set variable i = 0" "entry_locexpr: set variable i = 0" +gdb_test "bt" "^bt\r\n#0 +locexpr *\\(i=0, i@entry=30\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in main .*" \ + "entry_locexpr: bt" # Test @entry values for stack passed parameters. gdb_continue_to_breakpoint "entry_stack: stacktest" -# (gdb) bt full -# #0 stacktest (r1=1, r2=2, r3=3, r4=4, r5=5, r6=6, s1=11, s2=12) at gdb.arch/amd64-entry-value.cc:121 -# #1 0x0000000000400412 in main () at gdb.arch/amd64-entry-value.cc:142 -# Check s1 and s2 are suppressed: -gdb_test "bt full" "^bt full\r\n#0 +stacktest *\\(r1=1, r2=2, r3=3, r4=4, r5=5, r6=6, s1=11, s2=12\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in main .*" \ - "entry_stack: bt full at entry" +gdb_test "bt" "^bt\r\n#0 +stacktest *\\(r1=r1@entry=1, r2=r2@entry=2, \[^\r\n\]+, s1=s1@entry=11, s2=s2@entry=12, \[^\r\n\]+, d9=d9@entry=11\\.5, da=da@entry=12\\.5\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in main .*" \ + "entry_stack: bt at entry" gdb_continue_to_breakpoint "entry_stack: breakhere_stacktest" -# (gdb) bt full -# #0 stacktest (r1=1, r2=2, r3=3, r4=4, r5=5, r6=6, s1=3, s2=4) at gdb.arch/amd64-entry-value.cc:123 -# s1@entry = 11 -# s2@entry = 12 -# #1 0x0000000000400412 in main () at gdb.arch/amd64-entry-value.cc:130 -# Check s1 and s2 are present: -gdb_test "bt full" "^bt full\r\n#0 +stacktest *\\(r1=1, r2=2, r3=3, r4=4, r5=5, r6=6, s1=3, s2=4\\) \[^\r\n\]*\r\n\[ \t\]*s1@entry = 11\r\n\[ \t\]*s2@entry = 12\r\n#1 +0x\[0-9a-f\]+ in main .*" \ - "entry_stack: bt full" +gdb_test "bt" "^bt\r\n#0 +stacktest *\\(r1=r1@entry=1, r2=r2@entry=2, \[^\r\n\]+, s1=3, s1@entry=11, s2=4, s2@entry=12, \[^\r\n\]+, d9=3\\.5, d9@entry=11\\.5, da=4\\.5, da@entry=12\\.5\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in main .*" \ + "entry_stack: bt" gdb_test "p s1" " = 3" "entry_stack: p s1" gdb_test "p s1@entry" " = 11" "entry_stack: p s1@entry" gdb_test "p s2" " = 4" "entry_stack: p s2" gdb_test "p s2@entry" " = 12" "entry_stack: p s2@entry" +gdb_test "p d9" " = 3\\.5" "entry_stack: p d9" +gdb_test "p d9@entry" " = 11\\.5" "entry_stack: p d9@entry" +gdb_test "p da" " = 4\\.5" "entry_stack: p da" +gdb_test "p da@entry" " = 12\\.5" "entry_stack: p da@entry" + + +# Test various kinds of `set print entry-values'. + +gdb_continue_to_breakpoint "entry_equal: breakhere" + +gdb_test_no_output "set print entry-values no" "entry_equal: set print entry-values no" +gdb_test "frame" {\(val=5\).*} "entry_equal: frame: no" +gdb_test_no_output "set print entry-values only" "entry_equal: set print entry-values only" +gdb_test "frame" {\(val@entry=5\).*} "entry_equal: frame: only" +gdb_test_no_output "set print entry-values preferred" "entry_equal: set print entry-values preferred" +gdb_test "frame" {\(val@entry=5\).*} "entry_equal: frame: preferred" +gdb_test_no_output "set print entry-values if-needed" "entry_equal: set print entry-values if-needed" +gdb_test "frame" {\(val=5\).*} "entry_equal: frame: if-needed" +gdb_test_no_output "set print entry-values both" "entry_equal: set print entry-values both" +gdb_test "frame" {\(val=5, val@entry=5\).*} "entry_equal: frame: both" +gdb_test_no_output "set print entry-values compact" "entry_equal: set print entry-values compact" +gdb_test "frame" {\(val=val@entry=5\).*} "entry_equal: frame: compact" +gdb_test_no_output "set print entry-values default" "entry_equal: set print entry-values default" +gdb_test "frame" {\(val=val@entry=5\).*} "entry_equal: frame: default" + +gdb_continue_to_breakpoint "entry_different: breakhere" + +gdb_test_no_output "set print entry-values no" "entry_different: set print entry-values no" +gdb_test "frame" {\(val=6\).*} "entry_different: frame: no" +gdb_test_no_output "set print entry-values only" "entry_different: set print entry-values only" +gdb_test "frame" {\(val@entry=5\).*} "entry_different: frame: only" +gdb_test_no_output "set print entry-values preferred" "entry_different: set print entry-values preferred" +gdb_test "frame" {\(val@entry=5\).*} "entry_different: frame: preferred" +gdb_test_no_output "set print entry-values if-needed" "entry_different: set print entry-values if-needed" +gdb_test "frame" {\(val=6\).*} "entry_different: frame: if-needed" +gdb_test_no_output "set print entry-values both" "entry_different: set print entry-values both" +gdb_test "frame" {\(val=6, val@entry=5\).*} "entry_different: frame: both" +gdb_test_no_output "set print entry-values compact" "entry_different: set print entry-values compact" +gdb_test "frame" {\(val=6, val@entry=5\).*} "entry_different: frame: compact" +gdb_test_no_output "set print entry-values default" "entry_different: set print entry-values default" +gdb_test "frame" {\(val=6, val@entry=5\).*} "entry_different: frame: default" + +gdb_continue_to_breakpoint "entry_validity: breakhere" + +gdb_test_no_output "set print entry-values no" "entry_validity: set print entry-values no" +gdb_test "frame" {\(lost=, born=10\).*} "entry_validity: frame: no" +gdb_test_no_output "set print entry-values only" "entry_validity: set print entry-values only" +gdb_test "frame" {\(lost@entry=5, born@entry=\).*} "entry_validity: frame: only" +gdb_test_no_output "set print entry-values preferred" "entry_validity: set print entry-values preferred" +gdb_test "frame" {\(lost@entry=5, born=10\).*} "entry_validity: frame: preferred" +gdb_test_no_output "set print entry-values if-needed" "entry_validity: set print entry-values if-needed" +gdb_test "frame" {\(lost@entry=5, born=10\).*} "entry_validity: frame: if-needed" +gdb_test_no_output "set print entry-values both" "entry_validity: set print entry-values both" +gdb_test "frame" {\(lost=, lost@entry=5, born=10, born@entry=\).*} "entry_validity: frame: both" +gdb_test_no_output "set print entry-values compact" "entry_validity: set print entry-values compact" +gdb_test "frame" {\(lost@entry=5, born=10\).*} "entry_validity: frame: compact" +gdb_test_no_output "set print entry-values default" "entry_validity: set print entry-values default" +gdb_test "frame" {\(lost=, lost@entry=5, born=10\).*} "entry_validity: frame: default" + +gdb_continue_to_breakpoint "entry_invalid: breakhere" + +gdb_test_no_output "set print entry-values no" "entry_invalid: set print entry-values no" +gdb_test "frame" {\(inv=\).*} "entry_invalid: frame: no" +gdb_test_no_output "set print entry-values only" "entry_invalid: set print entry-values only" +gdb_test "frame" {\(inv@entry=\).*} "entry_invalid: frame: only" +gdb_test_no_output "set print entry-values preferred" "entry_invalid: set print entry-values preferred" +gdb_test "frame" {\(inv@entry=\).*} "entry_invalid: frame: preferred" +gdb_test_no_output "set print entry-values if-needed" "entry_invalid: set print entry-values if-needed" +gdb_test "frame" {\(inv=\).*} "entry_invalid: frame: if-needed" +gdb_test_no_output "set print entry-values both" "entry_invalid: set print entry-values both" +gdb_test "frame" {\(inv=, inv@entry=\).*} "entry_invalid: frame: both" +gdb_test_no_output "set print entry-values compact" "entry_invalid: set print entry-values compact" +gdb_test "frame" {\(inv=\).*} "entry_invalid: frame: compact" +gdb_test_no_output "set print entry-values default" "entry_invalid: set print entry-values default" +gdb_test "frame" {\(inv=\).*} "entry_invalid: frame: default" # Test @entry values for DW_AT_GNU_call_site_data_value parameters. gdb_continue_to_breakpoint "entry_reference: reference" -# (gdb) bt full -# #0 reference (refparam=@0x7fffffffdc3c: 5) at gdb.arch/amd64-entry-value.cc:131 -# refcopy = 5 -# #1 0x0000000000400424 in main () at gdb.arch/amd64-entry-value.cc:145 -# refvar = 5 -# Check refparam@entry is suppressed: -gdb_test "bt full" "#0 +reference \\(refparam=@0x\[0-9a-f\]+: 5\\) \[^\r\n\]*\r\n\[ \t\]*refcopy = 5\r\n#1 +0x\[0-9a-f\]+ in main \\(\\) \[^\r\n\]*\r\n\[ \t\]*refvar = 5" \ - "entry_reference: bt full at entry" +# GCC PR debug/49980: Missing stackparam1@entry and stackparam2@entry. +gdb_test "bt" "#0 +reference \\(regparam=regparam@entry=@0x\[0-9a-f\]+: 1, nodataparam=@0x\[0-9a-f\]+: 2, \[^\r\n\]+, stackparam1=@0x\[0-9a-f\]+: 11, stackparam2=@0x\[0-9a-f\]+: 12\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in main \\(\\) \[^\r\n\]*" \ + "entry_reference: bt at entry" gdb_continue_to_breakpoint "entry_reference: breakhere_reference" -# (gdb) bt full -# #0 reference (refparam=@0x7fffffffdc3c: 10) at gdb.arch/amd64-entry-value.cc:133 -# refparam@entry = @0x7fffffffdc3c: 5 -# refcopy = 5 -# #1 0x0000000000400424 in main () at gdb.arch/amd64-entry-value.cc:145 -# refvar = 10 -# Check refparam@entry is present: -gdb_test "bt full" "#0 +reference \\(refparam=@0x\[0-9a-f\]+: 10\\) \[^\r\n\]*\r\n\[ \t\]*refparam@entry = @0x\[0-9a-f\]+: 5\r\n\[ \t\]*refcopy = 5\r\n#1 +0x\[0-9a-f\]+ in main \\(\\) \[^\r\n\]*\r\n\[ \t\]*refvar = 10" \ - "entry_reference: bt full" -gdb_test "ptype refparam" " = int &" "entry_reference: ptype refparam" - -set test "entry_reference: p refparam" +# GCC PR debug/49980: Missing stackparam1@entry and stackparam2@entry. +gdb_test "bt" "#0 +reference \\(regparam=@0x\[0-9a-f\]+: 21, regparam@entry=@0x\[0-9a-f\]+: 1, nodataparam=@0x\[0-9a-f\]+: 22, \[^\r\n\]+, stackparam1=@0x\[0-9a-f\]+: 31, stackparam2=@0x\[0-9a-f\]+: 32\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in main \\(\\) \[^\r\n\]*" \ + "entry_reference: bt" +gdb_test "ptype regparam" " = int &" "entry_reference: ptype regparam" + +set test "entry_reference: p regparam" set addr "" -gdb_test_multiple "p refparam" $test { - -re " = \\(int &\\) @(0x\[0-9a-f\]+): 10\r\n$gdb_prompt $" { +gdb_test_multiple "p regparam" $test { + -re " = \\(int &\\) @(0x\[0-9a-f\]+): 21\r\n$gdb_prompt $" { set addr $expect_out(1,string) pass $test } } -gdb_test "ptype refparam@entry" " = int &" "entry_reference: ptype refparam@entry" -gdb_test "p refparam@entry" " = \\(int &\\) @$addr: 5" "entry_reference: p refparam@entry" -gdb_test "p &refparam@entry" " = \\(int \\*\\) $addr" "entry_reference: p &refparam@entry" -gdb_test "p refcopy" " = 5" "entry_reference: p refcopy" +gdb_test "ptype regparam@entry" " = int &" "entry_reference: ptype regparam@entry" +gdb_test "p regparam@entry" " = \\(int &\\) @$addr: 1" "entry_reference: p regparam@entry" +gdb_test "p ®param@entry" " = \\(int \\*\\) $addr" "entry_reference: p ®param@entry" +gdb_test "p regcopy" " = 1" "entry_reference: p regcopy" +gdb_test "p nodataparam" " = \\(int &\\) @0x\[0-9a-f\]+: 22" "entry_reference: p nodataparam" +gdb_test "p nodataparam@entry" "Cannot resolve DW_AT_GNU_call_site_data_value" "entry_reference: p nodataparam@entry" # Test virtual tail call frames. gdb_continue_to_breakpoint "tailcall: breakhere" -# #0 d (i=71) at gdb.arch/amd64-entry-value.cc:33 -# #1 0x0000000000400527 in c (i=7) at gdb.arch/amd64-entry-value.cc:38 -# #2 0x0000000000400545 in b (i=5) at gdb.arch/amd64-entry-value.cc:50 -# #3 0x00000000004003ee in main () at gdb.arch/amd64-entry-value.cc:60 -gdb_test "bt" "^bt\r\n#0 +d *\\(i=71\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in c \\(i=7\\) \[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in b \\(i=5\\) \[^\r\n\]*\r\n#3 +0x\[0-9a-f\]+ in main \[^\r\n\]*" \ +gdb_test "bt" "^bt\r\n#0 +d *\\(i=71, i@entry=70, j=73\\.5, j@entry=72\\.5\\) \[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in c \\(i=i@entry=7, j=j@entry=7\\.25\\) \[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in b \\(i=i@entry=5, j=j@entry=5\\.25\\) \[^\r\n\]*\r\n#3 +0x\[0-9a-f\]+ in main \[^\r\n\]*" \ "tailcall: bt" gdb_test "p i" " = 71" "tailcall: p i" gdb_test "p i@entry" " = 70" "tailcall: p i@entry" +gdb_test "p j" " = 73\\.5" "tailcall: p j" +gdb_test "p j@entry" " = 72\\.5" "tailcall: p j@entry" + +# Test $sp simulation for tail call frames. +#gdb_test {p/x $sp} " = 0x.*" +#gdb_test {p/x $pc} " = 0x.*" +gdb_test_no_output {set $sp0=$sp} +gdb_test "up" "\r\n#1 .*" +#gdb_test {p/x $sp} " = 0x.*" +gdb_test {p $sp0 == $sp} " = true" +gdb_test "frame 3" "\r\n#3 .*" +gdb_test {p $sp0 + sizeof (void *) == $sp} " = true" # Test partial-ambiguous virtual tail call frames chain. gdb_continue_to_breakpoint "ambiguous: breakhere" -# #0 d (i=) at gdb.arch/amd64-entry-value.cc:33 -# #1 0x0000000000400555 in amb_z (i=) at gdb.arch/amd64-entry-value.cc:56 -# #2 0x0000000000400565 in amb_y (i=) at gdb.arch/amd64-entry-value.cc:62 -# #3 0x0000000000400575 in amb_x (i=) at gdb.arch/amd64-entry-value.cc:68 -# --- here is missing a frame for ambiguous PC in amb (). -# #4 0x0000000000400595 in amb_b (i=101) at gdb.arch/amd64-entry-value.cc:83 -# #5 0x00000000004005a5 in amb_a (i=100) at gdb.arch/amd64-entry-value.cc:89 -# #6 0x00000000004003f8 in main () at gdb.arch/amd64-entry-value.cc:100 -gdb_test "bt" "^bt\r\n#0 +d \\(i=\\)\[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in amb_z \\(i=\\)\[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in amb_y \\(i=\\)\[^\r\n\]*\r\n#3 +0x\[0-9a-f\]+ in amb_x \\(i=\\)\[^\r\n\]*\r\n#4 +0x\[0-9a-f\]+ in amb_b \\(i=101\\)\[^\r\n\]*\r\n#5 +0x\[0-9a-f\]+ in amb_a \\(i=100\\)\[^\r\n\]*\r\n#6 +0x\[0-9a-f\]+ in main \\(\\)\[^\r\n\]*" \ +gdb_test "bt" "^bt\r\n#0 +d \\(i=, j=\\)\[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in amb_z \\(i=\\)\[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in amb_y \\(i=\\)\[^\r\n\]*\r\n#3 +0x\[0-9a-f\]+ in amb_x \\(i=\\)\[^\r\n\]*\r\n#4 +0x\[0-9a-f\]+ in amb_b \\(i=i@entry=101\\)\[^\r\n\]*\r\n#5 +0x\[0-9a-f\]+ in amb_a \\(i=i@entry=100\\)\[^\r\n\]*\r\n#6 +0x\[0-9a-f\]+ in main \\(\\)\[^\r\n\]*" \ "ambiguous: bt" @@ -159,10 +227,7 @@ gdb_test "bt" "^bt\r\n#0 +d \\(i=\\)\[^\r\n\]*\r\n#1 +0x\[0-9a-f\ gdb_continue_to_breakpoint "self: breakhere" -# #0 d (i=) at gdb.arch/amd64-entry-value.cc:33 -# #1 0x00000000004005df in self (i=) at gdb.arch/amd64-entry-value.cc:111 -# #2 0x0000000000400406 in main () at gdb.arch/amd64-entry-value.cc:124 -gdb_test "bt" "^bt\r\n#0 +d \\(i=\\)\[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in self \\(i=\\)\[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in main \\(\\)\[^\r\n\]*" \ +gdb_test "bt" "^bt\r\n#0 +d \\(i=, j=\\)\[^\r\n\]*\r\n#1 +0x\[0-9a-f\]+ in self \\(i=\\)\[^\r\n\]*\r\n#2 +0x\[0-9a-f\]+ in main \\(\\)\[^\r\n\]*" \ "self: bt" gdb_test_no_output "set verbose on" --- a/gdb/testsuite/gdb.base/break.exp +++ b/gdb/testsuite/gdb.base/break.exp @@ -916,13 +916,13 @@ set bp_location14 [gdb_get_line_number "set breakpoint 14 here" $srcfile1] gdb_test_multiple "continue" \ "run until breakpoint set at small function, optimized file" { - -re "Breakpoint $decimal, marker4 \\(d=177601976\\) at .*$srcfile1:$bp_location13\[\r\n\]+$bp_location13\[\t \]+void marker4.*" { + -re "Breakpoint $decimal, marker4 \\(d=(d@entry=)?177601976\\) at .*$srcfile1:$bp_location13\[\r\n\]+$bp_location13\[\t \]+void marker4.*" { pass "run until breakpoint set at small function, optimized file" } - -re "Breakpoint $decimal, $hex in marker4 \\(d=177601976\\) at .*$srcfile1:$bp_location13\[\r\n\]+$bp_location13\[\t \]+void marker4.*" { + -re "Breakpoint $decimal, $hex in marker4 \\(d=(d@entry=)?177601976\\) at .*$srcfile1:$bp_location13\[\r\n\]+$bp_location13\[\t \]+void marker4.*" { pass "run until breakpoint set at small function, optimized file" } - -re "Breakpoint $decimal, marker4 \\(d=177601976\\) at .*$srcfile1:$bp_location14\[\r\n\]+$bp_location14\[\t \]+void marker4.*" { + -re "Breakpoint $decimal, marker4 \\(d=(d@entry=)?177601976\\) at .*$srcfile1:$bp_location14\[\r\n\]+$bp_location14\[\t \]+void marker4.*" { # marker4() is defined at line 46 when compiled with -DPROTOTYPES pass "run until breakpoint set at small function, optimized file (line bp_location14)" } --- a/gdb/testsuite/gdb.mi/Makefile.in +++ b/gdb/testsuite/gdb.mi/Makefile.in @@ -9,7 +9,8 @@ PROGS = basics c_variable cpp_variable var-cmd dw2-ref-missing-frame \ mi-pending mi-pthreads mi-read-memory mi-regs mi-return \ mi-reverse mi-simplerun mi-stack mi-stepi mi-syn-frame \ mi-var-block mi-var-child mi-var-cmd mi-var-cp mi-var-display \ - mi-var-invalidate mi-var-invalidate_bis mi-watch mi2-basics \ + mi-var-invalidate mi-var-invalidate_bis mi-watch \ + mi2-amd64-entry-value mi2-basics \ mi2-break mi2-cli mi2-disassemble mi2-eval mi2-file \ mi2-pthreads mi2-regs mi2-return mi2-simplerun mi2-stepi \ mi2-var-block mi2-var-child mi2-var-cmd mi2-var-display \ --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi2-amd64-entry-value.c @@ -0,0 +1,70 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2011 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 . */ + +static volatile int v; + +static void __attribute__((noinline, noclone)) +e (int i, double j) +{ + v = 0; +} + +static int __attribute__((noinline, noclone)) +data (void) +{ + return 10; +} + +static int __attribute__((noinline, noclone)) +data2 (void) +{ + return 20; +} + +static int __attribute__((noinline, noclone)) +different (int val) +{ + val++; + e (val, val); +asm ("breakhere_different:"); + return val; +} + +static int __attribute__((noinline, noclone)) +validity (int lost, int born) +{ + lost = data (); + e (0, 0.0); +asm ("breakhere_validity:"); + return born; +} + +static void __attribute__((noinline, noclone)) +invalid (int inv) +{ + e (0, 0.0); +asm ("breakhere_invalid:"); +} + +int +main () +{ + different (5); + validity (5, data ()); + invalid (data2 ()); + return 0; +} --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi2-amd64-entry-value.exp @@ -0,0 +1,171 @@ +# Copyright (C) 2011 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 . + +load_lib mi-support.exp +set MIFLAGS "-i=mi2" + +gdb_exit +if [mi_gdb_start] { + continue +} + +set testfile mi2-amd64-entry-value +set srcfile ${testfile}.s +set opts {} + +if [info exists COMPILE] { + # make check RUNTESTFLAGS="gdb.mi/mi2-amd64-entry-value.exp COMPILE=1" + set srcfile ${testfile}.c + lappend opts debug optimize=-O2 +} elseif { ![istarget x86_64-*-* ] || ![is_lp64_target] } { + verbose "Skipping amd64-entry-value." + return +} + +set executable ${testfile} +set binfile ${objdir}/${subdir}/${executable} + +if [build_executable ${testfile}.exp ${executable} ${srcfile} $opts] { + return -1 +} + +mi_gdb_reinitialize_dir $srcdir/$subdir +mi_gdb_load ${binfile} + +foreach name {different breakhere_different breakhere_validity breakhere_invalid} { + mi_create_breakpoint $name .* .* .* .* .* .* "break $name" +} + + +# Test various kinds of `set print entry-values'. + +if {[mi_runto main] == -1} { + return -1 +} +mi_gdb_test "-gdb-set print entry-values no" {\^done} "no: set print entry-values" +mi_send_resuming_command "exec-continue" "no: entry_equal: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="5"}} .* .* {.* disp="keep"} "no: entry_equal: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="5"}\]} "no: entry_equal: -stack-list-variables" +mi_send_resuming_command "exec-continue" "no: entry_different: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="6"}} .* .* {.* disp="keep"} "no: entry_different: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="6"}\]} "no: entry_different: -stack-list-variables" +mi_send_resuming_command "exec-continue" "no: validity: continue" +mi_expect_stop "breakpoint-hit" .* {{name="lost",value=""},{name="born",value="10"}} .* .* {.* disp="keep"} "no: validity: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="lost",arg="1",value=""},{name="born",arg="1",value="10"}\]} "no: validity: -stack-list-variables" +mi_send_resuming_command "exec-continue" "no: invalid: continue" +mi_expect_stop "breakpoint-hit" .* {{name="inv",value=""}} .* .* {.* disp="keep"} "no: invalid: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="inv",arg="1",value=""}\]} "no: invalid: -stack-list-variables" + +if {[mi_runto main] == -1} { + return -1 +} +mi_gdb_test "-gdb-set print entry-values only" {\^done} "only: set print entry-values" +mi_send_resuming_command "exec-continue" "only: entry_equal: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val@entry",value="5"}} .* .* {.* disp="keep"} "only: entry_equal: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val@entry",arg="1",value="5"}\]} "only: entry_equal: -stack-list-variables" +mi_send_resuming_command "exec-continue" "only: entry_different: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val@entry",value="5"}} .* .* {.* disp="keep"} "only: entry_different: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val@entry",arg="1",value="5"}\]} "only: entry_different: -stack-list-variables" +mi_send_resuming_command "exec-continue" "only: validity: continue" +mi_expect_stop "breakpoint-hit" .* {{name="lost@entry",value="5"},{name="born@entry",value=""}} .* .* {.* disp="keep"} "only: validity: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="lost@entry",arg="1",value="5"},{name="born@entry",arg="1",value=""}\]} "only: validity: -stack-list-variables" +mi_send_resuming_command "exec-continue" "only: invalid: continue" +mi_expect_stop "breakpoint-hit" .* {{name="inv@entry",value=""}} .* .* {.* disp="keep"} "only: invalid: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="inv@entry",arg="1",value=""}\]} "only: invalid: -stack-list-variables" + +if {[mi_runto main] == -1} { + return -1 +} +mi_gdb_test "-gdb-set print entry-values preferred" {\^done} "preferred: set print entry-values" +mi_send_resuming_command "exec-continue" "preferred: entry_equal: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val@entry",value="5"}} .* .* {.* disp="keep"} "preferred: entry_equal: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val@entry",arg="1",value="5"}\]} "preferred: entry_equal: -stack-list-variables" +mi_send_resuming_command "exec-continue" "preferred: entry_different: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val@entry",value="5"}} .* .* {.* disp="keep"} "preferred: entry_different: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val@entry",arg="1",value="5"}\]} "preferred: entry_different: -stack-list-variables" +mi_send_resuming_command "exec-continue" "preferred: validity: continue" +mi_expect_stop "breakpoint-hit" .* {{name="lost@entry",value="5"},{name="born",value="10"}} .* .* {.* disp="keep"} "preferred: validity: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="lost@entry",arg="1",value="5"},{name="born",arg="1",value="10"}\]} "preferred: validity: -stack-list-variables" +mi_send_resuming_command "exec-continue" "preferred: invalid: continue" +mi_expect_stop "breakpoint-hit" .* {{name="inv@entry",value=""}} .* .* {.* disp="keep"} "preferred: invalid: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="inv@entry",arg="1",value=""}\]} "preferred: invalid: -stack-list-variables" + +if {[mi_runto main] == -1} { + return -1 +} +mi_gdb_test "-gdb-set print entry-values if-needed" {\^done} "if-needed: set print entry-values" +mi_send_resuming_command "exec-continue" "if-needed: entry_equal: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="5"}} .* .* {.* disp="keep"} "if-needed: entry_equal: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="5"}\]} "if-needed: entry_equal: -stack-list-variables" +mi_send_resuming_command "exec-continue" "if-needed: entry_different: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="6"}} .* .* {.* disp="keep"} "if-needed: entry_different: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="6"}\]} "if-needed: entry_different: -stack-list-variables" +mi_send_resuming_command "exec-continue" "if-needed: validity: continue" +mi_expect_stop "breakpoint-hit" .* {{name="lost@entry",value="5"},{name="born",value="10"}} .* .* {.* disp="keep"} "if-needed: validity: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="lost@entry",arg="1",value="5"},{name="born",arg="1",value="10"}\]} "if-needed: validity: -stack-list-variables" +mi_send_resuming_command "exec-continue" "if-needed: invalid: continue" +mi_expect_stop "breakpoint-hit" .* {{name="inv",value=""}} .* .* {.* disp="keep"} "if-needed: invalid: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="inv",arg="1",value=""}\]} "if-needed: invalid: -stack-list-variables" + +if {[mi_runto main] == -1} { + return -1 +} +mi_gdb_test "-gdb-set print entry-values both" {\^done} "both: set print entry-values" +mi_send_resuming_command "exec-continue" "both: entry_equal: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="5"},{name="val@entry",value="5"}} .* .* {.* disp="keep"} "both: entry_equal: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="5"},{name="val@entry",arg="1",value="5"}\]} "both: entry_equal: -stack-list-variables" +mi_send_resuming_command "exec-continue" "both: entry_different: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="6"},{name="val@entry",value="5"}} .* .* {.* disp="keep"} "both: entry_different: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="6"},{name="val@entry",arg="1",value="5"}\]} "both: entry_different: -stack-list-variables" +mi_send_resuming_command "exec-continue" "both: validity: continue" +mi_expect_stop "breakpoint-hit" .* {{name="lost",value=""},{name="lost@entry",value="5"},{name="born",value="10"},{name="born@entry",value=""}} .* .* {.* disp="keep"} "both: validity: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="lost",arg="1",value=""},{name="lost@entry",arg="1",value="5"},{name="born",arg="1",value="10"},{name="born@entry",arg="1",value=""}\]} "both: validity: -stack-list-variables" +mi_send_resuming_command "exec-continue" "both: invalid: continue" +mi_expect_stop "breakpoint-hit" .* {{name="inv",value=""},{name="inv@entry",value=""}} .* .* {.* disp="keep"} "both: invalid: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="inv",arg="1",value=""},{name="inv@entry",arg="1",value=""}\]} "both: invalid: -stack-list-variables" + +if {[mi_runto main] == -1} { + return -1 +} +mi_gdb_test "-gdb-set print entry-values compact" {\^done} "compact: set print entry-values" +mi_send_resuming_command "exec-continue" "compact: entry_equal: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="5"},{name="val@entry",value="5"}} .* .* {.* disp="keep"} "compact: entry_equal: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="5"},{name="val@entry",arg="1",value="5"}\]} "compact: entry_equal: -stack-list-variables" +mi_send_resuming_command "exec-continue" "compact: entry_different: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="6"},{name="val@entry",value="5"}} .* .* {.* disp="keep"} "compact: entry_different: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="6"},{name="val@entry",arg="1",value="5"}\]} "compact: entry_different: -stack-list-variables" +mi_send_resuming_command "exec-continue" "compact: validity: continue" +mi_expect_stop "breakpoint-hit" .* {{name="lost@entry",value="5"},{name="born",value="10"}} .* .* {.* disp="keep"} "compact: validity: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="lost@entry",arg="1",value="5"},{name="born",arg="1",value="10"}\]} "compact: validity: -stack-list-variables" +mi_send_resuming_command "exec-continue" "compact: invalid: continue" +mi_expect_stop "breakpoint-hit" .* {{name="inv",value=""}} .* .* {.* disp="keep"} "compact: invalid: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="inv",arg="1",value=""}\]} "compact: invalid: -stack-list-variables" + +if {[mi_runto main] == -1} { + return -1 +} +mi_gdb_test "-gdb-set print entry-values default" {\^done} "default: set print entry-values" +mi_send_resuming_command "exec-continue" "default: entry_equal: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="5"},{name="val@entry",value="5"}} .* .* {.* disp="keep"} "default: entry_equal: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="5"},{name="val@entry",arg="1",value="5"}\]} "default: entry_equal: -stack-list-variables" +mi_send_resuming_command "exec-continue" "default: entry_different: continue" +mi_expect_stop "breakpoint-hit" .* {{name="val",value="6"},{name="val@entry",value="5"}} .* .* {.* disp="keep"} "default: entry_different: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="val",arg="1",value="6"},{name="val@entry",arg="1",value="5"}\]} "default: entry_different: -stack-list-variables" +mi_send_resuming_command "exec-continue" "default: validity: continue" +mi_expect_stop "breakpoint-hit" .* {{name="lost",value=""},{name="lost@entry",value="5"},{name="born",value="10"}} .* .* {.* disp="keep"} "default: validity: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="lost",arg="1",value=""},{name="lost@entry",arg="1",value="5"},{name="born",arg="1",value="10"}\]} "default: validity: -stack-list-variables" +mi_send_resuming_command "exec-continue" "default: invalid: continue" +mi_expect_stop "breakpoint-hit" .* {{name="inv",value=""}} .* .* {.* disp="keep"} "default: invalid: stop" +mi_gdb_test "-stack-list-variables --all-values" {\^done,variables=\[{name="inv",arg="1",value=""}\]} "default: invalid: -stack-list-variables" --- a/gdb/valops.c +++ b/gdb/valops.c @@ -1740,22 +1740,22 @@ value_ind (struct value *arg1) base_type = check_typedef (value_type (arg1)); - if (TYPE_CODE (base_type) == TYPE_CODE_PTR) + if (VALUE_LVAL (arg1) == lval_computed) { - struct type *enc_type; + const struct lval_funcs *funcs = value_computed_funcs (arg1); - if (VALUE_LVAL (arg1) == lval_computed) + if (funcs->indirect) { - const struct lval_funcs *funcs = value_computed_funcs (arg1); + struct value *result = funcs->indirect (arg1); - if (funcs->indirect) - { - struct value *result = funcs->indirect (arg1); - - if (result) - return result; - } + if (result) + return result; } + } + + if (TYPE_CODE (base_type) == TYPE_CODE_PTR) + { + struct type *enc_type; /* We may be pointing to something embedded in a larger object. Get the real type of the enclosing object. */ --- a/gdb/valprint.c +++ b/gdb/valprint.c @@ -340,7 +340,7 @@ val_print_invalid_address (struct ui_file *stream) int val_print (struct type *type, const gdb_byte *valaddr, int embedded_offset, CORE_ADDR address, struct ui_file *stream, int recurse, - struct value *val, + const struct value *val, const struct value_print_options *options, const struct language_defn *language) { @@ -555,7 +555,7 @@ val_print_type_code_flags (struct type *type, const gdb_byte *valaddr, void val_print_scalar_formatted (struct type *type, const gdb_byte *valaddr, int embedded_offset, - struct value *val, + const struct value *val, const struct value_print_options *options, int size, struct ui_file *stream) @@ -1189,7 +1189,8 @@ void val_print_array_elements (struct type *type, const gdb_byte *valaddr, int embedded_offset, CORE_ADDR address, struct ui_file *stream, - int recurse, struct value *val, + int recurse, + const struct value *val, const struct value_print_options *options, unsigned int i) { --- a/gdb/valprint.h +++ b/gdb/valprint.h @@ -115,7 +115,7 @@ extern void maybe_print_array_index (struct type *index_type, LONGEST index, extern void val_print_array_elements (struct type *, const gdb_byte *, int, CORE_ADDR, struct ui_file *, int, - struct value *, + const struct value *, const struct value_print_options *, unsigned int); @@ -127,7 +127,8 @@ extern void val_print_type_code_flags (struct type *type, struct ui_file *stream); extern void val_print_scalar_formatted (struct type *, - const gdb_byte *, int, struct value *, + const gdb_byte *, int, + const struct value *, const struct value_print_options *, int, struct ui_file *); --- a/gdb/value.c +++ b/gdb/value.c @@ -1063,9 +1063,9 @@ set_value_pointed_to_offset (struct value *value, int val) } const struct lval_funcs * -value_computed_funcs (struct value *v) +value_computed_funcs (const struct value *v) { - gdb_assert (VALUE_LVAL (v) == lval_computed); + gdb_assert (value_lval_const (v) == lval_computed); return v->location.computed.funcs; } @@ -1084,6 +1084,12 @@ deprecated_value_lval_hack (struct value *value) return &value->lval; } +enum lval_type +value_lval_const (const struct value *value) +{ + return value->lval; +} + CORE_ADDR value_address (const struct value *value) { @@ -3083,26 +3089,36 @@ value_from_history_ref (char *h, char **endp) } struct value * +coerce_ref_if_computed (const struct value *arg) +{ + const struct lval_funcs *funcs; + + if (TYPE_CODE (check_typedef (value_type (arg))) != TYPE_CODE_REF) + return NULL; + + if (value_lval_const (arg) != lval_computed) + return NULL; + + funcs = value_computed_funcs (arg); + if (funcs->coerce_ref == NULL) + return NULL; + + return funcs->coerce_ref (arg); +} + +struct value * coerce_ref (struct value *arg) { struct type *value_type_arg_tmp = check_typedef (value_type (arg)); + struct value *retval; + + retval = coerce_ref_if_computed (arg); + if (retval) + return retval; if (TYPE_CODE (value_type_arg_tmp) != TYPE_CODE_REF) return arg; - if (VALUE_LVAL (arg) == lval_computed) - { - const struct lval_funcs *funcs = value_computed_funcs (arg); - - if (funcs->indirect) - { - struct value *result = funcs->indirect (arg); - - if (result) - return result; - } - } - return value_at_lazy (TYPE_TARGET_TYPE (value_type_arg_tmp), unpack_pointer (value_type (arg), value_contents (arg))); --- a/gdb/value.h +++ b/gdb/value.h @@ -175,14 +175,16 @@ struct lval_funcs /* Return 1 if any bit in VALUE is valid, 0 if they are all invalid. */ int (*check_any_valid) (const struct value *value); - /* If non-NULL, this is used to implement pointer and/or reference - indirection for this value. This method may return NULL, in which case - value_ind will fall back to ordinary indirection. - - TYPE_CODE (check_typedef (value)) specifies which operation should be - done. It is always either TYPE_CODE_PTR or TYPE_CODE_REF. */ + /* If non-NULL, this is used to implement pointer indirection for + this value. This method may return NULL, in which case value_ind + will fall back to ordinary indirection. */ struct value *(*indirect) (struct value *value); + /* If non-NULL, this is used to implement reference resolving for + this value. This method may return NULL, in which case coerce_ref + will fall back to ordinary references resolving. */ + struct value *(*coerce_ref) (const struct value *value); + /* If non-NULL, this is used to determine whether the indicated bits of VALUE are a synthetic pointer. */ int (*check_synthetic_pointer) (const struct value *value, @@ -216,7 +218,7 @@ extern struct value *allocate_optimized_out_value (struct type *type); /* If VALUE is lval_computed, return its lval_funcs structure. */ -extern const struct lval_funcs *value_computed_funcs (struct value *value); +extern const struct lval_funcs *value_computed_funcs (const struct value *); /* If VALUE is lval_computed, return its closure. The meaning of the returned value depends on the functions VALUE uses. */ @@ -317,6 +319,9 @@ extern void set_value_component_location (struct value *component, extern enum lval_type *deprecated_value_lval_hack (struct value *); #define VALUE_LVAL(val) (*deprecated_value_lval_hack (val)) +/* Like VALUE_LVAL, except the parameter can be const. */ +extern enum lval_type value_lval_const (const struct value *value); + /* If lval == lval_memory, return the address in the inferior. If lval == lval_register, return the byte offset into the registers structure. Otherwise, return 0. The returned address @@ -343,6 +348,11 @@ extern struct frame_id *deprecated_value_frame_id_hack (struct value *); extern short *deprecated_value_regnum_hack (struct value *); #define VALUE_REGNUM(val) (*deprecated_value_regnum_hack (val)) +/* Return value after lval_funcs->coerce_ref (after check_typedef). Return + NULL if lval_funcs->coerce_ref is not applicable for whatever reason. */ + +extern struct value *coerce_ref_if_computed (const struct value *arg); + /* Convert a REF to the object referenced. */ extern struct value *coerce_ref (struct value *value); @@ -793,7 +803,7 @@ extern struct value *value_release_to_mark (struct value *mark); extern int val_print (struct type *type, const gdb_byte *valaddr, int embedded_offset, CORE_ADDR address, struct ui_file *stream, int recurse, - struct value *val, + const struct value *val, const struct value_print_options *options, const struct language_defn *language); @@ -807,28 +817,11 @@ extern int val_print_string (struct type *elttype, const char *encoding, struct ui_file *stream, const struct value_print_options *options); -/* Specify how the @entry kind of function parameters should be printed. */ -enum print_argument -{ - /* Symbol is not a function parameter - it is a variable. */ - PVAVD_IS_NOT_ARGUMENT, - - /* Symbol is a function parameter, print only its @entry value if it is not - redundant together with the normal symbol printed value. */ - PVAVD_ARGUMENT_PRINT_ENTRYVAL_ONLY, - - /* Symbol is a function parameter, print its normal value. Print also its - @entry value if it is not redundant together with its normal printed - value. */ - PVAVD_ARGUMENT_PRINT_BOTH -}; - extern void print_variable_and_value (const char *name, struct symbol *var, struct frame_info *frame, struct ui_file *stream, - int indent, - enum print_argument print_argument); + int indent); extern int check_field (struct type *, const char *);