blob: 98ab0195f702bdf723df75ea2013fc6360f473bd [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 "tools/fidlcat/lib/statistics.h"
#include <algorithm>
#include <vector>
#include "tools/fidlcat/lib/syscall_decoder_dispatcher.h"
#include "tools/fidlcat/proto/session.pb.h"
namespace fidlcat {
void CloseHandleVisitor::VisitHandleValue(const fidl_codec::HandleValue* node,
const fidl_codec::Type* for_type) {
HandleInfo* handle_info =
output_event_->thread()->process()->SearchHandleInfo(node->handle().handle);
if (handle_info != nullptr) {
handle_info->AddCloseEvent(output_event_);
}
}
void CreateHandleVisitor::VisitHandleValue(const fidl_codec::HandleValue* node,
const fidl_codec::Type* for_type) {
HandleInfo* handle_info =
output_event_->thread()->process()->SearchHandleInfo(node->handle().handle);
if (handle_info != nullptr) {
handle_info->AddCreationEvent(output_event_);
}
}
void SyscallDisplayDispatcher::DisplaySummary(std::ostream& os) {
const char* separator = "";
// Displays all the processes one after the other.
for (const auto& process : processes()) {
FidlcatPrinter printer(
this, process.second.get(), os,
extra_generation_needs_colors() ? fidl_codec::WithColors : fidl_codec::WithoutColors, "");
printer << separator;
for (int i = 0; i < columns(); ++i) {
printer << '-';
}
printer << *process.second;
if (!process.second->handle_infos().empty()) {
printer << ": " << process.second->handle_infos().size()
<< ((process.second->handle_infos().size() == 1) ? " handle" : " handles");
}
printer << '\n';
fidl_codec::Indent indent(printer);
// For one process, displays all the handles of the process one after the other.
for (const auto& handle_info : process.second->handle_infos()) {
printer << '\n';
if (handle_info->startup()) {
printer << fidl_codec::Red << "startup " << fidl_codec::ResetColor;
}
if (handle_info->object_type() == ZX_OBJ_TYPE_NONE) {
printer << fidl_codec::Red << "handle " << fidl_codec::ResetColor;
}
printer.DisplayHandleInfo(handle_info);
printer << '\n';
{
fidl_codec::Indent indent(printer);
bool link_displayed = false;
zx_handle_t linked_handle =
inference().GetLinkedHandle(process.second->koid(), handle_info->handle());
if (linked_handle != ZX_HANDLE_INVALID) {
// Sometimes, a process creates a pair of channel ends (zx_channel_create).
// Here, we display the relation between thoise two channel ends.
printer << "linked to ";
printer.DisplayHandle(linked_handle);
printer << '\n';
link_displayed = true;
}
zx_koid_t linked_koid = inference().GetLinkedKoid(handle_info->koid());
if (linked_koid != ZX_KOID_INVALID) {
// Sometimes, for channels, we know which process owns the other end. Because the other
// end may have travelled from one process to another, we may have several processes.
const std::set<HandleInfo*>* linked_koid_handle_infos =
inference().GetKoidHandleInfos(linked_koid);
if (linked_koid_handle_infos != nullptr) {
for (const auto linked_handle_info : *linked_koid_handle_infos) {
Process* linked_process = linked_handle_info->thread()->process();
if (linked_process != process.second.get()) {
// We only display a relation if it's not in the same process (we already displayed
// the relation inside the process).
if (link_displayed) {
printer << "which is ";
} else {
// We haven't displayed yet a relation for the handle (it's not a channel created
// by zx_channel_create by the process).
printer << "linked to ";
link_displayed = true;
}
printer.DisplayHandleInfo(linked_handle_info);
printer << " in process " << linked_process->name() << ':' << fidl_codec::Red
<< linked_process->koid() << fidl_codec::ResetColor << '\n';
}
}
}
}
// Displays all the sessions for the handle.
const char* separator = "";
for (const auto& session : handle_info->sessions()) {
printer << separator;
const OutputEvent* creation_event = session->creation_event();
if (creation_event != nullptr) {
if (printer.display_stack_frame()) {
printer.DisplayStackFrame(creation_event->invoked_event()->stack_frame());
}
// Displays the creation event for the session.
printer << "created by ";
if (creation_event->syscall()->kind() == SyscallKind::kRegularSyscall) {
// The creation event is something like zx_channel_create, zx_timer_create, ...
printer << fidl_codec::Green << creation_event->syscall()->name()
<< fidl_codec::ResetColor;
} else {
// The creation event is a message read from a channel (for example zx_channel_read).
// The process received the event from the message.
auto creation_handle_info = creation_event->invoked_event()->GetHandleInfo(
creation_event->syscall()->SearchInlineMember("handle", /*invoked=*/true));
FX_DCHECK(creation_handle_info != nullptr);
printer.DisplayHandleInfo(creation_handle_info);
const fidl_codec::FidlMessageValue* message = creation_event->GetMessage();
FX_DCHECK(message != nullptr);
FX_DCHECK(message->method() != nullptr);
printer << " receiving " << fidl_codec::Green
<< message->method()->fully_qualified_name() << fidl_codec::ResetColor;
}
printer << '\n';
}
// Displays all the regular events for the handle.
{
fidl_codec::Indent indent(printer);
for (const auto& event : session->events()) {
event->Display(printer);
}
}
const OutputEvent* close_event = session->close_event();
if (close_event != nullptr) {
// Displays the close event for the session.
printer << "closed by ";
if (close_event->syscall()->kind() == SyscallKind::kRegularSyscall) {
// The close event is zx_handle_close or zx_handle_close_many.
printer << fidl_codec::Green << close_event->syscall()->name()
<< fidl_codec::ResetColor;
} else {
// The close event is a message sent to a channel to another process (for example
// zx_channel_write).
// The process sent the event using the message.
auto close_handle_info = close_event->invoked_event()->GetHandleInfo(
close_event->syscall()->SearchInlineMember("handle", /*invoked=*/true));
FX_DCHECK(close_handle_info != nullptr);
printer.DisplayHandleInfo(close_handle_info);
const fidl_codec::FidlMessageValue* message =
close_event->invoked_event()->GetMessage();
FX_DCHECK(message != nullptr);
FX_DCHECK(message->method() != nullptr);
printer << " sending " << fidl_codec::Green
<< message->method()->fully_qualified_name() << fidl_codec::ResetColor;
}
printer << '\n';
}
separator = "\n";
}
}
}
separator = "\n";
}
}
void SyscallDisplayDispatcher::DisplayThreads(std::ostream& os) {
std::vector<zx_koid_t> process_koids;
for (const auto& process : processes()) {
process_koids.emplace_back(process.first);
}
std::sort(process_koids.begin(), process_koids.end());
for (auto process_koid : process_koids) {
Process* process = processes()[process_koid].get();
FidlcatPrinter printer(
this, process, os,
extra_generation_needs_colors() ? fidl_codec::WithColors : fidl_codec::WithoutColors, "");
std::vector<zx_koid_t> thread_koids;
for (const auto& thread : threads()) {
if (thread.second->process()->koid() == process_koid) {
thread_koids.emplace_back(thread.first);
}
}
std::sort(thread_koids.begin(), thread_koids.end());
for (auto thread_koid : thread_koids) {
Thread* thread = threads()[thread_koid].get();
printer << "\n";
printer << "thread " << process->name() << " " << fidl_codec::Red << process_koid
<< fidl_codec::ResetColor << ":" << fidl_codec::Red << thread_koid
<< fidl_codec::ResetColor << "\n";
printer << "---------------------------------------------\n";
for (const auto& event : decoded_events()) {
if (event->ForThread(thread)) {
event->Display(printer);
}
}
}
}
}
} // namespace fidlcat