summaryrefslogtreecommitdiff
path: root/chromium/services/tracing/service.cc
blob: 6eaf742e678f49980913bfdb20099e0fc8fe42df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "services/tracing/service.h"

#include <stddef.h>
#include <stdint.h>

#include <utility>

#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "mojo/public/cpp/system/wait.h"
#include "services/service_manager/public/cpp/service_info.h"

namespace tracing {

Service::Service() : collector_binding_(this), tracing_active_(false) {
  registry_.AddInterface<mojom::Factory>(this);
  registry_.AddInterface<mojom::Collector>(this);
  registry_.AddInterface<mojom::StartupPerformanceDataCollector>(this);
}
Service::~Service() {}

void Service::OnBindInterface(const service_manager::ServiceInfo& source_info,
                              const std::string& interface_name,
                              mojo::ScopedMessagePipeHandle interface_pipe) {
  registry_.BindInterface(source_info.identity, interface_name,
                          std::move(interface_pipe));
}

bool Service::OnServiceManagerConnectionLost() {
  // TODO(beng): This is only required because Service isn't run by
  // ServiceRunner - instead it's launched automatically by the standalone
  // service manager. It shouldn't be.
  base::MessageLoop::current()->QuitWhenIdle();
  return false;
}

void Service::Create(const service_manager::Identity& remote_identity,
                     mojom::FactoryRequest request) {
  bindings_.AddBinding(this, std::move(request));
}

void Service::Create(const service_manager::Identity& remote_identity,
                     mojom::CollectorRequest request) {
  collector_binding_.Bind(std::move(request));
}

void Service::Create(const service_manager::Identity& remote_identity,
                     mojom::StartupPerformanceDataCollectorRequest request) {
  startup_performance_data_collector_bindings_.AddBinding(this,
                                                          std::move(request));
}

void Service::CreateRecorder(mojom::ProviderPtr provider) {
  if (tracing_active_) {
    mojom::RecorderPtr recorder_ptr;
    recorder_impls_.push_back(
        base::MakeUnique<Recorder>(MakeRequest(&recorder_ptr), sink_.get()));
    provider->StartTracing(tracing_categories_, std::move(recorder_ptr));
  }
  provider_ptrs_.AddPtr(std::move(provider));
}

void Service::Start(mojo::ScopedDataPipeProducerHandle stream,
                    const std::string& categories) {
  tracing_categories_ = categories;
  sink_.reset(new DataSink(std::move(stream)));
  provider_ptrs_.ForAllPtrs(
    [categories, this](mojom::Provider* controller) {
      mojom::RecorderPtr ptr;
      recorder_impls_.push_back(
          base::MakeUnique<Recorder>(MakeRequest(&ptr), sink_.get()));
      controller->StartTracing(categories, std::move(ptr));
    });
  tracing_active_ = true;
}

void Service::StopAndFlush() {
  // Remove any collectors that closed their message pipes before we called
  // StopTracing().
  for (int i = static_cast<int>(recorder_impls_.size()) - 1; i >= 0; --i) {
    if (!recorder_impls_[i]->RecorderHandle().is_valid()) {
      recorder_impls_.erase(recorder_impls_.begin() + i);
    }
  }

  tracing_active_ = false;
  provider_ptrs_.ForAllPtrs(
      [](mojom::Provider* controller) { controller->StopTracing(); });

  // Sending the StopTracing message to registered controllers will request that
  // they send trace data back via the collector interface and, when they are
  // done, close the collector pipe. We don't know how long they will take. We
  // want to read all data that any collector might send until all collectors or
  // closed or an (arbitrary) deadline has passed. Since the bindings don't
  // support this directly we do our own WaitMany over the handles and read
  // individual messages until all are closed or our absolute deadline has
  // elapsed.
  static const MojoDeadline kTimeToWaitMicros = 1000 * 1000;
  MojoTimeTicks end = MojoGetTimeTicksNow() + kTimeToWaitMicros;

  while (!recorder_impls_.empty()) {
    MojoTimeTicks now = MojoGetTimeTicksNow();
    if (now >= end)  // Timed out?
      break;

    std::vector<mojo::Handle> handles;
    std::vector<MojoHandleSignals> signals;
    for (const auto& it : recorder_impls_) {
      handles.push_back(it->RecorderHandle());
      signals.push_back(MOJO_HANDLE_SIGNAL_READABLE |
                        MOJO_HANDLE_SIGNAL_PEER_CLOSED);
    }
    std::vector<MojoHandleSignalsState> signals_states(signals.size());
    size_t result_index;

    // TODO(rockot): Use a timed wait here to avoid hanging forever in the case
    // of a misbehaving or unresponsive collector.
    MojoResult rv =
        mojo::WaitMany(handles.data(), signals.data(), handles.size(),
                       &result_index, signals_states.data());
    if (rv == MOJO_RESULT_OK || rv == MOJO_RESULT_FAILED_PRECONDITION) {
      // Iterate backwards so we can remove closed pipes from |recorder_impls_|
      // without invalidating subsequent offsets.
      for (size_t i = signals_states.size(); i != 0; --i) {
        size_t index = i - 1;
        MojoHandleSignals satisfied = signals_states[index].satisfied_signals;
        // To avoid dropping data, don't close unless there's no
        // readable signal.
        if (satisfied & MOJO_HANDLE_SIGNAL_READABLE)
          recorder_impls_[index]->TryRead();
        else if (satisfied & MOJO_HANDLE_SIGNAL_PEER_CLOSED)
          recorder_impls_.erase(recorder_impls_.begin() + index);
      }
    }
  }
  AllDataCollected();
}

void Service::SetServiceManagerProcessCreationTime(int64_t time) {
  if (startup_performance_times_.service_manager_process_creation_time == 0)
    startup_performance_times_.service_manager_process_creation_time = time;
}

void Service::SetServiceManagerMainEntryPointTime(int64_t time) {
  if (startup_performance_times_.service_manager_main_entry_point_time == 0)
    startup_performance_times_.service_manager_main_entry_point_time = time;
}

void Service::SetBrowserMessageLoopStartTicks(int64_t ticks) {
  if (startup_performance_times_.browser_message_loop_start_ticks == 0)
    startup_performance_times_.browser_message_loop_start_ticks = ticks;
}

void Service::SetBrowserWindowDisplayTicks(int64_t ticks) {
  if (startup_performance_times_.browser_window_display_ticks == 0)
    startup_performance_times_.browser_window_display_ticks = ticks;
}

void Service::SetBrowserOpenTabsTimeDelta(int64_t delta) {
  if (startup_performance_times_.browser_open_tabs_time_delta == 0)
    startup_performance_times_.browser_open_tabs_time_delta = delta;
}

void Service::SetFirstWebContentsMainFrameLoadTicks(int64_t ticks) {
  if (startup_performance_times_.first_web_contents_main_frame_load_ticks == 0)
    startup_performance_times_.first_web_contents_main_frame_load_ticks = ticks;
}

void Service::SetFirstVisuallyNonEmptyLayoutTicks(int64_t ticks) {
  if (startup_performance_times_.first_visually_non_empty_layout_ticks == 0)
    startup_performance_times_.first_visually_non_empty_layout_ticks = ticks;
}

void Service::GetStartupPerformanceTimes(
    const GetStartupPerformanceTimesCallback& callback) {
  callback.Run(startup_performance_times_.Clone());
}

void Service::AllDataCollected() {
  recorder_impls_.clear();
  sink_.reset();
}

}  // namespace tracing