#include "chrome/common/profiler/core_unwinders.h"
#include <memory>
#include <type_traits>
#include <vector>
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/no_destructor.h"
#include "base/profiler/profiler_buildflags.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "base/profiler/unwinder.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/profiler/native_unwinder_android_map_delegate_impl.h"
#include "chrome/common/profiler/process_type.h"
#include "components/sampling_profiler/process_type.h"
#include "components/version_info/channel.h"
#if defined(ARCH_CPU_ARMEL) && BUILDFLAG(ENABLE_ARM_CFI_TABLE)
#define ARM32_UNWINDING_SUPPORTED 1
#else
#define ARM32_UNWINDING_SUPPORTED 0
#endif
#if defined(ARCH_CPU_ARM64)
#define ARM64_UNWINDING_SUPPORTED 1
#else
#define ARM64_UNWINDING_SUPPORTED 0
#endif
#if ARM32_UNWINDING_SUPPORTED || ARM64_UNWINDING_SUPPORTED
#define UNWINDING_SUPPORTED 1
#else
#define UNWINDING_SUPPORTED 0
#endif
#if ARM32_UNWINDING_SUPPORTED
#include "base/android/apk_assets.h"
#include "base/android/library_loader/anchor_functions.h"
#include "base/files/memory_mapped_file.h"
#include "base/profiler/chrome_unwinder_android_32.h"
#endif
#if ARM64_UNWINDING_SUPPORTED
#include "base/profiler/frame_pointer_unwinder.h"
#endif
#if UNWINDING_SUPPORTED
#include "base/profiler/libunwindstack_unwinder_android.h"
#include "base/profiler/native_unwinder_android.h"
#include "chrome/android/modules/stack_unwinder/public/module.h"
#include "chrome/common/profiler/native_unwinder_android_map_delegate_impl.h"
extern "C" {
extern char __executable_start;
}
#endif
BASE_FEATURE(kInstallAndroidUnwindDfm, base::FEATURE_DISABLED_BY_DEFAULT);
namespace {
#if ARM32_UNWINDING_SUPPORTED
class ChromeUnwinderAndroid32Creator {
public:
ChromeUnwinderAndroid32Creator() {
constexpr char kCfiFileName[] = "assets/unwind_cfi_32_v2";
constexpr char kSplitName[] = "stack_unwinder";
base::MemoryMappedFile::Region cfi_region;
int fd = base::android::OpenApkAsset(kCfiFileName, kSplitName, &cfi_region);
CHECK_GE(fd, 0);
bool mapped_file_ok =
chrome_cfi_file_.Initialize(base::File(fd), cfi_region);
CHECK(mapped_file_ok);
}
ChromeUnwinderAndroid32Creator(const ChromeUnwinderAndroid32Creator&) =
delete;
ChromeUnwinderAndroid32Creator& operator=(
const ChromeUnwinderAndroid32Creator&) = delete;
std::unique_ptr<base::Unwinder> Create() {
return std::make_unique<base::ChromeUnwinderAndroid32>(
base::CreateChromeUnwindInfoAndroid32(
UNSAFE_TODO({chrome_cfi_file_.data(), chrome_cfi_file_.length()})),
reinterpret_cast<uintptr_t>(&__executable_start),
base::android::kStartOfText);
}
private:
base::MemoryMappedFile chrome_cfi_file_;
};
#endif
#if UNWINDING_SUPPORTED
std::vector<std::unique_ptr<base::Unwinder>> CreateLibunwindstackUnwinders() {
CHECK_NE(getpid(), gettid());
std::vector<std::unique_ptr<base::Unwinder>> unwinders;
unwinders.push_back(std::make_unique<base::LibunwindstackUnwinderAndroid>());
return unwinders;
}
std::vector<std::unique_ptr<base::Unwinder>> CreateCoreUnwinders() {
CHECK_NE(getpid(), gettid());
static base::NoDestructor<NativeUnwinderAndroidMapDelegateImpl> map_delegate;
#if ARM32_UNWINDING_SUPPORTED
static base::NoDestructor<ChromeUnwinderAndroid32Creator>
chrome_unwinder_android_32_creator;
#endif
std::vector<std::unique_ptr<base::Unwinder>> unwinders;
unwinders.push_back(std::make_unique<base::NativeUnwinderAndroid>(
reinterpret_cast<uintptr_t>(&__executable_start), map_delegate.get()));
#if ARM32_UNWINDING_SUPPORTED
unwinders.push_back(chrome_unwinder_android_32_creator->Create());
#else
unwinders.push_back(std::make_unique<base::FramePointerUnwinder>(
base::BindRepeating([](const base::Frame& current_frame) {
return current_frame.module &&
current_frame.module->GetBaseAddress() ==
reinterpret_cast<uintptr_t>(&__executable_start);
}),
false));
#endif
return unwinders;
}
class ModuleUnwindPrerequisitesDelegate : public UnwindPrerequisitesDelegate {
public:
void RequestInstallation(version_info::Channel ) override {
stack_unwinder::Module::RequestInstallation();
}
bool AreAvailable(version_info::Channel channel) override {
return stack_unwinder::Module::IsInstalled();
}
};
#endif
}
void RequestUnwindPrerequisitesInstallation(
version_info::Channel channel,
UnwindPrerequisitesDelegate* prerequites_delegate) {
CHECK_EQ(sampling_profiler::ProfilerProcessType::kBrowser,
GetProfilerProcessType(*base::CommandLine::ForCurrentProcess()));
if (AreUnwindPrerequisitesAvailable(channel, prerequites_delegate)) {
return;
}
#if UNWINDING_SUPPORTED && defined(OFFICIAL_BUILD) && \
BUILDFLAG(GOOGLE_CHROME_BRANDING)
ModuleUnwindPrerequisitesDelegate default_delegate;
if (prerequites_delegate == nullptr) {
prerequites_delegate = &default_delegate;
}
if (base::FeatureList::IsEnabled(kInstallAndroidUnwindDfm)) {
prerequites_delegate->RequestInstallation(channel);
}
#endif
}
bool AreUnwindPrerequisitesAvailable(
version_info::Channel channel,
UnwindPrerequisitesDelegate* prerequites_delegate) {
#if UNWINDING_SUPPORTED
#if defined(OFFICIAL_BUILD) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
if (!(channel == version_info::Channel::CANARY ||
channel == version_info::Channel::DEV ||
channel == version_info::Channel::BETA)) {
return false;
}
#endif
ModuleUnwindPrerequisitesDelegate default_delegate;
if (prerequites_delegate == nullptr) {
prerequites_delegate = &default_delegate;
}
return prerequites_delegate->AreAvailable(channel);
#else
return false;
#endif
}
#if UNWINDING_SUPPORTED
void LoadModule() {
CHECK(AreUnwindPrerequisitesAvailable(chrome::GetChannel()));
static base::NoDestructor<std::unique_ptr<stack_unwinder::Module>>
stack_unwinder_module(stack_unwinder::Module::Load());
}
#endif
base::StackSamplingProfiler::UnwindersFactory CreateCoreUnwindersFactory() {
if (!AreUnwindPrerequisitesAvailable(chrome::GetChannel())) {
return base::StackSamplingProfiler::UnwindersFactory();
}
#if UNWINDING_SUPPORTED
#if ARM32_UNWINDING_SUPPORTED
LoadModule();
return base::BindOnce(CreateCoreUnwinders);
#else
if (GetProfilerProcessType(*base::CommandLine::ForCurrentProcess()) ==
sampling_profiler::ProfilerProcessType::kBrowser &&
getpid() == gettid()) {
return CreateLibunwindstackUnwinderFactory();
}
return base::BindOnce(CreateCoreUnwinders);
#endif
#else
return base::StackSamplingProfiler::UnwindersFactory();
#endif
}
base::StackSamplingProfiler::UnwindersFactory
CreateLibunwindstackUnwinderFactory() {
if (!AreUnwindPrerequisitesAvailable(chrome::GetChannel())) {
return base::StackSamplingProfiler::UnwindersFactory();
}
#if UNWINDING_SUPPORTED
return base::BindOnce(CreateLibunwindstackUnwinders);
#else
return base::StackSamplingProfiler::UnwindersFactory();
#endif
}