forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
memory-infra: Service implementation
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
1 parent
d49206b
commit 1ce377e
Showing
8 changed files
with
488 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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", | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_ |
Oops, something went wrong.