Skip to content

stats: make it's possible to enable the new stats API#45846

Open
wbpcode wants to merge 3 commits into
envoyproxy:mainfrom
wbpcode:dev-add-flag-to-enable-new-stats-api
Open

stats: make it's possible to enable the new stats API#45846
wbpcode wants to merge 3 commits into
envoyproxy:mainfrom
wbpcode:dev-add-flag-to-enable-new-stats-api

Conversation

@wbpcode

@wbpcode wbpcode commented Jun 26, 2026

Copy link
Copy Markdown
Member

Commit Message: stats: make it's possible to enable the new stats API
Additional Description:

The PR did two things:

  1. used the stats_tags and use_all_default_tags to infer whether the new tag-friendly API should be used or not.
  2. merged the ScopImpl and TagScopeImpl into single class and use a bool flag to tell legacy code or new logic should be used. This is because we cannot get the stats_tags and use_all_default_tags when we constructing the ThreadLocalStore and the root scope. So, we need to ensure the a single scope implementation could support both legacy and new logic.

The new tag-friendly API will be enabled when: no custom stats_tags and use_all_default_tags is true.

NOTE: We still add a new runtime flag. This runtime flag is used to guard existing behavior in the process of migrating the existing stats API calling to use the new API. We need lots of changes to migrate and ensure all default tags could be emitted correctly by the new API. Before complete the migration, the runtime guard is necessary.

Risk Level: low.
Testing: unit.
Docs Changes: n/a.
Release Notes: n/a.
Platform Specific Features: n/a.

Signed-off-by: wbpcode/wangbaiping <wbphub@gmail.com>
@repokitteh-read-only

Copy link
Copy Markdown

CC @envoyproxy/runtime-guard-changes: FYI only for changes made to (source/common/runtime/runtime_features.cc).

🐱

Caused by: #45846 was opened by wbpcode.

see: more, trace.

wbpcode added 2 commits June 26, 2026 09:13
Signed-off-by: wbpcode/wangbaiping <wbphub@gmail.com>
@wbpcode

wbpcode commented Jun 26, 2026

Copy link
Copy Markdown
Member Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements the explicit-tags logic for the stats store, allowing scope-level tags to propagate to every stat without re-parsing flat names, controlled by the runtime guard envoy.reloadable_features.enable_stats_explicit_tags. The review feedback identifies a critical memory corruption issue in legacy mode where populateList is called with empty arguments, resulting in an incorrectly sized StatNameList. Additionally, suggestions are made to add safety assertions in SymbolTable::populateList and to use auto for type deduction when creating a shared pointer.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.


// Only single prefix for legacy mode. Both prefix_ and base_prefix_ are set to the same value,
// which is the flat (tagged) prefix.
parent_.alloc_.symbolTable().populateList(prefix, {}, {}, prefix_list_);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

In legacy mode, calling the 4-argument populateList with empty base_name and name_tags results in a corrupted StatNameList because stat_name_count is set to 2, but only 1 element's bytes are written. When the list is cleared in the destructor, it will iterate 2 times, reading out-of-bounds garbage bytes for the second element and calling symbol_table.free on them, which can cause memory corruption or crashes.

Since we only need to store a single prefix in legacy mode, we should use the 3-argument populateList overload instead, which correctly populates the list with exactly 1 element.

Suggested change
parent_.alloc_.symbolTable().populateList(prefix, {}, {}, prefix_list_);
parent_.alloc_.symbolTable().populateList(&prefix, 1, prefix_list_);

@wbpcode wbpcode Jun 26, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the populate list could handle empty stat name correctly. The empty stat name also has one byte.

Comment on lines +721 to +723
void SymbolTable::populateList(StatName tagged_name, StatName base_name, StatNameTagSpan name_tags,
StatNameList& list) {
const size_t stat_name_count = name_tags.size() * 2 + 2;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To prevent silent corruption of StatNameList when empty StatNames are passed, we should add assertions to ensure that tagged_name, base_name, and all tags in name_tags are not empty.

void SymbolTable::populateList(StatName tagged_name, StatName base_name, StatNameTagSpan name_tags,
                               StatNameList& list) {
  ASSERT(!tagged_name.empty());
  ASSERT(!base_name.empty());
  for (const auto& tag : name_tags) {
    ASSERT(!tag.first.empty());
    ASSERT(!tag.second.empty());
  }

  const size_t stat_name_count = name_tags.size() * 2 + 2;

@wbpcode wbpcode Jun 26, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the populate list could handle empty stat name correctly. The empty stat name also has one byte.

// re-interns them into the child's own pool).
const TagUtility::TagStatNameJoiner joiner(base_prefix_, prefix_tags_, prefix_, base_name,
name_tags, tagged_name, symbolTable());
std::shared_ptr<ScopeImpl> new_scope = std::make_shared<ScopeImpl>(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

According to the general rules, we should prefer using auto for variable declarations when the type is clearly deduced from the right-hand side (e.g., when using std::make_shared or std::make_unique).

Suggested change
std::shared_ptr<ScopeImpl> new_scope = std::make_shared<ScopeImpl>(
auto new_scope = std::make_shared<ScopeImpl>(
References
  1. Use 'auto' for variable declarations when the type is clearly deduced from the right-hand side (e.g., when using 'std::make_shared' or 'std::make_unique'), as explicit types in these cases are considered redundant and noisy.

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.

3 participants