| // 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; |
| }; |
| |
| // 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, int64_t returned_value, |
| std::shared_ptr<InvokedEvent> invoked_event) |
| : SyscallEvent(timestamp, thread, syscall), |
| returned_value_(returned_value), |
| invoked_event_(std::move(invoked_event)) {} |
| |
| int64_t returned_value() const { return returned_value_; } |
| 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_; |
| // 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_ |