heapscope 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rubocop.yml +119 -0
- data/CHANGELOG.md +86 -0
- data/CODE_OF_CONDUCT.md +38 -0
- data/CONTRIBUTING.md +72 -0
- data/Gemfile +11 -0
- data/LICENSE +21 -0
- data/README.md +342 -0
- data/Rakefile +49 -0
- data/SECURITY.md +39 -0
- data/assets/heapscope-logo.png +0 -0
- data/assets/logo.svg +24 -0
- data/assets/wordmark.svg +18 -0
- data/docs/README.md +20 -0
- data/docs/ROADMAP.md +29 -0
- data/docs/adr/0001-evidence-over-certainty.md +24 -0
- data/docs/adr/0002-local-only-privacy.md +19 -0
- data/docs/adr/0003-runtime-adapters.md +25 -0
- data/docs/adr/README.md +7 -0
- data/docs/allocators.md +13 -0
- data/docs/api/overview.md +70 -0
- data/docs/changelogs/0.1.0.md +22 -0
- data/docs/changelogs/0.2.0.md +49 -0
- data/docs/changelogs/0.3.0.md +38 -0
- data/docs/changelogs/0.4.0.md +32 -0
- data/docs/changelogs/0.5.0.md +18 -0
- data/docs/changelogs/0.6.0.md +23 -0
- data/docs/cli.md +98 -0
- data/docs/diagnostics/HS001_persistent_class_growth.md +39 -0
- data/docs/diagnostics/HS002_high_retention_ratio.md +24 -0
- data/docs/diagnostics/HS003_thread_local_retention.md +24 -0
- data/docs/diagnostics/HS004_unbounded_collection.md +19 -0
- data/docs/diagnostics/HS005_callback_accumulation.md +14 -0
- data/docs/diagnostics/HS006_closure_retention.md +15 -0
- data/docs/diagnostics/HS007_poor_gc_recovery.md +18 -0
- data/docs/diagnostics/HS008_baseline_regression.md +15 -0
- data/docs/diagnostics/HS009_high_allocation_pressure.md +13 -0
- data/docs/diagnostics/HS010_native_memory_mismatch.md +19 -0
- data/docs/guides/ci-budgets.md +43 -0
- data/docs/guides/first-retention-experiment.md +25 -0
- data/docs/guides/production-safe.md +17 -0
- data/docs/index.md +38 -0
- data/examples/cache_vs_leak.rb +30 -0
- data/examples/closure_capture.rb +20 -0
- data/examples/healthy_churn.rb +16 -0
- data/examples/heapscope.yml +12 -0
- data/examples/import_leak.rb +19 -0
- data/examples/probe_and_session.rb +22 -0
- data/examples/thread_local_leak.rb +31 -0
- data/exe/heapscope +7 -0
- data/heapscope.gemspec +73 -0
- data/lib/heapscope/aging.rb +114 -0
- data/lib/heapscope/analyzer.rb +333 -0
- data/lib/heapscope/baseline.rb +77 -0
- data/lib/heapscope/branding.rb +98 -0
- data/lib/heapscope/budget.rb +108 -0
- data/lib/heapscope/capabilities.rb +42 -0
- data/lib/heapscope/catalog.rb +71 -0
- data/lib/heapscope/cli/color.rb +67 -0
- data/lib/heapscope/cli/commands/capture.rb +289 -0
- data/lib/heapscope/cli/commands/diffing.rb +157 -0
- data/lib/heapscope/cli/commands/meta.rb +244 -0
- data/lib/heapscope/cli/commands/reporting.rb +228 -0
- data/lib/heapscope/cli/completion.rb +72 -0
- data/lib/heapscope/cli/help.rb +226 -0
- data/lib/heapscope/cli/support.rb +126 -0
- data/lib/heapscope/cli.rb +165 -0
- data/lib/heapscope/closures.rb +102 -0
- data/lib/heapscope/collector.rb +167 -0
- data/lib/heapscope/config.rb +196 -0
- data/lib/heapscope/detectors.rb +131 -0
- data/lib/heapscope/diff.rb +130 -0
- data/lib/heapscope/dominators.rb +87 -0
- data/lib/heapscope/errors.rb +13 -0
- data/lib/heapscope/extrapolation.rb +51 -0
- data/lib/heapscope/findings.rb +149 -0
- data/lib/heapscope/globals.rb +108 -0
- data/lib/heapscope/graph.rb +218 -0
- data/lib/heapscope/growth.rb +108 -0
- data/lib/heapscope/middleware.rb +35 -0
- data/lib/heapscope/minitest.rb +47 -0
- data/lib/heapscope/monitor.rb +160 -0
- data/lib/heapscope/noise.rb +48 -0
- data/lib/heapscope/notifications.rb +79 -0
- data/lib/heapscope/pack.rb +55 -0
- data/lib/heapscope/paths.rb +64 -0
- data/lib/heapscope/rails.rb +71 -0
- data/lib/heapscope/report/html.rb +283 -0
- data/lib/heapscope/report/markdown.rb +78 -0
- data/lib/heapscope/report/text.rb +204 -0
- data/lib/heapscope/report.rb +308 -0
- data/lib/heapscope/retention.rb +91 -0
- data/lib/heapscope/rspec.rb +118 -0
- data/lib/heapscope/runtime/base.rb +130 -0
- data/lib/heapscope/runtime/jruby.rb +28 -0
- data/lib/heapscope/runtime/mri.rb +86 -0
- data/lib/heapscope/runtime/truffleruby.rb +40 -0
- data/lib/heapscope/runtime/windows_rss.rb +75 -0
- data/lib/heapscope/runtime.rb +33 -0
- data/lib/heapscope/schema.rb +28 -0
- data/lib/heapscope/scorecard.rb +85 -0
- data/lib/heapscope/session.rb +90 -0
- data/lib/heapscope/sidekiq_middleware.rb +36 -0
- data/lib/heapscope/snapshot.rb +165 -0
- data/lib/heapscope/suggest.rb +99 -0
- data/lib/heapscope/tables.rb +58 -0
- data/lib/heapscope/trend_store.rb +41 -0
- data/lib/heapscope/version.rb +6 -0
- data/lib/heapscope.rb +307 -0
- metadata +211 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f1ac0f631627ce36c708e8a675265170999f9692b5a3d1c883941c01338a814c
|
|
4
|
+
data.tar.gz: ccf46c30e092d097872925d3a5cfcf124f2a8dd4d252beaf7f5666f9d48d38d1
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 22906c262dd5681cde4b144388818220aa991084679fd709186720e7c50cb4455bb0f7a51a51044b37cc4cb91392364bf55dd9857ad613fe3c96e2f482e9bcc8
|
|
7
|
+
data.tar.gz: 246a6b67bb0d24839ac72320e458e2955a01d5452da4aca8b1a0f41aa43d7592d2082cd0546f38e371af5b63ec4204efd6f9ae50d4f9e3f277792483fd1ce63d
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 3.1
|
|
3
|
+
NewCops: enable
|
|
4
|
+
SuggestExtensions: false
|
|
5
|
+
Exclude:
|
|
6
|
+
- "vendor/**/*"
|
|
7
|
+
- "examples/**/*"
|
|
8
|
+
- "benchmark/**/*"
|
|
9
|
+
- "*.gemspec"
|
|
10
|
+
|
|
11
|
+
Style/StringLiterals:
|
|
12
|
+
EnforcedStyle: double_quotes
|
|
13
|
+
|
|
14
|
+
Style/Documentation:
|
|
15
|
+
Enabled: false
|
|
16
|
+
|
|
17
|
+
Style/MutableConstant:
|
|
18
|
+
Enabled: false
|
|
19
|
+
|
|
20
|
+
Naming/MethodParameterName:
|
|
21
|
+
Enabled: false
|
|
22
|
+
|
|
23
|
+
Metrics/MethodLength:
|
|
24
|
+
Max: 100
|
|
25
|
+
Exclude:
|
|
26
|
+
- "lib/heapscope/report/html.rb"
|
|
27
|
+
- "lib/heapscope/report/text.rb"
|
|
28
|
+
|
|
29
|
+
Metrics/ClassLength:
|
|
30
|
+
Max: 400
|
|
31
|
+
|
|
32
|
+
Metrics/ModuleLength:
|
|
33
|
+
Max: 400
|
|
34
|
+
|
|
35
|
+
Metrics/AbcSize:
|
|
36
|
+
Max: 250
|
|
37
|
+
Exclude:
|
|
38
|
+
- "lib/heapscope/report/text.rb"
|
|
39
|
+
|
|
40
|
+
Metrics/CyclomaticComplexity:
|
|
41
|
+
Max: 45
|
|
42
|
+
Exclude:
|
|
43
|
+
- "lib/heapscope/report/text.rb"
|
|
44
|
+
|
|
45
|
+
Metrics/PerceivedComplexity:
|
|
46
|
+
Max: 45
|
|
47
|
+
Exclude:
|
|
48
|
+
- "lib/heapscope/report/text.rb"
|
|
49
|
+
|
|
50
|
+
Metrics/ParameterLists:
|
|
51
|
+
Max: 15
|
|
52
|
+
|
|
53
|
+
Metrics/BlockLength:
|
|
54
|
+
Max: 40
|
|
55
|
+
Exclude:
|
|
56
|
+
- "heapscope.gemspec"
|
|
57
|
+
- "test/**/*"
|
|
58
|
+
- "Rakefile"
|
|
59
|
+
|
|
60
|
+
Metrics/BlockNesting:
|
|
61
|
+
Max: 4
|
|
62
|
+
|
|
63
|
+
Layout/LineLength:
|
|
64
|
+
Max: 180
|
|
65
|
+
|
|
66
|
+
Style/StringLiteralsInInterpolation:
|
|
67
|
+
Enabled: false
|
|
68
|
+
|
|
69
|
+
Style/EmptyStringInsideInterpolation:
|
|
70
|
+
Enabled: false
|
|
71
|
+
|
|
72
|
+
Lint/ShadowedException:
|
|
73
|
+
Enabled: false
|
|
74
|
+
|
|
75
|
+
Lint/NoReturnInBeginEndBlocks:
|
|
76
|
+
Enabled: false
|
|
77
|
+
|
|
78
|
+
Layout/EmptyLinesAfterModuleInclusion:
|
|
79
|
+
Enabled: false
|
|
80
|
+
|
|
81
|
+
Lint/AmbiguousBlockAssociation:
|
|
82
|
+
Enabled: false
|
|
83
|
+
|
|
84
|
+
Lint/DuplicateBranch:
|
|
85
|
+
Enabled: false
|
|
86
|
+
|
|
87
|
+
Lint/UselessConstantScoping:
|
|
88
|
+
Enabled: false
|
|
89
|
+
|
|
90
|
+
Style/NumericPredicate:
|
|
91
|
+
Enabled: false
|
|
92
|
+
|
|
93
|
+
Style/BlockDelimiters:
|
|
94
|
+
Enabled: false
|
|
95
|
+
|
|
96
|
+
Style/MultipleComparison:
|
|
97
|
+
Enabled: false
|
|
98
|
+
|
|
99
|
+
Style/FormatStringToken:
|
|
100
|
+
Enabled: false
|
|
101
|
+
|
|
102
|
+
Style/MultilineBlockChain:
|
|
103
|
+
Enabled: false
|
|
104
|
+
|
|
105
|
+
Style/SafeNavigationChainLength:
|
|
106
|
+
Enabled: false
|
|
107
|
+
|
|
108
|
+
Naming/PredicateMethod:
|
|
109
|
+
Enabled: false
|
|
110
|
+
|
|
111
|
+
Lint/AmbiguousOperatorPrecedence:
|
|
112
|
+
Enabled: false
|
|
113
|
+
|
|
114
|
+
Lint/UnusedMethodArgument:
|
|
115
|
+
AllowUnusedKeywordArguments: true
|
|
116
|
+
IgnoreEmptyMethods: true
|
|
117
|
+
|
|
118
|
+
Style/OptionalBooleanParameter:
|
|
119
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
Detailed release write-ups live in [`docs/changelogs/`](docs/changelogs/).
|
|
9
|
+
|
|
10
|
+
## [Unreleased]
|
|
11
|
+
|
|
12
|
+
## [0.6.0] — 2026-08-13
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Budget presets: `rails_request`, `sidekiq_job`, `ci_strict` (`Budget.preset` / `HeapScope.budget_preset`)
|
|
17
|
+
- Finding priority ranking + dedupe (`Findings.rank_and_dedupe`, `Finding#priority`)
|
|
18
|
+
- Next-steps recommendations (`Suggest.next_steps`, `HeapScope.next_steps`, report sections)
|
|
19
|
+
- `heapscope watch` — monitor with anomaly alerts (RSS / live-slot spikes)
|
|
20
|
+
- Monitor `--alert` / `--rss-alert-bytes` / `--live-alert-slots`
|
|
21
|
+
- Slim snapshot JSON (`Snapshot#save(..., slim: true)`, `heapscope snapshot --slim`)
|
|
22
|
+
- `heapscope doctor --fix` + `--config-out` — write starter `heapscope.yml`
|
|
23
|
+
- `HeapScope.write_config!` / `ConfigLoader.write_starter!`
|
|
24
|
+
- Branding asset path helpers (`LOGO_SVG`, `WORDMARK_SVG`, `logo_svg_inline`)
|
|
25
|
+
- Examples package README with logo/wordmark
|
|
26
|
+
- Rake `docs:codes` task for diagnostic catalog generation
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Version **0.6.0** — cohesive professional product release
|
|
31
|
+
- `Probe` / `Scorecard` extracted from `session.rb` into `scorecard.rb`
|
|
32
|
+
- `ConfigLoader` moved from `noise.rb` into `config.rb`
|
|
33
|
+
- Framework ignore hints owned by `Noise::FRAMEWORK_HINTS` (Suggest reuses them)
|
|
34
|
+
- Analyzer / retention reports apply ranked, deduped findings
|
|
35
|
+
- CLI `suggest` prints next steps + ignore patterns; `--ignores-only` / JSON payload expanded
|
|
36
|
+
- HTML reports prefer packaged logo SVG when available; include Next steps
|
|
37
|
+
- README / docs / roadmap present a single current-product story (not stacked milestones)
|
|
38
|
+
- Supported versions table updated for 0.6.x
|
|
39
|
+
|
|
40
|
+
## [0.5.0] — 2026-08-13
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
|
|
44
|
+
- `.github/FUNDING.yml` with GitHub Sponsors (`theworker02`) and thanks.dev (`u/gh/theworker02`)
|
|
45
|
+
- GitHub Pages site under `site/` + `pages.yml` deploy workflow
|
|
46
|
+
- `HeapScope::Branding` — banner, funding URLs, report footers, `HeapScope.about` / `.branding`
|
|
47
|
+
- `HeapScope::Suggest` + `heapscope suggest` — recommended ignore patterns (never auto-applied)
|
|
48
|
+
- `HeapScope::Pack` + `heapscope pack` — local JSON+HTML+Markdown report bundles
|
|
49
|
+
- CLI `about` command; doctor/help/report footers include funding + docs links
|
|
50
|
+
- Gem metadata `funding_uri` / Pages `documentation_uri`
|
|
51
|
+
- Expansive CLI UX: global `--verbose` / `--quiet` / `--config` / `--json` / `--color` / `--no-color`
|
|
52
|
+
- Per-command help; modular CLI under `lib/heapscope/cli/`
|
|
53
|
+
- CLI commands: `probe`, `measure`, `retention`, `findings`, `scorecard`, `table`, `explain`,
|
|
54
|
+
`open`/`html`, `self-test`, `env`, `completion`, `man`; aliases `export`, `ignore-suggest`
|
|
55
|
+
- `Catalog.explain` / `Catalog.lookup` for diagnostic encyclopedia entries
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- Version **0.5.0**
|
|
60
|
+
- Homepage / source URIs → `https://github.com/theworker02/heapscope`
|
|
61
|
+
- `Catalog` extracted from `session.rb` into `catalog.rb`
|
|
62
|
+
- Suggest reuses `Noise` defaults instead of duplicating framework regexes
|
|
63
|
+
|
|
64
|
+
## [0.4.0] — 2026-08-13
|
|
65
|
+
|
|
66
|
+
Sessions, scorecards, schema validation, diagnostic catalog, GitHub templates.
|
|
67
|
+
|
|
68
|
+
## [0.3.0] — 2026-08-12
|
|
69
|
+
|
|
70
|
+
Aging, globals, closures, fibers, dominators, trends, doctor.
|
|
71
|
+
|
|
72
|
+
## [0.2.0] — 2026-08-12
|
|
73
|
+
|
|
74
|
+
Branding, detectors, richer HTML, eval harness, security/CoC.
|
|
75
|
+
|
|
76
|
+
## [0.1.0] — 2026-08-12
|
|
77
|
+
|
|
78
|
+
Initial retention toolkit.
|
|
79
|
+
|
|
80
|
+
[Unreleased]: https://github.com/theworker02/heapscope/compare/v0.6.0...HEAD
|
|
81
|
+
[0.6.0]: https://github.com/theworker02/heapscope/releases/tag/v0.6.0
|
|
82
|
+
[0.5.0]: https://github.com/theworker02/heapscope/releases/tag/v0.5.0
|
|
83
|
+
[0.4.0]: https://github.com/theworker02/heapscope/releases/tag/v0.4.0
|
|
84
|
+
[0.3.0]: https://github.com/theworker02/heapscope/releases/tag/v0.3.0
|
|
85
|
+
[0.2.0]: https://github.com/theworker02/heapscope/releases/tag/v0.2.0
|
|
86
|
+
[0.1.0]: https://github.com/theworker02/heapscope/releases/tag/v0.1.0
|
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in the
|
|
6
|
+
HeapScope community a harassment-free experience for everyone, regardless of age,
|
|
7
|
+
body size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
+
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
+
nationality, personal appearance, race, caste, color, religion, or sexual identity
|
|
10
|
+
and orientation.
|
|
11
|
+
|
|
12
|
+
## Our Standards
|
|
13
|
+
|
|
14
|
+
Examples of behavior that contributes to a positive environment:
|
|
15
|
+
|
|
16
|
+
- Demonstrating empathy and kindness
|
|
17
|
+
- Being respectful of differing opinions
|
|
18
|
+
- Giving and accepting constructive feedback
|
|
19
|
+
- Focusing on what is best for the community
|
|
20
|
+
|
|
21
|
+
Examples of unacceptable behavior:
|
|
22
|
+
|
|
23
|
+
- Trolling, insulting or derogatory comments, and personal or political attacks
|
|
24
|
+
- Public or private harassment
|
|
25
|
+
- Publishing others’ private information without permission
|
|
26
|
+
- Other conduct inappropriate in a professional setting
|
|
27
|
+
|
|
28
|
+
## Enforcement
|
|
29
|
+
|
|
30
|
+
Project maintainers are responsible for clarifying and enforcing standards.
|
|
31
|
+
Instances of abusive behavior may be reported to the maintainers via GitHub.
|
|
32
|
+
|
|
33
|
+
## Attribution
|
|
34
|
+
|
|
35
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
36
|
+
version 2.1.
|
|
37
|
+
|
|
38
|
+
[homepage]: https://www.contributor-covenant.org
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Contributing to HeapScope
|
|
2
|
+
|
|
3
|
+
Thanks for helping build retention-first Ruby diagnostics.
|
|
4
|
+
|
|
5
|
+
## Principles
|
|
6
|
+
|
|
7
|
+
1. Prefer **retention evidence** over allocation volume alone.
|
|
8
|
+
2. Separate **facts**, **derived metrics**, **hypotheses**, and **suspected causes**.
|
|
9
|
+
3. Never fabricate reachability roots or confidence percentages.
|
|
10
|
+
4. Keep the core dependency set minimal (stdlib + `fiddle`).
|
|
11
|
+
5. Optional integrations must not become required gems.
|
|
12
|
+
6. Degrade safely when a runtime capability is missing.
|
|
13
|
+
7. Treat privacy as a feature: no telemetry, no value dumps by default.
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bundle install
|
|
19
|
+
bundle exec rake test
|
|
20
|
+
bundle exec rubocop
|
|
21
|
+
bundle exec ruby benchmark/run.rb
|
|
22
|
+
bundle exec ruby benchmark/eval_harness.rb
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Project layout
|
|
26
|
+
|
|
27
|
+
| Path | Purpose |
|
|
28
|
+
|------|---------|
|
|
29
|
+
| `lib/heapscope/` | Library |
|
|
30
|
+
| `lib/heapscope/cli/` | Modular CLI |
|
|
31
|
+
| `test/` | Minitest suite |
|
|
32
|
+
| `examples/` | Demonstrations (+ README branding) |
|
|
33
|
+
| `docs/` | Guides + diagnostic encyclopedia |
|
|
34
|
+
| `assets/` | Logos / wordmark (canonical) |
|
|
35
|
+
| `site/` | GitHub Pages marketing site |
|
|
36
|
+
| `benchmark/` | Overhead + eval harness |
|
|
37
|
+
|
|
38
|
+
## Pull requests
|
|
39
|
+
|
|
40
|
+
- Conventional commits: `feat:`, `fix:`, `docs:`, `test:`, `chore:`, `refactor:`
|
|
41
|
+
- Update `CHANGELOG.md` under `[Unreleased]`
|
|
42
|
+
- Add `docs/changelogs/X.Y.Z.md` when cutting a release
|
|
43
|
+
- Add/adjust diagnostic docs when introducing finding codes
|
|
44
|
+
- Keep PRs focused
|
|
45
|
+
|
|
46
|
+
## Branding
|
|
47
|
+
|
|
48
|
+
Logo sources:
|
|
49
|
+
|
|
50
|
+
- `assets/logo.svg` — app mark
|
|
51
|
+
- `assets/wordmark.svg` — horizontal lockup
|
|
52
|
+
- `assets/heapscope-logo.png` — raster
|
|
53
|
+
|
|
54
|
+
Please don’t introduce purple-glow AI-slop aesthetics; HeapScope’s look is teal/slate, calm, technical.
|
|
55
|
+
|
|
56
|
+
## Code of conduct
|
|
57
|
+
|
|
58
|
+
See [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md).
|
|
59
|
+
|
|
60
|
+
## Security
|
|
61
|
+
|
|
62
|
+
See [`SECURITY.md`](SECURITY.md).
|
|
63
|
+
|
|
64
|
+
## Release
|
|
65
|
+
|
|
66
|
+
Semantic versioning. Standard Bundler flow:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# bump lib/heapscope/version.rb
|
|
70
|
+
# update CHANGELOG.md + docs/changelogs/
|
|
71
|
+
bundle exec rake release
|
|
72
|
+
```
|
data/Gemfile
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 HeapScope Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/wordmark.svg" alt="HeapScope" width="520"/>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<strong>Ruby object retention, heap growth, and memory leak diagnostics</strong><br/>
|
|
7
|
+
<em>Not just “how much memory” — <strong>why</strong> the process is keeping it.</em>
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<a href="https://rubygems.org/gems/heapscope"><img alt="Gem version" src="https://img.shields.io/gem/v/heapscope?color=0f766e&style=for-the-badge"></a>
|
|
12
|
+
<a href="https://github.com/theworker02/heapscope/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/theworker02/heapscope/ci.yml?branch=main&style=for-the-badge&label=CI"></a>
|
|
13
|
+
<a href="LICENSE"><img alt="MIT" src="https://img.shields.io/badge/license-MIT-334155?style=for-the-badge"></a>
|
|
14
|
+
<a href="#ruby-compatibility"><img alt="Ruby" src="https://img.shields.io/badge/ruby-%3E%3D%203.1-CC342D?style=for-the-badge&logo=ruby&logoColor=white"></a>
|
|
15
|
+
<a href="#privacy"><img alt="Privacy" src="https://img.shields.io/badge/privacy-local%20only-0f766e&style=for-the-badge"></a>
|
|
16
|
+
<a href="#no-saas"><img alt="No SaaS" src="https://img.shields.io/badge/SaaS-none-64748b&style=for-the-badge"></a>
|
|
17
|
+
<a href="https://thanks.dev/u/gh/theworker02"><img alt="thanks.dev" src="https://img.shields.io/badge/thanks.dev-theworker02-0f766e&style=for-the-badge"></a>
|
|
18
|
+
<a href="https://theworker02.github.io/heapscope/"><img alt="Docs" src="https://img.shields.io/badge/docs-Pages-0f766e?style=for-the-badge"></a>
|
|
19
|
+
<a href="CHANGELOG.md"><img alt="Changelog" src="https://img.shields.io/badge/changelog-0.6.0-0f766e&style=for-the-badge"></a>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
<p align="center">
|
|
23
|
+
<a href="https://theworker02.github.io/heapscope/">Website</a> ·
|
|
24
|
+
<a href="docs/index.md">Docs hub</a> ·
|
|
25
|
+
<a href="#quick-start">Quick start</a> ·
|
|
26
|
+
<a href="docs/cli.md">CLI</a> ·
|
|
27
|
+
<a href="#privacy">Privacy</a> ·
|
|
28
|
+
<a href="https://thanks.dev/u/gh/theworker02">Sponsor</a> ·
|
|
29
|
+
<a href="CHANGELOG.md">Changelog</a>
|
|
30
|
+
</p>
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## What HeapScope is
|
|
35
|
+
|
|
36
|
+
**HeapScope** is a local, evidence-driven Ruby gem for diagnosing **object retention**,
|
|
37
|
+
**abnormal heap growth**, **allocation hot spots**, **long-lived objects**, and
|
|
38
|
+
**leak-shaped patterns** in long-running processes.
|
|
39
|
+
|
|
40
|
+
It is designed for Rails, Puma, Sidekiq, background jobs, CLIs, and CI — distinguishing
|
|
41
|
+
allocation pressure from retention, and intentional caches from suspicious growth.
|
|
42
|
+
|
|
43
|
+
> **No SaaS.** No uploads. No telemetry. Everything runs in-process on your machine.
|
|
44
|
+
|
|
45
|
+
Release notes: [`docs/changelogs/0.6.0.md`](docs/changelogs/0.6.0.md)
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Why HeapScope
|
|
50
|
+
|
|
51
|
+
Traditional tools often answer: *“How much memory is the process using?”*
|
|
52
|
+
|
|
53
|
+
HeapScope answers: *“Why is this Ruby process retaining more memory than expected?”*
|
|
54
|
+
|
|
55
|
+
Every finding separates **observed facts**, **derived behavior**, **hypothesis**, and
|
|
56
|
+
**suspected cause** — and never claims `"Memory leak confirmed"` without strong evidence.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
# Gemfile
|
|
64
|
+
gem "heapscope", "~> 0.6"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
bundle add heapscope
|
|
69
|
+
# or
|
|
70
|
+
gem install heapscope
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Ruby compatibility
|
|
76
|
+
|
|
77
|
+
| Engine | Status |
|
|
78
|
+
|--------|--------|
|
|
79
|
+
| **MRI Ruby ≥ 3.1** | Primary target — full ObjectSpace / allocation tracing / memsize |
|
|
80
|
+
| JRuby | Adapter present; capabilities degrade safely |
|
|
81
|
+
| TruffleRuby | Adapter present; capabilities degrade safely |
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
puts HeapScope.capabilities
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Quick start
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
require "heapscope"
|
|
93
|
+
|
|
94
|
+
report = HeapScope.measure(force_gc: true) { perform_work }
|
|
95
|
+
puts report
|
|
96
|
+
report.save("report.json")
|
|
97
|
+
report.save_html("report.html")
|
|
98
|
+
|
|
99
|
+
# Ranked follow-ups
|
|
100
|
+
puts HeapScope.next_steps(report)
|
|
101
|
+
|
|
102
|
+
# CI gate
|
|
103
|
+
budget = HeapScope.budget_preset(:ci_strict)
|
|
104
|
+
HeapScope.check(budget: budget) { perform_work }
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
heapscope doctor --fix # write starter heapscope.yml
|
|
109
|
+
heapscope snapshot --slim -o before.json
|
|
110
|
+
heapscope diff before.json after.json --html report.html --fail-on-medium
|
|
111
|
+
heapscope suggest report.json # next steps + ignore hints
|
|
112
|
+
heapscope watch --duration 120 -o watch.json
|
|
113
|
+
heapscope about
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Full CLI: [docs/cli.md](docs/cli.md)
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Product surface
|
|
121
|
+
|
|
122
|
+
| Capability | How |
|
|
123
|
+
|------------|-----|
|
|
124
|
+
| Snapshots (lightweight / standard / deep / slim JSON) | `HeapScope.snapshot` / `heapscope snapshot` |
|
|
125
|
+
| Diff & compare | `HeapScope.compare` / `heapscope diff` |
|
|
126
|
+
| Block measure & multi-cycle retention | `measure` / `retention_test` |
|
|
127
|
+
| Ranked findings + next steps | analyzer + `Suggest` |
|
|
128
|
+
| Budget presets | `Budget.preset(:rails_request\|:sidekiq_job\|:ci_strict)` |
|
|
129
|
+
| Sessions & scorecards | `HeapScope.session` / `probe` |
|
|
130
|
+
| Watch / monitor with alerts | `heapscope watch` / `Monitor` |
|
|
131
|
+
| Report packs | `HeapScope.pack` / `heapscope pack` |
|
|
132
|
+
| Baselines & schema validation | `baseline` / `validate` |
|
|
133
|
+
| Rails / Rack / Sidekiq / RSpec / Minitest | optional require paths |
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Concepts
|
|
138
|
+
|
|
139
|
+
### Ruby heap vs RSS
|
|
140
|
+
|
|
141
|
+
HeapScope records **both** Ruby heap populations and process RSS.
|
|
142
|
+
**RSS growth ≠ Ruby object leak** — native extensions, allocators, mmap, and CoW matter.
|
|
143
|
+
|
|
144
|
+
### Allocated vs retained
|
|
145
|
+
|
|
146
|
+
```text
|
|
147
|
+
100,000 allocated + 99,500 freed → churn
|
|
148
|
+
100,000 allocated + 20,000 live → retention
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Forced GC
|
|
152
|
+
|
|
153
|
+
Opt-in only (`force_gc: true`). Never enabled by surprise; refuse deep walks in `production_safe`.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Budgets & CI
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
HeapScope::Budget.preset(:rails_request)
|
|
161
|
+
HeapScope::Budget.preset(:sidekiq_job)
|
|
162
|
+
HeapScope::Budget.preset(:ci_strict)
|
|
163
|
+
|
|
164
|
+
# or hand-tuned
|
|
165
|
+
HeapScope::Budget.new(
|
|
166
|
+
max_retained_objects: 1_000,
|
|
167
|
+
max_rss_growth: 30 * 1024 * 1024,
|
|
168
|
+
severity_threshold: :high
|
|
169
|
+
)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
heapscope baseline create report.json -o baseline.json
|
|
174
|
+
heapscope compare baseline.json current.json --threshold 0.5
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Guide: [docs/guides/ci-budgets.md](docs/guides/ci-budgets.md)
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Monitoring
|
|
182
|
+
|
|
183
|
+
```ruby
|
|
184
|
+
monitor = HeapScope::Monitor.start(interval: 10, mode: :lightweight, alert: true)
|
|
185
|
+
# ...
|
|
186
|
+
report = monitor.stop
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
heapscope watch --interval 10 --duration 600 -o monitor.json
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Alerts fire on RSS / live-slot spikes between samples (thresholds configurable).
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Diagnostics
|
|
198
|
+
|
|
199
|
+
| Code | Name |
|
|
200
|
+
|------|------|
|
|
201
|
+
| HS001 | persistent_class_growth |
|
|
202
|
+
| HS002 | high_retention_ratio |
|
|
203
|
+
| HS003 | thread_local_retention |
|
|
204
|
+
| HS004 | unbounded_collection |
|
|
205
|
+
| HS005 | callback_accumulation |
|
|
206
|
+
| HS006 | closure_retention |
|
|
207
|
+
| HS007 | poor_gc_recovery |
|
|
208
|
+
| HS008 | baseline_regression |
|
|
209
|
+
| HS009 | high_allocation_pressure |
|
|
210
|
+
| HS010 | native_memory_mismatch |
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
heapscope codes
|
|
214
|
+
heapscope explain HS001
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Encyclopedia: [`docs/diagnostics/`](docs/diagnostics/)
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Configuration
|
|
222
|
+
|
|
223
|
+
```ruby
|
|
224
|
+
HeapScope.configure do |config|
|
|
225
|
+
config.mode = :standard # lightweight | standard | deep | production_safe | development
|
|
226
|
+
config.track_allocations = false
|
|
227
|
+
config.ignore_patterns << /Zeitwerk/
|
|
228
|
+
config.inspect_values = false # privacy: off by default
|
|
229
|
+
end
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
heapscope doctor --fix --config-out heapscope.yml
|
|
234
|
+
heapscope --config heapscope.yml doctor
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Branding / funding URLs live in `HeapScope::Branding` (single source of truth).
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Reports
|
|
242
|
+
|
|
243
|
+
Text, Markdown, versioned JSON, and static HTML — all offline. HTML uses the same brand mark as the site and README. Reports include **NEXT STEPS** when findings warrant follow-up.
|
|
244
|
+
|
|
245
|
+
```ruby
|
|
246
|
+
HeapScope.pack(report, "./pack")
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Integrations
|
|
252
|
+
|
|
253
|
+
```ruby
|
|
254
|
+
require "heapscope/middleware" # Rack sample_rate
|
|
255
|
+
require "heapscope/rails" # request_retention helpers
|
|
256
|
+
require "heapscope/sidekiq_middleware"
|
|
257
|
+
require "heapscope/minitest"
|
|
258
|
+
require "heapscope/rspec"
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Core gem requires **stdlib only** (plus `fiddle` when available for Windows RSS).
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Privacy
|
|
266
|
+
|
|
267
|
+
<a id="privacy"></a>
|
|
268
|
+
<a id="no-saas"></a>
|
|
269
|
+
|
|
270
|
+
By default HeapScope:
|
|
271
|
+
|
|
272
|
+
- makes **no network calls**
|
|
273
|
+
- sends **no telemetry / analytics**
|
|
274
|
+
- does **not** serialize object values
|
|
275
|
+
- does **not** dump ENV, tokens, cookies, or request bodies
|
|
276
|
+
|
|
277
|
+
See [`SECURITY.md`](SECURITY.md).
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Examples
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
bundle exec ruby examples/healthy_churn.rb
|
|
285
|
+
bundle exec ruby examples/import_leak.rb
|
|
286
|
+
bundle exec ruby examples/showcase.rb
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
See [`examples/README.md`](examples/README.md).
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Architecture
|
|
294
|
+
|
|
295
|
+
```text
|
|
296
|
+
lib/heapscope.rb Public API
|
|
297
|
+
lib/heapscope/runtime/* Engine adapters + RSS
|
|
298
|
+
lib/heapscope/collector.rb Snapshot capture
|
|
299
|
+
lib/heapscope/diff.rb Population diffs
|
|
300
|
+
lib/heapscope/analyzer.rb Findings & suspects
|
|
301
|
+
lib/heapscope/findings.rb Codes + ranking/dedupe
|
|
302
|
+
lib/heapscope/suggest.rb Next steps + ignore hints
|
|
303
|
+
lib/heapscope/budget.rb CI budgets + presets
|
|
304
|
+
lib/heapscope/scorecard.rb Probe + executive scorecard
|
|
305
|
+
lib/heapscope/session.rb Named artifact sessions
|
|
306
|
+
lib/heapscope/report/* Text / HTML / Markdown
|
|
307
|
+
lib/heapscope/cli/ Modular CLI commands
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Roadmap
|
|
313
|
+
|
|
314
|
+
See [`docs/ROADMAP.md`](docs/ROADMAP.md). Current focus: deeper precision and optional CI marketplace packaging — not parallel product surfaces.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Contributing
|
|
319
|
+
|
|
320
|
+
See [`CONTRIBUTING.md`](CONTRIBUTING.md) and [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md).
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
bundle install
|
|
324
|
+
bundle exec rake test
|
|
325
|
+
bundle exec rubocop
|
|
326
|
+
gem build heapscope.gemspec
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## License
|
|
332
|
+
|
|
333
|
+
MIT © [@theworker02](https://github.com/theworker02) — see [`LICENSE`](LICENSE).
|
|
334
|
+
|
|
335
|
+
Sponsor: [GitHub Sponsors](https://github.com/sponsors/theworker02) · [thanks.dev/u/gh/theworker02](https://thanks.dev/u/gh/theworker02)
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
<p align="center">
|
|
340
|
+
<img src="assets/logo.svg" alt="" width="72"/><br/>
|
|
341
|
+
<sub>When <code>top</code> says memory is growing but profiling won’t say why — reach for HeapScope.</sub>
|
|
342
|
+
</p>
|