Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transaction-level and packet-level views of capture #145

Merged
merged 8 commits into from
Oct 8, 2024
Merged
254 changes: 213 additions & 41 deletions src/capture.rs

Large diffs are not rendered by default.

64 changes: 7 additions & 57 deletions src/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use anyhow::{Context, Error, bail};
use crate::capture::prelude::*;
use crate::rcu::SingleWriterRcu;
use crate::usb::{self, prelude::*, validate_packet};
use crate::vec_map::{VecMap, Key};
use crate::vec_map::VecMap;

struct EndpointData {
device_id: DeviceId,
Expand Down Expand Up @@ -504,33 +504,9 @@ impl EndpointData {
}
}

#[derive(Copy, Clone)]
struct EndpointKey {
dev_addr: DeviceAddr,
direction: Direction,
ep_num: EndpointNum,
}

impl Key for EndpointKey {
fn id(self) -> usize {
self.dev_addr.0 as usize * 32 +
self.direction as usize * 16 +
self.ep_num.0 as usize
}

fn key(id: usize) -> EndpointKey {
EndpointKey {
dev_addr: DeviceAddr((id / 32) as u8),
direction: Direction::from(((id / 16) % 2) as u8),
ep_num: EndpointNum((id % 16) as u8),
}
}
}

pub struct Decoder {
capture: CaptureWriter,
device_index: VecMap<DeviceAddr, DeviceId>,
endpoint_index: VecMap<EndpointKey, EndpointId>,
endpoint_data: VecMap<EndpointId, EndpointData>,
last_endpoint_state: Vec<u8>,
last_item_endpoint: Option<EndpointId>,
Expand All @@ -543,7 +519,6 @@ impl Decoder {
let mut decoder = Decoder {
capture,
device_index: VecMap::new(),
endpoint_index: VecMap::new(),
endpoint_data: VecMap::new(),
last_endpoint_state: Vec::new(),
last_item_endpoint: None,
Expand Down Expand Up @@ -604,46 +579,21 @@ impl Decoder {
Ok(self.capture)
}

pub fn token_endpoint(&mut self, pid: PID, token: &TokenFields)
fn packet_endpoint(&mut self, pid: PID, packet: &[u8])
-> Result<EndpointId, Error>
{
let dev_addr = token.device_address();
let ep_num = token.endpoint_number();
let direction = match (ep_num.0, pid) {
(0, _) => Direction::Out,
(_, PID::SETUP) => Direction::Out,
(_, PID::IN) => Direction::In,
(_, PID::OUT) => Direction::Out,
(_, PID::PING) => Direction::Out,
_ => bail!("PID {pid} does not indicate a direction")
};
let key = EndpointKey {
dev_addr,
ep_num,
direction
};
Ok(match self.endpoint_index.get(key) {
Some(id) => *id,
None => {
Ok(match self.capture.shared.packet_endpoint(pid, packet) {
Ok(id) => id,
Err(key) => {
let id = self.add_endpoint(
key.dev_addr, key.ep_num, key.direction)?;
self.endpoint_index.set(key, id);
self.capture.shared.endpoint_index
.update(|map| map.set(key, id));
id
}
})
}

fn packet_endpoint(&mut self, pid: PID, packet: &[u8])
-> Result<EndpointId, Error>
{
Ok(match PacketFields::from_packet(packet) {
PacketFields::SOF(_) => FRAMING_EP_ID,
PacketFields::Token(token) =>
self.token_endpoint(pid, &token)?,
_ => INVALID_EP_ID,
})
}

fn transaction_update(&mut self, packet_id: PacketId, packet: &[u8])
-> Result<(), Error>
{
Expand Down
31 changes: 20 additions & 11 deletions src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,23 @@ use gtk::{gio, glib};

use anyhow::Error;

use crate::capture::{CaptureReader, TrafficItem, DeviceItem};
use crate::capture::{
CaptureReader,
TrafficItem,
TrafficViewMode,
DeviceItem,
DeviceViewMode,
};
use crate::tree_list_model::{TreeListModel, ItemNodeRc};

/// Trait implemented by each of our ListModel implementations.
pub trait GenericModel<Item> where Self: Sized {
pub trait GenericModel<Item, ViewMode> where Self: Sized {
/// Whether this model has timestamps.
const HAS_TIMES: bool;

/// Create a new model instance for the given capture.
fn new(capture: CaptureReader,
view_mode: ViewMode,
#[cfg(any(test, feature="record-ui-test"))]
on_item_update: Rc<RefCell<dyn FnMut(u32, String)>>)
-> Result<Self, Error>;
Expand Down Expand Up @@ -49,24 +56,26 @@ pub trait GenericModel<Item> where Self: Sized {

/// Define the outer type exposed to our Rust code.
macro_rules! model {
($model: ident, $item: ident, $has_times: literal) => {
($model: ident, $item: ident, $view_mode: ident, $has_times: literal) => {

glib::wrapper! {
pub struct $model(ObjectSubclass<imp::$model>)
@implements gio::ListModel;
}

impl GenericModel<$item> for $model {
impl GenericModel<$item, $view_mode> for $model {
const HAS_TIMES: bool = $has_times;

fn new(capture: CaptureReader,
view_mode: $view_mode,
#[cfg(any(test, feature="record-ui-test"))]
on_item_update: Rc<RefCell<dyn FnMut(u32, String)>>)
-> Result<Self, Error>
{
let model: $model = glib::Object::new::<$model>();
let tree = TreeListModel::new(
capture,
view_mode,
#[cfg(any(test, feature="record-ui-test"))]
on_item_update)?;
model.imp().tree.replace(Some(tree));
Expand Down Expand Up @@ -112,26 +121,26 @@ macro_rules! model {
}

// Repeat the above boilerplate for each model.
model!(TrafficModel, TrafficItem, true);
model!(DeviceModel, DeviceItem, false);
model!(TrafficModel, TrafficItem, TrafficViewMode, true);
model!(DeviceModel, DeviceItem, DeviceViewMode, false);

/// The internal implementation module.
mod imp {
use gio::subclass::prelude::*;
use gtk::{gio, glib, prelude::*};

use std::cell::RefCell;
use crate::capture::{TrafficItem, DeviceItem};
use crate::capture::{TrafficItem, TrafficViewMode, DeviceItem, DeviceViewMode};
use crate::row_data::{TrafficRowData, DeviceRowData};
use crate::tree_list_model::TreeListModel;

/// Define the inner type to be used in the GObject type system.
macro_rules! model {
($model:ident, $item:ident, $row_data:ident) => {
($model:ident, $item:ident, $row_data:ident, $view_mode: ident) => {
#[derive(Default)]
pub struct $model {
pub(super) tree: RefCell<Option<
TreeListModel<$item, super::$model, $row_data>>>,
TreeListModel<$item, super::$model, $row_data, $view_mode>>>,
}

#[glib::object_subclass]
Expand Down Expand Up @@ -168,6 +177,6 @@ mod imp {
}

// Repeat the above boilerplate for each model.
model!(TrafficModel, TrafficItem, TrafficRowData);
model!(DeviceModel, DeviceItem, DeviceRowData);
model!(TrafficModel, TrafficItem, TrafficRowData, TrafficViewMode);
model!(DeviceModel, DeviceItem, DeviceRowData, DeviceViewMode);
}
24 changes: 14 additions & 10 deletions src/record_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,18 +149,19 @@ impl Recording {
}
}

pub fn log_items_changed<Model, Item>(
pub fn log_items_changed<Model, Item, ViewMode>(
&mut self,
name: &str,
model: &Model,
position: u32,
removed: u32,
added: u32)
where
Model: ListModelExt + GenericModel<Item>,
CaptureReader: ItemSource<Item>,
Model: ListModelExt + GenericModel<Item, ViewMode>,
CaptureReader: ItemSource<Item, ViewMode>,
Object: ToGenericRowData<Item>,
Item: Copy
Item: Copy,
ViewMode: Copy,
{
if (removed, added) == (0, 0) {
return;
Expand Down Expand Up @@ -195,13 +196,16 @@ impl Recording {
}
}

fn item_text<Model, Item>(&mut self,
model: &Model,
position: u32) -> String
where Model: ListModelExt + GenericModel<Item>,
CaptureReader: ItemSource<Item>,
fn item_text<Model, Item, ViewMode>(
&mut self,
model: &Model,
position: u32
) -> String
where Model: ListModelExt + GenericModel<Item, ViewMode>,
CaptureReader: ItemSource<Item, ViewMode>,
Object: ToGenericRowData<Item>,
Item: Copy
Item: Copy,
ViewMode: Copy
{
let item = model
.item(position)
Expand Down
29 changes: 15 additions & 14 deletions src/test_replay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use gtk::prelude::*;
use itertools::assert_equal;
use serde_json::Deserializer;

use crate::capture::TrafficViewMode;
use crate::decoder::Decoder;
use crate::pcap::Loader;
use crate::model::GenericModel;
Expand Down Expand Up @@ -179,19 +180,6 @@ fn set_expanded(ui: &mut UserInterface,
expanded: bool)
{
match name {
"traffic" => {
let model = ui.traffic_model
.as_ref()
.expect("UI has no traffic model");
let node = model.item(position)
.expect("Failed to retrieve list item")
.downcast::<TrafficRowData>()
.expect("List item is not TrafficRowData")
.node()
.expect("Failed to get node from TrafficRowData");
model.set_expanded(&node, position, expanded)
.expect("Failed to expand/collapse item");
},
"devices" => {
let model = ui.device_model
.as_ref()
Expand All @@ -205,6 +193,19 @@ fn set_expanded(ui: &mut UserInterface,
model.set_expanded(&node, position, expanded)
.expect("Failed to expand/collapse item");
},
_ => panic!("Unknown model name")
log_name => {
let mode = TrafficViewMode::from_log_name(log_name);
let model = ui.traffic_models
.get(&mode)
.expect("UI has no traffic model");
let node = model.item(position)
.expect("Failed to retrieve list item")
.downcast::<TrafficRowData>()
.expect("List item is not TrafficRowData")
.node()
.expect("Failed to get node from TrafficRowData");
model.set_expanded(&node, position, expanded)
.expect("Failed to expand/collapse item");
},
}
}
26 changes: 17 additions & 9 deletions src/tree_list_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,30 +346,35 @@ impl std::fmt::Display for ModelUpdate {
}
}

pub struct TreeListModel<Item, Model, RowData> {
pub struct TreeListModel<Item, Model, RowData, ViewMode> {
_marker: PhantomData<(Model, RowData)>,
capture: RefCell<CaptureReader>,
view_mode: ViewMode,
root: RootNodeRc<Item>,
regions: RefCell<BTreeMap<u64, Region<Item>>>,
#[cfg(any(test, feature="record-ui-test"))]
on_item_update: Rc<RefCell<dyn FnMut(u32, String)>>,
}

impl<Item, Model, RowData> TreeListModel<Item, Model, RowData>
impl<Item, Model, RowData, ViewMode> TreeListModel<Item, Model, RowData, ViewMode>
where Item: 'static + Copy + Debug,
Model: GenericModel<Item> + ListModelExt,
ViewMode: Copy,
Model: GenericModel<Item, ViewMode> + ListModelExt,
RowData: GenericRowData<Item> + IsA<Object> + Cast,
CaptureReader: ItemSource<Item>,
CaptureReader: ItemSource<Item, ViewMode>,
{
pub fn new(mut capture: CaptureReader,
view_mode: ViewMode,
#[cfg(any(test, feature="record-ui-test"))]
on_item_update: Rc<RefCell<dyn FnMut(u32, String)>>)
-> Result<Self, Error>
{
let (completion, item_count) = capture.item_children(None)?;
let (completion, item_count) =
capture.item_children(None, view_mode)?;
Ok(TreeListModel {
_marker: PhantomData,
capture: RefCell::new(capture.clone()),
view_mode,
root: Rc::new(RefCell::new(RootNode {
children: Children::new(item_count),
complete: completion.is_complete(),
Expand Down Expand Up @@ -749,7 +754,8 @@ where Item: 'static + Copy + Debug,

// Check if this node had children added and/or was completed.
let mut cap = self.capture.borrow_mut();
let (completion, new_direct_count) = cap.item_children(node.item())?;
let (completion, new_direct_count) =
cap.item_children(node.item(), self.view_mode)?;
let completed = completion.is_complete();
let children_added = new_direct_count - old_direct_count;
drop(node);
Expand Down Expand Up @@ -925,8 +931,10 @@ where Item: 'static + Copy + Debug,
// Otherwise, fetch it from the database.
let mut cap = self.capture.borrow_mut();
let mut parent = parent_ref.borrow_mut();
let item = cap.item(parent.item(), relative_position)?;
let (completion, child_count) = cap.item_children(Some(&item))?;
let item =
cap.item(parent.item(), self.view_mode, relative_position)?;
let (completion, child_count) =
cap.item_children(Some(&item), self.view_mode)?;
let node = ItemNode {
item,
parent: Rc::downgrade(&parent_ref),
Expand Down Expand Up @@ -958,7 +966,7 @@ where Item: 'static + Copy + Debug,

pub fn connectors(&self, item: &Item) -> String {
let mut cap = self.capture.borrow_mut();
match cap.connectors(item) {
match cap.connectors(self.view_mode, item) {
Ok(string) => string,
Err(e) => format!("Error: {e:?}")
}
Expand Down
Loading
Loading