blob: 0d5c18f5123ee2fc7f29be7fc4635026960754b3 [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.
#ifndef TOOLS_FIDLCAT_LIB_EVENT_H_
#define TOOLS_FIDLCAT_LIB_EVENT_H_
#include <zircon/types.h>
#include <map>
#include <memory>
#include <vector>
#include "src/developer/debug/zxdb/client/process.h"
#include "src/lib/fidl_codec/wire_object.h"
#include "src/lib/fidl_codec/wire_types.h"
#include "src/lib/fxl/memory/weak_ptr.h"
#include "tools/fidlcat/lib/fidlcat_printer.h"
#include "tools/fidlcat/proto/session.pb.h"
namespace fidlcat {
class Event;
class Location;
class OutputEvent;
class Syscall;
class SyscallDecoder;
class SyscallDisplayDispatcher;
class Thread;
class HandleSession {
public:
HandleSession() = default;
const OutputEvent* creation_event() const { return creation_event_; }
void set_creation_event(const OutputEvent* creation_event) { creation_event_ = creation_event; }
const std::vector<const Event*>& events() const { return events_; }
void add_event(const Event* event) { events_.emplace_back(event); }
const OutputEvent* close_event() const { return close_event_; }
void set_close_event(const OutputEvent* close_event) { close_event_ = close_event; }
private:
// The event which created the session.
const OutputEvent* creation_event_ = nullptr;
// All the regular events which use the handle during the session.
std::vector<const Event*> events_;
// The event which closed the session.
const OutputEvent* close_event_ = nullptr;
};
class HandleInfo {
public:
HandleInfo(Thread* thread, uint32_t handle, int64_t creation_time, bool startup)
: thread_(thread), handle_(handle), creation_time_(creation_time), startup_(startup) {}
Thread* thread() const { return thread_; }
uint32_t handle() const { return handle_; }
int64_t creation_time() const { return creation_time_; }
bool startup() const { return startup_; }
void set_startup() { startup_ = true; }
zx_obj_type_t object_type() const { return object_type_; }
void set_object_type(zx_obj_type_t object_type) { object_type_ = object_type; }
zx_rights_t rights() const { return rights_; }
void set_rights(zx_rights_t rights) { rights_ = rights; }
zx_koid_t koid() const { return koid_; }
void set_koid(zx_koid_t koid) { koid_ = koid; }
const std::vector<std::unique_ptr<HandleSession>>& sessions() const { return sessions_; }
void AddCreationEvent(const OutputEvent* creation_event) {
auto session = std::make_unique<HandleSession>();
session->set_creation_event(creation_event);
sessions_.emplace_back(std::move(session));
}
void AddEvent(const Event* event) {
if (!sessions_.empty()) {
HandleSession* session = sessions_.back().get();
if (session->close_event() == nullptr) {
session->add_event(event);
return;
}
}
auto session = std::make_unique<HandleSession>();
session->add_event(event);
sessions_.emplace_back(std::move(session));
}
void AddCloseEvent(const OutputEvent* close_event) {
if (!sessions_.empty()) {
HandleSession* session = sessions_.back().get();
if (session->close_event() == nullptr) {
session->set_close_event(close_event);
return;
}
}
auto session = std::make_unique<HandleSession>();
session->set_close_event(close_event);
sessions_.emplace_back(std::move(session));
}
private:
Thread* const thread_;
const uint32_t handle_;
const int64_t creation_time_;
bool startup_;
// The object type for the handle.
zx_obj_type_t object_type_ = ZX_OBJ_TYPE_NONE;
// The rights for the handle.
zx_rights_t rights_ = 0;
// The unique id assigned by the kernel to the object referenced by the handle.
zx_koid_t koid_ = ZX_KOID_INVALID;
// All the sessions for the handle. Usually, it will contain at most one session. However, some
// processes send a handle to themself (some tests, for example, use this feature). In that case,
// we will have several sessions for one handle.
std::vector<std::unique_ptr<HandleSession>> sessions_;
};
// A FIDL method used by one process.
class Method {
public:
explicit Method(const fidl_codec::ProtocolMethod* method) : method_(method) {}
const fidl_codec::ProtocolMethod* method() const { return method_; }
size_t event_count() const { return events_.size(); }
const std::vector<const OutputEvent*>& events() const { return events_; }
void AddEvent(const OutputEvent* event) { events_.emplace_back(event); }
private:
// The FIDL method.
const fidl_codec::ProtocolMethod* const method_;
// All the vents for this method (for one process).
std::vector<const OutputEvent*> events_;
};
// A FIDL protocol (interface) used by one process.
class Protocol {
public:
explicit Protocol(const fidl_codec::Protocol* interface) : interface_(interface) {}
const fidl_codec::Protocol* interface() const { return interface_; }
const std::map<fidl_codec::Ordinal64, std::unique_ptr<Method>>& methods() const {
return methods_;
}
uint64_t event_count() const { return event_count_; }
Method* GetMethod(fidl_codec::Ordinal64 ordinal, const fidl_codec::ProtocolMethod* method) {
auto result = methods_.find(ordinal);
if (result != methods_.end()) {
return result->second.get();
}
auto new_method = std::make_unique<Method>(method);
auto returned_value = new_method.get();
methods_.emplace(std::make_pair(ordinal, std::move(new_method)));
return returned_value;
}
void AddEvent(const OutputEvent* event, const fidl_codec::FidlMessageValue* message);
private:
// The FIDL interface.
const fidl_codec::Protocol* const interface_;
// All the methods of this interface used by one process.
std::map<fidl_codec::Ordinal64, std::unique_ptr<Method>> methods_;
// The event count for this interface for one process.
uint64_t event_count_ = 0;
};
class Process {
public:
Process(std::string_view name, zx_koid_t koid, fxl::WeakPtr<zxdb::Process> zxdb_process)
: name_(name), koid_(koid), zxdb_process_(zxdb_process) {}
const std::string& name() const { return name_; }
zx_koid_t koid() const { return koid_; }
zxdb::Process* zxdb_process() const { return zxdb_process_.get(); }
std::vector<HandleInfo*>& handle_infos() { return handle_infos_; }
std::map<uint32_t, HandleInfo*>& handle_info_map() { return handle_info_map_; }
const std::map<const fidl_codec::Protocol*, std::unique_ptr<Protocol>>& protocols() const {
return protocols_;
}
uint64_t event_count() const { return event_count_; }
void LoadHandleInfo(Inference* inference);
HandleInfo* SearchHandleInfo(uint32_t handle) const {
auto result = handle_info_map_.find(handle);
if (result == handle_info_map_.end()) {
return nullptr;
}
return result->second;
}
Protocol* GetProtocol(const fidl_codec::Protocol* interface) {
auto result = protocols_.find(interface);
if (result != protocols_.end()) {
return result->second.get();
}
auto protocol = std::make_unique<Protocol>(interface);
auto returned_value = protocol.get();
protocols_.emplace(std::make_pair(interface, std::move(protocol)));
return returned_value;
}
void AddEvent(const OutputEvent* event, const fidl_codec::FidlMessageValue* message);
private:
// The name of the process.
const std::string name_;
// The koid of the process.
const zx_koid_t koid_;
// The zxdb process for the koid.
fxl::WeakPtr<zxdb::Process> zxdb_process_;
// True if we are currently loading information about the process' handles.
bool loading_handle_info_ = false;
// True if we need to load again the info after the current load will be finished.
bool needs_to_load_handle_info_ = false;
// All the handles used by the process (first used handle first).
std::vector<HandleInfo*> handle_infos_;
// A map to quickly find a handle for a process.
std::map<uint32_t, HandleInfo*> handle_info_map_;
// All the protocols used by the process.
std::map<const fidl_codec::Protocol*, std::unique_ptr<Protocol>> protocols_;
// The count of events (read/write/call) for this process.
uint64_t event_count_ = 0;
};
inline FidlcatPrinter& operator<<(FidlcatPrinter& printer, const Process& process) {
printer << process.name() << ' ' << fidl_codec::Red << process.koid() << fidl_codec::ResetColor;
return printer;
}
class Thread {
public:
Thread(Process* process, zx_koid_t koid, bool displayed)
: process_(process), koid_(koid), displayed_(displayed) {}
Process* process() const { return process_; }
zx_koid_t koid() const { return koid_; }
bool displayed() const { return displayed_; }
private:
Process* const process_;
const zx_koid_t koid_;
const bool displayed_;
};
// Defines a location in the source (used by stack frames).
class Location {
public:
Location(const std::string& path, uint32_t line, uint32_t column, uint64_t address,
const std::string& symbol)
: path_(path), line_(line), column_(column), address_(address), symbol_(symbol) {}
const std::string& path() const { return path_; }
uint32_t line() const { return line_; }
uint32_t column() const { return column_; }
uint64_t address() const { return address_; }
const std::string& symbol() const { return symbol_; }
private:
const std::string path_;
const uint32_t line_;
const uint32_t column_;
const uint64_t address_;
const std::string symbol_;
};
class Event {
public:
explicit Event(int64_t timestamp) : timestamp_(timestamp) {}
virtual ~Event() = default;
// Timestamp in nanoseconds.
int64_t timestamp() const { return timestamp_; }
// Returns true if the event is associated to the thread.
virtual bool ForThread(Thread* thread) const { return false; }
// Method to downcast an event
virtual OutputEvent* AsOutputEvent() { return nullptr; }
// Write the content of the event into a protobuf event.
virtual void Write(proto::Event* dst) const = 0;
// Display a short version of the event (without all the details).
virtual void Display(FidlcatPrinter& printer, bool with_channel = false) const {}
private:
const int64_t timestamp_;
};
// Event which gives the result of a process launching.
class ProcessLaunchedEvent final : public Event {
public:
ProcessLaunchedEvent(int64_t timestamp, std::string_view command, std::string_view error_message)
: Event(timestamp), command_(command), error_message_(error_message) {}
const std::string& command() const { return command_; }
const std::string& error_message() const { return error_message_; }
void Write(proto::Event* dst) const override;
private:
const std::string command_;
const std::string error_message_;
};
// Event which tells that we started monitoring a process.
class ProcessMonitoredEvent final : public Event {
public:
ProcessMonitoredEvent(int64_t timestamp, Process* process, std::string_view error_message)
: Event(timestamp), process_(process), error_message_(error_message) {}
Process* process() const { return process_; }
const std::string& error_message() const { return error_message_; }
void Write(proto::Event* dst) const override;
private:
Process* const process_;
const std::string error_message_;
};
// Event which tells that we stop monitoring a process.
class StopMonitoringEvent final : public Event {
public:
StopMonitoringEvent(int64_t timestamp, Process* process) : Event(timestamp), process_(process) {}
Process* process() const { return process_; }
void Write(proto::Event* dst) const override;
private:
Process* const process_;
};
// Base classe for all events related to a thread.
class ThreadEvent : public Event {
public:
ThreadEvent(int64_t timestamp, Thread* thread) : Event(timestamp), thread_(thread) {}
Thread* thread() const { return thread_; }
bool ForThread(Thread* thread) const override { return thread == thread_; }
private:
Thread* const thread_;
};
// Base class for events related to a syscall.
class SyscallEvent : public ThreadEvent {
public:
SyscallEvent(int64_t timestamp, Thread* thread, const Syscall* syscall)
: ThreadEvent(timestamp, thread), syscall_(syscall) {}
const Syscall* syscall() const { return syscall_; }
const std::map<const fidl_codec::StructMember*, std::unique_ptr<fidl_codec::Value>>&
inline_fields() const {
return inline_fields_;
}
const std::map<const fidl_codec::StructMember*, std::unique_ptr<fidl_codec::Value>>&
outline_fields() const {
return outline_fields_;
}
void AddInlineField(const fidl_codec::StructMember* member,
std::unique_ptr<fidl_codec::Value> value) {
inline_fields_.emplace(std::make_pair(member, std::move(value)));
}
void AddOutlineField(const fidl_codec::StructMember* member,
std::unique_ptr<fidl_codec::Value> value) {
outline_fields_.emplace(std::make_pair(member, std::move(value)));
}
// Returns true if we need to load information about the handle (call to zx_object_get_info with
// ZX_INFO_HANDLE_TABLE). We need to load information about the handle if one of the handles of
// the event has an unknown koid.
bool NeedsToLoadHandleInfo(Inference* inference);
const fidl_codec::FidlMessageValue* GetMessage() const;
const fidl_codec::Value* GetValue(const fidl_codec::StructMember* member) const;
const fidl_codec::HandleValue* GetHandleValue(const fidl_codec::StructMember* member) const;
HandleInfo* GetHandleInfo(const fidl_codec::StructMember* member) const;
private:
const Syscall* const syscall_;
std::map<const fidl_codec::StructMember*, std::unique_ptr<fidl_codec::Value>> inline_fields_;
std::map<const fidl_codec::StructMember*, std::unique_ptr<fidl_codec::Value>> outline_fields_;
};
// Event that represents the arguments of a syscall (When the syscall is called).
class InvokedEvent final : public SyscallEvent {
public:
InvokedEvent(int64_t timestamp, Thread* thread, const Syscall* syscall)
: SyscallEvent(timestamp, thread, syscall) {}
uint32_t id() const { return id_; }
void set_id(uint32_t id) { id_ = id; }
const std::vector<Location>& stack_frame() const { return stack_frame_; }
std::vector<Location>& stack_frame() { return stack_frame_; }
bool displayed() const { return displayed_; }
void set_displayed() { displayed_ = true; }
HandleInfo* handle_info() const { return handle_info_; }
// For syscalls which read/write a FIDL message, computes the handle used to read/write the
// message.
void ComputeHandleInfo(SyscallDisplayDispatcher* dispatcher);
void Write(proto::Event* dst) const override;
void PrettyPrint(FidlcatPrinter& printer) const;
public:
uint32_t id_ = 0;
std::vector<Location> stack_frame_;
bool displayed_ = false;
// For syscalls which read/write a FIDL message, the handle used to read/write the message.
HandleInfo* handle_info_ = nullptr;
};
// The result of a function that returns >64 bits.
using result128_t = struct {
int64_t first_word;
uint64_t second_word;
};
// Represents the return of a value from the syscall. Typically, this is an
// integer status code, but zx_system_get_version_string returns a zx_string_view_t, so
// we provide something generic.
class ReturnEvent final : public SyscallEvent {
public:
ReturnEvent(int64_t timestamp, Thread* thread, const Syscall* syscall,
int64_t scalar_return_value)
: SyscallEvent(timestamp, thread, syscall),
scalar_return_value_({.first_word = scalar_return_value, .second_word = 0}) {}
ReturnEvent(int64_t timestamp, Thread* thread, const Syscall* syscall,
result128_t scalar_return_value)
: SyscallEvent(timestamp, thread, syscall), scalar_return_value_(scalar_return_value) {}
void SetReturnValue(std::unique_ptr<fidl_codec::Value> value) {
return_value_ = std::move(value);
}
void Write(proto::Event* dst) const override;
const fidl_codec::Value* GetValue() const { return return_value_.get(); }
void SetValue(SyscallDecoder* decoder);
// Returns a fidl_codec::Type corresponding to the return type of the syscall.
std::unique_ptr<fidl_codec::Type> Type();
// Returns the first 64 bits of the raw return value, which is typically the
// result status of the function.
uint64_t return_code() const { return scalar_return_value_.first_word; }
private:
const result128_t scalar_return_value_;
std::unique_ptr<fidl_codec::Value> return_value_;
};
// Event that represents the return value and out parameters when a syscall returns.
class OutputEvent final : public SyscallEvent {
public:
OutputEvent(int64_t timestamp, Thread* thread, const Syscall* syscall,
std::shared_ptr<ReturnEvent> return_event,
std::shared_ptr<InvokedEvent> invoked_event)
: SyscallEvent(timestamp, thread, syscall),
returned_value_(return_event),
invoked_event_(std::move(invoked_event)) {}
const fidl_codec::Value* returned_value() const { return returned_value_->GetValue(); }
const InvokedEvent* invoked_event() const { return invoked_event_.get(); }
OutputEvent* AsOutputEvent() override { return this; }
void Write(proto::Event* dst) const override;
void Display(FidlcatPrinter& printer, bool with_channel) const override;
void PrettyPrint(FidlcatPrinter& printer) const;
private:
// const int64_t returned_value_;
std::shared_ptr<ReturnEvent> returned_value_;
// The event which describes the input arguments for this syscall output event.
std::shared_ptr<InvokedEvent> invoked_event_;
};
// Event that represents an exception.
class ExceptionEvent final : public ThreadEvent {
public:
ExceptionEvent(int64_t timestamp, Thread* thread) : ThreadEvent(timestamp, thread) {}
const std::vector<Location>& stack_frame() const { return stack_frame_; }
std::vector<Location>& stack_frame() { return stack_frame_; }
void Write(proto::Event* dst) const override;
void PrettyPrint(FidlcatPrinter& printer) const;
private:
std::vector<Location> stack_frame_;
};
// Class to decode events from protobuf.
class EventDecoder {
public:
explicit EventDecoder(SyscallDisplayDispatcher* dispatcher) : dispatcher_(dispatcher) {}
SyscallDisplayDispatcher* dispatcher() const { return dispatcher_; }
// Decodes a protobuf event and dispatch it.
bool DecodeAndDispatchEvent(const proto::Event& proto_event);
private:
// Decode the values for a syscall event.
bool 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);
// Dispatcher used to decode the events.
SyscallDisplayDispatcher* dispatcher_;
// Map of all invoked events already decoded. Used to associate the invoked event to an output
// event.
std::map<uint32_t, std::shared_ptr<InvokedEvent>> invoked_events_;
};
} // namespace fidlcat
#endif // TOOLS_FIDLCAT_LIB_EVENT_H_