| 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)) {
|
| + 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
|
|
|