Skip to content

Commit

Permalink
cannoli: handle one client by default
Browse files Browse the repository at this point in the history
  • Loading branch information
evanrichter committed May 1, 2024
1 parent 267f995 commit ba38ae9
Show file tree
Hide file tree
Showing 10 changed files with 102 additions and 69 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cannoli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cannoli"
version = "0.1.0"
version = "0.2.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
139 changes: 86 additions & 53 deletions cannoli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -851,13 +851,17 @@ fn handle_client<T>(
Ok(())
}

/// Create a new Cannoli server. This will spin up the required processing
/// needed to talk with QEMU and deserialize messages, while dispatching
/// callbacks to a user-controlled `user_self`
/// Create a new Cannoli server and handle clients in parallel.
///
/// This will spin up the required processing needed to talk with QEMU and
/// deserialize messages, while dispatching callbacks to a user-defined `T`
/// which implements [`Cannoli`].
///
/// Create `threads` number of threads for every connection that comes in.
/// These threads will handle all Cannoli parsing and callbacks
pub fn create_cannoli<T>(threads: usize) -> Result<()>
///
/// See [`run`] to serve a single client.
pub fn run_forever<T>(threads: usize) -> Result<()>
where T: Cannoli + 'static,
T::PidContext: Send + Sync + 'static {
// Create socket, waiting for clients to connect and inform us about some
Expand All @@ -871,55 +875,7 @@ pub fn create_cannoli<T>(threads: usize) -> Result<()>
for stream in listener.incoming() {
// Spawn a thread on new connections
scope.spawn(move || {
// Get access to the stream
let mut stream = stream.expect("Failed to get TCP stream");

// Get the header
let mut header: MaybeUninit<ClientConn> =
MaybeUninit::uninit();
stream.read_exact(unsafe {
core::slice::from_raw_parts_mut(
header.as_mut_ptr() as *mut u8,
core::mem::size_of_val(&header))
}).expect("Failed to get client header");

// Get the actual header now that it's initialized
let header: ClientConn = unsafe { header.assume_init() };

// Get the pcomm and comm
let mut comm =
vec![0u8; header.pcomm_len as usize +
header.comm_len as usize];
stream.read_exact(&mut comm)
.expect("Failed to get client pcomm and comm");

// Construct client information
let ci = ClientInfo {
// IPC pipe UID
uid: header.uid,

// Architecture
arch: Architecture::from(header.arch),

// Endianness
big_endian: header.big_endian != 0,

// PIDs
ppid: header.ppid,
pid: header.pid,
tid: header.tid,

pcomm: std::str::from_utf8(
&comm[..header.pcomm_len as usize])
.ok().map(|x| x.to_string()),
comm: std::str::from_utf8(
&comm[header.pcomm_len as usize..])
.ok().map(|x| x.to_string()),
};

// Handle the client
handle_client::<T>(stream, threads, &ci)
.expect("Failed to handle client");
handle_connection::<T>(stream.expect("Failed to get TCP stream"), threads);
});
}

Expand All @@ -928,6 +884,83 @@ pub fn create_cannoli<T>(threads: usize) -> Result<()>
})
}

/// Create a new Cannoli server and handle one client.
///
/// This will spin up the required processing needed to talk with QEMU and
/// deserialize messages, while dispatching callbacks to a user-defined `T`
/// which implements [`Cannoli`].
///
/// Create `threads` number of threads for the connection that comes in.
/// These threads will handle all Cannoli parsing and callbacks
///
/// See [`run_forever`] to serve multiple clients in parallel.
pub fn run<T>(threads: usize) -> Result<()>
where T: Cannoli + 'static,
T::PidContext: Send + Sync + 'static {
// Create socket, waiting for clients to connect and inform us about some
// memory regions
let listener = TcpListener::bind("127.0.0.1:11458")
.map_err(Error::Bind)?;

// Wait for connection
let (stream, _from) = listener.accept().unwrap();

// Handle one connection then exit
handle_connection::<T>(stream, threads);

// All done!
Ok(())
}

fn handle_connection<T>(mut stream: TcpStream, threads: usize) where T: Cannoli + 'static {
// Get the header
let mut header: MaybeUninit<ClientConn> =
MaybeUninit::uninit();
stream.read_exact(unsafe {
core::slice::from_raw_parts_mut(
header.as_mut_ptr() as *mut u8,
core::mem::size_of_val(&header))
}).expect("Failed to get client header");

// Get the actual header now that it's initialized
let header: ClientConn = unsafe { header.assume_init() };

// Get the pcomm and comm
let mut comm =
vec![0u8; header.pcomm_len as usize +
header.comm_len as usize];
stream.read_exact(&mut comm)
.expect("Failed to get client pcomm and comm");

// Construct client information
let ci = ClientInfo {
// IPC pipe UID
uid: header.uid,

// Architecture
arch: Architecture::from(header.arch),

// Endianness
big_endian: header.big_endian != 0,

// PIDs
ppid: header.ppid,
pid: header.pid,
tid: header.tid,

pcomm: std::str::from_utf8(
&comm[..header.pcomm_len as usize])
.ok().map(|x| x.to_string()),
comm: std::str::from_utf8(
&comm[header.pcomm_len as usize..])
.ok().map(|x| x.to_string()),
};

// Handle the client
handle_client::<T>(stream, threads, &ci)
.expect("Failed to handle client");
}

/// Trait which must be implemented by a user to implement their hooks and
/// analysis of a given QEMU trace
pub trait Cannoli: Send + Sync {
Expand Down
4 changes: 2 additions & 2 deletions examples/backtrace/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#![feature(array_chunks)]

use std::sync::{Arc, Mutex};
use cannoli::{Cannoli, create_cannoli};
use cannoli::Cannoli;

use crate::utils::backtrace::{
BacktraceState,
Expand Down Expand Up @@ -128,5 +128,5 @@ impl Cannoli for BacktraceExample {
}

fn main() {
create_cannoli::<BacktraceExample>(4).unwrap();
cannoli::run::<BacktraceExample>(4).unwrap();
}
4 changes: 2 additions & 2 deletions examples/benchmark/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use std::sync::Arc;
use std::time::Instant;
use cannoli::{Cannoli, create_cannoli};
use cannoli::Cannoli;

/// Get the time stamp counter
fn rdtsc() -> u64 {
Expand Down Expand Up @@ -74,6 +74,6 @@ impl Cannoli for Benchmark {
}

fn main() {
create_cannoli::<Benchmark>(4).unwrap();
cannoli::run::<Benchmark>(4).unwrap();
}

4 changes: 2 additions & 2 deletions examples/benchmark_graph/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use std::sync::Arc;
use std::time::Instant;
use cannoli::{Cannoli, create_cannoli};
use cannoli::Cannoli;

/// Get the time stamp counter
fn rdtsc() -> u64 {
Expand Down Expand Up @@ -77,6 +77,6 @@ impl Cannoli for Benchmark {
}

fn main() {
create_cannoli::<Benchmark>(4).unwrap();
cannoli::run::<Benchmark>(4).unwrap();
}

4 changes: 2 additions & 2 deletions examples/coverage/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use std::sync::{Mutex, LazyLock, Arc};
use std::process::Command;
use std::collections::{HashMap, BTreeMap, BTreeSet};
use cannoli::{Cannoli, create_cannoli};
use cannoli::Cannoli;

struct CoverageDb {
/// Symbols for the process
Expand Down Expand Up @@ -200,6 +200,6 @@ impl Cannoli for Coverage {
}

fn main() {
create_cannoli::<Coverage>(4).unwrap();
cannoli::run::<Coverage>(4).unwrap();
}

4 changes: 2 additions & 2 deletions examples/regtrace/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#![feature(array_chunks)]

use std::sync::Arc;
use cannoli::{Cannoli, create_cannoli};
use cannoli::Cannoli;

/// The structure we implement [`Cannoli`] for!
struct Coverage;
Expand Down Expand Up @@ -42,6 +42,6 @@ impl Cannoli for Coverage {
}

fn main() {
create_cannoli::<Coverage>(4).unwrap();
cannoli::run::<Coverage>(4).unwrap();
}

4 changes: 2 additions & 2 deletions examples/symbolizer/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! An example user of Cannoli which symbolizes a trace

use std::sync::Arc;
use cannoli::{Cannoli, create_cannoli};
use cannoli::Cannoli;

/// An original pointer address, and then a resolved symbol + offset for that
/// address
Expand Down Expand Up @@ -170,6 +170,6 @@ impl Cannoli for Symbolizer {
}

fn main() {
create_cannoli::<Symbolizer>(2).unwrap();
cannoli::run::<Symbolizer>(2).unwrap();
}

4 changes: 2 additions & 2 deletions examples/visualization/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use std::sync::Arc;
use std::time::Instant;
use std::sync::atomic::{AtomicU64, Ordering};
use cannoli::{Cannoli, create_cannoli};
use cannoli::Cannoli;
use snuffles::{CameraMode, Vsync, Msaa, EventHandler, Window, Vertex};
use snuffles::{Persist, DrawCommand};

Expand Down Expand Up @@ -258,7 +258,7 @@ impl EventHandler for Renderer {
fn main() {
std::thread::spawn(|| {
// Create Cannoli handler!
create_cannoli::<Tracer>(2).expect("Cannoli handler returned error");
cannoli::run::<Tracer>(2).expect("Cannoli handler returned error");
});

Window::<Renderer>::new("Hello world",
Expand Down

0 comments on commit ba38ae9

Please sign in to comment.