blob: 22c45781a7905dbdf7bb80e7a99625669f05ad37 [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/event.h"
#include "src/lib/fidl_codec/proto_value.h"
#include "tools/fidlcat/lib/syscall_decoder_dispatcher.h"
#include "tools/fidlcat/proto/session.pb.h"
namespace fidlcat {
void Process::LoadHandleInfo(Inference* inference) {
zxdb::Process* zxdb_process = zxdb_process_.get();
if (zxdb_process == nullptr) {
return;
}
if (loading_handle_info_) {
// We are currently loading information about the handles. If we are unlucky, the result won't
// include information about handles we are now needing. Ask the process to do another load just
// after the current one to be sure to have all the handles we need (including the handle only
// needed after the start of the load).
needs_to_load_handle_info_ = true;
return;
}
loading_handle_info_ = true;
needs_to_load_handle_info_ = false;
zxdb_process->LoadInfoHandleTable(
[this, inference](zxdb::ErrOr<std::vector<debug_ipc::InfoHandle>> handles) {
loading_handle_info_ = false;
if (!handles.ok()) {
FX_LOGS(ERROR) << "msg: " << handles.err().msg();
} else {
for (const auto& handle : handles.value()) {
HandleInfo* handle_info = SearchHandleInfo(handle.handle_value);
if (handle_info != nullptr) {
// Associate the koid and the object type to the handle only if the handle is
// currently used by the monitored process. That is if the handle if referenced by an
// event.
// That means that we may need an extra load if the handle is already known by the
// kernel but not yet needed by the monitored process. This way we avoid creating
// a Handle object for handles we don't know the semantic.
handle_info->set_object_type(handle.type);
handle_info->set_rights(handle.rights);
handle_info->set_koid(handle.koid);
inference->AddKoidHandleInfo(handle.koid, handle_info);
}
if (handle.related_koid != ZX_HANDLE_INVALID) {
// However, the association of koids is always useful.
inference->AddLinkedKoids(handle.koid, handle.related_koid);
}
}
if (needs_to_load_handle_info_) {
needs_to_load_handle_info_ = false;
LoadHandleInfo(inference);
}
}
});
}
void Protocol::AddEvent(const OutputEvent* event, const fidl_codec::FidlMessageValue* message) {
Method* method = GetMethod(message->ordinal(), message->method());
method->AddEvent(event);
++event_count_;
}
void Process::AddEvent(const OutputEvent* event, const fidl_codec::FidlMessageValue* message) {
Protocol* protocol = GetProtocol(
(message->method() != nullptr) ? &message->method()->enclosing_protocol() : nullptr);
protocol->AddEvent(event, message);
++event_count_;
}
void ProcessLaunchedEvent::Write(proto::Event* dst) const {
dst->set_timestamp(timestamp());
proto::ProcessLaunchedEvent* event = dst->mutable_process_launched();
event->set_command(command());
event->set_error_message(error_message());
}
void ProcessMonitoredEvent::Write(proto::Event* dst) const {
dst->set_timestamp(timestamp());
proto::ProcessMonitoredEvent* event = dst->mutable_process_monitored();
event->set_process_koid(process()->koid());
event->set_error_message(error_message());
}
void StopMonitoringEvent::Write(proto::Event* dst) const {
dst->set_timestamp(timestamp());
proto::StopMonitoringEvent* event = dst->mutable_stop_monitoring();
event->set_process_koid(process()->koid());
}
bool SyscallEvent::NeedsToLoadHandleInfo(Inference* inference) {
for (const auto& field : inline_fields_) {
if (field.second->NeedsToLoadHandleInfo(timestamp(), thread()->koid(), inference)) {
return true;
}
}
for (const auto& field : outline_fields_) {
if (field.second->NeedsToLoadHandleInfo(timestamp(), thread()->koid(), inference)) {
return true;
}
}
return false;
}
const fidl_codec::FidlMessageValue* SyscallEvent::GetMessage() const {
if (outline_fields_.size() == 0) {
return nullptr;
}
return outline_fields_.begin()->second->AsFidlMessageValue();
}
const fidl_codec::Value* SyscallEvent::GetValue(const fidl_codec::StructMember* member) const {
if (member == nullptr) {
return nullptr;
}
auto result = inline_fields_.find(member);
if (result != inline_fields_.end()) {
return result->second.get();
}
auto result2 = outline_fields_.find(member);
if (result2 != outline_fields_.end()) {
return result2->second.get();
}
return nullptr;
}
const fidl_codec::HandleValue* SyscallEvent::GetHandleValue(
const fidl_codec::StructMember* member) const {
if (member == nullptr) {
return nullptr;
}
auto result = inline_fields_.find(member);
if (result == inline_fields_.end()) {
return nullptr;
}
return result->second->AsHandleValue();
}
HandleInfo* SyscallEvent::GetHandleInfo(const fidl_codec::StructMember* member) const {
if (member == nullptr) {
return nullptr;
}
auto result = inline_fields_.find(member);
if (result == inline_fields_.end()) {
return nullptr;
}
const fidl_codec::HandleValue* value = result->second->AsHandleValue();
if (value == nullptr) {
return nullptr;
}
return thread()->process()->SearchHandleInfo(value->handle().handle);
}
void InvokedEvent::ComputeHandleInfo(SyscallDisplayDispatcher* dispatcher) {
switch (syscall()->kind()) {
case SyscallKind::kChannelRead:
case SyscallKind::kChannelWrite:
case SyscallKind::kChannelCall: {
// Compute the handle which is used to read/write a message.
FX_DCHECK(!syscall()->input_inline_members().empty());
auto value = inline_fields().find(syscall()->input_inline_members()[0].get());
FX_DCHECK(value != inline_fields().end());
handle_info_ =
thread()->process()->SearchHandleInfo(value->second->AsHandleValue()->handle().handle);
if (handle_info_ == nullptr) {
handle_info_ = dispatcher->CreateHandleInfo(
thread(), value->second->AsHandleValue()->handle().handle, 0, false);
}
break;
}
default:
break;
}
}
void InvokedEvent::Write(proto::Event* dst) const {
dst->set_timestamp(timestamp());
proto::InvokedEvent* event = dst->mutable_invoked();
event->set_thread_koid(thread()->koid());
for (const auto& location : stack_frame_) {
proto::Location* proto_location = event->add_frame();
proto_location->set_path(location.path());
proto_location->set_line(location.line());
proto_location->set_column(location.column());
proto_location->set_address(location.address());
proto_location->set_symbol(location.symbol());
}
event->set_syscall(syscall()->name());
for (const auto& field : inline_fields()) {
fidl_codec::proto::Value value;
fidl_codec::ProtoVisitor visitor(&value);
field.second->Visit(&visitor, nullptr);
if (field.first->id() != 0) {
event->mutable_inline_id_fields()->insert(
google::protobuf::MapPair(static_cast<uint32_t>(field.first->id()), value));
} else {
event->mutable_inline_fields()->insert(google::protobuf::MapPair(field.first->name(), value));
}
}
for (const auto& field : outline_fields()) {
fidl_codec::proto::Value value;
fidl_codec::ProtoVisitor visitor(&value);
field.second->Visit(&visitor, nullptr);
if (field.first->id() != 0) {
event->mutable_outline_id_fields()->insert(
google::protobuf::MapPair(static_cast<uint32_t>(field.first->id()), value));
} else {
event->mutable_outline_fields()->insert(
google::protobuf::MapPair(field.first->name(), value));
}
}
}
void InvokedEvent::PrettyPrint(FidlcatPrinter& printer) const {
if (printer.display_stack_frame()) {
printer.DisplayStackFrame(stack_frame_);
}
printer << syscall()->name();
printer.DisplayInline(syscall()->input_inline_members(), inline_fields());
printer << '\n';
printer.DisplayOutline(syscall()->input_outline_members(), outline_fields());
}
void OutputEvent::Write(proto::Event* dst) const {
dst->set_timestamp(timestamp());
proto::OutputEvent* event = dst->mutable_output();
event->set_thread_koid(thread()->koid());
event->set_syscall(syscall()->name());
event->set_returned_value(returned_value());
event->set_invoked_event_id(invoked_event()->id());
for (const auto& field : inline_fields()) {
fidl_codec::proto::Value value;
fidl_codec::ProtoVisitor visitor(&value);
field.second->Visit(&visitor, nullptr);
if (field.first->id() != 0) {
event->mutable_inline_id_fields()->insert(
google::protobuf::MapPair(static_cast<uint32_t>(field.first->id()), value));
} else {
event->mutable_inline_fields()->insert(google::protobuf::MapPair(field.first->name(), value));
}
}
for (const auto& field : outline_fields()) {
fidl_codec::proto::Value value;
fidl_codec::ProtoVisitor visitor(&value);
field.second->Visit(&visitor, nullptr);
if (field.first->id() != 0) {
event->mutable_outline_id_fields()->insert(
google::protobuf::MapPair(static_cast<uint32_t>(field.first->id()), value));
} else {
event->mutable_outline_fields()->insert(
google::protobuf::MapPair(field.first->name(), value));
}
}
}
void OutputEvent::Display(FidlcatPrinter& printer, bool with_channel) const {
const fidl_codec::FidlMessageValue* message = invoked_event_->GetMessage();
if (message == nullptr) {
message = GetMessage();
if (message == nullptr) {
return;
}
}
printer << fidl_codec::Green << printer.dispatcher()->GetTime(timestamp())
<< fidl_codec::ResetColor << ' ';
switch (syscall()->kind()) {
case SyscallKind::kChannelRead:
printer << "read ";
break;
case SyscallKind::kChannelWrite:
printer << "write ";
break;
case SyscallKind::kChannelCall:
printer << "call ";
break;
default:
return;
}
const fidl_codec::ProtocolMethod* method = message->method();
if (message->ordinal() == kFidlOrdinalEpitaph) {
printer << fidl_codec::WhiteOnMagenta << "epitaph " << fidl_codec::ResetColor << ' '
<< ((message->epitaph_error() == "ZX_OK") ? fidl_codec::Green : fidl_codec::Red)
<< message->epitaph_error() << fidl_codec::ResetColor;
} else {
if (method == nullptr) {
printer << " ordinal=" << std::hex << message->ordinal() << std::dec;
} else {
printer << fidl_codec::WhiteOnMagenta
<< (message->is_request()
? "request "
: ((method->request() != nullptr) ? "response" : "event "))
<< fidl_codec::ResetColor << ' ' << fidl_codec::Green
<< method->enclosing_protocol().name() << '.' << method->name()
<< fidl_codec::ResetColor;
}
}
bool first_argument = true;
if (with_channel && (invoked_event()->handle_info() != nullptr)) {
printer << '(';
printer.DisplayHandleInfo(invoked_event()->handle_info());
first_argument = false;
}
if ((method != nullptr) && (method->short_display() != nullptr)) {
fidl_codec::Indent indent(printer);
const fidl_codec::PayloadableValue* request =
(syscall()->kind() == SyscallKind::kChannelRead)
? GetMessage()->decoded_request()
: invoked_event()->GetMessage()->decoded_request();
fidl_codec::semantic::SemanticContext context(&printer.inference(), printer.process()->koid(),
(invoked_event()->handle_info() == nullptr)
? ZX_HANDLE_INVALID
: invoked_event()->handle_info()->handle(),
request, nullptr, invoked_event()->timestamp());
for (const auto& expression : method->short_display()->inputs()) {
if (first_argument) {
printer << '(';
first_argument = false;
} else {
printer << ", ";
}
expression->PrettyPrint(printer, &context);
}
}
if (!first_argument) {
printer << ')';
}
printer << '\n';
if ((method != nullptr) && (method->short_display() != nullptr)) {
fidl_codec::Indent indent(printer);
const fidl_codec::PayloadableValue* request =
(syscall()->kind() == SyscallKind::kChannelRead)
? GetMessage()->decoded_request()
: invoked_event()->GetMessage()->decoded_request();
fidl_codec::semantic::SemanticContext context(&printer.inference(), printer.process()->koid(),
(invoked_event()->handle_info() == nullptr)
? ZX_HANDLE_INVALID
: invoked_event()->handle_info()->handle(),
request, nullptr, invoked_event()->timestamp());
bool first_result = true;
for (const auto& expression : method->short_display()->results()) {
printer << (first_result ? "-> " : ", ");
first_result = false;
expression->PrettyPrint(printer, &context);
}
if (!first_result) {
printer << '\n';
}
}
}
void OutputEvent::PrettyPrint(FidlcatPrinter& printer) const {
fidl_codec::Indent indent(printer);
switch (syscall()->return_type()) {
case SyscallReturnType::kNoReturn:
return;
case SyscallReturnType::kVoid:
if (inline_fields().empty() && outline_fields().empty()) {
return;
}
printer << "-> ";
break;
case SyscallReturnType::kStatus:
printer << "-> ";
printer.DisplayStatus(static_cast<zx_status_t>(returned_value_));
break;
case SyscallReturnType::kTicks:
printer << "-> " << fidl_codec::Green << "ticks" << fidl_codec::ResetColor << ": "
<< fidl_codec::Blue << static_cast<uint64_t>(returned_value_)
<< fidl_codec::ResetColor;
break;
case SyscallReturnType::kTime:
printer << "-> " << fidl_codec::Green << "time" << fidl_codec::ResetColor << ": ";
printer.DisplayTime(static_cast<zx_time_t>(returned_value_));
break;
case SyscallReturnType::kUint32:
printer << "-> " << fidl_codec::Blue << static_cast<uint32_t>(returned_value_)
<< fidl_codec::ResetColor;
break;
case SyscallReturnType::kUint64:
printer << "-> " << fidl_codec::Blue << static_cast<uint64_t>(returned_value_)
<< fidl_codec::ResetColor;
break;
}
// Adds the inline output arguments (if any).
if (!inline_fields().empty()) {
printer << ' ';
printer.DisplayInline(syscall()->output_inline_members(), inline_fields());
}
printer << '\n';
printer.DisplayOutline(syscall()->output_outline_members(), outline_fields());
}
void ExceptionEvent::Write(proto::Event* dst) const {
dst->set_timestamp(timestamp());
proto::ExceptionEvent* event = dst->mutable_exception();
event->set_thread_koid(thread()->koid());
for (const auto& location : stack_frame_) {
proto::Location* proto_location = event->add_frame();
proto_location->set_path(location.path());
proto_location->set_line(location.line());
proto_location->set_column(location.column());
proto_location->set_address(location.address());
proto_location->set_symbol(location.symbol());
}
}
void ExceptionEvent::PrettyPrint(FidlcatPrinter& printer) const {
printer.DisplayStackFrame(stack_frame_);
printer << fidl_codec::Red << "thread stopped on exception" << fidl_codec::ResetColor << '\n';
}
bool EventDecoder::DecodeAndDispatchEvent(const proto::Event& proto_event) {
switch (proto_event.Kind_case()) {
case proto::Event::kProcessLaunched: {
const proto::ProcessLaunchedEvent& content = proto_event.process_launched();
dispatcher_->AddProcessLaunchedEvent(std::make_shared<ProcessLaunchedEvent>(
proto_event.timestamp(), content.command(), content.error_message()));
return true;
}
case proto::Event::kProcessMonitored: {
const proto::ProcessMonitoredEvent& content = proto_event.process_monitored();
Process* process = dispatcher_->SearchProcess(content.process_koid());
if (process == nullptr) {
FX_LOGS(ERROR) << "Process " << content.process_koid() << " not found for event .";
return false;
}
dispatcher_->AddProcessMonitoredEvent(std::make_shared<ProcessMonitoredEvent>(
proto_event.timestamp(), process, content.error_message()));
return true;
}
case proto::Event::kStopMonitoring: {
const proto::StopMonitoringEvent& content = proto_event.stop_monitoring();
Process* process = dispatcher_->SearchProcess(content.process_koid());
if (process == nullptr) {
FX_LOGS(ERROR) << "Process " << content.process_koid() << " not found for event .";
return false;
}
dispatcher_->AddStopMonitoringEvent(
std::make_shared<StopMonitoringEvent>(proto_event.timestamp(), process));
return true;
}
case proto::Event::kInvoked: {
const proto::InvokedEvent& content = proto_event.invoked();
Thread* thread = dispatcher_->SearchThread(content.thread_koid());
if (thread == nullptr) {
FX_LOGS(ERROR) << "Thread " << content.thread_koid() << " not found for event.";
return false;
}
Syscall* syscall = dispatcher_->SearchSyscall(content.syscall());
if (syscall == nullptr) {
FX_LOGS(ERROR) << "Syscall " << content.syscall() << " not found.";
return false;
}
auto event = std::make_shared<InvokedEvent>(proto_event.timestamp(), thread, syscall);
if (!DecodeValues(event.get(), content.inline_fields(), content.inline_id_fields(),
content.outline_fields(), content.outline_id_fields(),
/*invoked=*/true)) {
return false;
}
for (int index = 0; index < content.frame_size(); ++index) {
const proto::Location& proto_location = content.frame(index);
event->stack_frame().emplace_back(proto_location.path(), proto_location.line(),
proto_location.column(), proto_location.address(),
proto_location.symbol());
}
invoked_events_.emplace(std::make_pair(invoked_events_.size(), event));
dispatcher_->AddInvokedEvent(std::move(event));
return true;
}
case proto::Event::kOutput: {
const proto::OutputEvent& content = proto_event.output();
Thread* thread = dispatcher_->SearchThread(content.thread_koid());
if (thread == nullptr) {
FX_LOGS(ERROR) << "Thread " << content.thread_koid() << " not found for event.";
return false;
}
Syscall* syscall = dispatcher_->SearchSyscall(content.syscall());
if (syscall == nullptr) {
FX_LOGS(ERROR) << "Syscall " << content.syscall() << " not found.";
return false;
}
auto invoked_event = invoked_events_.find(content.invoked_event_id());
if (invoked_event == invoked_events_.end()) {
FX_LOGS(ERROR) << "Invoked event " << content.invoked_event_id()
<< " not found for ouput event.";
return false;
}
auto event = std::make_shared<OutputEvent>(proto_event.timestamp(), thread, syscall,
content.returned_value(), invoked_event->second);
if (!DecodeValues(event.get(), content.inline_fields(), content.inline_id_fields(),
content.outline_fields(), content.outline_id_fields(),
/*invoked=*/false)) {
return false;
}
dispatcher_->AddOutputEvent(std::move(event));
return true;
}
case proto::Event::kException: {
const proto::ExceptionEvent& content = proto_event.exception();
Thread* thread = dispatcher_->SearchThread(content.thread_koid());
if (thread == nullptr) {
FX_LOGS(ERROR) << "Thread " << content.thread_koid() << " not found for event.";
return false;
}
auto event = std::make_shared<ExceptionEvent>(proto_event.timestamp(), thread);
for (int index = 0; index < content.frame_size(); ++index) {
const proto::Location& proto_location = content.frame(index);
event->stack_frame().emplace_back(proto_location.path(), proto_location.line(),
proto_location.column(), proto_location.address(),
proto_location.symbol());
}
dispatcher_->AddExceptionEvent(std::move(event));
return true;
}
default:
FX_LOGS(ERROR) << "Bad kind for event.";
return false;
}
}
bool EventDecoder::DecodeValues(
SyscallEvent* event,
const ::google::protobuf::Map<::std::string, ::fidl_codec::proto::Value>& inline_fields,
const ::google::protobuf::Map<uint32_t, ::fidl_codec::proto::Value>& inline_id_fields,
const ::google::protobuf::Map<::std::string, ::fidl_codec::proto::Value>& outline_fields,
const ::google::protobuf::Map<uint32_t, ::fidl_codec::proto::Value>& outline_id_fields,
bool invoked) {
bool ok = true;
for (const auto& proto_value : inline_fields) {
const fidl_codec::StructMember* member =
event->syscall()->SearchInlineMember(proto_value.first, invoked);
if (member == nullptr) {
FX_LOGS(ERROR) << "Member " << proto_value.first << " not found for "
<< event->syscall()->name() << '.';
ok = false;
} else {
std::unique_ptr<fidl_codec::Value> value =
fidl_codec::DecodeValue(dispatcher_->loader(), proto_value.second, member->type());
if (value == nullptr) {
ok = false;
} else {
event->AddInlineField(member, std::move(value));
}
}
}
for (const auto& proto_value : inline_id_fields) {
const fidl_codec::StructMember* member =
event->syscall()->SearchInlineMember(proto_value.first, invoked);
if (member == nullptr) {
FX_LOGS(ERROR) << "Member " << proto_value.first << " not found for "
<< event->syscall()->name() << '.';
ok = false;
} else {
std::unique_ptr<fidl_codec::Value> value =
fidl_codec::DecodeValue(dispatcher_->loader(), proto_value.second, member->type());
if (value == nullptr) {
ok = false;
} else {
event->AddInlineField(member, std::move(value));
}
}
}
for (const auto& proto_value : outline_fields) {
const fidl_codec::StructMember* member =
event->syscall()->SearchOutlineMember(proto_value.first, invoked);
if (member == nullptr) {
FX_LOGS(ERROR) << "Member " << proto_value.first << " not found for "
<< event->syscall()->name() << '.';
ok = false;
} else {
std::unique_ptr<fidl_codec::Value> value =
fidl_codec::DecodeValue(dispatcher_->loader(), proto_value.second, member->type());
if (value == nullptr) {
ok = false;
} else {
event->AddOutlineField(member, std::move(value));
}
}
}
for (const auto& proto_value : outline_id_fields) {
const fidl_codec::StructMember* member =
event->syscall()->SearchOutlineMember(proto_value.first, invoked);
if (member == nullptr) {
FX_LOGS(ERROR) << "Member " << proto_value.first << " not found for "
<< event->syscall()->name() << '.';
ok = false;
} else {
std::unique_ptr<fidl_codec::Value> value =
fidl_codec::DecodeValue(dispatcher_->loader(), proto_value.second, member->type());
if (value == nullptr) {
ok = false;
} else {
event->AddOutlineField(member, std::move(value));
}
}
}
return ok;
}
} // namespace fidlcat