Skip to content

Commit

Permalink
memory-infra: Service implementation
Browse files Browse the repository at this point in the history
This adds the memory instrumentation service. The service implements
the Coordinator interface.

Note about thread safety: all interface implementation's methods are
called from the thread that bound it to the message pipe. So, there is
no need to switch threads in the implementation. However, we should be
careful to bind the service on the correct thread, if needed (e.g. if
calls should be made from the UI thread).

A prototype of the end product: https://codereview.chromium.org/2571823002

BUG=679830

Review-Url: https://codereview.chromium.org/2647773002
Cr-Commit-Position: refs/heads/master@{#447349}
  • Loading branch information
chiniforooshan authored and Commit bot committed Jan 31, 2017
1 parent d49206b commit 1ce377e
Show file tree
Hide file tree
Showing 8 changed files with 488 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ _typemap_imports = [
"//mojo/common/typemaps.gni",
"//mojo/public/cpp/bindings/tests/chromium_typemaps.gni",
"//net/interfaces/typemaps.gni",
"//services/memory_instrumentation/public/cpp/typemaps.gni",
"//services/service_manager/public/cpp/typemaps.gni",
"//services/ui/gpu/interfaces/typemaps.gni",
"//services/ui/public/interfaces/ime/typemaps.gni",
Expand Down
1 change: 1 addition & 0 deletions services/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import("//testing/test.gni")
service_test("service_unittests") {
deps = [
"//services/image_decoder:tests",
"//services/memory_instrumentation:tests",
]

if (is_android) {
Expand Down
31 changes: 31 additions & 0 deletions services/memory_instrumentation/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright 2017 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.

source_set("lib") {
sources = [
"coordinator_impl.cc",
"coordinator_impl.h",
]

public_deps = [
"//base",
"//services/memory_instrumentation/public/cpp",
"//services/memory_instrumentation/public/interfaces",
]
}

source_set("tests") {
testonly = true

sources = [
"coordinator_impl_unittest.cc",
]

deps = [
":lib",
"//base",
"//mojo/public/cpp/bindings",
"//testing/gtest",
]
}
186 changes: 186 additions & 0 deletions services/memory_instrumentation/coordinator_impl.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// Copyright 2017 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 "services/memory_instrumentation/coordinator_impl.h"

#include "base/bind_helpers.h"
#include "base/lazy_instance.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/memory_dump_request_args.h"
#include "services/memory_instrumentation/public/interfaces/memory_instrumentation.mojom.h"

namespace memory_instrumentation {

namespace {

base::LazyInstance<CoordinatorImpl>::Leaky g_coordinator =
LAZY_INSTANCE_INITIALIZER;

} // namespace

// static
CoordinatorImpl* CoordinatorImpl::GetInstance() {
return g_coordinator.Pointer();
}

// TODO(chiniforooshan): Initialize the global MemoryDumpManager instance here.
// This is how the global MemoryDumpManager gets a reference to the delegate on
// the service (read the browser) process for service process memory dumps. This
// can be done when the delegate implementation is landed.
CoordinatorImpl::CoordinatorImpl()
: failed_memory_dump_count_(0) {}

CoordinatorImpl::~CoordinatorImpl() {}

void CoordinatorImpl::BindCoordinatorRequest(
mojom::CoordinatorRequest request) {
bindings_.AddBinding(this, std::move(request));
}

CoordinatorImpl::QueuedMemoryDumpRequest::QueuedMemoryDumpRequest(
const base::trace_event::MemoryDumpRequestArgs args,
const RequestGlobalMemoryDumpCallback callback)
: args(args), callback(callback) {}

CoordinatorImpl::QueuedMemoryDumpRequest::~QueuedMemoryDumpRequest() {}

void CoordinatorImpl::RequestGlobalMemoryDump(
const base::trace_event::MemoryDumpRequestArgs& args,
const RequestGlobalMemoryDumpCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());

bool another_dump_already_in_progress = !queued_memory_dump_requests_.empty();

// If this is a periodic memory dump request and there already is another
// request in the queue with the same level of detail, there's no point in
// enqueuing this request.
if (another_dump_already_in_progress &&
args.dump_type == base::trace_event::MemoryDumpType::PERIODIC_INTERVAL) {
for (const auto& request : queued_memory_dump_requests_) {
if (request.args.level_of_detail == args.level_of_detail) {
VLOG(1) << base::trace_event::MemoryDumpManager::kLogPrefix << " ("
<< base::trace_event::MemoryDumpTypeToString(args.dump_type)
<< ") skipped because another dump request with the same "
"level of detail ("
<< base::trace_event::MemoryDumpLevelOfDetailToString(
args.level_of_detail)
<< ") is already in the queue";
callback.Run(args.dump_guid, false /* success */);
return;
}
}
}

queued_memory_dump_requests_.emplace_back(args, callback);

// If another dump is already in progress, this dump will automatically be
// scheduled when the other dump finishes.
if (another_dump_already_in_progress)
return;

PerformNextQueuedGlobalMemoryDump();
}

void CoordinatorImpl::RegisterProcessLocalDumpManager(
mojom::ProcessLocalDumpManagerPtr process_manager) {
DCHECK(thread_checker_.CalledOnValidThread());

process_manager.set_connection_error_handler(
base::Bind(&CoordinatorImpl::UnregisterProcessLocalDumpManager,
base::Unretained(this),
process_manager.get()));
auto result = process_managers_.insert(
std::make_pair<mojom::ProcessLocalDumpManager*,
mojom::ProcessLocalDumpManagerPtr>(
process_manager.get(), std::move(process_manager)));
DCHECK(result.second);
}

void CoordinatorImpl::UnregisterProcessLocalDumpManager(
mojom::ProcessLocalDumpManager* process_manager) {
DCHECK(process_managers_.erase(process_manager) == 1);

// Check if we are waiting for an ack from this process-local manager.
if (pending_process_managers_.find(process_manager) !=
pending_process_managers_.end()) {
DCHECK(!queued_memory_dump_requests_.empty());
OnProcessMemoryDumpResponse(
process_manager,
queued_memory_dump_requests_.front().args.dump_guid,
false /* success */);
}
}

void CoordinatorImpl::PerformNextQueuedGlobalMemoryDump() {
DCHECK(!queued_memory_dump_requests_.empty());
const base::trace_event::MemoryDumpRequestArgs& args =
queued_memory_dump_requests_.front().args;

// No need to treat the service process different than other processes. The
// service process will register itself as a ProcessLocalDumpManager and will
// be treated like other process-local managers.
pending_process_managers_.clear();
failed_memory_dump_count_ = 0;
for (const auto& key_value : process_managers_) {
pending_process_managers_.insert(key_value.first);
key_value.second->RequestProcessMemoryDump(
args,
base::Bind(&CoordinatorImpl::OnProcessMemoryDumpResponse,
base::Unretained(this),
key_value.first));
}
// Run the callback in case there are no process-local managers.
FinalizeGlobalMemoryDumpIfAllManagersReplied();
}

void CoordinatorImpl::OnProcessMemoryDumpResponse(
mojom::ProcessLocalDumpManager* process_manager,
uint64_t dump_guid,
bool success) {
auto it = pending_process_managers_.find(process_manager);

DCHECK(!queued_memory_dump_requests_.empty());
if (queued_memory_dump_requests_.front().args.dump_guid != dump_guid ||
it == pending_process_managers_.end()) {
VLOG(1) << "Received unexpected memory dump response: " << dump_guid;
return;
}
pending_process_managers_.erase(it);

if (!success) {
++failed_memory_dump_count_;
VLOG(1) << base::trace_event::MemoryDumpManager::kLogPrefix
<< " failed because of NACK from provider";
}
FinalizeGlobalMemoryDumpIfAllManagersReplied();
}

void CoordinatorImpl::FinalizeGlobalMemoryDumpIfAllManagersReplied() {
if (pending_process_managers_.size() > 0)
return;

DCHECK(!queued_memory_dump_requests_.empty());
{
const auto& callback = queued_memory_dump_requests_.front().callback;
const bool global_success = failed_memory_dump_count_ == 0;
callback.Run(queued_memory_dump_requests_.front().args.dump_guid,
global_success);
}
queued_memory_dump_requests_.pop_front();

// Schedule the next queued dump (if applicable).
if (!queued_memory_dump_requests_.empty()) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(
&CoordinatorImpl::PerformNextQueuedGlobalMemoryDump,
base::Unretained(this)));
}
}

} // namespace memory_instrumentation
87 changes: 87 additions & 0 deletions services/memory_instrumentation/coordinator_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2017 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.

#ifndef SERVICES_MEMORY_INSTRUMENTATION_COORDINATOR_IMPL_H_
#define SERVICES_MEMORY_INSTRUMENTATION_COORDINATOR_IMPL_H_

#include <list>
#include <set>
#include <unordered_map>

#include "base/lazy_instance.h"
#include "base/memory/ref_counted.h"
#include "base/threading/thread_checker.h"
#include "base/trace_event/memory_dump_request_args.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "services/memory_instrumentation/public/cpp/coordinator.h"
#include "services/memory_instrumentation/public/interfaces/memory_instrumentation.mojom.h"

namespace memory_instrumentation {

class CoordinatorImpl : public Coordinator, public mojom::Coordinator {
public:
static CoordinatorImpl* GetInstance();

// Coordinator
void BindCoordinatorRequest(mojom::CoordinatorRequest) override;

private:
friend class CoordinatorImplTest; // For testing
friend struct base::DefaultLazyInstanceTraits<CoordinatorImpl>;

struct QueuedMemoryDumpRequest {
QueuedMemoryDumpRequest(
const base::trace_event::MemoryDumpRequestArgs args,
const RequestGlobalMemoryDumpCallback callback);
~QueuedMemoryDumpRequest();
const base::trace_event::MemoryDumpRequestArgs args;
const RequestGlobalMemoryDumpCallback callback;
};

CoordinatorImpl();
~CoordinatorImpl() override;

// mojom::Coordinator
void RegisterProcessLocalDumpManager(
mojom::ProcessLocalDumpManagerPtr process_manager) override;

// Broadcasts a dump request to all the process-local managers registered and
// notifies when all of them have completed, or the global dump attempt
// failed. This is in the mojom::Coordinator interface.
void RequestGlobalMemoryDump(
const base::trace_event::MemoryDumpRequestArgs& args,
const RequestGlobalMemoryDumpCallback& callback) override;

// Called when a process-local manager gets disconnected.
void UnregisterProcessLocalDumpManager(
mojom::ProcessLocalDumpManager* process_manager);

// Callback of RequestProcessMemoryDump.
void OnProcessMemoryDumpResponse(
mojom::ProcessLocalDumpManager* process_manager,
uint64_t dump_guid,
bool success);

void PerformNextQueuedGlobalMemoryDump();
void FinalizeGlobalMemoryDumpIfAllManagersReplied();

mojo::BindingSet<mojom::Coordinator> bindings_;

// Registered ProcessLocalDumpManagers.
std::unordered_map<mojom::ProcessLocalDumpManager*,
mojom::ProcessLocalDumpManagerPtr> process_managers_;

// Pending process managers for RequestGlobalMemoryDump.
std::set<mojom::ProcessLocalDumpManager*> pending_process_managers_;
int failed_memory_dump_count_;
std::list<QueuedMemoryDumpRequest> queued_memory_dump_requests_;

base::ThreadChecker thread_checker_;

DISALLOW_COPY_AND_ASSIGN(CoordinatorImpl);
};

} // namespace memory_instrumentation
#endif // SERVICES_MEMORY_INFSTRUMENTATION_COORDINATOR_IMPL_H_
Loading

0 comments on commit 1ce377e

Please sign in to comment.