Skip to content

Database snapshotting#234

Merged
miek merged 10 commits intogreatscottgadgets:mainfrom
martinling:snapshot
Jul 28, 2025
Merged

Database snapshotting#234
miek merged 10 commits intogreatscottgadgets:mainfrom
martinling:snapshot

Conversation

@martinling
Copy link
Member

@martinling martinling commented May 22, 2025

Rather than trying to work with live data from the database during UI updates, take a snapshot of the database state and then work with that snapshot until the next update. This eliminates a lot of potential for non-determinism that's otherwise hard to test or reproduce.

The challenge is to make the snapshotting cheap; this can be achieved by grouping all the AtomicU64 counters used by the database into a contiguous block of memory which serves as the state representation, and which can be snapshotted effectively with what is essentially just a memcpy by the writing thread, in between handling packets.

There's a lot of API churn in this PR, for two reasons:

  1. Since restoring a Counter requires a CounterSet, the Dump API is changed such that restore requires a db: &mut CounterSet argument.
  2. We need to handle both the case where we're working with a reader directly, and the case where we're working with a reader and a snapshot. This is achieved by having e.g. CaptureReader and CaptureSnapshotReader types that both implement a CaptureReaderOps trait. However, this means every single call site has to change from fields to methods, e.g. cap.packet_index.len() -> cap.packet_index().len().

Fixes some crashes in #150, and some much more occasional crashes in mainline Packetry.

@martinling martinling force-pushed the snapshot branch 10 times, most recently from db1735d to 3c2bcb9 Compare June 23, 2025 17:51
@martinling martinling changed the title WIP: Database snapshotting Database snapshotting Jun 23, 2025
@martinling martinling marked this pull request as ready for review June 23, 2025 17:56
@miek miek self-requested a review July 2, 2025 11:12
@miek
Copy link
Member

miek commented Jul 2, 2025

Trying this out, it works against a saved capture but soon after starting live capture I'm getting this panic:

Capture enabled, speed: High (480Mbps)

thread 'main' panicked at src/ui/tree_list_model.rs:786:30:
attempt to subtract with overflow
stack backtrace:
   0:     0x6461b86ca01a - std::backtrace_rs::backtrace::libunwind::trace::h88deb10bd0145eb8
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/../../backtrace/src/backtrace/libunwind.rs:116:5
   1:     0x6461b86ca01a - std::backtrace_rs::backtrace::trace_unsynchronized::he1036f5481c14dff
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
   2:     0x6461b86ca01a - std::sys::backtrace::_print_fmt::hecc345b6e70c4b20
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/sys/backtrace.rs:66:9
   3:     0x6461b86ca01a - <std::sys::backtrace::BacktraceLock::print::DisplayBacktrace as core::fmt::Display>::fmt::he089f96442833f67
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/sys/backtrace.rs:39:26
   4:     0x6461b86f3b63 - core::fmt::rt::Argument::fmt::h1f77cded99c71a14
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/fmt/rt.rs:177:76
   5:     0x6461b86f3b63 - core::fmt::write::h2f210ed4c94745cb
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/fmt/mod.rs:1440:21
   6:     0x6461b86c5c93 - std::io::Write::write_fmt::h7de08171ab770fb2
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/io/mod.rs:1887:15
   7:     0x6461b86c9e62 - std::sys::backtrace::BacktraceLock::print::h810fbd31421329e6
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/sys/backtrace.rs:42:9
   8:     0x6461b86caed0 - std::panicking::default_hook::{{closure}}::hbaad47ed9dc6356d
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:295:22
   9:     0x6461b86cacb0 - std::panicking::default_hook::h24e207139139d40a
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:322:9
  10:     0x6461b86cb632 - std::panicking::rust_panic_with_hook::ha9131beeb2ddc506
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:828:13
  11:     0x6461b86cb3a6 - std::panicking::begin_panic_handler::{{closure}}::h1bba0eaeb6da506f
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:694:13
  12:     0x6461b86ca519 - std::sys::backtrace::__rust_end_short_backtrace::h1d1ca3eade483f4c
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/sys/backtrace.rs:168:18
  13:     0x6461b86cb06d - rust_begin_unwind
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:692:5
  14:     0x6461b7ee41f0 - core::panicking::panic_fmt::h896a0727a1a943f9
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/panicking.rs:75:14
  15:     0x6461b7ee4847 - core::panicking::panic_const::panic_const_sub_overflow::hce21710976133341
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/panicking.rs:178:21
  16:     0x6461b7fa2544 - packetry::ui::tree_list_model::TreeListModel<Item,Model,RowData,ViewMode>::update_node::he45b35faba8b193b
                               at /home/mike/repos/packetry/src/ui/tree_list_model.rs:786:30
  17:     0x6461b7f9e4c2 - packetry::ui::tree_list_model::TreeListModel<Item,Model,RowData,ViewMode>::update_node::h172cc035e6ca2f3b
                               at /home/mike/repos/packetry/src/ui/tree_list_model.rs:844:32
  18:     0x6461b7f9ca67 - packetry::ui::tree_list_model::TreeListModel<Item,Model,RowData,ViewMode>::update::hd45b8cbd4b635c53
                               at /home/mike/repos/packetry/src/ui/tree_list_model.rs:745:9
  19:     0x6461b7fbda0c - <packetry::ui::model::TrafficModel as packetry::ui::model::GenericModel<packetry::item::TrafficItem,packetry::item::TrafficViewMode>>::update::h954d88bacbe6386d
                               at /home/mike/repos/packetry/src/ui/model.rs:96:17
  20:     0x6461b7eece13 - packetry::ui::update_view::{{closure}}::h6c27de99f1cb375f
                               at /home/mike/repos/packetry/src/ui/mod.rs:980:33
  21:     0x6461b7ee7d76 - packetry::ui::with_ui::{{closure}}::hb06615e95618c62b
                               at /home/mike/repos/packetry/src/ui/mod.rs:409:13
  22:     0x6461b7f2221d - std::thread::local::LocalKey<T>::try_with::hf438f916a3009009
                               at /home/mike/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:308:12
  23:     0x6461b7f1dcfe - std::thread::local::LocalKey<T>::with::hcce4d10e1095ea24
                               at /home/mike/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:272:9
  24:     0x6461b7ee639d - packetry::ui::with_ui::h918af08a5ddcf854
                               at /home/mike/repos/packetry/src/ui/mod.rs:407:5
  25:     0x6461b7f80176 - packetry::ui::update_view::ha6f7e1f64e4abc20
                               at /home/mike/repos/packetry/src/ui/mod.rs:948:5
  26:     0x6461b7eed886 - packetry::ui::update_view::{{closure}}::{{closure}}::h85c07cf0cd846faf
                               at /home/mike/repos/packetry/src/ui/mod.rs:1023:34
  27:     0x6461b7fd5341 - glib::source::fnmut_callback_wrapper::{{closure}}::hb01b9db03652301d
                               at /home/mike/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/glib-0.19.7/src/source.rs:232:9
  28:     0x6461b7fd496a - glib::source::trampoline::h7418fe8892e694b7
                               at /home/mike/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/glib-0.19.7/src/source.rs:86:5
  29:     0x7d8227001532 - <unknown>
  30:     0x7d822700049e - <unknown>
  31:     0x7d822705f737 - <unknown>
  32:     0x7d8226fffa63 - g_main_context_iteration
  33:     0x7d82271d387d - g_application_run
  34:     0x6461b7f4b3ce - gio::application::ApplicationExtManual::run_with_args::h800814e04ea35738
                               at /home/mike/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/gio-0.19.5/src/application.rs:29:13
  35:     0x6461b7f4b4b9 - gio::application::ApplicationExtManual::run::h987374b55c89281b
                               at /home/mike/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/gio-0.19.5/src/application.rs:22:9
  36:     0x6461b7fb0bdc - packetry::main::h0c0fada75759a16d
                               at /home/mike/repos/packetry/src/main.rs:102:9
  37:     0x6461b813dceb - core::ops::function::FnOnce::call_once::he0e2e255b4feac16
                               at /home/mike/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
  38:     0x6461b80f49be - std::sys::backtrace::__rust_begin_short_backtrace::h3e40a3bd1cea150e
                               at /home/mike/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/backtrace.rs:152:18
  39:     0x6461b808bb11 - std::rt::lang_start::{{closure}}::h3f3bb44f0d2fdbbe
                               at /home/mike/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:195:18
  40:     0x6461b86bf867 - core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::ha29694188d478cce
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/ops/function.rs:284:13
  41:     0x6461b86bf867 - std::panicking::try::do_call::h146aeebcd3824393
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:584:40
  42:     0x6461b86bf867 - std::panicking::try::hf3596e381ccb2229
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:547:19
  43:     0x6461b86bf867 - std::panic::catch_unwind::h7b076e21cb40b9bf
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panic.rs:358:14
  44:     0x6461b86bf867 - std::rt::lang_start_internal::{{closure}}::h481008de4850e7c0
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/rt.rs:174:48
  45:     0x6461b86bf867 - std::panicking::try::do_call::h865e627c98d1c4cc
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:584:40
  46:     0x6461b86bf867 - std::panicking::try::hf53b8617a1069bb3
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:547:19
  47:     0x6461b86bf867 - std::panic::catch_unwind::h9d30caa3add162e9
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panic.rs:358:14
  48:     0x6461b86bf867 - std::rt::lang_start_internal::hbb268f70c879621d
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/rt.rs:174:20
  49:     0x6461b808baea - std::rt::lang_start::h1b6848bd5e259175
                               at /home/mike/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:194:17
  50:     0x6461b7fb0d4e - main
  51:     0x7d8226c2a1ca - __libc_start_call_main
                               at ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
  52:     0x7d8226c2a28b - __libc_start_main_impl
                               at ./csu/../csu/libc-start.c:360:3
  53:     0x6461b7ee4af5 - _start
  54:                0x0 - <unknown>

thread 'main' panicked at library/core/src/panicking.rs:218:5:
panic in a function that cannot unwind
stack backtrace:
   0:     0x6461b86ca01a - std::backtrace_rs::backtrace::libunwind::trace::h88deb10bd0145eb8
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/../../backtrace/src/backtrace/libunwind.rs:116:5
   1:     0x6461b86ca01a - std::backtrace_rs::backtrace::trace_unsynchronized::he1036f5481c14dff
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
   2:     0x6461b86ca01a - std::sys::backtrace::_print_fmt::hecc345b6e70c4b20
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/sys/backtrace.rs:66:9
   3:     0x6461b86ca01a - <std::sys::backtrace::BacktraceLock::print::DisplayBacktrace as core::fmt::Display>::fmt::he089f96442833f67
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/sys/backtrace.rs:39:26
   4:     0x6461b86f3b63 - core::fmt::rt::Argument::fmt::h1f77cded99c71a14
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/fmt/rt.rs:177:76
   5:     0x6461b86f3b63 - core::fmt::write::h2f210ed4c94745cb
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/fmt/mod.rs:1440:21
   6:     0x6461b86c5c93 - std::io::Write::write_fmt::h7de08171ab770fb2
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/io/mod.rs:1887:15
   7:     0x6461b86c9e62 - std::sys::backtrace::BacktraceLock::print::h810fbd31421329e6
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/sys/backtrace.rs:42:9
   8:     0x6461b86caed0 - std::panicking::default_hook::{{closure}}::hbaad47ed9dc6356d
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:295:22
   9:     0x6461b86cacb0 - std::panicking::default_hook::h24e207139139d40a
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:322:9
  10:     0x6461b86cb632 - std::panicking::rust_panic_with_hook::ha9131beeb2ddc506
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:828:13
  11:     0x6461b86cb3a6 - std::panicking::begin_panic_handler::{{closure}}::h1bba0eaeb6da506f
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:694:13
  12:     0x6461b86ca519 - std::sys::backtrace::__rust_end_short_backtrace::h1d1ca3eade483f4c
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/sys/backtrace.rs:168:18
  13:     0x6461b86cb06d - rust_begin_unwind
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:692:5
  14:     0x6461b7ee422d - core::panicking::panic_nounwind_fmt::runtime::hdd2d1a56a8b6cee7
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/panicking.rs:117:22
  15:     0x6461b7ee422d - core::panicking::panic_nounwind_fmt::h0d5ff668f956fac4
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/intrinsics/mod.rs:3869:9
  16:     0x6461b7ee42c2 - core::panicking::panic_nounwind::h385b7d9bda51382d
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/panicking.rs:218:5
  17:     0x6461b7ee4425 - core::panicking::panic_cannot_unwind::h757b6ea37bf9b60a
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/panicking.rs:307:5
  18:     0x6461b7fd499a - glib::source::trampoline::h7418fe8892e694b7
                               at /home/mike/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/glib-0.19.7/src/source.rs:82:1
  19:     0x7d8227001532 - <unknown>
  20:     0x7d822700049e - <unknown>
  21:     0x7d822705f737 - <unknown>
  22:     0x7d8226fffa63 - g_main_context_iteration
  23:     0x7d82271d387d - g_application_run
  24:     0x6461b7f4b3ce - gio::application::ApplicationExtManual::run_with_args::h800814e04ea35738
                               at /home/mike/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/gio-0.19.5/src/application.rs:29:13
  25:     0x6461b7f4b4b9 - gio::application::ApplicationExtManual::run::h987374b55c89281b
                               at /home/mike/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/gio-0.19.5/src/application.rs:22:9
  26:     0x6461b7fb0bdc - packetry::main::h0c0fada75759a16d
                               at /home/mike/repos/packetry/src/main.rs:102:9
  27:     0x6461b813dceb - core::ops::function::FnOnce::call_once::he0e2e255b4feac16
                               at /home/mike/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
  28:     0x6461b80f49be - std::sys::backtrace::__rust_begin_short_backtrace::h3e40a3bd1cea150e
                               at /home/mike/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/backtrace.rs:152:18
  29:     0x6461b808bb11 - std::rt::lang_start::{{closure}}::h3f3bb44f0d2fdbbe
                               at /home/mike/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:195:18
  30:     0x6461b86bf867 - core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::ha29694188d478cce
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/core/src/ops/function.rs:284:13
  31:     0x6461b86bf867 - std::panicking::try::do_call::h146aeebcd3824393
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:584:40
  32:     0x6461b86bf867 - std::panicking::try::hf3596e381ccb2229
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:547:19
  33:     0x6461b86bf867 - std::panic::catch_unwind::h7b076e21cb40b9bf
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panic.rs:358:14
  34:     0x6461b86bf867 - std::rt::lang_start_internal::{{closure}}::h481008de4850e7c0
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/rt.rs:174:48
  35:     0x6461b86bf867 - std::panicking::try::do_call::h865e627c98d1c4cc
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:584:40
  36:     0x6461b86bf867 - std::panicking::try::hf53b8617a1069bb3
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panicking.rs:547:19
  37:     0x6461b86bf867 - std::panic::catch_unwind::h9d30caa3add162e9
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/panic.rs:358:14
  38:     0x6461b86bf867 - std::rt::lang_start_internal::hbb268f70c879621d
                               at /rustc/4d91de4e48198da2e33413efdcd9cd2cc0c46688/library/std/src/rt.rs:174:20
  39:     0x6461b808baea - std::rt::lang_start::h1b6848bd5e259175
                               at /home/mike/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:194:17
  40:     0x6461b7fb0d4e - main
  41:     0x7d8226c2a1ca - __libc_start_call_main
                               at ./csu/../sysdeps/nptl/libc_start_call_main.h:58:16
  42:     0x7d8226c2a28b - __libc_start_main_impl
                               at ./csu/../csu/libc-start.c:360:3
  43:     0x6461b7ee4af5 - _start
  44:                0x0 - <unknown>
thread caused non-unwinding panic. aborting.
Aborted (core dumped)

@martinling martinling force-pushed the snapshot branch 2 times, most recently from f65fc8f to 1444ebb Compare July 3, 2025 15:13
@martinling
Copy link
Member Author

OK, this actually required a bunch more work.

Previously, I was just letting the ListModel::item() implementation in TreeListModel use the CaptureReader held by the TreeListModel directly, on the thinking that fetching an item that's already known to exist shouldn't really depend on the snapshot state - and we can't easily pass in the current snapshot state there, because the ListModel interface doesn't give us somewhere to do so.

However, when we fetch an item we also fetch its child count, and that was screwing things up.

So I've made TreeListModel hold both a CaptureReader and a Snapshot, such that it can construct a CaptureSnapshot on the fly when it needs to do any DB access. But that CaptureReader needs to live in a RefCell, so there's some faffing around to make sure the borrows don't overlap at runtime.

@miek
Copy link
Member

miek commented Jul 8, 2025

I'm getting a new error now 🙂
If I connect up a HackRF for live capture, then hit reset on it during the capture, I get messages like "requested position 49642 but snapshot length is 49153".

@martinling
Copy link
Member Author

OK, I can reproduce that. Not sure what's going on yet. It goes away if I revert the last commit on this branch which adds that extra verification, but that then leads to different crashes later, so clearly something's still not using the right numbers.

@martinling martinling marked this pull request as draft July 9, 2025 15:20
@martinling martinling force-pushed the snapshot branch 2 times, most recently from 317f04c to cdad50f Compare July 16, 2025 17:25
@martinling
Copy link
Member Author

I've fixed several routes for state from the live capture to slip into the snapshot via reuse of things from CaptureShared and EndpointShared. However, the same bug persists. There must be something else too.

@martinling martinling force-pushed the snapshot branch 3 times, most recently from dfc6949 to 52d1dff Compare July 17, 2025 22:07
@martinling
Copy link
Member Author

martinling commented Jul 17, 2025

I've made some progress on debugging this, and I have a workaround, though not yet a proper fix.

The crash was happening when TreeListModel tried to update the text description of an item that had gained children since the last update.

When we first call ItemSource::description for an item, we catch errors and display them in the list view, where the text would have been. This avoids a crash, and is also really helpful for debugging the code that produces the descriptions, because it's possible to examine the problematic item in-situ and look at its contents.

However, when we update a description because something about the item has changed, we weren't doing that. So the workaround is just:

             if item_updated {
                 // The node's description may change.
-                let summary = cap.description(&item_node.item, false)?;
+                let summary = match cap.description(&item_node.item, false) {
+                    Ok(description) => description,
+                    Err(e) => format!("Error: {e}"),
+                }

With that error handling, everything works fine from the user's perspective, and I've not seen any errors actually become visible in the UI. However, adding some further code to log the errors, along with the previous description of the items in question, reveals what's failing:

Failed updating '13840 SOF packets': requested position 16123 but snapshot length is 15582
Failed updating '20 SOF packets': requested position 17469 but snapshot length is 17389
Failed updating '12081 SOF packets': requested position 30938 but snapshot length is 30124
Failed updating '9874 SOF packets': requested position 43503 but snapshot length is 42777
Failed updating '8762 SOF packets': requested position 56080 but snapshot length is 55767

The failed lookup is into the packet_data stream, and comes from a packet_pid() call in CaptureReaderOps::transaction, where it's trying to get the PID bytes of the first and last packets in the transaction.

Previously, when the requested range was present in the index, the
target_length parameter was irrelevant and not needed.

However, we now want to be able to pass in the length of a snapshot,
which may have a shorter length than the full target stream.
@martinling
Copy link
Member Author

I found the bug!

The problem was with the way I was implementing the target_range method for CompactSnapshot.

I was forwarding this to the target_range method on CompactReader and passing in the correct target_length parameter for the length of the target stream in the snapshot.

However, the target_range implementation on CompactReader ignores that parameter unless it thinks it needs it, so the resulting range wasn't being restricted to the length of the target stream in the snapshot.

The fix is in commit a0712bf.

I've kept the change to error handling on item updates, but removed the subsequent debug code.

This should now be ready to go.

@martinling martinling marked this pull request as ready for review July 26, 2025 00:27
Copy link
Member

@miek miek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, thank you!

@miek miek merged commit 0eeb77c into greatscottgadgets:main Jul 28, 2025
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants