1 // Copyright 2019 The Chromium Authors. All rights reserved.
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 "base/profiler/stack_sampling_profiler_test_util.h"
6
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/compiler_specific.h"
12 #include "base/location.h"
13 #include "base/path_service.h"
14 #include "base/profiler/profiler_buildflags.h"
15 #include "base/profiler/stack_buffer.h"
16 #include "base/profiler/stack_sampling_profiler.h"
17 #include "base/profiler/unwinder.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/test/bind.h"
20 #include "build/build_config.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22
23 #if defined(OS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)
24 #include "base/android/apk_assets.h"
25 #include "base/files/memory_mapped_file.h"
26 #include "base/profiler/chrome_unwinder_android.h"
27 #include "base/profiler/native_unwinder_android.h"
28 #endif
29
30 #if defined(OS_WIN)
31 // Windows doesn't provide an alloca function like Linux does.
32 // Fortunately, it provides _alloca, which functions identically.
33 #include <malloc.h>
34 #define alloca _alloca
35 #elif !defined(OS_BSD)
36 #include <alloca.h>
37 #endif
38
39 extern "C" {
40 // The address of |__executable_start| gives the start address of the
41 // executable or shared library. This value is used to find the offset address
42 // of the instruction in binary from PC.
43 extern char __executable_start;
44 }
45
46 namespace base {
47
48 namespace {
49
50 // A profile builder for test use that expects to receive exactly one sample.
51 class TestProfileBuilder : public ProfileBuilder {
52 public:
53 // The callback is passed the last sample recorded.
54 using CompletedCallback = OnceCallback<void(std::vector<Frame>)>;
55
TestProfileBuilder(ModuleCache * module_cache,CompletedCallback callback)56 TestProfileBuilder(ModuleCache* module_cache, CompletedCallback callback)
57 : module_cache_(module_cache), callback_(std::move(callback)) {}
58
59 ~TestProfileBuilder() override = default;
60
61 TestProfileBuilder(const TestProfileBuilder&) = delete;
62 TestProfileBuilder& operator=(const TestProfileBuilder&) = delete;
63
64 // ProfileBuilder:
GetModuleCache()65 ModuleCache* GetModuleCache() override { return module_cache_; }
RecordMetadata(const MetadataRecorder::MetadataProvider & metadata_provider)66 void RecordMetadata(
67 const MetadataRecorder::MetadataProvider& metadata_provider) override {}
68
OnSampleCompleted(std::vector<Frame> sample,TimeTicks sample_timestamp)69 void OnSampleCompleted(std::vector<Frame> sample,
70 TimeTicks sample_timestamp) override {
71 EXPECT_TRUE(sample_.empty());
72 sample_ = std::move(sample);
73 }
74
OnProfileCompleted(TimeDelta profile_duration,TimeDelta sampling_period)75 void OnProfileCompleted(TimeDelta profile_duration,
76 TimeDelta sampling_period) override {
77 EXPECT_FALSE(sample_.empty());
78 std::move(callback_).Run(std::move(sample_));
79 }
80
81 private:
82 ModuleCache* const module_cache_;
83 CompletedCallback callback_;
84 std::vector<Frame> sample_;
85 };
86
87 // The function to be executed by the code in the other library.
OtherLibraryCallback(void * arg)88 void OtherLibraryCallback(void* arg) {
89 OnceClosure* wait_for_sample = static_cast<OnceClosure*>(arg);
90
91 std::move(*wait_for_sample).Run();
92
93 // Prevent tail call.
94 volatile int i = 0;
95 ALLOW_UNUSED_LOCAL(i);
96 }
97
98 #if defined(OS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)
CreateNativeUnwinderAndroidForTesting(uintptr_t exclude_module_with_base_address)99 std::unique_ptr<NativeUnwinderAndroid> CreateNativeUnwinderAndroidForTesting(
100 uintptr_t exclude_module_with_base_address) {
101 class NativeUnwinderAndroidForTesting : public NativeUnwinderAndroid {
102 public:
103 explicit NativeUnwinderAndroidForTesting(
104 std::unique_ptr<unwindstack::Maps> memory_regions_map,
105 std::unique_ptr<unwindstack::Memory> process_memory,
106 uintptr_t exclude_module_with_base_address)
107 : NativeUnwinderAndroid(memory_regions_map.get(),
108 process_memory.get(),
109 exclude_module_with_base_address),
110 memory_regions_map_(std::move(memory_regions_map)),
111 process_memory_(std::move(process_memory)) {}
112 ~NativeUnwinderAndroidForTesting() override = default;
113
114 private:
115 std::unique_ptr<unwindstack::Maps> memory_regions_map_;
116 std::unique_ptr<unwindstack::Memory> process_memory_;
117 };
118 auto maps = NativeUnwinderAndroid::CreateMaps();
119 auto memory = NativeUnwinderAndroid::CreateProcessMemory();
120 return std::make_unique<NativeUnwinderAndroidForTesting>(
121 std::move(maps), std::move(memory), exclude_module_with_base_address);
122 }
123
CreateChromeUnwinderAndroidForTesting(uintptr_t chrome_module_base_address)124 std::unique_ptr<Unwinder> CreateChromeUnwinderAndroidForTesting(
125 uintptr_t chrome_module_base_address) {
126 static constexpr char kCfiFileName[] = "assets/unwind_cfi_32";
127 class ChromeUnwinderAndroidForTesting : public ChromeUnwinderAndroid {
128 public:
129 ChromeUnwinderAndroidForTesting(std::unique_ptr<MemoryMappedFile> cfi_file,
130 std::unique_ptr<ArmCFITable> cfi_table,
131 uintptr_t chrome_module_base_address)
132 : ChromeUnwinderAndroid(cfi_table.get(), chrome_module_base_address),
133 cfi_file_(std::move(cfi_file)),
134 cfi_table_(std::move(cfi_table)) {}
135 ~ChromeUnwinderAndroidForTesting() override = default;
136
137 private:
138 std::unique_ptr<MemoryMappedFile> cfi_file_;
139 std::unique_ptr<ArmCFITable> cfi_table_;
140 };
141
142 MemoryMappedFile::Region cfi_region;
143 int fd = base::android::OpenApkAsset(kCfiFileName, &cfi_region);
144 if (fd < 0)
145 return nullptr;
146 auto cfi_file = std::make_unique<MemoryMappedFile>();
147 if (!cfi_file->Initialize(base::File(fd), cfi_region))
148 return nullptr;
149 std::unique_ptr<ArmCFITable> cfi_table =
150 ArmCFITable::Parse({cfi_file->data(), cfi_file->length()});
151 if (!cfi_table)
152 return nullptr;
153
154 return std::make_unique<ChromeUnwinderAndroidForTesting>(
155 std::move(cfi_file), std::move(cfi_table), chrome_module_base_address);
156 }
157 #endif // #if defined(OS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)
158
159 } // namespace
160
TargetThread(OnceClosure to_run)161 TargetThread::TargetThread(OnceClosure to_run) : to_run_(std::move(to_run)) {}
162
163 TargetThread::~TargetThread() = default;
164
ThreadMain()165 void TargetThread::ThreadMain() {
166 thread_token_ = GetSamplingProfilerCurrentThreadToken();
167 std::move(to_run_).Run();
168 }
169
UnwindScenario(const SetupFunction & setup_function)170 UnwindScenario::UnwindScenario(const SetupFunction& setup_function)
171 : setup_function_(setup_function) {}
172
173 UnwindScenario::~UnwindScenario() = default;
174
GetWaitForSampleAddressRange() const175 FunctionAddressRange UnwindScenario::GetWaitForSampleAddressRange() const {
176 return WaitForSample(nullptr);
177 }
178
GetSetupFunctionAddressRange() const179 FunctionAddressRange UnwindScenario::GetSetupFunctionAddressRange() const {
180 return setup_function_.Run(OnceClosure());
181 }
182
GetOuterFunctionAddressRange() const183 FunctionAddressRange UnwindScenario::GetOuterFunctionAddressRange() const {
184 return InvokeSetupFunction(SetupFunction(), nullptr);
185 }
186
Execute(SampleEvents * events)187 void UnwindScenario::Execute(SampleEvents* events) {
188 InvokeSetupFunction(setup_function_, events);
189 }
190
191 // static
192 // Disable inlining for this function so that it gets its own stack frame.
193 NOINLINE FunctionAddressRange
InvokeSetupFunction(const SetupFunction & setup_function,SampleEvents * events)194 UnwindScenario::InvokeSetupFunction(const SetupFunction& setup_function,
195 SampleEvents* events) {
196 const void* start_program_counter = GetProgramCounter();
197
198 if (!setup_function.is_null()) {
199 const auto wait_for_sample_closure =
200 BindLambdaForTesting([&]() { UnwindScenario::WaitForSample(events); });
201 setup_function.Run(wait_for_sample_closure);
202 }
203
204 // Volatile to prevent a tail call to GetProgramCounter().
205 const void* volatile end_program_counter = GetProgramCounter();
206 return {start_program_counter, end_program_counter};
207 }
208
209 // static
210 // Disable inlining for this function so that it gets its own stack frame.
211 NOINLINE FunctionAddressRange
WaitForSample(SampleEvents * events)212 UnwindScenario::WaitForSample(SampleEvents* events) {
213 const void* start_program_counter = GetProgramCounter();
214
215 if (events) {
216 events->ready_for_sample.Signal();
217 events->sample_finished.Wait();
218 }
219
220 // Volatile to prevent a tail call to GetProgramCounter().
221 const void* volatile end_program_counter = GetProgramCounter();
222 return {start_program_counter, end_program_counter};
223 }
224
225 // Disable inlining for this function so that it gets its own stack frame.
226 NOINLINE FunctionAddressRange
CallWithPlainFunction(OnceClosure wait_for_sample)227 CallWithPlainFunction(OnceClosure wait_for_sample) {
228 const void* start_program_counter = GetProgramCounter();
229
230 if (!wait_for_sample.is_null())
231 std::move(wait_for_sample).Run();
232
233 // Volatile to prevent a tail call to GetProgramCounter().
234 const void* volatile end_program_counter = GetProgramCounter();
235 return {start_program_counter, end_program_counter};
236 }
237
238 // Disable inlining for this function so that it gets its own stack frame.
CallWithAlloca(OnceClosure wait_for_sample)239 NOINLINE FunctionAddressRange CallWithAlloca(OnceClosure wait_for_sample) {
240 const void* start_program_counter = GetProgramCounter();
241
242 // Volatile to force a dynamic stack allocation.
243 const volatile size_t alloca_size = 100;
244 // Use the memory via volatile writes to prevent the allocation from being
245 // optimized out.
246 volatile char* const allocation =
247 const_cast<volatile char*>(static_cast<char*>(alloca(alloca_size)));
248 for (volatile char* p = allocation; p < allocation + alloca_size; ++p)
249 *p = '\0';
250
251 if (!wait_for_sample.is_null())
252 std::move(wait_for_sample).Run();
253
254 // Volatile to prevent a tail call to GetProgramCounter().
255 const void* volatile end_program_counter = GetProgramCounter();
256 return {start_program_counter, end_program_counter};
257 }
258
259 // Disable inlining for this function so that it gets its own stack frame.
260 NOINLINE FunctionAddressRange
CallThroughOtherLibrary(NativeLibrary library,OnceClosure wait_for_sample)261 CallThroughOtherLibrary(NativeLibrary library, OnceClosure wait_for_sample) {
262 const void* start_program_counter = GetProgramCounter();
263
264 if (!wait_for_sample.is_null()) {
265 // A function whose arguments are a function accepting void*, and a void*.
266 using InvokeCallbackFunction = void (*)(void (*)(void*), void*);
267 EXPECT_TRUE(library);
268 InvokeCallbackFunction function = reinterpret_cast<InvokeCallbackFunction>(
269 GetFunctionPointerFromNativeLibrary(library, "InvokeCallbackFunction"));
270 EXPECT_TRUE(function);
271 (*function)(&OtherLibraryCallback, &wait_for_sample);
272 }
273
274 // Volatile to prevent a tail call to GetProgramCounter().
275 const void* volatile end_program_counter = GetProgramCounter();
276 return {start_program_counter, end_program_counter};
277 }
278
WithTargetThread(UnwindScenario * scenario,ProfileCallback profile_callback)279 void WithTargetThread(UnwindScenario* scenario,
280 ProfileCallback profile_callback) {
281 UnwindScenario::SampleEvents events;
282 TargetThread target_thread(
283 BindLambdaForTesting([&]() { scenario->Execute(&events); }));
284
285 PlatformThreadHandle target_thread_handle;
286 EXPECT_TRUE(PlatformThread::Create(0, &target_thread, &target_thread_handle));
287
288 events.ready_for_sample.Wait();
289
290 std::move(profile_callback).Run(target_thread.thread_token());
291
292 events.sample_finished.Signal();
293
294 PlatformThread::Join(target_thread_handle);
295 }
296
SampleScenario(UnwindScenario * scenario,ModuleCache * module_cache,UnwinderFactory aux_unwinder_factory)297 std::vector<Frame> SampleScenario(UnwindScenario* scenario,
298 ModuleCache* module_cache,
299 UnwinderFactory aux_unwinder_factory) {
300 StackSamplingProfiler::SamplingParams params;
301 params.sampling_interval = TimeDelta::FromMilliseconds(0);
302 params.samples_per_profile = 1;
303
304 std::vector<Frame> sample;
305 WithTargetThread(
306 scenario,
307 BindLambdaForTesting(
308 [&](SamplingProfilerThreadToken target_thread_token) {
309 WaitableEvent sampling_thread_completed(
310 WaitableEvent::ResetPolicy::MANUAL,
311 WaitableEvent::InitialState::NOT_SIGNALED);
312 StackSamplingProfiler profiler(
313 target_thread_token, params,
314 std::make_unique<TestProfileBuilder>(
315 module_cache,
316 BindLambdaForTesting([&sample, &sampling_thread_completed](
317 std::vector<Frame> result_sample) {
318 sample = std::move(result_sample);
319 sampling_thread_completed.Signal();
320 })),
321 CreateCoreUnwindersFactoryForTesting(module_cache));
322 if (aux_unwinder_factory)
323 profiler.AddAuxUnwinder(std::move(aux_unwinder_factory).Run());
324 profiler.Start();
325 sampling_thread_completed.Wait();
326 }));
327
328 return sample;
329 }
330
FormatSampleForDiagnosticOutput(const std::vector<Frame> & sample)331 std::string FormatSampleForDiagnosticOutput(const std::vector<Frame>& sample) {
332 std::string output;
333 for (const auto& frame : sample) {
334 output += StringPrintf(
335 "0x%p %s\n", reinterpret_cast<const void*>(frame.instruction_pointer),
336 frame.module ? frame.module->GetDebugBasename().AsUTF8Unsafe().c_str()
337 : "null module");
338 }
339 return output;
340 }
341
ExpectStackContains(const std::vector<Frame> & stack,const std::vector<FunctionAddressRange> & functions)342 void ExpectStackContains(const std::vector<Frame>& stack,
343 const std::vector<FunctionAddressRange>& functions) {
344 auto frame_it = stack.begin();
345 auto function_it = functions.begin();
346 for (; frame_it != stack.end() && function_it != functions.end();
347 ++frame_it) {
348 if (frame_it->instruction_pointer >=
349 reinterpret_cast<uintptr_t>(function_it->start) &&
350 frame_it->instruction_pointer <=
351 reinterpret_cast<uintptr_t>(function_it->end)) {
352 ++function_it;
353 }
354 }
355
356 EXPECT_EQ(function_it, functions.end())
357 << "Function in position " << function_it - functions.begin() << " at "
358 << function_it->start << " was not found in stack "
359 << "(or did not appear in the expected order):\n"
360 << FormatSampleForDiagnosticOutput(stack);
361 }
362
ExpectStackDoesNotContain(const std::vector<Frame> & stack,const std::vector<FunctionAddressRange> & functions)363 void ExpectStackDoesNotContain(
364 const std::vector<Frame>& stack,
365 const std::vector<FunctionAddressRange>& functions) {
366 struct FunctionAddressRangeCompare {
367 bool operator()(const FunctionAddressRange& a,
368 const FunctionAddressRange& b) const {
369 return std::make_pair(a.start, a.end) < std::make_pair(b.start, b.end);
370 }
371 };
372
373 std::set<FunctionAddressRange, FunctionAddressRangeCompare> seen_functions;
374 for (const auto& frame : stack) {
375 for (const auto& function : functions) {
376 if (frame.instruction_pointer >=
377 reinterpret_cast<uintptr_t>(function.start) &&
378 frame.instruction_pointer <=
379 reinterpret_cast<uintptr_t>(function.end)) {
380 seen_functions.insert(function);
381 }
382 }
383 }
384
385 for (const auto& function : seen_functions) {
386 ADD_FAILURE() << "Function at " << function.start
387 << " was unexpectedly found in stack:\n"
388 << FormatSampleForDiagnosticOutput(stack);
389 }
390 }
391
LoadOtherLibrary()392 NativeLibrary LoadOtherLibrary() {
393 // The lambda gymnastics works around the fact that we can't use ASSERT_*
394 // macros in a function returning non-null.
395 const auto load = [](NativeLibrary* library) {
396 FilePath other_library_path;
397 ASSERT_TRUE(PathService::Get(DIR_MODULE, &other_library_path));
398 other_library_path = other_library_path.AppendASCII(
399 GetLoadableModuleName("base_profiler_test_support_library"));
400 NativeLibraryLoadError load_error;
401 *library = LoadNativeLibrary(other_library_path, &load_error);
402 ASSERT_TRUE(*library) << "error loading " << other_library_path.value()
403 << ": " << load_error.ToString();
404 };
405
406 NativeLibrary library = nullptr;
407 load(&library);
408 return library;
409 }
410
GetAddressInOtherLibrary(NativeLibrary library)411 uintptr_t GetAddressInOtherLibrary(NativeLibrary library) {
412 EXPECT_TRUE(library);
413 uintptr_t address = reinterpret_cast<uintptr_t>(
414 GetFunctionPointerFromNativeLibrary(library, "InvokeCallbackFunction"));
415 EXPECT_NE(address, 0u);
416 return address;
417 }
418
CreateCoreUnwindersFactoryForTesting(ModuleCache * module_cache)419 StackSamplingProfiler::UnwindersFactory CreateCoreUnwindersFactoryForTesting(
420 ModuleCache* module_cache) {
421 #if defined(OS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)
422 std::vector<std::unique_ptr<Unwinder>> unwinders;
423 unwinders.push_back(CreateNativeUnwinderAndroidForTesting(
424 reinterpret_cast<uintptr_t>(&__executable_start)));
425 unwinders.push_back(CreateChromeUnwinderAndroidForTesting(
426 reinterpret_cast<uintptr_t>(&__executable_start)));
427 return BindOnce(
428 [](std::vector<std::unique_ptr<Unwinder>> unwinders) {
429 return unwinders;
430 },
431 std::move(unwinders));
432 #else
433 return StackSamplingProfiler::UnwindersFactory();
434 #endif
435 }
436
437 } // namespace base
438