PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/interpreter/executioncontext.py

https://bitbucket.org/pypy/pypy/
Python | 625 lines | 441 code | 73 blank | 111 comment | 78 complexity | 125142d20ea767c6017265d8cf2ebbe1 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import sys
  2. from pypy.interpreter.error import OperationError, get_cleared_operation_error
  3. from rpython.rlib.unroll import unrolling_iterable
  4. from rpython.rlib.objectmodel import specialize
  5. from rpython.rlib import jit, rgc
  6. TICK_COUNTER_STEP = 100
  7. def app_profile_call(space, w_callable, frame, event, w_arg):
  8. space.call_function(w_callable,
  9. space.wrap(frame),
  10. space.wrap(event), w_arg)
  11. class ExecutionContext(object):
  12. """An ExecutionContext holds the state of an execution thread
  13. in the Python interpreter."""
  14. # XXX JIT: when tracing (but not when blackholing!), the following
  15. # XXX fields should be known to a constant None or False:
  16. # XXX self.w_tracefunc, self.profilefunc
  17. # XXX frame.is_being_profiled
  18. # XXX [fijal] but they're not. is_being_profiled is guarded a bit all
  19. # over the place as well as w_tracefunc
  20. _immutable_fields_ = ['profilefunc?', 'w_tracefunc?']
  21. def __init__(self, space):
  22. self.space = space
  23. self.topframeref = jit.vref_None
  24. self.w_tracefunc = None
  25. self.is_tracing = 0
  26. self.compiler = space.createcompiler()
  27. self.profilefunc = None
  28. self.w_profilefuncarg = None
  29. self.thread_disappeared = False # might be set to True after os.fork()
  30. @staticmethod
  31. def _mark_thread_disappeared(space):
  32. # Called in the child process after os.fork() by interp_posix.py.
  33. # Marks all ExecutionContexts except the current one
  34. # with 'thread_disappeared = True'.
  35. me = space.getexecutioncontext()
  36. for ec in space.threadlocals.getallvalues().values():
  37. if ec is not me:
  38. ec.thread_disappeared = True
  39. def gettopframe(self):
  40. return self.topframeref()
  41. @jit.unroll_safe
  42. def gettopframe_nohidden(self):
  43. frame = self.topframeref()
  44. while frame and frame.hide():
  45. frame = frame.f_backref()
  46. return frame
  47. @staticmethod
  48. @jit.unroll_safe # should usually loop 0 times, very rarely more than once
  49. def getnextframe_nohidden(frame):
  50. frame = frame.f_backref()
  51. while frame and frame.hide():
  52. frame = frame.f_backref()
  53. return frame
  54. def enter(self, frame):
  55. frame.f_backref = self.topframeref
  56. self.topframeref = jit.virtual_ref(frame)
  57. def leave(self, frame, w_exitvalue, got_exception):
  58. try:
  59. if self.profilefunc:
  60. self._trace(frame, 'leaveframe', w_exitvalue)
  61. finally:
  62. frame_vref = self.topframeref
  63. self.topframeref = frame.f_backref
  64. if frame.escaped or got_exception:
  65. # if this frame escaped to applevel, we must ensure that also
  66. # f_back does
  67. f_back = frame.f_backref()
  68. if f_back:
  69. f_back.mark_as_escaped()
  70. # force the frame (from the JIT point of view), so that it can
  71. # be accessed also later
  72. frame_vref()
  73. jit.virtual_ref_finish(frame_vref, frame)
  74. # ________________________________________________________________
  75. def c_call_trace(self, frame, w_func, args=None):
  76. "Profile the call of a builtin function"
  77. self._c_call_return_trace(frame, w_func, args, 'c_call')
  78. def c_return_trace(self, frame, w_func, args=None):
  79. "Profile the return from a builtin function"
  80. self._c_call_return_trace(frame, w_func, args, 'c_return')
  81. def _c_call_return_trace(self, frame, w_func, args, event):
  82. if self.profilefunc is None:
  83. frame.getorcreatedebug().is_being_profiled = False
  84. else:
  85. # undo the effect of the CALL_METHOD bytecode, which would be
  86. # that even on a built-in method call like '[].append()',
  87. # w_func is actually the unbound function 'append'.
  88. from pypy.interpreter.function import FunctionWithFixedCode
  89. if isinstance(w_func, FunctionWithFixedCode) and args is not None:
  90. w_firstarg = args.firstarg()
  91. if w_firstarg is not None:
  92. from pypy.interpreter.function import descr_function_get
  93. w_func = descr_function_get(self.space, w_func, w_firstarg,
  94. self.space.type(w_firstarg))
  95. #
  96. self._trace(frame, event, w_func)
  97. def c_exception_trace(self, frame, w_exc):
  98. "Profile function called upon OperationError."
  99. if self.profilefunc is None:
  100. frame.getorcreatedebug().is_being_profiled = False
  101. else:
  102. self._trace(frame, 'c_exception', w_exc)
  103. def call_trace(self, frame):
  104. "Trace the call of a function"
  105. if self.gettrace() is not None or self.profilefunc is not None:
  106. self._trace(frame, 'call', self.space.w_None)
  107. if self.profilefunc:
  108. frame.getorcreatedebug().is_being_profiled = True
  109. def return_trace(self, frame, w_retval):
  110. "Trace the return from a function"
  111. if self.gettrace() is not None:
  112. self._trace(frame, 'return', w_retval)
  113. def bytecode_trace(self, frame, decr_by=TICK_COUNTER_STEP):
  114. "Trace function called before each bytecode."
  115. # this is split into a fast path and a slower path that is
  116. # not invoked every time bytecode_trace() is.
  117. self.bytecode_only_trace(frame)
  118. actionflag = self.space.actionflag
  119. if actionflag.decrement_ticker(decr_by) < 0:
  120. actionflag.action_dispatcher(self, frame) # slow path
  121. bytecode_trace._always_inline_ = True
  122. def _run_finalizers_now(self):
  123. # Tests only: run the actions now, to ensure that the
  124. # finalizable objects are really finalized. Used notably by
  125. # pypy.tool.pytest.apptest.
  126. self.space.actionflag.action_dispatcher(self, None)
  127. def bytecode_only_trace(self, frame):
  128. """
  129. Like bytecode_trace() but doesn't invoke any other events besides the
  130. trace function.
  131. """
  132. if (frame.get_w_f_trace() is None or self.is_tracing or
  133. self.gettrace() is None):
  134. return
  135. self.run_trace_func(frame)
  136. bytecode_only_trace._always_inline_ = True
  137. @jit.unroll_safe
  138. def run_trace_func(self, frame):
  139. code = frame.pycode
  140. d = frame.getorcreatedebug()
  141. if d.instr_lb <= frame.last_instr < d.instr_ub:
  142. if frame.last_instr < d.instr_prev_plus_one:
  143. # We jumped backwards in the same line.
  144. self._trace(frame, 'line', self.space.w_None)
  145. else:
  146. size = len(code.co_lnotab) / 2
  147. addr = 0
  148. line = code.co_firstlineno
  149. p = 0
  150. lineno = code.co_lnotab
  151. while size > 0:
  152. c = ord(lineno[p])
  153. if (addr + c) > frame.last_instr:
  154. break
  155. addr += c
  156. if c:
  157. d.instr_lb = addr
  158. line += ord(lineno[p + 1])
  159. p += 2
  160. size -= 1
  161. if size > 0:
  162. while True:
  163. size -= 1
  164. if size < 0:
  165. break
  166. addr += ord(lineno[p])
  167. if ord(lineno[p + 1]):
  168. break
  169. p += 2
  170. d.instr_ub = addr
  171. else:
  172. d.instr_ub = sys.maxint
  173. if d.instr_lb == frame.last_instr: # At start of line!
  174. d.f_lineno = line
  175. self._trace(frame, 'line', self.space.w_None)
  176. d.instr_prev_plus_one = frame.last_instr + 1
  177. def bytecode_trace_after_exception(self, frame):
  178. "Like bytecode_trace(), but without increasing the ticker."
  179. actionflag = self.space.actionflag
  180. self.bytecode_only_trace(frame)
  181. if actionflag.get_ticker() < 0:
  182. actionflag.action_dispatcher(self, frame) # slow path
  183. bytecode_trace_after_exception._always_inline_ = 'try'
  184. # NB. this function is not inlined right now. backendopt.inline would
  185. # need some improvements to handle this case, but it's not really an
  186. # issue
  187. def exception_trace(self, frame, operationerr):
  188. "Trace function called upon OperationError."
  189. if self.gettrace() is not None:
  190. self._trace(frame, 'exception', None, operationerr)
  191. #operationerr.print_detailed_traceback(self.space)
  192. @jit.dont_look_inside
  193. @specialize.arg(1)
  194. def sys_exc_info(self, for_hidden=False):
  195. """Implements sys.exc_info().
  196. Return an OperationError instance or None.
  197. Ignores exceptions within hidden frames unless for_hidden=True
  198. is specified.
  199. # NOTE: the result is not the wrapped sys.exc_info() !!!
  200. """
  201. return self.gettopframe()._exc_info_unroll(self.space, for_hidden)
  202. def set_sys_exc_info(self, operror):
  203. frame = self.gettopframe_nohidden()
  204. if frame: # else, the exception goes nowhere and is lost
  205. frame.last_exception = operror
  206. def clear_sys_exc_info(self):
  207. # Find the frame out of which sys_exc_info() would return its result,
  208. # and hack this frame's last_exception to become the cleared
  209. # OperationError (which is different from None!).
  210. frame = self.gettopframe_nohidden()
  211. while frame:
  212. if frame.last_exception is not None:
  213. frame.last_exception = get_cleared_operation_error(self.space)
  214. break
  215. frame = self.getnextframe_nohidden(frame)
  216. @jit.dont_look_inside
  217. def settrace(self, w_func):
  218. """Set the global trace function."""
  219. if self.space.is_w(w_func, self.space.w_None):
  220. self.w_tracefunc = None
  221. else:
  222. self.force_all_frames()
  223. self.w_tracefunc = w_func
  224. # Increase the JIT's trace_limit when we have a tracefunc, it
  225. # generates a ton of extra ops.
  226. jit.set_param(None, 'trace_limit', 10000)
  227. def gettrace(self):
  228. return jit.promote(self.w_tracefunc)
  229. def setprofile(self, w_func):
  230. """Set the global trace function."""
  231. if self.space.is_w(w_func, self.space.w_None):
  232. self.profilefunc = None
  233. self.w_profilefuncarg = None
  234. else:
  235. self.setllprofile(app_profile_call, w_func)
  236. def getprofile(self):
  237. return self.w_profilefuncarg
  238. def setllprofile(self, func, w_arg):
  239. if func is not None:
  240. if w_arg is None:
  241. raise ValueError("Cannot call setllprofile with real None")
  242. self.force_all_frames(is_being_profiled=True)
  243. self.profilefunc = func
  244. self.w_profilefuncarg = w_arg
  245. def force_all_frames(self, is_being_profiled=False):
  246. # "Force" all frames in the sense of the jit, and optionally
  247. # set the flag 'is_being_profiled' on them. A forced frame is
  248. # one out of which the jit will exit: if it is running so far,
  249. # in a piece of assembler currently running a CALL_MAY_FORCE,
  250. # then being forced means that it will fail the following
  251. # GUARD_NOT_FORCED operation, and so fall back to interpreted
  252. # execution. (We get this effect simply by reading the f_back
  253. # field of all frames, during the loop below.)
  254. frame = self.gettopframe_nohidden()
  255. while frame:
  256. if is_being_profiled:
  257. frame.getorcreatedebug().is_being_profiled = True
  258. frame = self.getnextframe_nohidden(frame)
  259. def call_tracing(self, w_func, w_args):
  260. is_tracing = self.is_tracing
  261. self.is_tracing = 0
  262. try:
  263. return self.space.call(w_func, w_args)
  264. finally:
  265. self.is_tracing = is_tracing
  266. def _trace(self, frame, event, w_arg, operr=None):
  267. if self.is_tracing or frame.hide():
  268. return
  269. space = self.space
  270. # Tracing cases
  271. if event == 'call':
  272. w_callback = self.gettrace()
  273. else:
  274. w_callback = frame.get_w_f_trace()
  275. if w_callback is not None and event != "leaveframe":
  276. if operr is not None:
  277. w_value = operr.get_w_value(space)
  278. w_arg = space.newtuple([operr.w_type, w_value,
  279. space.wrap(operr.get_traceback())])
  280. d = frame.getorcreatedebug()
  281. if d.w_locals is not None:
  282. # only update the w_locals dict if it exists
  283. # if it does not exist yet and the tracer accesses it via
  284. # frame.f_locals, it is filled by PyFrame.getdictscope
  285. frame.fast2locals()
  286. self.is_tracing += 1
  287. try:
  288. try:
  289. w_result = space.call_function(w_callback, space.wrap(frame), space.wrap(event), w_arg)
  290. if space.is_w(w_result, space.w_None):
  291. d.w_f_trace = None
  292. else:
  293. d.w_f_trace = w_result
  294. except:
  295. self.settrace(space.w_None)
  296. d.w_f_trace = None
  297. raise
  298. finally:
  299. self.is_tracing -= 1
  300. if d.w_locals is not None:
  301. frame.locals2fast()
  302. # Profile cases
  303. if self.profilefunc is not None:
  304. if not (event == 'leaveframe' or
  305. event == 'call' or
  306. event == 'c_call' or
  307. event == 'c_return' or
  308. event == 'c_exception'):
  309. return
  310. last_exception = frame.last_exception
  311. if event == 'leaveframe':
  312. event = 'return'
  313. assert self.is_tracing == 0
  314. self.is_tracing += 1
  315. try:
  316. try:
  317. self.profilefunc(space, self.w_profilefuncarg,
  318. frame, event, w_arg)
  319. except:
  320. self.profilefunc = None
  321. self.w_profilefuncarg = None
  322. raise
  323. finally:
  324. frame.last_exception = last_exception
  325. self.is_tracing -= 1
  326. def checksignals(self):
  327. """Similar to PyErr_CheckSignals(). If called in the main thread,
  328. and if signals are pending for the process, deliver them now
  329. (i.e. call the signal handlers)."""
  330. if self.space.check_signal_action is not None:
  331. self.space.check_signal_action.perform(self, None)
  332. def _freeze_(self):
  333. raise Exception("ExecutionContext instances should not be seen during"
  334. " translation. Now is a good time to inspect the"
  335. " traceback and see where this one comes from :-)")
  336. class AbstractActionFlag(object):
  337. """This holds in an integer the 'ticker'. If threads are enabled,
  338. it is decremented at each bytecode; when it reaches zero, we release
  339. the GIL. And whether we have threads or not, it is forced to zero
  340. whenever we fire any of the asynchronous actions.
  341. """
  342. _immutable_fields_ = ["checkinterval_scaled?"]
  343. def __init__(self):
  344. self._periodic_actions = []
  345. self._nonperiodic_actions = []
  346. self.has_bytecode_counter = False
  347. self.fired_actions = None
  348. # the default value is not 100, unlike CPython 2.7, but a much
  349. # larger value, because we use a technique that not only allows
  350. # but actually *forces* another thread to run whenever the counter
  351. # reaches zero.
  352. self.checkinterval_scaled = 10000 * TICK_COUNTER_STEP
  353. self._rebuild_action_dispatcher()
  354. def fire(self, action):
  355. """Request for the action to be run before the next opcode."""
  356. if not action._fired:
  357. action._fired = True
  358. if self.fired_actions is None:
  359. self.fired_actions = []
  360. self.fired_actions.append(action)
  361. # set the ticker to -1 in order to force action_dispatcher()
  362. # to run at the next possible bytecode
  363. self.reset_ticker(-1)
  364. def register_periodic_action(self, action, use_bytecode_counter):
  365. """NOT_RPYTHON:
  366. Register the PeriodicAsyncAction action to be called whenever the
  367. tick counter becomes smaller than 0. If 'use_bytecode_counter' is
  368. True, make sure that we decrease the tick counter at every bytecode.
  369. This is needed for threads. Note that 'use_bytecode_counter' can be
  370. False for signal handling, because whenever the process receives a
  371. signal, the tick counter is set to -1 by C code in signals.h.
  372. """
  373. assert isinstance(action, PeriodicAsyncAction)
  374. # hack to put the release-the-GIL one at the end of the list,
  375. # and the report-the-signals one at the start of the list.
  376. if use_bytecode_counter:
  377. self._periodic_actions.append(action)
  378. self.has_bytecode_counter = True
  379. else:
  380. self._periodic_actions.insert(0, action)
  381. self._rebuild_action_dispatcher()
  382. def getcheckinterval(self):
  383. return self.checkinterval_scaled // TICK_COUNTER_STEP
  384. def setcheckinterval(self, interval):
  385. MAX = sys.maxint // TICK_COUNTER_STEP
  386. if interval < 1:
  387. interval = 1
  388. elif interval > MAX:
  389. interval = MAX
  390. self.checkinterval_scaled = interval * TICK_COUNTER_STEP
  391. self.reset_ticker(-1)
  392. def _rebuild_action_dispatcher(self):
  393. periodic_actions = unrolling_iterable(self._periodic_actions)
  394. @jit.unroll_safe
  395. def action_dispatcher(ec, frame):
  396. # periodic actions (first reset the bytecode counter)
  397. self.reset_ticker(self.checkinterval_scaled)
  398. for action in periodic_actions:
  399. action.perform(ec, frame)
  400. # nonperiodic actions
  401. list = self.fired_actions
  402. if list is not None:
  403. self.fired_actions = None
  404. # NB. in case there are several actions, we reset each
  405. # 'action._fired' to false only when we're about to call
  406. # 'action.perform()'. This means that if
  407. # 'action.fire()' happens to be called any time before
  408. # the corresponding perform(), the fire() has no
  409. # effect---which is the effect we want, because
  410. # perform() will be called anyway.
  411. for action in list:
  412. action._fired = False
  413. action.perform(ec, frame)
  414. action_dispatcher._dont_inline_ = True
  415. self.action_dispatcher = action_dispatcher
  416. class ActionFlag(AbstractActionFlag):
  417. """The normal class for space.actionflag. The signal module provides
  418. a different one."""
  419. _ticker = 0
  420. def get_ticker(self):
  421. return self._ticker
  422. def reset_ticker(self, value):
  423. self._ticker = value
  424. def decrement_ticker(self, by):
  425. value = self._ticker
  426. if self.has_bytecode_counter: # this 'if' is constant-folded
  427. if jit.isconstant(by) and by == 0:
  428. pass # normally constant-folded too
  429. else:
  430. value -= by
  431. self._ticker = value
  432. return value
  433. class AsyncAction(object):
  434. """Abstract base class for actions that must be performed
  435. asynchronously with regular bytecode execution, but that still need
  436. to occur between two opcodes, not at a completely random time.
  437. """
  438. _fired = False
  439. def __init__(self, space):
  440. self.space = space
  441. def fire(self):
  442. """Request for the action to be run before the next opcode.
  443. The action must have been registered at space initalization time."""
  444. self.space.actionflag.fire(self)
  445. def perform(self, executioncontext, frame):
  446. """To be overridden."""
  447. class PeriodicAsyncAction(AsyncAction):
  448. """Abstract base class for actions that occur automatically
  449. every sys.checkinterval bytecodes.
  450. """
  451. class UserDelAction(AsyncAction):
  452. """An action that invokes all pending app-level __del__() method.
  453. This is done as an action instead of immediately when the
  454. WRootFinalizerQueue is triggered, because the latter can occur more
  455. or less anywhere in the middle of code that might not be happy with
  456. random app-level code mutating data structures under its feet.
  457. """
  458. def __init__(self, space):
  459. AsyncAction.__init__(self, space)
  460. self.finalizers_lock_count = 0 # see pypy/module/gc
  461. self.enabled_at_app_level = True # see pypy/module/gc
  462. self.pending_with_disabled_del = None
  463. def perform(self, executioncontext, frame):
  464. self._run_finalizers()
  465. @jit.dont_look_inside
  466. def _run_finalizers(self):
  467. while True:
  468. w_obj = self.space.finalizer_queue.next_dead()
  469. if w_obj is None:
  470. break
  471. self._call_finalizer(w_obj)
  472. def gc_disabled(self, w_obj):
  473. # If we're running in 'gc.disable()' mode, record w_obj in the
  474. # "call me later" list and return True. In normal mode, return
  475. # False. Use this function from some _finalize_() methods:
  476. # if a _finalize_() method would call some user-defined
  477. # app-level function, like a weakref callback, then first do
  478. # 'if gc.disabled(self): return'. Another attempt at
  479. # calling _finalize_() will be made after 'gc.enable()'.
  480. # (The exact rule for when to use gc_disabled() or not is a bit
  481. # vague, but most importantly this includes all user-level
  482. # __del__().)
  483. pdd = self.pending_with_disabled_del
  484. if pdd is None:
  485. return False
  486. else:
  487. pdd.append(w_obj)
  488. return True
  489. def _call_finalizer(self, w_obj):
  490. # Before calling the finalizers, clear the weakrefs, if any.
  491. w_obj.clear_all_weakrefs()
  492. # Look up and call the app-level __del__, if any.
  493. space = self.space
  494. if w_obj.typedef is None:
  495. w_del = None # obscure case: for WeakrefLifeline
  496. else:
  497. w_del = space.lookup(w_obj, '__del__')
  498. if w_del is not None:
  499. if self.gc_disabled(w_obj):
  500. return
  501. try:
  502. space.get_and_call_function(w_del, w_obj)
  503. except Exception as e:
  504. report_error(space, e, "method __del__ of ", w_obj)
  505. # Call the RPython-level _finalize_() method.
  506. try:
  507. w_obj._finalize_()
  508. except Exception as e:
  509. report_error(space, e, "finalizer of ", w_obj)
  510. def report_error(space, e, where, w_obj):
  511. if isinstance(e, OperationError):
  512. e.write_unraisable(space, where, w_obj)
  513. e.clear(space) # break up reference cycles
  514. else:
  515. addrstring = w_obj.getaddrstring(space)
  516. msg = ("RPython exception %s in %s<%s at 0x%s> ignored\n" % (
  517. str(e), where, space.type(w_obj).name, addrstring))
  518. space.call_method(space.sys.get('stderr'), 'write',
  519. space.wrap(msg))
  520. def make_finalizer_queue(W_Root, space):
  521. """Make a FinalizerQueue subclass which responds to GC finalizer
  522. events by 'firing' the UserDelAction class above. It does not
  523. directly fetches the objects to finalize at all; they stay in the
  524. GC-managed queue, and will only be fetched by UserDelAction
  525. (between bytecodes)."""
  526. class WRootFinalizerQueue(rgc.FinalizerQueue):
  527. Class = W_Root
  528. def finalizer_trigger(self):
  529. space.user_del_action.fire()
  530. space.user_del_action = UserDelAction(space)
  531. space.finalizer_queue = WRootFinalizerQueue()