blob: 5d69058f48a0d360d4d24fefe6ac30e01d401262 [file] [log] [blame]
// Copyright 2019 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 <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fbl/string.h>
#include <fbl/unique_fd.h>
#include <fuchsia/tracing/kernel/c/fidl.h>
#include <lib/fdio/fdio.h>
#include <lib/zx/channel.h>
#include <zircon/device/ktrace.h>
#include <zircon/status.h>
static const char kDevicePath[] = "/dev/misc/ktrace";
static const char kUsage[] = "\
Usage: ktrace [options] <control>\n\
Where <control> is one of:\n\
start <group_mask> - start tracing\n\
stop - stop tracing\n\
rewind - rewind trace buffer\n\
written - print bytes written to trace buffer\n\
Note: This value doesn't reset on \"rewind\". Instead, the rewind\n\
takes effect on the next \"start\".\n\
save <path> - save contents of trace buffer to <path>\n\
\n\
Options:\n\
--help - Duh.\n\
";
static void PrintUsage(FILE* f) {
fputs(kUsage, f);
}
static fbl::unique_fd OpenKtraceDeviceAsFd() {
fbl::unique_fd fd{open(kDevicePath, O_RDWR)};
if (!fd.is_valid()) {
fprintf(stderr, "Cannot open trace device %s: %s\n", kDevicePath,
strerror(errno));
exit(EXIT_FAILURE);
}
return fd;
}
static zx::channel OpenKtraceDeviceAsChannel() {
int fd{open(kDevicePath, O_RDWR)};
if (fd < 0) {
fprintf(stderr, "Cannot open trace device %s: %s\n", kDevicePath,
strerror(errno));
exit(EXIT_FAILURE);
}
zx::channel channel;
zx_status_t status = fdio_get_service_handle(fd, channel.reset_and_get_address());
if (status != ZX_OK) {
fprintf(stderr, "Unable to obtain channel handle from file descriptor: %s\n",
zx_status_get_string(status));
exit(EXIT_FAILURE);
}
return channel;
}
static int LogFidlError(zx_status_t status) {
fprintf(stderr, "Error in FIDL request: %s(%d)\n",
zx_status_get_string(status), status);
return EXIT_FAILURE;
}
static int DoStart(uint32_t group_mask) {
zx::channel channel{OpenKtraceDeviceAsChannel()};
zx_status_t start_status;
zx_status_t status = fuchsia_tracing_kernel_ControllerStart(
channel.get(), group_mask, &start_status);
if (status != ZX_OK) {
return LogFidlError(status);
}
if (start_status != ZX_OK) {
fprintf(stderr, "Error starting ktrace: %s(%d)\n",
zx_status_get_string(start_status), start_status);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
static int DoStop() {
zx::channel channel{OpenKtraceDeviceAsChannel()};
zx_status_t stop_status;
zx_status_t status = fuchsia_tracing_kernel_ControllerStop(
channel.get(), &stop_status);
if (status != ZX_OK) {
return LogFidlError(status);
}
if (stop_status != ZX_OK) {
fprintf(stderr, "Error stopping ktrace: %s(%d)\n",
zx_status_get_string(stop_status), stop_status);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
static int DoRewind() {
zx::channel channel{OpenKtraceDeviceAsChannel()};
zx_status_t rewind_status;
zx_status_t status = fuchsia_tracing_kernel_ControllerRewind(
channel.get(), &rewind_status);
if (status != ZX_OK) {
return LogFidlError(status);
}
if (rewind_status != ZX_OK) {
fprintf(stderr, "Error rewinding ktrace: %s(%d)\n",
zx_status_get_string(rewind_status), rewind_status);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
static int DoWritten() {
zx::channel channel{OpenKtraceDeviceAsChannel()};
zx_status_t written_status;
uint64_t bytes_written;
zx_status_t status = fuchsia_tracing_kernel_ControllerGetBytesWritten(
channel.get(), &written_status, &bytes_written);
if (status != ZX_OK) {
return LogFidlError(status);
}
if (written_status != ZX_OK) {
fprintf(stderr, "Error getting bytes written: %s(%d)\n",
zx_status_get_string(written_status), written_status);
return EXIT_FAILURE;
}
printf("Bytes written: %ld\n", bytes_written);
return EXIT_SUCCESS;
}
static int DoSave(const char* path) {
fbl::unique_fd in_fd{OpenKtraceDeviceAsFd()};
fbl::unique_fd out_fd(open(path, O_CREAT | O_TRUNC | O_WRONLY, 0666));
if (!out_fd.is_valid()) {
fprintf(stderr, "Unable to open file for writing: %s, %s\n", path, strerror(errno));
return EXIT_FAILURE;
}
// Read/write this many bytes at a time.
char buf[4096];
ssize_t bytes_read;
while ((bytes_read = read(in_fd.get(), buf, sizeof(buf))) > 0) {
ssize_t bytes_written = write(out_fd.get(), buf, bytes_read);
if (bytes_written < 0) {
fprintf(stderr, "I/O error saving buffer: %s\n", strerror(errno));
return EXIT_FAILURE;
}
if (bytes_written != bytes_read) {
fprintf(stderr, "Short write saving buffer: %zd vs %zd\n",
bytes_written, bytes_read);
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
static void EnsureNArgs(const fbl::String& cmd, int argc, int expected_argc) {
if (argc != expected_argc) {
fprintf(stderr, "Unexpected number of args for command %s\n", cmd.c_str());
PrintUsage(stderr);
exit(EXIT_FAILURE);
}
}
int main(int argc, char** argv) {
if (argc >= 2 && strcmp(argv[1], "--help") == 0) {
PrintUsage(stdout);
return EXIT_SUCCESS;
}
if (argc < 2) {
PrintUsage(stderr);
return EXIT_FAILURE;
}
const fbl::String cmd{argv[1]};
if (cmd == "start") {
EnsureNArgs(cmd, argc, 3);
int group_mask = atoi(argv[2]);
if (group_mask < 0) {
fprintf(stderr, "Invalid group mask\n");
return EXIT_FAILURE;
}
return DoStart(group_mask);
} else if (cmd == "stop") {
EnsureNArgs(cmd, argc, 2);
return DoStop();
} else if (cmd == "rewind") {
EnsureNArgs(cmd, argc, 2);
return DoRewind();
} else if (cmd == "written") {
EnsureNArgs(cmd, argc, 2);
return DoWritten();
} else if (cmd == "save") {
EnsureNArgs(cmd, argc, 3);
const char* path = argv[2];
return DoSave(path);
}
PrintUsage(stderr);
return EXIT_FAILURE;
}