diff --git a/Cargo.lock b/Cargo.lock index c740dcb2..b441b75a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,11 +2,29 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "anyhow" -version = "1.0.53" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +dependencies = [ + "backtrace", +] [[package]] name = "arc-swap" @@ -29,6 +47,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "bisection" version = "0.1.0" @@ -306,6 +339,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "gio" version = "0.15.5" @@ -569,6 +608,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1251ce8e8f9909600e127dcbe74ac50d8464e6685cf5953d37df7a741dbf9e9d" +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + [[package]] name = "memmap2" version = "0.5.8" @@ -587,6 +632,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -624,6 +678,15 @@ dependencies = [ "syn", ] +[[package]] +name = "object" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.9.0" @@ -634,6 +697,7 @@ checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" name = "packetry" version = "0.1.0" dependencies = [ + "anyhow", "arc-swap", "bisection", "bitfield", @@ -657,7 +721,6 @@ dependencies = [ "serde", "serde_json", "tempfile", - "thiserror", ] [[package]] @@ -858,6 +921,12 @@ dependencies = [ "libusb1-sys", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc_version" version = "0.3.3" diff --git a/Cargo.toml b/Cargo.toml index 02c9ad51..4873eb19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,6 @@ num_enum = "0.5.6" once_cell = "1.5" pcap-file = "2.0.0" tempfile = "3.3.0" -thiserror = "1.0.30" bitfield = "0.13.2" num-format = "0.4.0" humansize = "1.1.1" @@ -29,6 +28,7 @@ arc-swap = "1.6.0" lrumap = "0.1.0" memmap2 = "0.5.8" page_size = "0.5.0" +anyhow = { version = "1.0.79", features = ["backtrace"] } [dev-dependencies] serde = { version = "1.0.136", features = ["derive"] } diff --git a/src/backend/luna.rs b/src/backend/luna.rs index db2403c2..41062ae5 100644 --- a/src/backend/luna.rs +++ b/src/backend/luna.rs @@ -3,6 +3,7 @@ use std::thread::{spawn, JoinHandle}; use std::sync::mpsc::{channel, Sender, Receiver}; use std::time::Duration; +use anyhow::{Context as ErrorContext, Error, bail}; use num_enum::{FromPrimitive, IntoPrimitive}; use rusb::{ Context, @@ -70,20 +71,6 @@ impl State { } } -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Usb(#[from] rusb::Error), - #[error("channel send error")] - ChannelSend, - #[error("worker thread panic")] - ThreadPanic, - #[error("unsupported analyzer version: Gateware version is {0}. \ - Supported range is {MIN_SUPPORTED} or higher, \ - but not {NOT_SUPPORTED} or higher")] - WrongVersion(Version), -} - /// A Luna device attached to the system. pub struct LunaDevice { usb_device: Device, @@ -97,7 +84,7 @@ pub struct LunaHandle { } pub struct LunaStream { - receiver: Receiver, Error>>, + receiver: Receiver, rusb::Error>>, } pub struct LunaStop { @@ -134,13 +121,14 @@ impl LunaHandle { fn new(usb_handle: DeviceHandle) -> Result { let version = usb_handle .device() - .device_descriptor() - .map_err(Error::Usb)? + .device_descriptor()? .device_version(); if version >= MIN_SUPPORTED && version < NOT_SUPPORTED { Ok(Self { usb_handle }) } else { - Err(Error::WrongVersion(version)) + bail!("Unsupported analyzer version: Gateware version is {version}. \ + Supported range is {MIN_SUPPORTED} or higher, \ + but not {NOT_SUPPORTED} or higher") } } @@ -192,13 +180,13 @@ impl LunaHandle { packet_queue.extend(&buffer[..count]); while let Some(packet) = packet_queue.next() { tx.send(Ok(packet)) - .or(Err(Error::ChannelSend))?; + .context("Failed sending packet to channel")?; }; }, Err(rusb::Error::Timeout) => continue, Err(usb_error) => { - tx.send(Err(Error::from(usb_error))) - .or(Err(Error::ChannelSend))?; + tx.send(Err(usb_error)) + .context("Failed sending error to channel")?; return Err(Error::from(usb_error)); } } @@ -234,17 +222,17 @@ impl LunaHandle { } impl LunaStream { - pub fn next(&mut self) -> Option, Error>> { + pub fn next(&mut self) -> Option, rusb::Error>> { self.receiver.recv().ok() } } impl LunaStop { pub fn stop(self) -> Result<(), Error> { - use Error::*; println!("Requesting capture stop"); - self.stop_request.send(()).or(Err(ChannelSend))?; - self.worker.join().or(Err(ThreadPanic))? + self.stop_request.send(()).context("Failed sending stop request")?; + self.worker.join().err().context("Worker thread panic")?; + Ok(()) } } diff --git a/src/capture.rs b/src/capture.rs index f9dbe8ed..c5e5c07a 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -4,7 +4,6 @@ use std::ops::Range; use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64}; use std::sync::atomic::Ordering::{Acquire, Release}; use std::sync::Arc; -use std::num::TryFromIntError; use std::mem::size_of; use crate::id::{Id, HasLength}; @@ -12,15 +11,14 @@ use crate::data_stream::{ data_stream, data_stream_with_block_size, DataWriter, DataReader}; use crate::compact_index::{compact_index, CompactWriter, CompactReader}; use crate::rcu::SingleWriterRcu; -use crate::stream::StreamError; use crate::vec_map::VecMap; use crate::usb::{self, prelude::*}; use crate::util::{fmt_count, fmt_size}; +use anyhow::{Context, Error, bail}; use arc_swap::{ArcSwap, ArcSwapOption}; use bytemuck_derive::{Pod, Zeroable}; use num_enum::{IntoPrimitive, FromPrimitive}; -use thiserror::Error; // Use 2MB block size for packet data, which is a large page size on x86_64. const PACKET_DATA_BLOCK_SIZE: usize = 0x200000; @@ -66,7 +64,7 @@ pub struct CaptureReader { /// Create a capture reader-writer pair. pub fn create_capture() - -> Result<(CaptureWriter, CaptureReader), CaptureError> + -> Result<(CaptureWriter, CaptureReader), Error> { // Create all the required streams. let (data_writer, data_reader) = @@ -152,7 +150,7 @@ pub struct EndpointReader { /// Create a per-endpoint reader-writer pair. pub fn create_endpoint() - -> Result<(EndpointWriter, EndpointReader), CaptureError> + -> Result<(EndpointWriter, EndpointReader), Error> { // Create all the required streams. let (transactions_writer, transactions_reader) = compact_index()?; @@ -191,21 +189,6 @@ pub fn create_endpoint() Ok((writer, reader)) } -/// Error type returned by operations on a capture. -#[derive(Debug, Error)] -pub enum CaptureError { - #[error(transparent)] - StreamError(#[from] StreamError), - #[error(transparent)] - RangeError(#[from] TryFromIntError), - #[error("Descriptor missing")] - DescriptorMissing, - #[error("Indexing error: {0}")] - IndexError(String), -} - -use CaptureError::{DescriptorMissing, IndexError}; - pub type PacketByteId = Id; pub type PacketId = Id; pub type TransactionId = Id; @@ -360,11 +343,11 @@ impl DeviceData { } pub fn configuration(&self, number: &ConfigNum) - -> Result, CaptureError> + -> Result, Error> { match self.configurations.load().get(*number) { Some(config) => Ok(config.clone()), - None => Err(DescriptorMissing) + None => bail!("No descriptor for config {number}") } } @@ -423,7 +406,7 @@ impl DeviceData { } pub fn decode_request(&self, fields: &SetupFields, payload: &[u8]) - -> Result<(), CaptureError> + -> Result<(), Error> { let req_type = fields.type_fields.request_type(); let request = StandardRequest::from(fields.request); @@ -440,7 +423,7 @@ impl DeviceData { pub fn decode_descriptor_read(&self, fields: &SetupFields, payload: &[u8]) - -> Result<(), CaptureError> + -> Result<(), Error> { let recipient = fields.type_fields.recipient(); let desc_type = DescriptorType::from((fields.value >> 8) as u8); @@ -485,7 +468,7 @@ impl DeviceData { } fn decode_configuration_set(&self, fields: &SetupFields) - -> Result<(), CaptureError> + -> Result<(), Error> { let config_number = ConfigNum(fields.value.try_into()?); self.config_number.swap(Some(Arc::new(config_number))); @@ -505,24 +488,22 @@ impl DeviceData { impl Configuration { pub fn interface(&self, number: &InterfaceNum) - -> Result<&Interface, CaptureError> + -> Result<&Interface, Error> { match self.interfaces.get(*number) { Some(iface) => Ok(iface), - _ => Err(IndexError(format!( - "Configuration has no interface {number}"))) + _ => bail!("Configuration has no interface {number}") } } } impl Interface { pub fn endpoint_descriptor(&self, number: &InterfaceEpNum) - -> Result<&EndpointDescriptor, CaptureError> + -> Result<&EndpointDescriptor, Error> { match self.endpoint_descriptors.get(*number) { Some(desc) => Ok(desc), - _ => Err(IndexError(format!( - "Interface has no endpoint descriptor {number}"))) + _ => bail!("Interface has no endpoint descriptor {number}") } } } @@ -599,7 +580,7 @@ impl Transaction { fn description(&self, capture: &mut CaptureReader, endpoint: &Endpoint) - -> Result + -> Result { use PID::*; use StartComplete::*; @@ -624,7 +605,7 @@ impl Transaction { capture: &mut CaptureReader, endpoint: &Endpoint, pid: PID) - -> Result + -> Result { Ok(format!( "{} transaction on {}.{}{}", @@ -715,13 +696,12 @@ impl std::fmt::Display for Bytes<'_> { impl CaptureWriter { pub fn device_data(&self, id: DeviceId) - -> Result, CaptureError> + -> Result, Error> { Ok(self.shared.device_data .load() .get(id) - .ok_or_else(|| - IndexError(format!("Capture has no device with ID {id}")))? + .context("Capture has no device with ID {id}")? .clone()) } @@ -771,11 +751,10 @@ impl CaptureWriter { impl CaptureReader { pub fn endpoint_traffic(&mut self, endpoint_id: EndpointId) - -> Result<&mut EndpointReader, CaptureError> + -> Result<&mut EndpointReader, Error> { if self.shared.endpoint_readers.load().get(endpoint_id).is_none() { - return Err(IndexError(format!( - "Capture has no endpoint ID {endpoint_id}"))) + bail!("Capture has no endpoint ID {endpoint_id}") } if self.endpoint_readers.get(endpoint_id).is_none() { @@ -792,35 +771,31 @@ impl CaptureReader { } fn transfer_range(&mut self, entry: &TransferIndexEntry) - -> Result, CaptureError> + -> Result, Error> { let endpoint_id = entry.endpoint_id(); let ep_transfer_id = entry.transfer_id(); let ep_traf = self.endpoint_traffic(endpoint_id)?; - Ok(ep_traf.transfer_index.target_range( - ep_transfer_id, ep_traf.transaction_ids.len())?) + ep_traf.transfer_index.target_range( + ep_transfer_id, ep_traf.transaction_ids.len()) } fn transaction_fields(&mut self, transaction: &Transaction) - -> Result + -> Result { match transaction.data_packet_id { - None => Err(IndexError(String::from( - "Transaction has no data packet"))), + None => bail!("Transaction has no data packet"), Some(data_packet_id) => { let data_packet = self.packet(data_packet_id)?; match data_packet.first() { - None => Err(IndexError(String::from( - "Found empty packet instead of setup data"))), + None => bail!("Found empty packet instead of setup data"), Some(byte) => { let pid = PID::from(*byte); if pid != PID::DATA0 { - Err(IndexError(format!( - "Found {pid} packet instead of setup data"))) + bail!("Found {pid} packet instead of setup data") } else if data_packet.len() != 11 { - Err(IndexError(format!( - "Found DATA0 with packet length {} \ - instead of setup data", data_packet.len()))) + bail!("Found DATA0 with packet length {} \ + instead of setup data", data_packet.len()) } else { Ok(SetupFields::from_data_packet(&data_packet)) } @@ -831,30 +806,29 @@ impl CaptureReader { } fn transaction_bytes(&mut self, transaction: &Transaction) - -> Result, CaptureError> + -> Result, Error> { let data_packet_id = transaction.data_packet_id - .ok_or_else(||IndexError(String::from( - "Transaction has no data packet")))?; + .context("Transaction has no data packet")?; let packet_byte_range = self.packet_index.target_range( data_packet_id, self.packet_data.len())?; let data_byte_range = packet_byte_range.start + 1 .. packet_byte_range.end - 2; - Ok(self.packet_data.get_range(&data_byte_range)?) + self.packet_data.get_range(&data_byte_range) } fn transfer_bytes(&mut self, endpoint_id: EndpointId, data_range: &Range, length: usize) - -> Result, CaptureError> + -> Result, Error> { let mut transfer_bytes = Vec::with_capacity(length); let mut data_range = data_range.clone(); while transfer_bytes.len() < length { - let data_id = data_range.next().ok_or_else(|| IndexError(format!( + let data_id = data_range.next().with_context(|| format!( "Ran out of data events after fetching {}/{} requested bytes", - transfer_bytes.len(), length)))?; + transfer_bytes.len(), length))?; let ep_traf = self.endpoint_traffic(endpoint_id)?; let ep_transaction_id = ep_traf.data_transactions.get(data_id)?; let transaction_id = ep_traf.transaction_ids.get(ep_transaction_id)?; @@ -870,30 +844,30 @@ impl CaptureReader { } fn endpoint_state(&mut self, transfer_id: TransferId) - -> Result, CaptureError> + -> Result, Error> { let range = self.endpoint_state_index.target_range( transfer_id, self.endpoint_states.len())?; - Ok(self.endpoint_states.get_range(&range)?) + self.endpoint_states.get_range(&range) } pub fn packet(&mut self, id: PacketId) - -> Result, CaptureError> + -> Result, Error> { let range = self.packet_index.target_range( id, self.packet_data.len())?; - Ok(self.packet_data.get_range(&range)?) + self.packet_data.get_range(&range) } fn packet_pid(&mut self, id: PacketId) - -> Result + -> Result { let offset: Id = self.packet_index.get(id)?; Ok(PID::from(self.packet_data.get(offset)?)) } fn transaction(&mut self, id: TransactionId) - -> Result + -> Result { let packet_id_range = self.transaction_index.target_range( id, self.packet_index.len())?; @@ -952,7 +926,7 @@ impl CaptureReader { address: DeviceAddr, endpoint_id: EndpointId, range: Range) - -> Result + -> Result { let ep_traf = self.endpoint_traffic(endpoint_id)?; let transaction_ids = ep_traf.transaction_ids.get_range(&range)?; @@ -976,17 +950,16 @@ impl CaptureReader { } pub fn device_data(&self, id: &DeviceId) - -> Result, CaptureError> + -> Result, Error> { Ok(self.shared.device_data .load() .get(*id) - .ok_or_else(|| - IndexError(format!("Capture has no device with ID {id}")))? + .with_context(|| format!("Capture has no device with ID {id}"))? .clone()) } - fn device_version(&self, id: &DeviceId) -> Result { + fn device_version(&self, id: &DeviceId) -> Result { Ok(self.device_data(id)?.version()) } @@ -1004,7 +977,7 @@ impl CaptureReader { fn transfer_extended(&mut self, endpoint_id: EndpointId, transfer_id: TransferId) - -> Result + -> Result { use EndpointState::*; let count = self.transfer_index.len(); @@ -1033,7 +1006,7 @@ impl CaptureReader { impl EndpointReader { fn transfer_data_range(&mut self, range: &Range) - -> Result, CaptureError> + -> Result, Error> { let first_data_id = self.data_transactions.bisect_left(&range.start)?; let last_data_id = self.data_transactions.bisect_left(&range.end)?; @@ -1041,7 +1014,7 @@ impl EndpointReader { } fn transfer_data_length(&mut self, range: &Range) - -> Result + -> Result { if range.start == range.end { return Ok(0); @@ -1075,20 +1048,20 @@ impl CompletionStatus { pub trait ItemSource { fn item(&mut self, parent: Option<&Item>, index: u64) - -> Result; + -> Result; fn item_update(&mut self, item: &Item) - -> Result, CaptureError>; + -> Result, Error>; fn child_item(&mut self, parent: &Item, index: u64) - -> Result; + -> Result; fn item_children(&mut self, parent: Option<&Item>) - -> Result<(CompletionStatus, u64), CaptureError>; - fn summary(&mut self, item: &Item) -> Result; - fn connectors(&mut self, item: &Item) -> Result; + -> Result<(CompletionStatus, u64), Error>; + fn summary(&mut self, item: &Item) -> Result; + fn connectors(&mut self, item: &Item) -> Result; } impl ItemSource for CaptureReader { fn item(&mut self, parent: Option<&TrafficItem>, index: u64) - -> Result + -> Result { match parent { None => { @@ -1101,13 +1074,13 @@ impl ItemSource for CaptureReader { } fn item_update(&mut self, _item: &TrafficItem) - -> Result, CaptureError> + -> Result, Error> { Ok(None) } fn child_item(&mut self, parent: &TrafficItem, index: u64) - -> Result + -> Result { use TrafficItem::*; Ok(match parent { @@ -1123,13 +1096,12 @@ impl ItemSource for CaptureReader { Transaction(transfer_id, transaction_id) => Packet(*transfer_id, *transaction_id, { self.transaction_index.get(*transaction_id)? + index}), - Packet(..) => return Err(IndexError(String::from( - "Packets have no child items"))) + Packet(..) => bail!("Packets have no child items") }) } fn item_children(&mut self, parent: Option<&TrafficItem>) - -> Result<(CompletionStatus, u64), CaptureError> + -> Result<(CompletionStatus, u64), Error> { use TrafficItem::*; use CompletionStatus::*; @@ -1164,16 +1136,15 @@ impl ItemSource for CaptureReader { } fn summary(&mut self, item: &TrafficItem) - -> Result + -> Result { use TrafficItem::*; use usb::StartComplete::*; Ok(match item { Packet(.., packet_id) => { let packet = self.packet(*packet_id)?; - let first_byte = *packet.first().ok_or_else(|| - IndexError(format!( - "Packet {packet_id} is empty, cannot retrieve PID")))?; + let first_byte = *packet.first().with_context(|| format!( + "Packet {packet_id} is empty, cannot retrieve PID"))?; let pid = PID::from(first_byte); format!("{pid} packet{}", match PacketFields::from_packet(&packet) { @@ -1299,7 +1270,7 @@ impl ItemSource for CaptureReader { } fn connectors(&mut self, item: &TrafficItem) - -> Result + -> Result { use EndpointState::*; use TrafficItem::*; @@ -1395,7 +1366,7 @@ impl ItemSource for CaptureReader { impl ItemSource for CaptureReader { fn item(&mut self, parent: Option<&DeviceItem>, index: u64) - -> Result + -> Result { match parent { None => { @@ -1408,7 +1379,7 @@ impl ItemSource for CaptureReader { } fn item_update(&mut self, item: &DeviceItem) - -> Result, CaptureError> + -> Result, Error> { use DeviceItem::*; Ok(match item { @@ -1441,7 +1412,7 @@ impl ItemSource for CaptureReader { } fn child_item(&mut self, parent: &DeviceItem, index: u64) - -> Result + -> Result { use DeviceItem::*; Ok(match parent { @@ -1476,13 +1447,12 @@ impl ItemSource for CaptureReader { EndpointDescriptorField(*dev, *conf, *iface, *ep, EndpointField(index.try_into()?), self.device_version(dev)?), - _ => return Err(IndexError(String::from( - "This device item type cannot have children"))) + _ => bail!("This device item type cannot have children") }) } fn item_children(&mut self, parent: Option<&DeviceItem>) - -> Result<(CompletionStatus, u64), CaptureError> + -> Result<(CompletionStatus, u64), Error> { use DeviceItem::*; use CompletionStatus::*; @@ -1528,7 +1498,7 @@ impl ItemSource for CaptureReader { } fn summary(&mut self, item: &DeviceItem) - -> Result + -> Result { use DeviceItem::*; Ok(match item { @@ -1551,7 +1521,7 @@ impl ItemSource for CaptureReader { let strings = data.strings.load(); descriptor.field_text(*field, strings.as_ref()) }, - None => return Err(DescriptorMissing) + None => bail!("Device descriptor missing") } }, Configuration(_, conf) => format!( @@ -1593,7 +1563,7 @@ impl ItemSource for CaptureReader { }) } - fn connectors(&mut self, item: &DeviceItem) -> Result { + fn connectors(&mut self, item: &DeviceItem) -> Result { use DeviceItem::*; let depth = match item { Device(..) => 0, @@ -1709,7 +1679,6 @@ pub mod prelude { pub use super::{ create_endpoint, CaptureWriter, - CaptureError, Device, DeviceId, DeviceData, diff --git a/src/compact_index.rs b/src/compact_index.rs index 5e057531..030d965e 100644 --- a/src/compact_index.rs +++ b/src/compact_index.rs @@ -6,13 +6,13 @@ use std::ops::{Add, AddAssign, Range, Sub, SubAssign}; use std::sync::atomic::{AtomicU64, Ordering::{Acquire, Release}}; use std::sync::Arc; +use anyhow::{Error, bail}; use bisection::bisect_left; use itertools::multizip; use crate::data_stream::{data_stream, DataReader, DataWriter}; use crate::id::Id; use crate::index_stream::{index_stream, IndexReader, IndexWriter}; -use crate::stream::StreamError; use crate::util::{fmt_count, fmt_size}; type Offset = Id; @@ -68,7 +68,7 @@ type CompactPair = /// Returns a unique writer and a cloneable reader. /// pub fn compact_index() - -> Result, StreamError> + -> Result, Error> { let (segment_start_writer, segment_start_reader) = index_stream()?; let (segment_base_writer, segment_base_reader) = index_stream()?; @@ -122,7 +122,7 @@ where Position: Copy + From + Into, /// Add a single value to the end of the index. /// /// Returns the position of the added value. - pub fn push(&mut self, value: Value) -> Result { + pub fn push(&mut self, value: Value) -> Result { match self.current_base_value { None => self.start_segment(value)?, Some(current_base_value) => { @@ -153,7 +153,7 @@ where Position: Copy + From + Into, Ok(position) } - fn start_segment(&mut self, base_value: Value) -> Result<(), StreamError> { + fn start_segment(&mut self, base_value: Value) -> Result<(), Error> { let segment_start = Position::from(self.length); self.segment_start_writer.push(segment_start)?; self.segment_base_writer.push(base_value)?; @@ -189,13 +189,11 @@ where } /// Get a single value from the index, by position. - pub fn get(&mut self, position: Position) -> Result { + pub fn get(&mut self, position: Position) -> Result { // Check position is valid. let length = self.len(); if position.into() >= length { - return Err(StreamError::ReadPastEnd(format!( - "requested position {:?} but index length is {}", - position, length))) + bail!("requested position {position:?} but index length is {length}") } // Find the segment required. let segment_id = self.segment_start_reader.bisect_right(&position)? - 1; @@ -222,14 +220,12 @@ where /// Get multiple values from the index, for a range of positions. pub fn get_range(&mut self, range: &Range) - -> Result, StreamError> + -> Result, Error> { // Check range is valid. let length = self.len(); if range.end.into() > length { - return Err(StreamError::ReadPastEnd(format!( - "requested range {:?} but index length is {}", - range, length))) + bail!("requested range {range:?} but index length is {length}") } // Allocate space for the result. let total_count: usize = (range.end - range.start).try_into().unwrap(); @@ -313,7 +309,7 @@ where /// index, the range will be from the last value in the index to the /// end of the referenced data. pub fn target_range(&mut self, position: Position, target_length: u64) - -> Result, StreamError> + -> Result, Error> { let range = if position.into() + 2 > self.len() { let start = self.get(position)?; @@ -330,7 +326,7 @@ where /// Leftmost position where a value would be ordered within this index. pub fn bisect_left(&mut self, value: &Value) - -> Result + -> Result { let range = Position::from(0)..Position::from(self.len()); self.bisect_range_left(&range, value) @@ -338,7 +334,7 @@ where /// Leftmost position where a value would be ordered within this range. pub fn bisect_range_left(&mut self, range: &Range, value: &Value) - -> Result + -> Result { // Find the segment required. let segment_id = match self.segment_base_reader.bisect_right(value)? { diff --git a/src/data_stream.rs b/src/data_stream.rs index 9b703a85..7fca6919 100644 --- a/src/data_stream.rs +++ b/src/data_stream.rs @@ -2,10 +2,11 @@ use std::marker::PhantomData; use std::mem::size_of; use std::ops::{Deref, Range}; +use anyhow::Error; use bytemuck::{bytes_of, cast_slice, from_bytes, Pod}; use crate::id::Id; -use crate::stream::{stream, StreamReader, StreamWriter, StreamError, MIN_BLOCK}; +use crate::stream::{stream, StreamReader, StreamWriter, MIN_BLOCK}; use crate::util::{fmt_count, fmt_size}; /// Unique handle for append-only write access to a data stream. @@ -32,7 +33,7 @@ struct Values where Data: Deref { /// Returns a unique writer and a cloneable reader. /// pub fn data_stream() - -> Result<(DataWriter, DataReader), StreamError> + -> Result<(DataWriter, DataReader), Error> { data_stream_with_block_size::() } @@ -42,7 +43,7 @@ pub fn data_stream() /// Returns a unique writer and a cloneable reader. /// pub fn data_stream_with_block_size() - -> Result<(DataWriter, DataReader), StreamError> + -> Result<(DataWriter, DataReader), Error> { let (stream_writer, stream_reader) = stream()?; let data_writer = DataWriter { @@ -72,7 +73,7 @@ where Value: Pod + Default /// Add a single item to the end of the stream. /// /// Returns the position of the added item. - pub fn push(&mut self, item: &Value) -> Result, StreamError> { + pub fn push(&mut self, item: &Value) -> Result, Error> { let id = Id::::from_offset(self.size()); self.stream_writer.append(bytes_of(item))?; Ok(id) @@ -82,7 +83,7 @@ where Value: Pod + Default /// /// Returns the ID range of the added items. pub fn append(&mut self, items: &[Value]) - -> Result>, StreamError> + -> Result>, Error> { let mut size = self.size(); let start = Id::::from_offset(size); @@ -111,7 +112,7 @@ where Value: Pod + Default } /// Get a single item from the stream. - pub fn get(&mut self, id: Id) -> Result { + pub fn get(&mut self, id: Id) -> Result { let byte_range = id.offset_range(); let bytes = self.stream_reader.access(&byte_range)?; let value = from_bytes(&bytes); @@ -120,7 +121,7 @@ where Value: Pod + Default /// Get multiple items from the stream. pub fn get_range(&mut self, range: &Range>) - -> Result, StreamError> + -> Result, Error> { let count = (range.end - range.start).try_into().unwrap(); let mut result = Vec::with_capacity(count); @@ -140,7 +141,7 @@ where Value: Pod + Default /// requested length. May be called again to access further values. /// pub fn access(&mut self, range: &Range>) - -> Result, StreamError> + -> Result, Error> { let range = range.start.offset()..range.end.offset(); Ok(Values { diff --git a/src/decoder.rs b/src/decoder.rs index 0073f482..a1f5bfb9 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -1,20 +1,18 @@ use std::sync::atomic::Ordering::Release; use std::sync::Arc; +use anyhow::{Context, Error, bail}; + use crate::capture::prelude::*; use crate::rcu::SingleWriterRcu; use crate::usb::{self, prelude::*}; use crate::vec_map::{VecMap, Key}; -use CaptureError::IndexError; - impl PID { - fn from_packet(packet: &[u8]) -> Result { + fn from_packet(packet: &[u8]) -> Result { let first_byte = packet .first() - .ok_or_else(|| - IndexError(String::from( - "Packet is empty, cannot retrieve PID")))?; + .context("Packet is empty, cannot retrieve PID")?; Ok(PID::from(*first_byte)) } } @@ -97,7 +95,7 @@ struct TransactionState { } fn transaction_status(state: &Option, packet: &[u8]) - -> Result + -> Result { let next = PID::from_packet(packet)?; use PID::*; @@ -225,18 +223,16 @@ fn transaction_status(state: &Option, packet: &[u8]) } impl TransactionState { - fn start_pid(&self) -> Result { + fn start_pid(&self) -> Result { use TransactionStyle::*; match self.style { Simple(pid) | Split(.., Some(pid)) => Ok(pid), - _ => Err(IndexError(String::from( - "Transaction state has no token PID"))) + _ => bail!("Transaction state has no token PID") } } - fn endpoint_id(&self) -> Result { - self.endpoint_id.ok_or_else(|| IndexError(String::from( - "Transaction state has no endpoint ID"))) + fn endpoint_id(&self) -> Result { + self.endpoint_id.context("Transaction state has no endpoint ID") } fn extract_payload(&mut self, packet: &[u8]) { @@ -270,7 +266,7 @@ impl EndpointData { transaction: &mut TransactionState, success: bool, complete: bool) - -> Result<(TransferStatus, TransactionSideEffect), CaptureError> + -> Result<(TransferStatus, TransactionSideEffect), Error> { use TransactionStyle::*; let (ep_type, ep_max) = dev_data.endpoint_details(self.address); @@ -474,22 +470,20 @@ impl EndpointData { fn apply_effect(&mut self, transaction: &TransactionState, effect: TransactionSideEffect) - -> Result<(), CaptureError> + -> Result<(), Error> { use TransactionSideEffect::*; match effect { NoEffect => {}, PendingData(data) => { let ep_transaction_id = transaction.ep_transaction_id - .ok_or_else(|| IndexError(String::from( - "Pending data but no endpoint transaction ID set")))?; + .context("Pending data but no endpoint transaction ID set")?; self.pending_payload = Some((data, ep_transaction_id)); }, IndexData(length, ep_transaction_id) => { let ep_transaction_id = ep_transaction_id .or(transaction.ep_transaction_id) - .ok_or_else(|| IndexError(String::from( - "Data to index but no endpoint transaction ID set")))?; + .context("Data to index but no endpoint transaction ID set")?; self.writer.data_transactions.push(ep_transaction_id)?; self.writer.data_byte_counts.push(self.total_data)?; self.total_data += length as u64; @@ -534,7 +528,7 @@ pub struct Decoder { } impl Decoder { - pub fn new(capture: CaptureWriter) -> Result { + pub fn new(capture: CaptureWriter) -> Result { // Create the decoder. let mut decoder = Decoder { capture, @@ -585,7 +579,7 @@ impl Decoder { } pub fn handle_raw_packet(&mut self, packet: &[u8]) - -> Result<(), CaptureError> + -> Result<(), Error> { let data_range = self.capture.packet_data.append(packet)?; let packet_id = self.capture.packet_index.push(data_range.start)?; @@ -593,14 +587,14 @@ impl Decoder { Ok(()) } - pub fn finish(mut self) -> Result { + pub fn finish(mut self) -> Result { self.transaction_end(false, false)?; self.capture.shared.complete.store(true, Release); Ok(self.capture) } pub fn token_endpoint(&mut self, pid: PID, token: &TokenFields) - -> Result + -> Result { let dev_addr = token.device_address(); let ep_num = token.endpoint_number(); @@ -609,8 +603,7 @@ impl Decoder { (_, PID::IN) => Direction::In, (_, PID::OUT) => Direction::Out, (_, PID::PING) => Direction::Out, - _ => return Err(IndexError(format!( - "PID {pid} does not indicate a direction"))) + _ => bail!("PID {pid} does not indicate a direction") }; let key = EndpointKey { dev_addr, @@ -629,7 +622,7 @@ impl Decoder { } fn packet_endpoint(&mut self, packet: &[u8]) - -> Result + -> Result { let pid = PID::from_packet(packet)?; Ok(match PacketFields::from_packet(packet) { @@ -641,7 +634,7 @@ impl Decoder { } fn transaction_update(&mut self, packet_id: PacketId, packet: &[u8]) - -> Result<(), CaptureError> + -> Result<(), Error> { use TransactionStatus::*; use TransactionStyle::*; @@ -683,7 +676,7 @@ impl Decoder { } fn transaction_start(&mut self, packet_id: PacketId, packet: &[u8]) - -> Result<(), CaptureError> + -> Result<(), Error> { use PID::*; use TransactionStyle::*; @@ -711,7 +704,7 @@ impl Decoder { } fn transaction_append(&mut self, packet: &[u8]) - -> Result<(), CaptureError> + -> Result<(), Error> { use TransactionStyle::*; let pid = PID::from_packet(packet)?; @@ -735,13 +728,12 @@ impl Decoder { } Ok(()) } else { - Err(IndexError(String::from( - "No current transaction to append to"))) + bail!("No current transaction to append to") } } fn transaction_end(&mut self, success: bool, complete: bool) - -> Result<(), CaptureError> + -> Result<(), Error> { if let Some(mut state) = self.transaction_state.take() { if state.endpoint_id.is_some() { @@ -752,7 +744,7 @@ impl Decoder { } fn add_device(&mut self, address: DeviceAddr) - -> Result + -> Result { let device = Device { address }; let device_id = self.capture.devices.push(&device)?; @@ -767,7 +759,7 @@ impl Decoder { dev_addr: DeviceAddr, number: EndpointNum, direction: Direction) - -> Result + -> Result { let device_id = match self.device_index.get(dev_addr) { Some(id) => *id, @@ -794,7 +786,7 @@ impl Decoder { fn transfer_early_start(&mut self, transaction: &mut TransactionState, start: PID) - -> Result<(), CaptureError> + -> Result<(), Error> { use PID::*; let start_early = match (start, transaction.endpoint_id) { @@ -824,7 +816,7 @@ impl Decoder { Ok(()) } - fn transfer_early_append(&mut self) -> Result<(), CaptureError> { + fn transfer_early_append(&mut self) -> Result<(), Error> { use PID::*; use TransactionStyle::*; // Decide whether to index this transaction now. @@ -868,7 +860,7 @@ impl Decoder { transaction: &mut TransactionState, success: bool, complete: bool) - -> Result<(), CaptureError> + -> Result<(), Error> { use TransferStatus::*; let endpoint_id = transaction.endpoint_id()?; @@ -906,7 +898,7 @@ impl Decoder { fn transfer_start(&mut self, transaction: &mut TransactionState, done: bool) - -> Result<(), CaptureError> + -> Result<(), Error> { let endpoint_id = transaction.endpoint_id()?; let ep_data = &mut self.endpoint_data[endpoint_id]; @@ -932,7 +924,7 @@ impl Decoder { fn transfer_append(&mut self, transaction: &mut TransactionState, done: bool) - -> Result<(), CaptureError> + -> Result<(), Error> { let endpoint_id = transaction.endpoint_id()?; let ep_data = &mut self.endpoint_data[endpoint_id]; @@ -952,7 +944,7 @@ impl Decoder { } fn transfer_end(&mut self, transaction: &TransactionState) - -> Result<(), CaptureError> + -> Result<(), Error> { let endpoint_id = transaction.endpoint_id()?; let ep_data = &mut self.endpoint_data[endpoint_id]; @@ -972,7 +964,7 @@ impl Decoder { fn add_transfer(&mut self, endpoint_id: EndpointId, transaction: &mut TransactionState) - -> Result + -> Result { let ep_data = &mut self.endpoint_data[endpoint_id]; if let Some(transfer) = ep_data.active.take() { @@ -1002,7 +994,7 @@ impl Decoder { endpoint_id: EndpointId, ep_transfer_id: EndpointTransferId, start: bool) - -> Result + -> Result { self.add_endpoint_state(endpoint_id, start)?; let mut entry = TransferIndexEntry::default(); @@ -1016,7 +1008,7 @@ impl Decoder { fn add_endpoint_state(&mut self, endpoint_id: EndpointId, start: bool) - -> Result + -> Result { let endpoint_count = self.capture.endpoints.len() as usize; for i in 0..endpoint_count { @@ -1041,7 +1033,7 @@ impl Decoder { fn add_item(&mut self, item_endpoint_id: EndpointId, transfer_id: TransferId) - -> Result + -> Result { let item_id = self.capture.item_index.push(transfer_id)?; self.last_item_endpoint = Some(item_endpoint_id); diff --git a/src/index_stream.rs b/src/index_stream.rs index 50b5b976..0ea3b404 100644 --- a/src/index_stream.rs +++ b/src/index_stream.rs @@ -2,11 +2,12 @@ use std::cmp::min; use std::marker::PhantomData; use std::ops::Range; +use anyhow::Error; use bisection::{bisect_left, bisect_right}; use crate::data_stream::{data_stream, DataReader, DataWriter}; use crate::id::Id; -use crate::stream::{StreamError, MIN_BLOCK}; +use crate::stream::MIN_BLOCK; use crate::util::{fmt_count, fmt_size}; /// Unique handle for append-only write access to an index. @@ -28,7 +29,7 @@ type IndexPair = (IndexWriter, IndexReader); /// /// Returns a unique writer and a cloneable reader. /// -pub fn index_stream() -> Result, StreamError> { +pub fn index_stream() -> Result, Error> { let (data_writer, data_reader) = data_stream()?; let writer = IndexWriter { marker: PhantomData, @@ -57,7 +58,7 @@ where Position: From, Value: Into /// Add a single value to the end of the index. /// /// Returns the position of the added value. - pub fn push(&mut self, value: Value) -> Result { + pub fn push(&mut self, value: Value) -> Result { let id = self.data_writer.push(&value.into())?; let position = Position::from(id.into()); Ok(position) @@ -79,7 +80,7 @@ where Position: Copy + From + Into, } /// Get a single value from the index, by position. - pub fn get(&mut self, position: Position) -> Result { + pub fn get(&mut self, position: Position) -> Result { let id = Id::::from(position.into()); let value = self.data_reader.get(id)?; Ok(Value::from(value)) @@ -87,7 +88,7 @@ where Position: Copy + From + Into, /// Get multiple values from the index, for a range of positions. pub fn get_range(&mut self, range: &Range) - -> Result, StreamError> + -> Result, Error> { let start = Id::::from(range.start.into()); let end = Id::::from(range.end.into()); @@ -103,7 +104,7 @@ where Position: Copy + From + Into, /// index, the range will be from the last value in the index to the /// end of the referenced data. pub fn target_range(&mut self, position: Position, target_length: u64) - -> Result, StreamError> + -> Result, Error> { let stop = position.into() + 2; let range = if stop > self.len() { @@ -122,7 +123,7 @@ where Position: Copy + From + Into, /// Leftmost position where a value would be ordered within this index. pub fn bisect_left(&mut self, value: &Value) - -> Result + -> Result { let range = Position::from(0)..Position::from(self.len()); self.bisect_range_left(&range, value) @@ -130,7 +131,7 @@ where Position: Copy + From + Into, /// Rightmost position where a value would be ordered within this index. pub fn bisect_right(&mut self, value: &Value) - -> Result + -> Result { let range = Position::from(0)..Position::from(self.len()); self.bisect_range_right(&range, value) @@ -138,7 +139,7 @@ where Position: Copy + From + Into, /// Leftmost position where a value would be ordered within this range. pub fn bisect_range_left(&mut self, range: &Range, value: &Value) - -> Result + -> Result { let mut search_start = range.start.into(); let mut search_end = range.end.into(); @@ -180,7 +181,7 @@ where Position: Copy + From + Into, /// Rightmost position where a value would be ordered within this range. pub fn bisect_range_right(&mut self, range: &Range, value: &Value) - -> Result + -> Result { let mut search_start = range.start.into(); let mut search_end = range.end.into(); diff --git a/src/model/mod.rs b/src/model/mod.rs index 01054600..ac006af0 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -11,8 +11,10 @@ use { use gtk::subclass::prelude::*; use gtk::{gio, glib}; +use anyhow::Error; + use crate::capture::{CaptureReader, TrafficItem, DeviceItem}; -use crate::tree_list_model::{TreeListModel, ItemNodeRc, ModelError}; +use crate::tree_list_model::{TreeListModel, ItemNodeRc}; // Public part of the Model type. glib::wrapper! { @@ -26,13 +28,13 @@ pub trait GenericModel where Self: Sized { fn new(capture: CaptureReader, #[cfg(any(feature="test-ui-replay", feature="record-ui-test"))] on_item_update: Rc>) - -> Result; + -> Result; fn set_expanded(&self, node: &ItemNodeRc, position: u32, expanded: bool) - -> Result<(), ModelError>; - fn update(&self) -> Result; + -> Result<(), Error>; + fn update(&self) -> Result; fn summary(&self, item: &Item) -> String; fn connectors(&self, item: &Item) -> String; } @@ -41,7 +43,7 @@ impl GenericModel for TrafficModel { fn new(capture: CaptureReader, #[cfg(any(feature="test-ui-replay", feature="record-ui-test"))] on_item_update: Rc>) - -> Result + -> Result { let model: TrafficModel = glib::Object::new(&[]).expect("Failed to create TrafficModel"); @@ -57,14 +59,14 @@ impl GenericModel for TrafficModel { node: &ItemNodeRc, position: u32, expanded: bool) - -> Result<(), ModelError> + -> Result<(), Error> { let tree_opt = self.imp().tree.borrow(); let tree = tree_opt.as_ref().unwrap(); tree.set_expanded(self, node, position as u64, expanded) } - fn update(&self) -> Result { + fn update(&self) -> Result { let tree_opt = self.imp().tree.borrow(); let tree = tree_opt.as_ref().unwrap(); tree.update(self) @@ -87,7 +89,7 @@ impl GenericModel for DeviceModel { fn new(capture: CaptureReader, #[cfg(any(feature="test-ui-replay", feature="record-ui-test"))] on_item_update: Rc>) - -> Result + -> Result { let model: DeviceModel = glib::Object::new(&[]).expect("Failed to create DeviceModel"); @@ -103,14 +105,14 @@ impl GenericModel for DeviceModel { node: &ItemNodeRc, position: u32, expanded: bool) - -> Result<(), ModelError> + -> Result<(), Error> { let tree_opt = self.imp().tree.borrow(); let tree = tree_opt.as_ref().unwrap(); tree.set_expanded(self, node, position as u64, expanded) } - fn update(&self) -> Result { + fn update(&self) -> Result { let tree_opt = self.imp().tree.borrow(); let tree = tree_opt.as_ref().unwrap(); tree.update(self) diff --git a/src/stream.rs b/src/stream.rs index c40e35b0..472d3952 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -10,11 +10,11 @@ use std::slice; use std::sync::Arc; use std::sync::atomic::{AtomicU64, Ordering::{Acquire, Release}}; +use anyhow::{Context, Error, bail}; use arc_swap::{ArcSwap, ArcSwapOption}; use lrumap::LruBTreeMap; use memmap2::{Mmap, MmapOptions}; use tempfile::tempfile; -use thiserror::Error; /// Minimum block size, defined by largest minimum page size on target systems. pub const MIN_BLOCK: usize = 0x4000; // 16KB (Apple M1/M2) @@ -69,32 +69,6 @@ enum Data { Buffered(Arc>, Range), } -/// Error type returned by stream operations. -#[derive(Debug, Error)] -pub enum StreamError { - /// Failed to create temporary file to store the stream. - #[error("failed creating temporary file: {0}")] - TempFile(std::io::Error), - /// Failed to clone file handle to the stream file. - #[error("failed cloning file handle: {0}")] - CloneFile(std::io::Error), - /// Failed to write to the end of the stream file. - #[error("failed writing to stream file: {0}")] - WriteFile(std::io::Error), - /// Failed to create a memory mapping into part of the stream file. - #[error("failed mapping stream file: {0}")] - MapFile(std::io::Error), - /// Failed to allocate a buffer. - #[error("failed to allocate buffer")] - Alloc(), - /// Attempted to read past the end of the stream. - #[error("attemped to read past end of stream: {0}")] - ReadPastEnd(String), - /// Block size is not a multiple of the system page size. - #[error("block size {0:x} is not a multiple of the system page size {1:x}")] - PageSize(usize, usize), -} - // Number of most recent file mappings retained by each reader. const MAP_CACHE_PER_READER: usize = 4; @@ -105,11 +79,12 @@ type StreamPair = (StreamWriter, StreamReader); /// Returns a unique writer and a cloneable reader. /// pub fn stream() - -> Result, StreamError> + -> Result, Error> { let page_size = page_size::get(); if BLOCK_SIZE < page_size { - return Err(StreamError::PageSize(BLOCK_SIZE, page_size)) + bail!("Block size {BLOCK_SIZE:x} is not a multiple \ + of the system page size {page_size:x}") } let buffer = Arc::new(Buffer::new(0)?); let shared = Arc::new(Shared { @@ -142,7 +117,7 @@ impl StreamWriter { /// /// Returns the new stream length. /// - pub fn append(&mut self, mut data: &[u8]) -> Result { + pub fn append(&mut self, mut data: &[u8]) -> Result { let length = data.len(); let buffered = self.length as usize & Self::block_mask(); if buffered + length <= Self::block_size() { @@ -199,7 +174,7 @@ impl StreamWriter { /// Safety: The buffer must be full. /// #[inline(always)] - unsafe fn write_buffer_to_file(&mut self) -> Result<(), StreamError> { + unsafe fn write_buffer_to_file(&mut self) -> Result<(), Error> { unsafe { let buf = slice::from_raw_parts(self.buf, Self::block_size()); self.write_to_file(buf) @@ -210,21 +185,21 @@ impl StreamWriter { /// /// Safety: The data must be a multiple of the block size. /// - unsafe fn write_to_file(&mut self, data: &[u8]) -> Result<(), StreamError> { + unsafe fn write_to_file(&mut self, data: &[u8]) -> Result<(), Error> { // Create the file if it does not exist yet. let file = match &mut self.file { None => { - let file = tempfile().map_err(StreamError::TempFile)?; + let file = tempfile().context("Failed creating temporary file")?; self.shared.file.store(Some(Arc::new( - file.try_clone().map_err(StreamError::CloneFile)?))); + file.try_clone().context("Failed cloning file handle")?))); self.file.insert(file) }, Some(file) => file }; // Write the data to file. - file.write(data).map_err(StreamError::WriteFile)?; + file.write(data).context("Failed writing to stream file")?; // We must change the stream's current buffer to one for the new block. let block_base = self.length; @@ -282,16 +257,15 @@ impl StreamReader { /// requested length. The method may be called again to access further data. /// pub fn access(&mut self, range: &Range) - -> Result, StreamError> + -> Result, Error> { use Data::*; // First guarantee that the requested data exists, somewhere. let available_length = self.shared.length.load(Acquire); if range.end > available_length { - return Err(StreamError::ReadPastEnd(format!( - "requested read of range {:?}, but stream length is {}", - range, available_length))); + bail!("Requested read of range {range:?}, \ + but stream length is {available_length}") } // Identify the block and the range required from within it. @@ -331,8 +305,8 @@ impl StreamReader { .len(Self::block_size()) .map(file) }; - let new_mmap = - Arc::new(mmap_result.map_err(StreamError::MapFile)?); + let new_mmap = Arc::new( + mmap_result.context("Failed mapping stream file")?); self.mappings.push(block_base, Arc::clone(&new_mmap)); new_mmap } @@ -356,7 +330,7 @@ impl StreamReader { impl Buffer { /// Create a new buffer for the specified block. - fn new(block_base: u64) -> Result{ + fn new(block_base: u64) -> Result { Ok(Buffer { block_base, // Calling System.alloc safely requires the layout to be known to @@ -365,7 +339,7 @@ impl Buffer { ptr: unsafe { let ptr = System.alloc(Self::block_layout()); if ptr.is_null() { - return Err(StreamError::Alloc()); + bail!("Failed to allocate buffer") } ptr } diff --git a/src/tree_list_model.rs b/src/tree_list_model.rs index 9e26e499..d396dee7 100644 --- a/src/tree_list_model.rs +++ b/src/tree_list_model.rs @@ -4,36 +4,22 @@ use std::collections::{BTreeMap, HashSet}; use std::collections::btree_map::Entry; use std::fmt::Debug; use std::marker::PhantomData; -use std::num::TryFromIntError; use std::rc::{Rc, Weak}; +use anyhow::{Context, Error, bail}; + use gtk::prelude::{IsA, Cast, WidgetExt}; use gtk::glib::Object; use gtk::gio::prelude::ListModelExt; use derive_more::AddAssign; use itertools::Itertools; -use thiserror::Error; -use crate::capture::{CaptureReader, CaptureError, ItemSource}; +use crate::capture::{CaptureReader, ItemSource}; use crate::model::GenericModel; use crate::row_data::GenericRowData; use crate::expander::ExpanderWrapper; -#[derive(Error, Debug)] -pub enum ModelError { - #[error(transparent)] - CaptureError(#[from] CaptureError), - #[error(transparent)] - RangeError(#[from] TryFromIntError), - #[error("Node references a dropped parent")] - ParentDropped, - #[error("Internal error: {0}")] - InternalError(String), -} - -use ModelError::InternalError; - type RootNodeRc = Rc>>; pub type ItemNodeRc = Rc>>; pub type ItemNodeWeak = Weak>>; @@ -44,7 +30,7 @@ trait Node { fn item(&self) -> Option<&Item>; /// Parent of this node, or None if the root. - fn parent(&self) -> Result>, ModelError>; + fn parent(&self) -> Result>, Error>; /// Access the children of this node. fn children(&self) -> &Children; @@ -150,7 +136,7 @@ impl Node for RootNode { None } - fn parent(&self) -> Result>, ModelError> { + fn parent(&self) -> Result>, Error> { Ok(None) } @@ -176,8 +162,11 @@ impl Node for ItemNode where Item: Copy { Some(&self.item) } - fn parent(&self) -> Result>, ModelError> { - Ok(Some(self.parent.upgrade().ok_or(ModelError::ParentDropped)?)) + fn parent(&self) -> Result>, Error> { + Ok(Some(self.parent + .upgrade() + .context("Parent dropped")? + )) } fn children(&self) -> &Children { @@ -212,14 +201,14 @@ impl Node for ItemNode where Item: Copy { trait UpdateTotal { fn update_total(&self, expanded: bool, rows_affected: u64) - -> Result<(), ModelError>; + -> Result<(), Error>; } impl UpdateTotal for Rc> where T: Node + 'static, Item: Copy + 'static { fn update_total(&self, expanded: bool, rows_affected: u64) - -> Result<(), ModelError> + -> Result<(), Error> { let mut node_rc: AnyNodeRc = self.clone(); while let Some(parent_rc) = node_rc.clone().borrow().parent()? { @@ -375,7 +364,7 @@ where Item: 'static + Copy + Debug, pub fn new(mut capture: CaptureReader, #[cfg(any(feature="test-ui-replay", feature="record-ui-test"))] on_item_update: Rc>) - -> Result + -> Result { let (completion, item_count) = capture.item_children(None)?; Ok(TreeListModel { @@ -395,7 +384,7 @@ where Item: 'static + Copy + Debug, self.root.borrow().children().total_count } - fn check(&self) -> Result<(), ModelError> { + fn check(&self) -> Result<(), Error> { // Check that we have the expected number of rows in the region map. let expected_count = self.row_count(); let actual_count = self.regions @@ -405,9 +394,8 @@ where Item: 'static + Copy + Debug, .map(|(start, region)| start + region.length) .unwrap_or(0); if expected_count != actual_count { - Err(InternalError(format!( - "Region map total row count is {}, expected {}", - actual_count, expected_count))) + bail!("Region map total row count is {}, expected {}", + actual_count, expected_count) } else { Ok(()) } @@ -418,7 +406,7 @@ where Item: 'static + Copy + Debug, node_ref: &ItemNodeRc, position: u64, expanded: bool) - -> Result<(), ModelError> + -> Result<(), Error> { let mut node = node_ref.borrow_mut(); @@ -428,7 +416,7 @@ where Item: 'static + Copy + Debug, let parent_rc = node.parent .upgrade() - .ok_or(ModelError::ParentDropped)?; + .context("Parent dropped")?; let rows_affected = node.children.direct_count; let expanded_children = node.children.expanded.clone(); @@ -492,16 +480,15 @@ where Item: 'static + Copy + Debug, } fn expand(&self, position: u64, node_ref: &ItemNodeRc) - -> Result + -> Result { // Find the start of the parent region. let (&parent_start, _) = self.regions .borrow() .range(..position) .next_back() - .ok_or_else(|| - InternalError(format!( - "No region before position {position}")))?; + .with_context(|| format!( + "No region before position {position}"))?; // Find position of the new region relative to its parent. let relative_position = position - parent_start; @@ -510,9 +497,8 @@ where Item: 'static + Copy + Debug, let parent = self.regions .borrow_mut() .remove(&parent_start) - .ok_or_else(|| - InternalError(format!( - "Parent not found at position {parent_start}")))?; + .with_context(|| format!( + "Parent not found at position {parent_start}"))?; // Remove all following regions, to iterate over later. let following_regions = self.regions @@ -548,15 +534,14 @@ where Item: 'static + Copy + Debug, } fn collapse(&self, position: u64, node_ref: &ItemNodeRc) - -> Result + -> Result { // Clone the region starting at this position. let region = self.regions .borrow() .get(&position) - .ok_or_else(|| - InternalError(format!( - "No region to delete at position {position}")))? + .with_context(|| + format!("No region to delete at position {position}"))? .clone(); // Remove it with following regions, to iterate over and replace them. @@ -568,9 +553,7 @@ where Item: 'static + Copy + Debug, // Process the effects of removing this region. let update = match ®ion.source { // Root regions cannot be collapsed. - TopLevelItems() => return Err( - InternalError(String::from( - "Unable to collapse root region"))), + TopLevelItems() => bail!("Unable to collapse root region"), // Non-interleaved region is just removed. ChildrenOf(_) => { let (_, _region) = following_regions.next().unwrap(); @@ -595,7 +578,7 @@ where Item: 'static + Copy + Debug, } fn insert_region(&self, position: u64, region: Region) - -> Result<(), ModelError> + -> Result<(), Error> { match self.regions.borrow_mut().entry(position) { Entry::Occupied(mut entry) => { @@ -604,8 +587,7 @@ where Item: 'static + Copy + Debug, entry.insert(region); Ok(()) } else { - Err(InternalError(format!( - "At position {position}, overwriting region"))) + bail!("At position {position}, overwriting region") } }, Entry::Vacant(entry) => { @@ -622,7 +604,7 @@ where Item: 'static + Copy + Debug, parts_before: Vec>, new_region: Region, parts_after: Vec>) - -> Result + -> Result { let length_before: u64 = parts_before .iter() @@ -726,7 +708,7 @@ where Item: 'static + Copy + Debug, self.regions.replace(new_regions); } - pub fn update(&self, model: &Model) -> Result { + pub fn update(&self, model: &Model) -> Result { #[cfg(feature="debug-region-map")] let rows_before = self.row_count(); self.update_node(&self.root, 0, model)?; @@ -751,7 +733,7 @@ where Item: 'static + Copy + Debug, node_rc: &Rc>, mut position: u64, model: &Model) - -> Result + -> Result where T: Node + 'static, Rc>: NodeRcOps, { @@ -901,16 +883,15 @@ where Item: 'static + Copy + Debug, Ok(position) } - fn fetch(&self, position: u64) -> Result, ModelError> { + fn fetch(&self, position: u64) -> Result, Error> { // Fetch the region this row is in. let (start, region) = self.regions .borrow() .range(..=position) .next_back() .map(|(start, region)| (*start, region.clone())) - .ok_or_else(|| - InternalError(format!( - "No region before position {position}")))?; + .with_context(|| format!( + "No region before position {position}"))?; // Get the index of this row relative to the start of that region. let relative_position = region.offset + (position - start); diff --git a/src/ui.rs b/src/ui.rs index 53b82c30..a2d883fd 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -10,6 +10,8 @@ use std::time::Duration; #[cfg(feature="step-decoder")] use std::{io::Read, net::TcpListener}; +use anyhow::{Context as ErrorContext, Error, bail}; + use gtk::gio::ListModel; use gtk::glib::Object; use gtk::{ @@ -40,20 +42,17 @@ use gtk::{ }; use pcap_file::{ - PcapError, DataLink, pcap::{PcapReader, PcapWriter, PcapHeader, RawPcapPacket}, }; use rusb::Context; -use thiserror::Error; use crate::backend::luna::{LunaDevice, LunaHandle, LunaStop, Speed}; use crate::capture::{ create_capture, CaptureReader, CaptureWriter, - CaptureError, ItemSource, TrafficItem, DeviceItem, @@ -67,7 +66,6 @@ use crate::row_data::{ ToGenericRowData, TrafficRowData, DeviceRowData}; -use crate::tree_list_model::ModelError; use crate::util::{fmt_count, fmt_size}; #[cfg(any(feature="test-ui-replay", feature="record-ui-test"))] @@ -95,28 +93,6 @@ enum FileAction { Save, } -#[derive(Error, Debug)] -pub enum PacketryError { - #[error("capture data error: {0}")] - Capture(#[from] CaptureError), - #[error("tree model error: {0}")] - Model(#[from] ModelError), - #[error("I/O error: {0}")] - Io(#[from] std::io::Error), - #[error("pcap error: {0}")] - Pcap(#[from] PcapError), - #[error(transparent)] - Usb(#[from] rusb::Error), - #[error("device not found")] - NotFound, - #[error("LUNA error: {0}")] - Luna(#[from] crate::backend::luna::Error), - #[error("locking failed")] - Lock, - #[error("internal bug: {0}")] - Bug(&'static str) -} - struct DeviceSelector { usb_context: Option, devices: Vec, @@ -128,7 +104,7 @@ struct DeviceSelector { } impl DeviceSelector { - fn new() -> Result { + fn new() -> Result { let selector = DeviceSelector { usb_context: Context::new().ok(), devices: vec![], @@ -166,7 +142,7 @@ impl DeviceSelector { self.speed_dropdown.set_sensitive(sensitive); } - fn scan(&mut self) -> Result { + fn scan(&mut self) -> Result { self.devices = if let Some(context) = self.usb_context.as_mut() { LunaDevice::scan(context)? } else { @@ -189,7 +165,7 @@ impl DeviceSelector { Ok(available) } - fn open(&self) -> Result<(LunaHandle, Speed), PacketryError> { + fn open(&self) -> Result<(LunaHandle, Speed), Error> { let device_id = self.dev_dropdown.selected(); let device = &self.devices[device_id as usize]; let speed_id = self.speed_dropdown.selected() as usize; @@ -239,19 +215,19 @@ pub struct UserInterface { pub recording: Rc>, } -pub fn with_ui(f: F) -> Result<(), PacketryError> - where F: FnOnce(&mut UserInterface) -> Result<(), PacketryError> +pub fn with_ui(f: F) -> Result<(), Error> + where F: FnOnce(&mut UserInterface) -> Result<(), Error> { UI.with(|cell| { if let Some(ui) = cell.borrow_mut().as_mut() { f(ui) } else { - Err(PacketryError::Bug("UI not set up")) + bail!("UI not set up") } }) } -pub fn activate(application: &Application) -> Result<(), PacketryError> { +pub fn activate(application: &Application) -> Result<(), Error> { use FileAction::*; let window = gtk::ApplicationWindow::builder() @@ -441,18 +417,18 @@ fn create_view( let expander = ExpanderWrapper::new(); list_item.set_child(Some(&expander)); }); - let bind = move |list_item: &ListItem| { + let bind = move |list_item: &ListItem| -> Result<(), Error> { let row = list_item .item() - .or_bug("ListItem has no item")? + .context("ListItem has no item")? .downcast::() - .or_bug("Item is not RowData")?; + .or_else(|_| bail!("Item is not RowData"))?; let expander_wrapper = list_item .child() - .or_bug("ListItem has no child widget")? + .context("ListItem has no child widget")? .downcast::() - .or_bug("Child widget is not an ExpanderWrapper")?; + .or_else(|_| bail!("Child widget is not an ExpanderWrapper"))?; let expander = expander_wrapper.expander(); match row.node() { @@ -478,8 +454,7 @@ fn create_view( recording.borrow_mut().log_item_expanded( name, position, expanded); display_error( - model.set_expanded(&node_ref, position, expanded) - .map_err(PacketryError::Model)) + model.set_expanded(&node_ref, position, expanded)) }); expander_wrapper.set_handler(handler); node.attach_widget(&expander_wrapper); @@ -495,15 +470,15 @@ fn create_view( let unbind = move |list_item: &ListItem| { let row = list_item .item() - .or_bug("ListItem has no item")? + .context("ListItem has no item")? .downcast::() - .or_bug("Item is not RowData")?; + .or_else(|_| bail!("Item is not RowData"))?; let expander_wrapper = list_item .child() - .or_bug("ListItem has no child widget")? + .context("ListItem has no child widget")? .downcast::() - .or_bug("Child widget is not an ExpanderWrapper")?; + .or_else(|_| bail!("Child widget is not an ExpanderWrapper"))?; if let Ok(node_ref) = row.node() { node_ref.borrow().remove_widget(&expander_wrapper); @@ -529,7 +504,7 @@ fn create_view( (model, view) } -pub fn reset_capture() -> Result { +pub fn reset_capture() -> Result { let (writer, reader) = create_capture()?; with_ui(|ui| { let (traffic_model, traffic_view) = @@ -556,7 +531,7 @@ pub fn reset_capture() -> Result { Ok(writer) } -pub fn update_view() -> Result<(), PacketryError> { +pub fn update_view() -> Result<(), Error> { with_ui(|ui| { use FileAction::*; #[cfg(feature="record-ui-test")] @@ -631,7 +606,7 @@ pub fn update_view() -> Result<(), PacketryError> { }) } -fn choose_file(action: FileAction) -> Result<(), PacketryError> { +fn choose_file(action: FileAction) -> Result<(), Error> { use FileAction::*; let chooser = WINDOW.with(|cell| { let borrow = cell.borrow(); @@ -665,7 +640,7 @@ fn choose_file(action: FileAction) -> Result<(), PacketryError> { Ok(()) } -fn start_pcap(action: FileAction, path: PathBuf) -> Result<(), PacketryError> { +fn start_pcap(action: FileAction, path: PathBuf) -> Result<(), Error> { use FileAction::*; let writer = if action == Load { Some(reset_capture()?) @@ -741,7 +716,7 @@ fn start_pcap(action: FileAction, path: PathBuf) -> Result<(), PacketryError> { let length: u32 = bytes .len() .try_into() - .or_bug("Packet too large for pcap file")?; + .context("Packet too large for pcap file")?; let packet = RawPcapPacket { ts_sec: 0, ts_frac: 0, @@ -788,7 +763,7 @@ fn start_pcap(action: FileAction, path: PathBuf) -> Result<(), PacketryError> { }) } -pub fn stop_pcap() -> Result<(), PacketryError> { +pub fn stop_pcap() -> Result<(), Error> { STOP.store(true, Ordering::Relaxed); with_ui(|ui| { ui.scan_button.set_sensitive(true); @@ -797,7 +772,7 @@ pub fn stop_pcap() -> Result<(), PacketryError> { }) } -fn detect_hardware() -> Result<(), PacketryError> { +fn detect_hardware() -> Result<(), Error> { with_ui(|ui| { ui.selector.scan()?; ui.capture_button.set_sensitive(ui.selector.device_available()); @@ -805,7 +780,7 @@ fn detect_hardware() -> Result<(), PacketryError> { }) } -pub fn start_luna() -> Result<(), PacketryError> { +pub fn start_luna() -> Result<(), Error> { let writer = reset_capture()?; with_ui(|ui| { let (luna, speed) = ui.selector.open()?; @@ -849,7 +824,7 @@ pub fn start_luna() -> Result<(), PacketryError> { }) } -pub fn stop_luna() -> Result<(), PacketryError> { +pub fn stop_luna() -> Result<(), Error> { with_ui(|ui| { if let Some(stop_handle) = ui.stop_handle.take() { stop_handle.stop()?; @@ -860,10 +835,11 @@ pub fn stop_luna() -> Result<(), PacketryError> { }) } -pub fn display_error(result: Result<(), PacketryError>) { +pub fn display_error(result: Result<(), Error>) { #[cfg(not(feature="test-ui-replay"))] if let Err(e) = result { - let message = format!("{e}"); + let backtrace = e.backtrace(); + let message = format!("{e}\n\nBacktrace:\n\n{backtrace}"); gtk::glib::idle_add_once(move || { WINDOW.with(|win_opt| { match win_opt.borrow().as_ref() { @@ -889,19 +865,3 @@ pub fn display_error(result: Result<(), PacketryError>) { #[cfg(feature="test-ui-replay")] result.unwrap(); } - -trait OrBug { - fn or_bug(self, msg: &'static str) -> Result; -} - -impl OrBug for Option { - fn or_bug(self, msg: &'static str) -> Result { - self.ok_or(PacketryError::Bug(msg)) - } -} - -impl OrBug for Result { - fn or_bug(self, msg: &'static str) -> Result { - self.or(Err(PacketryError::Bug(msg))) - } -}