From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id WWPWBE/OomrnEz0AWB0awg (envelope-from ) for ; Thu, 10 Sep 2026 11:35:43 -0400 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=UPNZpdGx; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 0C2AD1E09E; Thu, 10 Sep 2026 11:35:43 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 17CBA1E091 for ; Thu, 10 Sep 2026 11:35:42 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 9D56E48FE095 for ; Thu, 10 Sep 2026 15:35:41 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 9D56E48FE095 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=UPNZpdGx Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id CAD6E4902653 for ; Thu, 10 Sep 2026 15:35:15 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org CAD6E4902653 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org CAD6E4902653 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1789054515; cv=none; b=wQDtT6EBYQ9sk/I7mjrEnyImFsaP3cLvLbO4Vss2q2ZIIzaGnO5yXvTsSnMYCoU7Pzrp/j9NWizP+NMyTt6af7CoXgihuMbNOGYa+Uk5DYXIKLKTJig7sWNsDAwNT9IXCgD4jp5ySNkWrE/uU1ep7sqZybItqVV47l6tIGTrRVQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1789054515; c=relaxed/simple; bh=JNh5IBo9m2eMSAKiQkzto11JrlH7BQ2UmkCc2+zPMa4=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=ujYmqaZnxDM0/wJNtmctjMZu36TwGMDqXOveZfkF5IgAIQUzMpxLO0ug5/oeArx0f1irHW4ew8/QRK3q/t9M4D6e6mIh2liYCI7KsgGtDYppzKTqQ6MtrMb3OjmG2gRoF0q2cshGAosFe0oOo3Ls/0RojQg8joV4UwydyD7TxRc= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=UPNZpdGx DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org CAD6E4902653 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1789054515; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=u4yr8TrSvmPOAPtqbqtlHDvloo2Fzl7QWRB6MCvu7R4=; b=UPNZpdGxW3C07LnB/QpttM4LkAfTuKmBcgmuT+X+EdB/6zihya6Q7hQSoLhTqy81byjFUK qIR13LhKHu+/4BQ2gspXKgT9+Elxv/b3UCB3Zgd9XdeHK2jaxQ358VH5YmMFxG/ACOKYpZ ZUNfiiIzR53yDTn9D+MJevS3aQ5cMJ4= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-261-aww6in6CP8WDCsnABjZ3fg-1; Thu, 10 Sep 2026 11:35:13 -0400 X-MC-Unique: aww6in6CP8WDCsnABjZ3fg-1 X-Mimecast-MFC-AGG-ID: aww6in6CP8WDCsnABjZ3fg_1789054512 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-4957287363bso55015125e9.0 for ; Thu, 10 Sep 2026 08:35:13 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789054512; x=1789659312; h=content-type:mime-version:message-id:date:references:in-reply-to :subject:cc:to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to:content-type; bh=u4yr8TrSvmPOAPtqbqtlHDvloo2Fzl7QWRB6MCvu7R4=; b=PK3qoBdqwFPWRlPLz9rFwJhzGp/afKJQPUmdicdPqa8HRHR2ebfqX8co8B5tRQqGSW 6XpsSsdhoAJBnI63Zd0V8iIV7mmkaxUOaKKbQo2FRhFsD+V9w4gIxzCEufVHIoH5TqoS VXkbV4apk7KTsgq2NRAsFcxoKREB2dOgascUdJqSV9gH8c5O/3r4YJrOsTOGRrxC0h5U LiFtA2mIWcCxwaBdZTcUzCpPv9D5UlaVmL63dydkoOLu4iBLhyA9IKEN69jnR1e/5Rwa 1lts4jsJGUJj5d2EYhm3ts3DHtzBQaLnX4Bp4zaoY8BEpk29mnxqMWccbbxYJNfmR5OM BSaQ== X-Forwarded-Encrypted: i=1; AKwUvBzdXGO5Gd/jAlIv8km2J4AmCmYSuoufwpLHP9vYcFxwAZSCiCSLnSKZBSH9j8xCWBWRGMt1BzzKrNKXtA==@sourceware.org X-Gm-Message-State: AFuF++kcT4k7OS4Z6kHvahdItS64SyCnXGsjLmbHmsXY78wMqAhEz/z5 KvhLng9UzjprFCC1+SfGmiWUocRNHX/sCb02Z/zcG2GQz0lQ68uSNyLyqpLdQfp/U8WubRfq5oB uf+G/7VcWa0LROjx4kZp6+7ZzcZSwiorQf8oHU9mytspPACzQXdYXQWjkwAriqsk= X-Gm-Gg: AYBFou1SEvZAP6V6maIwXWeO8ad/NPN9CadHvUFWXT6Ofp9tYj7y3K1fnkZ/uIyF4pF Ci3RUxIU7hCeAQV9iPSuJfAn0uKQiI8X2LYJfA+mK2d21QpPUaave+ZF/APQ4bh3m2piesEsdAi J1xW76KDNAHNF+e4SxMhmjsJkKtZ6NlOWsNlEg299ZZ1io/xZyVnqfc4A55DLZ4bitnrdRnFiAm gj/9VGibnRPjyC7kNfgY2olxpFk+09n7Y8sdcxa37uGTnXNzvOXsp4ggi0BVLhMFmb2Ikdc+NGr +AcUrShWvGauYvqm5ZUXbVm5OJbCn5F6wsFqYxhQYZWu7FD3jv3BJaUMxc2iyyw4eL2oPBsCwDj l3xnjaIbN0h68OLm7 X-Received: by 2002:a05:600c:4e49:b0:49c:fa20:cc05 with SMTP id 5b1f17b1804b1-49cfa20ccd2mr405003485e9.28.1789054512342; Thu, 10 Sep 2026 08:35:12 -0700 (PDT) X-Received: by 2002:a05:600c:4e49:b0:49c:fa20:cc05 with SMTP id 5b1f17b1804b1-49cfa20ccd2mr405000495e9.28.1789054510265; Thu, 10 Sep 2026 08:35:10 -0700 (PDT) Received: from localhost (59.6.93.209.dyn.plus.net. [209.93.6.59]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49d26be3e35sm78586385e9.2.2026.09.10.08.35.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 10 Sep 2026 08:35:09 -0700 (PDT) From: Andrew Burgess To: Matthieu Longo , gdb-patches@sourceware.org Cc: Simon Marchi , Thiago Jung Bauermann , Luis Machado , Luis Machado , Christina Joos , Kevin Buettner , Matthieu Longo Subject: Re: [PATCH v2 3/6] gdb: introduce helper class file_reader_t In-Reply-To: <20260825100912.514232-4-matthieu.longo@arm.com> References: <20260825100912.514232-1-matthieu.longo@arm.com> <20260825100912.514232-4-matthieu.longo@arm.com> Date: Thu, 10 Sep 2026 16:35:08 +0100 Message-ID: <87cxuldsr7.fsf@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: JVzVqayoa8QfTM4QMhkxSD8JoYD1IodjWLZHJ-JLXqA_1789054512 X-Mimecast-Originator: redhat.com Content-Type: text/plain X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org Matthieu Longo writes: > Wrap all the boilerplate code required to read a file in a new helper > class: file_reader_t. The class owns the file contents together with > the file path, and provides convenient accessors for the data, size and > typed views. It supports both null-terminated text files and binary files. > > This helper eliminates repeated calls to target_fileio_read_stralloc > and target_fileio_read_alloc, remove explicit memory management with s/remove/removes/ > gdb::unique_xmalloc_ptr, and simplifies the casting logic when working > with binary data. > > The patch converts some of the existing Linux, AMD64, and SPARC code that > reads files from /proc to use file_reader_t. As a side effect, > amd64_linux_lam_untag_mask and linux_process_address_in_memtag_page > may now return earlier in case the file is empty. > > Reviewed-By: Thiago Jung Bauermann > Reviewed-By: Christina Joos > --- > gdb/amd64-linux-tdep.c | 12 ++-- > gdb/linux-tdep.c | 122 ++++++++++++++++++----------------------- > gdb/sparc64-tdep.c | 15 +++-- > gdb/target.h | 78 ++++++++++++++++++++++++++ > 4 files changed, 143 insertions(+), 84 deletions(-) > > diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c > index 9b23db72bbe..52f16c953d1 100644 > --- a/gdb/amd64-linux-tdep.c > +++ b/gdb/amd64-linux-tdep.c > @@ -1848,14 +1848,11 @@ amd64_linux_lam_untag_mask () > if (inf->fake_pid_p) > return DEFAULT_TAG_MASK; > > - const std::string filename = string_printf ("/proc/%d/status", inf->pid); > - gdb::unique_xmalloc_ptr status_file > - = target_fileio_read_stralloc (nullptr, filename.c_str ()); > - > - if (status_file == nullptr) > + file_reader_t proc_status (string_printf ("/proc/%d/status", inf->pid)); > + if (!proc_status) > return DEFAULT_TAG_MASK; > > - std::string_view status_file_view (status_file.get ()); > + std::string_view status_file_view (proc_status.data ()); > constexpr std::string_view untag_mask_str = "untag_mask:\t"; > const size_t found = status_file_view.find (untag_mask_str); > if (found != std::string::npos) > @@ -1867,7 +1864,8 @@ amd64_linux_lam_untag_mask () > unsigned long long result = std::strtoul (start, &endptr, 0); > if (errno != 0 || endptr == start) > error (_("Failed to parse untag_mask from file %ps."), > - styled_string (file_name_style.style (), filename.c_str ())); > + styled_string (file_name_style.style (), > + proc_status.c_filepath ())); > > return result; > } > diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c > index 588984a1ca4..84614bc91a0 100644 > --- a/gdb/linux-tdep.c > +++ b/gdb/linux-tdep.c > @@ -1551,7 +1551,7 @@ parse_smaps_key_value (const char *keyword, const char *line, > DATA is the contents of the smaps file. The parsed contents are stored > into the SMAPS vector. */ > > -static std::vector > +static std::vector Throughout this patch there's a bunch of places where you've done nothing but delete the 'struct' prefix. It's OK to do this in code that you're touching anyway as part of this patch, but any, like this, that are in code that you'd not otherwise touch, are unrelated changes and should be moved into a separate patch. I think you should either drop these, or have a first patch which does a "remove some struct prefixes" cleanup, your choice. > @@ -2346,27 +2343,24 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p) > p->pr_pid = ptid.pid (); > > /* Copying the program name. Only the basename matters. */ > - basename = lbasename (fname.get ()); > + basename = lbasename (cmdline.data ()); > strncpy (p->pr_fname, basename, sizeof (p->pr_fname) - 1); > p->pr_fname[sizeof (p->pr_fname) - 1] = '\0'; > > const std::string &infargs = current_inferior ()->args (); > > /* The arguments of the program. */ > - std::string psargs = fname.get (); > + std::string psargs = cmdline.data (); > if (!infargs.empty ()) > psargs += ' ' + infargs; > > strncpy (p->pr_psargs, psargs.c_str (), sizeof (p->pr_psargs) - 1); > p->pr_psargs[sizeof (p->pr_psargs) - 1] = '\0'; > > - xsnprintf (filename, sizeof (filename), "/proc/%ld/stat", ptid.lwp ()); > - /* The contents of `/proc/PID/stat'. */ > - gdb::unique_xmalloc_ptr proc_stat_contents > - = target_fileio_read_stralloc (NULL, filename); > - char *proc_stat = proc_stat_contents.get (); > - > - if (proc_stat == NULL || *proc_stat == '\0') > + file_reader_t stat_freader > + (string_printf ("/proc/%ld/stat", ptid.lwp ())); > + const char *proc_stat = stat_freader.data (); > + if (!stat_freader || *proc_stat == '\0') We access the data here before checking if the read was successful. This works fine, but doesn't seem ideal. Later on I suggest that maybe file_reader_t::data should assert that we're no in the error state, and this is what I was looking at when I started thinking about that. If you really think we should support reading data when in an error state, then the data method should document what the return value is when the file_reader_t is in the error state. > diff --git a/gdb/target.h b/gdb/target.h > index 819279c08fc..017918b6582 100644 > --- a/gdb/target.h > +++ b/gdb/target.h > @@ -2341,6 +2341,84 @@ extern LONGEST target_fileio_read_alloc (struct inferior *inf, > extern gdb::unique_xmalloc_ptr target_fileio_read_stralloc > (struct inferior *inf, const char *filename, LONGEST *len = nullptr); > > +/* Helper class for reading the content of a file on the target. */ > +template > +class file_reader_t I'm pretty sure that types ending with _t are reserved by ... some spec. We should avoid this and ideally, pick a name that better describes what the class does, e.g. target_file_reader. > +{ > + /* The filepath of the file being read. */ > + std::string m_filepath; The ship has mostly sailed already, but "path" should be used for lists of locations, like the $PATH variable. It would be better to just use filename, m_filename, etc. I am fully aware that 'path' is used throughout GDB in place of filename, but we might as well avoid adding another here. This should be fixed throughout this class. > + /* Smart pointer to the data. */ > + gdb::unique_xmalloc_ptr m_data; > + /* Number of bytes read. */ > + LONGEST m_size; GDB style usually puts a space between member variables, e.g.: /* The filepath of the file being read. */ std::string m_filepath; /* Smart pointer to the data. */ gdb::unique_xmalloc_ptr m_data; /* Number of bytes read. */ LONGEST m_size; > + > +public: > + file_reader_t (const std::string &filepath) > + : m_filepath (filepath) > + , m_size (0) > + { > + if constexpr (std::is_same_v) > + m_data = target_fileio_read_stralloc (nullptr, m_filepath.c_str (), > + &m_size); > + else > + { > + gdb_byte *buf = nullptr; > + m_size = target_fileio_read_alloc (nullptr, m_filepath.c_str (), &buf); > + m_data = gdb::unique_xmalloc_ptr (reinterpret_cast(buf)); > + } > + } There's a bug hiding in here when T is not 'char'. If the file being read is empty then target_fileio_read_alloc returns 0 but leaves *BUF unchanged, i.e. as nullptr. Given that, despite successfully reading the empty file, empty() will return false and error() will return true. Also, given this is being written as a general helper class, it might be a good idea to define how the inferior is passed in, rather than leaving that for future users to do. > + > + file_reader_t (file_reader_t &&) = default; > + file_reader_t &operator= (file_reader_t &&) = default; > + > + DISABLE_COPY_AND_ASSIGN (file_reader_t); > + > + /* Return true if the file was read successfully but contained no data. */ > + bool empty () const noexcept > + { return m_data != nullptr && m_size == 0; } > + > + /* Return true if the file could not be read. */ > + bool error () const noexcept > + { return m_data == nullptr || m_size < 0; } > + > + /* Return true if the file was read successfully and is non-empty. */ > + explicit operator bool () const noexcept > + { return !(error () || empty ()); } I'm really not a fan of this API. Consider this code from earlier in this patch: file_reader_t proc_status (string_printf ("/proc/%d/status", inf->pid)); if (!proc_status) return DEFAULT_TAG_MASK; I don't think it's obvious that !proc_status means error or empty. I think a much less error prone API would be to just add a new member function: bool empty_or_error () const noexcept { return this->empty () || this->error (); } And then use that. It's more typing for sure, but it's also crystal clear what's going on. > + > + /* Return a pointer to the data. */ > + T *data () const noexcept > + { return m_data.get (); } Might be a good idea to assert that we're not in the error state. > + > + /* Return the number of bytes read. */ > + LONGEST size () const noexcept > + { > + /* For char buffers, size() corresponds to the size of the read data. Some > + null-terminator characters are possibly scattered throughout the data. > + Consequently, strlen() might not reflect the actual size. */ > + return m_size; > + } Again, maybe assert that we're not in the error state. I think there's only one user of this right now, and it already checks for errors before calling size. > + > + /* Return a span of the data. */ > + gdb::array_view view () const noexcept > + { return gdb::array_view (m_data.get (), size ()); } > + > + /* Return a span of the data, reinterpreted as U objects. */ > + template > + gdb::array_view cast_view () const noexcept > + { > + return gdb::array_view (reinterpret_cast (m_data.get ()), > + size () * sizeof (T) / sizeof (U)); > + } It would be a good idea to say in the comment what happens if the file size is not a multiple of 'sizeof (U)'. Or do we even want to support this case? Thanks, Andrew