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  }