| 726 | } |
| 727 | |
| 728 | fn hang_status( |
| 729 | tracker: &mut HangTracker, |
| 730 | display_signal: DisplaySignal, |
| 731 | now_ms: u64, |
| 732 | cpu_percent: f64, |
| 733 | ) -> HangStatus { |
| 734 | if display_signal.frame_sequence == 0 { |
| 735 | return HangStatus { |
| 736 | state: "unknown".to_owned(), |
| 737 | stale_ms: None, |
| 738 | reason: "No display frame signal is available yet.".to_owned(), |
| 739 | }; |
| 740 | } |
| 741 | |
| 742 | if tracker.last_frame_sequence != display_signal.frame_sequence { |
| 743 | tracker.last_frame_sequence = display_signal.frame_sequence; |
| 744 | tracker.last_frame_change_ms = now_ms; |
| 745 | } |
| 746 | if tracker.last_frame_change_ms == 0 { |
| 747 | tracker.last_frame_change_ms = display_signal.last_frame_at_ms.max(now_ms); |
| 748 | } |
| 749 | |
| 750 | let stale_ms = now_ms.saturating_sub(tracker.last_frame_change_ms); |
| 751 | if stale_ms >= 5_000 && cpu_percent >= 30.0 { |
| 752 | HangStatus { |
| 753 | state: "busy".to_owned(), |
| 754 | stale_ms: Some(stale_ms), |
| 755 | reason: "Display frames have not changed while the process is using CPU.".to_owned(), |
| 756 | } |
| 757 | } else if stale_ms >= 8_000 && cpu_percent < 5.0 { |
| 758 | HangStatus { |
| 759 | state: "quiet".to_owned(), |
| 760 | stale_ms: Some(stale_ms), |
| 761 | reason: "Display frames have not changed and the process is mostly idle.".to_owned(), |
| 762 | } |
| 763 | } else { |
| 764 | HangStatus { |
| 765 | state: "responsive".to_owned(), |
| 766 | stale_ms: Some(stale_ms), |
| 767 | reason: "Display frame progress is recent.".to_owned(), |
| 768 | } |
| 769 | } |
| 770 | } |
| 771 | |
| 772 | fn prune_history(history: &mut VecDeque<PerformanceSample>, now_ms: u64) { |
| 773 | let retention_start = now_ms.saturating_sub(HISTORY_RETENTION_MS); |