github.com/boomhut/fiber/v2@v2.0.0-20230603160335-b65c856e57d3/internal/gopsutil/cpu/cpu_dragonfly.go (about)

     1  package cpu
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os/exec"
     7  	"reflect"
     8  	"regexp"
     9  	"runtime"
    10  	"strconv"
    11  	"strings"
    12  	"unsafe"
    13  
    14  	"github.com/boomhut/fiber/v2/internal/gopsutil/common"
    15  	"golang.org/x/sys/unix"
    16  )
    17  
    18  var ClocksPerSec = float64(128)
    19  var cpuMatch = regexp.MustCompile(`^CPU:`)
    20  var originMatch = regexp.MustCompile(`Origin\s*=\s*"(.+)"\s+Id\s*=\s*(.+)\s+Stepping\s*=\s*(.+)`)
    21  var featuresMatch = regexp.MustCompile(`Features=.+<(.+)>`)
    22  var featuresMatch2 = regexp.MustCompile(`Features2=[a-f\dx]+<(.+)>`)
    23  var cpuEnd = regexp.MustCompile(`^Trying to mount root`)
    24  var cpuTimesSize int
    25  var emptyTimes cpuTimes
    26  
    27  func init() {
    28  	getconf, err := exec.LookPath("getconf")
    29  	if err != nil {
    30  		return
    31  	}
    32  	out, err := invoke.Command(getconf, "CLK_TCK")
    33  	// ignore errors
    34  	if err == nil {
    35  		i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
    36  		if err == nil {
    37  			ClocksPerSec = float64(i)
    38  		}
    39  	}
    40  }
    41  
    42  func timeStat(name string, t *cpuTimes) *TimesStat {
    43  	return &TimesStat{
    44  		User:   float64(t.User) / ClocksPerSec,
    45  		Nice:   float64(t.Nice) / ClocksPerSec,
    46  		System: float64(t.Sys) / ClocksPerSec,
    47  		Idle:   float64(t.Idle) / ClocksPerSec,
    48  		Irq:    float64(t.Intr) / ClocksPerSec,
    49  		CPU:    name,
    50  	}
    51  }
    52  
    53  func Times(percpu bool) ([]TimesStat, error) {
    54  	return TimesWithContext(context.Background(), percpu)
    55  }
    56  
    57  func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
    58  	if percpu {
    59  		buf, err := unix.SysctlRaw("kern.cp_times")
    60  		if err != nil {
    61  			return nil, err
    62  		}
    63  
    64  		// We can't do this in init due to the conflict with cpu.init()
    65  		if cpuTimesSize == 0 {
    66  			cpuTimesSize = int(reflect.TypeOf(cpuTimes{}).Size())
    67  		}
    68  
    69  		ncpus := len(buf) / cpuTimesSize
    70  		ret := make([]TimesStat, 0, ncpus)
    71  		for i := 0; i < ncpus; i++ {
    72  			times := (*cpuTimes)(unsafe.Pointer(&buf[i*cpuTimesSize]))
    73  			if *times == emptyTimes {
    74  				// CPU not present
    75  				continue
    76  			}
    77  			ret = append(ret, *timeStat(fmt.Sprintf("cpu%d", len(ret)), times))
    78  		}
    79  		return ret, nil
    80  	}
    81  
    82  	buf, err := unix.SysctlRaw("kern.cp_time")
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	times := (*cpuTimes)(unsafe.Pointer(&buf[0]))
    88  	return []TimesStat{*timeStat("cpu-total", times)}, nil
    89  }
    90  
    91  // Returns only one InfoStat on DragonflyBSD.  The information regarding core
    92  // count, however is accurate and it is assumed that all InfoStat attributes
    93  // are the same across CPUs.
    94  func Info() ([]InfoStat, error) {
    95  	return InfoWithContext(context.Background())
    96  }
    97  
    98  func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
    99  	const dmesgBoot = "/var/run/dmesg.boot"
   100  
   101  	c, err := parseDmesgBoot(dmesgBoot)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	var u32 uint32
   107  	if u32, err = unix.SysctlUint32("hw.clockrate"); err != nil {
   108  		return nil, err
   109  	}
   110  	c.Mhz = float64(u32)
   111  
   112  	var num int
   113  	var buf string
   114  	if buf, err = unix.Sysctl("hw.cpu_topology.tree"); err != nil {
   115  		return nil, err
   116  	}
   117  	num = strings.Count(buf, "CHIP")
   118  	c.Cores = int32(strings.Count(string(buf), "CORE") / num)
   119  
   120  	if c.ModelName, err = unix.Sysctl("hw.model"); err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	ret := make([]InfoStat, num)
   125  	for i := 0; i < num; i++ {
   126  		ret[i] = c
   127  	}
   128  
   129  	return ret, nil
   130  }
   131  
   132  func parseDmesgBoot(fileName string) (InfoStat, error) {
   133  	c := InfoStat{}
   134  	lines, _ := common.ReadLines(fileName)
   135  	for _, line := range lines {
   136  		if matches := cpuEnd.FindStringSubmatch(line); matches != nil {
   137  			break
   138  		} else if matches := originMatch.FindStringSubmatch(line); matches != nil {
   139  			c.VendorID = matches[1]
   140  			t, err := strconv.ParseInt(matches[2], 10, 32)
   141  			if err != nil {
   142  				return c, fmt.Errorf("unable to parse DragonflyBSD CPU stepping information from %q: %v", line, err)
   143  			}
   144  			c.Stepping = int32(t)
   145  		} else if matches := featuresMatch.FindStringSubmatch(line); matches != nil {
   146  			for _, v := range strings.Split(matches[1], ",") {
   147  				c.Flags = append(c.Flags, strings.ToLower(v))
   148  			}
   149  		} else if matches := featuresMatch2.FindStringSubmatch(line); matches != nil {
   150  			for _, v := range strings.Split(matches[1], ",") {
   151  				c.Flags = append(c.Flags, strings.ToLower(v))
   152  			}
   153  		}
   154  	}
   155  
   156  	return c, nil
   157  }
   158  
   159  func CountsWithContext(ctx context.Context, logical bool) (int, error) {
   160  	return runtime.NumCPU(), nil
   161  }