1// Copyright 2015 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/trace_event/memory_dump_manager.h"
6
7#include <stdint.h>
8
9#include <vector>
10
11#include "base/bind_helpers.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/message_loop/message_loop.h"
14#include "base/run_loop.h"
15#include "base/strings/stringprintf.h"
16#include "base/synchronization/waitable_event.h"
17#include "base/test/test_io_thread.h"
18#include "base/test/trace_event_analyzer.h"
19#include "base/thread_task_runner_handle.h"
20#include "base/threading/platform_thread.h"
21#include "base/threading/thread.h"
22#include "base/trace_event/memory_dump_provider.h"
23#include "base/trace_event/process_memory_dump.h"
24#include "base/trace_event/trace_buffer.h"
25#include "base/trace_event/trace_config_memory_test_util.h"
26#include "testing/gmock/include/gmock/gmock.h"
27#include "testing/gtest/include/gtest/gtest.h"
28
29using testing::_;
30using testing::AnyNumber;
31using testing::AtMost;
32using testing::Between;
33using testing::Invoke;
34using testing::Return;
35
36namespace base {
37namespace trace_event {
38
39// GTest matchers for MemoryDumpRequestArgs arguments.
40MATCHER(IsDetailedDump, "") {
41  return arg.level_of_detail == MemoryDumpLevelOfDetail::DETAILED;
42}
43
44MATCHER(IsLightDump, "") {
45  return arg.level_of_detail == MemoryDumpLevelOfDetail::LIGHT;
46}
47
48namespace {
49
50void RegisterDumpProvider(
51    MemoryDumpProvider* mdp,
52    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
53    const MemoryDumpProvider::Options& options) {
54  MemoryDumpManager* mdm = MemoryDumpManager::GetInstance();
55  mdm->set_dumper_registrations_ignored_for_testing(false);
56  mdm->RegisterDumpProvider(mdp, "TestDumpProvider", task_runner, options);
57  mdm->set_dumper_registrations_ignored_for_testing(true);
58}
59
60void RegisterDumpProvider(MemoryDumpProvider* mdp) {
61  RegisterDumpProvider(mdp, nullptr, MemoryDumpProvider::Options());
62}
63
64void OnTraceDataCollected(Closure quit_closure,
65                          trace_event::TraceResultBuffer* buffer,
66                          const scoped_refptr<RefCountedString>& json,
67                          bool has_more_events) {
68  buffer->AddFragment(json->data());
69  if (!has_more_events)
70    quit_closure.Run();
71}
72
73}  // namespace
74
75// Testing MemoryDumpManagerDelegate which, by default, short-circuits dump
76// requests locally to the MemoryDumpManager instead of performing IPC dances.
77class MemoryDumpManagerDelegateForTesting : public MemoryDumpManagerDelegate {
78 public:
79  MemoryDumpManagerDelegateForTesting() {
80    ON_CALL(*this, RequestGlobalMemoryDump(_, _))
81        .WillByDefault(Invoke(
82            this, &MemoryDumpManagerDelegateForTesting::CreateProcessDump));
83  }
84
85  MOCK_METHOD2(RequestGlobalMemoryDump,
86               void(const MemoryDumpRequestArgs& args,
87                    const MemoryDumpCallback& callback));
88
89  uint64_t GetTracingProcessId() const override {
90    NOTREACHED();
91    return MemoryDumpManager::kInvalidTracingProcessId;
92  }
93};
94
95class MockMemoryDumpProvider : public MemoryDumpProvider {
96 public:
97  MOCK_METHOD0(Destructor, void());
98  MOCK_METHOD2(OnMemoryDump,
99               bool(const MemoryDumpArgs& args, ProcessMemoryDump* pmd));
100
101  MockMemoryDumpProvider() : enable_mock_destructor(false) {}
102  ~MockMemoryDumpProvider() override {
103    if (enable_mock_destructor)
104      Destructor();
105  }
106
107  bool enable_mock_destructor;
108};
109
110class MemoryDumpManagerTest : public testing::Test {
111 public:
112  MemoryDumpManagerTest() : testing::Test(), kDefaultOptions() {}
113
114  void SetUp() override {
115    last_callback_success_ = false;
116    message_loop_.reset(new MessageLoop());
117    mdm_.reset(new MemoryDumpManager());
118    MemoryDumpManager::SetInstanceForTesting(mdm_.get());
119    ASSERT_EQ(mdm_.get(), MemoryDumpManager::GetInstance());
120    delegate_.reset(new MemoryDumpManagerDelegateForTesting);
121  }
122
123  void TearDown() override {
124    MemoryDumpManager::SetInstanceForTesting(nullptr);
125    mdm_.reset();
126    delegate_.reset();
127    message_loop_.reset();
128    TraceLog::DeleteForTesting();
129  }
130
131  // Turns a Closure into a MemoryDumpCallback, keeping track of the callback
132  // result and taking care of posting the closure on the correct task runner.
133  void DumpCallbackAdapter(scoped_refptr<SingleThreadTaskRunner> task_runner,
134                           Closure closure,
135                           uint64_t dump_guid,
136                           bool success) {
137    last_callback_success_ = success;
138    task_runner->PostTask(FROM_HERE, closure);
139  }
140
141 protected:
142  void InitializeMemoryDumpManager(bool is_coordinator) {
143    mdm_->set_dumper_registrations_ignored_for_testing(true);
144    mdm_->Initialize(delegate_.get(), is_coordinator);
145  }
146
147  void RequestGlobalDumpAndWait(MemoryDumpType dump_type,
148                                MemoryDumpLevelOfDetail level_of_detail) {
149    RunLoop run_loop;
150    MemoryDumpCallback callback =
151        Bind(&MemoryDumpManagerTest::DumpCallbackAdapter, Unretained(this),
152             MessageLoop::current()->task_runner(), run_loop.QuitClosure());
153    mdm_->RequestGlobalDump(dump_type, level_of_detail, callback);
154    run_loop.Run();
155  }
156
157  void EnableTracingWithLegacyCategories(const char* category) {
158    TraceLog::GetInstance()->SetEnabled(TraceConfig(category, ""),
159                                        TraceLog::RECORDING_MODE);
160  }
161
162  void EnableTracingWithTraceConfig(const std::string& trace_config) {
163    TraceLog::GetInstance()->SetEnabled(TraceConfig(trace_config),
164                                        TraceLog::RECORDING_MODE);
165  }
166
167  void DisableTracing() { TraceLog::GetInstance()->SetDisabled(); }
168
169  bool IsPeriodicDumpingEnabled() const {
170    return mdm_->periodic_dump_timer_.IsRunning();
171  }
172
173  int GetMaxConsecutiveFailuresCount() const {
174    return MemoryDumpManager::kMaxConsecutiveFailuresCount;
175  }
176
177  const MemoryDumpProvider::Options kDefaultOptions;
178  scoped_ptr<MemoryDumpManager> mdm_;
179  scoped_ptr<MemoryDumpManagerDelegateForTesting> delegate_;
180  bool last_callback_success_;
181
182 private:
183  scoped_ptr<MessageLoop> message_loop_;
184
185  // We want our singleton torn down after each test.
186  ShadowingAtExitManager at_exit_manager_;
187};
188
189// Basic sanity checks. Registers a memory dump provider and checks that it is
190// called, but only when memory-infra is enabled.
191TEST_F(MemoryDumpManagerTest, SingleDumper) {
192  InitializeMemoryDumpManager(false /* is_coordinator */);
193  MockMemoryDumpProvider mdp;
194  RegisterDumpProvider(&mdp);
195
196  // Check that the dumper is not called if the memory category is not enabled.
197  EnableTracingWithLegacyCategories("foobar-but-not-memory");
198  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(0);
199  EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
200  RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
201                           MemoryDumpLevelOfDetail::DETAILED);
202  DisableTracing();
203
204  // Now repeat enabling the memory category and check that the dumper is
205  // invoked this time.
206  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
207  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(3);
208  EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(3).WillRepeatedly(Return(true));
209  for (int i = 0; i < 3; ++i)
210    RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
211                             MemoryDumpLevelOfDetail::DETAILED);
212  DisableTracing();
213
214  mdm_->UnregisterDumpProvider(&mdp);
215
216  // Finally check the unregister logic: the delegate will be invoked but not
217  // the dump provider, as it has been unregistered.
218  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
219  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(3);
220  EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
221
222  for (int i = 0; i < 3; ++i) {
223    RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
224                             MemoryDumpLevelOfDetail::DETAILED);
225  }
226  DisableTracing();
227}
228
229// Checks that requesting dumps with high level of detail actually propagates
230// the level of the detail properly to OnMemoryDump() call on dump providers.
231TEST_F(MemoryDumpManagerTest, CheckMemoryDumpArgs) {
232  InitializeMemoryDumpManager(false /* is_coordinator */);
233  MockMemoryDumpProvider mdp;
234
235  RegisterDumpProvider(&mdp);
236  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
237  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
238  EXPECT_CALL(mdp, OnMemoryDump(IsDetailedDump(), _)).WillOnce(Return(true));
239  RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
240                           MemoryDumpLevelOfDetail::DETAILED);
241  DisableTracing();
242  mdm_->UnregisterDumpProvider(&mdp);
243
244  // Check that requesting dumps with low level of detail actually propagates to
245  // OnMemoryDump() call on dump providers.
246  RegisterDumpProvider(&mdp);
247  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
248  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
249  EXPECT_CALL(mdp, OnMemoryDump(IsLightDump(), _)).WillOnce(Return(true));
250  RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
251                           MemoryDumpLevelOfDetail::LIGHT);
252  DisableTracing();
253  mdm_->UnregisterDumpProvider(&mdp);
254}
255
256// Checks that the SharedSessionState object is acqually shared over time.
257TEST_F(MemoryDumpManagerTest, SharedSessionState) {
258  InitializeMemoryDumpManager(false /* is_coordinator */);
259  MockMemoryDumpProvider mdp1;
260  MockMemoryDumpProvider mdp2;
261  RegisterDumpProvider(&mdp1);
262  RegisterDumpProvider(&mdp2);
263
264  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
265  const MemoryDumpSessionState* session_state = mdm_->session_state().get();
266  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(2);
267  EXPECT_CALL(mdp1, OnMemoryDump(_, _))
268      .Times(2)
269      .WillRepeatedly(Invoke([session_state](const MemoryDumpArgs&,
270                                             ProcessMemoryDump* pmd) -> bool {
271        EXPECT_EQ(session_state, pmd->session_state().get());
272        return true;
273      }));
274  EXPECT_CALL(mdp2, OnMemoryDump(_, _))
275      .Times(2)
276      .WillRepeatedly(Invoke([session_state](const MemoryDumpArgs&,
277                                             ProcessMemoryDump* pmd) -> bool {
278        EXPECT_EQ(session_state, pmd->session_state().get());
279        return true;
280      }));
281
282  for (int i = 0; i < 2; ++i) {
283    RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
284                             MemoryDumpLevelOfDetail::DETAILED);
285  }
286
287  DisableTracing();
288}
289
290// Checks that the (Un)RegisterDumpProvider logic behaves sanely.
291TEST_F(MemoryDumpManagerTest, MultipleDumpers) {
292  InitializeMemoryDumpManager(false /* is_coordinator */);
293  MockMemoryDumpProvider mdp1;
294  MockMemoryDumpProvider mdp2;
295
296  // Enable only mdp1.
297  RegisterDumpProvider(&mdp1);
298  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
299  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
300  EXPECT_CALL(mdp1, OnMemoryDump(_, _)).WillOnce(Return(true));
301  EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(0);
302  RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
303                           MemoryDumpLevelOfDetail::DETAILED);
304  DisableTracing();
305
306  // Invert: enable mdp1 and disable mdp2.
307  mdm_->UnregisterDumpProvider(&mdp1);
308  RegisterDumpProvider(&mdp2);
309  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
310  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
311  EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(0);
312  EXPECT_CALL(mdp2, OnMemoryDump(_, _)).WillOnce(Return(true));
313  RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
314                           MemoryDumpLevelOfDetail::DETAILED);
315  DisableTracing();
316
317  // Enable both mdp1 and mdp2.
318  RegisterDumpProvider(&mdp1);
319  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
320  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
321  EXPECT_CALL(mdp1, OnMemoryDump(_, _)).WillOnce(Return(true));
322  EXPECT_CALL(mdp2, OnMemoryDump(_, _)).WillOnce(Return(true));
323  RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
324                           MemoryDumpLevelOfDetail::DETAILED);
325  DisableTracing();
326}
327
328// Checks that the dump provider invocations depend only on the current
329// registration state and not on previous registrations and dumps.
330TEST_F(MemoryDumpManagerTest, RegistrationConsistency) {
331  InitializeMemoryDumpManager(false /* is_coordinator */);
332  MockMemoryDumpProvider mdp;
333
334  RegisterDumpProvider(&mdp);
335
336  {
337    EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
338    EXPECT_CALL(mdp, OnMemoryDump(_, _)).WillOnce(Return(true));
339    EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
340    RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
341                             MemoryDumpLevelOfDetail::DETAILED);
342    DisableTracing();
343  }
344
345  mdm_->UnregisterDumpProvider(&mdp);
346
347  {
348    EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
349    EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
350    EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
351    RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
352                             MemoryDumpLevelOfDetail::DETAILED);
353    DisableTracing();
354  }
355
356  RegisterDumpProvider(&mdp);
357  mdm_->UnregisterDumpProvider(&mdp);
358
359  {
360    EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
361    EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
362    EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
363    RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
364                             MemoryDumpLevelOfDetail::DETAILED);
365    DisableTracing();
366  }
367
368  RegisterDumpProvider(&mdp);
369  mdm_->UnregisterDumpProvider(&mdp);
370  RegisterDumpProvider(&mdp);
371
372  {
373    EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
374    EXPECT_CALL(mdp, OnMemoryDump(_, _)).WillOnce(Return(true));
375    EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
376    RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
377                             MemoryDumpLevelOfDetail::DETAILED);
378    DisableTracing();
379  }
380}
381
382// Checks that the MemoryDumpManager respects the thread affinity when a
383// MemoryDumpProvider specifies a task_runner(). The test starts creating 8
384// threads and registering a MemoryDumpProvider on each of them. At each
385// iteration, one thread is removed, to check the live unregistration logic.
386TEST_F(MemoryDumpManagerTest, RespectTaskRunnerAffinity) {
387  InitializeMemoryDumpManager(false /* is_coordinator */);
388  const uint32_t kNumInitialThreads = 8;
389
390  std::vector<scoped_ptr<Thread>> threads;
391  std::vector<scoped_ptr<MockMemoryDumpProvider>> mdps;
392
393  // Create the threads and setup the expectations. Given that at each iteration
394  // we will pop out one thread/MemoryDumpProvider, each MDP is supposed to be
395  // invoked a number of times equal to its index.
396  for (uint32_t i = kNumInitialThreads; i > 0; --i) {
397    threads.push_back(make_scoped_ptr(new Thread("test thread")));
398    auto thread = threads.back().get();
399    thread->Start();
400    scoped_refptr<SingleThreadTaskRunner> task_runner = thread->task_runner();
401    mdps.push_back(make_scoped_ptr(new MockMemoryDumpProvider()));
402    auto mdp = mdps.back().get();
403    RegisterDumpProvider(mdp, task_runner, kDefaultOptions);
404    EXPECT_CALL(*mdp, OnMemoryDump(_, _))
405        .Times(i)
406        .WillRepeatedly(Invoke(
407            [task_runner](const MemoryDumpArgs&, ProcessMemoryDump*) -> bool {
408              EXPECT_TRUE(task_runner->RunsTasksOnCurrentThread());
409              return true;
410            }));
411  }
412  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
413
414  while (!threads.empty()) {
415    last_callback_success_ = false;
416    EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
417    RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
418                             MemoryDumpLevelOfDetail::DETAILED);
419    EXPECT_TRUE(last_callback_success_);
420
421    // Unregister a MDP and destroy one thread at each iteration to check the
422    // live unregistration logic. The unregistration needs to happen on the same
423    // thread the MDP belongs to.
424    {
425      RunLoop run_loop;
426      Closure unregistration =
427          Bind(&MemoryDumpManager::UnregisterDumpProvider,
428               Unretained(mdm_.get()), Unretained(mdps.back().get()));
429      threads.back()->task_runner()->PostTaskAndReply(FROM_HERE, unregistration,
430                                                      run_loop.QuitClosure());
431      run_loop.Run();
432    }
433    mdps.pop_back();
434    threads.back()->Stop();
435    threads.pop_back();
436  }
437
438  DisableTracing();
439}
440
441// Checks that providers get disabled after 3 consecutive failures, but not
442// otherwise (e.g., if interleaved).
443TEST_F(MemoryDumpManagerTest, DisableFailingDumpers) {
444  InitializeMemoryDumpManager(false /* is_coordinator */);
445  MockMemoryDumpProvider mdp1;
446  MockMemoryDumpProvider mdp2;
447
448  RegisterDumpProvider(&mdp1);
449  RegisterDumpProvider(&mdp2);
450  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
451
452  const int kNumDumps = 2 * GetMaxConsecutiveFailuresCount();
453  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(kNumDumps);
454
455  EXPECT_CALL(mdp1, OnMemoryDump(_, _))
456      .Times(GetMaxConsecutiveFailuresCount())
457      .WillRepeatedly(Return(false));
458
459  EXPECT_CALL(mdp2, OnMemoryDump(_, _))
460      .WillOnce(Return(false))
461      .WillOnce(Return(true))
462      .WillOnce(Return(false))
463      .WillOnce(Return(false))
464      .WillOnce(Return(true))
465      .WillOnce(Return(false));
466
467  for (int i = 0; i < kNumDumps; i++) {
468    RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
469                             MemoryDumpLevelOfDetail::DETAILED);
470  }
471
472  DisableTracing();
473}
474
475// Sneakily registers an extra memory dump provider while an existing one is
476// dumping and expect it to take part in the already active tracing session.
477TEST_F(MemoryDumpManagerTest, RegisterDumperWhileDumping) {
478  InitializeMemoryDumpManager(false /* is_coordinator */);
479  MockMemoryDumpProvider mdp1;
480  MockMemoryDumpProvider mdp2;
481
482  RegisterDumpProvider(&mdp1);
483  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
484
485  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(4);
486
487  EXPECT_CALL(mdp1, OnMemoryDump(_, _))
488      .Times(4)
489      .WillOnce(Return(true))
490      .WillOnce(
491          Invoke([&mdp2](const MemoryDumpArgs&, ProcessMemoryDump*) -> bool {
492            RegisterDumpProvider(&mdp2);
493            return true;
494          }))
495      .WillRepeatedly(Return(true));
496
497  // Depending on the insertion order (before or after mdp1), mdp2 might be
498  // called also immediately after it gets registered.
499  EXPECT_CALL(mdp2, OnMemoryDump(_, _))
500      .Times(Between(2, 3))
501      .WillRepeatedly(Return(true));
502
503  for (int i = 0; i < 4; i++) {
504    RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
505                             MemoryDumpLevelOfDetail::DETAILED);
506  }
507
508  DisableTracing();
509}
510
511// Like RegisterDumperWhileDumping, but unregister the dump provider instead.
512TEST_F(MemoryDumpManagerTest, UnregisterDumperWhileDumping) {
513  InitializeMemoryDumpManager(false /* is_coordinator */);
514  MockMemoryDumpProvider mdp1;
515  MockMemoryDumpProvider mdp2;
516
517  RegisterDumpProvider(&mdp1, ThreadTaskRunnerHandle::Get(), kDefaultOptions);
518  RegisterDumpProvider(&mdp2, ThreadTaskRunnerHandle::Get(), kDefaultOptions);
519  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
520
521  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(4);
522
523  EXPECT_CALL(mdp1, OnMemoryDump(_, _))
524      .Times(4)
525      .WillOnce(Return(true))
526      .WillOnce(
527          Invoke([&mdp2](const MemoryDumpArgs&, ProcessMemoryDump*) -> bool {
528            MemoryDumpManager::GetInstance()->UnregisterDumpProvider(&mdp2);
529            return true;
530          }))
531      .WillRepeatedly(Return(true));
532
533  // Depending on the insertion order (before or after mdp1), mdp2 might have
534  // been already called when UnregisterDumpProvider happens.
535  EXPECT_CALL(mdp2, OnMemoryDump(_, _))
536      .Times(Between(1, 2))
537      .WillRepeatedly(Return(true));
538
539  for (int i = 0; i < 4; i++) {
540    RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
541                             MemoryDumpLevelOfDetail::DETAILED);
542  }
543
544  DisableTracing();
545}
546
547// Checks that the dump does not abort when unregistering a provider while
548// dumping from a different thread than the dumping thread.
549TEST_F(MemoryDumpManagerTest, UnregisterDumperFromThreadWhileDumping) {
550  InitializeMemoryDumpManager(false /* is_coordinator */);
551  std::vector<scoped_ptr<TestIOThread>> threads;
552  std::vector<scoped_ptr<MockMemoryDumpProvider>> mdps;
553
554  for (int i = 0; i < 2; i++) {
555    threads.push_back(
556        make_scoped_ptr(new TestIOThread(TestIOThread::kAutoStart)));
557    mdps.push_back(make_scoped_ptr(new MockMemoryDumpProvider()));
558    RegisterDumpProvider(mdps.back().get(), threads.back()->task_runner(),
559                         kDefaultOptions);
560  }
561
562  int on_memory_dump_call_count = 0;
563
564  // When OnMemoryDump is called on either of the dump providers, it will
565  // unregister the other one.
566  for (const scoped_ptr<MockMemoryDumpProvider>& mdp : mdps) {
567    int other_idx = (mdps.front() == mdp);
568    TestIOThread* other_thread = threads[other_idx].get();
569    MockMemoryDumpProvider* other_mdp = mdps[other_idx].get();
570    auto on_dump = [this, other_thread, other_mdp, &on_memory_dump_call_count](
571        const MemoryDumpArgs& args, ProcessMemoryDump* pmd) {
572      other_thread->PostTaskAndWait(
573          FROM_HERE, base::Bind(&MemoryDumpManager::UnregisterDumpProvider,
574                                base::Unretained(&*mdm_), other_mdp));
575      on_memory_dump_call_count++;
576      return true;
577    };
578
579    // OnMemoryDump is called once for the provider that dumps first, and zero
580    // times for the other provider.
581    EXPECT_CALL(*mdp, OnMemoryDump(_, _))
582        .Times(AtMost(1))
583        .WillOnce(Invoke(on_dump));
584  }
585
586  last_callback_success_ = false;
587  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
588  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
589  RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
590                           MemoryDumpLevelOfDetail::DETAILED);
591  ASSERT_EQ(1, on_memory_dump_call_count);
592  ASSERT_TRUE(last_callback_success_);
593
594  DisableTracing();
595}
596
597// If a thread (with a dump provider living on it) is torn down during a dump
598// its dump provider should be skipped but the dump itself should succeed.
599TEST_F(MemoryDumpManagerTest, TearDownThreadWhileDumping) {
600  InitializeMemoryDumpManager(false /* is_coordinator */);
601  std::vector<scoped_ptr<TestIOThread>> threads;
602  std::vector<scoped_ptr<MockMemoryDumpProvider>> mdps;
603
604  for (int i = 0; i < 2; i++) {
605    threads.push_back(
606        make_scoped_ptr(new TestIOThread(TestIOThread::kAutoStart)));
607    mdps.push_back(make_scoped_ptr(new MockMemoryDumpProvider()));
608    RegisterDumpProvider(mdps.back().get(), threads.back()->task_runner(),
609                         kDefaultOptions);
610  }
611
612  int on_memory_dump_call_count = 0;
613
614  // When OnMemoryDump is called on either of the dump providers, it will
615  // tear down the thread of the other one.
616  for (const scoped_ptr<MockMemoryDumpProvider>& mdp : mdps) {
617    int other_idx = (mdps.front() == mdp);
618    TestIOThread* other_thread = threads[other_idx].get();
619    auto on_dump = [other_thread, &on_memory_dump_call_count](
620        const MemoryDumpArgs& args, ProcessMemoryDump* pmd) {
621      other_thread->Stop();
622      on_memory_dump_call_count++;
623      return true;
624    };
625
626    // OnMemoryDump is called once for the provider that dumps first, and zero
627    // times for the other provider.
628    EXPECT_CALL(*mdp, OnMemoryDump(_, _))
629        .Times(AtMost(1))
630        .WillOnce(Invoke(on_dump));
631  }
632
633  last_callback_success_ = false;
634  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
635  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
636  RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
637                           MemoryDumpLevelOfDetail::DETAILED);
638  ASSERT_EQ(1, on_memory_dump_call_count);
639  ASSERT_TRUE(last_callback_success_);
640
641  DisableTracing();
642}
643
644// Checks that a NACK callback is invoked if RequestGlobalDump() is called when
645// tracing is not enabled.
646TEST_F(MemoryDumpManagerTest, CallbackCalledOnFailure) {
647  InitializeMemoryDumpManager(false /* is_coordinator */);
648  MockMemoryDumpProvider mdp1;
649  RegisterDumpProvider(&mdp1);
650
651  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(0);
652  EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(0);
653
654  last_callback_success_ = true;
655  RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
656                           MemoryDumpLevelOfDetail::DETAILED);
657  EXPECT_FALSE(last_callback_success_);
658}
659
660// Checks that is the MemoryDumpManager is initialized after tracing already
661// began, it will still late-join the party (real use case: startup tracing).
662TEST_F(MemoryDumpManagerTest, InitializedAfterStartOfTracing) {
663  MockMemoryDumpProvider mdp;
664  RegisterDumpProvider(&mdp);
665  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
666
667  // First check that a RequestGlobalDump() issued before the MemoryDumpManager
668  // initialization gets NACK-ed cleanly.
669  {
670    EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
671    EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(0);
672    RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
673                             MemoryDumpLevelOfDetail::DETAILED);
674    EXPECT_FALSE(last_callback_success_);
675  }
676
677  // Now late-initialize the MemoryDumpManager and check that the
678  // RequestGlobalDump completes successfully.
679  {
680    EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(1);
681    EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
682    InitializeMemoryDumpManager(false /* is_coordinator */);
683    RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
684                             MemoryDumpLevelOfDetail::DETAILED);
685    EXPECT_TRUE(last_callback_success_);
686  }
687  DisableTracing();
688}
689
690// This test (and the MemoryDumpManagerTestCoordinator below) crystallizes the
691// expectations of the chrome://tracing UI and chrome telemetry w.r.t. periodic
692// dumps in memory-infra, handling gracefully the transition between the legacy
693// and the new-style (JSON-based) TraceConfig.
694TEST_F(MemoryDumpManagerTest, TraceConfigExpectations) {
695  InitializeMemoryDumpManager(false /* is_coordinator */);
696  MemoryDumpManagerDelegateForTesting& delegate = *delegate_;
697
698  // Don't trigger the default behavior of the mock delegate in this test,
699  // which would short-circuit the dump request to the actual
700  // CreateProcessDump().
701  // We don't want to create any dump in this test, only check whether the dumps
702  // are requested or not.
703  ON_CALL(delegate, RequestGlobalMemoryDump(_, _)).WillByDefault(Return());
704
705  // Enabling memory-infra in a non-coordinator process should not trigger any
706  // periodic dumps.
707  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
708  EXPECT_FALSE(IsPeriodicDumpingEnabled());
709  DisableTracing();
710
711  // Enabling memory-infra with the new (JSON) TraceConfig in a non-coordinator
712  // process with a fully defined trigger config should NOT enable any periodic
713  // dumps.
714  EnableTracingWithTraceConfig(
715      TraceConfigMemoryTestUtil::GetTraceConfig_PeriodicTriggers(1, 5));
716  EXPECT_FALSE(IsPeriodicDumpingEnabled());
717  DisableTracing();
718}
719
720TEST_F(MemoryDumpManagerTest, TraceConfigExpectationsWhenIsCoordinator) {
721  InitializeMemoryDumpManager(true /* is_coordinator */);
722  MemoryDumpManagerDelegateForTesting& delegate = *delegate_;
723  ON_CALL(delegate, RequestGlobalMemoryDump(_, _)).WillByDefault(Return());
724
725  // Enabling memory-infra with the legacy TraceConfig (category filter) in
726  // a coordinator process should enable periodic dumps.
727  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
728  EXPECT_TRUE(IsPeriodicDumpingEnabled());
729  DisableTracing();
730
731  // Enabling memory-infra with the new (JSON) TraceConfig in a coordinator
732  // process without specifying any "memory_dump_config" section should enable
733  // periodic dumps. This is to preserve the behavior chrome://tracing UI, that
734  // is: ticking memory-infra should dump periodically with the default config.
735  EnableTracingWithTraceConfig(
736      TraceConfigMemoryTestUtil::GetTraceConfig_NoTriggers());
737  EXPECT_TRUE(IsPeriodicDumpingEnabled());
738  DisableTracing();
739
740  // Enabling memory-infra with the new (JSON) TraceConfig in a coordinator
741  // process with an empty "memory_dump_config" should NOT enable periodic
742  // dumps. This is the way telemetry is supposed to use memory-infra with
743  // only explicitly triggered dumps.
744  EnableTracingWithTraceConfig(
745      TraceConfigMemoryTestUtil::GetTraceConfig_EmptyTriggers());
746  EXPECT_FALSE(IsPeriodicDumpingEnabled());
747  DisableTracing();
748
749  // Enabling memory-infra with the new (JSON) TraceConfig in a coordinator
750  // process with a fully defined trigger config should cause periodic dumps to
751  // be performed in the correct order.
752  RunLoop run_loop;
753  auto quit_closure = run_loop.QuitClosure();
754
755  const int kHeavyDumpRate = 5;
756  const int kLightDumpPeriodMs = 1;
757  const int kHeavyDumpPeriodMs = kHeavyDumpRate * kLightDumpPeriodMs;
758  // The expected sequence with light=1ms, heavy=5ms is H,L,L,L,L,H,...
759  testing::InSequence sequence;
760  EXPECT_CALL(delegate, RequestGlobalMemoryDump(IsDetailedDump(), _));
761  EXPECT_CALL(delegate, RequestGlobalMemoryDump(IsLightDump(), _))
762      .Times(kHeavyDumpRate - 1);
763  EXPECT_CALL(delegate, RequestGlobalMemoryDump(IsDetailedDump(), _));
764  EXPECT_CALL(delegate, RequestGlobalMemoryDump(IsLightDump(), _))
765      .Times(kHeavyDumpRate - 2);
766  EXPECT_CALL(delegate, RequestGlobalMemoryDump(IsLightDump(), _))
767      .WillOnce(Invoke([quit_closure](const MemoryDumpRequestArgs& args,
768                                      const MemoryDumpCallback& callback) {
769        ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, quit_closure);
770      }));
771
772  // Swallow all the final spurious calls until tracing gets disabled.
773  EXPECT_CALL(delegate, RequestGlobalMemoryDump(_, _)).Times(AnyNumber());
774
775  EnableTracingWithTraceConfig(
776      TraceConfigMemoryTestUtil::GetTraceConfig_PeriodicTriggers(
777          kLightDumpPeriodMs, kHeavyDumpPeriodMs));
778  run_loop.Run();
779  DisableTracing();
780}
781
782// Tests against race conditions that might arise when disabling tracing in the
783// middle of a global memory dump.
784TEST_F(MemoryDumpManagerTest, DisableTracingWhileDumping) {
785  base::WaitableEvent tracing_disabled_event(false, false);
786  InitializeMemoryDumpManager(false /* is_coordinator */);
787
788  // Register a bound dump provider.
789  scoped_ptr<Thread> mdp_thread(new Thread("test thread"));
790  mdp_thread->Start();
791  MockMemoryDumpProvider mdp_with_affinity;
792  RegisterDumpProvider(&mdp_with_affinity, mdp_thread->task_runner(),
793                       kDefaultOptions);
794
795  // Register also an unbound dump provider. Unbound dump providers are always
796  // invoked after bound ones.
797  MockMemoryDumpProvider unbound_mdp;
798  RegisterDumpProvider(&unbound_mdp, nullptr, kDefaultOptions);
799
800  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
801  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
802  EXPECT_CALL(mdp_with_affinity, OnMemoryDump(_, _))
803      .Times(1)
804      .WillOnce(
805          Invoke([&tracing_disabled_event](const MemoryDumpArgs&,
806                                           ProcessMemoryDump* pmd) -> bool {
807            tracing_disabled_event.Wait();
808
809            // At this point tracing has been disabled and the
810            // MemoryDumpManager.dump_thread_ has been shut down.
811            return true;
812          }));
813
814  // |unbound_mdp| should never be invoked because the thread for unbound dump
815  // providers has been shutdown in the meanwhile.
816  EXPECT_CALL(unbound_mdp, OnMemoryDump(_, _)).Times(0);
817
818  last_callback_success_ = true;
819  RunLoop run_loop;
820  MemoryDumpCallback callback =
821      Bind(&MemoryDumpManagerTest::DumpCallbackAdapter, Unretained(this),
822           MessageLoop::current()->task_runner(), run_loop.QuitClosure());
823  mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
824                          MemoryDumpLevelOfDetail::DETAILED, callback);
825  DisableTracing();
826  tracing_disabled_event.Signal();
827  run_loop.Run();
828
829  // RequestGlobalMemoryDump() should still suceed even if some threads were
830  // torn down during the dump.
831  EXPECT_TRUE(last_callback_success_);
832}
833
834TEST_F(MemoryDumpManagerTest, DumpOnBehalfOfOtherProcess) {
835  using trace_analyzer::Query;
836
837  InitializeMemoryDumpManager(false /* is_coordinator */);
838
839  // Standard provider with default options (create dump for current process).
840  MemoryDumpProvider::Options options;
841  MockMemoryDumpProvider mdp1;
842  RegisterDumpProvider(&mdp1, nullptr, options);
843
844  // Provider with out-of-process dumping.
845  MockMemoryDumpProvider mdp2;
846  options.target_pid = 123;
847  RegisterDumpProvider(&mdp2, nullptr, options);
848
849  // Another provider with out-of-process dumping.
850  MockMemoryDumpProvider mdp3;
851  options.target_pid = 456;
852  RegisterDumpProvider(&mdp3, nullptr, options);
853
854  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
855  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(1);
856  EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(1).WillRepeatedly(Return(true));
857  EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(1).WillRepeatedly(Return(true));
858  EXPECT_CALL(mdp3, OnMemoryDump(_, _)).Times(1).WillRepeatedly(Return(true));
859  RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
860                           MemoryDumpLevelOfDetail::DETAILED);
861  DisableTracing();
862
863  // Flush the trace into JSON.
864  trace_event::TraceResultBuffer buffer;
865  TraceResultBuffer::SimpleOutput trace_output;
866  buffer.SetOutputCallback(trace_output.GetCallback());
867  RunLoop run_loop;
868  buffer.Start();
869  trace_event::TraceLog::GetInstance()->Flush(
870      Bind(&OnTraceDataCollected, run_loop.QuitClosure(), Unretained(&buffer)));
871  run_loop.Run();
872  buffer.Finish();
873
874  // Analyze the JSON.
875  scoped_ptr<trace_analyzer::TraceAnalyzer> analyzer = make_scoped_ptr(
876      trace_analyzer::TraceAnalyzer::Create(trace_output.json_output));
877  trace_analyzer::TraceEventVector events;
878  analyzer->FindEvents(Query::EventPhaseIs(TRACE_EVENT_PHASE_MEMORY_DUMP),
879                       &events);
880
881  ASSERT_EQ(3u, events.size());
882  ASSERT_EQ(1u, trace_analyzer::CountMatches(events, Query::EventPidIs(123)));
883  ASSERT_EQ(1u, trace_analyzer::CountMatches(events, Query::EventPidIs(456)));
884  ASSERT_EQ(1u, trace_analyzer::CountMatches(
885                    events, Query::EventPidIs(GetCurrentProcId())));
886  ASSERT_EQ(events[0]->id, events[1]->id);
887  ASSERT_EQ(events[0]->id, events[2]->id);
888}
889
890// Tests the basics of the UnregisterAndDeleteDumpProviderSoon(): the
891// unregistration should actually delete the providers and not leak them.
892TEST_F(MemoryDumpManagerTest, UnregisterAndDeleteDumpProviderSoon) {
893  InitializeMemoryDumpManager(false /* is_coordinator */);
894  static const int kNumProviders = 3;
895  int dtor_count = 0;
896  std::vector<scoped_ptr<MemoryDumpProvider>> mdps;
897  for (int i = 0; i < kNumProviders; ++i) {
898    scoped_ptr<MockMemoryDumpProvider> mdp(new MockMemoryDumpProvider);
899    mdp->enable_mock_destructor = true;
900    EXPECT_CALL(*mdp, Destructor())
901        .WillOnce(Invoke([&dtor_count]() { dtor_count++; }));
902    RegisterDumpProvider(mdp.get(), nullptr, kDefaultOptions);
903    mdps.push_back(std::move(mdp));
904  }
905
906  while (!mdps.empty()) {
907    mdm_->UnregisterAndDeleteDumpProviderSoon(std::move(mdps.back()));
908    mdps.pop_back();
909  }
910
911  ASSERT_EQ(kNumProviders, dtor_count);
912}
913
914// This test checks against races when unregistering an unbound dump provider
915// from another thread while dumping. It registers one MDP and, when
916// OnMemoryDump() is called, it invokes UnregisterAndDeleteDumpProviderSoon()
917// from another thread. The OnMemoryDump() and the dtor call are expected to
918// happen on the same thread (the MemoryDumpManager utility thread).
919TEST_F(MemoryDumpManagerTest, UnregisterAndDeleteDumpProviderSoonDuringDump) {
920  InitializeMemoryDumpManager(false /* is_coordinator */);
921  scoped_ptr<MockMemoryDumpProvider> mdp(new MockMemoryDumpProvider);
922  mdp->enable_mock_destructor = true;
923  RegisterDumpProvider(mdp.get(), nullptr, kDefaultOptions);
924
925  base::PlatformThreadRef thread_ref;
926  auto self_unregister_from_another_thread = [&mdp, &thread_ref](
927      const MemoryDumpArgs&, ProcessMemoryDump*) -> bool {
928    thread_ref = PlatformThread::CurrentRef();
929    TestIOThread thread_for_unregistration(TestIOThread::kAutoStart);
930    thread_for_unregistration.PostTaskAndWait(
931        FROM_HERE,
932        base::Bind(
933            &MemoryDumpManager::UnregisterAndDeleteDumpProviderSoon,
934            base::Unretained(MemoryDumpManager::GetInstance()),
935            base::Passed(scoped_ptr<MemoryDumpProvider>(std::move(mdp)))));
936    thread_for_unregistration.Stop();
937    return true;
938  };
939  EXPECT_CALL(*mdp, OnMemoryDump(_, _))
940      .Times(1)
941      .WillOnce(Invoke(self_unregister_from_another_thread));
942  EXPECT_CALL(*mdp, Destructor())
943      .Times(1)
944      .WillOnce(Invoke([&thread_ref]() {
945        EXPECT_EQ(thread_ref, PlatformThread::CurrentRef());
946      }));
947
948  EnableTracingWithLegacyCategories(MemoryDumpManager::kTraceCategory);
949  EXPECT_CALL(*delegate_, RequestGlobalMemoryDump(_, _)).Times(2);
950  for (int i = 0; i < 2; ++i) {
951    RequestGlobalDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
952                             MemoryDumpLevelOfDetail::DETAILED);
953  }
954  DisableTracing();
955}
956
957}  // namespace trace_event
958}  // namespace base
959