Skip to content

feat(profile): add profile command to surface CPU profiling data#187

Open
BYK wants to merge 10 commits intomainfrom
feat/profile-command
Open

feat(profile): add profile command to surface CPU profiling data#187
BYK wants to merge 10 commits intomainfrom
feat/profile-command

Conversation

@BYK
Copy link
Member

@BYK BYK commented Feb 5, 2026

Summary

Adds a new sentry profile command with two subcommands to surface CPU profiling data from Sentry:

  • profile list - Lists transactions that have profiling data available
  • profile view <transaction> - Analyzes CPU profile for a transaction, showing hot paths and performance metrics

Features

  • Hot path detection with CPU time percentages
  • P75/P95/P99 percentile statistics per function
  • User code vs library code filtering (--all-frames to include all)
  • JSON output for CI/automation
  • --web flag to open profiles in Sentry UI
  • Configurable time periods (1h/24h/7d/14d/30d)
  • Transaction aliases for quick access via numeric index or short alias

Usage Examples

List Transactions with Profiles

$ sentry profile list sentry/sentry --period 7d

Transactions with Profiles in sentry/sentry (last 7d):

  #   ALIAS   TRANSACTION                                    PROFILES       p75
──────────────────────────────────────────────────────────────────────────────────
    1   i       /api/0/organizations/{org}/issues/                 357      1.6s
    2   e       /api/0/projects/{org}/{proj}/events/                42    501ms
    3   ju      /extensions/jira/issue-updated/                     18   29.5ms
    4   jc      /extensions/jira/issue-created/                     12   31.2ms

Tip: Use 'sentry profile view 1' or 'sentry profile view <alias>' to analyze.

View Profile Analysis

Access by index, alias, or full transaction name:

# By numeric index (from the list)
$ sentry profile view 1 --org sentry --project sentry

# By short alias
$ sentry profile view i --org sentry --project sentry

# By full transaction name
$ sentry profile view "/api/0/organizations/{org}/issues/" --org sentry --project sentry

Output:

/api/0/organizations/{org}/issues/: CPU Profile Analysis (last 7d)
════════════════════════════════════════════════════════════════

Performance Percentiles
  p75: 1.6s    p95: 2.8s    p99: 6.6s

Hot Paths (Top 10 by CPU time, user code only)
────────────────────────────────────────────────────────────
    #   Function                                  File:Line             % Time
    1   SentryEnvMiddleware.<locals>.SentryEnvMi  sentry/middleware/en     7.8%
    2   access_log_middleware.<locals>.middlewar  sentry/middleware/ac     7.8%
    3   SubdomainMiddleware.__call__              sentry/middleware/su     7.8%
    ...

Stale Alias Detection

If you change the period or project, the CLI warns you:

$ sentry profile view i --period 24h

Error: Transaction alias 'i' is from a different time period (cached: 7d, requested: 24h).

Suggestion: Run 'sentry profile list sentry/sentry --period 24h' to refresh aliases.

Other Options

# Include library/system frames
sentry profile view 1 --all-frames

# JSON output for automation
sentry profile view 1 --json

# Open in Sentry web UI
sentry profile view 1 --web

How Aliases Work

  • Aliases are generated from the last meaningful URL segment (e.g., /api/.../issues/i)
  • Stored in SQLite with fingerprint org:project:period for cache validation
  • Uses shortest unique prefix algorithm (same as project aliases for issues)

Closes #56

Add new `sentry profile` command with two subcommands:

- `profile list` - Lists transactions with profiling data available
- `profile view` - Analyzes CPU profile for a specific transaction,
  showing hot paths, percentiles, and optimization recommendations

Features:
- Flamegraph API integration for detailed call stack analysis
- Hot path detection with CPU time percentages
- P75/P95/P99 percentile statistics per function
- User code vs library code filtering (--all-frames to include all)
- JSON output support for CI/automation
- Web flag to open profiles in Sentry UI
- Configurable time periods (1h/24h/7d/14d/30d)

Closes #56
@github-actions
Copy link

github-actions bot commented Feb 5, 2026

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

  • (cli) Add plural command aliases for list commands by betegon in #209
  • (profile) Add profile command to surface CPU profiling data by BYK in #187

Bug Fixes 🐛

Upgrade

  • Handle EPERM in isProcessRunning for cross-user locks by BYK in #211
  • Replace curl pipe with direct binary download by BYK in #208

Documentation 📚

  • Add documentation for log command by betegon in #210

🤖 This preview updates automatically when you update the PR.

@github-actions
Copy link

github-actions bot commented Feb 5, 2026

Codecov Results 📊

❌ Patch coverage is 54.41%. Project has 2288 uncovered lines.
❌ Project coverage is 74.98%. Comparing base (base) to head (head).

Files with missing lines (46)
File Patch % Lines
human.ts 55.31% ⚠️ 391 Missing
resolve-target.ts 15.28% ⚠️ 366 Missing
api-client.ts 65.79% ⚠️ 221 Missing
oauth.ts 25.10% ⚠️ 194 Missing
profile.ts 5.48% ⚠️ 138 Missing
analyzer.ts 3.05% ⚠️ 127 Missing
upgrade.ts 67.31% ⚠️ 102 Missing
view.ts 47.70% ⚠️ 91 Missing
migration.ts 47.44% ⚠️ 82 Missing
browser.ts 4.11% ⚠️ 70 Missing
span-tree.ts 5.00% ⚠️ 57 Missing
telemetry.ts 77.51% ⚠️ 56 Missing
api.ts 89.80% ⚠️ 47 Missing
seer.ts 75.54% ⚠️ 45 Missing
schema.ts 90.65% ⚠️ 40 Missing
seer.ts 79.87% ⚠️ 30 Missing
preload.ts 53.23% ⚠️ 29 Missing
utils.ts 88.94% ⚠️ 25 Missing
detector.ts 90.10% ⚠️ 20 Missing
output.ts 15.00% ⚠️ 17 Missing
code-scanner.ts 95.00% ⚠️ 16 Missing
arg-parsing.ts 90.00% ⚠️ 12 Missing
dsn-cache.ts 94.62% ⚠️ 12 Missing
fix.ts 83.61% ⚠️ 10 Missing
qrcode.ts 33.33% ⚠️ 10 Missing
sentry-urls.ts 72.22% ⚠️ 10 Missing
fs-utils.ts 57.14% ⚠️ 9 Missing
project-root.ts 97.73% ⚠️ 7 Missing
version-check.ts 91.76% ⚠️ 7 Missing
feedback.ts 84.21% ⚠️ 6 Missing
auth.ts 95.52% ⚠️ 6 Missing
transaction-alias.ts 89.83% ⚠️ 6 Missing
upgrade.ts 93.83% ⚠️ 5 Missing
resolver.ts 94.57% ⚠️ 5 Missing
index.ts 95.96% ⚠️ 4 Missing
project-aliases.ts 97.40% ⚠️ 2 Missing
project-root-cache.ts 96.92% ⚠️ 2 Missing
transaction-aliases.ts 98.15% ⚠️ 2 Missing
json.ts 33.33% ⚠️ 2 Missing
alias.ts 99.42% ⚠️ 1 Missing
env-file.ts 99.19% ⚠️ 1 Missing
parser.ts 98.63% ⚠️ 1 Missing
colors.ts 97.96% ⚠️ 1 Missing
resolve-transaction.ts 98.96% ⚠️ 1 Missing
helpers.ts 94.74% ⚠️ 1 Missing
helpers.ts 94.74% ⚠️ 1 Missing
Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
- Coverage    76.73%    74.98%    -1.75%
==========================================
  Files           65        71        +6
  Lines         8419      9143      +724
  Branches         0         0         —
==========================================
+ Hits          6460      6855      +395
- Misses        1959      2288      +329
- Partials         0         0         —

Generated by Codecov Action

BYK added 7 commits February 5, 2026 11:29
- Make shared.profiles optional in schema (may be absent when no data)
- Handle null values for sample_durations_ns and sample_counts
- Fix hot path percentage calculation to use total self time
Enable quick access to transactions via numeric indices or short aliases:

- `sentry profile list` now shows # and ALIAS columns
- `sentry profile view 1` - access by numeric index
- `sentry profile view i` - access by alias (last unique segment)

Implementation:
- Add transaction_aliases SQLite table (schema v5)
- Add TransactionAliasEntry type for cached aliases
- Add buildTransactionAliases() using existing alias algorithm
- Add resolveTransaction() with stale cache detection
- Update formatters to show alias columns

Aliases are fingerprinted by org:project:period and error with helpful
messages when stale (e.g., different period) or unknown.
Add comprehensive test coverage for the transaction alias system:

- test/lib/transaction-alias.property.test.ts: Property-based tests
  using fast-check to verify extractTransactionSegment and
  buildTransactionAliases invariants

- test/lib/db/transaction-aliases.test.ts: Unit tests for SQLite
  storage layer including fingerprint building, CRUD operations,
  and stale detection

- test/lib/resolve-transaction.test.ts: Unit tests for transaction
  resolution including index/alias lookup, full name pass-through,
  and stale alias error handling
Resolve conflicts by combining:
- Profile commands (profile list, profile view)
- Log commands (log list, logs shortcut)
- Both profiling and logs API functions in api-client.ts
- Schema v5 with transaction_aliases table (composite primary key)
- SKILL.md documentation for both profile and log commands
…segments

The fallback path was returning numeric segments when all other segments
were placeholders or numeric. Now the fallback also filters out numeric
and placeholder patterns, defaulting to 'txn' if nothing meaningful found.

Fixes flaky property-based test 'does not return purely numeric segments'.
@BYK BYK marked this pull request as ready for review February 6, 2026 16:09
BYK added 2 commits February 6, 2026 17:01
- Fix mismatched default periods: both list and view now use 24h default
- Handle duplicate segments with numeric suffixes (issues, issues2, etc.)
- Add --web/-w flag to profile list command
- Fix schema repair for transaction_aliases to use custom DDL with
  composite primary key
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 4 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

projectSlug: string
): string {
return `${getSentryBaseUrl()}/organizations/${orgSlug}/profiling/?project=${projectSlug}`;
}
Copy link

Choose a reason for hiding this comment

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

Summary URL uses project slug instead of numeric ID

Medium Severity

buildProfilingSummaryUrl puts the project slug into the ?project= query parameter, but Sentry's web frontend expects a numeric project ID for this parameter. The --web flag in profile list returns before calling getProject() (which retrieves the numeric ID), so only the slug is available. This causes the opened browser URL to not correctly filter to the target project.

Additional Locations (1)

Fix in Cursor Fix in Web

orgSlug: string,
projectId: string | number,
transactionName: string,
statsPeriod = "7d"
Copy link

Choose a reason for hiding this comment

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

Default period mismatch between API function and CLI

Low Severity

getFlamegraph defaults statsPeriod to "7d", but both CLI commands (profile list and profile view) default their --period flag to "24h". While the CLI always passes the flag value so the mismatch doesn't currently trigger, any future caller omitting the parameter would silently get a different period than the documented CLI default.

Fix in Cursor Fix in Web

);
}
return value;
}
Copy link

Choose a reason for hiding this comment

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

Duplicated period validation logic across command files

Low Severity

VALID_PERIODS and parsePeriod are identically defined in both list.ts and view.ts. This duplicated validation logic means any future change (e.g., adding a new period option) needs to be applied in two places, risking inconsistency.

Additional Locations (1)

Fix in Cursor Fix in Web

" - Performance percentiles (p75, p95, p99)\n" +
" - Hot paths (functions consuming the most CPU time)\n" +
" - Recommendations for optimization\n\n" +
"By default, only user application code is shown. Use --all-frames to include library code.\n\n" +
Copy link

Choose a reason for hiding this comment

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

Help text references wrong flag name for allFrames

Medium Severity

The fullDescription tells users to "Use --all-frames" (kebab-case), but the flag is defined as allFrames (camelCase) and Stricli matches flag names exactly by default (no scanner.caseStyle is configured). Users following this help text would get an unrecognized flag error. The SKILL.md correctly documents it as --allFrames.

Fix in Cursor Fix in Web

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.

Add profile command to surface profiling data

1 participant