diff --git a/luprex/core/cpp/driver-common.cpp b/luprex/core/cpp/driver-common.cpp index f642e851..02a4505b 100644 --- a/luprex/core/cpp/driver-common.cpp +++ b/luprex/core/cpp/driver-common.cpp @@ -474,10 +474,10 @@ public: drv::ReplayPlayer player; player.open_logfile(fn); while (true) { - drv::ReplayPlayer::Error err = player.step(); - if (err != drv::ReplayPlayer::ERR_NONE) { - player.print_error(std::cerr); - return (err != drv::ReplayPlayer::ERR_LOGFILE_EOF) ? 1 : 0; + drv::ReplayPlayer::Status st = player.step(); + if (st != drv::ReplayPlayer::ST_REPLAYING) { + player.print_status(std::cerr); + return (st == drv::ReplayPlayer::ST_CLEAN_EXIT) ? 0 : 1; } } } @@ -567,6 +567,9 @@ public: SSL_CTX_free(ssl_ctx_with_root_certs_); SSL_CTX_free(ssl_ctx_with_server_certs_); DrivenEngine::set(nullptr); + + recorder_.clean_exit(); + return 0; } }; diff --git a/luprex/core/cpp/driver-util.cpp b/luprex/core/cpp/driver-util.cpp index dd85278e..096f6bfc 100644 --- a/luprex/core/cpp/driver-util.cpp +++ b/luprex/core/cpp/driver-util.cpp @@ -44,6 +44,7 @@ std::vector parse_control_lst(std::string_view ctrl) { enum DrvAction { CREATE_ENGINE, + CLEAN_EXIT, DRV_CLEAR_NEW_OUTGOING, DRV_SENT_OUTGOING, DRV_RECV_INCOMING, @@ -55,9 +56,10 @@ enum DrvAction { DRV_INVOKE_EVENT_UPDATE }; -static const char *action_string(DrvAction act) { +inline static const char *action_string(DrvAction act) { switch(act) { case CREATE_ENGINE: return "CREATE_ENGINE"; + case CLEAN_EXIT: return "CLEAN_EXIT"; case DRV_CLEAR_NEW_OUTGOING: return "DRV_CLEAR_NEW_OUTGOING"; case DRV_SENT_OUTGOING: return "DRV_SENT_OUTGOING"; case DRV_RECV_INCOMING: return "DRV_RECV_INCOMING"; @@ -71,7 +73,6 @@ static const char *action_string(DrvAction act) { } } - static void wlog_uint8(std::ofstream &s, uint8_t v) { s.put((char)v); } @@ -104,7 +105,7 @@ static void wlog_string(std::ofstream &s, std::string_view v) { } static void wlog_cmd_hash(std::ofstream &s, DrvAction act, uint32_t hash) { - std::cerr << "Logging " << action_string(act) << " " << hash << std::endl; + // std::cerr << "Logging " << action_string(act) << " " << hash << std::endl; wlog_uint8(s, act); wlog_uint32(s, hash); } @@ -158,7 +159,7 @@ std::string_view rlog_string(std::ifstream &s, char *rlog_buf) { } ReplayPlayer::ReplayPlayer() { - error_ = ERR_NONE; + status_ = ST_REPLAYING; buf_.reset(new char[RLOG_BUFSIZE]); } @@ -185,25 +186,26 @@ bool ReplayRecorder::open_logfile(const char *fn) { } -ReplayPlayer::Error ReplayPlayer::step() { - if (error_ != ERR_NONE) return error_; +ReplayPlayer::Status ReplayPlayer::step() { + if (status_ != ST_REPLAYING) return status_; uint8_t code = rlog_uint8(f_); if (f_.eof()) { - set_error(ERR_LOGFILE_EOF); - return error_; + set_status(ST_LOGFILE_ENDS_ABRUPTLY); + return status_; } int hash = rlog_uint32(f_); if (!f_.good()) { - set_error(ERR_LOGFILE_CORRUPT); - return error_; + set_status(ST_LOGFILE_CORRUPT); + return status_; } - std::cerr << "Executing: " << action_string(DrvAction(code)) << " " << eng::memhash() << std::endl; + // std::cerr << "Executing: " << action_string(DrvAction(code)) << " " << eng::memhash() << std::endl; if (hash != eng::memhash()) { - set_error(ERR_NONDERMINISTIC); - return error_; + set_status(ST_NONDERMINISTIC); + return status_; } switch (code) { case CREATE_ENGINE: create_engine(); break; + case CLEAN_EXIT: clean_exit(); break; case DRV_CLEAR_NEW_OUTGOING: drv_clear_new_outgoing(); break; case DRV_SENT_OUTGOING: drv_sent_outgoing(); break; case DRV_RECV_INCOMING: drv_recv_incoming(); break; @@ -216,33 +218,36 @@ ReplayPlayer::Error ReplayPlayer::step() { default: assert(false && "Replay Log contains invalid command."); } - return error_; + return status_; } -void ReplayPlayer::set_error(Error e) { - error_ = e; +void ReplayPlayer::set_status(Status e) { + status_ = e; f_.close(); f_.clear(); } -void ReplayPlayer::print_error(std::ostream &s) { - switch (error_) { - case ERR_NONE: +void ReplayPlayer::print_status(std::ostream &s) { + switch (status_) { + case ST_REPLAYING: s << "No errors detected: " << logfn_ << std::endl; return; - case ERR_OPEN_LOGFILE: + case ST_CLEAN_EXIT: + s << "Engine exited cleanly without errors: " << logfn_ << std::endl; + return; + case ST_ERR_OPENING_LOGFILE: s << "Could not open logfile: " << logfn_ << std::endl; return; - case ERR_LOGFILE_EOF: + case ST_LOGFILE_ENDS_ABRUPTLY: s << "Logfile reached end-of-file: " << logfn_ << std::endl; return; - case ERR_LOGFILE_CORRUPT: + case ST_LOGFILE_CORRUPT: s << "Logfile corrupt: " << logfn_ << std::endl; return; - case ERR_NONDERMINISTIC: + case ST_NONDERMINISTIC: s << "Nondeterminism detected: " << logfn_ << std::endl; return; - case ERR_CREATE_ENGINE: + case ST_COULDNT_CREATE_ENGINE: s << "Could not create engine: " << logfn_ << " " << engine_ << std::endl; return; } @@ -253,7 +258,7 @@ bool ReplayPlayer::open_logfile(const char *fn) { f_.clear(); f_.open(fn, std::ios_base::in | std::ios_base::binary); if (!f_.good()) { - set_error(ERR_OPEN_LOGFILE); + set_status(ST_ERR_OPENING_LOGFILE); return false; } return true; @@ -273,18 +278,29 @@ bool ReplayRecorder::create_engine(const char *kind) { void ReplayPlayer::create_engine() { std::string_view kind = rlog_string(f_, buf_.get()); if (!f_.good()) { - set_error(ERR_LOGFILE_CORRUPT); + set_status(ST_LOGFILE_CORRUPT); return; } engine_ = std::string(kind); e_ = DrivenEngine::make(kind); DrivenEngine::set(e_.get()); if (e_ == nullptr) { - set_error(ERR_CREATE_ENGINE); + set_status(ST_COULDNT_CREATE_ENGINE); return; } } +void ReplayRecorder::clean_exit() { + if (logging_) { + wlog_cmd_hash(f_, CLEAN_EXIT, eng::memhash()); + flush(); + } +} + +void ReplayPlayer::clean_exit() { + set_status(ST_CLEAN_EXIT); +} + void ReplayRecorder::drv_clear_new_outgoing() { if (logging_) { wlog_cmd_hash(f_, DRV_CLEAR_NEW_OUTGOING, eng::memhash()); @@ -316,16 +332,16 @@ void ReplayPlayer::drv_sent_outgoing() { int nbytes = rlog_uint16(f_); uint64_t hash = rlog_uint64(f_); if (!f_.good()) { - set_error(ERR_LOGFILE_CORRUPT); + set_status(ST_LOGFILE_CORRUPT); return; } std::string_view data = e_->drv_peek_outgoing(chid); if (nbytes > int(data.size())) { - set_error(ERR_NONDERMINISTIC); + set_status(ST_NONDERMINISTIC); return; } if (hash != SpookyHash::QkHash64(data.data(), nbytes)) { - set_error(ERR_NONDERMINISTIC); + set_status(ST_NONDERMINISTIC); return; } e_->drv_sent_outgoing(chid, nbytes); @@ -345,7 +361,7 @@ void ReplayPlayer::drv_recv_incoming() { int chid = rlog_uint16(f_); std::string_view data = rlog_string(f_, buf_.get()); if (!f_.good()) { - set_error(ERR_LOGFILE_CORRUPT); + set_status(ST_LOGFILE_CORRUPT); return; } e_->drv_recv_incoming(chid, data); @@ -365,7 +381,7 @@ void ReplayPlayer::drv_notify_close() { int chid = rlog_uint16(f_); std::string_view err = rlog_string(f_, buf_.get()); if (!f_.good()) { - set_error(ERR_LOGFILE_CORRUPT); + set_status(ST_LOGFILE_CORRUPT); return; } e_->drv_notify_close(chid, err); @@ -383,7 +399,7 @@ int ReplayRecorder::drv_notify_accept(int port) { void ReplayPlayer::drv_notify_accept() { int port = rlog_uint16(f_); if (!f_.good()) { - set_error(ERR_LOGFILE_CORRUPT); + set_status(ST_LOGFILE_CORRUPT); return; } e_->drv_notify_accept(port); @@ -415,7 +431,7 @@ void ReplayPlayer::drv_add_lua_source() { std::string fn(rlog_string(f_, buf_.get())); std::string_view data = rlog_string(f_, buf_.get()); if (!f_.good()) { - set_error(ERR_LOGFILE_CORRUPT); + set_status(ST_LOGFILE_CORRUPT); return; } e_->drv_add_lua_source(fn, data); @@ -438,17 +454,17 @@ void ReplayPlayer::drv_invoke_event_init() { std::vector argv; int argc = rlog_uint16(f_); if (!f_.good()) { - set_error(ERR_LOGFILE_CORRUPT); + set_status(ST_LOGFILE_CORRUPT); return; } if (argc > MAX_ARGC) { - set_error(ERR_LOGFILE_CORRUPT); + set_status(ST_LOGFILE_CORRUPT); return; } for (int i = 0; i < argc; i++) { std::string_view arg = rlog_string(f_, buf_.get()); if (!f_.good()) { - set_error(ERR_LOGFILE_CORRUPT); + set_status(ST_LOGFILE_CORRUPT); return; } argv.emplace_back(arg); @@ -472,7 +488,7 @@ void ReplayRecorder::drv_invoke_event_update(double clock) { void ReplayPlayer::drv_invoke_event_update() { double clock = rlog_double(f_); if (!f_.good()) { - set_error(ERR_LOGFILE_CORRUPT); + set_status(ST_LOGFILE_CORRUPT); return; } e_->drv_invoke_event_update(clock); diff --git a/luprex/core/cpp/driver-util.hpp b/luprex/core/cpp/driver-util.hpp index 1494941c..2d3f0af2 100644 --- a/luprex/core/cpp/driver-util.hpp +++ b/luprex/core/cpp/driver-util.hpp @@ -49,6 +49,11 @@ public: // 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(); } @@ -75,25 +80,27 @@ public: class ReplayPlayer { public: - enum Error { - ERR_NONE, - ERR_OPEN_LOGFILE, - ERR_LOGFILE_EOF, - ERR_LOGFILE_CORRUPT, - ERR_NONDERMINISTIC, - ERR_CREATE_ENGINE, + 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_; - Error error_; + Status status_; std::string logfn_; std::string engine_; - void set_error(Error e); + void set_status(Status e); void create_engine(); + void clean_exit(); void drv_clear_new_outgoing(); void drv_sent_outgoing(); void drv_recv_incoming(); @@ -114,16 +121,16 @@ public: // Execute a single step from the replay log. // - // Returns an error code, which is usually ERR_NONE. - // If it's anything else, display an error and stop. + // Returns a status code, which is usually ST_REPLAYING. + // If it's anything else, display the status and stop. // - Error step(); + Status step(); - // Print an error message. + // Print a status message. // - // Print a message associated with the most recent error. + // Print a message associated with the most recent status report. // - void print_error(std::ostream &s); + void print_status(std::ostream &s); }; } // namespace drv diff --git a/luprex/core/rep.log b/luprex/core/rep.log new file mode 100644 index 00000000..40bc80d4 Binary files /dev/null and b/luprex/core/rep.log differ