Index: base/trace_event/heap_profiler_stack_frame_deduplicator_unittest.cc |
diff --git a/base/trace_event/heap_profiler_stack_frame_deduplicator_unittest.cc b/base/trace_event/heap_profiler_stack_frame_deduplicator_unittest.cc |
index 2215edebb56fd5920eb7ccade2bb103f3dd9f165..291f99ba7aa5039696128a5202b3a5e25997dd71 100644 |
--- a/base/trace_event/heap_profiler_stack_frame_deduplicator_unittest.cc |
+++ b/base/trace_event/heap_profiler_stack_frame_deduplicator_unittest.cc |
@@ -8,12 +8,84 @@ |
#include <memory> |
#include "base/macros.h" |
+#include "base/memory/ptr_util.h" |
#include "base/trace_event/heap_profiler_allocation_context.h" |
+#include "base/trace_event/heap_profiler_string_deduplicator.h" |
+#include "base/trace_event/trace_event_argument.h" |
+#include "base/values.h" |
#include "testing/gtest/include/gtest/gtest.h" |
namespace base { |
namespace trace_event { |
+namespace { |
+ |
+constexpr static int kInvalidStackFrameIndex = |
+ StackFrameDeduplicator::FrameNode::kInvalidFrameIndex; |
+ |
+// Calls StackFrameDeduplicator::SerializeIncrementally() and returns |
+// ListValue with serialized entries. |
+std::unique_ptr<ListValue> SerializeEntriesIncrementally( |
+ StackFrameDeduplicator* dedup) { |
+ TracedValue traced_value; |
+ traced_value.BeginArray(""); |
+ dedup->SerializeIncrementally(&traced_value); |
+ traced_value.EndArray(); |
+ |
+ auto base_value = traced_value.ToBaseValue(); |
+ DictionaryValue* dictionary; |
+ std::unique_ptr<Value> entries; |
+ if (!base_value->GetAsDictionary(&dictionary) || |
+ !dictionary->Remove("", &entries)) { |
+ return nullptr; |
+ } |
+ return ListValue::From(std::move(entries)); |
+} |
+ |
+struct StackFrameMapping { |
+ StackFrameMapping(int id, |
+ StackFrame frame, |
+ int parent_id = kInvalidStackFrameIndex) |
+ : id(id), parent_id(parent_id) { |
+ EXPECT_EQ(StackFrame::Type::TRACE_EVENT_NAME, frame.type); |
+ name = static_cast<const char*>(frame.value); |
+ } |
+ |
+ int id; |
+ const char* name; |
+ int parent_id; |
+}; |
+ |
+std::unique_ptr<ListValue> SerializeMappingsAsEntries( |
+ StringDeduplicator* string_dedup, |
+ std::initializer_list<StackFrameMapping> mappings) { |
+ auto entries = MakeUnique<ListValue>(); |
+ for (const auto& mapping : mappings) { |
+ auto entry = MakeUnique<DictionaryValue>(); |
+ entry->SetInteger("id", mapping.id); |
+ entry->SetInteger("name_sid", string_dedup->Insert(mapping.name)); |
+ if (mapping.parent_id != kInvalidStackFrameIndex) { |
+ entry->SetInteger("parent", mapping.parent_id); |
+ } |
+ entries->Append(std::move(entry)); |
+ } |
+ return entries; |
+} |
+ |
+void ExpectIncrementalEntries( |
+ StackFrameDeduplicator* dedup, |
+ StringDeduplicator* string_dedup, |
+ std::initializer_list<StackFrameMapping> mappings) { |
+ auto entries = SerializeEntriesIncrementally(dedup); |
+ ASSERT_TRUE(entries); |
+ |
+ auto expected_entries = SerializeMappingsAsEntries(string_dedup, mappings); |
+ ASSERT_TRUE(expected_entries->Equals(entries.get())) |
+ << "expected_entries = " << *expected_entries << "entries = " << *entries; |
+} |
+ |
+} // namespace |
+ |
// Define all strings once, because the deduplicator requires pointer equality, |
// and string interning is unreliable. |
StackFrame kBrowserMain = StackFrame::FromTraceEventName("BrowserMain"); |
@@ -21,30 +93,52 @@ StackFrame kRendererMain = StackFrame::FromTraceEventName("RendererMain"); |
StackFrame kCreateWidget = StackFrame::FromTraceEventName("CreateWidget"); |
StackFrame kInitialize = StackFrame::FromTraceEventName("Initialize"); |
StackFrame kMalloc = StackFrame::FromTraceEventName("malloc"); |
+StackFrame kNull = StackFrame::FromTraceEventName(nullptr); |
+ |
+TEST(StackFrameDeduplicatorTest, ImplicitId0) { |
+ StackFrame null_bt[] = {kNull}; |
+ |
+ // Empty backtraces (begin == end) are mapped to an implicitly added |
+ // node #0. However, backtrace with a single null frame is not empty, |
+ // and should be mapped to some other id. |
+ |
+ StringDeduplicator string_dedup; |
+ StackFrameDeduplicator dedup(&string_dedup); |
+ |
+ // Node #0 is added implicitly and corresponds to an empty backtrace. |
+ ASSERT_EQ(dedup.begin() + 1, dedup.end()); |
+ ASSERT_EQ(0, dedup.Insert(std::begin(null_bt), std::begin(null_bt))); |
+ |
+ // Placeholder entry for ID 0 is a frame with NULL name and no parent. |
+ // However inserting such a frame should yield a different ID. |
+ ExpectIncrementalEntries(&dedup, &string_dedup, {{0, kNull}}); |
+ ASSERT_EQ(1, dedup.Insert(std::begin(null_bt), std::end(null_bt))); |
+} |
TEST(StackFrameDeduplicatorTest, SingleBacktrace) { |
StackFrame bt[] = {kBrowserMain, kCreateWidget, kMalloc}; |
// The call tree should look like this (index in brackets). |
// |
- // BrowserMain [0] |
- // CreateWidget [1] |
- // malloc [2] |
+ // BrowserMain [1] |
+ // CreateWidget [2] |
+ // malloc [3] |
- std::unique_ptr<StackFrameDeduplicator> dedup(new StackFrameDeduplicator); |
- ASSERT_EQ(2, dedup->Insert(std::begin(bt), std::end(bt))); |
+ StringDeduplicator string_dedup; |
+ StackFrameDeduplicator dedup(&string_dedup); |
+ ASSERT_EQ(3, dedup.Insert(std::begin(bt), std::end(bt))); |
- auto iter = dedup->begin(); |
+ auto iter = dedup.begin() + 1; // skip implicit node #0 |
ASSERT_EQ(kBrowserMain, (iter + 0)->frame); |
- ASSERT_EQ(-1, (iter + 0)->parent_frame_index); |
+ ASSERT_EQ(kInvalidStackFrameIndex, (iter + 0)->parent_frame_index); |
ASSERT_EQ(kCreateWidget, (iter + 1)->frame); |
- ASSERT_EQ(0, (iter + 1)->parent_frame_index); |
+ ASSERT_EQ(1, (iter + 1)->parent_frame_index); |
ASSERT_EQ(kMalloc, (iter + 2)->frame); |
- ASSERT_EQ(1, (iter + 2)->parent_frame_index); |
+ ASSERT_EQ(2, (iter + 2)->parent_frame_index); |
- ASSERT_EQ(iter + 3, dedup->end()); |
+ ASSERT_EQ(iter + 3, dedup.end()); |
} |
TEST(StackFrameDeduplicatorTest, SingleBacktraceWithNull) { |
@@ -56,24 +150,25 @@ TEST(StackFrameDeduplicatorTest, SingleBacktraceWithNull) { |
// |
// So the call tree should look like this (index in brackets). |
// |
- // BrowserMain [0] |
- // (null) [1] |
- // malloc [2] |
+ // BrowserMain [1] |
+ // (null) [2] |
+ // malloc [3] |
- std::unique_ptr<StackFrameDeduplicator> dedup(new StackFrameDeduplicator); |
- ASSERT_EQ(2, dedup->Insert(std::begin(bt), std::end(bt))); |
+ StringDeduplicator string_dedup; |
+ StackFrameDeduplicator dedup(&string_dedup); |
+ ASSERT_EQ(3, dedup.Insert(std::begin(bt), std::end(bt))); |
- auto iter = dedup->begin(); |
+ auto iter = dedup.begin() + 1; // skip implicit node #0 |
ASSERT_EQ(kBrowserMain, (iter + 0)->frame); |
- ASSERT_EQ(-1, (iter + 0)->parent_frame_index); |
+ ASSERT_EQ(kInvalidStackFrameIndex, (iter + 0)->parent_frame_index); |
ASSERT_EQ(null_frame, (iter + 1)->frame); |
- ASSERT_EQ(0, (iter + 1)->parent_frame_index); |
+ ASSERT_EQ(1, (iter + 1)->parent_frame_index); |
ASSERT_EQ(kMalloc, (iter + 2)->frame); |
- ASSERT_EQ(1, (iter + 2)->parent_frame_index); |
+ ASSERT_EQ(2, (iter + 2)->parent_frame_index); |
- ASSERT_EQ(iter + 3, dedup->end()); |
+ ASSERT_EQ(iter + 3, dedup.end()); |
} |
// Test that there can be different call trees (there can be multiple bottom |
@@ -85,32 +180,33 @@ TEST(StackFrameDeduplicatorTest, MultipleRoots) { |
// The call tree should look like this (index in brackets). |
// |
- // BrowserMain [0] |
- // CreateWidget [1] |
- // RendererMain [2] |
- // CreateWidget [3] |
+ // BrowserMain [1] |
+ // CreateWidget [2] |
+ // RendererMain [3] |
+ // CreateWidget [4] |
// |
// Note that there will be two instances of CreateWidget, |
// with different parents. |
- std::unique_ptr<StackFrameDeduplicator> dedup(new StackFrameDeduplicator); |
- ASSERT_EQ(1, dedup->Insert(std::begin(bt0), std::end(bt0))); |
- ASSERT_EQ(3, dedup->Insert(std::begin(bt1), std::end(bt1))); |
+ StringDeduplicator string_dedup; |
+ StackFrameDeduplicator dedup(&string_dedup); |
+ ASSERT_EQ(2, dedup.Insert(std::begin(bt0), std::end(bt0))); |
+ ASSERT_EQ(4, dedup.Insert(std::begin(bt1), std::end(bt1))); |
- auto iter = dedup->begin(); |
+ auto iter = dedup.begin() + 1; // skip implicit node #0 |
ASSERT_EQ(kBrowserMain, (iter + 0)->frame); |
- ASSERT_EQ(-1, (iter + 0)->parent_frame_index); |
+ ASSERT_EQ(kInvalidStackFrameIndex, (iter + 0)->parent_frame_index); |
ASSERT_EQ(kCreateWidget, (iter + 1)->frame); |
- ASSERT_EQ(0, (iter + 1)->parent_frame_index); |
+ ASSERT_EQ(1, (iter + 1)->parent_frame_index); |
ASSERT_EQ(kRendererMain, (iter + 2)->frame); |
- ASSERT_EQ(-1, (iter + 2)->parent_frame_index); |
+ ASSERT_EQ(kInvalidStackFrameIndex, (iter + 2)->parent_frame_index); |
ASSERT_EQ(kCreateWidget, (iter + 3)->frame); |
- ASSERT_EQ(2, (iter + 3)->parent_frame_index); |
+ ASSERT_EQ(3, (iter + 3)->parent_frame_index); |
- ASSERT_EQ(iter + 4, dedup->end()); |
+ ASSERT_EQ(iter + 4, dedup.end()); |
} |
TEST(StackFrameDeduplicatorTest, Deduplication) { |
@@ -119,33 +215,53 @@ TEST(StackFrameDeduplicatorTest, Deduplication) { |
// The call tree should look like this (index in brackets). |
// |
- // BrowserMain [0] |
- // CreateWidget [1] |
- // Initialize [2] |
+ // BrowserMain [1] |
+ // CreateWidget [2] |
+ // Initialize [3] |
// |
// Note that BrowserMain will be re-used. |
- std::unique_ptr<StackFrameDeduplicator> dedup(new StackFrameDeduplicator); |
- ASSERT_EQ(1, dedup->Insert(std::begin(bt0), std::end(bt0))); |
- ASSERT_EQ(2, dedup->Insert(std::begin(bt1), std::end(bt1))); |
+ StringDeduplicator string_dedup; |
+ StackFrameDeduplicator dedup(&string_dedup); |
+ ASSERT_EQ(2, dedup.Insert(std::begin(bt0), std::end(bt0))); |
+ ASSERT_EQ(3, dedup.Insert(std::begin(bt1), std::end(bt1))); |
- auto iter = dedup->begin(); |
+ auto iter = dedup.begin() + 1; // skip implicit node #0 |
ASSERT_EQ(kBrowserMain, (iter + 0)->frame); |
- ASSERT_EQ(-1, (iter + 0)->parent_frame_index); |
+ ASSERT_EQ(kInvalidStackFrameIndex, (iter + 0)->parent_frame_index); |
ASSERT_EQ(kCreateWidget, (iter + 1)->frame); |
- ASSERT_EQ(0, (iter + 1)->parent_frame_index); |
+ ASSERT_EQ(1, (iter + 1)->parent_frame_index); |
ASSERT_EQ(kInitialize, (iter + 2)->frame); |
- ASSERT_EQ(0, (iter + 2)->parent_frame_index); |
+ ASSERT_EQ(1, (iter + 2)->parent_frame_index); |
- ASSERT_EQ(iter + 3, dedup->end()); |
+ ASSERT_EQ(iter + 3, dedup.end()); |
// Inserting the same backtrace again should return the index of the existing |
// node. |
- ASSERT_EQ(1, dedup->Insert(std::begin(bt0), std::end(bt0))); |
- ASSERT_EQ(2, dedup->Insert(std::begin(bt1), std::end(bt1))); |
- ASSERT_EQ(dedup->begin() + 3, dedup->end()); |
+ ASSERT_EQ(2, dedup.Insert(std::begin(bt0), std::end(bt0))); |
+ ASSERT_EQ(3, dedup.Insert(std::begin(bt1), std::end(bt1))); |
+ ASSERT_EQ(4 /* 1 implicit + 3 added */, dedup.end() - dedup.begin()); |
+} |
+ |
+TEST(StackFrameDeduplicatorTest, SerializeIncrementally) { |
+ StringDeduplicator string_dedup; |
+ StackFrameDeduplicator dedup(&string_dedup); |
+ |
+ StackFrame bt0[] = {kBrowserMain, kCreateWidget}; |
+ ASSERT_EQ(2, dedup.Insert(std::begin(bt0), std::end(bt0))); |
+ |
+ ExpectIncrementalEntries( |
+ &dedup, &string_dedup, |
+ {{0, kNull}, {1, kBrowserMain}, {2, kCreateWidget, 1}}); |
+ |
+ StackFrame bt1[] = {kBrowserMain, kInitialize}; |
+ ASSERT_EQ(3, dedup.Insert(std::begin(bt1), std::end(bt1))); |
+ |
+ ExpectIncrementalEntries(&dedup, &string_dedup, {{3, kInitialize, 1}}); |
+ |
+ ExpectIncrementalEntries(&dedup, &string_dedup, {}); |
} |
} // namespace trace_event |