From e00802492637cc568425d9edf582c70a5428a3ef Mon Sep 17 00:00:00 2001 From: Alex K <8418476+fearful-symmetry@users.noreply.github.com> Date: Tue, 29 Jun 2021 13:47:07 -0700 Subject: [PATCH] Refactor of system/memory metricset (#26334) * init commit * start on linux implementation * finish linux, start work on darwin * fix build platform issues * fix metrics on darwin * add openbsd * add freebsd * add windows, aix * fix aix build * finish memory * fix up opt changes * cleanup metricset code * fix up folder methods * fix calculations, Opt API, gomod * make notice * go mod tidy, mage fmt * fix up linux/memory * update fields * update system tests * fix system tests, again * fix extra print statements * fix if block in fillPercentages * vix Value API * fix up tests, opt --- NOTICE.txt | 424 +++++++++--------- go.mod | 2 +- libbeat/metric/system/memory/memory.go | 172 ------- libbeat/metric/system/memory/memory_test.go | 96 ---- libbeat/metric/system/process/process.go | 16 +- metricbeat/docs/fields.asciidoc | 12 + metricbeat/internal/metrics/memory/memory.go | 102 +++++ .../internal/metrics/memory/memory_aix.go | 75 ++++ .../internal/metrics/memory/memory_darwin.go | 133 ++++++ .../internal/metrics/memory/memory_freebsd.go | 90 ++++ .../internal/metrics/memory/memory_linux.go | 143 ++++++ .../internal/metrics/memory/memory_openbsd.go | 236 ++++++++++ .../internal/metrics/memory/memory_test.go | 102 +++++ .../internal/metrics/memory/memory_windows.go | 51 +++ metricbeat/internal/metrics/opt.go | 80 +++- .../module/linux/memory/_meta/data.json | 14 +- metricbeat/module/linux/memory/data.go | 163 ++++--- .../module/linux}/memory/doc.go | 0 metricbeat/module/linux/memory/memory.go | 9 +- metricbeat/module/linux/memory/memory_test.go | 2 +- metricbeat/module/system/fields.go | 2 +- .../module/system/memory/_meta/data.json | 45 +- .../module/system/memory/_meta/fields.yml | 6 + .../module/system/memory/helper_linux.go | 32 ++ .../module/system/memory/helper_other.go | 38 ++ metricbeat/module/system/memory/memory.go | 79 +--- .../module/system/memory/memory_test.go | 2 +- metricbeat/module/system/test_system.py | 13 +- 28 files changed, 1502 insertions(+), 637 deletions(-) delete mode 100644 libbeat/metric/system/memory/memory.go delete mode 100644 libbeat/metric/system/memory/memory_test.go create mode 100644 metricbeat/internal/metrics/memory/memory.go create mode 100644 metricbeat/internal/metrics/memory/memory_aix.go create mode 100644 metricbeat/internal/metrics/memory/memory_darwin.go create mode 100644 metricbeat/internal/metrics/memory/memory_freebsd.go create mode 100644 metricbeat/internal/metrics/memory/memory_linux.go create mode 100644 metricbeat/internal/metrics/memory/memory_openbsd.go create mode 100644 metricbeat/internal/metrics/memory/memory_test.go create mode 100644 metricbeat/internal/metrics/memory/memory_windows.go rename {libbeat/metric/system => metricbeat/module/linux}/memory/doc.go (100%) create mode 100644 metricbeat/module/system/memory/helper_linux.go create mode 100644 metricbeat/module/system/memory/helper_other.go diff --git a/NOTICE.txt b/NOTICE.txt index 35e8d9ec736..9f3cb22b2ee 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -8539,6 +8539,218 @@ Contents of probable licence file $GOMODCACHE/github.com/elastic/go-ucfg@v0.8.3/ limitations under the License. +-------------------------------------------------------------------------------- +Dependency : github.com/elastic/go-windows +Version: v1.0.1 +Licence type (autodetected): Apache-2.0 +-------------------------------------------------------------------------------- + +Contents of probable licence file $GOMODCACHE/github.com/elastic/go-windows@v1.0.1/LICENSE.txt: + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + -------------------------------------------------------------------------------- Dependency : github.com/elastic/gosigar Version: v0.14.1 @@ -27056,218 +27268,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------------------------------- -Dependency : github.com/elastic/go-windows -Version: v1.0.1 -Licence type (autodetected): Apache-2.0 --------------------------------------------------------------------------------- - -Contents of probable licence file $GOMODCACHE/github.com/elastic/go-windows@v1.0.1/LICENSE.txt: - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - -------------------------------------------------------------------------------- Dependency : github.com/elazarl/goproxy Version: v0.0.0-20180725130230-947c36da3153 diff --git a/go.mod b/go.mod index 5b989515af4..f453a519371 100644 --- a/go.mod +++ b/go.mod @@ -74,7 +74,7 @@ require ( github.com/elastic/go-sysinfo v1.7.0 github.com/elastic/go-txfile v0.0.7 github.com/elastic/go-ucfg v0.8.3 - github.com/elastic/go-windows v1.0.1 // indirect + github.com/elastic/go-windows v1.0.1 github.com/elastic/gosigar v0.14.1 github.com/fatih/color v1.9.0 github.com/fsnotify/fsevents v0.1.1 diff --git a/libbeat/metric/system/memory/memory.go b/libbeat/metric/system/memory/memory.go deleted file mode 100644 index 72e351db384..00000000000 --- a/libbeat/metric/system/memory/memory.go +++ /dev/null @@ -1,172 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -// +build darwin freebsd linux openbsd windows - -package memory - -import ( - "github.com/pkg/errors" - - "github.com/elastic/beats/v7/libbeat/common" - "github.com/elastic/beats/v7/libbeat/logp" - sysinfo "github.com/elastic/go-sysinfo" - sysinfotypes "github.com/elastic/go-sysinfo/types" - sigar "github.com/elastic/gosigar" -) - -// MemStat includes the memory usage statistics and ratios of usage and total memory usage -type MemStat struct { - sigar.Mem - UsedPercent float64 `json:"used_p"` - ActualUsedPercent float64 `json:"actual_used_p"` -} - -// Get returns the memory stats of the host -func Get() (*MemStat, error) { - mem := sigar.Mem{} - err := mem.Get() - if err != nil { - return nil, err - } - - return &MemStat{Mem: mem}, nil -} - -// AddMemPercentage calculates the ratio of used and total size of memory -func AddMemPercentage(m *MemStat) { - if m.Mem.Total == 0 { - return - } - - perc := float64(m.Mem.Used) / float64(m.Mem.Total) - m.UsedPercent = common.Round(perc, common.DefaultDecimalPlacesCount) - - actualPerc := float64(m.Mem.ActualUsed) / float64(m.Mem.Total) - m.ActualUsedPercent = common.Round(actualPerc, common.DefaultDecimalPlacesCount) -} - -// SwapStat includes the current swap usage and the ratio of used and total swap size -type SwapStat struct { - sigar.Swap - UsedPercent float64 `json:"used_p"` -} - -// GetSwap returns the swap usage of the host -func GetSwap() (*SwapStat, error) { - swap := sigar.Swap{} - err := swap.Get() - if err != nil { - return nil, err - } - - // This shouldn't happen, but it has been reported to happen and - // this can provoke too big values for used swap. - // Workaround this by assuming that all swap is free in that case. - if swap.Free > swap.Total || swap.Used > swap.Total { - logger := logp.NewLogger("memory") - logger.Debugf("memory", - "Unexpected values for swap memory - total: %v free: %v used: %v. Setting swap used to 0.", - swap.Total, swap.Free, swap.Used) - swap.Free = swap.Total - swap.Used = 0 - } - - return &SwapStat{Swap: swap}, nil -} - -// GetMemoryEvent returns the event created from memory statistics -func GetMemoryEvent(memStat *MemStat) common.MapStr { - return common.MapStr{ - "total": memStat.Total, - "used": memStat.Used, - "free": memStat.Free, - "actual_used": memStat.ActualUsed, - "actual_free": memStat.ActualFree, - "used_p": memStat.UsedPercent, - "actual_used_p": memStat.ActualUsedPercent, - } -} - -// GetSwapEvent returns the event created from swap usage -func GetSwapEvent(swapStat *SwapStat) common.MapStr { - return common.MapStr{ - "total": swapStat.Total, - "used": swapStat.Used, - "free": swapStat.Free, - "used_p": swapStat.UsedPercent, - } -} - -// AddSwapPercentage calculates the ratio of used and total swap size -func AddSwapPercentage(s *SwapStat) { - if s.Swap.Total == 0 { - return - } - - perc := float64(s.Swap.Used) / float64(s.Swap.Total) - s.UsedPercent = common.Round(perc, common.DefaultDecimalPlacesCount) -} - -// HugeTLBPagesStat includes metrics about huge pages usage -type HugeTLBPagesStat struct { - sigar.HugeTLBPages - UsedPercent float64 `json:"used_p"` -} - -// GetHugeTLBPages returns huge pages usage metrics -func GetHugeTLBPages() (*HugeTLBPagesStat, error) { - pages := sigar.HugeTLBPages{} - err := pages.Get() - - if err == nil { - return &HugeTLBPagesStat{HugeTLBPages: pages}, nil - } - - if sigar.IsNotImplemented(err) { - return nil, nil - } - - return nil, err -} - -// AddHugeTLBPagesPercentage calculates ratio of used huge pages -func AddHugeTLBPagesPercentage(s *HugeTLBPagesStat) { - if s.Total == 0 { - return - } - - perc := float64(s.Total-s.Free+s.Reserved) / float64(s.Total) - s.UsedPercent = common.Round(perc, common.DefaultDecimalPlacesCount) -} - -// GetVMStat gets linux vmstat metrics -func GetVMStat() (*sysinfotypes.VMStatInfo, error) { - h, err := sysinfo.Host() - if err != nil { - return nil, errors.Wrap(err, "failed to read self process information") - } - if vmstatHandle, ok := h.(sysinfotypes.VMStat); ok { - info, err := vmstatHandle.VMStat() - if err != nil { - return nil, errors.Wrap(err, "error getting VMStat info") - } - return info, nil - } - return nil, nil - -} diff --git a/libbeat/metric/system/memory/memory_test.go b/libbeat/metric/system/memory/memory_test.go deleted file mode 100644 index e71e092de52..00000000000 --- a/libbeat/metric/system/memory/memory_test.go +++ /dev/null @@ -1,96 +0,0 @@ -// Licensed to Elasticsearch B.V. under one or more contributor -// license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright -// ownership. Elasticsearch B.V. licenses this file to you under -// the Apache License, Version 2.0 (the "License"); you may -// not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -// +build !integration -// +build darwin freebsd linux openbsd windows - -package memory - -import ( - "runtime" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/elastic/gosigar" -) - -func TestGetMemory(t *testing.T) { - mem, err := Get() - - assert.NotNil(t, mem) - assert.NoError(t, err) - - assert.True(t, (mem.Total > 0)) - assert.True(t, (mem.Used > 0)) - assert.True(t, (mem.Free >= 0)) - assert.True(t, (mem.ActualFree >= 0)) - assert.True(t, (mem.ActualUsed > 0)) -} - -func TestGetSwap(t *testing.T) { - if runtime.GOOS == "windows" { - return //no load data on windows - } - - swap, err := GetSwap() - - assert.NotNil(t, swap) - assert.NoError(t, err) - - assert.True(t, (swap.Total >= 0)) - assert.True(t, (swap.Used >= 0)) - assert.True(t, (swap.Free >= 0)) -} - -func TestMemPercentage(t *testing.T) { - m := MemStat{ - Mem: gosigar.Mem{ - Total: 7, - Used: 5, - Free: 2, - }, - } - AddMemPercentage(&m) - assert.Equal(t, m.UsedPercent, 0.7143) - - m = MemStat{ - Mem: gosigar.Mem{Total: 0}, - } - AddMemPercentage(&m) - assert.Equal(t, m.UsedPercent, 0.0) -} - -func TestActualMemPercentage(t *testing.T) { - m := MemStat{ - Mem: gosigar.Mem{ - Total: 7, - ActualUsed: 5, - ActualFree: 2, - }, - } - AddMemPercentage(&m) - assert.Equal(t, m.ActualUsedPercent, 0.7143) - - m = MemStat{ - Mem: gosigar.Mem{ - Total: 0, - }, - } - AddMemPercentage(&m) - assert.Equal(t, m.ActualUsedPercent, 0.0) -} diff --git a/libbeat/metric/system/process/process.go b/libbeat/metric/system/process/process.go index 9e7eb5ac932..3a301000a9a 100644 --- a/libbeat/metric/system/process/process.go +++ b/libbeat/metric/system/process/process.go @@ -32,7 +32,7 @@ import ( "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/match" "github.com/elastic/beats/v7/libbeat/logp" - "github.com/elastic/beats/v7/libbeat/metric/system/memory" + sysinfo "github.com/elastic/go-sysinfo" sigar "github.com/elastic/gosigar" "github.com/elastic/gosigar/cgroup" ) @@ -288,12 +288,20 @@ func GetOwnResourceUsageTimeInMillis() (int64, int64, error) { func (procStats *Stats) getProcessEvent(process *Process) common.MapStr { + // This is a holdover until we migrate this library to metricbeat/internal + // At which point we'll use the memory code there. var totalPhyMem uint64 - baseMem, err := memory.Get() + host, err := sysinfo.Host() if err != nil { - procStats.logger.Warnf("Getting memory details: %v", err) + procStats.logger.Warnf("Getting host details: %v", err) } else { - totalPhyMem = baseMem.Mem.Total + memStats, err := host.Memory() + if err != nil { + procStats.logger.Warnf("Getting memory details: %v", err) + } else { + totalPhyMem = memStats.Total + } + } proc := common.MapStr{ diff --git a/metricbeat/docs/fields.asciidoc b/metricbeat/docs/fields.asciidoc index 6d0d48c3dca..10db058421a 100644 --- a/metricbeat/docs/fields.asciidoc +++ b/metricbeat/docs/fields.asciidoc @@ -51614,6 +51614,18 @@ format: bytes The total amount of free memory in bytes. This value does not include memory consumed by system caches and buffers (see system.memory.actual.free). +type: long + +format: bytes + +-- + +*`system.memory.cached`*:: ++ +-- +Total Cached memory on system. + + type: long format: bytes diff --git a/metricbeat/internal/metrics/memory/memory.go b/metricbeat/internal/metrics/memory/memory.go new file mode 100644 index 00000000000..73597e8974f --- /dev/null +++ b/metricbeat/internal/metrics/memory/memory.go @@ -0,0 +1,102 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package memory + +import ( + "github.com/pkg/errors" + + "github.com/elastic/beats/v7/libbeat/common" + "github.com/elastic/beats/v7/metricbeat/internal/metrics" +) + +// Memory holds os-specifc memory usage data +// The vast majority of these values are cross-platform +// However, we're wrapping all them for the sake of safety, and for the more variable swap metrics +type Memory struct { + Total metrics.OptUint `struct:"total,omitempty"` + Used UsedMemStats `struct:"used,omitempty"` + + Free metrics.OptUint `struct:"free,omitempty"` + Cached metrics.OptUint `struct:"cached,omitempty"` + // "Actual" values are, technically, a linux-only concept + // For better or worse we've expanded it to include "derived" + // Memory values on other platforms, which we should + // probably keep for the sake of backwards compatibility + // However, because the derived value varies from platform to platform, + // We may want to more precisely document what these mean. + Actual ActualMemoryMetrics `struct:"actual,omitempty"` + + // Swap metrics + Swap SwapMetrics `struct:"swap,omitempty"` +} + +// UsedMemStats wraps used.* memory metrics +type UsedMemStats struct { + Pct metrics.OptFloat `struct:"pct,omitempty"` + Bytes metrics.OptUint `struct:"bytes,omitempty"` +} + +// ActualMemoryMetrics wraps the actual.* memory metrics +type ActualMemoryMetrics struct { + Free metrics.OptUint `struct:"free,omitempty"` + Used UsedMemStats `struct:"used,omitempty"` +} + +// SwapMetrics wraps swap.* memory metrics +type SwapMetrics struct { + Total metrics.OptUint `struct:"total,omitempty"` + Used UsedMemStats `struct:"used,omitempty"` + Free metrics.OptUint `struct:"free,omitempty"` +} + +// Get returns platform-independent memory metrics. +func Get(procfs string) (Memory, error) { + base, err := get(procfs) + if err != nil { + return Memory{}, errors.Wrap(err, "error getting system memory info") + } + base.fillPercentages() + return base, nil +} + +// IsZero implements the zeroer interface for structform's folders +func (used UsedMemStats) IsZero() bool { + return used.Pct.IsZero() && used.Bytes.IsZero() +} + +// IsZero implements the zeroer interface for structform's folders +func (swap SwapMetrics) IsZero() bool { + return swap.Free.IsZero() && swap.Used.IsZero() && swap.Total.IsZero() +} + +func (base *Memory) fillPercentages() { + // Add percentages + // In theory, `Used` and `Total` are available everywhere, so assume values are good. + if base.Total.Exists() && base.Total.ValueOr(0) != 0 { + percUsed := float64(base.Used.Bytes.ValueOr(0)) / float64(base.Total.ValueOr(1)) + base.Used.Pct = metrics.OptFloatWith(common.Round(percUsed, common.DefaultDecimalPlacesCount)) + + actualPercUsed := float64(base.Actual.Used.Bytes.ValueOr(0)) / float64(base.Total.ValueOr(0)) + base.Actual.Used.Pct = metrics.OptFloatWith(common.Round(actualPercUsed, common.DefaultDecimalPlacesCount)) + } + + if base.Swap.Total.ValueOr(0) != 0 && base.Swap.Used.Bytes.Exists() { + perc := float64(base.Swap.Used.Bytes.ValueOr(0)) / float64(base.Swap.Total.ValueOr(0)) + base.Swap.Used.Pct = metrics.OptFloatWith(common.Round(perc, common.DefaultDecimalPlacesCount)) + } +} diff --git a/metricbeat/internal/metrics/memory/memory_aix.go b/metricbeat/internal/metrics/memory/memory_aix.go new file mode 100644 index 00000000000..e936c518479 --- /dev/null +++ b/metricbeat/internal/metrics/memory/memory_aix.go @@ -0,0 +1,75 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package memory + +/* +#cgo LDFLAGS: -L/usr/lib -lperfstat + +#include +#include +#include +#include +#include +#include +#include +#include + +*/ +import "C" + +import ( + "fmt" + "os" + + "github.com/elastic/beats/v7/metricbeat/internal/metrics" +) + +var system struct { + ticks uint64 + btime uint64 + pagesize uint64 +} + +func init() { + // sysconf(_SC_CLK_TCK) returns the number of ticks by second. + system.ticks = uint64(C.sysconf(C._SC_CLK_TCK)) + system.pagesize = uint64(os.Getpagesize()) +} + +func get(_ string) (Memory, error) { + memData := Memory{} + meminfo := C.perfstat_memory_total_t{} + _, err := C.perfstat_memory_total(nil, &meminfo, C.sizeof_perfstat_memory_total_t, 1) + if err != nil { + return memData, fmt.Errorf("perfstat_memory_total: %s", err) + } + + totalMem := uint64(meminfo.real_total) * system.pagesize + freeMem := uint64(meminfo.real_free) * system.pagesize + + memData.Total = metrics.OptUintWith(totalMem) + memData.Free = metrics.OptUintWith(freeMem) + + kern := uint64(meminfo.numperm) * system.pagesize // number of pages in file cache + + memData.Used.Bytes = metrics.OptUintWith(totalMem - freeMem) + memData.Actual.Free = metrics.OptUintWith(freeMem + kern) + memData.Actual.Used.Bytes = metrics.OptUintWith(memData.Used.Bytes.ValueOr(0) - kern) + + return memData, nil +} diff --git a/metricbeat/internal/metrics/memory/memory_darwin.go b/metricbeat/internal/metrics/memory/memory_darwin.go new file mode 100644 index 00000000000..7f9d08e0a8c --- /dev/null +++ b/metricbeat/internal/metrics/memory/memory_darwin.go @@ -0,0 +1,133 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package memory + +/* +#include +#include +#include +#include +#include +#include +#include +#include +#include +*/ +import "C" + +import ( + "bytes" + "encoding/binary" + "fmt" + "syscall" + "unsafe" + + "github.com/pkg/errors" + + "github.com/elastic/beats/v7/metricbeat/internal/metrics" +) + +type xswUsage struct { + Total, Avail, Used uint64 +} + +// get is the darwin implementation for fetching Memory data +func get(_ string) (Memory, error) { + var vmstat C.vm_statistics_data_t + + mem := Memory{} + + var total uint64 + + if err := sysctlbyname("hw.memsize", &total); err != nil { + return Memory{}, errors.Wrap(err, "error getting memsize") + } + mem.Total = metrics.OptUintWith(total) + + if err := vmInfo(&vmstat); err != nil { + return Memory{}, errors.Wrap(err, "error getting VM info") + } + + kern := uint64(vmstat.inactive_count) << 12 + free := uint64(vmstat.free_count) << 12 + + mem.Free = metrics.OptUintWith(free) + mem.Used.Bytes = metrics.OptUintWith(total - free) + + mem.Actual.Free = metrics.OptUintWith(free + kern) + mem.Actual.Used.Bytes = metrics.OptUintWith((total - free) - kern) + + var err error + mem.Swap, err = getSwap() + if err != nil { + return mem, errors.Wrap(err, "error getting swap memory") + } + + return mem, nil +} + +// Get fetches swap data +func getSwap() (SwapMetrics, error) { + swUsage := xswUsage{} + + swap := SwapMetrics{} + if err := sysctlbyname("vm.swapusage", &swUsage); err != nil { + return swap, errors.Wrap(err, "error getting swap usage") + } + + swap.Total = metrics.OptUintWith(swUsage.Total) + swap.Used.Bytes = metrics.OptUintWith(swUsage.Used) + swap.Free = metrics.OptUintWith(swUsage.Avail) + + return swap, nil +} + +// generic Sysctl buffer unmarshalling +func sysctlbyname(name string, data interface{}) (err error) { + val, err := syscall.Sysctl(name) + if err != nil { + return err + } + + buf := []byte(val) + + switch v := data.(type) { + case *uint64: + *v = *(*uint64)(unsafe.Pointer(&buf[0])) + return + } + + bbuf := bytes.NewBuffer([]byte(val)) + return binary.Read(bbuf, binary.LittleEndian, data) +} + +func vmInfo(vmstat *C.vm_statistics_data_t) error { + var count C.mach_msg_type_number_t = C.HOST_VM_INFO_COUNT + + status := C.host_statistics( + C.host_t(C.mach_host_self()), + C.HOST_VM_INFO, + C.host_info_t(unsafe.Pointer(vmstat)), + &count) + + if status != C.KERN_SUCCESS { + return fmt.Errorf("host_statistics=%d", status) + } + + return nil +} diff --git a/metricbeat/internal/metrics/memory/memory_freebsd.go b/metricbeat/internal/metrics/memory/memory_freebsd.go new file mode 100644 index 00000000000..13c1ebb0748 --- /dev/null +++ b/metricbeat/internal/metrics/memory/memory_freebsd.go @@ -0,0 +1,90 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package memory + +import ( + "unsafe" + + "github.com/pkg/errors" + + "github.com/elastic/beats/v7/metricbeat/internal/metrics" +) + +/* +#include +#include +#include +#include +#include +#include +#include +#include +#include +*/ +import "C" + +func get(_ string) (Memory, error) { + val := C.uint32_t(0) + sc := C.size_t(4) + + memData := Memory{} + + name := C.CString("vm.stats.vm.v_page_count") + _, err := C.sysctlbyname(name, unsafe.Pointer(&val), &sc, nil, 0) + C.free(unsafe.Pointer(name)) + if err != nil { + return memData, errors.Errorf("error in vm.stats.vm.v_page_count") + } + pagecount := uint64(val) + + name = C.CString("vm.stats.vm.v_page_size") + _, err = C.sysctlbyname(name, unsafe.Pointer(&val), &sc, nil, 0) + C.free(unsafe.Pointer(name)) + if err != nil { + return memData, errors.Errorf("error in vm.stats.vm.v_page_size") + } + pagesize := uint64(val) + + name = C.CString("vm.stats.vm.v_free_count") + _, err = C.sysctlbyname(name, unsafe.Pointer(&val), &sc, nil, 0) + C.free(unsafe.Pointer(name)) + if err != nil { + return memData, errors.Errorf("error in vm.stats.vm.v_free_count") + } + + memFree := uint64(val) * pagesize + memData.Free = metrics.OptUintWith(memFree) + + name = C.CString("vm.stats.vm.v_inactive_count") + _, err = C.sysctlbyname(name, unsafe.Pointer(&val), &sc, nil, 0) + C.free(unsafe.Pointer(name)) + if err != nil { + return memData, errors.Errorf("error in vm.stats.vm.v_inactive_count") + } + kern := uint64(val) + + memTotal := uint64(pagecount * pagesize) + + memData.Total = metrics.OptUintWith(memTotal) + + memData.Used.Bytes = metrics.OptUintWith(memTotal - memFree) + memData.Actual.Free = metrics.OptUintWith(memFree + (kern * pagesize)) + memData.Actual.Used.Bytes = metrics.OptUintWith((memTotal - memFree) - (kern * pagesize)) + + return memData, nil +} diff --git a/metricbeat/internal/metrics/memory/memory_linux.go b/metricbeat/internal/metrics/memory/memory_linux.go new file mode 100644 index 00000000000..dc563e1b47d --- /dev/null +++ b/metricbeat/internal/metrics/memory/memory_linux.go @@ -0,0 +1,143 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package memory + +import ( + "bufio" + "bytes" + "io" + "io/ioutil" + "path/filepath" + "strconv" + "strings" + + "github.com/pkg/errors" + + "github.com/elastic/beats/v7/metricbeat/internal/metrics" +) + +// get is the linux implementation for fetching Memory data +func get(rootfs string) (Memory, error) { + table, err := ParseMeminfo(rootfs) + if err != nil { + return Memory{}, errors.Wrap(err, "error fetching meminfo") + } + + memData := Memory{} + + var free, cached uint64 + if total, ok := table["MemTotal"]; ok { + memData.Total = metrics.OptUintWith(total) + } + if free, ok := table["MemFree"]; ok { + memData.Free = metrics.OptUintWith(free) + } + if cached, ok := table["Cached"]; ok { + memData.Cached = metrics.OptUintWith(cached) + } + + // overlook parsing issues here + // On the very small chance some of these don't exist, + // It's not the end of the world + buffers, _ := table["Buffers"] + + if memAvail, ok := table["MemAvailable"]; ok { + // MemAvailable is in /proc/meminfo (kernel 3.14+) + memData.Actual.Free = metrics.OptUintWith(memAvail) + } else { + // in the future we may want to find another way to do this. + // "MemAvailable" and other more derivied metrics + // Are very relative, and can be unhelpful in cerntain workloads + // We may want to find a way to more clearly express to users + // where a certain value is coming from and what it represents + + // The use of `cached` here is particularly concerning, + // as under certain intense DB server workloads, the cached memory can be quite large + // and give the impression that we've passed memory usage watermark + memData.Actual.Free = metrics.OptUintWith(free + buffers + cached) + } + + memData.Used.Bytes = metrics.OptUintWith(memData.Total.ValueOr(0) - memData.Free.ValueOr(0)) + memData.Actual.Used.Bytes = metrics.OptUintWith(memData.Total.ValueOr(0) - memData.Actual.Free.ValueOr(0)) + + // Populate swap data + swapTotal, okST := table["SwapTotal"] + if okST { + memData.Swap.Total = metrics.OptUintWith(swapTotal) + } + swapFree, okSF := table["SwapFree"] + if okSF { + memData.Swap.Free = metrics.OptUintWith(swapFree) + } + + if okSF && okST { + memData.Swap.Used.Bytes = metrics.OptUintWith(swapTotal - swapFree) + } + + return memData, nil + +} + +// ParseMeminfo parses the contents of /proc/meminfo into a hashmap +func ParseMeminfo(rootfs string) (map[string]uint64, error) { + table := map[string]uint64{} + + meminfoPath := filepath.Join(rootfs, "/proc/meminfo") + err := readFile(meminfoPath, func(line string) bool { + fields := strings.Split(line, ":") + + if len(fields) != 2 { + return true // skip on errors + } + + valueUnit := strings.Fields(fields[1]) + value, err := strconv.ParseUint(valueUnit[0], 10, 64) + if err != nil { + return true // skip on errors + } + + if len(valueUnit) > 1 && valueUnit[1] == "kB" { + value *= 1024 + } + table[fields[0]] = value + + return true + }) + return table, err +} + +func readFile(file string, handler func(string) bool) error { + contents, err := ioutil.ReadFile(file) + if err != nil { + return errors.Wrapf(err, "error reading file %s", file) + } + + reader := bufio.NewReader(bytes.NewBuffer(contents)) + + for { + line, _, err := reader.ReadLine() + if err == io.EOF { + break + } + if !handler(string(line)) { + break + } + } + + return nil +} diff --git a/metricbeat/internal/metrics/memory/memory_openbsd.go b/metricbeat/internal/metrics/memory/memory_openbsd.go new file mode 100644 index 00000000000..51e6ef830e3 --- /dev/null +++ b/metricbeat/internal/metrics/memory/memory_openbsd.go @@ -0,0 +1,236 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package memory + +/* +#include +#include +#include +#include +#include +#include +#include +#include +*/ +import "C" + +import ( + "syscall" + "unsafe" + + "github.com/pkg/errors" + + "github.com/elastic/beats/v7/metricbeat/internal/metrics" +) + +// Uvmexp wraps memory data from sysctl +type Uvmexp struct { + pagesize uint32 + pagemask uint32 + pageshift uint32 + npages uint32 + free uint32 + active uint32 + inactive uint32 + paging uint32 + wired uint32 + zeropages uint32 + reserve_pagedaemon uint32 + reserve_kernel uint32 + anonpages uint32 + vnodepages uint32 + vtextpages uint32 + freemin uint32 + freetarg uint32 + inactarg uint32 + wiredmax uint32 + anonmin uint32 + vtextmin uint32 + vnodemin uint32 + anonminpct uint32 + vtextmi uint32 + npct uint32 + vnodeminpct uint32 + nswapdev uint32 + swpages uint32 + swpginuse uint32 + swpgonly uint32 + nswget uint32 + nanon uint32 + nanonneeded uint32 + nfreeanon uint32 + faults uint32 + traps uint32 + intrs uint32 + swtch uint32 + softs uint32 + syscalls uint32 + pageins uint32 + obsolete_swapins uint32 + obsolete_swapouts uint32 + pgswapin uint32 + pgswapout uint32 + forks uint32 + forks_ppwait uint32 + forks_sharevm uint32 + pga_zerohit uint32 + pga_zeromiss uint32 + zeroaborts uint32 + fltnoram uint32 + fltnoanon uint32 + fltpgwait uint32 + fltpgrele uint32 + fltrelck uint32 + fltrelckok uint32 + fltanget uint32 + fltanretry uint32 + fltamcopy uint32 + fltnamap uint32 + fltnomap uint32 + fltlget uint32 + fltget uint32 + flt_anon uint32 + flt_acow uint32 + flt_obj uint32 + flt_prcopy uint32 + flt_przero uint32 + pdwoke uint32 + pdrevs uint32 + pdswout uint32 + pdfreed uint32 + pdscans uint32 + pdanscan uint32 + pdobscan uint32 + pdreact uint32 + pdbusy uint32 + pdpageouts uint32 + pdpending uint32 + pddeact uint32 + pdreanon uint32 + pdrevnode uint32 + pdrevtext uint32 + fpswtch uint32 + kmapent uint32 +} + +// Bcachestats reports cache stats from sysctl +type Bcachestats struct { + numbufs uint64 + numbufpages uint64 + numdirtypages uint64 + numcleanpages uint64 + pendingwrites uint64 + pendingreads uint64 + numwrites uint64 + numreads uint64 + cachehits uint64 + busymapped uint64 + dmapages uint64 + highpages uint64 + delwribufs uint64 + kvaslots uint64 + kvaslots_avail uint64 +} + +// Swapent reports swap metrics from sysctl +type Swapent struct { + se_dev C.dev_t + se_flags int32 + se_nblks int32 + se_inuse int32 + se_priority int32 + sw_path []byte +} + +func get(_ string) (Memory, error) { + + memData := Memory{} + + n := uintptr(0) + var uvmexp Uvmexp + mib := [2]int32{C.CTL_VM, C.VM_UVMEXP} + n = uintptr(0) + // First we determine how much memory we'll need to pass later on (via `n`) + _, _, errno := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, 0, uintptr(unsafe.Pointer(&n)), 0, 0) + if errno != 0 || n == 0 { + return memData, errors.Errorf("Error in size VM_UVMEXP sysctl call, errno %d", errno) + } + + _, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 2, uintptr(unsafe.Pointer(&uvmexp)), uintptr(unsafe.Pointer(&n)), 0, 0) + if errno != 0 || n == 0 { + return memData, errors.Errorf("Error in VM_UVMEXP sysctl call, errno %d", errno) + } + + var bcachestats Bcachestats + mib3 := [3]int32{C.CTL_VFS, C.VFS_GENERIC, C.VFS_BCACHESTAT} + n = uintptr(0) + _, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib3[0])), 3, 0, uintptr(unsafe.Pointer(&n)), 0, 0) + if errno != 0 || n == 0 { + return memData, errors.Errorf("Error in size VFS_BCACHESTAT sysctl call, errno %d", errno) + } + _, _, errno = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib3[0])), 3, uintptr(unsafe.Pointer(&bcachestats)), uintptr(unsafe.Pointer(&n)), 0, 0) + if errno != 0 || n == 0 { + return memData, errors.Errorf("Error in VFS_BCACHESTAT sysctl call, errno %d", errno) + } + + memFree := uint64(uvmexp.free) << uvmexp.pageshift + memUsed := uint64(uvmexp.npages-uvmexp.free) << uvmexp.pageshift + + memData.Total = metrics.OptUintWith(uint64(uvmexp.npages) << uvmexp.pageshift) + memData.Used.Bytes = metrics.OptUintWith(memUsed) + memData.Free = metrics.OptUintWith(memFree) + + memData.Actual.Free = metrics.OptUintWith(memFree + (uint64(bcachestats.numbufpages) << uvmexp.pageshift)) + memData.Actual.Used.Bytes = metrics.OptUintWith(memUsed - (uint64(bcachestats.numbufpages) << uvmexp.pageshift)) + + var err error + memData.Swap, err = getSwap() + if err != nil { + return memData, errors.Wrap(err, "error getting swap data") + } + + return memData, nil +} + +func getSwap() (SwapMetrics, error) { + swapData := SwapMetrics{} + nswap := C.swapctl(C.SWAP_NSWAP, unsafe.Pointer(uintptr(0)), 0) + + // If there are no swap devices, nothing to do here. + if nswap == 0 { + return swapData, nil + } + + swdev := make([]Swapent, nswap) + + rnswap := C.swapctl(C.SWAP_STATS, unsafe.Pointer(&swdev[0]), nswap) + if rnswap == 0 { + return swapData, errors.Errorf("error in SWAP_STATS sysctl, swapctl returned %d", rnswap) + } + + for i := 0; i < int(nswap); i++ { + if swdev[i].se_flags&C.SWF_ENABLE == 2 { + swapData.Used.Bytes = metrics.OptUintWith(swapData.Used.Bytes.ValueOr(0) + uint64(swdev[i].se_inuse/(1024/C.DEV_BSIZE))) + swapData.Total = metrics.OptUintWith(swapData.Total.ValueOr(0) + uint64(swdev[i].se_nblks/(1024/C.DEV_BSIZE))) + } + } + + swapData.Free = metrics.OptUintWith(swapData.Total.ValueOr(0) - swapData.Used.Bytes.ValueOr(0)) + + return swapData, nil +} diff --git a/metricbeat/internal/metrics/memory/memory_test.go b/metricbeat/internal/metrics/memory/memory_test.go new file mode 100644 index 00000000000..5b7908290ec --- /dev/null +++ b/metricbeat/internal/metrics/memory/memory_test.go @@ -0,0 +1,102 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build !integration +// +build darwin freebsd linux openbsd windows + +package memory + +import ( + "runtime" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/elastic/beats/v7/metricbeat/internal/metrics" +) + +func TestGetMemory(t *testing.T) { + mem, err := Get("") + + assert.NotNil(t, mem) + assert.NoError(t, err) + + assert.True(t, mem.Total.Exists()) + assert.True(t, (mem.Total.ValueOr(0) > 0)) + + assert.True(t, mem.Used.Bytes.Exists()) + assert.True(t, (mem.Used.Bytes.ValueOr(0) > 0)) + + assert.True(t, mem.Free.Exists()) + assert.True(t, (mem.Free.ValueOr(0) >= 0)) + + assert.True(t, mem.Actual.Free.Exists()) + assert.True(t, (mem.Actual.Free.ValueOr(0) >= 0)) + + assert.True(t, mem.Actual.Used.Bytes.Exists()) + assert.True(t, (mem.Actual.Used.Bytes.ValueOr(0) > 0)) +} + +func TestGetSwap(t *testing.T) { + if runtime.GOOS == "freebsd" { + return //no load data on freebsd + } + + mem, err := Get("") + + assert.NotNil(t, mem) + assert.NoError(t, err) + + assert.True(t, mem.Swap.Total.Exists()) + assert.True(t, (mem.Swap.Total.ValueOr(0) >= 0)) + + assert.True(t, mem.Swap.Used.Bytes.Exists()) + assert.True(t, (mem.Swap.Used.Bytes.ValueOr(0) >= 0)) + + assert.True(t, mem.Swap.Free.Exists()) + assert.True(t, (mem.Swap.Free.ValueOr(0) >= 0)) +} + +func TestMemPercentage(t *testing.T) { + m := Memory{ + Total: metrics.OptUintWith(7), + Used: UsedMemStats{Bytes: metrics.OptUintWith(5)}, + Free: metrics.OptUintWith(2), + } + m.fillPercentages() + assert.Equal(t, m.Used.Pct.ValueOr(0), 0.7143) + + m = Memory{ + Total: metrics.OptUintWith(0), + } + m.fillPercentages() + assert.Equal(t, m.Used.Pct.ValueOr(0), 0.0) +} + +func TestActualMemPercentage(t *testing.T) { + m := Memory{ + Total: metrics.OptUintWith(7), + Actual: ActualMemoryMetrics{ + Used: UsedMemStats{Bytes: metrics.OptUintWith(5)}, + Free: metrics.OptUintWith(2), + }, + } + + m.fillPercentages() + assert.Equal(t, m.Actual.Used.Pct.ValueOr(0), 0.7143) + +} diff --git a/metricbeat/internal/metrics/memory/memory_windows.go b/metricbeat/internal/metrics/memory/memory_windows.go new file mode 100644 index 00000000000..b19bf977091 --- /dev/null +++ b/metricbeat/internal/metrics/memory/memory_windows.go @@ -0,0 +1,51 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package memory + +import ( + "github.com/pkg/errors" + + "github.com/elastic/beats/v7/metricbeat/internal/metrics" + "github.com/elastic/go-windows" +) + +// get is the windows implementation of get for memory metrics +func get(_ string) (Memory, error) { + + memData := Memory{} + + memoryStatusEx, err := windows.GlobalMemoryStatusEx() + if err != nil { + return memData, errors.Wrap(err, "Error fetching global memory status") + } + memData.Total = metrics.OptUintWith(memoryStatusEx.TotalPhys) + memData.Free = metrics.OptUintWith(memoryStatusEx.AvailPhys) + + memData.Used.Bytes = metrics.OptUintWith(memoryStatusEx.TotalPhys - memoryStatusEx.AvailPhys) + + // We shouldn't really be doing this, but we also don't want to make breaking changes right now, + // and memory.actual is used by quite a few visualizations + memData.Actual.Free = memData.Free + memData.Actual.Used.Bytes = memData.Used.Bytes + + memData.Swap.Free = metrics.OptUintWith(memoryStatusEx.AvailPageFile) + memData.Swap.Total = metrics.OptUintWith(memoryStatusEx.TotalPageFile) + memData.Swap.Used.Bytes = metrics.OptUintWith(memoryStatusEx.TotalPageFile - memoryStatusEx.AvailPageFile) + + return memData, nil +} diff --git a/metricbeat/internal/metrics/opt.go b/metricbeat/internal/metrics/opt.go index f2cdfb0f05c..a5f28975252 100644 --- a/metricbeat/internal/metrics/opt.go +++ b/metricbeat/internal/metrics/opt.go @@ -17,6 +17,10 @@ package metrics +import "github.com/elastic/go-structform" + +// Uint + // OptUint is a wrapper for "optional" types, with the bool value indicating // if the stored int is a legitimate value. type OptUint struct { @@ -24,8 +28,8 @@ type OptUint struct { value uint64 } -// NewNone returns a new OptUint wrapper -func NewNone() OptUint { +// NewUintNone returns a new OptUint wrapper +func NewUintNone() OptUint { return OptUint{ exists: false, value: 0, @@ -45,6 +49,11 @@ func (opt OptUint) IsZero() bool { return !opt.exists } +// Exists returns true if the underlying value exists +func (opt OptUint) Exists() bool { + return opt.exists +} + // ValueOr returns the stored value, or a given int // Please do not use this for populating reported data, // as we actually want to avoid sending zeros where values are functionally null @@ -63,3 +72,70 @@ func SumOptUint(opts ...OptUint) uint64 { } return sum } + +// Fold implements the folder interface for OptUint +func (in *OptUint) Fold(v structform.ExtVisitor) error { + if in.exists { + value := in.value + v.OnUint64(value) + } else { + v.OnNil() + } + return nil +} + +// Float + +// OptFloat is a wrapper for "optional" types, with the bool value indicating +// if the stored int is a legitimate value. +type OptFloat struct { + exists bool + value float64 +} + +// NewFloatNone returns a new uint wrapper +func NewFloatNone() OptFloat { + return OptFloat{ + exists: false, + value: 0, + } +} + +// OptFloatWith returns a new uint wrapper for the specified value +func OptFloatWith(f float64) OptFloat { + return OptFloat{ + exists: true, + value: f, + } +} + +// IsZero returns true if the underlying value nil +func (opt OptFloat) IsZero() bool { + return !opt.exists +} + +// Exists returns true if the underlying value exists +func (opt OptFloat) Exists() bool { + return opt.exists +} + +// ValueOr returns the stored value, or zero +// Please do not use this for populating reported data, +// as we actually want to avoid sending zeros where values are functionally null +func (opt OptFloat) ValueOr(f float64) float64 { + if opt.exists { + return opt.value + } + return f +} + +// Fold implements the folder interface for OptUint +func (in *OptFloat) Fold(v structform.ExtVisitor) error { + if in.exists { + value := in.value + v.OnFloat64(value) + } else { + v.OnNil() + } + return nil +} diff --git a/metricbeat/module/linux/memory/_meta/data.json b/metricbeat/module/linux/memory/_meta/data.json index 79a3d431869..cb4acfe5da0 100644 --- a/metricbeat/module/linux/memory/_meta/data.json +++ b/metricbeat/module/linux/memory/_meta/data.json @@ -26,25 +26,25 @@ }, "page_stats": { "direct_efficiency": { - "pct": 0.9228 + "pct": 0.9839 }, "kswapd_efficiency": { - "pct": 0.7523 + "pct": 0.7739 }, "pgfree": { - "pages": 16061818710 + "pages": 40941189636 }, "pgscan_direct": { - "pages": 1198580 + "pages": 1199988 }, "pgscan_kswapd": { - "pages": 50222460 + "pages": 19970993 }, "pgsteal_direct": { - "pages": 1106083 + "pages": 1180686 }, "pgsteal_kswapd": { - "pages": 37782783 + "pages": 15456470 } } } diff --git a/metricbeat/module/linux/memory/data.go b/metricbeat/module/linux/memory/data.go index d3f1a5ef2f8..eb4706e02f0 100644 --- a/metricbeat/module/linux/memory/data.go +++ b/metricbeat/module/linux/memory/data.go @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -// +build darwin freebsd linux openbsd windows +// +build linux package memory @@ -23,77 +23,128 @@ import ( "github.com/pkg/errors" "github.com/elastic/beats/v7/libbeat/common" - mem "github.com/elastic/beats/v7/libbeat/metric/system/memory" + "github.com/elastic/beats/v7/metricbeat/internal/metrics/memory" + sysinfo "github.com/elastic/go-sysinfo" + sysinfotypes "github.com/elastic/go-sysinfo/types" ) // FetchLinuxMemStats gets page_stat and huge pages data for linux func FetchLinuxMemStats(baseMap common.MapStr) error { - vmstat, err := mem.GetVMStat() + vmstat, err := GetVMStat() if err != nil { - return errors.Wrap(err, "VMStat") + return errors.Wrap(err, "error fetching VMStats") } - if vmstat != nil { - pageStats := common.MapStr{ - "pgscan_kswapd": common.MapStr{ - "pages": vmstat.PgscanKswapd, - }, - "pgscan_direct": common.MapStr{ - "pages": vmstat.PgscanDirect, - }, - "pgfree": common.MapStr{ - "pages": vmstat.Pgfree, - }, - "pgsteal_kswapd": common.MapStr{ - "pages": vmstat.PgstealKswapd, - }, - "pgsteal_direct": common.MapStr{ - "pages": vmstat.PgstealDirect, - }, - } - // This is similar to the vmeff stat gathered by sar - // these ratios calculate thhe efficiency of page reclaim - if vmstat.PgscanDirect != 0 { - pageStats["direct_efficiency"] = common.MapStr{ - "pct": common.Round(float64(vmstat.PgstealDirect)/float64(vmstat.PgscanDirect), common.DefaultDecimalPlacesCount), - } + pageStats := common.MapStr{ + "pgscan_kswapd": common.MapStr{ + "pages": vmstat.PgscanKswapd, + }, + "pgscan_direct": common.MapStr{ + "pages": vmstat.PgscanDirect, + }, + "pgfree": common.MapStr{ + "pages": vmstat.Pgfree, + }, + "pgsteal_kswapd": common.MapStr{ + "pages": vmstat.PgstealKswapd, + }, + "pgsteal_direct": common.MapStr{ + "pages": vmstat.PgstealDirect, + }, + } + // This is similar to the vmeff stat gathered by sar + // these ratios calculate thhe efficiency of page reclaim + if vmstat.PgscanDirect != 0 { + pageStats["direct_efficiency"] = common.MapStr{ + "pct": common.Round(float64(vmstat.PgstealDirect)/float64(vmstat.PgscanDirect), common.DefaultDecimalPlacesCount), } + } - if vmstat.PgscanKswapd != 0 { - pageStats["kswapd_efficiency"] = common.MapStr{ - "pct": common.Round(float64(vmstat.PgstealKswapd)/float64(vmstat.PgscanKswapd), common.DefaultDecimalPlacesCount), - } + if vmstat.PgscanKswapd != 0 { + pageStats["kswapd_efficiency"] = common.MapStr{ + "pct": common.Round(float64(vmstat.PgstealKswapd)/float64(vmstat.PgscanKswapd), common.DefaultDecimalPlacesCount), } - baseMap["page_stats"] = pageStats + } + baseMap["page_stats"] = pageStats + + thp, err := getHugePages() + if err != nil { + return errors.Wrap(err, "error getting huge pages") + } + thp["swap"] = common.MapStr{ + "out": common.MapStr{ + "pages": vmstat.ThpSwpout, + "fallback": vmstat.ThpSwpoutFallback, + }, } - hugePagesStat, err := mem.GetHugeTLBPages() + baseMap["hugepages"] = thp + + return nil +} + +func getHugePages() (common.MapStr, error) { + // see https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt + table, err := memory.ParseMeminfo("") if err != nil { - return errors.Wrap(err, "hugepages") + return nil, errors.Wrap(err, "error parsing meminfo") } - if hugePagesStat != nil { - mem.AddHugeTLBPagesPercentage(hugePagesStat) - thp := common.MapStr{ - "total": hugePagesStat.Total, - "used": common.MapStr{ - "bytes": hugePagesStat.TotalAllocatedSize, - "pct": hugePagesStat.UsedPercent, - }, - "free": hugePagesStat.Free, - "reserved": hugePagesStat.Reserved, - "surplus": hugePagesStat.Surplus, - "default_size": hugePagesStat.DefaultSize, + thp := common.MapStr{} + + total, okTotal := table["HugePages_Total"] + free, okFree := table["HugePages_Free"] + reserved, okReserved := table["HugePages_Rsvd"] + totalSize, okTotalSize := table["Hugetlb"] + defaultSize, okDefaultSize := table["Hugepagesize"] + + // Calculate percentages + if okTotal && okFree && okReserved { + thp.Put("total", total) + thp.Put("free", free) + thp.Put("reserved", reserved) + + // TODO: this repliactes the behavior of metricbeat in the past, + // but it might be possilbe to do something like (HugePages_Total*Hugepagesize)-Hugetlb / (HugePages_Total*Hugepagesize) + var perc float64 + if total > 0 { + perc = float64(total-free+reserved) / float64(total) } - if vmstat != nil { - thp["swap"] = common.MapStr{ - "out": common.MapStr{ - "pages": vmstat.ThpSwpout, - "fallback": vmstat.ThpSwpoutFallback, - }, - } + thp.Put("used.pct", common.Round(perc, common.DefaultDecimalPlacesCount)) + + if !okTotalSize && okDefaultSize { + thp.Put("used.bytes", (total-free+reserved)*defaultSize) } - baseMap["hugepages"] = thp } - return nil + if okTotalSize { + thp.Put("used.bytes", totalSize) + } + if okDefaultSize { + thp.Put("default_size", defaultSize) + } + if surplus, ok := table["HugePages_Surp"]; ok { + thp.Put("surplus", surplus) + } + + return thp, nil +} + +// GetVMStat gets linux vmstat metrics +func GetVMStat() (*sysinfotypes.VMStatInfo, error) { + // TODO: We may want to pull this code out of go-sysinfo. + // It's platform specific, and not used by anything else. + h, err := sysinfo.Host() + if err != nil { + return nil, errors.Wrap(err, "failed to read self process information") + } + vmstatHandle, ok := h.(sysinfotypes.VMStat) + if !ok { + return nil, errors.Wrap(err, "VMStat not available for platform") + } + info, err := vmstatHandle.VMStat() + if err != nil { + return nil, errors.Wrap(err, "error getting VMStat info") + } + return info, nil + } diff --git a/libbeat/metric/system/memory/doc.go b/metricbeat/module/linux/memory/doc.go similarity index 100% rename from libbeat/metric/system/memory/doc.go rename to metricbeat/module/linux/memory/doc.go diff --git a/metricbeat/module/linux/memory/memory.go b/metricbeat/module/linux/memory/memory.go index 163e3bea771..13f080196ad 100644 --- a/metricbeat/module/linux/memory/memory.go +++ b/metricbeat/module/linux/memory/memory.go @@ -15,9 +15,13 @@ // specific language governing permissions and limitations // under the License. +// +build linux + package memory import ( + "github.com/pkg/errors" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/libbeat/common/cfgwarn" "github.com/elastic/beats/v7/metricbeat/mb" @@ -54,7 +58,10 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { // of an error set the Error field of mb.Event or simply call report.Error(). func (m *MetricSet) Fetch(report mb.ReporterV2) error { rootEvent := common.MapStr{} - FetchLinuxMemStats(rootEvent) + err := FetchLinuxMemStats(rootEvent) + if err != nil { + return errors.Wrap(err, "error fetching memory stats") + } report.Event(mb.Event{ MetricSetFields: rootEvent, }) diff --git a/metricbeat/module/linux/memory/memory_test.go b/metricbeat/module/linux/memory/memory_test.go index 2980c94841e..52b0bdcc7e1 100644 --- a/metricbeat/module/linux/memory/memory_test.go +++ b/metricbeat/module/linux/memory/memory_test.go @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -// +build darwin freebsd linux openbsd windows +// +build linux package memory diff --git a/metricbeat/module/system/fields.go b/metricbeat/module/system/fields.go index fdeeea5f9c6..ffde1aef0bc 100644 --- a/metricbeat/module/system/fields.go +++ b/metricbeat/module/system/fields.go @@ -32,5 +32,5 @@ func init() { // AssetSystem returns asset data. // This is the base64 encoded gzipped contents of module/system. func AssetSystem() string { - return "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" + return "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" } diff --git a/metricbeat/module/system/memory/_meta/data.json b/metricbeat/module/system/memory/_meta/data.json index 06abee40a35..ce7da4541cf 100644 --- a/metricbeat/module/system/memory/_meta/data.json +++ b/metricbeat/module/system/memory/_meta/data.json @@ -15,13 +15,14 @@ "system": { "memory": { "actual": { - "free": 8461623296, + "free": 46533455872, "used": { - "bytes": 7159164928, - "pct": 0.4583 + "bytes": 20981358592, + "pct": 0.3108 } }, - "free": 1299234816, + "cached": 42114609152, + "free": 3916599296, "hugepages": { "default_size": 2097152, "free": 0, @@ -41,49 +42,49 @@ }, "page_stats": { "direct_efficiency": { - "pct": 0.9242 + "pct": 0.9871 }, "kswapd_efficiency": { - "pct": 0.7518 + "pct": 0.7105 }, "pgfree": { - "pages": 15924304810 + "pages": 69780946234 }, "pgscan_direct": { - "pages": 1185751 + "pages": 1512375 }, "pgscan_kswapd": { - "pages": 50008148 + "pages": 25880646 }, "pgsteal_direct": { - "pages": 1095884 + "pages": 1492831 }, "pgsteal_kswapd": { - "pages": 37594071 + "pages": 18387096 } }, "swap": { - "free": 7823421440, + "free": 8560832512, "in": { - "pages": 2702 + "pages": 727 }, "out": { - "pages": 23582 + "pages": 11197 }, "readahead": { - "cached": 554, - "pages": 986 + "cached": 9, + "pages": 45 }, - "total": 7897870336, + "total": 8589930496, "used": { - "bytes": 74448896, - "pct": 0.0094 + "bytes": 29097984, + "pct": 0.0034 } }, - "total": 15620788224, + "total": 67514814464, "used": { - "bytes": 14321553408, - "pct": 0.9168 + "bytes": 63598215168, + "pct": 0.942 } } } diff --git a/metricbeat/module/system/memory/_meta/fields.yml b/metricbeat/module/system/memory/_meta/fields.yml index ab80bf3ba28..9a8a7714a5e 100644 --- a/metricbeat/module/system/memory/_meta/fields.yml +++ b/metricbeat/module/system/memory/_meta/fields.yml @@ -23,6 +23,12 @@ The total amount of free memory in bytes. This value does not include memory consumed by system caches and buffers (see system.memory.actual.free). + - name: cached + type: long + format: bytes + description: > + Total Cached memory on system. + - name: used.pct type: scaled_float format: percent diff --git a/metricbeat/module/system/memory/helper_linux.go b/metricbeat/module/system/memory/helper_linux.go new file mode 100644 index 00000000000..a0ba099afae --- /dev/null +++ b/metricbeat/module/system/memory/helper_linux.go @@ -0,0 +1,32 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package memory + +import ( + "github.com/elastic/beats/v7/libbeat/common" + linux "github.com/elastic/beats/v7/metricbeat/module/linux/memory" + sysinfotypes "github.com/elastic/go-sysinfo/types" +) + +func fetchLinuxMemStats(baseMap common.MapStr) error { + return linux.FetchLinuxMemStats(baseMap) +} + +func getVMStat() (*sysinfotypes.VMStatInfo, error) { + return linux.GetVMStat() +} diff --git a/metricbeat/module/system/memory/helper_other.go b/metricbeat/module/system/memory/helper_other.go new file mode 100644 index 00000000000..adbeba59aa6 --- /dev/null +++ b/metricbeat/module/system/memory/helper_other.go @@ -0,0 +1,38 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +// +build darwin freebsd aix openbsd windows + +package memory + +import ( + "errors" + + "github.com/elastic/beats/v7/libbeat/common" + sysinfotypes "github.com/elastic/go-sysinfo/types" +) + +// These whole helper files are a shim until we can make breaking changes and remove these +// data enrichers from the metricset, as they're linux-only. +// DEPRECATE: 8.0 +func fetchLinuxMemStats(baseMap common.MapStr) error { + return errors.New("MemStats is only available on Linux") +} + +func getVMStat() (*sysinfotypes.VMStatInfo, error) { + return nil, errors.New("VMStat is only available on Linux") +} diff --git a/metricbeat/module/system/memory/memory.go b/metricbeat/module/system/memory/memory.go index 26c6bea1867..54565231c53 100644 --- a/metricbeat/module/system/memory/memory.go +++ b/metricbeat/module/system/memory/memory.go @@ -21,14 +21,15 @@ package memory import ( "fmt" + "runtime" "github.com/pkg/errors" "github.com/elastic/beats/v7/libbeat/common" - mem "github.com/elastic/beats/v7/libbeat/metric/system/memory" + "github.com/elastic/beats/v7/libbeat/common/transform/typeconv" + metrics "github.com/elastic/beats/v7/metricbeat/internal/metrics/memory" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/mb/parse" - linux "github.com/elastic/beats/v7/metricbeat/module/linux/memory" "github.com/elastic/beats/v7/metricbeat/module/system" ) @@ -58,74 +59,34 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { // Fetch fetches memory metrics from the OS. func (m *MetricSet) Fetch(r mb.ReporterV2) error { - memStat, err := mem.Get() - if err != nil { - return errors.Wrap(err, "memory") - } - mem.AddMemPercentage(memStat) - swapStat, err := mem.GetSwap() + eventRaw, err := metrics.Get("") if err != nil { - return errors.Wrap(err, "swap") - } - mem.AddSwapPercentage(swapStat) - - memory := common.MapStr{ - "total": memStat.Total, - "used": common.MapStr{ - "bytes": memStat.Used, - "pct": memStat.UsedPercent, - }, - "free": memStat.Free, - "actual": common.MapStr{ - "free": memStat.ActualFree, - "used": common.MapStr{ - "pct": memStat.ActualUsedPercent, - "bytes": memStat.ActualUsed, - }, - }, + return errors.Wrap(err, "error fetching memory metrics") } - vmstat, err := mem.GetVMStat() - if err != nil { - return errors.Wrap(err, "VMStat") - } - - swap := common.MapStr{ - "total": swapStat.Total, - "used": common.MapStr{ - "bytes": swapStat.Used, - "pct": swapStat.UsedPercent, - }, - "free": swapStat.Free, - } - - if vmstat != nil { - // Swap in and swap out numbers - swap["in"] = common.MapStr{ - "pages": vmstat.Pswpin, - } - swap["out"] = common.MapStr{ - "pages": vmstat.Pswpout, - } - //Swap readahead - //See https://www.kernel.org/doc/ols/2007/ols2007v2-pages-273-284.pdf - swap["readahead"] = common.MapStr{ - "pages": vmstat.SwapRa, - "cached": vmstat.SwapRaHit, - } - } + memory := common.MapStr{} + err = typeconv.Convert(&memory, &eventRaw) // for backwards compatibility, only report if we're not in fleet mode - if !m.IsAgent { - err := linux.FetchLinuxMemStats(memory) + // This is entirely linux-specific data that should live in linux/memory. + // DEPRECATE: remove this for 8.0 + if !m.IsAgent && runtime.GOOS == "linux" { + err := fetchLinuxMemStats(memory) if err != nil { return errors.Wrap(err, "error getting page stats") } + vmstat, err := getVMStat() + if err != nil { + return errors.Wrap(err, "Error getting VMStat data") + } + // Swap in and swap out numbers + memory.Put("swap.in.pages", vmstat.Pswpin) + memory.Put("swap.out.pages", vmstat.Pswpout) + memory.Put("swap.readahead.pages", vmstat.SwapRa) + memory.Put("swap.readahead.cached", vmstat.SwapRaHit) } - memory["swap"] = swap - r.Event(mb.Event{ MetricSetFields: memory, }) diff --git a/metricbeat/module/system/memory/memory_test.go b/metricbeat/module/system/memory/memory_test.go index 978d2de8ae1..515bfcb26b8 100644 --- a/metricbeat/module/system/memory/memory_test.go +++ b/metricbeat/module/system/memory/memory_test.go @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -// +build darwin freebsd linux openbsd windows +// +build darwin freebsd linux openbsd windows aix package memory diff --git a/metricbeat/module/system/test_system.py b/metricbeat/module/system/test_system.py index b1c83db81eb..e4d69723cf1 100644 --- a/metricbeat/module/system/test_system.py +++ b/metricbeat/module/system/test_system.py @@ -67,8 +67,11 @@ SYSTEM_FSSTAT_FIELDS = ["count", "total_files", "total_size"] +SYSTEM_MEMORY_FIELDS_LINUX = ["swap", "actual.free", "free", "total", "cached", "used.bytes", "used.pct", "actual.used.bytes", + "actual.used.pct", "hugepages", "page_stats"] + SYSTEM_MEMORY_FIELDS = ["swap", "actual.free", "free", "total", "used.bytes", "used.pct", "actual.used.bytes", - "actual.used.pct", "hugepages", "page_stats"] + "actual.used.pct", "hugepages", "page_stats"] SYSTEM_NETWORK_FIELDS = ["name", "out.bytes", "in.bytes", "out.packets", "in.packets", "in.error", "out.error", "in.dropped", "out.dropped"] @@ -384,7 +387,13 @@ def test_memory(self): if not re.match("(?i)linux", sys.platform) and not "page_stats" in memory: # Ensure presence of page_stats only in Linux memory["page_stats"] = None - self.assertCountEqual(self.de_dot(SYSTEM_MEMORY_FIELDS), memory.keys()) + + if sys.platform.startswith("linux"): + self.assertCountEqual(self.de_dot( + SYSTEM_MEMORY_FIELDS_LINUX), memory.keys()) + else: + self.assertCountEqual(self.de_dot( + SYSTEM_MEMORY_FIELDS), memory.keys()) # Check that percentages are calculated. mem = memory