PageRenderTime 54ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://bitbucket.org/pypy/pypy/
Python | 1419 lines | 1401 code | 13 blank | 5 comment | 34 complexity | 304cbff04bc738800978c215bd532e3d MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0

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

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

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