From 04d0a2399935bf768c366a3d2c39be237357477c Mon Sep 17 00:00:00 2001 From: devtejasx Date: Sat, 1 Aug 2026 12:29:43 +0530 Subject: [PATCH] Give ProfilerManager access to the State being profiled (#2234) Custom profilers that open a named region need the benchmark name, but the ProfilerManager hooks receive no context at all. Adding an overload of AfterSetupStart()/BeforeTeardownStop() that takes a State is not source compatible: the new overload hides the old one in every existing subclass. Instead, add a protected ProfilerManager::GetState() accessor that returns the State of the run being profiled. Existing subclasses keep compiling untouched. The accessor returns a pointer, not a reference, because there is no meaningful answer outside the hooks. State sets the pointer immediately before invoking a hook and clears it as soon as the hook returns, so a caller outside AfterSetupStart()/BeforeTeardownStop() observes nullptr rather than a stale State. The test asserts both: the benchmark name is readable inside the hook, and the accessor is null outside it. --- docs/user_guide.md | 5 +++++ include/benchmark/managers.h | 13 +++++++++++++ src/benchmark.cc | 4 ++++ test/profiler_manager_test.cc | 16 +++++++++++++++- 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/docs/user_guide.md b/docs/user_guide.md index 3fed9261d7..e37c19bfe2 100644 --- a/docs/user_guide.md +++ b/docs/user_guide.md @@ -1604,6 +1604,11 @@ If set, the `ProfilerManager::AfterSetupStart` and end of a separate benchmark run to allow user code to collect and report user-provided profile metrics. +From within either hook, `ProfilerManager::GetState()` returns a pointer to +the `benchmark::State` of the run being profiled, which gives access to run +information such as the benchmark name (e.g. for registering named profiling +regions). Outside of the two hooks it returns `nullptr`. + Output collected from this profiling run must be reported separately. diff --git a/include/benchmark/managers.h b/include/benchmark/managers.h index e8b6cd4749..e6784738f0 100644 --- a/include/benchmark/managers.h +++ b/include/benchmark/managers.h @@ -24,6 +24,8 @@ namespace benchmark { +class State; + class MemoryManager { public: static constexpr int64_t TombstoneValue = std::numeric_limits::max(); @@ -56,6 +58,17 @@ class ProfilerManager { virtual ~ProfilerManager() {} virtual void AfterSetupStart() = 0; virtual void BeforeTeardownStop() = 0; + + protected: + // The State of the benchmark run being profiled, giving access to e.g. the + // benchmark name (for a named profiling region). Non-null only while + // AfterSetupStart() or BeforeTeardownStop() is executing; any other caller + // observes nullptr. + const State* GetState() const { return state_; } + + private: + friend class State; + const State* state_ = nullptr; }; BENCHMARK_EXPORT diff --git a/src/benchmark.cc b/src/benchmark.cc index 91280295e8..55e5faadc7 100644 --- a/src/benchmark.cc +++ b/src/benchmark.cc @@ -331,7 +331,9 @@ void State::StartKeepRunning() { started_ = true; total_iterations_ = skipped() ? 0 : max_iterations; if (BENCHMARK_BUILTIN_EXPECT(profiler_manager_ != nullptr, false)) { + profiler_manager_->state_ = this; profiler_manager_->AfterSetupStart(); + profiler_manager_->state_ = nullptr; } manager_->StartStopBarrier(); if (!skipped()) { @@ -349,7 +351,9 @@ void State::FinishKeepRunning() { finished_ = true; manager_->StartStopBarrier(); if (BENCHMARK_BUILTIN_EXPECT(profiler_manager_ != nullptr, false)) { + profiler_manager_->state_ = this; profiler_manager_->BeforeTeardownStop(); + profiler_manager_->state_ = nullptr; } } diff --git a/test/profiler_manager_test.cc b/test/profiler_manager_test.cc index bd86d42c04..25b239b649 100644 --- a/test/profiler_manager_test.cc +++ b/test/profiler_manager_test.cc @@ -2,6 +2,7 @@ #include #include +#include #include "benchmark/benchmark_api.h" #include "benchmark/managers.h" @@ -13,11 +14,21 @@ namespace { class TestProfilerManager : public benchmark::ProfilerManager { public: - void AfterSetupStart() override { ++start_called; } + void AfterSetupStart() override { + ++start_called; + if (GetState() != nullptr) { + benchmark_name = GetState()->name(); + } + } void BeforeTeardownStop() override { ++stop_called; } + // Expose the protected accessor so the harness can verify it yields + // nullptr outside the hooks. + const benchmark::State* StateOutsideHooks() const { return GetState(); } + int start_called = 0; int stop_called = 0; + std::string benchmark_name; }; void BM_empty(benchmark::State& state) { @@ -55,4 +66,7 @@ int main(int argc, char* argv[]) { assert(pm->start_called == 1); assert(pm->stop_called == 1); + assert(pm->benchmark_name == "BM_empty"); + // Outside the hooks the state must not be observable. + assert(pm->StateOutsideHooks() == nullptr); }