1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <vector>
6
7 #include "base/containers/span.h"
8 #include "base/debug/stack_trace.h"
9 #include "base/logging.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/timer/lap_timer.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "testing/perf/perf_result_reporter.h"
14
15 namespace base {
16 namespace debug {
17
18 // Change kTimeLimit to something higher if you need more time to capture a
19 // trace.
20 constexpr base::TimeDelta kTimeLimit = base::Seconds(3);
21 constexpr int kWarmupRuns = 100;
22 constexpr int kTimeCheckInterval = 1000;
23 constexpr char kMetricStackTraceDuration[] = ".duration_per_run";
24 constexpr char kMetricStackTraceThroughput[] = ".throughput";
25 constexpr int kNumTracerObjAllocs = 5000;
26
SetUpReporter(const std::string & story_name)27 perf_test::PerfResultReporter SetUpReporter(const std::string& story_name) {
28 perf_test::PerfResultReporter reporter("StackTracePerf", story_name);
29 reporter.RegisterImportantMetric(kMetricStackTraceDuration, "ns");
30 reporter.RegisterImportantMetric(kMetricStackTraceThroughput, "runs/s");
31 return reporter;
32 }
33
34 class StackTracer {
35 public:
StackTracer(size_t trace_count)36 StackTracer(size_t trace_count) : trace_count_(trace_count) {}
Trace()37 void Trace() {
38 StackTrace st(trace_count_);
39 span<const void* const> addresses = st.addresses();
40 // make sure a valid array of stack frames is returned
41 ASSERT_FALSE(addresses.empty());
42 EXPECT_TRUE(addresses[0]);
43 // make sure the test generates the intended count of stack frames
44 EXPECT_EQ(trace_count_, addresses.size());
45 }
46
47 private:
48 const size_t trace_count_;
49 };
50
MultiObjTest(size_t trace_count)51 void MultiObjTest(size_t trace_count) {
52 // Measures average stack trace generation (unwinding) performance across
53 // multiple objects to get a more realistic figure. Calling
54 // base::debug::StraceTrace() repeatedly from the same object may lead to
55 // unrealistic performance figures that are optimised by the host (for
56 // example, CPU caches distorting the results), whereas MTE requires
57 // unwinding for allocations that occur all over the place.
58 perf_test::PerfResultReporter reporter =
59 SetUpReporter(base::StringPrintf("trace_count_%zu", trace_count));
60 LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval,
61 LapTimer::TimerMethod::kUseTimeTicks);
62 std::vector<std::unique_ptr<StackTracer>> tracers;
63 for (int i = 0; i < kNumTracerObjAllocs; ++i) {
64 tracers.push_back(std::make_unique<StackTracer>(trace_count));
65 }
66 std::vector<std::unique_ptr<StackTracer>>::iterator it = tracers.begin();
67 timer.Start();
68 do {
69 (*it)->Trace();
70 if (++it == tracers.end())
71 it = tracers.begin();
72 timer.NextLap();
73 } while (!timer.HasTimeLimitExpired());
74 reporter.AddResult(kMetricStackTraceDuration, timer.TimePerLap());
75 reporter.AddResult(kMetricStackTraceThroughput, timer.LapsPerSecond());
76 }
77
78 class StackTracePerfTest : public testing::TestWithParam<size_t> {};
79
80 INSTANTIATE_TEST_SUITE_P(,
81 StackTracePerfTest,
82 ::testing::Range(size_t(4), size_t(16), size_t(4)));
83
TEST_P(StackTracePerfTest,MultiObj)84 TEST_P(StackTracePerfTest, MultiObj) {
85 size_t parm = GetParam();
86 MultiObjTest(parm);
87 }
88
89 } // namespace debug
90 } // namespace base
91