use std::fmt;
use std::num::NonZeroUsize;
use std::str::FromStr;
use nix::sys::signal::Signal;
use reverie_syscalls::Sysno;
use serde::Deserialize;
use serde::Serialize;
use serde::Serializer;
use serde::de;
use crate::pid::DetTid;
use crate::time::DetTime;
use crate::time::LogicalDuration;
use crate::time::LogicalTime;
#[derive(PartialEq, Debug, Eq, Clone, Hash, Serialize, Deserialize)]
pub struct SchedEvent {
pub dettid: DetTid,
pub op: Op,
pub count: u32,
pub start_rip: Option<InstructionPointer>,
pub end_rip: Option<InstructionPointer>,
pub end_time: Option<LogicalTime>,
}
impl fmt::Display for SchedEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "(tid{}", self.dettid)?;
if self.count > 1 {
write!(f, " cnt={}", self.count)?;
}
if let Some(srip) = self.start_rip {
write!(f, " strt={:#x}", srip)?;
}
if let Some(erip) = self.end_rip {
write!(f, " end={:#x}", erip)?;
}
if let Some(time) = self.end_time {
write!(f, " time={}", time)?;
}
write!(f, " {:?})", self.op)?;
Ok(())
}
}
impl SchedEvent {
pub fn syscall(dettid: DetTid, sysno: Sysno, phase: SyscallPhase) -> SchedEvent {
SchedEvent {
dettid,
op: Op::Syscall(sysno, phase),
count: 1,
start_rip: None,
end_rip: None,
end_time: None,
}
}
pub fn branches(dettid: DetTid, count: u32) -> SchedEvent {
SchedEvent {
dettid,
op: Op::Branch,
count,
start_rip: None, end_rip: None,
end_time: None,
}
}
pub fn with_time(mut self, time: LogicalDuration) -> SchedEvent {
self.end_time = Some(time);
self
}
pub fn with_dettime(mut self, dt: &DetTime) -> SchedEvent {
self.end_time = Some(dt.without_starting());
self
}
pub fn with_start_rip(mut self, start_rip: InstructionPointer) -> Self {
self.start_rip = Some(start_rip);
self
}
pub fn with_end_rip(mut self, end_rip: InstructionPointer) -> Self {
self.end_rip = Some(end_rip);
self
}
}
pub type InstructionPointer = NonZeroUsize;
#[derive(PartialEq, Debug, Eq, Copy, Clone, Hash, Serialize, Deserialize)]
pub enum SyscallPhase {
Prehook,
Polling,
Posthook,
}
#[derive(PartialEq, Debug, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
pub struct SigWrapper(pub i32);
impl SigWrapper {
pub fn raw(&self) -> i32 {
self.0
}
pub fn signal(&self) -> Option<Signal> {
Signal::try_from(self.0).ok()
}
pub fn as_string(&self) -> String {
match self.signal() {
Some(signal) => signal.as_str().to_string(),
None => format!("SIG{}", self.0),
}
}
}
impl std::fmt::Display for SigWrapper {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_string())
}
}
impl Serialize for SigWrapper {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.as_string())
}
}
impl<'de> de::Deserialize<'de> for SigWrapper {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
struct SignalVisitor;
impl<'de> de::Visitor<'de> for SignalVisitor {
type Value = SigWrapper;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "string representing a signal")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
SigWrapper::from_str(v).map_err(serde::de::Error::custom)
}
}
deserializer.deserialize_str(SignalVisitor)
}
}
impl FromStr for SigWrapper {
type Err = anyhow::Error;
fn from_str(s: &str) -> anyhow::Result<Self> {
if let Ok(signal) = Signal::from_str(s) {
return Ok(SigWrapper(signal as i32));
}
if let Some(rest) = s.strip_prefix("SIG")
&& let Ok(raw) = rest.parse::<i32>()
&& raw > 0
{
return Ok(SigWrapper(raw));
}
anyhow::bail!("not a signal: {s}")
}
}
impl From<Signal> for SigWrapper {
fn from(signal: Signal) -> Self {
Self(signal as i32)
}
}
#[cfg(test)]
mod sigwrapper_tests {
use super::*;
#[test]
fn every_previously_representable_signal_serializes_byte_identically() {
for raw in 1..=31i32 {
let Ok(signal) = Signal::try_from(raw) else {
continue;
};
let wrapper = SigWrapper::from(signal);
let json = serde_json::to_string(&wrapper).expect("serialize");
let expected = serde_json::to_string(signal.as_str()).expect("serialize name");
assert_eq!(
json,
expected,
"signal {raw} ({}) changed its serialized form",
signal.as_str()
);
}
}
#[test]
fn realtime_signals_gain_a_distinct_spelling() {
for raw in 32..=64i32 {
let wrapper = SigWrapper(raw);
assert_eq!(wrapper.as_string(), format!("SIG{raw}"));
assert_eq!(wrapper.signal(), None, "nix must still not name {raw}");
}
}
#[test]
fn every_signal_round_trips_through_serde_and_fromstr() {
for raw in 1..=64i32 {
let wrapper = SigWrapper(raw);
let json = serde_json::to_string(&wrapper).expect("serialize");
let back: SigWrapper = serde_json::from_str(&json).expect("deserialize");
assert_eq!(back, wrapper, "serde round-trip lost signal {raw}");
let parsed = SigWrapper::from_str(&wrapper.as_string()).expect("from_str");
assert_eq!(parsed, wrapper, "FromStr round-trip lost signal {raw}");
}
}
#[test]
fn both_spellings_deserialize() {
let by_name: SigWrapper = serde_json::from_str("\"SIGUSR1\"").expect("name");
assert_eq!(by_name, SigWrapper::from(Signal::SIGUSR1));
let by_number: SigWrapper = serde_json::from_str("\"SIG40\"").expect("number");
assert_eq!(by_number, SigWrapper(40));
}
}
#[derive(PartialEq, Debug, Eq, Copy, Clone, Hash, Serialize, Deserialize)]
pub enum Op {
Branch,
Rdtsc,
Cpuid,
Syscall(Sysno, SyscallPhase),
OtherInstructions,
SignalReceived(SigWrapper),
}