diff --git a/receiver/hostmetricsreceiver/internal/scraper/swapscraper/pagefile_windows.go b/receiver/hostmetricsreceiver/internal/scraper/swapscraper/pagefile_windows.go index efb961cbf7b..e25b1d9620a 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/swapscraper/pagefile_windows.go +++ b/receiver/hostmetricsreceiver/internal/scraper/swapscraper/pagefile_windows.go @@ -24,14 +24,37 @@ import ( ) var ( - modPsapi = windows.NewLazySystemDLL("psapi.dll") - procEnumPageFilesW = modPsapi.NewProc("EnumPageFilesW") + modKernel32 = windows.NewLazySystemDLL("kernel32.dll") + modPsapi = windows.NewLazySystemDLL("psapi.dll") + + procGetNativeSystemInfo = modKernel32.NewProc("GetNativeSystemInfo") + procEnumPageFilesW = modPsapi.NewProc("EnumPageFilesW") ) +type systemInfo struct { + wProcessorArchitecture uint16 + wReserved uint16 + dwPageSize uint32 + lpMinimumApplicationAddress uintptr + lpMaximumApplicationAddress uintptr + dwActiveProcessorMask uintptr + dwNumberOfProcessors uint32 + dwProcessorType uint32 + dwAllocationGranularity uint32 + wProcessorLevel uint16 + wProcessorRevision uint16 +} + +func getPageSize() uint64 { + var sysInfo systemInfo + procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&sysInfo))) + return uint64(sysInfo.dwPageSize) +} + type pageFileData struct { - name string - used uint64 - total uint64 + name string + usedPages uint64 + totalPages uint64 } // system type as defined in https://docs.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-enum_page_file_information @@ -60,9 +83,9 @@ func pEnumPageFileCallbackW(pageFiles *[]*pageFileData, enumPageFileInfo *enumPa pageFileName := syscall.UTF16ToString((*lpFilenamePtr)[:]) pfData := &pageFileData{ - name: pageFileName, - used: enumPageFileInfo.totalInUse, - total: enumPageFileInfo.totalSize, + name: pageFileName, + usedPages: enumPageFileInfo.totalInUse, + totalPages: enumPageFileInfo.totalSize, } *pageFiles = append(*pageFiles, pfData) diff --git a/receiver/hostmetricsreceiver/internal/scraper/swapscraper/swap_metadata.go b/receiver/hostmetricsreceiver/internal/scraper/swapscraper/swap_metadata.go index cb7e9f0b8d5..5437e1ff9f4 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/swapscraper/swap_metadata.go +++ b/receiver/hostmetricsreceiver/internal/scraper/swapscraper/swap_metadata.go @@ -54,7 +54,7 @@ var swapUsageDescriptor = func() pdata.Metric { metric.InitEmpty() metric.SetName("system.swap.usage") metric.SetDescription("Swap (unix) or pagefile (windows) usage.") - metric.SetUnit("pages") + metric.SetUnit("bytes") metric.SetDataType(pdata.MetricDataTypeIntSum) sum := metric.IntSum() sum.InitEmpty() diff --git a/receiver/hostmetricsreceiver/internal/scraper/swapscraper/swap_scraper_windows.go b/receiver/hostmetricsreceiver/internal/scraper/swapscraper/swap_scraper_windows.go index 88653367bc1..a06eeb15380 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/swapscraper/swap_scraper_windows.go +++ b/receiver/hostmetricsreceiver/internal/scraper/swapscraper/swap_scraper_windows.go @@ -19,6 +19,7 @@ package swapscraper import ( "context" "math" + "sync" "time" "go.opentelemetry.io/collector/component/componenterror" @@ -39,6 +40,8 @@ type scraper struct { pageReadsPerSecCounter pdh.PerfCounterScraper pageWritesPerSecCounter pdh.PerfCounterScraper + pageSize uint64 + startTime pdata.TimestampUnixNano prevPagingScrapeTime time.Time cumulativePageReads float64 @@ -48,9 +51,16 @@ type scraper struct { pageFileStats func() ([]*pageFileData, error) } +var ( + once sync.Once + pageSize uint64 +) + // newSwapScraper creates a Swap Scraper func newSwapScraper(_ context.Context, cfg *Config) *scraper { - return &scraper{config: cfg, pageFileStats: getPageFileStats} + once.Do(func() { pageSize = getPageSize() }) + + return &scraper{config: cfg, pageSize: pageSize, pageFileStats: getPageFileStats} } // Initialize @@ -118,11 +128,11 @@ func (s *scraper) scrapeAndAppendSwapUsageMetric(metrics pdata.MetricSlice) erro idx := metrics.Len() metrics.Resize(idx + 1) - initializeSwapUsageMetric(metrics.At(idx), now, pageFiles) + s.initializeSwapUsageMetric(metrics.At(idx), now, pageFiles) return nil } -func initializeSwapUsageMetric(metric pdata.Metric, now pdata.TimestampUnixNano, pageFiles []*pageFileData) { +func (s *scraper) initializeSwapUsageMetric(metric pdata.Metric, now pdata.TimestampUnixNano, pageFiles []*pageFileData) { swapUsageDescriptor.CopyTo(metric) idps := metric.IntSum().DataPoints() @@ -130,8 +140,8 @@ func initializeSwapUsageMetric(metric pdata.Metric, now pdata.TimestampUnixNano, idx := 0 for _, pageFile := range pageFiles { - initializeSwapUsageDataPoint(idps.At(idx+0), now, pageFile.name, usedLabelValue, int64(pageFile.used)) - initializeSwapUsageDataPoint(idps.At(idx+1), now, pageFile.name, freeLabelValue, int64(pageFile.total-pageFile.used)) + initializeSwapUsageDataPoint(idps.At(idx+0), now, pageFile.name, usedLabelValue, int64(pageFile.usedPages*s.pageSize)) + initializeSwapUsageDataPoint(idps.At(idx+1), now, pageFile.name, freeLabelValue, int64((pageFile.totalPages-pageFile.usedPages)*s.pageSize)) idx += 2 } } diff --git a/receiver/hostmetricsreceiver/internal/scraper/swapscraper/swap_scraper_windows_test.go b/receiver/hostmetricsreceiver/internal/scraper/swapscraper/swap_scraper_windows_test.go index 21c3c3617f1..9fa9c21176c 100644 --- a/receiver/hostmetricsreceiver/internal/scraper/swapscraper/swap_scraper_windows_test.go +++ b/receiver/hostmetricsreceiver/internal/scraper/swapscraper/swap_scraper_windows_test.go @@ -30,17 +30,32 @@ import ( func TestScrapeMetrics_Errors(t *testing.T) { type testCase struct { name string - pageFileError error + pageSize uint64 + getPageFileStats func() ([]*pageFileData, error) pageReadsPerSecCounterReturnValue interface{} pageWritesPerSecCounterReturnValue interface{} expectedError string + expectedUsedValue int64 + expectedFreeValue int64 } + testPageSize := uint64(4096) + testPageFileData := &pageFileData{usedPages: 100, totalPages: 300} + testCases := []testCase{ { - name: "pageFileError", - pageFileError: errors.New("err1"), - expectedError: "err1", + name: "standard", + pageSize: testPageSize, + getPageFileStats: func() ([]*pageFileData, error) { + return []*pageFileData{testPageFileData}, nil + }, + expectedUsedValue: int64(testPageFileData.usedPages * testPageSize), + expectedFreeValue: int64((testPageFileData.totalPages - testPageFileData.usedPages) * testPageSize), + }, + { + name: "pageFileError", + getPageFileStats: func() ([]*pageFileData, error) { return nil, errors.New("err1") }, + expectedError: "err1", }, { name: "readsPerSecCounterError", @@ -55,7 +70,7 @@ func TestScrapeMetrics_Errors(t *testing.T) { }, { name: "multipleErrors", - pageFileError: errors.New("err1"), + getPageFileStats: func() ([]*pageFileData, error) { return nil, errors.New("err1") }, pageReadsPerSecCounterReturnValue: errors.New("err2"), expectedError: "[err1; err2]", }, @@ -64,8 +79,14 @@ func TestScrapeMetrics_Errors(t *testing.T) { for _, test := range testCases { t.Run(test.name, func(t *testing.T) { scraper := newSwapScraper(context.Background(), &Config{}) - if test.pageFileError != nil { - scraper.pageFileStats = func() ([]*pageFileData, error) { return nil, test.pageFileError } + if test.getPageFileStats != nil { + scraper.pageFileStats = test.getPageFileStats + } + if test.pageSize > 0 { + scraper.pageSize = test.pageSize + } else { + assert.Greater(t, pageSize, uint64(0)) + assert.Zero(t, pageSize%4096) // page size on Windows should always be a multiple of 4KB } err := scraper.Initialize(context.Background()) @@ -75,8 +96,15 @@ func TestScrapeMetrics_Errors(t *testing.T) { scraper.pageReadsPerSecCounter = pdh.NewMockPerfCounter(test.pageReadsPerSecCounterReturnValue) scraper.pageWritesPerSecCounter = pdh.NewMockPerfCounter(test.pageWritesPerSecCounterReturnValue) - _, err = scraper.ScrapeMetrics(context.Background()) - assert.EqualError(t, err, test.expectedError) + metrics, err := scraper.ScrapeMetrics(context.Background()) + if test.expectedError != "" { + assert.EqualError(t, err, test.expectedError) + return + } + + swapUsageMetric := metrics.At(0) + assert.Equal(t, test.expectedUsedValue, swapUsageMetric.IntSum().DataPoints().At(0).Value()) + assert.Equal(t, test.expectedFreeValue, swapUsageMetric.IntSum().DataPoints().At(1).Value()) }) } }