github.com/nats-io/nats-server/v2@v2.11.0-preview.2/server/pse/pse_darwin.go (about) 1 // Copyright 2015-2021 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 // On macs after some studying it seems that typical tools like ps and activity monitor report MaxRss and not 17 // current RSS. I wrote some C code to pull the real RSS and although it does not go down very often, when it does 18 // that is not reflected in the typical tooling one might compare us to, so we can skip cgo and just use rusage imo. 19 // We also do not use virtual memory in the upper layers at all, so ok to skip since rusage does not report vss. 20 21 import ( 22 "math" 23 "sync" 24 "syscall" 25 "time" 26 ) 27 28 type lastUsage struct { 29 sync.Mutex 30 last time.Time 31 cpu time.Duration 32 rss int64 33 pcpu float64 34 } 35 36 // To hold the last usage and call time. 37 var lu lastUsage 38 39 func init() { 40 updateUsage() 41 periodic() 42 } 43 44 // Get our usage. 45 func getUsage() (now time.Time, cpu time.Duration, rss int64) { 46 var ru syscall.Rusage 47 syscall.Getrusage(syscall.RUSAGE_SELF, &ru) 48 now = time.Now() 49 cpu = time.Duration(ru.Utime.Sec)*time.Second + time.Duration(ru.Utime.Usec)*time.Microsecond 50 cpu += time.Duration(ru.Stime.Sec)*time.Second + time.Duration(ru.Stime.Usec)*time.Microsecond 51 return now, cpu, ru.Maxrss 52 } 53 54 // Update last usage. 55 // We need to have a prior sample to compute pcpu. 56 func updateUsage() (pcpu float64, rss int64) { 57 lu.Lock() 58 defer lu.Unlock() 59 60 now, cpu, rss := getUsage() 61 // Don't skew pcpu by sampling too close to last sample. 62 if elapsed := now.Sub(lu.last); elapsed < 500*time.Millisecond { 63 // Always update rss. 64 lu.rss = rss 65 } else { 66 tcpu := float64(cpu - lu.cpu) 67 lu.last, lu.cpu, lu.rss = now, cpu, rss 68 // Want to make this one decimal place and not count on upper layers. 69 // Cores already taken into account via cpu time measurements. 70 lu.pcpu = math.Round(tcpu/float64(elapsed)*1000) / 10 71 } 72 return lu.pcpu, lu.rss 73 } 74 75 // Sampling function to keep pcpu relevant. 76 func periodic() { 77 updateUsage() 78 time.AfterFunc(time.Second, periodic) 79 } 80 81 // ProcUsage returns CPU and memory usage. 82 // Note upper layers do not use virtual memory size, so ok that it is not filled in here. 83 func ProcUsage(pcpu *float64, rss, vss *int64) error { 84 *pcpu, *rss = updateUsage() 85 return nil 86 }