pw_allocator#
This module provides various building blocks for a dynamic allocator. This is composed of the following parts:
block
: An implementation of a linked list of memory blocks, supporting splitting and merging of blocks.freelist
: A freelist, suitable for fast lookups of available memory chunks (i.e.block
s).
Heap Integrity Check#
The Block
class provides two check functions:
-
class Block#
The
Block
type is intended to be a building block component for allocators.In this design, there is an explicit pointer to
Next
andPrev
from the block header; the size is not encoded. The below diagram shows what this would look like for two blocks..------+---------------------------------.----------------------------- | Block A (first) | Block B (second) +------+------+--------------------------+------+------+--------------- | Next | Prev | usable space | Next | Prev | usable space.. +------+------+--------------------------+------+--+---+--------------- ^ | ^ | | '-------------------------------------' | | | '----------- Block B's prev points to Block A -----'
One use for these blocks is to use them as allocations, where each block represents an allocation handed out by
malloc()
. These blocks could also be used as part of a slab or buddy allocator.Each block also contains flags for whether it is the last block (i.e. whether the
Next
pointer points to a valid block, or just denotes the end of this block), and whether the block is in use. These are encoded into the last two bits of theNext
pointer, as follows:.-----------------------------------------------------------------------. | Block | +-----------------------------------------------------------------------+ | Next | Prev | usable space | +----------------+------+------+ + | | Ptr[N..2] | Last | Used | | | +----------------+------+------+------+---------------------------------+ ^ | '----------- Next() = Next & ~0x3 --------------------------------->
The first block in a chain is denoted by a nullptr
Prev
field, and the last block is denoted by theLast
bit being set.Note, this block class requires that the given block is aligned to an
alignof(Block*)
boundary. Because of this alignment requirement, each returned block will also be aligned to analignof(Block*)
boundary, and the size will always be rounded up to a multiple ofalignof(Block*)
.This class must be constructed using the static
Init
call.Public Functions
-
inline size_t OuterSize() const#
- Returns:
The total size of the block in bytes, including the header.
-
inline size_t InnerSize() const#
- Returns:
The number of usable bytes inside the block.
-
inline std::byte *UsableSpace()#
- Returns:
A pointer to the usable space inside this block.
-
Status Split(size_t head_block_inner_size, Block **new_block)#
Split this block, such that this block has an inner size of
head_block_inner_size
, and return a new block in the remainder of the space innew_block
.The
remainder
block will be aligned to analignof(Block*)
boundary (andhead_block_inner_size
will be rounded up). If the remaining space is not large enough to store a newBlock
after rounding, no splitting will occur.- Returns:
One of the the following:
OK
: The split completed successfully.INVALID_ARGUMENT
:new_block
isnull
.FAILED_PRECONDITION
: This block is in use and cannot be split.OUT_OF_RANGE
: The requested size for this block is greater than the currentinner_size
.RESOURCE_EXHAUSTED
: The split cannot occur because theremainder
block would not be large enough to store a block header.
-
Status MergeNext()#
Merges this block with the one that comes after it.
- return:
One of the following:
OK
: The merge was successful.OUT_OF_RANGE
: Attempting to merge the last block failed.FAILED_PRECONDITION
: The blocks could not be merged because one of them was in use.
- Pre:
The blocks must not be in use.
-
Status MergePrev()#
Merges this block with the one that comes before it.
- return:
One of the following:
OK
: The merge was successful.OUT_OF_RANGE
: Attempting to merge the first block failed.FAILED_PRECONDITION
: The blocks could not be merged because one of them was in use.
Warning
Merging with a previous block invalidates this block instance. Do not perform any operations on this instance after merging.
- Pre:
The blocks must not be in use.
-
inline bool Used() const#
Indicates whether the block is in use.
- Returns:
true
if the block is in use orfalse
if not.
-
inline bool Last() const#
Indicates whether this block is the last block or not (i.e. whether
NextBlock()
points to a valid block or not). This is needed becauseNextBlock()
points to the end of this block, whether there is a valid block there or not.- Returns:
true
is this is the last block orfalse
if not.
-
inline void MarkUsed()#
Marks this block as in use.
-
inline void MarkFree()#
Marks this block as free.
-
inline void MarkLast()#
Marks this block as the last one in the chain.
-
inline void ClearLast()#
Clears the last bit from this block.
-
inline Block *Prev() const#
- Returns:
The block immediately before this one. Returns a null pointer if this is the first block.
-
inline bool IsValid() const#
Checks if a block is valid.
- Returns:
false
if a block is corrupted. Returnstrue
if the following conditions are all true:The block is aligned
The prev/next fields match with the previous and next blocks
The poisoned bytes are not damaged
-
void CrashIfInvalid()#
Crashes if a block is invalid. Uses
PW_DCHECK
to log information about why the block is invalid. Does nothing if the block is valid.
Public Static Functions
-
static Status Init(const span<std::byte> region, Block **block)#
Creates the first block for a given memory region.
- return:
INVALID_ARGUMENT
if the given region is unaligned to too small, orOK
otherwise.
- Pre:
The start of the given memory region must be aligned to an
alignof(Block)
boundary.
-
static inline Block *FromUsableSpace(std::byte *usable_space)#
Warning
This method does not do any checking; passing a random pointer will return a non-null pointer.
- Returns:
A pointer to a
Block
, given a pointer to the start of the usable space inside the block. In other words, this operation is the opposite ofUsableSpace()
. In reality, this method just subtracts the appropriate amount fromusable_space
to point to the start of the owning block.
-
inline size_t OuterSize() const#
FreeList#
-
class FreeList#
Basic freelist implementation for an allocator. This implementation buckets by chunk size, with a list of user-provided buckets. Each bucket is a linked list of storage chunks. Because this freelist uses the added chunks themselves as list nodes, there is a lower bound of
sizeof(FreeList.FreeListNode)
bytes for chunks which can be added to this freelist. There is also an implicit bucket for “everything else”, for chunks which do not fit into a bucket.Each added chunk will be added to the smallest bucket under which it fits. If it does not fit into any user-provided bucket, it will be added to the default bucket.
As an example, assume that the
FreeList
is configured with buckets of sizes {64, 128, 256, and 512} bytes. The internal state may look like the following:bucket[0] (64B) --> chunk[12B] --> chunk[42B] --> chunk[64B] --> NULL bucket[1] (128B) --> chunk[65B] --> chunk[72B] --> NULL bucket[2] (256B) --> NULL bucket[3] (512B) --> chunk[312B] --> chunk[512B] --> chunk[416B] --> NULL bucket[4] (implicit) --> chunk[1024B] --> chunk[513B] --> NULL
Note that added chunks should be aligned to a 4-byte boundary.
This class is split into two parts:
FreeList
implements all of the logic, and takes in pointers for twopw::Vector
instances for its storage. This prevents us from having to specialise this class for everykMaxSize
parameter for the vector.FreeListBuffer
then provides the storage for these twopw::Vector
instances and instantiatesFreeListInternal
.
Subclassed by pw::allocator::FreeListBuffer< kNumBuckets >
Public Functions
-
Status AddChunk(span<std::byte> chunk)#
Adds a chunk to this freelist.
- Returns:
OK
- The chunk was added successfully.OUT_OF_RANGE
- The chunk could not be added for size reasons (e.g. the chunk is too small to store theFreeListNode
).
-
span<std::byte> FindChunk(size_t size) const#
Finds an eligible chunk for an allocation of size
size
.Note
This returns the first allocation possible within a given bucket; It does not currently optimize for finding the smallest chunk.
- Returns:
On success - A span representing the chunk.
On failure (e.g. there were no chunks available for that allocation) - A span with a size of 0.
Heap Poisoning#
Pigweed AI summary: The module disables heap poisoning by default due to the need for extra space, but users can enable it by using the build argument "pw_allocator_POISON_HEAP". When enabled, the allocator adds bytes before and after the usable space of each block and paints it with a randomized pattern. It checks if the pattern remains intact during each check and returns false if it is damaged.
By default, this module disables heap poisoning since it requires extra space.
User can enable heap poisoning by enabling the pw_allocator_POISON_HEAP
build arg.
$ gn args out
# Modify and save the args file to use heap poison.
pw_allocator_POISON_HEAP = true
When heap poisoning is enabled, pw_allocator
will add sizeof(void*)
bytes before and after the usable space of each Block
, and paint the space
with a hard-coded randomized pattern. During each check, pw_allocator
will check if the painted space still remains the pattern, and return false
if the pattern is damaged.
Heap Visualizer#
Pigweed AI summary: The Heap Visualizer is a tool provided by pw_allocator that allows users to visualize the state of the heap at the end of a dump file. The heap is represented by ASCII characters, with each character representing 4 bytes in the heap. The tool can be launched from a shell using the Pigweed environment, and requires arguments such as the path of the dump file and the start and end addresses of the heap. Options include enabling heap poisoning and specifying the size of a pointer. The tool and its
Functionality#
Pigweed AI summary: The pw_allocator provides a command called "pw heap-viewer" that helps visualize the state of the heap at the end of a dump file. The heap is represented by ASCII characters, with each character representing 4 bytes in the heap. An image is included to demonstrate this functionality.
pw_allocator
supplies a pw command pw heap-viewer
to help visualize
the state of the heap at the end of a dump file. The heap is represented by
ASCII characters, where each character represents 4 bytes in the heap.

Usage#
Pigweed AI summary: The heap visualizer can be launched from a shell using the Pigweed environment. The required arguments include the path of a file that contains malloc/free information, the start and end of the heap. Options include enabling heap poisoning and specifying the size of a pointer. The module and its documentation are currently incomplete and experimental.
The heap visualizer can be launched from a shell using the Pigweed environment.
$ pw heap-viewer --dump-file <directory of dump file> --heap-low-address
<hex address of heap lower address> --heap-high-address <hex address of heap
lower address> [options]
The required arguments are:
--dump-file
is the path of a file that containsmalloc/free
information. Each line in the dump file represents amalloc/free
call.malloc
is represented asm <size> <memory address>
andfree
is represented asf <memory address>
. For example, a dump file should look like:m 20 0x20004450 # malloc 20 bytes, the pointer is 0x20004450 m 8 0x2000447c # malloc 8 bytes, the pointer is 0x2000447c f 0x2000447c # free the pointer at 0x2000447c ...
Any line not formatted as the above will be ignored.
--heap-low-address
is the start of the heap. For example:--heap-low-address 0x20004440
--heap-high-address
is the end of the heap. For example:--heap-high-address 0x20006040
Options include the following:
--poison-enable
: If heap poisoning is enabled during the allocation or not. The value isFalse
if the option is not specified andTrue
otherwise.--pointer-size <integer of pointer size>
: The size of a pointer on the machine wheremalloc/free
is called. The default value is4
.
Note, this module, and its documentation, is currently incomplete and experimental.