pax_global_header00006660000000000000000000000064140063104730014510gustar00rootroot0000000000000052 comment=1c8b231f48676bf2bbe1ec84e8b7551e65a2005e automaxprocs-1.4.0/000077500000000000000000000000001400631047300142375ustar00rootroot00000000000000automaxprocs-1.4.0/.build/000077500000000000000000000000001400631047300154145ustar00rootroot00000000000000automaxprocs-1.4.0/.build/check_license.sh000077500000000000000000000004461400631047300205360ustar00rootroot00000000000000#!/bin/bash -e ERROR_COUNT=0 while read -r file do case "$(head -1 "${file}")" in *"Copyright (c) "*" Uber Technologies, Inc.") # everything's cool ;; *) echo "$file is missing license header." (( ERROR_COUNT++ )) ;; esac done < <(git ls-files "*\.go") exit $ERROR_COUNT automaxprocs-1.4.0/.codecov.yml000066400000000000000000000013721400631047300164650ustar00rootroot00000000000000coverage: range: 80..100 round: down precision: 2 status: project: # measuring the overall project coverage default: # context, you can create multiple ones with custom titles enabled: yes # must be yes|true to enable this status target: 90% # specify the target coverage for each commit status # option: "auto" (must increase from parent commit or pull request base) # option: "X%" a static target percentage to hit if_not_found: success # if parent is not found report status as success, error, or failure if_ci_failed: error # if ci fails report status as success, error, or failure automaxprocs-1.4.0/.gitignore000066400000000000000000000005151400631047300162300ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test vendor # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof *.pprof *.out *.log coverage.txt /bin cover.out cover.html automaxprocs-1.4.0/.travis.yml000066400000000000000000000005201400631047300163450ustar00rootroot00000000000000language: go sudo: false go_import_path: go.uber.org/automaxprocs env: global: - GO111MODULE=on matrix: include: - go: oldstable - go: stable env: LINT=1 install: - make install script: - test -z "$LINT" || make lint - make test after_success: - make cover - bash <(curl -s https://codecov.io/bash) automaxprocs-1.4.0/CHANGELOG.md000066400000000000000000000013021400631047300160440ustar00rootroot00000000000000# Changelog ## v1.4.0 (2021-02-01) - Support colons in cgroup names. - Remove linters from runtime dependencies. ## v1.3.0 (2020-01-23) - Migrate to Go modules. ## v1.2.0 (2018-02-22) - Fixed quota clamping to always round down rather than up; Rather than guaranteeing constant throttling at saturation, instead assume that the fractional CPU was added as a hedge for factors outside of Go's scheduler. ## v1.1.0 (2017-11-10) - Log the new value of `GOMAXPROCS` rather than the current value. - Make logs more explicit about whether `GOMAXPROCS` was modified or not. - Allow customization of the minimum `GOMAXPROCS`, and modify default from 2 to 1. ## v1.0.0 (2017-08-09) - Initial release. automaxprocs-1.4.0/CODE_OF_CONDUCT.md000066400000000000000000000062301400631047300170370ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at oss-conduct@uber.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]. [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ automaxprocs-1.4.0/CONTRIBUTING.md000066400000000000000000000046341400631047300164770ustar00rootroot00000000000000# Contributing We'd love your help improving this package! If you'd like to add new exported APIs, please [open an issue][open-issue] describing your proposal — discussing API changes ahead of time makes pull request review much smoother. In your issue, pull request, and any other communications, please remember to treat your fellow contributors with respect! We take our [code of conduct](CODE_OF_CONDUCT.md) seriously. Note that you'll need to sign [Uber's Contributor License Agreement][cla] before we can accept any of your contributions. If necessary, a bot will remind you to accept the CLA when you open your pull request. ## Setup [Fork][fork], then clone the repository: ``` mkdir -p $GOPATH/src/go.uber.org cd $GOPATH/src/go.uber.org git clone git@github.com:your_github_username/automaxprocs.git cd automaxprocs git remote add upstream https://github.com/uber-go/automaxprocs.git git fetch upstream ``` Install the test dependencies: ``` make dependencies ``` Make sure that the tests and the linters pass: ``` make test make lint ``` If you're not using the minor version of Go specified in the Makefile's `LINTABLE_MINOR_VERSIONS` variable, `make lint` doesn't do anything. This is fine, but it means that you'll only discover lint failures after you open your pull request. ## Making Changes Start by creating a new branch for your changes: ``` cd $GOPATH/src/go.uber.org/automaxprocs git checkout master git fetch upstream git rebase upstream/master git checkout -b cool_new_feature ``` Make your changes, then ensure that `make lint` and `make test` still pass. If you're satisfied with your changes, push them to your fork. ``` git push origin cool_new_feature ``` Then use the GitHub UI to open a pull request. At this point, you're waiting on us to review your changes. We *try* to respond to issues and pull requests within a few business days, and we may suggest some improvements or alternatives. Once your changes are approved, one of the project maintainers will merge them. We're much more likely to approve your changes if you: * Add tests for new functionality. * Write a [good commit message][commit-message]. * Maintain backward compatibility. [fork]: https://github.com/uber-go/automaxprocs/fork [open-issue]: https://github.com/uber-go/automaxprocs/issues/new [cla]: https://cla-assistant.io/uber-go/automaxprocs [commit-message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html automaxprocs-1.4.0/LICENSE000066400000000000000000000020521400631047300152430ustar00rootroot00000000000000Copyright (c) 2017 Uber Technologies, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 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.automaxprocs-1.4.0/Makefile000066400000000000000000000020651400631047300157020ustar00rootroot00000000000000export GOBIN ?= $(shell pwd)/bin GO_FILES := $(shell \ find . '(' -path '*/.*' -o -path './vendor' ')' -prune \ -o -name '*.go' -print | cut -b3-) GOLINT = $(GOBIN)/golint STATICCHECK = $(GOBIN)/staticcheck .PHONY: build build: go build ./... .PHONY: install install: go mod download .PHONY: test test: go test -race ./... .PHONY: cover cover: go test -coverprofile=cover.out -covermode=atomic -coverpkg=./... ./... go tool cover -html=cover.out -o cover.html $(GOLINT): tools/go.mod cd tools && go install golang.org/x/lint/golint $(STATICCHECK): tools/go.mod cd tools && go install honnef.co/go/tools/cmd/staticcheck .PHONY: lint lint: $(GOLINT) $(STATICCHECK) @rm -rf lint.log @echo "Checking gofmt" @gofmt -d -s $(GO_FILES) 2>&1 | tee lint.log @echo "Checking go vet" @go vet ./... 2>&1 | tee -a lint.log @echo "Checking golint" @$(GOLINT) ./... | tee -a lint.log @echo "Checking staticcheck" @$(STATICCHECK) ./... 2>&1 | tee -a lint.log @echo "Checking for license headers..." @./.build/check_license.sh | tee -a lint.log @[ ! -s lint.log ] automaxprocs-1.4.0/README.md000066400000000000000000000027001400631047300155150ustar00rootroot00000000000000# automaxprocs [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] Automatically set `GOMAXPROCS` to match Linux container CPU quota. ## Installation `go get -u go.uber.org/automaxprocs` ## Quick Start ```go import _ "go.uber.org/automaxprocs" func main() { // Your application logic here. } ``` ## Development Status: Stable All APIs are finalized, and no breaking changes will be made in the 1.x series of releases. Users of semver-aware dependency management systems should pin automaxprocs to `^1`. ## Contributing We encourage and support an active, healthy community of contributors — including you! Details are in the [contribution guide](CONTRIBUTING.md) and the [code of conduct](CODE_OF_CONDUCT.md). The automaxprocs maintainers keep an eye on issues and pull requests, but you can also report any negative conduct to oss-conduct@uber.com. That email list is a private, safe space; even the automaxprocs maintainers don't have access, so don't hesitate to hold us to a high standard.
Released under the [MIT License](LICENSE). [doc-img]: https://godoc.org/go.uber.org/automaxprocs?status.svg [doc]: https://godoc.org/go.uber.org/automaxprocs [ci-img]: https://travis-ci.com/uber-go/automaxprocs.svg?branch=master [ci]: https://travis-ci.com/uber-go/automaxprocs [cov-img]: https://codecov.io/gh/uber-go/automaxprocs/branch/master/graph/badge.svg [cov]: https://codecov.io/gh/uber-go/automaxprocs automaxprocs-1.4.0/automaxprocs.go000066400000000000000000000025721400631047300173210ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. // Package automaxprocs automatically sets GOMAXPROCS to match the Linux // container CPU quota, if any. package automaxprocs // import "go.uber.org/automaxprocs" import ( "log" "go.uber.org/automaxprocs/maxprocs" ) func init() { maxprocs.Set(maxprocs.Logger(log.Printf)) } automaxprocs-1.4.0/example_test.go000066400000000000000000000025041400631047300172610ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. package automaxprocs_test // Importing automaxprocs automatically adjusts GOMAXPROCS. import _ "go.uber.org/automaxprocs" // To render a whole-file example, we need a package-level declaration. var _ = "" func Example() {} automaxprocs-1.4.0/glide.yaml000066400000000000000000000002141400631047300162040ustar00rootroot00000000000000package: go.uber.org/automaxprocs import: [] testImport: - package: github.com/stretchr/testify version: ^1.1.4 subpackages: - assert automaxprocs-1.4.0/go.mod000066400000000000000000000003051400631047300153430ustar00rootroot00000000000000module go.uber.org/automaxprocs go 1.13 require ( github.com/kr/pretty v0.1.0 // indirect github.com/stretchr/testify v1.4.0 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) automaxprocs-1.4.0/go.sum000066400000000000000000000030431400631047300153720ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= automaxprocs-1.4.0/internal/000077500000000000000000000000001400631047300160535ustar00rootroot00000000000000automaxprocs-1.4.0/internal/cgroups/000077500000000000000000000000001400631047300175355ustar00rootroot00000000000000automaxprocs-1.4.0/internal/cgroups/cgroup.go000066400000000000000000000044561400631047300213740ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. // +build linux package cgroups import ( "bufio" "io" "os" "path/filepath" "strconv" ) // CGroup represents the data structure for a Linux control group. type CGroup struct { path string } // NewCGroup returns a new *CGroup from a given path. func NewCGroup(path string) *CGroup { return &CGroup{path: path} } // Path returns the path of the CGroup*. func (cg *CGroup) Path() string { return cg.path } // ParamPath returns the path of the given cgroup param under itself. func (cg *CGroup) ParamPath(param string) string { return filepath.Join(cg.path, param) } // readFirstLine reads the first line from a cgroup param file. func (cg *CGroup) readFirstLine(param string) (string, error) { paramFile, err := os.Open(cg.ParamPath(param)) if err != nil { return "", err } defer paramFile.Close() scanner := bufio.NewScanner(paramFile) if scanner.Scan() { return scanner.Text(), nil } if err := scanner.Err(); err != nil { return "", err } return "", io.ErrUnexpectedEOF } // readInt parses the first line from a cgroup param file as int. func (cg *CGroup) readInt(param string) (int, error) { text, err := cg.readFirstLine(param) if err != nil { return 0, err } return strconv.Atoi(text) } automaxprocs-1.4.0/internal/cgroups/cgroup_test.go000066400000000000000000000065421400631047300224310ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. // +build linux package cgroups import ( "path/filepath" "testing" "github.com/stretchr/testify/assert" ) func TestCGroupParamPath(t *testing.T) { cgroup := NewCGroup("/sys/fs/cgroup/cpu") assert.Equal(t, "/sys/fs/cgroup/cpu", cgroup.Path()) assert.Equal(t, "/sys/fs/cgroup/cpu/cpu.cfs_quota_us", cgroup.ParamPath("cpu.cfs_quota_us")) } func TestCGroupReadFirstLine(t *testing.T) { testTable := []struct { name string paramName string expectedContent string shouldHaveError bool }{ { name: "cpu", paramName: "cpu.cfs_period_us", expectedContent: "100000", shouldHaveError: false, }, { name: "absent", paramName: "cpu.stat", expectedContent: "", shouldHaveError: true, }, { name: "empty", paramName: "cpu.cfs_quota_us", expectedContent: "", shouldHaveError: true, }, } for _, tt := range testTable { cgroupPath := filepath.Join(testDataCGroupsPath, tt.name) cgroup := NewCGroup(cgroupPath) content, err := cgroup.readFirstLine(tt.paramName) assert.Equal(t, tt.expectedContent, content, tt.name) if tt.shouldHaveError { assert.Error(t, err, tt.name) } else { assert.NoError(t, err, tt.name) } } } func TestCGroupReadInt(t *testing.T) { testTable := []struct { name string paramName string expectedValue int shouldHaveError bool }{ { name: "cpu", paramName: "cpu.cfs_period_us", expectedValue: 100000, shouldHaveError: false, }, { name: "empty", paramName: "cpu.cfs_quota_us", expectedValue: 0, shouldHaveError: true, }, { name: "invalid", paramName: "cpu.cfs_quota_us", expectedValue: 0, shouldHaveError: true, }, { name: "absent", paramName: "cpu.cfs_quota_us", expectedValue: 0, shouldHaveError: true, }, } for _, tt := range testTable { cgroupPath := filepath.Join(testDataCGroupsPath, tt.name) cgroup := NewCGroup(cgroupPath) value, err := cgroup.readInt(tt.paramName) assert.Equal(t, tt.expectedValue, value, "%s/%s", tt.name, tt.paramName) if tt.shouldHaveError { assert.Error(t, err, tt.name) } else { assert.NoError(t, err, tt.name) } } } automaxprocs-1.4.0/internal/cgroups/cgroups.go000066400000000000000000000074421400631047300215550ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. // +build linux package cgroups const ( // _cgroupFSType is the Linux CGroup file system type used in // `/proc/$PID/mountinfo`. _cgroupFSType = "cgroup" // _cgroupSubsysCPU is the CPU CGroup subsystem. _cgroupSubsysCPU = "cpu" // _cgroupSubsysCPUAcct is the CPU accounting CGroup subsystem. _cgroupSubsysCPUAcct = "cpuacct" // _cgroupSubsysCPUSet is the CPUSet CGroup subsystem. _cgroupSubsysCPUSet = "cpuset" // _cgroupSubsysMemory is the Memory CGroup subsystem. _cgroupSubsysMemory = "memory" // _cgroupCPUCFSQuotaUsParam is the file name for the CGroup CFS quota // parameter. _cgroupCPUCFSQuotaUsParam = "cpu.cfs_quota_us" // _cgroupCPUCFSPeriodUsParam is the file name for the CGroup CFS period // parameter. _cgroupCPUCFSPeriodUsParam = "cpu.cfs_period_us" ) const ( _procPathCGroup = "/proc/self/cgroup" _procPathMountInfo = "/proc/self/mountinfo" ) // CGroups is a map that associates each CGroup with its subsystem name. type CGroups map[string]*CGroup // NewCGroups returns a new *CGroups from given `mountinfo` and `cgroup` files // under for some process under `/proc` file system (see also proc(5) for more // information). func NewCGroups(procPathMountInfo, procPathCGroup string) (CGroups, error) { cgroupSubsystems, err := parseCGroupSubsystems(procPathCGroup) if err != nil { return nil, err } cgroups := make(CGroups) newMountPoint := func(mp *MountPoint) error { if mp.FSType != _cgroupFSType { return nil } for _, opt := range mp.SuperOptions { subsys, exists := cgroupSubsystems[opt] if !exists { continue } cgroupPath, err := mp.Translate(subsys.Name) if err != nil { return err } cgroups[opt] = NewCGroup(cgroupPath) } return nil } if err := parseMountInfo(procPathMountInfo, newMountPoint); err != nil { return nil, err } return cgroups, nil } // NewCGroupsForCurrentProcess returns a new *CGroups instance for the current // process. func NewCGroupsForCurrentProcess() (CGroups, error) { return NewCGroups(_procPathMountInfo, _procPathCGroup) } // CPUQuota returns the CPU quota applied with the CPU cgroup controller. // It is a result of `cpu.cfs_quota_us / cpu.cfs_period_us`. If the value of // `cpu.cfs_quota_us` was not set (-1), the method returns `(-1, nil)`. func (cg CGroups) CPUQuota() (float64, bool, error) { cpuCGroup, exists := cg[_cgroupSubsysCPU] if !exists { return -1, false, nil } cfsQuotaUs, err := cpuCGroup.readInt(_cgroupCPUCFSQuotaUsParam) if defined := cfsQuotaUs > 0; err != nil || !defined { return -1, defined, err } cfsPeriodUs, err := cpuCGroup.readInt(_cgroupCPUCFSPeriodUsParam) if err != nil { return -1, false, err } return float64(cfsQuotaUs) / float64(cfsPeriodUs), true, nil } automaxprocs-1.4.0/internal/cgroups/cgroups_test.go000066400000000000000000000075751400631047300226230ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. // +build linux package cgroups import ( "path/filepath" "testing" "github.com/stretchr/testify/assert" ) func TestNewCGroups(t *testing.T) { cgroupsProcCGroupPath := filepath.Join(testDataProcPath, "cgroups", "cgroup") cgroupsProcMountInfoPath := filepath.Join(testDataProcPath, "cgroups", "mountinfo") testTable := []struct { subsys string path string }{ {_cgroupSubsysCPU, "/sys/fs/cgroup/cpu,cpuacct"}, {_cgroupSubsysCPUAcct, "/sys/fs/cgroup/cpu,cpuacct"}, {_cgroupSubsysCPUSet, "/sys/fs/cgroup/cpuset"}, {_cgroupSubsysMemory, "/sys/fs/cgroup/memory/large"}, } cgroups, err := NewCGroups(cgroupsProcMountInfoPath, cgroupsProcCGroupPath) assert.Equal(t, len(testTable), len(cgroups)) assert.NoError(t, err) for _, tt := range testTable { cgroup, exists := cgroups[tt.subsys] assert.Equal(t, true, exists, "%q expected to present in `cgroups`", tt.subsys) assert.Equal(t, tt.path, cgroup.path, "%q expected for `cgroups[%q].path`, got %q", tt.path, tt.subsys, cgroup.path) } } func TestNewCGroupsWithErrors(t *testing.T) { testTable := []struct { mountInfoPath string cgroupPath string }{ {"non-existing-file", "/dev/null"}, {"/dev/null", "non-existing-file"}, { "/dev/null", filepath.Join(testDataProcPath, "invalid-cgroup", "cgroup"), }, { filepath.Join(testDataProcPath, "invalid-mountinfo", "mountinfo"), "/dev/null", }, { filepath.Join(testDataProcPath, "untranslatable", "mountinfo"), filepath.Join(testDataProcPath, "untranslatable", "cgroup"), }, } for _, tt := range testTable { cgroups, err := NewCGroups(tt.mountInfoPath, tt.cgroupPath) assert.Nil(t, cgroups) assert.Error(t, err) } } func TestCGroupsCPUQuota(t *testing.T) { testTable := []struct { name string expectedQuota float64 expectedDefined bool shouldHaveError bool }{ { name: "cpu", expectedQuota: 6.0, expectedDefined: true, shouldHaveError: false, }, { name: "undefined", expectedQuota: -1.0, expectedDefined: false, shouldHaveError: false, }, { name: "undefined-period", expectedQuota: -1.0, expectedDefined: false, shouldHaveError: true, }, } cgroups := make(CGroups) quota, defined, err := cgroups.CPUQuota() assert.Equal(t, -1.0, quota, "nonexistent") assert.Equal(t, false, defined, "nonexistent") assert.NoError(t, err, "nonexistent") for _, tt := range testTable { cgroupPath := filepath.Join(testDataCGroupsPath, tt.name) cgroups[_cgroupSubsysCPU] = NewCGroup(cgroupPath) quota, defined, err := cgroups.CPUQuota() assert.Equal(t, tt.expectedQuota, quota, tt.name) assert.Equal(t, tt.expectedDefined, defined, tt.name) if tt.shouldHaveError { assert.Error(t, err, tt.name) } else { assert.NoError(t, err, tt.name) } } } automaxprocs-1.4.0/internal/cgroups/doc.go000066400000000000000000000023741400631047300206370ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. // Package cgroups provides utilities to access Linux control group (CGroups) // parameters (CPU quota, for example) for a given process. package cgroups automaxprocs-1.4.0/internal/cgroups/errors.go000066400000000000000000000035041400631047300214020ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. // +build linux package cgroups import "fmt" type cgroupSubsysFormatInvalidError struct { line string } type mountPointFormatInvalidError struct { line string } type pathNotExposedFromMountPointError struct { mountPoint string root string path string } func (err cgroupSubsysFormatInvalidError) Error() string { return fmt.Sprintf("invalid format for CGroupSubsys: %q", err.line) } func (err mountPointFormatInvalidError) Error() string { return fmt.Sprintf("invalid format for MountPoint: %q", err.line) } func (err pathNotExposedFromMountPointError) Error() string { return fmt.Sprintf("path %q is not a descendant of mount point root %q and cannot be exposed from %q", err.path, err.root, err.mountPoint) } automaxprocs-1.4.0/internal/cgroups/mountpoint.go000066400000000000000000000113621400631047300223030ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. // +build linux package cgroups import ( "bufio" "os" "path/filepath" "strconv" "strings" ) const ( _mountInfoSep = " " _mountInfoOptsSep = "," _mountInfoOptionalFieldsSep = "-" ) const ( _miFieldIDMountID = iota _miFieldIDParentID _miFieldIDDeviceID _miFieldIDRoot _miFieldIDMountPoint _miFieldIDOptions _miFieldIDOptionalFields _miFieldCountFirstHalf ) const ( _miFieldOffsetFSType = iota _miFieldOffsetMountSource _miFieldOffsetSuperOptions _miFieldCountSecondHalf ) const _miFieldCountMin = _miFieldCountFirstHalf + _miFieldCountSecondHalf // MountPoint is the data structure for the mount points in // `/proc/$PID/mountinfo`. See also proc(5) for more information. type MountPoint struct { MountID int ParentID int DeviceID string Root string MountPoint string Options []string OptionalFields []string FSType string MountSource string SuperOptions []string } // NewMountPointFromLine parses a line read from `/proc/$PID/mountinfo` and // returns a new *MountPoint. func NewMountPointFromLine(line string) (*MountPoint, error) { fields := strings.Split(line, _mountInfoSep) if len(fields) < _miFieldCountMin { return nil, mountPointFormatInvalidError{line} } mountID, err := strconv.Atoi(fields[_miFieldIDMountID]) if err != nil { return nil, err } parentID, err := strconv.Atoi(fields[_miFieldIDParentID]) if err != nil { return nil, err } for i, field := range fields[_miFieldIDOptionalFields:] { if field == _mountInfoOptionalFieldsSep { fsTypeStart := _miFieldIDOptionalFields + i + 1 if len(fields) != fsTypeStart+_miFieldCountSecondHalf { return nil, mountPointFormatInvalidError{line} } miFieldIDFSType := _miFieldOffsetFSType + fsTypeStart miFieldIDMountSource := _miFieldOffsetMountSource + fsTypeStart miFieldIDSuperOptions := _miFieldOffsetSuperOptions + fsTypeStart return &MountPoint{ MountID: mountID, ParentID: parentID, DeviceID: fields[_miFieldIDDeviceID], Root: fields[_miFieldIDRoot], MountPoint: fields[_miFieldIDMountPoint], Options: strings.Split(fields[_miFieldIDOptions], _mountInfoOptsSep), OptionalFields: fields[_miFieldIDOptionalFields:(fsTypeStart - 1)], FSType: fields[miFieldIDFSType], MountSource: fields[miFieldIDMountSource], SuperOptions: strings.Split(fields[miFieldIDSuperOptions], _mountInfoOptsSep), }, nil } } return nil, mountPointFormatInvalidError{line} } // Translate converts an absolute path inside the *MountPoint's file system to // the host file system path in the mount namespace the *MountPoint belongs to. func (mp *MountPoint) Translate(absPath string) (string, error) { relPath, err := filepath.Rel(mp.Root, absPath) if err != nil { return "", err } if relPath == ".." || strings.HasPrefix(relPath, "../") { return "", pathNotExposedFromMountPointError{ mountPoint: mp.MountPoint, root: mp.Root, path: absPath, } } return filepath.Join(mp.MountPoint, relPath), nil } // parseMountInfo parses procPathMountInfo (usually at `/proc/$PID/mountinfo`) // and yields parsed *MountPoint into newMountPoint. func parseMountInfo(procPathMountInfo string, newMountPoint func(*MountPoint) error) error { mountInfoFile, err := os.Open(procPathMountInfo) if err != nil { return err } defer mountInfoFile.Close() scanner := bufio.NewScanner(mountInfoFile) for scanner.Scan() { mountPoint, err := NewMountPointFromLine(scanner.Text()) if err != nil { return err } if err := newMountPoint(mountPoint); err != nil { return err } } return scanner.Err() } automaxprocs-1.4.0/internal/cgroups/mountpoint_test.go000066400000000000000000000135351400631047300233460ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. // +build linux package cgroups import ( "testing" "github.com/stretchr/testify/assert" ) func TestNewMountPointFromLine(t *testing.T) { testTable := []struct { name string line string expected *MountPoint }{ { name: "root", line: "1 0 252:0 / / rw,noatime - ext4 /dev/dm-0 rw,errors=remount-ro,data=ordered", expected: &MountPoint{ MountID: 1, ParentID: 0, DeviceID: "252:0", Root: "/", MountPoint: "/", Options: []string{"rw", "noatime"}, OptionalFields: []string{}, FSType: "ext4", MountSource: "/dev/dm-0", SuperOptions: []string{"rw", "errors=remount-ro", "data=ordered"}, }, }, { name: "cgroup", line: "31 23 0:24 /docker /sys/fs/cgroup/cpu rw,nosuid,nodev,noexec,relatime shared:1 - cgroup cgroup rw,cpu", expected: &MountPoint{ MountID: 31, ParentID: 23, DeviceID: "0:24", Root: "/docker", MountPoint: "/sys/fs/cgroup/cpu", Options: []string{"rw", "nosuid", "nodev", "noexec", "relatime"}, OptionalFields: []string{"shared:1"}, FSType: "cgroup", MountSource: "cgroup", SuperOptions: []string{"rw", "cpu"}, }, }, } for _, tt := range testTable { mountPoint, err := NewMountPointFromLine(tt.line) assert.Equal(t, tt.expected, mountPoint, tt.name) assert.NoError(t, err, tt.name) } } func TestNewMountPointFromLineErr(t *testing.T) { linesWithInvalidIDs := []string{ "invalidMountID 0 252:0 / / rw,noatime - ext4 /dev/dm-0 rw,errors=remount-ro,data=ordered", "1 invalidParentID 252:0 / / rw,noatime - ext4 /dev/dm-0 rw,errors=remount-ro,data=ordered", "invalidMountID invalidParentID 252:0 / / rw,noatime - ext4 /dev/dm-0 rw,errors=remount-ro,data=ordered", } for i, line := range linesWithInvalidIDs { mountPoint, err := NewMountPointFromLine(line) assert.Nil(t, mountPoint, "[%d] %q", i, line) assert.Error(t, err, line) } linesWithInvalidFields := []string{ "1 0 252:0 / / rw,noatime ext4 /dev/dm-0 rw,errors=remount-ro,data=ordered", "1 0 252:0 / / rw,noatime shared:1 - ext4 /dev/dm-0", "1 0 252:0 / / rw,noatime shared:1 ext4 - /dev/dm-0 rw,errors=remount-ro,data=ordered", "1 0 252:0 / / rw,noatime shared:1 ext4 /dev/dm-0 rw,errors=remount-ro,data=ordered", "random line", } for i, line := range linesWithInvalidFields { mountPoint, err := NewMountPointFromLine(line) errExpected := mountPointFormatInvalidError{line} assert.Nil(t, mountPoint, "[%d] %q", i, line) assert.Equal(t, err, errExpected, "[%d] %q", i, line) } } func TestMountPointTranslate(t *testing.T) { line := "31 23 0:24 /docker/0123456789abcdef /sys/fs/cgroup/cpu rw,nosuid,nodev,noexec,relatime shared:1 - cgroup cgroup rw,cpu" cgroupMountPoint, err := NewMountPointFromLine(line) assert.NotNil(t, cgroupMountPoint) assert.NoError(t, err) testTable := []struct { name string pathToTranslate string pathTranslated string }{ { name: "root", pathToTranslate: "/docker/0123456789abcdef", pathTranslated: "/sys/fs/cgroup/cpu", }, { name: "root-with-extra-slash", pathToTranslate: "/docker/0123456789abcdef/", pathTranslated: "/sys/fs/cgroup/cpu", }, { name: "descendant-from-root", pathToTranslate: "/docker/0123456789abcdef/large/cpu.cfs_quota_us", pathTranslated: "/sys/fs/cgroup/cpu/large/cpu.cfs_quota_us", }, } for _, tt := range testTable { path, err := cgroupMountPoint.Translate(tt.pathToTranslate) assert.Equal(t, tt.pathTranslated, path, tt.name) assert.NoError(t, err, tt.name) } } func TestMountPointTranslateError(t *testing.T) { line := "31 23 0:24 /docker/0123456789abcdef /sys/fs/cgroup/cpu rw,nosuid,nodev,noexec,relatime shared:1 - cgroup cgroup rw,cpu" cgroupMountPoint, err := NewMountPointFromLine(line) assert.NotNil(t, cgroupMountPoint) assert.NoError(t, err) inaccessiblePaths := []string{ "/", "/docker", "/docker/0123456789abcdef-let-me-hack-this-path", "/docker/0123456789abcde/abc/../../def", "/system.slice/docker.service", } for i, path := range inaccessiblePaths { translated, err := cgroupMountPoint.Translate(path) errExpected := pathNotExposedFromMountPointError{ mountPoint: cgroupMountPoint.MountPoint, root: cgroupMountPoint.Root, path: path, } assert.Equal(t, "", translated, "inaccessiblePaths[%d] == %q", i, path) assert.Equal(t, errExpected, err, "inaccessiblePaths[%d] == %q", i, path) } relPaths := []string{ "docker", "docker/0123456789abcde/large", "system.slice/docker.service", } for i, path := range relPaths { translated, err := cgroupMountPoint.Translate(path) assert.Equal(t, "", translated, "relPaths[%d] == %q", i, path) assert.Error(t, err, path) } } automaxprocs-1.4.0/internal/cgroups/subsys.go000066400000000000000000000054611400631047300214220ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. // +build linux package cgroups import ( "bufio" "os" "strconv" "strings" ) const ( _cgroupSep = ":" _cgroupSubsysSep = "," ) const ( _csFieldIDID = iota _csFieldIDSubsystems _csFieldIDName _csFieldCount ) // CGroupSubsys represents the data structure for entities in // `/proc/$PID/cgroup`. See also proc(5) for more information. type CGroupSubsys struct { ID int Subsystems []string Name string } // NewCGroupSubsysFromLine returns a new *CGroupSubsys by parsing a string in // the format of `/proc/$PID/cgroup` func NewCGroupSubsysFromLine(line string) (*CGroupSubsys, error) { fields := strings.SplitN(line, _cgroupSep, _csFieldCount) if len(fields) != _csFieldCount { return nil, cgroupSubsysFormatInvalidError{line} } id, err := strconv.Atoi(fields[_csFieldIDID]) if err != nil { return nil, err } cgroup := &CGroupSubsys{ ID: id, Subsystems: strings.Split(fields[_csFieldIDSubsystems], _cgroupSubsysSep), Name: fields[_csFieldIDName], } return cgroup, nil } // parseCGroupSubsystems parses procPathCGroup (usually at `/proc/$PID/cgroup`) // and returns a new map[string]*CGroupSubsys. func parseCGroupSubsystems(procPathCGroup string) (map[string]*CGroupSubsys, error) { cgroupFile, err := os.Open(procPathCGroup) if err != nil { return nil, err } defer cgroupFile.Close() scanner := bufio.NewScanner(cgroupFile) subsystems := make(map[string]*CGroupSubsys) for scanner.Scan() { cgroup, err := NewCGroupSubsysFromLine(scanner.Text()) if err != nil { return nil, err } for _, subsys := range cgroup.Subsystems { subsystems[subsys] = cgroup } } if err := scanner.Err(); err != nil { return nil, err } return subsystems, nil } automaxprocs-1.4.0/internal/cgroups/subsys_test.go000066400000000000000000000061601400631047300224560ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. // +build linux package cgroups import ( "strconv" "testing" "github.com/stretchr/testify/assert" ) func TestNewCGroupSubsysFromLine(t *testing.T) { testTable := []struct { name string line string expectedSubsys *CGroupSubsys }{ { name: "single-subsys", line: "1:cpu:/", expectedSubsys: &CGroupSubsys{ ID: 1, Subsystems: []string{"cpu"}, Name: "/", }, }, { name: "multi-subsys", line: "8:cpu,cpuacct,cpuset:/docker/1234567890abcdef", expectedSubsys: &CGroupSubsys{ ID: 8, Subsystems: []string{"cpu", "cpuacct", "cpuset"}, Name: "/docker/1234567890abcdef", }, }, { name: "multi-subsys", line: "12:cpu,cpuacct:/system.slice/containerd.service/kubepods-besteffort-podb41662f7_b03a_4c65_8ef9_6e4e55c3cf27.slice:cri-containerd:1753b7cbbf62734d812936961224d5bc0cf8f45214e0d5cdd1a781a053e7c48f", expectedSubsys: &CGroupSubsys{ ID: 12, Subsystems: []string{"cpu", "cpuacct"}, Name: "/system.slice/containerd.service/kubepods-besteffort-podb41662f7_b03a_4c65_8ef9_6e4e55c3cf27.slice:cri-containerd:1753b7cbbf62734d812936961224d5bc0cf8f45214e0d5cdd1a781a053e7c48f", }, }, } for _, tt := range testTable { subsys, err := NewCGroupSubsysFromLine(tt.line) assert.Equal(t, tt.expectedSubsys, subsys, tt.name) assert.NoError(t, err, tt.name) } } func TestNewCGroupSubsysFromLineErr(t *testing.T) { lines := []string{ "1:cpu", "not-a-number:cpu:/", } _, parseError := strconv.Atoi("not-a-number") testTable := []struct { name string line string expectedError error }{ { name: "fewer-fields", line: lines[0], expectedError: cgroupSubsysFormatInvalidError{lines[0]}, }, { name: "illegal-id", line: lines[1], expectedError: parseError, }, } for _, tt := range testTable { subsys, err := NewCGroupSubsysFromLine(tt.line) assert.Nil(t, subsys, tt.name) assert.Equal(t, tt.expectedError, err, tt.name) } } automaxprocs-1.4.0/internal/cgroups/testdata/000077500000000000000000000000001400631047300213465ustar00rootroot00000000000000automaxprocs-1.4.0/internal/cgroups/testdata/cgroups/000077500000000000000000000000001400631047300230305ustar00rootroot00000000000000automaxprocs-1.4.0/internal/cgroups/testdata/cgroups/cpu/000077500000000000000000000000001400631047300236175ustar00rootroot00000000000000automaxprocs-1.4.0/internal/cgroups/testdata/cgroups/cpu/cpu.cfs_period_us000066400000000000000000000000071400631047300271510ustar00rootroot00000000000000100000 automaxprocs-1.4.0/internal/cgroups/testdata/cgroups/cpu/cpu.cfs_quota_us000066400000000000000000000000071400631047300270200ustar00rootroot00000000000000600000 automaxprocs-1.4.0/internal/cgroups/testdata/cgroups/empty/000077500000000000000000000000001400631047300241665ustar00rootroot00000000000000automaxprocs-1.4.0/internal/cgroups/testdata/cgroups/empty/cpu.cfs_quota_us000066400000000000000000000000001400631047300273600ustar00rootroot00000000000000automaxprocs-1.4.0/internal/cgroups/testdata/cgroups/invalid/000077500000000000000000000000001400631047300244565ustar00rootroot00000000000000automaxprocs-1.4.0/internal/cgroups/testdata/cgroups/invalid/cpu.cfs_quota_us000066400000000000000000000000171400631047300276600ustar00rootroot00000000000000non-an-integer automaxprocs-1.4.0/internal/cgroups/testdata/cgroups/undefined-period/000077500000000000000000000000001400631047300262515ustar00rootroot00000000000000automaxprocs-1.4.0/internal/cgroups/testdata/cgroups/undefined-period/cpu.cfs_quota_us000066400000000000000000000000071400631047300314520ustar00rootroot00000000000000800000 automaxprocs-1.4.0/internal/cgroups/testdata/cgroups/undefined/000077500000000000000000000000001400631047300247715ustar00rootroot00000000000000automaxprocs-1.4.0/internal/cgroups/testdata/cgroups/undefined/cpu.cfs_period_us000066400000000000000000000000071400631047300303230ustar00rootroot00000000000000100000 automaxprocs-1.4.0/internal/cgroups/testdata/cgroups/undefined/cpu.cfs_quota_us000066400000000000000000000000031400631047300301660ustar00rootroot00000000000000-1 automaxprocs-1.4.0/internal/cgroups/testdata/proc/000077500000000000000000000000001400631047300223115ustar00rootroot00000000000000automaxprocs-1.4.0/internal/cgroups/testdata/proc/cgroups/000077500000000000000000000000001400631047300237735ustar00rootroot00000000000000automaxprocs-1.4.0/internal/cgroups/testdata/proc/cgroups/cgroup000066400000000000000000000000701400631047300252120ustar00rootroot000000000000003:memory:/docker/large 2:cpu,cpuacct:/docker 1:cpuset:/ automaxprocs-1.4.0/internal/cgroups/testdata/proc/cgroups/mountinfo000066400000000000000000000013271400631047300257370ustar00rootroot000000000000001 0 8:1 / / rw,noatime shared:1 - ext4 /dev/sda1 rw,errors=remount-ro,data=reordered 2 1 0:1 / /dev rw,relatime shared:2 - devtmpfs udev rw,size=10240k,nr_inodes=16487629,mode=755 3 1 0:2 / /proc rw,nosuid,nodev,noexec,relatime shared:3 - proc proc rw 4 1 0:3 / /sys rw,nosuid,nodev,noexec,relatime shared:4 - sysfs sysfs rw 5 4 0:4 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:5 - tmpfs tmpfs ro,mode=755 6 5 0:5 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:6 - cgroup cgroup rw,cpuset 7 5 0:6 /docker /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:7 - cgroup cgroup rw,cpu,cpuacct 8 5 0:7 /docker /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:8 - cgroup cgroup rw,memory automaxprocs-1.4.0/internal/cgroups/testdata/proc/invalid-cgroup/000077500000000000000000000000001400631047300252345ustar00rootroot00000000000000automaxprocs-1.4.0/internal/cgroups/testdata/proc/invalid-cgroup/cgroup000066400000000000000000000000311400631047300264500ustar00rootroot000000000000001:cpu:/cpu invalid-line: automaxprocs-1.4.0/internal/cgroups/testdata/proc/invalid-mountinfo/000077500000000000000000000000001400631047300257535ustar00rootroot00000000000000automaxprocs-1.4.0/internal/cgroups/testdata/proc/invalid-mountinfo/mountinfo000066400000000000000000000000611400631047300277110ustar00rootroot000000000000001 0 8:1 / / rw,noatime shared:1 - ext4 /dev/sda1 automaxprocs-1.4.0/internal/cgroups/testdata/proc/untranslatable/000077500000000000000000000000001400631047300253305ustar00rootroot00000000000000automaxprocs-1.4.0/internal/cgroups/testdata/proc/untranslatable/cgroup000066400000000000000000000000401400631047300265440ustar00rootroot000000000000001:cpu:/docker 2:cpuacct:/docker automaxprocs-1.4.0/internal/cgroups/testdata/proc/untranslatable/mountinfo000066400000000000000000000003371400631047300272740ustar00rootroot0000000000000031 23 0:24 / /sys/fs/cgroup/cpu rw,nosuid,nodev,noexec,relatime shared:1 - cgroup cgroup rw,cpu 32 23 0:25 /docker/0123456789abcdef /sys/fs/cgroup/cpuacct rw,nosuid,nodev,noexec,relatime shared:2 - cgroup cgroup rw,cpuacct automaxprocs-1.4.0/internal/cgroups/util_test.go000066400000000000000000000027431400631047300221060ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. // +build linux package cgroups import ( "os" "path/filepath" ) var ( pwd = mustGetWd() testDataPath = filepath.Join(pwd, "testdata") testDataCGroupsPath = filepath.Join(testDataPath, "cgroups") testDataProcPath = filepath.Join(testDataPath, "proc") ) func mustGetWd() string { pwd, err := os.Getwd() if err != nil { panic(err) } return pwd } automaxprocs-1.4.0/internal/runtime/000077500000000000000000000000001400631047300175365ustar00rootroot00000000000000automaxprocs-1.4.0/internal/runtime/cpu_quota_linux.go000066400000000000000000000033701400631047300233070ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. // +build linux package runtime import ( "math" cg "go.uber.org/automaxprocs/internal/cgroups" ) // CPUQuotaToGOMAXPROCS converts the CPU quota applied to the calling process // to a valid GOMAXPROCS value. func CPUQuotaToGOMAXPROCS(minValue int) (int, CPUQuotaStatus, error) { cgroups, err := cg.NewCGroupsForCurrentProcess() if err != nil { return -1, CPUQuotaUndefined, err } quota, defined, err := cgroups.CPUQuota() if !defined || err != nil { return -1, CPUQuotaUndefined, err } maxProcs := int(math.Floor(quota)) if minValue > 0 && maxProcs < minValue { return minValue, CPUQuotaMinUsed, nil } return maxProcs, CPUQuotaUsed, nil } automaxprocs-1.4.0/internal/runtime/cpu_quota_unsupported.go000066400000000000000000000026271400631047300245440ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. // +build !linux package runtime // CPUQuotaToGOMAXPROCS converts the CPU quota applied to the calling process // to a valid GOMAXPROCS value. This is Linux-specific and not supported in the // current OS. func CPUQuotaToGOMAXPROCS(_ int) (int, CPUQuotaStatus, error) { return -1, CPUQuotaUndefined, nil } automaxprocs-1.4.0/internal/runtime/runtime.go000066400000000000000000000027461400631047300215610ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. package runtime // CPUQuotaStatus presents the status of how CPU quota is used type CPUQuotaStatus int const ( // CPUQuotaUndefined is returned when CPU quota is undefined CPUQuotaUndefined CPUQuotaStatus = iota // CPUQuotaUsed is returned when a valid CPU quota can be used CPUQuotaUsed // CPUQuotaMinUsed is return when CPU quota is smaller than the min value CPUQuotaMinUsed ) automaxprocs-1.4.0/maxprocs/000077500000000000000000000000001400631047300160735ustar00rootroot00000000000000automaxprocs-1.4.0/maxprocs/example_test.go000066400000000000000000000031621400631047300211160ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. package maxprocs_test import ( "log" "go.uber.org/automaxprocs/maxprocs" ) func Example() { undo, err := maxprocs.Set() defer undo() if err != nil { log.Fatalf("failed to set GOMAXPROCS: %v", err) } // Insert your application logic here. } func ExampleLogger() { // By default, Set doesn't output any logs. You can enable logging by // supplying a printf implementation. undo, err := maxprocs.Set(maxprocs.Logger(log.Printf)) defer undo() if err != nil { log.Fatalf("failed to set GOMAXPROCS: %v", err) } } automaxprocs-1.4.0/maxprocs/maxprocs.go000066400000000000000000000077261400631047300202720ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. // Package maxprocs lets Go programs easily configure runtime.GOMAXPROCS to // match the configured Linux CPU quota. Unlike the top-level automaxprocs // package, it lets the caller configure logging and handle errors. package maxprocs // import "go.uber.org/automaxprocs/maxprocs" import ( "os" "runtime" iruntime "go.uber.org/automaxprocs/internal/runtime" ) const _maxProcsKey = "GOMAXPROCS" func currentMaxProcs() int { return runtime.GOMAXPROCS(0) } type config struct { printf func(string, ...interface{}) procs func(int) (int, iruntime.CPUQuotaStatus, error) minGOMAXPROCS int } func (c *config) log(fmt string, args ...interface{}) { if c.printf != nil { c.printf(fmt, args...) } } // An Option alters the behavior of Set. type Option interface { apply(*config) } // Logger uses the supplied printf implementation for log output. By default, // Set doesn't log anything. func Logger(printf func(string, ...interface{})) Option { return optionFunc(func(cfg *config) { cfg.printf = printf }) } // Min sets the minimum GOMAXPROCS value that will be used. // Any value below 1 is ignored. func Min(n int) Option { return optionFunc(func(cfg *config) { if n >= 1 { cfg.minGOMAXPROCS = n } }) } type optionFunc func(*config) func (of optionFunc) apply(cfg *config) { of(cfg) } // Set GOMAXPROCS to match the Linux container CPU quota (if any), returning // any error encountered and an undo function. // // Set is a no-op on non-Linux systems and in Linux environments without a // configured CPU quota. func Set(opts ...Option) (func(), error) { cfg := &config{ procs: iruntime.CPUQuotaToGOMAXPROCS, minGOMAXPROCS: 1, } for _, o := range opts { o.apply(cfg) } undoNoop := func() { cfg.log("maxprocs: No GOMAXPROCS change to reset") } // Honor the GOMAXPROCS environment variable if present. Otherwise, amend // `runtime.GOMAXPROCS()` with the current process' CPU quota if the OS is // Linux, and guarantee a minimum value of 1. The minimum guaranteed value // can be overriden using `maxprocs.Min()`. if max, exists := os.LookupEnv(_maxProcsKey); exists { cfg.log("maxprocs: Honoring GOMAXPROCS=%q as set in environment", max) return undoNoop, nil } maxProcs, status, err := cfg.procs(cfg.minGOMAXPROCS) if err != nil { return undoNoop, err } if status == iruntime.CPUQuotaUndefined { cfg.log("maxprocs: Leaving GOMAXPROCS=%v: CPU quota undefined", currentMaxProcs()) return undoNoop, nil } prev := currentMaxProcs() undo := func() { cfg.log("maxprocs: Resetting GOMAXPROCS to %v", prev) runtime.GOMAXPROCS(prev) } switch status { case iruntime.CPUQuotaMinUsed: cfg.log("maxprocs: Updating GOMAXPROCS=%v: using minimum allowed GOMAXPROCS", maxProcs) case iruntime.CPUQuotaUsed: cfg.log("maxprocs: Updating GOMAXPROCS=%v: determined from CPU quota", maxProcs) } runtime.GOMAXPROCS(maxProcs) return undo, nil } automaxprocs-1.4.0/maxprocs/maxprocs_test.go000066400000000000000000000135011400631047300213150ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. package maxprocs import ( "bytes" "errors" "fmt" "log" "os" "strconv" "testing" iruntime "go.uber.org/automaxprocs/internal/runtime" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func withMax(t testing.TB, n int, f func()) { prevStr, ok := os.LookupEnv(_maxProcsKey) want := strconv.FormatInt(int64(n), 10) require.NoError(t, os.Setenv(_maxProcsKey, want), "couldn't set GOMAXPROCS") f() if ok { require.NoError(t, os.Setenv(_maxProcsKey, prevStr), "couldn't restore original GOMAXPROCS value") return } require.NoError(t, os.Unsetenv(_maxProcsKey), "couldn't clear GOMAXPROCS") } func testLogger() (*bytes.Buffer, Option) { buf := bytes.NewBuffer(nil) printf := func(template string, args ...interface{}) { fmt.Fprintf(buf, template, args...) } return buf, Logger(printf) } func stubProcs(f func(int) (int, iruntime.CPUQuotaStatus, error)) Option { return optionFunc(func(cfg *config) { cfg.procs = f }) } func TestLogger(t *testing.T) { t.Run("default", func(t *testing.T) { // Calling Set without options should be safe. undo, err := Set() defer undo() require.NoError(t, err, "Set failed") }) t.Run("override", func(t *testing.T) { buf, opt := testLogger() undo, err := Set(opt) defer undo() require.NoError(t, err, "Set failed") assert.True(t, buf.Len() > 0, "didn't capture log output") }) } func TestSet(t *testing.T) { // Ensure that we've undone any modifications correctly. prev := currentMaxProcs() defer func() { require.Equal(t, prev, currentMaxProcs(), "didn't undo GOMAXPROCS changes") }() t.Run("EnvVarPresent", func(t *testing.T) { withMax(t, 42, func() { prev := currentMaxProcs() undo, err := Set() defer undo() require.NoError(t, err, "Set failed") assert.Equal(t, prev, currentMaxProcs(), "shouldn't alter GOMAXPROCS") }) }) t.Run("ErrorReadingQuota", func(t *testing.T) { opt := stubProcs(func(int) (int, iruntime.CPUQuotaStatus, error) { return 0, iruntime.CPUQuotaUndefined, errors.New("failed") }) prev := currentMaxProcs() undo, err := Set(opt) defer undo() require.Error(t, err, "Set should have failed") assert.Equal(t, "failed", err.Error(), "should pass errors up the stack") assert.Equal(t, prev, currentMaxProcs(), "shouldn't alter GOMAXPROCS") }) t.Run("QuotaUndefined", func(t *testing.T) { buf, logOpt := testLogger() quotaOpt := stubProcs(func(int) (int, iruntime.CPUQuotaStatus, error) { return 0, iruntime.CPUQuotaUndefined, nil }) prev := currentMaxProcs() undo, err := Set(logOpt, quotaOpt) defer undo() require.NoError(t, err, "Set failed") assert.Equal(t, prev, currentMaxProcs(), "shouldn't alter GOMAXPROCS") assert.Contains(t, buf.String(), "quota undefined", "unexpected log output") }) t.Run("QuotaUndefined return maxProcs=7", func(t *testing.T) { buf, logOpt := testLogger() quotaOpt := stubProcs(func(int) (int, iruntime.CPUQuotaStatus, error) { return 7, iruntime.CPUQuotaUndefined, nil }) prev := currentMaxProcs() undo, err := Set(logOpt, quotaOpt) defer undo() require.NoError(t, err, "Set failed") assert.Equal(t, prev, currentMaxProcs(), "shouldn't alter GOMAXPROCS") assert.Contains(t, buf.String(), "quota undefined", "unexpected log output") }) t.Run("QuotaTooSmall", func(t *testing.T) { buf, logOpt := testLogger() quotaOpt := stubProcs(func(min int) (int, iruntime.CPUQuotaStatus, error) { return min, iruntime.CPUQuotaMinUsed, nil }) undo, err := Set(logOpt, quotaOpt, Min(5)) defer undo() require.NoError(t, err, "Set failed") assert.Equal(t, 5, currentMaxProcs(), "should use min allowed GOMAXPROCS") assert.Contains(t, buf.String(), "using minimum allowed", "unexpected log output") }) t.Run("Min unused", func(t *testing.T) { buf, logOpt := testLogger() quotaOpt := stubProcs(func(min int) (int, iruntime.CPUQuotaStatus, error) { return min, iruntime.CPUQuotaMinUsed, nil }) // Min(-1) should be ignored. undo, err := Set(logOpt, quotaOpt, Min(5), Min(-1)) defer undo() require.NoError(t, err, "Set failed") assert.Equal(t, 5, currentMaxProcs(), "should use min allowed GOMAXPROCS") assert.Contains(t, buf.String(), "using minimum allowed", "unexpected log output") }) t.Run("QuotaUsed", func(t *testing.T) { opt := stubProcs(func(min int) (int, iruntime.CPUQuotaStatus, error) { assert.Equal(t, 1, min, "Default minimum value should be 1") return 42, iruntime.CPUQuotaUsed, nil }) undo, err := Set(opt) defer undo() require.NoError(t, err, "Set failed") assert.Equal(t, 42, currentMaxProcs(), "should change GOMAXPROCS to match quota") }) } func TestMain(m *testing.M) { if err := os.Unsetenv(_maxProcsKey); err != nil { log.Fatalf("Couldn't clear %s: %v\n", _maxProcsKey, err) } os.Exit(m.Run()) } automaxprocs-1.4.0/maxprocs/version.go000066400000000000000000000022671400631047300201160ustar00rootroot00000000000000// Copyright (c) 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. package maxprocs // Version is the current package version. const Version = "1.4.0" automaxprocs-1.4.0/tools/000077500000000000000000000000001400631047300153775ustar00rootroot00000000000000automaxprocs-1.4.0/tools/go.mod000066400000000000000000000005261400631047300165100ustar00rootroot00000000000000module go.uber.org/automaxprocs/tools go 1.13 require ( github.com/google/renameio v0.1.0 // indirect github.com/rogpeppe/go-internal v1.3.0 // indirect golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 golang.org/x/mod v0.4.1 // indirect golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect honnef.co/go/tools v0.1.1 ) automaxprocs-1.4.0/tools/go.sum000066400000000000000000000125031400631047300165330ustar00rootroot00000000000000github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f h1:kDxGY2VmgABOe55qheT/TFqUMtcTHnomIPS1iv3G4Ms= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.1.1 h1:EVDuO03OCZwpV2t/tLLxPmPiomagMoBOgfPt0FM+4IY= honnef.co/go/tools v0.1.1/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= automaxprocs-1.4.0/tools/tools.go000066400000000000000000000023701400631047300170700ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // 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. // +build tools package tools import ( // Tools we use during development. _ "golang.org/x/lint/golint" _ "honnef.co/go/tools/cmd/staticcheck" )