diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-12 14:27:29 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-13 09:35:20 +0000 |
commit | c30a6232df03e1efbd9f3b226777b07e087a1122 (patch) | |
tree | e992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/base/profiler | |
parent | 7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff) | |
download | qtwebengine-chromium-85-based.tar.gz |
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/base/profiler')
27 files changed, 644 insertions, 225 deletions
diff --git a/chromium/base/profiler/chrome_unwinder_android.cc b/chromium/base/profiler/chrome_unwinder_android.cc index 8eea2a8b5c8..4e19111fa46 100644 --- a/chromium/base/profiler/chrome_unwinder_android.cc +++ b/chromium/base/profiler/chrome_unwinder_android.cc @@ -13,16 +13,17 @@ namespace base { ChromeUnwinderAndroid::ChromeUnwinderAndroid( const ArmCFITable* cfi_table, - const ModuleCache::Module* chrome_module) - : cfi_table_(cfi_table), chrome_module_(chrome_module) { + uintptr_t chrome_module_base_address) + : cfi_table_(cfi_table), + chrome_module_base_address_(chrome_module_base_address) { DCHECK(cfi_table_); - DCHECK(chrome_module_); } ChromeUnwinderAndroid::~ChromeUnwinderAndroid() = default; bool ChromeUnwinderAndroid::CanUnwindFrom(const Frame& current_frame) const { - return current_frame.module == chrome_module_; + return current_frame.module && + current_frame.module->GetBaseAddress() == chrome_module_base_address_; } UnwindResult ChromeUnwinderAndroid::TryUnwind(RegisterContext* thread_context, diff --git a/chromium/base/profiler/chrome_unwinder_android.h b/chromium/base/profiler/chrome_unwinder_android.h index b8f67894a5e..6894380c022 100644 --- a/chromium/base/profiler/chrome_unwinder_android.h +++ b/chromium/base/profiler/chrome_unwinder_android.h @@ -19,7 +19,7 @@ namespace base { class BASE_EXPORT ChromeUnwinderAndroid : public Unwinder { public: ChromeUnwinderAndroid(const ArmCFITable* cfi_table, - const ModuleCache::Module* chrome_module); + uintptr_t chrome_module_base_address); ~ChromeUnwinderAndroid() override; ChromeUnwinderAndroid(const ChromeUnwinderAndroid&) = delete; ChromeUnwinderAndroid& operator=(const ChromeUnwinderAndroid&) = delete; @@ -43,7 +43,7 @@ class BASE_EXPORT ChromeUnwinderAndroid : public Unwinder { const ArmCFITable::FrameEntry& entry); const ArmCFITable* cfi_table_; - const ModuleCache::Module* const chrome_module_; + const uintptr_t chrome_module_base_address_; }; } // namespace base diff --git a/chromium/base/profiler/chrome_unwinder_android_unittest.cc b/chromium/base/profiler/chrome_unwinder_android_unittest.cc index e74a710497e..9e8b1bb4c9f 100644 --- a/chromium/base/profiler/chrome_unwinder_android_unittest.cc +++ b/chromium/base/profiler/chrome_unwinder_android_unittest.cc @@ -213,7 +213,8 @@ TEST(ChromeUnwinderAndroidTest, CanUnwindFrom) { auto non_chrome_module = std::make_unique<TestModule>(0x2000, 0x500, "OtherModule"); - ChromeUnwinderAndroid unwinder(cfi_table.get(), chrome_module.get()); + ChromeUnwinderAndroid unwinder(cfi_table.get(), + chrome_module->GetBaseAddress()); EXPECT_TRUE(unwinder.CanUnwindFrom({0x1100, chrome_module.get()})); EXPECT_FALSE(unwinder.CanUnwindFrom({0x2100, non_chrome_module.get()})); @@ -227,7 +228,8 @@ TEST(ChromeUnwinderAndroidTest, TryUnwind) { const ModuleCache::Module* chrome_module = AddNativeModule( &module_cache, std::make_unique<TestModule>(0x1000, 0x500)); - ChromeUnwinderAndroid unwinder(cfi_table.get(), chrome_module); + ChromeUnwinderAndroid unwinder(cfi_table.get(), + chrome_module->GetBaseAddress()); std::vector<uintptr_t> stack_buffer = { 0xFFFF, @@ -264,7 +266,8 @@ TEST(ChromeUnwinderAndroidTest, TryUnwindAbort) { const ModuleCache::Module* chrome_module = AddNativeModule( &module_cache, std::make_unique<TestModule>(0x1000, 0x500)); - ChromeUnwinderAndroid unwinder(cfi_table.get(), chrome_module); + ChromeUnwinderAndroid unwinder(cfi_table.get(), + chrome_module->GetBaseAddress()); std::vector<uintptr_t> stack_buffer = { 0xFFFF, @@ -295,7 +298,8 @@ TEST(ChromeUnwinderAndroidTest, TryUnwindNoData) { const ModuleCache::Module* chrome_module = AddNativeModule( &module_cache, std::make_unique<TestModule>(0x1000, 0x500)); - ChromeUnwinderAndroid unwinder(cfi_table.get(), chrome_module); + ChromeUnwinderAndroid unwinder(cfi_table.get(), + chrome_module->GetBaseAddress()); std::vector<uintptr_t> stack_buffer = {0xFFFF}; diff --git a/chromium/base/profiler/metadata_recorder.cc b/chromium/base/profiler/metadata_recorder.cc index a6e3cc22dde..f140c2547f6 100644 --- a/chromium/base/profiler/metadata_recorder.cc +++ b/chromium/base/profiler/metadata_recorder.cc @@ -113,15 +113,10 @@ MetadataRecorder::MetadataProvider::~MetadataProvider() = default; size_t MetadataRecorder::MetadataProvider::GetItems( ItemArray* const items) const { - // Assertion is only necessary so that thread annotations recognize that - // |read_lock_| is acquired. - metadata_recorder_->read_lock_.AssertAcquired(); return metadata_recorder_->GetItems(items); } size_t MetadataRecorder::GetItems(ItemArray* const items) const { - read_lock_.AssertAcquired(); - // If a writer adds a new item after this load, it will be ignored. We do // this instead of calling item_slots_used_.load() explicitly in the for loop // bounds checking, which would be expensive. diff --git a/chromium/base/profiler/metadata_recorder.h b/chromium/base/profiler/metadata_recorder.h index 6e120dd40b7..86296348463 100644 --- a/chromium/base/profiler/metadata_recorder.h +++ b/chromium/base/profiler/metadata_recorder.h @@ -187,8 +187,10 @@ class BASE_EXPORT MetadataRecorder { // Retrieves the first |available_slots| items in the metadata recorder and // copies them into |items|, returning the number of metadata items that // were copied. To ensure that all items can be copied, |available slots| - // should be greater than or equal to |MAX_METADATA_COUNT|. - size_t GetItems(ItemArray* const items) const; + // should be greater than or equal to |MAX_METADATA_COUNT|. Requires + // NO_THREAD_SAFETY_ANALYSIS because clang's analyzer doesn't understand the + // cross-class locking used in this class' implementation. + size_t GetItems(ItemArray* const items) const NO_THREAD_SAFETY_ANALYSIS; private: const MetadataRecorder* const metadata_recorder_; diff --git a/chromium/base/profiler/native_unwinder_android.cc b/chromium/base/profiler/native_unwinder_android.cc index fa06494aee2..c76a81959d4 100644 --- a/chromium/base/profiler/native_unwinder_android.cc +++ b/chromium/base/profiler/native_unwinder_android.cc @@ -4,21 +4,22 @@ #include "base/profiler/native_unwinder_android.h" +#include <sys/mman.h> + #include <string> #include <vector> -#include <sys/mman.h> - #include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/Elf.h" -#include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/Maps.h" #include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/Memory.h" #include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/Regs.h" #include "base/memory/ptr_util.h" +#include "base/notreached.h" #include "base/profiler/module_cache.h" #include "base/profiler/native_unwinder.h" #include "base/profiler/profile_builder.h" -#include "base/profiler/unwindstack_internal_android.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" #include "build/build_config.h" #if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS) @@ -32,20 +33,46 @@ namespace base { namespace { +// Returns the hex string build id given the binary build id from MapInfo. +// Returns the empty string if no build id is present. +// +// Build IDs follow a cross-platform format consisting of two fields +// concatenated together: +// - the module's unique id encoded as a hex string, and +// - the age suffix for incremental builds. +// +// On POSIX, the unique id comes from the ELF binary's .note.gnu.build-id +// section. The age field is always 0. +std::string EncodeBuildID(StringPiece map_info_build_id) { + if (map_info_build_id.empty()) + return std::string(); + + return HexEncode(map_info_build_id.data(), map_info_build_id.size()) + "0"; +} + +// We assume this is a file-backed region if the name looks like an absolute +// path, and return the basename. Otherwise we assume the region is not +// associated with a file and return the full name string. +FilePath CreateDebugBasename(StringPiece map_info_name) { + const FilePath path = FilePath(map_info_name); + return !map_info_name.empty() && map_info_name[0] == '/' ? path.BaseName() + : path; +} + class AndroidModule : public ModuleCache::Module { public: AndroidModule(unwindstack::MapInfo* map_info) : start_(map_info->start), size_(map_info->end - map_info->start), - build_id_(map_info->GetBuildID()), - name_(map_info->name) {} + build_id_(EncodeBuildID(map_info->GetBuildID())), + debug_basename_(CreateDebugBasename(map_info->name)) {} ~AndroidModule() override = default; uintptr_t GetBaseAddress() const override { return start_; } std::string GetId() const override { return build_id_; } - FilePath GetDebugBasename() const override { return FilePath(name_); } + FilePath GetDebugBasename() const override { return debug_basename_; } // Gets the size of the module. size_t GetSize() const override { return size_; } @@ -56,7 +83,7 @@ class AndroidModule : public ModuleCache::Module { const uintptr_t start_; const size_t size_; const std::string build_id_; - const std::string name_; + const FilePath debug_basename_; }; std::unique_ptr<unwindstack::Regs> CreateFromRegisterContext( @@ -88,6 +115,23 @@ void CopyToRegisterContext(unwindstack::Regs* regs, } // namespace +UnwindStackMemoryAndroid::UnwindStackMemoryAndroid(uintptr_t stack_ptr, + uintptr_t stack_top) + : stack_ptr_(stack_ptr), stack_top_(stack_top) { + DCHECK_LE(stack_ptr_, stack_top_); +} + +UnwindStackMemoryAndroid::~UnwindStackMemoryAndroid() = default; + +size_t UnwindStackMemoryAndroid::Read(uint64_t addr, void* dst, size_t size) { + if (addr < stack_ptr_) + return 0; + if (size >= stack_top_ || addr > stack_top_ - size) + return 0; + memcpy(dst, reinterpret_cast<void*>(addr), size); + return size; +} + // static std::unique_ptr<unwindstack::Maps> NativeUnwinderAndroid::CreateMaps() { auto maps = std::make_unique<unwindstack::LocalMaps>(); @@ -99,19 +143,7 @@ std::unique_ptr<unwindstack::Maps> NativeUnwinderAndroid::CreateMaps() { // static std::unique_ptr<unwindstack::Memory> NativeUnwinderAndroid::CreateProcessMemory() { - return std::make_unique<unwindstack::MemoryLocal>(); -} - -void NativeUnwinderAndroid::AddInitialModulesFromMaps( - const unwindstack::Maps& memory_regions_map, - ModuleCache* module_cache) { - for (const auto& region : memory_regions_map) { - // Only add executable regions. - if (!(region->flags & PROT_EXEC)) - continue; - module_cache->AddCustomNativeModule( - std::make_unique<AndroidModule>(region.get())); - } + return unwindstack::Memory::CreateLocalProcessMemory(); } NativeUnwinderAndroid::NativeUnwinderAndroid( @@ -160,7 +192,8 @@ UnwindResult NativeUnwinderAndroid::TryUnwind(RegisterContext* thread_context, uintptr_t rel_pc = elf->GetRelPc(cur_pc, map_info); bool finished = false; bool stepped = - elf->Step(rel_pc, rel_pc, regs.get(), &stack_memory, &finished); + elf->StepIfSignalHandler(rel_pc, regs.get(), &stack_memory) || + elf->Step(rel_pc, regs.get(), &stack_memory, &finished); if (stepped && finished) return UnwindResult::COMPLETED; @@ -207,6 +240,19 @@ UnwindResult NativeUnwinderAndroid::TryUnwind(RegisterContext* thread_context, return UnwindResult::UNRECOGNIZED_FRAME; } +// static +void NativeUnwinderAndroid::AddInitialModulesFromMaps( + const unwindstack::Maps& memory_regions_map, + ModuleCache* module_cache) { + for (const auto& region : memory_regions_map) { + // Only add executable regions. + if (!(region->flags & PROT_EXEC)) + continue; + module_cache->AddCustomNativeModule( + std::make_unique<AndroidModule>(region.get())); + } +} + void NativeUnwinderAndroid::EmitDexFrame(uintptr_t dex_pc, ModuleCache* module_cache, std::vector<Frame>* stack) const { diff --git a/chromium/base/profiler/native_unwinder_android.h b/chromium/base/profiler/native_unwinder_android.h index 926a581b32a..62774e6adf5 100644 --- a/chromium/base/profiler/native_unwinder_android.h +++ b/chromium/base/profiler/native_unwinder_android.h @@ -6,14 +6,26 @@ #define BASE_PROFILER_NATIVE_UNWINDER_ANDROID_H_ #include "base/profiler/unwinder.h" - -namespace unwindstack { -class Maps; -class Memory; -} // namespace unwindstack +#include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/Maps.h" +#include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/Memory.h" namespace base { +// Implementation of unwindstack::Memory that restricts memory access to a stack +// buffer, used by NativeUnwinderAndroid. While unwinding, only memory accesses +// within the stack should be performed to restore registers. +class UnwindStackMemoryAndroid : public unwindstack::Memory { + public: + UnwindStackMemoryAndroid(uintptr_t stack_ptr, uintptr_t stack_top); + ~UnwindStackMemoryAndroid() override; + + size_t Read(uint64_t addr, void* dst, size_t size) override; + + private: + const uintptr_t stack_ptr_; + const uintptr_t stack_top_; +}; + // Native unwinder implementation for Android, using libunwindstack. class NativeUnwinderAndroid : public Unwinder { public: @@ -22,10 +34,6 @@ class NativeUnwinderAndroid : public Unwinder { // all profiles in a process. static std::unique_ptr<unwindstack::Maps> CreateMaps(); static std::unique_ptr<unwindstack::Memory> CreateProcessMemory(); - // Adds modules found from executable loaded memory regions to |module_cache|. - static void AddInitialModulesFromMaps( - const unwindstack::Maps& memory_regions_map, - ModuleCache* module_cache); // |exclude_module_with_base_address| is used to exclude a specific module // and let another unwinder take control. TryUnwind() will exit with @@ -33,7 +41,7 @@ class NativeUnwinderAndroid : public Unwinder { // encountered in that module. NativeUnwinderAndroid(unwindstack::Maps* memory_regions_map, unwindstack::Memory* process_memory, - uintptr_t exclude_module_with_base_address = 0); + uintptr_t exclude_module_with_base_address); ~NativeUnwinderAndroid() override; NativeUnwinderAndroid(const NativeUnwinderAndroid&) = delete; @@ -47,6 +55,12 @@ class NativeUnwinderAndroid : public Unwinder { ModuleCache* module_cache, std::vector<Frame>* stack) const override; + // Adds modules found from executable loaded memory regions to |module_cache|. + // Public for test access. + static void AddInitialModulesFromMaps( + const unwindstack::Maps& memory_regions_map, + ModuleCache* module_cache); + private: void EmitDexFrame(uintptr_t dex_pc, ModuleCache* module_cache, diff --git a/chromium/base/profiler/native_unwinder_android_unittest.cc b/chromium/base/profiler/native_unwinder_android_unittest.cc index 236ab3ae6fc..c82a6e48f4c 100644 --- a/chromium/base/profiler/native_unwinder_android_unittest.cc +++ b/chromium/base/profiler/native_unwinder_android_unittest.cc @@ -4,7 +4,11 @@ #include "base/profiler/native_unwinder_android.h" +#include <sys/mman.h> + +#include <stdio.h> // For printf address. #include <string.h> +#include <iterator> #include "base/android/build_info.h" #include "base/android/jni_android.h" @@ -13,9 +17,9 @@ #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/profiler/unwindstack_internal_android.h" #include "base/test/bind_test_util.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" @@ -24,6 +28,27 @@ extern char __executable_start; namespace base { +namespace { + +// Add a MapInfo with the provided values to |maps|. +void AddMapInfo(uint64_t start, + uint64_t end, + uint64_t offset, + uint64_t flags, + const std::string& name, + const std::string& binary_build_id, + unwindstack::Maps& maps) { + maps.Add(start, end, offset, flags, name, /* load_bias = */ 0u); + unwindstack::MapInfo& map_info = **std::prev(maps.end()); + // Yes, this *is* how MapInfo wants this field set. The string is deleted in + // its destructor. + map_info.build_id = + reinterpret_cast<uintptr_t>(new std::string(binary_build_id)); + map_info.elf_offset = map_info.offset; +} + +} // namespace + class TestStackCopierDelegate : public StackCopier::Delegate { public: void OnStackCopy() override {} @@ -362,4 +387,153 @@ TEST(NativeUnwinderAndroidTest, UnwindStackMemoryTest) { check_read_succeeds(end - 1, 1); } +// Checks the debug basename for a module with a path name. +TEST(NativeUnwinderAndroidTest, ModuleDebugBasenameForPath) { + unwindstack::Maps maps; + + AddMapInfo(0x1000u, 0x2000u, 0u, PROT_READ | PROT_EXEC, "/usr/lib/foo.so", + {0xAA}, maps); + + ModuleCache module_cache; + NativeUnwinderAndroid::AddInitialModulesFromMaps(maps, &module_cache); + + std::vector<const ModuleCache::Module*> modules = module_cache.GetModules(); + + ASSERT_EQ(1u, modules.size()); + EXPECT_EQ("foo.so", modules[0]->GetDebugBasename().value()); +} + +// Checks the debug basename is the whole name for a module with a non-path +// name. +TEST(NativeUnwinderAndroidTest, ModuleDebugBasenameForNonPath) { + unwindstack::Maps maps; + + AddMapInfo(0x1000u, 0x2000u, 0u, PROT_READ | PROT_EXEC, "[foo / bar]", {0xAA}, + maps); + + ModuleCache module_cache; + NativeUnwinderAndroid::AddInitialModulesFromMaps(maps, &module_cache); + + std::vector<const ModuleCache::Module*> modules = module_cache.GetModules(); + + ASSERT_EQ(1u, modules.size()); + EXPECT_EQ("[foo / bar]", modules[0]->GetDebugBasename().value()); +} + +// Checks that the specified build id is returned. +TEST(NativeUnwinderAndroidTest, ModuleId) { + unwindstack::Maps maps; + + AddMapInfo(0x1000u, 0x2000u, 0u, PROT_READ | PROT_EXEC, "/lib/foo.so", + {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF}, maps); + + ModuleCache module_cache; + NativeUnwinderAndroid::AddInitialModulesFromMaps(maps, &module_cache); + + std::vector<const ModuleCache::Module*> modules = module_cache.GetModules(); + + ASSERT_EQ(1u, modules.size()); + // The id should have a '0' age field appended. + EXPECT_EQ("1234567890ABCDEF0", modules[0]->GetId()); +} + +// Checks that an empty module id has no age field appended. +TEST(NativeUnwinderAndroidTest, EmptyModuleId) { + unwindstack::Maps maps; + + AddMapInfo(0x1000u, 0x2000u, 0u, PROT_READ | PROT_EXEC, "/lib/foo.so", + std::string(), maps); + + ModuleCache module_cache; + NativeUnwinderAndroid::AddInitialModulesFromMaps(maps, &module_cache); + + std::vector<const ModuleCache::Module*> modules = module_cache.GetModules(); + + ASSERT_EQ(1u, modules.size()); + EXPECT_EQ(std::string(), modules[0]->GetId()); +} + +// ModuleCache::GetModuleForAddress() is not implemented for 64-bit arm. +#if defined(ARCH_CPU_ARM64) +#define MAYBE_ModuleState_SystemLibrary DISABLED_ModuleState_SystemLibrary +#else +#define MAYBE_ModuleState_SystemLibrary ModuleState_SystemLibrary +#endif +// Checks that the module state created by the unwinder is consistent with the +// state created by the ModuleCache. Checks the module for a system library. +TEST(NativeUnwinderAndroidTest, MAYBE_ModuleState_SystemLibrary) { + ModuleCache unwinder_module_cache; + NativeUnwinderAndroid::AddInitialModulesFromMaps( + *NativeUnwinderAndroid::CreateMaps(), &unwinder_module_cache); + + const uintptr_t c_library_function_address = + reinterpret_cast<uintptr_t>(&printf); + + std::vector<const ModuleCache::Module*> unwinder_modules = + unwinder_module_cache.GetModules(); + const auto unwinder_module_loc = std::find_if( + unwinder_modules.begin(), unwinder_modules.end(), + [c_library_function_address](const ModuleCache::Module* module) { + return c_library_function_address >= module->GetBaseAddress() && + c_library_function_address < + module->GetBaseAddress() + module->GetSize(); + }); + ASSERT_NE(unwinder_modules.end(), unwinder_module_loc); + const ModuleCache::Module* unwinder_module = *unwinder_module_loc; + + ModuleCache reference_module_cache; + const ModuleCache::Module* reference_module = + reference_module_cache.GetModuleForAddress(c_library_function_address); + ASSERT_NE(nullptr, reference_module); + + // TODO(https://crbug.com/1004855): Fix base address and size discrepancies + // and add checks. + EXPECT_EQ(reference_module->GetId(), unwinder_module->GetId()); + EXPECT_EQ(reference_module->GetDebugBasename(), + unwinder_module->GetDebugBasename()); +} + +// ModuleCache::GetModuleForAddress() is not implemented for 64-bit arm. +#if defined(ARCH_CPU_ARM64) +#define MAYBE_ModuleState_ChromeLibrary DISABLED_ModuleState_ChromeLibrary +#else +#define MAYBE_ModuleState_ChromeLibrary ModuleState_ChromeLibrary +#endif +// Checks that the module state created by the unwinder is consistent with the +// state created by the ModuleCache. Checks the module for a Chrome-compiled +// library. +TEST(NativeUnwinderAndroidTest, MAYBE_ModuleState_ChromeLibrary) { + ModuleCache unwinder_module_cache; + NativeUnwinderAndroid::AddInitialModulesFromMaps( + *NativeUnwinderAndroid::CreateMaps(), &unwinder_module_cache); + + const uintptr_t chrome_function_address = + reinterpret_cast<uintptr_t>(&CaptureScenario); + + std::vector<const ModuleCache::Module*> unwinder_modules = + unwinder_module_cache.GetModules(); + const auto unwinder_module_loc = std::find_if( + unwinder_modules.begin(), unwinder_modules.end(), + [chrome_function_address](const ModuleCache::Module* module) { + return chrome_function_address >= module->GetBaseAddress() && + chrome_function_address < + module->GetBaseAddress() + module->GetSize(); + }); + ASSERT_NE(unwinder_modules.end(), unwinder_module_loc); + const ModuleCache::Module* unwinder_module = *unwinder_module_loc; + + ModuleCache reference_module_cache; + const ModuleCache::Module* reference_module = + reference_module_cache.GetModuleForAddress(chrome_function_address); + ASSERT_NE(nullptr, reference_module); + + EXPECT_EQ(reference_module->GetBaseAddress(), + unwinder_module->GetBaseAddress()); + EXPECT_NE("", unwinder_module->GetId()); + EXPECT_EQ(reference_module->GetId(), unwinder_module->GetId()); + EXPECT_EQ(reference_module->GetDebugBasename(), + unwinder_module->GetDebugBasename()); + // TODO(https://crbug.com/1004855): Fix size discrepancy and add check. +} + } // namespace base diff --git a/chromium/base/profiler/stack_copier_signal.cc b/chromium/base/profiler/stack_copier_signal.cc index bbef65bf520..5a7d8b97806 100644 --- a/chromium/base/profiler/stack_copier_signal.cc +++ b/chromium/base/profiler/stack_copier_signal.cc @@ -11,10 +11,11 @@ #include <atomic> +#include "base/notreached.h" #include "base/profiler/register_context.h" #include "base/profiler/stack_buffer.h" #include "base/profiler/suspendable_thread_delegate.h" -#include "base/trace_event/trace_event.h" +#include "base/trace_event/base_tracing.h" #include "build/build_config.h" namespace base { diff --git a/chromium/base/profiler/stack_copier_unittest.cc b/chromium/base/profiler/stack_copier_unittest.cc index 7119af446db..4eb6e7d8486 100644 --- a/chromium/base/profiler/stack_copier_unittest.cc +++ b/chromium/base/profiler/stack_copier_unittest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <cstring> #include <memory> #include <numeric> diff --git a/chromium/base/profiler/stack_sampler.h b/chromium/base/profiler/stack_sampler.h index 9fc91051e85..9b4dfd971db 100644 --- a/chromium/base/profiler/stack_sampler.h +++ b/chromium/base/profiler/stack_sampler.h @@ -6,6 +6,7 @@ #define BASE_PROFILER_STACK_SAMPLER_H_ #include <memory> +#include <vector> #include "base/base_export.h" #include "base/macros.h" @@ -28,12 +29,14 @@ class BASE_EXPORT StackSampler { virtual ~StackSampler(); // Creates a stack sampler that records samples for thread with - // |thread_token|. Returns null if this platform does not support stack - // sampling. + // |thread_token|. Unwinders in |unwinders| must be stored in increasing + // priority to guide unwind attempts. Only the unwinder with the lowest + // priority is allowed to return with UnwindResult::COMPLETED. Returns null if + // this platform does not support stack sampling. static std::unique_ptr<StackSampler> Create( SamplingProfilerThreadToken thread_token, ModuleCache* module_cache, - std::unique_ptr<Unwinder> native_unwinder, + std::vector<std::unique_ptr<Unwinder>> core_unwinders, StackSamplerTestDelegate* test_delegate); // Gets the required size of the stack buffer. @@ -47,9 +50,8 @@ class BASE_EXPORT StackSampler { // thread being sampled). // Adds an auxiliary unwinder to handle additional, non-native-code unwind - // scenarios. When attempting to unwind, the relative priority of auxiliary - // unwinders is the inverse of the order of insertion, and the native - // unwinder is given the lowest priority + // scenarios. Unwinders must be inserted in increasing priority, following + // |unwinders| provided in Create(), to guide unwind attempts. virtual void AddAuxUnwinder(std::unique_ptr<Unwinder> unwinder) = 0; // Records a set of frames and returns them. diff --git a/chromium/base/profiler/stack_sampler_android.cc b/chromium/base/profiler/stack_sampler_android.cc index 8414de9e3f4..44df18b9a31 100644 --- a/chromium/base/profiler/stack_sampler_android.cc +++ b/chromium/base/profiler/stack_sampler_android.cc @@ -18,13 +18,15 @@ namespace base { std::unique_ptr<StackSampler> StackSampler::Create( SamplingProfilerThreadToken thread_token, ModuleCache* module_cache, - std::unique_ptr<Unwinder> native_unwinder, + std::vector<std::unique_ptr<Unwinder>> core_unwinders, StackSamplerTestDelegate* test_delegate) { - DCHECK(native_unwinder); + // |core_unwinders| must contain NativeUnwinderAndroid and + // ChromeUnwinderAndroid, respectively. + DCHECK_EQ(2U, core_unwinders.size()); return std::make_unique<StackSamplerImpl>( std::make_unique<StackCopierSignal>( std::make_unique<ThreadDelegatePosix>(thread_token)), - std::move(native_unwinder), module_cache, test_delegate); + std::move(core_unwinders), module_cache, test_delegate); } size_t StackSampler::GetStackBufferSize() { diff --git a/chromium/base/profiler/stack_sampler_impl.cc b/chromium/base/profiler/stack_sampler_impl.cc index 9903b33feea..7b28a6036e0 100644 --- a/chromium/base/profiler/stack_sampler_impl.cc +++ b/chromium/base/profiler/stack_sampler_impl.cc @@ -4,11 +4,11 @@ #include "base/profiler/stack_sampler_impl.h" +#include <iterator> #include <utility> #include "base/check.h" #include "base/compiler_specific.h" -#include "base/logging.h" #include "base/profiler/metadata_recorder.h" #include "base/profiler/profile_builder.h" #include "base/profiler/sample_metadata.h" @@ -64,15 +64,22 @@ class StackCopierDelegate : public StackCopier::Delegate { } // namespace -StackSamplerImpl::StackSamplerImpl(std::unique_ptr<StackCopier> stack_copier, - std::unique_ptr<Unwinder> native_unwinder, - ModuleCache* module_cache, - StackSamplerTestDelegate* test_delegate) +// |core_unwinders| is iterated backward since |core_unwinders| is passed in +// increasing priority order while |unwinders_| is stored in decreasing priority +// order. +StackSamplerImpl::StackSamplerImpl( + std::unique_ptr<StackCopier> stack_copier, + std::vector<std::unique_ptr<Unwinder>> core_unwinders, + ModuleCache* module_cache, + StackSamplerTestDelegate* test_delegate) : stack_copier_(std::move(stack_copier)), + unwinders_(std::make_move_iterator(core_unwinders.rbegin()), + std::make_move_iterator(core_unwinders.rend())), module_cache_(module_cache), test_delegate_(test_delegate) { - DCHECK(native_unwinder); - unwinders_.push_front(std::move(native_unwinder)); + DCHECK(!unwinders_.empty()); + for (const auto& unwinder : unwinders_) + unwinder->AddInitialModules(module_cache_); } StackSamplerImpl::~StackSamplerImpl() = default; @@ -89,6 +96,8 @@ void StackSamplerImpl::RecordStackFrames(StackBuffer* stack_buffer, RegisterContext thread_context; uintptr_t stack_top; TimeTicks timestamp; + + bool copy_stack_succeeded; { // Make this scope as small as possible because |metadata_provider| is // holding a lock. @@ -96,11 +105,15 @@ void StackSamplerImpl::RecordStackFrames(StackBuffer* stack_buffer, GetSampleMetadataRecorder()); StackCopierDelegate delegate(&unwinders_, profile_builder, &metadata_provider); - bool success = stack_copier_->CopyStack( + copy_stack_succeeded = stack_copier_->CopyStack( stack_buffer, &stack_top, ×tamp, &thread_context, &delegate); - if (!success) - return; } + if (!copy_stack_succeeded) { + profile_builder->OnSampleCompleted( + {}, timestamp.is_null() ? TimeTicks::Now() : timestamp); + return; + } + for (const auto& unwinder : unwinders_) unwinder->UpdateModules(module_cache_); @@ -155,8 +168,8 @@ std::vector<Frame> StackSamplerImpl::WalkStack( result = unwinder->get()->TryUnwind(thread_context, stack_top, module_cache, &stack); - // The native unwinder should be the only one that returns COMPLETED - // since the stack starts in native code. + // The unwinder with the lowest priority should be the only one that returns + // COMPLETED since the stack starts in native code. DCHECK(result != UnwindResult::COMPLETED || unwinder->get() == unwinders.back().get()); } while (result != UnwindResult::ABORTED && diff --git a/chromium/base/profiler/stack_sampler_impl.h b/chromium/base/profiler/stack_sampler_impl.h index cceee652f12..c7944303254 100644 --- a/chromium/base/profiler/stack_sampler_impl.h +++ b/chromium/base/profiler/stack_sampler_impl.h @@ -23,7 +23,7 @@ class Unwinder; class BASE_EXPORT StackSamplerImpl : public StackSampler { public: StackSamplerImpl(std::unique_ptr<StackCopier> stack_copier, - std::unique_ptr<Unwinder> native_unwinder, + std::vector<std::unique_ptr<Unwinder>> core_unwinders, ModuleCache* module_cache, StackSamplerTestDelegate* test_delegate = nullptr); ~StackSamplerImpl() override; diff --git a/chromium/base/profiler/stack_sampler_impl_unittest.cc b/chromium/base/profiler/stack_sampler_impl_unittest.cc index e3cd67ce59b..48b12c299ec 100644 --- a/chromium/base/profiler/stack_sampler_impl_unittest.cc +++ b/chromium/base/profiler/stack_sampler_impl_unittest.cc @@ -268,14 +268,21 @@ class FakeTestUnwinder : public Unwinder { std::vector<Result> results_; }; -base::circular_deque<std::unique_ptr<Unwinder>> MakeUnwinderList( +std::vector<std::unique_ptr<Unwinder>> MakeUnwinderVector( + std::unique_ptr<Unwinder> unwinder) { + std::vector<std::unique_ptr<Unwinder>> unwinders; + unwinders.push_back(std::move(unwinder)); + return unwinders; +} + +base::circular_deque<std::unique_ptr<Unwinder>> MakeUnwinderCircularDeque( std::unique_ptr<Unwinder> native_unwinder, std::unique_ptr<Unwinder> aux_unwinder) { base::circular_deque<std::unique_ptr<Unwinder>> unwinders; - if (aux_unwinder) - unwinders.push_back(std::move(aux_unwinder)); if (native_unwinder) - unwinders.push_back(std::move(native_unwinder)); + unwinders.push_front(std::move(native_unwinder)); + if (aux_unwinder) + unwinders.push_front(std::move(aux_unwinder)); return unwinders; } @@ -294,7 +301,9 @@ TEST(StackSamplerImplTest, MAYBE_CopyStack) { std::vector<uintptr_t> stack_copy; StackSamplerImpl stack_sampler_impl( std::make_unique<TestStackCopier>(stack), - std::make_unique<TestUnwinder>(stack.size(), &stack_copy), &module_cache); + MakeUnwinderVector( + std::make_unique<TestUnwinder>(stack.size(), &stack_copy)), + &module_cache); std::unique_ptr<StackBuffer> stack_buffer = std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t)); @@ -312,7 +321,9 @@ TEST(StackSamplerImplTest, CopyStackTimestamp) { TimeTicks timestamp = TimeTicks::UnixEpoch(); StackSamplerImpl stack_sampler_impl( std::make_unique<TestStackCopier>(stack, timestamp), - std::make_unique<TestUnwinder>(stack.size(), &stack_copy), &module_cache); + MakeUnwinderVector( + std::make_unique<TestUnwinder>(stack.size(), &stack_copy)), + &module_cache); std::unique_ptr<StackBuffer> stack_buffer = std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t)); @@ -330,7 +341,7 @@ TEST(StackSamplerImplTest, UnwinderInvokedWhileRecordingStackFrames) { TestProfileBuilder profile_builder(&module_cache); StackSamplerImpl stack_sampler_impl( std::make_unique<DelegateInvokingStackCopier>(), - std::move(owned_unwinder), &module_cache); + MakeUnwinderVector(std::move(owned_unwinder)), &module_cache); stack_sampler_impl.RecordStackFrames(stack_buffer.get(), &profile_builder); @@ -344,7 +355,8 @@ TEST(StackSamplerImplTest, AuxUnwinderInvokedWhileRecordingStackFrames) { TestProfileBuilder profile_builder(&module_cache); StackSamplerImpl stack_sampler_impl( std::make_unique<DelegateInvokingStackCopier>(), - std::make_unique<CallRecordingUnwinder>(), &module_cache); + MakeUnwinderVector(std::make_unique<CallRecordingUnwinder>()), + &module_cache); auto owned_aux_unwinder = std::make_unique<CallRecordingUnwinder>(); CallRecordingUnwinder* aux_unwinder = owned_aux_unwinder.get(); @@ -367,7 +379,7 @@ TEST(StackSamplerImplTest, WalkStack_Completed) { std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting( &module_cache, &thread_context, 0u, - MakeUnwinderList(std::move(native_unwinder), nullptr)); + MakeUnwinderCircularDeque(std::move(native_unwinder), nullptr)); ASSERT_EQ(2u, stack.size()); EXPECT_EQ(1u, stack[1].instruction_pointer); @@ -384,7 +396,7 @@ TEST(StackSamplerImplTest, WalkStack_Aborted) { std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting( &module_cache, &thread_context, 0u, - MakeUnwinderList(std::move(native_unwinder), nullptr)); + MakeUnwinderCircularDeque(std::move(native_unwinder), nullptr)); ASSERT_EQ(2u, stack.size()); EXPECT_EQ(1u, stack[1].instruction_pointer); @@ -400,7 +412,7 @@ TEST(StackSamplerImplTest, WalkStack_NotUnwound) { std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting( &module_cache, &thread_context, 0u, - MakeUnwinderList(std::move(native_unwinder), nullptr)); + MakeUnwinderCircularDeque(std::move(native_unwinder), nullptr)); ASSERT_EQ(1u, stack.size()); } @@ -421,7 +433,7 @@ TEST(StackSamplerImplTest, WalkStack_AuxUnwind) { WrapUnique(new FakeTestUnwinder({{UnwindResult::ABORTED, {1u}}})); std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting( &module_cache, &thread_context, 0u, - MakeUnwinderList(nullptr, std::move(aux_unwinder))); + MakeUnwinderCircularDeque(nullptr, std::move(aux_unwinder))); ASSERT_EQ(2u, stack.size()); EXPECT_EQ(GetTestInstructionPointer(), stack[0].instruction_pointer); @@ -447,7 +459,8 @@ TEST(StackSamplerImplTest, WalkStack_AuxThenNative) { std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting( &module_cache, &thread_context, 0u, - MakeUnwinderList(std::move(native_unwinder), std::move(aux_unwinder))); + MakeUnwinderCircularDeque(std::move(native_unwinder), + std::move(aux_unwinder))); ASSERT_EQ(3u, stack.size()); EXPECT_EQ(0u, stack[0].instruction_pointer); @@ -477,7 +490,8 @@ TEST(StackSamplerImplTest, WalkStack_NativeThenAux) { std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting( &module_cache, &thread_context, 0u, - MakeUnwinderList(std::move(native_unwinder), std::move(aux_unwinder))); + MakeUnwinderCircularDeque(std::move(native_unwinder), + std::move(aux_unwinder))); ASSERT_EQ(4u, stack.size()); EXPECT_EQ(0u, stack[0].instruction_pointer); diff --git a/chromium/base/profiler/stack_sampler_ios.cc b/chromium/base/profiler/stack_sampler_ios.cc index 82ad01f3028..99e68cecc2d 100644 --- a/chromium/base/profiler/stack_sampler_ios.cc +++ b/chromium/base/profiler/stack_sampler_ios.cc @@ -13,7 +13,7 @@ namespace base { std::unique_ptr<StackSampler> StackSampler::Create( SamplingProfilerThreadToken thread_token, ModuleCache* module_cache, - std::unique_ptr<Unwinder> native_unwinder, + std::vector<std::unique_ptr<Unwinder>> core_unwinders, StackSamplerTestDelegate* test_delegate) { return nullptr; } diff --git a/chromium/base/profiler/stack_sampler_mac.cc b/chromium/base/profiler/stack_sampler_mac.cc index 109f6425835..e88dfc57904 100644 --- a/chromium/base/profiler/stack_sampler_mac.cc +++ b/chromium/base/profiler/stack_sampler_mac.cc @@ -16,14 +16,14 @@ namespace base { std::unique_ptr<StackSampler> StackSampler::Create( SamplingProfilerThreadToken thread_token, ModuleCache* module_cache, - std::unique_ptr<Unwinder> native_unwinder, + std::vector<std::unique_ptr<Unwinder>> core_unwinders, StackSamplerTestDelegate* test_delegate) { - DCHECK(!native_unwinder); + DCHECK(core_unwinders.empty()); + core_unwinders.push_back(std::make_unique<NativeUnwinderMac>(module_cache)); return std::make_unique<StackSamplerImpl>( std::make_unique<StackCopierSuspend>( std::make_unique<SuspendableThreadDelegateMac>(thread_token)), - std::make_unique<NativeUnwinderMac>(module_cache), module_cache, - test_delegate); + std::move(core_unwinders), module_cache, test_delegate); } // static diff --git a/chromium/base/profiler/stack_sampler_posix.cc b/chromium/base/profiler/stack_sampler_posix.cc index 44215298c63..ae67f25cc4b 100644 --- a/chromium/base/profiler/stack_sampler_posix.cc +++ b/chromium/base/profiler/stack_sampler_posix.cc @@ -14,7 +14,7 @@ namespace base { std::unique_ptr<StackSampler> StackSampler::Create( SamplingProfilerThreadToken thread_token, ModuleCache* module_cache, - std::unique_ptr<Unwinder> native_unwinder, + std::vector<std::unique_ptr<Unwinder>> core_unwinders, StackSamplerTestDelegate* test_delegate) { return nullptr; } diff --git a/chromium/base/profiler/stack_sampler_win.cc b/chromium/base/profiler/stack_sampler_win.cc index c19009b77e4..2a29d4c0ee8 100644 --- a/chromium/base/profiler/stack_sampler_win.cc +++ b/chromium/base/profiler/stack_sampler_win.cc @@ -17,14 +17,15 @@ namespace base { std::unique_ptr<StackSampler> StackSampler::Create( SamplingProfilerThreadToken thread_token, ModuleCache* module_cache, - std::unique_ptr<Unwinder> native_unwinder, + std::vector<std::unique_ptr<Unwinder>> core_unwinders, StackSamplerTestDelegate* test_delegate) { - DCHECK(!native_unwinder); + DCHECK(core_unwinders.empty()); #if defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64) + core_unwinders.push_back(std::make_unique<NativeUnwinderWin>()); return std::make_unique<StackSamplerImpl>( std::make_unique<StackCopierSuspend>( std::make_unique<SuspendableThreadDelegateWin>(thread_token)), - std::make_unique<NativeUnwinderWin>(), module_cache, test_delegate); + std::move(core_unwinders), module_cache, test_delegate); #else return nullptr; #endif diff --git a/chromium/base/profiler/stack_sampling_profiler.cc b/chromium/base/profiler/stack_sampling_profiler.cc index 46634d18aea..f0ad057d521 100644 --- a/chromium/base/profiler/stack_sampling_profiler.cc +++ b/chromium/base/profiler/stack_sampling_profiler.cc @@ -5,6 +5,7 @@ #include "base/profiler/stack_sampling_profiler.h" #include <algorithm> +#include <cmath> #include <map> #include <utility> @@ -26,7 +27,7 @@ #include "base/threading/thread_restrictions.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" -#include "base/trace_event/trace_event.h" +#include "base/trace_event/base_tracing.h" namespace base { @@ -47,6 +48,36 @@ constexpr WaitableEvent::ResetPolicy kResetPolicy = // for referencing the active collection to the SamplingThread. const int kNullProfilerId = -1; +TimeTicks GetNextSampleTimeImpl(TimeTicks scheduled_current_sample_time, + TimeDelta sampling_interval, + TimeTicks now) { + // Schedule the next sample at the next sampling_interval-aligned time in + // the future that's sufficiently far enough from the current sample. In the + // general case this will be one sampling_interval from the current + // sample. In cases where sample tasks were unable to be executed, such as + // during system suspend or bad system-wide jank, we may have missed some + // samples. The right thing to do for those cases is to skip the missed + // samples since the rest of the systems also wasn't executing. + + // Ensure that the next sample time is at least half a sampling interval + // away. This causes the second sample after resume to be taken between 0.5 + // and 1.5 samples after the first, or 1 sample interval on average. The delay + // also serves to provide a grace period in the normal sampling case where the + // current sample may be taken slightly later than its scheduled time. + const TimeTicks earliest_next_sample_time = now + sampling_interval / 2; + + const TimeDelta minimum_time_delta_to_next_sample = + earliest_next_sample_time - scheduled_current_sample_time; + + // The minimum number of sampling intervals required to get from the scheduled + // current sample time to the earliest next sample time. + const int64_t required_sampling_intervals = static_cast<int64_t>( + std::ceil(minimum_time_delta_to_next_sample.InMicrosecondsF() / + sampling_interval.InMicroseconds())); + return scheduled_current_sample_time + + required_sampling_intervals * sampling_interval; +} + } // namespace // StackSamplingProfiler::SamplingThread -------------------------------------- @@ -597,9 +628,9 @@ void StackSamplingProfiler::SamplingThread::RecordSampleTask( // Schedule the next sample recording if there is one. if (++collection->sample_count < collection->params.samples_per_profile) { - if (!collection->params.keep_consistent_sampling_interval) - collection->next_sample_time = TimeTicks::Now(); - collection->next_sample_time += collection->params.sampling_interval; + collection->next_sample_time = GetNextSampleTimeImpl( + collection->next_sample_time, collection->params.sampling_interval, + TimeTicks::Now()); bool success = GetTaskRunnerOnSamplingThread()->PostDelayedTask( FROM_HERE, BindOnce(&SamplingThread::RecordSampleTask, Unretained(this), @@ -689,16 +720,25 @@ void StackSamplingProfiler::TestPeer::PerformSamplingThreadIdleShutdown( SamplingThread::TestPeer::ShutdownAssumingIdle(simulate_intervening_start); } +// static +TimeTicks StackSamplingProfiler::TestPeer::GetNextSampleTime( + TimeTicks scheduled_current_sample_time, + TimeDelta sampling_interval, + TimeTicks now) { + return GetNextSampleTimeImpl(scheduled_current_sample_time, sampling_interval, + now); +} + StackSamplingProfiler::StackSamplingProfiler( SamplingProfilerThreadToken thread_token, const SamplingParams& params, std::unique_ptr<ProfileBuilder> profile_builder, - std::unique_ptr<Unwinder> native_unwinder, + std::vector<std::unique_ptr<Unwinder>> unwinders, StackSamplerTestDelegate* test_delegate) : StackSamplingProfiler(params, std::move(profile_builder), nullptr) { sampler_ = StackSampler::Create(thread_token, profile_builder_->GetModuleCache(), - std::move(native_unwinder), test_delegate); + std::move(unwinders), test_delegate); } StackSamplingProfiler::StackSamplingProfiler( @@ -753,9 +793,6 @@ void StackSamplingProfiler::Start() { if (!sampler_) return; - if (pending_aux_unwinder_) - sampler_->AddAuxUnwinder(std::move(pending_aux_unwinder_)); - // The IsSignaled() check below requires that the WaitableEvent be manually // reset, to avoid signaling the event in IsSignaled() itself. static_assert(kResetPolicy == WaitableEvent::ResetPolicy::MANUAL, @@ -789,9 +826,10 @@ void StackSamplingProfiler::Stop() { void StackSamplingProfiler::AddAuxUnwinder(std::unique_ptr<Unwinder> unwinder) { if (profiler_id_ == kNullProfilerId) { - // We haven't started sampling, and so don't have a sampler to which we can - // pass the unwinder yet. Save it on the instance until we do. - pending_aux_unwinder_ = std::move(unwinder); + // We haven't started sampling, and so we can add |unwinder| to the sampler + // directly + if (sampler_) + sampler_->AddAuxUnwinder(std::move(unwinder)); return; } diff --git a/chromium/base/profiler/stack_sampling_profiler.h b/chromium/base/profiler/stack_sampling_profiler.h index c6784b8a8f4..1171cda73fc 100644 --- a/chromium/base/profiler/stack_sampling_profiler.h +++ b/chromium/base/profiler/stack_sampling_profiler.h @@ -72,30 +72,22 @@ class BASE_EXPORT StackSamplingProfiler { // Interval between samples during a sampling profile. This is the desired // duration from the start of one sample to the start of the next sample. TimeDelta sampling_interval = TimeDelta::FromMilliseconds(100); - - // When true, keeps the average sampling interval = |sampling_interval|, - // irrespective of how long each sample takes. If a sample takes too long, - // keeping the interval constant will lock out the sampled thread. When - // false, sample is created with an interval of |sampling_interval|, - // excluding the time taken by a sample. The metrics collected will not be - // accurate, since sampling could take arbitrary amount of time, but makes - // sure that the sampled thread gets at least the interval amount of time to - // run between samples. - bool keep_consistent_sampling_interval = true; }; - // Creates a profiler for the specified thread. |native_unwinder| is required - // on Android since the unwinder is provided outside StackSamplingProfiler, - // but must be null on other platforms. An optional |test_delegate| can be - // supplied by tests. + // Creates a profiler for the specified thread. |unwinders| is required on + // Android since the unwinder is provided outside StackSamplingProfiler, but + // must be empty on other platforms. When attempting to unwind, the relative + // priority of unwinders is the inverse of the order in |unwinders|. An + // optional |test_delegate| can be supplied by tests. // // The caller must ensure that this object gets destroyed before the thread // exits. - StackSamplingProfiler(SamplingProfilerThreadToken thread_token, - const SamplingParams& params, - std::unique_ptr<ProfileBuilder> profile_builder, - std::unique_ptr<Unwinder> native_unwinder = nullptr, - StackSamplerTestDelegate* test_delegate = nullptr); + StackSamplingProfiler( + SamplingProfilerThreadToken thread_token, + const SamplingParams& params, + std::unique_ptr<ProfileBuilder> profile_builder, + std::vector<std::unique_ptr<Unwinder>> core_unwinders = {}, + StackSamplerTestDelegate* test_delegate = nullptr); // Same as above function, with custom |sampler| implementation. The sampler // on Android is not implemented in base. @@ -153,6 +145,11 @@ class BASE_EXPORT StackSamplingProfiler { // runs. static void PerformSamplingThreadIdleShutdown( bool simulate_intervening_start); + + // Provides access to the method computing the next sample time. + static TimeTicks GetNextSampleTime(TimeTicks scheduled_current_sample_time, + TimeDelta sampling_interval, + TimeTicks now); }; private: @@ -191,10 +188,6 @@ class BASE_EXPORT StackSamplingProfiler { // thread sampling starts. std::unique_ptr<StackSampler> sampler_; - // If an AuxUnwinder is added before Start() it will be saved here until it - // can be passed to the sampling thread when thread sampling starts. - std::unique_ptr<Unwinder> pending_aux_unwinder_; - // This starts "signaled", is reset when sampling begins, and is signaled // when that sampling is complete and the profile_builder_'s // OnProfileCompleted function has executed. diff --git a/chromium/base/profiler/stack_sampling_profiler_test_util.cc b/chromium/base/profiler/stack_sampling_profiler_test_util.cc index 9043d0627be..3ea67f52672 100644 --- a/chromium/base/profiler/stack_sampling_profiler_test_util.cc +++ b/chromium/base/profiler/stack_sampling_profiler_test_util.cc @@ -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,67 @@ 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( + uintptr_t chrome_module_base_address) { + 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, + uintptr_t chrome_module_base_address) + : ChromeUnwinderAndroid(cfi_table.get(), chrome_module_base_address), + 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), chrome_module_base_address); +} +#endif // #if defined(OS_ANDROID) && BUILDFLAG(ENABLE_ARM_CFI_TABLE) + } // namespace TargetThread::TargetThread(OnceClosure to_run) : to_run_(std::move(to_run)) {} @@ -240,7 +315,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 +414,18 @@ 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( + reinterpret_cast<uintptr_t>(&__executable_start))); + return unwinders; +#else + return {}; +#endif +} + } // namespace base diff --git a/chromium/base/profiler/stack_sampling_profiler_test_util.h b/chromium/base/profiler/stack_sampling_profiler_test_util.h index ff2daebfe14..4337393ff73 100644 --- a/chromium/base/profiler/stack_sampling_profiler_test_util.h +++ b/chromium/base/profiler/stack_sampling_profiler_test_util.h @@ -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_ diff --git a/chromium/base/profiler/stack_sampling_profiler_unittest.cc b/chromium/base/profiler/stack_sampling_profiler_unittest.cc index b9fc305dd91..dc011172aea 100644 --- a/chromium/base/profiler/stack_sampling_profiler_unittest.cc +++ b/chromium/base/profiler/stack_sampling_profiler_unittest.cc @@ -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(); })), - nullptr, + 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(); })), - nullptr, &test_delegate); + CreateCoreUnwindersForTesting(module_cache), &test_delegate); profiler.Start(); @@ -488,10 +489,12 @@ 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)) -#define MAYBE_Alloca Alloca -#else +// 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 DISABLED_Alloca +#else +#define MAYBE_Alloca Alloca #endif PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_Alloca) { UnwindScenario scenario(BindRepeating(&CallWithAlloca)); @@ -506,10 +509,13 @@ PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_Alloca) { // Checks that a stack that runs through another library produces a stack with // the expected functions. // macOS ASAN is not yet supported - crbug.com/718628. -#if !(defined(ADDRESS_SANITIZER) && defined(OS_MACOSX)) -#define MAYBE_OtherLibrary OtherLibrary -#else +// Android is not supported when EXCLUDE_UNWIND_TABLES |other_library| doesn't +// have unwind tables. +#if (defined(ADDRESS_SANITIZER) && defined(OS_MACOSX)) || \ + (defined(OS_ANDROID) && BUILDFLAG(EXCLUDE_UNWIND_TABLES)) #define MAYBE_OtherLibrary DISABLED_OtherLibrary +#else +#define MAYBE_OtherLibrary OtherLibrary #endif PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_OtherLibrary) { ScopedNativeLibrary other_library(LoadOtherLibrary()); @@ -526,10 +532,13 @@ PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_OtherLibrary) { // Checks that a stack that runs through a library that is unloading produces a // stack, and doesn't crash. // Unloading is synchronous on the Mac, so this test is inapplicable. -#if !defined(OS_MACOSX) -#define MAYBE_UnloadingLibrary UnloadingLibrary -#else +// Android is not supported when EXCLUDE_UNWIND_TABLES |other_library| doesn't +// have unwind tables. +#if defined(OS_MACOSX) || \ + (defined(OS_ANDROID) && BUILDFLAG(EXCLUDE_UNWIND_TABLES)) #define MAYBE_UnloadingLibrary DISABLED_UnloadingLibrary +#else +#define MAYBE_UnloadingLibrary UnloadingLibrary #endif PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_UnloadingLibrary) { TestLibraryUnload(false, module_cache()); @@ -538,10 +547,11 @@ 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)) -#define MAYBE_UnloadedLibrary UnloadedLibrary -#else +// Android is not supported since modules are found before unwinding. +#if (defined(ADDRESS_SANITIZER) && defined(OS_MACOSX)) || defined(OS_ANDROID) #define MAYBE_UnloadedLibrary DISABLED_UnloadedLibrary +#else +#define MAYBE_UnloadedLibrary UnloadedLibrary #endif PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_UnloadedLibrary) { TestLibraryUnload(true, module_cache()); @@ -568,7 +578,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()); @@ -706,6 +717,73 @@ PROFILER_TEST_F(StackSamplingProfilerTest, StopDuringInterSampleInterval) { })); } +PROFILER_TEST_F(StackSamplingProfilerTest, GetNextSampleTime_NormalExecution) { + const auto& GetNextSampleTime = + StackSamplingProfiler::TestPeer::GetNextSampleTime; + + const TimeTicks scheduled_current_sample_time = TimeTicks::UnixEpoch(); + const TimeDelta sampling_interval = TimeDelta::FromMilliseconds(10); + + // When executing the sample at exactly the scheduled time the next sample + // should be one interval later. + EXPECT_EQ(scheduled_current_sample_time + sampling_interval, + GetNextSampleTime(scheduled_current_sample_time, sampling_interval, + scheduled_current_sample_time)); + + // When executing the sample less than half an interval after the scheduled + // time the next sample also should be one interval later. + EXPECT_EQ(scheduled_current_sample_time + sampling_interval, + GetNextSampleTime( + scheduled_current_sample_time, sampling_interval, + scheduled_current_sample_time + 0.4 * sampling_interval)); + + // When executing the sample less than half an interval before the scheduled + // time the next sample also should be one interval later. This is not + // expected to occur in practice since delayed tasks never run early. + EXPECT_EQ(scheduled_current_sample_time + sampling_interval, + GetNextSampleTime( + scheduled_current_sample_time, sampling_interval, + scheduled_current_sample_time - 0.4 * sampling_interval)); +} + +PROFILER_TEST_F(StackSamplingProfilerTest, GetNextSampleTime_DelayedExecution) { + const auto& GetNextSampleTime = + StackSamplingProfiler::TestPeer::GetNextSampleTime; + + const TimeTicks scheduled_current_sample_time = TimeTicks::UnixEpoch(); + const TimeDelta sampling_interval = TimeDelta::FromMilliseconds(10); + + // When executing the sample between 0.5 and 1.5 intervals after the scheduled + // time the next sample should be two intervals later. + EXPECT_EQ(scheduled_current_sample_time + 2 * sampling_interval, + GetNextSampleTime( + scheduled_current_sample_time, sampling_interval, + scheduled_current_sample_time + 0.6 * sampling_interval)); + EXPECT_EQ(scheduled_current_sample_time + 2 * sampling_interval, + GetNextSampleTime( + scheduled_current_sample_time, sampling_interval, + scheduled_current_sample_time + 1.0 * sampling_interval)); + EXPECT_EQ(scheduled_current_sample_time + 2 * sampling_interval, + GetNextSampleTime( + scheduled_current_sample_time, sampling_interval, + scheduled_current_sample_time + 1.4 * sampling_interval)); + + // Similarly when executing the sample between 9.5 and 10.5 intervals after + // the scheduled time the next sample should be 11 intervals later. + EXPECT_EQ(scheduled_current_sample_time + 11 * sampling_interval, + GetNextSampleTime( + scheduled_current_sample_time, sampling_interval, + scheduled_current_sample_time + 9.6 * sampling_interval)); + EXPECT_EQ(scheduled_current_sample_time + 11 * sampling_interval, + GetNextSampleTime( + scheduled_current_sample_time, sampling_interval, + scheduled_current_sample_time + 10.0 * sampling_interval)); + EXPECT_EQ(scheduled_current_sample_time + 11 * sampling_interval, + GetNextSampleTime( + scheduled_current_sample_time, sampling_interval, + scheduled_current_sample_time + 10.4 * sampling_interval)); +} + // Checks that we can destroy the profiler while profiling. PROFILER_TEST_F(StackSamplingProfilerTest, DestroyProfilerWhileProfiling) { SamplingParams params; @@ -720,8 +798,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(); @@ -1072,7 +1151,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, @@ -1085,7 +1165,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(); @@ -1120,8 +1201,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(); @@ -1206,7 +1287,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(); @@ -1246,7 +1328,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))); @@ -1286,7 +1369,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( @@ -1360,7 +1444,8 @@ PROFILER_TEST_F(StackSamplingProfilerTest, BindLambdaForTesting([&profile](Profile result_profile) { profile = std::move(result_profile); })), - nullptr, &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) diff --git a/chromium/base/profiler/unwindstack_internal_android.cc b/chromium/base/profiler/unwindstack_internal_android.cc deleted file mode 100644 index 92328590aa4..00000000000 --- a/chromium/base/profiler/unwindstack_internal_android.cc +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/profiler/unwindstack_internal_android.h" - -#include <string.h> - -#include "base/logging.h" - -namespace base { - -UnwindStackMemoryAndroid::UnwindStackMemoryAndroid(uintptr_t stack_ptr, - uintptr_t stack_top) - : stack_ptr_(stack_ptr), stack_top_(stack_top) { - DCHECK_LE(stack_ptr_, stack_top_); -} - -UnwindStackMemoryAndroid::~UnwindStackMemoryAndroid() = default; - -size_t UnwindStackMemoryAndroid::Read(uint64_t addr, void* dst, size_t size) { - if (addr < stack_ptr_) - return 0; - if (size >= stack_top_ || addr > stack_top_ - size) - return 0; - memcpy(dst, reinterpret_cast<void*>(addr), size); - return size; -} - -} // namespace base
\ No newline at end of file diff --git a/chromium/base/profiler/unwindstack_internal_android.h b/chromium/base/profiler/unwindstack_internal_android.h deleted file mode 100644 index 75058613fc7..00000000000 --- a/chromium/base/profiler/unwindstack_internal_android.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_PROFILER_UNWINDSTACK_INTERNAL_ANDROID_H_ -#define BASE_PROFILER_UNWINDSTACK_INTERNAL_ANDROID_H_ - -#include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/Maps.h" -#include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/Memory.h" - -// Avoid including this file directly in a header as it leaks headers from -// libunwindstack. In particular, it's not to be included directly or -// transitively from native_unwinder_android.h - -namespace base { - -// Implementation of unwindstack::Memory that restricts memory access to a stack -// buffer, used by NativeUnwinderAndroid. While unwinding, only memory accesses -// within the stack should be performed to restore registers. -class UnwindStackMemoryAndroid : public unwindstack::Memory { - public: - UnwindStackMemoryAndroid(uintptr_t stack_ptr, uintptr_t stack_top); - ~UnwindStackMemoryAndroid() override; - - size_t Read(uint64_t addr, void* dst, size_t size) override; - - private: - const uintptr_t stack_ptr_; - const uintptr_t stack_top_; -}; - -} // namespace base - -#endif // BASE_PROFILER_UNWINDSTACK_INTERNAL_ANDROID_H_
\ No newline at end of file diff --git a/chromium/base/profiler/win32_stack_frame_unwinder.cc b/chromium/base/profiler/win32_stack_frame_unwinder.cc index 700e747957c..553c1e3d55d 100644 --- a/chromium/base/profiler/win32_stack_frame_unwinder.cc +++ b/chromium/base/profiler/win32_stack_frame_unwinder.cc @@ -9,6 +9,7 @@ #include <utility> #include "base/macros.h" +#include "base/notreached.h" #include "build/build_config.h" namespace base { |