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