cpu_solaris.go 7.5 KB

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