summaryrefslogtreecommitdiff
path: root/chromium/base/profiler
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-06 12:48:11 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-13 09:33:43 +0000
commit7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (patch)
treefa14ba0ca8d2683ba2efdabd246dc9b18a1229c6 /chromium/base/profiler
parent79b4f909db1049fca459c07cca55af56a9b54fe3 (diff)
downloadqtwebengine-chromium-7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3.tar.gz
BASELINE: Update Chromium to 84.0.4147.141
Change-Id: Ib85eb4cfa1cbe2b2b81e5022c8cad5c493969535 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/base/profiler')
-rw-r--r--chromium/base/profiler/metadata_recorder.cc49
-rw-r--r--chromium/base/profiler/metadata_recorder.h111
-rw-r--r--chromium/base/profiler/metadata_recorder_unittest.cc78
-rw-r--r--chromium/base/profiler/module_cache_mac.cc117
-rw-r--r--chromium/base/profiler/module_cache_stub.cc15
-rw-r--r--chromium/base/profiler/native_unwinder_android.cc209
-rw-r--r--chromium/base/profiler/native_unwinder_android.h37
-rw-r--r--chromium/base/profiler/native_unwinder_android_unittest.cc365
-rw-r--r--chromium/base/profiler/native_unwinder_mac.cc3
-rw-r--r--chromium/base/profiler/native_unwinder_win.cc2
-rw-r--r--chromium/base/profiler/profile_builder.cc23
-rw-r--r--chromium/base/profiler/profile_builder.h38
-rw-r--r--chromium/base/profiler/register_context.h2
-rw-r--r--chromium/base/profiler/sample_metadata.cc35
-rw-r--r--chromium/base/profiler/sample_metadata.h79
-rw-r--r--chromium/base/profiler/sample_metadata_unittest.cc70
-rw-r--r--chromium/base/profiler/stack_copier.h3
-rw-r--r--chromium/base/profiler/stack_copier_signal.cc2
-rw-r--r--chromium/base/profiler/stack_copier_signal_unittest.cc13
-rw-r--r--chromium/base/profiler/stack_copier_suspend.cc2
-rw-r--r--chromium/base/profiler/stack_copier_suspend_unittest.cc13
-rw-r--r--chromium/base/profiler/stack_sampler.h8
-rw-r--r--chromium/base/profiler/stack_sampler_android.cc6
-rw-r--r--chromium/base/profiler/stack_sampler_impl.cc118
-rw-r--r--chromium/base/profiler/stack_sampler_impl.h25
-rw-r--r--chromium/base/profiler/stack_sampler_impl_unittest.cc62
-rw-r--r--chromium/base/profiler/stack_sampler_ios.cc4
-rw-r--r--chromium/base/profiler/stack_sampler_mac.cc5
-rw-r--r--chromium/base/profiler/stack_sampler_posix.cc4
-rw-r--r--chromium/base/profiler/stack_sampler_win.cc5
-rw-r--r--chromium/base/profiler/stack_sampling_profiler.cc8
-rw-r--r--chromium/base/profiler/stack_sampling_profiler.h8
-rw-r--r--chromium/base/profiler/stack_sampling_profiler_test_util.cc94
-rw-r--r--chromium/base/profiler/stack_sampling_profiler_test_util.h18
-rw-r--r--chromium/base/profiler/stack_sampling_profiler_unittest.cc101
-rw-r--r--chromium/base/profiler/suspendable_thread_delegate_mac.cc2
-rw-r--r--chromium/base/profiler/suspendable_thread_delegate_win.cc2
-rw-r--r--chromium/base/profiler/unwindstack_internal_android.cc30
-rw-r--r--chromium/base/profiler/unwindstack_internal_android.h34
39 files changed, 1245 insertions, 555 deletions
diff --git a/chromium/base/profiler/metadata_recorder.cc b/chromium/base/profiler/metadata_recorder.cc
index fd171d4dd6e..a6e3cc22dde 100644
--- a/chromium/base/profiler/metadata_recorder.cc
+++ b/chromium/base/profiler/metadata_recorder.cc
@@ -8,6 +8,20 @@
namespace base {
+const size_t MetadataRecorder::MAX_METADATA_COUNT;
+
+MetadataRecorder::Item::Item(uint64_t name_hash,
+ Optional<int64_t> key,
+ int64_t value)
+ : name_hash(name_hash), key(key), value(value) {}
+
+MetadataRecorder::Item::Item() : name_hash(0), value(0) {}
+
+MetadataRecorder::Item::Item(const Item& other) = default;
+
+MetadataRecorder::Item& MetadataRecorder::Item::Item::operator=(
+ const Item& other) = default;
+
MetadataRecorder::ItemInternal::ItemInternal() = default;
MetadataRecorder::ItemInternal::~ItemInternal() = default;
@@ -90,30 +104,22 @@ void MetadataRecorder::Remove(uint64_t name_hash, Optional<int64_t> key) {
}
}
-MetadataRecorder::ScopedGetItems::ScopedGetItems(
+MetadataRecorder::MetadataProvider::MetadataProvider(
MetadataRecorder* metadata_recorder)
: metadata_recorder_(metadata_recorder),
- auto_lock_(&metadata_recorder->read_lock_) {}
-
-MetadataRecorder::ScopedGetItems::~ScopedGetItems() {}
-
-// This function is marked as NO_THREAD_SAFETY_ANALYSIS because the analyzer
-// doesn't understand that the lock is acquired in the constructor initializer
-// list and can therefore be safely released here.
-size_t MetadataRecorder::ScopedGetItems::GetItems(
- ProfileBuilder::MetadataItemArray* const items) NO_THREAD_SAFETY_ANALYSIS {
- size_t item_count = metadata_recorder_->GetItems(items);
- auto_lock_.Release();
- return item_count;
-}
+ auto_lock_(metadata_recorder->read_lock_) {}
+
+MetadataRecorder::MetadataProvider::~MetadataProvider() = default;
-std::unique_ptr<ProfileBuilder::MetadataProvider>
-MetadataRecorder::CreateMetadataProvider() {
- return std::make_unique<MetadataRecorder::ScopedGetItems>(this);
+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(
- ProfileBuilder::MetadataItemArray* const items) const {
+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
@@ -134,7 +140,7 @@ size_t MetadataRecorder::GetItems(
// Because we wait until |is_active| is set to consider an item active and
// that field is always set last, we ignore half-created items.
if (item.is_active.load(std::memory_order_acquire)) {
- (*items)[write_index++] = ProfileBuilder::MetadataItem{
+ (*items)[write_index++] = Item{
item.name_hash, item.key, item.value.load(std::memory_order_relaxed)};
}
}
@@ -143,8 +149,7 @@ size_t MetadataRecorder::GetItems(
}
size_t MetadataRecorder::TryReclaimInactiveSlots(size_t item_slots_used) {
- const size_t remaining_slots =
- ProfileBuilder::MAX_METADATA_COUNT - item_slots_used;
+ const size_t remaining_slots = MAX_METADATA_COUNT - item_slots_used;
if (inactive_item_count_ == 0 || inactive_item_count_ < remaining_slots) {
// This reclaiming threshold has a few nice properties:
diff --git a/chromium/base/profiler/metadata_recorder.h b/chromium/base/profiler/metadata_recorder.h
index d85a3d978c4..6e120dd40b7 100644
--- a/chromium/base/profiler/metadata_recorder.h
+++ b/chromium/base/profiler/metadata_recorder.h
@@ -10,7 +10,6 @@
#include <utility>
#include "base/optional.h"
-#include "base/profiler/profile_builder.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
@@ -54,11 +53,11 @@ namespace base {
// allows readers to preallocate the data structure that we pass back
// the metadata in.
//
-// C) We shouldn't guard writes with a lock that also guards reads. It can take
-// ~30us from the time that the sampling thread requests that a thread be
-// suspended and the time that it actually happens. If all metadata writes
-// block their thread during that time, we're very likely to block all Chrome
-// threads for an additional 30us per sample.
+// C) We shouldn't guard writes with a lock that also guards reads, since the
+// read lock is held from the time that the sampling thread requests that a
+// thread be suspended up to the time that the thread is resumed. If all
+// metadata writes block their thread during that time, we're very likely to
+// block all Chrome threads.
//
// Ramifications:
//
@@ -94,8 +93,8 @@ namespace base {
//
// - No thread is using the recorder.
//
-// - A single writer is writing into the recorder without a simultaneous
-// read. The write will succeed.
+// - A single writer is writing into the recorder without a simultaneous read.
+// The write will succeed.
//
// - A reader is reading from the recorder without a simultaneous write. The
// read will succeed.
@@ -128,6 +127,23 @@ class BASE_EXPORT MetadataRecorder {
MetadataRecorder(const MetadataRecorder&) = delete;
MetadataRecorder& operator=(const MetadataRecorder&) = delete;
+ struct BASE_EXPORT Item {
+ Item(uint64_t name_hash, Optional<int64_t> key, int64_t value);
+ Item();
+
+ Item(const Item& other);
+ Item& operator=(const Item& other);
+
+ // The hash of the metadata name, as produced by HashMetricName().
+ uint64_t name_hash;
+ // The key if specified when setting the item.
+ Optional<int64_t> key;
+ // The value of the metadata item.
+ int64_t value;
+ };
+ static constexpr size_t MAX_METADATA_COUNT = 50;
+ typedef std::array<Item, MAX_METADATA_COUNT> ItemArray;
+
// Sets a value for a (|name_hash|, |key|) pair, overwriting any value
// previously set for the pair. Nullopt keys are treated as just another key
// state for the purpose of associating values.
@@ -137,62 +153,49 @@ class BASE_EXPORT MetadataRecorder {
// effect if such an item does not exist.
void Remove(uint64_t name_hash, Optional<int64_t> key);
- // Creates a MetadataProvider object for the recorder, which acquires the
- // necessary exclusive read lock and provides access to the recorder's items
- // via its GetItems() function. Reclaiming of inactive slots in the recorder
- // can't occur while this object lives, so it should be created as soon before
- // it's needed as possible. Calling GetItems() releases the lock held by the
- // object and can therefore only be called once during the object's lifetime.
+ // An object that provides access to a MetadataRecorder's items and holds the
+ // necessary exclusive read lock until the object is destroyed. Reclaiming of
+ // inactive slots in the recorder can't occur while this object lives, so it
+ // should be created as soon before it's needed as possible and released as
+ // soon as possible.
//
- // This object should be created *before* suspending the target
- // thread. Otherwise, that thread might be suspended while reclaiming inactive
- // slots and holding the read lock, which would cause the sampling thread to
- // deadlock.
+ // This object should be created *before* suspending the target thread and
+ // destroyed after resuming the target thread. Otherwise, that thread might be
+ // suspended while reclaiming inactive slots and holding the read lock, which
+ // would cause the sampling thread to deadlock.
//
// Example usage:
//
// MetadataRecorder r;
- // base::ProfileBuilder::MetadataItemArray arr;
+ // base::MetadataRecorder::ItemArray arr;
// size_t item_count;
// ...
// {
- // auto get_items = r.CreateMetadataProvider();
- // item_count = get_items.GetItems(arr);
+ // MetadtaRecorder::MetadataProvider provider;
+ // item_count = provider.GetItems(arr);
// }
- std::unique_ptr<ProfileBuilder::MetadataProvider> CreateMetadataProvider();
-
- private:
- // An object that provides access to a MetadataRecorder's items and holds the
- // necessary exclusive read lock until either GetItems() is called or the
- // object is destroyed.
- //
- // For usage and more details, see CreateMetadataProvider().
- class SCOPED_LOCKABLE ScopedGetItems
- : public ProfileBuilder::MetadataProvider {
+ class SCOPED_LOCKABLE BASE_EXPORT MetadataProvider {
public:
// Acquires an exclusive read lock on the metadata recorder which is held
- // until either GetItems() is called or the object is destroyed.
- ScopedGetItems(MetadataRecorder* metadata_recorder)
- EXCLUSIVE_LOCK_FUNCTION(metadata_recorder->read_lock_);
- ~ScopedGetItems() override UNLOCK_FUNCTION(metadata_recorder_->read_lock_);
- ScopedGetItems(const ScopedGetItems&) = delete;
- ScopedGetItems& operator=(const ScopedGetItems&) = delete;
+ // until the object is destroyed.
+ explicit MetadataProvider(MetadataRecorder* metadata_recorder)
+ EXCLUSIVE_LOCK_FUNCTION(metadata_recorder_->read_lock_);
+ ~MetadataProvider() UNLOCK_FUNCTION();
+ MetadataProvider(const MetadataProvider&) = delete;
+ MetadataProvider& operator=(const MetadataProvider&) = delete;
// 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|.
- //
- // This function releases the lock held by the object and can therefore only
- // be called once during the object's lifetime.
- size_t GetItems(ProfileBuilder::MetadataItemArray* const items) override
- EXCLUSIVE_LOCKS_REQUIRED(metadata_recorder_->read_lock_);
+ size_t GetItems(ItemArray* const items) const;
private:
const MetadataRecorder* const metadata_recorder_;
- base::ReleasableAutoLock auto_lock_;
+ base::AutoLock auto_lock_;
};
+ private:
// TODO(charliea): Support large quantities of metadata efficiently.
struct ItemInternal {
ItemInternal();
@@ -228,17 +231,14 @@ class BASE_EXPORT MetadataRecorder {
// after the reclamation.
size_t TryReclaimInactiveSlots(size_t item_slots_used)
EXCLUSIVE_LOCKS_REQUIRED(write_lock_) LOCKS_EXCLUDED(read_lock_);
- // Also protected by read_lock_, but current thread annotation limitations
- // prevent us from using thread annotations with locks acquired through
- // Lock::Try(). Updates item_slots_used_ to reflect the new item count and
- // returns the number of item slots used after the reclamation.
+ // Updates item_slots_used_ to reflect the new item count and returns the
+ // number of item slots used after the reclamation.
size_t ReclaimInactiveSlots(size_t item_slots_used)
- EXCLUSIVE_LOCKS_REQUIRED(write_lock_);
+ EXCLUSIVE_LOCKS_REQUIRED(write_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(read_lock_);
- // Protected by read_lock_, but current thread annotation limitations
- // prevent us from using thread annotations with locks acquired through
- // Lock::Try().
- size_t GetItems(ProfileBuilder::MetadataItemArray* const items) const;
+ size_t GetItems(ItemArray* const items) const
+ EXCLUSIVE_LOCKS_REQUIRED(read_lock_);
// Metadata items that the recorder has seen. Rather than implementing the
// metadata recorder as a dense array, we implement it as a sparse array where
@@ -248,7 +248,7 @@ class BASE_EXPORT MetadataRecorder {
//
// For the rationale behind this design (along with others considered), see
// https://docs.google.com/document/d/18shLhVwuFbLl_jKZxCmOfRB98FmNHdKl0yZZZ3aEO4U/edit#.
- std::array<ItemInternal, ProfileBuilder::MAX_METADATA_COUNT> items_;
+ std::array<ItemInternal, MAX_METADATA_COUNT> items_;
// The number of item slots used in the metadata map.
//
@@ -267,11 +267,6 @@ class BASE_EXPORT MetadataRecorder {
// A lock that guards against a reader trying to read items_ while inactive
// slots are being reclaimed.
- //
- // Note that we can't enforce that this lock is properly acquired through
- // thread annotations because thread annotations doesn't understand that
- // ScopedGetItems::GetItems() can only be called between ScopedGetItems's
- // constructor and destructor.
base::Lock read_lock_;
};
diff --git a/chromium/base/profiler/metadata_recorder_unittest.cc b/chromium/base/profiler/metadata_recorder_unittest.cc
index 79abb21a0de..4aff812a044 100644
--- a/chromium/base/profiler/metadata_recorder_unittest.cc
+++ b/chromium/base/profiler/metadata_recorder_unittest.cc
@@ -11,21 +11,22 @@
namespace base {
-bool operator==(const ProfileBuilder::MetadataItem& lhs,
- const ProfileBuilder::MetadataItem& rhs) {
+bool operator==(const MetadataRecorder::Item& lhs,
+ const MetadataRecorder::Item& rhs) {
return lhs.name_hash == rhs.name_hash && lhs.value == rhs.value;
}
-bool operator<(const ProfileBuilder::MetadataItem& lhs,
- const ProfileBuilder::MetadataItem& rhs) {
+bool operator<(const MetadataRecorder::Item& lhs,
+ const MetadataRecorder::Item& rhs) {
return lhs.name_hash < rhs.name_hash;
}
TEST(MetadataRecorderTest, GetItems_Empty) {
MetadataRecorder recorder;
- ProfileBuilder::MetadataItemArray items;
+ MetadataRecorder::ItemArray items;
- size_t item_count = recorder.CreateMetadataProvider()->GetItems(&items);
+ size_t item_count =
+ MetadataRecorder::MetadataProvider(&recorder).GetItems(&items);
EXPECT_EQ(0u, item_count);
}
@@ -35,10 +36,10 @@ TEST(MetadataRecorderTest, Set_NewNameHash) {
recorder.Set(10, nullopt, 20);
- ProfileBuilder::MetadataItemArray items;
+ MetadataRecorder::ItemArray items;
size_t item_count;
{
- item_count = recorder.CreateMetadataProvider()->GetItems(&items);
+ item_count = MetadataRecorder::MetadataProvider(&recorder).GetItems(&items);
ASSERT_EQ(1u, item_count);
EXPECT_EQ(10u, items[0].name_hash);
EXPECT_FALSE(items[0].key.has_value());
@@ -48,7 +49,7 @@ TEST(MetadataRecorderTest, Set_NewNameHash) {
recorder.Set(20, nullopt, 30);
{
- item_count = recorder.CreateMetadataProvider()->GetItems(&items);
+ item_count = MetadataRecorder::MetadataProvider(&recorder).GetItems(&items);
ASSERT_EQ(2u, item_count);
EXPECT_EQ(20u, items[1].name_hash);
EXPECT_FALSE(items[1].key.has_value());
@@ -61,8 +62,9 @@ TEST(MetadataRecorderTest, Set_ExistingNameNash) {
recorder.Set(10, nullopt, 20);
recorder.Set(10, nullopt, 30);
- ProfileBuilder::MetadataItemArray items;
- size_t item_count = recorder.CreateMetadataProvider()->GetItems(&items);
+ MetadataRecorder::ItemArray items;
+ size_t item_count =
+ MetadataRecorder::MetadataProvider(&recorder).GetItems(&items);
ASSERT_EQ(1u, item_count);
EXPECT_EQ(10u, items[0].name_hash);
EXPECT_FALSE(items[0].key.has_value());
@@ -71,10 +73,10 @@ TEST(MetadataRecorderTest, Set_ExistingNameNash) {
TEST(MetadataRecorderTest, Set_ReAddRemovedNameNash) {
MetadataRecorder recorder;
- ProfileBuilder::MetadataItemArray items;
- std::vector<ProfileBuilder::MetadataItem> expected;
+ MetadataRecorder::ItemArray items;
+ std::vector<MetadataRecorder::Item> expected;
for (size_t i = 0; i < items.size(); ++i) {
- expected.push_back(ProfileBuilder::MetadataItem{i, nullopt, 0});
+ expected.push_back(MetadataRecorder::Item{i, nullopt, 0});
recorder.Set(i, nullopt, 0);
}
@@ -85,14 +87,15 @@ TEST(MetadataRecorderTest, Set_ReAddRemovedNameNash) {
recorder.Remove(3, nullopt);
recorder.Set(3, nullopt, 0);
- size_t item_count = recorder.CreateMetadataProvider()->GetItems(&items);
+ size_t item_count =
+ MetadataRecorder::MetadataProvider(&recorder).GetItems(&items);
EXPECT_EQ(items.size(), item_count);
EXPECT_THAT(expected, ::testing::UnorderedElementsAreArray(items));
}
TEST(MetadataRecorderTest, Set_AddPastMaxCount) {
MetadataRecorder recorder;
- ProfileBuilder::MetadataItemArray items;
+ MetadataRecorder::ItemArray items;
for (size_t i = 0; i < items.size(); ++i) {
recorder.Set(i, nullopt, 0);
}
@@ -106,10 +109,10 @@ TEST(MetadataRecorderTest, Set_NulloptKeyIsIndependentOfNonNulloptKey) {
recorder.Set(10, 100, 20);
- ProfileBuilder::MetadataItemArray items;
+ MetadataRecorder::ItemArray items;
size_t item_count;
{
- item_count = recorder.CreateMetadataProvider()->GetItems(&items);
+ item_count = MetadataRecorder::MetadataProvider(&recorder).GetItems(&items);
ASSERT_EQ(1u, item_count);
EXPECT_EQ(10u, items[0].name_hash);
ASSERT_TRUE(items[0].key.has_value());
@@ -120,7 +123,7 @@ TEST(MetadataRecorderTest, Set_NulloptKeyIsIndependentOfNonNulloptKey) {
recorder.Set(10, nullopt, 30);
{
- item_count = recorder.CreateMetadataProvider()->GetItems(&items);
+ item_count = MetadataRecorder::MetadataProvider(&recorder).GetItems(&items);
ASSERT_EQ(2u, item_count);
EXPECT_EQ(10u, items[0].name_hash);
@@ -141,8 +144,9 @@ TEST(MetadataRecorderTest, Remove) {
recorder.Set(50, nullopt, 60);
recorder.Remove(30, nullopt);
- ProfileBuilder::MetadataItemArray items;
- size_t item_count = recorder.CreateMetadataProvider()->GetItems(&items);
+ MetadataRecorder::ItemArray items;
+ size_t item_count =
+ MetadataRecorder::MetadataProvider(&recorder).GetItems(&items);
ASSERT_EQ(2u, item_count);
EXPECT_EQ(10u, items[0].name_hash);
EXPECT_FALSE(items[0].key.has_value());
@@ -157,8 +161,9 @@ TEST(MetadataRecorderTest, Remove_DoesntExist) {
recorder.Set(10, nullopt, 20);
recorder.Remove(20, nullopt);
- ProfileBuilder::MetadataItemArray items;
- size_t item_count = recorder.CreateMetadataProvider()->GetItems(&items);
+ MetadataRecorder::ItemArray items;
+ size_t item_count =
+ MetadataRecorder::MetadataProvider(&recorder).GetItems(&items);
ASSERT_EQ(1u, item_count);
EXPECT_EQ(10u, items[0].name_hash);
EXPECT_FALSE(items[0].key.has_value());
@@ -173,8 +178,9 @@ TEST(MetadataRecorderTest, Remove_NulloptKeyIsIndependentOfNonNulloptKey) {
recorder.Remove(10, nullopt);
- ProfileBuilder::MetadataItemArray items;
- size_t item_count = recorder.CreateMetadataProvider()->GetItems(&items);
+ MetadataRecorder::ItemArray items;
+ size_t item_count =
+ MetadataRecorder::MetadataProvider(&recorder).GetItems(&items);
ASSERT_EQ(1u, item_count);
EXPECT_EQ(10u, items[0].name_hash);
ASSERT_TRUE(items[0].key.has_value());
@@ -185,34 +191,34 @@ TEST(MetadataRecorderTest, Remove_NulloptKeyIsIndependentOfNonNulloptKey) {
TEST(MetadataRecorderTest, ReclaimInactiveSlots) {
MetadataRecorder recorder;
- std::set<ProfileBuilder::MetadataItem> items_set;
+ std::set<MetadataRecorder::Item> items_set;
// Fill up the metadata map.
- for (size_t i = 0; i < ProfileBuilder::MAX_METADATA_COUNT; ++i) {
+ for (size_t i = 0; i < MetadataRecorder::MAX_METADATA_COUNT; ++i) {
recorder.Set(i, nullopt, i);
- items_set.insert(ProfileBuilder::MetadataItem{i, nullopt, i});
+ items_set.insert(MetadataRecorder::Item{i, nullopt, i});
}
// Remove every fourth entry to fragment the data.
size_t entries_removed = 0;
- for (size_t i = 3; i < ProfileBuilder::MAX_METADATA_COUNT; i += 4) {
+ for (size_t i = 3; i < MetadataRecorder::MAX_METADATA_COUNT; i += 4) {
recorder.Remove(i, nullopt);
++entries_removed;
- items_set.erase(ProfileBuilder::MetadataItem{i, nullopt, i});
+ items_set.erase(MetadataRecorder::Item{i, nullopt, i});
}
// Ensure that the inactive slots are reclaimed to make room for more entries.
for (size_t i = 1; i <= entries_removed; ++i) {
recorder.Set(i * 100, nullopt, i * 100);
- items_set.insert(ProfileBuilder::MetadataItem{i * 100, nullopt, i * 100});
+ items_set.insert(MetadataRecorder::Item{i * 100, nullopt, i * 100});
}
- ProfileBuilder::MetadataItemArray items_arr;
+ MetadataRecorder::ItemArray items_arr;
std::copy(items_set.begin(), items_set.end(), items_arr.begin());
- ProfileBuilder::MetadataItemArray recorder_items;
+ MetadataRecorder::ItemArray recorder_items;
size_t recorder_item_count =
- recorder.CreateMetadataProvider()->GetItems(&recorder_items);
- EXPECT_EQ(recorder_item_count, ProfileBuilder::MAX_METADATA_COUNT);
+ MetadataRecorder::MetadataProvider(&recorder).GetItems(&recorder_items);
+ EXPECT_EQ(recorder_item_count, MetadataRecorder::MAX_METADATA_COUNT);
EXPECT_THAT(recorder_items, ::testing::UnorderedElementsAreArray(items_arr));
}
@@ -220,7 +226,7 @@ TEST(MetadataRecorderTest, MetadataSlotsUsedUmaHistogram) {
MetadataRecorder recorder;
HistogramTester histogram_tester;
- for (size_t i = 0; i < ProfileBuilder::MAX_METADATA_COUNT; ++i) {
+ for (size_t i = 0; i < MetadataRecorder::MAX_METADATA_COUNT; ++i) {
recorder.Set(i * 10, nullopt, i * 100);
}
diff --git a/chromium/base/profiler/module_cache_mac.cc b/chromium/base/profiler/module_cache_mac.cc
index 2d895b7baef..30568aa2f8d 100644
--- a/chromium/base/profiler/module_cache_mac.cc
+++ b/chromium/base/profiler/module_cache_mac.cc
@@ -6,69 +6,108 @@
#include <dlfcn.h>
#include <mach-o/getsect.h>
+#include <string.h>
#include <uuid/uuid.h>
#include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
namespace base {
namespace {
-// Returns the unique build ID for a module loaded at |module_addr|. Returns the
-// empty string if the function fails to get the build ID.
+#if defined(ARCH_CPU_64_BITS)
+using MachHeaderType = mach_header_64;
+using SegmentCommandType = segment_command_64;
+constexpr uint32_t kMachHeaderMagic = MH_MAGIC_64;
+constexpr uint32_t kSegmentCommand = LC_SEGMENT_64;
+#else
+using MachHeaderType = mach_header;
+using SegmentCommandType = segment_command;
+constexpr uint32_t kMachHeaderMagic = MH_MAGIC;
+constexpr uint32_t kSegmentCommand = LC_SEGMENT;
+#endif
+
+// Returns the unique build ID and text segment size for a module loaded at
+// |module_addr|. Returns the empty string and 0 if the function fails to get
+// the build ID or size.
//
// Build IDs are created by the concatenation of the module's GUID (Windows) /
// UUID (Mac) and an "age" field that indicates how many times that GUID/UUID
// has been reused. In Windows binaries, the "age" field is present in the
// module header, but on the Mac, UUIDs are never reused and so the "age" value
// appended to the UUID is always 0.
-std::string GetUniqueId(const void* module_addr) {
- const mach_header_64* mach_header =
- reinterpret_cast<const mach_header_64*>(module_addr);
- DCHECK_EQ(MH_MAGIC_64, mach_header->magic);
-
- size_t offset = sizeof(mach_header_64);
- size_t offset_limit = sizeof(mach_header_64) + mach_header->sizeofcmds;
+void GetUniqueIdAndTextSize(const void* module_addr,
+ std::string* unique_id,
+ size_t* text_size) {
+ const MachHeaderType* mach_header =
+ reinterpret_cast<const MachHeaderType*>(module_addr);
+ DCHECK_EQ(mach_header->magic, kMachHeaderMagic);
+
+ size_t offset = sizeof(MachHeaderType);
+ size_t offset_limit = sizeof(MachHeaderType) + mach_header->sizeofcmds;
+ bool found_uuid = false;
+ bool found_text_size = false;
for (uint32_t i = 0; i < mach_header->ncmds; ++i) {
- if (offset + sizeof(load_command) >= offset_limit)
- return std::string();
+ if (offset + sizeof(load_command) >= offset_limit) {
+ unique_id->clear();
+ *text_size = 0;
+ return;
+ }
- const load_command* current_cmd = reinterpret_cast<const load_command*>(
+ const load_command* load_cmd = reinterpret_cast<const load_command*>(
reinterpret_cast<const uint8_t*>(mach_header) + offset);
- if (offset + current_cmd->cmdsize > offset_limit) {
+ if (offset + load_cmd->cmdsize > offset_limit) {
// This command runs off the end of the command list. This is malformed.
- return std::string();
+ unique_id->clear();
+ *text_size = 0;
+ return;
}
- if (current_cmd->cmd == LC_UUID) {
- if (current_cmd->cmdsize < sizeof(uuid_command)) {
+ if (load_cmd->cmd == LC_UUID) {
+ if (load_cmd->cmdsize < sizeof(uuid_command)) {
// This "UUID command" is too small. This is malformed.
- return std::string();
+ unique_id->clear();
+ } else {
+ const uuid_command* uuid_cmd =
+ reinterpret_cast<const uuid_command*>(load_cmd);
+ static_assert(sizeof(uuid_cmd->uuid) == sizeof(uuid_t),
+ "UUID field of UUID command should be 16 bytes.");
+ // The ID comprises the UUID concatenated with the Mac's "age" value
+ // which is always 0.
+ unique_id->assign(HexEncode(&uuid_cmd->uuid, sizeof(uuid_cmd->uuid)) +
+ "0");
}
-
- const uuid_command* uuid_cmd =
- reinterpret_cast<const uuid_command*>(current_cmd);
- static_assert(sizeof(uuid_cmd->uuid) == sizeof(uuid_t),
- "UUID field of UUID command should be 16 bytes.");
- // The ID is comprised of the UUID concatenated with the Mac's "age" value
- // which is always 0.
- return HexEncode(&uuid_cmd->uuid, sizeof(uuid_cmd->uuid)) + "0";
+ if (found_text_size)
+ return;
+ found_uuid = true;
+ } else if (load_cmd->cmd == kSegmentCommand) {
+ const SegmentCommandType* segment_cmd =
+ reinterpret_cast<const SegmentCommandType*>(load_cmd);
+ if (strncmp(segment_cmd->segname, SEG_TEXT,
+ sizeof(segment_cmd->segname)) == 0) {
+ *text_size = segment_cmd->vmsize;
+ // Compare result with library function call, which is slower than this
+ // code.
+ unsigned long text_size_from_libmacho;
+ DCHECK(getsegmentdata(mach_header, SEG_TEXT, &text_size_from_libmacho));
+ DCHECK_EQ(*text_size, text_size_from_libmacho);
+ }
+ if (found_uuid)
+ return;
+ found_text_size = true;
}
- offset += current_cmd->cmdsize;
+ offset += load_cmd->cmdsize;
}
- return std::string();
-}
-// Returns the size of the _TEXT segment of the module loaded at |module_addr|.
-size_t GetModuleTextSize(const void* module_addr) {
- const mach_header_64* mach_header =
- reinterpret_cast<const mach_header_64*>(module_addr);
- DCHECK_EQ(MH_MAGIC_64, mach_header->magic);
- unsigned long module_size;
- getsegmentdata(mach_header, SEG_TEXT, &module_size);
- return module_size;
+ if (!found_uuid) {
+ unique_id->clear();
+ }
+ if (!found_text_size) {
+ *text_size = 0;
+ }
}
} // namespace
@@ -77,9 +116,9 @@ class MacModule : public ModuleCache::Module {
public:
MacModule(const Dl_info& dl_info)
: base_address_(reinterpret_cast<uintptr_t>(dl_info.dli_fbase)),
- id_(GetUniqueId(dl_info.dli_fbase)),
- debug_basename_(FilePath(dl_info.dli_fname).BaseName()),
- size_(GetModuleTextSize(dl_info.dli_fbase)) {}
+ debug_basename_(FilePath(dl_info.dli_fname).BaseName()) {
+ GetUniqueIdAndTextSize(dl_info.dli_fbase, &id_, &size_);
+ }
MacModule(const MacModule&) = delete;
MacModule& operator=(const MacModule&) = delete;
diff --git a/chromium/base/profiler/module_cache_stub.cc b/chromium/base/profiler/module_cache_stub.cc
deleted file mode 100644
index 2c9231ad1e6..00000000000
--- a/chromium/base/profiler/module_cache_stub.cc
+++ /dev/null
@@ -1,15 +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/module_cache.h"
-
-namespace base {
-
-// static
-std::unique_ptr<const ModuleCache::Module> ModuleCache::CreateModuleForAddress(
- uintptr_t address) {
- return nullptr;
-}
-
-} // namespace base
diff --git a/chromium/base/profiler/native_unwinder_android.cc b/chromium/base/profiler/native_unwinder_android.cc
index d5f9cfeec4e..fa06494aee2 100644
--- a/chromium/base/profiler/native_unwinder_android.cc
+++ b/chromium/base/profiler/native_unwinder_android.cc
@@ -4,25 +4,226 @@
#include "base/profiler/native_unwinder_android.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/profiler/module_cache.h"
#include "base/profiler/native_unwinder.h"
#include "base/profiler/profile_builder.h"
+#include "base/profiler/unwindstack_internal_android.h"
+#include "build/build_config.h"
+
+#if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
+#include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/MachineArm.h"
+#include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/RegsArm.h"
+#elif defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_64_BITS)
+#include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/MachineArm64.h"
+#include "third_party/libunwindstack/src/libunwindstack/include/unwindstack/RegsArm64.h"
+#endif // #if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
namespace base {
+namespace {
+
+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) {}
+ ~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_); }
+
+ // Gets the size of the module.
+ size_t GetSize() const override { return size_; }
+
+ // True if this is a native module.
+ bool IsNative() const override { return true; }
+
+ const uintptr_t start_;
+ const size_t size_;
+ const std::string build_id_;
+ const std::string name_;
+};
+
+std::unique_ptr<unwindstack::Regs> CreateFromRegisterContext(
+ RegisterContext* thread_context) {
+#if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
+ return WrapUnique<unwindstack::Regs>(unwindstack::RegsArm::Read(
+ reinterpret_cast<void*>(&thread_context->arm_r0)));
+#elif defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_64_BITS)
+ return WrapUnique<unwindstack::Regs>(unwindstack::RegsArm64::Read(
+ reinterpret_cast<void*>(&thread_context->regs[0])));
+#else // #if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
+ NOTREACHED();
+ return nullptr;
+#endif // #if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
+}
+
+void CopyToRegisterContext(unwindstack::Regs* regs,
+ RegisterContext* thread_context) {
+#if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
+ memcpy(reinterpret_cast<void*>(&thread_context->arm_r0), regs->RawData(),
+ unwindstack::ARM_REG_LAST * sizeof(uint32_t));
+#elif defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_64_BITS)
+ memcpy(reinterpret_cast<void*>(&thread_context->regs[0]), regs->RawData(),
+ unwindstack::ARM64_REG_LAST * sizeof(uint32_t));
+#else // #if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
+ NOTREACHED();
+#endif // #if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
+}
+
+} // namespace
+
+// static
+std::unique_ptr<unwindstack::Maps> NativeUnwinderAndroid::CreateMaps() {
+ auto maps = std::make_unique<unwindstack::LocalMaps>();
+ if (maps->Parse())
+ return maps;
+ return nullptr;
+}
+
+// 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()));
+ }
+}
+
+NativeUnwinderAndroid::NativeUnwinderAndroid(
+ unwindstack::Maps* memory_regions_map,
+ unwindstack::Memory* process_memory,
+ uintptr_t exclude_module_with_base_address)
+ : memory_regions_map_(memory_regions_map),
+ process_memory_(process_memory),
+ exclude_module_with_base_address_(exclude_module_with_base_address) {}
+
+NativeUnwinderAndroid::~NativeUnwinderAndroid() = default;
+
+void NativeUnwinderAndroid::AddInitialModules(ModuleCache* module_cache) {
+ AddInitialModulesFromMaps(*memory_regions_map_, module_cache);
+}
bool NativeUnwinderAndroid::CanUnwindFrom(const Frame& current_frame) const {
- return false;
+ return current_frame.module && current_frame.module->IsNative() &&
+ current_frame.module->GetBaseAddress() !=
+ exclude_module_with_base_address_;
}
UnwindResult NativeUnwinderAndroid::TryUnwind(RegisterContext* thread_context,
uintptr_t stack_top,
ModuleCache* module_cache,
std::vector<Frame>* stack) const {
- return UnwindResult::ABORTED;
+ auto regs = CreateFromRegisterContext(thread_context);
+ DCHECK(regs);
+ unwindstack::ArchEnum arch = regs->Arch();
+
+ do {
+ uint64_t cur_pc = regs->pc();
+ uint64_t cur_sp = regs->sp();
+ unwindstack::MapInfo* map_info = memory_regions_map_->Find(cur_pc);
+ if (map_info == nullptr ||
+ map_info->flags & unwindstack::MAPS_FLAGS_DEVICE_MAP) {
+ break;
+ }
+
+ unwindstack::Elf* elf =
+ map_info->GetElf({process_memory_, [](unwindstack::Memory*) {}}, arch);
+ if (!elf->valid())
+ break;
+
+ UnwindStackMemoryAndroid stack_memory(cur_sp, stack_top);
+ 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);
+ if (stepped && finished)
+ return UnwindResult::COMPLETED;
+
+ if (!stepped) {
+ // Stepping failed. Try unwinding using return address.
+ if (stack->size() == 1) {
+ if (!regs->SetPcFromReturnAddress(&stack_memory))
+ return UnwindResult::ABORTED;
+ } else {
+ break;
+ }
+ }
+
+ // If the pc and sp didn't change, then consider everything stopped.
+ if (cur_pc == regs->pc() && cur_sp == regs->sp())
+ return UnwindResult::ABORTED;
+
+ // Exclusive range of expected stack pointer values after the unwind.
+ struct {
+ uintptr_t start;
+ uintptr_t end;
+ } expected_stack_pointer_range = {cur_sp, stack_top};
+ if (regs->sp() < expected_stack_pointer_range.start ||
+ regs->sp() >= expected_stack_pointer_range.end) {
+ return UnwindResult::ABORTED;
+ }
+
+ if (regs->dex_pc() != 0) {
+ // Add a frame to represent the dex file.
+ EmitDexFrame(regs->dex_pc(), module_cache, stack);
+
+ // Clear the dex pc so that we don't repeat this frame later.
+ regs->set_dex_pc(0);
+ }
+
+ // Add the frame to |stack|.
+ const ModuleCache::Module* module =
+ module_cache->GetModuleForAddress(regs->pc());
+ stack->emplace_back(regs->pc(), module);
+ } while (CanUnwindFrom(stack->back()));
+
+ // Restore registers necessary for further unwinding in |thread_context|.
+ CopyToRegisterContext(regs.get(), thread_context);
+ return UnwindResult::UNRECOGNIZED_FRAME;
}
-std::unique_ptr<Unwinder> CreateNativeUnwinder(ModuleCache* module_cache) {
- return std::make_unique<NativeUnwinderAndroid>();
+void NativeUnwinderAndroid::EmitDexFrame(uintptr_t dex_pc,
+ ModuleCache* module_cache,
+ std::vector<Frame>* stack) const {
+ const ModuleCache::Module* module = module_cache->GetModuleForAddress(dex_pc);
+ if (!module) {
+ // The region containing |dex_pc| may not be in |module_cache| since it's
+ // usually not executable (.dex file). Since non-executable regions
+ // are used much less commonly, it's lazily added here instead of from
+ // AddInitialModules().
+ unwindstack::MapInfo* map_info = memory_regions_map_->Find(dex_pc);
+ if (map_info) {
+ auto new_module = std::make_unique<AndroidModule>(map_info);
+ module = new_module.get();
+ module_cache->AddCustomNativeModule(std::move(new_module));
+ }
+ }
+ stack->emplace_back(dex_pc, module);
}
} // namespace base
diff --git a/chromium/base/profiler/native_unwinder_android.h b/chromium/base/profiler/native_unwinder_android.h
index 16f1b7b39aa..926a581b32a 100644
--- a/chromium/base/profiler/native_unwinder_android.h
+++ b/chromium/base/profiler/native_unwinder_android.h
@@ -7,25 +7,54 @@
#include "base/profiler/unwinder.h"
+namespace unwindstack {
+class Maps;
+class Memory;
+} // namespace unwindstack
+
namespace base {
// Native unwinder implementation for Android, using libunwindstack.
-//
-// TODO(charliea): Implement this class.
-// See: https://crbug.com/989102
class NativeUnwinderAndroid : public Unwinder {
public:
- NativeUnwinderAndroid() = default;
+ // Creates maps object from /proc/self/maps for use by NativeUnwinderAndroid.
+ // Since this is an expensive call, the maps object should be re-used across
+ // 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
+ // UNRECOGNIZED_FRAME and CanUnwindFrom() will return false when a frame is
+ // encountered in that module.
+ NativeUnwinderAndroid(unwindstack::Maps* memory_regions_map,
+ unwindstack::Memory* process_memory,
+ uintptr_t exclude_module_with_base_address = 0);
+ ~NativeUnwinderAndroid() override;
NativeUnwinderAndroid(const NativeUnwinderAndroid&) = delete;
NativeUnwinderAndroid& operator=(const NativeUnwinderAndroid&) = delete;
// Unwinder
+ void AddInitialModules(ModuleCache* module_cache) override;
bool CanUnwindFrom(const Frame& current_frame) const override;
UnwindResult TryUnwind(RegisterContext* thread_context,
uintptr_t stack_top,
ModuleCache* module_cache,
std::vector<Frame>* stack) const override;
+
+ private:
+ void EmitDexFrame(uintptr_t dex_pc,
+ ModuleCache* module_cache,
+ std::vector<Frame>* stack) const;
+
+ unwindstack::Maps* const memory_regions_map_;
+ unwindstack::Memory* const process_memory_;
+ const uintptr_t exclude_module_with_base_address_;
};
} // namespace base
diff --git a/chromium/base/profiler/native_unwinder_android_unittest.cc b/chromium/base/profiler/native_unwinder_android_unittest.cc
new file mode 100644
index 00000000000..236ab3ae6fc
--- /dev/null
+++ b/chromium/base/profiler/native_unwinder_android_unittest.cc
@@ -0,0 +1,365 @@
+// 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.
+
+#include "base/profiler/native_unwinder_android.h"
+
+#include <string.h>
+
+#include "base/android/build_info.h"
+#include "base/android/jni_android.h"
+#include "base/base_profiler_test_support_jni_headers/TestSupport_jni.h"
+#include "base/bind.h"
+#include "base/profiler/register_context.h"
+#include "base/profiler/stack_buffer.h"
+#include "base/profiler/stack_copier_signal.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"
+
+extern char __executable_start;
+
+namespace base {
+
+class TestStackCopierDelegate : public StackCopier::Delegate {
+ public:
+ void OnStackCopy() override {}
+};
+
+std::vector<Frame> CaptureScenario(
+ UnwindScenario* scenario,
+ ModuleCache* module_cache,
+ OnceCallback<void(RegisterContext*, uintptr_t, std::vector<Frame>*)>
+ unwind_callback) {
+ std::vector<Frame> sample;
+ WithTargetThread(
+ scenario,
+ BindLambdaForTesting(
+ [&](SamplingProfilerThreadToken target_thread_token) {
+ auto stack_copier = std::make_unique<StackCopierSignal>(
+ std::make_unique<ThreadDelegatePosix>(target_thread_token));
+ std::unique_ptr<StackBuffer> stack_buffer =
+ StackSampler::CreateStackBuffer();
+
+ RegisterContext thread_context;
+ uintptr_t stack_top;
+ TimeTicks timestamp;
+ TestStackCopierDelegate delegate;
+ bool success =
+ stack_copier->CopyStack(stack_buffer.get(), &stack_top,
+ &timestamp, &thread_context, &delegate);
+ ASSERT_TRUE(success);
+
+ sample.emplace_back(
+ RegisterContextInstructionPointer(&thread_context),
+ module_cache->GetModuleForAddress(
+ RegisterContextInstructionPointer(&thread_context)));
+
+ std::move(unwind_callback).Run(&thread_context, stack_top, &sample);
+ }));
+
+ return sample;
+}
+
+// Checks that the expected information is present in sampled frames.
+TEST(NativeUnwinderAndroidTest, PlainFunction) {
+ UnwindScenario scenario(BindRepeating(&CallWithPlainFunction));
+
+ std::unique_ptr<unwindstack::Maps> maps = NativeUnwinderAndroid::CreateMaps();
+ std::unique_ptr<unwindstack::Memory> memory =
+ NativeUnwinderAndroid::CreateProcessMemory();
+ auto unwinder =
+ std::make_unique<NativeUnwinderAndroid>(maps.get(), memory.get(), 0);
+
+ ModuleCache module_cache;
+ unwinder->AddInitialModules(&module_cache);
+ std::vector<Frame> sample =
+ CaptureScenario(&scenario, &module_cache,
+ BindLambdaForTesting([&](RegisterContext* thread_context,
+ uintptr_t stack_top,
+ std::vector<Frame>* sample) {
+ ASSERT_TRUE(unwinder->CanUnwindFrom(sample->back()));
+ UnwindResult result = unwinder->TryUnwind(
+ thread_context, stack_top, &module_cache, sample);
+ EXPECT_EQ(UnwindResult::COMPLETED, result);
+ }));
+
+ // Check that all the modules are valid.
+ for (const auto& frame : sample)
+ EXPECT_NE(nullptr, frame.module);
+
+ // The stack should contain a full unwind.
+ ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
+ scenario.GetSetupFunctionAddressRange(),
+ scenario.GetOuterFunctionAddressRange()});
+}
+
+// Checks that the unwinder handles stacks containing dynamically-allocated
+// stack memory.
+TEST(NativeUnwinderAndroidTest, Alloca) {
+ UnwindScenario scenario(BindRepeating(&CallWithAlloca));
+
+ std::unique_ptr<unwindstack::Maps> maps = NativeUnwinderAndroid::CreateMaps();
+ std::unique_ptr<unwindstack::Memory> memory =
+ NativeUnwinderAndroid::CreateProcessMemory();
+ auto unwinder =
+ std::make_unique<NativeUnwinderAndroid>(maps.get(), memory.get(), 0);
+
+ ModuleCache module_cache;
+ unwinder->AddInitialModules(&module_cache);
+ std::vector<Frame> sample =
+ CaptureScenario(&scenario, &module_cache,
+ BindLambdaForTesting([&](RegisterContext* thread_context,
+ uintptr_t stack_top,
+ std::vector<Frame>* sample) {
+ ASSERT_TRUE(unwinder->CanUnwindFrom(sample->back()));
+ UnwindResult result = unwinder->TryUnwind(
+ thread_context, stack_top, &module_cache, sample);
+ EXPECT_EQ(UnwindResult::COMPLETED, result);
+ }));
+
+ // Check that all the modules are valid.
+ for (const auto& frame : sample)
+ EXPECT_NE(nullptr, frame.module);
+
+ // The stack should contain a full unwind.
+ ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
+ scenario.GetSetupFunctionAddressRange(),
+ scenario.GetOuterFunctionAddressRange()});
+}
+
+// Checks that a stack that runs through another library produces a stack with
+// the expected functions.
+TEST(NativeUnwinderAndroidTest, OtherLibrary) {
+ NativeLibrary other_library = LoadOtherLibrary();
+ UnwindScenario scenario(
+ BindRepeating(&CallThroughOtherLibrary, Unretained(other_library)));
+
+ std::unique_ptr<unwindstack::Maps> maps = NativeUnwinderAndroid::CreateMaps();
+ std::unique_ptr<unwindstack::Memory> memory =
+ NativeUnwinderAndroid::CreateProcessMemory();
+ auto unwinder =
+ std::make_unique<NativeUnwinderAndroid>(maps.get(), memory.get(), 0);
+
+ ModuleCache module_cache;
+ unwinder->AddInitialModules(&module_cache);
+ std::vector<Frame> sample =
+ CaptureScenario(&scenario, &module_cache,
+ BindLambdaForTesting([&](RegisterContext* thread_context,
+ uintptr_t stack_top,
+ std::vector<Frame>* sample) {
+ ASSERT_TRUE(unwinder->CanUnwindFrom(sample->back()));
+ UnwindResult result = unwinder->TryUnwind(
+ thread_context, stack_top, &module_cache, sample);
+ EXPECT_EQ(UnwindResult::COMPLETED, result);
+ }));
+
+ // The stack should contain a full unwind.
+ ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
+ scenario.GetSetupFunctionAddressRange(),
+ scenario.GetOuterFunctionAddressRange()});
+}
+
+// Check that unwinding is interrupted for excluded modules.
+TEST(NativeUnwinderAndroidTest, ExcludeOtherLibrary) {
+ NativeLibrary other_library = LoadOtherLibrary();
+ UnwindScenario scenario(
+ BindRepeating(&CallThroughOtherLibrary, Unretained(other_library)));
+
+ std::unique_ptr<unwindstack::Maps> maps = NativeUnwinderAndroid::CreateMaps();
+ std::unique_ptr<unwindstack::Memory> memory =
+ NativeUnwinderAndroid::CreateProcessMemory();
+ ModuleCache module_cache;
+ NativeUnwinderAndroid::AddInitialModulesFromMaps(*maps, &module_cache);
+
+ auto unwinder = std::make_unique<NativeUnwinderAndroid>(
+ maps.get(), memory.get(),
+ module_cache.GetModuleForAddress(GetAddressInOtherLibrary(other_library))
+ ->GetBaseAddress());
+ std::vector<Frame> sample =
+ CaptureScenario(&scenario, &module_cache,
+ BindLambdaForTesting([&](RegisterContext* thread_context,
+ uintptr_t stack_top,
+ std::vector<Frame>* sample) {
+ ASSERT_TRUE(unwinder->CanUnwindFrom(sample->back()));
+ EXPECT_EQ(UnwindResult::UNRECOGNIZED_FRAME,
+ unwinder->TryUnwind(thread_context, stack_top,
+ &module_cache, sample));
+ EXPECT_FALSE(unwinder->CanUnwindFrom(sample->back()));
+ }));
+
+ ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange()});
+ ExpectStackDoesNotContain(sample, {scenario.GetSetupFunctionAddressRange(),
+ scenario.GetOuterFunctionAddressRange()});
+}
+
+// Check that unwinding can be resumed after an incomplete unwind.
+TEST(NativeUnwinderAndroidTest, ResumeUnwinding) {
+ NativeLibrary other_library = LoadOtherLibrary();
+ UnwindScenario scenario(
+ BindRepeating(&CallThroughOtherLibrary, Unretained(other_library)));
+
+ std::unique_ptr<unwindstack::Maps> maps = NativeUnwinderAndroid::CreateMaps();
+ std::unique_ptr<unwindstack::Memory> memory =
+ NativeUnwinderAndroid::CreateProcessMemory();
+ ModuleCache module_cache;
+ NativeUnwinderAndroid::AddInitialModulesFromMaps(*maps, &module_cache);
+
+ // Several unwinders are used to unwind different portion of the stack. This
+ // tests that NativeUnwinderAndroid can pick up from a state in the middle of
+ // the stack. This emulates having NativeUnwinderAndroid work with other
+ // unwinders, but doesn't reproduce what happens in production.
+ auto unwinder_for_all =
+ std::make_unique<NativeUnwinderAndroid>(maps.get(), memory.get(), 0);
+ auto unwinder_for_native = std::make_unique<NativeUnwinderAndroid>(
+ maps.get(), memory.get(),
+ reinterpret_cast<uintptr_t>(&__executable_start));
+ auto unwinder_for_chrome = std::make_unique<NativeUnwinderAndroid>(
+ maps.get(), memory.get(),
+ module_cache.GetModuleForAddress(GetAddressInOtherLibrary(other_library))
+ ->GetBaseAddress());
+
+ std::vector<Frame> sample = CaptureScenario(
+ &scenario, &module_cache,
+ BindLambdaForTesting([&](RegisterContext* thread_context,
+ uintptr_t stack_top,
+ std::vector<Frame>* sample) {
+ // |unwinder_for_native| unwinds through native frames, but stops at
+ // chrome frames. It might not contain SampleAddressRange.
+ ASSERT_TRUE(unwinder_for_native->CanUnwindFrom(sample->back()));
+ EXPECT_EQ(UnwindResult::UNRECOGNIZED_FRAME,
+ unwinder_for_native->TryUnwind(thread_context, stack_top,
+ &module_cache, sample));
+ EXPECT_FALSE(unwinder_for_native->CanUnwindFrom(sample->back()));
+
+ ExpectStackDoesNotContain(*sample,
+ {scenario.GetSetupFunctionAddressRange(),
+ scenario.GetOuterFunctionAddressRange()});
+ size_t prior_stack_size = sample->size();
+
+ // |unwinder_for_chrome| unwinds through Chrome frames, but stops at
+ // |other_library|. It won't contain SetupFunctionAddressRange.
+ ASSERT_TRUE(unwinder_for_chrome->CanUnwindFrom(sample->back()));
+ EXPECT_EQ(UnwindResult::UNRECOGNIZED_FRAME,
+ unwinder_for_chrome->TryUnwind(thread_context, stack_top,
+ &module_cache, sample));
+ EXPECT_FALSE(unwinder_for_chrome->CanUnwindFrom(sample->back()));
+ EXPECT_LT(prior_stack_size, sample->size());
+ ExpectStackContains(*sample, {scenario.GetWaitForSampleAddressRange()});
+ ExpectStackDoesNotContain(*sample,
+ {scenario.GetSetupFunctionAddressRange(),
+ scenario.GetOuterFunctionAddressRange()});
+
+ // |unwinder_for_all| should complete unwinding through all frames.
+ ASSERT_TRUE(unwinder_for_all->CanUnwindFrom(sample->back()));
+ EXPECT_EQ(UnwindResult::COMPLETED,
+ unwinder_for_all->TryUnwind(thread_context, stack_top,
+ &module_cache, sample));
+ }));
+
+ // The stack should contain a full unwind.
+ ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
+ scenario.GetSetupFunctionAddressRange(),
+ scenario.GetOuterFunctionAddressRange()});
+}
+
+struct JavaTestSupportParams {
+ OnceClosure wait_for_sample;
+ FunctionAddressRange range;
+};
+
+void JNI_TestSupport_InvokeCallbackFunction(JNIEnv* env, jlong context) {
+ const void* start_program_counter = GetProgramCounter();
+
+ JavaTestSupportParams* params =
+ reinterpret_cast<JavaTestSupportParams*>(context);
+ if (!params->wait_for_sample.is_null())
+ std::move(params->wait_for_sample).Run();
+
+ // Volatile to prevent a tail call to GetProgramCounter().
+ const void* volatile end_program_counter = GetProgramCounter();
+
+ params->range = {start_program_counter, end_program_counter};
+}
+
+// Checks that java frames can be unwound through.
+// Disabled, see: https://crbug.com/1076997
+TEST(NativeUnwinderAndroidTest, DISABLED_JavaFunction) {
+ auto* build_info = base::android::BuildInfo::GetInstance();
+ // Due to varying availability of compiled java unwind tables, unwinding is
+ // only expected to succeed on > SDK_VERSION_MARSHMALLOW.
+ bool can_always_unwind =
+ build_info->sdk_int() > base::android::SDK_VERSION_MARSHMALLOW;
+
+ UnwindScenario scenario(BindLambdaForTesting([](OnceClosure wait_for_sample) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ JavaTestSupportParams params{std::move(wait_for_sample), {}};
+ base::Java_TestSupport_callWithJavaFunction(
+ env, reinterpret_cast<uintptr_t>(&params));
+ return params.range;
+ }));
+
+ std::unique_ptr<unwindstack::Maps> maps = NativeUnwinderAndroid::CreateMaps();
+ std::unique_ptr<unwindstack::Memory> memory =
+ NativeUnwinderAndroid::CreateProcessMemory();
+ auto unwinder =
+ std::make_unique<NativeUnwinderAndroid>(maps.get(), memory.get(), 0);
+
+ ModuleCache module_cache;
+ unwinder->AddInitialModules(&module_cache);
+ std::vector<Frame> sample =
+ CaptureScenario(&scenario, &module_cache,
+ BindLambdaForTesting([&](RegisterContext* thread_context,
+ uintptr_t stack_top,
+ std::vector<Frame>* sample) {
+ ASSERT_TRUE(unwinder->CanUnwindFrom(sample->back()));
+ UnwindResult result = unwinder->TryUnwind(
+ thread_context, stack_top, &module_cache, sample);
+ if (can_always_unwind)
+ EXPECT_EQ(UnwindResult::COMPLETED, result);
+ }));
+
+ // Check that all the modules are valid.
+ for (const auto& frame : sample)
+ EXPECT_NE(nullptr, frame.module);
+
+ // The stack should contain a full unwind.
+ if (can_always_unwind) {
+ ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
+ scenario.GetSetupFunctionAddressRange(),
+ scenario.GetOuterFunctionAddressRange()});
+ }
+}
+
+TEST(NativeUnwinderAndroidTest, UnwindStackMemoryTest) {
+ std::vector<uint8_t> input = {1, 2, 3, 4, 5};
+ uintptr_t begin = reinterpret_cast<uintptr_t>(input.data());
+ uintptr_t end = reinterpret_cast<uintptr_t>(input.data() + input.size());
+ UnwindStackMemoryAndroid memory(begin, end);
+
+ const auto check_read_fails = [&](uintptr_t addr, size_t size) {
+ std::vector<uint8_t> output(size);
+ EXPECT_EQ(0U, memory.Read(addr, output.data(), size));
+ };
+ const auto check_read_succeeds = [&](uintptr_t addr, size_t size) {
+ std::vector<uint8_t> output(size);
+ EXPECT_EQ(size, memory.Read(addr, output.data(), size));
+ EXPECT_EQ(
+ 0, memcmp(reinterpret_cast<const uint8_t*>(addr), output.data(), size));
+ };
+
+ check_read_fails(begin - 1, 1);
+ check_read_fails(begin - 1, 2);
+ check_read_fails(end, 1);
+ check_read_fails(end, 2);
+ check_read_fails(end - 1, 2);
+
+ check_read_succeeds(begin, 1);
+ check_read_succeeds(begin, 5);
+ check_read_succeeds(end - 1, 1);
+}
+
+} // namespace base
diff --git a/chromium/base/profiler/native_unwinder_mac.cc b/chromium/base/profiler/native_unwinder_mac.cc
index 5e4da9ddefc..51717e1b5f2 100644
--- a/chromium/base/profiler/native_unwinder_mac.cc
+++ b/chromium/base/profiler/native_unwinder_mac.cc
@@ -9,7 +9,8 @@
#include <mach/vm_map.h>
#include <sys/ptrace.h>
-#include "base/logging.h"
+#include "base/check_op.h"
+#include "base/notreached.h"
#include "base/profiler/module_cache.h"
#include "base/profiler/native_unwinder.h"
#include "base/profiler/profile_builder.h"
diff --git a/chromium/base/profiler/native_unwinder_win.cc b/chromium/base/profiler/native_unwinder_win.cc
index e056045ed67..344b0901122 100644
--- a/chromium/base/profiler/native_unwinder_win.cc
+++ b/chromium/base/profiler/native_unwinder_win.cc
@@ -6,6 +6,8 @@
#include <winnt.h>
+#include "base/check_op.h"
+#include "base/notreached.h"
#include "base/profiler/native_unwinder.h"
#include "base/profiler/win32_stack_frame_unwinder.h"
diff --git a/chromium/base/profiler/profile_builder.cc b/chromium/base/profiler/profile_builder.cc
deleted file mode 100644
index 55d8dc18914..00000000000
--- a/chromium/base/profiler/profile_builder.cc
+++ /dev/null
@@ -1,23 +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/profile_builder.h"
-
-namespace base {
-
-const size_t ProfileBuilder::MAX_METADATA_COUNT;
-
-ProfileBuilder::MetadataItem::MetadataItem(uint64_t name_hash,
- Optional<int64_t> key,
- int64_t value)
- : name_hash(name_hash), key(key), value(value) {}
-
-ProfileBuilder::MetadataItem::MetadataItem() : name_hash(0), value(0) {}
-
-ProfileBuilder::MetadataItem::MetadataItem(const MetadataItem& other) = default;
-
-ProfileBuilder::MetadataItem& ProfileBuilder::MetadataItem::MetadataItem::
-operator=(const MetadataItem& other) = default;
-
-} // namespace base
diff --git a/chromium/base/profiler/profile_builder.h b/chromium/base/profiler/profile_builder.h
index 0c82b8f8b87..0454d8c2e47 100644
--- a/chromium/base/profiler/profile_builder.h
+++ b/chromium/base/profiler/profile_builder.h
@@ -8,8 +8,10 @@
#include <memory>
#include "base/base_export.h"
+#include "base/macros.h"
#include "base/optional.h"
#include "base/profiler/frame.h"
+#include "base/profiler/metadata_recorder.h"
#include "base/profiler/module_cache.h"
#include "base/time/time.h"
@@ -27,39 +29,14 @@ class BASE_EXPORT ProfileBuilder {
// up modules from addresses.
virtual ModuleCache* GetModuleCache() = 0;
- struct BASE_EXPORT MetadataItem {
- MetadataItem(uint64_t name_hash, Optional<int64_t> key, int64_t value);
- MetadataItem();
-
- MetadataItem(const MetadataItem& other);
- MetadataItem& operator=(const MetadataItem& other);
-
- // The hash of the metadata name, as produced by HashMetricName().
- uint64_t name_hash;
- // The key if specified when setting the item.
- Optional<int64_t> key;
- // The value of the metadata item.
- int64_t value;
- };
-
- static constexpr size_t MAX_METADATA_COUNT = 50;
- typedef std::array<MetadataItem, MAX_METADATA_COUNT> MetadataItemArray;
-
- class MetadataProvider {
- public:
- MetadataProvider() = default;
- virtual ~MetadataProvider() = default;
-
- virtual size_t GetItems(ProfileBuilder::MetadataItemArray* const items) = 0;
- };
-
// Records metadata to be associated with the current sample. To avoid
// deadlock on locks taken by the suspended profiled thread, implementations
// of this method must not execute any code that could take a lock, including
// heap allocation or use of CHECK/DCHECK/LOG statements. Generally
// implementations should simply atomically copy metadata state to be
// associated with the sample.
- virtual void RecordMetadata(MetadataProvider* metadata_provider) {}
+ virtual void RecordMetadata(
+ const MetadataRecorder::MetadataProvider& metadata_provider) {}
// Applies the specified metadata |item| to samples collected in the range
// [period_start, period_end), iff the profile already captured execution that
@@ -67,9 +44,10 @@ class BASE_EXPORT ProfileBuilder {
// towards samples in the middle of the period, at the expense of excluding
// periods overlapping the start or end of the profile. |period_end| must be
// <= TimeTicks::Now().
- virtual void ApplyMetadataRetrospectively(TimeTicks period_start,
- TimeTicks period_end,
- const MetadataItem& item) {}
+ virtual void ApplyMetadataRetrospectively(
+ TimeTicks period_start,
+ TimeTicks period_end,
+ const MetadataRecorder::Item& item) {}
// Records a new set of frames. Invoked when sampling a sample completes.
virtual void OnSampleCompleted(std::vector<Frame> frames,
diff --git a/chromium/base/profiler/register_context.h b/chromium/base/profiler/register_context.h
index 8ced8dae433..14380a52784 100644
--- a/chromium/base/profiler/register_context.h
+++ b/chromium/base/profiler/register_context.h
@@ -101,7 +101,7 @@ inline uintptr_t& RegisterContextFramePointer(mcontext_t* context) {
}
inline uintptr_t& RegisterContextInstructionPointer(mcontext_t* context) {
- return AsUintPtr(&context->arm_ip);
+ return AsUintPtr(&context->arm_pc);
}
#elif defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_64_BITS)
diff --git a/chromium/base/profiler/sample_metadata.cc b/chromium/base/profiler/sample_metadata.cc
index ac246f1041a..8bb48d2c00b 100644
--- a/chromium/base/profiler/sample_metadata.cc
+++ b/chromium/base/profiler/sample_metadata.cc
@@ -10,36 +10,39 @@
namespace base {
-ScopedSampleMetadata::ScopedSampleMetadata(StringPiece name, int64_t value)
- : name_hash_(HashMetricName(name)) {
+SampleMetadata::SampleMetadata(StringPiece name)
+ : name_hash_(HashMetricName(name)) {}
+
+void SampleMetadata::Set(int64_t value) {
GetSampleMetadataRecorder()->Set(name_hash_, nullopt, value);
}
-ScopedSampleMetadata::ScopedSampleMetadata(StringPiece name,
- int64_t key,
- int64_t value)
- : name_hash_(HashMetricName(name)), key_(key) {
+void SampleMetadata::Set(int64_t key, int64_t value) {
GetSampleMetadataRecorder()->Set(name_hash_, key, value);
}
-ScopedSampleMetadata::~ScopedSampleMetadata() {
- GetSampleMetadataRecorder()->Remove(name_hash_, key_);
+void SampleMetadata::Remove() {
+ GetSampleMetadataRecorder()->Remove(name_hash_, nullopt);
}
-void SetSampleMetadata(StringPiece name, int64_t value) {
- GetSampleMetadataRecorder()->Set(HashMetricName(name), nullopt, value);
+void SampleMetadata::Remove(int64_t key) {
+ GetSampleMetadataRecorder()->Remove(name_hash_, key);
}
-void SetSampleMetadata(StringPiece name, int64_t key, int64_t value) {
- GetSampleMetadataRecorder()->Set(HashMetricName(name), key, value);
+ScopedSampleMetadata::ScopedSampleMetadata(StringPiece name, int64_t value)
+ : name_hash_(HashMetricName(name)) {
+ GetSampleMetadataRecorder()->Set(name_hash_, nullopt, value);
}
-void RemoveSampleMetadata(StringPiece name) {
- GetSampleMetadataRecorder()->Remove(HashMetricName(name), nullopt);
+ScopedSampleMetadata::ScopedSampleMetadata(StringPiece name,
+ int64_t key,
+ int64_t value)
+ : name_hash_(HashMetricName(name)), key_(key) {
+ GetSampleMetadataRecorder()->Set(name_hash_, key, value);
}
-void RemoveSampleMetadata(StringPiece name, int64_t key) {
- GetSampleMetadataRecorder()->Remove(HashMetricName(name), key);
+ScopedSampleMetadata::~ScopedSampleMetadata() {
+ GetSampleMetadataRecorder()->Remove(name_hash_, key_);
}
// This function is friended by StackSamplingProfiler so must live directly in
diff --git a/chromium/base/profiler/sample_metadata.h b/chromium/base/profiler/sample_metadata.h
index a372fbafc75..4530e6ba18d 100644
--- a/chromium/base/profiler/sample_metadata.h
+++ b/chromium/base/profiler/sample_metadata.h
@@ -25,13 +25,15 @@
// For example:
//
// void DidStartLoad() {
-// base::SetSampleMetadata("Renderer.IsLoading", 1);
+// is_loading_metadata_.Set(1);
// }
//
// void DidFinishLoad() {
-// base::RemoveSampleMetadata("Renderer.IsLoading");
+// is_loading_metadata_.Remove();
// }
//
+// base::SampleMetadata is_loading_metadata_;
+//
// Alternatively, ScopedSampleMetadata can be used to ensure that the metadata
// is removed correctly.
//
@@ -51,6 +53,49 @@
namespace base {
+class BASE_EXPORT SampleMetadata {
+ public:
+ // Set the metadata value associated with |name|.
+ explicit SampleMetadata(StringPiece name);
+
+ SampleMetadata(const SampleMetadata&) = default;
+ ~SampleMetadata() = default;
+
+ SampleMetadata& operator=(const SampleMetadata&) = delete;
+
+ // Set the metadata value associated with |name| in the process-global stack
+ // sampling profiler metadata, overwriting any previous value set for that
+ // |name|.
+ void Set(int64_t value);
+
+ // Set the metadata value associated with the pair (|name|, |key|) in the
+ // process-global stack sampling profiler metadata, overwriting any previous
+ // value set for that (|name|, |key|) pair. This constructor allows the
+ // metadata to be associated with an additional user-defined key. One might
+ // supply a key based on the frame id, for example, to distinguish execution
+ // in service of scrolling between different frames. Prefer the previous
+ // function if no user-defined metadata is required. Note: values specified
+ // for a name and key are stored separately from values specified with only a
+ // name.
+ void Set(int64_t key, int64_t value);
+
+ // Removes the metadata item with the specified name from the process-global
+ // stack sampling profiler metadata.
+ //
+ // If such an item doesn't exist, this has no effect.
+ void Remove();
+
+ // Removes the metadata item with the specified (|name|, |key|) pair from the
+ // process-global stack sampling profiler metadata. This function does not
+ // alter values set with the name |name| but no key.
+ //
+ // If such an item doesn't exist, this has no effect.
+ void Remove(int64_t key);
+
+ private:
+ const uint64_t name_hash_;
+};
+
class BASE_EXPORT ScopedSampleMetadata {
public:
// Set the metadata value associated with |name|.
@@ -75,36 +120,6 @@ class BASE_EXPORT ScopedSampleMetadata {
Optional<int64_t> key_;
};
-// Set the metadata value associated with |name| in the process-global stack
-// sampling profiler metadata, overwriting any previous value set for that
-// |name|.
-BASE_EXPORT void SetSampleMetadata(StringPiece name, int64_t value);
-
-// Set the metadata value associated with the pair (|name|, |key|) in the
-// process-global stack sampling profiler metadata, overwriting any previous
-// value set for that (|name|, |key|) pair. This constructor allows the metadata
-// to be associated with an additional user-defined key. One might supply a key
-// based on the frame id, for example, to distinguish execution in service of
-// scrolling between different frames. Prefer the previous function if no
-// user-defined metadata is required. Note: values specified for a name and key
-// are stored separately from values specified with only a name.
-BASE_EXPORT void SetSampleMetadata(StringPiece name,
- int64_t key,
- int64_t value);
-
-// Removes the metadata item with the specified name from the process-global
-// stack sampling profiler metadata.
-//
-// If such an item doesn't exist, this has no effect.
-BASE_EXPORT void RemoveSampleMetadata(StringPiece name);
-
-// Removes the metadata item with the specified (|name|, |key|) pair from the
-// process-global stack sampling profiler metadata. This function does not alter
-// values set with the name |name| but no key.
-//
-// If such an item doesn't exist, this has no effect.
-BASE_EXPORT void RemoveSampleMetadata(StringPiece name, int64_t key);
-
// Applies the specified metadata to samples already recorded between
// |period_start| and |period_end| in all thread's active profiles, subject to
// the condition that the profile fully encompasses the period and the profile
diff --git a/chromium/base/profiler/sample_metadata_unittest.cc b/chromium/base/profiler/sample_metadata_unittest.cc
index fbb9ddf2818..7c76ce4e435 100644
--- a/chromium/base/profiler/sample_metadata_unittest.cc
+++ b/chromium/base/profiler/sample_metadata_unittest.cc
@@ -10,79 +10,81 @@
namespace base {
TEST(SampleMetadataTest, ScopedSampleMetadata) {
- ProfileBuilder::MetadataItemArray items;
- ASSERT_EQ(0u, GetSampleMetadataRecorder()->CreateMetadataProvider()->GetItems(
- &items));
+ MetadataRecorder::ItemArray items;
+ ASSERT_EQ(0u, MetadataRecorder::MetadataProvider(GetSampleMetadataRecorder())
+ .GetItems(&items));
{
ScopedSampleMetadata m("myname", 100);
ASSERT_EQ(1u,
- GetSampleMetadataRecorder()->CreateMetadataProvider()->GetItems(
- &items));
+ MetadataRecorder::MetadataProvider(GetSampleMetadataRecorder())
+ .GetItems(&items));
EXPECT_EQ(HashMetricName("myname"), items[0].name_hash);
EXPECT_FALSE(items[0].key.has_value());
EXPECT_EQ(100, items[0].value);
}
- ASSERT_EQ(0u, GetSampleMetadataRecorder()->CreateMetadataProvider()->GetItems(
- &items));
+ ASSERT_EQ(0u, MetadataRecorder::MetadataProvider(GetSampleMetadataRecorder())
+ .GetItems(&items));
}
TEST(SampleMetadataTest, ScopedSampleMetadataWithKey) {
- ProfileBuilder::MetadataItemArray items;
- ASSERT_EQ(0u, GetSampleMetadataRecorder()->CreateMetadataProvider()->GetItems(
- &items));
+ MetadataRecorder::ItemArray items;
+ ASSERT_EQ(0u, MetadataRecorder::MetadataProvider(GetSampleMetadataRecorder())
+ .GetItems(&items));
{
ScopedSampleMetadata m("myname", 10, 100);
ASSERT_EQ(1u,
- GetSampleMetadataRecorder()->CreateMetadataProvider()->GetItems(
- &items));
+ MetadataRecorder::MetadataProvider(GetSampleMetadataRecorder())
+ .GetItems(&items));
EXPECT_EQ(HashMetricName("myname"), items[0].name_hash);
ASSERT_TRUE(items[0].key.has_value());
EXPECT_EQ(10, *items[0].key);
EXPECT_EQ(100, items[0].value);
}
- ASSERT_EQ(0u, GetSampleMetadataRecorder()->CreateMetadataProvider()->GetItems(
- &items));
+ ASSERT_EQ(0u, MetadataRecorder::MetadataProvider(GetSampleMetadataRecorder())
+ .GetItems(&items));
}
TEST(SampleMetadataTest, SampleMetadata) {
- ProfileBuilder::MetadataItemArray items;
- ASSERT_EQ(0u, GetSampleMetadataRecorder()->CreateMetadataProvider()->GetItems(
- &items));
-
- SetSampleMetadata("myname", 100);
- ASSERT_EQ(1u, GetSampleMetadataRecorder()->CreateMetadataProvider()->GetItems(
- &items));
+ MetadataRecorder::ItemArray items;
+ ASSERT_EQ(0u, MetadataRecorder::MetadataProvider(GetSampleMetadataRecorder())
+ .GetItems(&items));
+
+ SampleMetadata metadata("myname");
+ metadata.Set(100);
+ ASSERT_EQ(1u, MetadataRecorder::MetadataProvider(GetSampleMetadataRecorder())
+ .GetItems(&items));
EXPECT_EQ(HashMetricName("myname"), items[0].name_hash);
EXPECT_FALSE(items[0].key.has_value());
EXPECT_EQ(100, items[0].value);
- RemoveSampleMetadata("myname");
- ASSERT_EQ(0u, GetSampleMetadataRecorder()->CreateMetadataProvider()->GetItems(
- &items));
+ metadata.Remove();
+ ASSERT_EQ(0u, MetadataRecorder::MetadataProvider(GetSampleMetadataRecorder())
+ .GetItems(&items));
}
TEST(SampleMetadataTest, SampleMetadataWithKey) {
- ProfileBuilder::MetadataItemArray items;
- ASSERT_EQ(0u, GetSampleMetadataRecorder()->CreateMetadataProvider()->GetItems(
- &items));
-
- SetSampleMetadata("myname", 10, 100);
- ASSERT_EQ(1u, GetSampleMetadataRecorder()->CreateMetadataProvider()->GetItems(
- &items));
+ MetadataRecorder::ItemArray items;
+ ASSERT_EQ(0u, MetadataRecorder::MetadataProvider(GetSampleMetadataRecorder())
+ .GetItems(&items));
+
+ SampleMetadata metadata("myname");
+ metadata.Set(10, 100);
+ ASSERT_EQ(1u, MetadataRecorder::MetadataProvider(GetSampleMetadataRecorder())
+ .GetItems(&items));
EXPECT_EQ(HashMetricName("myname"), items[0].name_hash);
ASSERT_TRUE(items[0].key.has_value());
EXPECT_EQ(10, *items[0].key);
EXPECT_EQ(100, items[0].value);
- RemoveSampleMetadata("myname", 10);
- ASSERT_EQ(0u, GetSampleMetadataRecorder()->CreateMetadataProvider()->GetItems(
- &items));
+ metadata.Remove(10);
+ ASSERT_EQ(0u, MetadataRecorder::MetadataProvider(GetSampleMetadataRecorder())
+ .GetItems(&items));
}
} // namespace base
diff --git a/chromium/base/profiler/stack_copier.h b/chromium/base/profiler/stack_copier.h
index b1eedcb6bbf..245ba276d37 100644
--- a/chromium/base/profiler/stack_copier.h
+++ b/chromium/base/profiler/stack_copier.h
@@ -35,9 +35,6 @@ class BASE_EXPORT StackCopier {
// deallocation, including indirectly via use of DCHECK/CHECK or other
// logging statements.
virtual void OnStackCopy() = 0;
-
- // Invoked after the stack has been copied and the target thread resumed.
- virtual void OnThreadResume() = 0;
};
virtual ~StackCopier();
diff --git a/chromium/base/profiler/stack_copier_signal.cc b/chromium/base/profiler/stack_copier_signal.cc
index 1b601703e7c..bbef65bf520 100644
--- a/chromium/base/profiler/stack_copier_signal.cc
+++ b/chromium/base/profiler/stack_copier_signal.cc
@@ -229,8 +229,6 @@ bool StackCopierSignal::CopyStack(StackBuffer* stack_buffer,
}
}
- delegate->OnThreadResume();
-
const uintptr_t bottom = RegisterContextStackPointer(params.context);
for (uintptr_t* reg :
thread_delegate_->GetRegistersToRewrite(thread_context)) {
diff --git a/chromium/base/profiler/stack_copier_signal_unittest.cc b/chromium/base/profiler/stack_copier_signal_unittest.cc
index 6e2953c1764..2e321b7e9f4 100644
--- a/chromium/base/profiler/stack_copier_signal_unittest.cc
+++ b/chromium/base/profiler/stack_copier_signal_unittest.cc
@@ -63,25 +63,13 @@ class TargetThread : public SimpleThread {
class TestStackCopierDelegate : public StackCopier::Delegate {
public:
void OnStackCopy() override {
- // We can't EXPECT_FALSE(on_thread_resume_was_invoked_) here because that
- // invocation is not reentrant.
on_stack_copy_was_invoked_ = true;
}
- void OnThreadResume() override {
- EXPECT_TRUE(on_stack_copy_was_invoked_);
- on_thread_resume_was_invoked_ = true;
- }
-
bool on_stack_copy_was_invoked() const { return on_stack_copy_was_invoked_; }
- bool on_thread_resume_was_invoked() const {
- return on_thread_resume_was_invoked_;
- }
-
private:
bool on_stack_copy_was_invoked_ = false;
- bool on_thread_resume_was_invoked_ = false;
};
} // namespace
@@ -179,7 +167,6 @@ TEST(StackCopierSignalTest, MAYBE_CopyStackDelegateInvoked) {
ASSERT_TRUE(result);
EXPECT_TRUE(stack_copier_delegate.on_stack_copy_was_invoked());
- EXPECT_TRUE(stack_copier_delegate.on_thread_resume_was_invoked());
}
// Limit to 32-bit Android, which is the platform we care about for this
diff --git a/chromium/base/profiler/stack_copier_suspend.cc b/chromium/base/profiler/stack_copier_suspend.cc
index d570c77cb01..7e320f776c9 100644
--- a/chromium/base/profiler/stack_copier_suspend.cc
+++ b/chromium/base/profiler/stack_copier_suspend.cc
@@ -64,8 +64,6 @@ bool StackCopierSuspend::CopyStack(StackBuffer* stack_buffer,
StackBuffer::kPlatformStackAlignment, stack_buffer->buffer());
}
- delegate->OnThreadResume();
-
*stack_top = reinterpret_cast<uintptr_t>(stack_copy_bottom) + (top - bottom);
for (uintptr_t* reg :
diff --git a/chromium/base/profiler/stack_copier_suspend_unittest.cc b/chromium/base/profiler/stack_copier_suspend_unittest.cc
index a81eca588f3..926afd3746c 100644
--- a/chromium/base/profiler/stack_copier_suspend_unittest.cc
+++ b/chromium/base/profiler/stack_copier_suspend_unittest.cc
@@ -85,25 +85,13 @@ class TestSuspendableThreadDelegate : public SuspendableThreadDelegate {
class TestStackCopierDelegate : public StackCopier::Delegate {
public:
void OnStackCopy() override {
- // We can't EXPECT_FALSE(on_thread_resume_was_invoked_) here because that
- // invocation is not reentrant.
on_stack_copy_was_invoked_ = true;
}
- void OnThreadResume() override {
- EXPECT_TRUE(on_stack_copy_was_invoked_);
- on_thread_resume_was_invoked_ = true;
- }
-
bool on_stack_copy_was_invoked() const { return on_stack_copy_was_invoked_; }
- bool on_thread_resume_was_invoked() const {
- return on_thread_resume_was_invoked_;
- }
-
private:
bool on_stack_copy_was_invoked_ = false;
- bool on_thread_resume_was_invoked_ = false;
};
} // namespace
@@ -218,7 +206,6 @@ TEST(StackCopierSuspendTest, CopyStackDelegateInvoked) {
&register_context, &stack_copier_delegate);
EXPECT_TRUE(stack_copier_delegate.on_stack_copy_was_invoked());
- EXPECT_TRUE(stack_copier_delegate.on_thread_resume_was_invoked());
}
TEST(StackCopierSuspendTest, RewriteRegisters) {
diff --git a/chromium/base/profiler/stack_sampler.h b/chromium/base/profiler/stack_sampler.h
index 517c3a99e24..9fc91051e85 100644
--- a/chromium/base/profiler/stack_sampler.h
+++ b/chromium/base/profiler/stack_sampler.h
@@ -33,8 +33,8 @@ class BASE_EXPORT StackSampler {
static std::unique_ptr<StackSampler> Create(
SamplingProfilerThreadToken thread_token,
ModuleCache* module_cache,
- StackSamplerTestDelegate* test_delegate,
- std::unique_ptr<Unwinder> native_unwinder = nullptr);
+ std::unique_ptr<Unwinder> native_unwinder,
+ StackSamplerTestDelegate* test_delegate);
// Gets the required size of the stack buffer.
static size_t GetStackBufferSize();
@@ -47,7 +47,9 @@ class BASE_EXPORT StackSampler {
// thread being sampled).
// Adds an auxiliary unwinder to handle additional, non-native-code unwind
- // scenarios.
+ // 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
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 80ec117fc81..8414de9e3f4 100644
--- a/chromium/base/profiler/stack_sampler_android.cc
+++ b/chromium/base/profiler/stack_sampler_android.cc
@@ -6,6 +6,7 @@
#include <pthread.h>
+#include "base/check.h"
#include "base/profiler/stack_copier_signal.h"
#include "base/profiler/stack_sampler_impl.h"
#include "base/profiler/thread_delegate_posix.h"
@@ -17,8 +18,9 @@ namespace base {
std::unique_ptr<StackSampler> StackSampler::Create(
SamplingProfilerThreadToken thread_token,
ModuleCache* module_cache,
- StackSamplerTestDelegate* test_delegate,
- std::unique_ptr<Unwinder> native_unwinder) {
+ std::unique_ptr<Unwinder> native_unwinder,
+ StackSamplerTestDelegate* test_delegate) {
+ DCHECK(native_unwinder);
return std::make_unique<StackSamplerImpl>(
std::make_unique<StackCopierSignal>(
std::make_unique<ThreadDelegatePosix>(thread_token)),
diff --git a/chromium/base/profiler/stack_sampler_impl.cc b/chromium/base/profiler/stack_sampler_impl.cc
index 336decd9f8b..9903b33feea 100644
--- a/chromium/base/profiler/stack_sampler_impl.cc
+++ b/chromium/base/profiler/stack_sampler_impl.cc
@@ -6,8 +6,10 @@
#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"
#include "base/profiler/stack_buffer.h"
@@ -31,16 +33,13 @@ namespace {
// the thread is suspended.
class StackCopierDelegate : public StackCopier::Delegate {
public:
- StackCopierDelegate(ModuleCache* module_cache,
- Unwinder* native_unwinder,
- Unwinder* aux_unwinder,
- ProfileBuilder* profile_builder)
- : module_cache_(module_cache),
- native_unwinder_(native_unwinder),
- aux_unwinder_(aux_unwinder),
+ StackCopierDelegate(
+ const base::circular_deque<std::unique_ptr<Unwinder>>* unwinders,
+ ProfileBuilder* profile_builder,
+ MetadataRecorder::MetadataProvider* metadata_provider)
+ : unwinders_(unwinders),
profile_builder_(profile_builder),
- metadata_provider_(
- GetSampleMetadataRecorder()->CreateMetadataProvider()) {}
+ metadata_provider_(metadata_provider) {}
StackCopierDelegate(const StackCopierDelegate&) = delete;
StackCopierDelegate& operator=(const StackCopierDelegate&) = delete;
@@ -51,35 +50,16 @@ class StackCopierDelegate : public StackCopier::Delegate {
// particular, it may not perform any heap allocation or deallocation,
// including indirectly via use of DCHECK/CHECK or other logging statements.
void OnStackCopy() override {
- native_unwinder_->OnStackCapture();
- if (aux_unwinder_)
- aux_unwinder_->OnStackCapture();
-
-#if !defined(OS_POSIX) || defined(OS_MACOSX)
- profile_builder_->RecordMetadata(metadata_provider_.get());
-#else
- // TODO(https://crbug.com/1056283): Support metadata recording on POSIX
- // platforms.
- ALLOW_UNUSED_LOCAL(profile_builder_);
-#endif
- }
-
- void OnThreadResume() override {
- // Reset this as soon as possible because it may hold a lock on the
- // metadata.
- metadata_provider_.reset();
+ for (const auto& unwinder : *unwinders_)
+ unwinder->OnStackCapture();
- native_unwinder_->UpdateModules(module_cache_);
- if (aux_unwinder_)
- aux_unwinder_->UpdateModules(module_cache_);
+ profile_builder_->RecordMetadata(*metadata_provider_);
}
private:
- ModuleCache* const module_cache_;
- Unwinder* const native_unwinder_;
- Unwinder* const aux_unwinder_;
+ const base::circular_deque<std::unique_ptr<Unwinder>>* unwinders_;
ProfileBuilder* const profile_builder_;
- std::unique_ptr<ProfileBuilder::MetadataProvider> metadata_provider_;
+ const MetadataRecorder::MetadataProvider* const metadata_provider_;
};
} // namespace
@@ -89,15 +69,17 @@ StackSamplerImpl::StackSamplerImpl(std::unique_ptr<StackCopier> stack_copier,
ModuleCache* module_cache,
StackSamplerTestDelegate* test_delegate)
: stack_copier_(std::move(stack_copier)),
- native_unwinder_(std::move(native_unwinder)),
module_cache_(module_cache),
- test_delegate_(test_delegate) {}
+ test_delegate_(test_delegate) {
+ DCHECK(native_unwinder);
+ unwinders_.push_front(std::move(native_unwinder));
+}
StackSamplerImpl::~StackSamplerImpl() = default;
void StackSamplerImpl::AddAuxUnwinder(std::unique_ptr<Unwinder> unwinder) {
- aux_unwinder_ = std::move(unwinder);
- aux_unwinder_->AddInitialModules(module_cache_);
+ unwinder->AddInitialModules(module_cache_);
+ unwinders_.push_front(std::move(unwinder));
}
void StackSamplerImpl::RecordStackFrames(StackBuffer* stack_buffer,
@@ -107,19 +89,26 @@ void StackSamplerImpl::RecordStackFrames(StackBuffer* stack_buffer,
RegisterContext thread_context;
uintptr_t stack_top;
TimeTicks timestamp;
- StackCopierDelegate delegate(module_cache_, native_unwinder_.get(),
- aux_unwinder_.get(), profile_builder);
- bool success = stack_copier_->CopyStack(stack_buffer, &stack_top, &timestamp,
- &thread_context, &delegate);
- if (!success)
- return;
+ {
+ // Make this scope as small as possible because |metadata_provider| is
+ // holding a lock.
+ MetadataRecorder::MetadataProvider metadata_provider(
+ GetSampleMetadataRecorder());
+ StackCopierDelegate delegate(&unwinders_, profile_builder,
+ &metadata_provider);
+ bool success = stack_copier_->CopyStack(
+ stack_buffer, &stack_top, &timestamp, &thread_context, &delegate);
+ if (!success)
+ return;
+ }
+ for (const auto& unwinder : unwinders_)
+ unwinder->UpdateModules(module_cache_);
if (test_delegate_)
test_delegate_->OnPreStackWalk();
profile_builder->OnSampleCompleted(
- WalkStack(module_cache_, &thread_context, stack_top,
- native_unwinder_.get(), aux_unwinder_.get()),
+ WalkStack(module_cache_, &thread_context, stack_top, unwinders_),
timestamp);
}
@@ -128,18 +117,16 @@ std::vector<Frame> StackSamplerImpl::WalkStackForTesting(
ModuleCache* module_cache,
RegisterContext* thread_context,
uintptr_t stack_top,
- Unwinder* native_unwinder,
- Unwinder* aux_unwinder) {
- return WalkStack(module_cache, thread_context, stack_top, native_unwinder,
- aux_unwinder);
+ const base::circular_deque<std::unique_ptr<Unwinder>>& unwinders) {
+ return WalkStack(module_cache, thread_context, stack_top, unwinders);
}
// static
-std::vector<Frame> StackSamplerImpl::WalkStack(ModuleCache* module_cache,
- RegisterContext* thread_context,
- uintptr_t stack_top,
- Unwinder* native_unwinder,
- Unwinder* aux_unwinder) {
+std::vector<Frame> StackSamplerImpl::WalkStack(
+ ModuleCache* module_cache,
+ RegisterContext* thread_context,
+ uintptr_t stack_top,
+ const base::circular_deque<std::unique_ptr<Unwinder>>& unwinders) {
std::vector<Frame> stack;
// Reserve enough memory for most stacks, to avoid repeated
// allocations. Approximately 99.9% of recorded stacks are 128 frames or
@@ -154,21 +141,24 @@ std::vector<Frame> StackSamplerImpl::WalkStack(ModuleCache* module_cache,
size_t prior_stack_size;
UnwindResult result;
do {
- // Choose an authoritative unwinder for the current module. Use the aux
- // unwinder if it thinks it can unwind from the current frame, otherwise use
- // the native unwinder.
- Unwinder* unwinder =
- aux_unwinder && aux_unwinder->CanUnwindFrom(stack.back())
- ? aux_unwinder
- : native_unwinder;
+ // Choose an authoritative unwinder for the current module. Use the first
+ // unwinder that thinks it can unwind from the current frame.
+ auto unwinder =
+ std::find_if(unwinders.begin(), unwinders.end(),
+ [&stack](const std::unique_ptr<Unwinder>& unwinder) {
+ return unwinder->CanUnwindFrom(stack.back());
+ });
+ if (unwinder == unwinders.end())
+ return stack;
prior_stack_size = stack.size();
- result =
- unwinder->TryUnwind(thread_context, stack_top, module_cache, &stack);
+ 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.
- DCHECK(result != UnwindResult::COMPLETED || unwinder == native_unwinder);
+ DCHECK(result != UnwindResult::COMPLETED ||
+ unwinder->get() == unwinders.back().get());
} while (result != UnwindResult::ABORTED &&
result != UnwindResult::COMPLETED &&
// Give up if the authoritative unwinder for the module was unable to
diff --git a/chromium/base/profiler/stack_sampler_impl.h b/chromium/base/profiler/stack_sampler_impl.h
index 725905e0ea9..cceee652f12 100644
--- a/chromium/base/profiler/stack_sampler_impl.h
+++ b/chromium/base/profiler/stack_sampler_impl.h
@@ -8,6 +8,7 @@
#include <memory>
#include "base/base_export.h"
+#include "base/containers/circular_deque.h"
#include "base/profiler/frame.h"
#include "base/profiler/register_context.h"
#include "base/profiler/stack_copier.h"
@@ -36,22 +37,22 @@ class BASE_EXPORT StackSamplerImpl : public StackSampler {
ProfileBuilder* profile_builder) override;
// Exposes the internal function for unit testing.
- static std::vector<Frame> WalkStackForTesting(ModuleCache* module_cache,
- RegisterContext* thread_context,
- uintptr_t stack_top,
- Unwinder* native_unwinder,
- Unwinder* aux_unwinder);
+ static std::vector<Frame> WalkStackForTesting(
+ ModuleCache* module_cache,
+ RegisterContext* thread_context,
+ uintptr_t stack_top,
+ const base::circular_deque<std::unique_ptr<Unwinder>>& unwinders);
private:
- static std::vector<Frame> WalkStack(ModuleCache* module_cache,
- RegisterContext* thread_context,
- uintptr_t stack_top,
- Unwinder* native_unwinder,
- Unwinder* aux_unwinder);
+ static std::vector<Frame> WalkStack(
+ ModuleCache* module_cache,
+ RegisterContext* thread_context,
+ uintptr_t stack_top,
+ const base::circular_deque<std::unique_ptr<Unwinder>>& unwinders);
const std::unique_ptr<StackCopier> stack_copier_;
- const std::unique_ptr<Unwinder> native_unwinder_;
- std::unique_ptr<Unwinder> aux_unwinder_;
+ // Store all unwinder in decreasing priority order.
+ base::circular_deque<std::unique_ptr<Unwinder>> unwinders_;
ModuleCache* const module_cache_;
StackSamplerTestDelegate* const test_delegate_;
};
diff --git a/chromium/base/profiler/stack_sampler_impl_unittest.cc b/chromium/base/profiler/stack_sampler_impl_unittest.cc
index 30a40af2ee3..e3cd67ce59b 100644
--- a/chromium/base/profiler/stack_sampler_impl_unittest.cc
+++ b/chromium/base/profiler/stack_sampler_impl_unittest.cc
@@ -9,6 +9,7 @@
#include <numeric>
#include <utility>
+#include "base/memory/ptr_util.h"
#include "base/profiler/module_cache.h"
#include "base/profiler/profile_builder.h"
#include "base/profiler/stack_buffer.h"
@@ -37,7 +38,7 @@ class TestProfileBuilder : public ProfileBuilder {
// ProfileBuilder
ModuleCache* GetModuleCache() override { return module_cache_; }
void RecordMetadata(
- ProfileBuilder::MetadataProvider* metadata_provider) override {}
+ const MetadataRecorder::MetadataProvider& metadata_provider) override {}
void OnSampleCompleted(std::vector<Frame> frames,
TimeTicks sample_timestamp) override {
@@ -97,7 +98,6 @@ class DelegateInvokingStackCopier : public StackCopier {
RegisterContext* thread_context,
Delegate* delegate) override {
delegate->OnStackCopy();
- delegate->OnThreadResume();
return true;
}
};
@@ -268,6 +268,17 @@ class FakeTestUnwinder : public Unwinder {
std::vector<Result> results_;
};
+base::circular_deque<std::unique_ptr<Unwinder>> MakeUnwinderList(
+ 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));
+ return unwinders;
+}
+
} // namespace
// TODO(crbug.com/1001923): Fails on Linux MSan.
@@ -351,10 +362,12 @@ TEST(StackSamplerImplTest, WalkStack_Completed) {
RegisterContextInstructionPointer(&thread_context) =
GetTestInstructionPointer();
module_cache.AddCustomNativeModule(std::make_unique<TestModule>(1u, 1u));
- FakeTestUnwinder native_unwinder({{UnwindResult::COMPLETED, {1u}}});
+ auto native_unwinder =
+ WrapUnique(new FakeTestUnwinder({{UnwindResult::COMPLETED, {1u}}}));
std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting(
- &module_cache, &thread_context, 0u, &native_unwinder, nullptr);
+ &module_cache, &thread_context, 0u,
+ MakeUnwinderList(std::move(native_unwinder), nullptr));
ASSERT_EQ(2u, stack.size());
EXPECT_EQ(1u, stack[1].instruction_pointer);
@@ -366,10 +379,12 @@ TEST(StackSamplerImplTest, WalkStack_Aborted) {
RegisterContextInstructionPointer(&thread_context) =
GetTestInstructionPointer();
module_cache.AddCustomNativeModule(std::make_unique<TestModule>(1u, 1u));
- FakeTestUnwinder native_unwinder({{UnwindResult::ABORTED, {1u}}});
+ auto native_unwinder =
+ WrapUnique(new FakeTestUnwinder({{UnwindResult::ABORTED, {1u}}}));
std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting(
- &module_cache, &thread_context, 0u, &native_unwinder, nullptr);
+ &module_cache, &thread_context, 0u,
+ MakeUnwinderList(std::move(native_unwinder), nullptr));
ASSERT_EQ(2u, stack.size());
EXPECT_EQ(1u, stack[1].instruction_pointer);
@@ -380,10 +395,12 @@ TEST(StackSamplerImplTest, WalkStack_NotUnwound) {
RegisterContext thread_context;
RegisterContextInstructionPointer(&thread_context) =
GetTestInstructionPointer();
- FakeTestUnwinder native_unwinder({{UnwindResult::UNRECOGNIZED_FRAME, {}}});
+ auto native_unwinder = WrapUnique(
+ new FakeTestUnwinder({{UnwindResult::UNRECOGNIZED_FRAME, {}}}));
std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting(
- &module_cache, &thread_context, 0u, &native_unwinder, nullptr);
+ &module_cache, &thread_context, 0u,
+ MakeUnwinderList(std::move(native_unwinder), nullptr));
ASSERT_EQ(1u, stack.size());
}
@@ -400,10 +417,11 @@ TEST(StackSamplerImplTest, WalkStack_AuxUnwind) {
{}, ToModuleVector(std::make_unique<TestModule>(
GetTestInstructionPointer(), 1u, false)));
- FakeTestUnwinder aux_unwinder({{UnwindResult::ABORTED, {1u}}});
-
+ auto aux_unwinder =
+ WrapUnique(new FakeTestUnwinder({{UnwindResult::ABORTED, {1u}}}));
std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting(
- &module_cache, &thread_context, 0u, nullptr, &aux_unwinder);
+ &module_cache, &thread_context, 0u,
+ MakeUnwinderList(nullptr, std::move(aux_unwinder)));
ASSERT_EQ(2u, stack.size());
EXPECT_EQ(GetTestInstructionPointer(), stack[0].instruction_pointer);
@@ -422,12 +440,14 @@ TEST(StackSamplerImplTest, WalkStack_AuxThenNative) {
// Inject a fake native module for the second frame.
module_cache.AddCustomNativeModule(std::make_unique<TestModule>(1u, 1u));
- FakeTestUnwinder aux_unwinder(
- {{UnwindResult::UNRECOGNIZED_FRAME, {1u}}, false});
- FakeTestUnwinder native_unwinder({{UnwindResult::COMPLETED, {2u}}});
+ auto aux_unwinder = WrapUnique(
+ new FakeTestUnwinder({{UnwindResult::UNRECOGNIZED_FRAME, {1u}}, false}));
+ auto native_unwinder =
+ WrapUnique(new FakeTestUnwinder({{UnwindResult::COMPLETED, {2u}}}));
std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting(
- &module_cache, &thread_context, 0u, &native_unwinder, &aux_unwinder);
+ &module_cache, &thread_context, 0u,
+ MakeUnwinderList(std::move(native_unwinder), std::move(aux_unwinder)));
ASSERT_EQ(3u, stack.size());
EXPECT_EQ(0u, stack[0].instruction_pointer);
@@ -449,13 +469,15 @@ TEST(StackSamplerImplTest, WalkStack_NativeThenAux) {
module_cache.UpdateNonNativeModules(
{}, ToModuleVector(std::make_unique<TestModule>(1u, 1u, false)));
- FakeTestUnwinder aux_unwinder(
- {{false}, {UnwindResult::UNRECOGNIZED_FRAME, {2u}}, {false}});
- FakeTestUnwinder native_unwinder({{UnwindResult::UNRECOGNIZED_FRAME, {1u}},
- {UnwindResult::COMPLETED, {3u}}});
+ auto aux_unwinder = WrapUnique(new FakeTestUnwinder(
+ {{false}, {UnwindResult::UNRECOGNIZED_FRAME, {2u}}, {false}}));
+ auto native_unwinder =
+ WrapUnique(new FakeTestUnwinder({{UnwindResult::UNRECOGNIZED_FRAME, {1u}},
+ {UnwindResult::COMPLETED, {3u}}}));
std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting(
- &module_cache, &thread_context, 0u, &native_unwinder, &aux_unwinder);
+ &module_cache, &thread_context, 0u,
+ MakeUnwinderList(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 ea2c91ede72..82ad01f3028 100644
--- a/chromium/base/profiler/stack_sampler_ios.cc
+++ b/chromium/base/profiler/stack_sampler_ios.cc
@@ -13,8 +13,8 @@ namespace base {
std::unique_ptr<StackSampler> StackSampler::Create(
SamplingProfilerThreadToken thread_token,
ModuleCache* module_cache,
- StackSamplerTestDelegate* test_delegate,
- std::unique_ptr<Unwinder> native_unwinder) {
+ std::unique_ptr<Unwinder> native_unwinder,
+ StackSamplerTestDelegate* test_delegate) {
return nullptr;
}
diff --git a/chromium/base/profiler/stack_sampler_mac.cc b/chromium/base/profiler/stack_sampler_mac.cc
index ef46c299cae..109f6425835 100644
--- a/chromium/base/profiler/stack_sampler_mac.cc
+++ b/chromium/base/profiler/stack_sampler_mac.cc
@@ -4,6 +4,7 @@
#include "base/profiler/stack_sampler.h"
+#include "base/check.h"
#include "base/profiler/native_unwinder_mac.h"
#include "base/profiler/stack_copier_suspend.h"
#include "base/profiler/stack_sampler_impl.h"
@@ -15,8 +16,8 @@ namespace base {
std::unique_ptr<StackSampler> StackSampler::Create(
SamplingProfilerThreadToken thread_token,
ModuleCache* module_cache,
- StackSamplerTestDelegate* test_delegate,
- std::unique_ptr<Unwinder> native_unwinder) {
+ std::unique_ptr<Unwinder> native_unwinder,
+ StackSamplerTestDelegate* test_delegate) {
DCHECK(!native_unwinder);
return std::make_unique<StackSamplerImpl>(
std::make_unique<StackCopierSuspend>(
diff --git a/chromium/base/profiler/stack_sampler_posix.cc b/chromium/base/profiler/stack_sampler_posix.cc
index a6728419e16..44215298c63 100644
--- a/chromium/base/profiler/stack_sampler_posix.cc
+++ b/chromium/base/profiler/stack_sampler_posix.cc
@@ -14,8 +14,8 @@ namespace base {
std::unique_ptr<StackSampler> StackSampler::Create(
SamplingProfilerThreadToken thread_token,
ModuleCache* module_cache,
- StackSamplerTestDelegate* test_delegate,
- std::unique_ptr<Unwinder> native_unwinder) {
+ std::unique_ptr<Unwinder> native_unwinder,
+ StackSamplerTestDelegate* test_delegate) {
return nullptr;
}
diff --git a/chromium/base/profiler/stack_sampler_win.cc b/chromium/base/profiler/stack_sampler_win.cc
index 6f6ff33900c..c19009b77e4 100644
--- a/chromium/base/profiler/stack_sampler_win.cc
+++ b/chromium/base/profiler/stack_sampler_win.cc
@@ -4,6 +4,7 @@
#include "base/profiler/stack_sampler.h"
+#include "base/check.h"
#include "base/profiler/native_unwinder_win.h"
#include "base/profiler/stack_copier_suspend.h"
#include "base/profiler/stack_sampler_impl.h"
@@ -16,8 +17,8 @@ namespace base {
std::unique_ptr<StackSampler> StackSampler::Create(
SamplingProfilerThreadToken thread_token,
ModuleCache* module_cache,
- StackSamplerTestDelegate* test_delegate,
- std::unique_ptr<Unwinder> native_unwinder) {
+ std::unique_ptr<Unwinder> native_unwinder,
+ StackSamplerTestDelegate* test_delegate) {
DCHECK(!native_unwinder);
#if defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64)
return std::make_unique<StackSamplerImpl>(
diff --git a/chromium/base/profiler/stack_sampling_profiler.cc b/chromium/base/profiler/stack_sampling_profiler.cc
index 62a0ceca452..46634d18aea 100644
--- a/chromium/base/profiler/stack_sampling_profiler.cc
+++ b/chromium/base/profiler/stack_sampling_profiler.cc
@@ -525,7 +525,7 @@ void StackSamplingProfiler::SamplingThread::ApplyMetadataToPastSamplesTask(
Optional<int64_t> key,
int64_t value) {
DCHECK_EQ(GetThreadId(), PlatformThread::CurrentId());
- ProfileBuilder::MetadataItem item(name_hash, key, value);
+ MetadataRecorder::Item item(name_hash, key, value);
for (auto& id_collection_pair : active_collections_) {
id_collection_pair.second->profile_builder->ApplyMetadataRetrospectively(
period_start, period_end, item);
@@ -693,10 +693,12 @@ StackSamplingProfiler::StackSamplingProfiler(
SamplingProfilerThreadToken thread_token,
const SamplingParams& params,
std::unique_ptr<ProfileBuilder> profile_builder,
+ std::unique_ptr<Unwinder> native_unwinder,
StackSamplerTestDelegate* test_delegate)
: StackSamplingProfiler(params, std::move(profile_builder), nullptr) {
- sampler_ = StackSampler::Create(
- thread_token, profile_builder_->GetModuleCache(), test_delegate);
+ sampler_ =
+ StackSampler::Create(thread_token, profile_builder_->GetModuleCache(),
+ std::move(native_unwinder), test_delegate);
}
StackSamplingProfiler::StackSamplingProfiler(
diff --git a/chromium/base/profiler/stack_sampling_profiler.h b/chromium/base/profiler/stack_sampling_profiler.h
index 846d2eaf27c..c6784b8a8f4 100644
--- a/chromium/base/profiler/stack_sampling_profiler.h
+++ b/chromium/base/profiler/stack_sampling_profiler.h
@@ -13,6 +13,7 @@
#include "base/optional.h"
#include "base/profiler/profile_builder.h"
#include "base/profiler/sampling_profiler_thread_token.h"
+#include "base/profiler/unwinder.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
@@ -83,14 +84,17 @@ class BASE_EXPORT StackSamplingProfiler {
bool keep_consistent_sampling_interval = true;
};
- // Creates a profiler for the specified thread. An optional |test_delegate|
- // can be supplied by tests.
+ // 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.
//
// 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);
// Same as above function, with custom |sampler| implementation. The sampler
diff --git a/chromium/base/profiler/stack_sampling_profiler_test_util.cc b/chromium/base/profiler/stack_sampling_profiler_test_util.cc
index f9ad0921a39..9043d0627be 100644
--- a/chromium/base/profiler/stack_sampling_profiler_test_util.cc
+++ b/chromium/base/profiler/stack_sampling_profiler_test_util.cc
@@ -9,12 +9,24 @@
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/location.h"
+#include "base/path_service.h"
+#include "base/profiler/stack_buffer.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "base/profiler/unwinder.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind_test_util.h"
+#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if defined(OS_WIN)
+// Windows doesn't provide an alloca function like Linux does.
+// Fortunately, it provides _alloca, which functions identically.
+#include <malloc.h>
+#define alloca _alloca
+#else
+#include <alloca.h>
+#endif
+
namespace base {
namespace {
@@ -35,7 +47,8 @@ class TestProfileBuilder : public ProfileBuilder {
// ProfileBuilder:
ModuleCache* GetModuleCache() override { return module_cache_; }
- void RecordMetadata(MetadataProvider* metadata_provider) override {}
+ void RecordMetadata(
+ const MetadataRecorder::MetadataProvider& metadata_provider) override {}
void OnSampleCompleted(std::vector<Frame> sample,
TimeTicks sample_timestamp) override {
@@ -55,6 +68,17 @@ class TestProfileBuilder : public ProfileBuilder {
std::vector<Frame> sample_;
};
+// The function to be executed by the code in the other library.
+void OtherLibraryCallback(void* arg) {
+ OnceClosure* wait_for_sample = static_cast<OnceClosure*>(arg);
+
+ std::move(*wait_for_sample).Run();
+
+ // Prevent tail call.
+ volatile int i = 0;
+ ALLOW_UNUSED_LOCAL(i);
+}
+
} // namespace
TargetThread::TargetThread(OnceClosure to_run) : to_run_(std::move(to_run)) {}
@@ -134,6 +158,47 @@ CallWithPlainFunction(OnceClosure wait_for_sample) {
return {start_program_counter, end_program_counter};
}
+// Disable inlining for this function so that it gets its own stack frame.
+NOINLINE FunctionAddressRange CallWithAlloca(OnceClosure wait_for_sample) {
+ const void* start_program_counter = GetProgramCounter();
+
+ // Volatile to force a dynamic stack allocation.
+ const volatile size_t alloca_size = 100;
+ // Use the memory via volatile writes to prevent the allocation from being
+ // optimized out.
+ volatile char* const allocation =
+ const_cast<volatile char*>(static_cast<char*>(alloca(alloca_size)));
+ for (volatile char* p = allocation; p < allocation + alloca_size; ++p)
+ *p = '\0';
+
+ if (!wait_for_sample.is_null())
+ std::move(wait_for_sample).Run();
+
+ // Volatile to prevent a tail call to GetProgramCounter().
+ const void* volatile end_program_counter = GetProgramCounter();
+ return {start_program_counter, end_program_counter};
+}
+
+// Disable inlining for this function so that it gets its own stack frame.
+NOINLINE FunctionAddressRange
+CallThroughOtherLibrary(NativeLibrary library, OnceClosure wait_for_sample) {
+ const void* start_program_counter = GetProgramCounter();
+
+ if (!wait_for_sample.is_null()) {
+ // A function whose arguments are a function accepting void*, and a void*.
+ using InvokeCallbackFunction = void (*)(void (*)(void*), void*);
+ EXPECT_TRUE(library);
+ InvokeCallbackFunction function = reinterpret_cast<InvokeCallbackFunction>(
+ GetFunctionPointerFromNativeLibrary(library, "InvokeCallbackFunction"));
+ EXPECT_TRUE(function);
+ (*function)(&OtherLibraryCallback, &wait_for_sample);
+ }
+
+ // Volatile to prevent a tail call to GetProgramCounter().
+ const void* volatile end_program_counter = GetProgramCounter();
+ return {start_program_counter, end_program_counter};
+}
+
void WithTargetThread(UnwindScenario* scenario,
ProfileCallback profile_callback) {
UnwindScenario::SampleEvents events;
@@ -246,4 +311,31 @@ void ExpectStackDoesNotContain(
}
}
+NativeLibrary LoadOtherLibrary() {
+ // The lambda gymnastics works around the fact that we can't use ASSERT_*
+ // macros in a function returning non-null.
+ const auto load = [](NativeLibrary* library) {
+ FilePath other_library_path;
+ ASSERT_TRUE(PathService::Get(DIR_MODULE, &other_library_path));
+ other_library_path = other_library_path.AppendASCII(
+ GetLoadableModuleName("base_profiler_test_support_library"));
+ NativeLibraryLoadError load_error;
+ *library = LoadNativeLibrary(other_library_path, &load_error);
+ ASSERT_TRUE(*library) << "error loading " << other_library_path.value()
+ << ": " << load_error.ToString();
+ };
+
+ NativeLibrary library = nullptr;
+ load(&library);
+ return library;
+}
+
+uintptr_t GetAddressInOtherLibrary(NativeLibrary library) {
+ EXPECT_TRUE(library);
+ uintptr_t address = reinterpret_cast<uintptr_t>(
+ GetFunctionPointerFromNativeLibrary(library, "InvokeCallbackFunction"));
+ EXPECT_NE(address, 0u);
+ return address;
+}
+
} // 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 2e0c2b9c9c1..ff2daebfe14 100644
--- a/chromium/base/profiler/stack_sampling_profiler_test_util.h
+++ b/chromium/base/profiler/stack_sampling_profiler_test_util.h
@@ -9,8 +9,10 @@
#include <vector>
#include "base/callback.h"
+#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"
@@ -91,6 +93,16 @@ class UnwindScenario {
// any special unwinding setup, to exercise the "normal" unwind scenario.
FunctionAddressRange CallWithPlainFunction(OnceClosure wait_for_sample);
+// Calls into |wait_for_sample| after using alloca(), to test unwinding with a
+// frame pointer.
+FunctionAddressRange CallWithAlloca(OnceClosure wait_for_sample);
+
+// Calls into |wait_for_sample| through a function within another library, to
+// test unwinding through multiple modules and scenarios involving unloaded
+// modules.
+FunctionAddressRange CallThroughOtherLibrary(NativeLibrary library,
+ OnceClosure wait_for_sample);
+
// The callback to perform profiling on the provided thread.
using ProfileCallback = OnceCallback<void(SamplingProfilerThreadToken)>;
@@ -122,6 +134,12 @@ void ExpectStackDoesNotContain(
const std::vector<Frame>& stack,
const std::vector<FunctionAddressRange>& functions);
+// Loads the other library, which defines a function to be called in the
+// WITH_OTHER_LIBRARY configuration.
+NativeLibrary LoadOtherLibrary();
+
+uintptr_t GetAddressInOtherLibrary(NativeLibrary library);
+
} // 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 071874700f5..b9fc305dd91 100644
--- a/chromium/base/profiler/stack_sampling_profiler_unittest.cc
+++ b/chromium/base/profiler/stack_sampling_profiler_unittest.cc
@@ -20,8 +20,6 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/metrics_hashes.h"
-#include "base/native_library.h"
-#include "base/path_service.h"
#include "base/profiler/sample_metadata.h"
#include "base/profiler/stack_sampler.h"
#include "base/profiler/stack_sampling_profiler.h"
@@ -66,69 +64,11 @@ using SamplingParams = StackSamplingProfiler::SamplingParams;
namespace {
-// Calls into |wait_for_sample| after using alloca(), to test unwinding with a
-// frame pointer.
-// Disable inlining for this function so that it gets its own stack frame.
-NOINLINE FunctionAddressRange CallWithAlloca(OnceClosure wait_for_sample) {
- const void* start_program_counter = GetProgramCounter();
-
- // Volatile to force a dynamic stack allocation.
- const volatile size_t alloca_size = 100;
- // Use the memory via volatile writes to prevent the allocation from being
- // optimized out.
- volatile char* const allocation =
- const_cast<volatile char*>(static_cast<char*>(alloca(alloca_size)));
- for (volatile char* p = allocation; p < allocation + alloca_size; ++p)
- *p = '\0';
-
- if (!wait_for_sample.is_null())
- std::move(wait_for_sample).Run();
-
- // Volatile to prevent a tail call to GetProgramCounter().
- const void* volatile end_program_counter = GetProgramCounter();
- return {start_program_counter, end_program_counter};
-}
-
-// The function to be executed by the code in the other library.
-void OtherLibraryCallback(void* arg) {
- OnceClosure* wait_for_sample = static_cast<OnceClosure*>(arg);
-
- std::move(*wait_for_sample).Run();
-
- // Prevent tail call.
- volatile int i = 0;
- ALLOW_UNUSED_LOCAL(i);
-}
-
-// Calls into |wait_for_sample| through a function within another library, to
-// test unwinding through multiple modules and scenarios involving unloaded
-// modules.
-// Disable inlining for this function so that it gets its own stack frame.
-NOINLINE FunctionAddressRange
-CallThroughOtherLibrary(NativeLibrary library, OnceClosure wait_for_sample) {
- const void* start_program_counter = GetProgramCounter();
-
- if (!wait_for_sample.is_null()) {
- // A function whose arguments are a function accepting void*, and a void*.
- using InvokeCallbackFunction = void (*)(void (*)(void*), void*);
- EXPECT_TRUE(library);
- InvokeCallbackFunction function = reinterpret_cast<InvokeCallbackFunction>(
- GetFunctionPointerFromNativeLibrary(library, "InvokeCallbackFunction"));
- EXPECT_TRUE(function);
-
- (*function)(&OtherLibraryCallback, &wait_for_sample);
- }
-
- // Volatile to prevent a tail call to GetProgramCounter().
- const void* volatile end_program_counter = GetProgramCounter();
- return {start_program_counter, end_program_counter};
-}
-
// State provided to the ProfileBuilder's ApplyMetadataRetrospectively function.
struct RetrospectiveMetadata {
TimeTicks period_start;
TimeTicks period_end;
- ProfileBuilder::MetadataItem item;
+ MetadataRecorder::Item item;
};
// Profile consists of a set of samples and other sampling information.
@@ -165,10 +105,11 @@ class TestProfileBuilder : public ProfileBuilder {
// ProfileBuilder:
ModuleCache* GetModuleCache() override;
void RecordMetadata(
- ProfileBuilder::MetadataProvider* metadata_provider) override;
- void ApplyMetadataRetrospectively(TimeTicks period_start,
- TimeTicks period_end,
- const MetadataItem& item) override;
+ const MetadataRecorder::MetadataProvider& metadata_provider) override;
+ void ApplyMetadataRetrospectively(
+ TimeTicks period_start,
+ TimeTicks period_end,
+ const MetadataRecorder::Item& item) override;
void OnSampleCompleted(std::vector<Frame> sample,
TimeTicks sample_timestamp) override;
void OnProfileCompleted(TimeDelta profile_duration,
@@ -203,14 +144,14 @@ ModuleCache* TestProfileBuilder::GetModuleCache() {
}
void TestProfileBuilder::RecordMetadata(
- ProfileBuilder::MetadataProvider* metadata_provider) {
+ const MetadataRecorder::MetadataProvider& metadata_provider) {
++record_metadata_count_;
}
void TestProfileBuilder::ApplyMetadataRetrospectively(
TimeTicks period_start,
TimeTicks period_end,
- const MetadataItem& item) {
+ const MetadataRecorder::Item& item) {
retrospective_metadata_.push_back(
RetrospectiveMetadata{period_start, period_end, item});
}
@@ -227,27 +168,6 @@ void TestProfileBuilder::OnProfileCompleted(TimeDelta profile_duration,
sampling_period});
}
-// Loads the other library, which defines a function to be called in the
-// WITH_OTHER_LIBRARY configuration.
-NativeLibrary LoadOtherLibrary() {
- // The lambda gymnastics works around the fact that we can't use ASSERT_*
- // macros in a function returning non-null.
- const auto load = [](NativeLibrary* library) {
- FilePath other_library_path;
- ASSERT_TRUE(PathService::Get(DIR_MODULE, &other_library_path));
- other_library_path = other_library_path.AppendASCII(
- GetLoadableModuleName("base_profiler_test_support_library"));
- NativeLibraryLoadError load_error;
- *library = LoadNativeLibrary(other_library_path, &load_error);
- ASSERT_TRUE(*library) << "error loading " << other_library_path.value()
- << ": " << load_error.ToString();
- };
-
- NativeLibrary library = nullptr;
- load(&library);
- return library;
-}
-
// Unloads |library| and returns when it has completed unloading. Unloading a
// library is asynchronous on Windows, so simply calling UnloadNativeLibrary()
// is insufficient to ensure it's been unloaded.
@@ -293,6 +213,7 @@ struct TestProfilerInfo {
profile = std::move(result_profile);
completed.Signal();
})),
+ nullptr,
delegate) {}
// The order here is important to ensure objects being referenced don't get
@@ -426,7 +347,7 @@ void TestLibraryUnload(bool wait_until_unloaded, ModuleCache* module_cache) {
profile = std::move(result_profile);
sampling_thread_completed.Signal();
})),
- &test_delegate);
+ nullptr, &test_delegate);
profiler.Start();
@@ -1439,7 +1360,7 @@ PROFILER_TEST_F(StackSamplingProfilerTest,
BindLambdaForTesting([&profile](Profile result_profile) {
profile = std::move(result_profile);
})),
- &post_sample_invoker);
+ nullptr, &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/suspendable_thread_delegate_mac.cc b/chromium/base/profiler/suspendable_thread_delegate_mac.cc
index 32fdd475cb2..a31a0caf2b1 100644
--- a/chromium/base/profiler/suspendable_thread_delegate_mac.cc
+++ b/chromium/base/profiler/suspendable_thread_delegate_mac.cc
@@ -8,7 +8,7 @@
#include <mach/thread_act.h>
#include <pthread.h>
-#include "base/logging.h"
+#include "base/check.h"
#include "base/mac/mach_logging.h"
#include "base/profiler/profile_builder.h"
diff --git a/chromium/base/profiler/suspendable_thread_delegate_win.cc b/chromium/base/profiler/suspendable_thread_delegate_win.cc
index e2a30cf34be..1fbc67eb3f2 100644
--- a/chromium/base/profiler/suspendable_thread_delegate_win.cc
+++ b/chromium/base/profiler/suspendable_thread_delegate_win.cc
@@ -7,8 +7,8 @@
#include <windows.h>
#include <winternl.h>
+#include "base/check.h"
#include "base/debug/alias.h"
-#include "base/logging.h"
#include "base/profiler/native_unwinder_win.h"
#include "build/build_config.h"
diff --git a/chromium/base/profiler/unwindstack_internal_android.cc b/chromium/base/profiler/unwindstack_internal_android.cc
new file mode 100644
index 00000000000..92328590aa4
--- /dev/null
+++ b/chromium/base/profiler/unwindstack_internal_android.cc
@@ -0,0 +1,30 @@
+// 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
new file mode 100644
index 00000000000..75058613fc7
--- /dev/null
+++ b/chromium/base/profiler/unwindstack_internal_android.h
@@ -0,0 +1,34 @@
+// 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