910e62b5创建于 1月15日历史提交
// 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 "content/browser/devtools/protocol/memory_handler.h"

#include <cinttypes>

#include "base/functional/bind.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/profiler/module_cache.h"
#include "base/sampling_heap_profiler/sampling_heap_profiler.h"
#include "base/strings/stringprintf.h"
#include "content/public/browser/child_process_host.h"
#include "content/public/browser/render_process_host.h"

namespace content {
namespace protocol {

namespace {
constexpr char kAnotherLeakDetectionInProgress[] =
    "Another leak detection in progress";
constexpr char kNoProcessToDetectLeaksIn[] = "No process to detect leaks in";
constexpr char kFailedToRunLeakDetection[] = "Failed to run leak detection";
}  // namespace

MemoryHandler::MemoryHandler()
    : DevToolsDomainHandler(Memory::Metainfo::domainName),
      process_host_id_(ChildProcessHost::kInvalidUniqueID) {}

MemoryHandler::~MemoryHandler() = default;

void MemoryHandler::Wire(UberDispatcher* dispatcher) {
  Memory::Dispatcher::wire(dispatcher, this);
}

void MemoryHandler::SetRenderer(int process_host_id,
                                RenderFrameHostImpl* frame_host) {
  process_host_id_ = process_host_id;
}

Response MemoryHandler::GetBrowserSamplingProfile(
    std::unique_ptr<Memory::SamplingProfile>* out_profile) {
  base::ModuleCache module_cache;
  auto samples = std::make_unique<Array<Memory::SamplingProfileNode>>();
  std::vector<base::SamplingHeapProfiler::Sample> raw_samples =
      base::SamplingHeapProfiler::Get()->GetSamples(0);

  for (auto& sample : raw_samples) {
    auto stack = std::make_unique<Array<String>>();
    for (const void* frame : sample.stack) {
      uintptr_t address = reinterpret_cast<uintptr_t>(frame);
      module_cache.GetModuleForAddress(address);  // Populates module_cache.
      stack->emplace_back(base::StringPrintf("0x%" PRIxPTR, address));
    }
    samples->emplace_back(Memory::SamplingProfileNode::Create()
                              .SetSize(sample.size)
                              .SetTotal(sample.total)
                              .SetStack(std::move(stack))
                              .Build());
  }

  auto modules = std::make_unique<Array<Memory::Module>>();
  for (const auto* module : module_cache.GetModules()) {
    modules->emplace_back(
        Memory::Module::Create()
            .SetName(base::StringPrintf(
                "%" PRFilePath, module->GetDebugBasename().value().c_str()))
            .SetUuid(module->GetId())
            .SetBaseAddress(
                base::StringPrintf("0x%" PRIxPTR, module->GetBaseAddress()))
            .SetSize(static_cast<double>(module->GetSize()))
            .Build());
  }

  *out_profile = Memory::SamplingProfile::Create()
                     .SetSamples(std::move(samples))
                     .SetModules(std::move(modules))
                     .Build();
  return Response::Success();
}

Response MemoryHandler::SetPressureNotificationsSuppressed(
    bool suppressed) {
  base::MemoryPressureListener::SetNotificationsSuppressed(suppressed);
  return Response::Success();
}

Response MemoryHandler::SimulatePressureNotification(
    const std::string& level) {
  base::MemoryPressureLevel parsed_level;
  if (level == protocol::Memory::PressureLevelEnum::Moderate) {
    parsed_level = base::MEMORY_PRESSURE_LEVEL_MODERATE;
  } else if (level == protocol::Memory::PressureLevelEnum::Critical) {
    parsed_level = base::MEMORY_PRESSURE_LEVEL_CRITICAL;
  } else {
    return Response::InvalidParams(base::StringPrintf(
        "Invalid memory pressure level '%s'", level.c_str()));
  }

  // Simulate memory pressure notification in the browser process.
  base::MemoryPressureListener::SimulatePressureNotification(parsed_level);
  return Response::Success();
}

void MemoryHandler::PrepareForLeakDetection(
    std::unique_ptr<PrepareForLeakDetectionCallback> callback) {
  if (prepare_for_leak_detection_callback_ ||
      get_dom_counters_for_leak_detection_callback_) {
    callback->sendFailure(
        Response::ServerError(kAnotherLeakDetectionInProgress));
    return;
  }

  RenderProcessHost* process = RenderProcessHost::FromID(process_host_id_);
  if (!process) {
    callback->sendFailure(Response::ServerError(kNoProcessToDetectLeaksIn));
    return;
  }

  prepare_for_leak_detection_callback_ = std::move(callback);

  RequestLeakDetection(process);
}

void MemoryHandler::GetDOMCountersForLeakDetection(
    std::unique_ptr<GetDOMCountersForLeakDetectionCallback> callback) {
  if (prepare_for_leak_detection_callback_ ||
      get_dom_counters_for_leak_detection_callback_) {
    callback->sendFailure(
        Response::ServerError(kAnotherLeakDetectionInProgress));
    return;
  }

  RenderProcessHost* process = RenderProcessHost::FromID(process_host_id_);
  if (!process) {
    callback->sendFailure(Response::ServerError(kNoProcessToDetectLeaksIn));
    return;
  }

  get_dom_counters_for_leak_detection_callback_ = std::move(callback);

  RequestLeakDetection(process);
}

void MemoryHandler::RequestLeakDetection(RenderProcessHost* process) {
  process->BindReceiver(leak_detector_.BindNewPipeAndPassReceiver());

  // Using base::Unretained(this) in the code below is safe because the
  // callbacks are passed to the mojo remote member, so they are guaranteed
  // not to survive this.
  leak_detector_.set_disconnect_handler(base::BindOnce(
      &MemoryHandler::OnLeakDetectorIsGone, base::Unretained(this)));

  leak_detector_->PerformLeakDetection(base::BindOnce(
      &MemoryHandler::OnLeakDetectionComplete, base::Unretained(this)));
}

void MemoryHandler::OnLeakDetectionComplete(
    blink::mojom::LeakDetectionResultPtr result) {
  if (prepare_for_leak_detection_callback_) {
    prepare_for_leak_detection_callback_->sendSuccess();
    prepare_for_leak_detection_callback_.reset();
  }
  if (get_dom_counters_for_leak_detection_callback_) {
    if (result) {
      get_dom_counters_for_leak_detection_callback_->sendSuccess(
          GetDOMCounters(*result.get()));
    } else {
      get_dom_counters_for_leak_detection_callback_->sendFailure(
          Response::ServerError(kFailedToRunLeakDetection));
    }
    get_dom_counters_for_leak_detection_callback_.reset();
  }
  leak_detector_.reset();
}

void MemoryHandler::OnLeakDetectorIsGone() {
  if (prepare_for_leak_detection_callback_) {
    prepare_for_leak_detection_callback_->sendFailure(
        Response::ServerError(kFailedToRunLeakDetection));
    prepare_for_leak_detection_callback_.reset();
  }
  if (get_dom_counters_for_leak_detection_callback_) {
    get_dom_counters_for_leak_detection_callback_->sendFailure(
        Response::ServerError(kFailedToRunLeakDetection));
    get_dom_counters_for_leak_detection_callback_.reset();
  }
  leak_detector_.reset();
}

std::unique_ptr<protocol::Array<protocol::Memory::DOMCounter>>
MemoryHandler::GetDOMCounters(const blink::mojom::LeakDetectionResult& result) {
  auto counters =
      std::make_unique<protocol::Array<protocol::Memory::DOMCounter>>();

#define ADD_COUNTER(name)                                       \
  counters->emplace_back(protocol::Memory::DOMCounter::Create() \
                             .SetName(#name)                    \
                             .SetCount(result.number_of_##name) \
                             .Build());
  ADD_COUNTER(live_audio_nodes)
  ADD_COUNTER(live_documents)
  ADD_COUNTER(live_nodes)
  ADD_COUNTER(live_layout_objects)
  ADD_COUNTER(live_resources)
  ADD_COUNTER(live_context_lifecycle_state_observers)
  ADD_COUNTER(live_frames)
  ADD_COUNTER(live_v8_per_context_data)
  ADD_COUNTER(worker_global_scopes)
  ADD_COUNTER(live_ua_css_resources)
  ADD_COUNTER(live_resource_fetchers)

#undef ADD_COUNTER

  return counters;
}

}  // namespace protocol
}  // namespace content