github.com/ssetin/penguincast@v0.2.0/src/fastStat/fastStat.go (about)

     1  // Package fastStat - read current process stat from /proc/[pid]/stat
     2  // used for CPU and memory usage monitoring
     3  package fastStat
     4  
     5  /* #include <unistd.h> */
     6  import "C"
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"math"
    12  	"os"
    13  	"path"
    14  	"runtime"
    15  	"strconv"
    16  	"strings"
    17  	"sync"
    18  )
    19  
    20  // ProcStats - Status information about the process
    21  // /proc/[pid]/stat
    22  type ProcStats struct {
    23  	Utime     float64 // Amount of time that this process has been scheduled in user mode
    24  	Stime     float64 // Amount of time that this process has been scheduled in kernel mode
    25  	Cutime    float64 // Amount of time that this process's waited-for chil‐dren have been scheduled in user mode
    26  	Cstime    float64 // Amount of time that this process's waited-for chil‐dren have been scheduled in kernel mode
    27  	Rss       int     // Resident Set Size: number of pages the process has in real memory
    28  	Starttime float64 // The time the process started after system boot
    29  
    30  	uptime float64 // for calculating between stat samples
    31  }
    32  
    33  // ProcStatsReader read info from /proc/[pid]/stat file
    34  type ProcStatsReader struct {
    35  	Pid      int
    36  	clkTck   float64
    37  	pageSize int
    38  	numCPU   int
    39  
    40  	mux  sync.Mutex
    41  	prev ProcStats
    42  
    43  	procFile *os.File
    44  }
    45  
    46  // Init - initialize proc stat reader
    47  func (p *ProcStatsReader) Init() error {
    48  	p.mux.Lock()
    49  	defer p.mux.Unlock()
    50  	p.Pid = os.Getpid()
    51  
    52  	p.clkTck = float64(C.sysconf(C._SC_CLK_TCK))
    53  	p.pageSize = int(C.sysconf(C._SC_PAGESIZE))
    54  	p.numCPU = int(C.sysconf(C._SC_NPROCESSORS_ONLN))
    55  
    56  	procFile, err := os.Open(fmt.Sprintf("/proc/%d/stat", p.Pid))
    57  	if err != nil {
    58  		procFile.Close()
    59  		return err
    60  	}
    61  	p.procFile = procFile
    62  	return nil
    63  }
    64  
    65  // Close - close proc/[pid]/stat file
    66  func (p *ProcStatsReader) Close() {
    67  	p.mux.Lock()
    68  	defer p.mux.Unlock()
    69  	if p.procFile != nil {
    70  		p.procFile.Close()
    71  	}
    72  }
    73  
    74  func parseFloat(val string) float64 {
    75  	floatVal, _ := strconv.ParseFloat(val, 64)
    76  	return floatVal
    77  }
    78  
    79  func parseInt(val string) int {
    80  	intVal, _ := strconv.Atoi(val)
    81  	return intVal
    82  }
    83  
    84  // read - reads a /proc/[pid]/stat file and returns statistics
    85  func (p *ProcStatsReader) read() (*ProcStats, error) {
    86  	if p.procFile == nil {
    87  		return nil, errors.New("ProcStatsReader have to be initialized")
    88  	}
    89  
    90  	_, err := p.procFile.Seek(0, 0)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	buf := make([]byte, 8192)
    96  	n, err := p.procFile.Read(buf)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  	splitAfter := strings.SplitAfter(string(buf[:n]), ")")
   101  	if len(splitAfter) == 0 || len(splitAfter) == 1 {
   102  		return nil, errors.New("Error parsing /proc/%d/stat")
   103  	}
   104  
   105  	uptimeBytes, err := ioutil.ReadFile(path.Join("/proc", "uptime"))
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	uptime, _ := strconv.ParseFloat(strings.Split(string(uptimeBytes), " ")[0], 64)
   110  
   111  	fields := strings.Split(splitAfter[1], " ")
   112  	stat := &ProcStats{
   113  		Utime:     parseFloat(fields[12]), // [idx+2]
   114  		Stime:     parseFloat(fields[13]),
   115  		Cutime:    parseFloat(fields[14]),
   116  		Cstime:    parseFloat(fields[15]),
   117  		Starttime: parseFloat(fields[20]),
   118  		Rss:       parseInt(fields[22]),
   119  		uptime:    uptime,
   120  	}
   121  
   122  	return stat, nil
   123  }
   124  
   125  // GetCPUAndMem - returns CPU and memory usage
   126  func (p *ProcStatsReader) GetCPUAndMem() (float64, int, error) {
   127  	var CPU float64
   128  	var Memory int
   129  
   130  	if runtime.GOOS == "linux" {
   131  
   132  		stat, err := p.read()
   133  		if err != nil {
   134  			return 0, 0, err
   135  		}
   136  
   137  		p.mux.Lock()
   138  		prev := p.prev
   139  		p.mux.Unlock()
   140  
   141  		systemTime := 0.0
   142  		userTime := 0.0
   143  		seconds := 0.0
   144  		if prev.Stime != 0.0 {
   145  			systemTime = prev.Stime
   146  		}
   147  		if prev.Utime != 0.0 {
   148  			userTime = prev.Utime
   149  		}
   150  
   151  		total := stat.Stime - systemTime + stat.Utime - userTime
   152  		total = total / p.clkTck
   153  
   154  		if prev.uptime != 0.0 {
   155  			seconds = stat.uptime - prev.uptime
   156  		} else {
   157  			seconds = stat.Starttime/p.clkTck - stat.uptime
   158  		}
   159  
   160  		seconds = math.Abs(seconds)
   161  		if seconds == 0 {
   162  			seconds = 1
   163  		}
   164  
   165  		CPU = (total / seconds) * 100 / float64(p.numCPU)
   166  		Memory = stat.Rss * p.pageSize
   167  
   168  		p.mux.Lock()
   169  		p.prev = *stat
   170  		p.mux.Unlock()
   171  	}
   172  	return CPU, Memory, nil
   173  
   174  }