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/resource_coordinator/tracing/coordinator_impl.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/bind_helpers.h" |
| 9 #include "base/callback_forward.h" |
| 10 #include "base/memory/ptr_util.h" |
| 11 #include "base/task_scheduler/post_task.h" |
| 12 #include "base/task_scheduler/task_traits.h" |
| 13 #include "base/threading/thread_task_runner_handle.h" |
| 14 #include "base/trace_event/trace_config.h" |
| 15 #include "mojo/common/data_pipe_utils.h" |
| 16 #include "services/resource_coordinator/public/cpp/tracing/chrome_agent.h" |
| 17 #include "services/resource_coordinator/tracing/agent_set_impl.h" |
| 18 #include "services/resource_coordinator/tracing/recorder_impl.h" |
| 19 #include "services/service_manager/public/cpp/bind_source_info.h" |
| 20 |
| 21 namespace { |
| 22 |
| 23 resource_coordinator::tracing::CoordinatorImpl* g_coordinator_impl; |
| 24 |
| 25 } // namespace |
| 26 |
| 27 namespace resource_coordinator { |
| 28 namespace tracing { |
| 29 |
| 30 const char CoordinatorImpl::kRequestBufferUsageType[] = "RequestBufferUsage"; |
| 31 |
| 32 // static |
| 33 CoordinatorImpl* CoordinatorImpl::GetInstance() { |
| 34 return g_coordinator_impl; |
| 35 } |
| 36 |
| 37 CoordinatorImpl::CoordinatorImpl() |
| 38 : binding_(this), task_runner_(base::ThreadTaskRunnerHandle::Get()) { |
| 39 DCHECK(!g_coordinator_impl); |
| 40 g_coordinator_impl = this; |
| 41 background_task_runner_ = base::CreateSequencedTaskRunnerWithTraits( |
| 42 base::TaskTraits().MayBlock().WithBaseSyncPrimitives().WithPriority( |
| 43 base::TaskPriority::BACKGROUND)); |
| 44 agent_set_ = base::MakeUnique<AgentSetImpl>(); |
| 45 mojom::AgentSetPtr agent_set_interface_ptr; |
| 46 agent_set_->BindAgentSetRequest(service_manager::BindSourceInfo(), |
| 47 mojo::MakeRequest(&agent_set_interface_ptr)); |
| 48 resource_coordinator::tracing::ChromeAgent::InitializeIfNeeded( |
| 49 std::move(agent_set_interface_ptr)); |
| 50 } |
| 51 |
| 52 CoordinatorImpl::~CoordinatorImpl() { |
| 53 g_coordinator_impl = nullptr; |
| 54 } |
| 55 |
| 56 void CoordinatorImpl::BindCoordinatorRequest( |
| 57 mojom::CoordinatorRequest request) { |
| 58 binding_.Bind(std::move(request)); |
| 59 } |
| 60 |
| 61 void CoordinatorImpl::StartTracing(mojo::ScopedDataPipeProducerHandle stream, |
| 62 const std::string& config) { |
| 63 DCHECK(!stream_.is_valid()); |
| 64 config_ = config; |
| 65 stream_ = std::move(stream); |
| 66 first_recorder_update_ = true; |
| 67 recording_label_.clear(); |
| 68 agent_set_->SetAgentProcessingCallback(base::Bind( |
| 69 &CoordinatorImpl::SendStartTracingToAgent, base::Unretained(this))); |
| 70 } |
| 71 |
| 72 void CoordinatorImpl::StopAndFlush() { |
| 73 DCHECK(IsTracing()); |
| 74 agent_set_->ResetAgentProcessingCallback(); |
| 75 agent_set_->ForAllAgents( |
| 76 [](AgentSetImpl::Entry* entry) { entry->agent()->StopAndFlush(); }); |
| 77 if (recorders_.size() == 0) |
| 78 OnFlushDone(); |
| 79 } |
| 80 |
| 81 void CoordinatorImpl::IsTracing(const IsTracingCallback& callback) { |
| 82 callback.Run(IsTracing()); |
| 83 } |
| 84 |
| 85 bool CoordinatorImpl::IsTracing() { |
| 86 return stream_.is_valid() || !get_categories_callback_.is_null(); |
| 87 } |
| 88 |
| 89 void CoordinatorImpl::RequestBufferUsage( |
| 90 const RequestBufferUsageCallback& callback) { |
| 91 if (!request_buffer_usage_callback_.is_null()) { |
| 92 callback.Run(false, 0, 0); |
| 93 return; |
| 94 } |
| 95 |
| 96 maximum_trace_buffer_usage_ = 0; |
| 97 approximate_event_count_ = 0; |
| 98 request_buffer_usage_callback_ = callback; |
| 99 pending_request_buffer_status_count_ = 0; |
| 100 agent_set_->ForAllAgents([this](AgentSetImpl::Entry* entry) { |
| 101 entry->AddDisconnectClosure( |
| 102 kRequestBufferUsageType, |
| 103 base::BindOnce(&CoordinatorImpl::OnRequestBufferStatusResponse, |
| 104 base::Unretained(this), entry, 0, 0)); |
| 105 entry->agent()->RequestBufferStatus( |
| 106 base::BindRepeating(&CoordinatorImpl::OnRequestBufferStatusResponse, |
| 107 base::Unretained(this), entry)); |
| 108 pending_request_buffer_status_count_++; |
| 109 }); |
| 110 } |
| 111 |
| 112 void CoordinatorImpl::OnRequestBufferStatusResponse(AgentSetImpl::Entry* entry, |
| 113 uint32_t capacity, |
| 114 uint32_t count) { |
| 115 if (!entry->RemoveDisconnectClosure(kRequestBufferUsageType)) |
| 116 return; |
| 117 |
| 118 DCHECK(pending_request_buffer_status_count_); |
| 119 if (capacity > 0) { |
| 120 float percent_full = |
| 121 static_cast<float>(static_cast<double>(count) / capacity); |
| 122 maximum_trace_buffer_usage_ = |
| 123 std::max(maximum_trace_buffer_usage_, percent_full); |
| 124 approximate_event_count_ += count; |
| 125 } |
| 126 |
| 127 if (--pending_request_buffer_status_count_ == 0) { |
| 128 request_buffer_usage_callback_.Run(true, maximum_trace_buffer_usage_, |
| 129 approximate_event_count_); |
| 130 request_buffer_usage_callback_.Reset(); |
| 131 } |
| 132 } |
| 133 |
| 134 void CoordinatorImpl::SendStartTracingToAgent(AgentSetImpl::Entry* entry) { |
| 135 bool is_array = entry->type() == mojom::TraceDataType::ARRAY; |
| 136 mojom::RecorderPtr ptr; |
| 137 recorders_[entry->label()].insert(base::MakeUnique<RecorderImpl>( |
| 138 MakeRequest(&ptr), is_array, |
| 139 base::BindRepeating(&CoordinatorImpl::OnRecorderUpdated, |
| 140 base::Unretained(this), entry->label()), |
| 141 background_task_runner_)); |
| 142 DCHECK(is_array || recorders_[entry->label()].size() == 1); |
| 143 entry->agent()->StartTracing(config_, std::move(ptr), |
| 144 !get_categories_callback_.is_null(), |
| 145 base::Closure()); |
| 146 } |
| 147 |
| 148 void CoordinatorImpl::OnFlushDone() { |
| 149 if (!get_categories_callback_.is_null()) { |
| 150 std::set<std::string> category_set; |
| 151 for (const auto& key_value : recorders_) { |
| 152 for (const auto& recorder : key_value.second) { |
| 153 const auto& recorder_categories = recorder->category_set(); |
| 154 category_set.insert(recorder_categories.begin(), |
| 155 recorder_categories.end()); |
| 156 } |
| 157 } |
| 158 |
| 159 std::string categories_str; |
| 160 bool first = true; |
| 161 for (const auto& category : category_set) { |
| 162 if (!first) |
| 163 categories_str += ","; |
| 164 first = false; |
| 165 categories_str += category; |
| 166 } |
| 167 get_categories_callback_.Run(categories_str); |
| 168 get_categories_callback_.Reset(); |
| 169 } |
| 170 |
| 171 recorders_.clear(); |
| 172 stream_.reset(); |
| 173 } |
| 174 |
| 175 void CoordinatorImpl::OnRecorderUpdated(const std::string& label) { |
| 176 DCHECK(background_task_runner_->RunsTasksOnCurrentThread()); |
| 177 if (!recording_label_.empty() && recording_label_ != label) |
| 178 return; |
| 179 while (true) { |
| 180 if (!recording_label_.empty() && ReadFromRecorders()) |
| 181 return; |
| 182 recording_label_.clear(); |
| 183 bool all_finished = true; |
| 184 for (const auto& key_value : recorders_) { |
| 185 for (const auto& recorder : key_value.second) { |
| 186 all_finished &= !recorder->is_recording(); |
| 187 if (recorder->data().size() > 0) { |
| 188 recording_label_ = key_value.first; |
| 189 write_label_ = true; |
| 190 break; |
| 191 } |
| 192 } |
| 193 if (recording_label_.size() != 0) |
| 194 break; |
| 195 } |
| 196 if (recording_label_.size() == 0) { |
| 197 // No recorder has any data for us, right now. |
| 198 if (all_finished) { |
| 199 if (!first_recorder_update_) |
| 200 mojo::common::BlockingCopyFromString("}", stream_); |
| 201 // Recorder connections should be closed on their binding thread. |
| 202 task_runner_->PostTask( |
| 203 FROM_HERE, |
| 204 base::Bind(&CoordinatorImpl::OnFlushDone, base::Unretained(this))); |
| 205 } |
| 206 return; |
| 207 } |
| 208 } |
| 209 } |
| 210 |
| 211 bool CoordinatorImpl::ReadFromRecorders() { |
| 212 DCHECK(background_task_runner_->RunsTasksOnCurrentThread()); |
| 213 bool all_finished = true; |
| 214 bool is_array = (*recorders_[recording_label_].begin())->is_array(); |
| 215 for (const auto& recorder : recorders_[recording_label_]) { |
| 216 all_finished &= !recorder->is_recording(); |
| 217 if (recorder->data().size() == 0) |
| 218 continue; |
| 219 DCHECK(stream_.is_valid()); |
| 220 if (write_label_) { |
| 221 std::string prefix = first_recorder_update_ ? "{\"" : "\""; |
| 222 prefix += recording_label_ + "\":" + (is_array ? "[" : "\""); |
| 223 mojo::common::BlockingCopyFromString(prefix, stream_); |
| 224 write_label_ = false; |
| 225 first_recorder_update_ = false; |
| 226 } else { |
| 227 if (recorder->is_array()) |
| 228 mojo::common::BlockingCopyFromString(",", stream_); |
| 229 } |
| 230 mojo::common::BlockingCopyFromString(recorder->data(), stream_); |
| 231 recorder->clear_data(); |
| 232 } |
| 233 if (all_finished) { |
| 234 if (!write_label_) |
| 235 mojo::common::BlockingCopyFromString(is_array ? "]" : "\"", stream_); |
| 236 return false; |
| 237 } |
| 238 return true; |
| 239 } |
| 240 |
| 241 void CoordinatorImpl::GetCategories(const GetCategoriesCallback& callback) { |
| 242 get_categories_callback_ = callback; |
| 243 StartTracing(mojo::ScopedDataPipeProducerHandle(), |
| 244 base::trace_event::TraceConfig("*", "").ToString()); |
| 245 StopAndFlush(); |
| 246 } |
| 247 |
| 248 } // namespace tracing |
| 249 } // namespace resource_coordinator |
OLD | NEW |