@@ -11,6 +11,24 @@ use std::path::Path;
1111#[ cfg( target_os = "linux" ) ]
1212use std:: path:: PathBuf ;
1313
14+ fn perf_log ( msg : & str ) {
15+ use std:: io:: Write ;
16+ for path in & [ "/var/log/openshell-perf.log" , "/tmp/openshell-perf.log" ] {
17+ if let Ok ( mut f) = std:: fs:: OpenOptions :: new ( )
18+ . create ( true )
19+ . append ( true )
20+ . open ( path)
21+ {
22+ let now = std:: time:: SystemTime :: now ( )
23+ . duration_since ( std:: time:: UNIX_EPOCH )
24+ . unwrap_or_default ( ) ;
25+ let _ = writeln ! ( f, "[{:.3}] {}" , now. as_secs_f64( ) , msg) ;
26+ return ;
27+ }
28+ }
29+ eprintln ! ( "PERF_LOG_FALLBACK: {msg}" ) ;
30+ }
31+
1432/// Read the binary path of a process via `/proc/{pid}/exe` symlink.
1533///
1634/// Returns the canonical path to the executable that the process is running.
@@ -48,9 +66,33 @@ pub fn resolve_tcp_peer_binary(entrypoint_pid: u32, peer_port: u16) -> Result<Pa
4866/// Needed for the ancestor walk: we must know the PID to walk `/proc/<pid>/status` PPid chain.
4967#[ cfg( target_os = "linux" ) ]
5068pub fn resolve_tcp_peer_identity ( entrypoint_pid : u32 , peer_port : u16 ) -> Result < ( PathBuf , u32 ) > {
69+ let start = std:: time:: Instant :: now ( ) ;
70+
71+ let phase = std:: time:: Instant :: now ( ) ;
5172 let inode = parse_proc_net_tcp ( entrypoint_pid, peer_port) ?;
73+ perf_log ( & format ! (
74+ " parse_proc_net_tcp: {}ms inode={}" ,
75+ phase. elapsed( ) . as_millis( ) , inode
76+ ) ) ;
77+
78+ let phase = std:: time:: Instant :: now ( ) ;
5279 let pid = find_pid_by_socket_inode ( inode, entrypoint_pid) ?;
80+ perf_log ( & format ! (
81+ " find_pid_by_socket_inode: {}ms pid={}" ,
82+ phase. elapsed( ) . as_millis( ) , pid
83+ ) ) ;
84+
85+ let phase = std:: time:: Instant :: now ( ) ;
5386 let path = binary_path ( pid. cast_signed ( ) ) ?;
87+ perf_log ( & format ! (
88+ " binary_path: {}ms path={}" ,
89+ phase. elapsed( ) . as_millis( ) , path. display( )
90+ ) ) ;
91+
92+ perf_log ( & format ! (
93+ " resolve_tcp_peer_identity TOTAL: {}ms" ,
94+ start. elapsed( ) . as_millis( )
95+ ) ) ;
5496 Ok ( ( path, pid) )
5597}
5698
@@ -227,34 +269,55 @@ fn parse_proc_net_tcp(pid: u32, peer_port: u16) -> Result<u64> {
227269/// `/proc/<pid>/fd/` for processes running as a different user.
228270#[ cfg( target_os = "linux" ) ]
229271fn find_pid_by_socket_inode ( inode : u64 , entrypoint_pid : u32 ) -> Result < u32 > {
272+ let start = std:: time:: Instant :: now ( ) ;
230273 let target = format ! ( "socket:[{inode}]" ) ;
231274
232- // First: scan descendants of the entrypoint process (targeted, most likely to succeed)
275+ let phase = std :: time :: Instant :: now ( ) ;
233276 let descendants = collect_descendant_pids ( entrypoint_pid) ;
277+ perf_log ( & format ! (
278+ " collect_descendant_pids: {}ms count={}" ,
279+ phase. elapsed( ) . as_millis( ) , descendants. len( )
280+ ) ) ;
281+
282+ let phase = std:: time:: Instant :: now ( ) ;
234283 for & pid in & descendants {
235284 if let Some ( found) = check_pid_fds ( pid, & target) {
285+ perf_log ( & format ! (
286+ " find_pid_by_socket_inode: {}ms found_pid={} scan=descendants" ,
287+ start. elapsed( ) . as_millis( ) , found
288+ ) ) ;
236289 return Ok ( found) ;
237290 }
238291 }
292+ perf_log ( & format ! (
293+ " descendant_fd_scan (not found): {}ms" ,
294+ phase. elapsed( ) . as_millis( )
295+ ) ) ;
239296
240- // Fallback: scan all of /proc in case the process isn't in the tree
241- // (e.g., if /proc/<pid>/task/<tid>/children wasn't available)
297+ let phase = std:: time:: Instant :: now ( ) ;
242298 if let Ok ( proc_dir) = std:: fs:: read_dir ( "/proc" ) {
243299 for entry in proc_dir. flatten ( ) {
244300 let name = entry. file_name ( ) ;
245301 let pid: u32 = match name. to_string_lossy ( ) . parse ( ) {
246302 Ok ( p) => p,
247303 Err ( _) => continue ,
248304 } ;
249- // Skip PIDs we already checked
250305 if descendants. contains ( & pid) {
251306 continue ;
252307 }
253308 if let Some ( found) = check_pid_fds ( pid, & target) {
309+ perf_log ( & format ! (
310+ " find_pid_by_socket_inode: {}ms found_pid={} scan=full_proc" ,
311+ start. elapsed( ) . as_millis( ) , found
312+ ) ) ;
254313 return Ok ( found) ;
255314 }
256315 }
257316 }
317+ perf_log ( & format ! (
318+ " full_proc_scan (not found): {}ms" ,
319+ phase. elapsed( ) . as_millis( )
320+ ) ) ;
258321
259322 Err ( miette:: miette!(
260323 "No process found owning socket inode {} \
@@ -318,9 +381,28 @@ fn collect_descendant_pids(root_pid: u32) -> Vec<u32> {
318381/// same hash, or the request is denied.
319382pub fn file_sha256 ( path : & Path ) -> Result < String > {
320383 use sha2:: { Digest , Sha256 } ;
384+ use std:: io:: Read ;
385+
386+ let start = std:: time:: Instant :: now ( ) ;
387+ let mut file = std:: fs:: File :: open ( path)
388+ . map_err ( |e| miette:: miette!( "Failed to open {}: {e}" , path. display( ) ) ) ?;
389+ let mut hasher = Sha256 :: new ( ) ;
390+ let mut buf = [ 0u8 ; 65536 ] ;
391+ let mut total_read = 0u64 ;
392+ loop {
393+ let n = file. read ( & mut buf) . into_diagnostic ( ) ?;
394+ if n == 0 {
395+ break ;
396+ }
397+ total_read += n as u64 ;
398+ hasher. update ( & buf[ ..n] ) ;
399+ }
321400
322- let bytes = std:: fs:: read ( path) . into_diagnostic ( ) ?;
323- let hash = Sha256 :: digest ( & bytes) ;
401+ let hash = hasher. finalize ( ) ;
402+ perf_log ( & format ! (
403+ " file_sha256: {}ms size={} path={}" ,
404+ start. elapsed( ) . as_millis( ) , total_read, path. display( )
405+ ) ) ;
324406 Ok ( hex:: encode ( hash) )
325407}
326408
0 commit comments