diff --git a/go.mod b/go.mod index d1281372c..6a85a47ac 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( codeberg.org/gruf/go-structr v0.1.1 codeberg.org/superseriousbusiness/exif-terminator v0.7.0 github.com/DmitriyVTitov/size v1.5.0 - github.com/KimMachineGun/automemlimit v0.4.0 + github.com/KimMachineGun/automemlimit v0.5.0 github.com/abema/go-mp4 v1.1.1 github.com/buckket/go-blurhash v1.1.0 github.com/coreos/go-oidc/v3 v3.9.0 @@ -143,6 +143,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/opencontainers/runtime-spec v1.0.2 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect diff --git a/go.sum b/go.sum index 4d18d23a2..d512e2b55 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DmitriyVTitov/size v1.5.0 h1:/PzqxYrOyOUX1BXj6J9OuVRVGe+66VL4D9FlUaW515g= github.com/DmitriyVTitov/size v1.5.0/go.mod h1:le6rNI4CoLQV1b9gzp1+3d7hMAD/uu2QcJ+aYbNgiU0= -github.com/KimMachineGun/automemlimit v0.4.0 h1:qOjSDbAUENEL6fiKmRKuAVhPaLijpoEHFDTE+I+prp0= -github.com/KimMachineGun/automemlimit v0.4.0/go.mod h1:pJhTW/nWJMj6SnWSU2TEKSlCaM+1N5Mej+IfS/5/Ol0= +github.com/KimMachineGun/automemlimit v0.5.0 h1:BeOe+BbJc8L5chL3OwzVYjVzyvPALdd5wxVVOWuUZmQ= +github.com/KimMachineGun/automemlimit v0.5.0/go.mod h1:di3GCKiu9Y+1fs92erCbUvKzPkNyViN3mA0vti/ykEQ= github.com/abema/go-mp4 v1.1.1 h1:OfzkdMO6SWTBR1ltNSVwlTHatrAK9I3iYLQfkdEMMuc= github.com/abema/go-mp4 v1.1.1/go.mod h1:vPl9t5ZK7K0x68jh12/+ECWBCXoWuIDtNgPtU2f04ws= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= @@ -416,6 +416,8 @@ github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNia github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e h1:s2RNOM/IGdY0Y6qfTeUKhDawdHDpK9RGBdx80qN4Ttw= github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e/go.mod h1:nBdnFKj15wFbf94Rwfq4m30eAcyY9V/IyKAGQFtqkW0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= diff --git a/vendor/github.com/KimMachineGun/automemlimit/README.md b/vendor/github.com/KimMachineGun/automemlimit/README.md index 192bc0b6f..c0e66d2a7 100644 --- a/vendor/github.com/KimMachineGun/automemlimit/README.md +++ b/vendor/github.com/KimMachineGun/automemlimit/README.md @@ -8,6 +8,14 @@ Automatically set `GOMEMLIMIT` to match Linux [cgroups(7)](https://man7.org/linu See more details about `GOMEMLIMIT` [here](https://tip.golang.org/doc/gc-guide#Memory_limit). +## Notice + +Version `v0.5.0` introduces a fallback to system memory limits as an experimental feature when cgroup limits are unavailable. Activate this by setting `AUTOMEMLIMIT_EXPERIMENT=system`. +You can also use system memory limits via `memlimit.FromSystem` provider directly. + +This feature is under evaluation and might become a default or be removed based on user feedback. +If you have any feedback about this feature, please open an issue. + ## Installation ```shell @@ -34,9 +42,17 @@ import "github.com/KimMachineGun/automemlimit/memlimit" func init() { memlimit.SetGoMemLimitWithOpts( memlimit.WithRatio(0.9), - memlimit.WithEnv(), memlimit.WithProvider(memlimit.FromCgroup), ) + memlimit.SetGoMemLimitWithOpts( + memlimit.WithRatio(0.9), + memlimit.WithProvider( + memlimit.ApplyFallback( + memlimit.FromCgroup, + memlimit.FromSystem, + ), + ), + ) memlimit.SetGoMemLimitWithEnv() memlimit.SetGoMemLimit(0.9) memlimit.SetGoMemLimitWithProvider(memlimit.Limit(1024*1024), 0.9) diff --git a/vendor/github.com/KimMachineGun/automemlimit/memlimit/cgroups.go b/vendor/github.com/KimMachineGun/automemlimit/memlimit/cgroups.go index cfe6d0f60..979bd3937 100644 --- a/vendor/github.com/KimMachineGun/automemlimit/memlimit/cgroups.go +++ b/vendor/github.com/KimMachineGun/automemlimit/memlimit/cgroups.go @@ -1,91 +1,12 @@ -//go:build linux -// +build linux - package memlimit import ( - "path/filepath" - - "github.com/containerd/cgroups/v3" - "github.com/containerd/cgroups/v3/cgroup1" - "github.com/containerd/cgroups/v3/cgroup2" + "errors" ) -const ( - cgroupMountPoint = "/sys/fs/cgroup" +var ( + // ErrNoCgroup is returned when the process is not in cgroup. + ErrNoCgroup = errors.New("process is not in cgroup") + // ErrCgroupsNotSupported is returned when the system does not support cgroups. + ErrCgroupsNotSupported = errors.New("cgroups is not supported on this system") ) - -// FromCgroup returns the memory limit based on the cgroups version on this system. -func FromCgroup() (uint64, error) { - switch cgroups.Mode() { - case cgroups.Legacy: - return FromCgroupV1() - case cgroups.Hybrid: - return FromCgroupHybrid() - case cgroups.Unified: - return FromCgroupV2() - } - return 0, ErrNoCgroup -} - -// FromCgroupV1 returns the memory limit from the cgroup v1. -func FromCgroupV1() (uint64, error) { - cg, err := cgroup1.Load(cgroup1.RootPath, cgroup1.WithHiearchy( - cgroup1.SingleSubsystem(cgroup1.Default, cgroup1.Memory), - )) - if err != nil { - return 0, err - } - - metrics, err := cg.Stat(cgroup1.IgnoreNotExist) - if err != nil { - return 0, err - } - - if limit := metrics.GetMemory().GetHierarchicalMemoryLimit(); limit != 0 { - return limit, nil - } - - return 0, ErrNoLimit -} - -// FromCgroupHybrid returns the memory limit from the cgroup v1 or v2. -// It checks the cgroup v2 first, and if it fails, it falls back to cgroup v1. -func FromCgroupHybrid() (uint64, error) { - limit, err := fromCgroupV2(filepath.Join(cgroupMountPoint, "unified")) - if err == nil { - return limit, nil - } else if err != ErrNoLimit { - return 0, err - } - - return FromCgroupV1() -} - -// FromCgroupV2 returns the memory limit from the cgroup v2. -func FromCgroupV2() (uint64, error) { - return fromCgroupV2(cgroupMountPoint) -} - -func fromCgroupV2(mountPoint string) (uint64, error) { - path, err := cgroup2.NestedGroupPath("") - if err != nil { - return 0, err - } - - m, err := cgroup2.Load(path, cgroup2.WithMountpoint(mountPoint)) - if err != nil { - return 0, err - } - - stats, err := m.Stat() - if err != nil { - return 0, err - } - - if limit := stats.GetMemory().GetUsageLimit(); limit != 0 { - return limit, nil - } - - return 0, ErrNoLimit -} diff --git a/vendor/github.com/KimMachineGun/automemlimit/memlimit/cgroups_linux.go b/vendor/github.com/KimMachineGun/automemlimit/memlimit/cgroups_linux.go new file mode 100644 index 000000000..98a954873 --- /dev/null +++ b/vendor/github.com/KimMachineGun/automemlimit/memlimit/cgroups_linux.go @@ -0,0 +1,98 @@ +//go:build linux +// +build linux + +package memlimit + +import ( + "math" + "os" + "path/filepath" + + "github.com/containerd/cgroups/v3" + "github.com/containerd/cgroups/v3/cgroup1" + "github.com/containerd/cgroups/v3/cgroup2" +) + +const ( + cgroupMountPoint = "/sys/fs/cgroup" +) + +// FromCgroup returns the memory limit based on the cgroups version on this system. +func FromCgroup() (uint64, error) { + switch cgroups.Mode() { + case cgroups.Legacy: + return FromCgroupV1() + case cgroups.Hybrid: + return FromCgroupHybrid() + case cgroups.Unified: + return FromCgroupV2() + } + return 0, ErrNoCgroup +} + +// FromCgroupV1 returns the memory limit from the cgroup v1. +func FromCgroupV1() (uint64, error) { + cg, err := cgroup1.Load(cgroup1.RootPath, cgroup1.WithHiearchy( + cgroup1.SingleSubsystem(cgroup1.Default, cgroup1.Memory), + )) + if err != nil { + return 0, err + } + + metrics, err := cg.Stat(cgroup1.IgnoreNotExist) + if err != nil { + return 0, err + } + + if limit := metrics.GetMemory().GetHierarchicalMemoryLimit(); limit != 0 && limit != getCgroupV1NoLimit() { + return limit, nil + } + + return 0, ErrNoLimit +} + +func getCgroupV1NoLimit() uint64 { + ps := uint64(os.Getpagesize()) + return math.MaxInt64 / ps * ps +} + +// FromCgroupHybrid returns the memory limit from the cgroup v1 or v2. +// It checks the cgroup v2 first, and if it fails, it falls back to cgroup v1. +func FromCgroupHybrid() (uint64, error) { + limit, err := fromCgroupV2(filepath.Join(cgroupMountPoint, "unified")) + if err == nil { + return limit, nil + } else if err != ErrNoLimit { + return 0, err + } + + return FromCgroupV1() +} + +// FromCgroupV2 returns the memory limit from the cgroup v2. +func FromCgroupV2() (uint64, error) { + return fromCgroupV2(cgroupMountPoint) +} + +func fromCgroupV2(mountPoint string) (uint64, error) { + path, err := cgroup2.NestedGroupPath("") + if err != nil { + return 0, err + } + + m, err := cgroup2.Load(path, cgroup2.WithMountpoint(mountPoint)) + if err != nil { + return 0, err + } + + stats, err := m.Stat() + if err != nil { + return 0, err + } + + if limit := stats.GetMemory().GetUsageLimit(); limit != 0 && limit != math.MaxUint64 { + return limit, nil + } + + return 0, ErrNoLimit +} diff --git a/vendor/github.com/KimMachineGun/automemlimit/memlimit/exp_system.go b/vendor/github.com/KimMachineGun/automemlimit/memlimit/exp_system.go new file mode 100644 index 000000000..dee95f520 --- /dev/null +++ b/vendor/github.com/KimMachineGun/automemlimit/memlimit/exp_system.go @@ -0,0 +1,14 @@ +package memlimit + +import ( + "github.com/pbnjay/memory" +) + +// FromSystem returns the total memory of the system. +func FromSystem() (uint64, error) { + limit := memory.TotalMemory() + if limit == 0 { + return 0, ErrNoLimit + } + return limit, nil +} diff --git a/vendor/github.com/KimMachineGun/automemlimit/memlimit/experiment.go b/vendor/github.com/KimMachineGun/automemlimit/memlimit/experiment.go new file mode 100644 index 000000000..2a7c320ed --- /dev/null +++ b/vendor/github.com/KimMachineGun/automemlimit/memlimit/experiment.go @@ -0,0 +1,59 @@ +package memlimit + +import ( + "fmt" + "os" + "reflect" + "strings" +) + +const ( + envAUTOMEMLIMIT_EXPERIMENT = "AUTOMEMLIMIT_EXPERIMENT" +) + +// Experiments is a set of experiment flags. +// It is used to enable experimental features. +// +// You can set the flags by setting the environment variable AUTOMEMLIMIT_EXPERIMENT. +// The value of the environment variable is a comma-separated list of experiment names. +// +// The following experiment names are known: +// +// - none: disable all experiments +// - system: enable fallback to system memory limit +type Experiments struct { + // System enables fallback to system memory limit. + System bool +} + +func parseExperiments() (Experiments, error) { + var exp Experiments + + // Create a map of known experiment names. + names := make(map[string]func(bool)) + rv := reflect.ValueOf(&exp).Elem() + rt := rv.Type() + for i := 0; i < rt.NumField(); i++ { + field := rv.Field(i) + names[strings.ToLower(rt.Field(i).Name)] = field.SetBool + } + + // Parse names. + for _, f := range strings.Split(os.Getenv(envAUTOMEMLIMIT_EXPERIMENT), ",") { + if f == "" { + continue + } + if f == "none" { + exp = Experiments{} + continue + } + val := true + set, ok := names[f] + if !ok { + return Experiments{}, fmt.Errorf("unknown AUTOMEMLIMIT_EXPERIMENT %s", f) + } + set(val) + } + + return exp, nil +} diff --git a/vendor/github.com/KimMachineGun/automemlimit/memlimit/memlimit.go b/vendor/github.com/KimMachineGun/automemlimit/memlimit/memlimit.go index dbb4d1c72..0d7a9853c 100644 --- a/vendor/github.com/KimMachineGun/automemlimit/memlimit/memlimit.go +++ b/vendor/github.com/KimMachineGun/automemlimit/memlimit/memlimit.go @@ -22,16 +22,11 @@ const ( var ( // ErrNoLimit is returned when the memory limit is not set. ErrNoLimit = errors.New("memory is not limited") - // ErrNoCgroup is returned when the process is not in cgroup. - ErrNoCgroup = errors.New("process is not in cgroup") - // ErrCgroupsNotSupported is returned when the system does not support cgroups. - ErrCgroupsNotSupported = errors.New("cgroups is not supported on this system") ) type config struct { logger *log.Logger ratio float64 - env bool provider Provider } @@ -50,10 +45,10 @@ func WithRatio(ratio float64) Option { // WithEnv configures whether to use environment variables. // // Default: false +// +// Deprecated: currently this does nothing. func WithEnv() Option { - return func(cfg *config) { - cfg.env = true - } + return func(cfg *config) {} } // WithProvider configures the provider. @@ -65,17 +60,24 @@ func WithProvider(provider Provider) Option { } } -// SetGoMemLimitWithOpts sets GOMEMLIMIT with options. +// SetGoMemLimitWithOpts sets GOMEMLIMIT with options and environment variables. +// +// You can configure how much memory of the cgroup's memory limit to set as GOMEMLIMIT +// through AUTOMEMLIMIT envrironment variable in the half-open range (0.0,1.0]. +// +// If AUTOMEMLIMIT is not set, it defaults to 0.9. (10% is the headroom for memory sources the Go runtime is unaware of.) +// If GOMEMLIMIT is already set or AUTOMEMLIMIT=off, this function does nothing. +// +// If AUTOMEMLIMIT_EXPERIMENT is set, it enables experimental features. +// Please see the documentation of Experiments for more details. // // Options: // - WithRatio -// - WithEnv (see more SetGoMemLimitWithEnv) // - WithProvider func SetGoMemLimitWithOpts(opts ...Option) (_ int64, _err error) { cfg := &config{ logger: log.New(io.Discard, "", log.LstdFlags), ratio: defaultAUTOMEMLIMIT, - env: false, provider: FromCgroup, } if os.Getenv(envAUTOMEMLIMIT_DEBUG) == "true" { @@ -90,6 +92,15 @@ func SetGoMemLimitWithOpts(opts ...Option) (_ int64, _err error) { } }() + exps, err := parseExperiments() + if err != nil { + return 0, fmt.Errorf("failed to parse experiments: %w", err) + } + if exps.System { + cfg.logger.Println("system experiment is enabled: using system memory limit as a fallback") + cfg.provider = ApplyFallback(cfg.provider, FromSystem) + } + snapshot := debug.SetMemoryLimit(-1) defer func() { err := recover() @@ -122,6 +133,10 @@ func SetGoMemLimitWithOpts(opts ...Option) (_ int64, _err error) { limit, err := setGoMemLimit(ApplyRatio(cfg.provider, ratio)) if err != nil { + if errors.Is(err, ErrNoLimit) { + cfg.logger.Printf("memory is not limited, skipping: %v\n", err) + return 0, nil + } return 0, fmt.Errorf("failed to set GOMEMLIMIT: %w", err) } @@ -130,14 +145,8 @@ func SetGoMemLimitWithOpts(opts ...Option) (_ int64, _err error) { return limit, nil } -// SetGoMemLimitWithEnv sets GOMEMLIMIT with the value from the environment variable. -// You can configure how much memory of the cgroup's memory limit to set as GOMEMLIMIT -// through AUTOMEMLIMIT in the half-open range (0.0,1.0]. -// -// If AUTOMEMLIMIT is not set, it defaults to 0.9. (10% is the headroom for memory sources the Go runtime is unaware of.) -// If GOMEMLIMIT is already set or AUTOMEMLIMIT=off, this function does nothing. func SetGoMemLimitWithEnv() { - _, _ = SetGoMemLimitWithOpts(WithEnv()) + _, _ = SetGoMemLimitWithOpts() } // SetGoMemLimit sets GOMEMLIMIT with the value from the cgroup's memory limit and given ratio. diff --git a/vendor/github.com/pbnjay/memory/LICENSE b/vendor/github.com/pbnjay/memory/LICENSE new file mode 100644 index 000000000..63ca4a6d2 --- /dev/null +++ b/vendor/github.com/pbnjay/memory/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2017, Jeremy Jay +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/pbnjay/memory/README.md b/vendor/github.com/pbnjay/memory/README.md new file mode 100644 index 000000000..e98f261a0 --- /dev/null +++ b/vendor/github.com/pbnjay/memory/README.md @@ -0,0 +1,41 @@ +# memory + +Package `memory` provides two methods reporting total physical system memory +accessible to the kernel, and free memory available to the running application. + +This package has no external dependency besides the standard library and default operating system tools. + +Documentation: +[![GoDoc](https://godoc.org/github.com/pbnjay/memory?status.svg)](https://godoc.org/github.com/pbnjay/memory) + +This is useful for dynamic code to minimize thrashing and other contention, similar to the stdlib `runtime.NumCPU` +See some history of the proposal at https://github.com/golang/go/issues/21816 + + +## Example + +```go +fmt.Printf("Total system memory: %d\n", memory.TotalMemory()) +fmt.Printf("Free memory: %d\n", memory.FreeMemory()) +``` + + +## Testing + +Tested/working on: + - macOS 10.12.6 (16G29), 10.15.7 (19H2) + - Windows 10 1511 (10586.1045) + - Linux RHEL (3.10.0-327.3.1.el7.x86_64) + - Raspberry Pi 3 (ARMv8) on Raspbian, ODROID-C1+ (ARMv7) on Ubuntu, C.H.I.P + (ARMv7). + - Amazon Linux 2 aarch64 (m6a.large, 4.14.203-156.332.amzn2.aarch64) + +Tested on virtual machines: + - Windows 7 SP1 386 + - Debian stretch 386 + - NetBSD 7.1 amd64 + 386 + - OpenBSD 6.1 amd64 + 386 + - FreeBSD 11.1 amd64 + 386 + - DragonFly BSD 4.8.1 amd64 + +If you have access to untested systems please test and report success/bugs. diff --git a/vendor/github.com/pbnjay/memory/doc.go b/vendor/github.com/pbnjay/memory/doc.go new file mode 100644 index 000000000..4e4f984c0 --- /dev/null +++ b/vendor/github.com/pbnjay/memory/doc.go @@ -0,0 +1,24 @@ +// Package memory provides a single method reporting total system memory +// accessible to the kernel. +package memory + +// TotalMemory returns the total accessible system memory in bytes. +// +// The total accessible memory is installed physical memory size minus reserved +// areas for the kernel and hardware, if such reservations are reported by +// the operating system. +// +// If accessible memory size could not be determined, then 0 is returned. +func TotalMemory() uint64 { + return sysTotalMemory() +} + +// FreeMemory returns the total free system memory in bytes. +// +// The total free memory is installed physical memory size minus reserved +// areas for other applications running on the same system. +// +// If free memory size could not be determined, then 0 is returned. +func FreeMemory() uint64 { + return sysFreeMemory() +} diff --git a/vendor/github.com/pbnjay/memory/memory_bsd.go b/vendor/github.com/pbnjay/memory/memory_bsd.go new file mode 100644 index 000000000..49d808a9e --- /dev/null +++ b/vendor/github.com/pbnjay/memory/memory_bsd.go @@ -0,0 +1,19 @@ +// +build freebsd openbsd dragonfly netbsd + +package memory + +func sysTotalMemory() uint64 { + s, err := sysctlUint64("hw.physmem") + if err != nil { + return 0 + } + return s +} + +func sysFreeMemory() uint64 { + s, err := sysctlUint64("hw.usermem") + if err != nil { + return 0 + } + return s +} diff --git a/vendor/github.com/pbnjay/memory/memory_darwin.go b/vendor/github.com/pbnjay/memory/memory_darwin.go new file mode 100644 index 000000000..a3f457699 --- /dev/null +++ b/vendor/github.com/pbnjay/memory/memory_darwin.go @@ -0,0 +1,49 @@ +// +build darwin + +package memory + +import ( + "os/exec" + "regexp" + "strconv" +) + +func sysTotalMemory() uint64 { + s, err := sysctlUint64("hw.memsize") + if err != nil { + return 0 + } + return s +} + +func sysFreeMemory() uint64 { + cmd := exec.Command("vm_stat") + outBytes, err := cmd.Output() + if err != nil { + return 0 + } + + rePageSize := regexp.MustCompile("page size of ([0-9]*) bytes") + reFreePages := regexp.MustCompile("Pages free: *([0-9]*)\\.") + + // default: page size of 4096 bytes + matches := rePageSize.FindSubmatchIndex(outBytes) + pageSize := uint64(4096) + if len(matches) == 4 { + pageSize, err = strconv.ParseUint(string(outBytes[matches[2]:matches[3]]), 10, 64) + if err != nil { + return 0 + } + } + + // ex: Pages free: 1126961. + matches = reFreePages.FindSubmatchIndex(outBytes) + freePages := uint64(0) + if len(matches) == 4 { + freePages, err = strconv.ParseUint(string(outBytes[matches[2]:matches[3]]), 10, 64) + if err != nil { + return 0 + } + } + return freePages * pageSize +} diff --git a/vendor/github.com/pbnjay/memory/memory_linux.go b/vendor/github.com/pbnjay/memory/memory_linux.go new file mode 100644 index 000000000..3d07711ce --- /dev/null +++ b/vendor/github.com/pbnjay/memory/memory_linux.go @@ -0,0 +1,29 @@ +// +build linux + +package memory + +import "syscall" + +func sysTotalMemory() uint64 { + in := &syscall.Sysinfo_t{} + err := syscall.Sysinfo(in) + if err != nil { + return 0 + } + // If this is a 32-bit system, then these fields are + // uint32 instead of uint64. + // So we always convert to uint64 to match signature. + return uint64(in.Totalram) * uint64(in.Unit) +} + +func sysFreeMemory() uint64 { + in := &syscall.Sysinfo_t{} + err := syscall.Sysinfo(in) + if err != nil { + return 0 + } + // If this is a 32-bit system, then these fields are + // uint32 instead of uint64. + // So we always convert to uint64 to match signature. + return uint64(in.Freeram) * uint64(in.Unit) +} diff --git a/vendor/github.com/pbnjay/memory/memory_windows.go b/vendor/github.com/pbnjay/memory/memory_windows.go new file mode 100644 index 000000000..c8500cc6f --- /dev/null +++ b/vendor/github.com/pbnjay/memory/memory_windows.go @@ -0,0 +1,60 @@ +// +build windows + +package memory + +import ( + "syscall" + "unsafe" +) + +// omitting a few fields for brevity... +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366589(v=vs.85).aspx +type memStatusEx struct { + dwLength uint32 + dwMemoryLoad uint32 + ullTotalPhys uint64 + ullAvailPhys uint64 + unused [5]uint64 +} + +func sysTotalMemory() uint64 { + kernel32, err := syscall.LoadDLL("kernel32.dll") + if err != nil { + return 0 + } + // GetPhysicallyInstalledSystemMemory is simpler, but broken on + // older versions of windows (and uses this under the hood anyway). + globalMemoryStatusEx, err := kernel32.FindProc("GlobalMemoryStatusEx") + if err != nil { + return 0 + } + msx := &memStatusEx{ + dwLength: 64, + } + r, _, _ := globalMemoryStatusEx.Call(uintptr(unsafe.Pointer(msx))) + if r == 0 { + return 0 + } + return msx.ullTotalPhys +} + +func sysFreeMemory() uint64 { + kernel32, err := syscall.LoadDLL("kernel32.dll") + if err != nil { + return 0 + } + // GetPhysicallyInstalledSystemMemory is simpler, but broken on + // older versions of windows (and uses this under the hood anyway). + globalMemoryStatusEx, err := kernel32.FindProc("GlobalMemoryStatusEx") + if err != nil { + return 0 + } + msx := &memStatusEx{ + dwLength: 64, + } + r, _, _ := globalMemoryStatusEx.Call(uintptr(unsafe.Pointer(msx))) + if r == 0 { + return 0 + } + return msx.ullAvailPhys +} diff --git a/vendor/github.com/pbnjay/memory/memsysctl.go b/vendor/github.com/pbnjay/memory/memsysctl.go new file mode 100644 index 000000000..438d9eff8 --- /dev/null +++ b/vendor/github.com/pbnjay/memory/memsysctl.go @@ -0,0 +1,21 @@ +// +build darwin freebsd openbsd dragonfly netbsd + +package memory + +import ( + "syscall" + "unsafe" +) + +func sysctlUint64(name string) (uint64, error) { + s, err := syscall.Sysctl(name) + if err != nil { + return 0, err + } + // hack because the string conversion above drops a \0 + b := []byte(s) + if len(b) < 8 { + b = append(b, 0) + } + return *(*uint64)(unsafe.Pointer(&b[0])), nil +} diff --git a/vendor/github.com/pbnjay/memory/stub.go b/vendor/github.com/pbnjay/memory/stub.go new file mode 100644 index 000000000..f29473ba0 --- /dev/null +++ b/vendor/github.com/pbnjay/memory/stub.go @@ -0,0 +1,10 @@ +// +build !linux,!darwin,!windows,!freebsd,!dragonfly,!netbsd,!openbsd + +package memory + +func sysTotalMemory() uint64 { + return 0 +} +func sysFreeMemory() uint64 { + return 0 +} diff --git a/vendor/modules.txt b/vendor/modules.txt index dc7cdc0f7..8ad21430c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -68,7 +68,7 @@ codeberg.org/superseriousbusiness/exif-terminator # github.com/DmitriyVTitov/size v1.5.0 ## explicit; go 1.14 github.com/DmitriyVTitov/size -# github.com/KimMachineGun/automemlimit v0.4.0 +# github.com/KimMachineGun/automemlimit v0.5.0 ## explicit; go 1.19 github.com/KimMachineGun/automemlimit github.com/KimMachineGun/automemlimit/memlimit @@ -418,6 +418,9 @@ github.com/oklog/ulid # github.com/opencontainers/runtime-spec v1.0.2 ## explicit github.com/opencontainers/runtime-spec/specs-go +# github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 +## explicit; go 1.16 +github.com/pbnjay/memory # github.com/pelletier/go-toml/v2 v2.1.1 ## explicit; go 1.16 github.com/pelletier/go-toml/v2