1
2
3
4
5 package main
6
7 import (
8 "bytes"
9 "fmt"
10 "os"
11 "os/exec"
12 "regexp"
13 "runtime"
14 "strconv"
15 "strings"
16 "syscall"
17 )
18
19 var (
20 cpuSetRE = regexp.MustCompile(`(\d,?)+`)
21 )
22
23 func init() {
24 register("FreeBSDNumCPU", FreeBSDNumCPU)
25 register("FreeBSDNumCPUHelper", FreeBSDNumCPUHelper)
26 }
27
28 func FreeBSDNumCPUHelper() {
29 fmt.Printf("%d\n", runtime.NumCPU())
30 }
31
32 func FreeBSDNumCPU() {
33 _, err := exec.LookPath("cpuset")
34 if err != nil {
35
36 fmt.Println("OK")
37 return
38 }
39 _, err = exec.LookPath("sysctl")
40 if err != nil {
41
42 fmt.Println("OK")
43 return
44 }
45 cmd := exec.Command("sysctl", "-n", "kern.smp.active")
46 output, err := cmd.CombinedOutput()
47 if err != nil {
48 fmt.Printf("fail to launch %#q, error: %s, output: %s\n", cmd, err, output)
49 return
50 }
51 if !bytes.Equal(output, []byte("1\n")) {
52
53 fmt.Println("OK")
54 return
55 }
56
57 list, err := getList()
58 if err != nil {
59 fmt.Printf("%s\n", err)
60 return
61 }
62 err = checkNCPU(list)
63 if err != nil {
64 fmt.Printf("%s\n", err)
65 return
66 }
67 if len(list) >= 2 {
68 err = checkNCPU(list[:len(list)-1])
69 if err != nil {
70 fmt.Printf("%s\n", err)
71 return
72 }
73 }
74 fmt.Println("OK")
75 return
76 }
77
78 func getList() ([]string, error) {
79 pid := syscall.Getpid()
80
81
82 cmd := exec.Command("cpuset", "-g", "-p", strconv.Itoa(pid))
83 output, err := cmd.CombinedOutput()
84 if err != nil {
85 return nil, fmt.Errorf("fail to execute %#q: %s", cmd, err)
86 }
87 output, _, ok := bytes.Cut(output, []byte("\n"))
88 if !ok {
89 return nil, fmt.Errorf("invalid output from %#q, '\\n' not found: %s", cmd, output)
90 }
91
92 _, cpus, ok := bytes.Cut(output, []byte(":"))
93 if !ok {
94 return nil, fmt.Errorf("invalid output from %#q, ':' not found: %s", cmd, output)
95 }
96
97 var list []string
98 for _, val := range bytes.Split(cpus, []byte(",")) {
99 index := string(bytes.TrimSpace(val))
100 if len(index) == 0 {
101 continue
102 }
103 list = append(list, index)
104 }
105 if len(list) == 0 {
106 return nil, fmt.Errorf("empty CPU list from %#q: %s", cmd, output)
107 }
108 return list, nil
109 }
110
111 func checkNCPU(list []string) error {
112 listString := strings.Join(list, ",")
113 if len(listString) == 0 {
114 return fmt.Errorf("could not check against an empty CPU list")
115 }
116
117 cListString := cpuSetRE.FindString(listString)
118 if len(cListString) == 0 {
119 return fmt.Errorf("invalid cpuset output '%s'", listString)
120 }
121
122 cmd := exec.Command("cpuset", "-l", cListString, os.Args[0], "FreeBSDNumCPUHelper")
123 output, err := cmd.CombinedOutput()
124 if err != nil {
125 return fmt.Errorf("fail to launch child %#q, error: %s, output: %s", cmd, err, output)
126 }
127
128
129 output = bytes.TrimSpace(output)
130 n, err := strconv.Atoi(string(output))
131 if err != nil {
132 return fmt.Errorf("fail to parse output from child %#q, error: %s, output: %s", cmd, err, output)
133 }
134 if n != len(list) {
135 return fmt.Errorf("runtime.NumCPU() expected to %d, got %d when run with CPU list %s", len(list), n, cListString)
136 }
137 return nil
138 }
139
View as plain text