|
| 1 | +#pragma once |
| 2 | + |
| 3 | +#include "esp_heap_caps.h" |
| 4 | +#include "sdkconfig.h" |
| 5 | + |
| 6 | +#include "base_component.hpp" |
| 7 | + |
| 8 | +namespace espp { |
| 9 | +/// HeapMonitor class |
| 10 | +/// This class provides functionality to monitor and report heap memory usage |
| 11 | +/// in ESP32 systems. It can be used to get information about different heap |
| 12 | +/// regions based on their flags (e.g., MALLOC_CAP_8BIT, MALLOC_CAP_INTERNAL, etc.). |
| 13 | +/// |
| 14 | +/// It provides methods to retrieve heap information, format it as a string, |
| 15 | +/// and log it. The class can be configured with specific heap flags and a |
| 16 | +/// name for the monitor. |
| 17 | +/// |
| 18 | +/// \section heap_monitor_ex1 Heap Monitor Example |
| 19 | +/// \snippet monitor_example.cpp HeapMonitor Example |
| 20 | +class HeapMonitor : public BaseComponent { |
| 21 | +public: |
| 22 | + /// Info about a heap region |
| 23 | + struct HeapInfo { |
| 24 | + int heap_flags; ///< Heap region flags bitmask (e.g., MALLOC_CAP_8BIT, MALLOC_CAP_INTERNAL, |
| 25 | + ///< etc.) |
| 26 | + size_t free_bytes; ///< Total free heap in bytes |
| 27 | + size_t min_free_bytes; ///< Minimum free heap in bytes |
| 28 | + size_t largest_free_block; ///< Largest free block in bytes |
| 29 | + size_t allocated_bytes; ///< Total allocated heap in bytes |
| 30 | + size_t total_size; ///< Total heap size in bytes |
| 31 | + }; |
| 32 | + |
| 33 | + /// Configuration for HeapMonitor |
| 34 | + struct Config { |
| 35 | + int heap_flags = MALLOC_CAP_DEFAULT; ///< Heap region flags bitmask (e.g., MALLOC_CAP_8BIT, |
| 36 | + ///< MALLOC_CAP_INTERNAL, etc.) |
| 37 | + std::string_view name = "HeapMonitor"; ///< Name of the heap monitor |
| 38 | + espp::Logger::Verbosity log_level = espp::Logger::Verbosity::INFO; ///< Log level for heap info |
| 39 | + }; |
| 40 | + |
| 41 | + /// @brief Constructor |
| 42 | + /// @param config Configuration for heap monitor |
| 43 | + explicit HeapMonitor(const Config &config) |
| 44 | + : BaseComponent(config.name, config.log_level) |
| 45 | + , heap_flags_(config.heap_flags) {} |
| 46 | + |
| 47 | + /// @brief Get the name of the heap region based on the heap flags |
| 48 | + /// @param heap_flags Heap region flags bitmask (e.g., MALLOC_CAP_8BIT, MALLOC_CAP_INTERNAL, etc.) |
| 49 | + /// @return Name of the heap region as a string |
| 50 | + static std::string get_region_name(int heap_flags); |
| 51 | + |
| 52 | + /// @brief Get heap info for a specific heap region |
| 53 | + /// @param heap_flags Heap region flags bitmask (e.g., MALLOC_CAP_8BIT, MALLOC_CAP_INTERNAL, etc.) |
| 54 | + /// @return HeapInfo structure with heap information |
| 55 | + static HeapInfo get_info(int heap_flags); |
| 56 | + |
| 57 | + /// @brief Get heap info for a specific heap region |
| 58 | + /// @param heap_flags Vector of heap region flags bitmasks (e.g., MALLOC_CAP_8BIT, |
| 59 | + /// MALLOC_CAP_INTERNAL, etc.) |
| 60 | + /// @return Vector of HeapInfo structures with heap information for each region |
| 61 | + static std::vector<HeapInfo> get_info(const std::vector<int> &heap_flags); |
| 62 | + |
| 63 | + /// @brief Get heap info for the configured heap region |
| 64 | + /// @return HeapInfo structure with heap information |
| 65 | + HeapInfo get_info() const { return get_info(heap_flags_); } |
| 66 | + |
| 67 | + /// @brief Get the header for the CSV output |
| 68 | + /// @return CSV header string |
| 69 | + static const std::string get_csv_header() { |
| 70 | + return "heap_flags,free_bytes,min_free_bytes,largest_free_block,allocated_bytes,total_size"; |
| 71 | + } |
| 72 | + |
| 73 | + /// @brief Get the header for the table output |
| 74 | + /// @return Table header string |
| 75 | + static const std::string get_table_header() { |
| 76 | + return " Min Free / Free / Biggest / Total | Type"; |
| 77 | + } |
| 78 | + |
| 79 | + /// @brief Get a string representation of the heap info in table format |
| 80 | + /// @param heap_flags Heap region flags bitmask(s) (e.g., MALLOC_CAP_8BIT, |
| 81 | + /// MALLOC_CAP_INTERNAL, etc.) |
| 82 | + /// @return Table string representation of the heap info, each line |
| 83 | + /// representing a heap region (entry in heap_flags vector) |
| 84 | + static std::string get_table(const std::vector<int> &heap_flags); |
| 85 | + |
| 86 | + /// @brief Get a string representation of the heap info in CSV format |
| 87 | + /// @param heap_flags Heap region flags bitmask(s) (e.g., MALLOC_CAP_8BIT, |
| 88 | + /// MALLOC_CAP_INTERNAL, etc.) |
| 89 | + /// @return CSV string representation of the heap info, each line |
| 90 | + /// representing a heap region (entry in heap_flags vector) |
| 91 | + static std::string get_csv(const std::vector<int> &heap_flags); |
| 92 | + |
| 93 | +protected: |
| 94 | + int heap_flags_; |
| 95 | +}; // class HeapMonitor |
| 96 | +} // namespace espp |
| 97 | + |
| 98 | +// libfmt formatting for the HeapInfo struct. Supports single line, csv, and |
| 99 | +// table outputs using a presentation format specifier |
| 100 | +template <> struct fmt::formatter<espp::HeapMonitor::HeapInfo> { |
| 101 | + // presentation format specifier: 's' for single line, 'c' for csv, 't' for table |
| 102 | + char presentation = 's'; |
| 103 | + |
| 104 | + template <typename ParseContext> constexpr auto parse(ParseContext &ctx) { |
| 105 | + // Parse the presentation format and store it in the formatter: |
| 106 | + auto it = ctx.begin(), end = ctx.end(); |
| 107 | + if (it != end && (*it == 's' || *it == 'c' || *it == 't')) { |
| 108 | + presentation = *it++; |
| 109 | + } |
| 110 | + |
| 111 | + // TODO: Check if reached the end of the range: |
| 112 | + // if (it != end && *it != '}') throw format_error("invalid format"); |
| 113 | + |
| 114 | + // Return the iterator to the end of the parsed range: |
| 115 | + return it; |
| 116 | + } |
| 117 | + |
| 118 | + template <typename FormatContext> |
| 119 | + auto format(espp::HeapMonitor::HeapInfo const &hi, FormatContext &ctx) const { |
| 120 | + switch (presentation) { |
| 121 | + default: |
| 122 | + // intentional fall-through back to default formatting |
| 123 | + case 's': // single line |
| 124 | + return fmt::format_to(ctx.out(), |
| 125 | + "HeapInfo: {{ flags: {}, free: {}, min_free: {}, largest_free_block: " |
| 126 | + "{}, allocated: {}, total: {} }}", |
| 127 | + hi.heap_flags, hi.free_bytes, hi.min_free_bytes, hi.largest_free_block, |
| 128 | + hi.allocated_bytes, hi.total_size); |
| 129 | + case 'c': // csv |
| 130 | + return fmt::format_to(ctx.out(), "{},{},{},{},{},{}", hi.heap_flags, hi.free_bytes, |
| 131 | + hi.min_free_bytes, hi.largest_free_block, hi.allocated_bytes, |
| 132 | + hi.total_size); |
| 133 | + case 't': // table |
| 134 | + // Format as a table with 8-digit width for each field |
| 135 | + // [ min free bytes / free bytes / largest free block / total size ] heap flags |
| 136 | + return fmt::format_to(ctx.out(), "[{:8d} / {:8d} / {:8d} / {:8d}] {}", hi.min_free_bytes, |
| 137 | + hi.free_bytes, hi.largest_free_block, hi.total_size, |
| 138 | + espp::HeapMonitor::get_region_name(hi.heap_flags)); |
| 139 | + } |
| 140 | + } |
| 141 | +}; |
0 commit comments