| /* |
| * Copyright (C) 2017 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. |
| */ |
| |
| #include "perfetto/protozero/scattered_stream_writer.h" |
| |
| #include <algorithm> |
| |
| #include "perfetto/base/logging.h" |
| |
| namespace protozero { |
| |
| ScatteredStreamWriter::Delegate::~Delegate() {} |
| |
| ScatteredStreamWriter::ScatteredStreamWriter(Delegate* delegate) |
| : delegate_(delegate), |
| cur_range_({nullptr, nullptr}), |
| write_ptr_(nullptr) {} |
| |
| ScatteredStreamWriter::~ScatteredStreamWriter() {} |
| |
| void ScatteredStreamWriter::Reset(ContiguousMemoryRange range) { |
| written_previously_ += static_cast<uint64_t>(write_ptr_ - cur_range_.begin); |
| cur_range_ = range; |
| write_ptr_ = range.begin; |
| PERFETTO_DCHECK(!write_ptr_ || write_ptr_ < cur_range_.end); |
| } |
| |
| void ScatteredStreamWriter::Extend() { |
| Reset(delegate_->GetNewBuffer()); |
| } |
| |
| void ScatteredStreamWriter::WriteBytesSlowPath(const uint8_t* src, |
| size_t size) { |
| size_t bytes_left = size; |
| while (bytes_left > 0) { |
| if (write_ptr_ >= cur_range_.end) |
| Extend(); |
| const size_t burst_size = std::min(bytes_available(), bytes_left); |
| WriteBytesUnsafe(src, burst_size); |
| bytes_left -= burst_size; |
| src += burst_size; |
| } |
| } |
| |
| // TODO(primiano): perf optimization: I suspect that at the end this will always |
| // be called with |size| == 4, in which case we might just hardcode it. |
| uint8_t* ScatteredStreamWriter::ReserveBytes(size_t size) { |
| if (write_ptr_ + size > cur_range_.end) { |
| // Assume the reservations are always < Delegate::GetNewBuffer().size(), |
| // so that one single call to Extend() will definitely give enough headroom. |
| Extend(); |
| PERFETTO_DCHECK(write_ptr_ + size <= cur_range_.end); |
| } |
| uint8_t* begin = write_ptr_; |
| write_ptr_ += size; |
| #if PERFETTO_DCHECK_IS_ON() |
| // In the past, the service had a matching DCHECK in |
| // TraceBuffer::TryPatchChunkContents, which was assuming that service and all |
| // producers are built with matching DCHECK levels. This turned out to be a |
| // source of problems and was removed in b/197340286. This memset is useless |
| // these days and is here only to maintain ABI compatibility between producers |
| // that use a v20+ SDK and older versions of the service that were built in |
| // debug mode. At some point around 2023 it should be safe to remove it. |
| // (running a debug version of traced in production seems a bad idea |
| // regardless). |
| memset(begin, 0, size); |
| #endif |
| return begin; |
| } |
| |
| } // namespace protozero |