| OLD | NEW |
| (Empty) |
| 1 // Copyright 2017 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 "services/memory_instrumentation/coordinator_impl.h" | |
| 6 | |
| 7 #include "base/bind_helpers.h" | |
| 8 #include "base/lazy_instance.h" | |
| 9 #include "base/location.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/memory/ref_counted.h" | |
| 12 #include "base/threading/thread_task_runner_handle.h" | |
| 13 #include "base/trace_event/memory_dump_manager.h" | |
| 14 #include "base/trace_event/memory_dump_request_args.h" | |
| 15 #include "services/memory_instrumentation/public/interfaces/memory_instrumentati
on.mojom.h" | |
| 16 | |
| 17 namespace memory_instrumentation { | |
| 18 | |
| 19 namespace { | |
| 20 | |
| 21 base::LazyInstance<CoordinatorImpl>::Leaky g_coordinator = | |
| 22 LAZY_INSTANCE_INITIALIZER; | |
| 23 | |
| 24 } // namespace | |
| 25 | |
| 26 // static | |
| 27 CoordinatorImpl* CoordinatorImpl::GetInstance() { | |
| 28 return g_coordinator.Pointer(); | |
| 29 } | |
| 30 | |
| 31 // TODO(chiniforooshan): Initialize the global MemoryDumpManager instance here. | |
| 32 // This is how the global MemoryDumpManager gets a reference to the delegate on | |
| 33 // the service (read the browser) process for service process memory dumps. This | |
| 34 // can be done when the delegate implementation is landed. | |
| 35 CoordinatorImpl::CoordinatorImpl() | |
| 36 : failed_memory_dump_count_(0) {} | |
| 37 | |
| 38 CoordinatorImpl::~CoordinatorImpl() {} | |
| 39 | |
| 40 void CoordinatorImpl::BindCoordinatorRequest( | |
| 41 mojom::CoordinatorRequest request) { | |
| 42 bindings_.AddBinding(this, std::move(request)); | |
| 43 } | |
| 44 | |
| 45 CoordinatorImpl::QueuedMemoryDumpRequest::QueuedMemoryDumpRequest( | |
| 46 const base::trace_event::MemoryDumpRequestArgs args, | |
| 47 const RequestGlobalMemoryDumpCallback callback) | |
| 48 : args(args), callback(callback) {} | |
| 49 | |
| 50 CoordinatorImpl::QueuedMemoryDumpRequest::~QueuedMemoryDumpRequest() {} | |
| 51 | |
| 52 void CoordinatorImpl::RequestGlobalMemoryDump( | |
| 53 const base::trace_event::MemoryDumpRequestArgs& args, | |
| 54 const RequestGlobalMemoryDumpCallback& callback) { | |
| 55 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 56 | |
| 57 bool another_dump_already_in_progress = !queued_memory_dump_requests_.empty(); | |
| 58 | |
| 59 // If this is a periodic memory dump request and there already is another | |
| 60 // request in the queue with the same level of detail, there's no point in | |
| 61 // enqueuing this request. | |
| 62 if (another_dump_already_in_progress && | |
| 63 args.dump_type == base::trace_event::MemoryDumpType::PERIODIC_INTERVAL) { | |
| 64 for (const auto& request : queued_memory_dump_requests_) { | |
| 65 if (request.args.level_of_detail == args.level_of_detail) { | |
| 66 VLOG(1) << base::trace_event::MemoryDumpManager::kLogPrefix << " (" | |
| 67 << base::trace_event::MemoryDumpTypeToString(args.dump_type) | |
| 68 << ") skipped because another dump request with the same " | |
| 69 "level of detail (" | |
| 70 << base::trace_event::MemoryDumpLevelOfDetailToString( | |
| 71 args.level_of_detail) | |
| 72 << ") is already in the queue"; | |
| 73 callback.Run(args.dump_guid, false /* success */); | |
| 74 return; | |
| 75 } | |
| 76 } | |
| 77 } | |
| 78 | |
| 79 queued_memory_dump_requests_.emplace_back(args, callback); | |
| 80 | |
| 81 // If another dump is already in progress, this dump will automatically be | |
| 82 // scheduled when the other dump finishes. | |
| 83 if (another_dump_already_in_progress) | |
| 84 return; | |
| 85 | |
| 86 PerformNextQueuedGlobalMemoryDump(); | |
| 87 } | |
| 88 | |
| 89 void CoordinatorImpl::RegisterProcessLocalDumpManager( | |
| 90 mojom::ProcessLocalDumpManagerPtr process_manager) { | |
| 91 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 92 | |
| 93 process_manager.set_connection_error_handler( | |
| 94 base::Bind(&CoordinatorImpl::UnregisterProcessLocalDumpManager, | |
| 95 base::Unretained(this), | |
| 96 process_manager.get())); | |
| 97 auto result = process_managers_.insert( | |
| 98 std::make_pair<mojom::ProcessLocalDumpManager*, | |
| 99 mojom::ProcessLocalDumpManagerPtr>( | |
| 100 process_manager.get(), std::move(process_manager))); | |
| 101 DCHECK(result.second); | |
| 102 } | |
| 103 | |
| 104 void CoordinatorImpl::UnregisterProcessLocalDumpManager( | |
| 105 mojom::ProcessLocalDumpManager* process_manager) { | |
| 106 DCHECK(process_managers_.erase(process_manager) == 1); | |
| 107 | |
| 108 // Check if we are waiting for an ack from this process-local manager. | |
| 109 if (pending_process_managers_.find(process_manager) != | |
| 110 pending_process_managers_.end()) { | |
| 111 DCHECK(!queued_memory_dump_requests_.empty()); | |
| 112 OnProcessMemoryDumpResponse( | |
| 113 process_manager, | |
| 114 queued_memory_dump_requests_.front().args.dump_guid, | |
| 115 false /* success */); | |
| 116 } | |
| 117 } | |
| 118 | |
| 119 void CoordinatorImpl::PerformNextQueuedGlobalMemoryDump() { | |
| 120 DCHECK(!queued_memory_dump_requests_.empty()); | |
| 121 const base::trace_event::MemoryDumpRequestArgs& args = | |
| 122 queued_memory_dump_requests_.front().args; | |
| 123 | |
| 124 // No need to treat the service process different than other processes. The | |
| 125 // service process will register itself as a ProcessLocalDumpManager and will | |
| 126 // be treated like other process-local managers. | |
| 127 pending_process_managers_.clear(); | |
| 128 failed_memory_dump_count_ = 0; | |
| 129 for (const auto& key_value : process_managers_) { | |
| 130 pending_process_managers_.insert(key_value.first); | |
| 131 key_value.second->RequestProcessMemoryDump( | |
| 132 args, | |
| 133 base::Bind(&CoordinatorImpl::OnProcessMemoryDumpResponse, | |
| 134 base::Unretained(this), | |
| 135 key_value.first)); | |
| 136 } | |
| 137 // Run the callback in case there are no process-local managers. | |
| 138 FinalizeGlobalMemoryDumpIfAllManagersReplied(); | |
| 139 } | |
| 140 | |
| 141 void CoordinatorImpl::OnProcessMemoryDumpResponse( | |
| 142 mojom::ProcessLocalDumpManager* process_manager, | |
| 143 uint64_t dump_guid, | |
| 144 bool success) { | |
| 145 auto it = pending_process_managers_.find(process_manager); | |
| 146 | |
| 147 DCHECK(!queued_memory_dump_requests_.empty()); | |
| 148 if (queued_memory_dump_requests_.front().args.dump_guid != dump_guid || | |
| 149 it == pending_process_managers_.end()) { | |
| 150 VLOG(1) << "Received unexpected memory dump response: " << dump_guid; | |
| 151 return; | |
| 152 } | |
| 153 pending_process_managers_.erase(it); | |
| 154 | |
| 155 if (!success) { | |
| 156 ++failed_memory_dump_count_; | |
| 157 VLOG(1) << base::trace_event::MemoryDumpManager::kLogPrefix | |
| 158 << " failed because of NACK from provider"; | |
| 159 } | |
| 160 FinalizeGlobalMemoryDumpIfAllManagersReplied(); | |
| 161 } | |
| 162 | |
| 163 void CoordinatorImpl::FinalizeGlobalMemoryDumpIfAllManagersReplied() { | |
| 164 if (pending_process_managers_.size() > 0) | |
| 165 return; | |
| 166 | |
| 167 DCHECK(!queued_memory_dump_requests_.empty()); | |
| 168 { | |
| 169 const auto& callback = queued_memory_dump_requests_.front().callback; | |
| 170 const bool global_success = failed_memory_dump_count_ == 0; | |
| 171 callback.Run(queued_memory_dump_requests_.front().args.dump_guid, | |
| 172 global_success); | |
| 173 } | |
| 174 queued_memory_dump_requests_.pop_front(); | |
| 175 | |
| 176 // Schedule the next queued dump (if applicable). | |
| 177 if (!queued_memory_dump_requests_.empty()) { | |
| 178 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 179 FROM_HERE, | |
| 180 base::Bind( | |
| 181 &CoordinatorImpl::PerformNextQueuedGlobalMemoryDump, | |
| 182 base::Unretained(this))); | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 } // namespace memory_instrumentation | |
| OLD | NEW |