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 }