PageRenderTime 69ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://bitbucket.org/kcr/pypy
Python | 1166 lines | 1155 code | 11 blank | 0 comment | 11 complexity | 31e41a8b4eb6fba6eec53d418325f033 MD5 | raw file
Possible License(s): Apache-2.0
  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. if os.name == 'posix':
  634. library_path = cbuilder.shared_library_name.dirpath()
  635. if sys.platform == 'darwin':
  636. monkeypatch.setenv('DYLD_LIBRARY_PATH', library_path)
  637. else:
  638. monkeypatch.setenv('LD_LIBRARY_PATH', library_path)
  639. out, err = cbuilder.cmdexec("a b")
  640. assert out == "3"
  641. def test_gcc_options(self):
  642. # check that the env var CC is correctly interpreted, even if
  643. # it contains the compiler name followed by some options.
  644. if sys.platform == 'win32':
  645. py.test.skip("only for gcc")
  646. from rpython.rtyper.lltypesystem import lltype, rffi
  647. dir = udir.ensure('test_gcc_options', dir=1)
  648. dir.join('someextraheader.h').write('#define someextrafunc() 42\n')
  649. eci = ExternalCompilationInfo(includes=['someextraheader.h'])
  650. someextrafunc = rffi.llexternal('someextrafunc', [], lltype.Signed,
  651. compilation_info=eci)
  652. def entry_point(argv):
  653. return someextrafunc()
  654. old_cc = os.environ.get('CC')
  655. try:
  656. os.environ['CC'] = 'gcc -I%s' % dir
  657. t, cbuilder = self.compile(entry_point)
  658. finally:
  659. if old_cc is None:
  660. del os.environ['CC']
  661. else:
  662. os.environ['CC'] = old_cc
  663. def test_inhibit_tail_call(self):
  664. # the point is to check that the f()->f() recursion stops
  665. from rpython.rlib.rstackovf import StackOverflow
  666. class Glob:
  667. pass
  668. glob = Glob()
  669. def f(n):
  670. glob.n = n
  671. if n <= 0:
  672. return 42
  673. return f(n+1)
  674. def entry_point(argv):
  675. try:
  676. return f(1)
  677. except StackOverflow:
  678. print 'hi!', glob.n
  679. return 0
  680. t, cbuilder = self.compile(entry_point, stackcheck=True)
  681. out = cbuilder.cmdexec("")
  682. text = out.strip()
  683. assert text.startswith("hi! ")
  684. n = int(text[4:])
  685. assert n > 500 and n < 5000000
  686. def test_set_length_fraction(self):
  687. # check for rpython.rlib.rstack._stack_set_length_fraction()
  688. from rpython.rlib.rstack import _stack_set_length_fraction
  689. from rpython.rlib.rstackovf import StackOverflow
  690. class A:
  691. n = 0
  692. glob = A()
  693. def f(n):
  694. glob.n += 1
  695. if n <= 0:
  696. return 42
  697. return f(n+1)
  698. def entry_point(argv):
  699. _stack_set_length_fraction(0.1)
  700. try:
  701. return f(1)
  702. except StackOverflow:
  703. glob.n = 0
  704. _stack_set_length_fraction(float(argv[1]))
  705. try:
  706. return f(1)
  707. except StackOverflow:
  708. print glob.n
  709. return 0
  710. t, cbuilder = self.compile(entry_point, stackcheck=True)
  711. counts = {}
  712. for fraction in [0.1, 0.4, 1.0]:
  713. out = cbuilder.cmdexec(str(fraction))
  714. print 'counts[%s]: %r' % (fraction, out)
  715. counts[fraction] = int(out.strip())
  716. #
  717. assert counts[1.0] >= 1000
  718. # ^^^ should actually be much more than 1000 for this small test
  719. assert counts[0.1] < counts[0.4] / 3
  720. assert counts[0.4] < counts[1.0] / 2
  721. assert counts[0.1] > counts[0.4] / 7
  722. assert counts[0.4] > counts[1.0] / 4
  723. def test_stack_criticalcode(self):
  724. # check for rpython.rlib.rstack._stack_criticalcode_start/stop()
  725. from rpython.rlib.rstack import _stack_criticalcode_start
  726. from rpython.rlib.rstack import _stack_criticalcode_stop
  727. from rpython.rlib.rstackovf import StackOverflow
  728. class A:
  729. pass
  730. glob = A()
  731. def f(n):
  732. if n <= 0:
  733. return 42
  734. try:
  735. return f(n+1)
  736. except StackOverflow:
  737. if glob.caught:
  738. print 'Oups! already caught!'
  739. glob.caught = True
  740. _stack_criticalcode_start()
  741. critical(100) # recurse another 100 times here
  742. _stack_criticalcode_stop()
  743. return 789
  744. def critical(n):
  745. if n > 0:
  746. n = critical(n - 1)
  747. return n - 42
  748. def entry_point(argv):
  749. glob.caught = False
  750. print f(1)
  751. return 0
  752. t, cbuilder = self.compile(entry_point, stackcheck=True)
  753. out = cbuilder.cmdexec('')
  754. assert out.strip() == '789'
  755. class TestMaemo(TestStandalone):
  756. def setup_class(cls):
  757. py.test.skip("TestMaemo: tests skipped for now")
  758. from rpython.translator.platform.maemo import check_scratchbox
  759. check_scratchbox()
  760. from rpython.config.translationoption import get_combined_translation_config
  761. config = get_combined_translation_config(translating=True)
  762. config.translation.platform = 'maemo'
  763. cls.config = config
  764. def test_profopt(self):
  765. py.test.skip("Unsupported")
  766. def test_prof_inline(self):
  767. py.test.skip("Unsupported")
  768. class TestThread(object):
  769. gcrootfinder = 'shadowstack'
  770. config = None
  771. def compile(self, entry_point):
  772. t = TranslationContext(self.config)
  773. t.config.translation.gc = "semispace"
  774. t.config.translation.gcrootfinder = self.gcrootfinder
  775. t.config.translation.thread = True
  776. t.buildannotator().build_types(entry_point, [s_list_of_strings])
  777. t.buildrtyper().specialize()
  778. #
  779. cbuilder = CStandaloneBuilder(t, entry_point, t.config)
  780. cbuilder.generate_source(defines=cbuilder.DEBUG_DEFINES)
  781. cbuilder.compile()
  782. #
  783. return t, cbuilder
  784. def test_stack_size(self):
  785. import time
  786. from rpython.rlib import rthread
  787. from rpython.rtyper.lltypesystem import lltype
  788. from rpython.rlib.objectmodel import invoke_around_extcall
  789. class State:
  790. pass
  791. state = State()
  792. def before():
  793. debug_print("releasing...")
  794. ll_assert(not rthread.acquire_NOAUTO(state.ll_lock, False),
  795. "lock not held!")
  796. rthread.release_NOAUTO(state.ll_lock)
  797. debug_print("released")
  798. def after():
  799. debug_print("waiting...")
  800. rthread.acquire_NOAUTO(state.ll_lock, True)
  801. debug_print("acquired")
  802. def recurse(n):
  803. if n > 0:
  804. return recurse(n-1)+1
  805. else:
  806. time.sleep(0.2) # invokes before/after
  807. return 0
  808. # recurse a lot
  809. RECURSION = 19500
  810. if sys.platform == 'win32':
  811. # If I understand it correctly:
  812. # - The stack size "reserved" for a new thread is a compile-time
  813. # option (by default: 1Mb). This is a minimum that user code
  814. # cannot control.
  815. # - set_stacksize() only sets the initially "committed" size,
  816. # which eventually requires a larger "reserved" size.
  817. # - The limit below is large enough to exceed the "reserved" size,
  818. # for small values of set_stacksize().
  819. RECURSION = 150 * 1000
  820. def bootstrap():
  821. recurse(RECURSION)
  822. state.count += 1
  823. def entry_point(argv):
  824. os.write(1, "hello world\n")
  825. error = rthread.set_stacksize(int(argv[1]))
  826. if error != 0:
  827. os.write(2, "set_stacksize(%d) returned %d\n" % (
  828. int(argv[1]), error))
  829. raise AssertionError
  830. # malloc a bit
  831. s1 = State(); s2 = State(); s3 = State()
  832. s1.x = 0x11111111; s2.x = 0x22222222; s3.x = 0x33333333
  833. # start 3 new threads
  834. state.ll_lock = rthread.allocate_ll_lock()
  835. after()
  836. state.count = 0
  837. invoke_around_extcall(before, after)
  838. ident1 = rthread.start_new_thread(bootstrap, ())
  839. ident2 = rthread.start_new_thread(bootstrap, ())
  840. ident3 = rthread.start_new_thread(bootstrap, ())
  841. # wait for the 3 threads to finish
  842. while True:
  843. if state.count == 3:
  844. break
  845. time.sleep(0.1) # invokes before/after
  846. # check that the malloced structures were not overwritten
  847. assert s1.x == 0x11111111
  848. assert s2.x == 0x22222222
  849. assert s3.x == 0x33333333
  850. os.write(1, "done\n")
  851. return 0
  852. t, cbuilder = self.compile(entry_point)
  853. # recursing should crash with only 32 KB of stack,
  854. # and it should eventually work with more stack
  855. for test_kb in [32, 128, 512, 1024, 2048, 4096, 8192, 16384,
  856. 32768, 65536]:
  857. print >> sys.stderr, 'Trying with %d KB of stack...' % (test_kb,),
  858. try:
  859. data = cbuilder.cmdexec(str(test_kb * 1024))
  860. except Exception, e:
  861. if e.__class__ is not Exception:
  862. raise
  863. print >> sys.stderr, 'segfault'
  864. # got a segfault! try with the next stack size...
  865. else:
  866. # it worked
  867. print >> sys.stderr, 'ok'
  868. assert data == 'hello world\ndone\n'
  869. assert test_kb > 32 # it cannot work with just 32 KB of stack
  870. break # finish
  871. else:
  872. py.test.fail("none of the stack sizes worked")
  873. def test_thread_and_gc(self):
  874. import time, gc
  875. from rpython.rlib import rthread
  876. from rpython.rtyper.lltypesystem import lltype
  877. from rpython.rlib.objectmodel import invoke_around_extcall
  878. class State:
  879. pass
  880. state = State()
  881. def before():
  882. ll_assert(not rthread.acquire_NOAUTO(state.ll_lock, False),
  883. "lock not held!")
  884. rthread.release_NOAUTO(state.ll_lock)
  885. def after():
  886. rthread.acquire_NOAUTO(state.ll_lock, True)
  887. rthread.gc_thread_run()
  888. class Cons:
  889. def __init__(self, head, tail):
  890. self.head = head
  891. self.tail = tail
  892. def bootstrap():
  893. rthread.gc_thread_start()
  894. state.xlist.append(Cons(123, Cons(456, None)))
  895. gc.collect()
  896. rthread.gc_thread_die()
  897. def new_thread():
  898. rthread.gc_thread_prepare()
  899. ident = rthread.start_new_thread(bootstrap, ())
  900. time.sleep(0.5) # enough time to start, hopefully
  901. return ident
  902. def entry_point(argv):
  903. os.write(1, "hello world\n")
  904. state.xlist = []
  905. x2 = Cons(51, Cons(62, Cons(74, None)))
  906. # start 5 new threads
  907. state.ll_lock = rthread.allocate_ll_lock()
  908. after()
  909. invoke_around_extcall(before, after)
  910. ident1 = new_thread()
  911. ident2 = new_thread()
  912. #
  913. gc.collect()
  914. #
  915. ident3 = new_thread()
  916. ident4 = new_thread()
  917. ident5 = new_thread()
  918. # wait for the 5 threads to finish
  919. while True:
  920. gc.collect()
  921. if len(state.xlist) == 5:
  922. break
  923. time.sleep(0.1) # invokes before/after
  924. # check that the malloced structures were not overwritten
  925. assert x2.head == 51
  926. assert x2.tail.head == 62
  927. assert x2.tail.tail.head == 74
  928. assert x2.tail.tail.tail is None
  929. # check the structures produced by the threads
  930. for i in range(5):
  931. assert state.xlist[i].head == 123
  932. assert state.xlist[i].tail.head == 456
  933. assert state.xlist[i].tail.tail is None
  934. os.write(1, "%d ok\n" % (i+1))
  935. return 0
  936. t, cbuilder = self.compile(entry_point)
  937. data = cbuilder.cmdexec('')
  938. assert data.splitlines() == ['hello world',
  939. '1 ok',
  940. '2 ok',
  941. '3 ok',
  942. '4 ok',
  943. '5 ok']
  944. def test_gc_with_fork_without_threads(self):
  945. from rpython.rlib.objectmodel import invoke_around_extcall
  946. if not hasattr(os, 'fork'):
  947. py.test.skip("requires fork()")
  948. def entry_point(argv):
  949. childpid = os.fork()
  950. if childpid == 0:
  951. print "Testing..."
  952. else:
  953. pid, status = os.waitpid(childpid, 0)
  954. assert pid == childpid
  955. assert status == 0
  956. print "OK."
  957. return 0
  958. t, cbuilder = self.compile(entry_point)
  959. data = cbuilder.cmdexec('')
  960. print repr(data)
  961. assert data.startswith('Testing...\nOK.')
  962. def test_thread_and_gc_with_fork(self):
  963. # This checks that memory allocated for the shadow stacks of the
  964. # other threads is really released when doing a fork() -- or at
  965. # least that the object referenced from stacks that are no longer
  966. # alive are really freed.
  967. import time, gc, os
  968. from rpython.rlib import rthread
  969. from rpython.rlib.objectmodel import invoke_around_extcall
  970. if not hasattr(os, 'fork'):
  971. py.test.skip("requires fork()")
  972. class State:
  973. pass
  974. state = State()
  975. def before():
  976. ll_assert(not rthread.acquire_NOAUTO(state.ll_lock, False),
  977. "lock not held!")
  978. rthread.release_NOAUTO(state.ll_lock)
  979. def after():
  980. rthread.acquire_NOAUTO(state.ll_lock, True)
  981. rthread.gc_thread_run()
  982. class Cons:
  983. def __init__(self, head, tail):
  984. self.head = head
  985. self.tail = tail
  986. class Stuff:
  987. def __del__(self):
  988. os.write(state.write_end, 'd')
  989. def allocate_stuff():
  990. s = Stuff()
  991. os.write(state.write_end, 'a')
  992. return s
  993. def run_in_thread():
  994. for i in range(10):
  995. state.xlist.append(Cons(123, Cons(456, None)))
  996. time.sleep(0.01)
  997. childpid = os.fork()
  998. return childpid
  999. def bootstrap():
  1000. rthread.gc_thread_start()
  1001. childpid = run_in_thread()
  1002. gc.collect() # collect both in the child and in the parent
  1003. gc.collect()
  1004. gc.collect()
  1005. if childpid == 0:
  1006. os.write(state.write_end, 'c') # "I did not die!" from child
  1007. else:
  1008. os.write(state.write_end, 'p') # "I did not die!" from parent
  1009. rthread.gc_thread_die()
  1010. def new_thread():
  1011. rthread.gc_thread_prepare()
  1012. ident = rthread.start_new_thread(bootstrap, ())
  1013. time.sleep(0.5) # enough time to start, hopefully
  1014. return ident
  1015. def start_arthreads():
  1016. s = allocate_stuff()
  1017. ident1 = new_thread()
  1018. ident2 = new_thread()
  1019. ident3 = new_thread()
  1020. ident4 = new_thread()
  1021. ident5 = new_thread()
  1022. # wait for 4 more seconds, which should be plenty of time
  1023. time.sleep(4)
  1024. keepalive_until_here(s)
  1025. def entry_point(argv):
  1026. os.write(1, "hello world\n")
  1027. state.xlist = []
  1028. state.deleted = 0
  1029. state.read_end, state.write_end = os.pipe()
  1030. x2 = Cons(51, Cons(62, Cons(74, None)))
  1031. # start 5 new threads
  1032. state.ll_lock = rthread.allocate_ll_lock()
  1033. after()
  1034. invoke_around_extcall(before, after)
  1035. start_arthreads()
  1036. # force freeing
  1037. gc.collect()
  1038. gc.collect()
  1039. gc.collect()
  1040. # return everything that was written to the pipe so far,
  1041. # followed by the final dot.
  1042. os.write(state.write_end, '.')
  1043. result = os.read(state.read_end, 256)
  1044. os.write(1, "got: %s\n" % result)
  1045. return 0
  1046. t, cbuilder = self.compile(entry_point)
  1047. data = cbuilder.cmdexec('')
  1048. print repr(data)
  1049. header, footer = data.splitlines()
  1050. assert header == 'hello world'
  1051. assert footer.startswith('got: ')
  1052. result = footer[5:]
  1053. # check that all 5 threads and 5 forked processes
  1054. # finished successfully, that we did 1 allocation,
  1055. # and that it was freed 6 times -- once in the parent
  1056. # process and once in every child process.
  1057. assert (result[-1] == '.'
  1058. and result.count('c') == result.count('p') == 5
  1059. and result.count('a') == 1
  1060. and result.count('d') == 6)