This issue tracker has been migrated to GitHub ,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2017年04月20日 09:36 by louielu, last changed 2022年04月11日 14:58 by admin.
| Pull Requests | |||
|---|---|---|---|
| URL | Status | Linked | Edit |
| PR 1212 | closed | louielu, 2017年04月20日 10:22 | |
| PR 1253 | closed | louielu, 2017年04月22日 09:41 | |
| Messages (4) | |||
|---|---|---|---|
| msg291957 - (view) | Author: Louie Lu (louielu) * | Date: 2017年04月20日 09:36 | |
This is a sub-problem of #9285, in #9285, we aim to provide cProfile and profile a context manager, this will need to add code like this: def __enter__(self): self.set_cmd('') sys.setprofile(self.dispatcher) return self Unfortunately, when setting up profiler via `sys.setprofile`, it will immediately work on next line `return self`, which cause the assertion inside `trace_dispatch_return` claim this is a "Bad return". Technically, `profile.Profile` can not go return upper than it's frame inside the frame stack. This behavior can be observed by this code: def fib(n): if n > 2: return fib(n - 1) + fib(n - 2) return n def foo(): pr = profile.Profile() # Profile was set in the `foo` frame, it can't get more upper than this # that means, we can't return to global frame when this profile is set sys.setprofile(pr.dispatcher) fib(5) # We didn't stop the profile here via sys.setprofile(None) # So it will return 0xDEADBEAF to global frame and cause a bad return return 0xDEADBEAF foo() Here this issue will provide the test of this behavior, then will make some modify in #9285 to prevent this situation when using profile as a context manager. |
|||
| msg292107 - (view) | Author: Alyssa Coghlan (ncoghlan) * (Python committer) | Date: 2017年04月22日 05:55 | |
Thanks Louie.
I've adjusted the issue title, as I think the core RFE is that we'd like the following helper function to work exactly the same way as calling sys.setprofile directly:
def setprofile_helper(f):
sys.setprofile(f)
The following utility function can be used to demonstrate the current discrepancy:
def profile_call(setprofile, call):
pr = profile.Profile()
setprofile(pr.dispatcher)
try:
call()
finally:
setprofile(None)
return pr
Specifically:
```
>>> profile_call(sys.setprofile, lambda:print("Profiled!"))
Profiled!
<profile.Profile object at 0x7fb2265966d8>
>>> profile_call(setprofile_helper, lambda:print("Profiled!"))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in profile_call
File "<stdin>", line 2, in setprofile_helper
File "/usr/lib64/python3.5/profile.py", line 209, in trace_dispatch_i
if self.dispatch[event](self, frame, t):
File "/usr/lib64/python3.5/profile.py", line 293, in trace_dispatch_return
assert frame is self.cur[-2].f_back, ("Bad return", self.cur[-3])
AssertionError: ('Bad return', ('profile', 0, 'profiler'))
```
If we can figure out a way to do it reliably, the most desirable approach would be to make helper functions like the above "just work", perhaps by remembering the stack frame where the profiler was *defined*, and using that as the reference point to construct the synthetic stack the first time a trace dispatch occurs.
If that transparent-to-the-user approach doesn't appear to be feasible, an alternative would be to offer a new private method on Profile objects to adjust their synthetic stack by one level, as if `sys.setprofile` had been called *before* the call into the helper function.
That is, a profiler-aware alternative to the helper function above might look like:
def profile_helper(pr):
sys.setprofile(pr.dispatcher)
pr._adjust_stack()
and that's then the approach that the `__enter__` method would use to implement context management support.
The advantage of the latter private option is that it wouldn't need to support arbitrary helper functions, just the specific implementation of `__enter__` for issue 9285.
In either case, I *don't* think changing the assertion is the right answer - instead, I think the internal profiler state needs to be adjusted so the assertion passes without modification.
|
|||
| msg292112 - (view) | Author: Louie Lu (louielu) * | Date: 2017年04月22日 09:20 | |
Thanks, Nick. Your analysis is very helpful. After some testing, I found the problem here is because when we using `sys.setprofile` in the helper function, we didn't simulate the call (from where profiler create to helper function), that cause profile's frame link breakup, so if we want to return to outside of helper function, it will report a bad return. A straightforward method is the latter method you propose, to make a profiler-aware function manually insert the frame into profiler's frame stack: def _adjust_frame(self): frame = sys._getframe(1) # Get helper function frame self.dispatch['call'](self, frame, 0) And adjust *before* install profiler: def profile_helper(pr): pr._adjust_frame() sys.setprofile(pr.dispatcher) Then we can get the correct thing we need. |
|||
| msg293593 - (view) | Author: Louie Lu (louielu) * | Date: 2017年05月13日 05:24 | |
PR-1212 Add an explicit test for dispatch_return's assertion, could @benjaminp, @tiran or @giampaolo help to review this? Thanks! |
|||
| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2022年04月11日 14:58:45 | admin | set | github: 74299 |
| 2017年05月13日 05:24:44 | louielu | set | nosy:
+ giampaolo.rodola, christian.heimes, benjamin.peterson messages: + msg293593 |
| 2017年04月22日 09:41:37 | louielu | set | pull_requests: + pull_request1368 |
| 2017年04月22日 09:20:28 | louielu | set | messages: + msg292112 |
| 2017年04月22日 05:55:20 | ncoghlan | set | messages:
+ msg292107 title: Add profile test case for trace_dispatch_return assertion -> Allow helper functions to wrap sys.setprofile |
| 2017年04月20日 10:22:07 | louielu | set | pull_requests: + pull_request1334 |
| 2017年04月20日 09:36:32 | louielu | create | |