Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions crates/echo-ipc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,23 @@ pub struct AppStatus {
pub last_run: Option<LastRun>,
pub language_warning: Option<String>,
pub recording_in_process: bool,
pub recording_session_id: Option<String>,
pub capture_stop_requested: bool,
pub recording_revision: u64,
pub current_exe: String,
pub first_path_hit: Option<String>,
pub stale_installs: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, TS)]
#[serde(rename_all = "camelCase")]
pub struct RecordingSnapshot {
pub session_id: Option<String>,
pub phase: AppPhase,
pub capture_stop_requested: bool,
pub revision: u64,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, TS)]
pub enum AppPhase {
Idle,
Expand Down Expand Up @@ -846,6 +858,7 @@ macro_rules! schema_types {
schema::NextSpeechRun => schema::NextSpeechRun,
schema::Readiness => schema::Readiness,
schema::RecordingPolicy => schema::RecordingPolicy,
schema::RecordingSnapshot => schema::RecordingSnapshot,
schema::RecoveryReason => schema::RecoveryReason,
schema::RecoveryTelemetry => schema::RecoveryTelemetry,
schema::ResolvedSpeechEngine => schema::ResolvedSpeechEngine,
Expand Down
28 changes: 24 additions & 4 deletions crates/echo/src/process_identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub struct ProcessObservation {
pub pid: u32,
pub start_time_ticks: u64,
pub state: char,
/// Earliest plausible start time, accounting for `/proc/uptime` precision.
pub start_unix_nanos: Option<u128>,
}

Expand Down Expand Up @@ -63,17 +64,27 @@ fn process_start_unix_nanos(start_time_ticks: u64) -> Option<u128> {
if ticks_per_second == 0 {
return None;
}
let uptime = std::fs::read_to_string("/proc/uptime").ok()?;
let uptime_nanos = decimal_seconds_to_nanos(uptime.split_whitespace().next()?)?;
let now_nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.ok()?
.as_nanos();
let uptime = std::fs::read_to_string("/proc/uptime").ok()?;
let uptime_nanos = decimal_seconds_to_nanos(uptime.split_whitespace().next()?)?;
let started_since_boot = (start_time_ticks as u128)
.checked_mul(1_000_000_000)?
.checked_div(ticks_per_second)?;
now_nanos
.checked_sub(uptime_nanos)?
earliest_start_time(now_nanos, uptime_nanos, started_since_boot)
}

fn earliest_start_time(
now_before_read: u128,
uptime_floor: u128,
started_since_boot: u128,
) -> Option<u128> {
// Linux truncates uptime to hundredths. Use its upper bound so a fresh
// legacy lock is not mistaken for a lock from before the process existed.
now_before_read
.checked_sub(uptime_floor.checked_add(10_000_000)?)?
.checked_add(started_since_boot)
}

Expand Down Expand Up @@ -120,6 +131,15 @@ fn native_word(raw: &[u8]) -> Option<u64> {
mod tests {
use super::*;

#[test]
fn uptime_precision_cannot_place_a_live_process_after_its_new_lock() {
let wall_before_read = 10_005_000_000;
let lock_created = 10_001_000_000;
let earliest = earliest_start_time(wall_before_read, 1_000_000_000, 1_000_000_000).unwrap();
assert_eq!(earliest, 9_995_000_000);
assert!(earliest <= lock_created);
}

#[test]
fn stat_parser_handles_spaces_and_parentheses_in_comm() {
let mut trailing = vec!["0"; 18];
Expand Down
Loading
Loading