blob: c02cd0814444a91fa8e84d386f6dfc5e131c64f9 [file] [log] [blame]
// Copyright 2020 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 "tracing.h"
#include <lib/zircon-internal/ktrace.h>
#include <fstream>
#include <iomanip>
#include "src/lib/fxl/logging.h"
// Rewinds kernel trace buffer.
void Tracing::Rewind() {
const auto status = zx_ktrace_control(root_resource_, KTRACE_ACTION_REWIND, 0, nullptr);
FXL_CHECK(status == ZX_OK) << "Failed to rewind kernel trace buffer.";
}
// Starts kernel tracing.
void Tracing::Start(uint32_t group_mask) {
const auto status = zx_ktrace_control(root_resource_, KTRACE_ACTION_START, group_mask, nullptr);
FXL_CHECK(status == ZX_OK) << "Failed to start tracing.";
running_ = true;
}
// Stops kernel tracing.
void Tracing::Stop() {
const auto status = zx_ktrace_control(root_resource_, KTRACE_ACTION_STOP, 0, nullptr);
FXL_CHECK(status == ZX_OK) << "Failed to stop tracing.";
running_ = false;
}
// Returns a string with human-readable translations of tag name, event, and any possible flags.
std::string Tracing::InterpretTag(const uint32_t tag, const TagDefinition* info) {
uint32_t event = KTRACE_EVENT(tag);
uint32_t flags = KTRACE_FLAGS(tag);
std::stringstream output;
output << info->name << "(0x" << std::hex << event << ")";
if (flags != 0)
output << ", flags 0x" << std::hex << flags;
return output.str();
}
// Writes human-readable translation for 16 byte records into file specified by <file>.
void Tracing::Write16B(std::ostream& file, const TagDefinition* info,
const ktrace_header_t* record) {
file << std::dec << record->ts << ": " << InterpretTag(record->tag, info) << ", arg 0x"
<< std::hex << record->tid << "\n";
}
// Writes human-readable translation for 32 byte records into file specified by <file>.
void Tracing::Write32B(std::ostream& file, const TagDefinition* info,
const ktrace_rec_32b_t* record) {
file << std::dec << record->ts << ": " << InterpretTag(record->tag, info) << ", tid 0x"
<< std::hex << record->tid << ", a 0x" << std::hex << record->a << ", b 0x" << std::hex
<< record->b << ", c 0x" << std::hex << record->c << ", d 0x" << std::hex << record->d
<< "\n";
}
// Writes human-readable translation name type records into file specified by <file>.
void Tracing::WriteName(std::ostream& file, const TagDefinition* info,
const ktrace_rec_name_t* record) {
file << InterpretTag(record->tag, info) << ", id 0x" << std::hex << record->id << ", arg 0x"
<< std::hex << record->arg << ", " << record->name << "\n";
}
// Performs same action as zx_ktrace_read, but returns bytes_read.
size_t Tracing::ReadAndReturnBytesRead(zx_handle_t handle, void* data, uint32_t offset, size_t len,
size_t* bytes_read) {
const auto status = zx_ktrace_read(handle, data, offset, len, bytes_read);
FXL_CHECK(status == ZX_OK) << "zx_ktrace_read failed.";
return *bytes_read;
}
// Reads trace buffer and converts output into human-readable format. Stores in location defined by
// <filepath>. Will overwrite any existing files with same name.
bool Tracing::WriteHumanReadable(std::ostream& human_readable_file) {
bool succeeded = true;
if (running_) {
FXL_LOG(WARNING) << "Tracing was running when human readable translation was started. Tracing "
"stopped.";
Stop();
}
char data_buf[4096];
size_t records_read = 0;
size_t bytes_read = 0;
uint32_t offset = 0;
const auto header_size = sizeof(ktrace_header_t);
if (!human_readable_file) {
FXL_LOG(ERROR) << "Failed to open file.";
return false;
}
while (ReadAndReturnBytesRead(root_resource_, data_buf, offset, header_size, &bytes_read) > 0) {
// Try reading more before assuming error.
if (bytes_read < header_size) {
size_t bytes_read_originally = bytes_read;
bytes_read = ReadAndReturnBytesRead(root_resource_, data_buf + bytes_read_originally,
offset + bytes_read_originally,
header_size - bytes_read_originally, &bytes_read);
// Record is incomplete because it is presumably at the end of the trace buffer. Update offset
// and redo read to make sure this is actually the case.
if (bytes_read == 0) {
offset += bytes_read_originally;
continue;
}
bytes_read += bytes_read_originally;
}
// Reading less bytes than defined by ktrace_header_t can lead to reading uninitialized memory.
if (bytes_read < header_size) {
FXL_LOG(ERROR) << "Error reading traces, trace read stopped.";
succeeded = false;
break;
}
ktrace_header_t* record = reinterpret_cast<ktrace_header_t*>(data_buf);
// If the record has zero length, something is wrong and the rest of the data will be junk.
if (KTRACE_LEN(record->tag) == 0) {
FXL_LOG(ERROR) << "Error reading traces, trace read stopped.";
succeeded = false;
break;
}
// Read trace payload.
if (KTRACE_LEN(record->tag) > bytes_read) {
offset += bytes_read;
bytes_read = ReadAndReturnBytesRead(root_resource_, data_buf + bytes_read, offset,
KTRACE_LEN(record->tag) - bytes_read, &bytes_read);
}
offset += bytes_read;
records_read++;
const uint32_t event = KTRACE_EVENT(record->tag);
if (event >= countof(kTags)) {
human_readable_file << "Unexpected event: 0x" << std::hex << event << "\n";
continue;
}
const TagDefinition* info = &kTags[event];
if (info->name == nullptr) {
human_readable_file << "Unexpected event: 0x" << std::hex << event << "\n";
continue;
}
switch (info->type) {
case kTag16B:
Write16B(human_readable_file, info, record);
break;
case kTag32B:
Write32B(human_readable_file, info, reinterpret_cast<ktrace_rec_32b_t*>(record));
break;
case kTagNAME:
WriteName(human_readable_file, info, reinterpret_cast<ktrace_rec_name_t*>(record));
break;
default:
human_readable_file << "Unexpected tag type: 0x" << std::hex << info->type << "\n";
break;
}
}
human_readable_file << "\nTotal records read: " << std::dec << records_read
<< "\nTotal bytes read: " << std::dec << offset + bytes_read << "\n";
return succeeded;
}