| // 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 |