Skip to content

Commit b23b920

Browse files
authored
Merge pull request #33 from sillydan1/patch/bug-hunting
Patch/bug hunting
2 parents a791f06 + a2d6d4a commit b23b920

File tree

10 files changed

+504
-75
lines changed

10 files changed

+504
-75
lines changed

src/runtime/TTA.cpp

Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,34 @@ TTA operator<<(const TTA& aa, const TTA::StateChange& b) {
3939
return a;
4040
}
4141

42+
#define FUNC_IMPL(a, func, b, res) std::visit(overload{[&b,&res](auto&& x){std::visit(overload{[&x,&res](auto&& y){res = func(x,y);}}, static_cast<TTASymbol_t>(b));}}, static_cast<TTASymbol_t>(a))
43+
template<typename T1, typename T2>
44+
auto t_ee(const T1&, const T2&) {
45+
return false;
46+
}
47+
auto t_ee(const bool& a, const bool& b) {return a == b;}
48+
auto t_ee(const int& a, const int& b) {return a == b;}
49+
auto t_ee(const long& a, const long& b) {return a == b;}
50+
auto t_ee(const int& a, const long& b) {return a == b;}
51+
auto t_ee(const long& a, const int& b) {return a == b;}
52+
auto t_ee(const int& a, const float& b) {return a == b;}
53+
auto t_ee(const long& a, const float& b) {return a == b;}
54+
auto t_ee(const float& a, const int& b) {return a == b;}
55+
auto t_ee(const float& a, const long& b) {return a == b;}
56+
auto t_ee(const float& a, const float& b) {return a == b;}
57+
auto t_ee(const TTATimerSymbol& a, const TTATimerSymbol& b) {return a.current_value == b.current_value;}
58+
auto t_ee(const std::string& a, const std::string& b) {return a == b;}
59+
bool operator==(const TTASymbol_t& a, const TTASymbol_t& b) {
60+
bool retVal = a.index() == b.index();
61+
if(!retVal)
62+
return false;
63+
FUNC_IMPL(a, t_ee, b, retVal);
64+
return retVal;
65+
}
66+
bool operator!=(const TTASymbol_t& a, const TTASymbol_t& b) {
67+
return !(a == b);
68+
}
69+
4270
TTASymbol_t TTASymbolValueFromTypeAndValueStrings(const std::string& typestr, const std::string& valuestr) {
4371
return PopulateValueFromString(TTASymbolTypeFromString(typestr), valuestr);
4472
}
@@ -178,6 +206,8 @@ std::optional<StateMultiChoice> TTA::GetChangesFromEdge(const TTA::Edge& choice,
178206
changes.component = currentComponent;
179207
bool DoesUpdateInfluenceOverlap = AccumulateUpdateInfluences(choice, changes.symbolsToChange, overlappingComponents, currentComponent);
180208
outInfluenceOverlap |= DoesUpdateInfluenceOverlap;
209+
if(DoesUpdateInfluenceOverlap)
210+
return {};
181211

182212
for(auto& symbolChange : changes.symbolsToChange)
183213
changes.symbolChanges.push_back(symbolChange.second);
@@ -186,13 +216,23 @@ std::optional<StateMultiChoice> TTA::GetChangesFromEdge(const TTA::Edge& choice,
186216
}
187217

188218
void TTA::WarnAboutComponentOverlap(component_overlap_t& overlappingComponents) const {
189-
spdlog::debug("Overlapping Components: (Tick#: {0})", tickCount);
190-
for (auto& componentCollection : overlappingComponents) {
191-
if (componentCollection.second.updates.size() > 1) {
192-
for (auto& compname : componentCollection.second.updates)
193-
spdlog::debug("{0}", compname.first);
194-
}
219+
std::vector<std::string> overlapping_errors{};
220+
for(auto it = overlappingComponents.begin(), end = overlappingComponents.end(); it != end; it = overlappingComponents.equal_range(it->first).second) {
221+
auto iter = overlappingComponents.equal_range(it->first);
222+
const std::string& compName = iter.first->second.componentName;
223+
for(auto& x = iter.first; x != iter.second; x++) {
224+
if(x->second.componentName != compName) {
225+
overlapping_errors.emplace_back(x->second.componentName + " and " + compName + " disagree on: " + it->first);
226+
break;
227+
}
228+
}
195229
}
230+
if(overlapping_errors.empty())
231+
return;
232+
233+
spdlog::trace("Overlapping Updates:");
234+
for(auto& err : overlapping_errors)
235+
spdlog::trace(err);
196236
}
197237

198238
TokenMap TTA::GetSymbolChangesAsMap(std::vector<UpdateExpression> &symbolChanges) const {
@@ -226,29 +266,11 @@ std::vector<TTA::StateChange> TTA::GetNextTickStates(const nondeterminism_strate
226266
auto enabledEdges = component.second.GetEnabledEdges(symbols); // TODO: Take Interesting variables into account
227267
if(!enabledEdges.empty()) {
228268
bool hasNondeterminism = WarnIfNondeterminism(enabledEdges, component.first);
229-
if(!hasNondeterminism) {
230-
// Simply pick the edge and apply it to the shared changes
231-
if(strategy != nondeterminism_strategy_t::VERIFICATION) {
232-
auto &pickedEdge = PickEdge(enabledEdges, strategy);
233-
auto changes = GetChangesFromEdge(pickedEdge, updateInfluenceOverlapGlobal, overlappingComponents, component.first);
234-
if (changes.has_value()) sharedChanges.Merge(changes.value());
235-
ApplyComponentLocation(sharedChanges.currentLocations, component, pickedEdge);
236-
} else {
237-
for(auto& edge : enabledEdges) {
238-
auto changes = GetChangesFromEdge(edge, updateInfluenceOverlapGlobal, overlappingComponents, component.first);
239-
if(changes.has_value()) {
240-
sharedChanges.Merge(changes.value());
241-
ApplyComponentLocation(sharedChanges.currentLocations, component, edge);
242-
}
243-
}
244-
}
245-
} else {
246-
for(auto& edge : enabledEdges) {
247-
auto changes = GetChangesFromEdge(edge, updateInfluenceOverlapGlobal, overlappingComponents, component.first);
248-
if(changes.has_value()) {
249-
ApplyComponentLocation(changes->currentLocations, component, edge);
250-
choiceChanges.push_back(changes.value());
251-
}
269+
for(auto& edge : enabledEdges) {
270+
auto changes = GetChangesFromEdge(edge, updateInfluenceOverlapGlobal, overlappingComponents, component.first);
271+
if(changes.has_value()) {
272+
ApplyComponentLocation(changes->currentLocations, component, edge);
273+
choiceChanges.push_back(changes.value());
252274
}
253275
}
254276
}
@@ -281,8 +303,10 @@ bool TTA::WarnIfNondeterminism(const std::vector<Edge>& edges, const std::string
281303

282304
std::string TTA::GetCurrentStateString() const {
283305
std::stringstream ss{}; ss << "{";
284-
for(auto& component : components) ss<<"\""<<component.first<<"\""<<": "<<"\""<<component.second.currentLocation.identifier<<"\",";
285-
for(auto& symbol : symbols.map()) ss<<"\""<<symbol.first<<"\""<<": "<<"\""<<symbol.second.str()<<"\",";
306+
for(auto& component : components)
307+
ss<<"\""<<component.first<<"\""<<": "<<"\""<<component.second.currentLocation.identifier<<"\",";
308+
for(auto& symbol : symbols.map())
309+
ss<<"\""<<symbol.first<<"\""<<": "<<"\""<<symbol.second.str()<<"\",";
286310
ss << R"("OBJECT_END":"true"})"; // This is just a bad way of ending a json object.
287311
return ss.str();
288312
}
@@ -385,21 +409,21 @@ bool AreRightHandSidesIndempotent(const std::map<std::string, std::vector<std::p
385409
bool TTA::AccumulateUpdateInfluences(const TTA::Edge& pickedEdge, std::multimap<std::string, UpdateExpression>& symbolsToChange, component_overlap_t& overlappingComponents, const std::string& currentComponent) const {
386410
bool updateInfluenceOverlap = false;
387411
for(auto& expr : pickedEdge.updateExpressions) {
412+
bool overlap_in_this_expr = false;
388413
auto it = overlappingComponents.find(expr.lhs);
389414
if(it != overlappingComponents.end()) {
390415
auto range = overlappingComponents.equal_range(expr.lhs);
391416
for(auto iter = range.first; iter != range.second; iter++) {
392-
if(iter->second.componentName == currentComponent) {
393-
symbolsToChange.insert({ expr.lhs, expr });
417+
if(iter->second.componentName == currentComponent)
394418
continue;
395-
}
396-
// TODO: Check for Indempotence!
419+
// TODO: Check for Idempotence!
397420
spdlog::debug("Overlapping update influence on evaluation of update on edge {0} --> {1}. "
398421
"Variable '{2}' is already being written to in this tick! - For more info, run with higher verbosity",
399422
TTAResugarizer::Resugar(pickedEdge.sourceLocation.identifier),
400423
TTAResugarizer::Resugar(pickedEdge.targetLocation.identifier),
401424
TTAResugarizer::Resugar(expr.lhs)); // TODO: Idempotent variable assignment
402425
updateInfluenceOverlap = true;
426+
overlap_in_this_expr = true;
403427
}
404428
}
405429
component_overlap c{
@@ -410,6 +434,8 @@ bool TTA::AccumulateUpdateInfluences(const TTA::Edge& pickedEdge, std::multimap<
410434
pickedEdge.targetLocation.identifier), expr.rhs)}
411435
};
412436
overlappingComponents.emplace(expr.lhs, std::move(c));
437+
if(overlap_in_this_expr)
438+
continue;
413439
symbolsToChange.insert({ expr.lhs, expr });
414440
}
415441
return updateInfluenceOverlap;

src/runtime/TTA.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,12 @@ struct TTA {
7272

7373
std::vector<Edge> GetEnabledEdges(const SymbolMap& symbolMap) const;
7474
};
75+
76+
#ifndef NDEBUG
77+
using ComponentMap = std::map<std::string, Component>;
78+
#else
7579
using ComponentMap = std::unordered_map<std::string, Component>;
80+
#endif
7681
using ComponentLocationMap = std::unordered_map<std::string, Location>;
7782
struct StateChange {
7883
ComponentLocationMap componentLocations;

src/runtime/TTASymbol.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#ifndef AALTITOAD_TTASYMBOL_H
2222
#define AALTITOAD_TTASYMBOL_H
2323
#include <aaltitoadpch.h>
24+
#include "extensions/overload"
2425

2526
struct TTATimerSymbol {
2627
float current_value;
@@ -39,6 +40,9 @@ using TTASymbol_t = std::variant<
3940
std::string
4041
>;
4142

43+
bool operator==(const TTASymbol_t&, const TTASymbol_t&);
44+
bool operator!=(const TTASymbol_t&, const TTASymbol_t&);
45+
4246
TTASymbol_t TTASymbolValueFromTypeAndValueStrings(const std::string& typestr, const std::string& valuestr);
4347
TTASymbol_t TTASymbolTypeFromString(const std::string& typestr);
4448
TTASymbol_t PopulateValueFromString(const TTASymbol_t& type, const std::string& valuestr);

src/verifier/ReachabilitySearcher.cpp

Lines changed: 63 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,11 @@ bool IsQuerySatisfiedHelper(const Query& query, const TTA& state) {
3939
case NodeType_t::CompEq:
4040
case NodeType_t::CompGreater:
4141
case NodeType_t::CompGreaterEq: {
42-
std::string exprstring{}; // This string can technically be precompiled.
43-
query.children[0].tree_apply([&exprstring]( const ASTNode& node ){ exprstring += node.token; });
44-
exprstring += query.root.token;
45-
query.children[1].tree_apply([&exprstring]( const ASTNode& node ){ exprstring += node.token; });
46-
spdlog::debug("Assembled expression '{0}'", exprstring);
47-
calculator c(exprstring.c_str());
48-
return c.eval(state.GetSymbols()).asBool();
42+
std::stringstream exprstring{}; // This string can technically be precompiled.
43+
query.children[0].tree_apply([&exprstring]( const ASTNode& node ){ exprstring << node.token; });
44+
exprstring << query.root.token;
45+
query.children[1].tree_apply([&exprstring]( const ASTNode& node ){ exprstring << node.token; });
46+
return calculator(exprstring.str().c_str()).eval(state.GetSymbols()).asBool();
4947
}
5048
case NodeType_t::SubExpr:
5149
case NodeType_t::Finally:
@@ -66,9 +64,7 @@ bool IsQuerySatisfiedHelper(const Query& query, const TTA& state) {
6664

6765
bool ReachabilitySearcher::IsQuerySatisfied(const Query& query, const TTA &state) {
6866
if(query.root.type == NodeType_t::Forall && query.children.begin()->root.type == NodeType_t::Globally) {
69-
auto invertedQ = Query(ASTNode{NodeType_t::Negation, "!"});
70-
invertedQ.insert(query);
71-
return IsQuerySatisfiedHelper(invertedQ, state);
67+
return !IsQuerySatisfiedHelper(query, state);
7268
}
7369
if(query.root.type != NodeType_t::Exists) {
7470
spdlog::critical("Only reachability queries are supported right now, sorry.");
@@ -96,17 +92,22 @@ void ReachabilitySearcher::OutputResults(const std::vector<QueryResultPair>& res
9692
if(CLIConfig::getInstance()["output"]) {
9793
std::ofstream outputFile{CLIConfig::getInstance()["output"].as_string(), std::ofstream::trunc};
9894
for(auto& r : results) {
99-
outputFile << ConvertASTToString(*r.query) << " : " << std::boolalpha << r.answer << "\n";
95+
auto answer = r.query->root.type == NodeType_t::Forall && r.query->children[0].root.type == NodeType_t::Globally ? !r.answer : r.answer;
96+
outputFile << ConvertASTToString(*r.query) << " : " << std::boolalpha << answer << "\n";
10097
}
10198
}
10299
}
103100

104-
void ReachabilitySearcher::PrintResults(const std::vector<QueryResultPair>& results) {
101+
auto ReachabilitySearcher::PrintResults(const std::vector<QueryResultPair>& results) -> int {
105102
OutputResults(results);
103+
auto acceptedResults = 0;
106104
spdlog::info("==== QUERY RESULTS ====");
107105
for(const auto& r : results) {
108106
spdlog::info("===================="); // Delimiter to make it easier to read
109-
spdlog::info("{0} : {1}", ConvertASTToString(*r.query), r.answer);
107+
auto answer = r.query->root.type == NodeType_t::Forall && r.query->children[0].root.type == NodeType_t::Globally ? !r.answer : r.answer;
108+
if(answer)
109+
acceptedResults++;
110+
spdlog::info("{0} : {1}", ConvertASTToString(*r.query), answer);
110111
auto stateHash = r.acceptingStateHash;
111112
auto state = r.acceptingState;
112113
std::vector<std::string> trace{};
@@ -135,13 +136,18 @@ void ReachabilitySearcher::PrintResults(const std::vector<QueryResultPair>& resu
135136
break;
136137
}
137138
}
139+
if(trace.empty()) {
140+
spdlog::info("No trace available");
141+
continue;
142+
}
138143
spdlog::info("Trace:");
139144
std::reverse(trace.begin(), trace.end());
140-
printf("[");
145+
printf("[\n");
141146
for(auto& stateStr : trace)
142147
printf("%s,\n", stateStr.c_str());
143148
printf("]\n");
144149
}
150+
return acceptedResults;
145151
}
146152

147153
auto debug_int_as_hex_str(size_t v) {
@@ -150,17 +156,32 @@ auto debug_int_as_hex_str(size_t v) {
150156
return std::string(buffer);
151157
}
152158

159+
std::string debug_get_current_state_string_human(const TTA& tta) {
160+
std::stringstream ss{};
161+
for(auto& c : tta.components)
162+
ss << c.second.currentLocation.identifier << ", ";
163+
return ss.str();
164+
}
165+
153166
void debug_print_passed_list(const ReachabilitySearcher& r) {
154167
spdlog::trace("==== PASSED LIST ====");
155168
for(auto& e : r.Passed) {
156-
spdlog::trace("Hash:{0} Prev:{1}",
169+
if(e.first == e.second.prevStateHash) {
170+
spdlog::warn("Hash:{0} Prev:{1} \tState:{2}",
171+
debug_int_as_hex_str(e.first),
172+
debug_int_as_hex_str(e.second.prevStateHash),
173+
debug_get_current_state_string_human(e.second.tta));
174+
continue;
175+
}
176+
spdlog::trace("Hash:{0} Prev:{1} \tState:{2}",
157177
debug_int_as_hex_str(e.first),
158-
debug_int_as_hex_str(e.second.prevStateHash));
178+
debug_int_as_hex_str(e.second.prevStateHash),
179+
debug_get_current_state_string_human(e.second.tta));
159180
}
160181
spdlog::trace("====/PASSED LIST ====");
161182
}
162183

163-
void debug_cleanup_waiting_list(ReachabilitySearcher& s, size_t curstatehash, const SearchState& state) {
184+
void cleanup_waiting_list(ReachabilitySearcher& s, size_t curstatehash, const SearchState& state) {
164185
bool found;
165186
do {
166187
found = false;
@@ -175,6 +196,13 @@ void debug_cleanup_waiting_list(ReachabilitySearcher& s, size_t curstatehash, co
175196
} while(found);
176197
}
177198

199+
std::string debug_get_symbol_map_string_representation(const TTA::SymbolMap& map) {
200+
std::ostringstream ss{};
201+
for(auto& symbol : map.map())
202+
ss << symbol.first << " :-> " << symbol.second << ", ";
203+
return ss.str();
204+
}
205+
178206
bool ReachabilitySearcher::ForwardReachabilitySearch(const nondeterminism_strategy_t& strategy) {
179207
auto stateit = Waiting.begin();
180208
while(stateit != Waiting.end()) {
@@ -183,9 +211,11 @@ bool ReachabilitySearcher::ForwardReachabilitySearch(const nondeterminism_strate
183211
AreQueriesSatisfied(query_results, state.tta, curstatehash);
184212
if(AreQueriesAnswered(query_results)) {
185213
Passed.emplace(std::make_pair(curstatehash, state));
186-
if(!CLIConfig::getInstance()["notrace"])
187-
PrintResults(query_results);
214+
if(CLIConfig::getInstance()["verbosity"] && CLIConfig::getInstance()["verbosity"].as_integer() >= 6)
215+
debug_print_passed_list(*this);
188216
spdlog::info("Found a positive result after searching: {0} states", Passed.size());
217+
if(!CLIConfig::getInstance()["notrace"])
218+
return query_results.size() - PrintResults(query_results) == 0;
189219
return true; // All the queries has been reached
190220
}
191221
// If the state is interesting, apply tock changes
@@ -198,14 +228,15 @@ bool ReachabilitySearcher::ForwardReachabilitySearch(const nondeterminism_strate
198228
AddToWaitingList(state.tta, allTickStateChanges, false, curstatehash);
199229

200230
Passed.emplace(std::make_pair(curstatehash, state));
201-
debug_cleanup_waiting_list(*this, curstatehash, state);
231+
232+
cleanup_waiting_list(*this, curstatehash, state);
202233
stateit = PickStateFromWaitingList(strategy);
203234
}
204-
if(!CLIConfig::getInstance()["notrace"])
205-
PrintResults(query_results);
206235
spdlog::info("Found a negative result after searching: {0} states", Passed.size());
207236
if(CLIConfig::getInstance()["verbosity"].as_integer() >= 6)
208237
debug_print_passed_list(*this);
238+
if(!CLIConfig::getInstance()["notrace"])
239+
return query_results.size() - PrintResults(query_results) == 0;
209240
return false;
210241
}
211242

@@ -228,8 +259,10 @@ void ReachabilitySearcher::AddToWaitingList(const TTA &state, const std::vector<
228259
auto nstate = state << change;
229260
auto nstatehash = nstate.GetCurrentStateHash();
230261
if (Passed.find(nstatehash) == Passed.end()) {
231-
if (nstatehash == state_hash)
232-
spdlog::warn("Colliding state hashes!");
262+
if (nstatehash == state_hash) {
263+
spdlog::warn("Recursive state hashes!");
264+
continue;
265+
}
233266
Waiting.emplace(std::make_pair(nstatehash, SearchState{nstate, state_hash, justTocked}));
234267
}
235268
}
@@ -238,12 +271,16 @@ void ReachabilitySearcher::AddToWaitingList(const TTA &state, const std::vector<
238271
if(statechanges.size() > 1) {
239272
auto baseChanges = state << *statechanges.begin();
240273
for (auto it = statechanges.begin() + 1; it != statechanges.end(); ++it) {
274+
if(it->IsEmpty())
275+
continue;
241276
/// This is a lot of copying large data objects... Figure something out with maybe std::move
242277
auto nstate = baseChanges << *it;
243278
auto nstatehash = nstate.GetCurrentStateHash();
244279
if (Passed.find(nstatehash) == Passed.end()) {
245-
if (nstatehash == state_hash)
246-
spdlog::warn("Colliding state hashes!");
280+
if (nstatehash == state_hash) {
281+
spdlog::warn("Recursive state hashes!");
282+
continue;
283+
}
247284
Waiting.emplace(std::make_pair(nstatehash, SearchState{nstate, state_hash, justTocked}));
248285
}
249286
}

src/verifier/ReachabilitySearcher.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ struct ReachabilitySearcher {
5555
bool IsQuerySatisfied(const Query& query, const TTA& state);
5656
void AreQueriesSatisfied(std::vector<QueryResultPair>& queries, const TTA& state, size_t state_hash);
5757
bool ForwardReachabilitySearch(const nondeterminism_strategy_t& strategy);
58-
void PrintResults(const std::vector<QueryResultPair>& results);
58+
auto PrintResults(const std::vector<QueryResultPair>& results) -> int;
5959
void OutputResults(const std::vector<QueryResultPair>& results);
6060
StateList::iterator PickStateFromWaitingList(const nondeterminism_strategy_t& strategy);
6161
};

0 commit comments

Comments
 (0)