2021-01-01 16:03:39 +08:00
|
|
|
/* Copyright (C) 2017-2021 Free Software Foundation, Inc.
|
Introduce gdb::byte_vector, add allocator that default-initializes
In some cases we've been replacing heap-allocated gdb_byte buffers
managed with xmalloc/make_cleanup(xfree) with gdb::vector<gdb_byte>.
That usually pessimizes the code a little bit because std::vector
value-initializes elements (which for gdb_byte means
zero-initialization), while if you're creating a temporary buffer,
you're most certaintly going to fill it in with some data. An
alternative is to use
unique_ptr<gdb_byte[]> buf (new gdb_byte[size]);
but it looks like that's not very popular.
Recently, a use of obstacks in dwarf2read.c was replaced with
std::vector<gdb_byte> and that as well introduced a pessimization for
always memsetting the buffer when it's garanteed that the zeros will
be overwritten immediately. (see dwarf2read.c change in this patch to
find it.)
So here's a different take at addressing this issue "by design":
#1 - Introduce default_init_allocator<T>
I.e., a custom allocator that does default construction using default
initialization, meaning, no more zero initialization. That's the
default_init_allocation<T> class added in this patch.
See "Notes" at
<http://en.cppreference.com/w/cpp/container/vector/resize>.
#2 - Introduce def_vector<T>
I.e., a convenience typedef, because typing the allocator is annoying:
using def_vector<T> = std::vector<T, gdb::default_init_allocator<T>>;
#3 - Introduce byte_vector
Because gdb_byte vectors will be the common thing, add a convenience
"byte_vector" typedef:
using byte_vector = def_vector<gdb_byte>;
which is really the same as:
std::vector<gdb_byte, gdb::default_init_allocator<gdb_byte>>;
The intent then is to make "gdb::byte_vector" be the go-to for dynamic
byte buffers. So the less friction, the better.
#4 - Adjust current code to use it.
To set the example going forward. Replace std::vector uses and also
unique_ptr<byte[]> uses.
One nice thing is that with this allocator, for changes like these:
-std::unique_ptr<byte[]> buf (new gdb_byte[some_size]);
+gdb::byte_vector buf (some_size);
fill_with_data (buf.data (), buf.size ());
the generated code is the same as before. I.e., the compiler
de-structures the vector and gets rid of the unused "reserved vs size"
related fields.
The other nice thing is that it's easier to write
gdb::byte_vector buf (size);
than
std::unique_ptr<gdb_byte[]> buf (new gdb_byte[size]);
or even (C++14):
auto buf = std::make_unique<gdb_byte[]> (size); // zero-initializes...
#5 - Suggest s/std::vector<gdb_byte>/gdb::byte_vector/ going forward.
Note that this commit actually fixes a couple of bugs where the current
code is incorrectly using "std::vector::reserve(new_size)" and then
accessing the vector's internal buffer beyond the vector's size: see
dwarf2loc.c and charset.c. That's undefined behavior and may trigger
debug mode assertion failures. With default_init_allocator,
"resize()" behaves like "reserve()" performance wise, in that it
leaves new elements with unspecified values, but, it does that safely
without triggering undefined behavior when you access those values.
gdb/ChangeLog:
2017-06-14 Pedro Alves <palves@redhat.com>
* ada-lang.c: Include "common/byte-vector.h".
(ada_value_primitive_packed_val): Use gdb::byte_vector.
* charset.c (wchar_iterator::iterate): Resize the vector instead
of reserving it.
* common/byte-vector.h: Include "common/def-vector.h".
(wchar_iterator::m_out): Now a gdb::def_vector<gdb_wchar_t>.
* cli/cli-dump.c: Include "common/byte-vector.h".
(dump_memory_to_file, restore_binary_file): Use gdb::byte_vector.
* common/byte-vector.h: New file.
* common/def-vector.h: New file.
* common/default-init-alloc.h: New file.
* dwarf2loc.c: Include "common/byte-vector.h".
(rw_pieced_value): Use gdb::byte_vector, and resize the vector
instead of reserving it.
* dwarf2read.c: Include "common/byte-vector.h".
(data_buf::m_vec): Now a gdb::byte_vector.
* gdb_regex.c: Include "common/def-vector.h".
(compiled_regex::compiled_regex): Use gdb::def_vector<char>.
* mi/mi-main.c: Include "common/byte-vector.h".
(mi_cmd_data_read_memory): Use gdb::byte_vector.
* printcmd.c: Include "common/byte-vector.h".
(print_scalar_formatted): Use gdb::byte_vector.
* valprint.c: Include "common/byte-vector.h".
(maybe_negate_by_bytes, print_decimal_chars): Use
gdb::byte_vector.
2017-06-14 18:08:52 +08:00
|
|
|
|
|
|
|
This file is part of GDB.
|
|
|
|
|
|
|
|
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 <http://www.gnu.org/licenses/>. */
|
|
|
|
|
|
|
|
#ifndef COMMON_DEFAULT_INIT_ALLOC_H
|
|
|
|
#define COMMON_DEFAULT_INIT_ALLOC_H
|
|
|
|
|
|
|
|
namespace gdb {
|
|
|
|
|
|
|
|
/* An allocator that default constructs using default-initialization
|
|
|
|
rather than value-initialization. The idea is to use this when you
|
|
|
|
don't want to default construct elements of containers of trivial
|
|
|
|
types using zero-initialization. */
|
|
|
|
|
|
|
|
/* Mostly as implementation convenience, this is implemented as an
|
|
|
|
adapter that given an allocator A, overrides 'A::construct()'. 'A'
|
|
|
|
defaults to std::allocator<T>. */
|
|
|
|
|
|
|
|
template<typename T, typename A = std::allocator<T>>
|
|
|
|
class default_init_allocator : public A
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
/* Pull in A's ctors. */
|
|
|
|
using A::A;
|
|
|
|
|
|
|
|
/* Override rebind. */
|
|
|
|
template<typename U>
|
|
|
|
struct rebind
|
|
|
|
{
|
|
|
|
/* A couple helpers just to make it a bit more readable. */
|
|
|
|
typedef std::allocator_traits<A> traits_;
|
|
|
|
typedef typename traits_::template rebind_alloc<U> alloc_;
|
|
|
|
|
|
|
|
/* This is what we're after. */
|
|
|
|
typedef default_init_allocator<U, alloc_> other;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Make the base allocator's construct method(s) visible. */
|
|
|
|
using A::construct;
|
|
|
|
|
|
|
|
/* .. and provide an override/overload for the case of default
|
|
|
|
construction (i.e., no arguments). This is where we construct
|
|
|
|
with default-init. */
|
|
|
|
template <typename U>
|
|
|
|
void construct (U *ptr)
|
|
|
|
noexcept (std::is_nothrow_default_constructible<U>::value)
|
|
|
|
{
|
|
|
|
::new ((void *) ptr) U; /* default-init */
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} /* namespace gdb */
|
|
|
|
|
|
|
|
#endif /* COMMON_DEFAULT_INIT_ALLOC_H */
|