277 lines
8.6 KiB
C++
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;
|
|
}
|
|
}
|