@@ -33,7 +33,8 @@ NOP_VALUE: int = opcode.opmap['NOP']
3333# The Op code should be 2 bytes as stated in
3434# https://docs.python.org/3/library/dis.html
3535# if sys.version_info[0:2] >= (3, 11):
36- NOP_BYTES: bytes = NOP_VALUE.to_bytes(2 , byteorder = byteorder)
36+ NOP_BYTES_LEN: int = 2
37+ NOP_BYTES: bytes = NOP_VALUE.to_bytes(NOP_BYTES_LEN, byteorder = byteorder)
3738
3839# This should be true for Python >=3.11a1
3940HAS_CO_QUALNAME: bool = hasattr (types.CodeType, ' co_qualname' )
@@ -151,6 +152,24 @@ cdef inline int64 compute_line_hash(uint64 block_hash, uint64 linenum):
151152 return block_hash ^ linenum
152153
153154
155+ cdef inline object multibyte_rstrip(bytes bytecode):
156+ """
157+ Returns:
158+ result (tuple[bytes, int])
159+ - First item is the bare unpadded bytecode
160+ - Second item is the number of :py:const:`NOP_BYTES`
161+ ``bytecode`` has been padded with
162+ """
163+ npad: int = 0
164+ nop_len: int = - NOP_BYTES_LEN
165+ nop_bytes: bytes = NOP_BYTES
166+ unpadded: bytes = bytecode
167+ while unpadded.endswith(nop_bytes):
168+ unpadded = unpadded[:nop_len]
169+ npad += 1
170+ return (unpadded, npad)
171+
172+
154173if CAN_USE_SYS_MONITORING:
155174 def _is_main_thread () -> bool:
156175 return threading.current_thread() == threading.main_thread()
@@ -378,26 +397,21 @@ cdef class LineProfiler:
378397 co_code: bytes = code.co_code
379398 code_hashes = []
380399 if any (co_code): # Normal Python functions
381- base_co_code: bytes = co_code
382400 # Figure out how much padding we need and strip the bytecode
383- # TODO: is there a way to do this faster? `.rstrip()` doesn't
384- # work (reliably) since `NOP_BYTES` should be 2-byte wide
385- npad_code: int = 0
386- nop_len: int = - len (NOP_BYTES)
387- while base_co_code.endswith(NOP_BYTES):
388- base_co_code = base_co_code[:nop_len]
389- npad_code += 1
401+ base_co_code: bytes
402+ npad_code: int
403+ base_co_code, npad_code = multibyte_rstrip(co_code)
390404 try :
391405 npad = self ._all_paddings[base_co_code]
392406 except KeyError :
393407 npad = 0
394408 self ._all_paddings[base_co_code] = max (npad, npad_code) + 1
395409 try :
396- neighbors = self ._all_instances_by_funcs[func_id]
397- neighbors .add(self )
410+ profilers_to_update = self ._all_instances_by_funcs[func_id]
411+ profilers_to_update .add(self )
398412 except KeyError :
399- neighbors = self ._all_instances_by_funcs[func_id] = WeakSet(
400- { self })
413+ profilers_to_update = WeakSet({ self })
414+ self ._all_instances_by_funcs[func_id] = profilers_to_update
401415 # Maintain `.dupes_map` (legacy)
402416 try :
403417 self .dupes_map[base_co_code].append(code)
@@ -413,8 +427,8 @@ cdef class LineProfiler:
413427 func.__code__ = code
414428 except AttributeError as e:
415429 func.__func__.__code__ = code
416- else : # No re-padding -> no need to update the neighbors
417- neighbors = {self }
430+ else : # No re-padding -> no need to update the other profs
431+ profilers_to_update = {self }
418432 # TODO: Since each line can be many bytecodes, this is kinda
419433 # inefficient
420434 # See if this can be sped up by not needing to iterate over
@@ -446,11 +460,11 @@ cdef class LineProfiler:
446460 # We can't replace the code object on Cython functions, but
447461 # we can *store* a copy with the correct metadata
448462 code = code.replace(co_filename = cython_source)
449- neighbors = {self }
463+ profilers_to_update = {self }
450464 # Update `._c_code_map` and `.code_hash_map` with the new line
451465 # hashes on `self` (and other instances profiling the same
452466 # function if we padded the bytecode)
453- for instance in neighbors :
467+ for instance in profilers_to_update :
454468 prof = < LineProfiler> instance
455469 try :
456470 line_hashes = prof.code_hash_map[code]
0 commit comments