-
Notifications
You must be signed in to change notification settings - Fork 1
fix: macOS metrics collection + show sub-phases in plots #162
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -16,7 +16,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Phase markers with shaded regions and labels | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Two separate plots are generated: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Build plot: cabal, stage1, stage2, stage2-utils, bindist phases | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Build plot: cabal, stage1, stage2, stage3-* phases (with sub-phases) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Test plot: test phase only | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -116,21 +116,121 @@ def format_duration(seconds): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return f"{hours}h {mins}m" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _hex_to_rgb(hex_color): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Convert '#RRGGBB' to (r, g, b) floats in [0,1].""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| h = hex_color.lstrip('#') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return tuple(int(h[i:i+2], 16) / 255.0 for i in (0, 2, 4)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _rgb_to_hex(r, g, b): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Convert (r, g, b) floats in [0,1] to '#RRGGBB'.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return '#{:02x}{:02x}{:02x}'.format( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int(min(max(r, 0), 1) * 255), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int(min(max(g, 0), 1) * 255), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int(min(max(b, 0), 1) * 255), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _vary_color(hex_color, index, total): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Generate a color variation for sub-phase `index` of `total`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Shifts lightness up/down symmetrically around the base color so that | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| adjacent sub-phases are visually distinct. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if total <= 1: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return hex_color | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| r, g, b = _hex_to_rgb(hex_color) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Spread from -0.15 to +0.15 lightness shift | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t = (index / (total - 1)) - 0.5 # [-0.5, 0.5] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| shift = t * 0.30 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return _rgb_to_hex(r + shift, g + shift, b + shift) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Top-level build phase names (and prefixes for stage3-*) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _BUILD_PARENTS = {'cabal', 'stage1', 'stage2'} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Base colors for top-level phases | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _PHASE_COLORS = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'cabal': '#FFD700', # Gold | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'stage1': '#FF6B6B', # Red | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'stage2': '#4ECDC4', # Teal | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'test': '#DDA0DD', # Plum | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _STAGE3_PALETTE = ['#FF8C42', '#6A0572', '#1B998B', '#E55934'] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _parent_of(name): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Return the parent phase name, or None if top-level. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'stage2.rts' -> 'stage2' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'stage3-x86_64-linux.libraries' -> 'stage3-x86_64-linux' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'cabal' -> None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if '.' in name: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return name.rsplit('.', 1)[0] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _base_color_for(name): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Return the base color for a phase (top-level or sub-phase).""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parent = _parent_of(name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lookup = parent if parent else name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if lookup in _PHASE_COLORS: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return _PHASE_COLORS[lookup] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if lookup.startswith('stage3-'): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| idx = int(hashlib.sha256(lookup.encode()).hexdigest(), 16) % len(_STAGE3_PALETTE) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return _STAGE3_PALETTE[idx] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return '#CCCCCC' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def select_display_phases(all_phases): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Choose which phases to display in the build plot. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| When sub-phases exist for a parent (e.g. stage2.rts, stage2.libraries), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| show only the sub-phases — they're more informative. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| When no sub-phases exist (e.g. cabal), show the parent phase. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Determine which parents have sub-phases | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parents_with_subs = set() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for name, _, _, _ in all_phases: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parent = _parent_of(name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if parent is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parents_with_subs.add(parent) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for phase in all_phases: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name = phase[0] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parent = _parent_of(name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if parent is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # This IS a sub-phase — always include it | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.append(phase) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| elif name not in parents_with_subs: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Top-level phase with no sub-phases — include it | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result.append(phase) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # else: top-level with sub-phases — skip (sub-phases cover it) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return result | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _display_label(name): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Produce a short display label for a phase. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Sub-phases strip the parent prefix for readability: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'stage2.rts' -> 'rts' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'stage2.executables' -> 'executables' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'cabal' -> 'cabal' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if '.' in name: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return name.rsplit('.', 1)[1] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def create_plot(timestamps, cpu, mem_used, mem_total, phases, title, output_file): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Create a single metrics plot for the given data and phases.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Define colors | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cpu_color = '#2E86AB' # Blue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mem_color = '#28A745' # Green | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| phase_colors = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'cabal': '#FFD700', # Gold | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'stage1': '#FF6B6B', # Red | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'stage2': '#4ECDC4', # Teal | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'test': '#DDA0DD', # Plum | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # stage3-* platforms get distinct colors | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stage3_palette = ['#FF8C42', '#6A0572', '#1B998B', '#E55934'] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Create figure with dual y-axes - wider aspect ratio (20:6) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cpu_color = '#2E86AB' # Blue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mem_color = '#28A745' # Green | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Create figure with dual y-axes — wider aspect ratio (20:6) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fig, ax1 = plt.subplots(figsize=(20, 6)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Calculate effective concurrency from max CPU usage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -158,47 +258,64 @@ def create_plot(timestamps, cpu, mem_used, mem_total, phases, title, output_file | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| max_mem_gb = max(mem_total) / 1024 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ax2.set_ylim(0, max_mem_gb * 1.1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Assign colors to phases — sub-phases get variations of their parent color | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Group sub-phases by parent to assign variation indices | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parent_groups = {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for name, _, _, _ in phases: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parent = _parent_of(name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if parent is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parent_groups.setdefault(parent, []).append(name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def color_for(name): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parent = _parent_of(name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if parent is not None and parent in parent_groups: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| siblings = parent_groups[parent] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| idx = siblings.index(name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return _vary_color(_base_color_for(name), idx, len(siblings)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+264
to
+274
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for name, _, _, _ in phases: | |
| parent = _parent_of(name) | |
| if parent is not None: | |
| parent_groups.setdefault(parent, []).append(name) | |
| def color_for(name): | |
| parent = _parent_of(name) | |
| if parent is not None and parent in parent_groups: | |
| siblings = parent_groups[parent] | |
| idx = siblings.index(name) | |
| return _vary_color(_base_color_for(name), idx, len(siblings)) | |
| parent_indices = {} | |
| for name, _, _, _ in phases: | |
| parent = _parent_of(name) | |
| if parent is not None: | |
| siblings = parent_groups.setdefault(parent, []) | |
| # The index for this name is the current length before appending | |
| parent_indices.setdefault(parent, {})[name] = len(siblings) | |
| siblings.append(name) | |
| def color_for(name): | |
| parent = _parent_of(name) | |
| if parent is not None and parent in parent_groups: | |
| siblings = parent_groups[parent] | |
| idx = parent_indices.get(parent, {}).get(name) | |
| if idx is not None: | |
| return _vary_color(_base_color_for(name), idx, len(siblings)) |
Copilot
AI
Mar 2, 2026
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.
The comment says labels alternate top/bottom, but the current logic only changes y_pos and keeps va the same ('top' in both branches). Either update the comment to reflect the actual behavior (alternating offsets), or implement true top/bottom alternation by changing vertical alignment and/or using annotation offsets (textcoords/xytext) so the two modes behave differently.
| # Label positioning — alternate top/bottom for adjacent sub-phases | |
| # to prevent overlap when phases are narrow | |
| # Label positioning — alternate vertical offsets near the top for | |
| # adjacent sub-phases to reduce overlap when phases are narrow |
Copilot
AI
Mar 2, 2026
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.
The comment says labels alternate top/bottom, but the current logic only changes y_pos and keeps va the same ('top' in both branches). Either update the comment to reflect the actual behavior (alternating offsets), or implement true top/bottom alternation by changing vertical alignment and/or using annotation offsets (textcoords/xytext) so the two modes behave differently.
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.
The note about
kern.cp_timebeing FreeBSD-only is misleading—kern.cp_timeis generally available on macOS viasysctl, and the original issue described in the PR is about PATH/tool availability in the CI shell. Consider rewording the comment to reflect the real reason for usingps(e.g., portability/simplicity and avoiding reliance onsysctlstate), rather than stating that the sysctl doesn’t exist on macOS.