Repository navigation
v0.5.0
Added
- Automatically recover profiling data after a BDS crash or forced process
termination. During execution and allocation profiling sessions (including the
background profiler), samples are written to a crash-safe recovery journal under
plugins/spark/profiles/recovery/. On the next startup, an unclean supported
session is replayed into a.sparkprofilefile underplugins/spark/profiles/.
The recovery directory is cleaned after a successful save. Both Windows and
Linux are supported. - Detect main-thread stalls with an independent watchdog thread that monitors a
monotonic heartbeat updated every tick. When the server thread stops ticking for
more than 5 seconds, stall begin/end events are recorded in the recovery journal
so that recovered profiles retain stall evidence. The watchdog never calls
Endstone APIs and never stops the profiler. - Add persistent Spark configuration (
config.tomlin the plugin data directory)
with configurableviewerUrl,bytebinUrl,bytesocksHost, background
profiler settings, and response broadcast toggle. Invalid config is reported and
preserved byte-for-byte while that startup uses safe defaults. Trusted viewer
keys are stored separately intrusted-viewers.json. - Add automatic background profiler that starts on plugin enable and runs
indefinitely at a configurable interval (default 10ms). A foreground
profiler (/spark profiler start) pauses the background session; stopping
the foreground profiler via/spark profiler stoprestarts the background.
Timeout and cancel do not restart the background profiler. - Add
/spark pingcommand with rolling 15-minute player ping RTT statistics
(min/median/p95/max),--player <name>lookup, and profile metadata
integration viaPlatformStatistics.Ping. - Add per-interface network throughput monitoring (RX/TX bytes and packets per
second) with 15-minute rolling averages, displayed in/spark healthand
exported in profile metadata viaSystemStatistics.NetInterface. - Add
/spark health --uploadto generate and upload a sparkHealthData
report (platform metadata, system resources, 15-minute time-window history,
and plugin list) to the spark viewer. - Add
/spark activitycommand with persisted activity log (profiler uploads,
saved profiles, and health report uploads), JSON storage with atomic writes
and corruption-safe loading, 60-day URL expiry, and--pagepagination. - Add
--not-combinedand--combine-allprofiler flags to control how sampled
threads appear in the viewer: separate roots per thread, a single merged root,
or the default pool-based grouping. - Preserve exact sampled native PCs and their independently validated function
roots in forward-compatible profile fields for offline normalization metrics. - Resolve Linux vtable labels for shared implementations by parsing Itanium RTTI
inheritance edges, attributing virtual functions to their common ancestor class
instead of leaving the RVA unresolved. - Label unresolved Linux main-executable functions that reference uniquely-owned
BDS debug trace strings ("N _functionName"patterns) with low-confidence
str?hints. - Label unresolved Linux main-executable functions that reference multiple unique
weak string hints with tentativestr?accumulated labels. - Propagate lambda type names from
std::function::__funcvtable thunks to their
single large unresolved call target on Linux with low-confidencecall?hints. - Label unresolved Linux main-executable functions with characteristic code
patterns (Knuth multiplicative hash, 64-bit hash multiplier, atomic
lock cmpxchg/lock xadd, binary search range-halving) with low-confidence
type?behavior hints. - Add
/spark profiler opencommand to open a live, auto-updating spark
viewer during an active execution profile. Connects to the spark WebSocket
relay with RSA2048-signed messages and uploads sampler data every 10 seconds.
The viewer closes automatically when the profiler stops, is cancelled, or
times out. - Add
/spark profiler trust-viewer --id <client id>command to approve a
pending live viewer client. Untrusted clients receive an UNTRUSTED connect
response; trusted clients receive ACCEPTED and gain access to the live
sampler data stream. Trusted public keys are persisted intrusted-viewers.json. - Add command aliases matching upstream spark:
samplerforprofiler,cpu
fortps,healthreportandhtforhealth,activitylogandlogfor
activity,tickmonitoringfortickmonitor. - Add per-command permissions (
spark.profiler,spark.tps,spark.ping,
spark.health,spark.activity,spark.tickmonitor) in addition to the
umbrellaendstone.command.sparkpermission. - Populate per-window entity and chunk counts in profile metadata from rolling
gauge samples, matching upstream spark'sWindowStatisticsfields. - Group world chunk statistics into regions using 8-neighbor connected-component
analysis, matching upstream spark'sWorldStatisticsProvider.groupIntoRegions()
algorithm. - Include safe
server.propertiesvalues (max-players, view-distance,
tick-distance, compression settings, etc.) in profile metadata via
SamplerMetadata.server_configurationsusing a strict whitelist that excludes
level-seed, passcodes, and any credential-like fields. - Pretty-print
activity.jsonwith 2-space indentation for readability.
Fixed
- Keep live viewer updates ordered across interrupted WebSocket sends and report unexpected transport closures.
- Remove expired call-tree nodes and thread roots from continuous background
profiles instead of retaining dead topology beyond the one-hour history window. - Serialize live snapshots with profiler lifecycle transitions so stop, cancel,
timeout, and shutdown cannot resume an ended sampling session. - Contain exceptions at every production worker entry and recover export/viewer
state when dispatch or background processing fails. - Preserve Linux signal-handler synchronization when bounded capture teardown
cannot prove that an active handler has completed. - Include the strict allowlisted server configuration map in health reports and
validate configured HTTP/WebSocket endpoint syntax before applying it. - Send a bounded WebSocket close frame during ordinary local viewer shutdown.
- Bound continuous background profile history to the upstream one-hour retention
window while preserving complete finite foreground profiles. - Keep live viewer metadata capture on the server thread and move relay setup,
compression, health uploads, and profile uploads to owned background workers. - Complete WebSocket receive-worker joins after remote disconnects and make live
viewer open/close/reconnect safe during stop, shutdown, and network failure. - Isolate recovery journal segments by session, validate segment continuity, retain
journals after profile save failures, and synchronize writer teardown with the
stall watchdog. - Validate activity page numbers without narrowing or overflowing pagination
arithmetic, including values aboveINT_MAX. - Preserve invalid user-owned
config.tomlfiles and validate endpoint, enum,
interval, type, and integer bounds before applying configuration. - Validate foreground profiler options before pausing the background profiler and
keep stop, cancel, timeout, and export transitions consistent. - Use non-blocking allocation lifecycle shards under contention and mark exported
allocation data incomplete whenever callback accounting is dropped. - Isolate delayed Linux sampling signals by capture generation and wait for queued
deliveries before restoring the process signal handler. - Serialize process-wide Windows DbgHelp ownership so live symbolization cannot
clean up an active capture session. - Rebaseline network interfaces on counter reset or reappearance, use 64-bit
Windows counters, and calculate rates from measured elapsed time. - Enforce formatting and project-owned clang-tidy checks in CI, and publish GitHub
releases only after both platform artifacts pass their full test suites. - Match upstream pool grouping labels for threads whose names do not identify a
numbered worker pool. - Save
.sparkprofilefiles as raw uncompressed protobuf instead of gzip-compressed
data. The spark viewer's file upload expects uncompressed protobuf; gzip-wrapped
files could not be opened when manually uploaded to spark.lucko.me. Bytebin uploads
remain gzip-compressed as before. - Reconstruct per-window tick statistics in crash-recovered profiles. Previously,
recovered profiles lackedWindowStatistics.ticksdata, causing the spark viewer's
"Time per tick" label to display total elapsed time instead of per-tick time. - Skip crash recovery for sessions that ended cleanly. Normal profiler stop,
plugin unload, and BDS shutdown all write a CLEAN_END marker to the recovery
journal; on the next startup, these journals are cleaned up without generating
a duplicate.sparkprofile. Only journals without CLEAN_END (crash or forced
termination) trigger recovery. - Refuse recovery for allocation live-only (
--alloc-live-only) sessions.
The recovery journal records allocation requests but not free/realloc events,
so retained-allocation lifecycle state cannot be reconstructed. The recovery
player returns an error instead of producing a semantically incorrect profile. - Stop the recovery journal writer when the profiler is shut down in allocation
mode, preventing the writer thread from outliving the plugin during unload. - Prevent BDS crash when running
/spark healthafter ~5 minutes of uptime.
The network interface display loop usedstd::vformatwith 8std::string
arguments, which threw a non-standard exception on Linux/libc++ once network
monitoring data became available. Replaced with direct string concatenation. - Catch any uncaught C++ exception in command dispatch and report it as an
error message instead of letting it propagate toMinecraftCommandsand
triggerstd::terminate. - Recover Linux function extents from validated CIE/FDE records, including
unindexed records in a safely terminated.eh_frame, instead of extending
every function to the next unwind-table start. - Keep addresses in unwind gaps and PLT entries out of adjacent functions while
normalizing sampled PCs inside exact half-open function ranges. - Serialize
server.propertiesmetadata as a single JSON object string under the
"server.properties"key inserver_configurations, matching the upstream
spark viewer's expected format. Previously, individual property key-value pairs
were sent as separate map entries, which caused the spark viewer to crash with
"An unexpected error occurred".
Changed
- Move live viewer data serialization, gzip compression, and bytebin upload off the
main thread onto a dedicated worker thread, eliminating 120-360 ms per-update
stalls during/spark profiler open. - Apply exponential backoff (5 s -> 15 s -> 30 s -> 60 s) to background profiler
start retries instead of attempting every tick (20 TPS) on failure. - Replace OpenSSL with Windows CNG/BCrypt for RSA2048 key generation,
SHA256withRSA signing, and signature verification. Windows builds no longer
depend on or compile OpenSSL; Linux continues to use OpenSSL. - Implement
disableResponseBroadcastconfig option: when false (default),
result notifications are broadcast to online players with spark permission;
when true, only the command sender receives the response. - BREAKING: Include sleeping threads in execution profiles by default,
replacing--include-sleepingwith--ignore-sleepingto opt out. - Group sampled threads by pool name by default (matching upstream spark's
BY_POOLmode) instead of emitting separate per-thread viewer roots. - Add deterministic Linux symbol-index timing, memory, range-quality, and batch
diagnostics to profile metadata, and evaluateLevel::tick()subtree
readability separately from whole-process coverage. - Replace fixed 32×32 chunk bucket grouping with 8-neighbor connected-component
region analysis matching upstream spark's algorithm. - Populate per-window entity and chunk counts in profile metadata instead of
omitting those fields.
Full Changelog: v0.4.1...v0.5.0