Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom allocator / heap profiler #350

Merged
merged 31 commits into from
Aug 13, 2024
Merged

Custom allocator / heap profiler #350

merged 31 commits into from
Aug 13, 2024

Conversation

matth-x
Copy link
Owner

@matth-x matth-x commented Aug 5, 2024

Add custom allocation hook functions and a built-in heap profiler.

The new Memory module provides methods for overriding the default C++ allocation and a central malloc / free selector. If enabled via build switches, the allocations will be handled by the Memory module, instead of going to the host system allocator directly. There exist two possible uses:

  • Decoration of the host system allocator, e.g. to track and measure allocations
  • Replacing the host system allocator by a custom allocator

If the Memory module is not enabled via build switches, then this has no effect on the allocations.

Usage

The following features can be enabled:

  • MO_OVERRIDE_ALLOCATION=1: enable routing allocations through the Memory module. The firmware can now set its custom allocator using mo_mem_set_malloc_free(...)
  • MO_ENABLE_HEAP_PROFILER=1: track allocations and enable the function MO_MEM_PRINT_STATS() to print heap stats. This feature depends on setting MO_OVERRIDE_ALLOCATION=1.

Changes

The API in MicroOcpp.h / MicroOcpp_c.h didn't change. But if the firmware integration gets beyond the API, then the following changes are relevant:

  • std::string has been replaced by MicroOcpp::String which is a typedef of either the same std::string if the Memory module is disabled, or std::basic_string<char, std::char_traits<char>, MicroOcpp::Allocator<char>> if enabled. To construct a String, use makeString(const char *memoryTag) and give it a memory tag which will show up in the heap statistics
  • std::vector has been replaced by MicroOcpp::Vector, a typedef of std::vector or std::vector<T, MicroOcpp::Allocator<T>>. Use makeVector(const char *memoryTag)
  • And last, DynamicJsonDocument is MicroOcpp::JsonDoc now which becomes DynamicJsonDocument again if the Memory module is off and BasicJsonDocument<ArduinoJsonAllocator> if on

Limitations

This method of redirecting all allocations depends on explicitly changing the allocation everywhere in the code base. It is likely that some allocations have been overlooked and in future changes, it could be forgotten about in some places.

For std::function, it's not really common to replace the internal allocator and therefore it's not well supported by STL implementations. For now, the MO-internal uses of std::function remain unchanged, making them a blind spot of the heap profiler. In future, they should be replaced by classic C-style callback functions for example.

If MO requires system resources (like snprintf) which allocate memory internally, that isn't taken into account. The Memory module can only track explicit allocations inside the MO library.

Example

The example output of running all unit tests looks like this:

 *** Heap usage statistics ***
Context - 0 B (max. 744 B)
EmptyJsonDoc - 0 B (max. 0 B)
Filesystem - 0 B (max. 88 B)
FilesystemIndex - 0 B (max. 1282 B)
Model - 0 B (max. 206 B)
OperationRegistry - 0 B (max. 4992 B)
Request - 0 B (max. 4914 B)
RequestQueue - 0 B (max. 14209 B)
Timestamp - 0 B (max. 32 B)
UnitTests - 0 B (max. 4096 B)
v16.Authorization.AuthorizationData - 0 B (max. 21 B)
v16.Authorization.AuthorizationList - 0 B (max. 584 B)
v16.Authorization.AuthorizationService - 0 B (max. 1472 B)
v16.Boot.BootService - 0 B (max. 949 B)
v16.Configuration.AllowOfflineTxForUnknownId - 0 B (max. 72 B)
v16.Configuration.AuthorizeRemoteTxRequests - 0 B (max. 72 B)

[118 more lines]

v16.Transactions.Transaction - 0 B (max. 736 B)
v16.Transactions.TransactionDeserialize - 0 B (max. 1024 B)
v16.Transactions.TransactionStore - 0 B (max. 1856 B)
Blocks: 0
Tags: 137
Current usage: 0 B
Maximum usage: 31544 B

The heap usage at exiting the unit tests is 0 bytes (MO has been deinitalized) and total maximum over all tests was 31544 bytes.

@matth-x matth-x marked this pull request as ready for review August 13, 2024 10:26
@matth-x matth-x merged commit 94e0918 into main Aug 13, 2024
5 checks passed
@matth-x matth-x deleted the feature/custom-allocator branch August 13, 2024 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant