Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/benchmark/benchmark_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ BENCHMARK_EXPORT void SetBenchmarkFilter(std::string value);

BENCHMARK_EXPORT int32_t GetBenchmarkVerbosity();

// Creates the display reporter selected by --benchmark_format. Returns a

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

if the caller is meant to own the returned type, then we should be returning a unique_ptr.

https://google.github.io/styleguide/cppguide.html#Ownership_and_Smart_Pointers

// fresh instance on every call; the caller owns and frees it.
BENCHMARK_EXPORT BenchmarkReporter* CreateDefaultDisplayReporter();

BENCHMARK_EXPORT size_t RunSpecifiedBenchmarks();
Expand Down
10 changes: 5 additions & 5 deletions src/benchmark.cc
Original file line number Diff line number Diff line change
Expand Up @@ -586,11 +586,11 @@ ConsoleReporter::OutputOptions GetOutputOptions(bool force_no_color) {
} // end namespace internal

BenchmarkReporter* CreateDefaultDisplayReporter() {
static auto* default_display_reporter =
internal::CreateReporter(FLAGS_benchmark_format,
internal::GetOutputOptions())
.release();
return default_display_reporter;
// Each caller owns the returned reporter, so never cache it in a static:
// the first caller would free it and later calls would dangle (#2240).
return internal::CreateReporter(FLAGS_benchmark_format,
internal::GetOutputOptions())
.release();
}

size_t RunSpecifiedBenchmarks() {
Expand Down
3 changes: 3 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ benchmark_add_test(NAME user_counters_thousands_test COMMAND user_counters_thous
compile_output_test(memory_manager_test)
benchmark_add_test(NAME memory_manager_test COMMAND memory_manager_test --benchmark_min_time=0.01s)

compile_benchmark_test(default_display_reporter_test)
benchmark_add_test(NAME default_display_reporter_test COMMAND default_display_reporter_test)

compile_output_test(profiler_manager_test)
benchmark_add_test(NAME profiler_manager_test COMMAND profiler_manager_test --benchmark_min_time=0.01s)

Expand Down
28 changes: 28 additions & 0 deletions test/default_display_reporter_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Regression test for https://github.com/google/benchmark/issues/2240:
// CreateDefaultDisplayReporter() cached its result in a function-local static
// even though each caller owns (and frees) the returned pointer, so the
// second call returned a dangling pointer. It must hand out a fresh reporter
// on every call.

#include <iostream>
#include <memory>

#include "benchmark/benchmark.h"

int main(int argc, char** argv) {
benchmark::MaybeReenterWithoutASLR(argc, argv);
benchmark::Initialize(&argc, argv);

std::unique_ptr<benchmark::BenchmarkReporter> first(
benchmark::CreateDefaultDisplayReporter());
std::unique_ptr<benchmark::BenchmarkReporter> second(
benchmark::CreateDefaultDisplayReporter());

if (first == nullptr || second == nullptr || first.get() == second.get()) {
std::cerr << "CreateDefaultDisplayReporter must return a fresh, "
"caller-owned reporter on every call\n";
return 1;
}

return 0;
}