@@ -12,10 +12,8 @@ use zerocopy::{AsBytes, FromBytes, FromZeroes};
12
12
#[ derive( Debug , Default ) ]
13
13
pub ( crate ) struct ProcessStats {
14
14
pub ( crate ) measurement_time : Duration ,
15
- pub ( crate ) unshared_rss : u64 ,
16
- pub ( crate ) unshared_vss : u64 ,
17
- pub ( crate ) shared_rss : u64 ,
18
- pub ( crate ) shared_vss : u64 ,
15
+ pub ( crate ) rss : usize ,
16
+ pub ( crate ) vss : usize ,
19
17
}
20
18
21
19
impl ProcessStats {
@@ -28,112 +26,88 @@ impl ProcessStats {
28
26
}
29
27
}
30
28
31
- /// There are some arrays with size `PRMAPSZ` holding names in `struct prmap`
32
- /// and `struct prxmap`.
33
- const PRMAPSZ : usize = 64 ;
34
-
35
- /// We're going to query our `/proc/<self>/xmap` here, which contains a series o
36
- /// `struct prxmap` records. So, define it here in the same way it's defined in
37
- /// illumos' `common/sys/procfs.h`
29
+ const PRFNSZ : usize = 16 ;
30
+ const PRARGSZ : usize = 80 ;
31
+
32
+ /// From `sys/types.h`
33
+ #[ allow( non_camel_case_types) ]
34
+ type taskid_t = i32 ;
35
+ /// From `sys/time_impl.h`
36
+ #[ allow( non_camel_case_types) ]
37
+ type timespec_t = [ i64 ; 2 ] ;
38
+ /// From `sys/time_impl.h`
39
+ #[ allow( non_camel_case_types) ]
40
+ type timestruc_t = timespec_t ;
41
+ /// From `sys/types.h`
42
+ #[ allow( non_camel_case_types) ]
43
+ type dev_t = u64 ;
44
+
45
+ /// `psinfo`'s definition depends on the data model reported by illumos. This is
46
+ /// in line with the 64-bit version of the struct, and ignores a few fields at
47
+ /// the end of the struct.
38
48
#[ derive( Copy , Clone , Debug , FromZeroes , AsBytes , FromBytes ) ]
39
- // Kind of weird to need packed here, but the struct size is 0x
49
+ # [ cfg ( target_arch = "x86_64" ) ]
40
50
#[ repr( C ) ]
41
- struct prxmap {
42
- /// virtual address of the mapping
43
- pr_vaddr : usize ,
44
- /// size of the mapping in bytes
51
+ struct psinfo {
52
+ pr_flag : i32 ,
53
+ pr_nlwp : i32 ,
54
+ pr_pid : u32 ,
55
+ pr_ppid : u32 ,
56
+ pr_pgid : u32 ,
57
+ pr_sid : u32 ,
58
+ pr_uid : u32 ,
59
+ pr_euid : u32 ,
60
+ pr_gid : u32 ,
61
+ pr_egid : u32 ,
62
+ pr_addr : usize ,
45
63
pr_size : usize ,
46
- /// name in /proc/<pid>/object
47
- pr_mapname : [ u8 ; PRMAPSZ ] ,
48
- /// offset into mapped object, if any
49
- pr_offset : i64 ,
50
- /// protection and attribute flags. See procfs.h' `MA_*` flags
51
- pr_mflags : i32 ,
52
- /// Pagesize (bytes) for this mapping
53
- pr_pagesize : i32 ,
54
- /// SysV shmid, -1 if not SysV shared memory
55
- pr_shmid : i32 ,
56
- /// illumos' `struct prxmap` on 64-bit operating systems has implicit
57
- /// internal padding between `pr_shmid` and `pr_dev`. This should always be
58
- /// zero.
59
- // Rust rules are that padding bytes are uninitialized, so transforming
60
- // `&prxmap` to a `&[u8]` is UB if there is internal padding.
61
- // Consequently, `AsBytes` and `FromBytes` require no internal padding, so
62
- // put a small field here to make the "padding" explicit.
63
- _internal_pad : i32 ,
64
- /// st_dev from stat64() of mapped object, or PRNODEV
65
- pr_dev : i64 ,
66
- /// st_ino from stat64() of mapped object, if any
67
- pr_ino : u64 ,
68
- /// pages of resident memory
69
- pr_rss : isize ,
70
- /// pages of resident anonymous memory
71
- pr_anon : isize ,
72
- /// pages of locked memory
73
- pr_locked : isize ,
74
- /// currently unused
75
- pr_pad : isize ,
76
- /// pagesize of the hat mapping
77
- pr_hatpagesize : usize ,
78
- /// filler for future expansion
79
- // illumos uses `ulong_t` here, which should be 32-bit, but
80
- // sizes only line up if it's u64?
81
- pr_filler : [ u64 ; 7 ] ,
64
+ pr_rssize : usize ,
65
+ // From `struct psinfo`. This seems to be present to ensure that `pr_ttydev`
66
+ // is 64-bit aligned even on 32-bit targets.
67
+ pr_pad1 : usize ,
68
+ pr_ttydev : dev_t ,
69
+ pr_pctcpu : u16 ,
70
+ pr_pctmem : u16 ,
71
+ // This padding is not explicitly present in illumos' `struct procfs`, but
72
+ // is none the less there due to C struct layout rules.
73
+ _pad2 : u32 ,
74
+ pr_start : timestruc_t ,
75
+ pr_time : timestruc_t ,
76
+ pr_ctime : timestruc_t ,
77
+ pr_fname : [ u8 ; PRFNSZ ] ,
78
+ pr_psargs : [ u8 ; PRARGSZ ] ,
79
+ pr_wstat : u32 ,
80
+ pr_argc : u32 ,
81
+ pr_argv : usize ,
82
+ pr_envp : usize ,
83
+ pr_dmodel : u8 ,
84
+ // More padding from `struct psinfo`.
85
+ pr_pad2 : [ u8 ; 3 ] ,
86
+ pr_taskid : taskid_t ,
82
87
}
83
88
84
- const MA_SHARED : i32 = 0x08 ;
89
+ pub fn process_stats ( ) -> anyhow:: Result < ProcessStats > {
90
+ let mut psinfo_file = std:: fs:: File :: open ( "/proc/self/psinfo" ) ?;
85
91
86
- // Computed here just because it's used in a few places in this file.
87
- const PRXMAPSZ : usize = std:: mem:: size_of :: < prxmap > ( ) ;
92
+ let mut stats = ProcessStats {
93
+ measurement_time : Duration :: ZERO ,
94
+ vss : 0 ,
95
+ rss : 0 ,
96
+ } ;
88
97
89
- impl prxmap {
90
- fn is_shared ( & self ) -> bool {
91
- self . pr_mflags & MA_SHARED == MA_SHARED
92
- }
93
- }
98
+ let mut info: psinfo = FromZeroes :: new_zeroed ( ) ;
94
99
95
- pub fn process_stats ( ) -> anyhow:: Result < ProcessStats > {
96
100
let stats_read_start = Instant :: now ( ) ;
97
101
98
- // Safety: getpid() does not alter any state, or even read mutable state.
99
- let mypid = unsafe { libc:: getpid ( ) } ;
100
- let xmap_path = format ! ( "/proc/{}/xmap" , mypid) ;
101
- let mut xmap_file = std:: fs:: File :: open ( & xmap_path) ?;
102
+ psinfo_file. read ( info. as_bytes_mut ( ) )
103
+ . context ( "reading struct psinfo from file" ) ?;
102
104
103
- let mut stats = ProcessStats {
104
- measurement_time : Duration :: ZERO ,
105
- unshared_rss : 0 ,
106
- unshared_vss : 0 ,
107
- shared_rss : 0 ,
108
- shared_vss : 0 ,
109
- } ;
105
+ stats. measurement_time = stats_read_start. elapsed ( ) ;
110
106
111
- let mut buf: [ prxmap ; 256 ] = [ FromZeroes :: new_zeroed ( ) ; 256 ] ;
112
- loop {
113
- let nread = xmap_file
114
- . read ( buf. as_bytes_mut ( ) )
115
- . context ( "reading struct prxmap from file" ) ?;
116
- if nread == 0 {
117
- // we've read all maps this go around.
118
- stats. measurement_time = stats_read_start. elapsed ( ) ;
119
- return Ok ( stats) ;
120
- }
121
-
122
- assert ! ( nread % PRXMAPSZ == 0 ) ;
123
- let maps_read = nread / PRXMAPSZ ;
124
-
125
- for buf in buf[ 0 ..maps_read] . iter ( ) {
126
- let map_rss = buf. pr_rss as u64 * buf. pr_pagesize as u64 ;
127
- let map_vss = buf. pr_size as u64 ;
128
- if buf. is_shared ( ) {
129
- stats. shared_rss += map_rss;
130
- stats. shared_vss += map_vss;
131
- } else {
132
- stats. unshared_rss += map_rss;
133
- stats. unshared_vss += map_vss;
134
- }
135
- }
136
- }
107
+ stats. vss = info. pr_size ;
108
+ stats. rss = info. pr_rssize ;
109
+
110
+ Ok ( stats)
137
111
}
138
112
139
113
async fn process_stats_task ( tx : watch:: Sender < ProcessStats > ) {
@@ -155,16 +129,3 @@ async fn process_stats_task(tx: watch::Sender<ProcessStats>) {
155
129
tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 15000 ) ) . await ;
156
130
}
157
131
}
158
-
159
- #[ cfg( test) ]
160
- mod test {
161
- use super :: prxmap;
162
- use std:: mem:: size_of;
163
-
164
- #[ test]
165
- fn prxmap_size ( ) {
166
- // Double check `prxmap` size against that of structures in
167
- // /proc/<pid>/xmap..
168
- assert_eq ! ( size_of:: <prxmap>( ) , 0xd8 ) ;
169
- }
170
- }
0 commit comments