Skip to content

Commit

Permalink
Merge pull request #27 from laixintao/lines-create
Browse files Browse the repository at this point in the history
refactor: move lines create to profile model
  • Loading branch information
laixintao authored Oct 4, 2023
2 parents b098536 + 36bfcb2 commit 3ee16f9
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 65 deletions.
57 changes: 46 additions & 11 deletions flameshow/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from dataclasses import dataclass, field
import datetime
import logging
import time
from typing import Dict, List
from typing_extensions import Self

Expand Down Expand Up @@ -88,18 +89,52 @@ class SampleType:

@dataclass
class Profile:
filename: str = ""

created_at: datetime.datetime | None = None
id_store: Dict[int, Frame] = field(default_factory=dict)

sample_types: List[SampleType] = field(default_factory=list)
# required
filename: str
root_stack: Frame
highest_lines: int
# total samples is one top most sample, it's a list that contains all
# its parents all the way up
total_sample: int
sample_types: List[SampleType]
id_store: Dict[int, Frame]

# optional
default_sample_type_index: int = -1

period_type: SampleType | None = None
period: int = 0
created_at: datetime.datetime | None = None

# init by post_init
lines: List = field(init=False)

def __post_init__(self):
"""
init_lines must be called before render
"""
t1 = time.time()
logger.info("start to create lines...")

root = self.root_stack

lines = [
[root],
]
current = root.children
line_no = 1

while len(current) > 0:
line = []
next_line = []

for child in current:
line.append(child)
next_line.extend(child.children)

lines.append(line)
line_no += 1
current = next_line

# TODO parse using protobuf
root_stack: Frame | None = None
highest_lines: int = 0
total_sample: int = 0
t2 = time.time()
logger.info("create lines done, took %.2f seconds", t2 - t1)
self.lines = lines
42 changes: 21 additions & 21 deletions flameshow/pprof_parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import gzip
import logging
import os
from typing import List
from typing import Dict, List

from flameshow.models import Frame, Profile, SampleType
from flameshow.utils import sizeof
Expand Down Expand Up @@ -164,7 +164,7 @@ def __init__(self, filename):
self.locations = []
self.highest = 0

self.id_store = {self.root._id: self.root}
self.id_store: Dict[int, Frame] = {self.root._id: self.root}

def idgenerator(self):
i = self.next_id
Expand All @@ -185,34 +185,34 @@ def parse(self, binary_data):
pbdata = unmarshal(binary_data)
self.parse_internal_data(pbdata)

pprof_profile = Profile()
pprof_profile.filename = self.filename
pprof_profile.sample_types = self.parse_sample_types(
pbdata.sample_type
)
pprof_profile.created_at = self.parse_created_at(pbdata.time_nanos)
pprof_profile.period = pbdata.period
pprof_profile.period_type = self.to_smaple_type(pbdata.period_type)

if pbdata.default_sample_type:
pprof_profile.default_sample_type_index = (
pbdata.default_sample_type
)
sample_types = self.parse_sample_types(pbdata.sample_type)

# WIP
root = self.root
root.values = [0] * len(pprof_profile.sample_types)
root.values = [0] * len(sample_types)
for pbsample in pbdata.sample:
child_frame = self.parse_sample(pbsample)
if not child_frame:
continue
root.values = list(map(sum, zip(root.values, child_frame.values)))
root.pile_up(child_frame)

pprof_profile.root_stack = root
pprof_profile.id_store = self.id_store
pprof_profile.total_sample = len(pbdata.sample)
pprof_profile.highest_lines = self.highest
pprof_profile = Profile(
filename=self.filename,
root_stack=root,
highest_lines=self.highest,
total_sample=len(pbdata.sample),
sample_types=sample_types,
id_store=self.id_store,
)

if pbdata.default_sample_type:
pprof_profile.default_sample_type_index = (
pbdata.default_sample_type
)

pprof_profile.created_at = self.parse_created_at(pbdata.time_nanos)
pprof_profile.period = pbdata.period
pprof_profile.period_type = self.to_smaple_type(pbdata.period_type)

return pprof_profile

Expand Down
34 changes: 2 additions & 32 deletions flameshow/render/flamegraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,38 +68,8 @@ def __init__(
self.view_frame = view_frame

# pre-render
self.lines = self.create_lines(profile)
self.frame_maps = None

def create_lines(self, profile):
t1 = time.time()
logger.info("start to create lines...")

root = profile.root_stack

lines = [
[root],
]
current = [root.children]
line_no = 1

while len(current) > 0:
line = []
next_line = []

for children_group in current:
for child in children_group:
line.append(child)
next_line.append(child.children)

lines.append(line)
line_no += 1
current = next_line

t2 = time.time()
logger.info("create lines done, took %.2f seconds", t2 - t1)
return lines

def render_lines(self, crop):
logger.info("render_lines!! crop: %s", crop)
my_width = crop.size.width
Expand All @@ -116,7 +86,7 @@ def generate_frame_maps(self, width, focused_stack_id):
"""
compute attributes for render for every frame
only re-computes with width, focused_stack changeing
only re-computes with width, focused_stack changing
"""
logger.info(
"lru cache miss, Generates frame map, for width=%d,"
Expand Down Expand Up @@ -198,7 +168,7 @@ def _generate_for_children(frame):

def render_line(self, y: int) -> Strip:
# logger.info("container_size: %s", self.container_size)
line = self.lines[y]
line = self.profile.lines[y]

if not self.frame_maps:
raise Exception("frame_maps is not init yet!")
Expand Down
26 changes: 26 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from flameshow.models import Profile, SampleType
from flameshow.pprof_parser.parser import ProfileParser, Frame


Expand All @@ -23,3 +24,28 @@ def test_pile_up():
root.pile_up(s1)
assert root.children[0].values == [7]
assert root.children[0].children[0].values == [6]


def test_profile_creataion():
root = Frame("root", 0, values=[5])
s1 = Frame("s1", 1, values=[4], parent=root)
s2 = Frame("s2", 2, values=[1], parent=s1)
s3 = Frame("s3", 3, values=[2], parent=s1)

root.children = [s1]
s1.children = [s2, s3]

p = Profile(
filename="abc",
root_stack=root,
highest_lines=1,
total_sample=2,
sample_types=[SampleType("goroutine", "count")],
id_store={
0: root,
1: s1,
2: s2,
3: s3,
},
)
assert p.lines == [[root], [s1], [s2, s3]]
25 changes: 24 additions & 1 deletion tests/test_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
from flameshow.pprof_parser import parse_profile
from flameshow.pprof_parser.parser import Line, Profile, SampleType, PprofFrame
from flameshow.render import FlameshowApp
from flameshow.models import Frame


@pytest.mark.asyncio
@pytest.mark.skip(reason="todo rewrite")
async def test_render_goroutine_child_not_100percent_of_parent(data_dir):
"""some goroutines are missing, child is not 100% of parent
should render 66.6% for the only child in some cases"""
Expand All @@ -27,8 +29,29 @@ async def test_render_goroutine_child_not_100percent_of_parent(data_dir):
assert child.styles.width.value == 66.67


@pytest.mark.skip(reason="todo rewrite")
def test_default_sample_types_heap():
p = Profile()
root = Frame("root", 0, values=[5])
s1 = Frame("s1", 1, values=[4], parent=root)
s2 = Frame("s2", 2, values=[1], parent=s1)
s3 = Frame("s3", 3, values=[2], parent=s1)

root.children = [s1]
s1.children = [s2, s3]

p = Profile(
filename="abc",
root_stack=root,
highest_lines=1,
total_sample=2,
sample_types=[SampleType("goroutine", "count")],
id_store={
0: root,
1: s1,
2: s2,
3: s3,
},
)
p.sample_types = [
SampleType("alloc_objects", "count"),
SampleType("alloc_space", "bytes"),
Expand Down

0 comments on commit 3ee16f9

Please sign in to comment.