-
Notifications
You must be signed in to change notification settings - Fork 14
JFR event types and emission for reference chains #796
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
Open
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
5ffbc4a
Model crashNow's intentional crash as a trap under scan-build
jbachorik 80173ad
Add JFR event types for reference chains
jbachorik d51dfb8
Keep chain events inside the recording buffer; keep truncated labels
jbachorik e3b3f66
Drop transient and stale line-number references from comments
jbachorik File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,12 +23,124 @@ | |
| #include <string> | ||
| #include <vector> | ||
|
|
||
| #include "arch.h" | ||
|
|
||
| const long DEFAULT_CPU_INTERVAL = 10 * 1000 * 1000; // 10 ms | ||
| const long DEFAULT_WALL_INTERVAL = 50 * 1000 * 1000; // 50 ms | ||
| const long DEFAULT_ALLOC_INTERVAL = 524287; // 512 KiB | ||
| const int DEFAULT_WALL_THREADS_PER_TICK = 16; | ||
| const int DEFAULT_JSTACKDEPTH = 2048; | ||
|
|
||
| // Every constant below is a provisional default pending empirical | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar problem in this file: way too much comments explaining the same fact over and over again. Make it short and concise once, and drop all the other repetitions. |
||
| // tuning - none of these values are backed by a benchmark run against this | ||
| // codebase. Each is chosen conservatively from cited precedent or from the | ||
| // shape of an existing, already-tuned subsystem, per the rationale below; | ||
| // replacing them with measured values is the outstanding JMH/ | ||
| // async-profiler benchmark work tracked under PROF-15341. | ||
| // | ||
| // Hop cap: mirrors HotSpot's own JFR leak-profiler chain cap (~200 hops, | ||
| // split 100/100 from leaf and from root), cited in | ||
| // doc/architecture/LiveHeapReferenceChains.md's "Approach B" section - the | ||
| // closest real-world precedent for "how many hops does a referrer-type | ||
| // chain typically need" that this codebase can cite without measuring it | ||
| // itself. | ||
| // Sub-option bitmask for the auto-tuner: tracks which referencechains | ||
| // sub-options were explicitly set by the operator, so the auto-tuner | ||
| // only overrides defaults that weren't. | ||
| constexpr u8 REF_CHAINS_TUNED_HOP_CAP = 1 << 0; | ||
| constexpr u8 REF_CHAINS_TUNED_BUDGET = 1 << 1; | ||
| constexpr u8 REF_CHAINS_TUNED_TTL = 1 << 2; | ||
| constexpr u8 REF_CHAINS_TUNED_FRONTIER_CAP = 1 << 3; | ||
| constexpr u8 REF_CHAINS_TUNED_PAUSE_TARGET = 1 << 4; | ||
| constexpr u8 REF_CHAINS_TUNED_PAIN_BUDGET = 1 << 5; | ||
| constexpr u8 REF_CHAINS_TUNED_FIRST_PASS_BUDGET = 1 << 6; | ||
|
|
||
| const int DEFAULT_REFERENCE_CHAINS_HOP_CAP = 200; | ||
| // Per-pass edge budget: no cited precedent gives a number for this (JFR's | ||
| // leak profiler does not bound itself by a per-pass edge count - it runs to | ||
| // completion inside one already-scheduled GC pause). Chosen as a round, | ||
| // conservative middle value intended to keep a single FollowReferences- | ||
| // triggered safepoint short without so small a budget that a search needs | ||
| // an impractical number of passes to make progress. A future benchmark pass | ||
| // should measure per-pass wall-clock pause distribution at this value and adjust. | ||
| const int DEFAULT_REFERENCE_CHAINS_BUDGET = 1000; // edges expanded per BFS pass | ||
| // Per-search TTL: a conservative round number (one minute) chosen so a | ||
| // slow-moving or stalled search is bounded to a human-noticeable but not | ||
| // excessive lifetime, in the absence of any measured "passes needed to | ||
| // reach a target sample at various depths" data (a future benchmark's stated goal). | ||
| const long DEFAULT_REFERENCE_CHAINS_TTL_MS = 60000; // per-search wall-clock TTL | ||
| // Frontier-size cap: sized relative to LivenessTracker's own tuned ceiling | ||
| // (MAX_TRACKING_TABLE_SIZE = 262144, livenessTracker.h) rather than derived | ||
| // from any BFS-specific measurement - the design doc explicitly flags that | ||
| // LivenessTracker's allocation-sample-rate sizing formula does not transfer | ||
| // to a graph-search frontier (Open Question 2), so this only borrows the | ||
| // same order of magnitude, quartered as a conservative starting point since | ||
| // a FrontierEntry is smaller than a TrackingEntry but per-hop fan-out could | ||
| // still be large. Not a scaled/derived value - just a conservative guess | ||
| // pending a future frontier-table peak-occupancy measurement. | ||
| const int DEFAULT_REFERENCE_CHAINS_FRONTIER_CAP = 65536; // max live frontier entries per search | ||
| // Pause-time-SLO ceiling (pause-time pacing controller): target ceiling, | ||
| // per pass, on wall-clock time spent inside the safepoint-triggering | ||
| // FollowReferences/GetObjectsWithTags call | ||
| // (ReferenceChainTracker::updatePacing(), referenceChains.cpp). Like every | ||
| // other constant in this block this is a round, provisional default with no | ||
| // benchmark behind it - picking the real number is explicitly a future | ||
| // measurement question (design doc's Open Question 2), not a value to guess | ||
| // here; this only exists so the feedback loop this ceiling drives has | ||
| // something to target end-to-end before that measurement happens. | ||
| const long DEFAULT_REFERENCE_CHAINS_PAUSE_TARGET_MS = 50; // ms per pass | ||
| // Pain budget refill rate (ReferenceChainTracker::PainBudget, painBudget.h): | ||
| // the fraction of wall-clock time a *restarted* search is allowed to spend | ||
| // inside FollowReferences/GetObjectsWithTags safepoints, on average, before a | ||
| // later restart must wait for the debt from the previous search's cost to | ||
| // drain. Expressed as an integer percent (1 = 1%) for readability - see | ||
| // PainBudget's own header comment for why this single ratio needs no | ||
| // benchmark-derived tuning the way the per-pass constants above do, only a | ||
| // choice of how much background cost is acceptable. Round, provisional | ||
| // default like every other constant in this block. | ||
| const int DEFAULT_REFERENCE_CHAINS_PAIN_BUDGET_PERCENT = 1; | ||
| // First-pass edge budget override: the search's one-and-only root-seeded | ||
| // FollowReferences(0, nullptr, nullptr, ...) call (ReferenceChainTracker::runPass()'s | ||
| // !_search_started branch) enumerates every GC root in one JVMTI-controlled | ||
| // traversal order and stops admitting once this budget is spent - any root | ||
| // FollowReferences had not yet reached is excluded from the frontier for the | ||
| // rest of that search (every later pass only expands forward from already- | ||
| // admitted frontier entries, see expandFrontier()'s own comment). Unlike | ||
| // DEFAULT_REFERENCE_CHAINS_BUDGET, which bounds every pass including the many | ||
| // cheap, per-node expansion passes that follow, this only ever spends once | ||
| // per search, so a much larger one-time ceiling is affordable. 0 (the | ||
| // default) means "no override - auto-scale from the per-pass budget instead | ||
| // of falling back to it plainly": ReferenceChainTracker::start()'s tuning | ||
| // pass scales it from _budget (AUTO_FIRST_PASS_BUDGET_MULTIPLIER, capped at | ||
| // MAX_REFERENCE_CHAINS_FIRST_PASS_BUDGET) unless this knob was set | ||
| // explicitly. | ||
| const int DEFAULT_REFERENCE_CHAINS_FIRST_PASS_BUDGET = 0; | ||
| // Upper clamp for an explicit firstpassbudget override: like painbudget just | ||
| // above, firstpassbudget was previously only floored at 0 with no ceiling. | ||
| // Unlike painbudget (a percentage, naturally bounded at 100), this is a raw | ||
| // edge count, so the ceiling is expressed relative to | ||
| // DEFAULT_REFERENCE_CHAINS_BUDGET (the per-pass budget every later pass is | ||
| // bounded by) rather than as its own standalone guess: a generous but finite | ||
| // multiple still lets the one-time root pass be far larger than a normal | ||
| // pass (its intended purpose) while keeping an operator from disabling the | ||
| // safepoint-pause-bounding mechanism entirely for that first FollowReferences | ||
| // call. | ||
| const int MAX_REFERENCE_CHAINS_FIRST_PASS_BUDGET = | ||
| DEFAULT_REFERENCE_CHAINS_BUDGET * 1000; | ||
| // Upper clamps for hops/budget/framecap: like MAX_REFERENCE_CHAINS_FIRST_PASS_BUDGET | ||
| // just above, these were previously only floored at 1 with no ceiling, so an | ||
| // operator typo (an extra digit) or a mistaken value flows straight into a | ||
| // loop bound (hops), a per-pass edge count (budget), or FrontierTable's | ||
| // capacity (framecap) unchecked. Same generous-but-finite-multiple-of-the- | ||
| // default approach as the first-pass budget clamp: large enough that no | ||
| // legitimate configuration should ever hit the ceiling, small enough to | ||
| // still fail a badly mistyped value safely instead of feeding it straight | ||
| // into an allocation or loop bound. | ||
| const int MAX_REFERENCE_CHAINS_HOP_CAP = DEFAULT_REFERENCE_CHAINS_HOP_CAP * 1000; | ||
| const int MAX_REFERENCE_CHAINS_BUDGET = DEFAULT_REFERENCE_CHAINS_BUDGET * 1000; | ||
| const int MAX_REFERENCE_CHAINS_FRONTIER_CAP = | ||
| DEFAULT_REFERENCE_CHAINS_FRONTIER_CAP * 1000; | ||
|
|
||
| const char *const EVENT_NOOP = "noop"; | ||
| const char *const EVENT_CPU = "cpu"; | ||
| const char *const EVENT_ALLOC = "alloc"; | ||
|
|
@@ -177,6 +289,23 @@ class Arguments { | |
| double _live_samples_ratio; | ||
| bool _record_heap_usage; | ||
| bool _gc_generations; | ||
| // Reference-chain tracking (PROF-15341). Read by ReferenceChainTracker::start() | ||
| // (referenceChains.cpp) to size the frontier table and seed the per-search | ||
| // hop/budget/TTL tunables and the pause-time-SLO ceiling that | ||
| // updatePacing() adapts the effective budget/cadence toward. | ||
| bool _reference_chains; | ||
| int _reference_chains_hop_cap; | ||
| int _reference_chains_budget; | ||
| long _reference_chains_ttl_ms; | ||
| int _reference_chains_frontier_cap; | ||
| long _reference_chains_pause_target_ms; | ||
| int _reference_chains_pain_budget_percent; | ||
| int _reference_chains_first_pass_budget; | ||
| // Bitmask of REF_CHAINS_TUNED_*: which sub-options were explicitly | ||
| // set by the operator, so the auto-tuner knows which defaults it may | ||
| // override. 0 = all defaults, none explicitly set. | ||
| u8 _reference_chains_tuned_mask; | ||
| // Explicit opt-in for the legacy whole-graph JVMTI FollowReferences walk. | ||
| long _nativemem; | ||
| int _jstackdepth; | ||
| int _safe_mode; | ||
|
|
@@ -219,6 +348,15 @@ class Arguments { | |
| _live_samples_ratio(0.1), // default to liveness-tracking 10% of the allocation samples | ||
| _record_heap_usage(false), | ||
| _gc_generations(false), | ||
| _reference_chains(false), | ||
| _reference_chains_hop_cap(DEFAULT_REFERENCE_CHAINS_HOP_CAP), | ||
| _reference_chains_budget(DEFAULT_REFERENCE_CHAINS_BUDGET), | ||
| _reference_chains_ttl_ms(DEFAULT_REFERENCE_CHAINS_TTL_MS), | ||
| _reference_chains_frontier_cap(DEFAULT_REFERENCE_CHAINS_FRONTIER_CAP), | ||
| _reference_chains_pause_target_ms(DEFAULT_REFERENCE_CHAINS_PAUSE_TARGET_MS), | ||
| _reference_chains_pain_budget_percent(DEFAULT_REFERENCE_CHAINS_PAIN_BUDGET_PERCENT), | ||
| _reference_chains_first_pass_budget(DEFAULT_REFERENCE_CHAINS_FIRST_PASS_BUDGET), | ||
| _reference_chains_tuned_mask(0), | ||
| _nativemem(-1), | ||
| _jstackdepth(DEFAULT_JSTACKDEPTH), | ||
| _safe_mode(0), | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That comment is way to verbose to justify why the values need to be floored and ceiling'ed. Either make them very concise or remove it altogether, it's kinda obvious that it's not a good idea to allow values outside the reasonable bounds here.