910e62b5创建于 1月15日历史提交
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#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  // ARM32_UNWINDING_SUPPORTED

#if ARM64_UNWINDING_SUPPORTED
#include "base/profiler/frame_pointer_unwinder.h"
#endif  // ARM64_UNWINDING_SUPPORTED

#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" {
// The address of |__executable_start| is the base address of the executable or
// shared library.
extern char __executable_start;
}
#endif  // UNWINDING_SUPPORTED

// See `RequestUnwindPrerequisitesInstallation` below.
BASE_FEATURE(kInstallAndroidUnwindDfm, base::FEATURE_DISABLED_BY_DEFAULT);

namespace {

// Encapsulates the setup required to create the Chrome unwinder on 32 bit
// Android.
#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()})),
        /* chrome_module_base_address= */
        reinterpret_cast<uintptr_t>(&__executable_start),
        /* text_section_start_address= */ base::android::kStartOfText);
  }

 private:
  base::MemoryMappedFile chrome_cfi_file_;
};
#endif  // ARM32_UNWINDING_SUPPORTED

#if UNWINDING_SUPPORTED
std::vector<std::unique_ptr<base::Unwinder>> CreateLibunwindstackUnwinders() {
  // Ensure that the unwinder initialization occurs off the main thread, since
  // it involves some additional latency.
  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() {
  // Ensure that the unwinder initialization occurs off the main thread, since
  // it involves some additional latency.
  CHECK_NE(getpid(), gettid());

  static base::NoDestructor<NativeUnwinderAndroidMapDelegateImpl> map_delegate;
#if ARM32_UNWINDING_SUPPORTED
  static base::NoDestructor<ChromeUnwinderAndroid32Creator>
      chrome_unwinder_android_32_creator;
#endif

  // Note order matters: the more general unwinder must appear first in the
  // vector.
  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
  // ARM32 requires our custom Chrome unwinder.
  unwinders.push_back(chrome_unwinder_android_32_creator->Create());
#else
  // ARM64 builds with frame pointers so we can use FramePointerUnwinder there.
  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);
      }),
      /*is_system_unwinder=*/false));
#endif
  return unwinders;
}

// Manages installation of the module prerequisite for unwinding. Android, in
// particular, requires a dynamic feature module to provide the native unwinder.
class ModuleUnwindPrerequisitesDelegate : public UnwindPrerequisitesDelegate {
 public:
  void RequestInstallation(version_info::Channel /* unused */) override {
    stack_unwinder::Module::RequestInstallation();
  }

  bool AreAvailable(version_info::Channel channel) override {
    return stack_unwinder::Module::IsInstalled();
  }
};
#endif  // UNWINDING_SUPPORTED

}  // namespace

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;
  }
  // We only want to incur the cost of universally downloading the module in
  // early channels, where profiling will occur over substantially all of
  // the population. When supporting later channels in the future we will
  // enable profiling for only a fraction of users and only download for
  // those users.
  //
  // The install occurs asynchronously, with the module available at the first
  // run of Chrome following install.
  if (base::FeatureList::IsEnabled(kInstallAndroidUnwindDfm)) {
    prerequites_delegate->RequestInstallation(channel);
  }
#endif
}

bool AreUnwindPrerequisitesAvailable(
    version_info::Channel channel,
    UnwindPrerequisitesDelegate* prerequites_delegate) {
// While non-Android platforms do not need any specific prerequisites beyond
// what is already bundled and available with Chrome for their platform-specific
// unwinders to work, Android, in particular, requires a DFM to be installed.
//
// Therefore, unwind prerequisites for non-supported Android platforms are not
// considered to be available by default, but prerequisites for non-Android
// platforms are considered to be available by default.
//
// This is also why we do not need to check `prerequites_delegate` for
// non-Android platforms. Regardless of the provided delegate, unwind
// prerequisites are always considered to be available for non-Android
// platforms.
#if UNWINDING_SUPPORTED
#if defined(OFFICIAL_BUILD) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
  // Sometimes, DFMs can be installed even if not requested by Chrome
  // explicitly (for instance, in some app stores). Therefore, even if the
  // unwinder module is installed, we only consider it to be available for
  // specific channels.
  if (!(channel == version_info::Channel::CANARY ||
        channel == version_info::Channel::DEV ||
        channel == version_info::Channel::BETA)) {
    return false;
  }
#endif  // defined(OFFICIAL_BUILD) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
  ModuleUnwindPrerequisitesDelegate default_delegate;
  if (prerequites_delegate == nullptr) {
    prerequites_delegate = &default_delegate;
  }
  return prerequites_delegate->AreAvailable(channel);
#else   // UNWINDING_SUPPORTED
  return false;
#endif  // UNWINDING_SUPPORTED
}

#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  // UNWINDING_SUPPORTED

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   // ARM32_UNWINDING_SUPPORTED
  // On ARM64 for now, mimic the existing support for browser main thread, which
  // uses the libunwindstack unwinder.
  // TODO(crbug.com/380487894): determine if we can avoid this special case and
  // just use the core unwinders, based on observed data quality.
  if (GetProfilerProcessType(*base::CommandLine::ForCurrentProcess()) ==
          sampling_profiler::ProfilerProcessType::kBrowser &&
      getpid() == gettid()) {
    return CreateLibunwindstackUnwinderFactory();
  }
  return base::BindOnce(CreateCoreUnwinders);
#endif  // ARM32_UNWINDING_SUPPORTED
#else   // UNWINDING_SUPPORTED
  return base::StackSamplingProfiler::UnwindersFactory();
#endif  // UNWINDING_SUPPORTED
}

base::StackSamplingProfiler::UnwindersFactory
CreateLibunwindstackUnwinderFactory() {
  if (!AreUnwindPrerequisitesAvailable(chrome::GetChannel())) {
    return base::StackSamplingProfiler::UnwindersFactory();
  }
#if UNWINDING_SUPPORTED
  return base::BindOnce(CreateLibunwindstackUnwinders);
#else   // UNWINDING_SUPPORTED
  return base::StackSamplingProfiler::UnwindersFactory();
#endif  // UNWINDING_SUPPORTED
}