Fix ESP font hot-path stack overflow

This commit is contained in:
liu weikai
2026-07-11 12:19:11 +08:00
parent 4f51c3fed7
commit 74c6a6fe5d
3 changed files with 120 additions and 6 deletions
+8
View File
@@ -68,6 +68,14 @@ if(BUILD_TESTING)
COMMAND trailmate_esp32_lvgl_sd_coredump_contract_smoke
"${TRAIL_MATE_REPO_ROOT}")
add_executable(trailmate_esp32_lvgl_font_hot_path_contract_smoke
tests/esp32_lvgl_font_hot_path_contract_smoke.cpp)
target_compile_features(trailmate_esp32_lvgl_font_hot_path_contract_smoke
PRIVATE cxx_std_17)
add_test(NAME trailmate_esp32_lvgl_font_hot_path_contract_smoke
COMMAND trailmate_esp32_lvgl_font_hot_path_contract_smoke
"${TRAIL_MATE_REPO_ROOT}")
add_executable(trailmate_product_target_profile_smoke
"${TRAIL_MATE_REPO_ROOT}/modules/product_composition/tests/test_target_profile.cpp")
target_link_libraries(trailmate_product_target_profile_smoke
@@ -0,0 +1,89 @@
#include <cassert>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <string>
namespace
{
std::string read_file(const std::filesystem::path& path)
{
std::ifstream stream(path, std::ios::binary);
assert(stream.is_open());
std::ostringstream out;
out << stream.rdbuf();
return out.str();
}
bool contains(const std::string& haystack, const char* needle)
{
return haystack.find(needle) != std::string::npos;
}
bool not_contains(const std::string& haystack, const char* needle)
{
return !contains(haystack, needle);
}
std::size_t position_of(const std::string& haystack, const char* needle)
{
const auto pos = haystack.find(needle);
assert(pos != std::string::npos);
return pos;
}
std::size_t position_of_after(const std::string& haystack,
const char* needle,
std::size_t offset)
{
const auto pos = haystack.find(needle, offset);
assert(pos != std::string::npos);
return pos;
}
} // namespace
int main(int argc, char** argv)
{
assert(argc == 2);
const std::filesystem::path repo_root = argv[1];
const std::string registry = read_file(
repo_root / "modules/ui_shared/src/ui/i18n/resource_pack_registry.cpp");
assert(contains(registry, "bool s_defer_font_load_overlay_present = false;"));
assert(contains(registry, "font_load_overlay_policy()"));
assert(contains(registry, "Policy::Overlay"));
assert(contains(registry, "Policy::OverlayImmediate"));
assert(contains(registry, "font_load_overlay_policy(),"));
const std::size_t forced_overlay = position_of(registry, "class ScopedForcedFontLoadOverlay");
const std::size_t defer_restore = position_of(registry, "previous_defer_present_");
const std::size_t defer_enable = position_of(registry, "s_defer_font_load_overlay_present = true;");
assert(forced_overlay < defer_restore);
assert(defer_restore < defer_enable);
const std::size_t hot_path_guard = position_of(registry, "bool can_activate_content_supplement_for_text");
const std::size_t hot_path_check = position_of(registry, "can_load_font_from_content_hot_path(pack)");
const std::size_t budget_check = position_of(registry, "can_add_content_supplement(pack)");
assert(hot_path_guard < hot_path_check);
assert(hot_path_check < budget_check);
const std::size_t content_ensure = position_of(registry, "bool ensure_content_font_for_text");
const std::size_t supplement_check = position_of(
registry,
"!can_activate_content_supplement_for_text(*candidate)");
const std::size_t hot_path_reason = position_of_after(registry, "\"ui_hot_path\"", supplement_check);
const std::size_t content_budget_reason = position_of_after(registry, "\"content_budget\"", supplement_check);
assert(content_ensure < supplement_check);
assert(supplement_check < hot_path_reason);
assert(supplement_check < content_budget_reason);
assert(not_contains(
registry,
"foreground::make_snapshot(foreground::Slot::I18nFontLoad,\n"
" foreground::Policy::OverlayImmediate"));
return 0;
}
@@ -191,6 +191,7 @@ unsigned s_ui_helper_route_diagnostics = 0;
unsigned s_direct_route_diagnostics = 0;
bool s_allow_sync_external_font_activation = false;
bool s_force_font_load_overlay = false;
bool s_defer_font_load_overlay_present = false;
std::uint32_t s_font_load_overlay_generation = 0;
std::uint32_t next_font_load_overlay_generation()
@@ -203,6 +204,13 @@ std::uint32_t next_font_load_overlay_generation()
return s_font_load_overlay_generation;
}
::ui::widgets::foreground_operation::Policy font_load_overlay_policy()
{
return s_defer_font_load_overlay_present
? ::ui::widgets::foreground_operation::Policy::Overlay
: ::ui::widgets::foreground_operation::Policy::OverlayImmediate;
}
class ScopedFontLoadOverlay
{
public:
@@ -218,7 +226,7 @@ class ScopedFontLoadOverlay
namespace foreground = ::ui::widgets::foreground_operation;
foreground::publish(
foreground::make_snapshot(foreground::Slot::I18nFontLoad,
foreground::Policy::OverlayImmediate,
font_load_overlay_policy(),
foreground::Priority::Blocking,
"Loading language pack...",
pack.display_name.empty()
@@ -330,14 +338,17 @@ class ScopedForcedFontLoadOverlay
{
public:
ScopedForcedFontLoadOverlay()
: previous_force_overlay_(s_force_font_load_overlay)
: previous_force_overlay_(s_force_font_load_overlay),
previous_defer_present_(s_defer_font_load_overlay_present)
{
s_force_font_load_overlay = true;
s_defer_font_load_overlay_present = true;
}
~ScopedForcedFontLoadOverlay()
{
s_force_font_load_overlay = previous_force_overlay_;
s_defer_font_load_overlay_present = previous_defer_present_;
}
ScopedForcedFontLoadOverlay(const ScopedForcedFontLoadOverlay&) = delete;
@@ -345,6 +356,7 @@ class ScopedForcedFontLoadOverlay
private:
bool previous_force_overlay_ = false;
bool previous_defer_present_ = false;
};
const char* safe_text(const char* value)
@@ -1616,11 +1628,14 @@ bool can_activate_content_supplement_for_text(const FontPackRecord& pack)
// Content text is data from contacts, chat, map labels, and extension pages.
// It must not be tied to the display locale: an English UI can still need a
// Chinese/Korean/emoji content font when those optional packs are installed.
// The guard here is coverage + memory-profile budget, while load_font_pack()
// still owns failure backoff so a missing/bad pack is not retried per label.
// The guard here is hot-path safety + coverage + memory-profile budget,
// while load_font_pack() still owns failure backoff so a missing/bad pack is
// not retried per label.
return pack.builtin ||
is_font_runtime_loaded(pack) ||
(font_pack_supports_content(pack) && can_add_content_supplement(pack));
(can_load_font_from_content_hot_path(pack) &&
font_pack_supports_content(pack) &&
can_add_content_supplement(pack));
#else
(void)pack;
return true;
@@ -3331,7 +3346,9 @@ bool ensure_content_font_for_text(const char* text)
!kAllowSynchronousContentSupplementFontLoad &&
!can_activate_content_supplement_for_text(*candidate))
{
log_font_load_deferred(*candidate, "content_supplement", "content_budget");
const char* reason =
can_load_font_from_content_hot_path(*candidate) ? "content_budget" : "ui_hot_path";
log_font_load_deferred(*candidate, "content_supplement", reason);
break;
}
if (!ensure_font_pack_loaded(candidate))