// Copyright 2013 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 "base/debug/trace_event_memory.h" #include #include #include "base/debug/trace_event_impl.h" #include "base/message_loop/message_loop.h" #include "testing/gtest/include/gtest/gtest.h" #if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED) #include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" #endif namespace base { namespace debug { // Tests for the trace event memory tracking system. Exists as a class so it // can be a friend of TraceMemoryController. class TraceMemoryTest : public testing::Test { public: TraceMemoryTest() {} ~TraceMemoryTest() override {} private: DISALLOW_COPY_AND_ASSIGN(TraceMemoryTest); }; ////////////////////////////////////////////////////////////////////////////// #if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED) TEST_F(TraceMemoryTest, TraceMemoryController) { MessageLoop message_loop; // Start with no observers of the TraceLog. EXPECT_EQ(0u, TraceLog::GetInstance()->GetObserverCountForTest()); // Creating a controller adds it to the TraceLog observer list. scoped_ptr controller( new TraceMemoryController( message_loop.message_loop_proxy(), ::HeapProfilerWithPseudoStackStart, ::HeapProfilerStop, ::GetHeapProfile)); EXPECT_EQ(1u, TraceLog::GetInstance()->GetObserverCountForTest()); EXPECT_TRUE( TraceLog::GetInstance()->HasEnabledStateObserver(controller.get())); // By default the observer isn't dumping memory profiles. EXPECT_FALSE(controller->IsTimerRunningForTest()); // Simulate enabling tracing. controller->StartProfiling(); message_loop.RunUntilIdle(); EXPECT_TRUE(controller->IsTimerRunningForTest()); // Simulate disabling tracing. controller->StopProfiling(); message_loop.RunUntilIdle(); EXPECT_FALSE(controller->IsTimerRunningForTest()); // Deleting the observer removes it from the TraceLog observer list. controller.reset(); EXPECT_EQ(0u, TraceLog::GetInstance()->GetObserverCountForTest()); } TEST_F(TraceMemoryTest, ScopedTraceMemory) { ScopedTraceMemory::InitForTest(); // Start with an empty stack. EXPECT_EQ(0, ScopedTraceMemory::GetStackDepthForTest()); { // Push an item. ScopedTraceMemory scope1("cat1", "name1"); EXPECT_EQ(1, ScopedTraceMemory::GetStackDepthForTest()); EXPECT_EQ("cat1", ScopedTraceMemory::GetScopeDataForTest(0).category); EXPECT_EQ("name1", ScopedTraceMemory::GetScopeDataForTest(0).name); { // One more item. ScopedTraceMemory scope2("cat2", "name2"); EXPECT_EQ(2, ScopedTraceMemory::GetStackDepthForTest()); EXPECT_EQ("cat2", ScopedTraceMemory::GetScopeDataForTest(1).category); EXPECT_EQ("name2", ScopedTraceMemory::GetScopeDataForTest(1).name); } // Ended scope 2. EXPECT_EQ(1, ScopedTraceMemory::GetStackDepthForTest()); } // Ended scope 1. EXPECT_EQ(0, ScopedTraceMemory::GetStackDepthForTest()); ScopedTraceMemory::CleanupForTest(); } void TestDeepScopeNesting(int current, int depth) { EXPECT_EQ(current, ScopedTraceMemory::GetStackDepthForTest()); ScopedTraceMemory scope("category", "name"); if (current < depth) TestDeepScopeNesting(current + 1, depth); EXPECT_EQ(current + 1, ScopedTraceMemory::GetStackDepthForTest()); } TEST_F(TraceMemoryTest, DeepScopeNesting) { ScopedTraceMemory::InitForTest(); // Ensure really deep scopes don't crash. TestDeepScopeNesting(0, 100); ScopedTraceMemory::CleanupForTest(); } #endif // defined(TRACE_MEMORY_SUPPORTED) ///////////////////////////////////////////////////////////////////////////// TEST_F(TraceMemoryTest, AppendHeapProfileTotalsAsTraceFormat) { // Empty input gives empty output. std::string empty_output; AppendHeapProfileTotalsAsTraceFormat("", &empty_output); EXPECT_EQ("", empty_output); // Typical case. const char input[] = "heap profile: 357: 55227 [ 14653: 2624014] @ heapprofile"; const std::string kExpectedOutput = "{\"current_allocs\": 357, \"current_bytes\": 55227, \"trace\": \"\"}"; std::string output; AppendHeapProfileTotalsAsTraceFormat(input, &output); EXPECT_EQ(kExpectedOutput, output); } TEST_F(TraceMemoryTest, AppendHeapProfileLineAsTraceFormat) { // Empty input gives empty output. std::string empty_output; EXPECT_FALSE(AppendHeapProfileLineAsTraceFormat("", &empty_output)); EXPECT_EQ("", empty_output); // Invalid input returns false. std::string junk_output; EXPECT_FALSE(AppendHeapProfileLineAsTraceFormat("junk", &junk_output)); // Input with normal category and name entries. const char kCategory[] = "category"; const char kName[] = "name"; std::ostringstream input; input << " 68: 4195 [ 1087: 98009] @ " << &kCategory << " " << &kName; const std::string kExpectedOutput = ",\n" "{" "\"current_allocs\": 68, " "\"current_bytes\": 4195, " "\"trace\": \"name \"" "}"; std::string output; EXPECT_TRUE( AppendHeapProfileLineAsTraceFormat(input.str().c_str(), &output)); EXPECT_EQ(kExpectedOutput, output); // Input with with the category "toplevel". // TODO(jamescook): Eliminate this special case and move the logic to the // trace viewer code. const char kTaskCategory[] = "toplevel"; const char kTaskName[] = "TaskName"; std::ostringstream input2; input2 << " 68: 4195 [ 1087: 98009] @ " << &kTaskCategory << " " << &kTaskName; const std::string kExpectedOutput2 = ",\n" "{" "\"current_allocs\": 68, " "\"current_bytes\": 4195, " "\"trace\": \"TaskName->PostTask \"" "}"; std::string output2; EXPECT_TRUE( AppendHeapProfileLineAsTraceFormat(input2.str().c_str(), &output2)); EXPECT_EQ(kExpectedOutput2, output2); // Zero current allocations is skipped. std::ostringstream zero_input; zero_input << " 0: 0 [ 1087: 98009] @ " << &kCategory << " " << &kName; std::string zero_output; EXPECT_FALSE(AppendHeapProfileLineAsTraceFormat(zero_input.str().c_str(), &zero_output)); EXPECT_EQ("", zero_output); } TEST_F(TraceMemoryTest, AppendHeapProfileAsTraceFormat) { // Empty input gives empty output. std::string empty_output; AppendHeapProfileAsTraceFormat("", &empty_output); EXPECT_EQ("", empty_output); // Typical case. const char input[] = "heap profile: 357: 55227 [ 14653: 2624014] @ heapprofile\n" " 95: 40940 [ 649: 114260] @\n" " 77: 32546 [ 742: 106234] @ 0x0 0x0\n" " 0: 0 [ 132: 4236] @ 0x0\n" "\n" "MAPPED_LIBRARIES:\n" "1be411fc1000-1be4139e4000 rw-p 00000000 00:00 0\n" "1be4139e4000-1be4139e5000 ---p 00000000 00:00 0\n"; const std::string kExpectedOutput = "[{" "\"current_allocs\": 357, " "\"current_bytes\": 55227, " "\"trace\": \"\"},\n" "{\"current_allocs\": 95, " "\"current_bytes\": 40940, " "\"trace\": \"\"},\n" "{\"current_allocs\": 77, " "\"current_bytes\": 32546, " "\"trace\": \"null \"" "}]\n"; std::string output; AppendHeapProfileAsTraceFormat(input, &output); EXPECT_EQ(kExpectedOutput, output); } TEST_F(TraceMemoryTest, StringFromHexAddress) { EXPECT_STREQ("null", StringFromHexAddress("0x0")); EXPECT_STREQ("error", StringFromHexAddress("not an address")); const char kHello[] = "hello"; std::ostringstream hex_address; hex_address << &kHello; EXPECT_STREQ(kHello, StringFromHexAddress(hex_address.str())); } } // namespace debug } // namespace base