From patchwork Tue May 25 10:16:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dodji Seketeli X-Patchwork-Id: 43564 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A79B33938381; Tue, 25 May 2021 10:16:11 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A79B33938381 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1621937771; bh=K7nWv3TO4NqDIg5l3TgCba5LCTdKE1YRvCegSBKQ88U=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Help: List-Subscribe:From:Reply-To:From; b=JzPCFBiqoMYkO3tL1vjmw0RaieLjmrqbdbaGdfZ6zYAAe/0FPUOhTKXt/i0dIeOOq cvL/oLyvg0SBwA2r80n3Xr+YgCaHneQVx+QgaBjMOzvsoSldYAxx0l2Q9RGrpuk/6Z sjfHo3sSbWXQyd3N/PmT8NoOMjfGWHb5OtagnNSg= X-Original-To: libabigail@sourceware.org Delivered-To: libabigail@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id 14A743899073 for ; Tue, 25 May 2021 10:16:08 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 14A743899073 Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-502-nvkAKI2jM8K5osDM3OKRLg-1; Tue, 25 May 2021 06:16:05 -0400 X-MC-Unique: nvkAKI2jM8K5osDM3OKRLg-1 Received: by mail-wm1-f71.google.com with SMTP id j142-20020a1c23940000b029017f23d6fcfaso730809wmj.1 for ; Tue, 25 May 2021 03:16:05 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:organization:date:message-id :user-agent:mime-version; bh=K7nWv3TO4NqDIg5l3TgCba5LCTdKE1YRvCegSBKQ88U=; b=MQISAC1KHCr9DisYVAR4an3vie0cchkSi9naTDrRGAwb15S4iWJQKgtIupbsV/wmU9 GuqISj2rOoaNJrwiignQ97l10Ew9xIIG+lWnANk6KFw+XjyMkP7Jy78bcb/ZJAHo2JRW Z8PKX8SYHOlj+mTITbfs38AUiJGpM+x8Dk1/xsSneE0hzcjWbCIbR08pBfDOo3NqcooW l68lwBcsoy8yxyhU+vq96iuDVPbTOxWs+NdlnAAQENCp+AfFleb22ygrP9uzJxoxspSJ WyuG3tK+e81Kp0Nq/ILzBZMFsE+TJgsw62KE7UGj3otB2a6zKsVtFH3G5nJ5EXjNYP+p Op4w== X-Gm-Message-State: AOAM530KzR4jQVX+GktKTj35CktXQfa5kbW15hPFmRIWia7kCsObjjd1 8FfJMWNwxmZptpT47zn8TN6O0Eobp2iloGEQT/kbKAuFdIW8ClbPVfJzlmeraaVzb6CKb+e12No n5JXPuYKOrKQy1sQfvnJj5rp9BVb8rG48NsQkfgBLkwQMnQkOyzaJccceCsIid8/LiRmJ X-Received: by 2002:adf:ef02:: with SMTP id e2mr11372491wro.162.1621937763762; Tue, 25 May 2021 03:16:03 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxnFdQjq9NR/F/kQ8vNd62C0Jk/5U6CKGfQIlvLpmHh781JIPPDCWKYzKm3B95fycrFXIdUXA== X-Received: by 2002:adf:ef02:: with SMTP id e2mr11372464wro.162.1621937763466; Tue, 25 May 2021 03:16:03 -0700 (PDT) Received: from localhost ([88.120.130.27]) by smtp.gmail.com with ESMTPSA id s11sm2284516wmf.14.2021.05.25.03.16.03 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 25 May 2021 03:16:03 -0700 (PDT) Received: by localhost (Postfix, from userid 1000) id 467845800FB; Tue, 25 May 2021 12:16:02 +0200 (CEST) To: libabigail@sourceware.org Subject: [PATCH, applied] Add primitives callable from the command line of the debugger Organization: Red Hat / France X-Operating-System: Fedora 35 X-URL: http://www.redhat.com Date: Tue, 25 May 2021 12:16:02 +0200 Message-ID: <87zgwj3yql.fsf@redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.8 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libabigail@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Mailing list of the Libabigail project List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-Patchwork-Original-From: Dodji Seketeli via Libabigail From: Dodji Seketeli Reply-To: Dodji Seketeli Errors-To: libabigail-bounces@sourceware.org Sender: "Libabigail" Hello, During debugging it can be extremely useful to be able to visualize the data members of a class type, instance of abigail::ir::class_decl*. It's actually useful to visualize the pretty representation (type name and kind) of all types and decls that inherit abigail::ir::type_or_decl_base, basically. Today, in the debugger, if we have a variable defined as "abigail::ir::type_or_decl_base* t", we can type: $ p t->get_pretty_representation(true, true); This would display something like: $ typedef foo_t However, if 't' is declared as: "abigail::ir::class_decl* t", then if we type: (gdb) p t->get_pretty_representation(true, true); We'll get something like: class foo_klass (gdb) So we get the kind and the name of the ABI artifact; but in case of a class, we don't get the details of its data members. This patch introduces a function named "debug" which, would be invoked on the 't' above like this: (gdb) p debug(t) I would yield: struct tm { // size in bits: 448 // translation unit: test24-drop-fns.cc // @: 0x5387a0, @canonical: 0x5387a0 int tm_sec; // uses canonical type '@0x538270' int tm_min; // uses canonical type '@0x538270' int tm_hour; // uses canonical type '@0x538270' int tm_mday; // uses canonical type '@0x538270' int tm_mon; // uses canonical type '@0x538270' int tm_year; // uses canonical type '@0x538270' int tm_wday; // uses canonical type '@0x538270' int tm_yday; // uses canonical type '@0x538270' int tm_isdst; // uses canonical type '@0x538270' long int tm_gmtoff; // uses canonical type '@0x461200' const char* tm_zone; // uses canonical type '@0x544528' }; (gdb) This gives much more information to understand what 't' designates. The patch also provides functions to retrieve one data member from a given type that happens to designate a class type. For instance: (gdb) p get_data_member(t, "tm_sec") This would yield: $19 = std::shared_ptr (use count 4, weak count 0) = {get() = 0x9d9a80} We could visualize that data member by doing: (gdb) p debug(get_data_member(t, "tm_sec")._M_ptr) int tm::tm_sec (gdb) The patch also provides a new 'debug_equals' function that allow us to easily perform an artifact comparison from the command line of the debugger, as well as methods to the environment type to poke at the canonical types available in the environment. These new debugging primitives already proved priceless while debugging issues that are fixed by subsequent patches to come. * include/abg-fwd.h (get_debug_representation, get_data_member) (debug, debug_equals): Declare new functions. * include/abg-ir.h (environment{get_canonical_types, get_canonical_type}): Declare new member functions. * src/abg-ir.cc (environment::{get_canonical_types, get_canonical_type}): Define new member functions. (get_debug_representation, get_data_member) (debug, debug_equals): Define new functions. Signed-off-by: Dodji Seketeli --- include/abg-fwd.h | 21 +++++ include/abg-ir.h | 4 + src/abg-ir.cc | 224 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 249 insertions(+) diff --git a/include/abg-fwd.h b/include/abg-fwd.h index cb03b54f..e7b621d4 100644 --- a/include/abg-fwd.h +++ b/include/abg-fwd.h @@ -999,6 +999,27 @@ get_class_or_union_flat_representation(const class_or_union_sptr& cou, bool internal, bool qualified_name = true); +string +get_debug_representation(const type_or_decl_base*); + +var_decl_sptr +get_data_member(class_or_union *, const char*); + +var_decl_sptr +get_data_member(type_base *clazz, const char* member_name); + +type_or_decl_base* +debug(const type_or_decl_base* artifact); + +type_base* +debug(const type_base* artifact); + +decl_base* +debug(const decl_base* artifact); + +bool +debug_equals(const type_or_decl_base *l, const type_or_decl_base *r); + bool odr_is_relevant(const type_or_decl_base&); diff --git a/include/abg-ir.h b/include/abg-ir.h index 62c94405..2fbc12e9 100644 --- a/include/abg-ir.h +++ b/include/abg-ir.h @@ -200,6 +200,10 @@ public: const config& get_config() const; + vector* get_canonical_types(const char* name); + + type_base* get_canonical_type(const char* name, unsigned index); + friend class class_or_union; friend class class_decl; friend class function_type; diff --git a/src/abg-ir.cc b/src/abg-ir.cc index d0f8c6b8..31abcc2e 100644 --- a/src/abg-ir.cc +++ b/src/abg-ir.cc @@ -3186,6 +3186,52 @@ const config& environment::get_config() const {return priv_->config_;} + +/// Get the vector of canonical types which have a given "string +/// representation". +/// +/// @param 'name', the textual representation of the type as returned +/// by type_or_decl_base::get_pretty_representation(/*internal=*/true, +/// /*qualified=*/true) +/// +/// This is useful to for debugging purposes as it's handy to use from +/// inside a debugger like GDB. +/// +/// @return a pointer to the vector of canonical types having the +/// representation @p name, or nullptr if no type with that +/// representation exists. +vector* +environment::get_canonical_types(const char* name) +{ + auto ti = get_canonical_types_map().find(name); + if (ti == get_canonical_types_map().end()) + return nullptr; + return &ti->second; +} + +/// Get a given canonical type which has a given "string +/// representation". +/// +/// @param 'name', the textual representation of the type as returned +/// by type_or_decl_base::get_pretty_representation(/*internal=*/true, +/// /*qualified=*/true). +/// +/// @param index, the index of the type in the vector of types that +/// all have the same textual representation @p 'name'. That vector +/// is returned by the function environment::get_canonical_types(). +/// +/// @return the canonical type which has the representation @p name, +/// and which is at index @p index in the vector of canonical types +/// having that same textual representation. +type_base* +environment::get_canonical_type(const char* name, unsigned index) +{ + vector *types = get_canonical_types(name); + if (!types ||index >= types->size()) + return nullptr; + return (*types)[index].get(); +} + // // @@ -8068,6 +8114,184 @@ get_class_or_union_flat_representation(const class_or_union_sptr& cou, internal, qualified_names);} +/// Get the textual representation of a type for debugging purposes. +/// +/// If the type is a class/union, this shows the data members, virtual +/// member functions, size, pointer value of its canonical type, etc. +/// Otherwise, this just shows the name of the artifact as returned by +/// type_or_decl_base:get_pretty_representation(). +/// +/// @param artifact the artifact to show a debugging representation of. +/// +/// @return a debugging string representation of @p artifact. +string +get_debug_representation(const type_or_decl_base* artifact) +{ + if (!artifact) + return string(""); + + class_or_union * c = is_class_or_union_type(artifact); + if (c) + { + class_decl *clazz = is_class_type(c); + string name = c->get_pretty_representation(/*internal=*/false, true); + std::ostringstream o; + o << name; + + if (clazz) + { + if (!clazz->get_base_specifiers().empty()) + o << " :" << std::endl; + for (auto &b : clazz->get_base_specifiers()) + { + o << " "; + if (b->get_is_virtual()) + o << "virtual "; + o << b->get_base_class()->get_pretty_representation(/*internal=*/false, + /*qualified=*/true) + << std::endl; + } + } + o << std::endl + << "{" + << " // size in bits: " << c->get_size_in_bits() << "\n" + << " // translation unit: " << c->get_translation_unit()->get_absolute_path() << std::endl + << " // @: " << std::hex << is_type(c) + << ", @canonical: " << c->get_canonical_type().get() << std::dec + << "\n\n"; + + for (auto m : c->get_data_members()) + { + type_base_sptr t = m->get_type(); + t = peel_typedef_pointer_or_reference_type(t); + + o << " " + << m->get_pretty_representation(/*internal=*/false, + /*qualified=*/false) + << ";"; + + if (t && t->get_canonical_type()) + o << " // uses canonical type '@" + << std::hex << t->get_canonical_type().get() << std::dec; + + o << "'" << std::endl; + } + + if (clazz && clazz->has_vtable()) + { + o << " // virtual member functions\n\n"; + for (auto f : clazz->get_virtual_mem_fns()) + o << " " << f->get_pretty_representation(/*internal=*/false, + /*qualified=*/false) + << ";" << std::endl; + } + + o << "};" << std::endl; + + return o.str(); + } + return artifact->get_pretty_representation(/*internal=*/true, + /*qualified=*/true); +} + +/// Get a given data member, referred to by its name, of a class type. +/// +/// @param clazz the class to consider. +/// +/// @param member_name name of the data member to get. +/// +/// @return the resulting data member or nullptr if none was found. +var_decl_sptr +get_data_member(class_or_union *clazz, const char* member_name) +{ + if (!clazz) + return var_decl_sptr(); + return clazz->find_data_member(member_name); +} + +/// Get a given data member, referred to by its name, of a class type. +/// +/// @param clazz the class to consider. +/// +/// @param member_name name of the data member to get. +/// +/// @return the resulting data member or nullptr if none was found. +var_decl_sptr +get_data_member(type_base *clazz, const char* member_name) +{return get_data_member(is_class_or_union_type(clazz), member_name);} + +/// Emit a textual representation of an artifact to std error stream +/// for debugging purposes. +/// +/// This is useful to invoke from within a command line debugger like +/// GDB to help make sense of a given ABI artifact. +/// +/// @param artifact the ABI artifact to emit the debugging +/// representation for. +/// +/// @return the artifact @p artifact. +type_or_decl_base* +debug(const type_or_decl_base* artifact) +{ + std::cerr << get_debug_representation(artifact) << std::endl; + return const_cast(artifact); +} + +/// Emit a textual representation of an artifact to std error stream +/// for debugging purposes. +/// +/// This is useful to invoke from within a command line debugger like +/// GDB to help make sense of a given ABI artifact. +/// +/// @param artifact the ABI artifact to emit the debugging +/// representation for. +/// +/// @return the artifact @p artifact. +type_base* +debug(const type_base* artifact) +{ + debug(static_cast(artifact)); + return const_cast(artifact); +} + +/// Emit a textual representation of an artifact to std error stream +/// for debugging purposes. +/// +/// This is useful to invoke from within a command line debugger like +/// GDB to help make sense of a given ABI artifact. +/// +/// @param artifact the ABI artifact to emit the debugging +/// representation for. +/// +/// @return the artifact @p artifact. +decl_base* +debug(const decl_base* artifact) +{ + debug(static_cast(artifact)); + return const_cast(artifact); +} + +/// Test if two ABI artifacts are equal. +/// +/// This can be useful when used from the command line of a debugger +/// like GDB. +/// +/// @param l the first ABI artifact to consider in the comparison. +/// +/// @param r the second ABI artifact to consider in the comparison. +/// +/// @return true iff @p l equals @p r. +bool +debug_equals(const type_or_decl_base *l, const type_or_decl_base *r) +{ + if (!!l != !!r) + return false; + if (!l && !r) + return true; + + return (*l == *r); +} + /// By looking at the language of the TU a given ABI artifact belongs /// to, test if the ONE Definition Rule should apply. ///