Commit 983f291e authored by Etienne Pierre-doray's avatar Etienne Pierre-doray Committed by Commit Bot

[Clank SSM]: Enable stack sampling profiler tests on android

Bug: 989102
Change-Id: If733b62f4e5b2b3044c20935ef443748b47399b5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2201658
Commit-Queue: Etienne Pierre-Doray <etiennep@chromium.org>
Reviewed-by: default avatarWez <wez@chromium.org>
Reviewed-by: default avatarMike Wittman <wittman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#775231}
parent 21869271
......@@ -91,6 +91,11 @@ dep_libevent =
# Determines whether message_pump_libevent should be used.
use_libevent = dep_libevent && !is_ios
# Whether or not cfi table should be enabled on arm.
# TODO(crbug.com/1090409): Replace can_unwind_with_cfi_table once sampling
# profiler is enabled on android.
enable_arm_cfi_table = is_android && !is_component_build && current_cpu == "arm"
if (is_android) {
import("//build/config/android/rules.gni")
}
......@@ -2186,6 +2191,7 @@ buildflag_header("debugging_buildflags") {
"CAN_UNWIND_WITH_FRAME_POINTERS=$can_unwind_with_frame_pointers",
"UNSAFE_DEVELOPER_BUILD=$is_unsafe_developer_build",
"CAN_UNWIND_WITH_CFI_TABLE=$can_unwind_with_cfi_table",
"ENABLE_ARM_CFI_TABLE=$enable_arm_cfi_table",
"ENABLE_GDBINIT_WARNING=$enable_gdbinit_warning",
"ENABLE_LLDBINIT_WARNING=$enable_lldbinit_warning",
]
......@@ -2505,6 +2511,9 @@ source_set("base_stack_sampling_profiler_test_util") {
"//base/test:test_support",
"//testing/gtest",
]
if (is_android) {
deps += [ ":native_unwinder_android" ]
}
}
bundle_data("base_unittests_bundle_data") {
......@@ -3010,7 +3019,7 @@ test("base_unittests") {
# generated from debug info in the binary. Removing "default_symbols" and
# adding symbols config removes the "strip_debug" config that strips the
# debug info, on base unittests apk.
if (can_unwind_with_cfi_table) {
if (can_unwind_with_cfi_table || enable_arm_cfi_table) {
configs -= [ "//build/config/compiler:default_symbols" ]
if (symbol_level == 2) {
configs += [ "//build/config/compiler:symbols" ]
......@@ -3018,6 +3027,8 @@ test("base_unittests") {
configs += [ "//build/config/compiler:minimal_symbols" ]
}
add_unwind_tables_in_apk = true
}
if (can_unwind_with_cfi_table) {
sources += [ "trace_event/cfi_backtrace_android_unittest.cc" ]
}
if (current_cpu == "arm") {
......
......@@ -13,6 +13,7 @@
#include "base/profiler/register_context.h"
#include "base/profiler/stack_buffer.h"
#include "base/profiler/stack_copier_signal.h"
#include "base/profiler/stack_sampler.h"
#include "base/profiler/stack_sampling_profiler_test_util.h"
#include "base/profiler/thread_delegate_posix.h"
#include "base/test/bind_test_util.h"
......
......@@ -18,6 +18,13 @@
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)
#include "base/android/apk_assets.h"
#include "base/files/memory_mapped_file.h"
#include "base/profiler/chrome_unwinder_android.h"
#include "base/profiler/native_unwinder_android.h"
#endif
#if defined(OS_WIN)
// Windows doesn't provide an alloca function like Linux does.
// Fortunately, it provides _alloca, which functions identically.
......@@ -27,6 +34,13 @@
#include <alloca.h>
#endif
extern "C" {
// The address of |__executable_start| gives the start address of the
// executable or shared library. This value is used to find the offset address
// of the instruction in binary from PC.
extern char __executable_start;
}
namespace base {
namespace {
......@@ -79,6 +93,69 @@ void OtherLibraryCallback(void* arg) {
ALLOW_UNUSED_LOCAL(i);
}
#if defined(OS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)
std::unique_ptr<NativeUnwinderAndroid> CreateNativeUnwinderAndroidForTesting(
uintptr_t exclude_module_with_base_address) {
class NativeUnwinderAndroidForTesting : public NativeUnwinderAndroid {
public:
explicit NativeUnwinderAndroidForTesting(
std::unique_ptr<unwindstack::Maps> memory_regions_map,
std::unique_ptr<unwindstack::Memory> process_memory,
uintptr_t exclude_module_with_base_address)
: NativeUnwinderAndroid(memory_regions_map.get(),
process_memory.get(),
exclude_module_with_base_address),
memory_regions_map_(std::move(memory_regions_map)),
process_memory_(std::move(process_memory)) {}
~NativeUnwinderAndroidForTesting() override = default;
private:
std::unique_ptr<unwindstack::Maps> memory_regions_map_;
std::unique_ptr<unwindstack::Memory> process_memory_;
};
auto maps = NativeUnwinderAndroid::CreateMaps();
auto memory = NativeUnwinderAndroid::CreateProcessMemory();
return std::make_unique<NativeUnwinderAndroidForTesting>(
std::move(maps), std::move(memory), exclude_module_with_base_address);
}
std::unique_ptr<Unwinder> CreateChromeUnwinderAndroidForTesting(
ModuleCache* module_cache) {
static constexpr char kCfiFileName[] = "assets/unwind_cfi_32";
class ChromeUnwinderAndroidForTesting : public ChromeUnwinderAndroid {
public:
ChromeUnwinderAndroidForTesting(std::unique_ptr<MemoryMappedFile> cfi_file,
std::unique_ptr<ArmCFITable> cfi_table,
const ModuleCache::Module* chrome_module)
: ChromeUnwinderAndroid(cfi_table.get(), chrome_module),
cfi_file_(std::move(cfi_file)),
cfi_table_(std::move(cfi_table)) {}
~ChromeUnwinderAndroidForTesting() override = default;
private:
std::unique_ptr<MemoryMappedFile> cfi_file_;
std::unique_ptr<ArmCFITable> cfi_table_;
};
MemoryMappedFile::Region cfi_region;
int fd = base::android::OpenApkAsset(kCfiFileName, &cfi_region);
if (fd < 0)
return nullptr;
auto cfi_file = std::make_unique<MemoryMappedFile>();
if (!cfi_file->Initialize(base::File(fd), cfi_region))
return nullptr;
std::unique_ptr<ArmCFITable> cfi_table =
ArmCFITable::Parse({cfi_file->data(), cfi_file->length()});
if (!cfi_table)
return nullptr;
return std::make_unique<ChromeUnwinderAndroidForTesting>(
std::move(cfi_file), std::move(cfi_table),
module_cache->GetModuleForAddress(
reinterpret_cast<uintptr_t>(&__executable_start)));
}
#endif // #if defined(OS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)
} // namespace
TargetThread::TargetThread(OnceClosure to_run) : to_run_(std::move(to_run)) {}
......@@ -240,7 +317,8 @@ std::vector<Frame> SampleScenario(UnwindScenario* scenario,
std::vector<Frame> result_sample) {
sample = std::move(result_sample);
sampling_thread_completed.Signal();
})));
})),
CreateCoreUnwindersForTesting(module_cache));
if (aux_unwinder_factory)
profiler.AddAuxUnwinder(std::move(aux_unwinder_factory).Run());
profiler.Start();
......@@ -338,4 +416,17 @@ uintptr_t GetAddressInOtherLibrary(NativeLibrary library) {
return address;
}
std::vector<std::unique_ptr<Unwinder>> CreateCoreUnwindersForTesting(
ModuleCache* module_cache) {
#if defined(OS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)
std::vector<std::unique_ptr<Unwinder>> unwinders;
unwinders.push_back(CreateNativeUnwinderAndroidForTesting(
reinterpret_cast<uintptr_t>(&__executable_start)));
unwinders.push_back(CreateChromeUnwinderAndroidForTesting(module_cache));
return unwinders;
#else
return {};
#endif
}
} // namespace base
......@@ -12,13 +12,13 @@
#include "base/native_library.h"
#include "base/profiler/frame.h"
#include "base/profiler/sampling_profiler_thread_token.h"
#include "base/profiler/stack_sampler.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
namespace base {
class Unwinder;
class ModuleCache;
// A thread to target for profiling that will run the supplied closure.
class TargetThread : public PlatformThread::Delegate {
......@@ -140,6 +140,12 @@ NativeLibrary LoadOtherLibrary();
uintptr_t GetAddressInOtherLibrary(NativeLibrary library);
// Creates a list of core unwinders required for StackSamplingProfilerTest.
// This is useful notably on Android, which requires ChromeUnwinderAndroid in
// addition to the native one.
std::vector<std::unique_ptr<Unwinder>> CreateCoreUnwindersForTesting(
ModuleCache* module_cache);
} // namespace base
#endif // BASE_PROFILER_STACK_SAMPLING_PROFILER_TEST_UTIL_H_
......@@ -47,7 +47,8 @@
// STACK_SAMPLING_PROFILER_SUPPORTED is used to conditionally enable the tests
// below for supported platforms (currently Win x64 and Mac x64).
#if defined(_WIN64) || (defined(OS_MACOSX) && !defined(OS_IOS))
#if defined(_WIN64) || (defined(OS_MACOSX) && !defined(OS_IOS)) || \
(defined(OS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE))
#define STACK_SAMPLING_PROFILER_SUPPORTED 1
#endif
......@@ -186,8 +187,8 @@ void SynchronousUnloadNativeLibrary(NativeLibrary library) {
::GetLastError() != ERROR_MOD_NOT_FOUND) {
PlatformThread::Sleep(TimeDelta::FromMilliseconds(1));
}
#elif defined(OS_MACOSX)
// Unloading a library on the Mac is synchronous.
#elif defined(OS_MACOSX) || defined(OS_ANDROID)
// Unloading a library on Mac and Android is synchronous.
#else
NOTIMPLEMENTED();
#endif
......@@ -213,7 +214,7 @@ struct TestProfilerInfo {
profile = std::move(result_profile);
completed.Signal();
})),
{},
CreateCoreUnwindersForTesting(module_cache),
delegate) {}
// The order here is important to ensure objects being referenced don't get
......@@ -347,7 +348,7 @@ void TestLibraryUnload(bool wait_until_unloaded, ModuleCache* module_cache) {
profile = std::move(result_profile);
sampling_thread_completed.Signal();
})),
{}, &test_delegate);
CreateCoreUnwindersForTesting(module_cache), &test_delegate);
profiler.Start();
......@@ -488,7 +489,9 @@ class TestAuxUnwinder : public Unwinder {
// Checks that the profiler handles stacks containing dynamically-allocated
// stack memory.
// macOS ASAN is not yet supported - crbug.com/718628.
#if !(defined(ADDRESS_SANITIZER) && defined(OS_MACOSX))
// Android is not supported since Chrome unwind tables don't support dynamic
// frames.
#if !(defined(ADDRESS_SANITIZER) && defined(OS_MACOSX)) && !defined(OS_ANDROID)
#define MAYBE_Alloca Alloca
#else
#define MAYBE_Alloca DISABLED_Alloca
......@@ -538,7 +541,8 @@ PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_UnloadingLibrary) {
// Checks that a stack that runs through a library that has been unloaded
// produces a stack, and doesn't crash.
// macOS ASAN is not yet supported - crbug.com/718628.
#if !(defined(ADDRESS_SANITIZER) && defined(OS_MACOSX))
// Android is not supported since modules are found before unwinding.
#if !(defined(ADDRESS_SANITIZER) && defined(OS_MACOSX)) && !defined(OS_ANDROID)
#define MAYBE_UnloadedLibrary UnloadedLibrary
#else
#define MAYBE_UnloadedLibrary DISABLED_UnloadedLibrary
......@@ -568,7 +572,8 @@ PROFILER_TEST_F(StackSamplingProfilerTest, StopWithoutStarting) {
[&profile, &sampling_completed](Profile result_profile) {
profile = std::move(result_profile);
sampling_completed.Signal();
})));
})),
CreateCoreUnwindersForTesting(module_cache()));
profiler.Stop(); // Constructed but never started.
EXPECT_FALSE(sampling_completed.IsSignaled());
......@@ -787,8 +792,9 @@ PROFILER_TEST_F(StackSamplingProfilerTest, DestroyProfilerWhileProfiling) {
BindLambdaForTesting([&profile](Profile result_profile) {
profile = std::move(result_profile);
}));
profiler.reset(new StackSamplingProfiler(target_thread_token, params,
std::move(profile_builder)));
profiler.reset(new StackSamplingProfiler(
target_thread_token, params, std::move(profile_builder),
CreateCoreUnwindersForTesting(module_cache())));
profiler->Start();
profiler.reset();
......@@ -1139,7 +1145,8 @@ PROFILER_TEST_F(StackSamplingProfilerTest, MultipleSampledThreads) {
[&profile1, &sampling_thread_completed1](Profile result_profile) {
profile1 = std::move(result_profile);
sampling_thread_completed1.Signal();
})));
})),
CreateCoreUnwindersForTesting(module_cache()));
WaitableEvent sampling_thread_completed2(
WaitableEvent::ResetPolicy::MANUAL,
......@@ -1152,7 +1159,8 @@ PROFILER_TEST_F(StackSamplingProfilerTest, MultipleSampledThreads) {
[&profile2, &sampling_thread_completed2](Profile result_profile) {
profile2 = std::move(result_profile);
sampling_thread_completed2.Signal();
})));
})),
CreateCoreUnwindersForTesting(module_cache()));
// Finally the real work.
profiler1.Start();
......@@ -1187,8 +1195,8 @@ class ProfilerThread : public SimpleThread {
BindLambdaForTesting([this](Profile result_profile) {
profile_ = std::move(result_profile);
completed_.Signal();
}))) {}
})),
CreateCoreUnwindersForTesting(module_cache)) {}
void Run() override {
run_.Wait();
profiler_.Start();
......@@ -1273,7 +1281,8 @@ PROFILER_TEST_F(StackSamplingProfilerTest, AddAuxUnwinder_BeforeStart) {
Profile result_profile) {
profile = std::move(result_profile);
sampling_thread_completed.Signal();
})));
})),
CreateCoreUnwindersForTesting(module_cache()));
profiler.AddAuxUnwinder(
std::make_unique<TestAuxUnwinder>(Frame(23, nullptr)));
profiler.Start();
......@@ -1313,7 +1322,8 @@ PROFILER_TEST_F(StackSamplingProfilerTest, AddAuxUnwinder_AfterStart) {
Profile result_profile) {
profile = std::move(result_profile);
sampling_thread_completed.Signal();
})));
})),
CreateCoreUnwindersForTesting(module_cache()));
profiler.Start();
profiler.AddAuxUnwinder(
std::make_unique<TestAuxUnwinder>(Frame(23, nullptr)));
......@@ -1353,7 +1363,8 @@ PROFILER_TEST_F(StackSamplingProfilerTest, AddAuxUnwinder_AfterStop) {
Profile result_profile) {
profile = std::move(result_profile);
sampling_thread_completed.Signal();
})));
})),
CreateCoreUnwindersForTesting(module_cache()));
profiler.Start();
profiler.Stop();
profiler.AddAuxUnwinder(
......@@ -1427,7 +1438,8 @@ PROFILER_TEST_F(StackSamplingProfilerTest,
BindLambdaForTesting([&profile](Profile result_profile) {
profile = std::move(result_profile);
})),
{}, &post_sample_invoker);
CreateCoreUnwindersForTesting(module_cache()),
&post_sample_invoker);
profiler.Start();
// Wait for 5 samples to be collected.
for (int i = 0; i < 5; ++i)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment