blob: 7c52a46201584d74827931471b602b0ccb44b51e [file] [log] [blame]
// Copyright 2019 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/inference.h"
#include <zircon/processargs.h>
#include <zircon/types.h>
#include <ios>
#include <ostream>
#include "src/lib/fidl_codec/printer.h"
#include "tools/fidlcat/lib/syscall_decoder.h"
#include "tools/fidlcat/lib/syscall_decoder_dispatcher.h"
namespace fidlcat {
void Inference::CreateHandleInfo(int64_t timestamp, zx_koid_t thread_koid, zx_handle_t handle) {
Thread* thread = dispatcher_->SearchThread(thread_koid);
FX_DCHECK(thread != nullptr);
dispatcher_->CreateHandleInfo(thread, handle, timestamp, /*startup=*/false);
}
bool Inference::NeedsToLoadHandleInfo(int64_t timestamp, zx_koid_t tid, zx_handle_t handle) const {
Thread* thread = dispatcher_->SearchThread(tid);
FX_DCHECK(thread != nullptr);
HandleInfo* handle_info = thread->process()->SearchHandleInfo(handle);
if (handle_info == nullptr) {
handle_info = dispatcher_->CreateHandleInfo(thread, handle, timestamp, /*startup=*/false);
}
return handle_info->koid() == ZX_KOID_INVALID;
}
// This is the first function which is intercepted. This gives us information about
// all the handles an application have at startup. However, for directory handles,
// we don't have the name of the directory.
void Inference::ExtractHandleInfos(int64_t timestamp, SyscallDecoder* decoder) {
constexpr int kNhandles = 0;
constexpr int kHandles = 1;
constexpr int kHandleInfo = 2;
// Get the values which have been harvest by the debugger using they argument number.
uint32_t nhandles = decoder->ArgumentValue(kNhandles);
const zx_handle_t* handles =
reinterpret_cast<const zx_handle_t*>(decoder->ArgumentContent(Stage::kEntry, kHandles));
const uint32_t* handle_info =
reinterpret_cast<const zx_handle_t*>(decoder->ArgumentContent(Stage::kEntry, kHandleInfo));
// Get the information about all the handles.
// The meaning of handle info is described in zircon/system/public/zircon/processargs.h
for (uint32_t handle = 0; handle < nhandles; ++handle) {
if (handles[handle] != 0) {
dispatcher_->CreateHandleInfo(decoder->fidlcat_thread(), handles[handle], timestamp,
/*startup=*/true);
uint32_t type = PA_HND_TYPE(handle_info[handle]);
switch (type) {
case PA_FD:
AddInferredHandleInfo(decoder->fidlcat_thread()->process()->koid(), handles[handle], "fd",
PA_HND_ARG(handle_info[handle]), "");
break;
case PA_DIRECTORY_REQUEST:
AddInferredHandleInfo(decoder->fidlcat_thread()->process()->koid(), handles[handle],
"directory-request", "/", "");
break;
default:
AddInferredHandleInfo(decoder->fidlcat_thread()->process()->koid(), handles[handle],
type);
break;
}
}
}
}
// This is the second function which is intercepted. This gives us information about
// all the handles which have not been used by processargs_extract_handles.
// This only adds information about directories.
void Inference::LibcExtensionsInit(int64_t timestamp, SyscallDecoder* decoder) {
constexpr int kHandleCount = 0;
constexpr int kHandles = 1;
constexpr int kHandleInfo = 2;
constexpr int kNameCount = 3;
constexpr int kNames = 4;
// Get the values which have been harvest by the debugger using they argument number.
uint32_t handle_count = decoder->ArgumentValue(kHandleCount);
const zx_handle_t* handles =
reinterpret_cast<const zx_handle_t*>(decoder->ArgumentContent(Stage::kEntry, kHandles));
const uint32_t* handle_info =
reinterpret_cast<const zx_handle_t*>(decoder->ArgumentContent(Stage::kEntry, kHandleInfo));
uint32_t name_count = decoder->ArgumentValue(kNameCount);
const uint64_t* names =
reinterpret_cast<const uint64_t*>(decoder->ArgumentContent(Stage::kEntry, kNames));
// Get the information about the remaining handles.
// The meaning of handle info is described in zircon/system/public/zircon/processargs.h
for (uint32_t handle = 0; handle < handle_count; ++handle) {
if (handles[handle] != 0) {
dispatcher_->CreateHandleInfo(decoder->fidlcat_thread(), handles[handle], timestamp,
/*startup=*/true);
uint32_t type = PA_HND_TYPE(handle_info[handle]);
switch (type) {
case PA_NS_DIR: {
uint32_t index = PA_HND_ARG(handle_info[handle]);
AddInferredHandleInfo(
decoder->fidlcat_thread()->process()->koid(), handles[handle], "dir",
(index < name_count) ? reinterpret_cast<const char*>(
decoder->BufferContent(Stage::kEntry, names[index]))
: "",
"");
break;
}
case PA_FD:
AddInferredHandleInfo(decoder->fidlcat_thread()->process()->koid(), handles[handle], "fd",
PA_HND_ARG(handle_info[handle]), "");
break;
case PA_DIRECTORY_REQUEST:
AddInferredHandleInfo(decoder->fidlcat_thread()->process()->koid(), handles[handle],
"directory-request", "/", "");
break;
default:
AddInferredHandleInfo(decoder->fidlcat_thread()->process()->koid(), handles[handle],
type);
break;
}
}
}
}
void Inference::InferMessage(const OutputEvent* event,
const fidl_codec::semantic::MethodSemantic* semantic,
fidl_codec::semantic::ContextType context_type) {
if (semantic == nullptr) {
return;
}
const fidl_codec::HandleValue* handle_value = event->invoked_event()->GetHandleValue(
event->syscall()->SearchInlineMember("handle", /*invoked=*/true));
if (handle_value->handle().handle != ZX_HANDLE_INVALID) {
dispatcher_->CreateHandleInfo(event->thread(), handle_value->handle().handle,
event->timestamp(),
/*startup=*/false);
const fidl_codec::semantic::InferredHandleInfo* inferred_handle_info =
GetInferredHandleInfo(event->thread()->process()->koid(), handle_value->handle().handle);
if (inferred_handle_info == nullptr) {
AddInferredHandleInfo(event->thread()->process()->koid(), handle_value->handle().handle,
"channel", next_channel_++, "");
}
const fidl_codec::FidlMessageValue* sent = event->invoked_event()->GetMessage();
const fidl_codec::FidlMessageValue* received = event->GetMessage();
const fidl_codec::PayloadableValue* request = nullptr;
const fidl_codec::PayloadableValue* response = nullptr;
switch (context_type) {
case fidl_codec::semantic::ContextType::kRead:
if (received != nullptr) {
request = received->decoded_request();
response = received->decoded_response();
}
break;
case fidl_codec::semantic::ContextType::kWrite:
if (sent != nullptr) {
request = sent->decoded_request();
response = sent->decoded_response();
}
break;
case fidl_codec::semantic::ContextType::kCall:
if (sent != nullptr) {
request = sent->decoded_request();
response = received->decoded_response();
}
break;
}
fidl_codec::semantic::AssignmentSemanticContext context(
this, event->thread()->process()->koid(), event->thread()->koid(),
handle_value->handle().handle, context_type, request, response, event->timestamp());
semantic->ExecuteAssignments(&context);
}
}
void Inference::ZxChannelCreate(const OutputEvent* event) {
const fidl_codec::HandleValue* out0 =
event->GetHandleValue(event->syscall()->SearchInlineMember("out0", /*invoked=*/false));
FX_DCHECK(out0 != nullptr);
const fidl_codec::HandleValue* out1 =
event->GetHandleValue(event->syscall()->SearchInlineMember("out1", /*invoked=*/false));
FX_DCHECK(out1 != nullptr);
if ((out0->handle().handle != ZX_HANDLE_INVALID) &&
(out1->handle().handle != ZX_HANDLE_INVALID)) {
dispatcher_->CreateHandleInfo(event->thread(), out0->handle().handle, event->timestamp(),
/*startup=*/false);
dispatcher_->CreateHandleInfo(event->thread(), out1->handle().handle, event->timestamp(),
/*startup=*/false);
// Provides the minimal semantic for both handles (that is they are channels).
AddInferredHandleInfo(event->thread()->process()->koid(), out0->handle().handle, "channel",
next_channel_++, "");
AddInferredHandleInfo(event->thread()->process()->koid(), out1->handle().handle, "channel",
next_channel_++, "");
// Links the two channels.
AddLinkedHandles(event->thread()->process()->koid(), out0->handle().handle,
out1->handle().handle);
}
}
void Inference::ZxPortCreate(const OutputEvent* event) {
const fidl_codec::HandleValue* out =
event->GetHandleValue(event->syscall()->SearchInlineMember("out", /*invoked=*/false));
FX_DCHECK(out != nullptr);
if (out->handle().handle != ZX_HANDLE_INVALID) {
dispatcher_->CreateHandleInfo(event->thread(), out->handle().handle, event->timestamp(),
/*startup=*/false);
// Provides the minimal semantic for the handle (that is it's a port).
AddInferredHandleInfo(event->thread()->process()->koid(), out->handle().handle, "port",
next_port_++, "");
}
}
void Inference::ZxTimerCreate(const OutputEvent* event) {
const fidl_codec::HandleValue* out =
event->GetHandleValue(event->syscall()->SearchInlineMember("out", /*invoked=*/false));
FX_DCHECK(out != nullptr);
if (out->handle().handle != ZX_HANDLE_INVALID) {
dispatcher_->CreateHandleInfo(event->thread(), out->handle().handle, event->timestamp(),
/*startup=*/false);
// Provides the minimal semantic for the handle (that is it's a timer).
AddInferredHandleInfo(event->thread()->process()->koid(), out->handle().handle, "timer",
next_timer_++, "");
}
}
} // namespace fidlcat