Skip to content

Commit 9aa036f

Browse files
author
Natasha Yogananda Jeppu
committed
Add new module and parse option to display byte extract and update metrics
This commit adds support for a new cbmc parse option '--show-byte-ops' to display the number of byte extracts and updates, along with corresponding ssa expressions. Supports display in json-ui format and write to file using the '--outfile <filename>'. Does not support display in xml-ui format. Template functions are defined to display relevant metrics for byte_extract_exprt and byte_update_exprt types. Note: The template functions can be used generally for any expression class derived from exprt. This PR addresses only byte_extract_exprt and byte_update_exprt.
1 parent 8ab6023 commit 9aa036f

File tree

7 files changed

+314
-1
lines changed

7 files changed

+314
-1
lines changed

src/cbmc/cbmc_parse_options.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,9 @@ void cbmc_parse_optionst::get_command_line_options(optionst &options)
198198
if(cmdline.isset("program-only"))
199199
options.set_option("program-only", true);
200200

201+
if(cmdline.isset("show-byte-ops"))
202+
options.set_option("show-byte-ops", true);
203+
201204
if(cmdline.isset("show-vcc"))
202205
options.set_option("show-vcc", true);
203206

@@ -640,7 +643,8 @@ int cbmc_parse_optionst::doit()
640643

641644
if(
642645
options.get_bool_option("program-only") ||
643-
options.get_bool_option("show-vcc"))
646+
options.get_bool_option("show-vcc") ||
647+
options.get_bool_option("show-byte-ops"))
644648
{
645649
if(options.get_bool_option("paths"))
646650
{

src/goto-checker/bmc_util.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ void run_property_decider(
173173
// clang-format off
174174
#define OPT_BMC \
175175
"(program-only)" \
176+
"(show-byte-ops)" \
176177
"(show-loops)" \
177178
"(show-vcc)" \
178179
"(show-goto-symex-steps)" \
@@ -203,6 +204,7 @@ void run_property_decider(
203204
" --show-goto-symex-steps show which steps symex travels, includes " \
204205
" diagnostic information\n" \
205206
" --program-only only show program expression\n" \
207+
" --show-byte-ops show all byte extracts and updates\n" \
206208
" --show-loops show the loops in the program\n" \
207209
" --depth nr limit search depth\n" \
208210
" --max-field-sensitivity-array-size M\n" \

src/goto-checker/multi_path_symex_only_checker.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ operator()(propertiest &properties)
5959
show_program(ns, equation);
6060
}
6161

62+
if(options.get_bool_option("show-byte-ops"))
63+
{
64+
show_byte_ops(options, ui_message_handler, ns, equation);
65+
}
66+
6267
resultt result(resultt::progresst::DONE);
6368
update_properties(properties, result.updated_properties);
6469
return result;

src/goto-checker/single_path_symex_only_checker.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ void single_path_symex_only_checkert::equation_output(
127127
if(options.get_bool_option("program-only"))
128128
show_program(ns, equation);
129129

130+
if(options.get_bool_option("show-byte-ops"))
131+
show_byte_ops(options, ui_message_handler, ns, equation);
132+
130133
if(options.get_bool_option("validate-ssa-equation"))
131134
{
132135
symex.validate(validation_modet::INVARIANT);

src/goto-diff/goto_diff_parse_options.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ void goto_diff_parse_optionst::get_command_line_options(optionst &options)
8383
if(cmdline.isset("program-only"))
8484
options.set_option("program-only", true);
8585

86+
if(cmdline.isset("show-byte-ops"))
87+
options.set_option("show-byte-ops", true);
88+
8689
if(cmdline.isset("show-vcc"))
8790
options.set_option("show-vcc", true);
8891

src/goto-symex/show_program.cpp

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,18 @@ Author: Daniel Kroening, kroening@kroening.com
1111

1212
#include "show_program.h"
1313

14+
#include <fstream>
1415
#include <iostream>
1516

1617
#include <goto-symex/symex_target_equation.h>
1718

1819
#include <langapi/language_util.h>
1920

21+
#include <util/byte_operators.h>
22+
#include <util/exit_codes.h>
23+
#include <util/json_irep.h>
24+
#include <util/ui_message.h>
25+
2026
/// Output a single SSA step
2127
/// \param ns: Namespace
2228
/// \param step: SSA step
@@ -78,3 +84,273 @@ void show_program(const namespacet &ns, const symex_target_equationt &equation)
7884
show_step(ns, step, "SHARED_WRITE", count);
7985
}
8086
}
87+
88+
template <typename expr_typet>
89+
std::size_t count_expr_typed(const exprt &expr)
90+
{
91+
static_assert(
92+
std::is_base_of<exprt, expr_typet>::value,
93+
"`expr_typet` is expected to be a type of `exprt`.");
94+
95+
std::size_t count = 0;
96+
expr.visit_pre([&](const exprt &e) {
97+
if(can_cast_expr<expr_typet>(e))
98+
count++;
99+
});
100+
101+
return count;
102+
}
103+
104+
bool duplicated_previous_step(const SSA_stept &ssa_step)
105+
{
106+
return !(
107+
ssa_step.is_assignment() || ssa_step.is_assert() || ssa_step.is_assume() ||
108+
ssa_step.is_constraint() || ssa_step.is_shared_read() ||
109+
ssa_step.is_shared_write());
110+
}
111+
112+
void show_ssa_step_plain(
113+
messaget::mstreamt &out,
114+
const namespacet &ns,
115+
const SSA_stept &ssa_step,
116+
const exprt &ssa_expr)
117+
{
118+
const irep_idt &function_id = ssa_step.source.function_id;
119+
const std::string ssa_expr_as_string = from_expr(ns, function_id, ssa_expr);
120+
121+
out << messaget::faint << "// " << ssa_step.source.pc->location_number << " ";
122+
out << ssa_step.source.pc->source_location.as_string() << "\n"
123+
<< messaget::reset;
124+
out << ssa_expr_as_string << "\n";
125+
}
126+
127+
json_objectt get_ssa_step_json(
128+
const namespacet &ns,
129+
const SSA_stept &ssa_step,
130+
const exprt &ssa_expr)
131+
{
132+
const std::string key_srcloc = "sourceLocation";
133+
const std::string key_ssa_expr = "ssaExpr";
134+
const std::string key_ssa_expr_as_string = "ssaExprString";
135+
136+
const irep_idt &function_id = ssa_step.source.function_id;
137+
const std::string ssa_expr_as_string = from_expr(ns, function_id, ssa_expr);
138+
139+
json_objectt json_ssa_step{
140+
{key_srcloc, json(ssa_step.source.pc->source_location)},
141+
{key_ssa_expr_as_string, json_stringt(ssa_expr_as_string)},
142+
{key_ssa_expr, json_irept(false).convert_from_irep(ssa_expr)}};
143+
144+
return json_ssa_step;
145+
}
146+
147+
template <typename expr_typet>
148+
void show_byte_op_plain(
149+
messaget::mstreamt &out,
150+
const namespacet &ns,
151+
const symex_target_equationt &equation)
152+
{
153+
std::size_t equation_byte_op_count = 0;
154+
for(const auto &step : equation.SSA_steps)
155+
{
156+
if(duplicated_previous_step(step))
157+
continue;
158+
159+
const exprt &ssa_expr = step.get_ssa_expr();
160+
const std::size_t ssa_expr_byte_op_count =
161+
count_expr_typed<expr_typet>(ssa_expr);
162+
163+
if(ssa_expr_byte_op_count == 0)
164+
continue;
165+
166+
equation_byte_op_count += ssa_expr_byte_op_count;
167+
show_ssa_step_plain(out, ns, step, ssa_expr);
168+
}
169+
170+
if(std::is_same<expr_typet, byte_extract_exprt>::value)
171+
out << '\n' << "Number of byte extracts: ";
172+
else if(std::is_same<expr_typet, byte_update_exprt>::value)
173+
out << '\n' << "Number of byte updates: ";
174+
else
175+
UNREACHABLE;
176+
177+
out << equation_byte_op_count << '\n';
178+
out << messaget::eom;
179+
}
180+
181+
template <typename expr_typet>
182+
std::string json_get_key_byte_op_list()
183+
{
184+
if(std::is_same<expr_typet, byte_extract_exprt>::value)
185+
return "byteExtractList";
186+
else if(std::is_same<expr_typet, byte_update_exprt>::value)
187+
return "byteUpdateList";
188+
else
189+
UNREACHABLE;
190+
}
191+
192+
template <typename expr_typet>
193+
std::string json_get_key_byte_op_num()
194+
{
195+
if(std::is_same<expr_typet, byte_extract_exprt>::value)
196+
return "numOfExtracts";
197+
else if(std::is_same<expr_typet, byte_update_exprt>::value)
198+
return "numOfUpdates";
199+
else
200+
UNREACHABLE;
201+
}
202+
203+
template <typename expr_typet>
204+
json_objectt
205+
get_byte_op_json(const namespacet &ns, const symex_target_equationt &equation)
206+
{
207+
// Get key values to be used in the json output based on byte operation type
208+
// 1. json_get_key_byte_op_list():
209+
// returns relevant json object key string where object records
210+
// a list of expressions for given byte operation.
211+
// 2. json_get_key_byte_op_num():
212+
// returns relevant json object key string where object number
213+
// of given byte operation.
214+
215+
const std::string key_byte_op_list = json_get_key_byte_op_list<expr_typet>();
216+
const std::string key_byte_op_num = json_get_key_byte_op_num<expr_typet>();
217+
218+
json_objectt byte_op_stats;
219+
json_arrayt &byte_op_list = byte_op_stats[key_byte_op_list].make_array();
220+
221+
std::size_t equation_byte_op_count = 0;
222+
for(const auto &step : equation.SSA_steps)
223+
{
224+
if(duplicated_previous_step(step))
225+
continue;
226+
227+
const exprt &ssa_expr = step.get_ssa_expr();
228+
const std::size_t ssa_expr_byte_op_count =
229+
count_expr_typed<expr_typet>(ssa_expr);
230+
231+
if(ssa_expr_byte_op_count == 0)
232+
continue;
233+
234+
equation_byte_op_count += ssa_expr_byte_op_count;
235+
byte_op_list.push_back(get_ssa_step_json(ns, step, ssa_expr));
236+
}
237+
238+
byte_op_stats[key_byte_op_num] =
239+
json_numbert(std::to_string(equation_byte_op_count));
240+
241+
return byte_op_stats;
242+
}
243+
244+
// Get key values to be used in the json output based on byte operation type
245+
// 1. json_get_key_byte_op_stats():
246+
// returns relevant json object key string where object records
247+
// statistics for given byte operation.
248+
template <typename expr_typet>
249+
std::string json_get_key_byte_op_stats()
250+
{
251+
if(std::is_same<expr_typet, byte_extract_exprt>::value)
252+
return "byteExtractStats";
253+
else if(std::is_same<expr_typet, byte_update_exprt>::value)
254+
return "byteUpdateStats";
255+
else
256+
UNREACHABLE;
257+
}
258+
259+
bool is_outfile_specified(const optionst &options)
260+
{
261+
const std::string &filename = options.get_option("outfile");
262+
return (!filename.empty() && filename != "-");
263+
}
264+
265+
void show_byte_ops_plain(
266+
ui_message_handlert &ui_message_handler,
267+
std::ostream &out,
268+
bool outfile_given,
269+
const namespacet &ns,
270+
const symex_target_equationt &equation)
271+
{
272+
messaget msg(ui_message_handler);
273+
if(outfile_given)
274+
{
275+
stream_message_handlert mout_handler(out);
276+
messaget mout(mout_handler);
277+
278+
msg.status() << "\nByte Extracts written to file" << messaget::eom;
279+
show_byte_op_plain<byte_extract_exprt>(mout.status(), ns, equation);
280+
281+
msg.status() << "\nByte Updates written to file" << messaget::eom;
282+
show_byte_op_plain<byte_update_exprt>(mout.status(), ns, equation);
283+
}
284+
else
285+
{
286+
msg.status() << "\nByte Extracts:" << messaget::eom;
287+
show_byte_op_plain<byte_extract_exprt>(msg.status(), ns, equation);
288+
289+
msg.status() << "\nByte Updates:" << messaget::eom;
290+
show_byte_op_plain<byte_update_exprt>(msg.status(), ns, equation);
291+
}
292+
}
293+
294+
void show_byte_ops_json(
295+
std::ostream &out,
296+
const namespacet &ns,
297+
const symex_target_equationt &equation)
298+
{
299+
json_objectt byte_ops_stats{
300+
{json_get_key_byte_op_stats<byte_extract_exprt>(),
301+
get_byte_op_json<byte_extract_exprt>(ns, equation)},
302+
{json_get_key_byte_op_stats<byte_update_exprt>(),
303+
get_byte_op_json<byte_update_exprt>(ns, equation)}};
304+
305+
json_objectt json_result;
306+
json_result["byteOpsStats"] = byte_ops_stats;
307+
308+
out << ",\n" << json_result;
309+
}
310+
311+
void show_byte_ops_xml(ui_message_handlert &ui_message_handler)
312+
{
313+
messaget msg(ui_message_handler);
314+
msg.error()
315+
<< "XML UI not supported for displaying byte extracts and updates."
316+
<< " Try --json-ui instead" << messaget::eom;
317+
318+
return;
319+
}
320+
321+
void show_byte_ops(
322+
const optionst &options,
323+
ui_message_handlert &ui_message_handler,
324+
const namespacet &ns,
325+
const symex_target_equationt &equation)
326+
{
327+
const std::string &filename = options.get_option("outfile");
328+
const bool outfile_given = is_outfile_specified(options);
329+
330+
std::ofstream of;
331+
332+
if(outfile_given)
333+
{
334+
of.open(filename, std::fstream::out);
335+
if(!of)
336+
throw invalid_command_line_argument_exceptiont(
337+
"failed to open output file: " + filename, "--outfile");
338+
}
339+
340+
std::ostream &out = outfile_given ? of : std::cout;
341+
342+
switch(ui_message_handler.get_ui())
343+
{
344+
case ui_message_handlert::uit::XML_UI:
345+
show_byte_ops_xml(ui_message_handler);
346+
break;
347+
348+
case ui_message_handlert::uit::JSON_UI:
349+
show_byte_ops_json(out, ns, equation);
350+
break;
351+
352+
case ui_message_handlert::uit::PLAIN:
353+
show_byte_ops_plain(ui_message_handler, out, outfile_given, ns, equation);
354+
break;
355+
}
356+
}

src/goto-symex/show_program.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Author: Daniel Kroening, kroening@kroening.com
1414

1515
class namespacet;
1616
class symex_target_equationt;
17+
class optionst;
18+
class ui_message_handlert;
1719

1820
/// Print the steps of \p equation on the standard output.
1921
///
@@ -25,4 +27,22 @@ class symex_target_equationt;
2527
/// \param equation: SSA form of the program
2628
void show_program(const namespacet &ns, const symex_target_equationt &equation);
2729

30+
/// Count and display all byte extract and byte update operations from equation
31+
/// on standard output or file.
32+
/// The name of the output file is given by the `outfile` option from
33+
/// \p options, the standard output is used if it is not provided.
34+
/// The format is either JSON or plain text depending on \p ui_message_handler;
35+
/// XML is not supported.
36+
/// For each step, if it's a byte extract or update, print location, ssa
37+
/// expression and compute number of extracts/updates in total in the equation.
38+
/// \param options: parsed options
39+
/// \param ui_message_handler: ui message handler
40+
/// \param ns: namespace
41+
/// \param equation: SSA form of the program
42+
void show_byte_ops(
43+
const optionst &options,
44+
ui_message_handlert &ui_message_handler,
45+
const namespacet &ns,
46+
const symex_target_equationt &equation);
47+
2848
#endif // CPROVER_GOTO_SYMEX_SHOW_PROGRAM_H

0 commit comments

Comments
 (0)