actsim-cluster-agent/src/actsim_agent/log_parser.cpp

277 lines
8.6 KiB
C++

/*************************************************************************
*
* 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 <regex>
#include "util.h"
#include "log_parser.hpp"
#include <cstdint>
#include <vector>
#include <string>
LogParser::LogParser(std::unique_ptr<DBSimOutputArtifact>& artifact, std::shared_ptr<pl::SimOutputArtifact> 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<DBSimOutputArtifact>& 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;
}
}