#ifndef DRIVER_UTIL_HPP #define DRIVER_UTIL_HPP #include "wrap-string.hpp" #include "wrap-vector.hpp" #include #include #include #include "drivenengine.hpp" namespace drv { void split_target(std::string_view target, std::string &cert, std::string &host, std::string &port); std::vector parse_control_lst(std::string_view ctrl); class ReplayRecorder { private: std::ofstream f_; UniqueDrivenEngine e_; bool logging_; void flush(); public: // Initialization consists of three steps: // // 1. The constructor, which creates an empty ReplayRecorder. // 2. Open the logfile for writing, if desired, using open_logfile. // 3. Make the engine, using create_engine. // // After that, you can use drv_xxx methods to send messages to the // engine. These messages will get logged if the replay log is open. // ReplayRecorder() : logging_(false) {} // Open the logfile. // // If you're going to open a logfile, you must do so before // creating the engine. Returns false if the logfile couldn't be // opened. // bool open_logfile(const char *fn); // Create the DrivenEngine. // // Returns false if the DrivenEngine couldn't be created. // bool create_engine(const char *kind); // Report to the logger that the engine is about to exit cleanly, // without any error. // void clean_exit(); // These don't need to be logged. // const eng::vector &drv_get_listen_ports() const { return e_->drv_get_listen_ports(); } const eng::vector &drv_get_new_outgoing() const { return e_->drv_get_new_outgoing(); } std::string_view drv_get_target(int chid) const { return e_->drv_get_target(chid); } bool drv_outgoing_empty(int chid) const { return e_->drv_outgoing_empty(chid); } bool drv_get_channel_released(int chid) const { return e_->drv_get_channel_released(chid); } std::string_view drv_peek_outgoing(int chid) const { return e_->drv_peek_outgoing(chid); } bool drv_get_rescan_lua_source() const { return e_->drv_get_rescan_lua_source(); } bool drv_get_stop_driver() const { return e_->drv_get_stop_driver(); } // These operations do need to be logged. // void drv_clear_new_outgoing(); void drv_sent_outgoing(int chid, int nbytes); void drv_recv_incoming(int chid, std::string_view data); void drv_notify_close(int chid, std::string_view err); int drv_notify_accept(int port); void drv_clear_lua_source(); void drv_add_lua_source(std::string_view fn, std::string_view data); void drv_invoke_event_init(int argc, char *argv[]); void drv_invoke_event_update(double clock); }; class ReplayPlayer { public: enum Status { ST_REPLAYING, ST_CLEAN_EXIT, ST_ERR_OPENING_LOGFILE, ST_LOGFILE_ENDS_ABRUPTLY, ST_LOGFILE_CORRUPT, ST_NONDERMINISTIC, ST_COULDNT_CREATE_ENGINE, }; private: std::ifstream f_; UniqueDrivenEngine e_; std::unique_ptr buf_; Status status_; std::string logfn_; std::string engine_; bool enable_stdout_; void set_status(Status e); void create_engine(); void clean_exit(); void drv_clear_new_outgoing(); void drv_sent_outgoing(); void drv_recv_incoming(); void drv_notify_close(); void drv_notify_accept(); void drv_clear_lua_source(); void drv_add_lua_source(); void drv_invoke_event_init(); void drv_invoke_event_update(); public: ReplayPlayer(); // Open the logfile for reading. // // Returns false if the logfile can't be opened. // bool open_logfile(const char *fn); // Enable stdout. // // Normally, stdout is suppressed during replay. // If you enable stdout, then the engine will print // all the same messages it did when running in the // first place. // void enable_stdout() { enable_stdout_ = true; } // Execute a single step from the replay log. // // Returns a status code, which is usually ST_REPLAYING. // If it's anything else, display the status and stop. // Status step(); // Print a status message. // // Print a message associated with the most recent status report. // void print_status(std::ostream &s); }; } // namespace drv #endif // DRIVER_UTIL_HPP