|
| 1 | +/*******************************************************************\ |
| 2 | +
|
| 3 | +Module: Java Convert Thread blocks |
| 4 | +
|
| 5 | +Author: Kurt Degiogrio, [email protected] |
| 6 | +
|
| 7 | +\*******************************************************************/ |
| 8 | + |
| 9 | +#include "java_bytecode_convert_threadblock.h" |
| 10 | +#include "expr2java.h" |
| 11 | +#include "java_types.h" |
| 12 | + |
| 13 | +#include <util/expr_iterator.h> |
| 14 | +#include <util/namespace.h> |
| 15 | +#include <util/cprover_prefix.h> |
| 16 | +#include <util/std_types.h> |
| 17 | +#include <util/arith_tools.h> |
| 18 | + |
| 19 | +// Disable linter to allow an std::string constant. |
| 20 | +const std::string next_thread_id = CPROVER_PREFIX "_next_thread_id";// NOLINT(*) |
| 21 | +const std::string thread_id = CPROVER_PREFIX "_thread_id";// NOLINT(*) |
| 22 | + |
| 23 | +/// Adds a new symbol to the symbol table if it doesn't exist. Otherwise, |
| 24 | +/// returns the existing one. |
| 25 | +/// /param name: name of the symbol to be generated |
| 26 | +/// /param base_name: base name of the symbol to be generated |
| 27 | +/// /param type: type of the symbol to be generated |
| 28 | +/// /param value: initial value of the symbol to be generated |
| 29 | +/// /param is_thread_local: if true this symbol will be set as thread local |
| 30 | +/// /param is_static_lifetime: if true this symbol will be set as static |
| 31 | +/// /return returns new or existing symbol. |
| 32 | +static symbolt add_or_get_symbol( |
| 33 | + symbol_tablet &symbol_table, |
| 34 | + const irep_idt &name, |
| 35 | + const irep_idt &base_name, |
| 36 | + const typet &type, |
| 37 | + const exprt &value, |
| 38 | + const bool is_thread_local, |
| 39 | + const bool is_static_lifetime) |
| 40 | +{ |
| 41 | + const symbolt* psymbol = nullptr; |
| 42 | + namespacet ns(symbol_table); |
| 43 | + ns.lookup(name, psymbol); |
| 44 | + if(psymbol != nullptr) |
| 45 | + return *psymbol; |
| 46 | + symbolt new_symbol; |
| 47 | + new_symbol.name = name; |
| 48 | + new_symbol.pretty_name = name; |
| 49 | + new_symbol.base_name = base_name; |
| 50 | + new_symbol.type = type; |
| 51 | + new_symbol.value = value; |
| 52 | + new_symbol.is_lvalue = true; |
| 53 | + new_symbol.is_state_var = true; |
| 54 | + new_symbol.is_static_lifetime = is_static_lifetime; |
| 55 | + new_symbol.is_thread_local = is_thread_local; |
| 56 | + new_symbol.mode = ID_java; |
| 57 | + symbol_table.add(new_symbol); |
| 58 | + return new_symbol; |
| 59 | +} |
| 60 | + |
| 61 | +/// Retrieve the first label identifier. This is used to mark the start of |
| 62 | +/// a thread block. |
| 63 | +/// /param id: unique thread block identifier |
| 64 | +/// /return: fully qualified label identifier |
| 65 | +static const std::string get_first_label_id(const std::string &id) |
| 66 | +{ |
| 67 | + return CPROVER_PREFIX "_THREAD_ENTRY_" + id; |
| 68 | +} |
| 69 | + |
| 70 | +/// Retrieve the second label identifier. This is used to mark the end of |
| 71 | +/// a thread block. |
| 72 | +/// /param id: unique thread block identifier |
| 73 | +/// /return: fully qualified label identifier |
| 74 | +static const std::string get_second_label_id(const std::string &id) |
| 75 | +{ |
| 76 | + return CPROVER_PREFIX "_THREAD_EXIT_" + id; |
| 77 | +} |
| 78 | + |
| 79 | +/// Retrieves a thread block identifier from a function call to |
| 80 | +/// CProver.startThread:(I)V or CProver.endThread:(I)V |
| 81 | +/// /param code: function call to CProver.startThread or CProver.endThread |
| 82 | +/// /return: unique thread block identifier |
| 83 | +static const std::string get_thread_block_identifier( |
| 84 | + const code_function_callt &f_code) |
| 85 | +{ |
| 86 | + PRECONDITION(f_code.arguments().size() == 1); |
| 87 | + const exprt &expr = f_code.arguments()[0]; |
| 88 | + mp_integer lbl_id = binary2integer(expr.op0().get_string(ID_value), false); |
| 89 | + return integer2string(lbl_id); |
| 90 | +} |
| 91 | + |
| 92 | +/// Transforms the codet stored in in \p f_code, which is a call to function |
| 93 | +/// CProver.startThread:(I)V into a block of code as described by the |
| 94 | +/// documentation of function convert_thread_block |
| 95 | +/// |
| 96 | +/// The resulting code_blockt is stored in the output parameter \p code. |
| 97 | +/// |
| 98 | +/// \param f_code: function call to CProver.startThread:(I)V |
| 99 | +/// \param [out] code: resulting transformation |
| 100 | +/// \param symbol_table: a symbol table |
| 101 | +static void instrument_start_thread( |
| 102 | + const code_function_callt &f_code, |
| 103 | + codet &code, |
| 104 | + symbol_tablet &symbol_table) |
| 105 | +{ |
| 106 | + PRECONDITION(f_code.arguments().size() == 1); |
| 107 | + |
| 108 | + // Create global variable __CPROVER__next_thread_id. Used as a counter |
| 109 | + // in-order to to assign ids to new threads. |
| 110 | + const symbolt &next_symbol = |
| 111 | + add_or_get_symbol( |
| 112 | + symbol_table, next_thread_id, next_thread_id, |
| 113 | + java_int_type(), |
| 114 | + from_integer(0, java_int_type()), false, true); |
| 115 | + |
| 116 | + // Create thread-local variable __CPROVER__thread_id. Holds the id of |
| 117 | + // the thread. |
| 118 | + const symbolt ¤t_symbol = |
| 119 | + add_or_get_symbol( |
| 120 | + symbol_table, thread_id, thread_id, java_int_type(), |
| 121 | + from_integer(0, java_int_type()), true, true); |
| 122 | + |
| 123 | + // Construct appropriate labels. |
| 124 | + // Note: java does not have labels so this should be safe. |
| 125 | + const std::string &thread_block_id = get_thread_block_identifier(f_code); |
| 126 | + const std::string &lbl1 = get_first_label_id(thread_block_id); |
| 127 | + const std::string &lbl2 = get_second_label_id(thread_block_id); |
| 128 | + |
| 129 | + // Instrument the following codet's: |
| 130 | + // |
| 131 | + // A: codet(id=ID_start_thread, destination=__CPROVER_THREAD_ENTRY_<ID>) |
| 132 | + // B: codet(id=ID_goto, destination=__CPROVER_THREAD_EXIT_<ID>) |
| 133 | + // C: codet(id=ID_label, label=__CPROVER_THREAD_ENTRY_<ID>) |
| 134 | + // C.1: codet(id=ID_atomic_begin) |
| 135 | + // D: CPROVER__next_thread_id += 1; |
| 136 | + // E: __CPROVER__thread_id = __CPROVER__next_thread_id; |
| 137 | + // F: codet(id=ID_atomic_end) |
| 138 | + |
| 139 | + codet tmp_a(ID_start_thread); |
| 140 | + tmp_a.set(ID_destination, lbl1); |
| 141 | + code_gotot tmp_b(lbl2); |
| 142 | + code_labelt tmp_c(lbl1); |
| 143 | + tmp_c.op0() = codet(ID_skip); |
| 144 | + |
| 145 | + exprt plus(ID_plus, java_int_type()); |
| 146 | + plus.copy_to_operands(next_symbol.symbol_expr()); |
| 147 | + plus.copy_to_operands(from_integer(1, java_int_type())); |
| 148 | + code_assignt tmp_d(next_symbol.symbol_expr(), plus); |
| 149 | + code_assignt tmp_e(current_symbol.symbol_expr(), next_symbol.symbol_expr()); |
| 150 | + |
| 151 | + code_blockt block; |
| 152 | + block.add(tmp_a); |
| 153 | + block.add(tmp_b); |
| 154 | + block.add(tmp_c); |
| 155 | + block.add(codet(ID_atomic_begin)); |
| 156 | + block.add(tmp_d); |
| 157 | + block.add(tmp_e); |
| 158 | + block.add(codet(ID_atomic_end)); |
| 159 | + block.add_source_location() = f_code.source_location(); |
| 160 | + |
| 161 | + code = block; |
| 162 | +} |
| 163 | + |
| 164 | +/// Transforms the codet stored in in \p f_code, which is a call to function |
| 165 | +/// CProver.endThread:(I)V into a block of code as described by the |
| 166 | +/// documentation of the function convert_thread_block. |
| 167 | +/// |
| 168 | +/// The resulting code_blockt is stored in the output parameter \p code. |
| 169 | +/// |
| 170 | +/// \param f_code: function call to CProver.endThread:(I)V |
| 171 | +/// \param [out] code: resulting transformation |
| 172 | +/// \param symbol_table: a symbol table |
| 173 | +static void instrument_endThread( |
| 174 | + const code_function_callt &f_code, |
| 175 | + codet &code, |
| 176 | + symbol_tablet symbol_table) |
| 177 | +{ |
| 178 | + PRECONDITION(f_code.arguments().size() == 1); |
| 179 | + |
| 180 | + // Build id, used to construct appropriate labels. |
| 181 | + // Note: java does not have labels so this should be safe |
| 182 | + const std::string &thread_block_id = get_thread_block_identifier(f_code); |
| 183 | + const std::string &lbl2 = get_second_label_id(thread_block_id); |
| 184 | + |
| 185 | + // Instrument the following code: |
| 186 | + // |
| 187 | + // A: codet(id=ID_end_thread) |
| 188 | + // B: codet(id=ID_label,label= __CPROVER_THREAD_EXIT_<ID>) |
| 189 | + codet tmp_a(ID_end_thread); |
| 190 | + code_labelt tmp_b(lbl2); |
| 191 | + tmp_b.op0() = codet(ID_skip); |
| 192 | + |
| 193 | + code_blockt block; |
| 194 | + block.add(tmp_a); |
| 195 | + block.add(tmp_b); |
| 196 | + block.add_source_location() = code.source_location(); |
| 197 | + |
| 198 | + code = block; |
| 199 | +} |
| 200 | + |
| 201 | +/// Transforms the codet stored in in \p f_code, which is a call to function |
| 202 | +/// CProver.getCurrentThreadID:()I into a code_assignt as described by the |
| 203 | +/// documentation of the function convert_thread_block. |
| 204 | +/// |
| 205 | +/// The resulting code_blockt is stored in the output parameter \p code. |
| 206 | +/// |
| 207 | +/// \param f_code: function call to CProver.getCurrentThreadID:()I |
| 208 | +/// \param [out] code: resulting transformation |
| 209 | +/// \param symbol_table: a symbol table |
| 210 | +static void instrument_getCurrentThreadID( |
| 211 | + const code_function_callt &f_code, |
| 212 | + codet &code, |
| 213 | + symbol_tablet symbol_table) |
| 214 | +{ |
| 215 | + PRECONDITION(f_code.arguments().size() == 0); |
| 216 | + |
| 217 | + const symbolt& current_symbol = |
| 218 | + add_or_get_symbol(symbol_table, |
| 219 | + thread_id, |
| 220 | + thread_id, |
| 221 | + java_int_type(), |
| 222 | + from_integer(0, java_int_type()), |
| 223 | + true, true); |
| 224 | + |
| 225 | + code_assignt code_assign(f_code.lhs(), current_symbol.symbol_expr()); |
| 226 | + code_assign.add_source_location() = f_code.source_location(); |
| 227 | + |
| 228 | + code = code_assign; |
| 229 | +} |
| 230 | + |
| 231 | +/// Iterate through the symbol table to find and appropriately instrument |
| 232 | +/// thread-blocks. |
| 233 | +/// |
| 234 | +/// A thread block is a sequence of codet's surrounded with calls to |
| 235 | +/// CProver.startThread:(I)V and CProver.endThread:(I)V. A thread block |
| 236 | +/// corresponds to the body of the thread to be created. The only parameter |
| 237 | +/// accepted by these functions is an integer used to differentiate between |
| 238 | +/// different thread blocks. Function startThread() is transformed into a codet |
| 239 | +/// ID_start_thread, which carries a target label in the field 'destination'. |
| 240 | +/// Similarly endThread() is transformed into a codet ID_end_thread, which |
| 241 | +/// marks the end of the thread body. Both codet's will later be transformed |
| 242 | +/// (in goto_convertt) into the goto instructions START_THREAD and END_THREAD. |
| 243 | +/// |
| 244 | +/// Additionally the variable __CPROVER__thread_id (thread local) will store |
| 245 | +/// the ID of the new thread. The new id is obtained by incrementing a global |
| 246 | +/// variable __CPROVER__next_thread_id. |
| 247 | +/// |
| 248 | +/// The ID of the thread is made accessible to the Java program by having calls |
| 249 | +/// to the function 'CProver.getCurrentThreadID()I' replaced by the variable |
| 250 | +/// __CPROVER__thread_id. We also perform this substitution in here. The |
| 251 | +/// substitution that we perform here assumes that calls to |
| 252 | +/// getCurrentThreadID() are ONLY made in the RHS of an expression. |
| 253 | +/// |
| 254 | +/// Example: |
| 255 | +/// |
| 256 | +/// \code |
| 257 | +/// CProver.startThread(333) |
| 258 | +/// ... // thread body |
| 259 | +/// CProver.endThread(333) |
| 260 | +/// \endcode |
| 261 | +/// |
| 262 | +/// Is transformed into: |
| 263 | +/// |
| 264 | +/// \code |
| 265 | +/// codet(id=ID_start_thread, destination=__CPROVER_THREAD_ENTRY_333) |
| 266 | +/// codet(id=ID_goto, destination=__CPROVER_THREAD_EXIT_333) |
| 267 | +/// codet(id=ID_label, label=__CPROVER_THREAD_ENTRY_333) |
| 268 | +/// codet(id=ID_atomic_begin) |
| 269 | +/// __CPROVER__next_thread_id += 1; |
| 270 | +/// __CPROVER__thread_id = __CPROVER__next_thread_id; |
| 271 | +/// ... // thread body |
| 272 | +/// codet(id=ID_end_thread) |
| 273 | +/// codet(id=ID_label, label=__CPROVER_THREAD_EXIT_333) |
| 274 | +/// \endcode |
| 275 | +/// |
| 276 | +/// Observe that the semantics of ID_start_thread roughly corresponds to: fork |
| 277 | +/// the current thread, continue the execution of the current thread in the |
| 278 | +/// next line, and continue the execution of the new thread at the destination |
| 279 | +/// field of the codet (__CPROVER_THREAD_ENTRY_333). |
| 280 | +/// |
| 281 | +/// Note: the current version assumes that a call to startThread(n), where n is |
| 282 | +/// an immediate integer, is in the same scope (nesting block) as a call to |
| 283 | +/// endThread(n). Some assertion will fail during symex if this is not the case. |
| 284 | +/// |
| 285 | +/// Note': the ID of the thread is assigned after the thread is created, this |
| 286 | +/// creates bogus interleavings. Currently, it's not possible to |
| 287 | +/// assign the thread ID before the creation of the thead, due to a bug in |
| 288 | +/// symex. See https://github.com/diffblue/cbmc/issues/1630/for more details. |
| 289 | +/// |
| 290 | +/// \param symbol_table: a symbol table |
| 291 | +void convert_threadblock(symbol_tablet &symbol_table) |
| 292 | +{ |
| 293 | + using instrument_callbackt = |
| 294 | + std::function<void(const code_function_callt&, codet&, symbol_tablet&)>; |
| 295 | + using expr_replacement_mapt = |
| 296 | + std::unordered_map<const exprt, instrument_callbackt, irep_hash>; |
| 297 | + |
| 298 | + namespacet ns(symbol_table); |
| 299 | + |
| 300 | + for(auto entry : symbol_table) |
| 301 | + { |
| 302 | + expr_replacement_mapt expr_replacement_map; |
| 303 | + const symbolt &symbol = entry.second; |
| 304 | + |
| 305 | + // Look for code_function_callt's (without breaking sharing) |
| 306 | + // and insert each one into a replacement map along with an associated |
| 307 | + // callback that will handle their instrumentation. |
| 308 | + for(auto it = symbol.value.depth_begin(), itend = symbol.value.depth_end(); |
| 309 | + it != itend; ++it) |
| 310 | + { |
| 311 | + instrument_callbackt cb; |
| 312 | + |
| 313 | + const exprt &expr = *it; |
| 314 | + if(expr.id() != ID_code) |
| 315 | + continue; |
| 316 | + |
| 317 | + const codet &code = to_code(expr); |
| 318 | + if(code.get_statement() != ID_function_call) |
| 319 | + continue; |
| 320 | + |
| 321 | + const code_function_callt &f_code = to_code_function_call(code); |
| 322 | + const std::string &f_name = expr2java(f_code.function(), ns); |
| 323 | + if(f_name == "org.cprover.CProver.startThread:(I)V") |
| 324 | + cb = std::bind(instrument_start_thread, std::placeholders::_1, |
| 325 | + std::placeholders::_2, std::placeholders::_3); |
| 326 | + else if(f_name == "org.cprover.CProver.endThread:(I)V") |
| 327 | + cb = std::bind(&instrument_endThread, std::placeholders::_1, |
| 328 | + std::placeholders::_2, std::placeholders::_3); |
| 329 | + else if(f_name == "org.cprover.CProver.getCurrentThreadID:()I") |
| 330 | + cb = std::bind(&instrument_getCurrentThreadID, std::placeholders::_1, |
| 331 | + std::placeholders::_2, std::placeholders::_3); |
| 332 | + |
| 333 | + if(cb) |
| 334 | + expr_replacement_map.insert({expr, cb}); |
| 335 | + } |
| 336 | + |
| 337 | + if(!expr_replacement_map.empty()) |
| 338 | + { |
| 339 | + symbolt &w_symbol = symbol_table.get_writeable_ref(entry.first); |
| 340 | + // Use expr_replacment_map to look for exprt's that need to be replaced. |
| 341 | + // Once found, get a non-const exprt (breaking sharing in the process) and |
| 342 | + // call it's associated instrumentation function. |
| 343 | + for(auto it = w_symbol.value.depth_begin(), |
| 344 | + itend = w_symbol.value.depth_end(); it != itend;) |
| 345 | + { |
| 346 | + expr_replacement_mapt::iterator m_it = expr_replacement_map.find(*it); |
| 347 | + if(m_it != expr_replacement_map.end()) |
| 348 | + { |
| 349 | + codet &code = to_code(it.mutate()); |
| 350 | + const code_function_callt &f_code = to_code_function_call(code); |
| 351 | + m_it->second(f_code, code, symbol_table); |
| 352 | + it.next_sibling_or_parent(); |
| 353 | + |
| 354 | + expr_replacement_map.erase(m_it); |
| 355 | + if(expr_replacement_map.empty()) |
| 356 | + break; |
| 357 | + } |
| 358 | + else |
| 359 | + ++it; |
| 360 | + } |
| 361 | + } |
| 362 | + } |
| 363 | +} |
0 commit comments