Codebase list golang-procfs / 85fadb6
Add NFSd stats (#70) * Add NFSd parser * Move util functions from xfs to procfs package. * Update stats * Move parsing tools to util package. * Move util package. * nfsd: Fix naming, make it compile, fix bugs * nfsd: add initial tests * nfsd: Add minimal bounds check on V4Ops * Add bounds check to the `th` line. * Add missing license headers. * Make no matching nfsd line `unknown`. * Use subtests. Ben Kochie authored 6 years ago Tobias Schmidt committed 6 years ago
7 changed file(s) with 751 addition(s) and 33 deletion(s). Raw diff Collapse all Expand all
0 rc 0 6 18622
1 fh 0 0 0 0 0
2 io 157286400 0
3 th 8 0 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000
4 ra 32 0 0 0 0 0 0 0 0 0 0 0
5 net 18628 0 18628 6
6 rpc 18628 0 0 0 0
7 proc2 18 2 69 0 0 4410 0 0 0 0 0 0 0 0 0 0 0 99 2
8 proc3 22 2 112 0 2719 111 0 0 0 0 0 0 0 0 0 0 0 27 216 0 2 1 0
9 proc4 2 2 10853
10 proc4ops 72 0 0 0 1098 2 0 0 0 0 8179 5896 0 0 0 0 5900 0 0 2 0 2 0 9609 0 2 150 1272 0 0 0 1236 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
44 "os"
55 "path"
66
7 "github.com/prometheus/procfs/nfsd"
78 "github.com/prometheus/procfs/xfs"
89 )
910
4344
4445 return xfs.ParseStats(f)
4546 }
47
48 // NFSdRPCStats retrieves NFS daemon RPC statistics.
49 func (fs FS) NFSdRPCStats() (*nfsd.RPCStats, error) {
50 f, err := os.Open(fs.Path("net/rpc/nfsd"))
51 if err != nil {
52 return nil, err
53 }
54 defer f.Close()
55
56 return nfsd.ParseRPCStats(f)
57 }
0 // Copyright 2018 The Prometheus Authors
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12
13 package util
14
15 import "strconv"
16
17 // ParseUint32s parses a slice of strings into a slice of uint32s.
18 func ParseUint32s(ss []string) ([]uint32, error) {
19 us := make([]uint32, 0, len(ss))
20 for _, s := range ss {
21 u, err := strconv.ParseUint(s, 10, 32)
22 if err != nil {
23 return nil, err
24 }
25
26 us = append(us, uint32(u))
27 }
28
29 return us, nil
30 }
31
32 // ParseUint64s parses a slice of strings into a slice of uint64s.
33 func ParseUint64s(ss []string) ([]uint64, error) {
34 us := make([]uint64, 0, len(ss))
35 for _, s := range ss {
36 u, err := strconv.ParseUint(s, 10, 64)
37 if err != nil {
38 return nil, err
39 }
40
41 us = append(us, u)
42 }
43
44 return us, nil
45 }
0 // Copyright 2018 The Prometheus Authors
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12
13 // Package nfsd implements parsing of /proc/net/rpc/nfsd.
14 // Fields are documented in https://www.svennd.be/nfsd-stats-explained-procnetrpcnfsd/
15 package nfsd
16
17 // ReplyCache models the "rc" line.
18 type ReplyCache struct {
19 Hits uint64
20 Misses uint64
21 NoCache uint64
22 }
23
24 // FileHandles models the "fh" line.
25 type FileHandles struct {
26 Stale uint64
27 TotalLookups uint64
28 AnonLookups uint64
29 DirNoCache uint64
30 NoDirNoCache uint64
31 }
32
33 // InputOutput models the "io" line.
34 type InputOutput struct {
35 Read uint64
36 Write uint64
37 }
38
39 // Threads models the "th" line.
40 type Threads struct {
41 Threads uint64
42 FullCnt uint64
43 }
44
45 // ReadAheadCache models the "ra" line.
46 type ReadAheadCache struct {
47 CacheSize uint64
48 CacheHistogram []uint64
49 NotFound uint64
50 }
51
52 // Network models the "net" line.
53 type Network struct {
54 NetCount uint64
55 UDPCount uint64
56 TCPCount uint64
57 TCPConnect uint64
58 }
59
60 // RPC models the "rpc" line.
61 type RPC struct {
62 RPCCount uint64
63 BadCnt uint64
64 BadFmt uint64
65 BadAuth uint64
66 BadcInt uint64
67 }
68
69 // V2Stats models the "proc2" line.
70 type V2Stats struct {
71 Null uint64
72 GetAttr uint64
73 SetAttr uint64
74 Root uint64
75 Lookup uint64
76 ReadLink uint64
77 Read uint64
78 WrCache uint64
79 Write uint64
80 Create uint64
81 Remove uint64
82 Rename uint64
83 Link uint64
84 SymLink uint64
85 MkDir uint64
86 RmDir uint64
87 ReadDir uint64
88 FsStat uint64
89 }
90
91 // V3Stats models the "proc3" line.
92 type V3Stats struct {
93 Null uint64
94 GetAttr uint64
95 SetAttr uint64
96 Lookup uint64
97 Access uint64
98 ReadLink uint64
99 Read uint64
100 Write uint64
101 Create uint64
102 MkDir uint64
103 SymLink uint64
104 MkNod uint64
105 Remove uint64
106 RmDir uint64
107 Rename uint64
108 Link uint64
109 ReadDir uint64
110 ReadDirPlus uint64
111 FsStat uint64
112 FsInfo uint64
113 PathConf uint64
114 Commit uint64
115 }
116
117 // V4Stats models the "proc4" line.
118 type V4Stats struct {
119 Null uint64
120 Compound uint64
121 }
122
123 // V4Ops models the "proc4ops" line: NFSv4 operations
124 // Variable list, see:
125 // v4.0 https://tools.ietf.org/html/rfc3010 (38 operations)
126 // v4.1 https://tools.ietf.org/html/rfc5661 (58 operations)
127 // v4.2 https://tools.ietf.org/html/draft-ietf-nfsv4-minorversion2-41 (71 operations)
128 type V4Ops struct {
129 //Values uint64 // Variable depending on v4.x sub-version. TODO: Will this always at least include the fields in this struct?
130 Op0Unused uint64
131 Op1Unused uint64
132 Op2Future uint64
133 Access uint64
134 Close uint64
135 Commit uint64
136 Create uint64
137 DelegPurge uint64
138 DelegReturn uint64
139 GetAttr uint64
140 GetFH uint64
141 Link uint64
142 Lock uint64
143 Lockt uint64
144 Locku uint64
145 Lookup uint64
146 LookupRoot uint64
147 Nverify uint64
148 Open uint64
149 OpenAttr uint64
150 OpenConfirm uint64
151 OpenDgrd uint64
152 PutFH uint64
153 PutPubFH uint64
154 PutRootFH uint64
155 Read uint64
156 ReadDir uint64
157 ReadLink uint64
158 Remove uint64
159 Rename uint64
160 Renew uint64
161 RestoreFH uint64
162 SaveFH uint64
163 SecInfo uint64
164 SetAttr uint64
165 Verify uint64
166 Write uint64
167 RelLockOwner uint64
168 }
169
170 // RPCStats models all stats from /proc/net/rpc/nfsd.
171 type RPCStats struct {
172 ReplyCache ReplyCache
173 FileHandles FileHandles
174 InputOutput InputOutput
175 Threads Threads
176 ReadAheadCache ReadAheadCache
177 Network Network
178 RPC RPC
179 V2Stats V2Stats
180 V3Stats V3Stats
181 V4Stats V4Stats
182 V4Ops V4Ops
183 }
0 // Copyright 2018 The Prometheus Authors
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12
13 package nfsd
14
15 import (
16 "bufio"
17 "fmt"
18 "io"
19 "strings"
20
21 "github.com/prometheus/procfs/internal/util"
22 )
23
24 // ParseRPCStats returns stats read from /proc/net/rpc/nfsd
25 func ParseRPCStats(r io.Reader) (*RPCStats, error) {
26 stats := &RPCStats{}
27
28 scanner := bufio.NewScanner(r)
29 for scanner.Scan() {
30 line := scanner.Text()
31 parts := strings.Fields(scanner.Text())
32 // require at least <key> <value>
33 if len(parts) < 2 {
34 return nil, fmt.Errorf("invalid NFSd metric line %q", line)
35 }
36 label := parts[0]
37
38 var values []uint64
39 var err error
40 if label == "th" {
41 if len(parts) < 3 {
42 return nil, fmt.Errorf("invalid NFSd th metric line %q", line)
43 }
44 values, err = util.ParseUint64s(parts[1:3])
45 } else {
46 values, err = util.ParseUint64s(parts[1:])
47 }
48 if err != nil {
49 return nil, fmt.Errorf("error parsing NFSd metric line: %s", err)
50 }
51
52 switch metricLine := parts[0]; metricLine {
53 case "rc":
54 stats.ReplyCache, err = parseReplyCache(values)
55 case "fh":
56 stats.FileHandles, err = parseFileHandles(values)
57 case "io":
58 stats.InputOutput, err = parseInputOutput(values)
59 case "th":
60 stats.Threads, err = parseThreads(values)
61 case "ra":
62 stats.ReadAheadCache, err = parseReadAheadCache(values)
63 case "net":
64 stats.Network, err = parseNetwork(values)
65 case "rpc":
66 stats.RPC, err = parseRPC(values)
67 case "proc2":
68 stats.V2Stats, err = parseV2Stats(values)
69 case "proc3":
70 stats.V3Stats, err = parseV3Stats(values)
71 case "proc4":
72 stats.V4Stats, err = parseV4Stats(values)
73 case "proc4ops":
74 stats.V4Ops, err = parseV4Ops(values)
75 default:
76 return nil, fmt.Errorf("unknown NFSd metric line %q", metricLine)
77 }
78 if err != nil {
79 return nil, fmt.Errorf("errors parsing NFSd metric line: %s", err)
80 }
81 }
82
83 if err := scanner.Err(); err != nil {
84 return nil, fmt.Errorf("error scanning NFSd file: %s", err)
85 }
86
87 return stats, nil
88 }
89
90 func parseReplyCache(v []uint64) (ReplyCache, error) {
91 if len(v) != 3 {
92 return ReplyCache{}, fmt.Errorf("invalid ReplyCache line %q", v)
93 }
94
95 return ReplyCache{
96 Hits: v[0],
97 Misses: v[1],
98 NoCache: v[2],
99 }, nil
100 }
101
102 func parseFileHandles(v []uint64) (FileHandles, error) {
103 if len(v) != 5 {
104 return FileHandles{}, fmt.Errorf("invalid FileHandles, line %q", v)
105 }
106
107 return FileHandles{
108 Stale: v[0],
109 TotalLookups: v[1],
110 AnonLookups: v[2],
111 DirNoCache: v[3],
112 NoDirNoCache: v[4],
113 }, nil
114 }
115
116 func parseInputOutput(v []uint64) (InputOutput, error) {
117 if len(v) != 2 {
118 return InputOutput{}, fmt.Errorf("invalid InputOutput line %q", v)
119 }
120
121 return InputOutput{
122 Read: v[0],
123 Write: v[1],
124 }, nil
125 }
126
127 func parseThreads(v []uint64) (Threads, error) {
128 if len(v) != 2 {
129 return Threads{}, fmt.Errorf("invalid Threads line %q", v)
130 }
131
132 return Threads{
133 Threads: v[0],
134 FullCnt: v[1],
135 }, nil
136 }
137
138 func parseReadAheadCache(v []uint64) (ReadAheadCache, error) {
139 if len(v) != 12 {
140 return ReadAheadCache{}, fmt.Errorf("invalid ReadAheadCache line %q", v)
141 }
142
143 return ReadAheadCache{
144 CacheSize: v[0],
145 CacheHistogram: v[1:11],
146 NotFound: v[11],
147 }, nil
148 }
149
150 func parseNetwork(v []uint64) (Network, error) {
151 if len(v) != 4 {
152 return Network{}, fmt.Errorf("invalid Network line %q", v)
153 }
154
155 return Network{
156 NetCount: v[0],
157 UDPCount: v[1],
158 TCPCount: v[2],
159 TCPConnect: v[3],
160 }, nil
161 }
162
163 func parseRPC(v []uint64) (RPC, error) {
164 if len(v) != 5 {
165 return RPC{}, fmt.Errorf("invalid RPC line %q", v)
166 }
167
168 return RPC{
169 RPCCount: v[0],
170 BadCnt: v[1],
171 BadFmt: v[2],
172 BadAuth: v[3],
173 BadcInt: v[4],
174 }, nil
175 }
176
177 func parseV2Stats(v []uint64) (V2Stats, error) {
178 values := int(v[0])
179 if len(v[1:]) != values || values != 18 {
180 return V2Stats{}, fmt.Errorf("invalid V2Stats line %q", v)
181 }
182
183 return V2Stats{
184 Null: v[1],
185 GetAttr: v[2],
186 SetAttr: v[3],
187 Root: v[4],
188 Lookup: v[5],
189 ReadLink: v[6],
190 Read: v[7],
191 WrCache: v[8],
192 Write: v[9],
193 Create: v[10],
194 Remove: v[11],
195 Rename: v[12],
196 Link: v[13],
197 SymLink: v[14],
198 MkDir: v[15],
199 RmDir: v[16],
200 ReadDir: v[17],
201 FsStat: v[18],
202 }, nil
203 }
204
205 func parseV3Stats(v []uint64) (V3Stats, error) {
206 values := int(v[0])
207 if len(v[1:]) != values || values != 22 {
208 return V3Stats{}, fmt.Errorf("invalid V3Stats line %q", v)
209 }
210
211 return V3Stats{
212 Null: v[1],
213 GetAttr: v[2],
214 SetAttr: v[3],
215 Lookup: v[4],
216 Access: v[5],
217 ReadLink: v[6],
218 Read: v[7],
219 Write: v[8],
220 Create: v[9],
221 MkDir: v[10],
222 SymLink: v[11],
223 MkNod: v[12],
224 Remove: v[13],
225 RmDir: v[14],
226 Rename: v[15],
227 Link: v[16],
228 ReadDir: v[17],
229 ReadDirPlus: v[18],
230 FsStat: v[19],
231 FsInfo: v[20],
232 PathConf: v[21],
233 Commit: v[22],
234 }, nil
235 }
236
237 func parseV4Stats(v []uint64) (V4Stats, error) {
238 values := int(v[0])
239 if len(v[1:]) != values || values != 2 {
240 return V4Stats{}, fmt.Errorf("invalid V4Stats line %q", v)
241 }
242
243 return V4Stats{
244 Null: v[1],
245 Compound: v[2],
246 }, nil
247 }
248
249 func parseV4Ops(v []uint64) (V4Ops, error) {
250 values := int(v[0])
251 if len(v[1:]) != values || values < 39 {
252 return V4Ops{}, fmt.Errorf("invalid V4Ops line %q", v)
253 }
254
255 stats := V4Ops{
256 Op0Unused: v[1],
257 Op1Unused: v[2],
258 Op2Future: v[3],
259 Access: v[4],
260 Close: v[5],
261 Commit: v[6],
262 Create: v[7],
263 DelegPurge: v[8],
264 DelegReturn: v[9],
265 GetAttr: v[10],
266 GetFH: v[11],
267 Link: v[12],
268 Lock: v[13],
269 Lockt: v[14],
270 Locku: v[15],
271 Lookup: v[16],
272 LookupRoot: v[17],
273 Nverify: v[18],
274 Open: v[19],
275 OpenAttr: v[20],
276 OpenConfirm: v[21],
277 OpenDgrd: v[22],
278 PutFH: v[23],
279 PutPubFH: v[24],
280 PutRootFH: v[25],
281 Read: v[26],
282 ReadDir: v[27],
283 ReadLink: v[28],
284 Remove: v[29],
285 Rename: v[30],
286 Renew: v[31],
287 RestoreFH: v[32],
288 SaveFH: v[33],
289 SecInfo: v[34],
290 SetAttr: v[35],
291 Verify: v[36],
292 Write: v[37],
293 RelLockOwner: v[38],
294 }
295
296 return stats, nil
297 }
0 // Copyright 2018 The Prometheus Authors
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12
13 package nfsd_test
14
15 import (
16 "reflect"
17 "strings"
18 "testing"
19
20 "github.com/prometheus/procfs/nfsd"
21 )
22
23 func TestNewNFSdRPCStats(t *testing.T) {
24 tests := []struct {
25 name string
26 content string
27 stats *nfsd.RPCStats
28 invalid bool
29 }{
30 {
31 name: "invalid file",
32 content: "invalid",
33 invalid: true,
34 }, {
35 name: "good file",
36 content: `rc 0 6 18622
37 fh 0 0 0 0 0
38 io 157286400 0
39 th 8 0 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000 0.000
40 ra 32 0 0 0 0 0 0 0 0 0 0 0
41 net 18628 0 18628 6
42 rpc 18628 0 0 0 0
43 proc2 18 2 69 0 0 4410 0 0 0 0 0 0 0 0 0 0 0 99 2
44 proc3 22 2 112 0 2719 111 0 0 0 0 0 0 0 0 0 0 0 27 216 0 2 1 0
45 proc4 2 2 10853
46 proc4ops 72 0 0 0 1098 2 0 0 0 0 8179 5896 0 0 0 0 5900 0 0 2 0 2 0 9609 0 2 150 1272 0 0 0 1236 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
47 `,
48 stats: &nfsd.RPCStats{
49 ReplyCache: nfsd.ReplyCache{
50 Hits: 0,
51 Misses: 6,
52 NoCache: 18622,
53 },
54 FileHandles: nfsd.FileHandles{
55 Stale: 0,
56 TotalLookups: 0,
57 AnonLookups: 0,
58 DirNoCache: 0,
59 NoDirNoCache: 0,
60 },
61 InputOutput: nfsd.InputOutput{
62 Read: 157286400,
63 Write: 0,
64 },
65 Threads: nfsd.Threads{
66 Threads: 8,
67 FullCnt: 0,
68 },
69 ReadAheadCache: nfsd.ReadAheadCache{
70 CacheSize: 32,
71 CacheHistogram: []uint64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
72 NotFound: 0,
73 },
74 Network: nfsd.Network{
75 NetCount: 18628,
76 UDPCount: 0,
77 TCPCount: 18628,
78 TCPConnect: 6,
79 },
80 RPC: nfsd.RPC{
81 RPCCount: 18628,
82 BadCnt: 0,
83 BadFmt: 0,
84 BadAuth: 0,
85 BadcInt: 0,
86 },
87 V2Stats: nfsd.V2Stats{
88 Null: 2,
89 GetAttr: 69,
90 SetAttr: 0,
91 Root: 0,
92 Lookup: 4410,
93 ReadLink: 0,
94 Read: 0,
95 WrCache: 0,
96 Write: 0,
97 Create: 0,
98 Remove: 0,
99 Rename: 0,
100 Link: 0,
101 SymLink: 0,
102 MkDir: 0,
103 RmDir: 0,
104 ReadDir: 99,
105 FsStat: 2,
106 },
107 V3Stats: nfsd.V3Stats{
108 Null: 2,
109 GetAttr: 112,
110 SetAttr: 0,
111 Lookup: 2719,
112 Access: 111,
113 ReadLink: 0,
114 Read: 0,
115 Write: 0,
116 Create: 0,
117 MkDir: 0,
118 SymLink: 0,
119 MkNod: 0,
120 Remove: 0,
121 RmDir: 0,
122 Rename: 0,
123 Link: 0,
124 ReadDir: 27,
125 ReadDirPlus: 216,
126 FsStat: 0,
127 FsInfo: 2,
128 PathConf: 1,
129 Commit: 0,
130 },
131 V4Stats: nfsd.V4Stats{
132 Null: 2,
133 Compound: 10853,
134 },
135 V4Ops: nfsd.V4Ops{
136 Op0Unused: 0,
137 Op1Unused: 0,
138 Op2Future: 0,
139 Access: 1098,
140 Close: 2,
141 Commit: 0,
142 Create: 0,
143 DelegPurge: 0,
144 DelegReturn: 0,
145 GetAttr: 8179,
146 GetFH: 5896,
147 Link: 0,
148 Lock: 0,
149 Lockt: 0,
150 Locku: 0,
151 Lookup: 5900,
152 LookupRoot: 0,
153 Nverify: 0,
154 Open: 2,
155 OpenAttr: 0,
156 OpenConfirm: 2,
157 OpenDgrd: 0,
158 PutFH: 9609,
159 PutPubFH: 0,
160 PutRootFH: 2,
161 Read: 150,
162 ReadDir: 1272,
163 ReadLink: 0,
164 Remove: 0,
165 Rename: 0,
166 Renew: 1236,
167 RestoreFH: 0,
168 SaveFH: 0,
169 SecInfo: 0,
170 SetAttr: 0,
171 Verify: 3,
172 Write: 3,
173 RelLockOwner: 0,
174 },
175 },
176 },
177 }
178
179 for _, tt := range tests {
180 t.Run(tt.name, func(t *testing.T) {
181 stats, err := nfsd.ParseRPCStats(strings.NewReader(tt.content))
182
183 if tt.invalid && err == nil {
184 t.Fatal("expected an error, but none occurred")
185 }
186 if !tt.invalid && err != nil {
187 t.Fatalf("unexpected error: %v", err)
188 }
189
190 if want, have := tt.stats, stats; !reflect.DeepEqual(want, have) {
191 t.Fatalf("unexpected NFS stats:\nwant:\n%v\nhave:\n%v", want, have)
192 }
193 })
194 }
195 }
1616 "bufio"
1717 "fmt"
1818 "io"
19 "strconv"
2019 "strings"
20
21 "github.com/prometheus/procfs/internal/util"
2122 )
2223
2324 // ParseStats parses a Stats from an input io.Reader, using the format
6768
6869 // Extended precision counters are uint64 values.
6970 if label == fieldXpc {
70 us, err := parseUint64s(ss[1:])
71 us, err := util.ParseUint64s(ss[1:])
7172 if err != nil {
7273 return nil, err
7374 }
8182 }
8283
8384 // All other counters are uint32 values.
84 us, err := parseUint32s(ss[1:])
85 us, err := util.ParseUint32s(ss[1:])
8586 if err != nil {
8687 return nil, err
8788 }
326327 ReadBytes: us[2],
327328 }, nil
328329 }
329
330 // parseUint32s parses a slice of strings into a slice of uint32s.
331 func parseUint32s(ss []string) ([]uint32, error) {
332 us := make([]uint32, 0, len(ss))
333 for _, s := range ss {
334 u, err := strconv.ParseUint(s, 10, 32)
335 if err != nil {
336 return nil, err
337 }
338
339 us = append(us, uint32(u))
340 }
341
342 return us, nil
343 }
344
345 // parseUint64s parses a slice of strings into a slice of uint64s.
346 func parseUint64s(ss []string) ([]uint64, error) {
347 us := make([]uint64, 0, len(ss))
348 for _, s := range ss {
349 u, err := strconv.ParseUint(s, 10, 64)
350 if err != nil {
351 return nil, err
352 }
353
354 us = append(us, u)
355 }
356
357 return us, nil
358 }