Skip to content

Commit

Permalink
Merge branch 'vara-dev' into f-InitTsUtis
Browse files Browse the repository at this point in the history
  • Loading branch information
vulder authored Oct 23, 2023
2 parents 2d01237 + 6ee52ba commit 53ca783
Show file tree
Hide file tree
Showing 14 changed files with 477 additions and 41 deletions.
36 changes: 33 additions & 3 deletions tests/report/test_gnu_time_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@
Average total size (kbytes): 0
Maximum resident set size (kbytes): 1804
Average resident set size (kbytes): 0
Major (requiring I/O) page faults: 0
Major (requiring I/O) page faults: 2
Minor (reclaiming a frame) page faults: 142
Voluntary context switches: 1
Involuntary context switches: 1
Swaps: 0
File system inputs: 0
File system outputs: 0
File system inputs: 1
File system outputs: 2
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Expand Down Expand Up @@ -63,6 +63,12 @@ def test_max_resident_size(self):
with self.assertRaises(WrongTimeReportFormat):
TimeReport._parse_max_resident_size(" Something other timed:")

def test_major_page_faults(self):
"""Test if we correctly parse the amount of major page faults from the
input line."""
with self.assertRaises(WrongTimeReportFormat):
TimeReport._parse_major_page_faults(" Something other timed:")

def test_max_resident_size_byte_type(self):
"""Test if we correctly parse the max resident size from the input
line."""
Expand Down Expand Up @@ -97,6 +103,30 @@ def test_system_time(self):
"""Test if we can extract the system time from the parsed file."""
self.assertEqual(self.report.system_time, timedelta(seconds=3))

def test_wall_clock_time(self):
"""Test if we can extract the wall clock time from the parsed file."""
self.assertEqual(self.report.wall_clock_time, timedelta(seconds=42))

def test_max_resident_size(self) -> None:
"""Test if we can extract the max resident size from the parsed file."""
self.assertEqual(self.report.max_res_size, 1804)

def test_major_page_faults(self) -> None:
"""Test if we can extract the number of major page faults from the
parsed file."""
self.assertEqual(self.report.major_page_faults, 2)

def test_minor_page_faults(self) -> None:
"""Test if we can extract the number of minor page faults from the
parsed file."""
self.assertEqual(self.report.minor_page_faults, 142)

def test_filesystem_io(self) -> None:
"""Test if we can extract the number of filesystem inputs/outputs from
the parsed file."""
self.assertEqual(self.report.filesystem_io[0], 1)
self.assertEqual(self.report.filesystem_io[1], 2)

def test_repr_str(self):
"""Test string representation of TimeReports."""
expected_result = """Command: echo
Expand Down
102 changes: 102 additions & 0 deletions tests/report/test_linux_perf_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"""Test LinuxPerfReport."""

import unittest
from pathlib import Path
from unittest import mock

from varats.report.linux_perf_report import LinuxPerfReport

PERF_REPORT_1 = """# started on Sun Jul 23 22:51:54 2023
Performance counter stats for 'echo foo:bar':
0.30 msec task-clock:u # 0.406 CPUs utilized
0 context-switches:u # 0.000 /sec
0 cpu-migrations:u # 0.000 /sec
64 page-faults:u # 212.723 K/sec
360,721 cycles:u # 1.199 GHz
26,199 stalled-cycles-frontend:u # 7.26% frontend cycles idle
111,008 stalled-cycles-backend:u # 30.77% backend cycles idle
200,655 instructions:u # 0.56 insn per cycle
# 0.55 stalled cycles per insn
48,631 branches:u # 161.639 M/sec
3,012 branch-misses:u # 6.19% of all branches
<not counted> L1-dcache-loads:u (0.00%)
<not counted> L1-dcache-load-misses:u (0.00%)
<not supported> LLC-loads:u
<not supported> LLC-load-misses:u
0.000741511 seconds time elapsed
0.000000000 seconds user
0.000822000 seconds sys
"""

PERF_REPORT_2 = """# started on Sun Jul 23 22:44:31 2023
Performance counter stats for 'foobar':
1.23 msec task-clock:u # 0.000 CPUs utilized
0 context-switches:u # 0.000 /sec
0 cpu-migrations:u # 0.000 /sec
132 page-faults:u # 107.572 K/sec
850,975 cycles:u # 0.693 GHz (12.81%)
140,154 stalled-cycles-frontend:u # 16.47% frontend cycles idle
1,012,322 stalled-cycles-backend:u # 118.96% backend cycles idle
1,785,912 instructions:u # 2.10 insn per cycle
# 0.57 stalled cycles per insn
325,708 branches:u # 265.433 M/sec
11,160 branch-misses:u # 3.43% of all branches
840,918 L1-dcache-loads:u # 685.298 M/sec (87.19%)
<not counted> L1-dcache-load-misses:u (0.00%)
<not supported> LLC-loads:u
<not supported> LLC-load-misses:u
5.945920439 seconds time elapsed
0.000376000 seconds user
0.001390000 seconds sys
"""


class TestLinuxPerfReport(unittest.TestCase):
"""Tests if the Linux perf report can be loaded correctly."""

report_1: LinuxPerfReport
report_2: LinuxPerfReport

@classmethod
def setUpClass(cls) -> None:
"""Load Linux perf report."""
with mock.patch(
"builtins.open", new=mock.mock_open(read_data=PERF_REPORT_1)
):
cls.report_1 = LinuxPerfReport(Path("fake_file_path"))

with mock.patch(
"builtins.open", new=mock.mock_open(read_data=PERF_REPORT_2)
):
cls.report_2 = LinuxPerfReport(Path("fake_file_path"))

def test_task_clock_parsing(self) -> None:
"""Checks if we correctly parsed the value for task clock."""
self.assertEqual(self.report_1.elapsed_time, 0.000741511)
self.assertEqual(self.report_2.elapsed_time, 5.945920439)

def test_context_switches_parsing(self) -> None:
"""Checks if we correctly parsed the value for context switches."""
self.assertEqual(self.report_1.ctx_switches, 0)
self.assertEqual(self.report_2.ctx_switches, 0)

def test_branch_misses_parsing(self) -> None:
"""Checks if we correctly parsed the value for branch misses."""
self.assertEqual(self.report_1.branch_misses, 3012)
self.assertEqual(self.report_2.branch_misses, 11160)
10 changes: 7 additions & 3 deletions tests/test_containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ class TestImageBase(unittest.TestCase):
def test_distro(self) -> None:
self.assertEqual(Distro.DEBIAN, ImageBase.DEBIAN_10.distro)

def test_distro_version_number(self) -> None:
self.assertEqual(10, ImageBase.DEBIAN_10.version)
self.assertEqual(12, ImageBase.DEBIAN_12.version)


class TestContainerSupport(unittest.TestCase):
"""Test container support related functionality."""
Expand Down Expand Up @@ -96,7 +100,7 @@ def test_create_stage_10_from_pip(self) -> None:
self.check_layer_type(layers[0], FromLayer)

varats_core_install_layer = self.check_layer_type(layers[2], RunLayer)
self.assertEqual("pip3", varats_core_install_layer.command)
self.assertEqual("pip", varats_core_install_layer.command)
self.assertTupleEqual(
("install", "--ignore-installed", "varats-core", "varats"),
varats_core_install_layer.args
Expand All @@ -123,7 +127,7 @@ def test_create_stage_10_from_source(self) -> None:
self.check_layer_type(layers[0], FromLayer)

varats_core_install_layer = self.check_layer_type(layers[4], RunLayer)
self.assertEqual("pip3", varats_core_install_layer.command)
self.assertEqual("pip", varats_core_install_layer.command)
self.assertTupleEqual(("install", "/varats/varats-core"),
varats_core_install_layer.args)
mounting_parameters = "type=bind,src=varats_src,target=/varats"
Expand All @@ -133,7 +137,7 @@ def test_create_stage_10_from_source(self) -> None:
varats_core_install_layer.kwargs)

varats_install_layer = self.check_layer_type(layers[5], RunLayer)
self.assertEqual("pip3", varats_install_layer.command)
self.assertEqual("pip", varats_install_layer.command)
self.assertTupleEqual(("install", "/varats/varats"),
varats_install_layer.args)
mounting_parameters = "type=bind,src=varats_src,target=/varats"
Expand Down
80 changes: 78 additions & 2 deletions varats-core/varats/report/gnu_time_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class TimeReport(BaseReport, shorthand="TR", file_type="txt"):

def __init__(self, path: Path) -> None:
super().__init__(path)

self.__filesystem_io = (-1, -1)
with open(self.path, 'r') as stream:
for line in stream:
line = line.strip()
Expand All @@ -64,6 +64,16 @@ def __init__(self, path: Path) -> None:
TimeReport._parse_wall_clock_time(line)
continue

if line.startswith("Major (requiring I/O) page faults"):
self.__major_page_faults: int = \
TimeReport._parse_major_page_faults(line)
continue

if line.startswith("Minor (reclaiming a frame) page faults"):
self.__minor_page_faults: int = \
TimeReport._parse_minor_page_faults(line)
continue

if line.startswith("Voluntary context switches"):
self.__voluntary_ctx_switches: int = \
TimeReport._parse_voluntary_ctx_switches(line)
Expand All @@ -74,7 +84,19 @@ def __init__(self, path: Path) -> None:
TimeReport._parse_involuntary_ctx_switches(line)
continue

# print("Not matched: ", line)
if line.startswith("File system inputs"):
self.__filesystem_io = (
TimeReport._parse_filesystem_io(line),
self.__filesystem_io[1]
)
continue

if line.startswith("File system outputs"):
self.__filesystem_io = (
self.__filesystem_io[0],
TimeReport._parse_filesystem_io(line)
)
continue

@property
def command_name(self) -> str:
Expand All @@ -101,6 +123,25 @@ def max_res_size(self) -> int:
"""Maximum resident size."""
return self.__max_resident_size

@property
def major_page_faults(self) -> int:
"""Major page faults (require I/O)."""
return self.__major_page_faults

@property
def minor_page_faults(self) -> int:
"""Minor page faults (reclaim a frame)."""
return self.__minor_page_faults

@property
def filesystem_io(self) -> tp.Tuple[int, int]:
"""
Filesystem inputs/outputs.
Returns: a tuple of (#inputs, #outputs)
"""
return self.__filesystem_io

@property
def voluntary_ctx_switches(self) -> int:
"""Number of voluntary context switches."""
Expand Down Expand Up @@ -217,6 +258,20 @@ def _parse_max_resident_size(line: str) -> int:
"Could not parse max resident set size: ", line
)

@staticmethod
def _parse_major_page_faults(line: str) -> int:
if line.startswith("Major (requiring I/O) page faults"):
return int(line.split(":")[1])

raise WrongTimeReportFormat("Could not parse major page faults: ", line)

@staticmethod
def _parse_minor_page_faults(line: str) -> int:
if line.startswith("Minor (reclaiming a frame) page faults"):
return int(line.split(":")[1])

raise WrongTimeReportFormat("Could not parse minor page faults: ", line)

@staticmethod
def _parse_voluntary_ctx_switches(line: str) -> int:
if line.startswith("Voluntary context switches"):
Expand All @@ -235,6 +290,15 @@ def _parse_involuntary_ctx_switches(line: str) -> int:
"Could not parse involuntary context switches: ", line
)

@staticmethod
def _parse_filesystem_io(line: str) -> int:
if line.startswith("File system "):
return int(line.split(":")[1])

raise WrongTimeReportFormat(
"Could not parse filesystem inputs/outputs: ", line
)


class TimeReportAggregate(
ReportAggregate[TimeReport],
Expand Down Expand Up @@ -268,6 +332,18 @@ def measurements_ctx_switches(self) -> tp.List[int]:
def max_resident_sizes(self) -> tp.List[int]:
return [report.max_res_size for report in self.reports()]

@property
def major_page_faults(self) -> tp.List[int]:
return [report.major_page_faults for report in self.reports()]

@property
def minor_page_faults(self) -> tp.List[int]:
return [report.minor_page_faults for report in self.reports()]

@property
def filesystem_io(self) -> tp.List[tp.Tuple[int, int]]:
return [report.filesystem_io for report in self.reports()]

@property
def summary(self) -> str:
import numpy as np # pylint: disable=import-outside-toplevel
Expand Down
Loading

0 comments on commit 53ca783

Please sign in to comment.