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

     1  package cpu
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"os/exec"
     8  	"regexp"
     9  	"runtime"
    10  	"sort"
    11  	"strconv"
    12  	"strings"
    13  )
    14  
    15  var ClocksPerSec = float64(128)
    16  
    17  func init() {
    18  	getconf, err := exec.LookPath("getconf")
    19  	if err != nil {
    20  		return
    21  	}
    22  	out, err := invoke.Command(getconf, "CLK_TCK")
    23  	// ignore errors
    24  	if err == nil {
    25  		i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
    26  		if err == nil {
    27  			ClocksPerSec = float64(i)
    28  		}
    29  	}
    30  }
    31  
    32  // sum all values in a float64 map with float64 keys
    33  func msum(x map[float64]float64) float64 {
    34  	total := 0.0
    35  	for _, y := range x {
    36  		total += y
    37  	}
    38  	return total
    39  }
    40  
    41  func Times(percpu bool) ([]TimesStat, error) {
    42  	return TimesWithContext(context.Background(), percpu)
    43  }
    44  
    45  func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
    46  	kstatSys, err := exec.LookPath("kstat")
    47  	if err != nil {
    48  		return nil, fmt.Errorf("cannot find kstat: %s", err)
    49  	}
    50  	cpu := make(map[float64]float64)
    51  	idle := make(map[float64]float64)
    52  	user := make(map[float64]float64)
    53  	kern := make(map[float64]float64)
    54  	iowt := make(map[float64]float64)
    55  	//swap := make(map[float64]float64)
    56  	kstatSysOut, err := invoke.CommandWithContext(ctx, kstatSys, "-p", "cpu_stat:*:*:/^idle$|^user$|^kernel$|^iowait$|^swap$/")
    57  	if err != nil {
    58  		return nil, fmt.Errorf("cannot execute kstat: %s", err)
    59  	}
    60  	re := regexp.MustCompile(`[:\s]+`)
    61  	for _, line := range strings.Split(string(kstatSysOut), "\n") {
    62  		fields := re.Split(line, -1)
    63  		if fields[0] != "cpu_stat" {
    64  			continue
    65  		}
    66  		cpuNumber, err := strconv.ParseFloat(fields[1], 64)
    67  		if err != nil {
    68  			return nil, fmt.Errorf("cannot parse cpu number: %s", err)
    69  		}
    70  		cpu[cpuNumber] = cpuNumber
    71  		switch fields[3] {
    72  		case "idle":
    73  			idle[cpuNumber], err = strconv.ParseFloat(fields[4], 64)
    74  			if err != nil {
    75  				return nil, fmt.Errorf("cannot parse idle: %s", err)
    76  			}
    77  		case "user":
    78  			user[cpuNumber], err = strconv.ParseFloat(fields[4], 64)
    79  			if err != nil {
    80  				return nil, fmt.Errorf("cannot parse user: %s", err)
    81  			}
    82  		case "kernel":
    83  			kern[cpuNumber], err = strconv.ParseFloat(fields[4], 64)
    84  			if err != nil {
    85  				return nil, fmt.Errorf("cannot parse kernel: %s", err)
    86  			}
    87  		case "iowait":
    88  			iowt[cpuNumber], err = strconv.ParseFloat(fields[4], 64)
    89  			if err != nil {
    90  				return nil, fmt.Errorf("cannot parse iowait: %s", err)
    91  			}
    92  			//not sure how this translates, don't report, add to kernel, something else?
    93  			/*case "swap":
    94  			swap[cpuNumber], err = strconv.ParseFloat(fields[4], 64)
    95  			if err != nil {
    96  				return nil, fmt.Errorf("cannot parse swap: %s", err)
    97  			} */
    98  		}
    99  	}
   100  	ret := make([]TimesStat, 0, len(cpu))
   101  	if percpu {
   102  		for _, c := range cpu {
   103  			ct := &TimesStat{
   104  				CPU:    fmt.Sprintf("cpu%d", int(cpu[c])),
   105  				Idle:   idle[c] / ClocksPerSec,
   106  				User:   user[c] / ClocksPerSec,
   107  				System: kern[c] / ClocksPerSec,
   108  				Iowait: iowt[c] / ClocksPerSec,
   109  			}
   110  			ret = append(ret, *ct)
   111  		}
   112  	} else {
   113  		ct := &TimesStat{
   114  			CPU:    "cpu-total",
   115  			Idle:   msum(idle) / ClocksPerSec,
   116  			User:   msum(user) / ClocksPerSec,
   117  			System: msum(kern) / ClocksPerSec,
   118  			Iowait: msum(iowt) / ClocksPerSec,
   119  		}
   120  		ret = append(ret, *ct)
   121  	}
   122  	return ret, nil
   123  }
   124  
   125  func Info() ([]InfoStat, error) {
   126  	return InfoWithContext(context.Background())
   127  }
   128  
   129  func InfoWithContext(ctx context.Context) ([]InfoStat, error) {
   130  	psrInfo, err := exec.LookPath("psrinfo")
   131  	if err != nil {
   132  		return nil, fmt.Errorf("cannot find psrinfo: %s", err)
   133  	}
   134  	psrInfoOut, err := invoke.CommandWithContext(ctx, psrInfo, "-p", "-v")
   135  	if err != nil {
   136  		return nil, fmt.Errorf("cannot execute psrinfo: %s", err)
   137  	}
   138  
   139  	isaInfo, err := exec.LookPath("isainfo")
   140  	if err != nil {
   141  		return nil, fmt.Errorf("cannot find isainfo: %s", err)
   142  	}
   143  	isaInfoOut, err := invoke.CommandWithContext(ctx, isaInfo, "-b", "-v")
   144  	if err != nil {
   145  		return nil, fmt.Errorf("cannot execute isainfo: %s", err)
   146  	}
   147  
   148  	procs, err := parseProcessorInfo(string(psrInfoOut))
   149  	if err != nil {
   150  		return nil, fmt.Errorf("error parsing psrinfo output: %s", err)
   151  	}
   152  
   153  	flags, err := parseISAInfo(string(isaInfoOut))
   154  	if err != nil {
   155  		return nil, fmt.Errorf("error parsing isainfo output: %s", err)
   156  	}
   157  
   158  	result := make([]InfoStat, 0, len(flags))
   159  	for _, proc := range procs {
   160  		procWithFlags := proc
   161  		procWithFlags.Flags = flags
   162  		result = append(result, procWithFlags)
   163  	}
   164  
   165  	return result, nil
   166  }
   167  
   168  var flagsMatch = regexp.MustCompile(`[\w\.]+`)
   169  
   170  func parseISAInfo(cmdOutput string) ([]string, error) {
   171  	words := flagsMatch.FindAllString(cmdOutput, -1)
   172  
   173  	// Sanity check the output
   174  	if len(words) < 4 || words[1] != "bit" || words[3] != "applications" {
   175  		return nil, errors.New("attempted to parse invalid isainfo output")
   176  	}
   177  
   178  	flags := make([]string, len(words)-4)
   179  	for i, val := range words[4:] {
   180  		flags[i] = val
   181  	}
   182  	sort.Strings(flags)
   183  
   184  	return flags, nil
   185  }
   186  
   187  var psrInfoMatch = regexp.MustCompile(`The physical processor has (?:([\d]+) virtual processor \(([\d]+)\)|([\d]+) cores and ([\d]+) virtual processors[^\n]+)\n(?:\s+ The core has.+\n)*\s+.+ \((\w+) ([\S]+) family (.+) model (.+) step (.+) clock (.+) MHz\)\n[\s]*(.*)`)
   188  
   189  const (
   190  	psrNumCoresOffset   = 1
   191  	psrNumCoresHTOffset = 3
   192  	psrNumHTOffset      = 4
   193  	psrVendorIDOffset   = 5
   194  	psrFamilyOffset     = 7
   195  	psrModelOffset      = 8
   196  	psrStepOffset       = 9
   197  	psrClockOffset      = 10
   198  	psrModelNameOffset  = 11
   199  )
   200  
   201  func parseProcessorInfo(cmdOutput string) ([]InfoStat, error) {
   202  	matches := psrInfoMatch.FindAllStringSubmatch(cmdOutput, -1)
   203  
   204  	var infoStatCount int32
   205  	result := make([]InfoStat, 0, len(matches))
   206  	for physicalIndex, physicalCPU := range matches {
   207  		var step int32
   208  		var clock float64
   209  
   210  		if physicalCPU[psrStepOffset] != "" {
   211  			stepParsed, err := strconv.ParseInt(physicalCPU[psrStepOffset], 10, 32)
   212  			if err != nil {
   213  				return nil, fmt.Errorf("cannot parse value %q for step as 32-bit integer: %s", physicalCPU[9], err)
   214  			}
   215  			step = int32(stepParsed)
   216  		}
   217  
   218  		if physicalCPU[psrClockOffset] != "" {
   219  			clockParsed, err := strconv.ParseInt(physicalCPU[psrClockOffset], 10, 64)
   220  			if err != nil {
   221  				return nil, fmt.Errorf("cannot parse value %q for clock as 32-bit integer: %s", physicalCPU[10], err)
   222  			}
   223  			clock = float64(clockParsed)
   224  		}
   225  
   226  		var err error
   227  		var numCores int64
   228  		var numHT int64
   229  		switch {
   230  		case physicalCPU[psrNumCoresOffset] != "":
   231  			numCores, err = strconv.ParseInt(physicalCPU[psrNumCoresOffset], 10, 32)
   232  			if err != nil {
   233  				return nil, fmt.Errorf("cannot parse value %q for core count as 32-bit integer: %s", physicalCPU[1], err)
   234  			}
   235  
   236  			for i := 0; i < int(numCores); i++ {
   237  				result = append(result, InfoStat{
   238  					CPU:        infoStatCount,
   239  					PhysicalID: strconv.Itoa(physicalIndex),
   240  					CoreID:     strconv.Itoa(i),
   241  					Cores:      1,
   242  					VendorID:   physicalCPU[psrVendorIDOffset],
   243  					ModelName:  physicalCPU[psrModelNameOffset],
   244  					Family:     physicalCPU[psrFamilyOffset],
   245  					Model:      physicalCPU[psrModelOffset],
   246  					Stepping:   step,
   247  					Mhz:        clock,
   248  				})
   249  				infoStatCount++
   250  			}
   251  		case physicalCPU[psrNumCoresHTOffset] != "":
   252  			numCores, err = strconv.ParseInt(physicalCPU[psrNumCoresHTOffset], 10, 32)
   253  			if err != nil {
   254  				return nil, fmt.Errorf("cannot parse value %q for core count as 32-bit integer: %s", physicalCPU[3], err)
   255  			}
   256  
   257  			numHT, err = strconv.ParseInt(physicalCPU[psrNumHTOffset], 10, 32)
   258  			if err != nil {
   259  				return nil, fmt.Errorf("cannot parse value %q for hyperthread count as 32-bit integer: %s", physicalCPU[4], err)
   260  			}
   261  
   262  			for i := 0; i < int(numCores); i++ {
   263  				result = append(result, InfoStat{
   264  					CPU:        infoStatCount,
   265  					PhysicalID: strconv.Itoa(physicalIndex),
   266  					CoreID:     strconv.Itoa(i),
   267  					Cores:      int32(numHT) / int32(numCores),
   268  					VendorID:   physicalCPU[psrVendorIDOffset],
   269  					ModelName:  physicalCPU[psrModelNameOffset],
   270  					Family:     physicalCPU[psrFamilyOffset],
   271  					Model:      physicalCPU[psrModelOffset],
   272  					Stepping:   step,
   273  					Mhz:        clock,
   274  				})
   275  				infoStatCount++
   276  			}
   277  		default:
   278  			return nil, errors.New("values for cores with and without hyperthreading are both set")
   279  		}
   280  	}
   281  	return result, nil
   282  }
   283  
   284  func CountsWithContext(ctx context.Context, logical bool) (int, error) {
   285  	return runtime.NumCPU(), nil
   286  }