libTriton version 1.0 build 1590
Loading...
Searching...
No Matches
bitwuzlaSolver.cpp
Go to the documentation of this file.
1
2/*
3** Copyright (C) - Triton
4**
5** This program is under the terms of the Apache License 2.0.
6*/
7
8#include <fstream>
9#include <regex>
10#include <string>
11
12#include <triton/astContext.hpp>
14#include <triton/exceptions.hpp>
20
21
22namespace triton {
23 namespace engines {
24 namespace solver {
25
27 this->timeout = 0;
28 this->memoryLimit = 0;
29
30 // Set bitwuzla abort function.
31 bitwuzla_set_abort_callback(this->abortCallback);
32 }
33
34
35 int32_t BitwuzlaSolver::terminateCallback(void* state) {
36 auto p = reinterpret_cast<SolverParams*>(state);
37
38 // Count elapsed time.
39 auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - p->start).count();
40
41 // Check timeout expired.
42 if (p->timeout && delta > p->timeout) {
44 return 1;
45 }
46
47 // Check memory limit.
48 #if defined(__unix__)
49
50 // Complete seconds elapsed from the start of solving.
51 auto delta_s = delta / 1000;
52
53 // Check memory limit every second. Don't perform check in first 100ms of solving.
54 if (p->memory_limit && delta > 100 && delta_s > p->last_mem_check) {
55 p->last_mem_check = delta_s;
56
57 // Parse system file to get current process memory consumption (VmRSS field).
58 size_t memory_usage = 0;
59 std::ifstream str("/proc/self/status");
60 if (str.is_open()) {
61 std::string line;
62 static std::regex vmsize("VmRSS:\\s*([0-9]+)\\s*kB");
63 while (std::getline(str, line)) {
64 std::smatch match;
65 if (std::regex_match(line, match, vmsize)) {
66 memory_usage = strtoul(match.str(1).c_str(), NULL, 10) / 1024;
67 break;
68 }
69 }
70 }
71 if (!memory_usage) {
72 return 0;
73 }
74 if (memory_usage > p->memory_limit) {
76 return 1;
77 }
78 }
79 #endif
80
81 return 0;
82 }
83
84
85 void BitwuzlaSolver::abortCallback(const char* msg) {
87 }
88
89
90 std::vector<std::unordered_map<triton::usize, SolverModel>> BitwuzlaSolver::getModels(const triton::ast::SharedAbstractNode& node,
91 triton::uint32 limit,
93 triton::uint32 timeout,
94 triton::uint32* solvingTime) const {
95 if (node == nullptr)
96 throw triton::exceptions::SolverEngine("BitwuzlaSolver::getModels(): Node cannot be null.");
97
98 if (node->isLogical() == false)
99 throw triton::exceptions::SolverEngine("BitwuzlaSolver::getModels(): Must be a logical node.");
100
101 // Create solver.
102 auto bzlaOptions = bitwuzla_options_new();
103 bitwuzla_set_option(bzlaOptions, BITWUZLA_OPT_PRODUCE_MODELS, 1);
104 auto bzla = bitwuzla_new(bzlaOptions);
105
106 // Convert Triton' AST to solver terms.
107 auto bzlaAst = triton::ast::TritonToBitwuzla();
108 bitwuzla_assert(bzla, bzlaAst.convert(node, bzla));
109
110 // Set solving params.
111 SolverParams p(this->timeout, this->memoryLimit);
112 if (this->timeout || this->memoryLimit) {
113 bitwuzla_set_termination_callback(bzla, this->terminateCallback, reinterpret_cast<void*>(&p));
114 }
115
116 // Get time of solving start.
117 auto start = std::chrono::system_clock::now();
118
119 // Check result.
120 auto res = bitwuzla_check_sat(bzla);
121
122 // Write back status.
123 if (status) {
124 switch (res) {
125 case BITWUZLA_SAT:
127 break;
128 case BITWUZLA_UNSAT:
130 break;
131 case BITWUZLA_UNKNOWN:
132 *status = p.status;
133 break;
134 }
135 }
136
137 std::vector<std::unordered_map<triton::usize, SolverModel>> ret;
138 while(res == BITWUZLA_SAT && limit >= 1) {
139 std::vector<BitwuzlaTerm> solution;
140 solution.reserve(bzlaAst.getVariables().size());
141
142 // Parse model.
143 std::unordered_map<triton::usize, SolverModel> model;
144 for (const auto& it : bzlaAst.getVariables()) {
145 const char* svalue = bitwuzla_term_value_get_str_fmt(bitwuzla_get_value(bzla, it.first), 2);
146 auto value = this->fromBvalueToUint512(svalue);
147 auto m = SolverModel(it.second, value);
148 model[m.getId()] = m;
149
150 // Negate current model to escape duplication in the next solution.
151 const auto& symvar_sort = bzlaAst.getBitvectorSorts().at(it.second->getSize());
152 auto cur_val = bitwuzla_mk_bv_value(symvar_sort, svalue, 2);
153 auto n = bitwuzla_mk_term2(BITWUZLA_KIND_EQUAL, it.first, cur_val);
154 solution.push_back(bitwuzla_mk_term1(BITWUZLA_KIND_NOT, n));
155 }
156
157 // Check that model is available.
158 if (model.empty()) {
159 break;
160 }
161
162 // Push model.
163 ret.push_back(model);
164
165 if (--limit) {
166 // Escape last model.
167 if (solution.size() > 1) {
168 bitwuzla_assert(bzla, bitwuzla_mk_term(BITWUZLA_KIND_OR, solution.size(), solution.data()));
169 }
170 else {
171 bitwuzla_assert(bzla, solution.front());
172 }
173
174 // Get next model.
175 res = bitwuzla_check_sat(bzla);
176 }
177 }
178
179 // Get time of solving end.
180 auto end = std::chrono::system_clock::now();
181
182 if (solvingTime)
183 *solvingTime = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
184
185 bitwuzla_delete(bzla);
186 bitwuzla_options_delete(bzlaOptions);
187
188 return ret;
189 }
190
191
194
195 this->getModels(node, 0, &st, timeout, solvingTime);
196
197 if (status) {
198 *status = st;
199 }
200 return st == triton::engines::solver::SAT;
201 }
202
203
204 std::unordered_map<triton::usize, SolverModel> BitwuzlaSolver::getModel(const triton::ast::SharedAbstractNode& node, triton::engines::solver::status_e* status, triton::uint32 timeout, triton::uint32* solvingTime) const {
205 auto models = this->getModels(node, 1, status, timeout, solvingTime);
206 return models.empty() ? std::unordered_map<triton::usize, SolverModel>() : models.front();
207 }
208
209
211 if (node == nullptr) {
212 throw triton::exceptions::AstLifting("BitwuzlaSolver::evaluate(): node cannot be null.");
213 }
214
215 auto bzlaOptions = bitwuzla_options_new();
216 bitwuzla_set_option(bzlaOptions, BITWUZLA_OPT_PRODUCE_MODELS, 1);
217 auto bzla = bitwuzla_new(bzlaOptions);
218
219 // Query check-sat on empty solver to put Bitwuzla in SAT-state. Thus, it should be able to evaluate concrete formulas.
220 if (bitwuzla_check_sat(bzla) != BITWUZLA_SAT) {
221 bitwuzla_delete(bzla);
222 bitwuzla_options_delete(bzlaOptions);
223 throw triton::exceptions::SolverEngine("BitwuzlaSolver::evaluate(): empty solver didn't return SAT.");
224 }
225
226 // Evaluate concrete AST in solver.
227 auto bzlaAst = triton::ast::TritonToBitwuzla(true);
228 auto term_value = bitwuzla_get_value(bzla, bzlaAst.convert(node, bzla));
229
230 triton::uint512 res = 0;
231 if (bitwuzla_term_is_bool(term_value)) {
232 res = bitwuzla_term_value_get_bool(term_value);
233 } else {
234 res = triton::uint512{bitwuzla_term_value_get_str_fmt(term_value, 10)};
235 }
236
237 bitwuzla_delete(bzla);
238 bitwuzla_options_delete(bzlaOptions);
239
240 return res;
241 }
242
243
244 std::string BitwuzlaSolver::getName(void) const {
245 return "Bitwuzla";
246 }
247
248
250 this->timeout = ms;
251 }
252
253
255 this->memoryLimit = limit;
256 }
257
258 triton::uint512 BitwuzlaSolver::fromBvalueToUint512(const char* value) const {
259 triton::usize len = strlen(value);
260 triton::usize pos = 0;
261 triton::uint512 res = 0;
262
263 // Convert short bitvector string directly.
264 if (len <= 64) {
265 return std::strtoull(value, 0, 2L);
266 }
267
268 // Convert long bitvector string by 64-bit chunks.
269 while (pos < len) {
270 triton::usize sublen = std::min(len - pos, (triton::usize) 64UL);
271 char* substr = new char[sublen + 1];
272 std::memcpy(substr, value + pos, sublen);
273 substr[sublen] = '\0';
274 res = (res << sublen) + std::strtoull(substr, 0, 2L);
275 pos += sublen;
276 delete[] substr;
277 }
278
279 return res;
280 }
281
282 };
283 };
284};
Converts a Triton's AST to Bitwuzla's AST.
TRITON_EXPORT std::unordered_map< triton::usize, SolverModel > getModel(const triton::ast::SharedAbstractNode &node, triton::engines::solver::status_e *status=nullptr, triton::uint32 timeout=0, triton::uint32 *solvingTime=nullptr) const
Computes and returns a model from a symbolic constraint.
TRITON_EXPORT std::string getName(void) const
Returns the name of this solver.
TRITON_EXPORT std::vector< std::unordered_map< triton::usize, SolverModel > > getModels(const triton::ast::SharedAbstractNode &node, triton::uint32 limit, triton::engines::solver::status_e *status=nullptr, triton::uint32 timeout=0, triton::uint32 *solvingTime=nullptr) const
Computes and returns several models from a symbolic constraint. The limit is the number of models ret...
TRITON_EXPORT BitwuzlaSolver()
Constructor.
TRITON_EXPORT triton::uint512 evaluate(const triton::ast::SharedAbstractNode &node) const
Evaluates a Triton's AST via Bitwuzla and returns a concrete value.
TRITON_EXPORT void setTimeout(triton::uint32 ms)
Defines a solver timeout (in milliseconds).
TRITON_EXPORT void setMemoryLimit(triton::uint32 mem)
Defines a solver memory consumption limit (in megabytes).
static void abortCallback(const char *msg)
Callback function that implements aborting of Bitwuzla solver with throwing exception.
static int32_t terminateCallback(void *state)
Callback function that implements termination of Bitwuzla solver on timeout and memory limit.
TRITON_EXPORT bool isSat(const triton::ast::SharedAbstractNode &node, triton::engines::solver::status_e *status=nullptr, triton::uint32 timeout=0, triton::uint32 *solvingTime=nullptr) const
Returns true if an expression is satisfiable.
This class is used to represent a constraint model solved.
The exception class used by all AST lifting (e.g z3 <-> triton).
The exception class used by the solver engine.
std::shared_ptr< triton::ast::AbstractNode > SharedAbstractNode
Shared Abstract Node.
Definition ast.hpp:59
std::size_t usize
unsigned MAX_INT 32 or 64 bits according to the CPU.
math::wide_integer::uint512_t uint512
unsigned 512-bits
std::uint32_t uint32
unisgned 32-bits
The Triton namespace.