Aquí hi ha 5 versions de wtop, des de 13 de juny del 2026 fins a l’actual, 1.3.1. Cadascuna enllaça amb els instal·ladors del mateix desenvolupador.
| Versió | Data | Mida | Fitxers |
|---|---|---|---|
| 1.3.1Darrera versió | 19 d’octubre del 2026 | — | 64 bits PortableARM64 Portable Notes de la versióFixed: wtop used ~5.7 GB of RAM from the moment it launched
v1.3.0 showed itself at the top of its own process list: about 5.7 GB working set and 9.8 GB private memory (around 18% of RAM on a 32 GB machine), reached within a second of launch. Upgrading is recommended for every v1.3.0 user. The bug affects all machines.
What was happening
This was not a leak. Memory jumped once at startup and then stayed flat. GODEBUG=gctrace=1 showed the Go heap doubling every ~30 ms until about 7.3 GB was live. A heap profile put 9.5 GB (99.9% of all allocations) in the per-core CPU collector.
wtop reads per-core CPU times with NtQuerySystemInformation(SystemProcessorPerformanceInformation). This call rejects any buffer whose length is not a whole number of 48-byte per-core entries, returning STATUS_INFO_LENGTH_MISMATCH. The buffer was sized NumCPU × 48 + 4096, with the extra 4 KB meant as headroom. That headroom made every call fail. The retry loop responded by doubling the buffer, but 4096 · 2ᵏ is never divisible by 3, so no amount of doubling could produce a valid length.
The loop stopped only by coincidence. The length parameter is a 32-bit ULONG, and on a 16-thread machine, after 20 doublings (4864 · 2²⁰ ≈ 5.1 GB), the value truncated to 0x30000000, which happens to be a multiple of 48. The call then succeeded, and the ~5 GB buffer was kept for reuse for the rest of the session. How far it grew before that coincidence depended on the machine's core count.
The fix (#45)
- The CPU buffer is now sized to exactly one entry per logical processor and only ever grows in whole entries. The loop uses the kernel's reported length when it is a whole number of entries, and is capped at 4,096 cores (~192 KB). Past the cap it returns an error instead of allocating.
- The process-table query had the same uncapped doubling loop. It wasn't misbehaving, since that info class has no entry-size rule, but it now has a 64 MB cap so it can't run away either.
- A new regression test (TestCPUBufferStaysBounded) checks that the buffer stays a whole number of entries, stays under the cap, and still covers every core.
Before / after
──────┬─────────────┬────────┬────────────
│Working set │Private │Live Go heap
──────┼─────────────┼────────┼────────────
v1.3.0│5,704 MB │9,816 MB│7,297 MB
──────┼─────────────┼────────┼────────────
v1.3.1│~65 MB (flat)│~63 MB │1–3 MB
──────┴─────────────┴────────┴────────────
Allocations for the first CPU collection fell from 9.51 GB to 4.3 MB. Most of the remaining footprint is native code (GPU driver and performance-counter DLLs), not wtop's own heap.
What's Changed
- fix(verify): isolate downloads per tag and add -Clean by @michaelsanford in #43
- Remove Snyk scanning by @michaelsanford in #44
- fix(collector): stop the CPU buffer growing to ~5 GB at startup by @michaelsanford in #45
Full Changelog: v1.3.0...v1.3.1 |
| 1.3.0 | 9 de setembre del 2026 | — | 64 bits PortableARM64 Portable Notes de la versióFour new sensors, web search on the selected process, and a substantial rewrite of the collection
path onto native NT and Win32 calls. Two new keys: i and ?.
Disk, battery, uptime and effective CPU clock
A Disk panel shows per-physical-disk read/write throughput and a busy percentage, taken from PDH
rather than gopsutil — disk.IOCounters reports cumulative counters keyed by volume, so two
volumes on one spindle double-count, and it has no equivalent of % Idle Time. Capacity bars are
drawn for each fixed volume.
Volumes are reconciled against the real physical disks before being shown. DRIVE_FIXED is not
proof a volume exists: Google Drive's mount reports fixed, reports NTFS, and mirrors the system
drive's exact byte size. Only volumes that some \PhysicalDisk instance claims survive, so no
phantom copy of C: appears.
Per-process disk I/O arrives as DISK R and DISK W columns, sortable like any other. These
cost no additional syscalls — ReadTransferCount and WriteTransferCount were already sitting
unused in the SYSTEM_PROCESS_INFORMATION buffer the process collector parses. Validated by writing
64 MiB and confirming ~58 MiB attributed within the sampling window.
The status bar gains battery and power state (AC/battery, charge, time remaining, battery-saver),
system uptime, and the effective CPU clock including turbo, read from
% Processor Performance against the nominal base clock. A boosting CPU reads above 100% of base, so
the figure is deliberately not clamped. On a desktop with no battery the power field is omitted
rather than left blank.
Web search on the selected process
? looks the selected process up in your default browser, on whatever search engine that browser is
actually set to — read out of the browser's own profile, so a Chrome or Edge switched to DuckDuckGo
searches DuckDuckGo rather than Google or Bing.
A child process is searched together with its nearest differently-named ancestor, so a bare
msedgewebview2.exe becomes GoogleDriveFS.exe msedgewebview2.exe and you find out which
application is actually hosting it.
Two things this required, for anyone reading the code: a Chromium browser cannot be told to run a
search from the command line — everything after -- goes through Chromium's URL fixup, so
msedge.exe -- "a.exe b.exe" silently navigates to http://a.exe%20b.exe/. There is no flag for it.
Gecko browsers do have one (--search), which is why the two families are driven differently. The
Chromium engine is therefore read from Preferences → default_search_provider_data; Edge leaves
that key null and keeps a readable copy in mirrored_template_url_data, so both are tried.
wtop itself opens no connection. The request is made by your browser, at your keypress, and is
subject to that search engine's privacy policy. PRIVACY.md
now says so explicitly.
Collection path rewritten onto native calls
Process collection is now a single NtQuerySystemInformation(SystemProcessInformation) call,
replacing 1,500+ Win32 syscalls and 16 worker goroutines per tick. Network collection goes directly
to GetIfTable2. NVIDIA GPUs are read in-process through nvml.dll, falling back to DXGI, PDH and
nvidia-smi — no subprocess on the common path.
─────────────────────┬─────────────────────────────────┬─────────────────────────────
Metric │Before │After
─────────────────────┼─────────────────────────────────┼─────────────────────────────
Process collection │48.8 ms / 1,026 KB / 6,717 allocs│10.8 ms / 110 KB / 525 allocs
─────────────────────┼─────────────────────────────────┼─────────────────────────────
CPU collection │111 µs / 7.9 KB / 42 allocs │1.04 µs / 336 B / 3 allocs
─────────────────────┼─────────────────────────────────┼─────────────────────────────
Network collection │7.80 ms / 75.0 KB / 54 allocs │1.19 ms / 7.9 KB / 25 allocs
─────────────────────┼─────────────────────────────────┼─────────────────────────────
Full collection cycle│42.4 ms / 1,118 KB / 6,915 allocs│13.5 ms / 122 KB / 565 allocs
─────────────────────┴───────────────── |
| 1.2.1 | 7 d’agost del 2026 | — | 64 bits PortableARM64 Portable Notes de la versióWhat's Changed
- Add qodana CI checks by @qodana-cloud[bot] in #8
- ci: migrate to golangci-lint v2 and verify config in CI by @michaelsanford in #9
- fix(gitignore): anchor build-output patterns so cmd/wtop is tracked by @michaelsanford in #10
- fix(collector): report zero rate when an interface counter resets by @michaelsanford in #11
- fix(ui): correct CPU label sizing, tree cycle drops and memory bar clamping by @michaelsanford in #12
- chore(deps): update gopsutil, x/sys and transitive dependencies by @michaelsanford in #13
- chore(ide): add shared GoLand run configurations by @michaelsanford in #14
- chore(git): check out go.mod and go.sum with LF endings by @michaelsanford in #15
- feat: mark the processes hosting wtop itself by @michaelsanford in #16
- ci(winget): draft the downstream PR until Microsoft's validation passes by @michaelsanford in #17
New Contributors
- @qodana-cloud[bot] made their first contribution in #8
Full Changelog: v1.2.0...v1.2.1 |
| 1.2.0 | 23 de juny del 2026 | — | 64 bits PortableARM64 Portable Notes de la versióv1.2.0 - Pool
Release Notes: v1.1.0 → v1.2.0
This release introduces significant performance optimizations (including parallel process collection and native GPU engine monitoring), layout and input enhancements, supply chain signature verification, and a streamlined Windows-native CI pipeline running on Go 1.26.4.
🚀 Performance Improvements
- Parallel Process Collection: Consolidated and parallelized process metadata querying using a concurrent worker pool (16 workers). This yields a ~22% speedup at idle, reducing average cycle collection time from ~31.3ms to ~24.4ms.
- Native GPU Engine Queries: Replaced the expensive PowerShell-based subprocess querying (Get-Counter) with a native Go implementation using Windows DXGI (for VRAM capacities/utilization) and PDH counter APIs (for 3D engine utilization). This reduces non-NVIDIA GPU query time from several seconds down to < 1ms, eliminating CPU spikes during collection.
🎨 User Experience & UI Enhancements
- Responsive Top Panel Layout: Added a dynamic 3-column top panel (Memory | GPU | Network) that automatically displays on terminals wider than 110 characters. On narrower screens, it gracefully falls back to a 2-column layout (Memory | GPU) to preserve space.
- Full Table Mouse & Navigation Support: Forwarded all terminal events to bubbles/table, enabling native mouse selection (click-to-focus), scroll-wheel list scrolling, and standard page key navigation (PageUp, PageDown, Home, End).
- StatusBar Wrap Prevention: Implemented rune-safe truncation for status bar elements (hostname, version, and legends) to prevent ugly wrapping on narrow screen widths.
- Scroll Selection Fix: Resolved alignment offsets in the selection highlights when scrolling long lists.
🔒 Supply Chain Security & Verification
- SBOM Signing & Attestation: Added a CycloneDX Software Bill of Materials (SBOM) generation step in the release workflow. The SBOM is attested and signed using keyless Cosign (via GitHub OIDC).
- Cryptographic Verification Script: Introduced the Verify-Release.ps1 PowerShell script, allowing users to verify the cryptographic signatures, SHA256 hashes, and OIDC attestations of downloaded binaries and SBOMs.
- WinGet Manifest Verification: Added a -WinGet validation flag to Verify-Release.ps1 to automatically inspect and verify WinGet manifests against GitHub releases.
🛠️ CI/CD & Pipeline Tooling
- Native Windows CI runner: Migrated the Build, Vet, Lint, and Test workflows from a Linux runner to execute natively on windows-latest. This ensures all Windows-specific code paths (DXGI/PDH) are compiled, linted, and tested under the expected OS target.
- Winget now automatically updates on release publish.
- Go 1.26.4 Toolchain Support: Configured the runner to use Go 1.26.4. To support this version, golangci-lint is now compiled from source on-the-fly inside the runner using Go 1.26.4.
- Normalized Line Endings: Added a .gitattributes configuration (*.go text eol=lf) to enforce LF line endings, preventing gofmt carriage return formatting failures on Windows runners and environments.
Full Changelog: v1.1.0...v1.2.0 |
| 1.1.0.0 | 13 de juny del 2026 | — | 64 bits PortableARM64 Portable |