blob: 191de3af373d6b4b7e33f4f75e0c4b079525da95 [file] [log] [blame]
// 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 "components/metrics/call_stack_profile_metadata.h"
#include <algorithm>
#include <iterator>
#include <tuple>
namespace metrics {
CallStackProfileMetadata::CallStackProfileMetadata() = default;
CallStackProfileMetadata::~CallStackProfileMetadata() = default;
// This function is invoked on the profiler thread while the target thread is
// suspended so must not take any locks, including indirectly through use of
// heap allocation, LOG, CHECK, or DCHECK.
void CallStackProfileMetadata::RecordMetadata(
base::ProfileBuilder::MetadataProvider* metadata_provider) {
metadata_item_count_ = metadata_provider->GetItems(&metadata_items_);
}
google::protobuf::RepeatedPtrField<CallStackProfile::MetadataItem>
CallStackProfileMetadata::CreateSampleMetadata(
google::protobuf::RepeatedField<uint64_t>* metadata_name_hashes) {
DCHECK_EQ(metadata_hashes_cache_.size(),
static_cast<size_t>(metadata_name_hashes->size()));
google::protobuf::RepeatedPtrField<CallStackProfile::MetadataItem>
metadata_items;
MetadataMap current_items =
CreateMetadataMap(metadata_items_, metadata_item_count_);
for (auto item :
GetNewOrModifiedMetadataItems(current_items, previous_items_)) {
size_t name_hash_index =
MaybeAppendNameHash(item.first.name_hash, metadata_name_hashes);
CallStackProfile::MetadataItem* profile_item = metadata_items.Add();
profile_item->set_name_hash_index(name_hash_index);
if (item.first.key.has_value())
profile_item->set_key(*item.first.key);
profile_item->set_value(item.second);
}
for (auto item : GetDeletedMetadataItems(current_items, previous_items_)) {
size_t name_hash_index =
MaybeAppendNameHash(item.first.name_hash, metadata_name_hashes);
CallStackProfile::MetadataItem* profile_item = metadata_items.Add();
profile_item->set_name_hash_index(name_hash_index);
if (item.first.key.has_value())
profile_item->set_key(*item.first.key);
// Leave the value empty to indicate that the item was deleted.
}
previous_items_ = std::move(current_items);
metadata_item_count_ = 0;
return metadata_items;
}
bool CallStackProfileMetadata::MetadataKeyCompare::operator()(
const MetadataKey& a,
const MetadataKey& b) const {
return std::tie(a.name_hash, a.key) < std::tie(b.name_hash, b.key);
}
CallStackProfileMetadata::MetadataKey::MetadataKey(uint64_t name_hash,
base::Optional<int64_t> key)
: name_hash(name_hash), key(key) {}
CallStackProfileMetadata::MetadataKey::MetadataKey(const MetadataKey& other) =
default;
CallStackProfileMetadata::MetadataKey& CallStackProfileMetadata::MetadataKey::
operator=(const MetadataKey& other) = default;
CallStackProfileMetadata::MetadataMap
CallStackProfileMetadata::CreateMetadataMap(
base::ProfileBuilder::MetadataItemArray items,
size_t item_count) {
MetadataMap item_map;
for (size_t i = 0; i < item_count; ++i)
item_map[MetadataKey{items[i].name_hash, items[i].key}] = items[i].value;
return item_map;
}
CallStackProfileMetadata::MetadataMap
CallStackProfileMetadata::GetNewOrModifiedMetadataItems(
const MetadataMap& current_items,
const MetadataMap& previous_items) {
MetadataMap new_or_modified_items;
// Find the new or modified items by subtracting any previous items that are
// exactly the same as the current items (i.e. equal in key *and* value).
auto key_and_value_comparator = [](const std::pair<MetadataKey, int64_t>& a,
const std::pair<MetadataKey, int64_t>& b) {
return std::tie(a.first.name_hash, a.first.key, a.second) <
std::tie(b.first.name_hash, b.first.key, b.second);
};
std::set_difference(
current_items.begin(), current_items.end(), previous_items.begin(),
previous_items.end(),
std::inserter(new_or_modified_items, new_or_modified_items.begin()),
key_and_value_comparator);
return new_or_modified_items;
}
CallStackProfileMetadata::MetadataMap
CallStackProfileMetadata::GetDeletedMetadataItems(
const MetadataMap& current_items,
const MetadataMap& previous_items) {
MetadataMap deleted_items;
// Find the deleted metadata items by subtracting the current items from the
// previous items, comparing items solely map key (as opposed to map key and
// value as in GetNewOrModifiedMetadataItems()). Comparing by key is necessary
// to distinguish modified items from deleted items: subtraction of modified
// items, which have the same key but different values, should produce the
// empty set. Deleted items have a key only in |previous_items| so should be
// retained in the result.
auto key_comparator = [](const std::pair<MetadataKey, int64_t>& lhs,
const std::pair<MetadataKey, int64_t>& rhs) {
return MetadataKeyCompare()(lhs.first, rhs.first);
};
std::set_difference(previous_items.begin(), previous_items.end(),
current_items.begin(), current_items.end(),
std::inserter(deleted_items, deleted_items.begin()),
key_comparator);
return deleted_items;
}
size_t CallStackProfileMetadata::MaybeAppendNameHash(
uint64_t name_hash,
google::protobuf::RepeatedField<uint64_t>* metadata_name_hashes) {
std::unordered_map<uint64_t, int>::iterator it;
bool inserted;
int next_item_index = metadata_name_hashes->size();
std::tie(it, inserted) =
metadata_hashes_cache_.emplace(name_hash, next_item_index);
if (inserted)
metadata_name_hashes->Add(name_hash);
return it->second;
}
} // namespace metrics