Skip to content

Commit c8e629f

Browse files
support sym_assume to prune subtrees
1 parent 2e56851 commit c8e629f

File tree

3 files changed

+137
-5
lines changed

3 files changed

+137
-5
lines changed

headers/wasm/concolic_driver.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class ManagedConcolicCleanup {
4848
if (driver.tree_file.has_value())
4949
ExploreTree.dump_graphviz(driver.tree_file.value());
5050

51-
// Clear the symbol bookkeeper
51+
// Clear the symbol bookkeeper
5252
SymBookKeeper.clear();
5353
}
5454
};
@@ -134,6 +134,7 @@ inline void ConcolicDriver::main_exploration_loop() {
134134

135135
inline void ConcolicDriver::run() {
136136
main_exploration_loop();
137+
ExploreTree.print_overall_result();
137138
Profile.print_summary();
138139
}
139140

headers/wasm/symbolic_rt.hpp

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@ struct NodeBox {
543543
std::monostate fillFailedNode();
544544
std::monostate fillUnreachableNode();
545545
std::monostate fillSnapshotNode(Snapshot_t snapshot);
546+
std::monostate fillNotToExploreNode();
546547
bool isUnexplored() const;
547548
std::vector<SymVal> collect_path_conds();
548549
int min_cost_of_reaching_here();
@@ -664,6 +665,22 @@ struct UnExploredNode : Node {
664665
}
665666
};
666667

668+
struct NotToExploreNode : Node {
669+
NotToExploreNode() {}
670+
std::string to_string() override { return "NotToExploreNode"; }
671+
672+
protected:
673+
void generate_dot(std::ostream &os, int parent_dot_id,
674+
const std::string &edge_label) override {
675+
int current_node_dot_id = current_id++;
676+
graphviz_node(os, current_node_dot_id, "NotToExplore", "box", "grey");
677+
678+
if (parent_dot_id != -1) {
679+
graphviz_edge(os, parent_dot_id, current_node_dot_id, edge_label);
680+
}
681+
}
682+
};
683+
667684
struct SnapshotNode : Node {
668685
SnapshotNode(Snapshot_t snapshot) : snapshot(snapshot) {}
669686
std::string to_string() override { return "SnapshotNode"; }
@@ -761,6 +778,15 @@ inline std::monostate NodeBox::fillSnapshotNode(Snapshot_t snapshot) {
761778
return std::monostate();
762779
}
763780

781+
inline std::monostate NodeBox::fillNotToExploreNode() {
782+
if (this->isUnexplored()) {
783+
node = std::make_unique<NotToExploreNode>();
784+
} else {
785+
assert(dynamic_cast<NotToExploreNode *>(node.get()) != nullptr);
786+
}
787+
return std::monostate();
788+
}
789+
764790
inline std::monostate NodeBox::fillFinishedNode() {
765791
if (this->isUnexplored()) {
766792
node = std::make_unique<Finished>();
@@ -896,6 +922,10 @@ class ExploreTree_t {
896922
return std::monostate();
897923
}
898924

925+
std::monostate fillNotToExploredNode() {
926+
return cursor->fillNotToExploreNode();
927+
}
928+
899929
bool worth_to_create_snapshot() {
900930
if (!ENABLE_COST_MODEL) {
901931
return REUSE_SNAPSHOT;
@@ -911,8 +941,8 @@ class ExploreTree_t {
911941
auto parent_cost =
912942
cursor->parent ? cursor->parent->min_cost_of_reaching_here() : 0;
913943
auto exec_from_parent_cost = reach_parent_cost + cursor->instr_cost;
914-
GENSYM_INFO("The score of snapshot tendency: " + std::to_string(exec_from_parent_cost -
915-
snapshot_cost));
944+
GENSYM_INFO("The score of snapshot tendency: " +
945+
std::to_string(exec_from_parent_cost - snapshot_cost));
916946
return snapshot_cost <= exec_from_parent_cost;
917947
}
918948

@@ -924,6 +954,7 @@ class ExploreTree_t {
924954
if_else_node != nullptr &&
925955
"Can't move cursor when the branch node is not initialized correctly!");
926956
int cost_from_parent = CostManager.dump_instr_cost();
957+
cursor->instr_cost = cost_from_parent;
927958
if (branch) {
928959
true_branch_cov_map[if_else_node->id] = true;
929960
if (worth_to_create_snapshot()) {
@@ -947,6 +978,26 @@ class ExploreTree_t {
947978
return std::monostate();
948979
}
949980

981+
std::monostate moveCursorNoControl(bool branch) {
982+
Profile.step(StepProfileKind::CURSOR_MOVE);
983+
assert(cursor != nullptr);
984+
auto if_else_node = dynamic_cast<IfElseNode *>(cursor->node.get());
985+
assert(
986+
if_else_node != nullptr &&
987+
"Can't move cursor when the branch node is not initialized correctly!");
988+
int cost_from_parent = CostManager.dump_instr_cost();
989+
cursor->instr_cost = cost_from_parent;
990+
if (branch) {
991+
true_branch_cov_map[if_else_node->id] = true;
992+
if_else_node->false_branch->fillNotToExploreNode();
993+
cursor = if_else_node->true_branch.get();
994+
} else {
995+
assert(false &&
996+
"moveCursorNoControl should not be used for false branch");
997+
}
998+
return std::monostate();
999+
}
1000+
9501001
std::monostate print() {
9511002
std::cout << root->node->to_string() << std::endl;
9521003
return std::monostate();
@@ -966,6 +1017,45 @@ class ExploreTree_t {
9661017
return std::monostate();
9671018
}
9681019

1020+
std::monostate print_overall_result() {
1021+
// Print how many paths have been explored, how many paths are unreachable,
1022+
// how many paths are failed, how many paths are finished successfully
1023+
int unexplored_count = 0;
1024+
int finished_count = 0;
1025+
int failed_count = 0;
1026+
int not_to_explore_count = 0;
1027+
int unreachable_count = 0;
1028+
std::function<void(NodeBox *)> dfs = [&](NodeBox *node) {
1029+
if (auto if_else_node = dynamic_cast<IfElseNode *>(node->node.get())) {
1030+
dfs(if_else_node->true_branch.get());
1031+
dfs(if_else_node->false_branch.get());
1032+
} else if (dynamic_cast<UnExploredNode *>(node->node.get())) {
1033+
unexplored_count += 1;
1034+
} else if (dynamic_cast<Finished *>(node->node.get())) {
1035+
finished_count += 1;
1036+
} else if (dynamic_cast<Failed *>(node->node.get())) {
1037+
failed_count += 1;
1038+
} else if (dynamic_cast<Unreachable *>(node->node.get())) {
1039+
unreachable_count += 1;
1040+
} else if (dynamic_cast<SnapshotNode *>(node->node.get())) {
1041+
// Snapshot node is considered unexplored
1042+
unexplored_count += 1;
1043+
} else if (dynamic_cast<NotToExploreNode *>(node->node.get())) {
1044+
not_to_explore_count += 1;
1045+
} else {
1046+
throw std::runtime_error("Unknown node type in explore tree");
1047+
}
1048+
};
1049+
dfs(root.get());
1050+
std::cout << "Explore Tree Overall Result:" << std::endl;
1051+
std::cout << " Unexplored paths: " << unexplored_count << std::endl;
1052+
std::cout << " Finished paths: " << finished_count << std::endl;
1053+
std::cout << " Failed paths: " << failed_count << std::endl;
1054+
std::cout << " Unreachable paths: " << unreachable_count << std::endl;
1055+
std::cout << " NotToExplore paths: " << not_to_explore_count << std::endl;
1056+
return std::monostate();
1057+
}
1058+
9691059
NodeBox *pick_unexplored() {
9701060
// Pick an unexplored node from the tree
9711061
// For now, we just iterate through the tree and return the first unexplored

src/main/scala/wasm/StagedConcolicMiniWasm.scala

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ object Counter {
3131
dict.clear()
3232
}
3333

34+
def getId(): Int = {
35+
val id = currentId
36+
currentId += 1
37+
id
38+
}
39+
3440
def getId(wir: WIR, nth: Int = 0): Int = {
3541
if (dict.contains((wir, nth))) {
3642
dict((wir, nth))
@@ -547,8 +553,28 @@ trait StagedWasmEvaluator extends SAIOps {
547553
case Import("i32", "symbolic", _) =>
548554
evalSymbolic(NumType(I32Type), rest, kont, mkont, trail)(ctx)
549555
case Import("i32", "sym_assume", _) =>
550-
// TODO: implement sym_assume
551-
eval(rest, kont, mkont, trail)(ctx.pop()._2)
556+
// symbolic assume is just like an if else that only has one branch, while another
557+
// is marked as not-to-explore
558+
val (condTy, newCtx) = ctx.pop()
559+
Predef.assert(condTy == NumType(I32Type), s"sym_assume only supports i32 condition, get $condTy")
560+
val cond = Stack.popC(condTy)
561+
val symCond = Stack.popS(condTy)
562+
val id = Counter.getId()
563+
ExploreTree.fillWithIfElse(symCond.s, id)
564+
def thnK: Rep[Cont[Unit]] = topFun((mk: Rep[MCont[Unit]]) => {
565+
info(s"Successfully assumed condition at $id")
566+
eval(rest, kont, mk, trail)(newCtx)
567+
})
568+
if (cond.toInt != 0) {
569+
ExploreTree.moveCursor(true)
570+
eval(rest, kont, mkont, trail)(newCtx)
571+
} else {
572+
val control = makeControl(thnK, mkont)
573+
ExploreTree.moveCursor(false, control)
574+
// just stop the execution at here
575+
ExploreTree.fillWithNotToExplore()
576+
}
577+
()
552578
case Import("i32", "sym_assert", _) =>
553579
// TODO: implement sym_assert
554580
eval(rest, kont, mkont, trail)(ctx.pop()._2)
@@ -637,6 +663,8 @@ trait StagedWasmEvaluator extends SAIOps {
637663

638664
def evalTop(mkont: Rep[MCont[Unit]], main: Option[String]): Rep[Unit] = {
639665
Counter.reset()
666+
Predef.println("[DEBUG]" + module)
667+
Predef.println("[DEBUG] module.defs: " + module.defs)
640668
val funBody: FuncBodyDef = main match {
641669
case Some(func_name) =>
642670
module.defs.flatMap({
@@ -889,6 +917,10 @@ trait StagedWasmEvaluator extends SAIOps {
889917
"tree-fill-if-else".reflectCtrlWith[Unit](s, id)
890918
}
891919

920+
def fillWithNotToExplore(): Rep[Unit] = {
921+
"tree-fill-not-to-explore".reflectCtrlWith[Unit]()
922+
}
923+
892924
def fillWithFinished(): Rep[Unit] = {
893925
"tree-fill-finished".reflectCtrlWith[Unit]()
894926
}
@@ -898,6 +930,11 @@ trait StagedWasmEvaluator extends SAIOps {
898930
"tree-move-cursor".reflectCtrlWith[Unit](branch, control)
899931
}
900932

933+
def moveCursor(branch: Boolean): Rep[Unit] = {
934+
// when moving cursor from to an unexplored node, we need to change the reuse state
935+
"tree-move-cursor-no-control".reflectCtrlWith[Unit](branch)
936+
}
937+
901938
def print(): Rep[Unit] = {
902939
"tree-print".reflectCtrlWith[Unit]()
903940
}
@@ -1543,10 +1580,14 @@ trait StagedWasmCppGen extends CGenBase with CppSAICodeGenBase {
15431580
emit("GENSYM_ASSERT("); shallow(cond); emit(")")
15441581
case Node(_, "tree-fill-if-else", List(sym, id), _) =>
15451582
emit("ExploreTree.fillIfElseNode("); shallow(sym); emit(", "); emit(id.toString); emit(")")
1583+
case Node(_, "tree-fill-not-to-explore", List(), _) =>
1584+
emit("ExploreTree.fillNotToExploredNode()")
15461585
case Node(_, "tree-fill-finished", List(), _) =>
15471586
emit("ExploreTree.fillFinishedNode()")
15481587
case Node(_, "tree-move-cursor", List(b, snapshot), _) =>
15491588
emit("ExploreTree.moveCursor("); shallow(b); emit(", "); shallow(snapshot); emit(")")
1589+
case Node(_, "tree-move-cursor-no-control", List(b), _) =>
1590+
emit("ExploreTree.moveCursorNoControl("); shallow(b); emit(")")
15501591
case Node(_, "add-instr-cost", List(n), _) =>
15511592
emit("CostManager.add_instr_cost("); shallow(n); emit(")")
15521593
case Node(_, "tree-print", List(), _) =>

0 commit comments

Comments
 (0)