1// Copyright 2013 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/feedback/tracing_manager.h"
6
7#include "base/bind.h"
8#include "base/prefs/pref_service.h"
9#include "chrome/browser/browser_process.h"
10#include "chrome/browser/feedback/feedback_util.h"
11#include "chrome/common/pref_names.h"
12#include "content/public/browser/trace_controller.h"
13
14namespace {
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}
20
21TracingManager::TracingManager()
22    : current_trace_id_(0),
23      weak_ptr_factory_(this) {
24  DCHECK(!g_tracing_manager);
25  g_tracing_manager = this;
26  StartTracing();
27}
28
29TracingManager::~TracingManager() {
30  DCHECK(g_tracing_manager == this);
31  g_tracing_manager = NULL;
32}
33
34int TracingManager::RequestTrace() {
35  // Return the current trace if one is being collected.
36  if (current_trace_id_)
37    return current_trace_id_;
38
39  current_trace_id_ = g_next_trace_id;
40  ++g_next_trace_id;
41  content::TraceController::GetInstance()->EndTracingAsync(this);
42  return current_trace_id_;
43}
44
45bool TracingManager::GetTraceData(int id, const TraceDataCallback& callback) {
46  // If a trace is being collected currently, send it via callback when
47  // complete.
48  if (current_trace_id_) {
49    // Only allow one trace data request at a time.
50    if (trace_callback_.is_null()) {
51      trace_callback_ = callback;
52      return true;
53    } else {
54      return false;
55    }
56  } else {
57    std::map<int, scoped_refptr<base::RefCountedString> >::iterator data =
58        trace_data_.find(id);
59    if (data == trace_data_.end())
60      return false;
61
62    // Always return the data asychronously, so the behavior is consistant.
63    base::MessageLoopProxy::current()->PostTask(
64        FROM_HERE,
65        base::Bind(callback, data->second));
66    return true;
67  }
68}
69
70void TracingManager::DiscardTraceData(int id) {
71  trace_data_.erase(id);
72
73  // If the trace is discarded before it is complete, clean up the accumulators.
74  if (id == current_trace_id_) {
75    current_trace_id_ = 0;
76    data_ = "";
77
78    // If the trace has already been requested, provide an empty string.
79    if (!trace_callback_.is_null()) {
80      trace_callback_.Run(scoped_refptr<base::RefCountedString>());
81      trace_callback_.Reset();
82    }
83  }
84}
85
86void TracingManager::StartTracing() {
87  content::TraceController::GetInstance()->BeginTracing(
88      this, "-test_*",
89      base::debug::TraceLog::RECORD_CONTINUOUSLY);
90}
91
92void TracingManager::OnEndTracingComplete() {
93  if (!current_trace_id_)
94    return;
95
96  std::string output_val;
97  FeedbackUtil::ZipString(data_, &output_val);
98
99  scoped_refptr<base::RefCountedString> output(
100      base::RefCountedString::TakeString(&output_val));
101
102  trace_data_[current_trace_id_] = output;
103
104  if (!trace_callback_.is_null()) {
105    trace_callback_.Run(output);
106    trace_callback_.Reset();
107  }
108
109  current_trace_id_ = 0;
110  data_ = "";
111
112  // Tracing has to be restarted asynchronous, so the TracingController can
113  // clean up.
114  base::MessageLoopProxy::current()->PostTask(
115      FROM_HERE,
116      base::Bind(&TracingManager::StartTracing,
117                 weak_ptr_factory_.GetWeakPtr()));
118}
119
120void TracingManager::OnTraceDataCollected(
121    const scoped_refptr<base::RefCountedString>& trace_fragment) {
122  if (current_trace_id_)
123    data_ += trace_fragment->data();
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