blob: 391758f8e1516ea14df404362ef98c00c271052e [file] [log] [blame]
// Copyright 2016 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 "garnet/bin/ktrace_provider/app.h"
#include <fcntl.h>
#include <unistd.h>
#include <lib/async/default.h>
#include <trace-engine/instrumentation.h>
#include <trace-provider/provider.h>
#include <zircon/device/ktrace.h>
#include <zircon/syscalls/log.h>
#include "garnet/bin/ktrace_provider/importer.h"
#include "garnet/bin/ktrace_provider/reader.h"
#include "lib/fxl/arraysize.h"
#include "lib/fxl/files/file.h"
#include "lib/fxl/logging.h"
namespace ktrace_provider {
namespace {
constexpr char kKTraceDev[] = "/dev/misc/ktrace";
struct KTraceCategory {
const char* name;
uint32_t group;
};
constexpr KTraceCategory kGroupCategories[] = {
{"kernel", KTRACE_GRP_ALL},
{"kernel:meta", KTRACE_GRP_META},
{"kernel:lifecycle", KTRACE_GRP_LIFECYCLE},
{"kernel:sched", KTRACE_GRP_SCHEDULER},
{"kernel:tasks", KTRACE_GRP_TASKS},
{"kernel:ipc", KTRACE_GRP_IPC},
{"kernel:irq", KTRACE_GRP_IRQ},
{"kernel:probe", KTRACE_GRP_PROBE},
{"kernel:arch", KTRACE_GRP_ARCH},
};
constexpr char kLogCategory[] = "log";
fxl::UniqueFD OpenKTrace() {
int result = open(kKTraceDev, O_WRONLY);
if (result < 0) {
FXL_LOG(ERROR) << "Failed to open " << kKTraceDev << ": errno=" << errno;
}
return fxl::UniqueFD(result); // take ownership here
}
void IoctlKtraceStop(int fd) {
zx_status_t status = ioctl_ktrace_stop(fd);
if (status != ZX_OK)
FXL_LOG(ERROR) << "ioctl_ktrace_stop failed: status=" << status;
}
void IoctlKtraceStart(int fd, uint32_t group_mask) {
zx_status_t status = ioctl_ktrace_start(fd, &group_mask);
if (status != ZX_OK)
FXL_LOG(ERROR) << "ioctl_ktrace_start failed: status=" << status;
}
} // namespace
App::App(const fxl::CommandLine& command_line)
: startup_context_(component::StartupContext::CreateFromStartupInfo()) {
trace_observer_.Start(async_get_default_dispatcher(),
[this] { UpdateState(); });
}
App::~App() {}
void App::UpdateState() {
uint32_t group_mask = 0;
bool capture_log = false;
if (trace_state() == TRACE_STARTED) {
size_t num_enabled_categories = 0;
for (size_t i = 0; i < arraysize(kGroupCategories); i++) {
auto& category = kGroupCategories[i];
if (trace_is_category_enabled(category.name)) {
group_mask |= category.group;
++num_enabled_categories;
}
}
// Avoid capturing log traces in the default case by detecting whether all
// categories are enabled or not.
capture_log = trace_is_category_enabled(kLogCategory) &&
num_enabled_categories != arraysize(kGroupCategories);
}
if (current_group_mask_ != group_mask) {
StopKTrace();
StartKTrace(group_mask);
}
if (capture_log) {
log_importer_.Start();
} else {
log_importer_.Stop();
}
}
void App::StartKTrace(uint32_t group_mask) {
FXL_DCHECK(!context_);
if (!group_mask) {
return; // nothing to trace
}
FXL_LOG(INFO) << "Starting ktrace";
fxl::UniqueFD fd = OpenKTrace();
if (!fd.is_valid()) {
return;
}
context_ = trace_acquire_prolonged_context();
if (!context_) {
// Tracing was disabled in the meantime.
return;
}
current_group_mask_ = group_mask;
IoctlKtraceStop(fd.get());
IoctlKtraceStart(fd.get(), group_mask);
FXL_LOG(INFO) << "Started ktrace";
}
void App::StopKTrace() {
if (!context_) {
return; // not currently tracing
}
FXL_DCHECK(current_group_mask_);
FXL_LOG(INFO) << "Stopping ktrace";
fxl::UniqueFD fd = OpenKTrace();
if (fd.is_valid()) {
IoctlKtraceStop(fd.get());
}
// Acquire a context for writing to the trace buffer.
auto buffer_context = trace_acquire_context();
Reader reader;
Importer importer(buffer_context);
if (!importer.Import(reader)) {
FXL_LOG(ERROR) << "Errors encountered while importing ktrace data";
}
trace_release_context(buffer_context);
trace_release_prolonged_context(context_);
context_ = nullptr;
current_group_mask_ = 0u;
}
} // namespace ktrace_provider