Skip to content

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 09 Aug 08:03
· 112 commits to main since this release

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 .sparkprofile file under plugins/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.toml in the plugin data directory)
    with configurable viewerUrl, 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 in trusted-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 stop restarts the background.
    Timeout and cancel do not restart the background profiler.
  • Add /spark ping command with rolling 15-minute player ping RTT statistics
    (min/median/p95/max), --player <name> lookup, and profile metadata
    integration via PlatformStatistics.Ping.
  • Add per-interface network throughput monitoring (RX/TX bytes and packets per
    second) with 15-minute rolling averages, displayed in /spark health and
    exported in profile metadata via SystemStatistics.NetInterface.
  • Add /spark health --upload to generate and upload a spark HealthData
    report (platform metadata, system resources, 15-minute time-window history,
    and plugin list) to the spark viewer.
  • Add /spark activity command 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 --page pagination.
  • Add --not-combined and --combine-all profiler 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 tentative str? accumulated labels.
  • Propagate lambda type names from std::function::__func vtable thunks to their
    single large unresolved call target on Linux with low-confidence call? 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 open command 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 in trusted-viewers.json.
  • Add command aliases matching upstream spark: sampler for profiler, cpu
    for tps, healthreport and ht for health, activitylog and log for
    activity, tickmonitoring for tickmonitor.
  • Add per-command permissions (spark.profiler, spark.tps, spark.ping,
    spark.health, spark.activity, spark.tickmonitor) in addition to the
    umbrella endstone.command.spark permission.
  • Populate per-window entity and chunk counts in profile metadata from rolling
    gauge samples, matching upstream spark's WindowStatistics fields.
  • Group world chunk statistics into regions using 8-neighbor connected-component
    analysis, matching upstream spark's WorldStatisticsProvider.groupIntoRegions()
    algorithm.
  • Include safe server.properties values (max-players, view-distance,
    tick-distance, compression settings, etc.) in profile metadata via
    SamplerMetadata.server_configurations using a strict whitelist that excludes
    level-seed, passcodes, and any credential-like fields.
  • Pretty-print activity.json with 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 above INT_MAX.
  • Preserve invalid user-owned config.toml files 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 .sparkprofile files 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 lacked WindowStatistics.ticks data, 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 health after ~5 minutes of uptime.
    The network interface display loop used std::vformat with 8 std::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 to MinecraftCommands and
    trigger std::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.properties metadata as a single JSON object string under the
    "server.properties" key in server_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 disableResponseBroadcast config 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-sleeping with --ignore-sleeping to opt out.
  • Group sampled threads by pool name by default (matching upstream spark's
    BY_POOL mode) instead of emitting separate per-thread viewer roots.
  • Add deterministic Linux symbol-index timing, memory, range-quality, and batch
    diagnostics to profile metadata, and evaluate Level::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