From e66b1cd1529cfd76b57f35918b030ab65de4d1b0 Mon Sep 17 00:00:00 2001 From: Uggah Date: Sun, 23 Apr 2023 18:01:21 +0200 Subject: [PATCH 1/5] Optimize publishing --- internal/pubsub/broker.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/pubsub/broker.go b/internal/pubsub/broker.go index 3bb80e8..0e59c38 100644 --- a/internal/pubsub/broker.go +++ b/internal/pubsub/broker.go @@ -76,11 +76,12 @@ func (broker *Broker) Publish(monitor string, message string) { broker.logger.Trace(fmt.Sprintf("Publishing message %s on monitor %s.", message, monitor)) for _, subscriber := range subscribers { - m := NewMessage(message, monitor) if !subscriber.active { continue } + m := NewMessage(message, monitor) + go (func(s *Subscriber) { s.signal(m) })(subscriber) From f02a4754c14fa7321e64599204440115020200e4 Mon Sep 17 00:00:00 2001 From: Uggah Date: Sun, 23 Apr 2023 18:05:00 +0200 Subject: [PATCH 2/5] Add module for getting cpu information --- cmd/excubitor/cmd/root.go | 8 +- internal/context/clock.go | 15 +++ internal/context/context.go | 13 ++- internal/http_server/http_server_test.go | 2 +- internal/integrated_modules/cpuinfo/cpu.go | 93 +++++++++++++++++++ .../integrated_modules/cpuinfo/cpu_test.go | 49 ++++++++++ .../integrated_modules/cpuinfo/cpuinfo.go | 52 +++++++++++ 7 files changed, 227 insertions(+), 5 deletions(-) create mode 100644 internal/context/clock.go create mode 100644 internal/integrated_modules/cpuinfo/cpu.go create mode 100644 internal/integrated_modules/cpuinfo/cpu_test.go create mode 100644 internal/integrated_modules/cpuinfo/cpuinfo.go diff --git a/cmd/excubitor/cmd/root.go b/cmd/excubitor/cmd/root.go index fe19aa0..3073fc3 100644 --- a/cmd/excubitor/cmd/root.go +++ b/cmd/excubitor/cmd/root.go @@ -3,6 +3,7 @@ package cmd import ( ctx "github.com/Excubitor-Monitoring/Excubitor-Backend/internal/context" "github.com/Excubitor-Monitoring/Excubitor-Backend/internal/http_server" + "github.com/Excubitor-Monitoring/Excubitor-Backend/internal/integrated_modules/cpuinfo" "github.com/Excubitor-Monitoring/Excubitor-Backend/internal/logging" "github.com/Excubitor-Monitoring/Excubitor-Backend/internal/pubsub" ) @@ -17,7 +18,12 @@ func Execute() error { } context := ctx.GetContext() - context.RegisterModule(ctx.NewModule("main")) + + context.RegisterModule(ctx.NewModule("main", func() { + logger.Trace("Tick!") + })) + context.RegisterModule(ctx.NewModule("cpu", cpuinfo.Tick)) + context.RegisterBroker(pubsub.NewBroker()) logger.Debug("Starting HTTP Server!") diff --git a/internal/context/clock.go b/internal/context/clock.go new file mode 100644 index 0000000..5683d82 --- /dev/null +++ b/internal/context/clock.go @@ -0,0 +1,15 @@ +package ctx + +import "time" + +func startClock() { + go func() { + for { + modules := GetContext().GetModules() + for _, module := range modules { + module.tickFunction() + } + time.Sleep(1000 * time.Millisecond) + } + }() +} diff --git a/internal/context/context.go b/internal/context/context.go index 5edd446..f06dcad 100644 --- a/internal/context/context.go +++ b/internal/context/context.go @@ -7,11 +7,12 @@ import ( ) type Module struct { - Name string `json:"name"` + Name string `json:"name"` + tickFunction func() } -func NewModule(name string) *Module { - return &Module{name} +func NewModule(name string, tickFunction func()) *Module { + return &Module{name, tickFunction} } type Context struct { @@ -41,7 +42,13 @@ func (ctx *Context) RegisterModule(module *Module) { ctx.lock.RLock() defer ctx.lock.RUnlock() + var once sync.Once + ctx.modules[module.Name] = module + + once.Do(func() { + startClock() + }) } func (ctx *Context) GetModules() []Module { diff --git a/internal/http_server/http_server_test.go b/internal/http_server/http_server_test.go index 285b807..a845c0e 100644 --- a/internal/http_server/http_server_test.go +++ b/internal/http_server/http_server_test.go @@ -10,7 +10,7 @@ import ( ) func TestInfo(t *testing.T) { - ctx.GetContext().RegisterModule(ctx.NewModule("TestModule")) + ctx.GetContext().RegisterModule(ctx.NewModule("TestModule", func() {})) req := httptest.NewRequest(http.MethodGet, "/info", nil) w := httptest.NewRecorder() diff --git a/internal/integrated_modules/cpuinfo/cpu.go b/internal/integrated_modules/cpuinfo/cpu.go new file mode 100644 index 0000000..979427a --- /dev/null +++ b/internal/integrated_modules/cpuinfo/cpu.go @@ -0,0 +1,93 @@ +package cpuinfo + +import ( + "fmt" + "regexp" + "strconv" +) + +type cpu struct { + Id uint `json:"id"` + CoreId uint `json:"core_id"` + SocketId uint `json:"socket_id"` + ModelName string `json:"model_name"` + ClockSpeed float64 `json:"clock_speed"` + CacheSize uint `json:"cache_size"` + Flags string `json:"flags"` +} + +func readCPU(paragraph string) (*cpu, error) { + id, err := getUInt("processor", paragraph) + if err != nil { + return nil, fmt.Errorf("could not parse processor id: %w", err) + } + + coreId, err := getUInt("core id", paragraph) + if err != nil { + return nil, fmt.Errorf("could not parse core id: %w", err) + } + + socketId, err := getUInt("physical id", paragraph) + if err != nil { + return nil, fmt.Errorf("could not parse physical id: %w", err) + } + + modelName, err := getString("model name", paragraph) + if err != nil { + return nil, fmt.Errorf("could not parse model name: %w", err) + } + + clockSpeed, err := getFloat64("cpu MHz", paragraph) + if err != nil { + return nil, fmt.Errorf("could not parse clock speed: %w", err) + } + + cache, err := getUInt("cache size", paragraph) + if err != nil { + return nil, fmt.Errorf("could not parse cache size: %w", err) + } + + flags, err := getString("flags", paragraph) + if err != nil { + return nil, fmt.Errorf("could not parse flags: %w", err) + } + + return &cpu{ + Id: id, + CoreId: coreId, + SocketId: socketId, + ModelName: modelName, + ClockSpeed: clockSpeed, + CacheSize: cache, + Flags: flags, + }, nil +} + +func getUInt(name string, paragraph string) (uint, error) { + regex := regexp.MustCompile(name + `\s+:\s+(\d+)`) + matches := regex.FindStringSubmatch(paragraph) + value, err := strconv.ParseUint(matches[1], 10, 32) + if err != nil { + return 9999, err + } + + return uint(value), nil +} + +func getFloat64(name string, paragraph string) (float64, error) { + regex := regexp.MustCompile(name + `\s+:\s+([[:digit:]]+.[[:digit:]]+)`) + matches := regex.FindStringSubmatch(paragraph) + value, err := strconv.ParseFloat(matches[1], 64) + if err != nil { + return 9999, err + } + + return value, nil +} + +func getString(name string, paragraph string) (string, error) { + regex := regexp.MustCompile(name + `\s+:\s+([[:print:]]+)`) + matches := regex.FindStringSubmatch(paragraph) + + return matches[1], nil +} diff --git a/internal/integrated_modules/cpuinfo/cpu_test.go b/internal/integrated_modules/cpuinfo/cpu_test.go new file mode 100644 index 0000000..072b0c0 --- /dev/null +++ b/internal/integrated_modules/cpuinfo/cpu_test.go @@ -0,0 +1,49 @@ +package cpuinfo + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestReadCPU(t *testing.T) { + testData := `processor : 2 +vendor_id : GenuineIntel +cpu family : 6 +model : 158 +model name : Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz +stepping : 9 +microcode : 0xf0 +cpu MHz : 4500.034 +cache size : 8192 KB +physical id : 0 +siblings : 8 +core id : 2 +cpu cores : 4 +apicid : 4 +initial apicid : 4 +fpu : yes +fpu_exception : yes +cpuid level : 22 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d arch_capabilities +vmx flags : vnmi preemption_timer invvpid ept_x_only ept_ad ept_1gb flexpriority tsc_offset vtpr mtf vapic ept vpid unrestricted_guest ple shadow_vmcs pml ept_mode_based_exec +bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa itlb_multihit srbds mmio_stale_data retbleed +bogomips : 8400.00 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management:` + + cpu, err := readCPU(testData) + if err != nil { + return + } + + assert.Equal(t, uint(2), cpu.Id) + assert.Equal(t, uint(2), cpu.CoreId) + assert.Equal(t, uint(0), cpu.SocketId) + assert.Equal(t, "Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz", cpu.ModelName) + assert.Equal(t, 4500.034, cpu.ClockSpeed) + assert.Equal(t, uint(8192), cpu.CacheSize) + assert.Equal(t, "fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d arch_capabilities", cpu.Flags) +} diff --git a/internal/integrated_modules/cpuinfo/cpuinfo.go b/internal/integrated_modules/cpuinfo/cpuinfo.go new file mode 100644 index 0000000..3a5726e --- /dev/null +++ b/internal/integrated_modules/cpuinfo/cpuinfo.go @@ -0,0 +1,52 @@ +package cpuinfo + +import ( + "encoding/json" + "fmt" + ctx "github.com/Excubitor-Monitoring/Excubitor-Backend/internal/context" + "github.com/Excubitor-Monitoring/Excubitor-Backend/internal/logging" + "os" + "regexp" +) + +var logger logging.Logger + +func Tick() { + logger = logging.GetLogger() + + cpus, err := readCPUInfo() + if err != nil { + logger.Error(fmt.Sprintf("Could not gather cpu information! Reason: %s", err)) + } + + jsonOutput, err := json.Marshal(cpus) + if err != nil { + logger.Error(fmt.Sprintf("Couldn't encode cpu information! Reason: %s", err)) + } + + broker := ctx.GetContext().GetBroker() + broker.Publish("CPU.CpuInfo", string(jsonOutput)) +} + +func readCPUInfo() ([]cpu, error) { + file, err := os.ReadFile("/proc/cpuinfo") + if err != nil { + return nil, err + } + + paragraphs := regexp.MustCompile(`\n\s*\n`).Split(string(file), -1) + + cpus := make([]cpu, len(paragraphs)-1) + + for i, p := range paragraphs { + if len(p) != 0 { + cpu, err := readCPU(p) + if err != nil { + return nil, err + } + cpus[i] = *cpu + } + } + + return cpus, nil +} From 85d50d1057bbe72140a3753c00e007a4805bf88e Mon Sep 17 00:00:00 2001 From: Uggah Date: Sun, 23 Apr 2023 18:06:25 +0200 Subject: [PATCH 3/5] Fix closure of closed channel --- internal/http_server/websocket.go | 22 +++++++++++++++------- internal/pubsub/subscriber.go | 10 +++++++++- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/internal/http_server/websocket.go b/internal/http_server/websocket.go index 32a0c56..a370ca9 100644 --- a/internal/http_server/websocket.go +++ b/internal/http_server/websocket.go @@ -8,6 +8,7 @@ import ( "github.com/gobwas/ws" "github.com/gobwas/ws/wsutil" "net" + "sync" ) type OpCode string @@ -114,16 +115,23 @@ func handleWebsocket(conn net.Conn) { switch content.OpCode { case GET: temporarySubscriber := broker.AddSubscriber() + + var receiveOnce sync.Once + logger.Trace(fmt.Sprintf("Added temporary subscriber to fulfill GET request from %s on monitor %s.", clientAddress, content.Target)) go temporarySubscriber.Listen(func(m *pubsub.Message) { - logger.Trace(fmt.Sprintf("Sending single message from %s to connection from %s", m.GetMonitor(), clientAddress)) - temporarySubscriber.Destruct() - err = sendMessage(conn, newMessage(REPLY, TargetAddress(m.GetMonitor()), m.GetMessageBody())) - if err != nil { - logger.Error(fmt.Sprintf("Couldn't send message to %s. Aborting connection...", clientAddress)) - return - } + receiveOnce.Do(func() { + logger.Trace(fmt.Sprintf("Sending single message from %s to connection from %s", m.GetMonitor(), clientAddress)) + broker.Unsubscribe(temporarySubscriber, string(content.Target)) + defer temporarySubscriber.Destruct() + + err = sendMessage(conn, newMessage(REPLY, TargetAddress(m.GetMonitor()), m.GetMessageBody())) + if err != nil { + logger.Error(fmt.Sprintf("Couldn't send message to %s. Aborting connection...", clientAddress)) + return + } + }) }) broker.Subscribe(temporarySubscriber, string(content.Target)) diff --git a/internal/pubsub/subscriber.go b/internal/pubsub/subscriber.go index 0546c5f..7dcbacd 100644 --- a/internal/pubsub/subscriber.go +++ b/internal/pubsub/subscriber.go @@ -15,6 +15,7 @@ type Subscriber struct { monitors map[string]bool active bool lock sync.RWMutex + wg sync.WaitGroup } func newSubscriber() (string, *Subscriber) { @@ -60,6 +61,9 @@ func (subscriber *Subscriber) signal(message *Message) { defer subscriber.lock.RUnlock() if subscriber.active { + subscriber.wg.Add(1) + defer subscriber.wg.Done() + subscriber.messages <- message } } @@ -82,5 +86,9 @@ func (subscriber *Subscriber) Destruct() { logger.Trace(fmt.Sprintf("Destructing subscriber %s", subscriber.id)) subscriber.active = false - close(subscriber.messages) + + go func() { + subscriber.wg.Wait() + close(subscriber.messages) + }() } From a64787242c04047fd8b3bdc5685b69bb18b644f2 Mon Sep 17 00:00:00 2001 From: Uggah Date: Sun, 23 Apr 2023 18:06:53 +0200 Subject: [PATCH 4/5] Fix resource leak --- internal/pubsub/subscriber.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/pubsub/subscriber.go b/internal/pubsub/subscriber.go index 7dcbacd..1fb7919 100644 --- a/internal/pubsub/subscriber.go +++ b/internal/pubsub/subscriber.go @@ -71,6 +71,10 @@ func (subscriber *Subscriber) signal(message *Message) { // Listen listens for messages on the messages channel and calls a Listener function with the message as an argument. func (subscriber *Subscriber) Listen(listener Listener) { for { + if !subscriber.active { + break + } + if message, ok := <-subscriber.messages; ok { logger.Trace(fmt.Sprintf("Subscriber %s received message from %s: %s", subscriber.id, message.GetMonitor(), message.GetMessageBody())) listener(message) From 0c3a36e91cf381daf1382819fa1ad8fcd504ab4b Mon Sep 17 00:00:00 2001 From: Uggah Date: Sat, 29 Apr 2023 17:35:21 +0200 Subject: [PATCH 5/5] Add more tests, comments --- internal/integrated_modules/cpuinfo/cpu.go | 1 + .../integrated_modules/cpuinfo/cpuinfo.go | 23 +++-- .../cpuinfo/cpuinfo_test.go | 94 +++++++++++++++++++ 3 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 internal/integrated_modules/cpuinfo/cpuinfo_test.go diff --git a/internal/integrated_modules/cpuinfo/cpu.go b/internal/integrated_modules/cpuinfo/cpu.go index 979427a..0f2e01e 100644 --- a/internal/integrated_modules/cpuinfo/cpu.go +++ b/internal/integrated_modules/cpuinfo/cpu.go @@ -16,6 +16,7 @@ type cpu struct { Flags string `json:"flags"` } +// readCPU parses a single thread/core from a cpuinfo file into a cpu struct. func readCPU(paragraph string) (*cpu, error) { id, err := getUInt("processor", paragraph) if err != nil { diff --git a/internal/integrated_modules/cpuinfo/cpuinfo.go b/internal/integrated_modules/cpuinfo/cpuinfo.go index 3a5726e..53bddd9 100644 --- a/internal/integrated_modules/cpuinfo/cpuinfo.go +++ b/internal/integrated_modules/cpuinfo/cpuinfo.go @@ -11,30 +11,41 @@ import ( var logger logging.Logger +// Tick is a function that is called whenever the context wants the module to report its values. func Tick() { logger = logging.GetLogger() - cpus, err := readCPUInfo() + cpuInfo, err := readCPUInfoFile() + if err != nil { + logger.Error(fmt.Sprintf("Could not read /proc/cpuinfo! Reason: %s", err)) + return + } + + cpus, err := readCPUInfo(string(cpuInfo)) if err != nil { logger.Error(fmt.Sprintf("Could not gather cpu information! Reason: %s", err)) + return } jsonOutput, err := json.Marshal(cpus) if err != nil { logger.Error(fmt.Sprintf("Couldn't encode cpu information! Reason: %s", err)) + return } broker := ctx.GetContext().GetBroker() broker.Publish("CPU.CpuInfo", string(jsonOutput)) } -func readCPUInfo() ([]cpu, error) { +// readCPUInfoFile reads the contents of /proc/cpuinfo and returns them in a byte slice. +func readCPUInfoFile() ([]byte, error) { file, err := os.ReadFile("/proc/cpuinfo") - if err != nil { - return nil, err - } + return file, err +} - paragraphs := regexp.MustCompile(`\n\s*\n`).Split(string(file), -1) +// readCPUInfo can parse multiple threads from a cpuinfo file. +func readCPUInfo(cpuInfo string) ([]cpu, error) { + paragraphs := regexp.MustCompile(`\n\s*\n`).Split(cpuInfo, -1) cpus := make([]cpu, len(paragraphs)-1) diff --git a/internal/integrated_modules/cpuinfo/cpuinfo_test.go b/internal/integrated_modules/cpuinfo/cpuinfo_test.go new file mode 100644 index 0000000..d3a50f7 --- /dev/null +++ b/internal/integrated_modules/cpuinfo/cpuinfo_test.go @@ -0,0 +1,94 @@ +package cpuinfo + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestReadCPUInfo(t *testing.T) { + testData := `processor : 0 +vendor_id : GenuineIntel +cpu family : 6 +model : 158 +model name : Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz +stepping : 9 +microcode : 0xf0 +cpu MHz : 4500.221 +cache size : 8192 KB +physical id : 0 +siblings : 8 +core id : 0 +cpu cores : 4 +apicid : 0 +initial apicid : 0 +fpu : yes +fpu_exception : yes +cpuid level : 22 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d arch_capabilities +vmx flags : vnmi preemption_timer invvpid ept_x_only ept_ad ept_1gb flexpriority tsc_offset vtpr mtf vapic ept vpid unrestricted_guest ple shadow_vmcs pml ept_mode_based_exec +bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa itlb_multihit srbds mmio_stale_data retbleed +bogomips : 8400.00 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management: + +processor : 1 +vendor_id : GenuineIntel +cpu family : 6 +model : 158 +model name : Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz +stepping : 9 +microcode : 0xf0 +cpu MHz : 4500.004 +cache size : 8192 KB +physical id : 0 +siblings : 8 +core id : 1 +cpu cores : 4 +apicid : 2 +initial apicid : 2 +fpu : yes +fpu_exception : yes +cpuid level : 22 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d arch_capabilities +vmx flags : vnmi preemption_timer invvpid ept_x_only ept_ad ept_1gb flexpriority tsc_offset vtpr mtf vapic ept vpid unrestricted_guest ple shadow_vmcs pml ept_mode_based_exec +bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa itlb_multihit srbds mmio_stale_data retbleed +bogomips : 8400.00 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +power management: + +` + + info, err := readCPUInfo(testData) + if err != nil { + t.Error(err) + return + } + + assert.Equal(t, 2, len(info)) + + firstCore := info[0] + secondCore := info[1] + + assert.Equal(t, uint(0), firstCore.Id) + assert.Equal(t, uint(0), firstCore.CoreId) + assert.Equal(t, uint(0), firstCore.SocketId) + assert.Equal(t, "Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz", firstCore.ModelName) + assert.Equal(t, 4500.221, firstCore.ClockSpeed) + assert.Equal(t, uint(8192), firstCore.CacheSize) + assert.Equal(t, "fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d arch_capabilities", firstCore.Flags) + + assert.Equal(t, uint(1), secondCore.Id) + assert.Equal(t, uint(1), secondCore.CoreId) + assert.Equal(t, uint(0), secondCore.SocketId) + assert.Equal(t, "Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz", secondCore.ModelName) + assert.Equal(t, 4500.004, secondCore.ClockSpeed) + assert.Equal(t, uint(8192), secondCore.CacheSize) + assert.Equal(t, "fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d arch_capabilities", secondCore.Flags) + +}