aboutsummaryrefslogtreecommitdiff
path: root/include/perfetto/tracing/tracing.h
blob: 9aae9e8fe07b6055b3c52bf3ebba941e319e2c36 (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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef INCLUDE_PERFETTO_TRACING_TRACING_H_
#define INCLUDE_PERFETTO_TRACING_TRACING_H_

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

#include <functional>
#include <memory>
#include <string>
#include <vector>

#include "perfetto/base/compiler.h"
#include "perfetto/base/export.h"
#include "perfetto/base/logging.h"
#include "perfetto/tracing/backend_type.h"
#include "perfetto/tracing/core/forward_decls.h"
#include "perfetto/tracing/internal/in_process_tracing_backend.h"
#include "perfetto/tracing/internal/system_tracing_backend.h"
#include "perfetto/tracing/tracing_policy.h"

namespace perfetto {

namespace internal {
class TracingMuxerImpl;
}

class TracingBackend;
class Platform;
class TracingSession;  // Declared below.

struct TracingError {
  enum ErrorCode : uint32_t {
    // Peer disconnection.
    kDisconnected = 1,

    // The Start() method failed. This is typically because errors in the passed
    // TraceConfig. More details are available in |message|.
    kTracingFailed = 2,
  };

  ErrorCode code;
  std::string message;

  TracingError(ErrorCode cd, std::string msg)
      : code(cd), message(std::move(msg)) {
    PERFETTO_CHECK(!message.empty());
  }
};

using LogLev = ::perfetto::base::LogLev;
using LogMessageCallbackArgs = ::perfetto::base::LogMessageCallbackArgs;
using LogMessageCallback = ::perfetto::base::LogMessageCallback;

struct TracingInitArgs {
  uint32_t backends = 0;                     // One or more BackendTypes.
  TracingBackend* custom_backend = nullptr;  // [Optional].

  // [Optional] Platform implementation. It allows the embedder to take control
  // of platform-specific bits like thread creation and TLS slot handling. If
  // not set it will use Platform::GetDefaultPlatform().
  Platform* platform = nullptr;

  // [Optional] Tune the size of the shared memory buffer between the current
  // process and the service backend(s). This is a trade-off between memory
  // footprint and the ability to sustain bursts of trace writes (see comments
  // in shared_memory_abi.h).
  // If set, the value must be a multiple of 4KB. The value can be ignored if
  // larger than kMaxShmSize (32MB) or not a multiple of 4KB.
  uint32_t shmem_size_hint_kb = 0;

  // [Optional] Specifies the preferred size of each page in the shmem buffer.
  // This is a trade-off between IPC overhead and fragmentation/efficiency of
  // the shmem buffer in presence of multiple writer threads.
  // Must be one of [4, 8, 16, 32].
  uint32_t shmem_page_size_hint_kb = 0;

  // [Optional] The length of the period during which shared-memory-buffer
  // chunks that have been filled with data are accumulated (batched) on the
  // producer side, before the service is notified of them over an out-of-band
  // IPC call. If, while this period lasts, the shared memory buffer gets too
  // full, the IPC call will be sent immediately. The value of this parameter is
  // a trade-off between IPC traffic overhead and the ability to sustain bursts
  // of trace writes. The higher the value, the more chunks will be batched and
  // the less buffer space will be available to hide the latency of the service,
  // and vice versa. For more details, see the SetBatchCommitsDuration method in
  // shared_memory_arbiter.h.
  //
  // Note: With the default value of 0ms, batching still happens but with a zero
  // delay, i.e. commits will be sent to the service at the next opportunity.
  uint32_t shmem_batch_commits_duration_ms = 0;

  // [Optional] If set, the policy object is notified when certain SDK events
  // occur and may apply policy decisions, such as denying connections. The
  // embedder is responsible for ensuring the object remains alive for the
  // lifetime of the process.
  TracingPolicy* tracing_policy = nullptr;

  // [Optional] If set, log messages generated by perfetto are passed to this
  // callback instead of being logged directly.
  LogMessageCallback log_message_callback = nullptr;

 protected:
  friend class Tracing;
  friend class internal::TracingMuxerImpl;

  // Used only by the DCHECK in tracing.cc, to check that the config is the
  // same in case of re-initialization.
  bool operator==(const TracingInitArgs& other) const {
    return std::tie(backends, custom_backend, platform, shmem_size_hint_kb,
                    shmem_page_size_hint_kb, in_process_backend_factory_,
                    system_backend_factory_, dcheck_is_on_) ==
           std::tie(other.backends, other.custom_backend, other.platform,
                    other.shmem_size_hint_kb, other.shmem_page_size_hint_kb,
                    other.in_process_backend_factory_,
                    other.system_backend_factory_, other.dcheck_is_on_);
  }

  using BackendFactoryFunction = TracingBackend* (*)();
  BackendFactoryFunction in_process_backend_factory_ = nullptr;
  BackendFactoryFunction system_backend_factory_ = nullptr;
  bool dcheck_is_on_ = PERFETTO_DCHECK_IS_ON();
};

// The entry-point for using perfetto.
class PERFETTO_EXPORT Tracing {
 public:
  // Initializes Perfetto with the given backends in the calling process and/or
  // with a user-provided backend. No-op if called more than once.
  static inline void Initialize(const TracingInitArgs& args)
      PERFETTO_ALWAYS_INLINE {
    TracingInitArgs args_copy(args);
    // This code is inlined to allow dead-code elimination for unused backends.
    // This saves ~200 KB when not using the in-process backend (b/148198993).
    // The logic behind it is the following:
    // Nothing other than the code below references the two GetInstance()
    // methods. From a linker-graph viewpoint, those GetInstance() pull in many
    // other pieces of the codebase (e.g. InProcessTracingBackend pulls the
    // whole TracingServiceImpl, SystemTracingBackend pulls the whole //ipc
    // layer). Due to the inline, the compiler can see through the code and
    // realize that some branches are always not taken. When that happens, no
    // reference to the backends' GetInstance() is emitted and that allows the
    // linker GC to get rid of the entire set of dependencies.
    if (args.backends & kInProcessBackend) {
      args_copy.in_process_backend_factory_ =
          &internal::InProcessTracingBackend::GetInstance;
    }
    if (args.backends & kSystemBackend) {
      args_copy.system_backend_factory_ =
          &internal::SystemTracingBackend::GetInstance;
    }
    InitializeInternal(args_copy);
  }

  // Checks if tracing has been initialized by calling |Initialize|.
  static bool IsInitialized();

  // Start a new tracing session using the given tracing backend. Use
  // |kUnspecifiedBackend| to select an available backend automatically.
  // For the moment this can be used only when initializing tracing in
  // kInProcess mode. For the system mode use the 'bin/perfetto' cmdline client.
  static std::unique_ptr<TracingSession> NewTrace(
      BackendType = kUnspecifiedBackend);

  // Uninitialize Perfetto. Only exposed for testing scenarios where it can be
  // guaranteed that no tracing sessions or other operations are happening when
  // this call is made.
  static void ResetForTesting();

 private:
  static void InitializeInternal(const TracingInitArgs&);

  Tracing() = delete;
};

class PERFETTO_EXPORT TracingSession {
 public:
  virtual ~TracingSession();

  // Configure the session passing the trace config.
  // If a writable file handle is given through |fd|, the trace will
  // automatically written to that file. Otherwise you should call ReadTrace()
  // to retrieve the trace data. This call does not take ownership of |fd|.
  // TODO(primiano): add an error callback.
  virtual void Setup(const TraceConfig&, int fd = -1) = 0;

  // Enable tracing asynchronously. Use SetOnStartCallback() to get a
  // notification when the session has fully started.
  virtual void Start() = 0;

  // Enable tracing and block until tracing has started. Note that if data
  // sources are registered after this call was initiated, the call may return
  // before the additional data sources have started. Also, if other producers
  // (e.g., with system-wide tracing) have registered data sources without start
  // notification support, this call may return before those data sources have
  // started.
  virtual void StartBlocking() = 0;

  // This callback will be invoked when all data sources have acknowledged that
  // tracing has started. This callback will be invoked on an internal perfetto
  // thread.
  virtual void SetOnStartCallback(std::function<void()>) = 0;

  // This callback can be used to get a notification when some error occured
  // (e.g., peer disconnection). Error type will be passed as an argument. This
  // callback will be invoked on an internal perfetto thread.
  virtual void SetOnErrorCallback(std::function<void(TracingError)>) = 0;

  // Issues a flush request, asking all data sources to ack the request, within
  // the specified timeout. A "flush" is a fence to ensure visibility of data in
  // the async tracing pipeline. It guarantees that all data written before the
  // Flush() call will be visible in the trace buffer and hence by the
  // ReadTrace() / ReadTraceBlocking() methods.
  // Args:
  //  callback: will be invoked on an internal perfetto thread when all data
  //    sources have acked, or the timeout is reached. The bool argument
  //    will be true if all data sources acked within the timeout, false if
  //    the timeout was hit or some other error occurred (e.g. the tracing
  //    session wasn't started or ended).
  //  timeout_ms: how much time the service will wait for data source acks. If
  //    0, the global timeout specified in the TraceConfig (flush_timeout_ms)
  //    will be used. If flush_timeout_ms is also unspecified, a default value
  //    of 5s will be used.
  // Known issues:
  //    Because flushing is still based on service-side scraping, the very last
  //    trace packet for each data source thread will not be visible. Fixing
  //    this requires either propagating the Flush() to the data sources or
  //    changing the order of atomic operations in the service (b/162206162).
  //    Until then, a workaround is to make sure to call
  //    DataSource::Trace([](TraceContext ctx) { ctx.Flush(); }) just before
  //    stopping, on each thread where DataSource::Trace has been previously
  //    called.
  virtual void Flush(std::function<void(bool)>, uint32_t timeout_ms = 0) = 0;

  // Blocking version of Flush(). Waits until all data sources have acked and
  // returns the success/failure status.
  bool FlushBlocking(uint32_t timeout_ms = 0);

  // Disable tracing asynchronously.
  // Use SetOnStopCallback() to get a notification when the tracing session is
  // fully stopped and all data sources have acked.
  virtual void Stop() = 0;

  // Disable tracing and block until tracing has stopped.
  virtual void StopBlocking() = 0;

  // This callback will be invoked when tracing is disabled.
  // This can happen either when explicitly calling TracingSession.Stop() or
  // when the trace reaches its |duration_ms| time limit.
  // This callback will be invoked on an internal perfetto thread.
  virtual void SetOnStopCallback(std::function<void()>) = 0;

  // Changes the TraceConfig for an active tracing session. The session must
  // have been configured and started before. Note that the tracing service
  // only supports changing a subset of TraceConfig fields,
  // see ConsumerEndpoint::ChangeTraceConfig().
  virtual void ChangeTraceConfig(const TraceConfig&) = 0;

  // Struct passed as argument to the callback passed to ReadTrace().
  // [data, size] is guaranteed to contain 1 or more full trace packets, which
  // can be decoded using trace.proto. No partial or truncated packets are
  // exposed. If the trace is empty this returns a zero-sized nullptr with
  // |has_more| == true to signal EOF.
  // This callback will be invoked on an internal perfetto thread.
  struct ReadTraceCallbackArgs {
    const char* data = nullptr;
    size_t size = 0;

    // When false, this will be the last invocation of the callback for this
    // read cycle.
    bool has_more = false;
  };

  // Reads back the trace data (raw protobuf-encoded bytes) asynchronously.
  // Can be called at any point during the trace, typically but not necessarily,
  // after stopping. If this is called before the end of the trace (i.e. before
  // Stop() / StopBlocking()), in almost all cases you need to call
  // Flush() / FlushBlocking() before Read(). This is to guarantee that tracing
  // data in-flight in the data sources is committed into the tracing buffers
  // before reading them.
  // Reading the trace data is a destructive operation w.r.t. contents of the
  // trace buffer and is not idempotent.
  // A single ReadTrace() call can yield >1 callback invocations, until
  // |has_more| is false.
  using ReadTraceCallback = std::function<void(ReadTraceCallbackArgs)>;
  virtual void ReadTrace(ReadTraceCallback) = 0;

  // Synchronous version of ReadTrace(). It blocks the calling thread until all
  // the trace contents are read. This is slow and inefficient (involves more
  // copies) and is mainly intended for testing.
  std::vector<char> ReadTraceBlocking();

  // Struct passed as an argument to the callback for GetTraceStats(). Contains
  // statistics about the tracing session.
  struct GetTraceStatsCallbackArgs {
    // Whether or not querying statistics succeeded.
    bool success = false;
    // Serialized TraceStats protobuf message. To decode:
    //
    //   perfetto::protos::gen::TraceStats trace_stats;
    //   trace_stats.ParseFromArray(args.trace_stats_data.data(),
    //                              args.trace_stats_data.size());
    //
    std::vector<uint8_t> trace_stats_data;
  };

  // Requests a snapshot of statistical data for this tracing session. Only one
  // query may be active at a time. This callback will be invoked on an internal
  // perfetto thread.
  using GetTraceStatsCallback = std::function<void(GetTraceStatsCallbackArgs)>;
  virtual void GetTraceStats(GetTraceStatsCallback) = 0;

  // Synchronous version of GetTraceStats() for convenience.
  GetTraceStatsCallbackArgs GetTraceStatsBlocking();

  // Struct passed as an argument to the callback for QueryServiceState().
  // Contains information about registered data sources.
  struct QueryServiceStateCallbackArgs {
    // Whether or not getting the service state succeeded.
    bool success = false;
    // Serialized TracingServiceState protobuf message. To decode:
    //
    //   perfetto::protos::gen::TracingServiceState state;
    //   state.ParseFromArray(args.service_state_data.data(),
    //                        args.service_state_data.size());
    //
    std::vector<uint8_t> service_state_data;
  };

  // Requests a snapshot of the tracing service state for this session. Only one
  // request per session may be active at a time. This callback will be invoked
  // on an internal perfetto thread.
  using QueryServiceStateCallback =
      std::function<void(QueryServiceStateCallbackArgs)>;
  virtual void QueryServiceState(QueryServiceStateCallback) = 0;

  // Synchronous version of QueryServiceState() for convenience.
  QueryServiceStateCallbackArgs QueryServiceStateBlocking();
};

}  // namespace perfetto

#endif  // INCLUDE_PERFETTO_TRACING_TRACING_H_