PageRenderTime 48ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/pypy/translator/c/test/test_standalone.py

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