Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add MOJO unwind method #24

Merged
merged 1 commit into from
Oct 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 55 additions & 2 deletions austin/format/mojo.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import typing as t
from dataclasses import dataclass
from dataclasses import field
from enum import Enum
from io import BufferedReader

from austin.stats import ProcessId
from austin.stats import ThreadName


__version__ = "0.1.0"

Expand Down Expand Up @@ -226,6 +230,21 @@


UNKNOWN = MojoString(1, "<unknown>")
InterpreterId = int


@dataclass
class MojoSample:
"""Austin sample."""

pid: ProcessId
iid: InterpreterId
thread: ThreadName
idle: bool = False
gc: bool = False
metrics: t.List[MojoMetric] = field(default_factory=list)
frames: t.List[MojoFrame] = field(default_factory=list)
metadata: t.Dict[str, str] = field(default_factory=dict)


class MojoFile:
Expand Down Expand Up @@ -255,9 +274,12 @@
raise ValueError("Not a MOJO file")

self.mojo_version = self.read_int()
self.metadata: t.Dict[str, str] = {}
self.samples: t.List[MojoSample] = []

self.header = bytes(self._last_bytes)
self._last_bytes.clear()
self._last_sample: t.Optional[MojoSample] = None

def ref(self, n: int) -> t.Tuple[int, int]:
"""Return a per-process reference key.
Expand Down Expand Up @@ -324,6 +346,12 @@
metadata = MojoMetadata(self.read_string(), self.read_string())
if metadata.key == "mode" and metadata.value == "full":
self._full_mode = True

if self._last_sample is not None:
self._last_sample.metadata[metadata.key] = metadata.value

Check warning on line 351 in austin/format/mojo.py

View check run for this annotation

Codecov / codecov/patch

austin/format/mojo.py#L351

Added line #L351 was not covered by tests
else:
self.metadata[metadata.key] = metadata.value

yield metadata

@handles(MojoEvents.STACK)
Expand All @@ -333,8 +361,12 @@

self._pid = pid = self.read_int()
iid = self.read_int() if self.mojo_version >= 3 else -1
thread = self.read_string()

self._last_sample = sample = MojoSample(thread=thread, pid=pid, iid=iid)
self.samples.append(sample)

yield MojoStack(pid, iid, self.read_string())
yield MojoStack(pid, iid, thread)

def _lookup_string(self) -> MojoString:
n = self.read_int()
Expand Down Expand Up @@ -369,7 +401,12 @@
@handles(MojoEvents.FRAME_REF)
def parse_frame_ref(self) -> t.Generator[MojoFrameReference, None, None]:
"""Parse a frame reference."""
yield MojoFrameReference(self._frame_map[self.ref(self.read_int())])
frame = self._frame_map[self.ref(self.read_int())]

assert self._last_sample is not None, self._last_sample
self._last_sample.frames.append(frame)

yield MojoFrameReference(frame)

@handles(MojoEvents.FRAME_KERNEL)
def parse_kernel_frame(self) -> t.Generator[MojoKernelFrame, None, None]:
Expand All @@ -386,6 +423,10 @@
self._metrics.append(metric)
return MojoEvent()

assert self._last_sample is not None, self._last_sample
self._last_sample.metrics.append(metric)
self._last_sample = None

return metric

@handles(MojoEvents.METRIC_TIME)
Expand All @@ -407,11 +448,18 @@
def parse_idle(self) -> t.Generator[MojoIdle, None, None]:
"""Parse idle event."""
self._metrics.append(1)

assert self._last_sample is not None, self._last_sample
self._last_sample.idle = True

Check warning on line 453 in austin/format/mojo.py

View check run for this annotation

Codecov / codecov/patch

austin/format/mojo.py#L452-L453

Added lines #L452 - L453 were not covered by tests

yield MojoIdle()

@handles(MojoEvents.GC)
def parse_gc(self) -> t.Generator[MojoSpecialFrame, None, None]:
"""Parse a GC event."""
assert self._last_sample is not None, self._last_sample
self._last_sample.gc = True

Check warning on line 461 in austin/format/mojo.py

View check run for this annotation

Codecov / codecov/patch

austin/format/mojo.py#L460-L461

Added lines #L460 - L461 were not covered by tests

yield MojoSpecialFrame("GC")

@handles(MojoEvents.STRING)
Expand Down Expand Up @@ -461,6 +509,11 @@
return
yield e

def unwind(self) -> None:
"""Read the MOJO file."""
for _ in self.parse():
pass


def main() -> None:
from argparse import ArgumentParser
Expand Down
17 changes: 17 additions & 0 deletions test/format/test_mojo.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,20 @@ def test_mojo_column_info():
def test_mojo_stack():
assert MojoStack(1, -1, "noiid").to_austin() == "P1;Tnoiid"
assert MojoStack(1, 2, "iid").to_austin() == "P1;T2:iid"


def test_mojo_data():
input = (DATA / "test").with_suffix(".mojo")

with input.open("rb") as stream:
m = MojoFile(stream)
m.unwind()

assert m.metadata == {
"austin": "3.4.0",
"duration": "1038089",
"interval": "100",
"mode": "wall",
}

assert len(m.samples) == 13227
Loading