pw_kvs#

Lightweight, persistent key-value store

Stable C++17 Code Size Impact: ~12 kB

#include <cstddef>

#include "pw_kvs/flash_test_partition.h"
#include "pw_kvs/key_value_store.h"
// Not a required dep; just here for demo comms
#include "pw_sys_io/sys_io.h"

// Create our key-value store (KVS). Sector and entry vals for this
// demo are based on @pigweed//pw_kvs:fake_flash_64_aligned_partition
constexpr size_t kMaxSectors = 6;
constexpr size_t kMaxEntries = 64;
static constexpr pw::kvs::EntryFormat kvs_format = {
  .magic = 0xd253a8a9,  // Prod apps should use a random number here
  .checksum = nullptr
};
pw::kvs::KeyValueStoreBuffer<kMaxEntries, kMaxSectors> kvs(
  &pw::kvs::FlashTestPartition(),
  kvs_format
);

kvs.Init();  // Initialize our KVS
std::byte in;
pw::sys_io::ReadByte(&in).IgnoreError();  // Get a char from the user
kvs.Put("in", in);  // Save the char to our key-value store (KVS)
std::byte out;
kvs.Get("in", &out);  // Test that the KVS stored the data correctly
pw::sys_io::WriteByte(out).IgnoreError();  // Echo the char back out
cc_binary(
    name = "app",
    srcs = ["app.cc"],
    # ...
    deps = [
        # ...
        "@pigweed//pw_kvs",
        "@pigweed//pw_kvs:fake_flash_64_aligned_partition",
        # ...
    ]
    # ...
)

pw_kvs is a flash-backed, persistent key-value storage (KVS) system with integrated wear leveling. It’s a relatively lightweight alternative to a file system.

Get started

Integrate pw_kvs into your project

Reference

pw_kvs API reference

Design

How pw_kvs manages state, does garbage collection, etc.

Code size analysis

The code size impact of using pw_kvs in your project

Get started#

Add @pigweed//pw_kvs to your target’s deps:

cc_binary(
  # ...
  deps = [
    # ...
    "@pigweed//pw_kvs",
    # ...
  ]
)

This assumes that your Bazel WORKSPACE has a repository named @pigweed that points to the upstream Pigweed repository.

Add $dir_pw_kvs to the deps list in your pw_executable() build target:

pw_executable("...") {
  # ...
  deps = [
    # ...
    "$dir_pw_kvs",
    # ...
  ]
}

Link your library to pw_kvs:

add_library(my_lib ...)
target_link_libraries(my_lib PUBLIC pw_kvs)

Use pw_kvs in your C++ code:

#include "pw_kvs/key_value_store.h"

// ...

Implement the flash memory and flash partition interfaces for your hardware. See pw_kvs/flash_memory.h.

Reference#

pw::kvs::KeyValueStore#

See Design for architectural details.

class KeyValueStore#

Flash-backed persistent key-value store (KVS) with integrated wear-leveling.

Instances are declared as instances of pw::kvs::KeyValueStoreBuffer<MAX_ENTRIES, MAX_SECTORS>, which allocates buffers for tracking entries and flash sectors.

#include "pw_kvs/key_value_store.h"
#include "pw_kvs/flash_test_partition.h"

constexpr size_t kMaxSectors = 6;
constexpr size_t kMaxEntries = 64;
static constexpr pw::kvs::EntryFormat kvs_format = {
  .magic = 0xd253a8a9,  // Prod apps should use a random number here
  .checksum = nullptr
};
pw::kvs::KeyValueStoreBuffer<kMaxEntries, kMaxSectors> kvs(
  &pw::kvs::FlashTestPartition(),
  kvs_format
);

kvs.Init();

Subclassed by pw::kvs::KeyValueStoreBuffer< kMaxEntries, kMaxUsableSectors, kRedundancy, kEntryFormats >

Public Functions

Status Init()#

Initializes the KVS. Must be called before calling other functions.

Returns:

Code

Description

OK

The KVS successfully initialized.

DATA_LOSS

The KVS initialized and is usable, but contains corrupt data.

UNKNOWN

Unknown error. The KVS is not initialized.

StatusWithSize Get(std::string_view key, span<std::byte> value, size_t offset_bytes = 0) const#

Reads the value of an entry in the KVS. The value is read into the provided buffer and the number of bytes read is returned. Reads can be started at an offset.

Parameters:
  • key[in] The name of the key.

  • value[out] The buffer to read the key’s value into.

  • offset_bytes[in] The byte offset to start the read at. Optional.

Returns:

Code

Description

OK

The entry was successfully read.

NOT_FOUND

The key is not present in the KVS.

DATA_LOSS

Found the entry, but the data was corrupted.

RESOURCE_EXHAUSTED

The buffer could not fit the entire value, but as many bytes as possible were written to it. The number of of bytes read is returned. The remainder of the value can be read by calling Get() again with an offset.

FAILED_PRECONDITION

The KVS is not initialized. Call Init() before calling this method.

INVALID_ARGUMENT

key is empty or too long, or value is too large.

template<typename Pointer, typename = std::enable_if_t<std::is_pointer<Pointer>::value>>
inline Status Get(
const std::string_view &key,
const Pointer &pointer,
) const#

Overload of Get() that accepts a pointer to a trivially copyable object.

If value is an array, call Get() with as_writable_bytes(span(array)), or pass a pointer to the array instead of the array itself.

template<typename T, typename std::enable_if_t<ConvertsToSpan<T>::value>* = nullptr>
inline Status Put(
const std::string_view &key,
const T &value,
)#

Adds a key-value entry to the KVS. If the key was already present, its value is overwritten.

Parameters:
  • key[in] The name of the key. All keys in the KVS must have a unique hash. If the hash of your key matches an existing key, nothing is added and ALREADY_EXISTS is returned.

  • value[in] The value for the key. This can be a span of bytes or a trivially copyable object.

Returns:

Code

Description

OK

The entry was successfully added or updated.

DATA_LOSS

Checksum validation failed after writing data.

RESOURCE_EXHAUSTED

Not enough space to add the entry.

ALREADY_EXISTS

The entry could not be added because a different key with the same hash is already in the KVS.

FAILED_PRECONDITION

The KVS is not initialized. Call Init() before calling this method.

INVALID_ARGUMENT

key is empty or too long, or value is too large.

Status Delete(std::string_view key)#

Removes a key-value entry from the KVS.

Parameters:

key[in] - The name of the key-value entry to delete.

Returns:

Code

Description

OK

The entry was successfully deleted.

NOT_FOUND

key is not present in the KVS.

DATA_LOSS

Checksum validation failed after recording the erase.

RESOURCE_EXHAUSTED

Insufficient space to mark the entry as deleted.

FAILED_PRECONDITION

The KVS is not initialized. Call Init() before calling this method.

INVALID_ARGUMENT

key is empty or too long.

StatusWithSize ValueSize(std::string_view key) const#

Returns the size of the value corresponding to the key.

Parameters:

key[in] - The name of the key.

Returns:

Code

Description

OK

The size was returned successfully.

NOT_FOUND

key is not present in the KVS.

DATA_LOSS

Checksum validation failed after reading the entry.

FAILED_PRECONDITION

The KVS is not initialized. Call Init() before calling this method.

INVALID_ARGUMENT

key is empty or too long.

inline Status HeavyMaintenance()#

Performs all maintenance possible, including all needed repairing of corruption and garbage collection of reclaimable space in the KVS. When configured for manual recovery, this (along with FullMaintenance()) is the only way KVS repair is triggered.

Warning

Performs heavy garbage collection of all reclaimable space, regardless of whether there’s other valid data in the sector. This method may cause a significant amount of moving of valid entries.

inline Status FullMaintenance()#

Perform all maintenance possible, including all needed repairing of corruption and garbage collection of reclaimable space in the KVS. When configured for manual recovery, this (along with HeavyMaintenance()) is the only way KVS repair is triggered.

Does not garbage collect sectors with valid data unless the KVS is mostly full.

Status PartialMaintenance()#

Performs a portion of KVS maintenance. If configured for at least lazy recovery, will do any needed repairing of corruption. Does garbage collection of part of the KVS, typically a single sector or similar unit that makes sense for the KVS implementation.

iterator begin() const#
Returns:

The first key-value entry in the container. Used for iteration.

inline iterator end() const#
Returns:

The last key-value entry in the container. Used for iteration.

inline size_t size() const#
Returns:

The number of valid entries in the KVS.

inline size_t total_entries_with_deleted() const#
Returns:

The number of valid entries and deleted entries yet to be collected.

inline size_t max_size() const#
Returns:

The maximum number of KV entries that’s possible in the KVS.

inline size_t empty() const#
Returns:

true if the KVS is empty.

inline uint32_t transaction_count() const#
Returns:

The number of transactions that have occurred since the KVS was first used. This value is retained across initializations, but is reset if the underlying flash is erased.

StorageStats GetStorageStats() const#
Returns:

A StorageStats struct with details about the current and past state of the KVS.

inline size_t redundancy() const#
Returns:

The number of identical copies written for each entry. A redundancy of 1 means that only a single copy is written for each entry.

inline bool error_detected() const#
Returns:

true if the KVS has any unrepaired errors.

inline size_t max_key_value_size_bytes() const#
Returns:

The maximum number of bytes allowed for a key-value combination.

bool CheckForErrors()#

Checks the KVS for any error conditions and returns true if any errors are present. Primarily intended for test and internal use.

Public Static Functions

static inline constexpr size_t max_key_value_size_bytes(size_t partition_sector_size_bytes)#
Returns:

The maximum number of bytes allowed for a given sector size for a key-value combination.

class Item#

Representation of a key-value entry during iteration.

Public Functions

inline const char *key() const#
Returns:

The key as a null-terminated string.

inline StatusWithSize Get(span<std::byte> value_buffer, size_t offset_bytes = 0) const#
Returns:

The value referred to by this iterator. Equivalent to pw::kvs::KeyValueStore::Get().

class iterator#

Supported iteration methods.

Public Functions

iterator &operator++()#

Increments to the next key-value entry in the container.

inline iterator operator++(int)#

Increments to the next key-value entry in the container.

inline const Item &operator*()#

Reads the entry’s key from flash.

inline const Item *operator->()#

Reads the entry into the Item object.

inline constexpr bool operator==(const iterator &rhs) const#

Equality comparison of two entries.

inline constexpr bool operator!=(const iterator &rhs) const#

Inequality comparison of two entries.

struct StorageStats#

Statistics about the KVS.

Statistics are since the KVS init. They’re not retained across reboots.

Public Members

size_t writable_bytes#

The number of writeable bytes remaining in the KVS. This number doesn’t include the one empty sector required for KVS garbage collection.

size_t in_use_bytes#

The number of bytes in the KVS that are already in use.

size_t reclaimable_bytes#

The maximum number of bytes possible to reclaim by garbage collection. The number of bytes actually reclaimed by maintenance depends on the type of maintenance that’s performed.

size_t sector_erase_count#

The total count of individual sector erases that have been performed.

size_t corrupt_sectors_recovered#

The number of corrupt sectors that have been recovered.

size_t missing_redundant_entries_recovered#

The number of missing redundant copies of entries that have been recovered.

Configuration#

PW_KVS_LOG_LEVEL#

Which log level to use for pw_kvs logs.

PW_KVS_MAX_FLASH_ALIGNMENT#

The maximum flash alignment supported.

PW_KVS_REMOVE_DELETED_KEYS_IN_HEAVY_MAINTENANCE#

Whether to remove deleted keys in heavy maintanence. This feature costs some code size (>1KB) and is only necessary if arbitrary key names are used. Without this feature, deleted key entries can fill the KVS, making it impossible to add more keys, even though most keys are deleted.

Design#

pw::kvs::KeyValueStore (“the KVS”) stores key and value data pairs. The key-value pairs are stored in flash partition as a key-value entry (KV entry) that consists of a header/metadata, the key data, and value data. KV entries are accessed through Put(), Get(), and Delete() operations.

Key-value entries#

Each key-value (KV) entry consists of a header/metadata, the key data, and value data. Individual KV entries are contained within a single flash sector; they do not cross sector boundaries. Because of this the maximum KV entry size is the partition sector size.

KV entries are appended as needed to sectors, with append operations spread over time. Each individual KV entry is written completely as a single high-level operation. KV entries are appended to a sector as long as space is available for a given KV entry. Multiple sectors can be active for writing at any time.

When an entry is updated, an entirely new entry is written to a new location that may or may not be located in the same sectore as the old entry. The new entry uses a transaction ID greater than the old entry. The old entry remains unaltered “on-disk” but is considered “stale”. It is garbage collected at some future time.

State#

The KVS does not store any data/metadata/state in flash beyond the KV entries. All KVS state can be derived from the stored KV entries. Current state is determined at boot from flash-stored KV entries and then maintained in RAM by the KVS. At all times the KVS is in a valid state on-flash; there are no windows of vulnerability to unexpected power loss or crash. The old entry for a key is maintained until the new entry for that key is written and verified.

Each KV entry has a unique transaction ID that is incremented for each KVS update transaction. When determining system state from flash-stored KV entries, the valid entry with the highest transaction ID is considered to be the “current” entry of the key. All stored entries of the same key with lower transaction IDs are considered old or “stale”.

Updates/rewrites of a key that has been previously stored is done as a new KV entry with an updated transaction ID and the new value for the key. The internal state of the KVS is updated to reflect the new entry. The previously stored KV entries for that key are not modified or removed from flash storage, until garbage collection reclaims the “stale” entries.

Garbage collection is done by copying any currently valid KV entries in the sector to be garbage collected to a different sector and then erasing the sector.

Flash sectors#

Each flash sector is written sequentially in an append-only manner, with each following entry write being at a higher address than all of the previous entry writes to that sector since erase. Once information (header, metadata, data, etc.) is written to flash, that information is not modified or cleared until a full sector erase occurs as part of garbage collection.

Individual KV entries are contained within a single flash sector; they do not cross sector boundaries. Flash sectors can contain as many KV entries as fit in the sector.

Sectors are the minimum erase size for both Flash memory and Flash partitions. Partitions may have a different logical sector size than the memory they are part of. Partition logical sectors may be smaller due to partition overhead (encryption, wear tracking, etc) or larger due to combining raw sectors into larger logical sectors.

Storage layers#

The flash storage used by the KVS is comprised of two layers, flash memory and flash partitions.

Flash memory#

pw::kvs::FlashMemory is the lower storage layer that manages the raw read/write/erase of the flash memory device. It is an abstract base class that needs a concrete implementation before it can be used.

pw::kvs::FakeFlashMemory is a variant that uses RAM rather than flash as the storage media. This is helpful for reducing physical flash wear during unit tests and development.

Flash partitions#

pw::kvs::FlashPartition is a subset of a pw::kvs::FlashMemory. Flash memory may have one or multiple partition instances that represent different parts of the memory, such as partitions for KVS, OTA, snapshots/crashlogs, etc. Each partition has its own separate logical address space starting from zero to size bytes of the partition. Partition logical addresses do not always map directly to memory addresses due to partition encryption, sector headers, etc.

Partitions support access via pw::kvs::NonSeekableWriter and pw::kvs::SeekableReader. The reader defaults to the full size of the partition but can optionally be limited to a smaller range.

pw::kvs::FlashPartition is a concrete class that can be used directly. It has several derived variants available, such as pw::kvs::FlashPartitionWithStats and pw::kvs::FlashPartitionWithLogicalSectors.

Alignment#

Writes to flash must have a start address that is a multiple of the flash write alignment. Write size must also be a multiple of flash write alignment. Write alignment varies by flash device and partition type. Reads from flash do not have any address or size alignment requirement; reads always have a minimum alignment of 1.

Flash partitions may have a different alignment than the Flash memory they are part of, so long as the partition’s alignment is a multiple of the alignment for the memory.

Allocation#

The KVS requires more storage space than the size of the key-value data stored. This is due to the always-free sector required for garbage collection and the “write and garbage collect later” approach it uses.

The KVS works poorly when stored data takes up more than 75% of the available storage. It works best when stored data is less than 50%. Applications that need to do garbage collection at scheduled times or that write very heavily can benefit from additional flash store space.

The flash storage used by the KVS is multiplied by the amount of Redundancy used. A redundancy of 2 will use twice the storage, for example.

Redundancy#

The KVS supports storing redundant copies of KV entries. For a given redundancy level (N), N total copies of each KV entry are stored. Redundant copies are always stored in different sectors. This protects against corruption or even full sector loss in N-1 sectors without data loss.

Redundancy increases flash usage proportional to the redundancy level. The RAM usage for KVS internal state has a small increase with redundancy.

Garbage collection#

Storage space occupied by stale Key-value entries is reclaimed and made available for reuse through a garbage collection process. The base garbage collection operation is done to reclaim one sector at a time.

The KVS always keeps at least one sector free at all times to ensure the ability to garbage collect. This free sector is used to copy valid entries from the sector to be garbage collected before erasing the sector to be garbage collected. The always-free sector is rotated as part of the KVS wear leveling.

Garbage collection can be performed manually, by invoking the methods below, or it can be configured to happen automatically.

Wear leveling (flash wear management)#

Wear leveling is accomplished by cycling selection of the next sector to write to. This cycling spreads flash wear across all free sectors so that no one sector is prematurely worn out.

The wear leveling decision-making process follows these guidelines:

  • Location of new writes/rewrites of KV entries will prefer sectors already in-use (partially filled), with new (blank) sectors used when no in-use sectors have large enough available space for the new write.

  • New (blank) sectors selected cycle sequentially between available free sectors.

  • The wear leveling system searches for the first available sector, starting from the current write sector + 1 and wraps around to start at the end of a partition. This spreads the erase/write cycles for heavily written/rewritten KV entries across all free sectors, reducing wear on any single sector.

  • Erase count is not considered in the wear leveling decision-making process.

  • Sectors with already written KV entries that are not modified will remain in the original sector and not participate in wear-leveling, so long as the KV entries in the sector remain unchanged.

Code size analysis#

The following size report details the memory usage of KeyValueStore and FlashPartition.

Label

Segment

Delta

KeyValueStore

FLASH

+316

[section .code]

-44

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_91_15

-32

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_113_23

+160

quorem

+11

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_111_19

-22

pw::bloat::BloatThisBinary()::_pw_tokenizer_string_entry_64_7

+16

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_91_13

+21

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_113_21

DEL

-16

_GLOBAL__sub_I_base_with_only_flash.cc

-4

p05.0

DEL

-2

pw::bloat::BloatThisBinary()::_pw_tokenizer_string_entry_64_5

NEW

+1,132

_printf_float

NEW

+600

_printf_i

NEW

+306

pw::kvs::internal::Sectors::Find()

NEW

+288

pw::kvs::KeyValueStore::UpdateEntriesToPrimaryFormat()

NEW

+268

pw::kvs::internal::Sectors::FindSectorToGarbageCollect()

NEW

+258

pw::kvs::KeyValueStore::WriteEntry()

NEW

+256

__sflush_r

NEW

+256

_malloc_r

NEW

+236

pw::kvs::KeyValueStore::FullMaintenanceHelper()

NEW

+230

_printf_common

NEW

+206

__cvt

NEW

+204

pw::tokenizer::EncodeArgs()

NEW

+200

pw::kvs::FakeFlashMemory::Write()

NEW

+184

pw::kvs::KeyValueStore::GarbageCollectSector()

NEW

+176

pw::kvs::KeyValueStore::GetSectorForWrite()

NEW

+160

pw::kvs::KeyValueStore::AppendEntry()

NEW

+144

_free_r

NEW

+144

pw::kvs::KeyValueStore::FullMaintenanceHelper()::$_0::operator()()

NEW

+136

pw::kvs::KeyValueStore::FixedSizeGet()

NEW

+136

pw::kvs::KeyValueStore::PutBytes()

NEW

+128

pw::kvs::KeyValueStore::CopyEntryToSector()

NEW

+128

pw::kvs::KeyValueStore::RemoveDeletedKeyEntries()

NEW

+128

pw::kvs::KeyValueStore::WriteEntryForNewKey()

NEW

+126

std::__find_if<>()

NEW

+120

pw::kvs::KeyValueStore::Get()

NEW

+118

__exponent

NEW

+118

pw::kvs::KeyValueStore::AddRedundantEntries()

NEW

+118

pw::kvs::KeyValueStore::RelocateEntry()

NEW

+108

pw::kvs::KeyValueStore::UpdateKeyDescriptor()

NEW

+108

std

NEW

+104

pw::kvs::KeyValueStore::RepairCorruptSectors()

NEW

+100

pw::kvs::KeyValueStore::ReadEntry()

NEW

+96

pw::kvs::FakeFlashMemory::Erase()

NEW

+92

pw::kvs::FlashError::Check()

NEW

+90

pw::kvs::KeyValueStore::CreateEntry()

NEW

+90

pw::kvs::KeyValueStore::EnsureEntryRedundancy()

NEW

+80

_fflush_r

NEW

+78

pw::kvs::FakeFlashMemory::Read()

NEW

+72

__libc_init_array

NEW

+72

pw::kvs::KeyValueStore::CreateOrUpdateKeyDescriptor()

NEW

+70

pw::kvs::KeyValueStore::RelocateKeyAddressesInSector()

NEW

+68

_pw_boot_Entry

NEW

+68

sbrk_aligned

NEW

+64

cleanup_stdio

NEW

+64

pw::kvs::KeyValueStore::GetAddressesForWrite()

NEW

+64

pw_sys_io_lm3s6965evb_Init

NEW

+60

global_stdio_init.part.0

NEW

+60

pw::kvs::KeyValueStore::EnsureFreeSectorExists()

NEW

+56

__swrite

NEW

+56

pw::kvs::KeyValueStore::WriteEntryForExistingKey()

NEW

+48

__sinit

NEW

+48

pw_varint_Encode64

NEW

+44

_GLOBAL__sub_I_with_kvs.cc

NEW

+44

pw::kvs::FakeFlashMemory::FlashAddressToMcuAddress()

NEW

+42

_pw_log_tokenized_EncodeTokenizedLog

NEW

+42

pw::kvs::KeyValueStore::FindEntry()

NEW

+36

__ascii_mbtowc

NEW

+36

__sseek

NEW

+36

_lseek_r

NEW

+36

pw::kvs::KeyValueStore::GarbageCollect()

NEW

+36

pw::kvs::KeyValueStore::Repair()

NEW

+36

pw::kvs::internal::Entry::Tombstone()

NEW

+36

pw::kvs::internal::Entry::Valid()

NEW

+36

pw::kvs::internal::Sectors::NextWritableAddress()

NEW

+34

__sread

NEW

+34

pw::kvs::KeyValueStore::ValueSize()

NEW

+34

pw::kvs::internal::(anonymous namespace)::Contains<>()

NEW

+32

_close_r

NEW

+32

memcmp

NEW

+32

pw::Vector<>::Append()

NEW

+32

pw::kvs::internal::Sectors::FindSpaceDuringGarbageCollection()

NEW

+32

pw::sys_io::WriteBytes()

NEW

+30

pw::kvs::internal::Sectors::FindSpace()

NEW

+30

pw::kvs::internal::Sectors::WearLeveledSectorFromIndex()

NEW

+30

pw::tokenizer::EncodedMessage<>::EncodedMessage()

NEW

+28

memchr

NEW

+28

memcpy

NEW

+28

pw_tokenizer_EncodeInt64()

NEW

+26

__ascii_wctomb

NEW

+24

pw::kvs::KeyValueStore::FindExisting()

NEW

+24

pw_StatusString

NEW

+24

pw_assert_tokenized_HandleCheckFailure

NEW

+24

pw_boot_PreStaticMemoryInit

NEW

+24

stdio_exit_handler

NEW

+22

pw::kvs::internal::Sectors::BaseAddress()

NEW

+20

pw::kvs::internal::EntryMetadata::AddNewAddress()

NEW

+20

pw::sys_io::WriteByte()

NEW

+20

pw_assert_HandleFailure

NEW

+18

OUTLINED_FUNCTION_2

NEW

+18

pw::kvs::internal::Entry::size()

NEW

+18

pw::span<>::operator[]()

NEW

+16

OUTLINED_FUNCTION_3

NEW

+16

_close

NEW

+16

pw_boot_PostMain

NEW

+14

OUTLINED_FUNCTION_5

NEW

+14

pw::span<>::front()

NEW

+12

__malloc_lock

NEW

+12

__sfp_lock_acquire

NEW

+12

pw::kvs::FlashPartition::Input

NEW

+12

pw::kvs::FlashPartition::Output

NEW

+10

OUTLINED_FUNCTION_4

NEW

+10

__aeabi_memset4

NEW

+10

__sclose

NEW

+8

_localeconv_r

NEW

+8

kvs_format

NEW

+8

pw::Vector<>::assign()

NEW

+8

pw::kvs::FakeFlashMemory::~FakeFlashMemory()

NEW

+8

pw_boot_Entry

NEW

+8

pw_log_tokenized_HandleLog

NEW

+6

OUTLINED_FUNCTION_0

NEW

+6

OUTLINED_FUNCTION_1

NEW

+6

__aeabi_memclr4

NEW

+6

pw::kvs::KeyValueStore::HeavyMaintenance()

NEW

+4

DefaultFaultHandler

NEW

+4

__aeabi_memcpy

NEW

+4

__aeabi_memmove4

NEW

+4

_exit

NEW

+4

pw_boot_PreMainInit

NEW

+4

std::find<>()

+10,152

FlashPartition

FLASH

DEL

-1,132

_printf_float

DEL

-600

_printf_i

DEL

-596

__aeabi_dmul

DEL

-464

__aeabi_ddiv

+30

[section .code]

-160

quorem

+8

_ctype_

DEL

-256

__sflush_r

DEL

-256

_malloc_r

DEL

-230

_printf_common

DEL

-206

__cvt

DEL

-204

pw::tokenizer::EncodeArgs()

DEL

-160

__aeabi_d2f

DEL

-144

_free_r

DEL

-140

__gtdf2

DEL

-120

pw::bloat::BloatThisBinary()

DEL

-118

__exponent

DEL

-108

__floatundidf

DEL

-108

std

DEL

-80

__aeabi_d2iz

DEL

-80

_fflush_r

DEL

-76

main

+30

pw::bloat::BloatThisBinary()::_pw_tokenizer_string_entry_64_7

DEL

-72

__libc_init_array

DEL

-68

_pw_boot_Entry

DEL

-68

sbrk_aligned

DEL

-64

cleanup_stdio

DEL

-64

pw_sys_io_lm3s6965evb_Init

DEL

-60

global_stdio_init.part.0

DEL

-56

__swrite

DEL

-48

__sinit

DEL

-48

pw_varint_Encode64

DEL

-44

__aeabi_dcmpun

DEL

-44

__extendsfdf2

DEL

-42

_pw_log_tokenized_EncodeTokenizedLog

DEL

-36

__ascii_mbtowc

DEL

-36

__sseek

DEL

-36

_lseek_r

DEL

-34

__sread

DEL

-32

__aeabi_cdrcmple

DEL

-32

_close_r

DEL

-32

pw::sys_io::WriteBytes()

DEL

-30

pw::tokenizer::EncodedMessage<>::EncodedMessage()

DEL

-28

memchr

DEL

-28

memcpy

DEL

-28

pw_tokenizer_EncodeInt64()

DEL

-26

__ascii_wctomb

DEL

-24

pw_assert_tokenized_HandleCheckFailure

DEL

-24

pw_boot_PreStaticMemoryInit

DEL

-24

stdio_exit_handler

DEL

-20

__aeabi_dcmpeq

DEL

-20

__aeabi_dcmpge

DEL

-20

__aeabi_dcmpgt

DEL

-20

__aeabi_dcmple

DEL

-20

__aeabi_dcmplt

DEL

-20

pw::sys_io::WriteByte()

DEL

-20

pw_assert_HandleFailure

DEL

-16

_close

DEL

-16

pw_boot_PostMain

DEL

-12

__malloc_lock

DEL

-12

__sfp_lock_acquire

DEL

-10

__aeabi_memset4

DEL

-10

__sclose

DEL

-8

_localeconv_r

DEL

-8

pw_boot_Entry

DEL

-8

pw_log_tokenized_HandleLog

DEL

-6

__aeabi_memclr

DEL

-6

pw_boot_PreMainInit

DEL

-4

DefaultFaultHandler

DEL

-4

__aeabi_memcpy

DEL

-4

__aeabi_memmove

DEL

-4

_exit

DEL

-2

pw_boot_PreStaticConstructorInit

NEW

+204

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_78_11

NEW

+197

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_70_7

NEW

+187

pw::kvs::FlashPartition::FlashPartition()::_pw_tokenizer_string_entry_98_1

NEW

+183

pw::containers::internal::CheckIntrusiveItemIsUncontained()::_pw_tokenizer_string_entry_28_3

NEW

+179

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_133_39

NEW

+179

pw::kvs::KeyValueStore::InitializeMetadata()::_pw_tokenizer_string_entry_304_83

NEW

+170

pw::kvs::FlashPartition::CheckBounds()::_pw_tokenizer_string_entry_203_11

NEW

+170

pw::kvs::KeyValueStore::GarbageCollectSector()::_pw_tokenizer_string_entry_1088_187

NEW

+169

pw::kvs::FakeFlashMemory::Erase()::_pw_tokenizer_string_entry_75_7

NEW

+167

pw::containers::internal::CheckIntrusiveContainerIsEmpty()::_pw_tokenizer_string_entry_22_1

NEW

+167

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_139_43

NEW

+167

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_91_15

NEW

+157

pw::kvs::FakeFlashMemory::FlashAddressToMcuAddress()::_pw_tokenizer_string_entry_136_23

NEW

+152

pw::kvs::FakeFlashMemory::Erase()::_pw_tokenizer_string_entry_66_3

NEW

+149

pw::kvs::FakeFlashMemory::Write()::_pw_tokenizer_string_entry_111_15

NEW

+148

pw::kvs::internal::EntryCache::AddNewOrUpdateExisting()::_pw_tokenizer_string_entry_189_21

NEW

+143

pw::kvs::KeyValueStore::InitializeMetadata()::_pw_tokenizer_string_entry_293_79

NEW

+142

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_123_35

NEW

+142

pw::kvs::KeyValueStore::WriteEntryForNewKey()::_pw_tokenizer_string_entry_702_123

NEW

+136

pw::kvs::FakeFlashMemory::Write()::_pw_tokenizer_string_entry_102_11

NEW

+130

pw::kvs::internal::EntryCache::Find()::_pw_tokenizer_string_entry_98_3

NEW

+129

pw::kvs::KeyValueStore::AppendEntry()::_pw_tokenizer_string_entry_869_147

NEW

+127

pw::kvs::FlashPartition::Write()::_pw_tokenizer_string_entry_124_5

NEW

+127

pw::kvs::KeyValueStore::FullMaintenanceHelper()::_pw_tokenizer_string_entry_953_155

NEW

+126

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_117_27

NEW

+125

pw::kvs::FakeFlashMemory::Write()::_pw_tokenizer_string_entry_119_19

NEW

+125

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_113_23

NEW

+125

pw::kvs::internal::EntryCache::RemoveEntry()::_pw_tokenizer_string_entry_131_17

NEW

+124

pw::kvs::FlashPartition::Erase()::_pw_tokenizer_string_entry_108_3

NEW

+124

pw::kvs::FlashPartition::Write()::_pw_tokenizer_string_entry_126_7

NEW

+124

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_119_31

NEW

+122

pw::kvs::KeyValueStore::ReadEntry()::_pw_tokenizer_string_entry_556_111

NEW

+119

pw::kvs::KeyValueStore::FullMaintenanceHelper()::_pw_tokenizer_string_entry_1018_167

NEW

+119

pw::kvs::KeyValueStore::GetSectorForWrite()::_pw_tokenizer_string_entry_837_135

NEW

+118

pw::kvs::internal::EntryCache::Find()::_pw_tokenizer_string_entry_106_7

NEW

+115

pw::kvs::KeyValueStore::InitializeMetadata()::_pw_tokenizer_string_entry_223_63

NEW

+114

pw::kvs::KeyValueStore::FullMaintenanceHelper()::$_0::operator()()::_pw_tokenizer_string_entry_982_159

NEW

+113

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_111_19

NEW

+113

pw::kvs::KeyValueStore::WriteEntryForNewKey()::_pw_tokenizer_string_entry_693_119

NEW

+112

pw::kvs::KeyValueStore::GetSectorForWrite()::_pw_tokenizer_string_entry_843_139

NEW

+108

pw::kvs::internal::EntryCache::Find()::_pw_tokenizer_string_entry_113_15

NEW

+105

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_64_3

NEW

+105

pw::kvs::internal::Entry::VerifyChecksumInFlash()::_pw_tokenizer_string_entry_216_11

NEW

+104

pw::kvs::internal::Entry::Read()::_pw_tokenizer_string_entry_56_3

NEW

+103

pw::kvs::KeyValueStore::FullMaintenanceHelper()::_pw_tokenizer_string_entry_940_151

NEW

+102

pw::kvs::KeyValueStore::FullMaintenanceHelper()::_pw_tokenizer_string_entry_1016_163

NEW

+96

pw::kvs::KeyValueStore::Repair()::_pw_tokenizer_string_entry_1303_251

NEW

+48

pw::kvs::FakeFlashMemory

NEW

+48

pw::kvs::FakeFlashMemoryBuffer<>

NEW

+48

pw::kvs::FlashMemory

NEW

+48

pw::kvs::FlashPartition

NEW

+45

pw::kvs::FakeFlashMemory::Erase()::_pw_tokenizer_string_entry_66_1

NEW

+45

pw::kvs::FakeFlashMemory::Erase()::_pw_tokenizer_string_entry_75_5

NEW

+45

pw::kvs::FakeFlashMemory::FlashAddressToMcuAddress()::_pw_tokenizer_string_entry_136_21

NEW

+45

pw::kvs::FakeFlashMemory::Write()::_pw_tokenizer_string_entry_102_9

NEW

+45

pw::kvs::FakeFlashMemory::Write()::_pw_tokenizer_string_entry_111_13

NEW

+45

pw::kvs::FakeFlashMemory::Write()::_pw_tokenizer_string_entry_119_17

NEW

+45

pw::kvs::FlashPartition::CheckBounds()::_pw_tokenizer_string_entry_203_9

NEW

+40

pw::kvs::KeyValueStore::AppendEntry()::_pw_tokenizer_string_entry_869_145

NEW

+40

pw::kvs::KeyValueStore::FullMaintenanceHelper()::$_0::operator()()::_pw_tokenizer_string_entry_982_157

NEW

+40

pw::kvs::KeyValueStore::FullMaintenanceHelper()::_pw_tokenizer_string_entry_1016_161

NEW

+40

pw::kvs::KeyValueStore::FullMaintenanceHelper()::_pw_tokenizer_string_entry_1018_165

NEW

+40

pw::kvs::KeyValueStore::FullMaintenanceHelper()::_pw_tokenizer_string_entry_940_149

NEW

+40

pw::kvs::KeyValueStore::FullMaintenanceHelper()::_pw_tokenizer_string_entry_953_153

NEW

+40

pw::kvs::KeyValueStore::GarbageCollectSector()::_pw_tokenizer_string_entry_1088_185

NEW

+40

pw::kvs::KeyValueStore::GetSectorForWrite()::_pw_tokenizer_string_entry_837_133

NEW

+40

pw::kvs::KeyValueStore::GetSectorForWrite()::_pw_tokenizer_string_entry_843_137

NEW

+40

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_111_17

NEW

+40

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_117_25

NEW

+40

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_119_29

NEW

+40

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_123_33

NEW

+40

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_133_37

NEW

+40

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_139_41

NEW

+40

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_64_1

NEW

+40

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_70_5

NEW

+40

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_78_9

NEW

+40

pw::kvs::KeyValueStore::InitializeMetadata()::_pw_tokenizer_string_entry_223_61

NEW

+40

pw::kvs::KeyValueStore::InitializeMetadata()::_pw_tokenizer_string_entry_293_77

NEW

+40

pw::kvs::KeyValueStore::InitializeMetadata()::_pw_tokenizer_string_entry_304_81

NEW

+40

pw::kvs::KeyValueStore::ReadEntry()::_pw_tokenizer_string_entry_556_109

NEW

+40

pw::kvs::KeyValueStore::Repair()::_pw_tokenizer_string_entry_1303_249

NEW

+40

pw::kvs::KeyValueStore::WriteEntryForNewKey()::_pw_tokenizer_string_entry_693_117

NEW

+40

pw::kvs::KeyValueStore::WriteEntryForNewKey()::_pw_tokenizer_string_entry_702_121

NEW

+40

pw::kvs::internal::Entry::Read()::_pw_tokenizer_string_entry_56_1

NEW

+40

pw::kvs::internal::Entry::VerifyChecksumInFlash()::_pw_tokenizer_string_entry_216_9

NEW

+40

pw::kvs::internal::EntryCache::AddNewOrUpdateExisting()::_pw_tokenizer_string_entry_189_19

NEW

+40

pw::kvs::internal::EntryCache::Find()::_pw_tokenizer_string_entry_106_5

NEW

+40

pw::kvs::internal::EntryCache::Find()::_pw_tokenizer_string_entry_113_13

NEW

+40

pw::kvs::internal::EntryCache::Find()::_pw_tokenizer_string_entry_98_1

NEW

+32

_GLOBAL__sub_I_fake_flash_test_partition.cc

NEW

+24

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_91_13

NEW

+20

__cxa_pure_virtual

NEW

+20

write

NEW

+19

pw::kvs::KeyValueStore::Init()::_pw_tokenizer_string_entry_113_21

NEW

+16

_GLOBAL__sub_I_base_with_only_flash.cc

NEW

+16

free

NEW

+16

std::get_terminate()

NEW

+10

std::terminate()

NEW

+8

__cxxabiv1::__terminate()

NEW

+8

operator delete()

NEW

+2

pw::bloat::BloatThisBinary()::_pw_tokenizer_string_entry_64_5

+1,832