PageRenderTime 62ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/rpython/translator/c/test/test_standalone.py

https://bitbucket.org/bwesterb/pypy
Python | 1162 lines | 1151 code | 11 blank | 0 comment | 11 complexity | a5a39747da029d9b93b22fc30d36f566 MD5 | raw file
  1. import py
  2. import sys, os, re
  3. from rpython.rlib.objectmodel import keepalive_until_here
  4. from rpython.rlib.rarithmetic import r_longlong
  5. from rpython.rlib.debug import ll_assert, have_debug_prints, debug_flush
  6. from rpython.rlib.debug import debug_print, debug_start, debug_stop, debug_offset
  7. from rpython.translator.translator import TranslationContext
  8. from rpython.translator.backendopt import all
  9. from rpython.translator.c.genc import CStandaloneBuilder, ExternalCompilationInfo
  10. from rpython.annotator.listdef import s_list_of_strings
  11. from rpython.tool.udir import udir
  12. from rpython.conftest import cdir
  13. from rpython.conftest import option
  14. class StandaloneTests(object):
  15. config = None
  16. def compile(self, entry_point, debug=True, shared=False,
  17. stackcheck=False):
  18. t = TranslationContext(self.config)
  19. t.buildannotator().build_types(entry_point, [s_list_of_strings])
  20. t.buildrtyper().specialize()
  21. if stackcheck:
  22. from rpython.translator.transform import insert_ll_stackcheck
  23. insert_ll_stackcheck(t)
  24. t.config.translation.shared = shared
  25. cbuilder = CStandaloneBuilder(t, entry_point, t.config)
  26. if debug:
  27. cbuilder.generate_source(defines=cbuilder.DEBUG_DEFINES)
  28. else:
  29. cbuilder.generate_source()
  30. cbuilder.compile()
  31. if option is not None and option.view:
  32. t.view()
  33. return t, cbuilder
  34. class TestStandalone(StandaloneTests):
  35. def test_hello_world(self):
  36. def entry_point(argv):
  37. os.write(1, "hello world\n")
  38. argv = argv[1:]
  39. os.write(1, "argument count: " + str(len(argv)) + "\n")
  40. for s in argv:
  41. os.write(1, " '" + str(s) + "'\n")
  42. return 0
  43. t, cbuilder = self.compile(entry_point)
  44. data = cbuilder.cmdexec('hi there')
  45. assert data.startswith('''hello world\nargument count: 2\n 'hi'\n 'there'\n''')
  46. # Verify that the generated C files have sane names:
  47. gen_c_files = [str(f) for f in cbuilder.extrafiles]
  48. for expfile in ('rpython_rlib_rposix.c',
  49. 'rpython_rtyper_lltypesystem_rstr.c',
  50. 'rpython_translator_c_test_test_standalone.c'):
  51. assert cbuilder.targetdir.join(expfile) in gen_c_files
  52. def test_print(self):
  53. def entry_point(argv):
  54. print "hello simpler world"
  55. argv = argv[1:]
  56. print "argument count:", len(argv)
  57. print "arguments:", argv
  58. print "argument lengths:",
  59. print [len(s) for s in argv]
  60. return 0
  61. t, cbuilder = self.compile(entry_point)
  62. data = cbuilder.cmdexec('hi there')
  63. assert data.startswith('''hello simpler world\n'''
  64. '''argument count: 2\n'''
  65. '''arguments: [hi, there]\n'''
  66. '''argument lengths: [2, 5]\n''')
  67. # NB. RPython has only str, not repr, so str() on a list of strings
  68. # gives the strings unquoted in the list
  69. def test_counters(self):
  70. from rpython.rtyper.lltypesystem import lltype
  71. from rpython.rtyper.lltypesystem.lloperation import llop
  72. def entry_point(argv):
  73. llop.instrument_count(lltype.Void, 'test', 2)
  74. llop.instrument_count(lltype.Void, 'test', 1)
  75. llop.instrument_count(lltype.Void, 'test', 1)
  76. llop.instrument_count(lltype.Void, 'test', 2)
  77. llop.instrument_count(lltype.Void, 'test', 1)
  78. return 0
  79. t = TranslationContext(self.config)
  80. t.config.translation.instrument = True
  81. t.buildannotator().build_types(entry_point, [s_list_of_strings])
  82. t.buildrtyper().specialize()
  83. cbuilder = CStandaloneBuilder(t, entry_point, config=t.config) # xxx
  84. cbuilder.generate_source()
  85. cbuilder.compile()
  86. counters_fname = udir.join("_counters_")
  87. os.environ['PYPY_INSTRUMENT_COUNTERS'] = str(counters_fname)
  88. try:
  89. data = cbuilder.cmdexec()
  90. finally:
  91. del os.environ['PYPY_INSTRUMENT_COUNTERS']
  92. f = counters_fname.open('rb')
  93. counters_data = f.read()
  94. f.close()
  95. import struct
  96. counters = struct.unpack("LLL", counters_data)
  97. assert counters == (0,3,2)
  98. def test_prof_inline(self):
  99. py.test.skip("broken by 5b0e029514d4, but we don't use it any more")
  100. if sys.platform == 'win32':
  101. py.test.skip("instrumentation support is unix only for now")
  102. def add(a,b):
  103. return a + b - b + b - b + b - b + b - b + b - b + b - b + b
  104. def entry_point(argv):
  105. tot = 0
  106. x = int(argv[1])
  107. while x > 0:
  108. tot = add(tot, x)
  109. x -= 1
  110. os.write(1, str(tot))
  111. return 0
  112. from rpython.translator.interactive import Translation
  113. t = Translation(entry_point, backend='c')
  114. # no counters
  115. t.backendopt(inline_threshold=100, profile_based_inline="500")
  116. exe = t.compile()
  117. out = py.process.cmdexec("%s 500" % exe)
  118. assert int(out) == 500*501/2
  119. t = Translation(entry_point, backend='c')
  120. # counters
  121. t.backendopt(inline_threshold=all.INLINE_THRESHOLD_FOR_TEST*0.5,
  122. profile_based_inline="500")
  123. exe = t.compile()
  124. out = py.process.cmdexec("%s 500" % exe)
  125. assert int(out) == 500*501/2
  126. def test_frexp(self):
  127. import math
  128. def entry_point(argv):
  129. m, e = math.frexp(0)
  130. x, y = math.frexp(0)
  131. print m, x
  132. return 0
  133. t, cbuilder = self.compile(entry_point)
  134. data = cbuilder.cmdexec('hi there')
  135. assert map(float, data.split()) == [0.0, 0.0]
  136. def test_profopt(self):
  137. def add(a,b):
  138. return a + b - b + b - b + b - b + b - b + b - b + b - b + b
  139. def entry_point(argv):
  140. tot = 0
  141. x = int(argv[1])
  142. while x > 0:
  143. tot = add(tot, x)
  144. x -= 1
  145. os.write(1, str(tot))
  146. return 0
  147. from rpython.translator.interactive import Translation
  148. # XXX this is mostly a "does not crash option"
  149. t = Translation(entry_point, backend='c', profopt="100")
  150. # no counters
  151. t.backendopt()
  152. exe = t.compile()
  153. out = py.process.cmdexec("%s 500" % exe)
  154. assert int(out) == 500*501/2
  155. t = Translation(entry_point, backend='c', profopt="100",
  156. noprofopt=True)
  157. # no counters
  158. t.backendopt()
  159. exe = t.compile()
  160. out = py.process.cmdexec("%s 500" % exe)
  161. assert int(out) == 500*501/2
  162. if hasattr(os, 'setpgrp'):
  163. def test_os_setpgrp(self):
  164. def entry_point(argv):
  165. os.setpgrp()
  166. return 0
  167. t, cbuilder = self.compile(entry_point)
  168. cbuilder.cmdexec("")
  169. def test_profopt_mac_osx_bug(self):
  170. if sys.platform == 'win32':
  171. py.test.skip("no profopt on win32")
  172. def entry_point(argv):
  173. import os
  174. pid = os.fork()
  175. if pid:
  176. os.waitpid(pid, 0)
  177. else:
  178. os._exit(0)
  179. return 0
  180. from rpython.translator.interactive import Translation
  181. # XXX this is mostly a "does not crash option"
  182. t = Translation(entry_point, backend='c', profopt="")
  183. # no counters
  184. t.backendopt()
  185. exe = t.compile()
  186. #py.process.cmdexec(exe)
  187. t = Translation(entry_point, backend='c', profopt="",
  188. noprofopt=True)
  189. # no counters
  190. t.backendopt()
  191. exe = t.compile()
  192. #py.process.cmdexec(exe)
  193. def test_standalone_large_files(self):
  194. filename = str(udir.join('test_standalone_largefile'))
  195. r4800000000 = r_longlong(4800000000L)
  196. def entry_point(argv):
  197. assert str(r4800000000 + r_longlong(len(argv))) == '4800000003'
  198. fd = os.open(filename, os.O_RDWR | os.O_CREAT, 0644)
  199. os.lseek(fd, r4800000000, 0)
  200. newpos = os.lseek(fd, 0, 1)
  201. if newpos == r4800000000:
  202. print "OK"
  203. else:
  204. print "BAD POS"
  205. os.close(fd)
  206. return 0
  207. t, cbuilder = self.compile(entry_point)
  208. data = cbuilder.cmdexec('hi there')
  209. assert data.strip() == "OK"
  210. def test_separate_files(self):
  211. # One file in translator/c/src
  212. fname = py.path.local(cdir).join('src', 'll_strtod.c')
  213. # One file in (another) subdir of the temp directory
  214. dirname = udir.join("test_dir").ensure(dir=1)
  215. fname2 = dirname.join("test_genc.c")
  216. fname2.write("""
  217. void f() {
  218. LL_strtod_formatd(12.3, 'f', 5);
  219. }""")
  220. files = [fname, fname2]
  221. def entry_point(argv):
  222. return 0
  223. t = TranslationContext(self.config)
  224. t.buildannotator().build_types(entry_point, [s_list_of_strings])
  225. t.buildrtyper().specialize()
  226. cbuilder = CStandaloneBuilder(t, entry_point, t.config)
  227. cbuilder.eci = cbuilder.eci.merge(
  228. ExternalCompilationInfo(separate_module_files=files))
  229. cbuilder.generate_source()
  230. makefile = udir.join(cbuilder.modulename, 'Makefile').read()
  231. # generated files are compiled in the same directory
  232. assert " ../test_dir/test_genc.c" in makefile
  233. assert " ../test_dir/test_genc.o" in makefile
  234. # but files from pypy source dir must be copied
  235. assert "translator/c/src" not in makefile
  236. assert " ll_strtod.c" in makefile
  237. assert " ll_strtod.o" in makefile
  238. def test_debug_print_start_stop(self):
  239. from rpython.rtyper.lltypesystem import rffi
  240. def entry_point(argv):
  241. x = "got:"
  242. debug_start ("mycat")
  243. if have_debug_prints(): x += "b"
  244. debug_print ("foo", r_longlong(2), "bar", 3)
  245. debug_start ("cat2")
  246. if have_debug_prints(): x += "c"
  247. debug_print ("baz")
  248. debug_stop ("cat2")
  249. if have_debug_prints(): x += "d"
  250. debug_print ("bok")
  251. debug_stop ("mycat")
  252. if have_debug_prints(): x += "a"
  253. debug_print("toplevel")
  254. debug_print("some int", rffi.cast(rffi.INT, 3))
  255. debug_flush()
  256. os.write(1, x + "." + str(debug_offset()) + '.\n')
  257. return 0
  258. t, cbuilder = self.compile(entry_point)
  259. # check with PYPYLOG undefined
  260. out, err = cbuilder.cmdexec("", err=True, env={})
  261. assert out.strip() == 'got:a.-1.'
  262. assert 'toplevel' in err
  263. assert 'mycat' not in err
  264. assert 'foo 2 bar 3' not in err
  265. assert 'cat2' not in err
  266. assert 'baz' not in err
  267. assert 'bok' not in err
  268. # check with PYPYLOG defined to an empty string (same as undefined)
  269. out, err = cbuilder.cmdexec("", err=True, env={'PYPYLOG': ''})
  270. assert out.strip() == 'got:a.-1.'
  271. assert 'toplevel' in err
  272. assert 'mycat' not in err
  273. assert 'foo 2 bar 3' not in err
  274. assert 'cat2' not in err
  275. assert 'baz' not in err
  276. assert 'bok' not in err
  277. # check with PYPYLOG=:- (means print to stderr)
  278. out, err = cbuilder.cmdexec("", err=True, env={'PYPYLOG': ':-'})
  279. assert out.strip() == 'got:bcda.-1.'
  280. assert 'toplevel' in err
  281. assert '{mycat' in err
  282. assert 'mycat}' in err
  283. assert 'foo 2 bar 3' in err
  284. assert '{cat2' in err
  285. assert 'cat2}' in err
  286. assert 'baz' in err
  287. assert 'bok' in err
  288. assert 'some int 3' in err
  289. # check with PYPYLOG=:somefilename
  290. path = udir.join('test_debug_xxx.log')
  291. out, err = cbuilder.cmdexec("", err=True,
  292. env={'PYPYLOG': ':%s' % path})
  293. size = os.stat(str(path)).st_size
  294. assert out.strip() == 'got:bcda.' + str(size) + '.'
  295. assert not err
  296. assert path.check(file=1)
  297. data = path.read()
  298. assert 'toplevel' in data
  299. assert '{mycat' in data
  300. assert 'mycat}' in data
  301. assert 'foo 2 bar 3' in data
  302. assert '{cat2' in data
  303. assert 'cat2}' in data
  304. assert 'baz' in data
  305. assert 'bok' in data
  306. # check with PYPYLOG=somefilename
  307. path = udir.join('test_debug_xxx_prof.log')
  308. out, err = cbuilder.cmdexec("", err=True, env={'PYPYLOG': str(path)})
  309. size = os.stat(str(path)).st_size
  310. assert out.strip() == 'got:a.' + str(size) + '.'
  311. assert not err
  312. assert path.check(file=1)
  313. data = path.read()
  314. assert 'toplevel' in data
  315. assert '{mycat' in data
  316. assert 'mycat}' in data
  317. assert 'foo 2 bar 3' not in data
  318. assert '{cat2' in data
  319. assert 'cat2}' in data
  320. assert 'baz' not in data
  321. assert 'bok' not in data
  322. # check with PYPYLOG=myc:somefilename (includes mycat but not cat2)
  323. path = udir.join('test_debug_xxx_myc.log')
  324. out, err = cbuilder.cmdexec("", err=True,
  325. env={'PYPYLOG': 'myc:%s' % path})
  326. size = os.stat(str(path)).st_size
  327. assert out.strip() == 'got:bda.' + str(size) + '.'
  328. assert not err
  329. assert path.check(file=1)
  330. data = path.read()
  331. assert 'toplevel' in data
  332. assert '{mycat' in data
  333. assert 'mycat}' in data
  334. assert 'foo 2 bar 3' in data
  335. assert 'cat2' not in data
  336. assert 'baz' not in data
  337. assert 'bok' in data
  338. # check with PYPYLOG=cat:somefilename (includes cat2 but not mycat)
  339. path = udir.join('test_debug_xxx_cat.log')
  340. out, err = cbuilder.cmdexec("", err=True,
  341. env={'PYPYLOG': 'cat:%s' % path})
  342. size = os.stat(str(path)).st_size
  343. assert out.strip() == 'got:ca.' + str(size) + '.'
  344. assert not err
  345. assert path.check(file=1)
  346. data = path.read()
  347. assert 'toplevel' in data
  348. assert 'mycat' not in data
  349. assert 'foo 2 bar 3' not in data
  350. assert 'cat2' in data
  351. assert 'baz' in data
  352. assert 'bok' not in data
  353. # check with PYPYLOG=myc,cat2:somefilename (includes mycat and cat2)
  354. path = udir.join('test_debug_xxx_myc_cat2.log')
  355. out, err = cbuilder.cmdexec("", err=True,
  356. env={'PYPYLOG': 'myc,cat2:%s' % path})
  357. size = os.stat(str(path)).st_size
  358. assert out.strip() == 'got:bcda.' + str(size) + '.'
  359. assert not err
  360. assert path.check(file=1)
  361. data = path.read()
  362. assert 'toplevel' in data
  363. assert '{mycat' in data
  364. assert 'mycat}' in data
  365. assert 'foo 2 bar 3' in data
  366. assert 'cat2' in data
  367. assert 'baz' in data
  368. assert 'bok' in data
  369. #
  370. # finally, check compiling with logging disabled
  371. from rpython.config.translationoption import get_combined_translation_config
  372. config = get_combined_translation_config(translating=True)
  373. config.translation.log = False
  374. self.config = config
  375. t, cbuilder = self.compile(entry_point)
  376. path = udir.join('test_debug_does_not_show_up.log')
  377. out, err = cbuilder.cmdexec("", err=True,
  378. env={'PYPYLOG': ':%s' % path})
  379. assert out.strip() == 'got:.-1.'
  380. assert not err
  381. assert path.check(file=0)
  382. def test_debug_print_start_stop_nonconst(self):
  383. def entry_point(argv):
  384. debug_start(argv[1])
  385. debug_print(argv[2])
  386. debug_stop(argv[1])
  387. return 0
  388. t, cbuilder = self.compile(entry_point)
  389. out, err = cbuilder.cmdexec("foo bar", err=True, env={'PYPYLOG': ':-'})
  390. lines = err.splitlines()
  391. assert '{foo' in lines[0]
  392. assert 'bar' == lines[1]
  393. assert 'foo}' in lines[2]
  394. def test_fatal_error(self):
  395. def g(x):
  396. if x == 1:
  397. raise ValueError
  398. else:
  399. raise KeyError
  400. def entry_point(argv):
  401. if len(argv) < 3:
  402. g(len(argv))
  403. return 0
  404. t, cbuilder = self.compile(entry_point)
  405. #
  406. out, err = cbuilder.cmdexec("", expect_crash=True)
  407. assert out.strip() == ''
  408. lines = err.strip().splitlines()
  409. idx = lines.index('Fatal RPython error: ValueError') # assert found
  410. lines = lines[:idx+1]
  411. assert len(lines) >= 4
  412. l0, l1, l2 = lines[-4:-1]
  413. assert l0 == 'RPython traceback:'
  414. assert re.match(r' File "\w+.c", line \d+, in entry_point', l1)
  415. assert re.match(r' File "\w+.c", line \d+, in g', l2)
  416. #
  417. out2, err2 = cbuilder.cmdexec("x", expect_crash=True)
  418. assert out2.strip() == ''
  419. lines2 = err2.strip().splitlines()
  420. idx = lines2.index('Fatal RPython error: KeyError') # assert found
  421. lines2 = lines2[:idx+1]
  422. l0, l1, l2 = lines2[-4:-1]
  423. assert l0 == 'RPython traceback:'
  424. assert re.match(r' File "\w+.c", line \d+, in entry_point', l1)
  425. assert re.match(r' File "\w+.c", line \d+, in g', l2)
  426. assert lines2[-2] != lines[-2] # different line number
  427. assert lines2[-3] == lines[-3] # same line number
  428. def test_fatal_error_finally_1(self):
  429. # a simple case of try:finally:
  430. def g(x):
  431. if x == 1:
  432. raise KeyError
  433. def h(x):
  434. try:
  435. g(x)
  436. finally:
  437. os.write(1, 'done.\n')
  438. def entry_point(argv):
  439. if len(argv) < 3:
  440. h(len(argv))
  441. return 0
  442. t, cbuilder = self.compile(entry_point)
  443. #
  444. out, err = cbuilder.cmdexec("", expect_crash=True)
  445. assert out.strip() == 'done.'
  446. lines = err.strip().splitlines()
  447. idx = lines.index('Fatal RPython error: KeyError') # assert found
  448. lines = lines[:idx+1]
  449. assert len(lines) >= 5
  450. l0, l1, l2, l3 = lines[-5:-1]
  451. assert l0 == 'RPython traceback:'
  452. assert re.match(r' File "\w+.c", line \d+, in entry_point', l1)
  453. assert re.match(r' File "\w+.c", line \d+, in h', l2)
  454. assert re.match(r' File "\w+.c", line \d+, in g', l3)
  455. def test_fatal_error_finally_2(self):
  456. # a try:finally: in which we raise and catch another exception
  457. def raiseme(x):
  458. if x == 1:
  459. raise ValueError
  460. def raise_and_catch(x):
  461. try:
  462. raiseme(x)
  463. except ValueError:
  464. pass
  465. def g(x):
  466. if x == 1:
  467. raise KeyError
  468. def h(x):
  469. try:
  470. g(x)
  471. finally:
  472. raise_and_catch(x)
  473. os.write(1, 'done.\n')
  474. def entry_point(argv):
  475. if len(argv) < 3:
  476. h(len(argv))
  477. return 0
  478. t, cbuilder = self.compile(entry_point)
  479. #
  480. out, err = cbuilder.cmdexec("", expect_crash=True)
  481. assert out.strip() == 'done.'
  482. lines = err.strip().splitlines()
  483. idx = lines.index('Fatal RPython error: KeyError') # assert found
  484. lines = lines[:idx+1]
  485. assert len(lines) >= 5
  486. l0, l1, l2, l3 = lines[-5:-1]
  487. assert l0 == 'RPython traceback:'
  488. assert re.match(r' File "\w+.c", line \d+, in entry_point', l1)
  489. assert re.match(r' File "\w+.c", line \d+, in h', l2)
  490. assert re.match(r' File "\w+.c", line \d+, in g', l3)
  491. def test_fatal_error_finally_3(self):
  492. py.test.skip("not implemented: "
  493. "a try:finally: in which we raise the *same* exception")
  494. def test_fatal_error_finally_4(self):
  495. # a try:finally: in which we raise (and don't catch) an exception
  496. def raiseme(x):
  497. if x == 1:
  498. raise ValueError
  499. def g(x):
  500. if x == 1:
  501. raise KeyError
  502. def h(x):
  503. try:
  504. g(x)
  505. finally:
  506. raiseme(x)
  507. os.write(1, 'done.\n')
  508. def entry_point(argv):
  509. if len(argv) < 3:
  510. h(len(argv))
  511. return 0
  512. t, cbuilder = self.compile(entry_point)
  513. #
  514. out, err = cbuilder.cmdexec("", expect_crash=True)
  515. assert out.strip() == ''
  516. lines = err.strip().splitlines()
  517. idx = lines.index('Fatal RPython error: ValueError') # assert found
  518. lines = lines[:idx+1]
  519. assert len(lines) >= 5
  520. l0, l1, l2, l3 = lines[-5:-1]
  521. assert l0 == 'RPython traceback:'
  522. assert re.match(r' File "\w+.c", line \d+, in entry_point', l1)
  523. assert re.match(r' File "\w+.c", line \d+, in h', l2)
  524. assert re.match(r' File "\w+.c", line \d+, in raiseme', l3)
  525. def test_assertion_error_debug(self):
  526. def entry_point(argv):
  527. assert len(argv) != 1
  528. return 0
  529. t, cbuilder = self.compile(entry_point, debug=True)
  530. out, err = cbuilder.cmdexec("", expect_crash=True)
  531. assert out.strip() == ''
  532. lines = err.strip().splitlines()
  533. assert 'in pypy_g_RPyRaiseException: AssertionError' in lines
  534. def test_assertion_error_nondebug(self):
  535. def g(x):
  536. assert x != 1
  537. def f(argv):
  538. try:
  539. g(len(argv))
  540. finally:
  541. print 'done'
  542. def entry_point(argv):
  543. f(argv)
  544. return 0
  545. t, cbuilder = self.compile(entry_point, debug=False)
  546. out, err = cbuilder.cmdexec("", expect_crash=True)
  547. assert out.strip() == ''
  548. lines = err.strip().splitlines()
  549. idx = lines.index('Fatal RPython error: AssertionError') # assert found
  550. lines = lines[:idx+1]
  551. assert len(lines) >= 4
  552. l0, l1, l2 = lines[-4:-1]
  553. assert l0 == 'RPython traceback:'
  554. assert re.match(r' File "\w+.c", line \d+, in f', l1)
  555. assert re.match(r' File "\w+.c", line \d+, in g', l2)
  556. # The traceback stops at f() because it's the first function that
  557. # captures the AssertionError, which makes the program abort.
  558. def test_int_lshift_too_large(self):
  559. from rpython.rlib.rarithmetic import LONG_BIT, LONGLONG_BIT
  560. def entry_point(argv):
  561. a = int(argv[1])
  562. b = int(argv[2])
  563. print a << b
  564. return 0
  565. t, cbuilder = self.compile(entry_point, debug=True)
  566. out = cbuilder.cmdexec("10 2", expect_crash=False)
  567. assert out.strip() == str(10 << 2)
  568. cases = [-4, LONG_BIT, LONGLONG_BIT]
  569. for x in cases:
  570. out, err = cbuilder.cmdexec("%s %s" % (1, x), expect_crash=True)
  571. lines = err.strip()
  572. assert 'The shift count is outside of the supported range' in lines
  573. def test_llong_rshift_too_large(self):
  574. from rpython.rlib.rarithmetic import LONG_BIT, LONGLONG_BIT
  575. def entry_point(argv):
  576. a = r_longlong(int(argv[1]))
  577. b = r_longlong(int(argv[2]))
  578. print a >> b
  579. return 0
  580. t, cbuilder = self.compile(entry_point, debug=True)
  581. out = cbuilder.cmdexec("10 2", expect_crash=False)
  582. assert out.strip() == str(10 >> 2)
  583. out = cbuilder.cmdexec("%s %s" % (-42, LONGLONG_BIT - 1), expect_crash=False)
  584. assert out.strip() == '-1'
  585. cases = [-4, LONGLONG_BIT]
  586. for x in cases:
  587. out, err = cbuilder.cmdexec("%s %s" % (1, x), expect_crash=True)
  588. lines = err.strip()
  589. assert 'The shift count is outside of the supported range' in lines
  590. def test_ll_assert_error_debug(self):
  591. def entry_point(argv):
  592. ll_assert(len(argv) != 1, "foobar")
  593. return 0
  594. t, cbuilder = self.compile(entry_point, debug=True)
  595. out, err = cbuilder.cmdexec("", expect_crash=True)
  596. assert out.strip() == ''
  597. lines = err.strip().splitlines()
  598. assert 'in pypy_g_entry_point: foobar' in lines
  599. def test_ll_assert_error_nondebug(self):
  600. py.test.skip("implement later, maybe: tracebacks even with ll_assert")
  601. def g(x):
  602. ll_assert(x != 1, "foobar")
  603. def f(argv):
  604. try:
  605. g(len(argv))
  606. finally:
  607. print 'done'
  608. def entry_point(argv):
  609. f(argv)
  610. return 0
  611. t, cbuilder = self.compile(entry_point)
  612. out, err = cbuilder.cmdexec("", expect_crash=True)
  613. assert out.strip() == ''
  614. lines = err.strip().splitlines()
  615. idx = lines.index('PyPy assertion failed: foobar') # assert found
  616. lines = lines[:idx+1]
  617. assert len(lines) >= 4
  618. l0, l1, l2 = lines[-4:-1]
  619. assert l0 == 'RPython traceback:'
  620. assert re.match(r' File "\w+.c", line \d+, in f', l1)
  621. assert re.match(r' File "\w+.c", line \d+, in g', l2)
  622. # The traceback stops at f() because it's the first function that
  623. # captures the AssertionError, which makes the program abort.
  624. def test_shared(self, monkeypatch):
  625. def f(argv):
  626. print len(argv)
  627. def entry_point(argv):
  628. f(argv)
  629. return 0
  630. t, cbuilder = self.compile(entry_point, shared=True)
  631. assert cbuilder.shared_library_name is not None
  632. assert cbuilder.shared_library_name != cbuilder.executable_name
  633. monkeypatch.setenv('LD_LIBRARY_PATH',
  634. cbuilder.shared_library_name.dirpath())
  635. out, err = cbuilder.cmdexec("a b")
  636. assert out == "3"
  637. def test_gcc_options(self):
  638. # check that the env var CC is correctly interpreted, even if
  639. # it contains the compiler name followed by some options.
  640. if sys.platform == 'win32':
  641. py.test.skip("only for gcc")
  642. from rpython.rtyper.lltypesystem import lltype, rffi
  643. dir = udir.ensure('test_gcc_options', dir=1)
  644. dir.join('someextraheader.h').write('#define someextrafunc() 42\n')
  645. eci = ExternalCompilationInfo(includes=['someextraheader.h'])
  646. someextrafunc = rffi.llexternal('someextrafunc', [], lltype.Signed,
  647. compilation_info=eci)
  648. def entry_point(argv):
  649. return someextrafunc()
  650. old_cc = os.environ.get('CC')
  651. try:
  652. os.environ['CC'] = 'gcc -I%s' % dir
  653. t, cbuilder = self.compile(entry_point)
  654. finally:
  655. if old_cc is None:
  656. del os.environ['CC']
  657. else:
  658. os.environ['CC'] = old_cc
  659. def test_inhibit_tail_call(self):
  660. # the point is to check that the f()->f() recursion stops
  661. from rpython.rlib.rstackovf import StackOverflow
  662. class Glob:
  663. pass
  664. glob = Glob()
  665. def f(n):
  666. glob.n = n
  667. if n <= 0:
  668. return 42
  669. return f(n+1)
  670. def entry_point(argv):
  671. try:
  672. return f(1)
  673. except StackOverflow:
  674. print 'hi!', glob.n
  675. return 0
  676. t, cbuilder = self.compile(entry_point, stackcheck=True)
  677. out = cbuilder.cmdexec("")
  678. text = out.strip()
  679. assert text.startswith("hi! ")
  680. n = int(text[4:])
  681. assert n > 500 and n < 5000000
  682. def test_set_length_fraction(self):
  683. # check for rpython.rlib.rstack._stack_set_length_fraction()
  684. from rpython.rlib.rstack import _stack_set_length_fraction
  685. from rpython.rlib.rstackovf import StackOverflow
  686. class A:
  687. n = 0
  688. glob = A()
  689. def f(n):
  690. glob.n += 1
  691. if n <= 0:
  692. return 42
  693. return f(n+1)
  694. def entry_point(argv):
  695. _stack_set_length_fraction(0.1)
  696. try:
  697. return f(1)
  698. except StackOverflow:
  699. glob.n = 0
  700. _stack_set_length_fraction(float(argv[1]))
  701. try:
  702. return f(1)
  703. except StackOverflow:
  704. print glob.n
  705. return 0
  706. t, cbuilder = self.compile(entry_point, stackcheck=True)
  707. counts = {}
  708. for fraction in [0.1, 0.4, 1.0]:
  709. out = cbuilder.cmdexec(str(fraction))
  710. print 'counts[%s]: %r' % (fraction, out)
  711. counts[fraction] = int(out.strip())
  712. #
  713. assert counts[1.0] >= 1000
  714. # ^^^ should actually be much more than 1000 for this small test
  715. assert counts[0.1] < counts[0.4] / 3
  716. assert counts[0.4] < counts[1.0] / 2
  717. assert counts[0.1] > counts[0.4] / 7
  718. assert counts[0.4] > counts[1.0] / 4
  719. def test_stack_criticalcode(self):
  720. # check for rpython.rlib.rstack._stack_criticalcode_start/stop()
  721. from rpython.rlib.rstack import _stack_criticalcode_start
  722. from rpython.rlib.rstack import _stack_criticalcode_stop
  723. from rpython.rlib.rstackovf import StackOverflow
  724. class A:
  725. pass
  726. glob = A()
  727. def f(n):
  728. if n <= 0:
  729. return 42
  730. try:
  731. return f(n+1)
  732. except StackOverflow:
  733. if glob.caught:
  734. print 'Oups! already caught!'
  735. glob.caught = True
  736. _stack_criticalcode_start()
  737. critical(100) # recurse another 100 times here
  738. _stack_criticalcode_stop()
  739. return 789
  740. def critical(n):
  741. if n > 0:
  742. n = critical(n - 1)
  743. return n - 42
  744. def entry_point(argv):
  745. glob.caught = False
  746. print f(1)
  747. return 0
  748. t, cbuilder = self.compile(entry_point, stackcheck=True)
  749. out = cbuilder.cmdexec('')
  750. assert out.strip() == '789'
  751. class TestMaemo(TestStandalone):
  752. def setup_class(cls):
  753. py.test.skip("TestMaemo: tests skipped for now")
  754. from rpython.translator.platform.maemo import check_scratchbox
  755. check_scratchbox()
  756. from rpython.config.translationoption import get_combined_translation_config
  757. config = get_combined_translation_config(translating=True)
  758. config.translation.platform = 'maemo'
  759. cls.config = config
  760. def test_profopt(self):
  761. py.test.skip("Unsupported")
  762. def test_prof_inline(self):
  763. py.test.skip("Unsupported")
  764. class TestThread(object):
  765. gcrootfinder = 'shadowstack'
  766. config = None
  767. def compile(self, entry_point):
  768. t = TranslationContext(self.config)
  769. t.config.translation.gc = "semispace"
  770. t.config.translation.gcrootfinder = self.gcrootfinder
  771. t.config.translation.thread = True
  772. t.buildannotator().build_types(entry_point, [s_list_of_strings])
  773. t.buildrtyper().specialize()
  774. #
  775. cbuilder = CStandaloneBuilder(t, entry_point, t.config)
  776. cbuilder.generate_source(defines=cbuilder.DEBUG_DEFINES)
  777. cbuilder.compile()
  778. #
  779. return t, cbuilder
  780. def test_stack_size(self):
  781. import time
  782. from rpython.rlib import rthread
  783. from rpython.rtyper.lltypesystem import lltype
  784. from rpython.rlib.objectmodel import invoke_around_extcall
  785. class State:
  786. pass
  787. state = State()
  788. def before():
  789. debug_print("releasing...")
  790. ll_assert(not rthread.acquire_NOAUTO(state.ll_lock, False),
  791. "lock not held!")
  792. rthread.release_NOAUTO(state.ll_lock)
  793. debug_print("released")
  794. def after():
  795. debug_print("waiting...")
  796. rthread.acquire_NOAUTO(state.ll_lock, True)
  797. debug_print("acquired")
  798. def recurse(n):
  799. if n > 0:
  800. return recurse(n-1)+1
  801. else:
  802. time.sleep(0.2) # invokes before/after
  803. return 0
  804. # recurse a lot
  805. RECURSION = 19500
  806. if sys.platform == 'win32':
  807. # If I understand it correctly:
  808. # - The stack size "reserved" for a new thread is a compile-time
  809. # option (by default: 1Mb). This is a minimum that user code
  810. # cannot control.
  811. # - set_stacksize() only sets the initially "committed" size,
  812. # which eventually requires a larger "reserved" size.
  813. # - The limit below is large enough to exceed the "reserved" size,
  814. # for small values of set_stacksize().
  815. RECURSION = 150 * 1000
  816. def bootstrap():
  817. recurse(RECURSION)
  818. state.count += 1
  819. def entry_point(argv):
  820. os.write(1, "hello world\n")
  821. error = rthread.set_stacksize(int(argv[1]))
  822. if error != 0:
  823. os.write(2, "set_stacksize(%d) returned %d\n" % (
  824. int(argv[1]), error))
  825. raise AssertionError
  826. # malloc a bit
  827. s1 = State(); s2 = State(); s3 = State()
  828. s1.x = 0x11111111; s2.x = 0x22222222; s3.x = 0x33333333
  829. # start 3 new threads
  830. state.ll_lock = rthread.allocate_ll_lock()
  831. after()
  832. state.count = 0
  833. invoke_around_extcall(before, after)
  834. ident1 = rthread.start_new_thread(bootstrap, ())
  835. ident2 = rthread.start_new_thread(bootstrap, ())
  836. ident3 = rthread.start_new_thread(bootstrap, ())
  837. # wait for the 3 threads to finish
  838. while True:
  839. if state.count == 3:
  840. break
  841. time.sleep(0.1) # invokes before/after
  842. # check that the malloced structures were not overwritten
  843. assert s1.x == 0x11111111
  844. assert s2.x == 0x22222222
  845. assert s3.x == 0x33333333
  846. os.write(1, "done\n")
  847. return 0
  848. t, cbuilder = self.compile(entry_point)
  849. # recursing should crash with only 32 KB of stack,
  850. # and it should eventually work with more stack
  851. for test_kb in [32, 128, 512, 1024, 2048, 4096, 8192, 16384,
  852. 32768, 65536]:
  853. print >> sys.stderr, 'Trying with %d KB of stack...' % (test_kb,),
  854. try:
  855. data = cbuilder.cmdexec(str(test_kb * 1024))
  856. except Exception, e:
  857. if e.__class__ is not Exception:
  858. raise
  859. print >> sys.stderr, 'segfault'
  860. # got a segfault! try with the next stack size...
  861. else:
  862. # it worked
  863. print >> sys.stderr, 'ok'
  864. assert data == 'hello world\ndone\n'
  865. assert test_kb > 32 # it cannot work with just 32 KB of stack
  866. break # finish
  867. else:
  868. py.test.fail("none of the stack sizes worked")
  869. def test_thread_and_gc(self):
  870. import time, gc
  871. from rpython.rlib import rthread
  872. from rpython.rtyper.lltypesystem import lltype
  873. from rpython.rlib.objectmodel import invoke_around_extcall
  874. class State:
  875. pass
  876. state = State()
  877. def before():
  878. ll_assert(not rthread.acquire_NOAUTO(state.ll_lock, False),
  879. "lock not held!")
  880. rthread.release_NOAUTO(state.ll_lock)
  881. def after():
  882. rthread.acquire_NOAUTO(state.ll_lock, True)
  883. rthread.gc_thread_run()
  884. class Cons:
  885. def __init__(self, head, tail):
  886. self.head = head
  887. self.tail = tail
  888. def bootstrap():
  889. rthread.gc_thread_start()
  890. state.xlist.append(Cons(123, Cons(456, None)))
  891. gc.collect()
  892. rthread.gc_thread_die()
  893. def new_thread():
  894. rthread.gc_thread_prepare()
  895. ident = rthread.start_new_thread(bootstrap, ())
  896. time.sleep(0.5) # enough time to start, hopefully
  897. return ident
  898. def entry_point(argv):
  899. os.write(1, "hello world\n")
  900. state.xlist = []
  901. x2 = Cons(51, Cons(62, Cons(74, None)))
  902. # start 5 new threads
  903. state.ll_lock = rthread.allocate_ll_lock()
  904. after()
  905. invoke_around_extcall(before, after)
  906. ident1 = new_thread()
  907. ident2 = new_thread()
  908. #
  909. gc.collect()
  910. #
  911. ident3 = new_thread()
  912. ident4 = new_thread()
  913. ident5 = new_thread()
  914. # wait for the 5 threads to finish
  915. while True:
  916. gc.collect()
  917. if len(state.xlist) == 5:
  918. break
  919. time.sleep(0.1) # invokes before/after
  920. # check that the malloced structures were not overwritten
  921. assert x2.head == 51
  922. assert x2.tail.head == 62
  923. assert x2.tail.tail.head == 74
  924. assert x2.tail.tail.tail is None
  925. # check the structures produced by the threads
  926. for i in range(5):
  927. assert state.xlist[i].head == 123
  928. assert state.xlist[i].tail.head == 456
  929. assert state.xlist[i].tail.tail is None
  930. os.write(1, "%d ok\n" % (i+1))
  931. return 0
  932. t, cbuilder = self.compile(entry_point)
  933. data = cbuilder.cmdexec('')
  934. assert data.splitlines() == ['hello world',
  935. '1 ok',
  936. '2 ok',
  937. '3 ok',
  938. '4 ok',
  939. '5 ok']
  940. def test_gc_with_fork_without_threads(self):
  941. from rpython.rlib.objectmodel import invoke_around_extcall
  942. if not hasattr(os, 'fork'):
  943. py.test.skip("requires fork()")
  944. def entry_point(argv):
  945. childpid = os.fork()
  946. if childpid == 0:
  947. print "Testing..."
  948. else:
  949. pid, status = os.waitpid(childpid, 0)
  950. assert pid == childpid
  951. assert status == 0
  952. print "OK."
  953. return 0
  954. t, cbuilder = self.compile(entry_point)
  955. data = cbuilder.cmdexec('')
  956. print repr(data)
  957. assert data.startswith('Testing...\nOK.')
  958. def test_thread_and_gc_with_fork(self):
  959. # This checks that memory allocated for the shadow stacks of the
  960. # other threads is really released when doing a fork() -- or at
  961. # least that the object referenced from stacks that are no longer
  962. # alive are really freed.
  963. import time, gc, os
  964. from rpython.rlib import rthread
  965. from rpython.rlib.objectmodel import invoke_around_extcall
  966. if not hasattr(os, 'fork'):
  967. py.test.skip("requires fork()")
  968. class State:
  969. pass
  970. state = State()
  971. def before():
  972. ll_assert(not rthread.acquire_NOAUTO(state.ll_lock, False),
  973. "lock not held!")
  974. rthread.release_NOAUTO(state.ll_lock)
  975. def after():
  976. rthread.acquire_NOAUTO(state.ll_lock, True)
  977. rthread.gc_thread_run()
  978. class Cons:
  979. def __init__(self, head, tail):
  980. self.head = head
  981. self.tail = tail
  982. class Stuff:
  983. def __del__(self):
  984. os.write(state.write_end, 'd')
  985. def allocate_stuff():
  986. s = Stuff()
  987. os.write(state.write_end, 'a')
  988. return s
  989. def run_in_thread():
  990. for i in range(10):
  991. state.xlist.append(Cons(123, Cons(456, None)))
  992. time.sleep(0.01)
  993. childpid = os.fork()
  994. return childpid
  995. def bootstrap():
  996. rthread.gc_thread_start()
  997. childpid = run_in_thread()
  998. gc.collect() # collect both in the child and in the parent
  999. gc.collect()
  1000. gc.collect()
  1001. if childpid == 0:
  1002. os.write(state.write_end, 'c') # "I did not die!" from child
  1003. else:
  1004. os.write(state.write_end, 'p') # "I did not die!" from parent
  1005. rthread.gc_thread_die()
  1006. def new_thread():
  1007. rthread.gc_thread_prepare()
  1008. ident = rthread.start_new_thread(bootstrap, ())
  1009. time.sleep(0.5) # enough time to start, hopefully
  1010. return ident
  1011. def start_arthreads():
  1012. s = allocate_stuff()
  1013. ident1 = new_thread()
  1014. ident2 = new_thread()
  1015. ident3 = new_thread()
  1016. ident4 = new_thread()
  1017. ident5 = new_thread()
  1018. # wait for 4 more seconds, which should be plenty of time
  1019. time.sleep(4)
  1020. keepalive_until_here(s)
  1021. def entry_point(argv):
  1022. os.write(1, "hello world\n")
  1023. state.xlist = []
  1024. state.deleted = 0
  1025. state.read_end, state.write_end = os.pipe()
  1026. x2 = Cons(51, Cons(62, Cons(74, None)))
  1027. # start 5 new threads
  1028. state.ll_lock = rthread.allocate_ll_lock()
  1029. after()
  1030. invoke_around_extcall(before, after)
  1031. start_arthreads()
  1032. # force freeing
  1033. gc.collect()
  1034. gc.collect()
  1035. gc.collect()
  1036. # return everything that was written to the pipe so far,
  1037. # followed by the final dot.
  1038. os.write(state.write_end, '.')
  1039. result = os.read(state.read_end, 256)
  1040. os.write(1, "got: %s\n" % result)
  1041. return 0
  1042. t, cbuilder = self.compile(entry_point)
  1043. data = cbuilder.cmdexec('')
  1044. print repr(data)
  1045. header, footer = data.splitlines()
  1046. assert header == 'hello world'
  1047. assert footer.startswith('got: ')
  1048. result = footer[5:]
  1049. # check that all 5 threads and 5 forked processes
  1050. # finished successfully, that we did 1 allocation,
  1051. # and that it was freed 6 times -- once in the parent
  1052. # process and once in every child process.
  1053. assert (result[-1] == '.'
  1054. and result.count('c') == result.count('p') == 5
  1055. and result.count('a') == 1
  1056. and result.count('d') == 6)