Skip to content

FIX: always padding the bytecode of profiled functions#425

Open
TTsangSC wants to merge 4 commits intopyutils:mainfrom
TTsangSC:bytecode-padding-fix
Open

FIX: always padding the bytecode of profiled functions#425
TTsangSC wants to merge 4 commits intopyutils:mainfrom
TTsangSC:bytecode-padding-fix

Conversation

@TTsangSC
Copy link
Collaborator

@TTsangSC TTsangSC commented Mar 9, 2026

Closes #424.

Summary

  • Function objects touched by profilers are now always bytecode-padded; this is different from the previous behavior of only padding the second function with identical bytecode onwards, circumventing the bug described in BUG: line events from non-profiled functions misattributed to profiled functions sharing bytecodes #424, where the profiler fails to distinguish between the single profiled function and its non-profiled bytecode "twin".
  • The internal logic of LineProfiler.add_function() has been streamlined to cut down on the need for bookkeeping, resulting in simpler code and a very slight performance gain.

Code changes

  • line_profiler/_line_profiler.pyx::LineProfiler
    • ._all_instances_by_funcs
      Removed this private class attribute; owing to the changes to .add_function() as described below, we no longer need to keep tabs on which profiler instance is profiling which function objects
    • .add_function()
      • The .co_code of the code object of a (normal non-Cython) profiled function is now ALWAYS padded with at least one NOP; this serves to mark profiled functions apart from non-profiled ones which happen to compile down to the same bytecode.
      • If the .co_code has already been padded, it is no longer re-padded in the event that multiple profiler instances are profiling the same function.
      • No re-padding also means that the line hashes don't change, so we no longer keep track of other instances profiling the same function objects and update their .code_hash_map and ._c_code_map.

Test changes

  • tests/test_line_profiler.py
    • test_nonprofiled_clashing_bytecodes()
      New test against the edge case described in the issue, making sure that line events from the non-profiled bytecode twin aren't misattributed to the profiled function
    • test_aggregate_profiling_data_between_code_version()
      Updated to no longer check for code-object change after decoration by the second profiler, because again we stopped re-padding bytecodes (FIX: Update hash tables of affected profiler instances when rewriting code objects  #351); note however that this test is now essentially obsolete and maybe we should just remove it...

TTsangSC added 4 commits March 9, 2026 12:29
tests/test_line_profiler.py::test_nonprofiled_clashing_bytecodes()
    New test (currently failing) for the case described in issue pyutils#424:
    - One uncalled function passed to the profiler
    - Another (bytecode-wise) identical function called, but never
      passed to the profiler
    - Even profiling is active, the profiler should not have recorded
      any line event, but it nontheless did
line_profiler/_line_profiler.pyx::LineProfiler.add_function()
    - Now making sure that ALL functions passed to the method will be
      NOP-bytecode-padded, instead of from the 2nd one onwards
    - No longer re-padding already NOP-bytecode-padded code objects
      (could be by another profiler instance or even the same one);
      this probably eliminates the need for keeping track on profiler
      instances and leaves room for further optimizations

tests/test_line_profiler.py
::test_aggregate_profiling_data_between_code_versions()
    - Updated test body to no longer test for bytecode alteration
      (because there is no longer any)
    - Added note in docstring stating the original context of the test
      and how it is no longer relevant (or should we just delete this
      test?)
line_profiler/_line_profiler.pyx::LineProfiler
    add_function()
        No longer managing a set of profilers whose internal states are
        to be updated (since it is now always just the instance itself)
    _all_instances_by_funcs
        No longer keeping this class attribute -- we no longer re-pad
        bytecodes, so other instances referring to the same function
        don't need to be retrievable
@TTsangSC
Copy link
Collaborator Author

TTsangSC commented Mar 9, 2026

Huh the build jobs choked on all non-Linux platforms like the merge pipeline for #423 with HTTP 429 during the Setting up build environment... phase, where cibuildwheel attempted to pull https://github.com/pypa/get-virtualenv/blob/20.32.0/public/virtualenv.pyz?raw=true. All the version numbers look the same as in the last successful workflow though, so IDK what has changed between them...

(Curiously I tried curl-ing the URL itself by to no avail – I didn't get an error code but 0 bytes were sent.)

@Erotemic
Copy link
Member

Retrying the workflow didn't seem to do the trick. I am noticing that we are still supporting 3.8, and we might want to consider dropping that at this point. But it's very strange that the error on windows is too many requests. It also looks like the issue might be on the main branch, and that did pass before I merged, so it very likely is some issue with the url being dead or something. I wonder if we drop 3.8 or update cibildwheel if it will work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: line events from non-profiled functions misattributed to profiled functions sharing bytecodes

2 participants