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  }