| // 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_; } | 
 |   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_; | 
 |   const 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::InterfaceMethod* method) : method_(method) {} | 
 |  | 
 |   const fidl_codec::InterfaceMethod* 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::InterfaceMethod* 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::Interface* interface) : interface_(interface) {} | 
 |  | 
 |   const fidl_codec::Interface* 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::InterfaceMethod* 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::Interface* 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::Interface*, 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::Interface* 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::Interface*, 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) : process_(process), koid_(koid) {} | 
 |  | 
 |   Process* process() const { return process_; } | 
 |   zx_koid_t koid() const { return koid_; } | 
 |  | 
 |  private: | 
 |   Process* const process_; | 
 |   const zx_koid_t koid_; | 
 | }; | 
 |  | 
 | // 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_; } | 
 |  | 
 |   // 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_; } | 
 |  | 
 |  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_ |