From patchwork Mon Apr 21 01:35:01 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Smundak X-Patchwork-Id: 629 Return-Path: X-Original-To: siddhesh@wilcox.dreamhost.com Delivered-To: siddhesh@wilcox.dreamhost.com Received: from homiemail-mx20.g.dreamhost.com (mx2.sub5.homie.mail.dreamhost.com [208.113.200.128]) by wilcox.dreamhost.com (Postfix) with ESMTP id F0197360078 for ; Sun, 20 Apr 2014 18:35:11 -0700 (PDT) Received: by homiemail-mx20.g.dreamhost.com (Postfix, from userid 14314964) id A33EB411DF8B8; Sun, 20 Apr 2014 18:35:11 -0700 (PDT) X-Original-To: gdb@patchwork.siddhesh.in Delivered-To: x14314964@homiemail-mx20.g.dreamhost.com Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by homiemail-mx20.g.dreamhost.com (Postfix) with ESMTPS id 7D5CD411DF890 for ; Sun, 20 Apr 2014 18:35:11 -0700 (PDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:mime-version:in-reply-to:references:date :message-id:subject:from:to:cc:content-type; q=dns; s=default; b= s2YfSRFvCzg4vI020iNxdGGEy6+1nWlCrYqFEB2MbsojkXvt7DScsWv1Cuq24h4S 5lcEmZsEaxdCGHCZfOhNF0Rl9kaOogVQtHRWhElQQ5igUdGJnxv4jZ8mKNhXuJbY KblCF5+i+HCNWYBY9ruEPQHcu6hmRifa67n2H/0+VIY= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:mime-version:in-reply-to:references:date :message-id:subject:from:to:cc:content-type; s=default; bh=Kyyne vZZUr+tJsq+60Wi8ALTu5g=; b=ofx2M8tyUT5zPzvUA9iGERlJDGNF001oERFcL qbmEtSCwjVR53VtAbQIzimRvraWz6sOr90FcsIDdFAALuewa/La84X70HYorVw/G CvvvMtAUXhh7ufhnB4lhrZujMh8+0pimyhoyXnos97z3+ByH4ySWmlRtCAmrig2h XGB+DI= Received: (qmail 28122 invoked by alias); 21 Apr 2014 01:35:08 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 28110 invoked by uid 89); 21 Apr 2014 01:35:07 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-0.8 required=5.0 tests=AWL, BAYES_20, KAM_STOCKGEN, RCVD_IN_DNSWL_LOW, RP_MATCHES_RCVD, SPF_PASS autolearn=no version=3.3.2 X-HELO: mail-oa0-f42.google.com Received: from mail-oa0-f42.google.com (HELO mail-oa0-f42.google.com) (209.85.219.42) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-SHA encrypted) ESMTPS; Mon, 21 Apr 2014 01:35:03 +0000 Received: by mail-oa0-f42.google.com with SMTP id i4so3721821oah.1 for ; Sun, 20 Apr 2014 18:35:01 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:date :message-id:subject:from:to:cc:content-type; bh=qPVikEDV8AWVaEAVoCTWWvw367eeAIMQatrUFVhxWJw=; b=AE/j+erNwGcBNtvoUTx25jYSslVCahOhBDcyLkMzEH/RLjsPR071tfqbM4JQwG7U55 B9eGg7GDdzk8RK7GL2sYi6LVssDkERn9Hvs/ICTSYDpYhwbnmmQksVGHem9NNZyVb67V ENzCaVDiuygeoIOWx3+MHmuisnwjFQGATb48BxBwooY62UlwJ77edGbtRjso3HMJ+s/q n1jA0EKQ/e474GB6WuCOGNCjliP+Xz6WAoo3VyxRhxGFjpPc9+5s9CC6VTu+gjd0jKri uG9FuRdf7p020UM6KUh3dlz1z5jrXEXlRCNxHU74NnaY5rENfFx1y3imkcdwM7Ewe5q9 sf3Q== X-Gm-Message-State: ALoCoQnwS7VAM0SMaBAsPbZZpcHH2ETprq3PxqBHsGCT7hAzqZOQOD+BLdYuAM0cqk2KC9x8fAzoFftZEgglq5aO3cQvEf46cPgGF8tFWu8AWWmUo5J/ZpUx8mr+o6MUM7/AHikbCmhpbAfOErPB7MRTpqzixvjCib2giUf3Y6w4fUXYTeOnsb89WC6dpR5wnIkI81KLuHzTHONnG85fA63U3/0NCRkTTw== MIME-Version: 1.0 X-Received: by 10.182.236.162 with SMTP id uv2mr25159654obc.40.1398044101737; Sun, 20 Apr 2014 18:35:01 -0700 (PDT) Received: by 10.182.84.197 with HTTP; Sun, 20 Apr 2014 18:35:01 -0700 (PDT) In-Reply-To: <21320.15195.857721.50656@ruffy.mtv.corp.google.com> References: <20131226183618.D264CA18A0@sasha2.mtv.corp.google.com> <21204.13416.607204.485255@ruffy.mtv.corp.google.com> <21320.15195.857721.50656@ruffy.mtv.corp.google.com> Date: Sun, 20 Apr 2014 18:35:01 -0700 Message-ID: Subject: Re: [RFC][PATCH] Allow JIT unwinder provide symbol information From: Alexander Smundak To: Doug Evans Cc: gdb-patches X-DH-Original-To: gdb@patchwork.siddhesh.in I've reshuffled the structs to make them v1-compatible, and added two tests, one to verify that old JIT readers still work, and the other for the current JIT reader. The revised patch is below. 2013-12-19 Sasha Smundak * gdb/NEWS: Announcement. * gdb/frame-unwind.h (frame_symbol_type): New function type. (struct frame_unwind): Add the pointer to the frame_symbol_type. * gdb/frame.c (get_frame_symbol_info): New function. * gdb/frame.h (struct frame_symbol_info): Declare the struct containing symbol information returned by the unwinder. (get_frame_symbol_info): Declare it. * gdb/jit-reader.in (GDB_READER_INTERFACE_VERSION): New version. (gdb_unwind_stash): New function type. (gdb_find_symbol): Ditto. (gdb_get_jtid): Ditto. (gdb_enumerate_shared): Ditto. (gdb_unwind_debug_flag): Ditto. (gdb_architecture_name): Ditto. (gdb_pointer_size): Ditto. (struct gdb_unwind_callbacks): Add members pointing to gdb_unwind_stash, unwind_reg_get, gdb_find_symbol, gdb_get_jtid, gdb_enumerate_shared, gdb_unwind_debug_flag, gdb_architecture_name and gdb_pointer_size functions. (enum jit_frame_symbol_attr): New enum. (enum jit_frame_language): Ditto. (gdb_get_symbol_attr): New function type. (struct gdb_reader_funcs): Add member pointing to gdb_get_symbol_attr function. * gdb/jit.c: Include arch-utils.h. Include solist.h. Include ptid.h. (jit_prepend_unwinder): Declare. (jit_find_symbol_address): Declare. (jit_get_current_jtid): Declare. (jit_enumerate_shared): Declare. (jit_unwind_debug_flag): Declare. (jit_unwind_architecture_name): Declare. (jit_unwind_pointer_size): Declare. (jit_unwind_debug): Declare. (show_jit_unwind_debug): New function. (jit_breakpoint_re_set_internal): Add the call to jit_prepend_unwinder. (struct jit_unwind_private): Add a member containing the pointer to JIT reader's private area. (jit_get_register_from_frame): New function returning a register from the given frame (refactored from jit_unwind_reg_get_impl). (jit_unwind_reg_get_impl): Now a wrapper around jit_get_register_from_frame. (jit_unwind_get_cpu_register_value): New function to get "live" registers. (jit_stash_impl): Allocate private data area for the JIT reader. (jit_dealloc_cache): Free JIT reader private area. (jit_set_unwind_callbacks_vtable): New function, refactored from two different places where callbacks were set up. (jit_frame_sniffer): Use jit_set_unwind_callbacks_vtable. (jit_frame_language_to_language): New function converting JIT reader's language type to the GDB internal language type. (jit_symbol): New function returning symbol information supplied by the JIT reader. (jit_frame_unwind): Add the pointer to jit_symbol. (jit_find_symbol_address): New GDB callback for the JIT reader returning symbols's address. (jit_get_current_jtid): New GDB callback for the JIT reader returning the current thread ID. (jit_enumerate_shared): New GDB callback for the JIT reader to traverse inferior's shared objects. (jit_unwind_debug_flag): New GDB callback returning JIT unwind debugging flags. (jit_unwind_architecture_name): New GDB callback returning inferior's architecture name. (jit_unwind_pointer_size): New GDB callback returning inferior's pointer size. (_initialize_jit): Register 'show/set debug jitunwind' subcommand. * gdb/stack.c (find_frame_funname): If symbol information from the unwindeer if available. (find_frame_source_location): New function to return symbol source location from the unwinder if available. (print_frame): Use arguments information and source location from the unwinder if available. * gdb/testsuite/gdb.arch/amd64-jit-reader-v1.c: JIT reader that implements JIT reader ABI v1. * gdb/testsuite/gdb.arch/amd64-jit-reader-v1.exp: verifies that the current JIT reader framework is backwards compatible. * gdb/testsuite/gdb.arch/amd64-jit-reader.c: JIT reader that implements the current JIT reader ABI. * gdb/testsuite/gdb.arch/amd64-jit-reader.exp: verifies current JIT reader framework functionality. * gdb/testsuite/gdb.arch/amd64-jit-unwind.c: an application to test JIT reader unwinding. On Fri, Apr 11, 2014 at 11:58 AM, Doug Evans wrote: > Alexander Smundak writes: > >[...] > > diff --git a/gdb/jit-reader.in b/gdb/jit-reader.in > > index 6e2ee64..a6a0ab1 100644 > > --- a/gdb/jit-reader.in > > +++ b/gdb/jit-reader.in > > @@ -270,7 +307,14 @@ struct gdb_unwind_callbacks > > gdb_unwind_reg_get *reg_get; > > gdb_unwind_reg_set *reg_set; > > gdb_target_read *target_read; > > - > > + gdb_unwind_stash *stash; > > + gdb_unwind_reg_get *cpu_reg_get; > > + gdb_find_symbol *find_symbol; > > + gdb_get_jtid *get_jtid; > > + gdb_enumerate_shared *enumerate_shared; > > + gdb_unwind_debug_flag *debug_flag; > > + gdb_architecture_name *architecture_name; > > + gdb_pointer_size *pointer_size; > > /* For internal use by GDB. */ > > void *priv_data; > > }; > > Oops, missed this one. > > Move the new entries to the end. > > @@ -270,7 +307,14 @@ struct gdb_unwind_callbacks > gdb_unwind_reg_get *reg_get; > gdb_unwind_reg_set *reg_set; > gdb_target_read *target_read; > > /* For internal use by GDB. */ > void *priv_data; > + > + /* New entries for version 2 interface. */ > + gdb_unwind_stash *stash; > + gdb_unwind_reg_get *cpu_reg_get; > + gdb_find_symbol *find_symbol; > + gdb_get_jtid *get_jtid; > + gdb_enumerate_shared *enumerate_shared; > + gdb_unwind_debug_flag *debug_flag; > + gdb_architecture_name *architecture_name; > + gdb_pointer_size *pointer_size; > }; diff --git a/gdb/NEWS b/gdb/NEWS index df233fc..df0f730 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -3,6 +3,9 @@ *** Changes since GDB 7.7 +* Revised JIT interface allows creating plugins that display + JIT_specific symbol information. + * Guile scripting GDB now has support for scripting using Guile. Whether this is diff --git a/gdb/frame-unwind.h b/gdb/frame-unwind.h index 9507be2..407b882 100644 --- a/gdb/frame-unwind.h +++ b/gdb/frame-unwind.h @@ -140,6 +140,12 @@ typedef void (frame_dealloc_cache_ftype) (struct frame_info *self, typedef struct gdbarch *(frame_prev_arch_ftype) (struct frame_info *this_frame, void **this_prologue_cache); +/* Return unwinder-specific symbol info or NULL. */ + +typedef const struct frame_symbol_info* (frame_symbol_ftype) + (struct frame_info *this_frame, + void **this_prologue_cache); + struct frame_unwind { /* The frame's type. Should this instead be a collection of @@ -154,6 +160,9 @@ struct frame_unwind frame_sniffer_ftype *sniffer; frame_dealloc_cache_ftype *dealloc_cache; frame_prev_arch_ftype *prev_arch; + /* For the JIT interface to give external code a chance to supply + symbol information describing the frame. */ + frame_symbol_ftype *symbol_info; }; /* Register a frame unwinder, _prepending_ it to the front of the diff --git a/gdb/frame.c b/gdb/frame.c index 97d54e9..6a4a511 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -2307,6 +2307,16 @@ find_frame_sal (struct frame_info *frame, struct symtab_and_line *sal) (*sal) = find_pc_line (pc, notcurrent); } +/* If frame-specific unwinder has symbol info, return it. */ + +const struct frame_symbol_info * +get_frame_symbol_info (struct frame_info *fi) +{ + return ((fi->unwind->symbol_info == NULL) + ? NULL + : fi->unwind->symbol_info (fi, &fi->prologue_cache)); +} + /* Per "frame.h", return the ``address'' of the frame. Code should really be using get_frame_id(). */ CORE_ADDR diff --git a/gdb/frame.h b/gdb/frame.h index e451a93..c7365c7 100644 --- a/gdb/frame.h +++ b/gdb/frame.h @@ -491,6 +491,23 @@ enum unwind_stop_reason #undef FIRST_ERROR }; +/* Unwinder-specific symbol information. */ + +struct frame_symbol_info +{ + const char *function; + const char *source_file; + const char *arguments; + enum language language; + int source_line; +}; + +/* Return symbol information supplied by the unwinder. If this frame's + unwinder implements frame_unwind.symbol_info method, calls it and + returns the result, otherwise returns NULL. */ + +const struct frame_symbol_info *get_frame_symbol_info (struct frame_info *); + /* Return the reason why we can't unwind past this frame. */ enum unwind_stop_reason get_frame_unwind_stop_reason (struct frame_info *); diff --git a/gdb/jit-reader.in b/gdb/jit-reader.in index 6e2ee64..543259a 100644 --- a/gdb/jit-reader.in +++ b/gdb/jit-reader.in @@ -26,7 +26,7 @@ extern "C" { /* Versioning information. See gdb_reader_funcs. */ -#define GDB_READER_INTERFACE_VERSION 1 +#define GDB_READER_INTERFACE_VERSION 2 /* Readers must be released under a GPL compatible license. To declare that the reader is indeed released under a GPL compatible @@ -260,6 +260,43 @@ typedef struct gdb_reg_value *(gdb_unwind_reg_get) typedef void (gdb_unwind_reg_set) (struct gdb_unwind_callbacks *cb, int regnum, struct gdb_reg_value *val); +/* Provides access to the stashed data associated with the current frame. + On first call, allocates the memory and zeroes it. On subsequent calls + returns the pointer to the allocated memory. */ + +typedef void * (gdb_unwind_stash) (struct gdb_unwind_callbacks *cb, + size_t size); + +/* Look through all the current minimal symbol tables and find the + first minimal symbol that matches SYMBOL_NAME. Return symbol's + address, 0 if symbol is unknown. + If it is a C++ symbol, SYMBOL_NAME is in a mangled form. */ + +typedef GDB_CORE_ADDR (gdb_find_symbol) (const char *symbol_name); + +/* Returns thread ID of the current thread or 0. */ + +typedef long (gdb_get_jtid) (void); + +/* Iterate over shared objects present in the inferior, calling given + callback function for each shared object. The callback is passed + the shared object file name, expanded to something GDB can open, + and data pointer. Once the callback function returns non-zero, + stop iteration and return shared object file name. */ + +typedef const char * (gdb_enumerate_shared) (int (*) (const char *so_name, + void *data), + void *data); + +/* Returns debug flags setting for the unwinding. */ +typedef unsigned int (gdb_unwind_debug_flag) (void); + +/* Returns inferior's architecture name. */ +typedef const char * (gdb_architecture_name) (void); + +/* Returns inferior's pointer size in bytes. */ +typedef size_t (gdb_pointer_size)(void); + /* This struct is passed to unwind in gdb_reader_funcs, and is to be used to unwind the current frame (current being the frame whose registers can be read using reg_get) into the earlier frame. The @@ -273,6 +310,15 @@ struct gdb_unwind_callbacks /* For internal use by GDB. */ void *priv_data; + /* These are provided by JIT API v2 and above. */ + gdb_unwind_stash *stash; + gdb_unwind_reg_get *cpu_reg_get; + gdb_find_symbol *find_symbol; + gdb_get_jtid *get_jtid; + gdb_enumerate_shared *enumerate_shared; + gdb_unwind_debug_flag *debug_flag; + gdb_architecture_name *architecture_name; + gdb_pointer_size *pointer_size; }; /* Forward declaration. */ @@ -306,6 +352,39 @@ typedef enum gdb_status (gdb_unwind_frame) (struct gdb_reader_funcs *self, typedef struct gdb_frame_id (gdb_get_frame_id) (struct gdb_reader_funcs *self, struct gdb_unwind_callbacks *c); +enum jit_frame_symbol_attr { + JIT_FRAME_SYMBOL_ATTR_FUNCTION_NAME, + JIT_FRAME_SYMBOL_ATTR_LANGUAGE, + JIT_FRAME_SYMBOL_ATTR_SOURCE_FILE, + JIT_FRAME_SYMBOL_ATTR_SOURCE_LINE, + JIT_FRAME_SYMBOL_ATTR_FUNCTION_ARGS, +}; + +enum jit_frame_language { + frame_language_unknown, /* Language not known */ + frame_language_auto, /* Placeholder for automatic setting */ + frame_language_c, /* C */ + frame_language_cplus, /* C++ */ + frame_language_d, /* D */ + frame_language_go, /* Go */ + frame_language_objc, /* Objective-C */ + frame_language_java, /* Java */ + frame_language_fortran, /* Fortran */ + frame_language_m2, /* Modula-2 */ + frame_language_asm, /* Assembly language */ + frame_language_pascal, /* Pascal */ + frame_language_ada, /* Ada */ + frame_language_opencl, /* OpenCL */ + frame_language_minimal, /* All other languages, minimal support only */ + frame_nr_languages +}; + +/* Return given attribute of a symbol associated with the current frame. */ + +typedef void * (gdb_get_symbol_attr) (struct gdb_reader_funcs *self, + struct gdb_unwind_callbacks *c, + enum jit_frame_symbol_attr attr); + /* Called when a reader is being unloaded. This function should also free SELF, if required. */ @@ -337,6 +416,8 @@ struct gdb_reader_funcs gdb_unwind_frame *unwind; gdb_get_frame_id *get_frame_id; gdb_destroy_reader *destroy; + /* Members below are supplied by JIT API v2 and above. */ + gdb_get_symbol_attr *get_symbol_attr; }; #ifdef __cplusplus diff --git a/gdb/jit.c b/gdb/jit.c index db6c1b0..46f9dcc 100644 --- a/gdb/jit.c +++ b/gdb/jit.c @@ -21,6 +21,7 @@ #include "jit.h" #include "jit-reader.h" +#include "arch-utils.h" #include "block.h" #include "breakpoint.h" #include "command.h" @@ -33,6 +34,7 @@ #include "observer.h" #include "objfiles.h" #include "regcache.h" +#include "solist.h" #include "symfile.h" #include "symtab.h" #include "target.h" @@ -40,6 +42,7 @@ #include #include "exceptions.h" #include "gdb_bfd.h" +#include "ptid.h" static const char *jit_reader_dir = NULL; @@ -53,6 +56,22 @@ static const struct program_space_data *jit_program_space_data = NULL; static void jit_inferior_init (struct gdbarch *gdbarch); +static void jit_prepend_unwinder (struct gdbarch *gdbarch); + +static CORE_ADDR jit_unwind_find_symbol_address (const char *); + +static long jit_unwind_get_current_jtid (void); + +static const char *jit_unwind_enumerate_shared (int (*callback) (const char *, + void *), + void *); + +static unsigned int jit_unwind_debug_flag (void); + +static const char *jit_unwind_architecture_name (void); + +static size_t jit_unwind_pointer_size (void); + /* An unwinder is registered for every gdbarch. This key is used to remember if the unwinder has been registered for a particular gdbarch. */ @@ -70,6 +89,15 @@ show_jit_debug (struct ui_file *file, int from_tty, fprintf_filtered (file, _("JIT debugging is %s.\n"), value); } +static unsigned jit_unwind_debug; + +static void +show_jit_unwind_debug (struct ui_file *file, int from_tty, + struct cmd_list_element *c, const char *value) +{ + fprintf_filtered (file, _("JIT unwinder debugging is %s.\n"), value); +} + struct target_buffer { CORE_ADDR base; @@ -186,7 +214,7 @@ jit_reader_load (const char *file_name) error (_("Reader not GPL compatible.")); funcs = init_fn (); - if (funcs->reader_version != GDB_READER_INTERFACE_VERSION) + if (funcs->reader_version > GDB_READER_INTERFACE_VERSION) error (_("Reader version does not match GDB version.")); new_reader = XCNEW (struct jit_reader); @@ -1022,6 +1050,8 @@ jit_breakpoint_re_set_internal (struct gdbarch *gdbarch, struct jit_objfile_data *objf_data; CORE_ADDR addr; + jit_prepend_unwinder (gdbarch); + if (ps_data->objfile == NULL) { /* Lookup the registration symbol. If it is missing, then we @@ -1079,6 +1109,12 @@ struct jit_unwind_private /* The frame being unwound. */ struct frame_info *this_frame; + + /* Symbol associated with this frame. */ + struct frame_symbol_info symbol_info; + + /* Memory allocated on behalf of JIT handler. */ + void *stash; }; /* Sets the value of a particular register in this frame. */ @@ -1113,29 +1149,65 @@ reg_value_free_impl (struct gdb_reg_value *value) xfree (value); } -/* Get the value of register REGNUM in the previous frame. */ +/* Get the value of register REGNUM in the given frame. */ static struct gdb_reg_value * -jit_unwind_reg_get_impl (struct gdb_unwind_callbacks *cb, int regnum) +jit_get_register_from_frame (struct frame_info *this_frame, int regnum) { - struct jit_unwind_private *priv; struct gdb_reg_value *value; int gdb_reg, size; struct gdbarch *frame_arch; - priv = cb->priv_data; - frame_arch = get_frame_arch (priv->this_frame); + frame_arch = get_frame_arch (this_frame); gdb_reg = gdbarch_dwarf2_reg_to_regnum (frame_arch, regnum); size = register_size (frame_arch, gdb_reg); value = xmalloc (sizeof (struct gdb_reg_value) + size - 1); - value->defined = deprecated_frame_register_read (priv->this_frame, gdb_reg, + value->defined = deprecated_frame_register_read (this_frame, gdb_reg, value->value); value->size = size; value->free = reg_value_free_impl; return value; } +/* Get the value of register REGNUM in the previous frame. */ + +static struct gdb_reg_value * +jit_unwind_reg_get_impl (struct gdb_unwind_callbacks *cb, int regnum) +{ + return jit_get_register_from_frame ( + ((struct jit_unwind_private *) cb->priv_data)->this_frame, regnum); +} + +/* Get the value of the register REGNUM in the innermost frame. */ + +static struct gdb_reg_value * +jit_unwind_get_cpu_register_value (struct gdb_unwind_callbacks *cb, int regnum) +{ + struct frame_info *frame + = ((struct jit_unwind_private *) cb->priv_data)->this_frame; + struct frame_info *next_frame; + + while ((next_frame = get_next_frame (frame)) != NULL) + frame = next_frame; + return jit_get_register_from_frame (frame, regnum); +} + +/* Return memory area allocated for the JIT reader. The area is allocated on + demand (the first time this function is called with non-zero size). + Calling this function with size == 0 will not allocate memory but will + return its address if it has been already allocated. */ + +static void * +jit_unwind_stash_impl (struct gdb_unwind_callbacks *cb, size_t size) +{ + struct jit_unwind_private *priv_data = cb->priv_data; + + if (!priv_data->stash && size) + priv_data->stash = xcalloc (size, 1); + return priv_data->stash; +} + /* gdb_reg_value has a free function, which must be called on each saved register value. */ @@ -1154,9 +1226,27 @@ jit_dealloc_cache (struct frame_info *this_frame, void *cache) priv_data->registers[i]->free (priv_data->registers[i]); xfree (priv_data->registers); + xfree (priv_data->stash); xfree (priv_data); } +static void +jit_set_unwind_callbacks_vtable (struct gdb_unwind_callbacks *callbacks, + int allow_reg_set) +{ + callbacks->reg_get = jit_unwind_reg_get_impl; + callbacks->reg_set = allow_reg_set ? jit_unwind_reg_set_impl : NULL; + callbacks->target_read = jit_target_read_impl; + callbacks->stash = jit_unwind_stash_impl; + callbacks->cpu_reg_get = jit_unwind_get_cpu_register_value; + callbacks->find_symbol = jit_unwind_find_symbol_address; + callbacks->get_jtid = jit_unwind_get_current_jtid; + callbacks->enumerate_shared = jit_unwind_enumerate_shared; + callbacks->debug_flag = jit_unwind_debug_flag; + callbacks->architecture_name = jit_unwind_architecture_name; + callbacks->pointer_size = jit_unwind_pointer_size; +} + /* The frame sniffer for the pseudo unwinder. While this is nominally a frame sniffer, in the case where the JIT @@ -1173,9 +1263,7 @@ jit_frame_sniffer (const struct frame_unwind *self, struct gdb_unwind_callbacks callbacks; struct gdb_reader_funcs *funcs; - callbacks.reg_get = jit_unwind_reg_get_impl; - callbacks.reg_set = jit_unwind_reg_set_impl; - callbacks.target_read = jit_target_read_impl; + jit_set_unwind_callbacks_vtable (&callbacks, 1); if (loaded_jit_reader == NULL) return 0; @@ -1229,9 +1317,7 @@ jit_frame_this_id (struct frame_info *this_frame, void **cache, /* We don't expect the frame_id function to set any registers, so we set reg_set to NULL. */ - callbacks.reg_get = jit_unwind_reg_get_impl; - callbacks.reg_set = NULL; - callbacks.target_read = jit_target_read_impl; + jit_set_unwind_callbacks_vtable (&callbacks, 0); callbacks.priv_data = &private; gdb_assert (loaded_jit_reader); @@ -1261,6 +1347,74 @@ jit_frame_prev_register (struct frame_info *this_frame, void **cache, int reg) return frame_unwind_got_optimized (this_frame, reg); } +/* Convert jit_frame_language enum to GDB's internal language enum. */ + +static enum language +jit_frame_language_to_language (enum jit_frame_language frame_language) +{ +#define LANG_CASE(x) frame_language_##x: return language_##x + switch (frame_language) + { + LANG_CASE (auto); + LANG_CASE (c); + LANG_CASE (cplus); + LANG_CASE (d); + LANG_CASE (go); + LANG_CASE (objc); + LANG_CASE (java); + LANG_CASE (fortran); + LANG_CASE (m2); + LANG_CASE (asm); + LANG_CASE (pascal); + LANG_CASE (ada); + LANG_CASE (opencl); + LANG_CASE (minimal); + default: + return language_unknown; + } +#undef LANG_CASE +} + +/* Returns unwinder-specific symbol info. */ + +static const struct frame_symbol_info * +jit_symbol (struct frame_info *this_frame, void **cache) +{ + struct gdb_reader_funcs *funcs; + struct gdb_unwind_callbacks callbacks; + struct jit_unwind_private *priv_data; + + /* Backwards compatibility: JIT reader v1 does not support this. */ + if (*cache == NULL || loaded_jit_reader->functions->reader_version == 1) + return NULL; + + jit_set_unwind_callbacks_vtable (&callbacks, 0); + priv_data = *cache; + callbacks.priv_data = priv_data; + + gdb_assert (loaded_jit_reader); + + funcs = loaded_jit_reader->functions; + priv_data->symbol_info.function + = funcs->get_symbol_attr (funcs, &callbacks, + JIT_FRAME_SYMBOL_ATTR_FUNCTION_NAME); + priv_data->symbol_info.language + = jit_frame_language_to_language ( + (enum jit_frame_language) + (funcs->get_symbol_attr (funcs, &callbacks, + JIT_FRAME_SYMBOL_ATTR_LANGUAGE))); + priv_data->symbol_info.source_file + = funcs->get_symbol_attr (funcs, &callbacks, + JIT_FRAME_SYMBOL_ATTR_SOURCE_FILE); + priv_data->symbol_info.source_line = (int)(uintptr_t) + funcs->get_symbol_attr (funcs, &callbacks, + JIT_FRAME_SYMBOL_ATTR_SOURCE_LINE); + priv_data->symbol_info.arguments + = funcs->get_symbol_attr (funcs, &callbacks, + JIT_FRAME_SYMBOL_ATTR_FUNCTION_ARGS); + return &priv_data->symbol_info; +} + /* Relay everything back to the unwinder registered by the JIT debug info reader.*/ @@ -1270,9 +1424,11 @@ static const struct frame_unwind jit_frame_unwind = default_frame_unwind_stop_reason, jit_frame_this_id, jit_frame_prev_register, - NULL, + NULL, /* frame_data */ jit_frame_sniffer, - jit_dealloc_cache + jit_dealloc_cache, + NULL, /* prev_arch */ + jit_symbol, }; @@ -1313,8 +1469,6 @@ jit_inferior_init (struct gdbarch *gdbarch) if (jit_debug) fprintf_unfiltered (gdb_stdlog, "jit_inferior_init\n"); - jit_prepend_unwinder (gdbarch); - ps_data = get_jit_program_space_data (); if (jit_breakpoint_re_set_internal (gdbarch, ps_data) != 0) return; @@ -1446,6 +1600,68 @@ free_objfile_data (struct objfile *objfile, void *data) xfree (data); } +/* Locates exported symbol in the target and returns its address. */ + +CORE_ADDR +jit_unwind_find_symbol_address (const char *symbol_name) +{ + struct bound_minimal_symbol bound_min_symbol + = lookup_bound_minimal_symbol (symbol_name); + + return (bound_min_symbol.minsym == NULL) + ? 0 : BMSYMBOL_VALUE_ADDRESS (bound_min_symbol); +} + +/* Returns thread ID of the inferior's current thread. */ + +long +jit_unwind_get_current_jtid (void) +{ + long jtid = ptid_get_lwp (inferior_ptid); + + /* ptid_get_lwp returns 0 if inferior is run via gdbserver. + However, ptid_get_tid returns 0 for the local inferior. Ugly. */ + return jtid ? jtid : ptid_get_tid (inferior_ptid); +} + +/* Calls provided function for every shared object currently loaded into + the inferior until the function returns non zero. */ + +const char * +jit_unwind_enumerate_shared (int (*callback) (const char *so_name, + void *data), + void *data) +{ + struct so_list *so; + + for (so = master_so_list (); so; so = so->next) + { + if (callback (so->so_name, data)) + return so->so_name; + } + return NULL; +} + +static unsigned int +jit_unwind_debug_flag (void) +{ + return jit_unwind_debug; +} + + +/* Return current architecture's string name. */ +static const char * +jit_unwind_architecture_name (void) +{ + return gdbarch_bfd_arch_info (target_gdbarch ())->printable_name; +} + +static size_t +jit_unwind_pointer_size (void) +{ + return gdbarch_ptr_bit (target_gdbarch()) / 8; +} + /* Initialize the jit_gdbarch_data slot with an instance of struct jit_gdbarch_data_type */ @@ -1475,6 +1691,13 @@ _initialize_jit (void) NULL, show_jit_debug, &setdebuglist, &showdebuglist); + add_setshow_zuinteger_cmd ("jitunwind", class_maintenance, &jit_unwind_debug, + _("Set JIT frame unwinder debug."), + _("Show JIT frame unwinder debug."), + _("A collection of bit flags for debugging."), + NULL, + show_jit_unwind_debug, + &setdebuglist, &showdebuglist); observer_attach_inferior_exit (jit_inferior_exit_hook); observer_attach_breakpoint_deleted (jit_breakpoint_deleted); diff --git a/gdb/stack.c b/gdb/stack.c index da7d977..68f4b68 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -1039,6 +1039,15 @@ find_frame_funname (struct frame_info *frame, char **funname, enum language *funlang, struct symbol **funcp) { struct symbol *func; + const struct frame_symbol_info *frame_symbol = get_frame_symbol_info (frame); + + if (frame_symbol != NULL) + { + *funname = xstrdup (frame_symbol->function); + *funlang = frame_symbol->language; + *funcp = NULL; + return; + } *funname = NULL; *funlang = language_unknown; @@ -1126,6 +1135,23 @@ find_frame_funname (struct frame_info *frame, char **funname, } } +/* Fill unwinder-specific source location for the frame if available and + return 1. Otherwise return 0. */ + +static int +find_frame_source_location (struct frame_info *fi, const char **file, + int *line) +{ + const struct frame_symbol_info *frame_symbol = get_frame_symbol_info (fi); + + if (frame_symbol == NULL || frame_symbol->source_file == NULL) + return 0; + + *file = frame_symbol->source_file; + *line = frame_symbol->source_line; + return 1; +} + static void print_frame (struct frame_info *frame, int print_level, enum print_what print_what, int print_args, @@ -1141,6 +1167,8 @@ print_frame (struct frame_info *frame, int print_level, struct symbol *func; CORE_ADDR pc = 0; int pc_p; + const char *file; + int line; pc_p = get_frame_pc_if_available (frame, &pc); @@ -1201,7 +1229,16 @@ print_frame (struct frame_info *frame, int print_level, args_list_chain = make_cleanup_ui_out_list_begin_end (uiout, "args"); TRY_CATCH (e, RETURN_MASK_ERROR) { - print_frame_args (func, frame, numargs, gdb_stdout); + const struct frame_symbol_info *frame_symbol; + frame_symbol = get_frame_symbol_info (frame); + + if (frame_symbol != NULL) + { + if (frame_symbol->arguments != NULL) + ui_out_text (uiout, frame_symbol->arguments); + } + else + print_frame_args (func, frame, numargs, gdb_stdout); } /* FIXME: ARGS must be a list. If one argument is a string it will have " that will not be properly escaped. */ @@ -1210,7 +1247,21 @@ print_frame (struct frame_info *frame, int print_level, QUIT; } ui_out_text (uiout, ")"); - if (sal.symtab) + + if (find_frame_source_location (frame, &file, &line)) + { + annotate_frame_source_begin (); + ui_out_wrap_hint (uiout, " "); + ui_out_text (uiout, " at "); + annotate_frame_source_file (); + ui_out_field_string (uiout, "file", file); + annotate_frame_source_file_end (); + ui_out_text (uiout, ":"); + annotate_frame_source_line (); + ui_out_field_int (uiout, "line", line); + annotate_frame_source_end (); + } + else if (sal.symtab) { const char *filename_display; diff --git a/gdb/testsuite/gdb.arch/amd64-jit-reader-v1.c b/gdb/testsuite/gdb.arch/amd64-jit-reader-v1.c new file mode 100644 index 0000000..f75052e --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-jit-reader-v1.c @@ -0,0 +1,135 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2012-2014 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 . */ + +#include +#include +#include "jit-reader.h" + +GDB_DECLARE_GPL_COMPATIBLE_READER + +extern struct gdb_reader_funcs *gdb_init_reader (void); + +static enum gdb_status debug_info_provider (struct gdb_reader_funcs *self, + struct gdb_symbol_callbacks *cb, + void *memory, long memory_sz); +static enum gdb_status frame_unwinder (struct gdb_reader_funcs *self, + struct gdb_unwind_callbacks *cb); +static struct gdb_frame_id frame_id_provider(struct gdb_reader_funcs *self, + struct gdb_unwind_callbacks *cb); +static void destructor (struct gdb_reader_funcs *self); + +#define DATA_COOKIE 0xdeaffead +static int jit_data = DATA_COOKIE; + +static struct +{ + int reader_version; + void *priv_data; + gdb_read_debug_info *read; + gdb_unwind_frame *unwind; + gdb_get_frame_id *get_frame_id; + gdb_destroy_reader *destroy; +} gdb_reader_funcs_v1 = +{ + 1, /* version */ + (void *)&jit_data, + debug_info_provider, + frame_unwinder, + frame_id_provider, + destructor +}; + +struct gdb_reader_funcs * +gdb_init_reader (void) +{ + fprintf (stderr, "Test JIT reader loaded.\n"); + return (struct gdb_reader_funcs *)&gdb_reader_funcs_v1; +} + +enum gdb_status +debug_info_provider (struct gdb_reader_funcs *self, + struct gdb_symbol_callbacks *cb, + void *memory, long memory_sz) +{ + if (*(int *)(self->priv_data) != DATA_COOKIE) + return GDB_FAIL; + fprintf (stderr, "Debug info provider called for %p..%p.\n", + memory, (void *)(memory_sz + (char *)memory)); + return GDB_SUCCESS; +} + +void +destructor (struct gdb_reader_funcs *self) +{ + fprintf (stderr, "Test JIT reader unloaded.\n"); + return; +} + +static GDB_CORE_ADDR +_get_frame_register (struct gdb_unwind_callbacks *cb, int reg_no) +{ + struct gdb_reg_value *reg = cb->reg_get (cb, reg_no); + GDB_CORE_ADDR reg_value = *(GDB_CORE_ADDR *)reg->value; + reg->free (reg); + return reg_value; +} + +static void +_set_frame_register (struct gdb_unwind_callbacks *cb, int reg_no, + GDB_CORE_ADDR value) +{ + struct gdb_reg_value *reg = cb->reg_get (cb, reg_no); + *(GDB_CORE_ADDR *)(reg->value) = value; + reg->defined = 1; + (cb->reg_set)(cb, reg_no, reg); +} + +#define REG_BP 6 +#define REG_PC 16 +#define REG_SP 7 +enum gdb_status +frame_unwinder (struct gdb_reader_funcs *self, + struct gdb_unwind_callbacks *cb) +{ + GDB_CORE_ADDR bp = _get_frame_register (cb, REG_BP); + GDB_CORE_ADDR pc = _get_frame_register (cb, REG_PC); + GDB_CORE_ADDR sp = _get_frame_register (cb, REG_SP); + GDB_CORE_ADDR word; + cb->target_read(bp, &word, 8); + fprintf (stderr, "frame_unwinder called for pc=%p, bp=%p, *bp=%p.\n", (void *)pc, (void *)bp, (void *)word); + if (word != bp) + return GDB_FAIL; + /* Found a frame that the test program altered for us. Tell GDB what + the previous frame is. The test program saved the correct outer + frame's FP is one word above. */ + cb->target_read (bp - 8, &word, 8); + _set_frame_register (cb, REG_BP, word); + cb->target_read (bp + 8, &word, 8); + _set_frame_register (cb, REG_PC, word); + _set_frame_register (cb, REG_SP, bp + 16); + return GDB_SUCCESS; +} + +struct gdb_frame_id frame_id_provider (struct gdb_reader_funcs *self, + struct gdb_unwind_callbacks *cb) +{ + struct gdb_frame_id frame_id = + { _get_frame_register (cb, REG_PC), + _get_frame_register (cb, REG_SP) }; + fprintf (stderr, "frame_id_provider called.\n"); + return frame_id; +} diff --git a/gdb/testsuite/gdb.arch/amd64-jit-reader-v1.exp b/gdb/testsuite/gdb.arch/amd64-jit-reader-v1.exp new file mode 100644 index 0000000..c52a7c9 --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-jit-reader-v1.exp @@ -0,0 +1,81 @@ +# Copyright 2010-2014 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 . + +# Please email any bugs, comments, and/or additions to this file to: +# bug-gdb@gnu.org + +# This file is part of the gdb testsuite. It tests that the the current +# JIT reader framework is backwards compatible with old (v1) JIT readers. + +if {[skip_shlib_tests]} { + untested "Shared libraries are not supported" + return -1 +} + +if {[get_compiler_info]} { + return -1 +} + +if { ![istarget x86_64-*-* ] || ![is_lp64_target] } { + verbose "Skipping amd64 JIT reader tests." + return +} + +set testfile "amd64-jit-unwind" +set srcfile ${testfile}.c +set binfile [standard_output_file ${testfile}] +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list debug]] != "" } { + untested "could not compile test program" + return -1 +} + +set jit_reader "amd64-jit-reader-v1" +set jit_reader_source "${srcdir}/${subdir}/${jit_reader}.c" +set jit_reader_binfile [standard_output_file ${jit_reader}.so] + +if { [gdb_compile_shlib ${jit_reader_source} ${jit_reader_binfile} {additional_flags="-I.."}] != "" } { + untested "Cannot compile amd-jit-reader" + return -1 +} + +# Check that GDB is unable to show the backtrace for jit-unwind without +# JIT reader. +clean_restart $testfile +if { ![runto_main] } { + fail "Can't run to main" + return +} +gdb_breakpoint [gdb_get_line_number "break backtrace-broken" ] +gdb_continue_to_breakpoint "break backtrace-broken" +gdb_test "where" "Backtrace stopped: frame did not save the .*" +gdb_test "continue" "Continuing\..*$inferior_exited_re.*" + +# Check that GDB shows backtrace with JIT reader present +# and that JIT reader can be unloaded. +clean_restart $testfile +if { ![runto_main] } { + fail "Can't run to main" + return +} +gdb_test "jit-reader-load ${jit_reader_binfile}" "Test JIT reader loaded." +gdb_breakpoint [gdb_get_line_number "break backtrace-broken" ] +gdb_continue_to_breakpoint "break backtrace-broken" +gdb_test_sequence "where" "Bad backtrace" { + "\[\r\n\]+#0 .* break_backtrace \\(\\) at " + "\[\r\n\]+#1 .* break_backtrace_caller \\(\\) at " + "\[\r\n\]+#2 .* main \\(.*\\) at" +} +gdb_test "continue" "Continuing\..*$inferior_exited_re.*" +gdb_test "jit-reader-unload" "Test JIT reader unloaded." diff --git a/gdb/testsuite/gdb.arch/amd64-jit-reader.c b/gdb/testsuite/gdb.arch/amd64-jit-reader.c new file mode 100644 index 0000000..b40c52b --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-jit-reader.c @@ -0,0 +1,152 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2012-2014 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 . */ + +#include +#include +#include "jit-reader.h" + +GDB_DECLARE_GPL_COMPATIBLE_READER + +extern struct gdb_reader_funcs *gdb_init_reader (void); + +static enum gdb_status debug_info_provider (struct gdb_reader_funcs *self, + struct gdb_symbol_callbacks *cb, + void *memory, long memory_sz); +static enum gdb_status frame_unwinder (struct gdb_reader_funcs *self, + struct gdb_unwind_callbacks *cb); +static struct gdb_frame_id frame_id_provider(struct gdb_reader_funcs *self, + struct gdb_unwind_callbacks *cb); +static void destructor (struct gdb_reader_funcs *self); +static void * get_symbol_attr (struct gdb_reader_funcs *self, + struct gdb_unwind_callbacks *c, + enum jit_frame_symbol_attr attr); + +#define DATA_COOKIE 0xdeaffead +static int jit_data = DATA_COOKIE; + +static struct gdb_reader_funcs gdb_reader_funcs = +{ + GDB_READER_INTERFACE_VERSION, /* version */ + (void *)&jit_data, + debug_info_provider, + frame_unwinder, + frame_id_provider, + destructor, + get_symbol_attr +}; + +struct gdb_reader_funcs * +gdb_init_reader (void) +{ + fprintf (stderr, "Test JIT reader loaded.\n"); + return (struct gdb_reader_funcs *)&gdb_reader_funcs; +} + +enum gdb_status +debug_info_provider (struct gdb_reader_funcs *self, + struct gdb_symbol_callbacks *cb, + void *memory, long memory_sz) +{ + if (*(int *)(self->priv_data) != DATA_COOKIE) + return GDB_FAIL; + fprintf (stderr, "Debug info provider called for %p..%p.\n", + memory, (void *)(memory_sz + (char *)memory)); + return GDB_SUCCESS; +} + +void +destructor (struct gdb_reader_funcs *self) +{ + fprintf (stderr, "Test JIT reader unloaded.\n"); + return; +} + +static GDB_CORE_ADDR +_get_frame_register (struct gdb_unwind_callbacks *cb, int reg_no) +{ + struct gdb_reg_value *reg = cb->reg_get (cb, reg_no); + GDB_CORE_ADDR reg_value = *(GDB_CORE_ADDR *)reg->value; + reg->free (reg); + return reg_value; +} + +static void +_set_frame_register (struct gdb_unwind_callbacks *cb, int reg_no, + GDB_CORE_ADDR value) +{ + struct gdb_reg_value *reg = cb->reg_get (cb, reg_no); + *(GDB_CORE_ADDR *) (reg->value) = value; + reg->defined = 1; + (cb->reg_set) (cb, reg_no, reg); +} + +#define REG_BP 6 +#define REG_PC 16 +#define REG_SP 7 +enum gdb_status +frame_unwinder (struct gdb_reader_funcs *self, + struct gdb_unwind_callbacks *cb) +{ + GDB_CORE_ADDR bp = _get_frame_register (cb, REG_BP); + GDB_CORE_ADDR pc = _get_frame_register (cb, REG_PC); + GDB_CORE_ADDR sp = _get_frame_register (cb, REG_SP); + GDB_CORE_ADDR word; + cb->target_read(bp, &word, 8); + fprintf (stderr, "frame_unwinder called for pc=%p, bp=%p, *bp=%p.\n", (void *)pc, (void *)bp, (void *)word); + if (word != bp) + return GDB_FAIL; + /* Found a frame that the test program altered for us. Tell GDB what + the previous frame is. The test program saved the correct outer + frame's FP one word above. */ + cb->target_read (bp - 8, &word, 8); + _set_frame_register (cb, REG_BP, word); + cb->target_read (bp + 8, &word, 8); + _set_frame_register (cb, REG_PC, word); + _set_frame_register (cb, REG_SP, bp + 16); + return GDB_SUCCESS; +} + +struct gdb_frame_id frame_id_provider (struct gdb_reader_funcs *self, + struct gdb_unwind_callbacks *cb) +{ + struct gdb_frame_id frame_id = + { _get_frame_register (cb, REG_PC), + _get_frame_register (cb, REG_SP) }; + fprintf (stderr, "frame_id_provider called.\n"); + return frame_id; +} + +void * +get_symbol_attr (struct gdb_reader_funcs *self, struct gdb_unwind_callbacks *c, + enum jit_frame_symbol_attr attr) +{ + switch (attr) { + case JIT_FRAME_SYMBOL_ATTR_FUNCTION_NAME: + return "restored_break_backtrace"; + case JIT_FRAME_SYMBOL_ATTR_LANGUAGE: + return (void *) frame_language_c; + case JIT_FRAME_SYMBOL_ATTR_SOURCE_FILE: + return ""; + case JIT_FRAME_SYMBOL_ATTR_SOURCE_LINE: + return (void *) 42; + case JIT_FRAME_SYMBOL_ATTR_FUNCTION_ARGS: + return ""; + default: + fprintf (stderr, "Bad enum value: %d\n", (int)attr); + return NULL; + } +} diff --git a/gdb/testsuite/gdb.arch/amd64-jit-reader.exp b/gdb/testsuite/gdb.arch/amd64-jit-reader.exp new file mode 100644 index 0000000..1b57e14 --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-jit-reader.exp @@ -0,0 +1,80 @@ +# Copyright 2010-2014 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 . + +# Please email any bugs, comments, and/or additions to this file to: +# bug-gdb@gnu.org + +# This file is part of the gdb testsuite. + +if {[skip_shlib_tests]} { + untested "Shared libraries are not supported" + return -1 +} + +if {[get_compiler_info]} { + return -1 +} + +if { ![istarget x86_64-*-* ] || ![is_lp64_target] } { + verbose "Skipping amd64 JIT reader tests." + return +} + +set testfile "amd64-jit-unwind" +set srcfile ${testfile}.c +set binfile [standard_output_file ${testfile}] +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list debug]] != "" } { + untested "could not compile test program" + return -1 +} + +set jit_reader "amd64-jit-reader" +set jit_reader_source "${srcdir}/${subdir}/${jit_reader}.c" +set jit_reader_binfile [standard_output_file ${jit_reader}.so] + +if { [gdb_compile_shlib ${jit_reader_source} ${jit_reader_binfile} {additional_flags="-I.."}] != "" } { + untested "Cannot compile amd-jit-reader" + return -1 +} + +# Check that GDB is unable to show the backtrace for jit-unwind without +# JIT reader. +clean_restart $testfile +if { ![runto_main] } { + fail "Can't run to main" + return +} +gdb_breakpoint [gdb_get_line_number "break backtrace-broken" ] +gdb_continue_to_breakpoint "break backtrace-broken" +gdb_test "where" "Backtrace stopped: frame did not save the .*" +gdb_test "continue" "Continuing\..*$inferior_exited_re.*" + +# Check that GDB shows backtrace with JIT reader present +# and that JIT reader can be unloaded. +clean_restart $testfile +if { ![runto_main] } { + fail "Can't run to main" + return +} +gdb_test "jit-reader-load ${jit_reader_binfile}" "Test JIT reader loaded." +gdb_breakpoint [gdb_get_line_number "break backtrace-broken" ] +gdb_continue_to_breakpoint "break backtrace-broken" +gdb_test_sequence "where" "Bad backtrace" { + "\[\r\n\]+#0 .* restored_break_backtrace \\(\\) at :42" + "\[\r\n\]+#1 .* break_backtrace_caller \\(\\) at " + "\[\r\n\]+#2 .* main \\(.*\\) at" +} +gdb_test "continue" "Continuing\..*$inferior_exited_re.*" +gdb_test "jit-reader-unload" "Test JIT reader unloaded." diff --git a/gdb/testsuite/gdb.arch/amd64-jit-unwind.c b/gdb/testsuite/gdb.arch/amd64-jit-unwind.c new file mode 100644 index 0000000..20c1e22 --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-jit-unwind.c @@ -0,0 +1,68 @@ +/* This test program is part of GDB, the GNU debugger. + + Copyright 2011-2014 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 . */ + +/* This program is used to verify that a JIT handler can unwind custom + stack frames. */ + +#include +#include +#include + +static void * +swap_value (void **location, void *new_value) +{ + void *old_value = *location; + *location = new_value; + return old_value; +} + +#define MY_FRAME (__builtin_frame_address (0)) +static void +break_backtrace () +{ + /* Save outer frame address, then corrupt the unwind chain by + setting the outer frame address in it to self. This is + ABI-specific: the first word of the frame contains previous frame + address in amd64. */ + void *outer_fp = swap_value ((void **)MY_FRAME, MY_FRAME); + + /* Verify the compiler allocates the first local variable one word + below frame. This is where test JIT reader expects to find the + correct outer frame address. */ + if (&outer_fp + 1 != (void **)MY_FRAME) + { + fprintf (stderr, "First variable should be allocated one word below " + "the frame, got variable's address %p, frame at %p instead\n", + &outer_fp, MY_FRAME); + abort(); + } + + /* Now restore it so that we can return. The test sets the + breakpoint just before this happens, and GDB will not be able to + show the backtrace without JIT reader. */ + swap_value (MY_FRAME, outer_fp); /* break backtrace-broken */ +} + +static void break_backtrace_caller () +{ + break_backtrace (); +} + +int main (int argc, char *argv[]) +{ + break_backtrace_caller (); +}