blob: 09252a9d4429c14fa52bbce95fde6725bc5371ad [file] [log] [blame]
// Copyright 2024 The Fuchsia 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 "src/performance/trace_manager/buffer_forwarder.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/trace-engine/fields.h>
#include "lib/fit/defer.h"
namespace tracing {
TransferStatus BufferForwarder::WriteMagicNumberRecord() const {
size_t num_words = 1u;
uint64_t record = trace::MagicNumberRecordFields::Type::Make(
trace::ToUnderlyingType(trace::RecordType::kMetadata)) |
trace::MagicNumberRecordFields::RecordSize::Make(num_words) |
trace::MagicNumberRecordFields::MetadataType::Make(
trace::ToUnderlyingType(trace::MetadataType::kTraceInfo)) |
trace::MagicNumberRecordFields::TraceInfoType::Make(
trace::ToUnderlyingType(trace::TraceInfoType::kMagicNumber)) |
trace::MagicNumberRecordFields::Magic::Make(trace::kMagicValue);
return WriteBuffer({reinterpret_cast<uint8_t*>(&record), trace::WordsToBytes(num_words)});
}
TransferStatus BufferForwarder::WriteProviderInfoRecord(uint32_t provider_id,
const std::string& name) const {
size_t num_words = 1u + trace::BytesToWords(trace::Pad(name.size()));
std::vector<uint64_t> record(num_words);
record[0] = trace::ProviderInfoMetadataRecordFields::Type::Make(
trace::ToUnderlyingType(trace::RecordType::kMetadata)) |
trace::ProviderInfoMetadataRecordFields::RecordSize::Make(num_words) |
trace::ProviderInfoMetadataRecordFields::MetadataType::Make(
trace::ToUnderlyingType(trace::MetadataType::kProviderInfo)) |
trace::ProviderInfoMetadataRecordFields::Id::Make(provider_id) |
trace::ProviderInfoMetadataRecordFields::NameLength::Make(name.size());
memcpy(&record[1], name.c_str(), name.size());
return WriteBuffer({reinterpret_cast<uint8_t*>(record.data()), trace::WordsToBytes(num_words)});
}
TransferStatus BufferForwarder::WriteProviderSectionRecord(uint32_t provider_id) const {
size_t num_words = 1u;
uint64_t record = trace::ProviderSectionMetadataRecordFields::Type::Make(
trace::ToUnderlyingType(trace::RecordType::kMetadata)) |
trace::ProviderSectionMetadataRecordFields::RecordSize::Make(num_words) |
trace::ProviderSectionMetadataRecordFields::MetadataType::Make(
trace::ToUnderlyingType(trace::MetadataType::kProviderSection)) |
trace::ProviderSectionMetadataRecordFields::Id::Make(provider_id);
return WriteBuffer({reinterpret_cast<uint8_t*>(&record), trace::WordsToBytes(num_words)});
}
TransferStatus BufferForwarder::WriteProviderBufferOverflowEvent(uint32_t provider_id) const {
size_t num_words = 1u;
uint64_t record = trace::ProviderEventMetadataRecordFields::Type::Make(
trace::ToUnderlyingType(trace::RecordType::kMetadata)) |
trace::ProviderEventMetadataRecordFields::RecordSize::Make(num_words) |
trace::ProviderEventMetadataRecordFields::MetadataType::Make(
trace::ToUnderlyingType(trace::MetadataType::kProviderEvent)) |
trace::ProviderEventMetadataRecordFields::Id::Make(provider_id) |
trace::ProviderEventMetadataRecordFields::Event::Make(
trace::ToUnderlyingType(trace::ProviderEventType::kBufferOverflow));
return WriteBuffer({reinterpret_cast<uint8_t*>(&record), trace::WordsToBytes(num_words)});
}
uint64_t GetBufferWordsWritten(const uint64_t* buffer, uint64_t size_in_words) {
const uint64_t* start = buffer;
const uint64_t* current = start;
const uint64_t* end = start + size_in_words;
while (current < end) {
auto type = trace::RecordFields::Type::Get<trace::RecordType>(*current);
uint64_t length;
if (type != trace::RecordType::kLargeRecord) {
length = trace::RecordFields::RecordSize::Get<size_t>(*current);
} else {
length = trace::LargeBlobFields::RecordSize::Get<size_t>(*current);
}
if (length == 0 || length > trace::RecordFields::kMaxRecordSizeBytes ||
current + length >= end) {
break;
}
current += length;
}
return current - start;
}
TransferStatus BufferForwarder::WriteChunkBy(BufferForwarder::ForwardStrategy strategy,
const zx::vmo& vmo, uint64_t vmo_offset,
uint64_t size) const {
FX_LOGS(INFO) << ": Writing chunk: vmo offset 0x" << std::hex << vmo_offset << ", size 0x"
<< std::hex << size
<< (strategy == ForwardStrategy::Size ? ", by-size" : ", by-record");
// TODO(gmtr): This is run on the async loop and we may block on the socket write below. We should
// instead write as much as possible to the socket and if we get ZX_SHOULD_WAIT we instead
// schedule a continuation on the async loop when the socket becomes writable.
uint64_t size_in_words = trace::BytesToWords(size);
// For paranoia purposes verify size is a multiple of the word size so we
// don't risk overflowing the buffer later.
FX_DCHECK(trace::WordsToBytes(size_in_words) == size);
// The passed in vmo_offset isn't necessarily page aligned. Unlike zx_vmar_read, zx_vmar_map
// requires a page aligned offset, so we'll need to fudge the offset a bit. Truncate the offset to
// be page aligned and then add back the extra bytes to the size of the region that we are mapping
// and then also offset the addr we get back from the mapping.
uint64_t page_aligned_offset = vmo_offset & (~(ZX_PAGE_SIZE - 1));
uint64_t page_aligned_remainder = vmo_offset % ZX_PAGE_SIZE;
zx_vaddr_t addr;
zx_status_t map_result = zx::vmar::root_self()->map(ZX_VM_PERM_READ, 0, vmo, page_aligned_offset,
size + page_aligned_remainder, &addr);
if (map_result != ZX_OK) {
FX_PLOGS(ERROR, map_result) << "Failed to read data from buffer_vmo: " << "offset="
<< page_aligned_offset << ", size=" << size;
return TransferStatus::kProviderError;
}
auto d = fit::defer([addr, size]() { zx::vmar::root_self()->unmap(addr, size); });
zx_vaddr_t offset_addr = addr + page_aligned_remainder;
uint64_t bytes_written;
if (strategy == BufferForwarder::ForwardStrategy::Records) {
uint64_t words_written =
GetBufferWordsWritten(reinterpret_cast<const uint64_t*>(offset_addr), size_in_words);
bytes_written = trace::WordsToBytes(words_written);
FX_LOGS(INFO) << "By-record -> " << bytes_written << " bytes";
} else {
bytes_written = size;
}
return WriteBuffer({reinterpret_cast<const uint8_t*>(offset_addr), bytes_written});
}
TransferStatus BufferForwarder::WriteBuffer(cpp20::span<const uint8_t> data) const {
while (!data.empty()) {
size_t actual = 0;
if (zx_status_t status = destination_.write(0u, data.data(), data.size(), &actual);
status != ZX_OK) {
if (status == ZX_ERR_SHOULD_WAIT) {
zx_signals_t pending = 0;
if (zx_status_t status = destination_.wait_one(ZX_SOCKET_WRITABLE | ZX_SOCKET_PEER_CLOSED,
zx::time::infinite(), &pending);
status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Wait on socket failed: " << status;
return TransferStatus::kWriteError;
}
if (pending & ZX_SOCKET_WRITABLE) {
continue;
}
if (pending & ZX_SOCKET_PEER_CLOSED) {
FX_PLOGS(ERROR, status) << "Peer closed while writing to socket";
return TransferStatus::kReceiverDead;
}
}
return TransferStatus::kWriteError;
}
data = data.subspan(actual);
}
return TransferStatus::kComplete;
}
} // namespace tracing