| // 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 <fuchsia/tracing/kernel/c/fidl.h> |
| #include <lib/async/default.h> |
| #include <lib/fdio/fdio.h> |
| #include <lib/zx/channel.h> |
| #include <src/lib/fxl/arraysize.h> |
| #include <src/lib/fxl/logging.h> |
| #include <trace-engine/instrumentation.h> |
| #include <trace-provider/provider.h> |
| #include <zircon/device/ktrace.h> |
| #include <zircon/status.h> |
| #include <zircon/syscalls/log.h> |
| |
| #include "garnet/bin/ktrace_provider/device_reader.h" |
| #include "garnet/bin/ktrace_provider/importer.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}, |
| }; |
| |
| // Meta category to retain current contents of ktrace buffer. |
| constexpr char kRetainCategory[] = "kernel:retain"; |
| |
| constexpr char kLogCategory[] = "log"; |
| |
| zx::channel OpenKTrace() { |
| int fd = open(kKTraceDev, O_WRONLY); |
| if (fd < 0) { |
| FXL_LOG(ERROR) << "Failed to open " << kKTraceDev << ": errno=" << errno; |
| return zx::channel(); |
| } |
| zx::channel channel; |
| zx_status_t status = |
| fdio_get_service_handle(fd, channel.reset_and_get_address()); |
| if (status != ZX_OK) { |
| FXL_LOG(ERROR) << "Failed to get " << kKTraceDev |
| << " channel: " << zx_status_get_string(status); |
| return zx::channel(); |
| } |
| return channel; |
| } |
| |
| void LogFidlFailure(const char* rqst_name, zx_status_t fidl_status, |
| zx_status_t rqst_status) { |
| if (fidl_status != ZX_OK) { |
| FXL_LOG(ERROR) << "Ktrace FIDL " << rqst_name |
| << " failed: status=" << fidl_status; |
| } else if (rqst_status != ZX_OK) { |
| FXL_LOG(ERROR) << "Ktrace " << rqst_name |
| << " failed: status=" << rqst_status; |
| } |
| } |
| |
| void RequestKtraceStop(const zx::channel& channel) { |
| zx_status_t stop_status; |
| zx_status_t status = |
| fuchsia_tracing_kernel_ControllerStop(channel.get(), &stop_status); |
| LogFidlFailure("stop", status, stop_status); |
| } |
| |
| void RequestKtraceRewind(const zx::channel& channel) { |
| zx_status_t rewind_status; |
| zx_status_t status = |
| fuchsia_tracing_kernel_ControllerRewind(channel.get(), &rewind_status); |
| LogFidlFailure("rewind", status, rewind_status); |
| } |
| |
| void RequestKtraceStart(const zx::channel& channel, uint32_t group_mask) { |
| zx_status_t start_status; |
| zx_status_t status = fuchsia_tracing_kernel_ControllerStart( |
| channel.get(), group_mask, &start_status); |
| LogFidlFailure("start", status, start_status); |
| } |
| |
| } // namespace |
| |
| App::App(const fxl::CommandLine& command_line) |
| : component_context_(sys::ComponentContext::Create()) { |
| trace_observer_.Start(async_get_default_dispatcher(), |
| [this] { UpdateState(); }); |
| } |
| |
| App::~App() {} |
| |
| void App::UpdateState() { |
| uint32_t group_mask = 0; |
| bool capture_log = false; |
| bool retain_current_data = 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); |
| |
| // The default case is everything is enabled, but |kRetainCategory| must be |
| // explicitly passed. |
| retain_current_data = trace_is_category_enabled(kRetainCategory) && |
| num_enabled_categories != arraysize(kGroupCategories); |
| } |
| |
| if (current_group_mask_ != group_mask) { |
| StopKTrace(); |
| StartKTrace(group_mask, retain_current_data); |
| } |
| |
| if (capture_log) { |
| log_importer_.Start(); |
| } else { |
| log_importer_.Stop(); |
| } |
| } |
| |
| void App::StartKTrace(uint32_t group_mask, bool retain_current_data) { |
| FXL_DCHECK(!context_); |
| if (!group_mask) { |
| return; // nothing to trace |
| } |
| |
| FXL_LOG(INFO) << "Starting ktrace"; |
| |
| zx::channel channel = OpenKTrace(); |
| if (!channel) { |
| return; |
| } |
| |
| context_ = trace_acquire_prolonged_context(); |
| if (!context_) { |
| // Tracing was disabled in the meantime. |
| return; |
| } |
| current_group_mask_ = group_mask; |
| |
| RequestKtraceStop(channel); |
| if (!retain_current_data) { |
| RequestKtraceRewind(channel); |
| } |
| RequestKtraceStart(channel, group_mask); |
| |
| FXL_VLOG(1) << "Ktrace started"; |
| } |
| |
| void App::StopKTrace() { |
| if (!context_) { |
| return; // not currently tracing |
| } |
| FXL_DCHECK(current_group_mask_); |
| |
| FXL_LOG(INFO) << "Stopping ktrace"; |
| |
| { |
| zx::channel channel = OpenKTrace(); |
| if (channel) { |
| RequestKtraceStop(channel); |
| } |
| } |
| |
| // Acquire a context for writing to the trace buffer. |
| auto buffer_context = trace_acquire_context(); |
| |
| DeviceReader 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; |
| |
| FXL_VLOG(1) << "Ktrace stopped"; |
| } |
| |
| } // namespace ktrace_provider |