remove top level constructor functions
This removes top level 'New<Thing>' constructor functions
in order to make the API slightly smaller and more consistent.
Fixes issue #44
Also includes some minor readme and godoc updates.
Signed-off-by: Paul Gier <pgier@redhat.com>
Paul Gier
4 years ago
21 | 21 | stats, err := fs.NewStat() |
22 | 22 | ``` |
23 | 23 | |
24 | Some sub-packages such as `blockdevice`, require access to both the proc and sys filesystems. | |
25 | ||
26 | ```go | |
27 | fs, err := blockdevice.NewFS("/proc", "/sys") | |
28 | stats, err := fs.ProcDiskstats() | |
29 | ``` | |
30 | ||
24 | 31 | ## Building and Testing |
25 | 32 | |
26 | 33 | The procfs library is normally built as part of another application. However, when making |
29 | 36 | ### Updating Test Fixtures |
30 | 37 | |
31 | 38 | The procfs library includes a set of test fixtures which include many example files from |
32 | the `/proc` and `/sys` filesystems. These fixtures are included as a ttar (text tar) file | |
39 | the `/proc` and `/sys` filesystems. These fixtures are included as a [ttar](https://github.com/ideaship/ttar) file | |
33 | 40 | which is extracted automatically during testing. To add/update the test fixtures, first |
34 | 41 | ensure the `fixtures` directory is up to date by removing the existing directory and then |
35 | 42 | extracting the ttar file using `make fixtures/.unpacked` or just `make test`. |
31 | 31 | sys *fs.FS |
32 | 32 | } |
33 | 33 | |
34 | // NewDefaultFS returns a new Bcache using the default sys fs mount point. It will error | |
35 | // if the mount point can't be read. | |
36 | func NewDefaultFS() (FS, error) { | |
37 | return NewFS(fs.DefaultSysMountPoint) | |
38 | } | |
39 | ||
34 | 40 | // NewFS returns a new Bcache using the given sys fs mount point. It will error |
35 | 41 | // if the mount point can't be read. |
36 | 42 | func NewFS(mountPoint string) (FS, error) { |
94 | 94 | sys *fs.FS |
95 | 95 | } |
96 | 96 | |
97 | // NewDefaultFS returns a new blockdevice fs using the default mountPoints for proc and sys. | |
98 | // It will error if either of these mount points can't be read. | |
99 | func NewDefaultFS() (FS, error) { | |
100 | return NewFS(fs.DefaultProcMountPoint, fs.DefaultSysMountPoint) | |
101 | } | |
102 | ||
97 | 103 | // NewFS returns a new blockdevice fs using the given mountPoints for proc and sys. |
98 | 104 | // It will error if either of these mount points can't be read. |
99 | 105 | func NewFS(procMountPoint string, sysMountPoint string) (FS, error) { |
30 | 30 | Sizes []float64 |
31 | 31 | } |
32 | 32 | |
33 | // NewBuddyInfo reads the buddyinfo statistics. | |
34 | func NewBuddyInfo() ([]BuddyInfo, error) { | |
35 | fs, err := NewFS(DefaultMountPoint) | |
36 | if err != nil { | |
37 | return nil, err | |
38 | } | |
39 | ||
40 | return fs.NewBuddyInfo() | |
41 | } | |
42 | ||
43 | 33 | // NewBuddyInfo reads the buddyinfo statistics from the specified `proc` filesystem. |
44 | func (fs FS) NewBuddyInfo() ([]BuddyInfo, error) { | |
34 | func (fs FS) BuddyInfo() ([]BuddyInfo, error) { | |
45 | 35 | file, err := os.Open(fs.proc.Path("buddyinfo")) |
46 | 36 | if err != nil { |
47 | 37 | return nil, err |
18 | 18 | ) |
19 | 19 | |
20 | 20 | func TestBuddyInfo(t *testing.T) { |
21 | buddyInfo, err := getProcFixtures(t).NewBuddyInfo() | |
21 | buddyInfo, err := getProcFixtures(t).BuddyInfo() | |
22 | 22 | if err != nil { |
23 | 23 | t.Fatal(err) |
24 | 24 | } |
25 | 25 | // DefaultMountPoint is the common mount point of the proc filesystem. |
26 | 26 | const DefaultMountPoint = fs.DefaultProcMountPoint |
27 | 27 | |
28 | // NewDefaultFS returns a new proc FS mounted under the default proc mountPoint. | |
29 | // It will error if the mount point directory can't be read or is a file. | |
30 | func NewDefaultFS() (FS, error) { | |
31 | return NewFS(DefaultMountPoint) | |
32 | } | |
33 | ||
28 | 34 | // NewFS returns a new proc FS mounted under the given proc mountPoint. It will error |
29 | 35 | // if the mount point directory can't be read or is a file. |
30 | 36 | func NewFS(mountPoint string) (FS, error) { |
61 | 61 | Weight uint64 |
62 | 62 | } |
63 | 63 | |
64 | // NewIPVSStats reads the IPVS statistics. | |
65 | func NewIPVSStats() (IPVSStats, error) { | |
66 | fs, err := NewFS(DefaultMountPoint) | |
67 | if err != nil { | |
68 | return IPVSStats{}, err | |
69 | } | |
70 | ||
71 | return fs.NewIPVSStats() | |
72 | } | |
73 | ||
74 | // NewIPVSStats reads the IPVS statistics from the specified `proc` filesystem. | |
75 | func (fs FS) NewIPVSStats() (IPVSStats, error) { | |
64 | // IPVSStats reads the IPVS statistics from the specified `proc` filesystem. | |
65 | func (fs FS) IPVSStats() (IPVSStats, error) { | |
76 | 66 | file, err := os.Open(fs.proc.Path("net/ip_vs_stats")) |
77 | 67 | if err != nil { |
78 | 68 | return IPVSStats{}, err |
130 | 120 | return stats, nil |
131 | 121 | } |
132 | 122 | |
133 | // NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs. | |
134 | func NewIPVSBackendStatus() ([]IPVSBackendStatus, error) { | |
135 | fs, err := NewFS(DefaultMountPoint) | |
136 | if err != nil { | |
137 | return []IPVSBackendStatus{}, err | |
138 | } | |
139 | ||
140 | return fs.NewIPVSBackendStatus() | |
141 | } | |
142 | ||
143 | // NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs from the specified `proc` filesystem. | |
144 | func (fs FS) NewIPVSBackendStatus() ([]IPVSBackendStatus, error) { | |
123 | // IPVSBackendStatus reads and returns the status of all (virtual,real) server pairs from the specified `proc` filesystem. | |
124 | func (fs FS) IPVSBackendStatus() ([]IPVSBackendStatus, error) { | |
145 | 125 | file, err := os.Open(fs.proc.Path("net/ip_vs")) |
146 | 126 | if err != nil { |
147 | 127 | return nil, err |
162 | 162 | if err != nil { |
163 | 163 | t.Fatal(err) |
164 | 164 | } |
165 | stats, err := fs.NewIPVSStats() | |
165 | stats, err := fs.IPVSStats() | |
166 | 166 | if err != nil { |
167 | 167 | t.Fatal(err) |
168 | 168 | } |
216 | 216 | } |
217 | 217 | |
218 | 218 | func TestIPVSBackendStatus(t *testing.T) { |
219 | backendStats, err := getProcFixtures(t).NewIPVSBackendStatus() | |
219 | backendStats, err := getProcFixtures(t).IPVSBackendStatus() | |
220 | 220 | if err != nil { |
221 | 221 | t.Fatal(err) |
222 | 222 | } |
42 | 42 | } |
43 | 43 | |
44 | 44 | // ParseMDStat parses an mdstat-file and returns a struct with the relevant infos. |
45 | // TODO: this should just be called "MDStat" and use a separate parseMDStat function | |
45 | 46 | func (fs FS) ParseMDStat() (mdstates []MDStat, err error) { |
46 | 47 | mdStatusFilePath := fs.proc.Path("mdstat") |
47 | 48 | content, err := ioutil.ReadFile(mdStatusFilePath) |
360 | 360 | if tt.s != "" { |
361 | 361 | mounts, err = parseMountStats(strings.NewReader(tt.s)) |
362 | 362 | } else { |
363 | proc, e := getProcFixtures(t).NewProc(26231) | |
363 | proc, e := getProcFixtures(t).Proc(26231) | |
364 | 364 | if e != nil { |
365 | 365 | t.Fatalf("failed to create proc: %v", err) |
366 | 366 | } |
46 | 46 | // are interface names. |
47 | 47 | type NetDev map[string]NetDevLine |
48 | 48 | |
49 | // NewNetDev returns kernel/system statistics read from /proc/net/dev. | |
50 | func NewNetDev() (NetDev, error) { | |
51 | fs, err := NewFS(DefaultMountPoint) | |
52 | if err != nil { | |
53 | return nil, err | |
54 | } | |
55 | ||
56 | return fs.NewNetDev() | |
57 | } | |
58 | ||
59 | // NewNetDev returns kernel/system statistics read from /proc/net/dev. | |
60 | func (fs FS) NewNetDev() (NetDev, error) { | |
49 | // NetDev returns kernel/system statistics read from /proc/net/dev. | |
50 | func (fs FS) NetDev() (NetDev, error) { | |
61 | 51 | return newNetDev(fs.proc.Path("net/dev")) |
62 | 52 | } |
63 | 53 | |
64 | // NewNetDev returns kernel/system statistics read from /proc/[pid]/net/dev. | |
65 | func (p Proc) NewNetDev() (NetDev, error) { | |
54 | // NetDev returns kernel/system statistics read from /proc/[pid]/net/dev. | |
55 | func (p Proc) NetDev() (NetDev, error) { | |
66 | 56 | return newNetDev(p.path("net/dev")) |
67 | 57 | } |
68 | 58 |
30 | 30 | } |
31 | 31 | } |
32 | 32 | |
33 | func TestNewNetDev(t *testing.T) { | |
33 | func TestNetDev(t *testing.T) { | |
34 | 34 | fs, err := NewFS(procTestFixtures) |
35 | 35 | if err != nil { |
36 | 36 | t.Fatal(err) |
37 | 37 | } |
38 | 38 | |
39 | netDev, err := fs.NewNetDev() | |
39 | netDev, err := fs.NetDev() | |
40 | 40 | if err != nil { |
41 | 41 | t.Fatal(err) |
42 | 42 | } |
58 | 58 | } |
59 | 59 | } |
60 | 60 | |
61 | func TestProcNewNetDev(t *testing.T) { | |
62 | p, err := getProcFixtures(t).NewProc(26231) | |
61 | func TestProcNetDev(t *testing.T) { | |
62 | p, err := getProcFixtures(t).Proc(26231) | |
63 | 63 | if err != nil { |
64 | 64 | t.Fatal(err) |
65 | 65 | } |
66 | 66 | |
67 | netDev, err := p.NewNetDev() | |
67 | netDev, err := p.NetDev() | |
68 | 68 | if err != nil { |
69 | 69 | t.Fatal(err) |
70 | 70 | } |
274 | 274 | proc *fs.FS |
275 | 275 | } |
276 | 276 | |
277 | // NewDefaultFS returns a new FS mounted under the default mountPoint. It will error | |
278 | // if the mount point can't be read. | |
279 | func NewDefaultFS() (FS, error) { | |
280 | return NewFS(fs.DefaultProcMountPoint) | |
281 | } | |
282 | ||
277 | 283 | // NewFS returns a new FS mounted under the given mountPoint. It will error |
278 | 284 | // if the mount point can't be read. |
279 | 285 | func NewFS(mountPoint string) (FS, error) { |
53 | 53 | if err != nil { |
54 | 54 | return Proc{}, err |
55 | 55 | } |
56 | return fs.NewProc(pid) | |
56 | return fs.Proc(pid) | |
57 | 57 | } |
58 | 58 | |
59 | 59 | // AllProcs returns a list of all currently available processes under /proc. |
75 | 75 | if err != nil { |
76 | 76 | return Proc{}, err |
77 | 77 | } |
78 | return fs.NewProc(pid) | |
79 | } | |
80 | ||
81 | // NewProc returns a process for the given pid. | |
82 | func (fs FS) NewProc(pid int) (Proc, error) { | |
78 | return fs.Proc(pid) | |
79 | } | |
80 | ||
81 | // Proc returns a process for the given pid. | |
82 | func (fs FS) Proc(pid int) (Proc, error) { | |
83 | 83 | if _, err := os.Stat(fs.proc.Path(strconv.Itoa(pid))); err != nil { |
84 | 84 | return Proc{}, err |
85 | 85 | } |
38 | 38 | CancelledWriteBytes int64 |
39 | 39 | } |
40 | 40 | |
41 | // NewIO creates a new ProcIO instance from a given Proc instance. | |
42 | func (p Proc) NewIO() (ProcIO, error) { | |
41 | // IO creates a new ProcIO instance from a given Proc instance. | |
42 | func (p Proc) IO() (ProcIO, error) { | |
43 | 43 | pio := ProcIO{} |
44 | 44 | |
45 | 45 | f, err := os.Open(p.path("io")) |
15 | 15 | import "testing" |
16 | 16 | |
17 | 17 | func TestProcIO(t *testing.T) { |
18 | p, err := getProcFixtures(t).NewProc(26231) | |
18 | p, err := getProcFixtures(t).Proc(26231) | |
19 | 19 | if err != nil { |
20 | 20 | t.Fatal(err) |
21 | 21 | } |
22 | 22 | |
23 | s, err := p.NewIO() | |
23 | s, err := p.IO() | |
24 | 24 | if err != nil { |
25 | 25 | t.Fatal(err) |
26 | 26 | } |
76 | 76 | limitsDelimiter = regexp.MustCompile(" +") |
77 | 77 | ) |
78 | 78 | |
79 | // NewLimits returns the current soft limits of the process. | |
80 | func (p Proc) NewLimits() (ProcLimits, error) { | |
79 | // Limits returns the current soft limits of the process. | |
80 | func (p Proc) Limits() (ProcLimits, error) { | |
81 | 81 | f, err := os.Open(p.path("limits")) |
82 | 82 | if err != nil { |
83 | 83 | return ProcLimits{}, err |
14 | 14 | |
15 | 15 | import "testing" |
16 | 16 | |
17 | func TestNewLimits(t *testing.T) { | |
18 | p, err := getProcFixtures(t).NewProc(26231) | |
17 | func TestLimits(t *testing.T) { | |
18 | p, err := getProcFixtures(t).Proc(26231) | |
19 | 19 | if err != nil { |
20 | 20 | t.Fatal(err) |
21 | 21 | } |
22 | 22 | |
23 | l, err := p.NewLimits() | |
23 | l, err := p.Limits() | |
24 | 24 | if err != nil { |
25 | 25 | t.Fatal(err) |
26 | 26 | } |
28 | 28 | // Namespaces contains all of the namespaces that the process is contained in. |
29 | 29 | type Namespaces map[string]Namespace |
30 | 30 | |
31 | // NewNamespaces reads from /proc/<pid>/ns/* to get the namespaces of which the | |
31 | // Namespaces reads from /proc/<pid>/ns/* to get the namespaces of which the | |
32 | 32 | // process is a member. |
33 | func (p Proc) NewNamespaces() (Namespaces, error) { | |
33 | func (p Proc) Namespaces() (Namespaces, error) { | |
34 | 34 | d, err := os.Open(p.path("ns")) |
35 | 35 | if err != nil { |
36 | 36 | return nil, err |
17 | 17 | ) |
18 | 18 | |
19 | 19 | func TestNewNamespaces(t *testing.T) { |
20 | p, err := getProcFixtures(t).NewProc(26231) | |
20 | p, err := getProcFixtures(t).Proc(26231) | |
21 | 21 | if err != nil { |
22 | 22 | t.Fatal(err) |
23 | 23 | } |
24 | 24 | |
25 | namespaces, err := p.NewNamespaces() | |
25 | namespaces, err := p.Namespaces() | |
26 | 26 | if err != nil { |
27 | 27 | t.Fatal(err) |
28 | 28 | } |
50 | 50 | Full *PSILine |
51 | 51 | } |
52 | 52 | |
53 | // NewPSIStatsForResource reads pressure stall information for the specified | |
54 | // resource. At time of writing this can be either "cpu", "memory" or "io". | |
55 | func NewPSIStatsForResource(resource string) (PSIStats, error) { | |
56 | fs, err := NewFS(DefaultMountPoint) | |
57 | if err != nil { | |
58 | return PSIStats{}, err | |
59 | } | |
60 | ||
61 | return fs.NewPSIStatsForResource(resource) | |
62 | } | |
63 | ||
64 | // NewPSIStatsForResource reads pressure stall information from /proc/pressure/<resource> | |
65 | func (fs FS) NewPSIStatsForResource(resource string) (PSIStats, error) { | |
53 | // PSIStatsForResource reads pressure stall information for the specified | |
54 | // resource from /proc/pressure/<resource>. At time of writing this can be | |
55 | // either "cpu", "memory" or "io". | |
56 | func (fs FS) PSIStatsForResource(resource string) (PSIStats, error) { | |
66 | 57 | file, err := os.Open(fs.proc.Path(fmt.Sprintf("%s/%s", "pressure", resource))) |
67 | 58 | if err != nil { |
68 | 59 | return PSIStats{}, fmt.Errorf("psi_stats: unavailable for %s", resource) |
19 | 19 | |
20 | 20 | func TestPSIStats(t *testing.T) { |
21 | 21 | t.Run("fake", func(*testing.T) { |
22 | stats, err := getProcFixtures(t).NewPSIStatsForResource("fake") | |
22 | stats, err := getProcFixtures(t).PSIStatsForResource("fake") | |
23 | 23 | if err == nil { |
24 | 24 | t.Fatal("fake resource does not have PSI statistics") |
25 | 25 | } |
30 | 30 | }) |
31 | 31 | |
32 | 32 | t.Run("cpu", func(t *testing.T) { |
33 | stats, err := getProcFixtures(t).NewPSIStatsForResource("cpu") | |
33 | stats, err := getProcFixtures(t).PSIStatsForResource("cpu") | |
34 | 34 | if err != nil { |
35 | 35 | t.Fatal(err) |
36 | 36 | } |
67 | 67 | |
68 | 68 | for _, resource := range res { |
69 | 69 | t.Run(resource, func(t *testing.T) { |
70 | stats, err := getProcFixtures(t).NewPSIStatsForResource(resource) | |
70 | stats, err := getProcFixtures(t).PSIStatsForResource(resource) | |
71 | 71 | if err != nil { |
72 | 72 | t.Fatal(err) |
73 | 73 | } |
103 | 103 | proc fs.FS |
104 | 104 | } |
105 | 105 | |
106 | // NewStat returns the current status information of the process. | |
107 | func (p Proc) NewStat() (ProcStat, error) { | |
106 | // Stat returns the current status information of the process. | |
107 | func (p Proc) Stat() (ProcStat, error) { | |
108 | 108 | f, err := os.Open(p.path("stat")) |
109 | 109 | if err != nil { |
110 | 110 | return ProcStat{}, err |
177 | 177 | // StartTime returns the unix timestamp of the process in seconds. |
178 | 178 | func (s ProcStat) StartTime() (float64, error) { |
179 | 179 | fs := FS{proc: s.proc} |
180 | stat, err := fs.NewStat() | |
180 | stat, err := fs.Stat() | |
181 | 181 | if err != nil { |
182 | 182 | return 0, err |
183 | 183 | } |
18 | 18 | ) |
19 | 19 | |
20 | 20 | func TestProcStat(t *testing.T) { |
21 | p, err := getProcFixtures(t).NewProc(26231) | |
21 | p, err := getProcFixtures(t).Proc(26231) | |
22 | 22 | if err != nil { |
23 | 23 | t.Fatal(err) |
24 | 24 | } |
25 | 25 | |
26 | s, err := p.NewStat() | |
26 | s, err := p.Stat() | |
27 | 27 | if err != nil { |
28 | 28 | t.Fatal(err) |
29 | 29 | } |
117 | 117 | if err != nil { |
118 | 118 | return ProcStat{}, err |
119 | 119 | } |
120 | p, err := fs.NewProc(pid) | |
120 | p, err := fs.Proc(pid) | |
121 | 121 | if err != nil { |
122 | 122 | return ProcStat{}, err |
123 | 123 | } |
124 | 124 | |
125 | return p.NewStat() | |
125 | return p.Stat() | |
126 | 126 | } |
17 | 17 | ) |
18 | 18 | |
19 | 19 | func TestProcStatus(t *testing.T) { |
20 | p, err := getProcFixtures(t).NewProc(26231) | |
20 | p, err := getProcFixtures(t).Proc(26231) | |
21 | 21 | if err != nil { |
22 | 22 | t.Fatal(err) |
23 | 23 | } |
61 | 61 | } |
62 | 62 | |
63 | 63 | func TestProcStatusName(t *testing.T) { |
64 | p, err := getProcFixtures(t).NewProc(26231) | |
64 | p, err := getProcFixtures(t).Proc(26231) | |
65 | 65 | if err != nil { |
66 | 66 | t.Fatal(err) |
67 | 67 | } |
21 | 21 | func TestSelf(t *testing.T) { |
22 | 22 | fs := getProcFixtures(t) |
23 | 23 | |
24 | p1, err := fs.NewProc(26231) | |
24 | p1, err := fs.Proc(26231) | |
25 | 25 | if err != nil { |
26 | 26 | t.Fatal(err) |
27 | 27 | } |
57 | 57 | {process: 26232, want: []string{}}, |
58 | 58 | {process: 26233, want: []string{"com.github.uiautomator"}}, |
59 | 59 | } { |
60 | p1, err := getProcFixtures(t).NewProc(tt.process) | |
60 | p1, err := getProcFixtures(t).Proc(tt.process) | |
61 | 61 | if err != nil { |
62 | 62 | t.Fatal(err) |
63 | 63 | } |
79 | 79 | {process: 26231, want: "vim"}, |
80 | 80 | {process: 26232, want: "ata_sff"}, |
81 | 81 | } { |
82 | p1, err := getProcFixtures(t).NewProc(tt.process) | |
82 | p1, err := getProcFixtures(t).Proc(tt.process) | |
83 | 83 | if err != nil { |
84 | 84 | t.Fatal(err) |
85 | 85 | } |
101 | 101 | {process: 26231, want: "/usr/bin/vim"}, |
102 | 102 | {process: 26232, want: ""}, |
103 | 103 | } { |
104 | p, err := getProcFixtures(t).NewProc(tt.process) | |
104 | p, err := getProcFixtures(t).Proc(tt.process) | |
105 | 105 | if err != nil { |
106 | 106 | t.Fatal(err) |
107 | 107 | } |
125 | 125 | {process: 26232, want: "/does/not/exist", brokenLink: true}, |
126 | 126 | {process: 26233, want: ""}, |
127 | 127 | } { |
128 | p, err := getProcFixtures(t).NewProc(tt.process) | |
128 | p, err := getProcFixtures(t).Proc(tt.process) | |
129 | 129 | if err != nil { |
130 | 130 | t.Fatal(err) |
131 | 131 | } |
153 | 153 | {process: 26232, want: "/does/not/exist", brokenLink: true}, |
154 | 154 | {process: 26233, want: ""}, |
155 | 155 | } { |
156 | p, err := getProcFixtures(t).NewProc(tt.process) | |
156 | p, err := getProcFixtures(t).Proc(tt.process) | |
157 | 157 | if err != nil { |
158 | 158 | t.Fatal(err) |
159 | 159 | } |
172 | 172 | } |
173 | 173 | |
174 | 174 | func TestFileDescriptors(t *testing.T) { |
175 | p1, err := getProcFixtures(t).NewProc(26231) | |
175 | p1, err := getProcFixtures(t).Proc(26231) | |
176 | 176 | if err != nil { |
177 | 177 | t.Fatal(err) |
178 | 178 | } |
187 | 187 | } |
188 | 188 | |
189 | 189 | func TestFileDescriptorTargets(t *testing.T) { |
190 | p1, err := getProcFixtures(t).NewProc(26231) | |
190 | p1, err := getProcFixtures(t).Proc(26231) | |
191 | 191 | if err != nil { |
192 | 192 | t.Fatal(err) |
193 | 193 | } |
209 | 209 | } |
210 | 210 | |
211 | 211 | func TestFileDescriptorsLen(t *testing.T) { |
212 | p1, err := getProcFixtures(t).NewProc(26231) | |
212 | p1, err := getProcFixtures(t).Proc(26231) | |
213 | 213 | if err != nil { |
214 | 214 | t.Fatal(err) |
215 | 215 | } |
77 | 77 | SoftIRQ SoftIRQStat |
78 | 78 | } |
79 | 79 | |
80 | // NewStat returns kernel/system statistics read from /proc/stat. | |
81 | func NewStat() (Stat, error) { | |
82 | fs, err := NewFS(DefaultMountPoint) | |
83 | if err != nil { | |
84 | return Stat{}, err | |
85 | } | |
86 | ||
87 | return fs.NewStat() | |
88 | } | |
89 | ||
90 | 80 | // Parse a cpu statistics line and returns the CPUStat struct plus the cpu id (or -1 for the overall sum). |
91 | 81 | func parseCPUStat(line string) (CPUStat, int64, error) { |
92 | 82 | cpuStat := CPUStat{} |
148 | 138 | return softIRQStat, total, nil |
149 | 139 | } |
150 | 140 | |
151 | // NewStat returns an information about current kernel/system statistics. | |
152 | func (fs FS) NewStat() (Stat, error) { | |
141 | // Stat returns an information about current kernel/system statistics. | |
142 | func (fs FS) Stat() (Stat, error) { | |
153 | 143 | // See https://www.kernel.org/doc/Documentation/filesystems/proc.txt |
154 | 144 | |
155 | 145 | f, err := os.Open(fs.proc.Path("stat")) |
15 | 15 | import "testing" |
16 | 16 | |
17 | 17 | func TestStat(t *testing.T) { |
18 | s, err := getProcFixtures(t).NewStat() | |
18 | s, err := getProcFixtures(t).Stat() | |
19 | 19 | if err != nil { |
20 | 20 | t.Fatal(err) |
21 | 21 | } |
101 | 101 | // The map keys are the names of the power supplies. |
102 | 102 | type PowerSupplyClass map[string]PowerSupply |
103 | 103 | |
104 | // NewPowerSupplyClass returns info for all power supplies read from /sys/class/power_supply/. | |
105 | func NewPowerSupplyClass() (PowerSupplyClass, error) { | |
106 | fs, err := NewFS(DefaultMountPoint) | |
107 | if err != nil { | |
108 | return nil, err | |
109 | } | |
110 | ||
111 | return fs.NewPowerSupplyClass() | |
112 | } | |
113 | ||
114 | // NewPowerSupplyClass returns info for all power supplies read from /sys/class/power_supply/. | |
115 | func (fs FS) NewPowerSupplyClass() (PowerSupplyClass, error) { | |
104 | // PowerSupplyClass returns info for all power supplies read from /sys/class/power_supply/. | |
105 | func (fs FS) PowerSupplyClass() (PowerSupplyClass, error) { | |
116 | 106 | path := fs.sys.Path("class/power_supply") |
117 | 107 | |
118 | 108 | powerSupplyDirs, err := ioutil.ReadDir(path) |
20 | 20 | "testing" |
21 | 21 | ) |
22 | 22 | |
23 | func TestNewPowerSupplyClass(t *testing.T) { | |
23 | func TestPowerSupplyClass(t *testing.T) { | |
24 | 24 | fs, err := NewFS(sysTestFixtures) |
25 | 25 | if err != nil { |
26 | 26 | t.Fatal(err) |
27 | 27 | } |
28 | 28 | |
29 | psc, err := fs.NewPowerSupplyClass() | |
29 | psc, err := fs.PowerSupplyClass() | |
30 | 30 | if err != nil { |
31 | 31 | t.Fatal(err) |
32 | 32 | } |
34 | 34 | Passive *uint64 // Optional: millidegrees Celsius. (0 for disabled, > 1000 for enabled+value) |
35 | 35 | } |
36 | 36 | |
37 | // NewClassThermalZoneStats returns Thermal Zone metrics for all zones. | |
38 | func (fs FS) NewClassThermalZoneStats() ([]ClassThermalZoneStats, error) { | |
37 | // ClassThermalZoneStats returns Thermal Zone metrics for all zones. | |
38 | func (fs FS) ClassThermalZoneStats() ([]ClassThermalZoneStats, error) { | |
39 | 39 | zones, err := filepath.Glob(fs.sys.Path("class/thermal/thermal_zone[0-9]*")) |
40 | 40 | if err != nil { |
41 | 41 | return []ClassThermalZoneStats{}, err |
27 | 27 | t.Fatal(err) |
28 | 28 | } |
29 | 29 | |
30 | thermalTest, err := fs.NewClassThermalZoneStats() | |
30 | thermalTest, err := fs.ClassThermalZoneStats() | |
31 | 31 | if err != nil { |
32 | 32 | t.Fatal(err) |
33 | 33 | } |
25 | 25 | // DefaultMountPoint is the common mount point of the sys filesystem. |
26 | 26 | const DefaultMountPoint = fs.DefaultSysMountPoint |
27 | 27 | |
28 | // NewDefaultFS returns a new FS mounted under the default mountPoint. It will error | |
29 | // if the mount point can't be read. | |
30 | func NewDefaultFS() (FS, error) { | |
31 | return NewFS(DefaultMountPoint) | |
32 | } | |
33 | ||
28 | 34 | // NewFS returns a new FS mounted under the given mountPoint. It will error |
29 | 35 | // if the mount point can't be read. |
30 | 36 | func NewFS(mountPoint string) (FS, error) { |
64 | 64 | // are interface (iface) names. |
65 | 65 | type NetClass map[string]NetClassIface |
66 | 66 | |
67 | // NewNetClass returns info for all net interfaces (iface) read from /sys/class/net/<iface>. | |
68 | func NewNetClass() (NetClass, error) { | |
69 | fs, err := NewFS(DefaultMountPoint) | |
70 | if err != nil { | |
71 | return nil, err | |
72 | } | |
73 | ||
74 | return fs.NewNetClass() | |
75 | } | |
76 | ||
77 | 67 | // NetClassDevices scans /sys/class/net for devices and returns them as a list of names. |
78 | 68 | func (fs FS) NetClassDevices() ([]string, error) { |
79 | 69 | var res []string |
94 | 84 | return res, nil |
95 | 85 | } |
96 | 86 | |
97 | // NewNetClass returns info for all net interfaces (iface) read from /sys/class/net/<iface>. | |
98 | func (fs FS) NewNetClass() (NetClass, error) { | |
87 | // NetClass returns info for all net interfaces (iface) read from /sys/class/net/<iface>. | |
88 | func (fs FS) NetClass() (NetClass, error) { | |
99 | 89 | devices, err := fs.NetClassDevices() |
100 | 90 | if err != nil { |
101 | 91 | return nil, err |
38 | 38 | } |
39 | 39 | } |
40 | 40 | |
41 | func TestNewNetClass(t *testing.T) { | |
41 | func TestNetClass(t *testing.T) { | |
42 | 42 | fs, err := NewFS(sysTestFixtures) |
43 | 43 | if err != nil { |
44 | 44 | t.Fatal(err) |
45 | 45 | } |
46 | 46 | |
47 | nc, err := fs.NewNetClass() | |
47 | nc, err := fs.NetClass() | |
48 | 48 | if err != nil { |
49 | 49 | t.Fatal(err) |
50 | 50 | } |
45 | 45 | |
46 | 46 | // TODO: Add thermal_throttle support. |
47 | 47 | |
48 | // NewSystemCpufreq returns CPU frequency metrics for all CPUs. | |
49 | func NewSystemCpufreq() ([]SystemCPUCpufreqStats, error) { | |
50 | fs, err := NewFS(DefaultMountPoint) | |
51 | if err != nil { | |
52 | return []SystemCPUCpufreqStats{}, err | |
53 | } | |
54 | ||
55 | return fs.NewSystemCpufreq() | |
56 | } | |
57 | ||
58 | // NewSystemCpufreq returns CPU frequency metrics for all CPUs. | |
59 | func (fs FS) NewSystemCpufreq() ([]SystemCPUCpufreqStats, error) { | |
48 | // SystemCpufreq returns CPU frequency metrics for all CPUs. | |
49 | func (fs FS) SystemCpufreq() ([]SystemCPUCpufreqStats, error) { | |
60 | 50 | var g errgroup.Group |
61 | 51 | |
62 | 52 | cpus, err := filepath.Glob(fs.sys.Path("devices/system/cpu/cpu[0-9]*")) |
23 | 23 | return &v |
24 | 24 | } |
25 | 25 | |
26 | func TestNewSystemCpufreq(t *testing.T) { | |
26 | func TestSystemCpufreq(t *testing.T) { | |
27 | 27 | fs, err := NewFS(sysTestFixtures) |
28 | 28 | if err != nil { |
29 | 29 | t.Fatal(err) |
30 | 30 | } |
31 | 31 | |
32 | c, err := fs.NewSystemCpufreq() | |
32 | c, err := fs.SystemCpufreq() | |
33 | 33 | if err != nil { |
34 | 34 | t.Fatal(err) |
35 | 35 | } |
176 | 176 | sys *fs.FS |
177 | 177 | } |
178 | 178 | |
179 | // NewDefaultFS returns a new XFS handle using the default proc and sys mountPoints. | |
180 | // It will error if either of the mounts point can't be read. | |
181 | func NewDefaultFS() (FS, error) { | |
182 | return NewFS(fs.DefaultProcMountPoint, fs.DefaultSysMountPoint) | |
183 | } | |
184 | ||
179 | 185 | // NewFS returns a new XFS handle using the given proc and sys mountPoints. It will error |
180 | 186 | // if either of the mounts point can't be read. |
181 | 187 | func NewFS(procMountPoint string, sysMountPoint string) (FS, error) { |