Skip to content

Commit 4173b8b

Browse files
committed
fix(profiling): make sure lineno is always an int and funcname is never None
In some cases, f_lineno can be `None` which would trigger an error in the pprof output. This is not even caught by mypy, and I've proposed a fix here: python/typeshed#6769 This patch makes sure we always use 0 if the line number is `None`. It also makes sure a funcname is always passed — in practice it's now the case, but make sure typing reflects that. Fixes DataDog#3046
1 parent 13f9c6c commit 4173b8b

File tree

4 files changed

+26
-11
lines changed

4 files changed

+26
-11
lines changed

ddtrace/profiling/collector/_traceback.pyx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ cpdef traceback_to_frames(traceback, max_nframes):
1212
if nframes < max_nframes:
1313
frame = tb.tb_frame
1414
code = frame.f_code
15-
frames.insert(0, (code.co_filename, frame.f_lineno, code.co_name))
15+
lineno = 0 if frame.f_lineno is None else frame.f_lineno
16+
frames.insert(0, (code.co_filename, lineno, code.co_name))
1617
nframes += 1
1718
tb = tb.tb_next
1819
return frames, nframes
@@ -30,6 +31,7 @@ cpdef pyframe_to_frames(frame, max_nframes):
3031
nframes += 1
3132
if len(frames) < max_nframes:
3233
code = frame.f_code
33-
frames.append((code.co_filename, frame.f_lineno, code.co_name))
34+
lineno = 0 if frame.f_lineno is None else frame.f_lineno
35+
frames.append((code.co_filename, lineno, code.co_name))
3436
frame = frame.f_back
3537
return frames, nframes

ddtrace/profiling/collector/threading.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import attr
99

10+
import ddtrace
1011
from ddtrace.internal import compat
1112
from ddtrace.internal import nogevent
1213
from ddtrace.internal.utils import attr as attr_utils
@@ -19,6 +20,10 @@
1920
from ddtrace.vendor import wrapt
2021

2122

23+
if typing.TYPE_CHECKING:
24+
from ddtrace.profiling import recorder as ddrecorder
25+
26+
2227
@event.event_class
2328
class LockEventBase(event.StackBasedEvent):
2429
"""Base Lock event."""
@@ -62,7 +67,16 @@ def _current_thread():
6267

6368

6469
class _ProfiledLock(wrapt.ObjectProxy):
65-
def __init__(self, wrapped, recorder, tracer, max_nframes, capture_sampler, endpoint_collection_enabled):
70+
def __init__(
71+
self,
72+
wrapped, # type: threading.Lock
73+
recorder, # type: ddrecorder.Recorder
74+
tracer, # type: ddtrace.Tracer
75+
max_nframes, # type: int
76+
capture_sampler, # type: collector.CaptureSampler
77+
endpoint_collection_enabled, # type: bool
78+
):
79+
# type: (...) -> None
6680
wrapt.ObjectProxy.__init__(self, wrapped)
6781
self._self_recorder = recorder
6882
self._self_tracer = tracer
@@ -71,7 +85,10 @@ def __init__(self, wrapped, recorder, tracer, max_nframes, capture_sampler, endp
7185
self._self_endpoint_collection_enabled = endpoint_collection_enabled
7286
frame = sys._getframe(2 if WRAPT_C_EXT else 3)
7387
code = frame.f_code
74-
self._self_name = "%s:%d" % (os.path.basename(code.co_filename), frame.f_lineno)
88+
self._self_name = "%s:%d" % (
89+
os.path.basename(code.co_filename),
90+
0 if frame.f_lineno is None else frame.f_lineno,
91+
)
7592

7693
def acquire(self, *args, **kwargs):
7794
if not self._self_capture_sampler.capture():

ddtrace/profiling/event.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
_T = typing.TypeVar("_T")
1010

1111
# (filename, line number, function name)
12-
FrameType = typing.Tuple[str, int, typing.Optional[str]]
12+
FrameType = typing.Tuple[str, int, str]
1313
StackTraceType = typing.List[FrameType]
1414

1515

ddtrace/profiling/exporter/pprof.pyx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,20 +151,16 @@ class _PprofConverter(object):
151151
self,
152152
filename: str,
153153
lineno: int,
154-
funcname: typing.Optional[str] = None,
154+
funcname: str,
155155
) -> pprof_LocationType:
156156
try:
157157
return self._locations[(filename, lineno, funcname)]
158158
except KeyError:
159-
if funcname is None:
160-
real_funcname = "<unknown function>"
161-
else:
162-
real_funcname = funcname
163159
location = pprof_pb2.Location( # type: ignore[attr-defined]
164160
id=self._last_location_id.generate(),
165161
line=[
166162
pprof_pb2.Line( # type: ignore[attr-defined]
167-
function_id=self._to_Function(filename, real_funcname).id,
163+
function_id=self._to_Function(filename, funcname).id,
168164
line=lineno,
169165
),
170166
],

0 commit comments

Comments
 (0)