summaryrefslogtreecommitdiff
path: root/chromium/base/profiler
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-12 14:27:29 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-13 09:35:20 +0000
commitc30a6232df03e1efbd9f3b226777b07e087a1122 (patch)
treee992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/base/profiler
parent7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff)
downloadqtwebengine-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')
-rw-r--r--chromium/base/profiler/chrome_unwinder_android.cc9
-rw-r--r--chromium/base/profiler/chrome_unwinder_android.h4
-rw-r--r--chromium/base/profiler/chrome_unwinder_android_unittest.cc12
-rw-r--r--chromium/base/profiler/metadata_recorder.cc5
-rw-r--r--chromium/base/profiler/metadata_recorder.h6
-rw-r--r--chromium/base/profiler/native_unwinder_android.cc90
-rw-r--r--chromium/base/profiler/native_unwinder_android.h34
-rw-r--r--chromium/base/profiler/native_unwinder_android_unittest.cc176
-rw-r--r--chromium/base/profiler/stack_copier_signal.cc3
-rw-r--r--chromium/base/profiler/stack_copier_unittest.cc1
-rw-r--r--chromium/base/profiler/stack_sampler.h14
-rw-r--r--chromium/base/profiler/stack_sampler_android.cc8
-rw-r--r--chromium/base/profiler/stack_sampler_impl.cc37
-rw-r--r--chromium/base/profiler/stack_sampler_impl.h2
-rw-r--r--chromium/base/profiler/stack_sampler_impl_unittest.cc42
-rw-r--r--chromium/base/profiler/stack_sampler_ios.cc2
-rw-r--r--chromium/base/profiler/stack_sampler_mac.cc8
-rw-r--r--chromium/base/profiler/stack_sampler_posix.cc2
-rw-r--r--chromium/base/profiler/stack_sampler_win.cc7
-rw-r--r--chromium/base/profiler/stack_sampling_profiler.cc62
-rw-r--r--chromium/base/profiler/stack_sampling_profiler.h39
-rw-r--r--chromium/base/profiler/stack_sampling_profiler_test_util.cc92
-rw-r--r--chromium/base/profiler/stack_sampling_profiler_test_util.h8
-rw-r--r--chromium/base/profiler/stack_sampling_profiler_unittest.cc141
-rw-r--r--chromium/base/profiler/unwindstack_internal_android.cc30
-rw-r--r--chromium/base/profiler/unwindstack_internal_android.h34
-rw-r--r--chromium/base/profiler/win32_stack_frame_unwinder.cc1
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, &timestamp, &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 {