1*6777b538SAndroid Build Coastguard Worker // Copyright 2023 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #include <thread>
6*6777b538SAndroid Build Coastguard Worker #include <vector>
7*6777b538SAndroid Build Coastguard Worker
8*6777b538SAndroid Build Coastguard Worker #include "base/allocator/dispatcher/notification_data.h"
9*6777b538SAndroid Build Coastguard Worker #include "base/allocator/dispatcher/subsystem.h"
10*6777b538SAndroid Build Coastguard Worker #include "base/debug/allocation_trace.h"
11*6777b538SAndroid Build Coastguard Worker #include "base/strings/stringprintf.h"
12*6777b538SAndroid Build Coastguard Worker #include "base/timer/lap_timer.h"
13*6777b538SAndroid Build Coastguard Worker #include "testing/gtest/include/gtest/gtest.h"
14*6777b538SAndroid Build Coastguard Worker #include "testing/perf/perf_result_reporter.h"
15*6777b538SAndroid Build Coastguard Worker
16*6777b538SAndroid Build Coastguard Worker namespace base {
17*6777b538SAndroid Build Coastguard Worker namespace debug {
18*6777b538SAndroid Build Coastguard Worker namespace {
19*6777b538SAndroid Build Coastguard Worker // Change kTimeLimit to something higher if you need more time to capture a
20*6777b538SAndroid Build Coastguard Worker // trace.
21*6777b538SAndroid Build Coastguard Worker constexpr base::TimeDelta kTimeLimit = base::Seconds(3);
22*6777b538SAndroid Build Coastguard Worker constexpr int kWarmupRuns = 100;
23*6777b538SAndroid Build Coastguard Worker constexpr int kTimeCheckInterval = 1000;
24*6777b538SAndroid Build Coastguard Worker constexpr char kMetricStackTraceDuration[] = ".duration_per_run";
25*6777b538SAndroid Build Coastguard Worker constexpr char kMetricStackTraceThroughput[] = ".throughput";
26*6777b538SAndroid Build Coastguard Worker
27*6777b538SAndroid Build Coastguard Worker enum class HandlerFunctionSelector { OnAllocation, OnFree };
28*6777b538SAndroid Build Coastguard Worker
29*6777b538SAndroid Build Coastguard Worker // An executor to perform the actual notification of the recorder. The correct
30*6777b538SAndroid Build Coastguard Worker // handler function is selected using template specialization based on the
31*6777b538SAndroid Build Coastguard Worker // HandlerFunctionSelector.
32*6777b538SAndroid Build Coastguard Worker template <HandlerFunctionSelector HandlerFunction>
33*6777b538SAndroid Build Coastguard Worker struct HandlerFunctionExecutor {
34*6777b538SAndroid Build Coastguard Worker void operator()(base::debug::tracer::AllocationTraceRecorder& recorder) const;
35*6777b538SAndroid Build Coastguard Worker };
36*6777b538SAndroid Build Coastguard Worker
37*6777b538SAndroid Build Coastguard Worker template <>
38*6777b538SAndroid Build Coastguard Worker struct HandlerFunctionExecutor<HandlerFunctionSelector::OnAllocation> {
operator ()base::debug::__anon6276e8950111::HandlerFunctionExecutor39*6777b538SAndroid Build Coastguard Worker void operator()(
40*6777b538SAndroid Build Coastguard Worker base::debug::tracer::AllocationTraceRecorder& recorder) const {
41*6777b538SAndroid Build Coastguard Worker // Since the recorder just stores the value, we can use any value for
42*6777b538SAndroid Build Coastguard Worker // address and size that we want.
43*6777b538SAndroid Build Coastguard Worker recorder.OnAllocation(
44*6777b538SAndroid Build Coastguard Worker base::allocator::dispatcher::AllocationNotificationData(
45*6777b538SAndroid Build Coastguard Worker &recorder, sizeof(recorder), nullptr,
46*6777b538SAndroid Build Coastguard Worker base::allocator::dispatcher::AllocationSubsystem::
47*6777b538SAndroid Build Coastguard Worker kPartitionAllocator));
48*6777b538SAndroid Build Coastguard Worker }
49*6777b538SAndroid Build Coastguard Worker };
50*6777b538SAndroid Build Coastguard Worker
51*6777b538SAndroid Build Coastguard Worker template <>
52*6777b538SAndroid Build Coastguard Worker struct HandlerFunctionExecutor<HandlerFunctionSelector::OnFree> {
operator ()base::debug::__anon6276e8950111::HandlerFunctionExecutor53*6777b538SAndroid Build Coastguard Worker void operator()(
54*6777b538SAndroid Build Coastguard Worker base::debug::tracer::AllocationTraceRecorder& recorder) const {
55*6777b538SAndroid Build Coastguard Worker recorder.OnFree(base::allocator::dispatcher::FreeNotificationData(
56*6777b538SAndroid Build Coastguard Worker &recorder,
57*6777b538SAndroid Build Coastguard Worker base::allocator::dispatcher::AllocationSubsystem::kPartitionAllocator));
58*6777b538SAndroid Build Coastguard Worker }
59*6777b538SAndroid Build Coastguard Worker };
60*6777b538SAndroid Build Coastguard Worker } // namespace
61*6777b538SAndroid Build Coastguard Worker
62*6777b538SAndroid Build Coastguard Worker class AllocationTraceRecorderPerfTest
63*6777b538SAndroid Build Coastguard Worker : public testing::TestWithParam<
64*6777b538SAndroid Build Coastguard Worker std::tuple<HandlerFunctionSelector, size_t>> {
65*6777b538SAndroid Build Coastguard Worker protected:
66*6777b538SAndroid Build Coastguard Worker // The result data of a single thread. From the results of all the single
67*6777b538SAndroid Build Coastguard Worker // threads the final results will be calculated.
68*6777b538SAndroid Build Coastguard Worker struct ResultData {
69*6777b538SAndroid Build Coastguard Worker TimeDelta time_per_lap;
70*6777b538SAndroid Build Coastguard Worker float laps_per_second = 0.0;
71*6777b538SAndroid Build Coastguard Worker int number_of_laps = 0;
72*6777b538SAndroid Build Coastguard Worker };
73*6777b538SAndroid Build Coastguard Worker
74*6777b538SAndroid Build Coastguard Worker // The data of a single test thread.
75*6777b538SAndroid Build Coastguard Worker struct ThreadRunnerData {
76*6777b538SAndroid Build Coastguard Worker std::thread thread;
77*6777b538SAndroid Build Coastguard Worker ResultData result_data;
78*6777b538SAndroid Build Coastguard Worker };
79*6777b538SAndroid Build Coastguard Worker
80*6777b538SAndroid Build Coastguard Worker // Create and setup the result reporter.
81*6777b538SAndroid Build Coastguard Worker const char* GetHandlerDescriptor(HandlerFunctionSelector handler_function);
82*6777b538SAndroid Build Coastguard Worker perf_test::PerfResultReporter SetUpReporter(
83*6777b538SAndroid Build Coastguard Worker HandlerFunctionSelector handler_function,
84*6777b538SAndroid Build Coastguard Worker size_t number_of_allocating_threads);
85*6777b538SAndroid Build Coastguard Worker
86*6777b538SAndroid Build Coastguard Worker // Select the correct test function which shall be used for the current test.
87*6777b538SAndroid Build Coastguard Worker using TestFunction =
88*6777b538SAndroid Build Coastguard Worker void (*)(base::debug::tracer::AllocationTraceRecorder& recorder,
89*6777b538SAndroid Build Coastguard Worker ResultData& result_data);
90*6777b538SAndroid Build Coastguard Worker
91*6777b538SAndroid Build Coastguard Worker static TestFunction GetTestFunction(HandlerFunctionSelector handler_function);
92*6777b538SAndroid Build Coastguard Worker template <HandlerFunctionSelector HandlerFunction>
93*6777b538SAndroid Build Coastguard Worker static void TestFunctionImplementation(
94*6777b538SAndroid Build Coastguard Worker base::debug::tracer::AllocationTraceRecorder& recorder,
95*6777b538SAndroid Build Coastguard Worker ResultData& result_data);
96*6777b538SAndroid Build Coastguard Worker
97*6777b538SAndroid Build Coastguard Worker // The test management function. Using the the above auxiliary functions it is
98*6777b538SAndroid Build Coastguard Worker // responsible to setup the result reporter, select the correct test function,
99*6777b538SAndroid Build Coastguard Worker // spawn the specified number of worker threads and post process the results.
100*6777b538SAndroid Build Coastguard Worker void PerformTest(HandlerFunctionSelector handler_function,
101*6777b538SAndroid Build Coastguard Worker size_t number_of_allocating_threads);
102*6777b538SAndroid Build Coastguard Worker };
103*6777b538SAndroid Build Coastguard Worker
GetHandlerDescriptor(HandlerFunctionSelector handler_function)104*6777b538SAndroid Build Coastguard Worker const char* AllocationTraceRecorderPerfTest::GetHandlerDescriptor(
105*6777b538SAndroid Build Coastguard Worker HandlerFunctionSelector handler_function) {
106*6777b538SAndroid Build Coastguard Worker switch (handler_function) {
107*6777b538SAndroid Build Coastguard Worker case HandlerFunctionSelector::OnAllocation:
108*6777b538SAndroid Build Coastguard Worker return "OnAllocation";
109*6777b538SAndroid Build Coastguard Worker case HandlerFunctionSelector::OnFree:
110*6777b538SAndroid Build Coastguard Worker return "OnFree";
111*6777b538SAndroid Build Coastguard Worker }
112*6777b538SAndroid Build Coastguard Worker }
113*6777b538SAndroid Build Coastguard Worker
SetUpReporter(HandlerFunctionSelector handler_function,size_t number_of_allocating_threads)114*6777b538SAndroid Build Coastguard Worker perf_test::PerfResultReporter AllocationTraceRecorderPerfTest::SetUpReporter(
115*6777b538SAndroid Build Coastguard Worker HandlerFunctionSelector handler_function,
116*6777b538SAndroid Build Coastguard Worker size_t number_of_allocating_threads) {
117*6777b538SAndroid Build Coastguard Worker const std::string story_name = base::StringPrintf(
118*6777b538SAndroid Build Coastguard Worker "(%s;%zu-threads)", GetHandlerDescriptor(handler_function),
119*6777b538SAndroid Build Coastguard Worker number_of_allocating_threads);
120*6777b538SAndroid Build Coastguard Worker
121*6777b538SAndroid Build Coastguard Worker perf_test::PerfResultReporter reporter("AllocationRecorderPerf", story_name);
122*6777b538SAndroid Build Coastguard Worker reporter.RegisterImportantMetric(kMetricStackTraceDuration, "ns");
123*6777b538SAndroid Build Coastguard Worker reporter.RegisterImportantMetric(kMetricStackTraceThroughput, "runs/s");
124*6777b538SAndroid Build Coastguard Worker return reporter;
125*6777b538SAndroid Build Coastguard Worker }
126*6777b538SAndroid Build Coastguard Worker
127*6777b538SAndroid Build Coastguard Worker AllocationTraceRecorderPerfTest::TestFunction
GetTestFunction(HandlerFunctionSelector handler_function)128*6777b538SAndroid Build Coastguard Worker AllocationTraceRecorderPerfTest::GetTestFunction(
129*6777b538SAndroid Build Coastguard Worker HandlerFunctionSelector handler_function) {
130*6777b538SAndroid Build Coastguard Worker switch (handler_function) {
131*6777b538SAndroid Build Coastguard Worker case HandlerFunctionSelector::OnAllocation:
132*6777b538SAndroid Build Coastguard Worker return TestFunctionImplementation<HandlerFunctionSelector::OnAllocation>;
133*6777b538SAndroid Build Coastguard Worker case HandlerFunctionSelector::OnFree:
134*6777b538SAndroid Build Coastguard Worker return TestFunctionImplementation<HandlerFunctionSelector::OnFree>;
135*6777b538SAndroid Build Coastguard Worker }
136*6777b538SAndroid Build Coastguard Worker }
137*6777b538SAndroid Build Coastguard Worker
PerformTest(HandlerFunctionSelector handler_function,size_t number_of_allocating_threads)138*6777b538SAndroid Build Coastguard Worker void AllocationTraceRecorderPerfTest::PerformTest(
139*6777b538SAndroid Build Coastguard Worker HandlerFunctionSelector handler_function,
140*6777b538SAndroid Build Coastguard Worker size_t number_of_allocating_threads) {
141*6777b538SAndroid Build Coastguard Worker perf_test::PerfResultReporter reporter =
142*6777b538SAndroid Build Coastguard Worker SetUpReporter(handler_function, number_of_allocating_threads);
143*6777b538SAndroid Build Coastguard Worker
144*6777b538SAndroid Build Coastguard Worker TestFunction test_function = GetTestFunction(handler_function);
145*6777b538SAndroid Build Coastguard Worker
146*6777b538SAndroid Build Coastguard Worker base::debug::tracer::AllocationTraceRecorder the_recorder;
147*6777b538SAndroid Build Coastguard Worker
148*6777b538SAndroid Build Coastguard Worker std::vector<ThreadRunnerData> notifying_threads;
149*6777b538SAndroid Build Coastguard Worker notifying_threads.reserve(number_of_allocating_threads);
150*6777b538SAndroid Build Coastguard Worker
151*6777b538SAndroid Build Coastguard Worker // Setup the threads. After creation, each thread immediately starts running.
152*6777b538SAndroid Build Coastguard Worker // We expect the creation of the threads to be so quick that the delay from
153*6777b538SAndroid Build Coastguard Worker // first to last thread is negligible.
154*6777b538SAndroid Build Coastguard Worker for (size_t i = 0; i < number_of_allocating_threads; ++i) {
155*6777b538SAndroid Build Coastguard Worker auto& last_item = notifying_threads.emplace_back();
156*6777b538SAndroid Build Coastguard Worker
157*6777b538SAndroid Build Coastguard Worker last_item.thread = std::thread{test_function, std::ref(the_recorder),
158*6777b538SAndroid Build Coastguard Worker std::ref(last_item.result_data)};
159*6777b538SAndroid Build Coastguard Worker }
160*6777b538SAndroid Build Coastguard Worker
161*6777b538SAndroid Build Coastguard Worker TimeDelta average_time_per_lap;
162*6777b538SAndroid Build Coastguard Worker float average_laps_per_second = 0;
163*6777b538SAndroid Build Coastguard Worker
164*6777b538SAndroid Build Coastguard Worker // Wait for each thread to finish and collect its result data.
165*6777b538SAndroid Build Coastguard Worker for (auto& item : notifying_threads) {
166*6777b538SAndroid Build Coastguard Worker item.thread.join();
167*6777b538SAndroid Build Coastguard Worker // When finishing, each threads writes its results into result_data. So,
168*6777b538SAndroid Build Coastguard Worker // from here we gather its performance statistics.
169*6777b538SAndroid Build Coastguard Worker average_time_per_lap += item.result_data.time_per_lap;
170*6777b538SAndroid Build Coastguard Worker average_laps_per_second += item.result_data.laps_per_second;
171*6777b538SAndroid Build Coastguard Worker }
172*6777b538SAndroid Build Coastguard Worker
173*6777b538SAndroid Build Coastguard Worker average_time_per_lap /= number_of_allocating_threads;
174*6777b538SAndroid Build Coastguard Worker average_laps_per_second /= number_of_allocating_threads;
175*6777b538SAndroid Build Coastguard Worker
176*6777b538SAndroid Build Coastguard Worker reporter.AddResult(kMetricStackTraceDuration, average_time_per_lap);
177*6777b538SAndroid Build Coastguard Worker reporter.AddResult(kMetricStackTraceThroughput, average_laps_per_second);
178*6777b538SAndroid Build Coastguard Worker }
179*6777b538SAndroid Build Coastguard Worker
180*6777b538SAndroid Build Coastguard Worker template <HandlerFunctionSelector HandlerFunction>
TestFunctionImplementation(base::debug::tracer::AllocationTraceRecorder & recorder,ResultData & result_data)181*6777b538SAndroid Build Coastguard Worker void AllocationTraceRecorderPerfTest::TestFunctionImplementation(
182*6777b538SAndroid Build Coastguard Worker base::debug::tracer::AllocationTraceRecorder& recorder,
183*6777b538SAndroid Build Coastguard Worker ResultData& result_data) {
184*6777b538SAndroid Build Coastguard Worker LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval,
185*6777b538SAndroid Build Coastguard Worker LapTimer::TimerMethod::kUseTimeTicks);
186*6777b538SAndroid Build Coastguard Worker
187*6777b538SAndroid Build Coastguard Worker HandlerFunctionExecutor<HandlerFunction> handler_executor;
188*6777b538SAndroid Build Coastguard Worker
189*6777b538SAndroid Build Coastguard Worker timer.Start();
190*6777b538SAndroid Build Coastguard Worker do {
191*6777b538SAndroid Build Coastguard Worker handler_executor(recorder);
192*6777b538SAndroid Build Coastguard Worker
193*6777b538SAndroid Build Coastguard Worker timer.NextLap();
194*6777b538SAndroid Build Coastguard Worker } while (!timer.HasTimeLimitExpired());
195*6777b538SAndroid Build Coastguard Worker
196*6777b538SAndroid Build Coastguard Worker result_data.time_per_lap = timer.TimePerLap();
197*6777b538SAndroid Build Coastguard Worker result_data.laps_per_second = timer.LapsPerSecond();
198*6777b538SAndroid Build Coastguard Worker result_data.number_of_laps = timer.NumLaps();
199*6777b538SAndroid Build Coastguard Worker }
200*6777b538SAndroid Build Coastguard Worker
201*6777b538SAndroid Build Coastguard Worker INSTANTIATE_TEST_SUITE_P(
202*6777b538SAndroid Build Coastguard Worker ,
203*6777b538SAndroid Build Coastguard Worker AllocationTraceRecorderPerfTest,
204*6777b538SAndroid Build Coastguard Worker ::testing::Combine(::testing::Values(HandlerFunctionSelector::OnAllocation,
205*6777b538SAndroid Build Coastguard Worker HandlerFunctionSelector::OnFree),
206*6777b538SAndroid Build Coastguard Worker ::testing::Values(1, 5, 10, 20, 40, 80)));
207*6777b538SAndroid Build Coastguard Worker
TEST_P(AllocationTraceRecorderPerfTest,TestNotification)208*6777b538SAndroid Build Coastguard Worker TEST_P(AllocationTraceRecorderPerfTest, TestNotification) {
209*6777b538SAndroid Build Coastguard Worker const auto parameters = GetParam();
210*6777b538SAndroid Build Coastguard Worker const HandlerFunctionSelector handler_function = std::get<0>(parameters);
211*6777b538SAndroid Build Coastguard Worker const size_t number_of_threads = std::get<1>(parameters);
212*6777b538SAndroid Build Coastguard Worker PerformTest(handler_function, number_of_threads);
213*6777b538SAndroid Build Coastguard Worker }
214*6777b538SAndroid Build Coastguard Worker
215*6777b538SAndroid Build Coastguard Worker } // namespace debug
216*6777b538SAndroid Build Coastguard Worker } // namespace base
217