Skip to content

Make xprofile time/memory measured and bottlenecks structured#87

Merged
koriym merged 2 commits into
1.xfrom
xprofile-accuracy
Jun 26, 2026
Merged

Make xprofile time/memory measured and bottlenecks structured#87
koriym merged 2 commits into
1.xfrom
xprofile-accuracy

Conversation

@koriym

@koriym koriym commented Jun 26, 2026

Copy link
Copy Markdown
Owner

Summary

xprofile's time_ms/memory_mb were heuristic estimates and bottlenecks was a preformatted string array. This makes the metrics real measurements from the cachegrind summary: line and structures bottlenecks for machine processing.

Independent of the xstep slim-down (#86) — a separate concern (accuracy/quality, not size), branched off 1.x.

What changed

Measured time/memory (was estimated)

The cachegrind file carries summary: <total_time> <total_memory> with units declared in the events: header (Time_(10ns) Memory_(bytes)). We now parse those for the real totals, keeping the old heuristic only as a fallback when the summary or its units are missing.

Structured bottlenecks (was string array)

["main (54%)"] becomes [{"function":"main","percentage":54.0,"time_ms":0.07}], so consumers can jq-filter/sort by percentage or time.

Latent bug fixed

Per-function cost summed the line number (1st cost column) instead of the Time column (2nd), making percentages meaningless. Fixed to sum Time — now percentages and time_ms are time-based, and the bottleneck time_ms values sum to the reported total.

Measured before / after

Same command (xprofile --json -- php tests/fixtures/debug_test.php):

field before (estimated) after (measured)
time_ms 0 (rounds to zero) 0.16
memory_mb 3.1 (fixed-base formula) 2.08
bottlenecks ["main (54%)", ...] [{function, percentage, time_ms}, ...]

Verification

  • validate-profile-json confirms output conforms to the updated schema
  • composer tests green (293 tests, PHPStan/Psalm/cs)
  • Format details live in the schema; the skill and MCP descriptions defer to it (same principle as Slim down xstep step-recording JSON output #86)

Note

src/XdebugProfiler.php and two profile DTOs are dead code (never instantiated; both CLI and MCP go through bin/xprofile) and also mis-parse the summary line. Their removal is tracked separately, out of scope here.

Summary by CodeRabbit

  • New Features

    • Performance profiles now report execution time and peak memory using measured values when available.
    • Bottleneck results are now returned as structured entries with function names, percentages, and optional timing data.
  • Bug Fixes

    • Fixed bottleneck calculations so function costs are summed from the correct Cachegrind data field.
    • Improved fallback behavior when measured summary values are unavailable.
  • Documentation

    • Updated usage examples and schema details to match the new output format.

time_ms and memory_mb were heuristic estimates (time_ms could round to 0;
memory_mb was a fixed-base formula). Parse the cachegrind `summary:` line for
the real measured totals (units from the `events:` header, Time_(10ns) and
Memory_(bytes)), keeping the estimate only as a fallback.

bottlenecks were preformatted strings like "main (54%)". Emit a structured
array [{function, percentage, time_ms}] so consumers can jq-filter/sort.

Also fix a latent bug: per-function cost summed the line number (1st cost
column) instead of the Time column (2nd), so percentages were meaningless.
Now percentages and time_ms are time-based, and the bottleneck time_ms values
sum to the reported total.

Schema, MCP tool description, skill, and TROUBLESHOOTING jq example follow.
@koriym

koriym commented Jun 26, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@koriym, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 43 minutes and 5 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b6100054-a06c-481c-b862-f62bde09d78b

📥 Commits

Reviewing files that changed from the base of the PR and between d47a893 and c7fb0f4.

📒 Files selected for processing (3)
  • bin/xprofile
  • skills/xdebug/SKILL.md
  • src/McpServer.php
📝 Walkthrough

Walkthrough

xprofile now reads measured execution time and peak memory from Cachegrind summary data, accumulates function time from the correct cost column, and emits structured bottleneck objects. The schema and related docs were updated to match the new output shape.

Changes

Xprofile output and contract updates

Layer / File(s) Summary
Cachegrind parsing and stats
bin/xprofile
analyzeCachegrindFile() now extracts measured totals from Cachegrind summary data, function cost accumulation uses the time column, and the final stats prefer measured execution time and peak memory while emitting structured bottlenecks.
Schema and guidance
docs/schemas/xprofile.json, docs/TROUBLESHOOTING.md, skills/xdebug/SKILL.md, src/McpServer.php
The xprofile schema, examples, troubleshooting jq query, skill guidance, and MCP tool/prompt descriptions now describe measured totals and the structured bottlenecks array shape.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • koriym/xdebug-mcp#48: Also changes xprofile tool/schema wording and related metadata around the xprofile output contract.

Suggested reviewers

  • sourcery-ai

Poem

A rabbit hopped through Cachegrind lines,
And found the time in measured signs.
His bottlenecks now stand in tidy rows,
With memory tall as moonlit clover grows. 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: measured time/memory and structured bottlenecks in xprofile.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch xprofile-accuracy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@bin/xprofile`:
- Around line 395-412: The bottleneck output format in xprofile was changed to
an array of structured entries, but the default CLI rendering still treats each
item as a string. Update displayHumanReadableProfile() to detect and render the
bottleneck_functions entries as objects/arrays (showing function, percentage,
and time_ms when present) instead of echoing them directly, so the top functions
remain readable in the default CLI path.
- Around line 250-262: The Cachegrind parsing in xprofile hard-codes Time_(10ns)
and Memory_(bytes) column positions, which breaks when xdebug.cachegrind_events
changes the event order. Update the parsing logic to read the events: header
first, map the indices for Time_(10ns) and Memory_(bytes), and then use those
indices consistently in the summary processing and cost-line aggregation code
paths. Use the existing xprofile Cachegrind parsing section and the
summary/cost-line handling logic as the main places to change.

In `@src/McpServer.php`:
- Line 121: The runtime metadata for the profile performance tool is advertising
the wrong contract key: the description in McpServer and the mirrored wording in
skills/xdebug/SKILL.md mention a $schema URL, but bin/xprofile actually emits
schema. Update the text in the relevant metadata/description strings to
advertise the actual schema field name consistently, using the
profile/performance description and any mirrored skill docs so MCP clients see
the correct payload shape.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cc37a586-ca8c-41d0-ba23-b343e99ba246

📥 Commits

Reviewing files that changed from the base of the PR and between c218088 and d47a893.

📒 Files selected for processing (5)
  • bin/xprofile
  • docs/TROUBLESHOOTING.md
  • docs/schemas/xprofile.json
  • skills/xdebug/SKILL.md
  • src/McpServer.php

Comment thread bin/xprofile Outdated
Comment thread bin/xprofile
Comment thread src/McpServer.php Outdated
- Read the cachegrind `events:` header to locate the Time/Memory columns
  instead of hard-coding positions, so a non-default xdebug.cachegrind_events
  order works. The summary parse and the cost-line aggregation both use the
  mapped indices.
- Render structured bottlenecks in the human-readable CLI path
  (displayHumanReadableProfile was echoing the arrays as "Array").
- Fix the profile tool description: xprofile emits `schema`, not `$schema`
  (McpServer description + mirrored SKILL wording).
@koriym
koriym merged commit a8ce64d into 1.x Jun 26, 2026
7 checks passed
@koriym
koriym deleted the xprofile-accuracy branch June 26, 2026 15:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant