From aea718f3f9c2930815f64061244478d6faa6d312 Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Mon, 8 Sep 2025 15:41:32 +0800 Subject: [PATCH 1/7] fix(repo): fix build error on idf v6.0 --- idf_component.yml | 2 +- library.properties | 2 +- src/esp_utils_versions.h | 2 +- src/thread/esp_utils_thread.cpp | 21 ++++++++++++++++----- src/thread/esp_utils_thread.hpp | 24 +++++++++++++++++++----- test_apps/main/CMakeLists.txt | 1 + test_apps/main/idf_component.yml | 2 -- 7 files changed, 39 insertions(+), 15 deletions(-) diff --git a/idf_component.yml b/idf_component.yml index eef82b6..b61a777 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,4 @@ -version: "0.3.0" +version: "0.3.1" description: esp-lib-utils is a library designed for ESP SoCs to provide utility functions, including logging, checking, and memory. url: https://github.com/esp-arduino-libs/esp-lib-utils repository: https://github.com/esp-arduino-libs/esp-lib-utils.git diff --git a/library.properties b/library.properties index 68846d0..aabb19a 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=esp-lib-utils -version=0.3.0 +version=0.3.1 author=espressif maintainer=espressif sentence=esp-lib-utils is a library designed for ESP SoCs to provide utility functions, including logging, checking, and memory. diff --git a/src/esp_utils_versions.h b/src/esp_utils_versions.h index e7579eb..b99fd41 100644 --- a/src/esp_utils_versions.h +++ b/src/esp_utils_versions.h @@ -8,7 +8,7 @@ /* Library Version */ #define ESP_UTILS_VERSION_MAJOR 0 #define ESP_UTILS_VERSION_MINOR 3 -#define ESP_UTILS_VERSION_PATCH 0 +#define ESP_UTILS_VERSION_PATCH 1 /* File `esp_utils_conf.h` */ #define ESP_UTILS_CONF_VERSION_MAJOR 1 diff --git a/src/thread/esp_utils_thread.cpp b/src/thread/esp_utils_thread.cpp index 2dea94a..53573ab 100644 --- a/src/thread/esp_utils_thread.cpp +++ b/src/thread/esp_utils_thread.cpp @@ -3,7 +3,9 @@ * * SPDX-License-Identifier: Apache-2.0 */ -#include "esp_pthread.h" +#if defined(ESP_PLATFORM) +# include "esp_pthread.h" +#endif #include "check/esp_utils_check.h" #include "log/esp_utils_log.hpp" #include "esp_utils_thread.hpp" @@ -18,14 +20,14 @@ void ThreadConfig::dump() const "\t-core_id(%d)\n" "\t-priority(%d)\n" "\t-stack_size(%d)\n" -#if ESP_THREAD_CONFIG_STACK_CAPS_VALID +#if !defined(ESP_UTILS_THREAD_CONFIG_STACK_CAPS_INVALID) "\t-stack_in_ext(%s)\n" #endif - , (name == nullptr) ? CONFIG_PTHREAD_TASK_NAME_DEFAULT : name + , name , core_id , static_cast(priority) , static_cast(stack_size) -#if ESP_THREAD_CONFIG_STACK_CAPS_VALID +#if !defined(ESP_UTILS_THREAD_CONFIG_STACK_CAPS_INVALID) , stack_in_ext ? "true" : "false" #endif ); @@ -40,27 +42,36 @@ thread_config_guard::thread_config_guard(const ThreadConfig &config) config.dump(); #endif +#if defined(ESP_PLATFORM) auto new_cfg = esp_pthread_get_default_config(); new_cfg.thread_name = config.name; new_cfg.stack_size = config.stack_size; new_cfg.prio = config.priority; new_cfg.inherit_cfg = false; new_cfg.pin_to_core = config.core_id; -#if ESP_THREAD_CONFIG_STACK_CAPS_VALID +#if !defined(ESP_UTILS_THREAD_CONFIG_STACK_CAPS_INVALID) new_cfg.stack_alloc_caps = (config.stack_in_ext ? MALLOC_CAP_SPIRAM : MALLOC_CAP_INTERNAL) | MALLOC_CAP_8BIT; #endif auto err = esp_pthread_set_cfg(&new_cfg); ESP_UTILS_CHECK_FALSE_EXIT(err == ESP_OK, "Set thread config failed(%s)", esp_err_to_name(err)); +#else + (void)config; + ESP_UTILS_LOGW("Not supported on non-ESP platforms"); +#endif } thread_config_guard::~thread_config_guard() { ESP_UTILS_LOG_TRACE_GUARD(); +#if defined(ESP_PLATFORM) auto new_cfg = esp_pthread_get_default_config(); auto err = esp_pthread_set_cfg(&new_cfg); ESP_UTILS_CHECK_FALSE_EXIT(err == ESP_OK, "Set thread config failed(%s)", esp_err_to_name(err)); +#else + ESP_UTILS_LOGW("Not supported on non-ESP platforms"); +#endif } }; // namespace esp_utils diff --git a/src/thread/esp_utils_thread.hpp b/src/thread/esp_utils_thread.hpp index 33d3d61..1abe454 100644 --- a/src/thread/esp_utils_thread.hpp +++ b/src/thread/esp_utils_thread.hpp @@ -5,22 +5,36 @@ */ #pragma once +#if defined(ESP_PLATFORM) #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#define ESP_THREAD_CONFIG_STACK_CAPS_VALID (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)) +#define ESP_UTILS_THREAD_CONFIG_STACK_CAPS_INVALID (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0)) +#endif namespace esp_utils { +#if defined(ESP_PLATFORM) +constexpr const char *THREAD_CONFIG_NAME_DEFAULT = CONFIG_PTHREAD_TASK_NAME_DEFAULT; +constexpr int THREAD_CONFIG_CORE_DEFAULT = ((CONFIG_PTHREAD_TASK_CORE_DEFAULT == -1) ? tskNO_AFFINITY : CONFIG_PTHREAD_TASK_CORE_DEFAULT); +constexpr size_t THREAD_CONFIG_PRIO_DEFAULT = CONFIG_PTHREAD_TASK_PRIO_DEFAULT; +constexpr size_t THREAD_CONFIG_STACK_SIZE_DEFAULT = CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT; +#else +constexpr const char *THREAD_CONFIG_NAME_DEFAULT = "pthread"; +constexpr int THREAD_CONFIG_CORE_DEFAULT = 0; +constexpr size_t THREAD_CONFIG_PRIO_DEFAULT = 0; +constexpr size_t THREAD_CONFIG_STACK_SIZE_DEFAULT = 0; +#endif + struct ThreadConfig { void dump() const; const char *name = nullptr; - int core_id = (CONFIG_PTHREAD_TASK_CORE_DEFAULT == -1) ? tskNO_AFFINITY : CONFIG_PTHREAD_TASK_CORE_DEFAULT; - size_t priority = CONFIG_PTHREAD_TASK_PRIO_DEFAULT; - size_t stack_size = CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT; -#if ESP_THREAD_CONFIG_STACK_CAPS_VALID + int core_id = THREAD_CONFIG_CORE_DEFAULT; + size_t priority = THREAD_CONFIG_PRIO_DEFAULT; + size_t stack_size = THREAD_CONFIG_STACK_SIZE_DEFAULT; +#if !defined(ESP_UTILS_THREAD_CONFIG_STACK_CAPS_INVALID) bool stack_in_ext = false; #endif }; diff --git a/test_apps/main/CMakeLists.txt b/test_apps/main/CMakeLists.txt index 7c5b91d..6d05b65 100644 --- a/test_apps/main/CMakeLists.txt +++ b/test_apps/main/CMakeLists.txt @@ -1,6 +1,7 @@ idf_component_register( SRC_DIRS "." INCLUDE_DIRS "." + PRIV_REQUIRES unity esp_timer WHOLE_ARCHIVE ) diff --git a/test_apps/main/idf_component.yml b/test_apps/main/idf_component.yml index 06cfa10..688c0c9 100644 --- a/test_apps/main/idf_component.yml +++ b/test_apps/main/idf_component.yml @@ -1,7 +1,5 @@ ## IDF Component Manager Manifest File dependencies: - test_utils: - path: ${IDF_PATH}/tools/unit-test-app/components/test_utils esp-lib-utils: version: "*" override_path: "../../../esp-lib-utils" From d99ab8145c7519b3fc1f05196816394d88077c21 Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Tue, 21 Oct 2025 09:28:53 +0800 Subject: [PATCH 2/7] feat(more): add profiler --- CHANGELOG.md | 10 + src/esp_lib_utils.h | 3 + src/profiler/esp_utils_time_profiler.cpp | 310 +++++++++++++++++++++++ src/profiler/esp_utils_time_profiler.hpp | 127 ++++++++++ test_apps/main/test_profiler.cpp | 73 ++++++ test_apps/sdkconfig.defaults | 1 + 6 files changed, 524 insertions(+) create mode 100644 src/profiler/esp_utils_time_profiler.cpp create mode 100644 src/profiler/esp_utils_time_profiler.hpp create mode 100644 test_apps/main/test_profiler.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index aa602c0..0faa0bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # ChangeLog +## v0.3.1 - 2025-10-21 + +### Enhancements: + +* feat(profiler): add time profiler module + +### Bugfixes: + +* fix(repo): fix build error on idf v6.0 + ## v0.3.0 - 2025-07-24 ### Breaking Changes: diff --git a/src/esp_lib_utils.h b/src/esp_lib_utils.h index eb0c636..d88a31e 100644 --- a/src/esp_lib_utils.h +++ b/src/esp_lib_utils.h @@ -24,6 +24,9 @@ /* Log */ #include "log/esp_utils_log.hpp" +/* Profiler */ +#include "profiler/esp_utils_time_profiler.hpp" + /* Thread */ #include "thread/esp_utils_thread.hpp" diff --git a/src/profiler/esp_utils_time_profiler.cpp b/src/profiler/esp_utils_time_profiler.cpp new file mode 100644 index 0000000..f2bf747 --- /dev/null +++ b/src/profiler/esp_utils_time_profiler.cpp @@ -0,0 +1,310 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include "esp_utils_time_profiler.hpp" + +namespace esp_utils { + +TimeProfiler &TimeProfiler::instance() +{ + static TimeProfiler inst; + return inst; +} + +TimeProfiler::TimeProfiler() : root_("root") {} + +void TimeProfiler::set_format_options(const FormatOptions &options) +{ + std::lock_guard lock(mutex_); + format_ = options; +} + +void TimeProfiler::enter_scope(const std::string &name) +{ + auto &local_stack = thread_local_stack(); + Node *parent = local_stack.empty() ? &root_ : local_stack.top(); + + std::lock_guard lock(mutex_); + auto &child = parent->children[name]; + if (!child) { + child = std::make_unique(name, parent); + } + local_stack.push(child.get()); + start_times()[local_stack.top()] = Clock::now(); +} + +void TimeProfiler::leave_scope() +{ + auto &local_stack = thread_local_stack(); + if (local_stack.empty()) { + return; + } + Node *node = local_stack.top(); + local_stack.pop(); + + auto end = Clock::now(); + auto start_it = start_times().find(node); + if (start_it != start_times().end()) { + double duration = to_unit(end - start_it->second); + start_times().erase(start_it); + std::lock_guard lock(mutex_); + node->total += duration; + if (duration < node->min) { + node->min = duration; + } + if (duration > node->max) { + node->max = duration; + } + node->count++; + } +} + +void TimeProfiler::start_event(const std::string &name) +{ + std::lock_guard lock(mutex_); + event_stacks_[name].push_back(Clock::now()); +} + +void TimeProfiler::end_event(const std::string &name) +{ + TimePoint end = Clock::now(); + std::lock_guard lock(mutex_); + auto it = event_stacks_.find(name); + if (it == event_stacks_.end() || it->second.empty()) { + return; + } + TimePoint start = it->second.back(); + it->second.pop_back(); + if (it->second.empty()) { + event_stacks_.erase(it); + } + double duration = to_unit(end - start); + + Node *node = find_or_create_node(name); + node->total += duration; + if (duration < node->min) { + node->min = duration; + } + if (duration > node->max) { + node->max = duration; + } + node->count++; +} + +void TimeProfiler::report() +{ + std::lock_guard lock(mutex_); + std::cout << "\n=== Performance Tree Report ===\n"; + std::cout << "(Unit: " << unit_name() << ")\n"; + + const double overall_total = sum_children_total(&root_); + print_header(); + + auto children = sorted_children(&root_); + for (size_t i = 0; i < children.size(); ++i) { + Node *child = children[i]; + const bool is_last = (i + 1 == children.size()); + print_node(child, "", is_last, /*parent_total=*/overall_total, /*overall_total=*/overall_total); + } + std::cout << "===============================\n"; +} + +void TimeProfiler::clear() +{ + std::lock_guard lock(mutex_); + root_.children.clear(); + event_stacks_.clear(); +} + +std::stack &TimeProfiler::thread_local_stack() +{ + static thread_local std::stack stack; + return stack; +} + +std::unordered_map &TimeProfiler::start_times() +{ + static thread_local std::unordered_map times; + return times; +} + +TimeProfiler::Node *TimeProfiler::find_or_create_node(const std::string &name) +{ + auto &child = root_.children[name]; + if (!child) { + child = std::make_unique(name, &root_); + } + return child.get(); +} + +double TimeProfiler::to_unit(std::chrono::duration d) const +{ + switch (format_.time_unit) { + case FormatOptions::TimeUnit::Microseconds: return d.count() * 1000000.0; + case FormatOptions::TimeUnit::Milliseconds: return d.count() * 1000.0; + case FormatOptions::TimeUnit::Seconds: return d.count(); + } + return d.count(); +} + +std::string TimeProfiler::unit_name() const +{ + switch (format_.time_unit) { + case FormatOptions::TimeUnit::Microseconds: return "us"; + case FormatOptions::TimeUnit::Milliseconds: return "ms"; + case FormatOptions::TimeUnit::Seconds: return "s"; + } + return "unknown"; +} + +void TimeProfiler::print_node(Node *node, + const std::string &prefix, + bool is_last, + double parent_total, + double overall_total) const +{ + const double avg = node->count > 0 ? node->total / static_cast(node->count) : 0.0; + const double minv = node->count > 0 ? node->min : 0.0; + const double maxv = node->count > 0 ? node->max : 0.0; + const double children_sum = sum_children_total(node); + const double self_time = node->total - children_sum; + const double pct_parent = parent_total > 0.0 ? (node->total * 100.0 / parent_total) : 0.0; + const double pct_total = overall_total > 0.0 ? (node->total * 100.0 / overall_total) : 0.0; + + const char *branch_mid = format_.use_unicode ? "├─ " : "|- "; + const char *branch_end = format_.use_unicode ? "└─ " : "`- "; + const char *pad_mid = format_.use_unicode ? "│ " : "| "; + const char *pad_end = " "; + const std::string connector = std::string(is_last ? branch_end : branch_mid); + const std::string next_prefix = prefix + (is_last ? pad_end : pad_mid); + + std::cout.setf(std::ios::fixed); + std::cout << std::setprecision(format_.precision); + const std::string display_prefix = prefix + connector; + const char *color_reset = "\033[0m"; + const char *color_start = ""; + if (format_.use_color) { + if (pct_total >= 50.0) { + color_start = "\033[31m"; // red + } else if (pct_total >= 20.0) { + color_start = "\033[33m"; // yellow + } else if (pct_total >= 5.0) { + color_start = "\033[36m"; // cyan + } + } + const bool colorize = format_.use_color && *color_start != '\0'; + const std::string name_colored = colorize ? (std::string(color_start) + node->name + color_reset) : node->name; + int occupy = static_cast(display_prefix.size() + node->name.size()); + int pad = format_.name_width - occupy; + if (pad < 1) { + pad = 1; + } + + std::cout << display_prefix + << name_colored + << std::string(pad, ' ') + << " | " << std::setw(format_.calls_width) << node->count + << " | " << std::setw(format_.num_width) << node->total + << " | " << std::setw(format_.num_width) << self_time + << " | " << std::setw(format_.num_width) << avg + << " | " << std::setw(format_.num_width) << minv + << " | " << std::setw(format_.num_width) << maxv; + + if (format_.show_percentages) { + std::ostringstream p1, p2; + p1.setf(std::ios::fixed); p1 << std::setprecision(2) << pct_parent << '%'; + p2.setf(std::ios::fixed); p2 << std::setprecision(2) << pct_total << '%'; + std::cout << " | " << std::setw(format_.percent_width) << p1.str() + << " | " << std::setw(format_.percent_width) << p2.str(); + } + std::cout << "\n"; + + auto children = sorted_children(node); + for (size_t i = 0; i < children.size(); ++i) { + Node *child = children[i]; + const bool child_is_last = (i + 1 == children.size()); + print_node(child, next_prefix, child_is_last, /*parent_total=*/node->total, overall_total); + } +} + +void TimeProfiler::print_header() const +{ + std::ostringstream oss; + oss.setf(std::ios::fixed); + oss << std::left << std::setw(format_.name_width) << "Name" + << " | " << std::setw(format_.calls_width) << "calls" + << " | " << std::setw(format_.num_width) << "total" + << " | " << std::setw(format_.num_width) << "self" + << " | " << std::setw(format_.num_width) << "avg" + << " | " << std::setw(format_.num_width) << "min" + << " | " << std::setw(format_.num_width) << "max"; + if (format_.show_percentages) { + oss << " | " << std::setw(format_.percent_width) << "%parent" + << " | " << std::setw(format_.percent_width) << "%total"; + } + std::cout << oss.str() << "\n"; + std::cout << std::string(oss.str().size(), '-') << "\n"; +} + +std::string TimeProfiler::format_percent(double pct) +{ + std::ostringstream os; + os.setf(std::ios::fixed); + os << std::setprecision(2) << pct << '%'; + return os.str(); +} + +double TimeProfiler::sum_children_total(const Node *node) const +{ + double sum = 0.0; + for (const auto &kv : node->children) { + if (kv.second) { + sum += kv.second->total; + } + } + return sum; +} + +std::vector TimeProfiler::sorted_children(Node *node) const +{ + std::vector result; + result.reserve(node->children.size()); + for (auto &kv : node->children) { + if (kv.second) { + result.push_back(kv.second.get()); + } + } + switch (format_.sort_by) { + case FormatOptions::SortBy::TotalDesc: + std::sort(result.begin(), result.end(), [](const Node * a, const Node * b) { + return a->total > b->total; + }); + break; + case FormatOptions::SortBy::NameAsc: + std::sort(result.begin(), result.end(), [](const Node * a, const Node * b) { + return a->name < b->name; + }); + break; + case FormatOptions::SortBy::None: + break; + } + return result; +} + +TimeProfileScope::TimeProfileScope(const std::string &name) +{ + TimeProfiler::instance().enter_scope(name); +} +TimeProfileScope::~TimeProfileScope() +{ + TimeProfiler::instance().leave_scope(); +} + +} // namespace esp_utils diff --git a/src/profiler/esp_utils_time_profiler.hpp b/src/profiler/esp_utils_time_profiler.hpp new file mode 100644 index 0000000..9ec170c --- /dev/null +++ b/src/profiler/esp_utils_time_profiler.hpp @@ -0,0 +1,127 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace esp_utils { + +class TimeProfiler { +public: + struct Node { + std::string name; + double total = 0.0; + size_t count = 0; + double min = std::numeric_limits::infinity(); + double max = 0.0; + std::unordered_map> children; + Node *parent = nullptr; + + Node(const std::string &n, Node *p = nullptr) : name(n), parent(p) {} + }; + + struct FormatOptions { + enum class SortBy { + TotalDesc, + NameAsc, + None + }; + enum class TimeUnit { + Microseconds, + Milliseconds, + Seconds + }; + + int name_width = 22; + int calls_width = 6; + int num_width = 10; + int percent_width = 7; + int precision = 2; + bool use_unicode = true; + bool show_percentages = true; + bool use_color = false; + SortBy sort_by = SortBy::TotalDesc; + TimeUnit time_unit = TimeUnit::Milliseconds; + }; + + using Clock = std::chrono::high_resolution_clock; + using TimePoint = std::chrono::time_point; + + static TimeProfiler &instance(); + + // ---------- 配置 ---------- + void set_format_options(const FormatOptions &options); + + // ---------- RAII Scope ---------- + void enter_scope(const std::string &name); + + void leave_scope(); + + // ---------- Cross-thread Events ---------- + void start_event(const std::string &name); + + void end_event(const std::string &name); + + // ---------- Output ---------- + void report(); + + void clear(); + +private: + TimeProfiler(); + + static std::stack &thread_local_stack(); + + static std::unordered_map &start_times(); + + Node *find_or_create_node(const std::string &name); + + double to_unit(std::chrono::duration d) const; + + std::string unit_name() const; + + void print_node(Node *node, + const std::string &prefix, + bool is_last, + double parent_total, + double overall_total) const; + + void print_header() const; + + mutable std::mutex mutex_; + Node root_; + + std::unordered_map> event_stacks_; + FormatOptions format_{}; + + static std::string format_percent(double pct); + + double sum_children_total(const Node *node) const; + + std::vector sorted_children(Node *node) const; +}; + +class TimeProfileScope { +public: + explicit TimeProfileScope(const std::string &name); + ~TimeProfileScope(); +}; + +} // namespace esp_utils + +#define ESP_UTILS_TIME_PROFILE_SCOPE(name) esp_utils::TimeProfileScope _profile_scope_##__LINE__(name) diff --git a/test_apps/main/test_profiler.cpp b/test_apps/main/test_profiler.cpp new file mode 100644 index 0000000..693bb41 --- /dev/null +++ b/test_apps/main/test_profiler.cpp @@ -0,0 +1,73 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include +#include +#include +#include "unity.h" +#if defined(ESP_UTILS_LOG_TAG) +#undef ESP_UTILS_LOG_TAG +#endif +#define ESP_UTILS_LOG_TAG "TestProfiler" +#include "esp_lib_utils.h" + +using namespace esp_utils; + +void do_test(size_t sleep_time) +{ + ESP_UTILS_TIME_PROFILE_SCOPE("do_test"); + std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time)); +} + +void work_task() +{ + ESP_UTILS_TIME_PROFILE_SCOPE("work_task"); + do_test(12 + rand() % 8); +} + +std::thread async_task(int index) +{ + std::string event_name = "cross_thread_event"; + TimeProfiler::instance().start_event(event_name); + return std::thread([event_name, index] { + ESP_UTILS_TIME_PROFILE_SCOPE("async_task_" + std::to_string(index)); + do_test(40); + TimeProfiler::instance().end_event(event_name); + }); +} + +TEST_CASE("Test profiler functions on cpp", "[utils][plugin][CPP]") +{ + auto &prof = TimeProfiler::instance(); + TimeProfiler::FormatOptions opt; + opt.use_unicode = true; + opt.use_color = true; // 开启颜色高亮(>50%红色,>20%黄色,>5%青色) + opt.sort_by = TimeProfiler::FormatOptions::SortBy::TotalDesc; + opt.show_percentages = true; + opt.name_width = 40; + opt.calls_width = 6; + opt.num_width = 10; + opt.percent_width = 7; + opt.precision = 2; + opt.time_unit = TimeProfiler::FormatOptions::TimeUnit::Milliseconds; + prof.set_format_options(opt); + + std::vector workers; + workers.reserve(5); + for (int i = 0; i < 5; ++i) { + ESP_UTILS_TIME_PROFILE_SCOPE("main_iteration"); + work_task(); + workers.emplace_back(async_task(i)); + } + for (auto &t : workers) { + if (t.joinable()) { + t.join(); + } + } + TimeProfiler::instance().report(); + + TimeProfiler::instance().clear(); +} diff --git a/test_apps/sdkconfig.defaults b/test_apps/sdkconfig.defaults index 14d24f8..9c9a697 100644 --- a/test_apps/sdkconfig.defaults +++ b/test_apps/sdkconfig.defaults @@ -1,4 +1,5 @@ CONFIG_ESP_TASK_WDT_EN=n +CONFIG_ESP_MAIN_TASK_STACK_SIZE=5120 CONFIG_FREERTOS_HZ=1000 CONFIG_COMPILER_CXX_EXCEPTIONS=y CONFIG_ESP_UTILS_CONF_MEM_ENABLE_CXX_GLOB_ALLOC=y From 8ae8f1dd3d7362c48f5142659fc8e1df6fae776f Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Wed, 22 Oct 2025 12:01:53 +0800 Subject: [PATCH 3/7] fix(repo): fix build error on idf v6.0 --- src/thread/esp_utils_thread.cpp | 6 +++--- src/thread/esp_utils_thread.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/thread/esp_utils_thread.cpp b/src/thread/esp_utils_thread.cpp index 53573ab..9863778 100644 --- a/src/thread/esp_utils_thread.cpp +++ b/src/thread/esp_utils_thread.cpp @@ -20,14 +20,14 @@ void ThreadConfig::dump() const "\t-core_id(%d)\n" "\t-priority(%d)\n" "\t-stack_size(%d)\n" -#if !defined(ESP_UTILS_THREAD_CONFIG_STACK_CAPS_INVALID) +#if !ESP_UTILS_THREAD_CONFIG_STACK_CAPS_INVALID "\t-stack_in_ext(%s)\n" #endif , name , core_id , static_cast(priority) , static_cast(stack_size) -#if !defined(ESP_UTILS_THREAD_CONFIG_STACK_CAPS_INVALID) +#if !ESP_UTILS_THREAD_CONFIG_STACK_CAPS_INVALID , stack_in_ext ? "true" : "false" #endif ); @@ -49,7 +49,7 @@ thread_config_guard::thread_config_guard(const ThreadConfig &config) new_cfg.prio = config.priority; new_cfg.inherit_cfg = false; new_cfg.pin_to_core = config.core_id; -#if !defined(ESP_UTILS_THREAD_CONFIG_STACK_CAPS_INVALID) +#if !ESP_UTILS_THREAD_CONFIG_STACK_CAPS_INVALID new_cfg.stack_alloc_caps = (config.stack_in_ext ? MALLOC_CAP_SPIRAM : MALLOC_CAP_INTERNAL) | MALLOC_CAP_8BIT; #endif diff --git a/src/thread/esp_utils_thread.hpp b/src/thread/esp_utils_thread.hpp index 1abe454..b4ea1a8 100644 --- a/src/thread/esp_utils_thread.hpp +++ b/src/thread/esp_utils_thread.hpp @@ -34,7 +34,7 @@ struct ThreadConfig { int core_id = THREAD_CONFIG_CORE_DEFAULT; size_t priority = THREAD_CONFIG_PRIO_DEFAULT; size_t stack_size = THREAD_CONFIG_STACK_SIZE_DEFAULT; -#if !defined(ESP_UTILS_THREAD_CONFIG_STACK_CAPS_INVALID) +#if !ESP_UTILS_THREAD_CONFIG_STACK_CAPS_INVALID bool stack_in_ext = false; #endif }; From 552c0de0f4c967a8129826e6e8d52064386434b4 Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Wed, 22 Oct 2025 17:08:29 +0800 Subject: [PATCH 4/7] feat(more): add profiler --- src/profiler/esp_utils_time_profiler.cpp | 1 + src/profiler/esp_utils_time_profiler.hpp | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/profiler/esp_utils_time_profiler.cpp b/src/profiler/esp_utils_time_profiler.cpp index f2bf747..511f1e1 100644 --- a/src/profiler/esp_utils_time_profiler.cpp +++ b/src/profiler/esp_utils_time_profiler.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "esp_utils_time_profiler.hpp" namespace esp_utils { diff --git a/src/profiler/esp_utils_time_profiler.hpp b/src/profiler/esp_utils_time_profiler.hpp index 9ec170c..637f018 100644 --- a/src/profiler/esp_utils_time_profiler.hpp +++ b/src/profiler/esp_utils_time_profiler.hpp @@ -11,13 +11,8 @@ #include #include #include -#include -#include -#include #include #include -#include -#include namespace esp_utils { From 24e82b0a4d8ce831a186ce2af7b47f88beb3d481 Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Wed, 22 Oct 2025 17:22:52 +0800 Subject: [PATCH 5/7] feat(plugin_registry): support to release instance --- src/more/esp_utils_plugin_registry.hpp | 144 +++++++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/src/more/esp_utils_plugin_registry.hpp b/src/more/esp_utils_plugin_registry.hpp index a1a113a..b3457da 100644 --- a/src/more/esp_utils_plugin_registry.hpp +++ b/src/more/esp_utils_plugin_registry.hpp @@ -476,6 +476,150 @@ class PluginRegistry { return removed_count; } + /** + * @brief Release (reset) the instance for the first plugin matching the given name + * + * @param[in] name Plugin name + * @return true if a non-null instance was found and released; false otherwise + */ + static bool releaseInstance(const std::string &name) + { + std::lock_guard lock(getMutex()); + auto &plugins = getPlugins(); + + for (auto &plugin : plugins) { + if (plugin.name == name) { + if (plugin.instance) { + plugin.instance.reset(); + return true; + } + return false; + } + } + return false; + } + + /** + * @brief Release (reset) instances for all plugins matching the given name + * + * @param[in] name Plugin name + * @return Number of instances actually released (were non-null) + */ + static size_t releaseAllInstancesByName(const std::string &name) + { + std::lock_guard lock(getMutex()); + auto &plugins = getPlugins(); + + size_t released_count = 0; + for (auto &plugin : plugins) { + if (plugin.name == name && plugin.instance) { + plugin.instance.reset(); + ++released_count; + } + } + return released_count; + } + + /** + * @brief Release (reset) the instance for the first plugin of specified type + * + * @tparam PluginType Specific plugin type + * @return true if a non-null instance was found and released; false otherwise + */ + template + static bool releaseInstanceByType() + { + static_assert(std::is_base_of_v, "PluginType must inherit from base type T"); + + auto type_key = std::type_index(typeid(PluginType)); + std::lock_guard lock(getMutex()); + auto &plugins = getPlugins(); + + for (auto &plugin : plugins) { + if (plugin.type_idx == type_key) { + if (plugin.instance) { + plugin.instance.reset(); + return true; + } + return false; + } + } + return false; + } + + /** + * @brief Release (reset) instances for all plugins of specified type + * + * @tparam PluginType Specific plugin type + * @return Number of instances actually released (were non-null) + */ + template + static size_t releaseAllInstancesByType() + { + static_assert(std::is_base_of_v, "PluginType must inherit from base type T"); + + auto type_key = std::type_index(typeid(PluginType)); + std::lock_guard lock(getMutex()); + auto &plugins = getPlugins(); + + size_t released_count = 0; + for (auto &plugin : plugins) { + if (plugin.type_idx == type_key && plugin.instance) { + plugin.instance.reset(); + ++released_count; + } + } + return released_count; + } + + /** + * @brief Release (reset) the specific instance by name and type (first match only) + * + * @tparam PluginType Specific plugin type + * @param[in] name Plugin name + * @return true if a non-null instance was found and released; false otherwise + */ + template + static bool releaseSpecificInstance(const std::string &name) + { + static_assert(std::is_base_of_v, "PluginType must inherit from base type T"); + + auto type_key = std::type_index(typeid(PluginType)); + std::lock_guard lock(getMutex()); + auto &plugins = getPlugins(); + + for (auto &plugin : plugins) { + if (plugin.name == name && plugin.type_idx == type_key) { + if (plugin.instance) { + plugin.instance.reset(); + return true; + } + return false; + } + } + return false; + } + + /** + * @brief Release (reset) all instances for all registered plugins + * + * @return Number of instances actually released (were non-null) + */ + static size_t releaseAllInstances() + { + std::lock_guard lock(getMutex()); + auto &plugins = getPlugins(); + + size_t released_count = 0; + for (auto &plugin : plugins) { + if (plugin.instance) { + plugin.instance.reset(); + ++released_count; + } + } + return released_count; + } + /** * @brief Register a plugin with factory function * From ce06f33a1c0ac554a4223a9bb9b7f452d58abf1f Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Fri, 24 Oct 2025 16:51:30 +0800 Subject: [PATCH 6/7] feat(more): add profiler --- src/profiler/esp_utils_time_profiler.cpp | 10 +++++----- src/profiler/esp_utils_time_profiler.hpp | 14 +++++++++----- test_apps/main/test_profiler.cpp | 18 +++++++++--------- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/profiler/esp_utils_time_profiler.cpp b/src/profiler/esp_utils_time_profiler.cpp index 511f1e1..f85400a 100644 --- a/src/profiler/esp_utils_time_profiler.cpp +++ b/src/profiler/esp_utils_time_profiler.cpp @@ -13,7 +13,7 @@ namespace esp_utils { -TimeProfiler &TimeProfiler::instance() +TimeProfiler &TimeProfiler::get_instance() { static TimeProfiler inst; return inst; @@ -299,13 +299,13 @@ std::vector TimeProfiler::sorted_children(Node *node) cons return result; } -TimeProfileScope::TimeProfileScope(const std::string &name) +TimeProfilerScope::TimeProfilerScope(const std::string &name) { - TimeProfiler::instance().enter_scope(name); + TimeProfiler::get_instance().enter_scope(name); } -TimeProfileScope::~TimeProfileScope() +TimeProfilerScope::~TimeProfilerScope() { - TimeProfiler::instance().leave_scope(); + TimeProfiler::get_instance().leave_scope(); } } // namespace esp_utils diff --git a/src/profiler/esp_utils_time_profiler.hpp b/src/profiler/esp_utils_time_profiler.hpp index 637f018..10aacf2 100644 --- a/src/profiler/esp_utils_time_profiler.hpp +++ b/src/profiler/esp_utils_time_profiler.hpp @@ -57,7 +57,7 @@ class TimeProfiler { using Clock = std::chrono::high_resolution_clock; using TimePoint = std::chrono::time_point; - static TimeProfiler &instance(); + static TimeProfiler &get_instance(); // ---------- 配置 ---------- void set_format_options(const FormatOptions &options); @@ -111,12 +111,16 @@ class TimeProfiler { std::vector sorted_children(Node *node) const; }; -class TimeProfileScope { +class TimeProfilerScope { public: - explicit TimeProfileScope(const std::string &name); - ~TimeProfileScope(); + explicit TimeProfilerScope(const std::string &name); + ~TimeProfilerScope(); }; } // namespace esp_utils -#define ESP_UTILS_TIME_PROFILE_SCOPE(name) esp_utils::TimeProfileScope _profile_scope_##__LINE__(name) +#define ESP_UTILS_TIME_PROFILER_SCOPE(name) esp_utils::TimeProfilerScope _profile_scope_##__LINE__(name) + +#define ESP_UTILS_TIME_PROFILER_START_EVENT(name) esp_utils::TimeProfiler::get_instance().start_event(name) + +#define ESP_UTILS_TIME_PROFILER_END_EVENT(name) esp_utils::TimeProfiler::get_instance().end_event(name) diff --git a/test_apps/main/test_profiler.cpp b/test_apps/main/test_profiler.cpp index 693bb41..d519af7 100644 --- a/test_apps/main/test_profiler.cpp +++ b/test_apps/main/test_profiler.cpp @@ -18,30 +18,30 @@ using namespace esp_utils; void do_test(size_t sleep_time) { - ESP_UTILS_TIME_PROFILE_SCOPE("do_test"); + ESP_UTILS_TIME_PROFILER_SCOPE("do_test"); std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time)); } void work_task() { - ESP_UTILS_TIME_PROFILE_SCOPE("work_task"); + ESP_UTILS_TIME_PROFILER_SCOPE("work_task"); do_test(12 + rand() % 8); } std::thread async_task(int index) { std::string event_name = "cross_thread_event"; - TimeProfiler::instance().start_event(event_name); + TimeProfiler::get_instance().start_event(event_name); return std::thread([event_name, index] { - ESP_UTILS_TIME_PROFILE_SCOPE("async_task_" + std::to_string(index)); + ESP_UTILS_TIME_PROFILER_SCOPE("async_task_" + std::to_string(index)); do_test(40); - TimeProfiler::instance().end_event(event_name); + TimeProfiler::get_instance().end_event(event_name); }); } TEST_CASE("Test profiler functions on cpp", "[utils][plugin][CPP]") { - auto &prof = TimeProfiler::instance(); + auto &prof = TimeProfiler::get_instance(); TimeProfiler::FormatOptions opt; opt.use_unicode = true; opt.use_color = true; // 开启颜色高亮(>50%红色,>20%黄色,>5%青色) @@ -58,7 +58,7 @@ TEST_CASE("Test profiler functions on cpp", "[utils][plugin][CPP]") std::vector workers; workers.reserve(5); for (int i = 0; i < 5; ++i) { - ESP_UTILS_TIME_PROFILE_SCOPE("main_iteration"); + ESP_UTILS_TIME_PROFILER_SCOPE("main_iteration"); work_task(); workers.emplace_back(async_task(i)); } @@ -67,7 +67,7 @@ TEST_CASE("Test profiler functions on cpp", "[utils][plugin][CPP]") t.join(); } } - TimeProfiler::instance().report(); + TimeProfiler::get_instance().report(); - TimeProfiler::instance().clear(); + TimeProfiler::get_instance().clear(); } From 2653d6c0b755387d3a7d05f6c7b304a0c8eb8ec1 Mon Sep 17 00:00:00 2001 From: Liu Zhongwei Date: Sat, 25 Oct 2025 15:24:02 +0800 Subject: [PATCH 7/7] fix(log): fix trace guard debug logs cannot be disabled --- src/log/esp_utils_log.h | 54 ++++++++++++++++++--------- src/log/esp_utils_log.hpp | 27 +++++++++++--- src/log/impl/esp_utils_log_impl_esp.h | 26 ++++++------- src/log/impl/esp_utils_log_impl_std.h | 25 ++++++------- test_apps/main/test_on_c.c | 9 ++++- test_apps/main/test_on_cpp.cpp | 7 +++- 6 files changed, 96 insertions(+), 52 deletions(-) diff --git a/src/log/esp_utils_log.h b/src/log/esp_utils_log.h index 5827130..c091a78 100644 --- a/src/log/esp_utils_log.h +++ b/src/log/esp_utils_log.h @@ -16,29 +16,49 @@ # error "Invalid log implementation" #endif -#ifndef ESP_UTILS_LOG_TAG -# define ESP_UTILS_LOG_TAG "Utils" -#endif +#define ESP_UTILS_LOGD_IMPL(tag, format, ...) ESP_UTILS_LOGD_IMPL_FUNC(tag, "[%s:%04d](%s): " format, \ + esp_utils_log_extract_file_name(__FILE__), __LINE__, __func__, ##__VA_ARGS__) +#define ESP_UTILS_LOGI_IMPL(tag, format, ...) ESP_UTILS_LOGI_IMPL_FUNC(tag, "[%s:%04d](%s): " format, \ + esp_utils_log_extract_file_name(__FILE__), __LINE__, __func__, ##__VA_ARGS__) +#define ESP_UTILS_LOGW_IMPL(tag, format, ...) ESP_UTILS_LOGW_IMPL_FUNC(tag, "[%s:%04d](%s): " format, \ + esp_utils_log_extract_file_name(__FILE__), __LINE__, __func__, ##__VA_ARGS__) +#define ESP_UTILS_LOGE_IMPL(tag, format, ...) ESP_UTILS_LOGE_IMPL_FUNC(tag, "[%s:%04d](%s): " format, \ + esp_utils_log_extract_file_name(__FILE__), __LINE__, __func__, ##__VA_ARGS__) -#define ESP_UTILS_LOG_LEVEL(level, format, ...) do { \ - if (level == ESP_UTILS_LOG_LEVEL_DEBUG) { ESP_UTILS_LOGD_IMPL(ESP_UTILS_LOG_TAG, format, ##__VA_ARGS__); } \ - else if (level == ESP_UTILS_LOG_LEVEL_INFO) { ESP_UTILS_LOGI_IMPL(ESP_UTILS_LOG_TAG, format, ##__VA_ARGS__); } \ - else if (level == ESP_UTILS_LOG_LEVEL_WARNING) { ESP_UTILS_LOGW_IMPL(ESP_UTILS_LOG_TAG, format, ##__VA_ARGS__); } \ - else if (level == ESP_UTILS_LOG_LEVEL_ERROR) { ESP_UTILS_LOGE_IMPL(ESP_UTILS_LOG_TAG, format, ##__VA_ARGS__); } \ - else { } \ +#define ESP_UTILS_LOG_LEVEL_IMPL(tag, level, format, ...) do { \ + if (level >= ESP_UTILS_CONF_LOG_LEVEL) { \ + if (level == ESP_UTILS_LOG_LEVEL_DEBUG) { ESP_UTILS_LOGD_IMPL(tag, format, ##__VA_ARGS__); } \ + else if (level == ESP_UTILS_LOG_LEVEL_INFO) { ESP_UTILS_LOGI_IMPL(tag, format, ##__VA_ARGS__); } \ + else if (level == ESP_UTILS_LOG_LEVEL_WARNING) { ESP_UTILS_LOGW_IMPL(tag, format, ##__VA_ARGS__); } \ + else if (level == ESP_UTILS_LOG_LEVEL_ERROR) { ESP_UTILS_LOGE_IMPL(tag, format, ##__VA_ARGS__); } \ + else { } \ + } \ } while(0) -#define ESP_UTILS_LOG_LEVEL_LOCAL(level, format, ...) do { \ - if (level >= ESP_UTILS_CONF_LOG_LEVEL) ESP_UTILS_LOG_LEVEL(level, format, ##__VA_ARGS__); \ - } while(0) +/** + * Macros to simplify logging calls with target tag + */ +#define ESP_UTILS_TAG_LOGD(tag, format, ...) ESP_UTILS_LOG_LEVEL_IMPL(tag, ESP_UTILS_LOG_LEVEL_DEBUG, format, ##__VA_ARGS__) +#define ESP_UTILS_TAG_LOGI(tag, format, ...) ESP_UTILS_LOG_LEVEL_IMPL(tag, ESP_UTILS_LOG_LEVEL_INFO, format, ##__VA_ARGS__) +#define ESP_UTILS_TAG_LOGW(tag, format, ...) ESP_UTILS_LOG_LEVEL_IMPL(tag, ESP_UTILS_LOG_LEVEL_WARNING, format, ##__VA_ARGS__) +#define ESP_UTILS_TAG_LOGE(tag, format, ...) ESP_UTILS_LOG_LEVEL_IMPL(tag, ESP_UTILS_LOG_LEVEL_ERROR, format, ##__VA_ARGS__) + +#if defined(ESP_UTILS_LOG_TAG) +# define ESP_UTILS_LOG_LEVEL(level, format, ...) \ + ESP_UTILS_LOG_LEVEL_IMPL(ESP_UTILS_LOG_TAG, level, format, ##__VA_ARGS__); +#else +# define ESP_UTILS_LOG_TAG_DEFAULT "Utils" +# define ESP_UTILS_LOG_LEVEL(level, format, ...) \ + ESP_UTILS_LOG_LEVEL_IMPL(ESP_UTILS_LOG_TAG_DEFAULT, level, format, ##__VA_ARGS__); +#endif /** - * Macros to simplify logging calls + * Macros to simplify logging calls with a fixed tag (`ESP_UTILS_LOG_TAG` or `ESP_UTILS_LOG_TAG_DEFAULT`) */ -#define ESP_UTILS_LOGD(format, ...) ESP_UTILS_LOG_LEVEL_LOCAL(ESP_UTILS_LOG_LEVEL_DEBUG, format, ##__VA_ARGS__) -#define ESP_UTILS_LOGI(format, ...) ESP_UTILS_LOG_LEVEL_LOCAL(ESP_UTILS_LOG_LEVEL_INFO, format, ##__VA_ARGS__) -#define ESP_UTILS_LOGW(format, ...) ESP_UTILS_LOG_LEVEL_LOCAL(ESP_UTILS_LOG_LEVEL_WARNING, format, ##__VA_ARGS__) -#define ESP_UTILS_LOGE(format, ...) ESP_UTILS_LOG_LEVEL_LOCAL(ESP_UTILS_LOG_LEVEL_ERROR, format, ##__VA_ARGS__) +#define ESP_UTILS_LOGD(format, ...) ESP_UTILS_LOG_LEVEL(ESP_UTILS_LOG_LEVEL_DEBUG, format, ##__VA_ARGS__) +#define ESP_UTILS_LOGI(format, ...) ESP_UTILS_LOG_LEVEL(ESP_UTILS_LOG_LEVEL_INFO, format, ##__VA_ARGS__) +#define ESP_UTILS_LOGW(format, ...) ESP_UTILS_LOG_LEVEL(ESP_UTILS_LOG_LEVEL_WARNING, format, ##__VA_ARGS__) +#define ESP_UTILS_LOGE(format, ...) ESP_UTILS_LOG_LEVEL(ESP_UTILS_LOG_LEVEL_ERROR, format, ##__VA_ARGS__) /** * Micros to log trace of function calls diff --git a/src/log/esp_utils_log.hpp b/src/log/esp_utils_log.hpp index fb06074..a610d5c 100644 --- a/src/log/esp_utils_log.hpp +++ b/src/log/esp_utils_log.hpp @@ -173,14 +173,31 @@ class log_trace_guard { // The following macros are deprecated, please use `ESP_UTILS_LOG_TRACE_GUARD()` instead # define ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS() ESP_UTILS_LOGD("(@%p) Enter", this) # define ESP_UTILS_LOG_TRACE_EXIT_WITH_THIS() ESP_UTILS_LOGD("(@%p) Exit", this) - # if ESP_UTILS_LOG_CXX20_SUPPORT # define ESP_UTILS_LOG_MAKE_FS(str) []{ constexpr esp_utils::detail::FixedString s(str); return s; }() -# define ESP_UTILS_LOG_TRACE_GUARD() esp_utils::detail::log_trace_guard _log_trace_guard_{} -# define ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS() esp_utils::detail::log_trace_guard _log_trace_guard_{this} +# if defined(ESP_UTILS_LOG_TAG) +# define ESP_UTILS_LOG_TRACE_GUARD() \ + esp_utils::detail::log_trace_guard _log_trace_guard_## __LINE__{}; +# define ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS() \ + esp_utils::detail::log_trace_guard _log_trace_guard_## __LINE__{this}; +# else +# define ESP_UTILS_LOG_TRACE_GUARD() \ + esp_utils::detail::log_trace_guard _log_trace_guard_## __LINE__{}; +# define ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS() \ + esp_utils::detail::log_trace_guard _log_trace_guard_## __LINE__{this}; +# endif # else -# define ESP_UTILS_LOG_TRACE_GUARD() esp_utils::detail::log_trace_guard _log_trace_guard_{ESP_UTILS_LOG_TAG, __func__, __FILE__, __LINE__} -# define ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS() esp_utils::detail::log_trace_guard _log_trace_guard_{ESP_UTILS_LOG_TAG, __func__, __FILE__, __LINE__, this} +# if defined(ESP_UTILS_LOG_TAG) +# define ESP_UTILS_LOG_TRACE_GUARD() \ + esp_utils::detail::log_trace_guard _log_trace_guard_## __LINE__{ESP_UTILS_LOG_TAG, __func__, __FILE__, __LINE__}; +# define ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS() \ + esp_utils::detail::log_trace_guard _log_trace_guard_## __LINE__{ESP_UTILS_LOG_TAG, __func__, __FILE__, __LINE__, this} +# else +# define ESP_UTILS_LOG_TRACE_GUARD() \ + esp_utils::detail::log_trace_guard _log_trace_guard_## __LINE__{ESP_UTILS_LOG_TAG_DEFAULT, __func__, __FILE__, __LINE__}; +# define ESP_UTILS_LOG_TRACE_GUARD_WITH_THIS() \ + esp_utils::detail::log_trace_guard _log_trace_guard_## __LINE__{ESP_UTILS_LOG_TAG_DEFAULT, __func__, __FILE__, __LINE__, this}; +# endif # endif #else # define ESP_UTILS_LOG_TRACE_ENTER_WITH_THIS() diff --git a/src/log/impl/esp_utils_log_impl_esp.h b/src/log/impl/esp_utils_log_impl_esp.h index 4d4245e..6a681f0 100644 --- a/src/log/impl/esp_utils_log_impl_esp.h +++ b/src/log/impl/esp_utils_log_impl_esp.h @@ -7,17 +7,15 @@ #include "esp_log.h" - -#define ESP_UTILS_LOGD_IMPL_FUNC(TAG, format, ...) ESP_LOGD(TAG, format, ##__VA_ARGS__) -#define ESP_UTILS_LOGI_IMPL_FUNC(TAG, format, ...) ESP_LOGI(TAG, format, ##__VA_ARGS__) -#define ESP_UTILS_LOGW_IMPL_FUNC(TAG, format, ...) ESP_LOGW(TAG, format, ##__VA_ARGS__) -#define ESP_UTILS_LOGE_IMPL_FUNC(TAG, format, ...) ESP_LOGE(TAG, format, ##__VA_ARGS__) - -#define ESP_UTILS_LOGD_IMPL(TAG, format, ...) ESP_UTILS_LOGD_IMPL_FUNC(TAG, "[%s:%04d](%s): " format, \ - esp_utils_log_extract_file_name(__FILE__), __LINE__, __func__, ##__VA_ARGS__) -#define ESP_UTILS_LOGI_IMPL(TAG, format, ...) ESP_UTILS_LOGI_IMPL_FUNC(TAG, "[%s:%04d](%s): " format, \ - esp_utils_log_extract_file_name(__FILE__), __LINE__, __func__, ##__VA_ARGS__) -#define ESP_UTILS_LOGW_IMPL(TAG, format, ...) ESP_UTILS_LOGW_IMPL_FUNC(TAG, "[%s:%04d](%s): " format, \ - esp_utils_log_extract_file_name(__FILE__), __LINE__, __func__, ##__VA_ARGS__) -#define ESP_UTILS_LOGE_IMPL(TAG, format, ...) ESP_UTILS_LOGE_IMPL_FUNC(TAG, "[%s:%04d](%s): " format, \ - esp_utils_log_extract_file_name(__FILE__), __LINE__, __func__, ##__VA_ARGS__) +#if !defined(ESP_UTILS_LOGD_IMPL_FUNC) +# define ESP_UTILS_LOGD_IMPL_FUNC(TAG, format, ...) ESP_LOGD(TAG, format, ##__VA_ARGS__) +#endif +#if !defined(ESP_UTILS_LOGI_IMPL_FUNC) +# define ESP_UTILS_LOGI_IMPL_FUNC(TAG, format, ...) ESP_LOGI(TAG, format, ##__VA_ARGS__) +#endif +#if !defined(ESP_UTILS_LOGW_IMPL_FUNC) +# define ESP_UTILS_LOGW_IMPL_FUNC(TAG, format, ...) ESP_LOGW(TAG, format, ##__VA_ARGS__) +#endif +#if !defined(ESP_UTILS_LOGE_IMPL_FUNC) +# define ESP_UTILS_LOGE_IMPL_FUNC(TAG, format, ...) ESP_LOGE(TAG, format, ##__VA_ARGS__) +#endif diff --git a/src/log/impl/esp_utils_log_impl_std.h b/src/log/impl/esp_utils_log_impl_std.h index 6d694c8..899499d 100644 --- a/src/log/impl/esp_utils_log_impl_std.h +++ b/src/log/impl/esp_utils_log_impl_std.h @@ -7,16 +7,15 @@ #include -#define ESP_UTILS_LOGD_IMPL_FUNC(TAG, format, ...) printf("[D][%s]" format "\n", TAG, ##__VA_ARGS__) -#define ESP_UTILS_LOGI_IMPL_FUNC(TAG, format, ...) printf("[I][%s]" format "\n", TAG, ##__VA_ARGS__) -#define ESP_UTILS_LOGW_IMPL_FUNC(TAG, format, ...) printf("[W][%s]" format "\n", TAG, ##__VA_ARGS__) -#define ESP_UTILS_LOGE_IMPL_FUNC(TAG, format, ...) printf("[E][%s]" format "\n", TAG, ##__VA_ARGS__) - -#define ESP_UTILS_LOGD_IMPL(TAG, format, ...) ESP_UTILS_LOGD_IMPL_FUNC(TAG, "[%s:%04d](%s): " format, \ - esp_utils_log_extract_file_name(__FILE__), __LINE__, __func__, ##__VA_ARGS__) -#define ESP_UTILS_LOGI_IMPL(TAG, format, ...) ESP_UTILS_LOGI_IMPL_FUNC(TAG, "[%s:%04d](%s): " format, \ - esp_utils_log_extract_file_name(__FILE__), __LINE__, __func__, ##__VA_ARGS__) -#define ESP_UTILS_LOGW_IMPL(TAG, format, ...) ESP_UTILS_LOGW_IMPL_FUNC(TAG, "[%s:%04d](%s): " format, \ - esp_utils_log_extract_file_name(__FILE__), __LINE__, __func__, ##__VA_ARGS__) -#define ESP_UTILS_LOGE_IMPL(TAG, format, ...) ESP_UTILS_LOGE_IMPL_FUNC(TAG, "[%s:%04d](%s): " format, \ - esp_utils_log_extract_file_name(__FILE__), __LINE__, __func__, ##__VA_ARGS__) +#if !defined(ESP_UTILS_LOGD_IMPL_FUNC) +# define ESP_UTILS_LOGD_IMPL_FUNC(TAG, format, ...) printf("[D][%s]" format "\n", TAG, ##__VA_ARGS__) +#endif +#if !defined(ESP_UTILS_LOGI_IMPL_FUNC) +# define ESP_UTILS_LOGI_IMPL_FUNC(TAG, format, ...) printf("[I][%s]" format "\n", TAG, ##__VA_ARGS__) +#endif +#if !defined(ESP_UTILS_LOGW_IMPL_FUNC) +# define ESP_UTILS_LOGW_IMPL_FUNC(TAG, format, ...) printf("[W][%s]" format "\n", TAG, ##__VA_ARGS__) +#endif +#if !defined(ESP_UTILS_LOGE_IMPL_FUNC) +# define ESP_UTILS_LOGE_IMPL_FUNC(TAG, format, ...) printf("[E][%s]" format "\n", TAG, ##__VA_ARGS__) +#endif diff --git a/test_apps/main/test_on_c.c b/test_apps/main/test_on_c.c index 8d4b973..f648d18 100644 --- a/test_apps/main/test_on_c.c +++ b/test_apps/main/test_on_c.c @@ -1,10 +1,10 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 */ #include "unity.h" -#define ESP_UTILS_LOG_TAG "TestC" +#define ESP_UTILS_LOG_TAG "TestC::DefaultTag" #include "esp_lib_utils.h" #include "esp_utils_helpers.h" @@ -17,6 +17,11 @@ TEST_CASE("Test log functions on C", "[utils][log][C]") ESP_UTILS_LOGW("This is a warning message"); ESP_UTILS_LOGE("This is an error message"); + ESP_UTILS_TAG_LOGD("TestC::CustomTag", "This is a debug message"); + ESP_UTILS_TAG_LOGI("TestC::CustomTag", "This is an info message"); + ESP_UTILS_TAG_LOGW("TestC::CustomTag", "This is a warning message"); + ESP_UTILS_TAG_LOGE("TestC::CustomTag", "This is an error message"); + ESP_UTILS_LOG_TRACE_EXIT(); } diff --git a/test_apps/main/test_on_cpp.cpp b/test_apps/main/test_on_cpp.cpp index 14e56e2..dac2db8 100644 --- a/test_apps/main/test_on_cpp.cpp +++ b/test_apps/main/test_on_cpp.cpp @@ -6,7 +6,7 @@ #include #include #include "unity.h" -#define ESP_UTILS_LOG_TAG "TestCpp" +#define ESP_UTILS_LOG_TAG "TestCpp::DefaultTag" #include "esp_lib_utils.h" #include "esp_utils_helpers.h" @@ -37,6 +37,11 @@ TEST_CASE("Test log functions on cpp", "[utils][log][CPP]") ESP_UTILS_LOGI("This is an info message"); ESP_UTILS_LOGW("This is a warning message"); ESP_UTILS_LOGE("This is an error message"); + + ESP_UTILS_TAG_LOGD("TestCpp::CustomTag", "This is a debug message"); + ESP_UTILS_TAG_LOGI("TestCpp::CustomTag", "This is an info message"); + ESP_UTILS_TAG_LOGW("TestCpp::CustomTag", "This is a warning message"); + ESP_UTILS_TAG_LOGE("TestCpp::CustomTag", "This is an error message"); } #define MALLOC_GOOD_SIZE (1 * 1024)