Repository navigation
feat: add --redact/--compress/--open for heap-dump, jstall bundle, and JRE-only fallback - #56
Open
parttimenerd wants to merge 71 commits into
Open
parttimenerd wants to merge 71 commits into
parttimenerd wants to merge 71 commits into
Conversation
- Embeds hprof-redact binaries (linux/amd64, linux/arm64, darwin/arm64, windows/amd64) via //go:embed; extracted to ~/.cache/cf-java-plugin/ on first use (SHA8-keyed, reused on subsequent runs). - --compress: passes gz=1 to jmap (JDK 17+) to compress the dump on the remote container before transfer, saving bandwidth. jvmmon path falls back to post-creation gzip on the container. Output is .hprof.gz. - --redact: pipes the downloaded dump through hprof-redact (lean mode: zeros primitive arrays only). Output is -redacted.hprof or -redacted.hprof.gz when --compress is also set. - --redact-complete: like --redact but zeros all primitive values (instance scalar fields + CLASS_DUMP statics) via two-pass redaction. - --compress and --redact can be combined: compress on remote to reduce transfer, redact locally (hprof-redact reads .hprof.gz natively). - Adds FindHeapDumpGzFile to utils for locating *.hprof.gz on the container (jvmmon+compress path). - Extracts osWindows and cmdHeapDump constants to satisfy goconst.
- hprof-analyzer release.yml: add aarch64-pc-windows-msvc target on windows-11-arm runner; compile-all includes GOOS=windows GOARCH=arm64 - redact.go: embed dist/hprof-redact-windows-arm64.exe (stub until v0.3.1 ships); add windows/arm64 case; guard against 0-byte stubs - cfutils.go: add CopyOverCatGunzip (io.Pipe + compress/gzip for transparent streaming decompression) and ProbeRemoteFileGzip (checks gzip magic bytes 1f8b via SSH) - cf_cli_java_plugin.go: heap-dump SSHCommand now uses shell-level gz probe (jmap -h | grep gz) so jmap auto-uses gz=1 on JDK 17+; Go post-command probes the remote file magic bytes to decide: - remote gz + no --compress → CopyOverCatGunzip, save as .hprof - remote gz + --compress → CopyOverCat, save as .hprof.gz - not gz + --compress → warn JDK 17+ required, save uncompressed Removes @JMAP_GZ Go expansion (replaced by shell probe); keeps @COMPRESS_FLAG for jvmmon path
- appInstanceIndexSet: simonleung8/flags IsSet() returns true for any registered flag whose non-zero default was set at init time, making it impossible to distinguish user-provided from default. Compare against known default (-1) instead. - CheckRequiredTools wrapped in !options.DryRun guard so dry-run works without CF login or SSH access. - var err declaration hoisted before GenerateFiles block (needed after CheckRequiredTools was scoped inside the if block).
…-dump - Feature list in intro - Examples: --redact, --redact-complete, --compress, combined usage - New "Heap Dump Privacy" subsection: redaction modes table, what is preserved, file naming, supported platforms - New "Compressed Transfer" subsection: JDK 17+ requirement, fallback behaviour, transparent gz transfer without --compress - CHANGELOG [Unreleased]: all four new behaviours
- Use context.WithTimeout(context.Background(), 5s) for srv.Shutdown instead of the request context (which may already be canceled); suppress contextcheck lint with explanation since this is intentional - Add empty title arg to Windows `cmd /c start` to avoid URL being interpreted as the window title - Replace non-blocking done-channel check in test with a 2s timeout select to avoid a false-pass race condition
…eakage and restrict access to single file
… in serveFileOnce
…s only affects local file format
golangci-lint typecheck fails when the dist/ hprof-redact binaries are missing (go:embed pattern not satisfied). Add a download step mirroring build.py, placed after the existing jstall download and before lint.
The previous attempt tried to curl the binaries directly, but they are packed inside tar.gz/zip archives. Add --deps-only flag to build.py to run only the download+extraction step (no go build), and use it in the PR validation workflow before golangci-lint.
dbriemann
reviewed
Sep 18, 2026
ansteiner
requested changes
Sep 18, 2026
cf install-plugin rejects binaries without the .exe extension on Windows, causing "temp/...exe doesn't exist" errors. build.py now outputs cf-cli-java-plugin-windows-amd64.exe, README and release body URLs updated accordingly, and the CI smoke-test asserts the extension before installing.
Member
Author
|
There is now Windows support (tested with amd64 Cygwin and PowerShell) for the jstall-related features. I tested these features manually. |
- WorkloadApp: read PORT env var instead of hardcoding 8080 - jdk21/jdk25 manifests: bump memory to 512M and cap ReservedCodeCacheSize=64M so the memory calculator fits within limits - Add run-on-all.sh: run any cf java command across all (or filtered) apps in parallel, group output by similarity, --keep-tmp support
Exit code 10 from jstall means "ran successfully but found a warning condition" (e.g. JVM older than ~4 months). The plugin was surfacing this as an error: "jstall execution failed: exit status 10" Treat it as success — the warning is already printed to stdout by jstall. Also adds JSTALL_LOCAL=/path/to/jar make variable so developers can test against a locally built jstall JAR without publishing to GitHub: JSTALL_LOCAL=../jstall/target/jstall.jar make install
… JDK 11 Two causes of the crash: 1. Memory: JDK 11 default ReservedCodeCacheSize=240M exhausted 256M container. Bump to 512M and cap ReservedCodeCacheSize=64M (same fix as jdk21/jdk25). 2. The sap_java_buildpack_jakarta injects a logging-level-change-agent JAR compiled for Java 17+ (class file version 61.0), which fails with UnsupportedClassVersionError on Java 11 (max class version 55.0). Exclude LoggingLevelChangeAgent from the frameworks list via JBP_CONFIG_COMPONENTS.
…-all.sh arg order jdk11 and jdk17 were crashing at startup: the CF java-buildpack memory calculator defaults ReservedCodeCacheSize=240M on JDK 11/17, exceeding the 256M limit. Bump to 512M and cap to 64M (same fix applied earlier to jdk21/jdk25/sapmachine11). Also fix run-on-all.sh: the CF CLI syntax is 'cf java SUBCOMMAND APP [options]', but the script was calling 'cf java APP SUBCOMMAND'. Insert the app name after CF_ARGS[0] (the subcommand) so e.g. './run-on-all.sh -- asprof-status' works.
…mp jstall to 0.8.0
Add AttachSocketNCHelper shell function (nc_jcmd) that communicates with
the JVM via HotSpot attach socket, enabling diagnostics on JRE-only
containers where jcmd/jstack are absent.
- thread-dump: try jstack → jvmmon → nc_jcmd Thread.print (with clear
error if all three are absent)
- vm-info: try jcmd VM.info → nc_jcmd VM.info (removed RequiredTools hard-fail)
- vm-version: try jcmd VM.version → nc_jcmd VM.version
The nc_jcmd helper handles attach-socket creation on demand (SIGQUIT
handshake) and resolves macOS $TMPDIR trailing-slash via ${TMPDIR%/}.
Requires netcat-openbsd or nmap-ncat on the container.
heap-dump: fall back to GC.heap_dump via nc_jcmd when jmap/jvmmon absent. Verifies file exists after write; clear error if nc also unavailable. jcmd: remove RequiredTools hard-fail; try jcmd binary first, fall back to nc_jcmd for arbitrary jcmd commands on JRE-only containers. @Args is split via "set -- @Args" so nc_jcmd receives command + args as separate positional parameters. nc_jcmd: propagate non-zero return codes from attach protocol as errors instead of silently returning empty output. Add nc_available() helper to avoid repeating the nc -h probe inline.
HotSpot's attach protocol expects the entire command line (command name + space-separated args) as a single NUL-terminated field: printf '1\0jcmd\0GC.heap_dump /tmp/out.hprof\0\0\0' It then splits that field on spaces to extract command and arguments. The previous format sent command and each arg as separate NUL-delimited fields, which the JVM ignored — causing "filename is mandatory" for GC.heap_dump and silent no-ops for any multi-arg command. Simplify nc_jcmd to: nc_jcmd PID CMDLINE... where "$*" after shift builds the full command line string naturally.
…ly jstall note, specific SSH error detail
- fix: decompress gzip stream before passing to hprof-redact when remote jmap used gz=1 (JDK 17+); previously hprof-redact received raw gzip bytes and would corrupt or reject the file (fixes --redact on JDK 17+ containers) - fix: single-quote app name in jstall --ssh fallback path to handle app names with spaces or shell metacharacters - fix: remove WriteTimeout from one-shot heap dump HTTP server; a 30s deadline killed large (>3GB) dump transfers to the browser mid-stream - warn: print a warning when --redact/--redact-complete/--compress/--open/ --open-url are passed to non-heap-dump commands (previously silently ignored) - refactor: replace inline nc -U checks in thread-dump/vm-info/vm-version shell snippets with nc_available() for consistency with heap-dump/jcmd
- jvmmon path: write setHeapDumpOnDemandPath.sh into @fspath (not the current directory) and remove it after use; previously the script was left behind on the container after every jvmmon heap dump - redact: remove pre-write-access check (create+remove before hprof-redact runs); MkdirAll already covers the directory case and any write failure will surface naturally from hprof-redact itself without the TOCTOU window
…uffer The 192 KB file fit entirely in the kernel TCP send buffer, so io.Copy completed successfully even after the client disconnected — spuriously triggering server shutdown and failing the test. Also updates dist/jstall-minimal.jar with the vm-vitals two-section fix (recent samples + extremes capped at max(10, topN)).
Update dist/jstall-minimal.jar to v0.8.2 (vm-vitals parser rewrite, Trends + Observations sections, heap-comm/meta-comm duplicate-column fix). Update CHANGELOG to document the new vm-vitals features. Add two unit tests covering the app/command swap-detection logic.
This branch has not been deployed
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
New heap-dump flags
--redact: zeros primitive arrays (byte[],char[], etc.) before saving (lean redaction mode). Uses the bundled hprof-redact binary. Supported on Linux (amd64/arm64), macOS (Apple Silicon), Windows (amd64/arm64).--redact-complete: zeros all primitive arrays and individual primitive fields (complete redaction, maximum privacy). Mutually exclusive with--redact.--compress: saves the dump as.hprof.gzinstead of decompressing after transfer (requires JDK 17+ on container).jmap gz=1to reduce SSH transfer size, decompressing on the fly.--compresskeeps the file compressed locally.--open: after downloading, spins up a temporary single-serve local HTTP server and opens hprof-analyzer in the browser with the dump pre-loaded. Server shuts down automatically after the file is fetched once.--open-url <URL>: override the hprof-analyzer base URL (e.g. a locally running instance). Implies--open.Bundled jstall (JVM inspection)
cf java jstall APP_NAME(or thestatus/record-statusshortcuts). Requires Java 17+ locally.status(thread analysis, metaspace, GC, compiler queue),record-status(repeated sampling),flame-graph,heap-info. All jstall subcommands available via--args.JRE-only container support
heap-dump,thread-dump,vm-info,vm-version,jcmd, and all jstall-based commands now work on containers without JDK tools (jmap,jstack,jcmd) by falling back to the HotSpot attach socket vianc -U.netcat-openbsdornmap-ncaton the container. Supports JDK 9–25 on Linux and macOS.Windows fixes
cf install-plugin <URL>now works on Windows: Windows binary released ascf-cli-java-plugin-windows-amd64.exe(with.exe). CF CLI previously rejected the temp file without the extension.cf java statusand other jstall-based commands now work on Windows: JVM propertyjdk.lang.Process.allowAmbiguousCommands=falseensures embedded quotes in remote shell payloads are correctly escaped viaProcessBuilder.UX improvements
cf java my-app heap-dump): suggests the corrected command if the first argument matches a known app.Maintenance
upload-artifactto v7,download-artifactto v8.darwin/amd64dropped.Security notes (
--open)127.0.0.1only (loopback, not reachable from network)io.Copydirectly (nothttp.ServeFile) to prevent path traversal