Merge changes I2dfa0347,I951ff7a7 * changes: Do not blindly emplace in interner. Use unordered_set for interner.
diff --git a/src/profiling/memory/bookkeeping.h b/src/profiling/memory/bookkeeping.h index 32166c2..1559459 100644 --- a/src/profiling/memory/bookkeeping.h +++ b/src/profiling/memory/bookkeeping.h
@@ -17,6 +17,7 @@ #ifndef SRC_PROFILING_MEMORY_BOOKKEEPING_H_ #define SRC_PROFILING_MEMORY_BOOKKEEPING_H_ +#include <functional> #include <map> #include <string> #include <vector> @@ -110,6 +111,11 @@ std::tie(other.build_id, other.offset, other.start, other.end, other.load_bias, other.path_components); } + bool operator==(const Mapping& other) const { + return std::tie(build_id, offset, start, end, load_bias, path_components) == + std::tie(other.build_id, other.offset, other.start, other.end, + other.load_bias, other.path_components); + } }; struct Frame { @@ -123,6 +129,11 @@ return std::tie(mapping, function_name, rel_pc) < std::tie(other.mapping, other.function_name, other.rel_pc); } + + bool operator==(const Frame& other) const { + return std::tie(mapping, function_name, rel_pc) == + std::tie(other.mapping, other.function_name, other.rel_pc); + } }; // Graph of function callsites. This is shared between heap dumps for @@ -366,4 +377,35 @@ } // namespace profiling } // namespace perfetto +namespace std { +template <> +struct hash<::perfetto::profiling::Mapping> { + using argument_type = ::perfetto::profiling::Mapping; + using result_type = size_t; + result_type operator()(const argument_type& mapping) { + size_t h = + std::hash<::perfetto::profiling::InternID>{}(mapping.build_id.id()); + h ^= std::hash<uint64_t>{}(mapping.offset); + h ^= std::hash<uint64_t>{}(mapping.start); + h ^= std::hash<uint64_t>{}(mapping.end); + h ^= std::hash<uint64_t>{}(mapping.load_bias); + for (const auto& path : mapping.path_components) + h ^= std::hash<uint64_t>{}(path.id()); + return h; + } +}; + +template <> +struct hash<::perfetto::profiling::Frame> { + using argument_type = ::perfetto::profiling::Frame; + using result_type = size_t; + result_type operator()(const argument_type& frame) { + size_t h = std::hash<::perfetto::profiling::InternID>{}(frame.mapping.id()); + h ^= std::hash<::perfetto::profiling::InternID>{}(frame.function_name.id()); + h ^= std::hash<uint64_t>{}(frame.rel_pc); + return h; + } +}; +} // namespace std + #endif // SRC_PROFILING_MEMORY_BOOKKEEPING_H_
diff --git a/src/profiling/memory/interner.h b/src/profiling/memory/interner.h index e5b6ed1..e67fcb5 100644 --- a/src/profiling/memory/interner.h +++ b/src/profiling/memory/interner.h
@@ -19,7 +19,8 @@ #include <stddef.h> #include <stdint.h> -#include <set> +#include <functional> +#include <unordered_set> #include "perfetto/base/logging.h" @@ -37,6 +38,13 @@ : data(std::forward<U...>(args...)), id(i), interner(in) {} bool operator<(const Entry& other) const { return data < other.data; } + bool operator==(const Entry& other) const { return data == other.data; } + + struct Hash { + size_t operator()(const Entry& e) const noexcept { + return std::hash<T>{}(e.data); + } + }; const T data; size_t ref_count = 0; @@ -77,6 +85,10 @@ return entry_ < other.entry_; } + bool operator==(const Interned& other) const { + return entry_ == other.entry_; + } + const T* operator->() const { return &entry_->data; } private: @@ -85,11 +97,17 @@ template <typename... U> Interned Intern(U... args) { - auto itr_and_inserted = - entries_.emplace(this, next_id, std::forward<U...>(args...)); - if (itr_and_inserted.second) + Entry item(this, next_id, std::forward<U...>(args...)); + auto it = entries_.find(item); + if (it == entries_.cend()) { + // This does not invalidate pointers to entries we hold in Interned. See + // https://timsong-cpp.github.io/cppwp/n3337/unord.req#8 + auto it_and_inserted = entries_.emplace(std::move(item)); next_id++; - Entry& entry = const_cast<Entry&>(*itr_and_inserted.first); + it = it_and_inserted.first; + PERFETTO_DCHECK(it_and_inserted.second); + } + Entry& entry = const_cast<Entry&>(*it); entry.ref_count++; return Interned(&entry); } @@ -104,7 +122,7 @@ entries_.erase(*entry); } uint64_t next_id = 1; - std::set<Entry> entries_; + std::unordered_set<Entry, typename Entry::Hash> entries_; static_assert(sizeof(Interned) == sizeof(void*), "interned things should be small"); };
diff --git a/src/profiling/memory/interner_unittest.cc b/src/profiling/memory/interner_unittest.cc index dd166cb..6b239fd 100644 --- a/src/profiling/memory/interner_unittest.cc +++ b/src/profiling/memory/interner_unittest.cc
@@ -129,25 +129,6 @@ ASSERT_EQ(interner.entry_count_for_testing(), 0); } -class NoCopyOrMove { - public: - NoCopyOrMove(const NoCopyOrMove&) = delete; - NoCopyOrMove& operator=(const NoCopyOrMove&) = delete; - NoCopyOrMove(const NoCopyOrMove&&) = delete; - NoCopyOrMove& operator=(const NoCopyOrMove&&) = delete; - NoCopyOrMove(int d) : data(d) {} - ~NoCopyOrMove() {} - bool operator<(const NoCopyOrMove& other) const { return data < other.data; } - - private: - int data; -}; - -TEST(InternerStringTest, NoCopyOrMove) { - Interner<NoCopyOrMove> interner; - Interned<NoCopyOrMove> interned_str = interner.Intern(1); -} - } // namespace } // namespace profiling } // namespace perfetto