1#![allow(dead_code)]
12
13use alloc::string::String;
14use alloc::vec::Vec;
15use spin::Mutex;
16
17const DRAW_BUDGET_MS: u64 = 2000;
21const OFFENSE_LIMIT: u32 = 3;
23
24struct DrawWatch {
25 active_app: Option<String>,
27 start_ms: u64,
29 hang_reported: bool,
31 offenders: Vec<(String, u32)>,
33 not_responding: Vec<String>,
35}
36
37static WATCH: Mutex<DrawWatch> = Mutex::new(DrawWatch {
38 active_app: None,
39 start_ms: 0,
40 hang_reported: false,
41 offenders: Vec::new(),
42 not_responding: Vec::new(),
43});
44
45pub fn enter(app_name: &str) {
47 let now = crate::kernel::timer::get_system_time_ms();
48 let mut w = WATCH.lock();
49 w.active_app = Some(String::from(app_name));
50 w.start_ms = now;
51 w.hang_reported = false;
52}
53
54pub fn exit() {
59 let now = crate::kernel::timer::get_system_time_ms();
60 let mut w = WATCH.lock();
61 let elapsed = now.wrapping_sub(w.start_ms);
62 let was_near_hang = w.hang_reported;
63 if let Some(name) = w.active_app.take() {
64 if was_near_hang {
65 let mut found = false;
67 let mut hit_limit = false;
68 for (n, c) in w.offenders.iter_mut() {
69 if *n == name {
70 *c += 1;
71 if *c >= OFFENSE_LIMIT {
72 hit_limit = true;
73 }
74 found = true;
75 break;
76 }
77 }
78 if !found {
79 w.offenders.push((name.clone(), 1));
80 }
81 crate::println!(
82 "[watchdog] app '{}' near-hang draw took {} ms",
83 name,
84 elapsed
85 );
86 if hit_limit && !w.not_responding.iter().any(|n| n == &name) {
87 w.not_responding.push(name.clone());
88 crate::println!(
89 "[watchdog] app '{}' marked NOT RESPONDING — its draw will be skipped",
90 name
91 );
92 }
93 } else if elapsed > DRAW_BUDGET_MS {
94 crate::println!(
96 "[watchdog] app '{}' slow draw: {} ms (budget {} ms)",
97 name,
98 elapsed,
99 DRAW_BUDGET_MS
100 );
101 } else {
102 w.offenders.retain(|(n, _)| n != &name);
104 }
105 }
106}
107
108pub fn check_from_irq() {
111 if let Some(mut w) = WATCH.try_lock() {
113 if w.hang_reported {
114 return;
115 }
116 if let Some(name) = w.active_app.clone() {
117 let now = crate::kernel::timer::get_system_time_ms();
118 let elapsed = now.wrapping_sub(w.start_ms);
119 if elapsed > DRAW_BUDGET_MS * 2 {
120 w.hang_reported = true;
121 crate::println!(
122 "[watchdog] HANG suspected: app '{}' has been drawing for {} ms",
123 name,
124 elapsed
125 );
126 }
127 }
128 }
129}
130
131pub fn is_not_responding(app_name: &str) -> bool {
133 let w = WATCH.lock();
134 w.not_responding.iter().any(|n| n == app_name)
135}
136
137pub fn clear(app_name: &str) {
139 let mut w = WATCH.lock();
140 w.offenders.retain(|(n, _)| n != app_name);
141 w.not_responding.retain(|n| n != app_name);
142}