gdbsupport: remove xmalloc in format_pieces

Remove the use of xmalloc (and the arbitrary allocation size) in
format_pieces.  This turned out a bit more involved than expected, but
not too bad.

format_pieces::m_storage is a buffer with multiple concatenated
null-terminated strings, referenced by format_piece::string.  Change
this to an std::string, while keeping its purpose (use the std::string
as a buffer with embedded null characters).

However, because the std::string's internal buffer can be reallocated as
it grows, and I do not want to hardcode a big reserved size like we have
now, it's not possible to store the direct pointer to the string in
format_piece::string.  Those pointers would become stale as the buffer
gets reallocated.  Therefore, change format_piece to hold an index into
the storage instead.  Add format_pieces::piece_str for the callers to be
able to access the piece's string.  This requires changing the few
callers, but in a trivial way.

The selftest also needs to be updated.  I want to keep the test cases
as-is, where the expected pieces contain the expected string, and not
hard-code an expected index.  To achieve this, add the
expected_format_piece structure.  Note that the previous
format_piece::operator== didn't compare the n_int_args fields, while the
test provides expected values for that field.  I guess that was a
mistake.  The new code checks it, and the test still passes.

Change-Id: I80630ff60e01c8caaa800ae22f69a9a7660bc9e9
Reviewed-By: Keith Seitz <keiths@redhat.com>
This commit is contained in:
Simon Marchi
2025-08-20 12:50:08 -04:00
committed by Simon Marchi
parent 51b281ccfa
commit bd21dd6807
6 changed files with 89 additions and 86 deletions

View File

@@ -97,10 +97,6 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions,
*arg = s;
}
/* Need extra space for the '\0's. Doubling the size is sufficient. */
char *current_substring = (char *) xmalloc (strlen (string) * 2 + 1000);
m_storage.reset (current_substring);
/* Now scan the string for %-specs and see what kinds of args they want.
argclass classifies the %-specs so we can give printf-type functions
something of the right size. */
@@ -126,13 +122,12 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions,
continue;
}
const char *sub_start = current_substring;
std::string::size_type sub_start = m_storage.size ();
strncpy (current_substring, prev_start, f - 1 - prev_start);
current_substring += f - 1 - prev_start;
*current_substring++ = '\0';
m_storage.append (prev_start, f - 1 - prev_start);
m_storage += '\0';
if (*sub_start != '\0')
if (m_storage[sub_start] != '\0')
m_pieces.emplace_back (sub_start, literal_piece, 0);
const char *percent_loc = f - 1;
@@ -374,7 +369,7 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions,
f++;
sub_start = current_substring;
sub_start = m_storage.size ();
if (lcount > 1 && !seen_i64 && USE_PRINTF_I64)
{
@@ -382,11 +377,9 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions,
Convert %lld to %I64d. */
int length_before_ll = f - percent_loc - 1 - lcount;
strncpy (current_substring, percent_loc, length_before_ll);
strcpy (current_substring + length_before_ll, "I64");
current_substring[length_before_ll + 3] =
percent_loc[length_before_ll + lcount];
current_substring += length_before_ll + 4;
m_storage.append (percent_loc, length_before_ll);
m_storage += "I64";
m_storage += percent_loc[length_before_ll + lcount];
}
else if (this_argclass == wide_string_arg
|| this_argclass == wide_char_arg)
@@ -394,18 +387,13 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions,
/* Convert %ls or %lc to %s. */
int length_before_ls = f - percent_loc - 2;
strncpy (current_substring, percent_loc, length_before_ls);
strcpy (current_substring + length_before_ls, "s");
current_substring += length_before_ls + 2;
m_storage.append (percent_loc, length_before_ls);
m_storage += "s";
}
else
{
strncpy (current_substring, percent_loc, f - percent_loc);
current_substring += f - percent_loc;
}
*current_substring++ = '\0';
m_storage.append (percent_loc, f - percent_loc);
m_storage += '\0';
prev_start = f;
m_pieces.emplace_back (sub_start, this_argclass, n_int_args);
@@ -415,11 +403,9 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions,
if (f > prev_start)
{
const char *sub_start = current_substring;
strncpy (current_substring, prev_start, f - prev_start);
current_substring += f - prev_start;
*current_substring++ = '\0';
std::string::size_type sub_start = m_storage.size ();
m_storage.append (prev_start, f - prev_start);
/* No need for a final '\0', std::string already has one. */
m_pieces.emplace_back (sub_start, literal_piece, 0);
}

View File

@@ -20,8 +20,6 @@
#ifndef GDBSUPPORT_FORMAT_H
#define GDBSUPPORT_FORMAT_H
#include <string_view>
#if defined(__MINGW32__) && !defined(PRINTF_HAS_LONG_LONG)
# define USE_PRINTF_I64 1
# define PRINTF_HAS_LONG_LONG
@@ -51,21 +49,15 @@ enum argclass
struct format_piece
{
format_piece (const char *str, enum argclass argc, int n)
: string (str),
format_piece (std::string::size_type start, enum argclass argc, int n)
: start (start),
argclass (argc),
n_int_args (n)
{
gdb_assert (str != nullptr);
}
{}
bool operator== (const format_piece &other) const
{
return (this->argclass == other.argclass
&& std::string_view (this->string) == other.string);
}
/* Where this piece starts, within FORMAT_PIECES::M_STORAGE. */
std::string::size_type start;
const char *string;
enum argclass argclass;
/* Count the number of preceding 'int' arguments that must be passed
along. This is used for a width or precision of '*'. Note that
@@ -95,10 +87,17 @@ public:
return m_pieces.end ();
}
/* Return the string associated to PIECE. */
const char *piece_str (const format_piece &piece)
{ return &m_storage[piece.start]; }
private:
std::vector<format_piece> m_pieces;
gdb::unique_xmalloc_ptr<char> m_storage;
/* This is used as a buffer of concatenated null-terminated strings. The
individual strings are referenced by FORMAT_PIECE::START. */
std::string m_storage;
};
#endif /* GDBSUPPORT_FORMAT_H */