1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/debug/trace_event_memory.h"
6
7#include <sstream>
8#include <string>
9
10#include "base/debug/trace_event_impl.h"
11#include "base/message_loop/message_loop.h"
12#include "testing/gtest/include/gtest/gtest.h"
13
14#if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED)
15#include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h"
16#endif
17
18namespace base {
19namespace debug {
20
21// Tests for the trace event memory tracking system. Exists as a class so it
22// can be a friend of TraceMemoryController.
23class TraceMemoryTest : public testing::Test {
24 public:
25  TraceMemoryTest() {}
26  virtual ~TraceMemoryTest() {}
27
28 private:
29  DISALLOW_COPY_AND_ASSIGN(TraceMemoryTest);
30};
31
32//////////////////////////////////////////////////////////////////////////////
33
34#if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED)
35
36TEST_F(TraceMemoryTest, TraceMemoryController) {
37  MessageLoop message_loop;
38
39  // Start with no observers of the TraceLog.
40  EXPECT_EQ(0u, TraceLog::GetInstance()->GetObserverCountForTest());
41
42  // Creating a controller adds it to the TraceLog observer list.
43  scoped_ptr<TraceMemoryController> controller(
44      new TraceMemoryController(
45          message_loop.message_loop_proxy(),
46          ::HeapProfilerWithPseudoStackStart,
47          ::HeapProfilerStop,
48          ::GetHeapProfile));
49  EXPECT_EQ(1u, TraceLog::GetInstance()->GetObserverCountForTest());
50  EXPECT_TRUE(
51      TraceLog::GetInstance()->HasEnabledStateObserver(controller.get()));
52
53  // By default the observer isn't dumping memory profiles.
54  EXPECT_FALSE(controller->IsTimerRunningForTest());
55
56  // Simulate enabling tracing.
57  controller->StartProfiling();
58  message_loop.RunUntilIdle();
59  EXPECT_TRUE(controller->IsTimerRunningForTest());
60
61  // Simulate disabling tracing.
62  controller->StopProfiling();
63  message_loop.RunUntilIdle();
64  EXPECT_FALSE(controller->IsTimerRunningForTest());
65
66  // Deleting the observer removes it from the TraceLog observer list.
67  controller.reset();
68  EXPECT_EQ(0u, TraceLog::GetInstance()->GetObserverCountForTest());
69}
70
71TEST_F(TraceMemoryTest, ScopedTraceMemory) {
72  ScopedTraceMemory::InitForTest();
73
74  // Start with an empty stack.
75  EXPECT_EQ(0, ScopedTraceMemory::GetStackDepthForTest());
76
77  {
78    // Push an item.
79    ScopedTraceMemory scope1("cat1", "name1");
80    EXPECT_EQ(1, ScopedTraceMemory::GetStackDepthForTest());
81    EXPECT_EQ("cat1", ScopedTraceMemory::GetScopeDataForTest(0).category);
82    EXPECT_EQ("name1", ScopedTraceMemory::GetScopeDataForTest(0).name);
83
84    {
85      // One more item.
86      ScopedTraceMemory scope2("cat2", "name2");
87      EXPECT_EQ(2, ScopedTraceMemory::GetStackDepthForTest());
88      EXPECT_EQ("cat2", ScopedTraceMemory::GetScopeDataForTest(1).category);
89      EXPECT_EQ("name2", ScopedTraceMemory::GetScopeDataForTest(1).name);
90    }
91
92    // Ended scope 2.
93    EXPECT_EQ(1, ScopedTraceMemory::GetStackDepthForTest());
94  }
95
96  // Ended scope 1.
97  EXPECT_EQ(0, ScopedTraceMemory::GetStackDepthForTest());
98
99  ScopedTraceMemory::CleanupForTest();
100}
101
102void TestDeepScopeNesting(int current, int depth) {
103  EXPECT_EQ(current, ScopedTraceMemory::GetStackDepthForTest());
104  ScopedTraceMemory scope("category", "name");
105  if (current < depth)
106    TestDeepScopeNesting(current + 1, depth);
107  EXPECT_EQ(current + 1, ScopedTraceMemory::GetStackDepthForTest());
108}
109
110TEST_F(TraceMemoryTest, DeepScopeNesting) {
111  ScopedTraceMemory::InitForTest();
112
113  // Ensure really deep scopes don't crash.
114  TestDeepScopeNesting(0, 100);
115
116  ScopedTraceMemory::CleanupForTest();
117}
118
119#endif  // defined(TRACE_MEMORY_SUPPORTED)
120
121/////////////////////////////////////////////////////////////////////////////
122
123TEST_F(TraceMemoryTest, AppendHeapProfileTotalsAsTraceFormat) {
124  // Empty input gives empty output.
125  std::string empty_output;
126  AppendHeapProfileTotalsAsTraceFormat("", &empty_output);
127  EXPECT_EQ("", empty_output);
128
129  // Typical case.
130  const char input[] =
131      "heap profile:    357:    55227 [ 14653:  2624014] @ heapprofile";
132  const std::string kExpectedOutput =
133      "{\"current_allocs\": 357, \"current_bytes\": 55227, \"trace\": \"\"}";
134  std::string output;
135  AppendHeapProfileTotalsAsTraceFormat(input, &output);
136  EXPECT_EQ(kExpectedOutput, output);
137}
138
139TEST_F(TraceMemoryTest, AppendHeapProfileLineAsTraceFormat) {
140  // Empty input gives empty output.
141  std::string empty_output;
142  EXPECT_FALSE(AppendHeapProfileLineAsTraceFormat("", &empty_output));
143  EXPECT_EQ("", empty_output);
144
145  // Invalid input returns false.
146  std::string junk_output;
147  EXPECT_FALSE(AppendHeapProfileLineAsTraceFormat("junk", &junk_output));
148
149  // Input with normal category and name entries.
150  const char kCategory[] = "category";
151  const char kName[] = "name";
152  std::ostringstream input;
153  input << "   68:     4195 [  1087:    98009] @ " << &kCategory << " "
154        << &kName;
155  const std::string kExpectedOutput =
156      ",\n"
157      "{"
158      "\"current_allocs\": 68, "
159      "\"current_bytes\": 4195, "
160      "\"trace\": \"name \""
161      "}";
162  std::string output;
163  EXPECT_TRUE(
164      AppendHeapProfileLineAsTraceFormat(input.str().c_str(), &output));
165  EXPECT_EQ(kExpectedOutput, output);
166
167  // Input with with the category "toplevel".
168  // TODO(jamescook): Eliminate this special case and move the logic to the
169  // trace viewer code.
170  const char kTaskCategory[] = "toplevel";
171  const char kTaskName[] = "TaskName";
172  std::ostringstream input2;
173  input2 << "   68:     4195 [  1087:    98009] @ " << &kTaskCategory << " "
174        << &kTaskName;
175  const std::string kExpectedOutput2 =
176      ",\n"
177      "{"
178      "\"current_allocs\": 68, "
179      "\"current_bytes\": 4195, "
180      "\"trace\": \"TaskName->PostTask \""
181      "}";
182  std::string output2;
183  EXPECT_TRUE(
184      AppendHeapProfileLineAsTraceFormat(input2.str().c_str(), &output2));
185  EXPECT_EQ(kExpectedOutput2, output2);
186
187  // Zero current allocations is skipped.
188  std::ostringstream zero_input;
189  zero_input << "   0:     0 [  1087:    98009] @ " << &kCategory << " "
190             << &kName;
191  std::string zero_output;
192  EXPECT_FALSE(AppendHeapProfileLineAsTraceFormat(zero_input.str().c_str(),
193                                                  &zero_output));
194  EXPECT_EQ("", zero_output);
195}
196
197TEST_F(TraceMemoryTest, AppendHeapProfileAsTraceFormat) {
198  // Empty input gives empty output.
199  std::string empty_output;
200  AppendHeapProfileAsTraceFormat("", &empty_output);
201  EXPECT_EQ("", empty_output);
202
203  // Typical case.
204  const char input[] =
205      "heap profile:    357:    55227 [ 14653:  2624014] @ heapprofile\n"
206      "   95:    40940 [   649:   114260] @\n"
207      "   77:    32546 [   742:   106234] @ 0x0 0x0\n"
208      "    0:        0 [   132:     4236] @ 0x0\n"
209      "\n"
210      "MAPPED_LIBRARIES:\n"
211      "1be411fc1000-1be4139e4000 rw-p 00000000 00:00 0\n"
212      "1be4139e4000-1be4139e5000 ---p 00000000 00:00 0\n";
213  const std::string kExpectedOutput =
214      "[{"
215      "\"current_allocs\": 357, "
216      "\"current_bytes\": 55227, "
217      "\"trace\": \"\"},\n"
218      "{\"current_allocs\": 95, "
219      "\"current_bytes\": 40940, "
220      "\"trace\": \"\"},\n"
221      "{\"current_allocs\": 77, "
222      "\"current_bytes\": 32546, "
223      "\"trace\": \"null \""
224      "}]\n";
225  std::string output;
226  AppendHeapProfileAsTraceFormat(input, &output);
227  EXPECT_EQ(kExpectedOutput, output);
228}
229
230TEST_F(TraceMemoryTest, StringFromHexAddress) {
231  EXPECT_STREQ("null", StringFromHexAddress("0x0"));
232  EXPECT_STREQ("error", StringFromHexAddress("not an address"));
233  const char kHello[] = "hello";
234  std::ostringstream hex_address;
235  hex_address << &kHello;
236  EXPECT_STREQ(kHello, StringFromHexAddress(hex_address.str()));
237}
238
239}  // namespace debug
240}  // namespace base
241