PageRenderTime 55ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/bin/storm.py

#
Python | 2396 lines | 2394 code | 1 blank | 1 comment | 5 complexity | 89c98e850de8cef6534394a249c839f8 MD5 | raw file

Large files files are truncated, but you can click here to view the full 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

Large files files are truncated, but you can click here to view the full file