Skip to content

Commit

Permalink
ref(profiling): Rename profiling frame keys (#1680)
Browse files Browse the repository at this point in the history
Standardizing the names of the keys in the frames across SDKs so we're going to
rename them.
  • Loading branch information
Zylphrex committed Oct 13, 2022
1 parent bb879ab commit 17e92b3
Show file tree
Hide file tree
Showing 2 changed files with 338 additions and 29 deletions.
93 changes: 66 additions & 27 deletions sentry_sdk/profiler.py
Expand Up @@ -29,6 +29,8 @@
from sentry_sdk._types import MYPY
from sentry_sdk.utils import nanosecond_time

RawFrameData = namedtuple("RawFrameData", ["function", "abs_path", "lineno"])

if MYPY:
from types import FrameType
from typing import Any
Expand All @@ -39,10 +41,46 @@
from typing import List
from typing import Optional
from typing import Sequence
from typing import Tuple
from typing_extensions import TypedDict
import sentry_sdk.tracing


FrameData = namedtuple("FrameData", ["name", "file", "line"])
RawSampleData = Tuple[int, Sequence[Tuple[int, Sequence[RawFrameData]]]]

ProcessedStack = Tuple[int, ...]

ProcessedSample = TypedDict(
"ProcessedSample",
{
"elapsed_since_start_ns": str,
"thread_id": str,
"stack_id": int,
},
)

ProcessedFrame = TypedDict(
"ProcessedFrame",
{
"function": str,
"filename": str,
"lineno": int,
},
)

ProcessedThreadMetadata = TypedDict(
"ProcessedThreadMetadata",
{"name": str},
)

ProcessedProfile = TypedDict(
"ProcessedProfile",
{
"frames": List[ProcessedFrame],
"stacks": List[ProcessedStack],
"samples": List[ProcessedSample],
"thread_metadata": Dict[str, ProcessedThreadMetadata],
},
)


_sample_buffer = None # type: Optional[SampleBuffer]
Expand Down Expand Up @@ -132,7 +170,7 @@ def _sample_stack(*args, **kwargs):


def extract_stack(frame, max_stack_depth=MAX_STACK_DEPTH):
# type: (Optional[FrameType], int) -> Sequence[FrameData]
# type: (Optional[FrameType], int) -> Sequence[RawFrameData]
"""
Extracts the stack starting the specified frame. The extracted stack
assumes the specified frame is the top of the stack, and works back
Expand All @@ -149,10 +187,10 @@ def extract_stack(frame, max_stack_depth=MAX_STACK_DEPTH):
frame = frame.f_back

return [
FrameData(
name=get_frame_name(frame),
file=frame.f_code.co_filename,
line=frame.f_lineno,
RawFrameData(
function=get_frame_name(frame),
abs_path=frame.f_code.co_filename,
lineno=frame.f_lineno,
)
for frame in stack
]
Expand Down Expand Up @@ -268,12 +306,12 @@ class SampleBuffer(object):
def __init__(self, capacity):
# type: (int) -> None

self.buffer = [None] * capacity
self.capacity = capacity
self.idx = 0
self.buffer = [None] * capacity # type: List[Optional[RawSampleData]]
self.capacity = capacity # type: int
self.idx = 0 # type: int

def write(self, sample):
# type: (Any) -> None
# type: (RawSampleData) -> None
"""
Writing to the buffer is not thread safe. There is the possibility
that parallel writes will overwrite one another.
Expand All @@ -290,12 +328,12 @@ def write(self, sample):
self.idx = (idx + 1) % self.capacity

def slice_profile(self, start_ns, stop_ns):
# type: (int, int) -> Dict[str, Any]
samples = [] # type: List[Any]
stacks = dict() # type: Dict[Any, int]
stacks_list = list() # type: List[Any]
frames = dict() # type: Dict[FrameData, int]
frames_list = list() # type: List[Any]
# type: (int, int) -> ProcessedProfile
samples = [] # type: List[ProcessedSample]
stacks = dict() # type: Dict[ProcessedStack, int]
stacks_list = list() # type: List[ProcessedStack]
frames = dict() # type: Dict[RawFrameData, int]
frames_list = list() # type: List[ProcessedFrame]

# TODO: This is doing an naive iteration over the
# buffer and extracting the appropriate samples.
Expand All @@ -311,20 +349,16 @@ def slice_profile(self, start_ns, stop_ns):
continue

for tid, stack in raw_sample[1]:
sample = {
"elapsed_since_start_ns": str(ts - start_ns),
"thread_id": str(tid),
}
current_stack = []

for frame in stack:
if frame not in frames:
frames[frame] = len(frames)
frames_list.append(
{
"name": frame.name,
"file": frame.file,
"line": frame.line,
"function": frame.function,
"filename": frame.abs_path,
"lineno": frame.lineno,
}
)
current_stack.append(frames[frame])
Expand All @@ -334,8 +368,13 @@ def slice_profile(self, start_ns, stop_ns):
stacks[current_stack] = len(stacks)
stacks_list.append(current_stack)

sample["stack_id"] = stacks[current_stack]
samples.append(sample)
samples.append(
{
"elapsed_since_start_ns": str(ts - start_ns),
"thread_id": str(tid),
"stack_id": stacks[current_stack],
}
)

# This collects the thread metadata at the end of a profile. Doing it
# this way means that any threads that terminate before the profile ends
Expand All @@ -345,7 +384,7 @@ def slice_profile(self, start_ns, stop_ns):
"name": thread.name,
}
for thread in threading.enumerate()
}
} # type: Dict[str, ProcessedThreadMetadata]

return {
"stacks": stacks_list,
Expand Down

0 comments on commit 17e92b3

Please sign in to comment.