cpu_darwin.go 4.9 KB
Newer Older
1 2
// +build darwin

S
Shirou WAKAYAMA 已提交
3
package cpu
4 5

import (
S
Shirou WAKAYAMA 已提交
6
	"fmt"
7
	"io"
8
	"os"
9
	"os/exec"
10
	"path/filepath"
11 12
	"strconv"
	"strings"
13 14

	common "github.com/shirou/gopsutil/common"
15 16
)

17
var HELPER_PATH = filepath.Join(os.Getenv("HOME"), ".gopsutil_cpu_helper")
18

19 20 21 22
// enable cpu helper. It may become security problem.
// This env valiable approach will be changed.
const HELPER_ENABLE_ENV = "ALLLOW_INSECURE_CPU_HELPER"

23
var ClocksPerSec = float64(100)
24 25 26 27 28

func init() {
	out, err := exec.Command("/usr/bin/getconf", "CLK_TCK").Output()
	// ignore errors
	if err == nil {
29
		i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
30 31 32 33
		if err == nil {
			ClocksPerSec = float64(i)
		}
	}
34 35 36

	// adhoc compile on the host. Errors will be ignored.
	// gcc is required to compile.
37
	if !common.PathExists(HELPER_PATH) && os.Getenv(HELPER_ENABLE_ENV) == "yes" {
38 39 40 41 42 43 44 45 46
		cmd := exec.Command("gcc", "-o", HELPER_PATH, "-x", "c", "-")
		stdin, err := cmd.StdinPipe()
		if err != nil {
			return
		}
		io.WriteString(stdin, cpu_helper_src)
		stdin.Close()
		cmd.Output()
	}
47
}
48 49 50

func CPUTimes(percpu bool) ([]CPUTimesStat, error) {
	var ret []CPUTimesStat
51
	if !common.PathExists(HELPER_PATH) {
52
		return nil, fmt.Errorf("could not get cpu time")
53 54
	}

55
	out, err := exec.Command(HELPER_PATH).Output()
56 57 58
	if err != nil {
		return ret, err
	}
59

60 61 62 63 64 65
	for _, line := range strings.Split(string(out), "\n") {
		f := strings.Split(string(line), ",")
		if len(f) != 5 {
			continue
		}
		cpu, err := strconv.ParseFloat(f[0], 64)
K
KenjiTakahashi 已提交
66 67 68
		if err != nil {
			return ret, err
		}
69 70 71 72 73
		// cpu:99 means total, so just ignore if percpu
		if (percpu && cpu == 99) || (!percpu && cpu != 99) {
			continue
		}
		user, err := strconv.ParseFloat(f[1], 64)
K
KenjiTakahashi 已提交
74 75 76
		if err != nil {
			return ret, err
		}
77
		sys, err := strconv.ParseFloat(f[2], 64)
K
KenjiTakahashi 已提交
78 79 80
		if err != nil {
			return ret, err
		}
81
		idle, err := strconv.ParseFloat(f[3], 64)
K
KenjiTakahashi 已提交
82 83 84
		if err != nil {
			return ret, err
		}
85
		nice, err := strconv.ParseFloat(f[4], 64)
K
KenjiTakahashi 已提交
86 87 88 89
		if err != nil {
			return ret, err
		}
		c := CPUTimesStat{
90 91 92 93
			User:   float64(user / ClocksPerSec),
			Nice:   float64(nice / ClocksPerSec),
			System: float64(sys / ClocksPerSec),
			Idle:   float64(idle / ClocksPerSec),
K
KenjiTakahashi 已提交
94 95 96 97
		}
		if !percpu {
			c.CPU = "cpu-total"
		} else {
98
			c.CPU = fmt.Sprintf("cpu%d", uint16(cpu))
K
KenjiTakahashi 已提交
99 100 101
		}
		ret = append(ret, c)
	}
102 103 104 105 106 107 108
	return ret, nil
}

// Returns only one CPUInfoStat on FreeBSD
func CPUInfo() ([]CPUInfoStat, error) {
	var ret []CPUInfoStat

109 110 111 112 113
	out, err := exec.Command("/usr/sbin/sysctl", "machdep.cpu").Output()
	if err != nil {
		return ret, err
	}

114
	c := CPUInfoStat{}
115 116
	for _, line := range strings.Split(string(out), "\n") {
		values := strings.Fields(line)
117 118 119
		if len(values) < 1 {
			continue
		}
120

121
		t, err := strconv.ParseInt(values[1], 10, 64)
122
		// err is not checked here because some value is string.
123 124 125 126 127 128 129
		if strings.HasPrefix(line, "machdep.cpu.brand_string") {
			c.ModelName = strings.Join(values[1:], " ")
		} else if strings.HasPrefix(line, "machdep.cpu.family") {
			c.Family = values[1]
		} else if strings.HasPrefix(line, "machdep.cpu.model") {
			c.Model = values[1]
		} else if strings.HasPrefix(line, "machdep.cpu.stepping") {
130 131 132
			if err != nil {
				return ret, err
			}
133
			c.Stepping = int32(t)
134 135 136 137 138 139
		} else if strings.HasPrefix(line, "machdep.cpu.features") {
			for _, v := range values[1:] {
				c.Flags = append(c.Flags, strings.ToLower(v))
			}
		} else if strings.HasPrefix(line, "machdep.cpu.leaf7_features") {
			for _, v := range values[1:] {
140 141
				c.Flags = append(c.Flags, strings.ToLower(v))
			}
142 143
		} else if strings.HasPrefix(line, "machdep.cpu.extfeatures") {
			for _, v := range values[1:] {
144 145
				c.Flags = append(c.Flags, strings.ToLower(v))
			}
146
		} else if strings.HasPrefix(line, "machdep.cpu.core_count") {
147 148 149
			if err != nil {
				return ret, err
			}
150
			c.Cores = int32(t)
151
		} else if strings.HasPrefix(line, "machdep.cpu.cache.size") {
152 153 154
			if err != nil {
				return ret, err
			}
155
			c.CacheSize = int32(t)
156 157
		} else if strings.HasPrefix(line, "machdep.cpu.vendor") {
			c.VendorID = values[1]
158 159
		}

160 161
		// TODO:
		// c.Mhz = mustParseFloat64(values[1])
162 163 164 165
	}

	return append(ret, c), nil
}
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213

const cpu_helper_src = `
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <stdio.h>

int main() {
  	natural_t cpuCount;
	processor_info_array_t ia;
	mach_msg_type_number_t ic;

	kern_return_t error = host_processor_info(mach_host_self(),
		PROCESSOR_CPU_LOAD_INFO, &cpuCount, &ia, &ic);
	if (error) {
		return error;
	}

	processor_cpu_load_info_data_t* cpuLoadInfo =
		(processor_cpu_load_info_data_t*) ia;

	unsigned int all[4];
	// cpu_no,user,system,idle,nice
	for (int cpu=0; cpu<cpuCount; cpu++){
	  printf("%d,", cpu);
	  for (int i=0; i<4; i++){
		printf("%d", cpuLoadInfo[cpu].cpu_ticks[i]);
		all[i] = all[i] + cpuLoadInfo[cpu].cpu_ticks[i];
		if (i != 3){
		  printf(",");
		}else{
		  printf("\n");
		}
	  }
	}
	printf("99,");
	for (int i=0; i<4; i++){
	  printf("%d", all[i]);
	  if (i != 3){
		printf(",");
	  }else{
		printf("\n");
	  }
	}

	vm_deallocate(mach_task_self(), (vm_address_t)ia, ic);
	return error;
}
`