1
2
3 package process
4
5 import (
6 "bufio"
7 "bytes"
8 "context"
9 "encoding/json"
10 "fmt"
11 "io/ioutil"
12 "math"
13 "os"
14 "path/filepath"
15 "strconv"
16 "strings"
17
18 "github.com/shirou/gopsutil/cpu"
19 "github.com/shirou/gopsutil/internal/common"
20 "github.com/shirou/gopsutil/net"
21 "github.com/tklauser/go-sysconf"
22 "golang.org/x/sys/unix"
23 )
24
25 var PageSize = uint64(os.Getpagesize())
26
27 const PrioProcess = 0
28
29 var ClockTicks = 100
30
31 func init() {
32 clkTck, err := sysconf.Sysconf(sysconf.SC_CLK_TCK)
33
34 if err == nil {
35 ClockTicks = int(clkTck)
36 }
37 }
38
39
40 type MemoryInfoExStat struct {
41 RSS uint64 `json:"rss"`
42 VMS uint64 `json:"vms"`
43 Shared uint64 `json:"shared"`
44 Text uint64 `json:"text"`
45 Lib uint64 `json:"lib"`
46 Data uint64 `json:"data"`
47 Dirty uint64 `json:"dirty"`
48 }
49
50 func (m MemoryInfoExStat) String() string {
51 s, _ := json.Marshal(m)
52 return string(s)
53 }
54
55 type MemoryMapsStat struct {
56 Path string `json:"path"`
57 Rss uint64 `json:"rss"`
58 Size uint64 `json:"size"`
59 Pss uint64 `json:"pss"`
60 SharedClean uint64 `json:"sharedClean"`
61 SharedDirty uint64 `json:"sharedDirty"`
62 PrivateClean uint64 `json:"privateClean"`
63 PrivateDirty uint64 `json:"privateDirty"`
64 Referenced uint64 `json:"referenced"`
65 Anonymous uint64 `json:"anonymous"`
66 Swap uint64 `json:"swap"`
67 }
68
69
70 func (m MemoryMapsStat) String() string {
71 s, _ := json.Marshal(m)
72 return string(s)
73 }
74
75 func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
76 _, ppid, _, _, _, _, _, err := p.fillFromStatWithContext(ctx)
77 if err != nil {
78 return -1, err
79 }
80 return ppid, nil
81 }
82
83 func (p *Process) NameWithContext(ctx context.Context) (string, error) {
84 if p.name == "" {
85 if err := p.fillNameWithContext(ctx); err != nil {
86 return "", err
87 }
88 }
89 return p.name, nil
90 }
91
92 func (p *Process) TgidWithContext(ctx context.Context) (int32, error) {
93 if p.tgid == 0 {
94 if err := p.fillFromStatusWithContext(ctx); err != nil {
95 return 0, err
96 }
97 }
98 return p.tgid, nil
99 }
100
101 func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
102 return p.fillFromExeWithContext(ctx)
103 }
104
105 func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
106 return p.fillFromCmdlineWithContext(ctx)
107 }
108
109 func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
110 return p.fillSliceFromCmdlineWithContext(ctx)
111 }
112
113 func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
114 _, _, _, createTime, _, _, _, err := p.fillFromStatWithContext(ctx)
115 if err != nil {
116 return 0, err
117 }
118 return createTime, nil
119 }
120
121 func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
122 return p.fillFromCwdWithContext(ctx)
123 }
124
125 func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
126 err := p.fillFromStatusWithContext(ctx)
127 if err != nil {
128 return nil, err
129 }
130 if p.parent == 0 {
131 return nil, fmt.Errorf("wrong number of parents")
132 }
133 return NewProcessWithContext(ctx, p.parent)
134 }
135
136 func (p *Process) StatusWithContext(ctx context.Context) (string, error) {
137 err := p.fillFromStatusWithContext(ctx)
138 if err != nil {
139 return "", err
140 }
141 return p.status, nil
142 }
143
144 func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) {
145
146 pid := p.Pid
147 statPath := common.HostProc(strconv.Itoa(int(pid)), "stat")
148 contents, err := ioutil.ReadFile(statPath)
149 if err != nil {
150 return false, err
151 }
152 fields := strings.Fields(string(contents))
153 if len(fields) < 8 {
154 return false, fmt.Errorf("insufficient data in %s", statPath)
155 }
156 pgid := fields[4]
157 tpgid := fields[7]
158 return pgid == tpgid, nil
159 }
160
161 func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
162 err := p.fillFromStatusWithContext(ctx)
163 if err != nil {
164 return []int32{}, err
165 }
166 return p.uids, nil
167 }
168
169 func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
170 err := p.fillFromStatusWithContext(ctx)
171 if err != nil {
172 return []int32{}, err
173 }
174 return p.gids, nil
175 }
176
177 func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) {
178 err := p.fillFromStatusWithContext(ctx)
179 if err != nil {
180 return []int32{}, err
181 }
182 return p.groups, nil
183 }
184
185 func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
186 t, _, _, _, _, _, _, err := p.fillFromStatWithContext(ctx)
187 if err != nil {
188 return "", err
189 }
190 termmap, err := getTerminalMap()
191 if err != nil {
192 return "", err
193 }
194 terminal := termmap[t]
195 return terminal, nil
196 }
197
198 func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
199 _, _, _, _, _, nice, _, err := p.fillFromStatWithContext(ctx)
200 if err != nil {
201 return 0, err
202 }
203 return nice, nil
204 }
205
206 func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
207 return 0, common.ErrNotImplementedError
208 }
209
210 func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) {
211 return p.RlimitUsageWithContext(ctx, false)
212 }
213
214 func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) {
215 rlimits, err := p.fillFromLimitsWithContext(ctx)
216 if !gatherUsed || err != nil {
217 return rlimits, err
218 }
219
220 _, _, _, _, rtprio, nice, _, err := p.fillFromStatWithContext(ctx)
221 if err != nil {
222 return nil, err
223 }
224 if err := p.fillFromStatusWithContext(ctx); err != nil {
225 return nil, err
226 }
227
228 for i := range rlimits {
229 rs := &rlimits[i]
230 switch rs.Resource {
231 case RLIMIT_CPU:
232 times, err := p.TimesWithContext(ctx)
233 if err != nil {
234 return nil, err
235 }
236 rs.Used = uint64(times.User + times.System)
237 case RLIMIT_DATA:
238 rs.Used = uint64(p.memInfo.Data)
239 case RLIMIT_STACK:
240 rs.Used = uint64(p.memInfo.Stack)
241 case RLIMIT_RSS:
242 rs.Used = uint64(p.memInfo.RSS)
243 case RLIMIT_NOFILE:
244 n, err := p.NumFDsWithContext(ctx)
245 if err != nil {
246 return nil, err
247 }
248 rs.Used = uint64(n)
249 case RLIMIT_MEMLOCK:
250 rs.Used = uint64(p.memInfo.Locked)
251 case RLIMIT_AS:
252 rs.Used = uint64(p.memInfo.VMS)
253 case RLIMIT_LOCKS:
254
255 case RLIMIT_SIGPENDING:
256 rs.Used = p.sigInfo.PendingProcess
257 case RLIMIT_NICE:
258
259
260 rs.Used = uint64(nice)
261 case RLIMIT_RTPRIO:
262 rs.Used = uint64(rtprio)
263 }
264 }
265
266 return rlimits, err
267 }
268
269 func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
270 return p.fillFromIOWithContext(ctx)
271 }
272
273 func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) {
274 err := p.fillFromStatusWithContext(ctx)
275 if err != nil {
276 return nil, err
277 }
278 return p.numCtxSwitches, nil
279 }
280
281 func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
282 _, fnames, err := p.fillFromfdListWithContext(ctx)
283 return int32(len(fnames)), err
284 }
285
286 func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
287 err := p.fillFromStatusWithContext(ctx)
288 if err != nil {
289 return 0, err
290 }
291 return p.numThreads, nil
292 }
293
294 func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) {
295 ret := make(map[int32]*cpu.TimesStat)
296 taskPath := common.HostProc(strconv.Itoa(int(p.Pid)), "task")
297
298 tids, err := readPidsFromDir(taskPath)
299 if err != nil {
300 return nil, err
301 }
302
303 for _, tid := range tids {
304 _, _, cpuTimes, _, _, _, _, err := p.fillFromTIDStatWithContext(ctx, tid)
305 if err != nil {
306 return nil, err
307 }
308 ret[tid] = cpuTimes
309 }
310
311 return ret, nil
312 }
313
314 func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
315 _, _, cpuTimes, _, _, _, _, err := p.fillFromStatWithContext(ctx)
316 if err != nil {
317 return nil, err
318 }
319 return cpuTimes, nil
320 }
321
322 func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) {
323 return nil, common.ErrNotImplementedError
324 }
325
326 func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
327 meminfo, _, err := p.fillFromStatmWithContext(ctx)
328 if err != nil {
329 return nil, err
330 }
331 return meminfo, nil
332 }
333
334 func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) {
335 _, memInfoEx, err := p.fillFromStatmWithContext(ctx)
336 if err != nil {
337 return nil, err
338 }
339 return memInfoEx, nil
340 }
341
342 func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) {
343 _, _, _, _, _, _, pageFaults, err := p.fillFromStatWithContext(ctx)
344 if err != nil {
345 return nil, err
346 }
347 return pageFaults, nil
348
349 }
350
351 func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
352 pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
353 if err != nil {
354 if len(pids) == 0 {
355 return nil, ErrorNoChildren
356 }
357 return nil, err
358 }
359 ret := make([]*Process, 0, len(pids))
360 for _, pid := range pids {
361 np, err := NewProcessWithContext(ctx, pid)
362 if err != nil {
363 return nil, err
364 }
365 ret = append(ret, np)
366 }
367 return ret, nil
368 }
369
370 func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) {
371 _, ofs, err := p.fillFromfdWithContext(ctx)
372 if err != nil {
373 return nil, err
374 }
375 ret := make([]OpenFilesStat, len(ofs))
376 for i, o := range ofs {
377 ret[i] = *o
378 }
379
380 return ret, nil
381 }
382
383 func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) {
384 return net.ConnectionsPidWithContext(ctx, "all", p.Pid)
385 }
386
387 func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) {
388 return net.ConnectionsPidMaxWithContext(ctx, "all", p.Pid, max)
389 }
390
391 func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) {
392 filename := common.HostProc(strconv.Itoa(int(p.Pid)), "net/dev")
393 return net.IOCountersByFileWithContext(ctx, pernic, filename)
394 }
395
396 func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) {
397 pid := p.Pid
398 var ret []MemoryMapsStat
399 if grouped {
400 ret = make([]MemoryMapsStat, 1)
401 }
402 smapsPath := common.HostProc(strconv.Itoa(int(pid)), "smaps")
403 contents, err := ioutil.ReadFile(smapsPath)
404 if err != nil {
405 return nil, err
406 }
407 lines := strings.Split(string(contents), "\n")
408
409
410 getBlock := func(firstLine []string, block []string) (MemoryMapsStat, error) {
411 m := MemoryMapsStat{}
412 m.Path = firstLine[len(firstLine)-1]
413
414 for _, line := range block {
415 if strings.Contains(line, "VmFlags") {
416 continue
417 }
418 field := strings.Split(line, ":")
419 if len(field) < 2 {
420 continue
421 }
422 v := strings.Trim(field[1], "kB")
423 v = strings.TrimSpace(v)
424 t, err := strconv.ParseUint(v, 10, 64)
425 if err != nil {
426 return m, err
427 }
428
429 switch field[0] {
430 case "Size":
431 m.Size = t
432 case "Rss":
433 m.Rss = t
434 case "Pss":
435 m.Pss = t
436 case "Shared_Clean":
437 m.SharedClean = t
438 case "Shared_Dirty":
439 m.SharedDirty = t
440 case "Private_Clean":
441 m.PrivateClean = t
442 case "Private_Dirty":
443 m.PrivateDirty = t
444 case "Referenced":
445 m.Referenced = t
446 case "Anonymous":
447 m.Anonymous = t
448 case "Swap":
449 m.Swap = t
450 }
451 }
452 return m, nil
453 }
454
455 var firstLine []string
456 blocks := make([]string, 0, 16)
457 for i, line := range lines {
458 fields := strings.Fields(line)
459
460 if (len(fields) > 0 && !strings.HasSuffix(fields[0], ":")) || i == len(lines)-1 {
461
462 if len(firstLine) > 0 && len(blocks) > 0 {
463 g, err := getBlock(firstLine, blocks)
464 if err != nil {
465 return &ret, err
466 }
467 if grouped {
468 ret[0].Size += g.Size
469 ret[0].Rss += g.Rss
470 ret[0].Pss += g.Pss
471 ret[0].SharedClean += g.SharedClean
472 ret[0].SharedDirty += g.SharedDirty
473 ret[0].PrivateClean += g.PrivateClean
474 ret[0].PrivateDirty += g.PrivateDirty
475 ret[0].Referenced += g.Referenced
476 ret[0].Anonymous += g.Anonymous
477 ret[0].Swap += g.Swap
478 } else {
479 ret = append(ret, g)
480 }
481 }
482
483 blocks = make([]string, 0, 16)
484 firstLine = fields
485 } else {
486 blocks = append(blocks, line)
487 }
488 }
489
490 return &ret, nil
491 }
492
493 func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) {
494 environPath := common.HostProc(strconv.Itoa(int(p.Pid)), "environ")
495
496 environContent, err := ioutil.ReadFile(environPath)
497 if err != nil {
498 return nil, err
499 }
500
501 return strings.Split(string(environContent), "\000"), nil
502 }
503
504
507
508 func limitToInt(val string) (int32, error) {
509 if val == "unlimited" {
510 return math.MaxInt32, nil
511 } else {
512 res, err := strconv.ParseInt(val, 10, 32)
513 if err != nil {
514 return 0, err
515 }
516 return int32(res), nil
517 }
518 }
519
520
521 func (p *Process) fillNameWithContext(ctx context.Context) error {
522 err := p.fillFromCommWithContext(ctx)
523 if err == nil && p.name != "" && len(p.name) < 15 {
524 return nil
525 }
526 return p.fillFromStatusWithContext(ctx)
527 }
528
529
530 func (p *Process) fillFromCommWithContext(ctx context.Context) error {
531 pid := p.Pid
532 statPath := common.HostProc(strconv.Itoa(int(pid)), "comm")
533 contents, err := ioutil.ReadFile(statPath)
534 if err != nil {
535 return err
536 }
537
538 p.name = strings.TrimSuffix(string(contents), "\n")
539 return nil
540 }
541
542
543 func (p *Process) fillFromLimitsWithContext(ctx context.Context) ([]RlimitStat, error) {
544 pid := p.Pid
545 limitsFile := common.HostProc(strconv.Itoa(int(pid)), "limits")
546 d, err := os.Open(limitsFile)
547 if err != nil {
548 return nil, err
549 }
550 defer d.Close()
551
552 var limitStats []RlimitStat
553
554 limitsScanner := bufio.NewScanner(d)
555 for limitsScanner.Scan() {
556 var statItem RlimitStat
557
558 str := strings.Fields(limitsScanner.Text())
559
560
561 if strings.Contains(str[len(str)-1], "Units") {
562 continue
563 }
564
565
566 statItem.Hard, err = limitToInt(str[len(str)-1])
567 if err != nil {
568
569 str = str[:len(str)-1]
570 statItem.Hard, err = limitToInt(str[len(str)-1])
571 if err != nil {
572 return nil, err
573 }
574 }
575
576 str = str[:len(str)-1]
577
578
579 statItem.Soft, err = limitToInt(str[len(str)-1])
580 if err != nil {
581 return nil, err
582 }
583
584 str = str[:len(str)-1]
585
586
587 resourceName := strings.Join(str, " ")
588 switch resourceName {
589 case "Max cpu time":
590 statItem.Resource = RLIMIT_CPU
591 case "Max file size":
592 statItem.Resource = RLIMIT_FSIZE
593 case "Max data size":
594 statItem.Resource = RLIMIT_DATA
595 case "Max stack size":
596 statItem.Resource = RLIMIT_STACK
597 case "Max core file size":
598 statItem.Resource = RLIMIT_CORE
599 case "Max resident set":
600 statItem.Resource = RLIMIT_RSS
601 case "Max processes":
602 statItem.Resource = RLIMIT_NPROC
603 case "Max open files":
604 statItem.Resource = RLIMIT_NOFILE
605 case "Max locked memory":
606 statItem.Resource = RLIMIT_MEMLOCK
607 case "Max address space":
608 statItem.Resource = RLIMIT_AS
609 case "Max file locks":
610 statItem.Resource = RLIMIT_LOCKS
611 case "Max pending signals":
612 statItem.Resource = RLIMIT_SIGPENDING
613 case "Max msgqueue size":
614 statItem.Resource = RLIMIT_MSGQUEUE
615 case "Max nice priority":
616 statItem.Resource = RLIMIT_NICE
617 case "Max realtime priority":
618 statItem.Resource = RLIMIT_RTPRIO
619 case "Max realtime timeout":
620 statItem.Resource = RLIMIT_RTTIME
621 default:
622 continue
623 }
624
625 limitStats = append(limitStats, statItem)
626 }
627
628 if err := limitsScanner.Err(); err != nil {
629 return nil, err
630 }
631
632 return limitStats, nil
633 }
634
635
636 func (p *Process) fillFromfdListWithContext(ctx context.Context) (string, []string, error) {
637 pid := p.Pid
638 statPath := common.HostProc(strconv.Itoa(int(pid)), "fd")
639 d, err := os.Open(statPath)
640 if err != nil {
641 return statPath, []string{}, err
642 }
643 defer d.Close()
644 fnames, err := d.Readdirnames(-1)
645 return statPath, fnames, err
646 }
647
648
649 func (p *Process) fillFromfdWithContext(ctx context.Context) (int32, []*OpenFilesStat, error) {
650 statPath, fnames, err := p.fillFromfdListWithContext(ctx)
651 if err != nil {
652 return 0, nil, err
653 }
654 numFDs := int32(len(fnames))
655
656 var openfiles []*OpenFilesStat
657 for _, fd := range fnames {
658 fpath := filepath.Join(statPath, fd)
659 filepath, err := os.Readlink(fpath)
660 if err != nil {
661 continue
662 }
663 t, err := strconv.ParseUint(fd, 10, 64)
664 if err != nil {
665 return numFDs, openfiles, err
666 }
667 o := &OpenFilesStat{
668 Path: filepath,
669 Fd: t,
670 }
671 openfiles = append(openfiles, o)
672 }
673
674 return numFDs, openfiles, nil
675 }
676
677
678 func (p *Process) fillFromCwdWithContext(ctx context.Context) (string, error) {
679 pid := p.Pid
680 cwdPath := common.HostProc(strconv.Itoa(int(pid)), "cwd")
681 cwd, err := os.Readlink(cwdPath)
682 if err != nil {
683 return "", err
684 }
685 return string(cwd), nil
686 }
687
688
689 func (p *Process) fillFromExeWithContext(ctx context.Context) (string, error) {
690 pid := p.Pid
691 exePath := common.HostProc(strconv.Itoa(int(pid)), "exe")
692 exe, err := os.Readlink(exePath)
693 if err != nil {
694 return "", err
695 }
696 return string(exe), nil
697 }
698
699
700 func (p *Process) fillFromCmdlineWithContext(ctx context.Context) (string, error) {
701 pid := p.Pid
702 cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline")
703 cmdline, err := ioutil.ReadFile(cmdPath)
704 if err != nil {
705 return "", err
706 }
707 ret := strings.FieldsFunc(string(cmdline), func(r rune) bool {
708 return r == '\u0000'
709 })
710
711 return strings.Join(ret, " "), nil
712 }
713
714 func (p *Process) fillSliceFromCmdlineWithContext(ctx context.Context) ([]string, error) {
715 pid := p.Pid
716 cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline")
717 cmdline, err := ioutil.ReadFile(cmdPath)
718 if err != nil {
719 return nil, err
720 }
721 if len(cmdline) == 0 {
722 return nil, nil
723 }
724 if cmdline[len(cmdline)-1] == 0 {
725 cmdline = cmdline[:len(cmdline)-1]
726 }
727 parts := bytes.Split(cmdline, []byte{0})
728 var strParts []string
729 for _, p := range parts {
730 strParts = append(strParts, string(p))
731 }
732
733 return strParts, nil
734 }
735
736
737 func (p *Process) fillFromIOWithContext(ctx context.Context) (*IOCountersStat, error) {
738 pid := p.Pid
739 ioPath := common.HostProc(strconv.Itoa(int(pid)), "io")
740 ioline, err := ioutil.ReadFile(ioPath)
741 if err != nil {
742 return nil, err
743 }
744 lines := strings.Split(string(ioline), "\n")
745 ret := &IOCountersStat{}
746
747 for _, line := range lines {
748 field := strings.Fields(line)
749 if len(field) < 2 {
750 continue
751 }
752 t, err := strconv.ParseUint(field[1], 10, 64)
753 if err != nil {
754 return nil, err
755 }
756 param := field[0]
757 if strings.HasSuffix(param, ":") {
758 param = param[:len(param)-1]
759 }
760 switch param {
761 case "syscr":
762 ret.ReadCount = t
763 case "syscw":
764 ret.WriteCount = t
765 case "read_bytes":
766 ret.ReadBytes = t
767 case "write_bytes":
768 ret.WriteBytes = t
769 }
770 }
771
772 return ret, nil
773 }
774
775
776 func (p *Process) fillFromStatmWithContext(ctx context.Context) (*MemoryInfoStat, *MemoryInfoExStat, error) {
777 pid := p.Pid
778 memPath := common.HostProc(strconv.Itoa(int(pid)), "statm")
779 contents, err := ioutil.ReadFile(memPath)
780 if err != nil {
781 return nil, nil, err
782 }
783 fields := strings.Split(string(contents), " ")
784
785 vms, err := strconv.ParseUint(fields[0], 10, 64)
786 if err != nil {
787 return nil, nil, err
788 }
789 rss, err := strconv.ParseUint(fields[1], 10, 64)
790 if err != nil {
791 return nil, nil, err
792 }
793 memInfo := &MemoryInfoStat{
794 RSS: rss * PageSize,
795 VMS: vms * PageSize,
796 }
797
798 shared, err := strconv.ParseUint(fields[2], 10, 64)
799 if err != nil {
800 return nil, nil, err
801 }
802 text, err := strconv.ParseUint(fields[3], 10, 64)
803 if err != nil {
804 return nil, nil, err
805 }
806 lib, err := strconv.ParseUint(fields[4], 10, 64)
807 if err != nil {
808 return nil, nil, err
809 }
810 dirty, err := strconv.ParseUint(fields[5], 10, 64)
811 if err != nil {
812 return nil, nil, err
813 }
814
815 memInfoEx := &MemoryInfoExStat{
816 RSS: rss * PageSize,
817 VMS: vms * PageSize,
818 Shared: shared * PageSize,
819 Text: text * PageSize,
820 Lib: lib * PageSize,
821 Dirty: dirty * PageSize,
822 }
823
824 return memInfo, memInfoEx, nil
825 }
826
827
828 func (p *Process) fillFromStatusWithContext(ctx context.Context) error {
829 pid := p.Pid
830 statPath := common.HostProc(strconv.Itoa(int(pid)), "status")
831 contents, err := ioutil.ReadFile(statPath)
832 if err != nil {
833 return err
834 }
835 lines := strings.Split(string(contents), "\n")
836 p.numCtxSwitches = &NumCtxSwitchesStat{}
837 p.memInfo = &MemoryInfoStat{}
838 p.sigInfo = &SignalInfoStat{}
839 for _, line := range lines {
840 tabParts := strings.SplitN(line, "\t", 2)
841 if len(tabParts) < 2 {
842 continue
843 }
844 value := tabParts[1]
845 switch strings.TrimRight(tabParts[0], ":") {
846 case "Name":
847 p.name = strings.Trim(value, " \t")
848 if len(p.name) >= 15 {
849 cmdlineSlice, err := p.CmdlineSlice()
850 if err != nil {
851 return err
852 }
853 if len(cmdlineSlice) > 0 {
854 extendedName := filepath.Base(cmdlineSlice[0])
855 if strings.HasPrefix(extendedName, p.name) {
856 p.name = extendedName
857 } else {
858 p.name = cmdlineSlice[0]
859 }
860 }
861 }
862
863 p.name = string([]byte(p.name))
864 case "State":
865 p.status = value[0:1]
866
867 p.status = string([]byte(p.status))
868 case "PPid", "Ppid":
869 pval, err := strconv.ParseInt(value, 10, 32)
870 if err != nil {
871 return err
872 }
873 p.parent = int32(pval)
874 case "Tgid":
875 pval, err := strconv.ParseInt(value, 10, 32)
876 if err != nil {
877 return err
878 }
879 p.tgid = int32(pval)
880 case "Uid":
881 p.uids = make([]int32, 0, 4)
882 for _, i := range strings.Split(value, "\t") {
883 v, err := strconv.ParseInt(i, 10, 32)
884 if err != nil {
885 return err
886 }
887 p.uids = append(p.uids, int32(v))
888 }
889 case "Gid":
890 p.gids = make([]int32, 0, 4)
891 for _, i := range strings.Split(value, "\t") {
892 v, err := strconv.ParseInt(i, 10, 32)
893 if err != nil {
894 return err
895 }
896 p.gids = append(p.gids, int32(v))
897 }
898 case "Groups":
899 groups := strings.Fields(value)
900 p.groups = make([]int32, 0, len(groups))
901 for _, i := range groups {
902 v, err := strconv.ParseInt(i, 10, 32)
903 if err != nil {
904 return err
905 }
906 p.groups = append(p.groups, int32(v))
907 }
908 case "Threads":
909 v, err := strconv.ParseInt(value, 10, 32)
910 if err != nil {
911 return err
912 }
913 p.numThreads = int32(v)
914 case "voluntary_ctxt_switches":
915 v, err := strconv.ParseInt(value, 10, 64)
916 if err != nil {
917 return err
918 }
919 p.numCtxSwitches.Voluntary = v
920 case "nonvoluntary_ctxt_switches":
921 v, err := strconv.ParseInt(value, 10, 64)
922 if err != nil {
923 return err
924 }
925 p.numCtxSwitches.Involuntary = v
926 case "VmRSS":
927 value := strings.Trim(value, " kB")
928 v, err := strconv.ParseUint(value, 10, 64)
929 if err != nil {
930 return err
931 }
932 p.memInfo.RSS = v * 1024
933 case "VmSize":
934 value := strings.Trim(value, " kB")
935 v, err := strconv.ParseUint(value, 10, 64)
936 if err != nil {
937 return err
938 }
939 p.memInfo.VMS = v * 1024
940 case "VmSwap":
941 value := strings.Trim(value, " kB")
942 v, err := strconv.ParseUint(value, 10, 64)
943 if err != nil {
944 return err
945 }
946 p.memInfo.Swap = v * 1024
947 case "VmHWM":
948 value := strings.Trim(value, " kB")
949 v, err := strconv.ParseUint(value, 10, 64)
950 if err != nil {
951 return err
952 }
953 p.memInfo.HWM = v * 1024
954 case "VmData":
955 value := strings.Trim(value, " kB")
956 v, err := strconv.ParseUint(value, 10, 64)
957 if err != nil {
958 return err
959 }
960 p.memInfo.Data = v * 1024
961 case "VmStk":
962 value := strings.Trim(value, " kB")
963 v, err := strconv.ParseUint(value, 10, 64)
964 if err != nil {
965 return err
966 }
967 p.memInfo.Stack = v * 1024
968 case "VmLck":
969 value := strings.Trim(value, " kB")
970 v, err := strconv.ParseUint(value, 10, 64)
971 if err != nil {
972 return err
973 }
974 p.memInfo.Locked = v * 1024
975 case "SigPnd":
976 if len(value) > 16 {
977 value = value[len(value)-16:]
978 }
979 v, err := strconv.ParseUint(value, 16, 64)
980 if err != nil {
981 return err
982 }
983 p.sigInfo.PendingThread = v
984 case "ShdPnd":
985 if len(value) > 16 {
986 value = value[len(value)-16:]
987 }
988 v, err := strconv.ParseUint(value, 16, 64)
989 if err != nil {
990 return err
991 }
992 p.sigInfo.PendingProcess = v
993 case "SigBlk":
994 if len(value) > 16 {
995 value = value[len(value)-16:]
996 }
997 v, err := strconv.ParseUint(value, 16, 64)
998 if err != nil {
999 return err
1000 }
1001 p.sigInfo.Blocked = v
1002 case "SigIgn":
1003 if len(value) > 16 {
1004 value = value[len(value)-16:]
1005 }
1006 v, err := strconv.ParseUint(value, 16, 64)
1007 if err != nil {
1008 return err
1009 }
1010 p.sigInfo.Ignored = v
1011 case "SigCgt":
1012 if len(value) > 16 {
1013 value = value[len(value)-16:]
1014 }
1015 v, err := strconv.ParseUint(value, 16, 64)
1016 if err != nil {
1017 return err
1018 }
1019 p.sigInfo.Caught = v
1020 }
1021
1022 }
1023 return nil
1024 }
1025
1026 func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (uint64, int32, *cpu.TimesStat, int64, uint32, int32, *PageFaultsStat, error) {
1027 pid := p.Pid
1028 var statPath string
1029
1030 if tid == -1 {
1031 statPath = common.HostProc(strconv.Itoa(int(pid)), "stat")
1032 } else {
1033 statPath = common.HostProc(strconv.Itoa(int(pid)), "task", strconv.Itoa(int(tid)), "stat")
1034 }
1035
1036 contents, err := ioutil.ReadFile(statPath)
1037 if err != nil {
1038 return 0, 0, nil, 0, 0, 0, nil, err
1039 }
1040
1041 fields := splitProcStat(contents)
1042
1043 terminal, err := strconv.ParseUint(fields[7], 10, 64)
1044 if err != nil {
1045 return 0, 0, nil, 0, 0, 0, nil, err
1046 }
1047
1048 ppid, err := strconv.ParseInt(fields[4], 10, 32)
1049 if err != nil {
1050 return 0, 0, nil, 0, 0, 0, nil, err
1051 }
1052 utime, err := strconv.ParseFloat(fields[14], 64)
1053 if err != nil {
1054 return 0, 0, nil, 0, 0, 0, nil, err
1055 }
1056
1057 stime, err := strconv.ParseFloat(fields[15], 64)
1058 if err != nil {
1059 return 0, 0, nil, 0, 0, 0, nil, err
1060 }
1061
1062
1063
1064
1065 var iotime float64
1066 if len(fields) > 42 {
1067 iotime, err = strconv.ParseFloat(fields[42], 64)
1068 if err != nil {
1069 iotime = 0
1070 }
1071 } else {
1072 iotime = 0
1073 }
1074
1075 cpuTimes := &cpu.TimesStat{
1076 CPU: "cpu",
1077 User: utime / float64(ClockTicks),
1078 System: stime / float64(ClockTicks),
1079 Iowait: iotime / float64(ClockTicks),
1080 }
1081
1082 bootTime, _ := common.BootTimeWithContext(ctx)
1083 t, err := strconv.ParseUint(fields[22], 10, 64)
1084 if err != nil {
1085 return 0, 0, nil, 0, 0, 0, nil, err
1086 }
1087 ctime := (t / uint64(ClockTicks)) + uint64(bootTime)
1088 createTime := int64(ctime * 1000)
1089
1090 rtpriority, err := strconv.ParseInt(fields[18], 10, 32)
1091 if err != nil {
1092 return 0, 0, nil, 0, 0, 0, nil, err
1093 }
1094 if rtpriority < 0 {
1095 rtpriority = rtpriority*-1 - 1
1096 } else {
1097 rtpriority = 0
1098 }
1099
1100
1101
1102 snice, _ := unix.Getpriority(PrioProcess, int(pid))
1103 nice := int32(snice)
1104
1105 minFault, err := strconv.ParseUint(fields[10], 10, 64)
1106 if err != nil {
1107 return 0, 0, nil, 0, 0, 0, nil, err
1108 }
1109 cMinFault, err := strconv.ParseUint(fields[11], 10, 64)
1110 if err != nil {
1111 return 0, 0, nil, 0, 0, 0, nil, err
1112 }
1113 majFault, err := strconv.ParseUint(fields[12], 10, 64)
1114 if err != nil {
1115 return 0, 0, nil, 0, 0, 0, nil, err
1116 }
1117 cMajFault, err := strconv.ParseUint(fields[13], 10, 64)
1118 if err != nil {
1119 return 0, 0, nil, 0, 0, 0, nil, err
1120 }
1121
1122 faults := &PageFaultsStat{
1123 MinorFaults: minFault,
1124 MajorFaults: majFault,
1125 ChildMinorFaults: cMinFault,
1126 ChildMajorFaults: cMajFault,
1127 }
1128
1129 return terminal, int32(ppid), cpuTimes, createTime, uint32(rtpriority), nice, faults, nil
1130 }
1131
1132 func (p *Process) fillFromStatWithContext(ctx context.Context) (uint64, int32, *cpu.TimesStat, int64, uint32, int32, *PageFaultsStat, error) {
1133 return p.fillFromTIDStatWithContext(ctx, -1)
1134 }
1135
1136 func pidsWithContext(ctx context.Context) ([]int32, error) {
1137 return readPidsFromDir(common.HostProc())
1138 }
1139
1140 func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
1141 out := []*Process{}
1142
1143 pids, err := PidsWithContext(ctx)
1144 if err != nil {
1145 return out, err
1146 }
1147
1148 for _, pid := range pids {
1149 p, err := NewProcessWithContext(ctx, pid)
1150 if err != nil {
1151 continue
1152 }
1153 out = append(out, p)
1154 }
1155
1156 return out, nil
1157 }
1158
1159 func readPidsFromDir(path string) ([]int32, error) {
1160 var ret []int32
1161
1162 d, err := os.Open(path)
1163 if err != nil {
1164 return nil, err
1165 }
1166 defer d.Close()
1167
1168 fnames, err := d.Readdirnames(-1)
1169 if err != nil {
1170 return nil, err
1171 }
1172 for _, fname := range fnames {
1173 pid, err := strconv.ParseInt(fname, 10, 32)
1174 if err != nil {
1175
1176 continue
1177 }
1178 ret = append(ret, int32(pid))
1179 }
1180
1181 return ret, nil
1182 }
1183
1184 func splitProcStat(content []byte) []string {
1185 nameStart := bytes.IndexByte(content, '(')
1186 nameEnd := bytes.LastIndexByte(content, ')')
1187 restFields := strings.Fields(string(content[nameEnd+2:]))
1188 name := content[nameStart+1 : nameEnd]
1189 pid := strings.TrimSpace(string(content[:nameStart]))
1190 fields := make([]string, 3, len(restFields)+3)
1191 fields[1] = string(pid)
1192 fields[2] = string(name)
1193 fields = append(fields, restFields...)
1194 return fields
1195 }
1196
View as plain text