chromium/content/browser/tracing/memory_tracing_browsertest.cc

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stdint.h>

#include <memory>

#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/memory_dump_provider.h"
#include "base/trace_event/memory_dump_request_args.h"
#include "base/trace_event/trace_config_memory_test_util.h"
#include "base/trace_event/trace_log.h"
#include "build/build_config.h"
#include "content/browser/tracing/tracing_controller_impl.h"
#include "content/public/browser/tracing_controller.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
#include "testing/gmock/include/gmock/gmock.h"

MemoryDumpArgs;
MemoryDumpDeterminism;
MemoryDumpLevelOfDetail;
MemoryDumpManager;
MemoryDumpType;
ProcessMemoryDump;
_;
Return;

namespace content {

// A mock dump provider, used to check that dump requests actually end up
// creating memory dumps.
class MockDumpProvider : public base::trace_event::MemoryDumpProvider {};

class MemoryTracingTest : public ContentBrowserTest {};

// Run SingleProcessMemoryTracingTests only on Android, since these tests are
// intended to give coverage to Android WebView.
#if BUILDFLAG(IS_ANDROID)

class SingleProcessMemoryTracingTest : public MemoryTracingTest {
 public:
  SingleProcessMemoryTracingTest() {}

  void SetUpCommandLine(base::CommandLine* command_line) override {
    command_line->AppendSwitch(switches::kSingleProcess);
  }
};

// https://crbug.com/788788
#if BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)
#define MAYBE_BrowserInitiatedSingleDump
#else
#define MAYBE_BrowserInitiatedSingleDump
#endif  // BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)

// Checks that a memory dump initiated from a the main browser thread ends up in
// a single dump even in single process mode.
IN_PROC_BROWSER_TEST_F(SingleProcessMemoryTracingTest,
                       MAYBE_BrowserInitiatedSingleDump) {
  Navigate(shell());

  EXPECT_CALL(*mock_dump_provider_, OnMemoryDump(_,_)).WillOnce(Return(true));
  EXPECT_CALL(*this, OnMemoryDumpDone(_, true /* success */));

  EnableMemoryTracing();
  RequestGlobalDumpAndWait(false /* from_renderer_thread */,
                           MemoryDumpType::kExplicitlyTriggered,
                           MemoryDumpLevelOfDetail::kDetailed);
  DisableTracing();
}

// https://crbug.com/788788
#if BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)
#define MAYBE_RendererInitiatedSingleDump
#else
#define MAYBE_RendererInitiatedSingleDump
#endif  // BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)

// Checks that a memory dump initiated from a renderer thread ends up in a
// single dump even in single process mode.
IN_PROC_BROWSER_TEST_F(SingleProcessMemoryTracingTest,
                       MAYBE_RendererInitiatedSingleDump) {
  Navigate(shell());

  EXPECT_CALL(*mock_dump_provider_, OnMemoryDump(_,_)).WillOnce(Return(true));
  EXPECT_CALL(*this, OnMemoryDumpDone(_, true /* success */));

  EnableMemoryTracing();
  RequestGlobalDumpAndWait(true /* from_renderer_thread */,
                           MemoryDumpType::kExplicitlyTriggered,
                           MemoryDumpLevelOfDetail::kDetailed);
  DisableTracing();
}

// https://crbug.com/788788
#if BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)
#define MAYBE_ManyInterleavedDumps
#else
#define MAYBE_ManyInterleavedDumps
#endif  // BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)
IN_PROC_BROWSER_TEST_F(SingleProcessMemoryTracingTest,
                       MAYBE_ManyInterleavedDumps) {
  Navigate(shell());

  EXPECT_CALL(*mock_dump_provider_, OnMemoryDump(_,_))
      .Times(4)
      .WillRepeatedly(Return(true));
  EXPECT_CALL(*this, OnMemoryDumpDone(_, true /* success */)).Times(4);

  EnableMemoryTracing();
  RequestGlobalDumpAndWait(true /* from_renderer_thread */,
                           MemoryDumpType::kExplicitlyTriggered,
                           MemoryDumpLevelOfDetail::kDetailed);
  RequestGlobalDumpAndWait(false /* from_renderer_thread */,
                           MemoryDumpType::kExplicitlyTriggered,
                           MemoryDumpLevelOfDetail::kDetailed);
  RequestGlobalDumpAndWait(false /* from_renderer_thread */,
                           MemoryDumpType::kExplicitlyTriggered,
                           MemoryDumpLevelOfDetail::kDetailed);
  RequestGlobalDumpAndWait(true /* from_renderer_thread */,
                           MemoryDumpType::kExplicitlyTriggered,
                           MemoryDumpLevelOfDetail::kDetailed);
  DisableTracing();
}

// Checks that, if there already is a memory dump in progress, subsequent memory
// dump requests are queued and carried out after it's finished. Also checks
// that periodic dump requests fail in case there is already a request in the
// queue with the same level of detail.
// Flaky failures on all platforms. https://crbug.com/752613
IN_PROC_BROWSER_TEST_F(SingleProcessMemoryTracingTest, DISABLED_QueuedDumps) {
  Navigate(shell());

  EnableMemoryTracing();

  // Issue the following 6 global memory dump requests:
  //
  //   0 (ED)  req-------------------------------------->ok
  //   1 (PD)      req->fail(0)
  //   2 (PL)                   req------------------------>ok
  //   3 (PL)                       req->fail(2)
  //   4 (EL)                                    req---------->ok
  //   5 (ED)                                        req--------->ok
  //   6 (PL)                                                        req->ok
  //
  // where P=kPeriodicInterval, E=kExplicitlyTriggered, D=kDetailed and L=kLight.

  EXPECT_CALL(*mock_dump_provider_, OnMemoryDump(_, _))
      .Times(5)
      .WillRepeatedly(Return(true));

  EXPECT_CALL(*this, OnMemoryDumpDone(0, true /* success */));
  RequestGlobalDump(true /* from_renderer_thread */,
                    MemoryDumpType::kExplicitlyTriggered,
                    MemoryDumpLevelOfDetail::kDetailed);

  // This dump should fail immediately because there's already a detailed dump
  // request in the queue.
  EXPECT_CALL(*this, OnMemoryDumpDone(1, false /* success */));
  RequestGlobalDump(true /* from_renderer_thread */,
                    MemoryDumpType::kPeriodicInterval,
                    MemoryDumpLevelOfDetail::kDetailed);

  EXPECT_CALL(*this, OnMemoryDumpDone(2, true /* success */));
  RequestGlobalDump(true /* from_renderer_thread */,
                    MemoryDumpType::kPeriodicInterval,
                    MemoryDumpLevelOfDetail::kLight);

  // This dump should fail immediately because there's already a light dump
  // request in the queue.
  EXPECT_CALL(*this, OnMemoryDumpDone(3, false /* success */));
  RequestGlobalDump(true /* from_renderer_thread */,
                    MemoryDumpType::kPeriodicInterval,
                    MemoryDumpLevelOfDetail::kLight);

  EXPECT_CALL(*this, OnMemoryDumpDone(4, true /* success */));
  RequestGlobalDump(true /* from_renderer_thread */,
                    MemoryDumpType::kExplicitlyTriggered,
                    MemoryDumpLevelOfDetail::kLight);

  EXPECT_CALL(*this, OnMemoryDumpDone(5, true /* success */));
  RequestGlobalDumpAndWait(true /* from_renderer_thread */,
                           MemoryDumpType::kExplicitlyTriggered,
                           MemoryDumpLevelOfDetail::kDetailed);

  EXPECT_CALL(*this, OnMemoryDumpDone(6, true /* success */));
  RequestGlobalDumpAndWait(true /* from_renderer_thread */,
                           MemoryDumpType::kPeriodicInterval,
                           MemoryDumpLevelOfDetail::kLight);

  DisableTracing();
}

#endif  // BUILDFLAG(IS_ANDROID)

// Flaky on Mac. crbug.com/809809
// Failing on Android ASAN. crbug.com/1041392
// TODO(crbug.com/40720107): OSMetrics::GetProcessMemoryMaps is not
// implemented on Fuchsia
#if BUILDFLAG(IS_MAC) ||                                     \
    (BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)) || \
    BUILDFLAG(IS_FUCHSIA)
#define MAYBE_BrowserInitiatedDump
#else
#define MAYBE_BrowserInitiatedDump
#endif
// Checks that a memory dump initiated from a the main browser thread ends up in
// a successful dump.
IN_PROC_BROWSER_TEST_F(MemoryTracingTest, MAYBE_BrowserInitiatedDump) {}

}  // namespace content