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 "chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/files/file.h"
12 #include "base/logging.h"
13 #include "base/memory/singleton.h"
14 #include "base/no_destructor.h"
15 #include "base/posix/unix_domain_socket.h"
16 #include "base/threading/thread_task_runner_handle.h"
17 #include "base/time/time.h"
18 #include "base/trace_event/trace_config.h"
19 #include "base/trace_event/trace_event.h"
20 #include "components/arc/arc_browser_context_keyed_service_factory_base.h"
21 #include "components/arc/arc_service_manager.h"
22 #include "components/arc/mojom/tracing.mojom.h"
23 #include "components/arc/session/arc_bridge_service.h"
24 #include "content/public/browser/browser_task_traits.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "mojo/public/cpp/system/platform_handle.h"
27 #include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
28 #include "services/tracing/public/cpp/perfetto/system_trace_writer.h"
29 #include "services/tracing/public/mojom/constants.mojom.h"
30 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
31 
32 namespace arc {
33 
34 namespace {
35 
36 // The maximum size used to store one trace event. The ad hoc trace format for
37 // atrace is 1024 bytes. Here we add additional size as we're using JSON and
38 // have additional data fields.
39 constexpr size_t kArcTraceMessageLength = 1024 + 512;
40 
41 // The prefix of the categories to be shown on the trace selection UI.
42 // The space at the end of the string is intentional as the separator between
43 // the prefix and the real categories.
44 constexpr char kCategoryPrefix[] = TRACE_DISABLED_BY_DEFAULT("android ");
45 
46 // Singleton factory for ArcTracingBridge.
47 class ArcTracingBridgeFactory
48     : public internal::ArcBrowserContextKeyedServiceFactoryBase<
49           ArcTracingBridge,
50           ArcTracingBridgeFactory> {
51  public:
52   // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
53   static constexpr const char* kName = "ArcTracingBridgeFactory";
54 
GetInstance()55   static ArcTracingBridgeFactory* GetInstance() {
56     return base::Singleton<ArcTracingBridgeFactory>::get();
57   }
58 
59  private:
60   friend base::DefaultSingletonTraits<ArcTracingBridgeFactory>;
61   ArcTracingBridgeFactory() = default;
62   ~ArcTracingBridgeFactory() override = default;
63 };
64 
65 // Perfetto data source which coordinates ARC tracing sessions with perfetto's
66 // PerfettoProducer when perfetto is used as the tracing backend.
67 class ArcTracingDataSource
68     : public tracing::PerfettoTracedProcess::DataSourceBase {
69  public:
GetInstance()70   static ArcTracingDataSource* GetInstance() {
71     static base::NoDestructor<ArcTracingDataSource> instance;
72     return instance.get();
73   }
74 
75   // Called after constructing |bridge|.
RegisterBridgeOnUI(ArcTracingBridge * bridge)76   void RegisterBridgeOnUI(ArcTracingBridge* bridge) {
77     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
78     DCHECK_EQ(ArcTracingBridge::State::kDisabled, bridge->state());
79     bool success = bridges_.insert(bridge).second;
80     DCHECK(success);
81 
82     if (producer_ && !stop_complete_callback_) {
83       // We're currently tracing, so start the new bridge, too.
84       // |this| never gets destructed, so it's OK to bind an unretained pointer.
85       bridge->StartTracing(
86           data_source_config_.chrome_config().trace_config(),
87           base::BindOnce(&ArcTracingDataSource::OnTracingStartedOnUI,
88                          base::Unretained(this)));
89     }
90   }
91 
92   // Called when destructing |bridge|.
UnregisterBridgeOnUI(ArcTracingBridge * bridge)93   void UnregisterBridgeOnUI(ArcTracingBridge* bridge) {
94     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
95     const size_t erase_count = bridges_.erase(bridge);
96     DCHECK_EQ(1u, erase_count);
97 
98     // Make sure we don't continue to wait for any of the bridge's callbacks.
99     OnTracingStartedOnUI(false /*success*/);
100     OnTraceDataOnUI(std::string() /*data*/);
101   }
102 
103  private:
104   friend class base::NoDestructor<ArcTracingDataSource>;
105 
ArcTracingDataSource()106   ArcTracingDataSource()
107       : DataSourceBase(tracing::mojom::kArcTraceDataSourceName),
108         perfetto_task_runner_(tracing::PerfettoTracedProcess::Get()
109                                   ->GetTaskRunner()
110                                   ->GetOrCreateTaskRunner()) {
111     tracing::PerfettoTracedProcess::Get()->AddDataSource(this);
112   }
113 
114   // Note that ArcTracingDataSource is a singleton that's never destroyed.
115   ~ArcTracingDataSource() override = default;
116 
117   // tracing::PerfettoProducer::DataSourceBase.
StartTracing(tracing::PerfettoProducer * producer,const perfetto::DataSourceConfig & data_source_config)118   void StartTracing(
119       tracing::PerfettoProducer* producer,
120       const perfetto::DataSourceConfig& data_source_config) override {
121     // |this| never gets destructed, so it's OK to bind an unretained pointer.
122     // |producer| is a singleton that is never destroyed.
123     content::GetUIThreadTaskRunner({})->PostTask(
124         FROM_HERE,
125         base::BindOnce(&ArcTracingDataSource::StartTracingOnUI,
126                        base::Unretained(this), producer, data_source_config));
127   }
128 
StopTracing(base::OnceClosure stop_complete_callback)129   void StopTracing(base::OnceClosure stop_complete_callback) override {
130     // |this| never gets destructed, so it's OK to bind an unretained pointer.
131     content::GetUIThreadTaskRunner({})->PostTask(
132         FROM_HERE, base::BindOnce(&ArcTracingDataSource::StopTracingOnUI,
133                                   base::Unretained(this),
134                                   std::move(stop_complete_callback)));
135   }
136 
Flush(base::RepeatingClosure flush_complete_callback)137   void Flush(base::RepeatingClosure flush_complete_callback) override {
138     // ARC's tracing service doesn't currently support flushing while recording.
139     flush_complete_callback.Run();
140   }
141 
142   // Starts all registered bridges.
StartTracingOnUI(tracing::PerfettoProducer * producer,const perfetto::DataSourceConfig & data_source_config)143   void StartTracingOnUI(tracing::PerfettoProducer* producer,
144                         const perfetto::DataSourceConfig& data_source_config) {
145     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
146 
147     data_source_config_ = data_source_config;
148 
149     for (ArcTracingBridge* bridge : bridges_) {
150       // |this| never gets destructed, so it's OK to bind an unretained pointer.
151       bridge->StartTracing(
152           data_source_config_.chrome_config().trace_config(),
153           base::BindOnce(&ArcTracingDataSource::OnTracingStartedOnUI,
154                          base::Unretained(this)));
155     }
156   }
157 
158   // Stops all registered bridges. Calls |stop_complete_callback| when all
159   // bridges have stopped.
StopTracingOnUI(base::OnceClosure stop_complete_callback)160   void StopTracingOnUI(base::OnceClosure stop_complete_callback) {
161     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
162 
163     // We may receive a StopTracing without StartTracing.
164     if (!producer_)
165       return;
166 
167     // We may still be in startup. In this case, store a callback to rerun
168     // StopTracingOnUI() once startup is complete.
169     if (IsAnyBridgeStarting()) {
170       DCHECK(!pending_stop_tracing_);
171       pending_stop_tracing_ = base::BindOnce(
172           &ArcTracingDataSource::StopTracingOnUI, base::Unretained(this),
173           std::move(stop_complete_callback));
174       return;
175     }
176 
177     stop_complete_callback_ = std::move(stop_complete_callback);
178 
179     for (ArcTracingBridge* bridge : bridges_) {
180       // StopTracingOnUI should only be called once all bridges have completed
181       // or abandoned startup.
182       DCHECK_NE(ArcTracingBridge::State::kStarting, bridge->state());
183       if (bridge->state() != ArcTracingBridge::State::kEnabled)
184         continue;
185       // |this| never gets destructed, so it's OK to bind an unretained pointer.
186       bridge->StopAndFlush(base::BindOnce(
187           &ArcTracingDataSource::OnTraceDataOnUI, base::Unretained(this)));
188     }
189 
190     // There may not have been any bridges left in State::kEnabled. This will
191     // call the callback if all bridges are already stopped.
192     OnTraceDataOnUI(std::string());
193   }
194 
195   // Called by each bridge when it has started tracing. Also called when a
196   // bridge is unregisted.
OnTracingStartedOnUI(bool success)197   void OnTracingStartedOnUI(bool success) {
198     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
199     if (!IsAnyBridgeStarting() && pending_stop_tracing_)
200       std::move(pending_stop_tracing_).Run();
201   }
202 
203   // Called by each bridge when it has stopped tracing. Also called when a
204   // bridge is unregisted. Records the supplied |data| into the
205   // |producer_|'s buffer.
OnTraceDataOnUI(const std::string & data)206   void OnTraceDataOnUI(const std::string& data) {
207     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
208 
209     // When a bridge unregisters, we may not actually be stopping.
210     if (!stop_complete_callback_)
211       return;
212 
213     DCHECK(producer_);
214 
215     if (!data.empty()) {
216       if (!trace_writer_) {
217         trace_writer_ =
218             std::make_unique<tracing::SystemTraceWriter<std::string>>(
219                 producer_, data_source_config_.target_buffer(),
220                 tracing::SystemTraceWriter<std::string>::TraceType::kJson);
221       } else {
222         trace_writer_->WriteData(",");
223       }
224       trace_writer_->WriteData(data);
225     }
226 
227     if (AreAllBridgesStopped()) {
228       if (!trace_writer_) {
229         OnTraceDataCommittedOnUI();
230         return;
231       }
232       trace_writer_->Flush(
233           base::BindOnce(&ArcTracingDataSource::OnTraceDataCommittedOnUI,
234                          base::Unretained(this)));
235     }
236   }
237 
OnTraceDataCommittedOnUI()238   void OnTraceDataCommittedOnUI() {
239     producer_ = nullptr;
240     perfetto_task_runner_->PostTask(FROM_HERE,
241                                     std::move(stop_complete_callback_));
242   }
243 
IsAnyBridgeStarting() const244   bool IsAnyBridgeStarting() const {
245     for (ArcTracingBridge* bridge : bridges_) {
246       if (bridge->state() == ArcTracingBridge::State::kStarting)
247         return true;
248     }
249     return false;
250   }
251 
AreAllBridgesStopped() const252   bool AreAllBridgesStopped() const {
253     for (ArcTracingBridge* bridge : bridges_) {
254       if (bridge->state() != ArcTracingBridge::State::kDisabled)
255         return false;
256     }
257     return true;
258   }
259 
260   scoped_refptr<base::SequencedTaskRunner> perfetto_task_runner_;
261   std::set<ArcTracingBridge*> bridges_;
262   // In case StopTracing() is called before tracing was started for all bridges,
263   // this stores a callback to StopTracing() that's executed when all bridges
264   // have started.
265   base::OnceClosure pending_stop_tracing_;
266   // Called when all bridges have completed stopping, notifying
267   // PerfettoProducer.
268   base::OnceClosure stop_complete_callback_;
269   perfetto::DataSourceConfig data_source_config_;
270   std::unique_ptr<tracing::SystemTraceWriter<std::string>> trace_writer_;
271 
272   DISALLOW_COPY_AND_ASSIGN(ArcTracingDataSource);
273 };
274 
275 }  // namespace
276 
277 struct ArcTracingBridge::Category {
278   // The name used by Android to trigger tracing.
279   std::string name;
280   // The full name shown in the tracing UI in chrome://tracing.
281   std::string full_name;
282 };
283 
284 // static
GetForBrowserContext(content::BrowserContext * context)285 ArcTracingBridge* ArcTracingBridge::GetForBrowserContext(
286     content::BrowserContext* context) {
287   return ArcTracingBridgeFactory::GetForBrowserContext(context);
288 }
289 
ArcTracingBridge(content::BrowserContext * context,ArcBridgeService * bridge_service)290 ArcTracingBridge::ArcTracingBridge(content::BrowserContext* context,
291                                    ArcBridgeService* bridge_service)
292     : arc_bridge_service_(bridge_service),
293       agent_(this),
294       reader_(std::make_unique<ArcTracingReader>()) {
295   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
296   arc_bridge_service_->tracing()->AddObserver(this);
297   ArcTracingDataSource::GetInstance()->RegisterBridgeOnUI(this);
298 }
299 
~ArcTracingBridge()300 ArcTracingBridge::~ArcTracingBridge() {
301   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
302   ArcTracingDataSource::GetInstance()->UnregisterBridgeOnUI(this);
303   arc_bridge_service_->tracing()->RemoveObserver(this);
304 
305   // Delete the reader on the IO thread.
306   content::GetIOThreadTaskRunner({})->DeleteSoon(FROM_HERE, reader_.release());
307 }
308 
GetCategories(std::set<std::string> * category_set)309 void ArcTracingBridge::GetCategories(std::set<std::string>* category_set) {
310   base::AutoLock lock(categories_lock_);
311   for (const auto& category : categories_) {
312     category_set->insert(category.full_name);
313   }
314 }
315 
OnConnectionReady()316 void ArcTracingBridge::OnConnectionReady() {
317   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
318   mojom::TracingInstance* tracing_instance = ARC_GET_INSTANCE_FOR_METHOD(
319       arc_bridge_service_->tracing(), QueryAvailableCategories);
320   if (!tracing_instance)
321     return;
322   tracing_instance->QueryAvailableCategories(base::BindOnce(
323       &ArcTracingBridge::OnCategoriesReady, weak_ptr_factory_.GetWeakPtr()));
324 }
325 
OnCategoriesReady(const std::vector<std::string> & categories)326 void ArcTracingBridge::OnCategoriesReady(
327     const std::vector<std::string>& categories) {
328   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
329 
330   base::AutoLock lock(categories_lock_);
331   // There is no API in TraceLog to remove a category from the UI. As an
332   // alternative, the old category that is no longer in |categories_| will be
333   // ignored when calling |StartTracing|.
334   categories_.clear();
335   for (const auto& category : categories)
336     categories_.emplace_back(Category{category, kCategoryPrefix + category});
337 }
338 
StartTracing(const std::string & config,SuccessCallback callback)339 void ArcTracingBridge::StartTracing(const std::string& config,
340                                     SuccessCallback callback) {
341   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
342 
343   if (state_ != State::kDisabled) {
344     DLOG(WARNING) << "Cannot start tracing, it is already enabled.";
345     std::move(callback).Run(false /*success*/);
346     return;
347   }
348   state_ = State::kStarting;
349 
350   base::trace_event::TraceConfig trace_config(config);
351 
352   base::ScopedFD write_fd, read_fd;
353   bool success =
354       trace_config.IsSystraceEnabled() && CreateSocketPair(&read_fd, &write_fd);
355 
356   if (!success) {
357     OnArcTracingStarted(std::move(callback), false /*success*/);
358     return;
359   }
360 
361   mojom::TracingInstance* tracing_instance =
362       ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->tracing(), StartTracing);
363   if (!tracing_instance) {
364     OnArcTracingStarted(std::move(callback), false /*success*/);
365     return;
366   }
367 
368   std::vector<std::string> selected_categories;
369   {
370     base::AutoLock lock(categories_lock_);
371     for (const auto& category : categories_) {
372       if (trace_config.IsCategoryGroupEnabled(category.full_name))
373         selected_categories.push_back(category.name);
374     }
375   }
376 
377   tracing_instance->StartTracing(
378       selected_categories, mojo::WrapPlatformFile(std::move(write_fd)),
379       base::BindOnce(&ArcTracingBridge::OnArcTracingStarted,
380                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
381 
382   // |reader_| will be destroyed after us on the IO thread, so it's OK to use an
383   // unretained pointer.
384   content::GetIOThreadTaskRunner({})->PostTask(
385       FROM_HERE,
386       base::BindOnce(&ArcTracingReader::StartTracing,
387                      base::Unretained(reader_.get()), std::move(read_fd)));
388 }
389 
OnArcTracingStarted(SuccessCallback callback,bool success)390 void ArcTracingBridge::OnArcTracingStarted(SuccessCallback callback,
391                                            bool success) {
392   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
393   DCHECK_EQ(State::kStarting, state_);
394   state_ = success ? State::kEnabled : State::kDisabled;
395   std::move(callback).Run(success);
396 }
397 
StopAndFlush(TraceDataCallback callback)398 void ArcTracingBridge::StopAndFlush(TraceDataCallback callback) {
399   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
400 
401   if (state_ != State::kEnabled) {
402     DLOG(WARNING) << "Cannot stop tracing, it is not enabled.";
403     std::move(callback).Run(std::string());
404     return;
405   }
406   state_ = State::kStopping;
407 
408   mojom::TracingInstance* tracing_instance =
409       ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->tracing(), StopTracing);
410   if (!tracing_instance) {
411     OnArcTracingStopped(std::move(callback), false);
412     return;
413   }
414   tracing_instance->StopTracing(
415       base::BindOnce(&ArcTracingBridge::OnArcTracingStopped,
416                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
417 }
418 
OnArcTracingStopped(TraceDataCallback tracing_stopped_callback,bool success)419 void ArcTracingBridge::OnArcTracingStopped(
420     TraceDataCallback tracing_stopped_callback,
421     bool success) {
422   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
423   DCHECK_EQ(State::kStopping, state_);
424   if (!success) {
425     DLOG(WARNING) << "Failed to stop ARC tracing, closing file anyway.";
426   }
427   // |reader_| will be destroyed after us on the IO thread, so it's OK to use an
428   // unretained pointer.
429   content::GetIOThreadTaskRunner({})->PostTaskAndReplyWithResult(
430       FROM_HERE,
431       base::BindOnce(&ArcTracingReader::StopTracing,
432                      base::Unretained(reader_.get())),
433       base::BindOnce(&ArcTracingBridge::OnTracingReaderStopped,
434                      weak_ptr_factory_.GetWeakPtr(),
435                      std::move(tracing_stopped_callback)));
436 }
437 
OnTracingReaderStopped(TraceDataCallback tracing_stopped_callback,const std::string & data)438 void ArcTracingBridge::OnTracingReaderStopped(
439     TraceDataCallback tracing_stopped_callback,
440     const std::string& data) {
441   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
442   DCHECK_EQ(State::kStopping, state_);
443   state_ = State::kDisabled;
444   std::move(tracing_stopped_callback).Run(data);
445 }
446 
ArcTracingAgent(ArcTracingBridge * bridge)447 ArcTracingBridge::ArcTracingAgent::ArcTracingAgent(ArcTracingBridge* bridge)
448     : bridge_(bridge) {}
449 
450 ArcTracingBridge::ArcTracingAgent::~ArcTracingAgent() = default;
451 
GetCategories(std::set<std::string> * category_set)452 void ArcTracingBridge::ArcTracingAgent::GetCategories(
453     std::set<std::string>* category_set) {
454   bridge_->GetCategories(category_set);
455 }
456 
457 ArcTracingBridge::ArcTracingReader::ArcTracingReader() = default;
458 
~ArcTracingReader()459 ArcTracingBridge::ArcTracingReader::~ArcTracingReader() {
460   DCHECK(!fd_watcher_);
461 }
462 
StartTracing(base::ScopedFD read_fd)463 void ArcTracingBridge::ArcTracingReader::StartTracing(base::ScopedFD read_fd) {
464   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
465   read_fd_ = std::move(read_fd);
466   // We own |fd_watcher_|, so it's OK to use an unretained pointer.
467   fd_watcher_ = base::FileDescriptorWatcher::WatchReadable(
468       read_fd_.get(),
469       base::BindRepeating(&ArcTracingReader::OnTraceDataAvailable,
470                           base::Unretained(this)));
471 }
472 
OnTraceDataAvailable()473 void ArcTracingBridge::ArcTracingReader::OnTraceDataAvailable() {
474   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
475 
476   char buf[kArcTraceMessageLength];
477   std::vector<base::ScopedFD> unused_fds;
478   ssize_t n = base::UnixDomainSocket::RecvMsg(
479       read_fd_.get(), buf, kArcTraceMessageLength, &unused_fds);
480   if (n < 0) {
481     DPLOG(WARNING) << "Unexpected error while reading trace from client.";
482     // Do nothing here as StopTracing will do the clean up and the existing
483     // trace logs will be returned.
484     return;
485   }
486 
487   if (n == 0) {
488     // When EOF is reached, stop listening for changes since there's never
489     // going to be any more data to be read. The rest of the cleanup will be
490     // done in StopTracing.
491     fd_watcher_.reset();
492     return;
493   }
494 
495   if (n > static_cast<ssize_t>(kArcTraceMessageLength)) {
496     DLOG(WARNING) << "Unexpected data size when reading trace from client.";
497     return;
498   }
499   ring_buffer_.SaveToBuffer(std::string(buf, n));
500 }
501 
StopTracing()502 std::string ArcTracingBridge::ArcTracingReader::StopTracing() {
503   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
504   fd_watcher_.reset();
505   read_fd_.reset();
506 
507   bool append_comma = false;
508   std::string data;
509   for (auto it = ring_buffer_.Begin(); it; ++it) {
510     if (append_comma)
511       data.append(",");
512     else
513       append_comma = true;
514     data.append(**it);
515   }
516   ring_buffer_.Clear();
517 
518   return data;
519 }
520 
521 }  // namespace arc
522