get.pme.sh/pnats@v0.0.0-20240304004023-26bb5a137ed0/server/pse/pse_linux.go (about) 1 // Copyright 2015-2018 The NATS Authors 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package pse 15 16 import ( 17 "bytes" 18 "fmt" 19 "os" 20 "sync/atomic" 21 "syscall" 22 "time" 23 ) 24 25 var ( 26 procStatFile string 27 ticks int64 28 lastTotal int64 29 lastSeconds int64 30 ipcpu int64 31 ) 32 33 const ( 34 utimePos = 13 35 stimePos = 14 36 startPos = 21 37 vssPos = 22 38 rssPos = 23 39 ) 40 41 func init() { 42 // Avoiding to generate docker image without CGO 43 ticks = 100 // int64(C.sysconf(C._SC_CLK_TCK)) 44 procStatFile = fmt.Sprintf("/proc/%d/stat", os.Getpid()) 45 periodic() 46 } 47 48 // Sampling function to keep pcpu relevant. 49 func periodic() { 50 contents, err := os.ReadFile(procStatFile) 51 if err != nil { 52 return 53 } 54 fields := bytes.Fields(contents) 55 56 // PCPU 57 pstart := parseInt64(fields[startPos]) 58 utime := parseInt64(fields[utimePos]) 59 stime := parseInt64(fields[stimePos]) 60 total := utime + stime 61 62 var sysinfo syscall.Sysinfo_t 63 if err := syscall.Sysinfo(&sysinfo); err != nil { 64 return 65 } 66 67 seconds := int64(sysinfo.Uptime) - (pstart / ticks) 68 69 // Save off temps 70 lt := lastTotal 71 ls := lastSeconds 72 73 // Update last sample 74 lastTotal = total 75 lastSeconds = seconds 76 77 // Adjust to current time window 78 total -= lt 79 seconds -= ls 80 81 if seconds > 0 { 82 atomic.StoreInt64(&ipcpu, (total*1000/ticks)/seconds) 83 } 84 85 time.AfterFunc(1*time.Second, periodic) 86 } 87 88 // ProcUsage returns CPU usage 89 func ProcUsage(pcpu *float64, rss, vss *int64) error { 90 contents, err := os.ReadFile(procStatFile) 91 if err != nil { 92 return err 93 } 94 fields := bytes.Fields(contents) 95 96 // Memory 97 *rss = (parseInt64(fields[rssPos])) << 12 98 *vss = parseInt64(fields[vssPos]) 99 100 // PCPU 101 // We track this with periodic sampling, so just load and go. 102 *pcpu = float64(atomic.LoadInt64(&ipcpu)) / 10.0 103 104 return nil 105 } 106 107 // Ascii numbers 0-9 108 const ( 109 asciiZero = 48 110 asciiNine = 57 111 ) 112 113 // parseInt64 expects decimal positive numbers. We 114 // return -1 to signal error 115 func parseInt64(d []byte) (n int64) { 116 if len(d) == 0 { 117 return -1 118 } 119 for _, dec := range d { 120 if dec < asciiZero || dec > asciiNine { 121 return -1 122 } 123 n = n*10 + (int64(dec) - asciiZero) 124 } 125 return n 126 }