We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
TypeError: '<' not supported between instances of 'NoneType' and 'str'
### Infos
We're using DD to monitor our systems. From time to time we see the following errors popping up in our Sentry environment.

Maybe related to #3019?

### Which version of dd-trace-py are you using?
0.56.0

### Which version of pip are you using?
latest

### What is the result that you get?
``` 
TypeError: '<' not supported between instances of 'NoneType' and 'str'
  File "ddtrace/profiling/scheduler.py", line 53, in flush
    exp.export(events, start, self._last_export)
  File "ddtrace/profiling/exporter/http.py", line 150, in export
    profile = super(PprofHTTPExporter, self).export(events, start_time_ns, end_time_ns)
  File "ddtrace/profiling/exporter/pprof.pyx", line 630, in ddtrace.profiling.exporter.pprof.PprofExporter.export
  File "ddtrace/profiling/exporter/pprof.pyx", line 314, in ddtrace.profiling.exporter.pprof._PprofConverter._build_profile
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ddtrace/profiling/event.py`
Content:
```
1 import typing
2 
3 import attr
4 
5 from ddtrace import span as ddspan
6 from ddtrace.internal import compat
7 
8 
9 _T = typing.TypeVar("_T")
10 
11 # (filename, line number, function name)
12 FrameType = typing.Tuple[str, int, typing.Optional[str]]
13 StackTraceType = typing.List[FrameType]
14 
15 
16 def event_class(
17     klass,  # type: typing.Type[_T]
18 ):
19     # type: (...) -> typing.Type[_T]
20     return attr.s(slots=True)(klass)
21 
22 
23 @event_class
24 class Event(object):
25     """An event happening at a point in time."""
26 
27     timestamp = attr.ib(factory=compat.time_ns)
28 
29     @property
30     def name(self):
31         # type: (...) -> str
32         """Name of the event."""
33         return self.__class__.__name__
34 
35 
36 @event_class
37 class TimedEvent(Event):
38     """An event that has a duration."""
39 
40     duration = attr.ib(default=None)
41 
42 
43 @event_class
44 class SampleEvent(Event):
45     """An event representing a sample gathered from the system."""
46 
47     sampling_period = attr.ib(default=None)
48 
49 
50 @event_class
51 class StackBasedEvent(SampleEvent):
52     thread_id = attr.ib(default=None, type=typing.Optional[int])
53     thread_name = attr.ib(default=None, type=typing.Optional[str])
54     thread_native_id = attr.ib(default=None, type=typing.Optional[int])
55     task_id = attr.ib(default=None, type=typing.Optional[int])
56     task_name = attr.ib(default=None, type=typing.Optional[str])
57     frames = attr.ib(default=None, type=StackTraceType)
58     nframes = attr.ib(default=0, type=int)
59     local_root_span_id = attr.ib(default=None, type=typing.Optional[int])
60     span_id = attr.ib(default=None, type=typing.Optional[int])
61     trace_type = attr.ib(default=None, type=typing.Optional[str])
62     trace_resource_container = attr.ib(default=None, type=typing.List[str])
63 
64     def set_trace_info(
65         self,
66         span,  # type: typing.Optional[ddspan.Span]
67         endpoint_collection_enabled,  # type: bool
68     ):
69         # type: (...) -> None
70         if span:
71             self.span_id = span.span_id
72             if span._local_root is not None:
73                 self.local_root_span_id = span._local_root.span_id
74                 self.trace_type = span._local_root._span_type
75                 if endpoint_collection_enabled:
76                     self.trace_resource_container = span._local_root._resource
77
```

Path: `ddtrace/profiling/collector/threading.py`
Content:
```
1 from __future__ import absolute_import
2 
3 import os.path
4 import sys
5 import threading
6 import typing
7 
8 import attr
9 
10 from ddtrace.internal import compat
11 from ddtrace.internal import nogevent
12 from ddtrace.internal.utils import attr as attr_utils
13 from ddtrace.internal.utils import formats
14 from ddtrace.profiling import collector
15 from ddtrace.profiling import event
16 from ddtrace.profiling.collector import _task
17 from ddtrace.profiling.collector import _threading
18 from ddtrace.profiling.collector import _traceback
19 from ddtrace.vendor import wrapt
20 
21 
22 @event.event_class
23 class LockEventBase(event.StackBasedEvent):
24     """Base Lock event."""
25 
26     lock_name = attr.ib(default="<unknown lock name>", type=str)
27     sampling_pct = attr.ib(default=0, type=int)
28 
29 
30 @event.event_class
31 class LockAcquireEvent(LockEventBase):
32     """A lock has been acquired."""
33 
34     wait_time_ns = attr.ib(default=0, type=int)
35 
36 
37 @event.event_class
38 class LockReleaseEvent(LockEventBase):
39     """A lock has been released."""
40 
41     locked_for_ns = attr.ib(default=0, type=int)
42 
43 
44 def _current_thread():
45     # type: (...) -> typing.Tuple[int, str]
46     thread_id = nogevent.thread_get_ident()
47     return thread_id, _threading.get_thread_name(thread_id)
48 
49 
50 # We need to know if wrapt is compiled in C or not. If it's not using the C module, then the wrappers function will
51 # appear in the stack trace and we need to hide it.
52 if os.environ.get("WRAPT_DISABLE_EXTENSIONS"):
53     WRAPT_C_EXT = False
54 else:
55     try:
56         import ddtrace.vendor.wrapt._wrappers as _w  # noqa: F401
57     except ImportError:
58         WRAPT_C_EXT = False
59     else:
60         WRAPT_C_EXT = True
61         del _w
62 
63 
64 class _ProfiledLock(wrapt.ObjectProxy):
65     def __init__(self, wrapped, recorder, tracer, max_nframes, capture_sampler, endpoint_collection_enabled):
66         wrapt.ObjectProxy.__init__(self, wrapped)
67         self._self_recorder = recorder
68         self._self_tracer = tracer
69         self._self_max_nframes = max_nframes
70         self._self_capture_sampler = capture_sampler
71         self._self_endpoint_collection_enabled = endpoint_collection_enabled
72         frame = sys._getframe(2 if WRAPT_C_EXT else 3)
73         code = frame.f_code
74         self._self_name = "%s:%d" % (os.path.basename(code.co_filename), frame.f_lineno)
75 
76     def acquire(self, *args, **kwargs):
77         if not self._self_capture_sampler.capture():
78             return self.__wrapped__.acquire(*args, **kwargs)
79 
80         start = compat.monotonic_ns()
81         try:
82             return self.__wrapped__.acquire(*args, **kwargs)
83         finally:
84             try:
85                 end = self._self_acquired_at = compat.monotonic_ns()
86                 thread_id, thread_name = _current_thread()
87                 task_id, task_name, task_frame = _task.get_task(thread_id)
88 
89                 if task_frame is None:
90                     frame = sys._getframe(1)
91                 else:
92                     frame = task_frame
93 
94                 frames, nframes = _traceback.pyframe_to_frames(frame, self._self_max_nframes)
95 
96                 event = LockAcquireEvent(
97                     lock_name=self._self_name,
98                     frames=frames,
99                     nframes=nframes,
100                     thread_id=thread_id,
101                     thread_name=thread_name,
102                     task_id=task_id,
103                     task_name=task_name,
104                     wait_time_ns=end - start,
105                     sampling_pct=self._self_capture_sampler.capture_pct,
106                 )
107 
108                 if self._self_tracer is not None:
109                     event.set_trace_info(self._self_tracer.current_span(), self._self_endpoint_collection_enabled)
110 
111                 self._self_recorder.push_event(event)
112             except Exception:
113                 pass
114 
115     def release(
116         self,
117         *args,  # type: typing.Any
118         **kwargs  # type: typing.Any
119     ):
120         # type: (...) -> None
121         try:
122             return self.__wrapped__.release(*args, **kwargs)
123         finally:
124             try:
125                 if hasattr(self, "_self_acquired_at"):
126                     try:
127                         end = compat.monotonic_ns()
128                         thread_id, thread_name = _current_thread()
129                         task_id, task_name, task_frame = _task.get_task(thread_id)
130 
131                         if task_frame is None:
132                             frame = sys._getframe(1)
133                         else:
134                             frame = task_frame
135 
136                         frames, nframes = _traceback.pyframe_to_frames(frame, self._self_max_nframes)
137 
138                         event = LockReleaseEvent(  # type: ignore[call-arg]
139                             lock_name=self._self_name,
140                             frames=frames,
141                             nframes=nframes,
142                             thread_id=thread_id,
143                             thread_name=thread_name,
144                             task_id=task_id,
145                             task_name=task_name,
146                             locked_for_ns=end - self._self_acquired_at,
147                             sampling_pct=self._self_capture_sampler.capture_pct,
148                         )
149 
150                         if self._self_tracer is not None:
151                             event.set_trace_info(
152                                 self._self_tracer.current_span(), self._self_endpoint_collection_enabled
153                             )
154 
155                         self._self_recorder.push_event(event)
156                     finally:
157                         del self._self_acquired_at
158             except Exception:
159                 pass
160 
161     acquire_lock = acquire
162 
163 
164 class FunctionWrapper(wrapt.FunctionWrapper):
165     # Override the __get__ method: whatever happens, _allocate_lock is always considered by Python like a "static"
166     # method, even when used as a class attribute. Python never tried to "bind" it to a method, because it sees it is a
167     # builtin function. Override default wrapt behavior here that tries to detect bound method.
168     def __get__(self, instance, owner=None):
169         return self
170 
171 
172 @attr.s
173 class LockCollector(collector.CaptureSamplerCollector):
174     """Record lock usage."""
175 
176     nframes = attr.ib(factory=attr_utils.from_env("DD_PROFILING_MAX_FRAMES", 64, int))
177     endpoint_collection_enabled = attr.ib(
178         factory=attr_utils.from_env("DD_PROFILING_ENDPOINT_COLLECTION_ENABLED", True, formats.asbool)
179     )
180 
181     tracer = attr.ib(default=None)
182 
183     def _start_service(self):  # type: ignore[override]
184         # type: (...) -> None
185         """Start collecting `threading.Lock` usage."""
186         self.patch()
187         super(LockCollector, self)._start_service()
188 
189     def _stop_service(self):  # type: ignore[override]
190         # type: (...) -> None
191         """Stop collecting `threading.Lock` usage."""
192         super(LockCollector, self)._stop_service()
193         self.unpatch()
194 
195     def patch(self):
196         # type: (...) -> None
197         """Patch the threading module for tracking lock allocation."""
198         # We only patch the lock from the `threading` module.
199         # Nobody should use locks from `_thread`; if they do so, then it's deliberate and we don't profile.
200         self.original = threading.Lock
201 
202         def _allocate_lock(wrapped, instance, args, kwargs):
203             lock = wrapped(*args, **kwargs)
204             return _ProfiledLock(
205                 lock, self.recorder, self.tracer, self.nframes, self._capture_sampler, self.endpoint_collection_enabled
206             )
207 
208         threading.Lock = FunctionWrapper(self.original, _allocate_lock)  # type: ignore[misc]
209 
210     def unpatch(self):
211         # type: (...) -> None
212         """Unpatch the threading module for tracking lock allocation."""
213         threading.Lock = self.original  # type: ignore[misc]
214
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/ddtrace/profiling/collector/threading.py b/ddtrace/profiling/collector/threading.py
--- a/ddtrace/profiling/collector/threading.py
+++ b/ddtrace/profiling/collector/threading.py
@@ -7,6 +7,7 @@
 
 import attr
 
+import ddtrace
 from ddtrace.internal import compat
 from ddtrace.internal import nogevent
 from ddtrace.internal.utils import attr as attr_utils
@@ -19,6 +20,10 @@
 from ddtrace.vendor import wrapt
 
 
+if typing.TYPE_CHECKING:
+    from ddtrace.profiling import recorder as ddrecorder
+
+
 @event.event_class
 class LockEventBase(event.StackBasedEvent):
     """Base Lock event."""
@@ -62,7 +67,16 @@
 
 
 class _ProfiledLock(wrapt.ObjectProxy):
-    def __init__(self, wrapped, recorder, tracer, max_nframes, capture_sampler, endpoint_collection_enabled):
+    def __init__(
+        self,
+        wrapped,  # type: threading.Lock
+        recorder,  # type: ddrecorder.Recorder
+        tracer,  # type: ddtrace.Tracer
+        max_nframes,  # type: int
+        capture_sampler,  # type: collector.CaptureSampler
+        endpoint_collection_enabled,  # type: bool
+    ):
+        # type: (...) -> None
         wrapt.ObjectProxy.__init__(self, wrapped)
         self._self_recorder = recorder
         self._self_tracer = tracer
@@ -71,7 +85,10 @@
         self._self_endpoint_collection_enabled = endpoint_collection_enabled
         frame = sys._getframe(2 if WRAPT_C_EXT else 3)
         code = frame.f_code
-        self._self_name = "%s:%d" % (os.path.basename(code.co_filename), frame.f_lineno)
+        self._self_name = "%s:%d" % (
+            os.path.basename(code.co_filename),
+            0 if frame.f_lineno is None else frame.f_lineno,
+        )
 
     def acquire(self, *args, **kwargs):
         if not self._self_capture_sampler.capture():
diff --git a/ddtrace/profiling/event.py b/ddtrace/profiling/event.py
--- a/ddtrace/profiling/event.py
+++ b/ddtrace/profiling/event.py
@@ -9,7 +9,7 @@
 _T = typing.TypeVar("_T")
 
 # (filename, line number, function name)
-FrameType = typing.Tuple[str, int, typing.Optional[str]]
+FrameType = typing.Tuple[str, int, str]
 StackTraceType = typing.List[FrameType]

{"golden_diff": "diff --git a/ddtrace/profiling/collector/threading.py b/ddtrace/profiling/collector/threading.py\n--- a/ddtrace/profiling/collector/threading.py\n+++ b/ddtrace/profiling/collector/threading.py\n@@ -7,6 +7,7 @@\n \n import attr\n \n+import ddtrace\n from ddtrace.internal import compat\n from ddtrace.internal import nogevent\n from ddtrace.internal.utils import attr as attr_utils\n@@ -19,6 +20,10 @@\n from ddtrace.vendor import wrapt\n \n \n+if typing.TYPE_CHECKING:\n+    from ddtrace.profiling import recorder as ddrecorder\n+\n+\n @event.event_class\n class LockEventBase(event.StackBasedEvent):\n     \"\"\"Base Lock event.\"\"\"\n@@ -62,7 +67,16 @@\n \n \n class _ProfiledLock(wrapt.ObjectProxy):\n-    def __init__(self, wrapped, recorder, tracer, max_nframes, capture_sampler, endpoint_collection_enabled):\n+    def __init__(\n+        self,\n+        wrapped,  # type: threading.Lock\n+        recorder,  # type: ddrecorder.Recorder\n+        tracer,  # type: ddtrace.Tracer\n+        max_nframes,  # type: int\n+        capture_sampler,  # type: collector.CaptureSampler\n+        endpoint_collection_enabled,  # type: bool\n+    ):\n+        # type: (...) -> None\n         wrapt.ObjectProxy.__init__(self, wrapped)\n         self._self_recorder = recorder\n         self._self_tracer = tracer\n@@ -71,7 +85,10 @@\n         self._self_endpoint_collection_enabled = endpoint_collection_enabled\n         frame = sys._getframe(2 if WRAPT_C_EXT else 3)\n         code = frame.f_code\n-        self._self_name = \"%s:%d\" % (os.path.basename(code.co_filename), frame.f_lineno)\n+        self._self_name = \"%s:%d\" % (\n+            os.path.basename(code.co_filename),\n+            0 if frame.f_lineno is None else frame.f_lineno,\n+        )\n \n     def acquire(self, *args, **kwargs):\n         if not self._self_capture_sampler.capture():\ndiff --git a/ddtrace/profiling/event.py b/ddtrace/profiling/event.py\n--- a/ddtrace/profiling/event.py\n+++ b/ddtrace/profiling/event.py\n@@ -9,7 +9,7 @@\n _T = typing.TypeVar(\"_T\")\n \n # (filename, line number, function name)\n-FrameType = typing.Tuple[str, int, typing.Optional[str]]\n+FrameType = typing.Tuple[str, int, str]\n StackTraceType = typing.List[FrameType]\n", "issue": "TypeError: '<' not supported between instances of 'NoneType' and 'str'\n### Infos\r\nWe're using DD to monitor our systems. From time to time we see the following errors popping up in our Sentry environment.\r\n\r\nMaybe related to #3019?\r\n\r\n### Which version of dd-trace-py are you using?\r\n0.56.0\r\n\r\n### Which version of pip are you using?\r\nlatest\r\n\r\n### What is the result that you get?\r\n``` \r\nTypeError: '<' not supported between instances of 'NoneType' and 'str'\r\n  File \"ddtrace/profiling/scheduler.py\", line 53, in flush\r\n    exp.export(events, start, self._last_export)\r\n  File \"ddtrace/profiling/exporter/http.py\", line 150, in export\r\n    profile = super(PprofHTTPExporter, self).export(events, start_time_ns, end_time_ns)\r\n  File \"ddtrace/profiling/exporter/pprof.pyx\", line 630, in ddtrace.profiling.exporter.pprof.PprofExporter.export\r\n  File \"ddtrace/profiling/exporter/pprof.pyx\", line 314, in ddtrace.profiling.exporter.pprof._PprofConverter._build_profile\r\n```\n", "before_files": [{"content": "import typing\n\nimport attr\n\nfrom ddtrace import span as ddspan\nfrom ddtrace.internal import compat\n\n\n_T = typing.TypeVar(\"_T\")\n\n# (filename, line number, function name)\nFrameType = typing.Tuple[str, int, typing.Optional[str]]\nStackTraceType = typing.List[FrameType]\n\n\ndef event_class(\n    klass,  # type: typing.Type[_T]\n):\n    # type: (...) -> typing.Type[_T]\n    return attr.s(slots=True)(klass)\n\n\n@event_class\nclass Event(object):\n    \"\"\"An event happening at a point in time.\"\"\"\n\n    timestamp = attr.ib(factory=compat.time_ns)\n\n    @property\n    def name(self):\n        # type: (...) -> str\n        \"\"\"Name of the event.\"\"\"\n        return self.__class__.__name__\n\n\n@event_class\nclass TimedEvent(Event):\n    \"\"\"An event that has a duration.\"\"\"\n\n    duration = attr.ib(default=None)\n\n\n@event_class\nclass SampleEvent(Event):\n    \"\"\"An event representing a sample gathered from the system.\"\"\"\n\n    sampling_period = attr.ib(default=None)\n\n\n@event_class\nclass StackBasedEvent(SampleEvent):\n    thread_id = attr.ib(default=None, type=typing.Optional[int])\n    thread_name = attr.ib(default=None, type=typing.Optional[str])\n    thread_native_id = attr.ib(default=None, type=typing.Optional[int])\n    task_id = attr.ib(default=None, type=typing.Optional[int])\n    task_name = attr.ib(default=None, type=typing.Optional[str])\n    frames = attr.ib(default=None, type=StackTraceType)\n    nframes = attr.ib(default=0, type=int)\n    local_root_span_id = attr.ib(default=None, type=typing.Optional[int])\n    span_id = attr.ib(default=None, type=typing.Optional[int])\n    trace_type = attr.ib(default=None, type=typing.Optional[str])\n    trace_resource_container = attr.ib(default=None, type=typing.List[str])\n\n    def set_trace_info(\n        self,\n        span,  # type: typing.Optional[ddspan.Span]\n        endpoint_collection_enabled,  # type: bool\n    ):\n        # type: (...) -> None\n        if span:\n            self.span_id = span.span_id\n            if span._local_root is not None:\n                self.local_root_span_id = span._local_root.span_id\n                self.trace_type = span._local_root._span_type\n                if endpoint_collection_enabled:\n                    self.trace_resource_container = span._local_root._resource\n", "path": "ddtrace/profiling/event.py"}, {"content": "from __future__ import absolute_import\n\nimport os.path\nimport sys\nimport threading\nimport typing\n\nimport attr\n\nfrom ddtrace.internal import compat\nfrom ddtrace.internal import nogevent\nfrom ddtrace.internal.utils import attr as attr_utils\nfrom ddtrace.internal.utils import formats\nfrom ddtrace.profiling import collector\nfrom ddtrace.profiling import event\nfrom ddtrace.profiling.collector import _task\nfrom ddtrace.profiling.collector import _threading\nfrom ddtrace.profiling.collector import _traceback\nfrom ddtrace.vendor import wrapt\n\n\n@event.event_class\nclass LockEventBase(event.StackBasedEvent):\n    \"\"\"Base Lock event.\"\"\"\n\n    lock_name = attr.ib(default=\"<unknown lock name>\", type=str)\n    sampling_pct = attr.ib(default=0, type=int)\n\n\n@event.event_class\nclass LockAcquireEvent(LockEventBase):\n    \"\"\"A lock has been acquired.\"\"\"\n\n    wait_time_ns = attr.ib(default=0, type=int)\n\n\n@event.event_class\nclass LockReleaseEvent(LockEventBase):\n    \"\"\"A lock has been released.\"\"\"\n\n    locked_for_ns = attr.ib(default=0, type=int)\n\n\ndef _current_thread():\n    # type: (...) -> typing.Tuple[int, str]\n    thread_id = nogevent.thread_get_ident()\n    return thread_id, _threading.get_thread_name(thread_id)\n\n\n# We need to know if wrapt is compiled in C or not. If it's not using the C module, then the wrappers function will\n# appear in the stack trace and we need to hide it.\nif os.environ.get(\"WRAPT_DISABLE_EXTENSIONS\"):\n    WRAPT_C_EXT = False\nelse:\n    try:\n        import ddtrace.vendor.wrapt._wrappers as _w  # noqa: F401\n    except ImportError:\n        WRAPT_C_EXT = False\n    else:\n        WRAPT_C_EXT = True\n        del _w\n\n\nclass _ProfiledLock(wrapt.ObjectProxy):\n    def __init__(self, wrapped, recorder, tracer, max_nframes, capture_sampler, endpoint_collection_enabled):\n        wrapt.ObjectProxy.__init__(self, wrapped)\n        self._self_recorder = recorder\n        self._self_tracer = tracer\n        self._self_max_nframes = max_nframes\n        self._self_capture_sampler = capture_sampler\n        self._self_endpoint_collection_enabled = endpoint_collection_enabled\n        frame = sys._getframe(2 if WRAPT_C_EXT else 3)\n        code = frame.f_code\n        self._self_name = \"%s:%d\" % (os.path.basename(code.co_filename), frame.f_lineno)\n\n    def acquire(self, *args, **kwargs):\n        if not self._self_capture_sampler.capture():\n            return self.__wrapped__.acquire(*args, **kwargs)\n\n        start = compat.monotonic_ns()\n        try:\n            return self.__wrapped__.acquire(*args, **kwargs)\n        finally:\n            try:\n                end = self._self_acquired_at = compat.monotonic_ns()\n                thread_id, thread_name = _current_thread()\n                task_id, task_name, task_frame = _task.get_task(thread_id)\n\n                if task_frame is None:\n                    frame = sys._getframe(1)\n                else:\n                    frame = task_frame\n\n                frames, nframes = _traceback.pyframe_to_frames(frame, self._self_max_nframes)\n\n                event = LockAcquireEvent(\n                    lock_name=self._self_name,\n                    frames=frames,\n                    nframes=nframes,\n                    thread_id=thread_id,\n                    thread_name=thread_name,\n                    task_id=task_id,\n                    task_name=task_name,\n                    wait_time_ns=end - start,\n                    sampling_pct=self._self_capture_sampler.capture_pct,\n                )\n\n                if self._self_tracer is not None:\n                    event.set_trace_info(self._self_tracer.current_span(), self._self_endpoint_collection_enabled)\n\n                self._self_recorder.push_event(event)\n            except Exception:\n                pass\n\n    def release(\n        self,\n        *args,  # type: typing.Any\n        **kwargs  # type: typing.Any\n    ):\n        # type: (...) -> None\n        try:\n            return self.__wrapped__.release(*args, **kwargs)\n        finally:\n            try:\n                if hasattr(self, \"_self_acquired_at\"):\n                    try:\n                        end = compat.monotonic_ns()\n                        thread_id, thread_name = _current_thread()\n                        task_id, task_name, task_frame = _task.get_task(thread_id)\n\n                        if task_frame is None:\n                            frame = sys._getframe(1)\n                        else:\n                            frame = task_frame\n\n                        frames, nframes = _traceback.pyframe_to_frames(frame, self._self_max_nframes)\n\n                        event = LockReleaseEvent(  # type: ignore[call-arg]\n                            lock_name=self._self_name,\n                            frames=frames,\n                            nframes=nframes,\n                            thread_id=thread_id,\n                            thread_name=thread_name,\n                            task_id=task_id,\n                            task_name=task_name,\n                            locked_for_ns=end - self._self_acquired_at,\n                            sampling_pct=self._self_capture_sampler.capture_pct,\n                        )\n\n                        if self._self_tracer is not None:\n                            event.set_trace_info(\n                                self._self_tracer.current_span(), self._self_endpoint_collection_enabled\n                            )\n\n                        self._self_recorder.push_event(event)\n                    finally:\n                        del self._self_acquired_at\n            except Exception:\n                pass\n\n    acquire_lock = acquire\n\n\nclass FunctionWrapper(wrapt.FunctionWrapper):\n    # Override the __get__ method: whatever happens, _allocate_lock is always considered by Python like a \"static\"\n    # method, even when used as a class attribute. Python never tried to \"bind\" it to a method, because it sees it is a\n    # builtin function. Override default wrapt behavior here that tries to detect bound method.\n    def __get__(self, instance, owner=None):\n        return self\n\n\n@attr.s\nclass LockCollector(collector.CaptureSamplerCollector):\n    \"\"\"Record lock usage.\"\"\"\n\n    nframes = attr.ib(factory=attr_utils.from_env(\"DD_PROFILING_MAX_FRAMES\", 64, int))\n    endpoint_collection_enabled = attr.ib(\n        factory=attr_utils.from_env(\"DD_PROFILING_ENDPOINT_COLLECTION_ENABLED\", True, formats.asbool)\n    )\n\n    tracer = attr.ib(default=None)\n\n    def _start_service(self):  # type: ignore[override]\n        # type: (...) -> None\n        \"\"\"Start collecting `threading.Lock` usage.\"\"\"\n        self.patch()\n        super(LockCollector, self)._start_service()\n\n    def _stop_service(self):  # type: ignore[override]\n        # type: (...) -> None\n        \"\"\"Stop collecting `threading.Lock` usage.\"\"\"\n        super(LockCollector, self)._stop_service()\n        self.unpatch()\n\n    def patch(self):\n        # type: (...) -> None\n        \"\"\"Patch the threading module for tracking lock allocation.\"\"\"\n        # We only patch the lock from the `threading` module.\n        # Nobody should use locks from `_thread`; if they do so, then it's deliberate and we don't profile.\n        self.original = threading.Lock\n\n        def _allocate_lock(wrapped, instance, args, kwargs):\n            lock = wrapped(*args, **kwargs)\n            return _ProfiledLock(\n                lock, self.recorder, self.tracer, self.nframes, self._capture_sampler, self.endpoint_collection_enabled\n            )\n\n        threading.Lock = FunctionWrapper(self.original, _allocate_lock)  # type: ignore[misc]\n\n    def unpatch(self):\n        # type: (...) -> None\n        \"\"\"Unpatch the threading module for tracking lock allocation.\"\"\"\n        threading.Lock = self.original  # type: ignore[misc]\n", "path": "ddtrace/profiling/collector/threading.py"}], "after_files": [{"content": "import typing\n\nimport attr\n\nfrom ddtrace import span as ddspan\nfrom ddtrace.internal import compat\n\n\n_T = typing.TypeVar(\"_T\")\n\n# (filename, line number, function name)\nFrameType = typing.Tuple[str, int, str]\nStackTraceType = typing.List[FrameType]\n\n\ndef event_class(\n    klass,  # type: typing.Type[_T]\n):\n    # type: (...) -> typing.Type[_T]\n    return attr.s(slots=True)(klass)\n\n\n@event_class\nclass Event(object):\n    \"\"\"An event happening at a point in time.\"\"\"\n\n    timestamp = attr.ib(factory=compat.time_ns)\n\n    @property\n    def name(self):\n        # type: (...) -> str\n        \"\"\"Name of the event.\"\"\"\n        return self.__class__.__name__\n\n\n@event_class\nclass TimedEvent(Event):\n    \"\"\"An event that has a duration.\"\"\"\n\n    duration = attr.ib(default=None)\n\n\n@event_class\nclass SampleEvent(Event):\n    \"\"\"An event representing a sample gathered from the system.\"\"\"\n\n    sampling_period = attr.ib(default=None)\n\n\n@event_class\nclass StackBasedEvent(SampleEvent):\n    thread_id = attr.ib(default=None, type=typing.Optional[int])\n    thread_name = attr.ib(default=None, type=typing.Optional[str])\n    thread_native_id = attr.ib(default=None, type=typing.Optional[int])\n    task_id = attr.ib(default=None, type=typing.Optional[int])\n    task_name = attr.ib(default=None, type=typing.Optional[str])\n    frames = attr.ib(default=None, type=StackTraceType)\n    nframes = attr.ib(default=0, type=int)\n    local_root_span_id = attr.ib(default=None, type=typing.Optional[int])\n    span_id = attr.ib(default=None, type=typing.Optional[int])\n    trace_type = attr.ib(default=None, type=typing.Optional[str])\n    trace_resource_container = attr.ib(default=None, type=typing.List[str])\n\n    def set_trace_info(\n        self,\n        span,  # type: typing.Optional[ddspan.Span]\n        endpoint_collection_enabled,  # type: bool\n    ):\n        # type: (...) -> None\n        if span:\n            self.span_id = span.span_id\n            if span._local_root is not None:\n                self.local_root_span_id = span._local_root.span_id\n                self.trace_type = span._local_root._span_type\n                if endpoint_collection_enabled:\n                    self.trace_resource_container = span._local_root._resource\n", "path": "ddtrace/profiling/event.py"}, {"content": "from __future__ import absolute_import\n\nimport os.path\nimport sys\nimport threading\nimport typing\n\nimport attr\n\nimport ddtrace\nfrom ddtrace.internal import compat\nfrom ddtrace.internal import nogevent\nfrom ddtrace.internal.utils import attr as attr_utils\nfrom ddtrace.internal.utils import formats\nfrom ddtrace.profiling import collector\nfrom ddtrace.profiling import event\nfrom ddtrace.profiling.collector import _task\nfrom ddtrace.profiling.collector import _threading\nfrom ddtrace.profiling.collector import _traceback\nfrom ddtrace.vendor import wrapt\n\n\nif typing.TYPE_CHECKING:\n    from ddtrace.profiling import recorder as ddrecorder\n\n\n@event.event_class\nclass LockEventBase(event.StackBasedEvent):\n    \"\"\"Base Lock event.\"\"\"\n\n    lock_name = attr.ib(default=\"<unknown lock name>\", type=str)\n    sampling_pct = attr.ib(default=0, type=int)\n\n\n@event.event_class\nclass LockAcquireEvent(LockEventBase):\n    \"\"\"A lock has been acquired.\"\"\"\n\n    wait_time_ns = attr.ib(default=0, type=int)\n\n\n@event.event_class\nclass LockReleaseEvent(LockEventBase):\n    \"\"\"A lock has been released.\"\"\"\n\n    locked_for_ns = attr.ib(default=0, type=int)\n\n\ndef _current_thread():\n    # type: (...) -> typing.Tuple[int, str]\n    thread_id = nogevent.thread_get_ident()\n    return thread_id, _threading.get_thread_name(thread_id)\n\n\n# We need to know if wrapt is compiled in C or not. If it's not using the C module, then the wrappers function will\n# appear in the stack trace and we need to hide it.\nif os.environ.get(\"WRAPT_DISABLE_EXTENSIONS\"):\n    WRAPT_C_EXT = False\nelse:\n    try:\n        import ddtrace.vendor.wrapt._wrappers as _w  # noqa: F401\n    except ImportError:\n        WRAPT_C_EXT = False\n    else:\n        WRAPT_C_EXT = True\n        del _w\n\n\nclass _ProfiledLock(wrapt.ObjectProxy):\n    def __init__(\n        self,\n        wrapped,  # type: threading.Lock\n        recorder,  # type: ddrecorder.Recorder\n        tracer,  # type: ddtrace.Tracer\n        max_nframes,  # type: int\n        capture_sampler,  # type: collector.CaptureSampler\n        endpoint_collection_enabled,  # type: bool\n    ):\n        # type: (...) -> None\n        wrapt.ObjectProxy.__init__(self, wrapped)\n        self._self_recorder = recorder\n        self._self_tracer = tracer\n        self._self_max_nframes = max_nframes\n        self._self_capture_sampler = capture_sampler\n        self._self_endpoint_collection_enabled = endpoint_collection_enabled\n        frame = sys._getframe(2 if WRAPT_C_EXT else 3)\n        code = frame.f_code\n        self._self_name = \"%s:%d\" % (\n            os.path.basename(code.co_filename),\n            0 if frame.f_lineno is None else frame.f_lineno,\n        )\n\n    def acquire(self, *args, **kwargs):\n        if not self._self_capture_sampler.capture():\n            return self.__wrapped__.acquire(*args, **kwargs)\n\n        start = compat.monotonic_ns()\n        try:\n            return self.__wrapped__.acquire(*args, **kwargs)\n        finally:\n            try:\n                end = self._self_acquired_at = compat.monotonic_ns()\n                thread_id, thread_name = _current_thread()\n                task_id, task_name, task_frame = _task.get_task(thread_id)\n\n                if task_frame is None:\n                    frame = sys._getframe(1)\n                else:\n                    frame = task_frame\n\n                frames, nframes = _traceback.pyframe_to_frames(frame, self._self_max_nframes)\n\n                event = LockAcquireEvent(\n                    lock_name=self._self_name,\n                    frames=frames,\n                    nframes=nframes,\n                    thread_id=thread_id,\n                    thread_name=thread_name,\n                    task_id=task_id,\n                    task_name=task_name,\n                    wait_time_ns=end - start,\n                    sampling_pct=self._self_capture_sampler.capture_pct,\n                )\n\n                if self._self_tracer is not None:\n                    event.set_trace_info(self._self_tracer.current_span(), self._self_endpoint_collection_enabled)\n\n                self._self_recorder.push_event(event)\n            except Exception:\n                pass\n\n    def release(\n        self,\n        *args,  # type: typing.Any\n        **kwargs  # type: typing.Any\n    ):\n        # type: (...) -> None\n        try:\n            return self.__wrapped__.release(*args, **kwargs)\n        finally:\n            try:\n                if hasattr(self, \"_self_acquired_at\"):\n                    try:\n                        end = compat.monotonic_ns()\n                        thread_id, thread_name = _current_thread()\n                        task_id, task_name, task_frame = _task.get_task(thread_id)\n\n                        if task_frame is None:\n                            frame = sys._getframe(1)\n                        else:\n                            frame = task_frame\n\n                        frames, nframes = _traceback.pyframe_to_frames(frame, self._self_max_nframes)\n\n                        event = LockReleaseEvent(  # type: ignore[call-arg]\n                            lock_name=self._self_name,\n                            frames=frames,\n                            nframes=nframes,\n                            thread_id=thread_id,\n                            thread_name=thread_name,\n                            task_id=task_id,\n                            task_name=task_name,\n                            locked_for_ns=end - self._self_acquired_at,\n                            sampling_pct=self._self_capture_sampler.capture_pct,\n                        )\n\n                        if self._self_tracer is not None:\n                            event.set_trace_info(\n                                self._self_tracer.current_span(), self._self_endpoint_collection_enabled\n                            )\n\n                        self._self_recorder.push_event(event)\n                    finally:\n                        del self._self_acquired_at\n            except Exception:\n                pass\n\n    acquire_lock = acquire\n\n\nclass FunctionWrapper(wrapt.FunctionWrapper):\n    # Override the __get__ method: whatever happens, _allocate_lock is always considered by Python like a \"static\"\n    # method, even when used as a class attribute. Python never tried to \"bind\" it to a method, because it sees it is a\n    # builtin function. Override default wrapt behavior here that tries to detect bound method.\n    def __get__(self, instance, owner=None):\n        return self\n\n\n@attr.s\nclass LockCollector(collector.CaptureSamplerCollector):\n    \"\"\"Record lock usage.\"\"\"\n\n    nframes = attr.ib(factory=attr_utils.from_env(\"DD_PROFILING_MAX_FRAMES\", 64, int))\n    endpoint_collection_enabled = attr.ib(\n        factory=attr_utils.from_env(\"DD_PROFILING_ENDPOINT_COLLECTION_ENABLED\", True, formats.asbool)\n    )\n\n    tracer = attr.ib(default=None)\n\n    def _start_service(self):  # type: ignore[override]\n        # type: (...) -> None\n        \"\"\"Start collecting `threading.Lock` usage.\"\"\"\n        self.patch()\n        super(LockCollector, self)._start_service()\n\n    def _stop_service(self):  # type: ignore[override]\n        # type: (...) -> None\n        \"\"\"Stop collecting `threading.Lock` usage.\"\"\"\n        super(LockCollector, self)._stop_service()\n        self.unpatch()\n\n    def patch(self):\n        # type: (...) -> None\n        \"\"\"Patch the threading module for tracking lock allocation.\"\"\"\n        # We only patch the lock from the `threading` module.\n        # Nobody should use locks from `_thread`; if they do so, then it's deliberate and we don't profile.\n        self.original = threading.Lock\n\n        def _allocate_lock(wrapped, instance, args, kwargs):\n            lock = wrapped(*args, **kwargs)\n            return _ProfiledLock(\n                lock, self.recorder, self.tracer, self.nframes, self._capture_sampler, self.endpoint_collection_enabled\n            )\n\n        threading.Lock = FunctionWrapper(self.original, _allocate_lock)  # type: ignore[misc]\n\n    def unpatch(self):\n        # type: (...) -> None\n        \"\"\"Unpatch the threading module for tracking lock allocation.\"\"\"\n        threading.Lock = self.original  # type: ignore[misc]\n", "path": "ddtrace/profiling/collector/threading.py"}]}