Skip to content

Commit 98affef

Browse files
committed
fix(profiling): access lock acquired time only when the lock is held (#11881)
Manual backport of #11811 to 2.19
1 parent 8d79908 commit 98affef

File tree

2 files changed

+60
-60
lines changed

2 files changed

+60
-60
lines changed

ddtrace/profiling/collector/_lock.py

Lines changed: 54 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -179,69 +179,63 @@ def acquire(self, *args, **kwargs):
179179

180180
def _release(self, inner_func, *args, **kwargs):
181181
# type (typing.Any, typing.Any) -> None
182+
183+
start = None
184+
if hasattr(self, "_self_acquired_at"):
185+
# _self_acquired_at is only set when the acquire was captured
186+
# if it's not set, we're not capturing the release
187+
start = self._self_acquired_at
188+
182189
try:
183190
return inner_func(*args, **kwargs)
184191
finally:
185-
try:
186-
if hasattr(self, "_self_acquired_at"):
187-
try:
188-
end = compat.monotonic_ns()
189-
thread_id, thread_name = _current_thread()
190-
task_id, task_name, task_frame = _task.get_task(thread_id)
191-
lock_name = (
192-
"%s:%s" % (self._self_init_loc, self._self_name) if self._self_name else self._self_init_loc
193-
)
194-
195-
if task_frame is None:
196-
# See the comments in _acquire
197-
frame = sys._getframe(2)
198-
else:
199-
frame = task_frame
200-
201-
frames, nframes = _traceback.pyframe_to_frames(frame, self._self_max_nframes)
202-
203-
if self._self_export_libdd_enabled:
204-
thread_native_id = _threading.get_thread_native_id(thread_id)
205-
206-
handle = ddup.SampleHandle()
207-
handle.push_monotonic_ns(end)
208-
handle.push_lock_name(lock_name)
209-
handle.push_release(
210-
end - self._self_acquired_at, 1
211-
) # AFAICT, capture_pct does not adjust anything here
212-
handle.push_threadinfo(thread_id, thread_native_id, thread_name)
213-
handle.push_task_id(task_id)
214-
handle.push_task_name(task_name)
215-
216-
if self._self_tracer is not None:
217-
handle.push_span(self._self_tracer.current_span())
218-
for frame in frames:
219-
handle.push_frame(frame.function_name, frame.file_name, 0, frame.lineno)
220-
handle.flush_sample()
221-
else:
222-
event = self.RELEASE_EVENT_CLASS(
223-
lock_name=lock_name,
224-
frames=frames,
225-
nframes=nframes,
226-
thread_id=thread_id,
227-
thread_name=thread_name,
228-
task_id=task_id,
229-
task_name=task_name,
230-
locked_for_ns=end - self._self_acquired_at,
231-
sampling_pct=self._self_capture_sampler.capture_pct,
232-
)
233-
234-
if self._self_tracer is not None:
235-
event.set_trace_info(
236-
self._self_tracer.current_span(), self._self_endpoint_collection_enabled
237-
)
238-
239-
self._self_recorder.push_event(event)
240-
finally:
241-
del self._self_acquired_at
242-
except Exception as e:
243-
LOG.warning("Error recording lock release event: %s", e)
244-
pass # nosec
192+
if start is not None:
193+
end = time.monotonic_ns()
194+
thread_id, thread_name = _current_thread()
195+
task_id, task_name, task_frame = _task.get_task(thread_id)
196+
lock_name = "%s:%s" % (self._self_init_loc, self._self_name) if self._self_name else self._self_init_loc
197+
198+
if task_frame is None:
199+
# See the comments in _acquire
200+
frame = sys._getframe(2)
201+
else:
202+
frame = task_frame
203+
204+
frames, nframes = _traceback.pyframe_to_frames(frame, self._self_max_nframes)
205+
206+
if self._self_export_libdd_enabled:
207+
thread_native_id = _threading.get_thread_native_id(thread_id)
208+
209+
handle = ddup.SampleHandle()
210+
handle.push_monotonic_ns(end)
211+
handle.push_lock_name(lock_name)
212+
handle.push_release(end - start, 1) # AFAICT, capture_pct does not adjust anything here
213+
handle.push_threadinfo(thread_id, thread_native_id, thread_name)
214+
handle.push_task_id(task_id)
215+
handle.push_task_name(task_name)
216+
217+
if self._self_tracer is not None:
218+
handle.push_span(self._self_tracer.current_span())
219+
for frame in frames:
220+
handle.push_frame(frame.function_name, frame.file_name, 0, frame.lineno)
221+
handle.flush_sample()
222+
else:
223+
event = self.RELEASE_EVENT_CLASS(
224+
lock_name=lock_name,
225+
frames=frames,
226+
nframes=nframes,
227+
thread_id=thread_id,
228+
thread_name=thread_name,
229+
task_id=task_id,
230+
task_name=task_name,
231+
locked_for_ns=end - start,
232+
sampling_pct=self._self_capture_sampler.capture_pct,
233+
)
234+
235+
if self._self_tracer is not None:
236+
event.set_trace_info(self._self_tracer.current_span(), self._self_endpoint_collection_enabled)
237+
238+
self._self_recorder.push_event(event)
245239

246240
def release(self, *args, **kwargs):
247241
return self._release(self.__wrapped__.release, *args, **kwargs)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
fixes:
3+
- |
4+
profiling: This fix resolves a data race issue accessing lock's acquired
5+
time, leading to an ``AttributeError``: ``_Profiled_ThreadingLock`` object
6+
has no attribute ``self_acquired_at``

0 commit comments

Comments
 (0)