PageRenderTime 1663ms CodeModel.GetById 29ms RepoModel.GetById 13ms app.codeStats 0ms

/bin/storm.py

#
Python | 2396 lines | 2394 code | 1 blank | 1 comment | 5 complexity | 89c98e850de8cef6534394a249c839f8 MD5 | raw file
  1. # Main STORM instrumentation script
  2. usage = """
  3. STORM 0.1
  4. Usage: python storm.py -d directory_name [options ...]
  5. STORM command line options:
  6. -d (or --dir) - directory of the example
  7. -h (or --help) - help
  8. -o (or --output) - final instrumented output bpl file name (default is a.bpl)
  9. -k - max number of contexts K (default is 1, which is sequential
  10. case)
  11. --no-exceptions - don't throw exceptions in Schedule, etc. This can be unsound,
  12. but improves performance.
  13. --no-check - skip checking (running boogie) and just generate instrumented
  14. bpl file
  15. --no-compile - do not compile the code, just use already created test.bpl
  16. (speed up testing if the example is compiling slowly)
  17. --only-compile - just compile the code with HAVOC. HAVOC's output is test.bpl.
  18. --generate-sx - just generate the sx query with the given name.
  19. --generate-smt - just generate the smt query with the given name.
  20. --havoc-output - specify HAVOC's output file to be used as STORM's input.
  21. Default is test.bpl.
  22. --generate-trace - adds instrumentation necessary for generating error traces.
  23. --time - measure Boogie execution time using the ptime utility (ptime
  24. has to be installed for this switch to work)
  25. -z (or --z3) - prover log output name. If specified, Z3 is going to be
  26. executed on the query after Boogie. Needed only if we want to
  27. measure Z3 time separately.
  28. --cegar - runs the cegar algorithm for refining tracked fields
  29. """
  30. import sys, re, os, ConfigParser, getopt
  31. from collections import deque
  32. from generate_error_trace import generateErrorTrace
  33. # Globals
  34. boogieDir = os.environ.get("HAVOC_BOOGIE_DIR")
  35. assert boogieDir, "Set HAVOC_BOOGIE_DIR environment variable"
  36. boogieBin = str(boogieDir) + '\\boogie.exe'
  37. f = os.popen(boogieBin, 'r')
  38. boogieCheckStr = f.read()
  39. f.close()
  40. assert boogieCheckStr == '*** Error: No input files were specified.\n', "Calling booogie.exe failed or unexpected output from boogie.exe"
  41. boogieOpts = '/noinfer /timeLimit:600 /errorLimit:1'
  42. boogieHeuristics = [
  43. ['/z3opt:ARRAY_WEAK=true',
  44. '/z3opt:ARRAY_EXTENSIONAL=false',
  45. '/useArrayTheory'],
  46. ['/z3opt:RELEVANCY=0',
  47. '/z3opt:ARRAY_WEAK=true',
  48. '/z3opt:ARRAY_EXTENSIONAL=false',
  49. '/useArrayTheory']
  50. ]
  51. stormPrelude = """
  52. var raiseException : bool;
  53. var errorReached : bool;
  54. var k : int;
  55. var __storm_atomic : bool;
  56. var __storm_init : bool;
  57. var tid : int;
  58. var tidCount : int;
  59. procedure /* dummy comment */ {:inline 1} storm_getThreadID() returns (tidRet:int)
  60. {
  61. tidRet := tid;
  62. return;
  63. }
  64. """
  65. contextSwitch = """
  66. procedure contextSwitch();
  67. modifies k;
  68. ensures __storm_atomic ==> old(k) == k;
  69. ensures(old(k) <= k);
  70. ensures(k < %%K%%);
  71. """
  72. contextSwitchCall = """
  73. call contextSwitch();
  74. """
  75. mainProcedure = 'storm_main'
  76. procNotInlined = set(['havoc_assert', 'havoc_assume', '__HAVOC_free',
  77. '__HAVOC_malloc', 'nondet_choice', 'det_choice', 'storm_nondet',
  78. 'storm_getThreadID',
  79. '_strdup', '_xstrcasecmp', '_xstrcmp', 'contextSwitch',
  80. '__storm_assert_dummy',
  81. '__storm_atomic_begin_dummy', '__storm_atomic_end_dummy',
  82. '__storm_init_begin_dummy', '__storm_init_end_dummy',
  83. '__storm_end_dummy', '__storm_switch_dummy'])
  84. procNoRaiseExceptionCheck = set([mainProcedure])
  85. instrumentedGlobals = set()
  86. uninstrumentedGlobals = set()
  87. globalVars = set(['alloc', 'k', 'errorReached', 'raiseException', '__storm_atomic',
  88. '__storm_init', 'tid', 'tidCount'])
  89. abstractKeepFields = []
  90. abstractKeepFieldsRead = []
  91. abstractKeepFieldsAll = set()
  92. constantFields = []
  93. resources = []
  94. K = 1
  95. outputBplFile = 'a.bpl'
  96. outputZ3File = ''
  97. runBoogie = True
  98. noExceptions = False
  99. measureTime = False
  100. exampleDir = ''
  101. compile = True
  102. onlyCompile = False
  103. generateSx = ''
  104. generateSmt = ''
  105. havocOutput = 'test.bpl'
  106. generateTrace = False
  107. cegar = False
  108. loopUnroll = ''
  109. # Main - this is where everything starts
  110. def main():
  111. processCommandLine()
  112. os.chdir(exampleDir) # go to the example directory
  113. readConfiguration()
  114. # compile the example with HAVOC
  115. if compile or onlyCompile:
  116. runHavoc()
  117. if onlyCompile:
  118. sys.exit()
  119. # read output of HAVOC into bpl
  120. fin = open(havocOutput, 'r')
  121. bpl = fin.read()
  122. fin.close()
  123. if cegar:
  124. cegarLoop(bpl)
  125. else:
  126. bpl = initialProcessing(bpl) # get rid of alloc assumes, havoc_free etc.
  127. emptyProcedures = findEmptyProcedures(bpl) # Don't inline procedures without bodies
  128. procNotInlined.update(emptyProcedures)
  129. bpl = instrumentStormMain(bpl)
  130. bpl = instrumentThreads(bpl)
  131. bpl = replaceGlobalsWithScalars(bpl) # replace Mem[errorReached] --> errorReached, Mem[k] --> k
  132. if not noExceptions:
  133. bpl = putRaiseExceptionChecks(bpl)
  134. boogieResult = runOnlyBoogie(bpl)
  135. if generateTrace:
  136. l = re.compile('Boogie program verifier finished with 1 verified, 0 errors')
  137. m = l.search(boogieResult)
  138. if not m:
  139. l = re.compile('(.*).bpl')
  140. m = l.match(outputBplFile)
  141. assert m
  142. traceName = str(m.group(1)) + '_trace.txt'
  143. generateErrorTrace(boogieResult, 'tmp.bpl', outputBplFile, traceName)
  144. # Process command line arguments
  145. def processCommandLine():
  146. global K
  147. global outputBplFile
  148. global outputZ3File
  149. global runBoogie
  150. global noExceptions
  151. global measureTime
  152. global contextSwitch
  153. global contextSwitchCall
  154. global exampleDir
  155. global compile
  156. global onlyCompile
  157. global generateSx
  158. global generateSmt
  159. global havocOutput
  160. global generateTrace
  161. global cegar
  162. global loopUnroll
  163. try:
  164. opts, args = getopt.getopt(sys.argv[1:], "hd:o:k:z:",
  165. ["help", "dir=", "output=", "no-exceptions", "no-check",
  166. "no-compile", "only-compile", "generate-sx=", "generate-smt=",
  167. "havoc-output=", "generate-trace", "time", "z3=", "cegar",
  168. "loop-unroll="])
  169. except getopt.GetoptError, err:
  170. # print help information and exit:
  171. print str(err) # will print something like "option -a not recognized"
  172. print "Type python storm.py -h for help."
  173. sys.exit(2)
  174. for o, a in opts:
  175. if o in ("-h", "--help"):
  176. print usage
  177. sys.exit()
  178. elif o in ("-d", "--dir"):
  179. exampleDir = a
  180. elif o in ("-o", "--output"):
  181. outputBplFile = a
  182. elif o in ("-k"):
  183. K = int(a)
  184. elif o in ("--no-exceptions"):
  185. noExceptions = True
  186. elif o in ("--no-check"):
  187. runBoogie = False
  188. elif o in ("--no-compile"):
  189. compile = False
  190. elif o in ("--only-compile"):
  191. onlyCompile = True
  192. elif o in ("--generate-sx"):
  193. generateSx = a
  194. elif o in ("--generate-smt"):
  195. generateSmt = a
  196. elif o in ("--havoc-output"):
  197. havocOutput = a
  198. elif o in ("--generate-trace"):
  199. generateTrace = True
  200. elif o in ("--time"):
  201. measureTime = True
  202. elif o in ("-z", "--z3"):
  203. outputZ3File = a
  204. elif o in ("--cegar"):
  205. cegar = True
  206. elif o in ("--loop-unroll"):
  207. loopUnroll = '/loopUnroll:' + str(int(a))
  208. else:
  209. print "Unhandled option.\nType python storm.py -h for help."
  210. sys.exit(2)
  211. # print "Dir = " + exampleDir
  212. # print "Output = " + outputBplFile
  213. # print "K = " + K
  214. if exampleDir == '':
  215. print "Example directory not specified.\nType python storm.py -h for help."
  216. sys.exit(2)
  217. assert generateSx == '' or generateSmt == ''
  218. assert K >= 1
  219. if generateTrace:
  220. assert loopUnroll, "Error trace generation works only with the loop-unroll option"
  221. contextSwitchCall += '\n'
  222. for i in range(0, K):
  223. if i == 0:
  224. contextSwitchCall += ' if (k == 0) {\n'
  225. else:
  226. contextSwitchCall += ' } else if (k == ' + str(i) + ') {\n'
  227. contextSwitchCall += ' call storm_context_' + str(i) + '();\n'
  228. contextSwitchCall += ' }\n'
  229. # Read configuration from storm.config
  230. def readConfiguration():
  231. config = ConfigParser.ConfigParser()
  232. if cegar:
  233. config.read("storm_cegar.config")
  234. else:
  235. config.read("storm.config")
  236. instrumentedGlobals.update(set(tokenize(config.get("storm_config", "instrumented_globals"))))
  237. if config.has_option("storm_config", "uninstrumented_globals"):
  238. uninstrumentedGlobals.update(set(tokenize(config.get("storm_config", "uninstrumented_globals"))))
  239. procNotInlined.update(set(tokenize(config.get("storm_config", "proc_not_inlined"))))
  240. procNoRaiseExceptionCheck.update(procNotInlined)
  241. abstractKeepFields.extend(tokenize(config.get("storm_config", "abstract_keep_fields")))
  242. abstractKeepFieldsRead.extend(tokenize(config.get("storm_config", "abstract_keep_fields_read")))
  243. constantFields.extend(tokenize(config.get("storm_config", "cegar_constant_fields")))
  244. resources.extend(tokenize(config.get("storm_config", "resources")))
  245. abstractKeepFieldsAll.update(set(abstractKeepFields), set(abstractKeepFieldsRead))
  246. def tokenize(configStr):
  247. tokens = []
  248. p = re.compile('([a-zA-Z0-9_]+)([, ]*)')
  249. iter = p.finditer(configStr)
  250. for match in iter:
  251. token = match.group(1)
  252. tokens.append(token)
  253. return tokens
  254. # Running HAVOC in the example's directory
  255. def runHavoc():
  256. print 'Compiling example directory with HAVOC...\n'
  257. havocDir = os.environ.get("STORM_ROOT") + '\\havoc'
  258. # Run EspFe
  259. os.system('nmake -nologo > espfe.dmp')
  260. os.system('dir /b/s *.rawcfgf > system.cfg.files')
  261. # Generate blob
  262. os.system(havocDir + '\\cfglink /DoVMap=false /DoSCFGMerging=true /RemoveLinkage=true /CFGFiles=@system.cfg.files')
  263. # Run translator
  264. os.system(havocDir + '\\ctobpl /BreakAtAssert=1 /RunDirectory=. /SETTINGS=' + havocDir + '\\havoc.config')
  265. # Clean
  266. f = os.popen('nmake clean', 'r')
  267. f.close()
  268. print 'Done compiling...\n'
  269. # Initial processing doing a few replacementes not falling into any category...
  270. # Most of this stuff should be done by HAVOC.
  271. def initialProcessing(bpl):
  272. print 'Initial processing...\n'
  273. p = re.compile('%%K%%')
  274. contextSwitchTmp = p.sub(str(K), contextSwitch)
  275. contextProcedures = '\n'
  276. for i in range(0, K):
  277. procNotInlined.add('storm_context_' + str(i))
  278. contextProcedures += 'procedure storm_context_' + str(i) + '();\n'
  279. bpl = stormPrelude + contextProcedures + contextSwitchTmp + bpl
  280. p = re.compile('//TAG: alloc is always > 0.*?free ensures INT_LEQ\(old\(alloc\), alloc\);', re.DOTALL)
  281. bpl = p.sub('', bpl)
  282. p = re.compile('free ensures .*;\n')
  283. bpl = p.sub('', bpl)
  284. p = re.compile('//TAG: havoc memory locations by default[ ]*\n')
  285. bpl = p.sub('', bpl)
  286. p = re.compile('//TAG: requires .*?\n')
  287. bpl = p.sub('', bpl)
  288. p = re.compile('requires INT_GEQ\(obj_size, 0\);.*\n')
  289. bpl = p.sub('free requires INT_GEQ(obj_size, 0);\n', bpl)
  290. p = re.compile('assume LOOP_([a-zA-Z0-9_]*)_Res_.+ == Res_.+;\n')
  291. bpl = p.sub('', bpl)
  292. p = re.compile('ensures old\(alloc\) <= alloc;\n')
  293. bpl = p.sub('', bpl)
  294. p = re.compile('[ ]*assume[ ]+INT_LT\(.+,[ ]*alloc\)[ ]*;[ ]*\n')
  295. bpl = p.sub('', bpl)
  296. p = re.compile('/\*assert \*/ assume INT_LEQ\(.+, alloc\);\n')
  297. bpl = p.sub('', bpl)
  298. p = re.compile('procedure storm_getThreadID\(\) returns \(ret:int\);\n')
  299. bpl = p.sub('', bpl)
  300. # Removing the "name" type
  301. p = re.compile('type name;\n')
  302. bpl = p.sub('', bpl)
  303. p = re.compile('var Mem: \[name\]\[int\]int;\n')
  304. bpl = p.sub('', bpl)
  305. p = re.compile('function Field\(int\) returns \(name\);\n')
  306. bpl = p.sub('', bpl)
  307. p = re.compile('const unique .*:name;\n')
  308. bpl = p.sub('', bpl)
  309. p = re.compile('var LOOP_.*_Mem:\[name\]\[int\]int;\n')
  310. bpl = p.sub('', bpl)
  311. p = re.compile('LOOP_.*_Mem := Mem;\n')
  312. bpl = p.sub('', bpl)
  313. # Get rid of all Base stuff - sometimes those blow up
  314. p = re.compile('function Base\(int\) returns \(int\);\n')
  315. bpl = p.sub('', bpl)
  316. p = re.compile('//axiom\(forall x: int :: {Base\(x\)} Base\(x\) <= x\);\n')
  317. bpl = p.sub('', bpl)
  318. p = re.compile('axiom\(forall x: int :: {Base\(x\)} INT_LEQ\(Base\(x\), x\)\);\n')
  319. bpl = p.sub('', bpl)
  320. p = re.compile('axiom\(forall b:int, a:int, t:name :: {MatchBase\(b, a, T.Ptr\(t\)\)} MatchBase\(b, a, T.Ptr\(t\)\) <==> Base\(a\) == b\);\n')
  321. bpl = p.sub('', bpl)
  322. p = re.compile('axiom\(forall v:int, t:name :: {HasType\(v, T.Ptr\(t\)\)} HasType\(v, T.Ptr\(t\)\) <==> \(v == 0 \|\| \(INT_GT\(v, 0\) && Match\(v, t\) && MatchBase\(Base\(v\), v, t\)\)\)\);\n')
  323. bpl = p.sub('', bpl)
  324. p = re.compile('ensures Base\(new\) == new;\n')
  325. bpl = p.sub('', bpl)
  326. p = re.compile('axiom\(Base\(.+\) == .+\);\n')
  327. bpl = p.sub('', bpl)
  328. p = re.compile('assume \(Base\(.+\) == .+\);\n')
  329. bpl = p.sub('', bpl)
  330. p = re.compile('type byte;.*FourBytesToInt\(c0, c1, c2, c3\) ==> b0 == c0 && b1 == c1 && b2 == c2 && b3 == c3\);', re.DOTALL)
  331. bpl = p.sub('', bpl)
  332. p = re.compile('function Equal\(\[int\]bool, \[int\]bool\) returns \(bool\);.*Unified\(M\[Field\(x\) := M\[Field\(x\)\]\[x := y\]\]\) == Unified\(M\)\[x := y\]\);', re.DOTALL)
  333. bpl = p.sub('', bpl)
  334. p = re.compile('function Match\(a:int, t:name\) returns \(bool\);.*Field\(a\) == T.Ptr\(t\)\);', re.DOTALL)
  335. bpl = p.sub('', bpl)
  336. p = re.compile('axiom\(forall a:int, b:int :: {BIT_BAND\(a,b\)}.*{BIT_BAND\(a,b\)} a == 0 \|\| b == 0 ==> BIT_BAND\(a,b\) == 0\);', re.DOTALL)
  337. bpl = p.sub('', bpl)
  338. p = re.compile('axiom\(forall a:int, b:int :: {DIV\(a,b\)}.*a > b \* \(DIV\(a,b\) \+ 1\)[ ]*\n[ ]*\);', re.DOTALL)
  339. bpl = p.sub('', bpl)
  340. p = re.compile('function POW2\(a:int\) returns \(bool\);.*axiom POW2\(33554432\);', re.DOTALL)
  341. bpl = p.sub('', bpl)
  342. p = re.compile('procedure nondet_choice\(\) returns \(x:int\);.*ensures x == DetChoiceFunc\(old\(detChoiceCnt\)\);', re.DOTALL)
  343. bpl = p.sub('', bpl)
  344. p = re.compile('[ ]*call[ ]+__storm_assert_dummy[ ]*\(\);\n([ ]*goto[ ]+[^;]*;)\n')
  345. bpl = p.sub('errorReached := true;\nraiseException := true;\n__storm_atomic := false;\n__storm_init := false;\ngoto label_1;\n', bpl)
  346. p = re.compile('[ ]*call[ ]+__storm_atomic_begin_dummy[ ]*\(\);\n')
  347. bpl = p.sub('__storm_atomic := true;\n', bpl)
  348. p = re.compile('[ ]*call[ ]+__storm_atomic_end_dummy[ ]*\(\);\n')
  349. bpl = p.sub('if (!__storm_init) {__storm_atomic := false;}\n' + contextSwitchCall, bpl)
  350. p = re.compile('[ ]*call[ ]+__storm_init_begin_dummy[ ]*\(\);\n')
  351. bpl = p.sub('__storm_atomic := true; __storm_init := true;\n', bpl)
  352. p = re.compile('[ ]*call[ ]+__storm_init_end_dummy[ ]*\(\);\n')
  353. bpl = p.sub('__storm_atomic := false; __storm_init := false;\n', bpl)
  354. p = re.compile('[ ]*call[ ]+__storm_switch_dummy[ ]*\(\);\n')
  355. bpl = p.sub(contextSwitchCall, bpl)
  356. p = re.compile('function .*Inv\(int\) returns \(int\);\n')
  357. bpl = p.sub('', bpl)
  358. p = re.compile('function _S_.*Inv\(\[int\]bool\) returns \(\[int\]bool\);\n')
  359. bpl = p.sub('', bpl)
  360. p = re.compile('function _S_.*\(\[int\]bool\) returns \(\[int\]bool\);\n')
  361. bpl = p.sub('', bpl)
  362. p = re.compile('axiom \(forall x:int :: {.*Inv\(.*\(x\)\)} .*Inv\(.*\(x\)\) == x\);\n')
  363. bpl = p.sub('', bpl)
  364. p = re.compile('axiom \(forall x:int :: {.*Inv\(x\)} .*\(.*Inv\(x\)\) == x\);\n')
  365. bpl = p.sub('', bpl)
  366. p = re.compile('axiom \(forall x:int, S:\[int\]bool :: {_S_.*\(S\)\[x\]} _S_.*\(S\)\[x\] <==> S\[.*Inv\(x\)\]\);\n')
  367. bpl = p.sub('', bpl)
  368. p = re.compile('axiom \(forall x:int, S:\[int\]bool :: {_S_.*Inv\(S\)\[x\]} _S_.*Inv\(S\)\[x\] <==> S\[.*\(x\)\]\);\n')
  369. bpl = p.sub('', bpl)
  370. p = re.compile('axiom \(forall x:int, S:\[int\]bool :: {S\[x\], _S_.*\(S\)} S\[x\] ==> _S_.*\(S\)\[.*\(x\)\]\);\n')
  371. bpl = p.sub('', bpl)
  372. p = re.compile('axiom \(forall x:int, S:\[int\]bool :: {_S_.*\(S\)\[x\]} _S_.*\(S\)\[x\] <==> S\[.*Inv\(x\)\]\);\n')
  373. bpl = p.sub('', bpl)
  374. p = re.compile('axiom \(forall x:int, S:\[int\]bool :: {S\[x\], _S_.*Inv\(S\)} S\[x\] ==> _S_.*Inv\(S\)\[.*Inv\(x\)\]\);\n')
  375. bpl = p.sub('', bpl)
  376. p = re.compile('//axiom \(forall x:int :: {.*Inv\(x\)} .*Inv\(x\) == x - .*\);\n')
  377. bpl = p.sub('', bpl)
  378. p = re.compile('axiom \(forall x:int :: {.*Inv\(x\)} .*Inv\(x\) == INT_SUB\(x,.+\)\);\n')
  379. bpl = p.sub('', bpl)
  380. p = re.compile('axiom \(forall x:int :: {MINUS_BOTH_PTR_OR_BOTH_INT\(x, .*, .*\)} MINUS_BOTH_PTR_OR_BOTH_INT\(x, .*, .*\) == .*Inv\(x\)\);\n')
  381. bpl = p.sub('', bpl)
  382. p = re.compile('axiom \(forall x:int :: {MINUS_LEFT_PTR\(x, .*, .*\)} MINUS_LEFT_PTR\(x, .*, .*\) == .*Inv\(x\)\);\n')
  383. bpl = p.sub('', bpl)
  384. p = re.compile('//axiom \(forall x:int :: {.+\(x\)} .+\(x\) == PLUS\(x, 1, [0-9]+\)\);\n')
  385. bpl = p.sub('', bpl)
  386. p = re.compile('axiom \(forall x:int :: {.+\(x\)} .+\(x\) == PLUS\(x, 1, [0-9]+\)\);\n')
  387. bpl = p.sub('', bpl)
  388. return bpl
  389. # Find procedures without bodies in our bpl file. We should not try to inline
  390. # those.
  391. def findEmptyProcedures(bpl):
  392. print 'Finding empty procedures...'
  393. lines = bpl.splitlines(True)
  394. emptyProcedures = set()
  395. procedureEmpty = False
  396. for line in lines:
  397. procDef = re.compile('procedure[ ]*([a-zA-Z0-9_]*)\((.*)\)[ ]*(;?)')
  398. m = procDef.match(line)
  399. if m:
  400. if procedureEmpty and not procedureName in procNotInlined:
  401. emptyProcedures.add(procedureName)
  402. procedureName = m.group(1)
  403. procedureEmpty = True
  404. start = re.compile('start:')
  405. m = start.match(line)
  406. if m:
  407. procedureEmpty = False
  408. if procedureEmpty and not procedureName in procNotInlined:
  409. emptyProcedures.add(procedureName)
  410. print 'Empty procedures: ' + ', '.join(emptyProcedures) + '\n'
  411. return emptyProcedures
  412. # Add k = 0, errorReached = false in the beginning of storm_main, and the
  413. # final assertion in the end.
  414. def instrumentStormMain(bpl):
  415. print 'Instrumenting storm_main...\n'
  416. lines = bpl.splitlines(True)
  417. # count the total number of threads
  418. threadCounter = 0
  419. asyncRe = re.compile('[ ]*call[ ]+{:async}[ ]+.*;\n')
  420. for line in lines:
  421. match = asyncRe.match(line)
  422. if match:
  423. threadCounter += 1
  424. # create __storm_thread_done variables
  425. newLines = []
  426. for i in range(threadCounter):
  427. globalVars.add('__storm_thread_done_' + str(i))
  428. newLines.append('var __storm_thread_done_' + str(i) + ' : bool;\n')
  429. # instrument storm_main
  430. foundMain = False
  431. for line in lines:
  432. l = re.compile('[ ]*procedure[ ]+' + mainProcedure + '.*\)\n')
  433. m = l.match(line)
  434. if m:
  435. foundMain = True
  436. newLines.append(line)
  437. newLines.append('free requires INT_LT(0, alloc);\n')
  438. newLines.append('free requires INT_LT(0, tid);\n')
  439. newLines.append('free requires INT_LT(tid, tidCount);\n')
  440. continue
  441. l = re.compile('[ ]*start:\n')
  442. m = l.match(line)
  443. if m and foundMain:
  444. newLines.append(line)
  445. for i in range(threadCounter):
  446. newLines.append('__storm_thread_done_' + str(i) + ' := false;\n')
  447. newLines.append('k := 0;\n')
  448. newLines.append('errorReached := false;\n')
  449. newLines.append('__storm_atomic := false;\n')
  450. newLines.append('__storm_init := false;\n')
  451. continue
  452. l = re.compile('[ ]*call[ ]+__storm_end_dummy[ ]*\(\);\n')
  453. m = l.match(line)
  454. if m:
  455. for i in range(threadCounter):
  456. newLines.append('assume errorReached || __storm_thread_done_' + str(i) + ';\n')
  457. newLines.append('if (!errorReached) {k := ' + str(K - 1) + ';}\n')
  458. newLines.append('raiseException := false;\n')
  459. continue
  460. l = re.compile('[ ]*label_1:\n')
  461. m = l.match(line)
  462. if m and foundMain:
  463. foundMain = False
  464. newLines.append(line)
  465. newLines.append('assert !errorReached;\n')
  466. continue
  467. newLines.append(line)
  468. return ''.join(newLines)
  469. # Add k = 0, raiseException = false in the beginning of each thread.
  470. def instrumentThreads(bpl):
  471. print 'Instrumenting threads...\n'
  472. lines = bpl.splitlines(True)
  473. inProc = False # used to mark procedure entry
  474. newLines = []
  475. procLines = []
  476. totalThreadCounter = 0
  477. for line in lines:
  478. if not inProc:
  479. newLines.append(line)
  480. # matching procedure start
  481. l = re.compile('{\n')
  482. m = l.match(line)
  483. if m:
  484. threadCounter = 0
  485. inProc = True
  486. continue
  487. if not inProc:
  488. continue
  489. # matching procedure end
  490. l = re.compile('}\n')
  491. m = l.match(line)
  492. if m:
  493. # adding declarations of introduced help variables to each procedure
  494. if 0 < threadCounter:
  495. procLines.insert(0, 'var tidCount_old : int;\n')
  496. for i in range(threadCounter):
  497. procLines.insert(0, 'var k_old_' + str(i) + ' : int;\n')
  498. procLines.insert(0, 'var tid_old_' + str(i) + ' : int;\n')
  499. newLines.extend(procLines)
  500. del procLines[:]
  501. newLines.append(line)
  502. inProc = False
  503. continue
  504. asyncRe = re.compile('[ ]*call[ ]+{:async}[ ]+(.*);\n')
  505. match = asyncRe.match(line)
  506. if match:
  507. procLines.append('k_old_' + str(threadCounter) + ' := k;\n')
  508. procLines.append('tid_old_' + str(threadCounter) + ' := tid;\n')
  509. procLines.append('tidCount_old := tidCount; havoc tidCount; assume tidCount_old < tidCount;\n')
  510. procLines.append('tid := tidCount;\n')
  511. procLines.append('raiseException := false;\n')
  512. for i in range(0, K):
  513. if i == 0:
  514. procLines.append(' if (k == 0) {\n')
  515. else:
  516. procLines.append(' } else if (k == ' + str(i) + ') {\n')
  517. procLines.append(' call storm_context_' + str(i) + '();\n')
  518. procLines.append(' }\n')
  519. procLines.append(contextSwitchCall)
  520. procLines.append('/* Don\'t put raiseException check on this call */ call ' + match.group(1) + ';\n')
  521. procLines.append('if (errorReached || !raiseException) {__storm_thread_done_' + str(totalThreadCounter) + ' := true;}\n')
  522. procLines.append('k := k_old_' + str(threadCounter) + ';\n')
  523. procLines.append('tid := tid_old_' + str(threadCounter) + ';\n')
  524. threadCounter += 1
  525. totalThreadCounter += 1
  526. else:
  527. procLines.append(line)
  528. return ''.join(newLines)
  529. # Replace instrumented globals stored on heap with scalars, i.e. replace Mem[x] with x
  530. # when x is an instrumented global. Also add those globals to modifies sets of
  531. # all procedures that are inlined and mark those procedures with ":inline 1".
  532. def replaceGlobalsWithScalars(bpl):
  533. print 'Replacing instrumented globals with scalars...\n'
  534. orGlobalVars = '|'.join(globalVars)
  535. p = re.compile('const unique (' + orGlobalVars + ') : int;')
  536. bpl = p.sub('', bpl)
  537. # replace declarations of instrumented globals with scalars and introduce
  538. # their copies
  539. orInstrumentedGlobals = '|'.join(instrumentedGlobals)
  540. p = re.compile('const unique (' + orInstrumentedGlobals + ') : int;')
  541. substitution = ''
  542. for i in range(0, K):
  543. substitution += r'var \1_' + str(i) + r' : int;\n'
  544. for i in range(1, K):
  545. substitution += r'var \1_s_' + str(i) + r' : int;\n'
  546. bpl = p.sub(substitution, bpl)
  547. orUninstrumentedGlobals = '|'.join(uninstrumentedGlobals)
  548. p = re.compile('const unique (' + orUninstrumentedGlobals + ') : int;')
  549. bpl = p.sub(r'var \1 : int;\n', bpl)
  550. modifiedGlobals = set(globalVars)
  551. modifiedGlobals.update(uninstrumentedGlobals)
  552. modifiedGlobals.update(g + '_' + str(i) for i in range(0, K) for g in instrumentedGlobals)
  553. modifiedGlobals.update(g + '_s_' + str(i) for i in range(1, K) for g in instrumentedGlobals)
  554. p = re.compile('procedure[ ]*([a-zA-Z0-9_]*)\((.*)\)[ ]*(;?)')
  555. bpl = p.sub(lambda match: addInlineModifies(match, modifiedGlobals), bpl)
  556. orGlobalVars = '|'.join(globalVars | instrumentedGlobals | uninstrumentedGlobals)
  557. p = re.compile('axiom\((' + orGlobalVars + ') != 0\);\n')
  558. bpl = p.sub('', bpl)
  559. # p = re.compile('axiom\(Base\((' + orGlobalVars + ')\) == (' + orGlobalVars + ')\);\n')
  560. # bpl = p.sub('', bpl)
  561. p = re.compile('Mem_T.[a-zA-Z0-9_]*\[(' + orGlobalVars + ')\]')
  562. bpl = p.sub(r'\1', bpl)
  563. p = re.compile('LOOP_[a-zA-Z0-9_]*_Mem_T.[a-zA-Z0-9_]*\[(LOOP_[a-zA-Z0-9_]*_(' + orGlobalVars + '))\]')
  564. bpl = p.sub(r'\1', bpl)
  565. p = re.compile('Mem_T.[a-zA-Z0-9_]* := Mem_T.[a-zA-Z0-9_]*\[(' + orGlobalVars + ') := ([a-zA-Z0-9_$.]*)\];')
  566. bpl = p.sub(r'\1 := \2;', bpl)
  567. return bpl
  568. def addInlineModifies(match, modifiedGlobals):
  569. procName = match.group(1)
  570. procDef = ''
  571. if procName in procNotInlined or procName == mainProcedure:
  572. procDef += 'procedure ' + procName + '(' + match.group(2) + ')' + match.group(3)
  573. else:
  574. procDef += 'procedure {:inline 1} ' + procName + '(' + match.group(2) + ')' + match.group(3)
  575. if not procName in procNotInlined:
  576. procDef += '\nmodifies ' + ', '.join(modifiedGlobals) + ';'
  577. return procDef
  578. # Putting raiseException checks after procedure calls.
  579. #
  580. # raiseException variable is used to model exceptions that are introduced during
  581. # instrumentation (see CAV paper). raiseException is going to be set to true
  582. # when exception is thrown. Once raiseException is set, we have to return from
  583. # all procedures in the current call chain. That is accomplished by instrumenting
  584. # each procedure call like this:
  585. #
  586. # call foo(); ---> call foo(); if (raiseException) {return;}
  587. #
  588. def putRaiseExceptionChecks(bpl):
  589. print 'Putting raiseException checks after procedure calls...\n'
  590. lines = bpl.splitlines(True)
  591. newLines = []
  592. for line in lines:
  593. newLines.append(line);
  594. l = re.compile('call .*?([^ :=()]*)[ ]*\(.*;\n')
  595. m = l.match(line)
  596. if m:
  597. if m.group(1) not in procNoRaiseExceptionCheck:
  598. newLines.append('if (raiseException) {goto label_1;}\n')
  599. return ''.join(newLines)
  600. def justRunBoogie(checkCmd):
  601. f = os.popen(checkCmd, 'r')
  602. boogieResult = f.read()
  603. f.close()
  604. return boogieResult
  605. # Run just boogie on abstraction
  606. def runOnlyBoogie(bpl):
  607. bpl = fieldAbstraction(bpl) # havocs reads/removes writes based on "abstract_keep_fields"
  608. bpl = explicitCaseSplit(bpl, abstractKeepFields, [], True) # if (k==i) St[Mem_i/Mem]
  609. if resources:
  610. bpl = explicitCaseSplitResource(bpl, resources, True); # if (k==i) St[Res_i/Res]
  611. bpl, header = inline(bpl) # write tmp.bpl to disk, call Boogie to do inlining, collect inlined storm_main
  612. # write output
  613. fout = open(outputBplFile, 'w')
  614. fout.write(bpl)
  615. fout.close()
  616. boogieResult = ''
  617. if runBoogie:
  618. # run Boogie
  619. print 'Running Boogie...\n'
  620. if measureTime:
  621. checkCmd = 'ptime ' + boogieBin + ' ' + outputBplFile + ' ' + boogieOpts
  622. else:
  623. checkCmd = boogieBin + ' ' + outputBplFile + ' ' + boogieOpts
  624. if not outputZ3File == '':
  625. checkCmd += ' /proverLog:' + outputZ3File
  626. if not generateSx == '':
  627. checkCmd += ' /noinfer /timeLimit:5 /errorLimit:1 /proverLog:' + generateSx
  628. elif not generateSmt == '':
  629. checkCmd += ' /noinfer /timeLimit:5 /errorLimit:1 /prover:SMTLib /smtOutput:' + generateSmt
  630. p = re.compile('time out')
  631. for parameters in boogieHeuristics:
  632. checkCmdTmp = checkCmd
  633. for param in parameters:
  634. checkCmdTmp += ' ' + param
  635. boogieResult = justRunBoogie(checkCmdTmp)
  636. if not p.search(boogieResult):
  637. break
  638. print boogieResult
  639. # run Z3
  640. if not outputZ3File == '':
  641. print 'Running Z3...\n'
  642. if measureTime:
  643. z3Cmd = 'ptime z3 ' + outputZ3File + ' /T:3600'
  644. else:
  645. z3Cmd = 'z3 ' + outputZ3File + ' /T:3600'
  646. f = os.popen(z3Cmd, 'r')
  647. boogieResult = f.read()
  648. f.close()
  649. print boogieResult
  650. return boogieResult
  651. # Field abstraction (see CAV paper).
  652. # Havocks (i.e. introduces nondet values) memory reads and skips (i.e. removes)
  653. # memory writes to untracked fields.
  654. def fieldAbstraction(bpl):
  655. print 'Field abstraction...\n'
  656. p = re.compile('var Mem_T.([a-zA-Z0-9_]*) : \[int\]int;\n')
  657. bpl = p.sub(removeMem, bpl)
  658. p = re.compile('modifies Mem_T.([a-zA-Z0-9_]*);\n')
  659. bpl = p.sub(removeMem, bpl)
  660. lines = bpl.splitlines(True)
  661. inProc = False # used to mark procedure entry
  662. newLines = []
  663. procLines = []
  664. for line in lines:
  665. if not inProc:
  666. newLines.append(line)
  667. # matching procedure start
  668. l = re.compile('{\n')
  669. m = l.match(line)
  670. if m:
  671. inProc = True
  672. nondetVarCounterMax = 0
  673. varCounterMax = 0
  674. continue
  675. if not inProc:
  676. continue
  677. # matching procedure end
  678. l = re.compile('}\n')
  679. m = l.match(line)
  680. if m:
  681. # adding declarations of introduced help variables to each procedure
  682. for i in reversed(range(0, varCounterMax)):
  683. procLines.insert(0, 'var myVar_' + str(i) + ':int;\n')
  684. for i in reversed(range(0, nondetVarCounterMax)):
  685. procLines.insert(0, 'var myNondetVar_' + str(i) + ':int;\n')
  686. newLines.extend(procLines)
  687. del procLines[:]
  688. newLines.append(line)
  689. inProc = False
  690. continue
  691. l = re.compile('assert[ ]+(.*);\n')
  692. m = l.match(line)
  693. if m:
  694. procLines.append(line)
  695. continue
  696. nondetVarCounter = 0
  697. varCounter = 0
  698. l = re.compile('call .*;\n')
  699. m = l.match(line)
  700. if m:
  701. # replacing all memory reads in a call statement with scalar variables
  702. # myVar_n or myNondetVar_n (where n is a counter) depending on whether the
  703. # field is tracked or not (myNondetVar_n is a nondeterministic variable,
  704. # i.e. havocked, introduced for untracked fields)
  705. l = re.compile('(.*)(Mem_T.([a-zA-Z0-9_]*)\[[^\[\]=]*\])(.*);\n')
  706. m = l.match(line)
  707. while m:
  708. field = m.group(3)
  709. if field in abstractKeepFieldsAll:
  710. var = 'myVar_' + str(varCounter)
  711. procLines.append(var + ' := ' + m.group(2) + ';\n')
  712. varCounter += 1
  713. else:
  714. var = 'myNondetVar_' + str(nondetVarCounter)
  715. procLines.append('havoc ' + var + ';\n')
  716. nondetVarCounter += 1
  717. line = m.group(1) + var + m.group(4) + ';\n'
  718. m = l.match(line)
  719. else:
  720. # replacing all memory reads of untracked fields in a statement with
  721. # nondeterministic scalar variables myNondetVar_n (where n is a counter)
  722. brackets = 0
  723. atLeastOneMatch = True
  724. while atLeastOneMatch:
  725. atLeastOneMatch = False
  726. lineChanged = True
  727. # this regular expression is black magic :), but it works
  728. l = re.compile('Mem_T.([a-zA-Z0-9_]*)\[([^=]*?\[){' + str(brackets) + ',' + str(brackets) + '}' \
  729. '[^\[\]=]*?(\][^=]*?){' + str(brackets) + ',' + str(brackets) + '}\]')
  730. brackets += 1
  731. while lineChanged:
  732. lineChanged = False
  733. iter = l.finditer(line)
  734. for match in iter:
  735. atLeastOneMatch = True
  736. field = match.group(1)
  737. if field in abstractKeepFieldsAll:
  738. var = 'myVar_' + str(varCounter)
  739. procLines.append(var + ' := ' + line[match.start():match.end()] + ';\n')
  740. line = line[0:match.start()] + var + line[match.end():]
  741. varCounter += 1
  742. lineChanged = True
  743. break
  744. else:
  745. var = 'myNondetVar_' + str(nondetVarCounter)
  746. procLines.append('havoc ' + var + ';\n')
  747. line = line[0:match.start()] + var + line[match.end():]
  748. nondetVarCounter += 1
  749. lineChanged = True
  750. break
  751. if varCounter > varCounterMax:
  752. varCounterMax = varCounter
  753. if nondetVarCounter > nondetVarCounterMax:
  754. nondetVarCounterMax = nondetVarCounter
  755. # skipping memory writes to untracked fields
  756. l = re.compile('Mem_T.([a-zA-Z0-9_]*) := Mem_T.[a-zA-Z0-9_]*\[.*\];\n')
  757. m = l.match(line)
  758. if m and not m.group(1) in abstractKeepFieldsAll:
  759. procLines.append('// skip memory write\n')
  760. continue
  761. # removing untracked fields from loop invariants (loop assignments used in
  762. # the translation of 'old' by HAVOC)
  763. l = re.compile('LOOP_[a-zA-Z0-9_]*_Mem_T.([a-zA-Z0-9_]*):=Mem_T.[a-zA-Z0-9_]*;\n')
  764. m = l.match(line)
  765. if m and not m.group(1) in abstractKeepFieldsAll:
  766. continue
  767. procLines.append(line)
  768. return ''.join(newLines)
  769. def removeMem(match):
  770. field = match.group(1)
  771. if not field in abstractKeepFieldsAll:
  772. return ''
  773. else:
  774. return match.group()
  775. # Explicit case split for memory and instrumented globals.
  776. # Replaces every memory and instrumented globals access (i.e. read or write of
  777. # one of the Mem maps, or read or write to an instrumented global G) in a
  778. # statement St with the appropriate copy of the Mem map/global and then
  779. # introduces the case-split on k (see CAV paper):
  780. #
  781. # St --->
  782. # if (k==0) St[Mem_0/Mem,G_0/G];
  783. # else if (k==1) St[Mem_1/Mem,G_1/G];
  784. # ....
  785. # else if (k==i) St[Mem_i/Mem,G_i/G];
  786. # ....
  787. # else if (k==K - 1) St[Mem_K-1/Mem,G_K-1/G]
  788. #
  789. # In addition, a call to contextSwitch (i.e. procedure Schedule from CAV paper)
  790. # is added after each such statement.
  791. # In the end, adds instrumentation assumes to the beggining and end of
  792. # storm_main.
  793. # Note: processedFields argument is used only with CEGAR
  794. #
  795. def explicitCaseSplit(bpl, abstractKeepFields, processedFields, putSwitch):
  796. print 'Explicit case splitting memories and instrumented globals...\n'
  797. lines = bpl.splitlines(True)
  798. newLines = [] # freshly generated lines that will replace what we currently have
  799. for line in lines:
  800. memRegExpr = re.compile('(.*)Mem_T\.([a-zA-Z0-9_]+)\[(.*);\n')
  801. if instrumentedGlobals:
  802. orGlobalVars = '|'.join(instrumentedGlobals)
  803. instrGlobalRegExpr = re.compile('(.*[^_]|^)(' + orGlobalVars + ')([^_].*;|;)\n')
  804. instrGlobalMatch = instrGlobalRegExpr.match(line)
  805. else:
  806. instrGlobalRegExpr = None
  807. instrGlobalMatch = None
  808. memMatch = memRegExpr.match(line)
  809. if memMatch and memMatch.group(2) in abstractKeepFields:
  810. # is statement contains Mem reads/writes
  811. instrLines = []
  812. for i in range(0, K):
  813. # replace Mem and G with Mem_i and G_i and store into instrLines
  814. tmpLine = line
  815. memMatch = memRegExpr.match(tmpLine)
  816. while memMatch:
  817. tmpLine = memRegExpr.sub(r'\1Mem_' + str(i) + r'_T.\2[\3;\n', tmpLine)
  818. memMatch = memRegExpr.match(tmpLine)
  819. if instrGlobalRegExpr:
  820. instrGlobalMatch = instrGlobalRegExpr.match(tmpLine)
  821. while instrGlobalMatch:
  822. tmpLine = instrGlobalRegExpr.sub(r'\1\2_' + str(i) + r'\3\n', tmpLine)
  823. instrGlobalMatch = instrGlobalRegExpr.match(tmpLine)
  824. instrLines.append(tmpLine)
  825. # assert(F); is instrumented as:
  826. # assert(k == 0 ==> F[Mem_0/Mem,G_0/G]);
  827. # assert(k == 1 ==> F[Mem_1/Mem,G_1/G]);
  828. # ....
  829. # assert(k == i ==> F[Mem_i/Mem,G_i/G]);
  830. # ....
  831. # assert(k == K-1 ==> F[Mem_K-1/Mem,G_K-1/G]);
  832. l = re.compile('[ ]*assert[ ]*(.*);\n')
  833. m = l.match(line)
  834. if m:
  835. for i in range(0, K):
  836. m = l.match(instrLines[i])
  837. newLines.append('assert (k == ' + str(i) + ' ==> (' + m.group(1) + '));\n')
  838. continue
  839. # assume(F); is instrumented as:
  840. # assume(k == 0 ==> F[Mem_0/Mem,G_0/G]);
  841. # assume(k == 1 ==> F[Mem_1/Mem,G_1/G]);
  842. # ....
  843. # assume(k == i ==> F[Mem_i/Mem,G_i/G]);
  844. # ....
  845. # assume(k == K-1 ==> F[Mem_K-1/Mem,G_K-1/G]);
  846. l = re.compile('[ ]*assume[ ]*(.*);\n')
  847. m = l.match(line)
  848. if m:
  849. for i in range(0, K):
  850. m = l.match(instrLines[i])
  851. newLines.append('assume (k == ' + str(i) + ' ==> (' + m.group(1) + '));\n')
  852. if putSwitch:
  853. newLines.append(contextSwitchCall)
  854. continue
  855. l = re.compile('[ ]*Mem.*:=.*;\n')
  856. m = l.match(line)
  857. if not m:
  858. # if statement is not memory assignment
  859. if K > 1:
  860. newLines.append(' if (k == 0) {\n')
  861. newLines.append(instrLines[0])
  862. for i in range(1, K):
  863. newLines.append(' } else if (k == ' + str(i) + ') {\n')
  864. newLines.append(instrLines[i])
  865. newLines.append(' }\n')
  866. else:
  867. newLines.append(instrLines[0])
  868. if putSwitch:
  869. newLines.append(contextSwitchCall)
  870. continue
  871. l = re.compile('[ ]*Mem_T\.([a-zA-Z0-9_]+) := Mem_T\.([a-zA-Z0-9_]+)\[.*\];\n')
  872. m = l.match(line)
  873. if m and m.group(1) in abstractKeepFields and m.group(2) in abstractKeepFields:
  874. # if statement is memory assignment
  875. if K > 1:
  876. newLines.append(' if (k == 0) {\n')
  877. l = re.compile('[ ]*Mem_T\.([a-zA-Z0-9_]*) := Mem_0_T\.([a-zA-Z0-9_]*\[.*\]);\n')
  878. m = l.match(instrLines[0])
  879. newLines.append('Mem_0_T.' + m.group(1) + ' := Mem_0_T.' + m.group(2) + ';\n')
  880. for i in range(1, K):
  881. newLines.append(' } else if (k == ' + str(i) + ') {\n')
  882. l = re.compile('[ ]*Mem_T\.([a-zA-Z0-9_]*) := Mem_' + str(i) + '_T\.([a-zA-Z0-9_]*\[.*\]);\n')
  883. m = l.match(instrLines[i])
  884. newLines.append('Mem_' + str(i) + '_T.' + m.group(1) + ' := Mem_' + str(i) + '_T.' + m.group(2) + ';\n')
  885. newLines.append(' }\n')
  886. else:
  887. l = re.compile('[ ]*Mem_T\.([a-zA-Z0-9_]*) := Mem_0_T\.([a-zA-Z0-9_]*\[.*\]);\n')
  888. m = l.match(instrLines[0])
  889. newLines.append('Mem_0_T.' + m.group(1) + ' := Mem_0_T.' + m.group(2) + ';\n')
  890. if putSwitch:
  891. newLines.append(contextSwitchCall)
  892. continue
  893. assert False
  894. elif instrGlobalMatch and not line.startswith('modifies ') and not line.startswith('var '):
  895. # if statement contains only instrumented global reads/writes
  896. instrLines = []
  897. for i in range(0, K):
  898. tmpLine = line
  899. instrGlobalMatch = instrGlobalRegExpr.match(tmpLine)
  900. while instrGlobalMatch:
  901. tmpLine = instrGlobalRegExpr.sub(r'\1\2_' + str(i) + r'\3\n', tmpLine)
  902. instrGlobalMatch = instrGlobalRegExpr.match(tmpLine)
  903. instrLines.append(tmpLine)
  904. l = re.compile('assert[ ]*(.*);\n')
  905. m = l.match(line)
  906. if m:
  907. for i in range(0, K):
  908. m = l.match(instrLines[i])
  909. newLines.append('assert (k == ' + str(i) + ' ==> (' + m.group(1) + '));\n')
  910. continue
  911. l = re.compile('assume[ ]*(.*);\n')
  912. m = l.match(line)
  913. if m:
  914. for i in range(0, K):
  915. m = l.match(instrLines[i])
  916. newLines.append('assume (k == ' + str(i) + ' ==> (' + m.group(1) + '));\n')
  917. if putSwitch:
  918. newLines.append(contextSwitchCall)
  919. continue
  920. if K > 1:
  921. newLines.append(' if (k == 0) {\n')
  922. newLines.append(instrLines[0])
  923. for i in range(1, K):
  924. newLines.append(' } else if (k == ' + str(i) + ') {\n')
  925. newLines.append(instrLines[i])
  926. newLines.append(' }\n')
  927. else:
  928. newLines.append(instrLines[0])
  929. if putSwitch and not line.startswith('LOOP'):
  930. newLines.append(contextSwitchCall)
  931. else:
  932. newLines.append(line)
  933. lines = newLines
  934. newLines = []
  935. memories = []
  936. inlinedProcedure = True
  937. for line in lines:
  938. l = re.compile('[ ]*procedure([ ]+{:inline 1}[ ]+|[ ]+)([a-zA-Z0-9_]*)\((.*)\)')
  939. m = l.search(line)
  940. if m:
  941. procName = m.group(2)
  942. if procName in procNotInlined:
  943. inlinedProcedure = False
  944. else:
  945. inlinedProcedure = True
  946. l = re.compile('[ ]*var Mem_T\.([a-zA-Z0-9_]+) : \[int\]int;\n')
  947. m = l.match(line)
  948. if m and m.group(1) in abstractKeepFields:
  949. # replace memory maps declarations with declarations of copies of memory
  950. # maps (Mem_i) and unconstrained constants (Mem_s_i)
  951. if not m.group(1) in processedFields:
  952. for i in range(0, K):
  953. newLines.append('var Mem_' + str(i) + '_T.' + m.group(1) + ' : [int]int;\n')
  954. for i in range(1, K):
  955. newLines.append('var Mem_s_' + str(i) + '_T.' + m.group(1) + ' : [int]int;\n')
  956. memories.append(m.group(1));
  957. continue
  958. l = re.compile('[ ]*var LOOP_([a-zA-Z0-9_]*)_Mem_T\.([a-zA-Z0-9_]+):\[int\]int;\n')
  959. m = l.match(line)
  960. if m and m.group(2) in abstractKeepFields:
  961. if not m.group(1) in processedFields:
  962. for i in range(0, K):
  963. newLines.append('var LOOP_' + m.group(1) + '_Mem_' + str(i) + '_T.' + m.group(2) + ':[int]int;\n')
  964. continue
  965. l = re.compile('[ ]*LOOP_([a-zA-Z0-9_]*)_Mem_T\.([a-zA-Z0-9_]+):=Mem_T\.[a-zA-Z0-9_]*;\n')
  966. m = l.match(line)
  967. if m and m.group(2) in abstractKeepFields:
  968. if not m.group(1) in processedFields:
  969. for i in range(0, K):
  970. newLines.append('LOOP_' + m.group(1) + '_Mem_' + str(i) + '_T.' + m.group(2) + ':=Mem_' + str(i) + '_T.' + m.group(2) + ';\n')
  971. continue
  972. l = re.compile('[ ]*modifies Mem_T\.([a-zA-Z0-9_]+);\n')
  973. m = l.match(line)
  974. if m and m.group(1) in abstractKeepFields:
  975. if inlinedProcedure and not m.group(1) in processedFields:
  976. for i in range(0, K):
  977. newLines.append('modifies Mem_' + str(i) + '_T.' + m.group(1) + ';\n')
  978. continue
  979. newLines.append(line)
  980. return addInstrumentationAssumes(''.join(newLines), memories)
  981. # Adds instrumentation assumes for memories and instrumented globals to the
  982. # beginning of the storm_main procedure and in the end, just before the
  983. # errorReached assertion
  984. def addInstrumentationAssumes(bpl, memories):
  985. lines = bpl.splitlines(True)
  986. newLines = []
  987. foundMain = False
  988. for line in lines:
  989. l = re.compile('(.*)' + mainProcedure + '\(\)\n')
  990. m = l.match(line)
  991. if m:
  992. foundMain = True
  993. l = re.compile('[ ]*start(#[0-9])?:\n')
  994. m = l.match(line)
  995. if m and foundMain:
  996. newLines.append(line)
  997. for mem in memories:
  998. for i in range(1, K):
  999. newLines.append('assume Mem_' + str(i) + '_T.' + mem + ' == Mem_s_' + str(i) + '_T.' + mem + ';\n')
  1000. for var in instrumentedGlobals:
  1001. for i in range(1, K):
  1002. newLines.append('assume ' + var + '_' + str(i) + ' == ' + var + '_s_' + str(i) + ';\n')
  1003. continue
  1004. l = re.compile('[ ]*assert.+!errorReached.*;\n')
  1005. m = l.match(line)
  1006. if m and foundMain:
  1007. foundMain = False
  1008. for mem in memories:
  1009. for i in range(0, K-1):
  1010. newLines.append('assume Mem_' + str(i) + '_T.' + mem + ' == Mem_s_' + str(i+1) + '_T.' + mem + ';\n')
  1011. for var in instrumentedGlobals:
  1012. for i in range(0, K-1):
  1013. newLines.append('assume ' + var + '_' + str(i) + ' == ' + var + '_s_' + str(i+1) + ';\n')
  1014. newLines.append(line)
  1015. continue
  1016. newLines.append(line)
  1017. return ''.join(newLines)
  1018. # Explicit case split for HAVOC's resources.
  1019. # Replaces every resource access (i.e. read or write of one of the resource
  1020. # maps) in a statement St with the appropriate copy of the resource and then
  1021. # introduces the case-split on k (see CAV paper):
  1022. #
  1023. # St --->
  1024. # if (k==0) St[Res_0/Res];
  1025. # else if (k==1) St[Res_1/Res];
  1026. # ....
  1027. # else if (k==i) St[Res_i/Res];
  1028. # ....
  1029. # else if (k==K - 1) St[Res_K-1/Res]
  1030. #
  1031. # In addition, a call to contextSwitch (i.e. procedure Schedule from CAV paper)
  1032. # is added after each such statement.
  1033. # In the end, adds instrumentation assumes to the beggining and end of
  1034. # storm_main.
  1035. #
  1036. def explicitCaseSplitResource(bpl, resources, putSwitch):
  1037. print 'Explicit case splitting resources...\n'
  1038. lines = bpl.splitlines(True)
  1039. orResources = '|'.join(resources)
  1040. usedResources = []
  1041. newLines = []
  1042. inlinedProcedure = True
  1043. for line in lines:
  1044. l = re.compile('[ ]*procedure([ ]+{:inline 1}[ ]+|[ ]+)([a-zA-Z0-9_]*)\((.*)\)')
  1045. m = l.search(line)
  1046. if m:
  1047. procName = m.group(2)
  1048. if procName in procNotInlined:
  1049. inlinedProcedure = False
  1050. else:
  1051. inlinedProcedure = True
  1052. resourceRegExpr = re.compile('[ ]*var Res_(' + orResources + '):\[int\]int;\n')
  1053. resourceMatch = resourceRegExpr.match(line)
  1054. if resourceMatch:
  1055. usedResources.append(resourceMatch.group(1))
  1056. for i in range(0, K):
  1057. newLines.append('var Res_' + str(i) + '_' + resourceMatch.group(1) + ' : [int]int;\n')
  1058. for i in range(1, K):
  1059. newLines.append('var Res_s_' + str(i) + '_' + resourceMatch.group(1) + ' : [int]int;\n')
  1060. continue
  1061. l = re.compile('[ ]*var LOOP_([a-zA-Z0-9_]*)_Res_(' + orResources + '):\[int\]int;\n')
  1062. m = l.match(line)
  1063. if m:
  1064. for i in range(0, K):
  1065. newLines.append('var LOOP_' + m.group(1) + '_Res_' + str(i) + '_' + m.group(2) + ':[int]int;\n')
  1066. continue
  1067. l = re.compile('[ ]*LOOP_([a-zA-Z0-9_]*)_Res_(' + orResources + ') := Res_(' + orResources + ');\n')
  1068. m = l.match(line)
  1069. if m:
  1070. for i in range(0, K):
  1071. newLines.append('LOOP_' + m.group(1) + '_Res_' + str(i) + '_' + m.group(2) + ' := Res_' + str(i) + '_' + m.group(2) + ';\n')
  1072. continue
  1073. l = re.compile('[ ]*modifies Res_(' + orResources + ');\n')
  1074. m = l.match(line)
  1075. if m:
  1076. if inlinedProcedure:
  1077. for i in range(0, K):
  1078. newLines.append('modifies Res_' + str(i) + '_' + m.group(1) + ';\n')
  1079. continue
  1080. newLines.append(line)
  1081. lines = newLines
  1082. newLines = []
  1083. for line in lines:
  1084. l = re.compile('(.*)Res_(' + orResources + ')(.*);\n')
  1085. m = l.match(line)
  1086. if m:
  1087. instrLines = []
  1088. for i in range(0, K):
  1089. tmpLine = line
  1090. m = l.match(tmpLine)
  1091. while m:
  1092. tmpLine = l.sub(r'\1Res_' + str(i) + r'_\2\3;\n', tmpLine)
  1093. m = l.match(tmpLine)
  1094. instrLines.append(tmpLine)
  1095. l = re.compile('[ ]*assert[ ]*(.*);\n')
  1096. m = l.match(line)
  1097. if m:
  1098. for i in range(0, K):
  1099. m = l.match(instrLines[i])
  1100. newLines.append('assert (k == ' + str(i) + ' ==> (' + m.group(1) + '));\n')
  1101. continue
  1102. l = re.compile('[ ]*assume[ ]*(.*);\n')
  1103. m = l.match(line)
  1104. if m:
  1105. for i in range(0, K):
  1106. m = l.match(instrLines[i])
  1107. newLines.append('assume (k == ' + str(i) + ' ==> (' + m.group(1) + '));\n')
  1108. if putSwitch:
  1109. newLines.append(contextSwitchCall)
  1110. continue
  1111. l = re.compile('[ ]*Res.*:=.*;\n')
  1112. m = l.match(line)
  1113. if not m:
  1114. if K > 1:
  1115. newLines.append(' if (k == 0) {\n')
  1116. newLines.append(instrLines[0])
  1117. for i in range(1, K):
  1118. newLines.append(' } else if (k == ' + str(i) + ') {\n')
  1119. newLines.append(instrLines[i])
  1120. newLines.append(' }\n')
  1121. else:
  1122. newLines.append(instrLines[0])
  1123. if putSwitch:
  1124. newLines.append(contextSwitchCall)
  1125. continue
  1126. l = re.compile('[ ]*Res_(' + orResources + ') := Res_((' + orResources + ')\[.*\]);\n')
  1127. m = l.match(line)
  1128. if m:
  1129. if K > 1:
  1130. newLines.append(' if (k == 0) {\n')
  1131. l = re.compile('[ ]*Res_0_(' + orResources + ') := Res_0_([a-zA-Z0-9_]*\[.*\]);\n')
  1132. m = l.match(instrLines[0])
  1133. newLines.append('Res_0_' + m.group(1) + ' := Res_0_' + m.group(2) + ';\n')
  1134. for i in range(1, K):
  1135. newLines.append(' } else if (k == ' + str(i) + ') {\n')
  1136. l = re.compile('[ ]*Res_' + str(i) + '_(' + orResources + ') := Res_' + str(i) + '_([a-zA-Z0-9_]*\[.*\]);\n')
  1137. m = l.match(instrLines[i])
  1138. newLines.append('Res_' + str(i) + '_' + m.group(1) + ' := Res_' + str(i) + '_' + m.group(2) + ';\n')
  1139. newLines.append(' }\n')
  1140. else:
  1141. l = re.compile('[ ]*Res_0_(' + orResources + ') := Res_0_([a-zA-Z0-9_]*\[.*\]);\n')
  1142. m = l.match(instrLines[0])
  1143. newLines.append('Res_0_' + m.group(1) + ' := Res_0_' + m.group(2) + ';\n')
  1144. if putSwitch:
  1145. newLines.append(contextSwitchCall)
  1146. continue
  1147. assert False
  1148. else:
  1149. newLines.append(line)
  1150. return addInstrumentationResourceAssumes(''.join(newLines), usedResources)
  1151. # Adds instrumentation assumes for used resources to the beginning of the storm_main
  1152. # procedure and in the end, just before the errorReached assertion
  1153. def addInstrumentationResourceAssumes(bpl, usedResources):
  1154. lines = bpl.splitlines(True)
  1155. newLines = []
  1156. foundMain = False
  1157. for line in lines:
  1158. l = re.compile('(.*)' + mainProcedure + '\(\)\n')
  1159. m = l.match(line)
  1160. if m:
  1161. foundMain = True
  1162. l = re.compile('[ ]*start:\n')
  1163. m = l.match(line)
  1164. if m and foundMain:
  1165. newLines.append(line)
  1166. for resource in usedResources:
  1167. for i in range(1, K):
  1168. newLines.append('assume Res_' + str(i) + '_' + resource + ' == Res_s_' + str(i) + '_' + resource + ';\n')
  1169. continue
  1170. l = re.compile('[ ]*assert.+!errorReached.*;\n')
  1171. m = l.match(line)
  1172. if m and foundMain:
  1173. foundMain = False
  1174. for resource in usedResources:
  1175. for i in range(0, K-1):
  1176. newLines.append('assume Res_' + str(i) + '_' + resource + ' == Res_s_' + str(i+1) + '_' + resource + ';\n')
  1177. newLines.append(line)
  1178. continue
  1179. newLines.append(line)
  1180. return ''.join(newLines)
  1181. # Inlining: write tmp.bpl to disk, call Boogie to do inlining, collect inlined storm_main
  1182. def inline(bpl):
  1183. # bpl = cleanUpNondets(bpl) # clean up nondet variables that we don't need
  1184. # Raise exceptions before __hv_assumes
  1185. if not noExceptions:
  1186. p = re.compile('//TAG: .*?\n')
  1187. bpl = p.sub('havoc raiseException; if (raiseException) {return;}\n', bpl)
  1188. print 'Inlining...\n'
  1189. fout = open('tmp.bpl', 'w')
  1190. fout.write(bpl)
  1191. fout.close()
  1192. inlineCmd = boogieBin + ' tmp.bpl /noinfer /inline:assume /proc:' + mainProcedure + ' /noVerify /coalesceBlocks:0 /printInstrumented'
  1193. f = os.popen(inlineCmd, 'r')
  1194. inlined = f.read()
  1195. f.close()
  1196. p = re.compile(r'\*\*\* Error:')
  1197. if p.search(inlined):
  1198. print 'Boogie ERROR when inlining!'
  1199. sys.exit()
  1200. p = re.compile('\A.*(procedure ' + mainProcedure + '\(\);(.*?))(after inlining procedure calls|Boogie program verifier finished|procedure[ ]+{:inline 1}[ ]+|\Z).*', re.DOTALL)
  1201. match = p.match(inlined)
  1202. inlined = match.group(1)
  1203. p = re.compile('\A(.*?)(procedure {:inline 1}|procedure ' + mainProcedure + '[ ]*\(\))', re.DOTALL)
  1204. match = p.match(bpl)
  1205. header = match.group(1)
  1206. bpl = header + inlined
  1207. bpl = removeDeadVariables(bpl) # remove dead vars
  1208. bpl = removeDeadVariables(bpl) # remove dead vars
  1209. print 'Unrolling loops...\n'
  1210. fout = open('tmp1.bpl', 'w')
  1211. fout.write(bpl)
  1212. fout.close()
  1213. unrollCmd = boogieBin + ' tmp1.bpl /noinfer /proc:' + mainProcedure + ' /noVerify /coalesceBlocks:0 /printInstrumented ' + loopUnroll
  1214. f = os.popen(unrollCmd, 'r')
  1215. unrolled = f.read()
  1216. f.close()
  1217. p = re.compile(r'\*\*\* Error:')
  1218. if p.search(unrolled):
  1219. print 'Boogie ERROR when unrolling loops!'
  1220. sys.exit()
  1221. p = re.compile('\A.*(procedure ' + mainProcedure + '\(\);(.*?))(after inlining procedure calls|Boogie program verifier finished|procedure[ ]+{:inline 1}[ ]+|\Z).*', re.DOTALL)
  1222. match = p.match(unrolled)
  1223. unrolled = match.group(1)
  1224. p = re.compile('\A(.*?)(procedure {:inline 1}|procedure ' + mainProcedure + '[ ]*\(\))', re.DOTALL)
  1225. match = p.match(bpl)
  1226. header = match.group(1)
  1227. bpl = header + unrolled
  1228. bpl = removeDeadVariables(bpl) # remove dead vars
  1229. bpl = removeDeadVariables(bpl) # remove dead vars
  1230. # Raise exceptions before "assume false" (unrolled loop)
  1231. if not noExceptions:
  1232. p = re.compile('assume false;\n')
  1233. bpl = p.sub('havoc raiseException; if (raiseException) {return;} assume false;\n', bpl)
  1234. return bpl, header
  1235. # Cleaning up havocs and assumes of nondet variables - optimization done after
  1236. # inlining
  1237. def cleanUpNondets(bpl):
  1238. print 'Cleaning up havocked nondet variables...\n'
  1239. p = re.compile('([^:]*:)\n[ ]*havoc ([a-zA-Z0-9_$.]*);\n[ ]*havoc ([a-zA-Z0-9_$.]*);\n[ ]*assume [^;]+;\n([ ]*goto[ ]+[^;]*;)\n')
  1240. bpl = p.sub(r'\1\n\4\n', bpl)
  1241. p = re.compile('([^:]*:)\n[ ]*havoc ([a-zA-Z0-9_$.]*);\n[ ]*assume [^;]+;\n([ ]*goto[ ]+[^;]*;)\n')
  1242. bpl = p.sub(r'\1\n\3\n', bpl)
  1243. return bpl
  1244. # Remove dead variables - optimization done after inlining
  1245. def removeDeadVariables(bpl):
  1246. print 'Removing dead variables...\n'
  1247. lines = bpl.splitlines(True)
  1248. mainFound = False
  1249. varDeclLineNo = -1
  1250. varDeclEndLineNo = -1
  1251. varDeclLine = ''
  1252. liveVars = set()
  1253. varTypes = {}
  1254. p = re.compile('^[ ]*var ([a-zA-Z0-9_$.]*): (int|bool|\[int\]name|\[int\]int|\[name\]\[int\]int);\n')
  1255. for i, line in enumerate(lines):
  1256. match = p.match(line)
  1257. if line == 'procedure ' + mainProcedure + '();\n':
  1258. mainFound = True
  1259. if mainFound and match:
  1260. if varDeclLineNo == -1:
  1261. varDeclLineNo = i
  1262. var = match.group(1)
  1263. liveVars.add(var)
  1264. varTypes[var] = match.group(2)
  1265. lines[i] = '// var declaration removed\n'
  1266. elif mainFound and not varDeclLineNo == -1:
  1267. varDeclEndLineNo = i
  1268. break
  1269. updated = True
  1270. while updated:
  1271. updated = False
  1272. vars = {}
  1273. varsLines = {}
  1274. while liveVars:
  1275. var = liveVars.pop()
  1276. vars[var] = 0
  1277. varsLines[var] = -1
  1278. p = re.compile('^[ ]*([a-zA-Z0-9_$.]*) := .*;\n') # this line shouldn't catch calls
  1279. p1 = re.compile('([a-zA-Z0-9_$.]*)')
  1280. for i, line in enumerate(lines[varDeclEndLineNo:]):
  1281. lineNo = i + varDeclEndLineNo
  1282. match = p.match(line)
  1283. if match:
  1284. var = match.group(1)
  1285. if var in vars:
  1286. if not varsLines[var] == -1:
  1287. vars[var] = 0
  1288. liveVars.add(var)
  1289. varsLines[var] = lineNo
  1290. iter = p1.finditer(line)
  1291. for match in iter:
  1292. var = match.group(1)
  1293. if var in vars:
  1294. if vars[var] == 0:
  1295. vars[var] = 1
  1296. else:
  1297. del vars[var]
  1298. del varsLines[var]
  1299. liveVars.add(var)
  1300. dead = filter(lambda item: item[1] == 1, vars.items())
  1301. for var, key in dead:
  1302. lineNo = varsLines[var]
  1303. if not lineNo == -1:
  1304. updated = True
  1305. lines[lineNo] = '// skipped dead var\n'
  1306. else:
  1307. liveVars.add(var)
  1308. if liveVars:
  1309. var = liveVars.pop()
  1310. varDeclLine = ' var ' + var + ': ' + varTypes[var]
  1311. while liveVars:
  1312. var = liveVars.pop()
  1313. varDeclLine += ', ' + var + ': ' + varTypes[var]
  1314. varDeclLine += ';\n'
  1315. lines[varDeclLineNo] = varDeclLine
  1316. bpl = ''.join(lines)
  1317. p = re.compile('// var declaration removed\n')
  1318. bpl = p.sub('', bpl)
  1319. p = re.compile('// skipped dead var\n')
  1320. bpl = p.sub('', bpl)
  1321. return bpl
  1322. ###############################################################################
  1323. # CEGAR loop stuff below
  1324. # Currently deprecated and not tested!
  1325. ###############################################################################
  1326. # Field abstraction concrete (used by CEGAR loop)
  1327. def concreteFieldAbstraction(bpl):
  1328. print 'Field abstraction on concrete file...\n'
  1329. lines = bpl.splitlines(True)
  1330. inProc = False # used to mark procedure entry
  1331. newLines = []
  1332. procLines = []
  1333. for line in lines:
  1334. if not inProc:
  1335. newLines.append(line)
  1336. # matching procedure start
  1337. l = re.compile('{\n')
  1338. m = l.match(line)
  1339. if m:
  1340. inProc = True
  1341. varCounterMax = 0
  1342. continue
  1343. if not inProc:
  1344. continue
  1345. # matching procedure end
  1346. l = re.compile('}\n')
  1347. m = l.match(line)
  1348. if m:
  1349. # adding declarations of introduced help variables to each procedure
  1350. for i in reversed(range(0, varCounterMax)):
  1351. procLines.insert(0, 'var myVar_' + str(i) + ':int;\n')
  1352. newLines.extend(procLines)
  1353. del procLines[:]
  1354. newLines.append(line)
  1355. inProc = False
  1356. continue
  1357. l = re.compile('assert[ ]+(.*);\n')
  1358. m = l.match(line)
  1359. if m:
  1360. procLines.append(line)
  1361. continue
  1362. varCounter = 0
  1363. l = re.compile('call .*;\n')
  1364. m = l.match(line)
  1365. if m:
  1366. # replacing all memory reads in a call statement with scalar variables
  1367. # myVar_n
  1368. l = re.compile('(.*)(Mem_T.([a-zA-Z0-9_]*)\[[^\[\]=]*\])(.*);\n')
  1369. m = l.match(line)
  1370. while m:
  1371. field = m.group(3)
  1372. var = 'myVar_' + str(varCounter)
  1373. procLines.append(var + ' := ' + m.group(2) + ';\n')
  1374. varCounter += 1
  1375. line = m.group(1) + var + m.group(4) + ';\n'
  1376. m = l.match(line)
  1377. else:
  1378. brackets = 0
  1379. atLeastOneMatch = True
  1380. while atLeastOneMatch:
  1381. atLeastOneMatch = False
  1382. lineChanged = True
  1383. # this regular expression is black magic :), but it works
  1384. l = re.compile('Mem_T.([a-zA-Z0-9_]*)\[([^=]*?\[){' + str(brackets) + ',' + str(brackets) + '}' \
  1385. '[^\[\]=]*?(\][^=]*?){' + str(brackets) + ',' + str(brackets) + '}\]')
  1386. brackets += 1
  1387. while lineChanged:
  1388. lineChanged = False
  1389. iter = l.finditer(line)
  1390. for match in iter:
  1391. atLeastOneMatch = True
  1392. field = match.group(1)
  1393. var = 'myVar_' + str(varCounter)
  1394. procLines.append(var + ' := ' + line[match.start():match.end()] + ';\n')
  1395. line = line[0:match.start()] + var + line[match.end():]
  1396. varCounter += 1
  1397. lineChanged = True
  1398. break
  1399. if varCounter > varCounterMax:
  1400. varCounterMax = varCounter
  1401. procLines.append(line)
  1402. return ''.join(newLines)
  1403. # Explicit case split for mem (used by CEGAR loop)
  1404. def concreteExplicitCaseSplit(bpl, abstractKeepFields):
  1405. print 'Explicit case splitting concrete...\n'
  1406. lines = bpl.splitlines(True)
  1407. orAbstractKeepFields = '|'.join(abstractKeepFields)
  1408. newLines = [] # freshly generated lines that will replace what we currently have
  1409. for line in lines:
  1410. # BEGIN hacky sollution
  1411. # This special case assures that we are not going to introduce case splits in
  1412. # the concrete program for a memory write that was removed from the abstract program
  1413. # (remember, CFGs of the two have to be the same to be able to replay counterexamples).
  1414. l = re.compile('[^=]*:=[ ]*Mem_T.([a-zA-Z0-9_]*)\[(.*);\n')
  1415. m = l.match(line)
  1416. if m and not m.group(1) in abstractKeepFields:
  1417. newLines.append(line)
  1418. continue
  1419. # END hacky sollution
  1420. memRegExpr = re.compile('(.*)Mem_T\.(' + orAbstractKeepFields + ')\[(.*);\n')
  1421. memMatch = memRegExpr.match(line)
  1422. if instrumentedGlobals:
  1423. orGlobalVars = '|'.join(instrumentedGlobals)
  1424. instrGlobalRegExpr = re.compile('(.*[^_]|^)(' + orGlobalVars + ')([^_].*;|;)\n')
  1425. instrGlobalMatch = instrGlobalRegExpr.match(line)
  1426. else:
  1427. instrGlobalRegExpr = None
  1428. instrGlobalMatch = None
  1429. if memMatch:
  1430. # is statement contains Mem reads/writes
  1431. instrLines = []
  1432. for i in range(0, K):
  1433. # replace Mem and G with Mem_i and G_i and store into instrLines
  1434. tmpLine = line
  1435. memMatch = memRegExpr.match(tmpLine)
  1436. while memMatch:
  1437. tmpLine = memRegExpr.sub(r'\1Mem_' + str(i) + r'_T.\2[\3;\n', tmpLine)
  1438. memMatch = memRegExpr.match(tmpLine)
  1439. if instrGlobalRegExpr:
  1440. instrGlobalMatch = instrGlobalRegExpr.match(tmpLine)
  1441. while instrGlobalMatch:
  1442. tmpLine = instrGlobalRegExpr.sub(r'\1\2_' + str(i) + r'\3\n', tmpLine)
  1443. instrGlobalMatch = instrGlobalRegExpr.match(tmpLine)
  1444. instrLines.append(tmpLine)
  1445. # assert(F); is instrumented as:
  1446. # assert(k == 0 ==> F[Mem_0/Mem,G_0/G]);
  1447. # assert(k == 1 ==> F[Mem_1/Mem,G_1/G]);
  1448. # ....
  1449. # assert(k == i ==> F[Mem_i/Mem,G_i/G]);
  1450. # ....
  1451. # assert(k == K-1 ==> F[Mem_K-1/Mem,G_K-1/G]);
  1452. l = re.compile('[ ]*assert[ ]*(.*);\n')
  1453. m = l.match(line)
  1454. if m:
  1455. for i in range(0, K):
  1456. m = l.match(instrLines[i])
  1457. newLines.append('assert (k == ' + str(i) + ' ==> (' + m.group(1) + '));\n')
  1458. continue
  1459. # assume(F); is instrumented as:
  1460. # assume(k == 0 ==> F[Mem_0/Mem,G_0/G]);
  1461. # assume(k == 1 ==> F[Mem_1/Mem,G_1/G]);
  1462. # ....
  1463. # assume(k == i ==> F[Mem_i/Mem,G_i/G]);
  1464. # ....
  1465. # assume(k == K-1 ==> F[Mem_K-1/Mem,G_K-1/G]);
  1466. l = re.compile('[ ]*assume[ ]*(.*);\n')
  1467. m = l.match(line)
  1468. if m:
  1469. for i in range(0, K):
  1470. m = l.match(instrLines[i])
  1471. newLines.append('assume (k == ' + str(i) + ' ==> (' + m.group(1) + '));\n')
  1472. newLines.append(contextSwitchCall)
  1473. continue
  1474. l = re.compile('[ ]*Mem.*:=.*;\n')
  1475. m = l.match(line)
  1476. if not m:
  1477. # if statement is not memory assignment
  1478. if K > 1:
  1479. newLines.append(' if (k == 0) {\n')
  1480. newLines.append(instrLines[0])
  1481. for i in range(1, K):
  1482. newLines.append(' } else if (k == ' + str(i) + ') {\n')
  1483. newLines.append(instrLines[i])
  1484. newLines.append(' }\n')
  1485. else:
  1486. newLines.append(instrLines[0])
  1487. newLines.append(contextSwitchCall)
  1488. continue
  1489. l = re.compile('[ ]*Mem_T\.(' + orAbstractKeepFields + ') := Mem_T\.((' + orAbstractKeepFields + ')\[.*\]);\n')
  1490. m = l.match(line)
  1491. if m:
  1492. # if statement is memory assignment
  1493. if K > 1:
  1494. newLines.append(' if (k == 0) {\n')
  1495. l = re.compile('[ ]*Mem_T\.([a-zA-Z0-9_]*) := Mem_0_T\.([a-zA-Z0-9_]*\[.*\]);\n')
  1496. m = l.match(instrLines[0])
  1497. newLines.append('Mem_0_T.' + m.group(1) + ' := Mem_0_T.' + m.group(2) + ';\n')
  1498. for i in range(1, K):
  1499. newLines.append(' } else if (k == ' + str(i) + ') {\n')
  1500. l = re.compile('[ ]*Mem_T\.([a-zA-Z0-9_]*) := Mem_' + str(i) + '_T\.([a-zA-Z0-9_]*\[.*\]);\n')
  1501. m = l.match(instrLines[i])
  1502. newLines.append('Mem_' + str(i) + '_T.' + m.group(1) + ' := Mem_' + str(i) + '_T.' + m.group(2) + ';\n')
  1503. newLines.append(' }\n')
  1504. else:
  1505. l = re.compile('[ ]*Mem_T\.([a-zA-Z0-9_]*) := Mem_0_T\.([a-zA-Z0-9_]*\[.*\]);\n')
  1506. m = l.match(instrLines[0])
  1507. newLines.append('Mem_0_T.' + m.group(1) + ' := Mem_0_T.' + m.group(2) + ';\n')
  1508. newLines.append(contextSwitchCall)
  1509. continue
  1510. assert False
  1511. elif instrGlobalMatch and not line.startswith('modifies ') and not line.startswith('var '):
  1512. # if statement contains only instrumented global reads/writes
  1513. instrLines = []
  1514. for i in range(0, K):
  1515. tmpLine = line
  1516. instrGlobalMatch = instrGlobalRegExpr.match(tmpLine)
  1517. while instrGlobalMatch:
  1518. tmpLine = instrGlobalRegExpr.sub(r'\1\2_' + str(i) + r'\3\n', tmpLine)
  1519. instrGlobalMatch = instrGlobalRegExpr.match(tmpLine)
  1520. instrLines.append(tmpLine)
  1521. l = re.compile('assert[ ]*(.*);\n')
  1522. m = l.match(line)
  1523. if m:
  1524. for i in range(0, K):
  1525. m = l.match(instrLines[i])
  1526. newLines.append('assert (k == ' + str(i) + ' ==> (' + m.group(1) + '));\n')
  1527. continue
  1528. l = re.compile('assume[ ]*(.*);\n')
  1529. m = l.match(line)
  1530. if m:
  1531. for i in range(0, K):
  1532. m = l.match(instrLines[i])
  1533. newLines.append('assume (k == ' + str(i) + ' ==> (' + m.group(1) + '));\n')
  1534. newLines.append(contextSwitchCall)
  1535. continue
  1536. if K > 1:
  1537. newLines.append(' if (k == 0) {\n')
  1538. newLines.append(instrLines[0])
  1539. for i in range(1, K):
  1540. newLines.append(' } else if (k == ' + str(i) + ') {\n')
  1541. newLines.append(instrLines[i])
  1542. newLines.append(' }\n')
  1543. else:
  1544. newLines.append(instrLines[0])
  1545. if not line.startswith('LOOP'):
  1546. newLines.append(contextSwitchCall)
  1547. else:
  1548. newLines.append(line)
  1549. lines = newLines
  1550. newLines = []
  1551. memories = []
  1552. inlinedProcedure = True
  1553. for line in lines:
  1554. l = re.compile('[ ]*procedure([ ]+{:inline 1}[ ]+|[ ]+)([a-zA-Z0-9_]*)\((.*)\)')
  1555. m = l.search(line)
  1556. if m:
  1557. procName = m.group(2)
  1558. if procName in procNotInlined:
  1559. inlinedProcedure = False
  1560. else:
  1561. inlinedProcedure = True
  1562. l = re.compile('[ ]*var Mem_T\.(' + orAbstractKeepFields + ') : \[int\]int;\n')
  1563. m = l.match(line)
  1564. if m:
  1565. # replace memory maps declarations with declarations of copies of memory
  1566. # maps (Mem_i) and unconstrained constants (Mem_s_i)
  1567. for i in range(0, K):
  1568. newLines.append('var Mem_' + str(i) + '_T.' + m.group(1) + ' : [int]int;\n')
  1569. for i in range(1, K):
  1570. newLines.append('var Mem_s_' + str(i) + '_T.' + m.group(1) + ' : [int]int;\n')
  1571. memories.append(m.group(1));
  1572. continue
  1573. l = re.compile('[ ]*var LOOP_([a-zA-Z0-9_]*)_Mem_T\.(' + orAbstractKeepFields + '):\[int\]int;\n')
  1574. m = l.match(line)
  1575. if m:
  1576. for i in range(0, K):
  1577. newLines.append('var LOOP_' + m.group(1) + '_Mem_' + str(i) + '_T.' + m.group(2) + ':[int]int;\n')
  1578. continue
  1579. l = re.compile('[ ]*LOOP_([a-zA-Z0-9_]*)_Mem_T\.(' + orAbstractKeepFields + '):=Mem_T.[a-zA-Z0-9_]*;\n')
  1580. m = l.match(line)
  1581. if m:
  1582. for i in range(0, K):
  1583. newLines.append('LOOP_' + m.group(1) + '_Mem_' + str(i) + '_T.' + m.group(2) + ':=Mem_' + str(i) + '_T.' + m.group(2) + ';\n')
  1584. continue
  1585. l = re.compile('[ ]*modifies Mem_T\.(' + orAbstractKeepFields + ');\n')
  1586. m = l.match(line)
  1587. if m:
  1588. if inlinedProcedure:
  1589. for i in range(0, K):
  1590. newLines.append('modifies Mem_' + str(i) + '_T.' + m.group(1) + ';\n')
  1591. continue
  1592. newLines.append(line)
  1593. return addInstrumentationAssumes(''.join(newLines), memories)
  1594. # CEGAR loop main procedure
  1595. def cegarLoop(bplConcreteInitial):
  1596. global contextSwitchCall
  1597. l = re.compile('(.*).bpl')
  1598. m = l.match(outputBplFile)
  1599. assert m
  1600. exampleName = str(m.group(1))
  1601. bpl = initialProcessing(bplConcreteInitial) # get rid of alloc assumes, havoc_free etc.
  1602. emptyProcedures = findEmptyProcedures(bpl) # Don't inline procedures without bodies
  1603. procNotInlined.update(emptyProcedures)
  1604. bpl = instrumentStormMain(bpl)
  1605. bpl = instrumentThreads(bpl)
  1606. bpl = replaceGlobalsWithScalars(bpl) # replace Mem[errorReached] --> errorReached, Mem[k] --> k
  1607. if not noExceptions:
  1608. bpl = putRaiseExceptionChecks(bpl)
  1609. allFields = findAllFields(bpl)
  1610. bplBeforeFieldAbstraction = bpl
  1611. # CEGAR loop
  1612. addedContextSwitchTracking = False
  1613. iteration = 0
  1614. while True:
  1615. iteration += 1
  1616. print 'CEGAR Iteration ' + str(iteration)
  1617. bpl = fieldAbstraction(bplBeforeFieldAbstraction) # havocs reads/removes writes based on "abstract_keep_fields"
  1618. bpl = explicitCaseSplit(bpl, abstractKeepFields, [], True) # if (k==i) St[Mem_i/Mem]
  1619. if resources:
  1620. bpl = explicitCaseSplitResource(bpl, resources, True); # if (k==i) St[Res_i/Res]
  1621. bpl, header = inline(bpl) # generate tmp.bpl and calls Tmp.bpl and collects storm_main
  1622. # write output
  1623. abstractFilename = exampleName + '_' + str(iteration) + '.bpl'
  1624. fout = open(abstractFilename, 'w')
  1625. fout.write(bpl)
  1626. fout.close()
  1627. # run Boogie
  1628. print 'Running Boogie on abstraction...\n'
  1629. if measureTime:
  1630. checkCmd = 'ptime ' + boogieBin + ' ' + abstractFilename + ' ' + boogieOpts
  1631. else:
  1632. checkCmd = boogieBin + ' ' + abstractFilename + ' ' + boogieOpts
  1633. p = re.compile('time out')
  1634. for parameters in boogieHeuristics:
  1635. checkCmdTmp = checkCmd
  1636. for param in parameters:
  1637. checkCmdTmp += ' ' + param
  1638. result = justRunBoogie(checkCmdTmp)
  1639. if not p.search(result):
  1640. break
  1641. print result
  1642. if p.search(result):
  1643. print 'Boogie TIMED OUT checking abstraction!'
  1644. print 'Our final set of fields: ' + ', '.join(abstractKeepFields)
  1645. print 'Our final set of all fields: ' + ', '.join(abstractKeepFieldsAll)
  1646. sys.exit()
  1647. p = re.compile(r'\*\*\* Error:')
  1648. if p.search(result):
  1649. print 'Boogie ERROR when checking abstraction!'
  1650. print 'Our final set of fields: ' + ', '.join(abstractKeepFields)
  1651. print 'Our final set of all fields: ' + ', '.join(abstractKeepFieldsAll)
  1652. sys.exit()
  1653. p = re.compile('1 verified, 0 errors')
  1654. if not p.search(result):
  1655. bplConcrete = concreteFieldAbstraction(bplBeforeFieldAbstraction) # havocs reads/removes writes based on "abstract_keep_fields"
  1656. bplConcrete = concreteExplicitCaseSplit(bplConcrete, abstractKeepFields) # if (k==i) St[Mem_i/Mem]
  1657. if resources:
  1658. bplConcrete = explicitCaseSplitResource(bplConcrete, resources, True); # if (k==i) St[Res_i/Res]
  1659. bplConcrete, headerConcrete = inline(bplConcrete) # generate tmp.bpl and calls Tmp.bpl and collects storm_main
  1660. # write concrete output
  1661. concreteFilename = 'concrete_' + str(iteration) + '.bpl'
  1662. fout = open(concreteFilename, 'w')
  1663. fout.write(bplConcrete)
  1664. fout.close()
  1665. traceBpl = explicitCaseSplit(generateTraceBplProgram(bplConcrete,
  1666. headerConcrete, result), allFields, abstractKeepFields, False)
  1667. if checkIfRealBug(traceBpl):
  1668. if addedContextSwitchTracking:
  1669. print 'BUG FOUND!\n'
  1670. print 'Generating error trace...\n'
  1671. bpl = fieldAbstraction(bplBeforeFieldAbstraction) # havocs reads/removes writes based on "abstract_keep_fields"
  1672. bpl = explicitCaseSplit(bpl, abstractKeepFields, [], True) # if (k==i) St[Mem_i/Mem]
  1673. if resources:
  1674. bpl = explicitCaseSplitResource(bpl, resources, True); # if (k==i) St[Res_i/Res]
  1675. bpl, header = inline(bpl) # generate tmp.bpl and calls Tmp.bpl and collects storm_main
  1676. # write output
  1677. os.system(r'cp tmp.bpl ' + exampleName + '_trace.bpl')
  1678. abstractFilename = exampleName + '_inlined_trace.bpl'
  1679. fout = open(abstractFilename, 'w')
  1680. fout.write(bpl)
  1681. fout.close()
  1682. # run Boogie
  1683. print 'Running Boogie to generate error trace...\n'
  1684. if measureTime:
  1685. checkCmd = 'ptime ' + boogieBin + ' ' + abstractFilename + ' ' + boogieOpts
  1686. else:
  1687. checkCmd = boogieBin + ' ' + abstractFilename + ' ' + boogieOpts
  1688. p = re.compile('time out')
  1689. for parameters in boogieHeuristics:
  1690. checkCmdTmp = checkCmd
  1691. for param in parameters:
  1692. checkCmdTmp += ' ' + param
  1693. result = justRunBoogie(checkCmdTmp)
  1694. if not p.search(result):
  1695. break
  1696. if p.search(result):
  1697. print 'Boogie TIMED OUT checking inlined trace!'
  1698. print 'Our final set of fields: ' + ', '.join(abstractKeepFields)
  1699. print 'Our final set of all fields: ' + ', '.join(abstractKeepFieldsAll)
  1700. sys.exit()
  1701. # write trace
  1702. fout = open(exampleName + '_bpl_trace.txt', 'w')
  1703. fout.write(result)
  1704. fout.close()
  1705. if generateTrace:
  1706. traceName = exampleName + '_trace.txt'
  1707. generateErrorTrace(result, 'tmp.bpl', abstractFilename, traceName)
  1708. break
  1709. else:
  1710. print 'Adding context switch tracking for generating error trace...\n'
  1711. addedContextSwitchTracking = True
  1712. contextSwitchCall += '\n'
  1713. for i in range(0, K):
  1714. if i == 0:
  1715. contextSwitchCall += ' if (k == 0) {\n'
  1716. else:
  1717. contextSwitchCall += ' } else if (k == ' + str(i) + ') {\n'
  1718. contextSwitchCall += ' call storm_context_' + str(i) + '();\n'
  1719. contextSwitchCall += ' }\n'
  1720. bpl = initialProcessing(bplConcreteInitial) # get rid of alloc assumes, havoc_free etc.
  1721. emptyProcedures = findEmptyProcedures(bpl) # Don't inline procedures without bodies
  1722. procNotInlined.update(emptyProcedures)
  1723. bpl = instrumentStormMain(bpl)
  1724. bpl = instrumentThreads(bpl)
  1725. bpl = replaceGlobalsWithScalars(bpl) # replace Mem[errorReached] --> errorReached, Mem[k] --> k
  1726. if not noExceptions:
  1727. bpl = putRaiseExceptionChecks(bpl)
  1728. bplBeforeFieldAbstraction = bpl
  1729. continue
  1730. importantFields = optimizeFields(traceBpl, allFields, iteration)
  1731. assert importantFields
  1732. print 'Important fields: ' + ', '.join(importantFields)
  1733. # adding important fields to our global sets of fields
  1734. newFieldFound = False
  1735. for field in importantFields:
  1736. if not field in abstractKeepFields and not field in constantFields:
  1737. abstractKeepFields.append(field)
  1738. newFieldFound = True
  1739. if not field in abstractKeepFieldsAll:
  1740. abstractKeepFieldsAll.add(field)
  1741. newFieldFound = True
  1742. assert newFieldFound
  1743. print 'Current set of fields: ' + ', '.join(abstractKeepFields)
  1744. print 'Current set of all fields: ' + ', '.join(abstractKeepFieldsAll)
  1745. else:
  1746. print 'PROGRAM CHECKED and WE ARE DONE!'
  1747. print 'Our final set of fields: ' + ', '.join(abstractKeepFields)
  1748. print 'Our final set of all fields: ' + ', '.join(abstractKeepFieldsAll)
  1749. break
  1750. def findAllFields(bpl):
  1751. l = re.compile('var Mem_T.([a-zA-Z0-9_]*) : \[int\]int;\n')
  1752. fields = []
  1753. iter = l.finditer(bpl)
  1754. for match in iter:
  1755. fields.append(match.group(1))
  1756. return fields
  1757. def checkIfRealBug(traceBpl):
  1758. # write concrete trace bpl program
  1759. concreteFilename = 'check_if_real_bug.bpl'
  1760. fout = open(concreteFilename, 'w')
  1761. fout.write(traceBpl)
  1762. fout.close()
  1763. print 'Running Boogie on concrete trace...\n'
  1764. if measureTime:
  1765. checkCmd = 'ptime ' + boogieBin + ' ' + concreteFilename + ' ' + boogieOpts
  1766. else:
  1767. checkCmd = boogieBin + ' ' + concreteFilename + ' ' + boogieOpts
  1768. p = re.compile('time out')
  1769. for parameters in boogieHeuristics:
  1770. checkCmdTmp = checkCmd
  1771. for param in parameters:
  1772. checkCmdTmp += ' ' + param
  1773. result = justRunBoogie(checkCmdTmp)
  1774. if not p.search(result):
  1775. break
  1776. print result
  1777. p = re.compile('1 verified, 0 errors')
  1778. if not p.search(result):
  1779. return True
  1780. else:
  1781. return False
  1782. def optimizeFields(traceBpl, allFields, iteration):
  1783. currentFields = []
  1784. for field in allFields:
  1785. if traceContainsField(traceBpl, field):
  1786. currentFields.append(field)
  1787. importantFields = []
  1788. while currentFields:
  1789. currentField = currentFields.pop()
  1790. currentTraceBpl = traceFieldAbstraction(traceBpl, currentFields + importantFields)
  1791. # write concrete trace bpl program
  1792. concreteFilename = 'concrete_trace_' + str(iteration) + '.bpl'
  1793. fout = open(concreteFilename, 'w')
  1794. fout.write(currentTraceBpl)
  1795. fout.close()
  1796. print 'Running Boogie on concrete trace...\n'
  1797. if measureTime:
  1798. checkCmd = 'ptime ' + boogieBin + ' ' + concreteFilename + ' ' + boogieOpts
  1799. else:
  1800. checkCmd = boogieBin + ' ' + concreteFilename + ' ' + boogieOpts
  1801. p = re.compile('time out')
  1802. for parameters in boogieHeuristics:
  1803. checkCmdTmp = checkCmd
  1804. for param in parameters:
  1805. checkCmdTmp += ' ' + param
  1806. result = justRunBoogie(checkCmdTmp)
  1807. if not p.search(result):
  1808. break
  1809. p = re.compile('1 verified, 0 errors')
  1810. if not p.search(result):
  1811. importantFields.append(currentField)
  1812. print 'IMPORTANT FIELD: ' + currentField
  1813. return importantFields
  1814. def generateTraceBplProgram(bplConcrete, headerConcrete, result):
  1815. concreteTrace = cutTrace(bplConcrete, result)
  1816. p = re.compile('(procedure ' + mainProcedure + '\(\);.*?)start#[0-9]:', re.DOTALL)
  1817. m = p.search(bplConcrete)
  1818. assert m
  1819. mainHead = m.group(1)
  1820. p = re.compile('modifies (.*?);')
  1821. m = p.search(mainHead)
  1822. assert m
  1823. modifiesStr = m.group(1)
  1824. mems = tokenizeLabels(modifiesStr)
  1825. modifiesStr = ''
  1826. for mem in mems:
  1827. modifiesStr += 'modifies ' + mem + ';\n'
  1828. mainHead = p.sub(modifiesStr, mainHead)
  1829. traceBpl = headerConcrete + mainHead + concreteTrace + '}\n'
  1830. return traceBpl
  1831. def cutTrace(bpl, boogieTrace):
  1832. trace = extractTraceLabels(boogieTrace)
  1833. trace = completeTrace(bpl, trace)
  1834. traceProgram = ''
  1835. for prev, next in byPairs(trace):
  1836. l = re.compile('[ ]+(' + prev.replace('$', '[$]') + ':.*?)goto[ ]+([^;]*);', re.DOTALL)
  1837. m = l.search(bpl)
  1838. assert m
  1839. if m:
  1840. traceProgram += ' ' + m.group(1)
  1841. nextLabels = tokenizeLabels(m.group(2))
  1842. assert nextLabels.count(next) == 1
  1843. traceProgram += 'goto ' + next + ';\n'
  1844. l = re.compile('[ ]+(' + next.replace('$', '[$]') + ':.*?return;\n)', re.DOTALL)
  1845. m = l.search(bpl)
  1846. assert m
  1847. if m:
  1848. traceProgram += ' ' + m.group(1)
  1849. return traceProgram
  1850. def completeTrace(bpl, trace):
  1851. completedTrace = []
  1852. # append dummy end block
  1853. trace.append('dummy_final_block')
  1854. for prev, next in byPairs(trace):
  1855. # print 'Prev: ' + prev
  1856. # print 'Next: ' + next
  1857. completedTrace.append(prev)
  1858. nextLabelFound = False
  1859. while not nextLabelFound:
  1860. # if this is our final dummy block, and we found a block with assert !errorReached,
  1861. # we are done
  1862. if next == 'dummy_final_block':
  1863. l = re.compile('[ ]+' + prev.replace('$', '[$]') + ':([^#]*?)return;', re.DOTALL)
  1864. m = l.search(bpl)
  1865. if m:
  1866. blockBody = m.group(1)
  1867. l = re.compile('assert !errorReached;')
  1868. if l.search(blockBody):
  1869. break
  1870. # find the basic block belonging to prev
  1871. l = re.compile('[ ]+' + prev.replace('$', '[$]') + ':[^#]*?goto[ ]+([^;]*);', re.DOTALL)
  1872. m = l.search(bpl)
  1873. assert m
  1874. nextLabelsOfPrev = tokenizeLabels(m.group(1))
  1875. # print 'Next labels of prev: ' + ', '.join(nextLabelsOfPrev)
  1876. assert nextLabelsOfPrev
  1877. if not nextLabelsOfPrev.count(next) == 0:
  1878. # next label found right away
  1879. assert nextLabelsOfPrev.count(next) == 1
  1880. nextLabelFound = True
  1881. elif len(nextLabelsOfPrev) == 1:
  1882. # only one next label of prev block so just go forward
  1883. completedTrace.append(nextLabelsOfPrev[0])
  1884. prev = nextLabelsOfPrev[0]
  1885. else:
  1886. # explore future to see if you have to follow left or right label
  1887. assert len(nextLabelsOfPrev) >= 2
  1888. labelsQueue = deque()
  1889. for label in nextLabelsOfPrev:
  1890. labelsQueue.append((label, label))
  1891. while True:
  1892. queuedLabel = labelsQueue.popleft()
  1893. queuedLabelStart = queuedLabel[0]
  1894. queuedLabelEnd = queuedLabel[1]
  1895. # if this is our final dummy block, and we found a block with assert !errorReached,
  1896. # we are done
  1897. if next == 'dummy_final_block':
  1898. l = re.compile('[ ]+' + queuedLabelEnd.replace('$', '[$]') + ':([^#]*?)return;', re.DOTALL)
  1899. m = l.search(bpl)
  1900. if m:
  1901. blockBody = m.group(1)
  1902. l = re.compile('assert !errorReached;')
  1903. if l.search(blockBody):
  1904. next = queuedLabelEnd
  1905. break
  1906. l = re.compile('[ ]+' + queuedLabelEnd.replace('$', '[$]') + ':([^#]*?)goto[ ]+([^;]*);', re.DOTALL)
  1907. m = l.search(bpl)
  1908. assert m
  1909. # if block contains a return statement it's a dead end, so drop it and
  1910. # try the next one in the queue
  1911. blockBody = m.group(1)
  1912. l = re.compile('return;')
  1913. if l.search(blockBody):
  1914. continue
  1915. nextLabelsOfQueuedLabel = tokenizeLabels(m.group(2))
  1916. if not nextLabelsOfQueuedLabel.count(next) == 0:
  1917. # we found our next label, so add the branch we took in the beginning to
  1918. # our trace
  1919. assert nextLabelsOfQueuedLabel.count(next) == 1
  1920. completedTrace.append(queuedLabelStart)
  1921. prev = queuedLabelStart
  1922. break
  1923. else:
  1924. for nextLabelOfQueuedLabel in nextLabelsOfQueuedLabel:
  1925. labelsQueue.append((queuedLabelStart, nextLabelOfQueuedLabel))
  1926. if not next == 'dummy_final_block':
  1927. completedTrace.append(next)
  1928. return completedTrace
  1929. def tokenizeLabels(labelsStr):
  1930. labels = []
  1931. p = re.compile('([^ ,]+)([, ]*)')
  1932. iter = p.finditer(labelsStr)
  1933. for match in iter:
  1934. label = match.group(1)
  1935. labels.append(label)
  1936. return labels
  1937. def byPairs(iterable):
  1938. sequence = iter(iterable)
  1939. previous = sequence.next()
  1940. while True:
  1941. current = sequence.next()
  1942. yield previous, current
  1943. previous = current
  1944. def extractTraceLabels(boogieTrace):
  1945. trace = []
  1946. l = re.compile('.*: ([^ ]*)\n')
  1947. lines = boogieTrace.splitlines(True)
  1948. for line in lines:
  1949. m = l.match(line)
  1950. if m:
  1951. trace.append(m.group(1))
  1952. return trace
  1953. def traceFieldAbstraction(bpl, keepFields):
  1954. print 'Trace field abstraction...\n'
  1955. lines = bpl.splitlines(True)
  1956. inProc = False
  1957. newLines = []
  1958. procLines = []
  1959. for line in lines:
  1960. if not inProc:
  1961. newLines.append(line)
  1962. l = re.compile('{\n')
  1963. m = l.match(line)
  1964. if m:
  1965. inProc = True
  1966. procLines.append('var trace_myNondetVar_0:int;\n')
  1967. procLines.append('var trace_myNondetVar_1:int;\n')
  1968. procLines.append('var trace_myNondetVar_2:int;\n')
  1969. procLines.append('var trace_myNondetVar_3:int;\n')
  1970. procLines.append('var trace_myNondetVar_4:int;\n')
  1971. procLines.append('var trace_myNondetVar_5:int;\n')
  1972. procLines.append('var trace_myVar_0:int;\n')
  1973. procLines.append('var trace_myVar_1:int;\n')
  1974. procLines.append('var trace_myVar_2:int;\n')
  1975. procLines.append('var trace_myVar_3:int;\n')
  1976. procLines.append('var trace_myVar_4:int;\n')
  1977. procLines.append('var trace_myVar_5:int;\n')
  1978. continue
  1979. if not inProc:
  1980. continue
  1981. l = re.compile('}\n')
  1982. m = l.match(line)
  1983. if m:
  1984. newLines.extend(procLines)
  1985. del procLines[:]
  1986. newLines.append(line)
  1987. inProc = False
  1988. continue
  1989. l = re.compile('assert[ ]+(.*);\n')
  1990. m = l.match(line)
  1991. if m:
  1992. procLines.append(line)
  1993. continue
  1994. l = re.compile('call .*;\n')
  1995. m = l.match(line)
  1996. if m:
  1997. l = re.compile('(.*)(Mem_[0-9]+_T.([a-zA-Z0-9_]*)\[[^\[\]=]*\])(.*);\n')
  1998. m = l.match(line)
  1999. counter = 0
  2000. while m:
  2001. field = m.group(3)
  2002. if field in keepFields:
  2003. var = 'trace_myVar_' + str(counter)
  2004. procLines.append(var + ' := ' + m.group(2) + ';\n')
  2005. else:
  2006. var = 'trace_myNondetVar_' + str(counter)
  2007. procLines.append('havoc ' + var + ';\n')
  2008. line = m.group(1) + var + m.group(4) + ';\n'
  2009. m = l.match(line)
  2010. counter += 1
  2011. else:
  2012. brackets = 0
  2013. counter = 0
  2014. atLeastOneMatch = True
  2015. while atLeastOneMatch:
  2016. atLeastOneMatch = False
  2017. lineChanged = True
  2018. l = re.compile('Mem_[0-9]+_T.([a-zA-Z0-9_]*)\[([^=]*?\[){' + str(brackets) + ',' + str(brackets) + '}[^\[\]=]*?(\][^=]*?){' + str(brackets) + ',' + str(brackets) + '}\]')
  2019. brackets += 1
  2020. while lineChanged:
  2021. lineChanged = False
  2022. iter = l.finditer(line)
  2023. for match in iter:
  2024. atLeastOneMatch = True
  2025. field = match.group(1)
  2026. if not field in keepFields:
  2027. var = 'trace_myNondetVar_' + str(counter)
  2028. procLines.append('havoc ' + var + ';\n')
  2029. line = line[0:match.start()] + var + line[match.end():]
  2030. counter += 1
  2031. lineChanged = True
  2032. break
  2033. l = re.compile('[ ]*Mem_[0-9]+_T.([a-zA-Z0-9_]*) := Mem_[0-9]+_T.[a-zA-Z0-9_]*\[.*\];\n')
  2034. m = l.match(line)
  2035. if m and not m.group(1) in keepFields:
  2036. procLines.append('// skip memory write\n')
  2037. continue
  2038. l = re.compile('[ ]*LOOP_[a-zA-Z0-9_]*_Mem_[0-9]+_T.([a-zA-Z0-9_]*):=Mem_[0-9]+_T.[a-zA-Z0-9_]*;\n')
  2039. m = l.match(line)
  2040. if m and not m.group(1) in keepFields:
  2041. continue
  2042. procLines.append(line)
  2043. return ''.join(newLines)
  2044. def traceContainsField(bpl, searchedField):
  2045. lines = bpl.splitlines(True)
  2046. for line in lines:
  2047. l = re.compile('call .*;\n')
  2048. m = l.match(line)
  2049. if m:
  2050. l = re.compile('(.*)(Mem_[0-9]+_T.([a-zA-Z0-9_]*)\[[^\[\]=]*\])(.*);\n')
  2051. m = l.match(line)
  2052. while m:
  2053. field = m.group(3)
  2054. if field == searchedField:
  2055. return True
  2056. line = m.group(1) + 'tmp' + m.group(4) + ';\n'
  2057. m = l.match(line)
  2058. else:
  2059. brackets = 0
  2060. atLeastOneMatch = True
  2061. while atLeastOneMatch:
  2062. atLeastOneMatch = False
  2063. l = re.compile('Mem_[0-9]+_T.([a-zA-Z0-9_]*)\[([^=]*?\[){' + str(brackets) + ',' + str(brackets) + '}' \
  2064. '[^\[\]=]*?(\][^=]*?){' + str(brackets) + ',' + str(brackets) + '}\]')
  2065. brackets += 1
  2066. iter = l.finditer(line)
  2067. for match in iter:
  2068. atLeastOneMatch = True
  2069. field = match.group(1)
  2070. if field == searchedField:
  2071. return True
  2072. l = re.compile('[ ]*Mem_[0-9]+_T.([a-zA-Z0-9_]*) := Mem_[0-9]+_T.[a-zA-Z0-9_]*\[.*\];\n')
  2073. m = l.match(line)
  2074. if m and m.group(1) == searchedField:
  2075. return True
  2076. l = re.compile('[ ]*LOOP_[a-zA-Z0-9_]*_Mem_[0-9]+_T.([a-zA-Z0-9_]*):=Mem_[0-9]+_T.[a-zA-Z0-9_]*;\n')
  2077. m = l.match(line)
  2078. if m and m.group(1) == searchedField:
  2079. return True
  2080. return False
  2081. # Call main
  2082. if __name__ == '__main__':
  2083. main()