Chromium Code Reviews| 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..a2fec81caa0ad63e8e1394e98e0244eb789a93ad 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,81 @@ |
| #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 { |
| + |
| +// Calls StackFrameDeduplicator::ExportIncrementally() and returns ListValue |
| +// with exported entries. |
| +std::unique_ptr<ListValue> ExportEntriesIncrementally( |
| + StackFrameDeduplicator* dedup) { |
| + TracedValue traced_value; |
| + traced_value.BeginArray(""); |
| + dedup->ExportIncrementally(&traced_value); |
| + traced_value.EndArray(); |
| + |
| + auto base_value = traced_value.ToBaseValue(); |
| + const DictionaryValue* dictionary; |
| + const ListValue* entries; |
| + if (!base_value->GetAsDictionary(&dictionary) || |
| + !dictionary->GetList("", &entries)) { |
|
Primiano Tucci (use gerrit)
2017/03/09 11:47:45
Minor thing: I think that if instead of GetList yo
DmitrySkiba
2017/03/14 22:12:47
Done.
|
| + return nullptr; |
| + } |
| + |
| + return WrapUnique(entries->DeepCopy()); |
| +} |
| + |
| +struct StackFrameMapping { |
| + StackFrameMapping(int id, StackFrame frame, int parent_id = -1) |
| + : 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> ConvertMappingsToExportedEntries( |
| + 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 != -1) { |
| + 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 = ExportEntriesIncrementally(dedup); |
| + ASSERT_TRUE(entries); |
| + |
| + auto expected_entries = |
| + ConvertMappingsToExportedEntries(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 +90,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(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 +147,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(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 +177,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(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(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 +212,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(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, ExportIncrementally) { |
| + 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 |