1// Copyright 2014 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 "components/feedback/tracing_manager.h"
6
7#include "base/bind.h"
8#include "base/memory/ref_counted_memory.h"
9#include "base/message_loop/message_loop_proxy.h"
10#include "components/feedback/feedback_util.h"
11#include "content/public/browser/tracing_controller.h"
12
13namespace {
14
15// Only once trace manager can exist at a time.
16TracingManager* g_tracing_manager = NULL;
17// Trace IDs start at 1 and increase.
18int g_next_trace_id = 1;
19// Name of the file to store the tracing data as.
20const base::FilePath::CharType kTracingFilename[] =
21    FILE_PATH_LITERAL("tracing.json");
22}
23
24TracingManager::TracingManager()
25    : current_trace_id_(0),
26      weak_ptr_factory_(this) {
27  DCHECK(!g_tracing_manager);
28  g_tracing_manager = this;
29  StartTracing();
30}
31
32TracingManager::~TracingManager() {
33  DCHECK(g_tracing_manager == this);
34  g_tracing_manager = NULL;
35}
36
37int TracingManager::RequestTrace() {
38  // Return the current trace if one is being collected.
39  if (current_trace_id_)
40    return current_trace_id_;
41
42  current_trace_id_ = g_next_trace_id;
43  ++g_next_trace_id;
44  content::TracingController::GetInstance()->DisableRecording(
45      content::TracingController::CreateStringSink(
46          base::Bind(&TracingManager::OnTraceDataCollected,
47                     weak_ptr_factory_.GetWeakPtr())));
48  return current_trace_id_;
49}
50
51bool TracingManager::GetTraceData(int id, const TraceDataCallback& callback) {
52  // If a trace is being collected currently, send it via callback when
53  // complete.
54  if (current_trace_id_) {
55    // Only allow one trace data request at a time.
56    if (trace_callback_.is_null()) {
57      trace_callback_ = callback;
58      return true;
59    } else {
60      return false;
61    }
62  } else {
63    std::map<int, scoped_refptr<base::RefCountedString> >::iterator data =
64        trace_data_.find(id);
65    if (data == trace_data_.end())
66      return false;
67
68    // Always return the data asychronously, so the behavior is consistant.
69    base::MessageLoopProxy::current()->PostTask(
70        FROM_HERE,
71        base::Bind(callback, data->second));
72    return true;
73  }
74}
75
76void TracingManager::DiscardTraceData(int id) {
77  trace_data_.erase(id);
78
79  // If the trace is discarded before it is complete, clean up the accumulators.
80  if (id == current_trace_id_) {
81    current_trace_id_ = 0;
82
83    // If the trace has already been requested, provide an empty string.
84    if (!trace_callback_.is_null()) {
85      trace_callback_.Run(scoped_refptr<base::RefCountedString>());
86      trace_callback_.Reset();
87    }
88  }
89}
90
91void TracingManager::StartTracing() {
92  content::TracingController::GetInstance()->EnableRecording(
93      base::debug::CategoryFilter(),
94      base::debug::TraceOptions(),
95      content::TracingController::EnableRecordingDoneCallback());
96}
97
98void TracingManager::OnTraceDataCollected(base::RefCountedString* trace_data) {
99  if (!current_trace_id_)
100    return;
101
102  std::string output_val;
103  feedback_util::ZipString(
104      base::FilePath(kTracingFilename), trace_data->data(), &output_val);
105
106  scoped_refptr<base::RefCountedString> output(
107      base::RefCountedString::TakeString(&output_val));
108
109  trace_data_[current_trace_id_] = output;
110
111  if (!trace_callback_.is_null()) {
112    trace_callback_.Run(output);
113    trace_callback_.Reset();
114  }
115
116  current_trace_id_ = 0;
117
118  // Tracing has to be restarted asynchronous, so the TracingController can
119  // clean up.
120  base::MessageLoopProxy::current()->PostTask(
121      FROM_HERE,
122      base::Bind(&TracingManager::StartTracing,
123                 weak_ptr_factory_.GetWeakPtr()));
124}
125
126// static
127scoped_ptr<TracingManager> TracingManager::Create() {
128  if (g_tracing_manager)
129    return scoped_ptr<TracingManager>();
130  return scoped_ptr<TracingManager>(new TracingManager());
131}
132
133TracingManager* TracingManager::Get() {
134  return g_tracing_manager;
135}
136