874b6f20c2
Основа — Horizon (MIT). История апстрима не переносится: этот репозиторий ведёт свою.
73 lines
3.6 KiB
Python
73 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Verify the streaming JFR reader against the JDK's own JSON exporter."""
|
|
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
import subprocess
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
BUILD = ROOT / ".local/jfr-tests"
|
|
|
|
|
|
def main(collector="default"):
|
|
BUILD.mkdir(parents=True, exist_ok=True)
|
|
fixture = BUILD / "fixture.jfr"
|
|
gc_flags = ["-XX:+UseZGC"] if collector == "zgc" else []
|
|
subprocess.run(["java", "-Xmx128M", *gc_flags, str(ROOT / "tests/java/JfrFixture.java"), str(fixture)], check=True)
|
|
summary = json.loads(subprocess.check_output(
|
|
["java", str(ROOT / "tools/JfrSummary.java"), str(fixture)], text=True))
|
|
events = json.loads(subprocess.check_output(
|
|
["jfr", "print", "--json", "--stack-depth", "1", "--events",
|
|
"jdk.ThreadAllocationStatistics,jdk.GarbageCollection,jdk.GCPhasePause,jdk.GCHeapSummary,jdk.ExecutionSample,jdk.NativeMethodSample,jdk.ObjectAllocationSample", str(fixture)],
|
|
text=True))["recording"]["events"]
|
|
counters = {}
|
|
for event in events:
|
|
values = event["values"]
|
|
if event["type"] == "jdk.ThreadAllocationStatistics" and values["thread"]:
|
|
thread = values["thread"]
|
|
key = thread["javaThreadId"]
|
|
counters.setdefault(key, []).append((values["startTime"], values["allocated"]))
|
|
for row in summary["allocation_threads"]:
|
|
samples = sorted(counters[row["thread_id"]])
|
|
assert row["counter_observations"] == len(samples)
|
|
if len(samples) > 1 and samples[0][0] != samples[-1][0]:
|
|
assert row["observed_allocated_bytes"] == samples[-1][1] - samples[0][1]
|
|
assert row["observed_seconds"] > 0
|
|
else:
|
|
assert row["observed_allocated_bytes"] is None
|
|
fixture_row = next(row for row in summary["allocation_threads"] if row["thread"].startswith("Horizon JFR fixture"))
|
|
assert '"quoted"\tthread' in fixture_row["thread"]
|
|
assert fixture_row["observed_allocated_bytes"] > 1_000_000
|
|
assert summary["gc_cycles"] == sum(e["type"] == "jdk.GarbageCollection" for e in events)
|
|
assert summary["gc_pause_events"] == sum(e["type"] == "jdk.GCPhasePause" for e in events)
|
|
assert summary["heap_used_peak_at_gc_bytes"] == max(
|
|
e["values"]["heapUsed"] for e in events if e["type"] == "jdk.GCHeapSummary")
|
|
for key, types in [("execution_threads", {"jdk.ExecutionSample", "jdk.NativeMethodSample"}),
|
|
("allocation_sample_threads", {"jdk.ObjectAllocationSample"})]:
|
|
expected = {}
|
|
for event in events:
|
|
if event["type"] not in types:
|
|
continue
|
|
field = "eventThread" if event["type"] == "jdk.ObjectAllocationSample" else "sampledThread"
|
|
thread = event["values"][field]
|
|
if thread:
|
|
expected[thread["javaThreadId"]] = expected.get(thread["javaThreadId"], 0) + 1
|
|
assert {row["thread_id"]: row["samples"] for row in summary[key]} == expected
|
|
def no_weight_keys(value):
|
|
if isinstance(value, dict):
|
|
assert "weight" not in value and "weighted_bytes" not in value
|
|
for child in value.values():
|
|
no_weight_keys(child)
|
|
elif isinstance(value, list):
|
|
for child in value:
|
|
no_weight_keys(child)
|
|
no_weight_keys(summary)
|
|
print("Streaming JFR summary matches real JDK allocation counters, stack-sample counts, GC event counts, heap observations and escaped thread names.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--gc", choices=["default", "zgc"], default="default")
|
|
main(parser.parse_args().gc)
|