<commit_before>// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package driver implements the core pprof functionality. It can be
// parameterized with a flag implementation, fetch and symbolize
// mechanisms.
package driver

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/google/pprof/internal/plugin"
	"github.com/google/pprof/internal/report"
	"github.com/google/pprof/profile"
)

// PProf acquires a profile, and symbolizes it using a profile
// manager. Then it generates a report formatted according to the
// options selected through the flags package.
func PProf(eo *plugin.Options) error {
	// Remove any temporary files created during pprof processing.
	defer cleanupTempFiles()

	o := setDefaults(eo)

	src, cmd, err := parseFlags(o)
	if err != nil {
		return err
	}

	p, err := fetchProfiles(src, o)
	if err != nil {
		return err
	}

	if cmd != nil {
		return generateReport(p, cmd, pprofVariables, o)
	}

	if src.HTTPHostport != "" {
		return serveWebInterface(src.HTTPHostport, p, o)
	}
	return interactive(p, o)
}

func generateRawReport(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) (*command, *report.Report, error) {
	p = p.Copy() // Prevent modification to the incoming profile.

	// Identify units of numeric tags in profile.
	numLabelUnits := identifyNumLabelUnits(p, o.UI)

	// Get report output format
	c := pprofCommands[cmd[0]]
	if c == nil {
		panic("unexpected nil command")
	}

	vars = applyCommandOverrides(cmd[0], c.format, vars)

	// Delay focus after configuring report to get percentages on all samples.
	relative := vars["relative_percentages"].boolValue()
	if relative {
		if err := applyFocus(p, numLabelUnits, vars, o.UI); err != nil {
			return nil, nil, err
		}
	}
	ropt, err := reportOptions(p, numLabelUnits, vars)
	if err != nil {
		return nil, nil, err
	}
	ropt.OutputFormat = c.format
	if len(cmd) == 2 {
		s, err := regexp.Compile(cmd[1])
		if err != nil {
			return nil, nil, fmt.Errorf("parsing argument regexp %s: %v", cmd[1], err)
		}
		ropt.Symbol = s
	}

	rpt := report.New(p, ropt)
	if !relative {
		if err := applyFocus(p, numLabelUnits, vars, o.UI); err != nil {
			return nil, nil, err
		}
	}
	if err := aggregate(p, vars); err != nil {
		return nil, nil, err
	}

	return c, rpt, nil
}

func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) error {
	c, rpt, err := generateRawReport(p, cmd, vars, o)
	if err != nil {
		return err
	}

	// Generate the report.
	dst := new(bytes.Buffer)
	if err := report.Generate(dst, rpt, o.Obj); err != nil {
		return err
	}
	src := dst

	// If necessary, perform any data post-processing.
	if c.postProcess != nil {
		dst = new(bytes.Buffer)
		if err := c.postProcess(src, dst, o.UI); err != nil {
			return err
		}
		src = dst
	}

	// If no output is specified, use default visualizer.
	output := vars["output"].value
	if output == "" {
		if c.visualizer != nil {
			return c.visualizer(src, os.Stdout, o.UI)
		}
		_, err := src.WriteTo(os.Stdout)
		return err
	}

	// Output to specified file.
	o.UI.PrintErr("Generating report in ", output)
	out, err := o.Writer.Open(output)
	if err != nil {
		return err
	}
	if _, err := src.WriteTo(out); err != nil {
		out.Close()
		return err
	}
	return out.Close()
}

func applyCommandOverrides(cmd string, outputFormat int, v variables) variables {
	// Some report types override the trim flag to false below. This is to make
	// sure the default heuristics of excluding insignificant nodes and edges
	// from the call graph do not apply. One example where it is important is
	// annotated source or disassembly listing. Those reports run on a specific
	// function (or functions), but the trimming is applied before the function
	// data is selected. So, with trimming enabled, the report could end up
	// showing no data if the specified function is "uninteresting" as far as the
	// trimming is concerned.
	trim := v["trim"].boolValue()

	switch cmd {
	case "disasm", "weblist":
		trim = false
		v.set("addresses", "t")
		v.set("noinlines", "t")
	case "peek":
		trim = false
	case "list":
		trim = false
		v.set("lines", "t")
		// Do not force 'noinlines' to be false so that specifying
		// "-list foo -noinlines" is supported and works as expected.
	case "text", "top", "topproto":
		if v["nodecount"].intValue() == -1 {
			v.set("nodecount", "0")
		}
	default:
		if v["nodecount"].intValue() == -1 {
			v.set("nodecount", "80")
		}
	}

	switch outputFormat {
	case report.Proto, report.Raw, report.Callgrind:
		trim = false
		v.set("addresses", "t")
		v.set("noinlines", "f")
	}

	if !trim {
		v.set("nodecount", "0")
		v.set("nodefraction", "0")
		v.set("edgefraction", "0")
	}
	return v
}

func aggregate(prof *profile.Profile, v variables) error {
	var function, filename, linenumber, address bool
	inlines := !v["noinlines"].boolValue()
	switch {
	case v["addresses"].boolValue():
		if inlines {
			return nil
		}
		function = true
		filename = true
		linenumber = true
		address = true
	case v["lines"].boolValue():
		function = true
		filename = true
		linenumber = true
	case v["files"].boolValue():
		filename = true
	case v["functions"].boolValue():
		function = true
	case v["filefunctions"].boolValue():
		function = true
		filename = true
	default:
		return fmt.Errorf("unexpected granularity")
	}
	return prof.Aggregate(inlines, function, filename, linenumber, address)
}

func reportOptions(p *profile.Profile, numLabelUnits map[string]string, vars variables) (*report.Options, error) {
	si, mean := vars["sample_index"].value, vars["mean"].boolValue()
	value, meanDiv, sample, err := sampleFormat(p, si, mean)
	if err != nil {
		return nil, err
	}

	stype := sample.Type
	if mean {
		stype = "mean_" + stype
	}

	if vars["divide_by"].floatValue() == 0 {
		return nil, fmt.Errorf("zero divisor specified")
	}

	var filters []string
	for _, k := range []string{"focus", "ignore", "hide", "show", "show_from", "tagfocus", "tagignore", "tagshow", "taghide"} {
		v := vars[k].value
		if v != "" {
			filters = append(filters, k+"="+v)
		}
	}

	ropt := &report.Options{
		CumSort:      vars["cum"].boolValue(),
		CallTree:     vars["call_tree"].boolValue(),
		DropNegative: vars["drop_negative"].boolValue(),

		CompactLabels: vars["compact_labels"].boolValue(),
		Ratio:         1 / vars["divide_by"].floatValue(),

		NodeCount:    vars["nodecount"].intValue(),
		NodeFraction: vars["nodefraction"].floatValue(),
		EdgeFraction: vars["edgefraction"].floatValue(),

		ActiveFilters: filters,
		NumLabelUnits: numLabelUnits,

		SampleValue:       value,
		SampleMeanDivisor: meanDiv,
		SampleType:        stype,
		SampleUnit:        sample.Unit,

		OutputUnit: vars["unit"].value,

		SourcePath: vars["source_path"].stringValue(),
		TrimPath:   vars["trim_path"].stringValue(),
	}

	if len(p.Mapping) > 0 && p.Mapping[0].File != "" {
		ropt.Title = filepath.Base(p.Mapping[0].File)
	}

	return ropt, nil
}

// identifyNumLabelUnits returns a map of numeric label keys to the units
// associated with those keys.
func identifyNumLabelUnits(p *profile.Profile, ui plugin.UI) map[string]string {
	numLabelUnits, ignoredUnits := p.NumLabelUnits()

	// Print errors for tags with multiple units associated with
	// a single key.
	for k, units := range ignoredUnits {
		ui.PrintErr(fmt.Sprintf("For tag %s used unit %s, also encountered unit(s) %s", k, numLabelUnits[k], strings.Join(units, ", ")))
	}
	return numLabelUnits
}

type sampleValueFunc func([]int64) int64

// sampleFormat returns a function to extract values out of a profile.Sample,
// and the type/units of those values.
func sampleFormat(p *profile.Profile, sampleIndex string, mean bool) (value, meanDiv sampleValueFunc, v *profile.ValueType, err error) {
	if len(p.SampleType) == 0 {
		return nil, nil, nil, fmt.Errorf("profile has no samples")
	}
	index, err := p.SampleIndexByName(sampleIndex)
	if err != nil {
		return nil, nil, nil, err
	}
	value = valueExtractor(index)
	if mean {
		meanDiv = valueExtractor(0)
	}
	v = p.SampleType[index]
	return
}

func valueExtractor(ix int) sampleValueFunc {
	return func(v []int64) int64 {
		return v[ix]
	}
}
<commit_msg>Add a comment on why 'noinlines' is forced for disasm reports. (#422)<commit_after>// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package driver implements the core pprof functionality. It can be
// parameterized with a flag implementation, fetch and symbolize
// mechanisms.
package driver

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/google/pprof/internal/plugin"
	"github.com/google/pprof/internal/report"
	"github.com/google/pprof/profile"
)

// PProf acquires a profile, and symbolizes it using a profile
// manager. Then it generates a report formatted according to the
// options selected through the flags package.
func PProf(eo *plugin.Options) error {
	// Remove any temporary files created during pprof processing.
	defer cleanupTempFiles()

	o := setDefaults(eo)

	src, cmd, err := parseFlags(o)
	if err != nil {
		return err
	}

	p, err := fetchProfiles(src, o)
	if err != nil {
		return err
	}

	if cmd != nil {
		return generateReport(p, cmd, pprofVariables, o)
	}

	if src.HTTPHostport != "" {
		return serveWebInterface(src.HTTPHostport, p, o)
	}
	return interactive(p, o)
}

func generateRawReport(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) (*command, *report.Report, error) {
	p = p.Copy() // Prevent modification to the incoming profile.

	// Identify units of numeric tags in profile.
	numLabelUnits := identifyNumLabelUnits(p, o.UI)

	// Get report output format
	c := pprofCommands[cmd[0]]
	if c == nil {
		panic("unexpected nil command")
	}

	vars = applyCommandOverrides(cmd[0], c.format, vars)

	// Delay focus after configuring report to get percentages on all samples.
	relative := vars["relative_percentages"].boolValue()
	if relative {
		if err := applyFocus(p, numLabelUnits, vars, o.UI); err != nil {
			return nil, nil, err
		}
	}
	ropt, err := reportOptions(p, numLabelUnits, vars)
	if err != nil {
		return nil, nil, err
	}
	ropt.OutputFormat = c.format
	if len(cmd) == 2 {
		s, err := regexp.Compile(cmd[1])
		if err != nil {
			return nil, nil, fmt.Errorf("parsing argument regexp %s: %v", cmd[1], err)
		}
		ropt.Symbol = s
	}

	rpt := report.New(p, ropt)
	if !relative {
		if err := applyFocus(p, numLabelUnits, vars, o.UI); err != nil {
			return nil, nil, err
		}
	}
	if err := aggregate(p, vars); err != nil {
		return nil, nil, err
	}

	return c, rpt, nil
}

func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) error {
	c, rpt, err := generateRawReport(p, cmd, vars, o)
	if err != nil {
		return err
	}

	// Generate the report.
	dst := new(bytes.Buffer)
	if err := report.Generate(dst, rpt, o.Obj); err != nil {
		return err
	}
	src := dst

	// If necessary, perform any data post-processing.
	if c.postProcess != nil {
		dst = new(bytes.Buffer)
		if err := c.postProcess(src, dst, o.UI); err != nil {
			return err
		}
		src = dst
	}

	// If no output is specified, use default visualizer.
	output := vars["output"].value
	if output == "" {
		if c.visualizer != nil {
			return c.visualizer(src, os.Stdout, o.UI)
		}
		_, err := src.WriteTo(os.Stdout)
		return err
	}

	// Output to specified file.
	o.UI.PrintErr("Generating report in ", output)
	out, err := o.Writer.Open(output)
	if err != nil {
		return err
	}
	if _, err := src.WriteTo(out); err != nil {
		out.Close()
		return err
	}
	return out.Close()
}

func applyCommandOverrides(cmd string, outputFormat int, v variables) variables {
	// Some report types override the trim flag to false below. This is to make
	// sure the default heuristics of excluding insignificant nodes and edges
	// from the call graph do not apply. One example where it is important is
	// annotated source or disassembly listing. Those reports run on a specific
	// function (or functions), but the trimming is applied before the function
	// data is selected. So, with trimming enabled, the report could end up
	// showing no data if the specified function is "uninteresting" as far as the
	// trimming is concerned.
	trim := v["trim"].boolValue()

	switch cmd {
	case "disasm", "weblist":
		trim = false
		v.set("addresses", "t")
		// Force the 'noinlines' mode so that source locations for a given address
		// collapse and there is only one for the given address. Without this
		// cumulative metrics would be double-counted when annotating the assembly.
		// This is because the merge is done by address and in case of an inlined
		// stack each of the inlined entries is a separate callgraph node.
		v.set("noinlines", "t")
	case "peek":
		trim = false
	case "list":
		trim = false
		v.set("lines", "t")
		// Do not force 'noinlines' to be false so that specifying
		// "-list foo -noinlines" is supported and works as expected.
	case "text", "top", "topproto":
		if v["nodecount"].intValue() == -1 {
			v.set("nodecount", "0")
		}
	default:
		if v["nodecount"].intValue() == -1 {
			v.set("nodecount", "80")
		}
	}

	switch outputFormat {
	case report.Proto, report.Raw, report.Callgrind:
		trim = false
		v.set("addresses", "t")
		v.set("noinlines", "f")
	}

	if !trim {
		v.set("nodecount", "0")
		v.set("nodefraction", "0")
		v.set("edgefraction", "0")
	}
	return v
}

func aggregate(prof *profile.Profile, v variables) error {
	var function, filename, linenumber, address bool
	inlines := !v["noinlines"].boolValue()
	switch {
	case v["addresses"].boolValue():
		if inlines {
			return nil
		}
		function = true
		filename = true
		linenumber = true
		address = true
	case v["lines"].boolValue():
		function = true
		filename = true
		linenumber = true
	case v["files"].boolValue():
		filename = true
	case v["functions"].boolValue():
		function = true
	case v["filefunctions"].boolValue():
		function = true
		filename = true
	default:
		return fmt.Errorf("unexpected granularity")
	}
	return prof.Aggregate(inlines, function, filename, linenumber, address)
}

func reportOptions(p *profile.Profile, numLabelUnits map[string]string, vars variables) (*report.Options, error) {
	si, mean := vars["sample_index"].value, vars["mean"].boolValue()
	value, meanDiv, sample, err := sampleFormat(p, si, mean)
	if err != nil {
		return nil, err
	}

	stype := sample.Type
	if mean {
		stype = "mean_" + stype
	}

	if vars["divide_by"].floatValue() == 0 {
		return nil, fmt.Errorf("zero divisor specified")
	}

	var filters []string
	for _, k := range []string{"focus", "ignore", "hide", "show", "show_from", "tagfocus", "tagignore", "tagshow", "taghide"} {
		v := vars[k].value
		if v != "" {
			filters = append(filters, k+"="+v)
		}
	}

	ropt := &report.Options{
		CumSort:      vars["cum"].boolValue(),
		CallTree:     vars["call_tree"].boolValue(),
		DropNegative: vars["drop_negative"].boolValue(),

		CompactLabels: vars["compact_labels"].boolValue(),
		Ratio:         1 / vars["divide_by"].floatValue(),

		NodeCount:    vars["nodecount"].intValue(),
		NodeFraction: vars["nodefraction"].floatValue(),
		EdgeFraction: vars["edgefraction"].floatValue(),

		ActiveFilters: filters,
		NumLabelUnits: numLabelUnits,

		SampleValue:       value,
		SampleMeanDivisor: meanDiv,
		SampleType:        stype,
		SampleUnit:        sample.Unit,

		OutputUnit: vars["unit"].value,

		SourcePath: vars["source_path"].stringValue(),
		TrimPath:   vars["trim_path"].stringValue(),
	}

	if len(p.Mapping) > 0 && p.Mapping[0].File != "" {
		ropt.Title = filepath.Base(p.Mapping[0].File)
	}

	return ropt, nil
}

// identifyNumLabelUnits returns a map of numeric label keys to the units
// associated with those keys.
func identifyNumLabelUnits(p *profile.Profile, ui plugin.UI) map[string]string {
	numLabelUnits, ignoredUnits := p.NumLabelUnits()

	// Print errors for tags with multiple units associated with
	// a single key.
	for k, units := range ignoredUnits {
		ui.PrintErr(fmt.Sprintf("For tag %s used unit %s, also encountered unit(s) %s", k, numLabelUnits[k], strings.Join(units, ", ")))
	}
	return numLabelUnits
}

type sampleValueFunc func([]int64) int64

// sampleFormat returns a function to extract values out of a profile.Sample,
// and the type/units of those values.
func sampleFormat(p *profile.Profile, sampleIndex string, mean bool) (value, meanDiv sampleValueFunc, v *profile.ValueType, err error) {
	if len(p.SampleType) == 0 {
		return nil, nil, nil, fmt.Errorf("profile has no samples")
	}
	index, err := p.SampleIndexByName(sampleIndex)
	if err != nil {
		return nil, nil, nil, err
	}
	value = valueExtractor(index)
	if mean {
		meanDiv = valueExtractor(0)
	}
	v = p.SampleType[index]
	return
}

func valueExtractor(ix int) sampleValueFunc {
	return func(v []int64) int64 {
		return v[ix]
	}
}
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2020 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package envconfig

import (
	"os"
	"strings"
)

const (
	// XDSBootstrapFileNameEnv is the env variable to set bootstrap file name.
	// Do not use this and read from env directly. Its value is read and kept in
	// variable XDSBootstrapFileName.
	//
	// When both bootstrap FileName and FileContent are set, FileName is used.
	XDSBootstrapFileNameEnv = "GRPC_XDS_BOOTSTRAP"
	// XDSBootstrapFileContentEnv is the env variable to set bootstrap file
	// content. Do not use this and read from env directly. Its value is read
	// and kept in variable XDSBootstrapFileContent.
	//
	// When both bootstrap FileName and FileContent are set, FileName is used.
	XDSBootstrapFileContentEnv = "GRPC_XDS_BOOTSTRAP_CONFIG"

	ringHashSupportEnv           = "GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH"
	clientSideSecuritySupportEnv = "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT"
	aggregateAndDNSSupportEnv    = "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER"
	rbacSupportEnv               = "GRPC_XDS_EXPERIMENTAL_RBAC"
	outlierDetectionSupportEnv   = "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION"
	federationEnv                = "GRPC_EXPERIMENTAL_XDS_FEDERATION"
	rlsInXDSEnv                  = "GRPC_EXPERIMENTAL_XDS_RLS_LB"

	c2pResolverTestOnlyTrafficDirectorURIEnv = "GRPC_TEST_ONLY_GOOGLE_C2P_RESOLVER_TRAFFIC_DIRECTOR_URI"
)

var (
	// XDSBootstrapFileName holds the name of the file which contains xDS
	// bootstrap configuration. Users can specify the location of the bootstrap
	// file by setting the environment variable "GRPC_XDS_BOOTSTRAP".
	//
	// When both bootstrap FileName and FileContent are set, FileName is used.
	XDSBootstrapFileName = os.Getenv(XDSBootstrapFileNameEnv)
	// XDSBootstrapFileContent holds the content of the xDS bootstrap
	// configuration. Users can specify the bootstrap config by setting the
	// environment variable "GRPC_XDS_BOOTSTRAP_CONFIG".
	//
	// When both bootstrap FileName and FileContent are set, FileName is used.
	XDSBootstrapFileContent = os.Getenv(XDSBootstrapFileContentEnv)
	// XDSRingHash indicates whether ring hash support is enabled, which can be
	// disabled by setting the environment variable
	// "GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH" to "false".
	XDSRingHash = !strings.EqualFold(os.Getenv(ringHashSupportEnv), "false")
	// XDSClientSideSecurity is used to control processing of security
	// configuration on the client-side.
	//
	// Note that there is no env var protection for the server-side because we
	// have a brand new API on the server-side and users explicitly need to use
	// the new API to get security integration on the server.
	XDSClientSideSecurity = !strings.EqualFold(os.Getenv(clientSideSecuritySupportEnv), "false")
	// XDSAggregateAndDNS indicates whether processing of aggregated cluster
	// and DNS cluster is enabled, which can be enabled by setting the
	// environment variable
	// "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER" to
	// "true".
	XDSAggregateAndDNS = !strings.EqualFold(os.Getenv(aggregateAndDNSSupportEnv), "false")

	// XDSRBAC indicates whether xDS configured RBAC HTTP Filter is enabled,
	// which can be disabled by setting the environment variable
	// "GRPC_XDS_EXPERIMENTAL_RBAC" to "false".
	XDSRBAC = !strings.EqualFold(os.Getenv(rbacSupportEnv), "false")
	// XDSOutlierDetection indicates whether outlier detection support is
	// enabled, which can be enabled by setting the environment variable
	// "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION" to "true".
	XDSOutlierDetection = strings.EqualFold(os.Getenv(outlierDetectionSupportEnv), "true")
	// XDSFederation indicates whether federation support is enabled.
	XDSFederation = strings.EqualFold(os.Getenv(federationEnv), "true")

	// XDSRLS indicates whether processing of Cluster Specifier plugins and
	// support for the RLS CLuster Specifier is enabled, which can be enabled by
	// setting the environment variable "GRPC_EXPERIMENTAL_XDS_RLS_LB" to
	// "true".
	XDSRLS = strings.EqualFold(os.Getenv(rlsInXDSEnv), "true")

	// C2PResolverTestOnlyTrafficDirectorURI is the TD URI for testing.
	C2PResolverTestOnlyTrafficDirectorURI = os.Getenv(c2pResolverTestOnlyTrafficDirectorURIEnv)
)
<commit_msg>xds: Fixed GoLang regression for Outlier Detection (#5537)<commit_after>/*
 *
 * Copyright 2020 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package envconfig

import (
	"os"
	"strings"
)

const (
	// XDSBootstrapFileNameEnv is the env variable to set bootstrap file name.
	// Do not use this and read from env directly. Its value is read and kept in
	// variable XDSBootstrapFileName.
	//
	// When both bootstrap FileName and FileContent are set, FileName is used.
	XDSBootstrapFileNameEnv = "GRPC_XDS_BOOTSTRAP"
	// XDSBootstrapFileContentEnv is the env variable to set bootstrap file
	// content. Do not use this and read from env directly. Its value is read
	// and kept in variable XDSBootstrapFileContent.
	//
	// When both bootstrap FileName and FileContent are set, FileName is used.
	XDSBootstrapFileContentEnv = "GRPC_XDS_BOOTSTRAP_CONFIG"

	ringHashSupportEnv           = "GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH"
	clientSideSecuritySupportEnv = "GRPC_XDS_EXPERIMENTAL_SECURITY_SUPPORT"
	aggregateAndDNSSupportEnv    = "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER"
	rbacSupportEnv               = "GRPC_XDS_EXPERIMENTAL_RBAC"
	federationEnv                = "GRPC_EXPERIMENTAL_XDS_FEDERATION"
	rlsInXDSEnv                  = "GRPC_EXPERIMENTAL_XDS_RLS_LB"

	c2pResolverTestOnlyTrafficDirectorURIEnv = "GRPC_TEST_ONLY_GOOGLE_C2P_RESOLVER_TRAFFIC_DIRECTOR_URI"
)

var (
	// XDSBootstrapFileName holds the name of the file which contains xDS
	// bootstrap configuration. Users can specify the location of the bootstrap
	// file by setting the environment variable "GRPC_XDS_BOOTSTRAP".
	//
	// When both bootstrap FileName and FileContent are set, FileName is used.
	XDSBootstrapFileName = os.Getenv(XDSBootstrapFileNameEnv)
	// XDSBootstrapFileContent holds the content of the xDS bootstrap
	// configuration. Users can specify the bootstrap config by setting the
	// environment variable "GRPC_XDS_BOOTSTRAP_CONFIG".
	//
	// When both bootstrap FileName and FileContent are set, FileName is used.
	XDSBootstrapFileContent = os.Getenv(XDSBootstrapFileContentEnv)
	// XDSRingHash indicates whether ring hash support is enabled, which can be
	// disabled by setting the environment variable
	// "GRPC_XDS_EXPERIMENTAL_ENABLE_RING_HASH" to "false".
	XDSRingHash = !strings.EqualFold(os.Getenv(ringHashSupportEnv), "false")
	// XDSClientSideSecurity is used to control processing of security
	// configuration on the client-side.
	//
	// Note that there is no env var protection for the server-side because we
	// have a brand new API on the server-side and users explicitly need to use
	// the new API to get security integration on the server.
	XDSClientSideSecurity = !strings.EqualFold(os.Getenv(clientSideSecuritySupportEnv), "false")
	// XDSAggregateAndDNS indicates whether processing of aggregated cluster
	// and DNS cluster is enabled, which can be enabled by setting the
	// environment variable
	// "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER" to
	// "true".
	XDSAggregateAndDNS = !strings.EqualFold(os.Getenv(aggregateAndDNSSupportEnv), "false")

	// XDSRBAC indicates whether xDS configured RBAC HTTP Filter is enabled,
	// which can be disabled by setting the environment variable
	// "GRPC_XDS_EXPERIMENTAL_RBAC" to "false".
	XDSRBAC = !strings.EqualFold(os.Getenv(rbacSupportEnv), "false")
	// XDSOutlierDetection indicates whether outlier detection support is
	// enabled, which can be enabled by setting the environment variable
	// "GRPC_EXPERIMENTAL_ENABLE_OUTLIER_DETECTION" to "true".
	XDSOutlierDetection = false
	// XDSFederation indicates whether federation support is enabled.
	XDSFederation = strings.EqualFold(os.Getenv(federationEnv), "true")

	// XDSRLS indicates whether processing of Cluster Specifier plugins and
	// support for the RLS CLuster Specifier is enabled, which can be enabled by
	// setting the environment variable "GRPC_EXPERIMENTAL_XDS_RLS_LB" to
	// "true".
	XDSRLS = strings.EqualFold(os.Getenv(rlsInXDSEnv), "true")

	// C2PResolverTestOnlyTrafficDirectorURI is the TD URI for testing.
	C2PResolverTestOnlyTrafficDirectorURI = os.Getenv(c2pResolverTestOnlyTrafficDirectorURIEnv)
)
<|endoftext|>


<commit_before>// Copyright 2019 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package jsutil

import (
	"syscall/js"
)

var (
	arrayBuffer  = js.Global().Get("ArrayBuffer")
	uint8Array   = js.Global().Get("Uint8Array")
	float32Array = js.Global().Get("Float32Array")
)

var (
	// temporaryArrayBuffer is a temporary buffer used at gl.readPixels or gl.texSubImage2D.
	// The read data is converted to Go's byte slice as soon as possible.
	// To avoid often allocating ArrayBuffer, reuse the buffer whenever possible.
	temporaryArrayBuffer = arrayBuffer.New(16)

	// temporaryUint8Array is a Uint8ArrayBuffer whose underlying buffer is always temporaryArrayBuffer.
	temporaryUint8Array = uint8Array.New(temporaryArrayBuffer)

	// temporaryFloat32Array is a Float32ArrayBuffer whose underlying buffer is always temporaryArrayBuffer.
	temporaryFloat32Array = float32Array.New(temporaryArrayBuffer)
)

func ensureTemporaryArrayBufferSize(byteLength int) {
	if bufl := temporaryArrayBuffer.Get("byteLength").Int(); bufl < byteLength {
		for bufl < byteLength {
			bufl *= 2
		}
		temporaryArrayBuffer = arrayBuffer.New(bufl)
	}
	if temporaryUint8Array.Get("byteLength").Int() < temporaryArrayBuffer.Get("byteLength").Int() {
		temporaryUint8Array = uint8Array.New(temporaryArrayBuffer)
	}
	if temporaryFloat32Array.Get("byteLength").Int() < temporaryArrayBuffer.Get("byteLength").Int() {
		temporaryFloat32Array = float32Array.New(temporaryArrayBuffer)
	}
}

// TemporaryUint8Array returns a Uint8Array whose length is at least minLength.
// Be careful that the length can exceed the given minLength.
// data must be a slice of a numeric type for initialization, or nil if you don't need initialization.
func TemporaryUint8Array(minLength int, data interface{}) js.Value {
	ensureTemporaryArrayBufferSize(minLength)
	if data != nil {
		copySliceToTemporaryArrayBuffer(data)
	}
	return temporaryUint8Array
}

// TemporaryFloat32Array returns a Float32Array whose length is at least minLength.
// Be careful that the length can exceed the given minLength.
// data must be a slice of a numeric type for initialization, or nil if you don't need initialization.
func TemporaryFloat32Array(minLength int, data interface{}) js.Value {
	ensureTemporaryArrayBufferSize(minLength * 4)
	if data != nil {
		copySliceToTemporaryArrayBuffer(data)
	}
	return temporaryFloat32Array
}
<commit_msg>jsutil: Reduce calls of (js.Value).Get<commit_after>// Copyright 2019 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package jsutil

import (
	"syscall/js"
)

var (
	arrayBuffer  = js.Global().Get("ArrayBuffer")
	uint8Array   = js.Global().Get("Uint8Array")
	float32Array = js.Global().Get("Float32Array")
)

var (
	// temporaryArrayBuffer is a temporary buffer used at gl.readPixels or gl.texSubImage2D.
	// The read data is converted to Go's byte slice as soon as possible.
	// To avoid often allocating ArrayBuffer, reuse the buffer whenever possible.
	temporaryArrayBuffer           = arrayBuffer.New(16)
	temporaryArrayBufferByteLength = temporaryArrayBuffer.Get("byteLength")

	// temporaryUint8Array is a Uint8ArrayBuffer whose underlying buffer is always temporaryArrayBuffer.
	temporaryUint8Array           = uint8Array.New(temporaryArrayBuffer)
	temporaryUint8ArrayByteLength = temporaryUint8Array.Get("byteLength")

	// temporaryFloat32Array is a Float32ArrayBuffer whose underlying buffer is always temporaryArrayBuffer.
	temporaryFloat32Array           = float32Array.New(temporaryArrayBuffer)
	temporaryFloat32ArrayByteLength = temporaryFloat32Array.Get("byteLength")
)

func ensureTemporaryArrayBufferSize(byteLength int) {
	bufl := temporaryArrayBufferByteLength.Int()
	if bufl < byteLength {
		for bufl < byteLength {
			bufl *= 2
		}
		temporaryArrayBuffer = arrayBuffer.New(bufl)
		temporaryArrayBufferByteLength = temporaryArrayBuffer.Get("byteLength")
	}
	if temporaryUint8ArrayByteLength.Int() < bufl {
		temporaryUint8Array = uint8Array.New(temporaryArrayBuffer)
		temporaryUint8ArrayByteLength = temporaryUint8Array.Get("byteLength")
	}
	if temporaryFloat32ArrayByteLength.Int() < bufl {
		temporaryFloat32Array = float32Array.New(temporaryArrayBuffer)
		temporaryFloat32ArrayByteLength = temporaryFloat32Array.Get("byteLength")
	}
}

// TemporaryUint8Array returns a Uint8Array whose length is at least minLength.
// Be careful that the length can exceed the given minLength.
// data must be a slice of a numeric type for initialization, or nil if you don't need initialization.
func TemporaryUint8Array(minLength int, data interface{}) js.Value {
	ensureTemporaryArrayBufferSize(minLength)
	if data != nil {
		copySliceToTemporaryArrayBuffer(data)
	}
	return temporaryUint8Array
}

// TemporaryFloat32Array returns a Float32Array whose length is at least minLength.
// Be careful that the length can exceed the given minLength.
// data must be a slice of a numeric type for initialization, or nil if you don't need initialization.
func TemporaryFloat32Array(minLength int, data interface{}) js.Value {
	ensureTemporaryArrayBufferSize(minLength * 4)
	if data != nil {
		copySliceToTemporaryArrayBuffer(data)
	}
	return temporaryFloat32Array
}
<|endoftext|>


<commit_before>package cache

import (
	"context"
	"go/ast"
	"go/types"
	"sort"
	"sync"

	"golang.org/x/tools/go/analysis"
	"golang.org/x/tools/go/packages"
	"golang.org/x/tools/internal/lsp/source"
)

// Package contains the type information needed by the source package.
type Package struct {
	id, pkgPath string
	files       []string
	syntax      []*ast.File
	errors      []packages.Error
	imports     map[string]*Package
	types       *types.Package
	typesInfo   *types.Info
	typesSizes  types.Sizes

	// The analysis cache holds analysis information for all the packages in a view.
	// Each graph node (action) is one unit of analysis.
	// Edges express package-to-package (vertical) dependencies,
	// and analysis-to-analysis (horizontal) dependencies.
	mu       sync.Mutex
	analyses map[*analysis.Analyzer]*analysisEntry
}

type analysisEntry struct {
	ready chan struct{}
	*source.Action
}

func (pkg *Package) GetActionGraph(ctx context.Context, a *analysis.Analyzer) (*source.Action, error) {
	if ctx.Err() != nil {
		return nil, ctx.Err()
	}

	pkg.mu.Lock()
	e, ok := pkg.analyses[a]
	if ok {
		// cache hit
		pkg.mu.Unlock()

		// wait for entry to become ready or the context to be cancelled
		select {
		case <-e.ready:
		case <-ctx.Done():
			return nil, ctx.Err()
		}
	} else {
		// cache miss
		e = &analysisEntry{
			ready: make(chan struct{}),
			Action: &source.Action{
				Analyzer: a,
				Pkg:      pkg,
			},
		}
		pkg.analyses[a] = e
		pkg.mu.Unlock()

		// This goroutine becomes responsible for populating
		// the entry and broadcasting its readiness.

		// Add a dependency on each required analyzers.
		for _, req := range a.Requires {
			act, err := pkg.GetActionGraph(ctx, req)
			if err != nil {
				return nil, err
			}
			e.Deps = append(e.Deps, act)
		}

		// An analysis that consumes/produces facts
		// must run on the package's dependencies too.
		if len(a.FactTypes) > 0 {
			importPaths := make([]string, 0, len(pkg.imports))
			for importPath := range pkg.imports {
				importPaths = append(importPaths, importPath)
			}
			sort.Strings(importPaths) // for determinism
			for _, importPath := range importPaths {
				dep := pkg.imports[importPath]
				act, err := dep.GetActionGraph(ctx, a)
				if err != nil {
					return nil, err
				}
				e.Deps = append(e.Deps, act)
			}
		}
		close(e.ready)
	}
	return e.Action, nil
}

func (pkg *Package) GetFilenames() []string {
	return pkg.files
}

func (pkg *Package) GetSyntax() []*ast.File {
	return pkg.syntax
}

func (pkg *Package) GetErrors() []packages.Error {
	return pkg.errors
}

func (pkg *Package) GetTypes() *types.Package {
	return pkg.types
}

func (pkg *Package) GetTypesInfo() *types.Info {
	return pkg.typesInfo
}

func (pkg *Package) GetTypesSizes() types.Sizes {
	return pkg.typesSizes
}

func (pkg *Package) IsIllTyped() bool {
	return pkg.types == nil && pkg.typesInfo == nil
}
<commit_msg>internal/lsp: fix stuck diagnostic messages<commit_after>package cache

import (
	"context"
	"go/ast"
	"go/types"
	"sort"
	"sync"

	"golang.org/x/tools/go/analysis"
	"golang.org/x/tools/go/packages"
	"golang.org/x/tools/internal/lsp/source"
)

// Package contains the type information needed by the source package.
type Package struct {
	id, pkgPath string
	files       []string
	syntax      []*ast.File
	errors      []packages.Error
	imports     map[string]*Package
	types       *types.Package
	typesInfo   *types.Info
	typesSizes  types.Sizes

	// The analysis cache holds analysis information for all the packages in a view.
	// Each graph node (action) is one unit of analysis.
	// Edges express package-to-package (vertical) dependencies,
	// and analysis-to-analysis (horizontal) dependencies.
	mu       sync.Mutex
	analyses map[*analysis.Analyzer]*analysisEntry
}

type analysisEntry struct {
	done      chan struct{}
	succeeded bool
	*source.Action
}

func (pkg *Package) GetActionGraph(ctx context.Context, a *analysis.Analyzer) (*source.Action, error) {
	if ctx.Err() != nil {
		return nil, ctx.Err()
	}

	pkg.mu.Lock()
	e, ok := pkg.analyses[a]
	if ok {
		// cache hit
		pkg.mu.Unlock()

		// wait for entry to become ready or the context to be cancelled
		select {
		case <-e.done:
			// If the goroutine we are waiting on was cancelled, we should retry.
			// If errors other than cancelation/timeout become possible, it may
			// no longer be appropriate to always retry here.
			if !e.succeeded {
				return pkg.GetActionGraph(ctx, a)
			}
		case <-ctx.Done():
			return nil, ctx.Err()
		}
	} else {
		// cache miss
		e = &analysisEntry{
			done: make(chan struct{}),
			Action: &source.Action{
				Analyzer: a,
				Pkg:      pkg,
			},
		}
		pkg.analyses[a] = e
		pkg.mu.Unlock()

		defer func() {
			// If we got an error, clear out our defunct cache entry. We don't cache
			// errors since they could depend on our dependencies, which can change.
			// Currently the only possible error is context.Canceled, though, which
			// should also not be cached.
			if !e.succeeded {
				pkg.mu.Lock()
				delete(pkg.analyses, a)
				pkg.mu.Unlock()
			}

			// Always close done so waiters don't get stuck.
			close(e.done)
		}()

		// This goroutine becomes responsible for populating
		// the entry and broadcasting its readiness.

		// Add a dependency on each required analyzers.
		for _, req := range a.Requires {
			act, err := pkg.GetActionGraph(ctx, req)
			if err != nil {
				return nil, err
			}
			e.Deps = append(e.Deps, act)
		}

		// An analysis that consumes/produces facts
		// must run on the package's dependencies too.
		if len(a.FactTypes) > 0 {
			importPaths := make([]string, 0, len(pkg.imports))
			for importPath := range pkg.imports {
				importPaths = append(importPaths, importPath)
			}
			sort.Strings(importPaths) // for determinism
			for _, importPath := range importPaths {
				dep := pkg.imports[importPath]
				act, err := dep.GetActionGraph(ctx, a)
				if err != nil {
					return nil, err
				}
				e.Deps = append(e.Deps, act)
			}
		}

		e.succeeded = true
	}
	return e.Action, nil
}

func (pkg *Package) GetFilenames() []string {
	return pkg.files
}

func (pkg *Package) GetSyntax() []*ast.File {
	return pkg.syntax
}

func (pkg *Package) GetErrors() []packages.Error {
	return pkg.errors
}

func (pkg *Package) GetTypes() *types.Package {
	return pkg.types
}

func (pkg *Package) GetTypesInfo() *types.Info {
	return pkg.typesInfo
}

func (pkg *Package) GetTypesSizes() types.Sizes {
	return pkg.typesSizes
}

func (pkg *Package) IsIllTyped() bool {
	return pkg.types == nil && pkg.typesInfo == nil
}
<|endoftext|>


<commit_before>// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package arena

import (
	"testing"
)

func TestSimpleArenaAllocator(t *testing.T) {
	arena := NewAllocator(1000)
	slice := arena.Alloc(10)
	if arena.off != 10 {
		t.Error("off not match, expect 10 bug got", arena.off)
	}

	if len(slice) != 0 {
		t.Error("slice len not match, expect 0 bug got", len(slice))
	}

	if cap(slice) != 10 {
		t.Error("slice len not match, expect 10 bug got", cap(slice))
	}

	slice = arena.Alloc(20)
	if arena.off != 30 {
		t.Error("off not match, expect 30 bug got", arena.off)
	}

	if len(slice) != 0 {
		t.Error("slice len not match, expect 0 bug got", len(slice))
	}

	if cap(slice) != 20 {
		t.Error("slice len not match, expect 20 bug got", cap(slice))
	}

	slice = arena.Alloc(1024)

	if arena.off != 30 {
		t.Error("off not match, expect 30 bug got", arena.off)
	}

	if len(slice) != 0 {
		t.Error("slice len not match, expect 0 bug got", len(slice))
	}

	if cap(slice) != 1024 {
		t.Error("slice len not match, expect 1024 bug got", cap(slice))
	}

	slice = arena.AllocWithLen(2, 10)

	if arena.off != 40 {
		t.Error("off not match, expect 40 bug got", arena.off)
	}

	if len(slice) != 2 {
		t.Error("slice len not match, expect 2 bug got", len(slice))
	}

	if cap(slice) != 10 {
		t.Error("slice len not match, expect 10 bug got", cap(slice))
	}

	arena.Reset()
	if arena.off != 0 || cap(arena.arena) != 1000 {
		t.Error("off should be 0")
	}

	if cap(arena.arena) != 1000 {
		t.Error("off should be 0")
	}
}

func TestStdAllocator(t *testing.T) {
	slice := StdAllocator.Alloc(20)
	if len(slice) != 0 {
		t.Error("length not match")
	}

	if cap(slice) != 20 {
		t.Error("cap not match")
	}

	slice = StdAllocator.AllocWithLen(10, 20)
	if len(slice) != 10 {
		t.Error("length not match")
	}

	if cap(slice) != 20 {
		t.Error("cap not match")
	}
}
<commit_msg>arena: clean up tests. (#1150)<commit_after>// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package arena

import (
	"testing"
)

func TestSimpleArenaAllocator(t *testing.T) {
	arena := NewAllocator(1000)
	slice := arena.Alloc(10)
	if arena.off != 10 {
		t.Error("off not match, expect 10 bug got", arena.off)
	}

	if len(slice) != 0 || cap(slice) != 10 {
		t.Error("slice length or cap not match")
	}

	slice = arena.Alloc(20)
	if arena.off != 30 {
		t.Error("off not match, expect 30 bug got", arena.off)
	}

	if len(slice) != 0 || cap(slice) != 20 {
		t.Error("slice length or cap not match")
	}

	slice = arena.Alloc(1024)
	if arena.off != 30 {
		t.Error("off not match, expect 30 bug got", arena.off)
	}

	if len(slice) != 0 || cap(slice) != 1024 {
		t.Error("slice length or cap not match")
	}

	slice = arena.AllocWithLen(2, 10)
	if arena.off != 40 {
		t.Error("off not match, expect 40 bug got", arena.off)
	}

	if len(slice) != 2 || cap(slice) != 10 {
		t.Error("slice length or cap not match")
	}

	arena.Reset()
	if arena.off != 0 || cap(arena.arena) != 1000 {
		t.Error("off or cap not match")
	}

	if cap(arena.arena) != 1000 {
		t.Error("cap not match")
	}
}

func TestStdAllocator(t *testing.T) {
	slice := StdAllocator.Alloc(20)
	if len(slice) != 0 {
		t.Error("length not match")
	}

	if cap(slice) != 20 {
		t.Error("cap not match")
	}

	slice = StdAllocator.AllocWithLen(10, 20)
	if len(slice) != 10 {
		t.Error("length not match")
	}

	if cap(slice) != 20 {
		t.Error("cap not match")
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License. See the AUTHORS file
// for names of contributors.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

package retry

import (
	"testing"
	"time"

	"github.com/cockroachdb/cockroach/util/stop"
)

func TestRetryExceedsMaxBackoff(t *testing.T) {
	opts := Options{
		InitialBackoff: time.Microsecond * 10,
		MaxBackoff:     time.Microsecond * 10,
		Multiplier:     10e4,
		MaxRetries:     1,
	}

	// tolerate a duration two orders of magnitude lower than if MaxBackoff is
	// not respected.
	fudgeFactor := opts.Multiplier / 10e2

	attempts := 0
	start := time.Now()
	for r := Start(opts); r.Next(); attempts++ {
	}

	duration := time.Since(start)

	if expDuration := time.Duration(fudgeFactor) * time.Duration(opts.MaxRetries+1) * opts.MaxBackoff; duration > expDuration {
		t.Errorf("expected retry loop to run for %s, actually ran for %s", expDuration, duration)
	}

	if expAttempts := opts.MaxRetries + 1; attempts != expAttempts {
		t.Errorf("expected %d attempts, got %d attempts", expAttempts, attempts)
	}
}

func TestRetryExceedsMaxAttempts(t *testing.T) {
	opts := Options{
		InitialBackoff: time.Microsecond * 10,
		MaxBackoff:     time.Second,
		Multiplier:     2,
		MaxRetries:     1,
	}

	attempts := 0
	for r := Start(opts); r.Next(); attempts++ {
	}

	if expAttempts := opts.MaxRetries + 1; attempts != expAttempts {
		t.Errorf("expected %d attempts, got %d attempts", expAttempts, attempts)
	}
}

func TestRetryReset(t *testing.T) {
	opts := Options{
		InitialBackoff: time.Microsecond * 10,
		MaxBackoff:     time.Second,
		Multiplier:     2,
		MaxRetries:     1,
	}

	expAttempts := opts.MaxRetries + 1

	attempts := 0
	// Backoff loop has 1 allowed retry; we always call Reset, so
	// just make sure we get to 2 attempts and then break.
	for r := Start(opts); r.Next(); attempts++ {
		if attempts == expAttempts {
			break
		}
		r.Reset()
	}
	if attempts != expAttempts {
		t.Errorf("expected %d attempts, got %d", expAttempts, attempts)
	}
}

func TestRetryStop(t *testing.T) {
	opts := Options{
		InitialBackoff: time.Microsecond * 10,
		MaxBackoff:     time.Second,
		Multiplier:     2,
		Stopper:        stop.NewStopper(),
	}

	var attempts int

	// Create a retry loop which will never stop without stopper.
	for r := Start(opts); r.Next(); attempts++ {
		go opts.Stopper.Stop()
		// Don't race the stopper, just wait for it to do its thing.
		<-opts.Stopper.ShouldStop()
	}

	if expAttempts := 1; attempts != expAttempts {
		t.Errorf("expected %d attempts, got %d", expAttempts, attempts)
	}
}
<commit_msg>Don't race the stopper for real<commit_after>// Copyright 2014 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License. See the AUTHORS file
// for names of contributors.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

package retry

import (
	"testing"
	"time"

	"github.com/cockroachdb/cockroach/util/stop"
)

func TestRetryExceedsMaxBackoff(t *testing.T) {
	opts := Options{
		InitialBackoff: time.Microsecond * 10,
		MaxBackoff:     time.Microsecond * 10,
		Multiplier:     10e4,
		MaxRetries:     1,
	}

	// tolerate a duration two orders of magnitude lower than if MaxBackoff is
	// not respected.
	fudgeFactor := opts.Multiplier / 10e2

	attempts := 0
	start := time.Now()
	for r := Start(opts); r.Next(); attempts++ {
	}

	duration := time.Since(start)

	if expDuration := time.Duration(fudgeFactor) * time.Duration(opts.MaxRetries+1) * opts.MaxBackoff; duration > expDuration {
		t.Errorf("expected retry loop to run for %s, actually ran for %s", expDuration, duration)
	}

	if expAttempts := opts.MaxRetries + 1; attempts != expAttempts {
		t.Errorf("expected %d attempts, got %d attempts", expAttempts, attempts)
	}
}

func TestRetryExceedsMaxAttempts(t *testing.T) {
	opts := Options{
		InitialBackoff: time.Microsecond * 10,
		MaxBackoff:     time.Second,
		Multiplier:     2,
		MaxRetries:     1,
	}

	attempts := 0
	for r := Start(opts); r.Next(); attempts++ {
	}

	if expAttempts := opts.MaxRetries + 1; attempts != expAttempts {
		t.Errorf("expected %d attempts, got %d attempts", expAttempts, attempts)
	}
}

func TestRetryReset(t *testing.T) {
	opts := Options{
		InitialBackoff: time.Microsecond * 10,
		MaxBackoff:     time.Second,
		Multiplier:     2,
		MaxRetries:     1,
	}

	expAttempts := opts.MaxRetries + 1

	attempts := 0
	// Backoff loop has 1 allowed retry; we always call Reset, so
	// just make sure we get to 2 attempts and then break.
	for r := Start(opts); r.Next(); attempts++ {
		if attempts == expAttempts {
			break
		}
		r.Reset()
	}
	if attempts != expAttempts {
		t.Errorf("expected %d attempts, got %d", expAttempts, attempts)
	}
}

func TestRetryStop(t *testing.T) {
	opts := Options{
		InitialBackoff: time.Second,
		MaxBackoff:     time.Second,
		Multiplier:     2,
		Stopper:        stop.NewStopper(),
	}

	var attempts int

	// Create a retry loop which will never stop without stopper.
	for r := Start(opts); r.Next(); attempts++ {
		go opts.Stopper.Stop()
		// Don't race the stopper, just wait for it to do its thing.
		<-opts.Stopper.ShouldStop()
	}

	if expAttempts := 1; attempts != expAttempts {
		t.Errorf("expected %d attempts, got %d", expAttempts, attempts)
	}
}
<|endoftext|>


<commit_before>package logical

import "time"

// LogicalSystemConfig exposes system configuration information in a safe way
// for other logical backends to consume
type SystemConfig struct {
	DefaultLeaseTTL func() time.Duration
	MaxLeaseTTL     func() time.Duration
}
<commit_msg>Add some documentation to SystemConfig<commit_after>package logical

import "time"

// SystemConfig exposes system configuration information in a safe way
// for other logical backends to consume
type SystemConfig struct {

	// DefaultLeaseTTL returns the default lease TTL set in Vault configuration
	DefaultLeaseTTL func() time.Duration

	// MaxLeaseTTL returns the max lease TTL set in Vault configuration; backend
	// authors should take care not to issue credentials that last longer than
	// this value, as Vault will revoke them
	MaxLeaseTTL func() time.Duration
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package machine

import (
	"fmt"
	"io/ioutil"
	"regexp"
	"strconv"
	"strings"

	// s390/s390x changes
	"runtime"
	"syscall"

	"github.com/golang/glog"
	info "github.com/google/cadvisor/info/v1"
	"github.com/google/cadvisor/utils"
	"github.com/google/cadvisor/utils/sysfs"
	"github.com/google/cadvisor/utils/sysinfo"
)

// The utils/machine package contains functions that extract machine-level specs.

var cpuRegExp = regexp.MustCompile("processor\\t*: +([0-9]+)")
var coreRegExp = regexp.MustCompile("core id\\t*: +([0-9]+)")
var nodeRegExp = regexp.MustCompile("physical id\\t*: +([0-9]+)")
var CpuClockSpeedMHz = regexp.MustCompile("cpu MHz\\t*: +([0-9]+.[0-9]+)")
var memoryCapacityRegexp = regexp.MustCompile("MemTotal: *([0-9]+) kB")
var swapCapacityRegexp = regexp.MustCompile("SwapTotal: *([0-9]+) kB")

// GetClockSpeed returns the CPU clock speed, given a []byte formatted as the /proc/cpuinfo file.
func GetClockSpeed(procInfo []byte) (uint64, error) {
	// s390/s390x changes
	if true == isSystemZ() {
		return 0, nil
	}

	// First look through sys to find a max supported cpu frequency.
	const maxFreqFile = "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"
	if utils.FileExists(maxFreqFile) {
		val, err := ioutil.ReadFile(maxFreqFile)
		if err != nil {
			return 0, err
		}
		var maxFreq uint64
		n, err := fmt.Sscanf(string(val), "%d", &maxFreq)
		if err != nil || n != 1 {
			return 0, fmt.Errorf("could not parse frequency %q", val)
		}
		return maxFreq, nil
	}
	// Fall back to /proc/cpuinfo
	matches := CpuClockSpeedMHz.FindSubmatch(procInfo)
	if len(matches) != 2 {
		//Check if we are running on Power systems which have a different format
		CpuClockSpeedMHz, _ = regexp.Compile("clock\\t*: +([0-9]+.[0-9]+)MHz")
		matches = CpuClockSpeedMHz.FindSubmatch(procInfo)
		if len(matches) != 2 {
			return 0, fmt.Errorf("could not detect clock speed from output: %q", string(procInfo))
		}
	}
	speed, err := strconv.ParseFloat(string(matches[1]), 64)
	if err != nil {
		return 0, err
	}
	// Convert to kHz
	return uint64(speed * 1000), nil
}

// GetMachineMemoryCapacity returns the machine's total memory from /proc/meminfo.
// Returns the total memory capacity as an int64 (number of bytes).
func GetMachineMemoryCapacity() (int64, error) {
	out, err := ioutil.ReadFile("/proc/meminfo")
	if err != nil {
		return 0, err
	}

	memoryCapacity, err := parseCapacity(out, memoryCapacityRegexp)
	if err != nil {
		return 0, err
	}
	return memoryCapacity, err
}

// GetMachineSwapCapacity returns the machine's total swap from /proc/meminfo.
// Returns the total swap capacity as an int64 (number of bytes).
func GetMachineSwapCapacity() (int64, error) {
	out, err := ioutil.ReadFile("/proc/meminfo")
	if err != nil {
		return 0, err
	}

	swapCapacity, err := parseCapacity(out, swapCapacityRegexp)
	if err != nil {
		return 0, err
	}
	return swapCapacity, err
}

// parseCapacity matches a Regexp in a []byte, returning the resulting value in bytes.
// Assumes that the value matched by the Regexp is in KB.
func parseCapacity(b []byte, r *regexp.Regexp) (int64, error) {
	matches := r.FindSubmatch(b)
	if len(matches) != 2 {
		return -1, fmt.Errorf("failed to match regexp in output: %q", string(b))
	}
	m, err := strconv.ParseInt(string(matches[1]), 10, 64)
	if err != nil {
		return -1, err
	}

	// Convert to bytes.
	return m * 1024, err
}

func GetTopology(sysFs sysfs.SysFs, cpuinfo string) ([]info.Node, int, error) {
	nodes := []info.Node{}

	// s390/s390x changes
	if true == isSystemZ() {
		return nodes, getNumCores(), nil
	}

	numCores := 0
	lastThread := -1
	lastCore := -1
	lastNode := -1
	for _, line := range strings.Split(cpuinfo, "\n") {
		ok, val, err := extractValue(line, cpuRegExp)
		if err != nil {
			return nil, -1, fmt.Errorf("could not parse cpu info from %q: %v", line, err)
		}
		if ok {
			thread := val
			numCores++
			if lastThread != -1 {
				// New cpu section. Save last one.
				nodeIdx, err := addNode(&nodes, lastNode)
				if err != nil {
					return nil, -1, fmt.Errorf("failed to add node %d: %v", lastNode, err)
				}
				nodes[nodeIdx].AddThread(lastThread, lastCore)
				lastCore = -1
				lastNode = -1
			}
			lastThread = thread
		}
		ok, val, err = extractValue(line, coreRegExp)
		if err != nil {
			return nil, -1, fmt.Errorf("could not parse core info from %q: %v", line, err)
		}
		if ok {
			lastCore = val
		}
		ok, val, err = extractValue(line, nodeRegExp)
		if err != nil {
			return nil, -1, fmt.Errorf("could not parse node info from %q: %v", line, err)
		}
		if ok {
			lastNode = val
		}
	}
	nodeIdx, err := addNode(&nodes, lastNode)
	if err != nil {
		return nil, -1, fmt.Errorf("failed to add node %d: %v", lastNode, err)
	}
	nodes[nodeIdx].AddThread(lastThread, lastCore)
	if numCores < 1 {
		return nil, numCores, fmt.Errorf("could not detect any cores")
	}
	for idx, node := range nodes {
		caches, err := sysinfo.GetCacheInfo(sysFs, node.Cores[0].Threads[0])
		if err != nil {
			glog.Errorf("failed to get cache information for node %d: %v", node.Id, err)
			continue
		}
		numThreadsPerCore := len(node.Cores[0].Threads)
		numThreadsPerNode := len(node.Cores) * numThreadsPerCore
		for _, cache := range caches {
			c := info.Cache{
				Size:  cache.Size,
				Level: cache.Level,
				Type:  cache.Type,
			}
			if cache.Cpus == numThreadsPerNode && cache.Level > 2 {
				// Add a node-level cache.
				nodes[idx].AddNodeCache(c)
			} else if cache.Cpus == numThreadsPerCore {
				// Add to each core.
				nodes[idx].AddPerCoreCache(c)
			}
			// Ignore unknown caches.
		}
	}
	return nodes, numCores, nil
}

func extractValue(s string, r *regexp.Regexp) (bool, int, error) {
	matches := r.FindSubmatch([]byte(s))
	if len(matches) == 2 {
		val, err := strconv.ParseInt(string(matches[1]), 10, 32)
		if err != nil {
			return true, -1, err
		}
		return true, int(val), nil
	}
	return false, -1, nil
}

func findNode(nodes []info.Node, id int) (bool, int) {
	for i, n := range nodes {
		if n.Id == id {
			return true, i
		}
	}
	return false, -1
}

func addNode(nodes *[]info.Node, id int) (int, error) {
	var idx int
	if id == -1 {
		// Some VMs don't fill topology data. Export single package.
		id = 0
	}

	ok, idx := findNode(*nodes, id)
	if !ok {
		// New node
		node := info.Node{Id: id}
		// Add per-node memory information.
		meminfo := fmt.Sprintf("/sys/devices/system/node/node%d/meminfo", id)
		out, err := ioutil.ReadFile(meminfo)
		// Ignore if per-node info is not available.
		if err == nil {
			m, err := parseCapacity(out, memoryCapacityRegexp)
			if err != nil {
				return -1, err
			}
			node.Memory = uint64(m)
		}
		*nodes = append(*nodes, node)
		idx = len(*nodes) - 1
	}
	return idx, nil
}

// s390/s390x changes
func getMachineArch() (string, error) {
	uname := syscall.Utsname{}
	err := syscall.Uname(&uname)
	if err != nil {
		return "", err
	}

	var arch string
	for _, val := range uname.Machine {
		arch += string(int(val))
	}

	return arch, nil
}

// s390/s390x changes
func isSystemZ() bool {
	arch, err := getMachineArch()
	if err == nil {
		if true == strings.Contains(arch, "390") {
			return true
		}
	}
	return false
}

// s390/s390x changes
func getNumCores() int {
	maxProcs := runtime.GOMAXPROCS(0)
	numCPU := runtime.NumCPU()

	if maxProcs < numCPU {
		return maxProcs
	}

	return numCPU
}
<commit_msg>ClockSpeed not defined in /proc/cpuinfo for AArch64, so return 0.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package machine

import (
	"fmt"
	"io/ioutil"
	"regexp"
	"strconv"
	"strings"

	// s390/s390x changes
	"runtime"
	"syscall"

	"github.com/golang/glog"
	info "github.com/google/cadvisor/info/v1"
	"github.com/google/cadvisor/utils"
	"github.com/google/cadvisor/utils/sysfs"
	"github.com/google/cadvisor/utils/sysinfo"
)

// The utils/machine package contains functions that extract machine-level specs.

var cpuRegExp = regexp.MustCompile("processor\\t*: +([0-9]+)")
var coreRegExp = regexp.MustCompile("core id\\t*: +([0-9]+)")
var nodeRegExp = regexp.MustCompile("physical id\\t*: +([0-9]+)")
var CpuClockSpeedMHz = regexp.MustCompile("cpu MHz\\t*: +([0-9]+.[0-9]+)")
var memoryCapacityRegexp = regexp.MustCompile("MemTotal: *([0-9]+) kB")
var swapCapacityRegexp = regexp.MustCompile("SwapTotal: *([0-9]+) kB")

// GetClockSpeed returns the CPU clock speed, given a []byte formatted as the /proc/cpuinfo file.
func GetClockSpeed(procInfo []byte) (uint64, error) {
	// s390/s390x and aarch64 changes
	if true == isSystemZ() || true == isAArch64() {
		return 0, nil
	}

	// First look through sys to find a max supported cpu frequency.
	const maxFreqFile = "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"
	if utils.FileExists(maxFreqFile) {
		val, err := ioutil.ReadFile(maxFreqFile)
		if err != nil {
			return 0, err
		}
		var maxFreq uint64
		n, err := fmt.Sscanf(string(val), "%d", &maxFreq)
		if err != nil || n != 1 {
			return 0, fmt.Errorf("could not parse frequency %q", val)
		}
		return maxFreq, nil
	}
	// Fall back to /proc/cpuinfo
	matches := CpuClockSpeedMHz.FindSubmatch(procInfo)
	if len(matches) != 2 {
		//Check if we are running on Power systems which have a different format
		CpuClockSpeedMHz, _ = regexp.Compile("clock\\t*: +([0-9]+.[0-9]+)MHz")
		matches = CpuClockSpeedMHz.FindSubmatch(procInfo)
		if len(matches) != 2 {
			return 0, fmt.Errorf("could not detect clock speed from output: %q", string(procInfo))
		}
	}
	speed, err := strconv.ParseFloat(string(matches[1]), 64)
	if err != nil {
		return 0, err
	}
	// Convert to kHz
	return uint64(speed * 1000), nil
}

// GetMachineMemoryCapacity returns the machine's total memory from /proc/meminfo.
// Returns the total memory capacity as an int64 (number of bytes).
func GetMachineMemoryCapacity() (int64, error) {
	out, err := ioutil.ReadFile("/proc/meminfo")
	if err != nil {
		return 0, err
	}

	memoryCapacity, err := parseCapacity(out, memoryCapacityRegexp)
	if err != nil {
		return 0, err
	}
	return memoryCapacity, err
}

// GetMachineSwapCapacity returns the machine's total swap from /proc/meminfo.
// Returns the total swap capacity as an int64 (number of bytes).
func GetMachineSwapCapacity() (int64, error) {
	out, err := ioutil.ReadFile("/proc/meminfo")
	if err != nil {
		return 0, err
	}

	swapCapacity, err := parseCapacity(out, swapCapacityRegexp)
	if err != nil {
		return 0, err
	}
	return swapCapacity, err
}

// parseCapacity matches a Regexp in a []byte, returning the resulting value in bytes.
// Assumes that the value matched by the Regexp is in KB.
func parseCapacity(b []byte, r *regexp.Regexp) (int64, error) {
	matches := r.FindSubmatch(b)
	if len(matches) != 2 {
		return -1, fmt.Errorf("failed to match regexp in output: %q", string(b))
	}
	m, err := strconv.ParseInt(string(matches[1]), 10, 64)
	if err != nil {
		return -1, err
	}

	// Convert to bytes.
	return m * 1024, err
}

func GetTopology(sysFs sysfs.SysFs, cpuinfo string) ([]info.Node, int, error) {
	nodes := []info.Node{}

	// s390/s390x changes
	if true == isSystemZ() {
		return nodes, getNumCores(), nil
	}

	numCores := 0
	lastThread := -1
	lastCore := -1
	lastNode := -1
	for _, line := range strings.Split(cpuinfo, "\n") {
		ok, val, err := extractValue(line, cpuRegExp)
		if err != nil {
			return nil, -1, fmt.Errorf("could not parse cpu info from %q: %v", line, err)
		}
		if ok {
			thread := val
			numCores++
			if lastThread != -1 {
				// New cpu section. Save last one.
				nodeIdx, err := addNode(&nodes, lastNode)
				if err != nil {
					return nil, -1, fmt.Errorf("failed to add node %d: %v", lastNode, err)
				}
				nodes[nodeIdx].AddThread(lastThread, lastCore)
				lastCore = -1
				lastNode = -1
			}
			lastThread = thread
		}
		ok, val, err = extractValue(line, coreRegExp)
		if err != nil {
			return nil, -1, fmt.Errorf("could not parse core info from %q: %v", line, err)
		}
		if ok {
			lastCore = val
		}
		ok, val, err = extractValue(line, nodeRegExp)
		if err != nil {
			return nil, -1, fmt.Errorf("could not parse node info from %q: %v", line, err)
		}
		if ok {
			lastNode = val
		}
	}
	nodeIdx, err := addNode(&nodes, lastNode)
	if err != nil {
		return nil, -1, fmt.Errorf("failed to add node %d: %v", lastNode, err)
	}
	nodes[nodeIdx].AddThread(lastThread, lastCore)
	if numCores < 1 {
		return nil, numCores, fmt.Errorf("could not detect any cores")
	}
	for idx, node := range nodes {
		caches, err := sysinfo.GetCacheInfo(sysFs, node.Cores[0].Threads[0])
		if err != nil {
			glog.Errorf("failed to get cache information for node %d: %v", node.Id, err)
			continue
		}
		numThreadsPerCore := len(node.Cores[0].Threads)
		numThreadsPerNode := len(node.Cores) * numThreadsPerCore
		for _, cache := range caches {
			c := info.Cache{
				Size:  cache.Size,
				Level: cache.Level,
				Type:  cache.Type,
			}
			if cache.Cpus == numThreadsPerNode && cache.Level > 2 {
				// Add a node-level cache.
				nodes[idx].AddNodeCache(c)
			} else if cache.Cpus == numThreadsPerCore {
				// Add to each core.
				nodes[idx].AddPerCoreCache(c)
			}
			// Ignore unknown caches.
		}
	}
	return nodes, numCores, nil
}

func extractValue(s string, r *regexp.Regexp) (bool, int, error) {
	matches := r.FindSubmatch([]byte(s))
	if len(matches) == 2 {
		val, err := strconv.ParseInt(string(matches[1]), 10, 32)
		if err != nil {
			return true, -1, err
		}
		return true, int(val), nil
	}
	return false, -1, nil
}

func findNode(nodes []info.Node, id int) (bool, int) {
	for i, n := range nodes {
		if n.Id == id {
			return true, i
		}
	}
	return false, -1
}

func addNode(nodes *[]info.Node, id int) (int, error) {
	var idx int
	if id == -1 {
		// Some VMs don't fill topology data. Export single package.
		id = 0
	}

	ok, idx := findNode(*nodes, id)
	if !ok {
		// New node
		node := info.Node{Id: id}
		// Add per-node memory information.
		meminfo := fmt.Sprintf("/sys/devices/system/node/node%d/meminfo", id)
		out, err := ioutil.ReadFile(meminfo)
		// Ignore if per-node info is not available.
		if err == nil {
			m, err := parseCapacity(out, memoryCapacityRegexp)
			if err != nil {
				return -1, err
			}
			node.Memory = uint64(m)
		}
		*nodes = append(*nodes, node)
		idx = len(*nodes) - 1
	}
	return idx, nil
}

// s390/s390x changes
func getMachineArch() (string, error) {
	uname := syscall.Utsname{}
	err := syscall.Uname(&uname)
	if err != nil {
		return "", err
	}

	var arch string
	for _, val := range uname.Machine {
		arch += string(int(val))
	}

	return arch, nil
}

// aarch64 changes
func isAArch64() bool {
	arch, err := getMachineArch()
	if err == nil {
		if true == strings.Contains(arch, "aarch64") {
			return true
		}
	}
	return false
}

// s390/s390x changes
func isSystemZ() bool {
	arch, err := getMachineArch()
	if err == nil {
		if true == strings.Contains(arch, "390") {
			return true
		}
	}
	return false
}

// s390/s390x changes
func getNumCores() int {
	maxProcs := runtime.GOMAXPROCS(0)
	numCPU := runtime.NumCPU()

	if maxProcs < numCPU {
		return maxProcs
	}

	return numCPU
}
<|endoftext|>


<commit_before>package config

import (
	"context"
	"fmt"
	"log"
	"net/url"
	"time"

	"github.com/grafeas/voucher/v2/metrics"
	"github.com/spf13/viper"
	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
	"go.opentelemetry.io/otel/sdk/metric"
	"go.opentelemetry.io/otel/sdk/resource"
	semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
)

func MetricsClient(secrets *Secrets) (metrics.Client, error) {
	tags := viper.GetStringSlice("statsd.tags")

	switch backend := viper.GetString("statsd.backend"); backend {
	case "statsd", "":
		if statsdAddr := viper.GetString("statsd.addr"); statsdAddr != "" {
			sampleRate := viper.GetFloat64("statsd.sample_rate")
			return metrics.NewStatsdClient(statsdAddr, sampleRate, tags)
		}

		log.Printf("No metrics client configured")
		return &metrics.NoopClient{}, nil
	case "otel", "opentelemetry":
		ctx := context.Background()
		exporter, err := otelExporter(ctx)
		if err != nil {
			return nil, fmt.Errorf("creating otel exporter: %w", err)
		}
		interval := viper.GetDuration("statsd.interval")
		if interval == 0 {
			interval = time.Minute
		}
		res, err := resource.New(ctx, resource.WithAttributes(
			semconv.ServiceNameKey.String("voucher"),
		))
		if err != nil {
			return nil, fmt.Errorf("creating otel resource: %w", err)
		}

		mp := metric.NewMeterProvider(
			metric.WithResource(res),
			metric.WithReader(metric.NewPeriodicReader(exporter, metric.WithInterval(interval))),
		)
		return metrics.NewOpenTelemetryClient(mp, exporter)
	case "datadog":
		if secrets.Datadog.APIKey != "" && secrets.Datadog.AppKey != "" {
			return metrics.NewDatadogClient(secrets.Datadog.APIKey, secrets.Datadog.AppKey, metrics.WithDatadogTags(tags)), nil
		}
		return &metrics.NoopClient{}, fmt.Errorf("missing secrets for datadog")
	default:
		return &metrics.NoopClient{}, fmt.Errorf("unknown statsd backend: %s", backend)
	}
}

func otelExporter(ctx context.Context) (metric.Exporter, error) {
	insecure := viper.GetBool("statsd.insecure")

	addr := viper.GetString("statsd.addr")
	otelURL, err := url.Parse(addr)
	if err != nil {
		return nil, fmt.Errorf("parsing otel url: %w", err)
	}
	switch otelURL.Scheme {
	case "grpc":
		opts := []otlpmetricgrpc.Option{
			otlpmetricgrpc.WithEndpoint(addr),
		}
		if insecure {
			opts = append(opts, otlpmetricgrpc.WithInsecure())
		}
		return otlpmetricgrpc.New(ctx, opts...)
	case "http", "https":
		opts := []otlpmetrichttp.Option{
			otlpmetrichttp.WithEndpoint(addr),
		}
		if insecure {
			opts = append(opts, otlpmetrichttp.WithInsecure())
		}
		return otlpmetrichttp.New(ctx, opts...)
	default:
		return nil, fmt.Errorf("unknown otel scheme: %s", otelURL.Scheme)
	}
}
<commit_msg>metrics: endpoint normalization<commit_after>package config

import (
	"context"
	"fmt"
	"log"
	"net/url"
	"time"

	"github.com/grafeas/voucher/v2/metrics"
	"github.com/sirupsen/logrus"
	"github.com/spf13/viper"
	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
	"go.opentelemetry.io/otel/sdk/metric"
	"go.opentelemetry.io/otel/sdk/resource"
	semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
)

func MetricsClient(secrets *Secrets) (metrics.Client, error) {
	tags := viper.GetStringSlice("statsd.tags")

	switch backend := viper.GetString("statsd.backend"); backend {
	case "statsd", "":
		if statsdAddr := viper.GetString("statsd.addr"); statsdAddr != "" {
			sampleRate := viper.GetFloat64("statsd.sample_rate")
			return metrics.NewStatsdClient(statsdAddr, sampleRate, tags)
		}

		log.Printf("No metrics client configured")
		return &metrics.NoopClient{}, nil
	case "otel", "opentelemetry":
		ctx := context.Background()
		exporter, err := otelExporter(ctx)
		if err != nil {
			return nil, fmt.Errorf("creating otel exporter: %w", err)
		}
		interval := viper.GetDuration("statsd.interval")
		if interval == 0 {
			interval = time.Minute
		}
		res, err := resource.New(ctx, resource.WithAttributes(
			semconv.ServiceNameKey.String("voucher"),
		))
		if err != nil {
			return nil, fmt.Errorf("creating otel resource: %w", err)
		}

		mp := metric.NewMeterProvider(
			metric.WithResource(res),
			metric.WithReader(metric.NewPeriodicReader(exporter, metric.WithInterval(interval))),
		)
		return metrics.NewOpenTelemetryClient(mp, exporter)
	case "datadog":
		if secrets != nil && secrets.Datadog.APIKey != "" && secrets.Datadog.AppKey != "" {
			return metrics.NewDatadogClient(secrets.Datadog.APIKey, secrets.Datadog.AppKey, metrics.WithDatadogTags(tags)), nil
		}
		return &metrics.NoopClient{}, fmt.Errorf("missing secrets for datadog")
	default:
		return &metrics.NoopClient{}, fmt.Errorf("unknown statsd backend: %s", backend)
	}
}

func otelExporter(ctx context.Context) (metric.Exporter, error) {
	insecure := viper.GetBool("statsd.insecure")

	addr := viper.GetString("statsd.addr")
	otelURL, err := url.Parse(addr)
	if err != nil {
		return nil, fmt.Errorf("parsing otel url: %w", err)
	}

	log := logrus.WithFields(logrus.Fields{
		"otel_addr": addr,
		"insecure":  insecure,
		"scheme":    otelURL.Scheme,
		"host":      otelURL.Host,
	})

	switch otelURL.Scheme {
	case "grpc":
		opts := []otlpmetricgrpc.Option{
			otlpmetricgrpc.WithEndpoint(otelURL.Host),
		}
		if insecure {
			opts = append(opts, otlpmetricgrpc.WithInsecure())
		}
		log.Info("creating otel exporter")
		return otlpmetricgrpc.New(ctx, opts...)
	case "http", "https":
		opts := []otlpmetrichttp.Option{
			otlpmetrichttp.WithEndpoint(otelURL.Host),
		}
		if insecure {
			opts = append(opts, otlpmetrichttp.WithInsecure())
		}
		log.Info("creating otel exporter")
		return otlpmetrichttp.New(ctx, opts...)
	default:
		return nil, fmt.Errorf("unknown otel scheme: %s", otelURL.Scheme)
	}
}
<|endoftext|>