pax_global_header 0000666 0000000 0000000 00000000064 14456021071 0014512 g ustar 00root root 0000000 0000000 52 comment=e83e959d8845f659d02749ba5598fd62a7129e4e
automaxprocs-1.5.3/ 0000775 0000000 0000000 00000000000 14456021071 0014245 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/.build/ 0000775 0000000 0000000 00000000000 14456021071 0015422 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/.build/check_license.sh 0000775 0000000 0000000 00000000446 14456021071 0020544 0 ustar 00root root 0000000 0000000 #!/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.5.3/.codecov.yml 0000664 0000000 0000000 00000001372 14456021071 0016473 0 ustar 00root root 0000000 0000000 coverage:
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.5.3/.github/ 0000775 0000000 0000000 00000000000 14456021071 0015605 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/.github/workflows/ 0000775 0000000 0000000 00000000000 14456021071 0017642 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/.github/workflows/fossa.yaml 0000664 0000000 0000000 00000000502 14456021071 0021636 0 ustar 00root root 0000000 0000000 name: FOSSA Analysis
on: push
jobs:
build:
runs-on: ubuntu-latest
if: github.repository_owner == 'uber-go'
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: FOSSA analysis
uses: fossas/fossa-action@v1
with:
api-key: ${{ secrets.FOSSA_API_KEY }}
automaxprocs-1.5.3/.github/workflows/go.yml 0000664 0000000 0000000 00000001550 14456021071 0020773 0 ustar 00root root 0000000 0000000 name: Go
on:
push:
branches: ['*']
tags: ['v*']
pull_request:
branches: ['*']
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
go: ["1.19.x", "1.20.x"]
include:
- go: 1.20.x
latest: true
steps:
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go }}
- name: Checkout code
uses: actions/checkout@v3
- name: Load cached dependencies
uses: actions/cache@v1
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Lint
if: matrix.latest
run: make lint
- name: Test
run: make cover
- name: Upload coverage to codecov.io
uses: codecov/codecov-action@v3
automaxprocs-1.5.3/.gitignore 0000664 0000000 0000000 00000000515 14456021071 0016236 0 ustar 00root root 0000000 0000000 # 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.5.3/CHANGELOG.md 0000664 0000000 0000000 00000002154 14456021071 0016060 0 ustar 00root root 0000000 0000000 # Changelog
## v1.5.3 (2023-07-19)
- Fix mountinfo parsing when super options have fields with spaces.
- Fix division by zero while parsing cgroups.
## v1.5.2 (2023-03-16)
- Support child control cgroups
- Fix file descriptor leak
- Update dependencies
## v1.5.1 (2022-04-06)
- Fix cgroups v2 mountpoint detection.
## v1.5.0 (2022-04-05)
- Add support for cgroups v2.
Thanks to @emadolsky for their contribution to this release.
## 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.5.3/CODE_OF_CONDUCT.md 0000664 0000000 0000000 00000006230 14456021071 0017045 0 ustar 00root root 0000000 0000000 # 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.5.3/CONTRIBUTING.md 0000664 0000000 0000000 00000004634 14456021071 0016505 0 ustar 00root root 0000000 0000000 # 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.5.3/LICENSE 0000664 0000000 0000000 00000002052 14456021071 0015251 0 ustar 00root root 0000000 0000000 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. automaxprocs-1.5.3/Makefile 0000664 0000000 0000000 00000002076 14456021071 0015712 0 ustar 00root root 0000000 0000000 export 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@2023.1.2
.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.5.3/README.md 0000664 0000000 0000000 00000005050 14456021071 0015524 0 ustar 00root root 0000000 0000000 # 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.
}
```
# Performance
Data measured from Uber's internal load balancer. We ran the load balancer with 200% CPU quota (i.e., 2 cores):
| GOMAXPROCS | RPS | P50 (ms) | P99.9 (ms) |
| ------------------ | --------- | -------- | ---------- |
| 1 | 28,893.18 | 1.46 | 19.70 |
| 2 (equal to quota) | 44,715.07 | 0.84 | 26.38 |
| 3 | 44,212.93 | 0.66 | 30.07 |
| 4 | 41,071.15 | 0.57 | 42.94 |
| 8 | 33,111.69 | 0.43 | 64.32 |
| Default (24) | 22,191.40 | 0.45 | 76.19 |
When `GOMAXPROCS` is increased above the CPU quota, we see P50 decrease slightly, but see significant increases to P99. We also see that the total RPS handled also decreases.
When `GOMAXPROCS` is higher than the CPU quota allocated, we also saw significant throttling:
```
$ cat /sys/fs/cgroup/cpu,cpuacct/system.slice/[...]/cpu.stat
nr_periods 42227334
nr_throttled 131923
throttled_time 88613212216618
```
Once `GOMAXPROCS` was reduced to match the CPU quota, we saw no CPU throttling.
## 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://github.com/uber-go/automaxprocs/actions/workflows/go.yml/badge.svg
[ci]: https://github.com/uber-go/automaxprocs/actions/workflows/go.yml
[cov-img]: https://codecov.io/gh/uber-go/automaxprocs/branch/master/graph/badge.svg
[cov]: https://codecov.io/gh/uber-go/automaxprocs
automaxprocs-1.5.3/automaxprocs.go 0000664 0000000 0000000 00000002572 14456021071 0017327 0 ustar 00root root 0000000 0000000 // 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.5.3/example_test.go 0000664 0000000 0000000 00000002504 14456021071 0017267 0 ustar 00root root 0000000 0000000 // 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.5.3/glide.yaml 0000664 0000000 0000000 00000000214 14456021071 0016212 0 ustar 00root root 0000000 0000000 package: go.uber.org/automaxprocs
import: []
testImport:
- package: github.com/stretchr/testify
version: ^1.1.4
subpackages:
- assert
automaxprocs-1.5.3/go.mod 0000664 0000000 0000000 00000000574 14456021071 0015361 0 ustar 00root root 0000000 0000000 module go.uber.org/automaxprocs
go 1.18
require (
github.com/prashantv/gostub v1.1.0
github.com/stretchr/testify v1.7.1
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
automaxprocs-1.5.3/go.sum 0000664 0000000 0000000 00000003457 14456021071 0015411 0 ustar 00root root 0000000 0000000 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/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/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
automaxprocs-1.5.3/internal/ 0000775 0000000 0000000 00000000000 14456021071 0016061 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/ 0000775 0000000 0000000 00000000000 14456021071 0017543 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/cgroup.go 0000664 0000000 0000000 00000004477 14456021071 0021405 0 ustar 00root root 0000000 0000000 // 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.
//go:build linux
// +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.5.3/internal/cgroups/cgroup_test.go 0000664 0000000 0000000 00000006563 14456021071 0022442 0 ustar 00root root 0000000 0000000 // 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.
//go:build linux
// +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.5.3/internal/cgroups/cgroups.go 0000664 0000000 0000000 00000007535 14456021071 0021566 0 ustar 00root root 0000000 0000000 // 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.
//go:build linux
// +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 defined := cfsPeriodUs > 0; err != nil || !defined {
return -1, defined, err
}
return float64(cfsQuotaUs) / float64(cfsPeriodUs), true, nil
}
automaxprocs-1.5.3/internal/cgroups/cgroups2.go 0000664 0000000 0000000 00000011024 14456021071 0021634 0 ustar 00root root 0000000 0000000 // Copyright (c) 2022 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.
//go:build linux
// +build linux
package cgroups
import (
"bufio"
"errors"
"fmt"
"io"
"os"
"path"
"strconv"
"strings"
)
const (
// _cgroupv2CPUMax is the file name for the CGroup-V2 CPU max and period
// parameter.
_cgroupv2CPUMax = "cpu.max"
// _cgroupFSType is the Linux CGroup-V2 file system type used in
// `/proc/$PID/mountinfo`.
_cgroupv2FSType = "cgroup2"
_cgroupv2MountPoint = "/sys/fs/cgroup"
_cgroupV2CPUMaxDefaultPeriod = 100000
_cgroupV2CPUMaxQuotaMax = "max"
)
const (
_cgroupv2CPUMaxQuotaIndex = iota
_cgroupv2CPUMaxPeriodIndex
)
// ErrNotV2 indicates that the system is not using cgroups2.
var ErrNotV2 = errors.New("not using cgroups2")
// CGroups2 provides access to cgroups data for systems using cgroups2.
type CGroups2 struct {
mountPoint string
groupPath string
cpuMaxFile string
}
// NewCGroups2ForCurrentProcess builds a CGroups2 for the current process.
//
// This returns ErrNotV2 if the system is not using cgroups2.
func NewCGroups2ForCurrentProcess() (*CGroups2, error) {
return newCGroups2From(_procPathMountInfo, _procPathCGroup)
}
func newCGroups2From(mountInfoPath, procPathCGroup string) (*CGroups2, error) {
isV2, err := isCGroupV2(mountInfoPath)
if err != nil {
return nil, err
}
if !isV2 {
return nil, ErrNotV2
}
subsystems, err := parseCGroupSubsystems(procPathCGroup)
if err != nil {
return nil, err
}
// Find v2 subsystem by looking for the `0` id
var v2subsys *CGroupSubsys
for _, subsys := range subsystems {
if subsys.ID == 0 {
v2subsys = subsys
break
}
}
if v2subsys == nil {
return nil, ErrNotV2
}
return &CGroups2{
mountPoint: _cgroupv2MountPoint,
groupPath: v2subsys.Name,
cpuMaxFile: _cgroupv2CPUMax,
}, nil
}
func isCGroupV2(procPathMountInfo string) (bool, error) {
var (
isV2 bool
newMountPoint = func(mp *MountPoint) error {
isV2 = isV2 || (mp.FSType == _cgroupv2FSType && mp.MountPoint == _cgroupv2MountPoint)
return nil
}
)
if err := parseMountInfo(procPathMountInfo, newMountPoint); err != nil {
return false, err
}
return isV2, nil
}
// CPUQuota returns the CPU quota applied with the CPU cgroup2 controller.
// It is a result of reading cpu quota and period from cpu.max file.
// It will return `cpu.max / cpu.period`. If cpu.max is set to max, it returns
// (-1, false, nil)
func (cg *CGroups2) CPUQuota() (float64, bool, error) {
cpuMaxParams, err := os.Open(path.Join(cg.mountPoint, cg.groupPath, cg.cpuMaxFile))
if err != nil {
if os.IsNotExist(err) {
return -1, false, nil
}
return -1, false, err
}
defer cpuMaxParams.Close()
scanner := bufio.NewScanner(cpuMaxParams)
if scanner.Scan() {
fields := strings.Fields(scanner.Text())
if len(fields) == 0 || len(fields) > 2 {
return -1, false, fmt.Errorf("invalid format")
}
if fields[_cgroupv2CPUMaxQuotaIndex] == _cgroupV2CPUMaxQuotaMax {
return -1, false, nil
}
max, err := strconv.Atoi(fields[_cgroupv2CPUMaxQuotaIndex])
if err != nil {
return -1, false, err
}
var period int
if len(fields) == 1 {
period = _cgroupV2CPUMaxDefaultPeriod
} else {
period, err = strconv.Atoi(fields[_cgroupv2CPUMaxPeriodIndex])
if err != nil {
return -1, false, err
}
if period == 0 {
return -1, false, errors.New("zero value for period is not allowed")
}
}
return float64(max) / float64(period), true, nil
}
if err := scanner.Err(); err != nil {
return -1, false, err
}
return 0, false, io.ErrUnexpectedEOF
}
automaxprocs-1.5.3/internal/cgroups/cgroups2_test.go 0000664 0000000 0000000 00000012514 14456021071 0022700 0 ustar 00root root 0000000 0000000 // Copyright (c) 2022 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.
//go:build linux
// +build linux
package cgroups
import (
"os"
"os/user"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCGroupsIsCGroupV2(t *testing.T) {
tests := []struct {
name string
isV2 bool
wantErr bool // should be false if isV2 is true
}{
{
name: "mountinfo",
isV2: false,
wantErr: false,
},
{
name: "mountinfo-v1-v2",
isV2: false,
wantErr: false,
},
{
name: "mountinfo-v2",
isV2: true,
wantErr: false,
},
{
name: "mountinfo-nonexistent",
isV2: false,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mountInfoPath := filepath.Join(testDataProcPath, "v2", tt.name)
procCgroupPath := filepath.Join(testDataProcPath, "v2", "cgroup-root")
_, err := newCGroups2From(mountInfoPath, procCgroupPath)
switch {
case tt.wantErr:
assert.Error(t, err)
case !tt.isV2:
assert.ErrorIs(t, err, ErrNotV2)
default:
assert.NoError(t, err)
}
})
}
}
func TestCGroupsCPUQuotaV2(t *testing.T) {
tests := []struct {
name string
want float64
wantOK bool
wantErr string
}{
{
name: "set",
want: 2.5,
wantOK: true,
},
{
name: "unset",
want: -1.0,
wantOK: false,
},
{
name: "only-max",
want: 5.0,
wantOK: true,
},
{
name: "invalid-max",
wantErr: `parsing "asdf": invalid syntax`,
},
{
name: "invalid-period",
wantErr: `parsing "njn": invalid syntax`,
},
{
name: "nonexistent",
want: -1.0,
wantOK: false,
},
{
name: "empty",
wantErr: "unexpected EOF",
},
{
name: "too-few-fields",
wantErr: "invalid format",
},
{
name: "too-many-fields",
wantErr: "invalid format",
},
{
name: "zero-period",
wantErr: "zero value for period is not allowed",
},
}
mountPoint := filepath.Join(testDataCGroupsPath, "v2")
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
quota, defined, err := (&CGroups2{
mountPoint: mountPoint,
groupPath: "/",
cpuMaxFile: tt.name,
}).CPUQuota()
if len(tt.wantErr) > 0 {
require.Error(t, err, tt.name)
assert.Contains(t, err.Error(), tt.wantErr)
} else {
require.NoError(t, err, tt.name)
assert.Equal(t, tt.want, quota, tt.name)
assert.Equal(t, tt.wantOK, defined, tt.name)
}
})
}
}
func TestCGroup2GroupPathDiscovery(t *testing.T) {
tests := []struct {
procCgroup string
wantPath string
}{
{
procCgroup: "cgroup-root",
wantPath: "/",
},
{
procCgroup: "cgroup-subdir",
wantPath: "/Example",
},
}
for _, tt := range tests {
t.Run(tt.procCgroup, func(t *testing.T) {
mountInfoPath := filepath.Join(testDataProcPath, "v2", "mountinfo-v2")
procCgroupPath := filepath.Join(testDataProcPath, "v2", tt.procCgroup)
cgroups, err := newCGroups2From(mountInfoPath, procCgroupPath)
require.NoError(t, err)
assert.Equal(t, tt.wantPath, cgroups.groupPath)
})
}
}
func TestCGroup2GroupPathDiscovery_Errors(t *testing.T) {
t.Run("no matching subsystem", func(t *testing.T) {
mountInfoPath := filepath.Join(testDataProcPath, "v2", "mountinfo-v2")
procCgroupPath := filepath.Join(testDataProcPath, "v2", "cgroup-no-match")
_, err := newCGroups2From(mountInfoPath, procCgroupPath)
assert.ErrorIs(t, err, ErrNotV2)
})
t.Run("invalid subsystems", func(t *testing.T) {
mountInfoPath := filepath.Join(testDataProcPath, "v2", "mountinfo-v2")
procCgroupPath := filepath.Join(testDataProcPath, "v2", "cgroup-invalid")
_, err := newCGroups2From(mountInfoPath, procCgroupPath)
assert.Contains(t, err.Error(), "invalid format for CGroupSubsys")
})
}
func TestCGroupsCPUQuotaV2_OtherErrors(t *testing.T) {
t.Run("no permissions to open", func(t *testing.T) {
if u, err := user.Current(); err == nil && u.Uid == "0" {
t.Skip("running as root, test skipped")
}
t.Parallel()
const name = "foo"
mountPoint := t.TempDir()
require.NoError(t, os.WriteFile(filepath.Join(mountPoint, name), nil /* write only*/, 0222))
_, _, err := (&CGroups2{mountPoint: mountPoint, groupPath: "/", cpuMaxFile: name}).CPUQuota()
require.Error(t, err)
assert.Contains(t, err.Error(), "permission denied")
})
}
automaxprocs-1.5.3/internal/cgroups/cgroups_test.go 0000664 0000000 0000000 00000010012 14456021071 0022605 0 ustar 00root root 0000000 0000000 // 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.
//go:build linux
// +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: "zero-period",
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.5.3/internal/cgroups/doc.go 0000664 0000000 0000000 00000002374 14456021071 0020645 0 ustar 00root root 0000000 0000000 // 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.5.3/internal/cgroups/errors.go 0000664 0000000 0000000 00000003525 14456021071 0021413 0 ustar 00root root 0000000 0000000 // 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.
//go:build linux
// +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.5.3/internal/cgroups/mountpoint.go 0000664 0000000 0000000 00000012017 14456021071 0022307 0 ustar 00root root 0000000 0000000 // 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.
//go:build linux
// +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 {
// End of optional fields.
fsTypeStart := _miFieldIDOptionalFields + i + 1
// Now we know where the optional fields end, split the line again with a
// limit to avoid issues with spaces in super options as present on WSL.
fields = strings.SplitN(line, _mountInfoSep, fsTypeStart+_miFieldCountSecondHalf)
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.5.3/internal/cgroups/mountpoint_test.go 0000664 0000000 0000000 00000015143 14456021071 0023351 0 ustar 00root root 0000000 0000000 // 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.
//go:build linux
// +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"},
},
},
{
name: "wsl",
line: `560 77 0:138 / /Docker/host rw,noatime - 9p drvfs rw,dirsync,aname=drvfs;path=C:\Program Files\Docker\Docker\resources;symlinkroot=/mnt/,mmap,access=client,msize=262144,trans=virtio`,
expected: &MountPoint{
MountID: 560,
ParentID: 77,
DeviceID: "0:138",
Root: "/",
MountPoint: "/Docker/host",
Options: []string{"rw", "noatime"},
OptionalFields: []string{},
FSType: "9p",
MountSource: "drvfs",
SuperOptions: []string{
"rw",
"dirsync",
`aname=drvfs;path=C:\Program Files\Docker\Docker\resources;symlinkroot=/mnt/`,
"mmap",
"access=client",
"msize=262144",
"trans=virtio",
},
},
},
}
for _, tt := range testTable {
mountPoint, err := NewMountPointFromLine(tt.line)
if assert.NoError(t, err, tt.name) {
assert.Equal(t, tt.expected, mountPoint, 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.5.3/internal/cgroups/subsys.go 0000664 0000000 0000000 00000005502 14456021071 0021424 0 ustar 00root root 0000000 0000000 // 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.
//go:build linux
// +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.5.3/internal/cgroups/subsys_test.go 0000664 0000000 0000000 00000006201 14456021071 0022460 0 ustar 00root root 0000000 0000000 // 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.
//go:build linux
// +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.5.3/internal/cgroups/testdata/ 0000775 0000000 0000000 00000000000 14456021071 0021354 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/ 0000775 0000000 0000000 00000000000 14456021071 0023036 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/cpu/ 0000775 0000000 0000000 00000000000 14456021071 0023625 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/cpu/cpu.cfs_period_us 0000664 0000000 0000000 00000000007 14456021071 0027157 0 ustar 00root root 0000000 0000000 100000
automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/cpu/cpu.cfs_quota_us 0000664 0000000 0000000 00000000007 14456021071 0027026 0 ustar 00root root 0000000 0000000 600000
automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/empty/ 0000775 0000000 0000000 00000000000 14456021071 0024174 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/empty/cpu.cfs_quota_us 0000664 0000000 0000000 00000000000 14456021071 0027366 0 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/invalid/ 0000775 0000000 0000000 00000000000 14456021071 0024464 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/invalid/cpu.cfs_quota_us 0000664 0000000 0000000 00000000017 14456021071 0027666 0 ustar 00root root 0000000 0000000 non-an-integer
automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/undefined-period/ 0000775 0000000 0000000 00000000000 14456021071 0026257 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/undefined-period/cpu.cfs_quota_us 0000664 0000000 0000000 00000000007 14456021071 0031460 0 ustar 00root root 0000000 0000000 800000
automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/undefined/ 0000775 0000000 0000000 00000000000 14456021071 0024777 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/undefined/cpu.cfs_period_us 0000664 0000000 0000000 00000000007 14456021071 0030331 0 ustar 00root root 0000000 0000000 100000
automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/undefined/cpu.cfs_quota_us 0000664 0000000 0000000 00000000003 14456021071 0030174 0 ustar 00root root 0000000 0000000 -1
automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/v2/ 0000775 0000000 0000000 00000000000 14456021071 0023365 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/v2/empty 0000664 0000000 0000000 00000000000 14456021071 0024434 0 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/v2/invalid-max 0000664 0000000 0000000 00000000013 14456021071 0025513 0 ustar 00root root 0000000 0000000 asdf 100000 automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/v2/invalid-period 0000664 0000000 0000000 00000000012 14456021071 0026207 0 ustar 00root root 0000000 0000000 500000 njn automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/v2/only-max 0000664 0000000 0000000 00000000006 14456021071 0025050 0 ustar 00root root 0000000 0000000 500000 automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/v2/set 0000664 0000000 0000000 00000000015 14456021071 0024077 0 ustar 00root root 0000000 0000000 250000 100000 automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/v2/too-few-fields 0000664 0000000 0000000 00000000001 14456021071 0026123 0 ustar 00root root 0000000 0000000
automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/v2/too-many-fields 0000664 0000000 0000000 00000000022 14456021071 0026311 0 ustar 00root root 0000000 0000000 250000 100000 100
automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/v2/unset 0000664 0000000 0000000 00000000012 14456021071 0024437 0 ustar 00root root 0000000 0000000 max 100000 automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/v2/zero-period 0000664 0000000 0000000 00000000011 14456021071 0025537 0 ustar 00root root 0000000 0000000 250000 0
automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/zero-period/ 0000775 0000000 0000000 00000000000 14456021071 0025275 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/zero-period/cpu.cfs_period_us 0000664 0000000 0000000 00000000002 14456021071 0030622 0 ustar 00root root 0000000 0000000 0
automaxprocs-1.5.3/internal/cgroups/testdata/cgroups/zero-period/cpu.cfs_quota_us 0000664 0000000 0000000 00000000007 14456021071 0030476 0 ustar 00root root 0000000 0000000 600000
automaxprocs-1.5.3/internal/cgroups/testdata/proc/ 0000775 0000000 0000000 00000000000 14456021071 0022317 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/testdata/proc/cgroups/ 0000775 0000000 0000000 00000000000 14456021071 0024001 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/testdata/proc/cgroups/cgroup 0000664 0000000 0000000 00000000070 14456021071 0025220 0 ustar 00root root 0000000 0000000 3:memory:/docker/large
2:cpu,cpuacct:/docker
1:cpuset:/
automaxprocs-1.5.3/internal/cgroups/testdata/proc/cgroups/mountinfo 0000664 0000000 0000000 00000001327 14456021071 0025745 0 ustar 00root root 0000000 0000000 1 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.5.3/internal/cgroups/testdata/proc/invalid-cgroup/ 0000775 0000000 0000000 00000000000 14456021071 0025242 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/testdata/proc/invalid-cgroup/cgroup 0000664 0000000 0000000 00000000031 14456021071 0026456 0 ustar 00root root 0000000 0000000 1:cpu:/cpu
invalid-line:
automaxprocs-1.5.3/internal/cgroups/testdata/proc/invalid-mountinfo/ 0000775 0000000 0000000 00000000000 14456021071 0025761 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/testdata/proc/invalid-mountinfo/mountinfo 0000664 0000000 0000000 00000000061 14456021071 0027717 0 ustar 00root root 0000000 0000000 1 0 8:1 / / rw,noatime shared:1 - ext4 /dev/sda1
automaxprocs-1.5.3/internal/cgroups/testdata/proc/untranslatable/ 0000775 0000000 0000000 00000000000 14456021071 0025336 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/testdata/proc/untranslatable/cgroup 0000664 0000000 0000000 00000000040 14456021071 0026552 0 ustar 00root root 0000000 0000000 1:cpu:/docker
2:cpuacct:/docker
automaxprocs-1.5.3/internal/cgroups/testdata/proc/untranslatable/mountinfo 0000664 0000000 0000000 00000000337 14456021071 0027302 0 ustar 00root root 0000000 0000000 31 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.5.3/internal/cgroups/testdata/proc/v2/ 0000775 0000000 0000000 00000000000 14456021071 0022646 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/cgroups/testdata/proc/v2/cgroup-invalid 0000664 0000000 0000000 00000000021 14456021071 0025505 0 ustar 00root root 0000000 0000000 0\using\colons\/
automaxprocs-1.5.3/internal/cgroups/testdata/proc/v2/cgroup-no-match 0000664 0000000 0000000 00000000005 14456021071 0025567 0 ustar 00root root 0000000 0000000 1::/
automaxprocs-1.5.3/internal/cgroups/testdata/proc/v2/cgroup-root 0000664 0000000 0000000 00000000004 14456021071 0025043 0 ustar 00root root 0000000 0000000 0::/ automaxprocs-1.5.3/internal/cgroups/testdata/proc/v2/cgroup-subdir 0000664 0000000 0000000 00000000014 14456021071 0025351 0 ustar 00root root 0000000 0000000 0::/Example
automaxprocs-1.5.3/internal/cgroups/testdata/proc/v2/mountinfo 0000664 0000000 0000000 00000001327 14456021071 0024612 0 ustar 00root root 0000000 0000000 1 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.5.3/internal/cgroups/testdata/proc/v2/mountinfo-v1-v2 0000664 0000000 0000000 00000003062 14456021071 0025461 0 ustar 00root root 0000000 0000000 33 24 0:28 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:9 - tmpfs tmpfs ro,mode=755,inode64
34 33 0:29 / /sys/fs/cgroup/unified rw,nosuid,nodev,noexec,relatime shared:10 - cgroup2 cgroup2 rw,nsdelegate
35 33 0:30 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:11 - cgroup cgroup rw,xattr,name=systemd
39 33 0:34 / /sys/fs/cgroup/misc rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,misc
40 33 0:35 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,net_cls,net_prio
41 33 0:36 / /sys/fs/cgroup/rdma rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,rdma
42 33 0:37 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:19 - cgroup cgroup rw,memory
43 33 0:38 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:20 - cgroup cgroup rw,blkio
44 33 0:39 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:21 - cgroup cgroup rw,cpu,cpuacct
45 33 0:40 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:22 - cgroup cgroup rw,pids
46 33 0:41 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:23 - cgroup cgroup rw,hugetlb
47 33 0:42 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:24 - cgroup cgroup rw,freezer
48 33 0:43 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:25 - cgroup cgroup rw,perf_event
49 33 0:44 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:26 - cgroup cgroup rw,devices
50 33 0:45 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:27 - cgroup cgroup rw,cpuset
automaxprocs-1.5.3/internal/cgroups/testdata/proc/v2/mountinfo-v2 0000664 0000000 0000000 00000000303 14456021071 0025130 0 ustar 00root root 0000000 0000000 34 33 0:29 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:10 - cgroup2 cgroup rw,nsdelegate
34 33 0:29 / /sys/fs/foo rw,nosuid,nodev,noexec,relatime shared:10 - foo cgroup rw,nsdelegate
automaxprocs-1.5.3/internal/cgroups/util_test.go 0000664 0000000 0000000 00000002764 14456021071 0022117 0 ustar 00root root 0000000 0000000 // 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.
//go:build linux
// +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.5.3/internal/runtime/ 0000775 0000000 0000000 00000000000 14456021071 0017544 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/internal/runtime/cpu_quota_linux.go 0000664 0000000 0000000 00000004145 14456021071 0023316 0 ustar 00root root 0000000 0000000 // 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.
//go:build linux
// +build linux
package runtime
import (
"errors"
"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 := newQueryer()
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
}
type queryer interface {
CPUQuota() (float64, bool, error)
}
var (
_newCgroups2 = cg.NewCGroups2ForCurrentProcess
_newCgroups = cg.NewCGroupsForCurrentProcess
)
func newQueryer() (queryer, error) {
cgroups, err := _newCgroups2()
if err == nil {
return cgroups, nil
}
if errors.Is(err, cg.ErrNotV2) {
return _newCgroups()
}
return nil, err
}
automaxprocs-1.5.3/internal/runtime/cpu_quota_linux_test.go 0000664 0000000 0000000 00000004760 14456021071 0024360 0 ustar 00root root 0000000 0000000 // Copyright (c) 2022 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.
//go:build linux
// +build linux
package runtime
import (
"errors"
"fmt"
"testing"
"github.com/prashantv/gostub"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/automaxprocs/internal/cgroups"
)
func TestNewQueryer(t *testing.T) {
t.Run("use v2", func(t *testing.T) {
stubs := newStubs(t)
c2 := new(cgroups.CGroups2)
stubs.StubFunc(&_newCgroups2, c2, nil)
got, err := newQueryer()
require.NoError(t, err)
assert.Same(t, c2, got)
})
t.Run("v2 error", func(t *testing.T) {
stubs := newStubs(t)
giveErr := errors.New("great sadness")
stubs.StubFunc(&_newCgroups2, nil, giveErr)
_, err := newQueryer()
assert.ErrorIs(t, err, giveErr)
})
t.Run("use v1", func(t *testing.T) {
stubs := newStubs(t)
stubs.StubFunc(&_newCgroups2, nil,
fmt.Errorf("not v2: %w", cgroups.ErrNotV2))
c1 := make(cgroups.CGroups)
stubs.StubFunc(&_newCgroups, c1, nil)
got, err := newQueryer()
require.NoError(t, err)
assert.IsType(t, c1, got, "must be a v1 cgroup")
})
t.Run("v1 error", func(t *testing.T) {
stubs := newStubs(t)
stubs.StubFunc(&_newCgroups2, nil, cgroups.ErrNotV2)
giveErr := errors.New("great sadness")
stubs.StubFunc(&_newCgroups, nil, giveErr)
_, err := newQueryer()
assert.ErrorIs(t, err, giveErr)
})
}
func newStubs(t *testing.T) *gostub.Stubs {
stubs := gostub.New()
t.Cleanup(stubs.Reset)
return stubs
}
automaxprocs-1.5.3/internal/runtime/cpu_quota_unsupported.go 0000664 0000000 0000000 00000002651 14456021071 0024547 0 ustar 00root root 0000000 0000000 // 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.
//go:build !linux
// +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.5.3/internal/runtime/runtime.go 0000664 0000000 0000000 00000002750 14456021071 0021562 0 ustar 00root root 0000000 0000000 // 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 returned when CPU quota is smaller than the min value
CPUQuotaMinUsed
)
automaxprocs-1.5.3/maxprocs/ 0000775 0000000 0000000 00000000000 14456021071 0016101 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/maxprocs/example_test.go 0000664 0000000 0000000 00000003162 14456021071 0021124 0 ustar 00root root 0000000 0000000 // 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.5.3/maxprocs/maxprocs.go 0000664 0000000 0000000 00000007727 14456021071 0020301 0 ustar 00root root 0000000 0000000 // 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 overridden 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.5.3/maxprocs/maxprocs_test.go 0000664 0000000 0000000 00000013501 14456021071 0021323 0 ustar 00root root 0000000 0000000 // 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.5.3/maxprocs/version.go 0000664 0000000 0000000 00000002267 14456021071 0020124 0 ustar 00root root 0000000 0000000 // 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.5.2"
automaxprocs-1.5.3/tools/ 0000775 0000000 0000000 00000000000 14456021071 0015405 5 ustar 00root root 0000000 0000000 automaxprocs-1.5.3/tools/go.mod 0000664 0000000 0000000 00000001022 14456021071 0016506 0 ustar 00root root 0000000 0000000 module go.uber.org/automaxprocs/tools
go 1.18
require (
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616
honnef.co/go/tools v0.3.0-0.dev.0.20220306074811-23e1086441d2
)
require (
github.com/BurntSushi/toml v1.0.0 // indirect
golang.org/x/exp/typeparams v0.0.0-20220328175248-053ad81199eb // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/sys v0.1.0 // indirect
golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
)
automaxprocs-1.5.3/tools/go.sum 0000664 0000000 0000000 00000005267 14456021071 0016552 0 ustar 00root root 0000000 0000000 github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp/typeparams v0.0.0-20220328175248-053ad81199eb h1:fP6C8Xutcp5AlakmT/SkQot0pMicROAsEX7OfNPuG10=
golang.org/x/exp/typeparams v0.0.0-20220328175248-053ad81199eb/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
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/sync v0.0.0-20190423024810-112230192c58/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.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
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=
honnef.co/go/tools v0.3.0-0.dev.0.20220306074811-23e1086441d2 h1:utiSabORbG/JeX7MlmKMdmsjwom2+v8zmdb6SoBe4UY=
honnef.co/go/tools v0.3.0-0.dev.0.20220306074811-23e1086441d2/go.mod h1:dZI0HmIvwDMW8owtLBJxTHoeX48yuF5p5pDy3y73jGU=
automaxprocs-1.5.3/tools/tools.go 0000664 0000000 0000000 00000002411 14456021071 0017072 0 ustar 00root root 0000000 0000000 // 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.
//go:build tools
// +build tools
package tools
import (
// Tools we use during development.
_ "golang.org/x/lint/golint"
_ "honnef.co/go/tools/cmd/staticcheck"
)