@@ -57,50 +57,56 @@ def sample(self, collector, duration_sec=10):
5757 last_sample_time = start_time
5858 realtime_update_interval = 1.0 # Update every second
5959 last_realtime_update = start_time
60+ interrupted = False
6061
61- while running_time < duration_sec :
62- # Check if live collector wants to stop
63- if hasattr (collector , 'running' ) and not collector .running :
64- break
65-
66- current_time = time .perf_counter ()
67- if next_time < current_time :
68- try :
69- stack_frames = self .unwinder .get_stack_trace ()
70- collector .collect (stack_frames )
71- except ProcessLookupError :
72- duration_sec = current_time - start_time
62+ try :
63+ while running_time < duration_sec :
64+ # Check if live collector wants to stop
65+ if hasattr (collector , 'running' ) and not collector .running :
7366 break
74- except (RuntimeError , UnicodeDecodeError , MemoryError , OSError ):
75- collector .collect_failed_sample ()
76- errors += 1
77- except Exception as e :
78- if not self ._is_process_running ():
79- break
80- raise e from None
81-
82- # Track actual sampling intervals for real-time stats
83- if num_samples > 0 :
84- actual_interval = current_time - last_sample_time
85- self .sample_intervals .append (
86- 1.0 / actual_interval
87- ) # Convert to Hz
88- self .total_samples += 1
89-
90- # Print real-time statistics if enabled
91- if (
92- self .realtime_stats
93- and (current_time - last_realtime_update )
94- >= realtime_update_interval
95- ):
96- self ._print_realtime_stats ()
97- last_realtime_update = current_time
98-
99- last_sample_time = current_time
100- num_samples += 1
101- next_time += sample_interval_sec
10267
68+ current_time = time .perf_counter ()
69+ if next_time < current_time :
70+ try :
71+ stack_frames = self .unwinder .get_stack_trace ()
72+ collector .collect (stack_frames )
73+ except ProcessLookupError :
74+ duration_sec = current_time - start_time
75+ break
76+ except (RuntimeError , UnicodeDecodeError , MemoryError , OSError ):
77+ collector .collect_failed_sample ()
78+ errors += 1
79+ except Exception as e :
80+ if not self ._is_process_running ():
81+ break
82+ raise e from None
83+
84+ # Track actual sampling intervals for real-time stats
85+ if num_samples > 0 :
86+ actual_interval = current_time - last_sample_time
87+ self .sample_intervals .append (
88+ 1.0 / actual_interval
89+ ) # Convert to Hz
90+ self .total_samples += 1
91+
92+ # Print real-time statistics if enabled
93+ if (
94+ self .realtime_stats
95+ and (current_time - last_realtime_update )
96+ >= realtime_update_interval
97+ ):
98+ self ._print_realtime_stats ()
99+ last_realtime_update = current_time
100+
101+ last_sample_time = current_time
102+ num_samples += 1
103+ next_time += sample_interval_sec
104+
105+ running_time = time .perf_counter () - start_time
106+ except KeyboardInterrupt :
107+ interrupted = True
103108 running_time = time .perf_counter () - start_time
109+ print ("Interrupted by user." )
104110
105111 # Clear real-time stats line if it was being displayed
106112 if self .realtime_stats and len (self .sample_intervals ) > 0 :
@@ -121,7 +127,7 @@ def sample(self, collector, duration_sec=10):
121127 collector .set_stats (self .sample_interval_usec , running_time , sample_rate , error_rate , mode = self .mode )
122128
123129 expected_samples = int (duration_sec / sample_interval_sec )
124- if num_samples < expected_samples and not is_live_mode :
130+ if num_samples < expected_samples and not is_live_mode and not interrupted :
125131 print (
126132 f"Warning: missed { expected_samples - num_samples } samples "
127133 f"from the expected total of { expected_samples } "
0 commit comments