blob: 7f8325d86dfbe4a0ccade9c8a41a4c56090afcc2 [file] [log] [blame]
Primiano Tucci4f9b6d72017-12-05 20:59:16 +00001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "src/tracing/core/service_impl.h"
18
19#include <inttypes.h>
Primiano Tucci53589332017-12-19 11:31:13 +010020#include <string.h>
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000021
22#include <algorithm>
23
24#include "perfetto/base/logging.h"
25#include "perfetto/base/task_runner.h"
Primiano Tucci53589332017-12-19 11:31:13 +010026#include "perfetto/protozero/proto_utils.h"
Primiano Tucci42e2de12017-12-07 16:46:04 +000027#include "perfetto/tracing/core/consumer.h"
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000028#include "perfetto/tracing/core/data_source_config.h"
29#include "perfetto/tracing/core/producer.h"
30#include "perfetto/tracing/core/shared_memory.h"
Primiano Tucci53589332017-12-19 11:31:13 +010031#include "perfetto/tracing/core/trace_config.h"
32#include "perfetto/tracing/core/trace_packet.h"
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000033
34namespace perfetto {
35
36// TODO add ThreadChecker everywhere.
37
Primiano Tucci53589332017-12-19 11:31:13 +010038using protozero::proto_utils::ParseVarInt;
39
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000040namespace {
41constexpr size_t kPageSize = 4096;
42constexpr size_t kDefaultShmSize = kPageSize * 16; // 64 KB.
43constexpr size_t kMaxShmSize = kPageSize * 1024; // 4 MB.
44} // namespace
45
46// static
47std::unique_ptr<Service> Service::CreateInstance(
48 std::unique_ptr<SharedMemory::Factory> shm_factory,
49 base::TaskRunner* task_runner) {
50 return std::unique_ptr<Service>(
51 new ServiceImpl(std::move(shm_factory), task_runner));
52}
53
54ServiceImpl::ServiceImpl(std::unique_ptr<SharedMemory::Factory> shm_factory,
55 base::TaskRunner* task_runner)
Primiano Tucci53589332017-12-19 11:31:13 +010056 : task_runner_(task_runner),
57 shm_factory_(std::move(shm_factory)),
58 buffer_ids_(kMaxTraceBuffers) {
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000059 PERFETTO_DCHECK(task_runner_);
60}
61
62ServiceImpl::~ServiceImpl() {
63 // TODO handle teardown of all Producer.
64}
65
66std::unique_ptr<Service::ProducerEndpoint> ServiceImpl::ConnectProducer(
67 Producer* producer,
68 size_t shared_buffer_size_hint_bytes) {
69 const ProducerID id = ++last_producer_id_;
Primiano Tucci3324dfc2017-12-20 14:35:58 +010070 PERFETTO_DLOG("Producer %" PRIu64 " connected", id);
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000071 size_t shm_size = std::min(shared_buffer_size_hint_bytes, kMaxShmSize);
72 if (shm_size % kPageSize || shm_size < kPageSize)
73 shm_size = kDefaultShmSize;
74
75 // TODO(primiano): right now Create() will suicide in case of OOM if the mmap
76 // fails. We should instead gracefully fail the request and tell the client
77 // to go away.
78 auto shared_memory = shm_factory_->CreateSharedMemory(shm_size);
79 std::unique_ptr<ProducerEndpointImpl> endpoint(new ProducerEndpointImpl(
80 id, this, task_runner_, producer, std::move(shared_memory)));
81 auto it_and_inserted = producers_.emplace(id, endpoint.get());
82 PERFETTO_DCHECK(it_and_inserted.second);
83 task_runner_->PostTask(std::bind(&Producer::OnConnect, endpoint->producer()));
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000084 return std::move(endpoint);
85}
86
87void ServiceImpl::DisconnectProducer(ProducerID id) {
Primiano Tucci3324dfc2017-12-20 14:35:58 +010088 PERFETTO_DLOG("Producer %" PRIu64 " disconnected", id);
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000089 PERFETTO_DCHECK(producers_.count(id));
90 producers_.erase(id);
Primiano Tucci4f9b6d72017-12-05 20:59:16 +000091}
92
93ServiceImpl::ProducerEndpointImpl* ServiceImpl::GetProducer(
94 ProducerID id) const {
95 auto it = producers_.find(id);
96 if (it == producers_.end())
97 return nullptr;
98 return it->second;
99}
100
Primiano Tucci42e2de12017-12-07 16:46:04 +0000101std::unique_ptr<Service::ConsumerEndpoint> ServiceImpl::ConnectConsumer(
102 Consumer* consumer) {
Primiano Tucci3324dfc2017-12-20 14:35:58 +0100103 PERFETTO_DLOG("Consumer %p connected", reinterpret_cast<void*>(consumer));
Primiano Tucci42e2de12017-12-07 16:46:04 +0000104 std::unique_ptr<ConsumerEndpointImpl> endpoint(
105 new ConsumerEndpointImpl(this, task_runner_, consumer));
106 auto it_and_inserted = consumers_.emplace(endpoint.get());
107 PERFETTO_DCHECK(it_and_inserted.second);
108 task_runner_->PostTask(std::bind(&Consumer::OnConnect, endpoint->consumer()));
109 return std::move(endpoint);
110}
111
112void ServiceImpl::DisconnectConsumer(ConsumerEndpointImpl* consumer) {
Primiano Tucci3324dfc2017-12-20 14:35:58 +0100113 PERFETTO_DLOG("Consumer %p disconnected", reinterpret_cast<void*>(consumer));
Primiano Tucci42e2de12017-12-07 16:46:04 +0000114 PERFETTO_DCHECK(consumers_.count(consumer));
115 // TODO: In next CL, tear down the trace sessions for the consumer.
116 consumers_.erase(consumer);
117}
118
Primiano Tucci53589332017-12-19 11:31:13 +0100119void ServiceImpl::EnableTracing(ConsumerEndpointImpl* consumer,
120 const TraceConfig& cfg) {
Primiano Tucci3324dfc2017-12-20 14:35:58 +0100121 PERFETTO_DLOG("Enabling tracing for consumer %p",
122 reinterpret_cast<void*>(consumer));
123
Primiano Tucci53589332017-12-19 11:31:13 +0100124 if (tracing_sessions_.count(consumer)) {
125 PERFETTO_DLOG(
126 "A Consumer is trying to EnableTracing() but another tracing session "
127 "is already active");
128 // TODO(primiano): make this a bool and return failure to the IPC layer.
129 return;
130 }
131 TracingSession& ts =
132 tracing_sessions_.emplace(consumer, TracingSession{}).first->second;
133
134 // Initialize the log buffers.
135 bool did_allocate_all_buffers = true;
136 for (const TraceConfig::BufferConfig& buffer_cfg : cfg.buffers()) {
137 BufferID id = static_cast<BufferID>(buffer_ids_.Allocate());
138 if (!id) {
139 did_allocate_all_buffers = false;
140 break;
141 }
142 PERFETTO_DCHECK(ts.trace_buffers.count(id) == 0);
143 // TODO(primiano): make TraceBuffer::kBufferPageSize dynamic.
Primiano Tuccibbaa58c2017-12-20 13:48:20 +0100144 const size_t buf_size = buffer_cfg.size_kb() * 1024u;
145 auto it_and_inserted = ts.trace_buffers.emplace(id, TraceBuffer(buf_size));
146 if (!it_and_inserted.second || !it_and_inserted.first->second.is_valid()) {
147 did_allocate_all_buffers = false;
148 break;
149 }
Primiano Tucci53589332017-12-19 11:31:13 +0100150 }
151
152 // This can happen if all the kMaxTraceBuffers slots are taken (i.e. we are
153 // not talking about OOM here, just creating > kMaxTraceBuffers entries). In
154 // this case, free all the previously allocated buffers and abort.
155 // TODO: add a test to cover this case, this is quite subtle.
156 if (!did_allocate_all_buffers) {
157 for (const auto& kv : ts.trace_buffers)
158 buffer_ids_.Free(kv.first);
159 ts.trace_buffers.clear();
160 return; // TODO(primiano): return failure condition?
161 }
162
163 // Enable the data sources on the producers.
164 for (const TraceConfig::DataSource& cfg_data_source : cfg.data_sources()) {
165 // Scan all the registered data sources with a matching name.
166 auto range = data_sources_.equal_range(cfg_data_source.config().name());
167 for (auto it = range.first; it != range.second; it++) {
168 const RegisteredDataSource& reg_data_source = it->second;
169 // TODO(primiano): match against |producer_name_filter|.
170
171 const ProducerID producer_id = reg_data_source.producer_id;
172 auto producer_iter = producers_.find(producer_id);
173 if (producer_iter == producers_.end()) {
174 PERFETTO_DCHECK(false); // Something in the unregistration is broken.
175 continue;
176 }
177 ProducerEndpointImpl* producer = producer_iter->second;
178 DataSourceInstanceID inst_id = ++last_data_source_instance_id_;
179 ts.data_source_instances.emplace(producer_id, inst_id);
180 producer->producer()->CreateDataSourceInstance(inst_id,
181 cfg_data_source.config());
182 }
183 }
184} // namespace perfetto
185
186void ServiceImpl::DisableTracing(ConsumerEndpointImpl* consumer) {
Primiano Tucci3324dfc2017-12-20 14:35:58 +0100187 PERFETTO_DLOG("Disabling tracing for consumer %p",
188 reinterpret_cast<void*>(consumer));
Primiano Tucci53589332017-12-19 11:31:13 +0100189 auto it = tracing_sessions_.find(consumer);
190 if (it == tracing_sessions_.end()) {
191 PERFETTO_DLOG("No active tracing session found for the Consumer");
192 return;
193 }
194 TracingSession& tracing_session = it->second;
195 for (const auto& data_source_inst : tracing_session.data_source_instances) {
196 auto producer_it = producers_.find(data_source_inst.first);
197 if (producer_it == producers_.end())
198 continue; // This could legitimately happen if a Producer disconnects.
199 producer_it->second->producer()->TearDownDataSourceInstance(
200 data_source_inst.second);
201 }
202 tracing_session.data_source_instances.clear();
Primiano Tucci42e2de12017-12-07 16:46:04 +0000203}
204
Primiano Tucci53589332017-12-19 11:31:13 +0100205void ServiceImpl::ReadBuffers(ConsumerEndpointImpl* consumer) {
Primiano Tucci3324dfc2017-12-20 14:35:58 +0100206 PERFETTO_DLOG("Reading buffers for consumer %p",
207 reinterpret_cast<void*>(consumer));
Primiano Tucci53589332017-12-19 11:31:13 +0100208 auto it = tracing_sessions_.find(consumer);
209 if (it == tracing_sessions_.end()) {
210 PERFETTO_DLOG(
211 "Consumer invoked ReadBuffers() but no tracing session is active");
212 return; // TODO(primiano): signal failure?
213 }
214 // TODO(primiano): Most of this code is temporary and we should find a better
215 // solution to bookkeep the log buffer (e.g., an allocator-like freelist)
216 // rather than leveraging the SharedMemoryABI (which is intended only for the
217 // Producer <> Service SMB and not for the TraceBuffer itself).
218 auto weak_consumer = consumer->GetWeakPtr();
219 for (auto& buf_it : it->second.trace_buffers) {
220 TraceBuffer& tbuf = buf_it.second;
221 SharedMemoryABI& abi = *tbuf.abi;
222 for (size_t i = 0; i < tbuf.num_pages(); i++) {
223 const size_t page_idx = (i + tbuf.cur_page) % tbuf.num_pages();
224 if (abi.is_page_free(page_idx))
225 continue;
226 uint32_t layout = abi.page_layout_dbg(page_idx);
227 size_t num_chunks = abi.GetNumChunksForLayout(layout);
228 for (size_t chunk_idx = 0; chunk_idx < num_chunks; chunk_idx++) {
229 if (abi.GetChunkState(page_idx, chunk_idx) ==
230 SharedMemoryABI::kChunkFree) {
231 continue;
232 }
233 auto chunk = abi.GetChunkUnchecked(page_idx, layout, chunk_idx);
234 uint16_t num_packets;
235 uint8_t flags;
236 std::tie(num_packets, flags) = chunk.GetPacketCountAndFlags();
237 const uint8_t* ptr = chunk.payload_begin();
Primiano Tucci42e2de12017-12-07 16:46:04 +0000238
Primiano Tucci53589332017-12-19 11:31:13 +0100239 // shared_ptr is really a workardound for the fact that is not possible
240 // to std::move() move-only types in labmdas until C++17.
241 std::shared_ptr<std::vector<TracePacket>> packets(
242 new std::vector<TracePacket>());
243 packets->reserve(num_packets);
244
245 for (size_t pack_idx = 0; pack_idx < num_packets; pack_idx++) {
246 uint64_t pack_size = 0;
247 ptr = ParseVarInt(ptr, chunk.end(), &pack_size);
248 // TODO stitching, look at the flags.
249 bool skip = (pack_idx == 0 &&
250 flags & SharedMemoryABI::ChunkHeader::
251 kFirstPacketContinuesFromPrevChunk) ||
252 (pack_idx == num_packets - 1 &&
253 flags & SharedMemoryABI::ChunkHeader::
254 kLastPacketContinuesOnNextChunk);
255
256 PERFETTO_DLOG(" #%-3zu len:%" PRIu64 " skip: %d\n", pack_idx,
257 pack_size, skip);
258 if (ptr > chunk.end() - pack_size) {
259 PERFETTO_DLOG("out of bounds!\n");
260 break;
261 }
262 if (!skip) {
263 packets->emplace_back();
264 packets->back().AddChunk(Chunk(ptr, pack_size));
265 }
266 ptr += pack_size;
267 } // for(packet)
268 task_runner_->PostTask([weak_consumer, packets]() {
269 if (weak_consumer)
Primiano Tucci3324dfc2017-12-20 14:35:58 +0100270 weak_consumer->consumer()->OnTraceData(std::move(*packets),
271 true /*has_more*/);
Primiano Tucci53589332017-12-19 11:31:13 +0100272 });
273 } // for(chunk)
274 } // for(page_idx)
275 } // for(buffer_id)
276 task_runner_->PostTask([weak_consumer]() {
277 if (weak_consumer)
278 weak_consumer->consumer()->OnTraceData(std::vector<TracePacket>(),
279 false /*has_more*/);
280 });
Primiano Tucci42e2de12017-12-07 16:46:04 +0000281}
282
283void ServiceImpl::FreeBuffers(ConsumerEndpointImpl*) {
Primiano Tucci53589332017-12-19 11:31:13 +0100284 // TODO(primiano): implement here.
Primiano Tucci3324dfc2017-12-20 14:35:58 +0100285 PERFETTO_DLOG("FreeBuffers() not implemented yet");
Primiano Tucci42e2de12017-12-07 16:46:04 +0000286}
287
Primiano Tucci53589332017-12-19 11:31:13 +0100288void ServiceImpl::RegisterDataSource(ProducerID producer_id,
289 DataSourceID ds_id,
290 const DataSourceDescriptor& desc) {
Primiano Tucci3324dfc2017-12-20 14:35:58 +0100291 PERFETTO_DLOG("Producer %" PRIu64
292 " registered data source \"%s\", ID: %" PRIu64,
293 producer_id, desc.name().c_str(), ds_id);
294
Primiano Tucci53589332017-12-19 11:31:13 +0100295 PERFETTO_DCHECK(!desc.name().empty());
296 data_sources_.emplace(desc.name(),
297 RegisteredDataSource{producer_id, ds_id, desc});
298}
299
300void ServiceImpl::CopyProducerPageIntoLogBuffer(ProducerID producer_id,
301 BufferID target_buffer,
302 const uint8_t* src,
303 size_t size) {
304 // TODO right now the page_size in the SMB and the trace_buffers_ can
305 // mismatch. Remove the ability to decide the page size on the Producer.
306
307 // TODO(primiano): We should have a direct index to find the TargetBuffer and
308 // perform ACL checks without iterating through all the producers.
309 TraceBuffer* tbuf = nullptr;
310 for (auto& sessions_it : tracing_sessions_) {
311 for (auto& tbuf_it : sessions_it.second.trace_buffers) {
312 const BufferID id = tbuf_it.first;
313 if (id == target_buffer) {
314 // TODO(primiano): we should have some stronger check to prevent that
315 // the Producer passes |target_buffer| which is valid, but that we never
316 // asked it to use. Essentially we want to prevent a malicious producer
317 // to inject data into a log buffer that has nothing to do with it.
318 tbuf = &tbuf_it.second;
319 break;
320 }
321 }
322 }
323
324 if (!tbuf) {
325 PERFETTO_DLOG("Could not find target buffer %u for producer %" PRIu64,
326 target_buffer, producer_id);
327 return;
328 }
329
330 PERFETTO_DCHECK(size == TraceBuffer::kBufferPageSize);
331 uint8_t* dst = tbuf->get_next_page();
332
333 // TODO(primiano): use sendfile(). Requires to make the tbuf itself
334 // a file descriptor (just use SharedMemory without sharing it).
335 PERFETTO_DLOG("Copying page %p from producer %" PRIu64,
336 reinterpret_cast<const void*>(src), producer_id);
337 memcpy(dst, src, size);
338}
339
Primiano Tucci42e2de12017-12-07 16:46:04 +0000340////////////////////////////////////////////////////////////////////////////////
341// ServiceImpl::ConsumerEndpointImpl implementation
342////////////////////////////////////////////////////////////////////////////////
343
344ServiceImpl::ConsumerEndpointImpl::ConsumerEndpointImpl(ServiceImpl* service,
345 base::TaskRunner*,
346 Consumer* consumer)
347 : service_(service), consumer_(consumer), weak_ptr_factory_(this) {}
348
349ServiceImpl::ConsumerEndpointImpl::~ConsumerEndpointImpl() {
350 consumer_->OnDisconnect();
351 service_->DisconnectConsumer(this);
352}
353
354void ServiceImpl::ConsumerEndpointImpl::EnableTracing(const TraceConfig& cfg) {
355 service_->EnableTracing(this, cfg);
356}
357
358void ServiceImpl::ConsumerEndpointImpl::DisableTracing() {
359 service_->DisableTracing(this);
360}
361
362void ServiceImpl::ConsumerEndpointImpl::ReadBuffers() {
363 service_->ReadBuffers(this);
364}
365
366void ServiceImpl::ConsumerEndpointImpl::FreeBuffers() {
367 service_->FreeBuffers(this);
368}
369
370base::WeakPtr<ServiceImpl::ConsumerEndpointImpl>
371ServiceImpl::ConsumerEndpointImpl::GetWeakPtr() {
372 return weak_ptr_factory_.GetWeakPtr();
373}
374
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000375////////////////////////////////////////////////////////////////////////////////
376// ServiceImpl::ProducerEndpointImpl implementation
377////////////////////////////////////////////////////////////////////////////////
378
379ServiceImpl::ProducerEndpointImpl::ProducerEndpointImpl(
380 ProducerID id,
381 ServiceImpl* service,
382 base::TaskRunner* task_runner,
383 Producer* producer,
384 std::unique_ptr<SharedMemory> shared_memory)
385 : id_(id),
386 service_(service),
387 task_runner_(task_runner),
388 producer_(std::move(producer)),
Primiano Tucci53589332017-12-19 11:31:13 +0100389 shared_memory_(std::move(shared_memory)),
390 shmem_abi_(reinterpret_cast<uint8_t*>(shared_memory_->start()),
391 shared_memory_->size(),
392 kPageSize) {
393 // TODO(primiano): make the page-size for the SHM dynamic and find a way to
394 // communicate that to the Producer (add a field to the
395 // InitializeConnectionResponse IPC).
396}
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000397
398ServiceImpl::ProducerEndpointImpl::~ProducerEndpointImpl() {
399 producer_->OnDisconnect();
400 service_->DisconnectProducer(id_);
401}
402
403void ServiceImpl::ProducerEndpointImpl::RegisterDataSource(
Primiano Tucci53589332017-12-19 11:31:13 +0100404 const DataSourceDescriptor& desc,
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000405 RegisterDataSourceCallback callback) {
Primiano Tucci53589332017-12-19 11:31:13 +0100406 DataSourceID ds_id = ++last_data_source_id_;
407 if (!desc.name().empty()) {
408 service_->RegisterDataSource(id_, ds_id, desc);
409 } else {
410 PERFETTO_DLOG("Received RegisterDataSource() with empty name");
411 ds_id = 0;
412 }
413 task_runner_->PostTask(std::bind(std::move(callback), ds_id));
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000414}
415
416void ServiceImpl::ProducerEndpointImpl::UnregisterDataSource(
417 DataSourceID dsid) {
418 PERFETTO_CHECK(dsid);
Primiano Tucci53589332017-12-19 11:31:13 +0100419 // TODO(primiano): implement the bookkeeping logic.
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000420}
421
422void ServiceImpl::ProducerEndpointImpl::NotifySharedMemoryUpdate(
423 const std::vector<uint32_t>& changed_pages) {
Primiano Tucci53589332017-12-19 11:31:13 +0100424 for (uint32_t page_idx : changed_pages) {
425 if (page_idx >= shmem_abi_.num_pages())
426 continue; // Very likely a malicious producer playing dirty.
427
428 if (!shmem_abi_.is_page_complete(page_idx))
429 continue;
430 if (!shmem_abi_.TryAcquireAllChunksForReading(page_idx))
431 continue;
432
433 // TODO: we should start collecting individual chunks from non fully
434 // complete pages after a while.
435
436 service_->CopyProducerPageIntoLogBuffer(
437 id_, shmem_abi_.get_target_buffer(page_idx),
438 shmem_abi_.page_start(page_idx), shmem_abi_.page_size());
439
440 shmem_abi_.ReleaseAllChunksAsFree(page_idx);
441 }
442}
443
444SharedMemory* ServiceImpl::ProducerEndpointImpl::shared_memory() const {
445 return shared_memory_.get();
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000446}
447
Primiano Tucciaf429f92017-12-19 01:51:50 +0100448std::unique_ptr<TraceWriter>
449ServiceImpl::ProducerEndpointImpl::CreateTraceWriter(BufferID) {
450 // TODO(primiano): not implemented yet.
451 // This code path is hit only in in-process configuration, where tracing
452 // Service and Producer are hosted in the same process. It's a use case we
453 // want to support, but not too interesting right now.
454 PERFETTO_CHECK(false);
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000455}
456
Primiano Tucci53589332017-12-19 11:31:13 +0100457////////////////////////////////////////////////////////////////////////////////
458// ServiceImpl::TraceBuffer implementation
459////////////////////////////////////////////////////////////////////////////////
460
461ServiceImpl::TraceBuffer::TraceBuffer(size_t sz) : size(sz) {
Primiano Tuccibbaa58c2017-12-20 13:48:20 +0100462 data = base::PageAllocator::AllocateMayFail(size);
463 if (!data) {
Primiano Tucci53589332017-12-19 11:31:13 +0100464 PERFETTO_ELOG("Trace buffer allocation failed (size: %zu, page_size: %zu)",
465 size, kBufferPageSize);
Primiano Tuccibbaa58c2017-12-20 13:48:20 +0100466 size = 0;
Primiano Tucci53589332017-12-19 11:31:13 +0100467 return;
468 }
Primiano Tucci53589332017-12-19 11:31:13 +0100469 abi.reset(new SharedMemoryABI(get_page(0), size, kBufferPageSize));
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000470}
471
Primiano Tucci53589332017-12-19 11:31:13 +0100472ServiceImpl::TraceBuffer::~TraceBuffer() = default;
473ServiceImpl::TraceBuffer::TraceBuffer(ServiceImpl::TraceBuffer&&) noexcept =
474 default;
475ServiceImpl::TraceBuffer& ServiceImpl::TraceBuffer::operator=(
476 ServiceImpl::TraceBuffer&&) = default;
477
Primiano Tucci4f9b6d72017-12-05 20:59:16 +0000478} // namespace perfetto