/************************************************************************* * * This file is part of the ACT library * * Copyright (c) 2024 Fabian Posch * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * ************************************************************************** */ #include "../../include/cluster/artifact.hpp" #include #include "util.h" #include "log_parser.hpp" #include #include #include LogParser::LogParser(std::unique_ptr& artifact, std::shared_ptr reference) : artifact(artifact) { this->reference = reference; this->has_reference = true; // get the output token timing iterator this->timing_it = reference->get_output_token_timings().begin(); this->reference_ott_end = reference->get_output_token_timings().end(); } LogParser::LogParser(std::unique_ptr& artifact) : artifact(artifact) { this->has_reference = false; }; void LogParser::parse_log(const std::string& line) { // DEBUG_PRINT("Parsing log line " + line); // check for output tokens check_token_count(line); // check for excl high constraint violations check_coding_fault(line); // check for value fault check_value_timing_fault(line); // check for glitch check_glitch(line); this->artifact->add_log_output(line); } void LogParser::parse_error([[maybe_unused]]const std::string& line) { // DEBUG_PRINT("Parsing error line " + line); // actsim actually outputs everything on stdout // Only the warnings in the beginning are on stderr. this->artifact->add_err_output(line); } void LogParser::finalize() { this->artifact->fix_size(); this->artifact->clear_logs(); /* So the only way to do this cleanly is to make sure that either - the model has not sent all tokens yet -> deadlock - or the model has sent everything but there is one missing -> token fault This has the consequence that we cannot inject close to the end of the test! This also means that we don't really know if a deadlock occurred without a reference run to go off. Only that there was a potential token count difference. */ if (has_reference) { // model has not sent all tokens yet if (artifact->get_output_token_timings().size() < reference->get_output_token_timings().size()) { std::cout << "Token count based deadlock, reference had " << reference->get_output_token_timings().size() << " dut had " << artifact->get_output_token_timings().size() << std::endl; // a deadlock must have occured artifact->set_fault_deadlock(true); failure_mode = true; DEBUG_PRINT("Deadlock detected during finalization (compared to reference)"); DEBUG_PRINT("Reference had " + std::to_string(reference->output_tokens) + " tokens, task had " + std::to_string(dut_output_tokens_) + " + " + std::to_string(output_token_difference_)); // model has sent all tokens but DUT has not sent all of them } else if (output_token_difference_ != 0) { // a token amount error has occurred artifact->set_fault_token_count(true); failure_mode = true; DEBUG_PRINT("Token count mismatch detected during finalization (compared to reference)"); } // if there is no failure condition, // we don't need to save the log //if (!failure_mode) { // artifact->clear_logs(); //} } else { // if token difference != 0: token count error if (output_token_difference_ != 0) { artifact->set_fault_token_count(true); artifact->set_output_tokens(dut_output_tokens_); DEBUG_PRINT("Token count mismatch detected during finalization."); } } } inline void LogParser::check_token_count(const std::string& line) { // difference counter should be back at 0 when log is finished // -> both model and DUT have emitted the same number of tokens const std::string model_token = "Model response received"; const std::string dut_token = "DUT response received"; if (line.find(model_token) != std::string::npos) { ++output_token_difference_; } if (line.find(dut_token) != std::string::npos) { --output_token_difference_; ++dut_output_tokens_; } } inline void LogParser::check_value_timing_fault(const std::string& line) { // simply check if the standard test failed output // is given by the scoreboard const std::string test_failed = "TEST FAILED"; if (line.find(test_failed) != std::string::npos) { artifact->set_fault_value(true); failure_mode = true; DEBUG_PRINT("Value error detected"); handle_output_token(line); return; } // if we passed the test we still need to check for timing issues const std::string test_succeeded = "TEST SUCCESS"; if (line.find(test_succeeded) != std::string::npos) { // DEBUG_PRINT("Successful output token detected"); handle_output_token(line); return; } } inline void LogParser::handle_output_token(const std::string& line) { // add the timing to the artifact auto timing = extract_timestamp(line); artifact->add_output_token_timing(timing); // check if there is also a timing error if (has_reference) { // make sure there is still a token to compare to left if (timing_it == reference_ott_end) { // there is a mismatch in tokens artifact->set_fault_token_count(true); failure_mode = true; DEBUG_PRINT("Tried to compare token timing but no reference token left."); return; } // check if the timings align if (timing != *timing_it) { // timings don't line up! artifact->set_fault_timing_deviation(true); failure_mode = true; DEBUG_PRINT("Token timing does not line up."); } // increment the iterator ++timing_it; } } inline void LogParser::check_coding_fault(const std::string& line) { // check for actsim's excl-hi warning const std::string excl_hi_violated = "WARNING: excl-hi constraint in"; if (line.find(excl_hi_violated) != std::string::npos) { artifact->set_fault_coding(true); failure_mode = true; DEBUG_PRINT("Excl-hi constraint violated"); } } bool LogParser::check_busy_deadlock() { // we allow for more than 3x the events to happen compared // to the reference run, then we assume a deadlock // if there is no reference, we have nothing to do if (!has_reference) { return false; } if (artifact->get_size() > (reference->get_size() * 3)) { failure_mode = true; DEBUG_PRINT("Busy deadlock detected, reference size is " + std::to_string(reference->get_size()) + ", ours is " + std::to_string(artifact->get_size()) ); this->artifact->set_fault_deadlock(true); std::cout << "Killing busy deadlock" << std::endl; return true; } return false; } inline void LogParser::check_glitch(const std::string& line) { // simply check if the standard glitch output is given const std::string glitch_detected = "WARNING: weak-interference on"; if (line.find(glitch_detected) != std::string::npos) { artifact->set_fault_glitch(true); failure_mode = true; DEBUG_PRINT("Glitch in interface detected"); } } inline uint32_t LogParser::extract_timestamp(const std::string& line) { // regex match the timestamp format std::regex pattern(R"(^\[\s*(\d+)\s*\])"); std::smatch match; if (std::regex_search(line, match, pattern)) { return std::stoi(match[1].str()); } else { return 0; } }