mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-03 19:17:29 -05:00 
			
		
		
		
	diagnostics: Implemented collection functions and create first metrics
- Also implemented robust error handling and failovers - Vendored klauspost/cpuid
This commit is contained in:
		
							parent
							
								
									8f0b44b8a4
								
							
						
					
					
						commit
						388ff6bc0a
					
				@ -27,6 +27,7 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
	"github.com/klauspost/cpuid"
 | 
			
		||||
	"github.com/mholt/caddy"
 | 
			
		||||
	"github.com/mholt/caddy/caddytls"
 | 
			
		||||
	"github.com/mholt/caddy/diagnostics"
 | 
			
		||||
@ -51,6 +52,7 @@ func init() {
 | 
			
		||||
	flag.StringVar(&caddytls.DefaultEmail, "email", "", "Default ACME CA account email address")
 | 
			
		||||
	flag.DurationVar(&acme.HTTPClient.Timeout, "catimeout", acme.HTTPClient.Timeout, "Default ACME CA HTTP timeout")
 | 
			
		||||
	flag.StringVar(&logfile, "log", "", "Process log file")
 | 
			
		||||
	flag.BoolVar(&noDiag, "no-diagnostics", false, "Disable diagnostic reporting")
 | 
			
		||||
	flag.StringVar(&caddy.PidFile, "pidfile", "", "Path to write pid file")
 | 
			
		||||
	flag.BoolVar(&caddy.Quiet, "quiet", false, "Quiet mode (no initialization output)")
 | 
			
		||||
	flag.StringVar(&revoke, "revoke", "", "Hostname for which to revoke the certificate")
 | 
			
		||||
@ -88,7 +90,9 @@ func Run() {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// initialize diagnostics client
 | 
			
		||||
	initDiagnostics()
 | 
			
		||||
	if !noDiag {
 | 
			
		||||
		initDiagnostics()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check for one-time actions
 | 
			
		||||
	if revoke != "" {
 | 
			
		||||
@ -146,6 +150,23 @@ func Run() {
 | 
			
		||||
	// Execute instantiation events
 | 
			
		||||
	caddy.EmitEvent(caddy.InstanceStartupEvent, instance)
 | 
			
		||||
 | 
			
		||||
	// Begin diagnostics (these are no-ops if diagnostics disabled)
 | 
			
		||||
	diagnostics.Set("caddy_version", appVersion)
 | 
			
		||||
	// TODO: plugins
 | 
			
		||||
	diagnostics.Set("num_listeners", len(instance.Servers()))
 | 
			
		||||
	diagnostics.Set("os", runtime.GOOS)
 | 
			
		||||
	diagnostics.Set("arch", runtime.GOARCH)
 | 
			
		||||
	diagnostics.Set("cpu", struct {
 | 
			
		||||
		NumLogical int    `json:"num_logical"`
 | 
			
		||||
		AESNI      bool   `json:"aes_ni"`
 | 
			
		||||
		BrandName  string `json:"brand_name"`
 | 
			
		||||
	}{
 | 
			
		||||
		NumLogical: runtime.NumCPU(),
 | 
			
		||||
		AESNI:      cpuid.CPU.AesNi(),
 | 
			
		||||
		BrandName:  cpuid.CPU.BrandName,
 | 
			
		||||
	})
 | 
			
		||||
	diagnostics.StartEmitting()
 | 
			
		||||
 | 
			
		||||
	// Twiddle your thumbs
 | 
			
		||||
	instance.Wait()
 | 
			
		||||
}
 | 
			
		||||
@ -321,6 +342,7 @@ var (
 | 
			
		||||
	version    bool
 | 
			
		||||
	plugins    bool
 | 
			
		||||
	validate   bool
 | 
			
		||||
	noDiag     bool
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Build information obtained with the help of -ldflags
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,7 @@ import (
 | 
			
		||||
	"github.com/mholt/caddy/caddyfile"
 | 
			
		||||
	"github.com/mholt/caddy/caddyhttp/staticfiles"
 | 
			
		||||
	"github.com/mholt/caddy/caddytls"
 | 
			
		||||
	"github.com/mholt/caddy/diagnostics"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const serverType = "http"
 | 
			
		||||
@ -205,6 +206,8 @@ func (h *httpContext) MakeServers() ([]caddy.Server, error) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	diagnostics.Set("num_sites", len(h.siteConfigs))
 | 
			
		||||
 | 
			
		||||
	// we must map (group) each config to a bind address
 | 
			
		||||
	groups, err := groupSiteConfigsByListenAddr(h.siteConfigs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 | 
			
		||||
@ -36,6 +36,7 @@ import (
 | 
			
		||||
	"github.com/mholt/caddy"
 | 
			
		||||
	"github.com/mholt/caddy/caddyhttp/staticfiles"
 | 
			
		||||
	"github.com/mholt/caddy/caddytls"
 | 
			
		||||
	"github.com/mholt/caddy/diagnostics"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Server is the HTTP server implementation.
 | 
			
		||||
@ -345,6 +346,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	go diagnostics.AppendUniqueString("user_agent", r.Header.Get("User-Agent"))
 | 
			
		||||
 | 
			
		||||
	// copy the original, unchanged URL into the context
 | 
			
		||||
	// so it can be referenced by middlewares
 | 
			
		||||
	urlCopy := *r.URL
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,7 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/mholt/caddy"
 | 
			
		||||
	"github.com/mholt/caddy/diagnostics"
 | 
			
		||||
	"github.com/xenolf/lego/acme"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -276,6 +277,8 @@ Attempts:
 | 
			
		||||
		break
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go diagnostics.Increment("acme_certificates_obtained")
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -350,8 +353,9 @@ func (c *ACMEClient) Renew(name string) error {
 | 
			
		||||
		return errors.New("too many renewal attempts; last error: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Executes Cert renew events
 | 
			
		||||
	caddy.EmitEvent(caddy.CertRenewEvent, name)
 | 
			
		||||
	go diagnostics.Increment("acme_certificates_obtained")
 | 
			
		||||
	go diagnostics.Increment("acme_certificates_renewed")
 | 
			
		||||
 | 
			
		||||
	return saveCertResource(storage, newCertMeta)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										250
									
								
								diagnostics/collection.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								diagnostics/collection.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,250 @@
 | 
			
		||||
// Copyright 2015 Light Code Labs, LLC
 | 
			
		||||
//
 | 
			
		||||
// 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 diagnostics
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"log"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Init initializes this package so that it may
 | 
			
		||||
// be used. Do not call this function more than
 | 
			
		||||
// once. Init panics if it is called more than
 | 
			
		||||
// once or if the UUID value is empty. Once this
 | 
			
		||||
// function is called, the rest of the package
 | 
			
		||||
// may safely be used. If this function is not
 | 
			
		||||
// called, the collector functions may still be
 | 
			
		||||
// invoked, but they will be no-ops.
 | 
			
		||||
func Init(instanceID uuid.UUID) {
 | 
			
		||||
	if enabled {
 | 
			
		||||
		panic("already initialized")
 | 
			
		||||
	}
 | 
			
		||||
	if instanceID.String() == "" {
 | 
			
		||||
		panic("empty UUID")
 | 
			
		||||
	}
 | 
			
		||||
	instanceUUID = instanceID
 | 
			
		||||
	enabled = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StartEmitting sends the current payload and begins the
 | 
			
		||||
// transmission cycle for updates. This is the first
 | 
			
		||||
// update sent, and future ones will be sent until
 | 
			
		||||
// StopEmitting is called.
 | 
			
		||||
//
 | 
			
		||||
// This function is non-blocking (it spawns a new goroutine).
 | 
			
		||||
//
 | 
			
		||||
// This function panics if it was called more than once.
 | 
			
		||||
// It is a no-op if this package was not initialized.
 | 
			
		||||
func StartEmitting() {
 | 
			
		||||
	if !enabled {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	updateTimerMu.Lock()
 | 
			
		||||
	if updateTimer != nil {
 | 
			
		||||
		updateTimerMu.Unlock()
 | 
			
		||||
		panic("updates already started")
 | 
			
		||||
	}
 | 
			
		||||
	updateTimerMu.Unlock()
 | 
			
		||||
	updateMu.Lock()
 | 
			
		||||
	if updating {
 | 
			
		||||
		updateMu.Unlock()
 | 
			
		||||
		panic("update already in progress")
 | 
			
		||||
	}
 | 
			
		||||
	updateMu.Unlock()
 | 
			
		||||
	go logEmit(false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StopEmitting sends the current payload and terminates
 | 
			
		||||
// the update cycle. No more updates will be sent.
 | 
			
		||||
//
 | 
			
		||||
// It is a no-op if the package was never initialized
 | 
			
		||||
// or if emitting was never started.
 | 
			
		||||
func StopEmitting() {
 | 
			
		||||
	if !enabled {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	updateTimerMu.Lock()
 | 
			
		||||
	if updateTimer == nil {
 | 
			
		||||
		updateTimerMu.Unlock()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	updateTimerMu.Unlock()
 | 
			
		||||
	logEmit(true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set puts a value in the buffer to be included
 | 
			
		||||
// in the next emission. It overwrites any
 | 
			
		||||
// previous value.
 | 
			
		||||
//
 | 
			
		||||
// This function is safe for multiple goroutines,
 | 
			
		||||
// and it is recommended to call this using the
 | 
			
		||||
// go keyword after the call to SendHello so it
 | 
			
		||||
// doesn't block crucial code.
 | 
			
		||||
func Set(key string, val interface{}) {
 | 
			
		||||
	if !enabled {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bufferMu.Lock()
 | 
			
		||||
	if bufferItemCount >= maxBufferItems {
 | 
			
		||||
		bufferMu.Unlock()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := buffer[key]; !ok {
 | 
			
		||||
		bufferItemCount++
 | 
			
		||||
	}
 | 
			
		||||
	buffer[key] = val
 | 
			
		||||
	bufferMu.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Append appends value to a list named key.
 | 
			
		||||
// If key is new, a new list will be created.
 | 
			
		||||
// If key maps to a type that is not a list,
 | 
			
		||||
// an error is logged, and this is a no-op.
 | 
			
		||||
//
 | 
			
		||||
// TODO: is this function needed/useful?
 | 
			
		||||
func Append(key string, value interface{}) {
 | 
			
		||||
	if !enabled {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bufferMu.Lock()
 | 
			
		||||
	if bufferItemCount >= maxBufferItems {
 | 
			
		||||
		bufferMu.Unlock()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// TODO: Test this...
 | 
			
		||||
	bufVal, inBuffer := buffer[key]
 | 
			
		||||
	sliceVal, sliceOk := bufVal.([]interface{})
 | 
			
		||||
	if inBuffer && !sliceOk {
 | 
			
		||||
		bufferMu.Unlock()
 | 
			
		||||
		log.Printf("[PANIC] Diagnostics: key %s already used for non-slice value", key)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if sliceVal == nil {
 | 
			
		||||
		buffer[key] = []interface{}{value}
 | 
			
		||||
	} else if sliceOk {
 | 
			
		||||
		buffer[key] = append(sliceVal, value)
 | 
			
		||||
	}
 | 
			
		||||
	bufferItemCount++
 | 
			
		||||
	bufferMu.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AppendUniqueString adds value to a set named key.
 | 
			
		||||
// Set items are unordered. Values in the set
 | 
			
		||||
// are unique, but repeat values are counted.
 | 
			
		||||
//
 | 
			
		||||
// If key is new, a new set will be created.
 | 
			
		||||
// If key maps to a type that is not a string
 | 
			
		||||
// set, an error is logged, and this is a no-op.
 | 
			
		||||
func AppendUniqueString(key, value string) {
 | 
			
		||||
	if !enabled {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bufferMu.Lock()
 | 
			
		||||
	if bufferItemCount >= maxBufferItems {
 | 
			
		||||
		bufferMu.Unlock()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bufVal, inBuffer := buffer[key]
 | 
			
		||||
	mapVal, mapOk := bufVal.(map[string]int)
 | 
			
		||||
	if inBuffer && !mapOk {
 | 
			
		||||
		bufferMu.Unlock()
 | 
			
		||||
		log.Printf("[PANIC] Diagnostics: key %s already used for non-map value", key)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if mapVal == nil {
 | 
			
		||||
		buffer[key] = map[string]int{value: 1}
 | 
			
		||||
		bufferItemCount++
 | 
			
		||||
	} else if mapOk {
 | 
			
		||||
		mapVal[value]++
 | 
			
		||||
	}
 | 
			
		||||
	bufferMu.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AppendUniqueInt adds value to a set named key.
 | 
			
		||||
// Set items are unordered. Values in the set
 | 
			
		||||
// are unique, but repeat values are counted.
 | 
			
		||||
//
 | 
			
		||||
// If key is new, a new set will be created.
 | 
			
		||||
// If key maps to a type that is not an integer
 | 
			
		||||
// set, an error is logged, and this is a no-op.
 | 
			
		||||
func AppendUniqueInt(key string, value int) {
 | 
			
		||||
	if !enabled {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bufferMu.Lock()
 | 
			
		||||
	if bufferItemCount >= maxBufferItems {
 | 
			
		||||
		bufferMu.Unlock()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bufVal, inBuffer := buffer[key]
 | 
			
		||||
	mapVal, mapOk := bufVal.(map[int]int)
 | 
			
		||||
	if inBuffer && !mapOk {
 | 
			
		||||
		bufferMu.Unlock()
 | 
			
		||||
		log.Printf("[PANIC] Diagnostics: key %s already used for non-map value", key)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if mapVal == nil {
 | 
			
		||||
		buffer[key] = map[int]int{value: 1}
 | 
			
		||||
		bufferItemCount++
 | 
			
		||||
	} else if mapOk {
 | 
			
		||||
		mapVal[value]++
 | 
			
		||||
	}
 | 
			
		||||
	bufferMu.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Increment adds 1 to a value named key.
 | 
			
		||||
// If it does not exist, it is created with
 | 
			
		||||
// a value of 1. If key maps to a type that
 | 
			
		||||
// is not an integer, an error is logged,
 | 
			
		||||
// and this is a no-op.
 | 
			
		||||
func Increment(key string) {
 | 
			
		||||
	incrementOrDecrement(key, true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decrement is the same as increment except
 | 
			
		||||
// it subtracts 1.
 | 
			
		||||
func Decrement(key string) {
 | 
			
		||||
	incrementOrDecrement(key, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// inc == true:  increment
 | 
			
		||||
// inc == false: decrement
 | 
			
		||||
func incrementOrDecrement(key string, inc bool) {
 | 
			
		||||
	if !enabled {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bufferMu.Lock()
 | 
			
		||||
	bufVal, inBuffer := buffer[key]
 | 
			
		||||
	intVal, intOk := bufVal.(int)
 | 
			
		||||
	if inBuffer && !intOk {
 | 
			
		||||
		bufferMu.Unlock()
 | 
			
		||||
		log.Printf("[PANIC] Diagnostics: key %s already used for non-integer value", key)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if !inBuffer {
 | 
			
		||||
		if bufferItemCount >= maxBufferItems {
 | 
			
		||||
			bufferMu.Unlock()
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		bufferItemCount++
 | 
			
		||||
	}
 | 
			
		||||
	if inc {
 | 
			
		||||
		buffer[key] = intVal + 1
 | 
			
		||||
	} else {
 | 
			
		||||
		buffer[key] = intVal - 1
 | 
			
		||||
	}
 | 
			
		||||
	bufferMu.Unlock()
 | 
			
		||||
}
 | 
			
		||||
@ -12,14 +12,252 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
// Package diagnostics implements the client for server-side diagnostics
 | 
			
		||||
// of the network. Functions in this package are synchronous and blocking
 | 
			
		||||
// unless otherwise specified. For convenience, most functions here do
 | 
			
		||||
// not return errors, but errors are logged to the standard logger.
 | 
			
		||||
//
 | 
			
		||||
// To use this package, first call Init(). You can then call any of the
 | 
			
		||||
// collection/aggregation functions. Call StartEmitting() when you are
 | 
			
		||||
// ready to begin sending diagnostic updates.
 | 
			
		||||
//
 | 
			
		||||
// When collecting metrics (functions like Set, Append*, or Increment),
 | 
			
		||||
// it may be desirable and even recommended to run invoke them in a new
 | 
			
		||||
// goroutine (use the go keyword) in case there is lock contention;
 | 
			
		||||
// they are thread-safe (unless noted), and you may not want them to
 | 
			
		||||
// block the main thread of execution. However, sometimes blocking
 | 
			
		||||
// may be necessary too; for example, adding startup metrics to the
 | 
			
		||||
// buffer before the call to StartEmitting().
 | 
			
		||||
package diagnostics
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Init(uuid uuid.UUID) {
 | 
			
		||||
	instanceUUID = uuid
 | 
			
		||||
// logEmit calls emit and then logs the error, if any.
 | 
			
		||||
func logEmit(final bool) {
 | 
			
		||||
	err := emit(final)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf("[ERROR] Sending diganostics: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// emit sends an update to the diagnostics server.
 | 
			
		||||
// If final is true, no future updates will be scheduled.
 | 
			
		||||
// Otherwise, the next update will be scheduled.
 | 
			
		||||
func emit(final bool) error {
 | 
			
		||||
	if !enabled {
 | 
			
		||||
		return fmt.Errorf("diagnostics not enabled")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// ensure only one update happens at a time;
 | 
			
		||||
	// skip update if previous one still in progress
 | 
			
		||||
	updateMu.Lock()
 | 
			
		||||
	if updating {
 | 
			
		||||
		updateMu.Unlock()
 | 
			
		||||
		log.Println("[NOTICE] Skipping this diagnostics update because previous one is still working")
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	updating = true
 | 
			
		||||
	updateMu.Unlock()
 | 
			
		||||
	defer func() {
 | 
			
		||||
		updateMu.Lock()
 | 
			
		||||
		updating = false
 | 
			
		||||
		updateMu.Unlock()
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	// terminate any pending update if this is the last one
 | 
			
		||||
	if final {
 | 
			
		||||
		updateTimerMu.Lock()
 | 
			
		||||
		updateTimer.Stop()
 | 
			
		||||
		updateTimer = nil
 | 
			
		||||
		updateTimerMu.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	payloadBytes, err := makePayloadAndResetBuffer()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// this will hold the server's reply
 | 
			
		||||
	var reply Response
 | 
			
		||||
 | 
			
		||||
	// transmit the payload - use a loop to retry in case of failure
 | 
			
		||||
	for i := 0; i < 4; i++ {
 | 
			
		||||
		if i > 0 && err != nil {
 | 
			
		||||
			// don't hammer the server; first failure might have been
 | 
			
		||||
			// a fluke, but back off more after that
 | 
			
		||||
			log.Printf("[WARNING] Sending diagnostics (attempt %d): %v - waiting and retrying", i, err)
 | 
			
		||||
			time.Sleep(time.Duration(i*i*i) * time.Second)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// send it
 | 
			
		||||
		var resp *http.Response
 | 
			
		||||
		resp, err = httpClient.Post(endpoint+instanceUUID.String(), "application/json", bytes.NewReader(payloadBytes))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// ensure we can read the response
 | 
			
		||||
		if ct := resp.Header.Get("Content-Type"); (resp.StatusCode < 300 || resp.StatusCode >= 400) &&
 | 
			
		||||
			!strings.Contains(ct, "json") {
 | 
			
		||||
			err = fmt.Errorf("diagnostics server replied with unknown content-type: %s", ct)
 | 
			
		||||
			resp.Body.Close()
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// read the response body
 | 
			
		||||
		err = json.NewDecoder(resp.Body).Decode(&reply)
 | 
			
		||||
		resp.Body.Close() // close response body as soon as we're done with it
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// ensure we won't slam the diagnostics server
 | 
			
		||||
		if reply.NextUpdate < 1*time.Second {
 | 
			
		||||
			reply.NextUpdate = defaultUpdateInterval
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// make sure we didn't send the update too soon; if so,
 | 
			
		||||
		// just wait and try again -- this is a special case of
 | 
			
		||||
		// error that we handle differently, as you can see
 | 
			
		||||
		if resp.StatusCode == http.StatusTooManyRequests {
 | 
			
		||||
			log.Printf("[NOTICE] Sending diagnostics: we were too early; waiting %s before trying again", reply.NextUpdate)
 | 
			
		||||
			time.Sleep(reply.NextUpdate)
 | 
			
		||||
			continue
 | 
			
		||||
		} else if resp.StatusCode >= 400 {
 | 
			
		||||
			err = fmt.Errorf("diagnostics server returned status code %d", resp.StatusCode)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		break
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		// (remember, if there was an error, we return it
 | 
			
		||||
		// below, so it will get logged if it's supposed to)
 | 
			
		||||
		log.Println("[INFO] Sending diagnostics: success")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// even if there was an error after retrying, we should
 | 
			
		||||
	// schedule the next update using our default update
 | 
			
		||||
	// interval because the server might be healthy later
 | 
			
		||||
 | 
			
		||||
	// schedule the next update (if this wasn't the last one and
 | 
			
		||||
	// if the remote server didn't tell us to stop sending)
 | 
			
		||||
	if !final && !reply.Stop {
 | 
			
		||||
		updateTimerMu.Lock()
 | 
			
		||||
		updateTimer = time.AfterFunc(reply.NextUpdate, func() {
 | 
			
		||||
			logEmit(false)
 | 
			
		||||
		})
 | 
			
		||||
		updateTimerMu.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// makePayloadAndResetBuffer prepares a payload
 | 
			
		||||
// by emptying the collection buffer. It returns
 | 
			
		||||
// the bytes of the payload to send to the server.
 | 
			
		||||
// Since the buffer is reset by this, if the
 | 
			
		||||
// resulting byte slice is lost, the payload is
 | 
			
		||||
// gone with it.
 | 
			
		||||
func makePayloadAndResetBuffer() ([]byte, error) {
 | 
			
		||||
	// make a local pointer to the buffer, then reset
 | 
			
		||||
	// the buffer to an empty map to clear it out
 | 
			
		||||
	bufferMu.Lock()
 | 
			
		||||
	bufCopy := buffer
 | 
			
		||||
	buffer = make(map[string]interface{})
 | 
			
		||||
	bufferItemCount = 0
 | 
			
		||||
	bufferMu.Unlock()
 | 
			
		||||
 | 
			
		||||
	// encode payload in preparation for transmission
 | 
			
		||||
	payload := Payload{
 | 
			
		||||
		InstanceID: instanceUUID.String(),
 | 
			
		||||
		Timestamp:  time.Now().UTC(),
 | 
			
		||||
		Data:       bufCopy,
 | 
			
		||||
	}
 | 
			
		||||
	return json.Marshal(payload)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Response contains the body of a response from the
 | 
			
		||||
// diagnostics server.
 | 
			
		||||
type Response struct {
 | 
			
		||||
	// NextUpdate is how long to wait before the next update.
 | 
			
		||||
	NextUpdate time.Duration `json:"next_update"`
 | 
			
		||||
 | 
			
		||||
	// Stop instructs the diagnostics server to stop sending
 | 
			
		||||
	// diagnostics. This would only be done under extenuating
 | 
			
		||||
	// circumstances, but we are prepared for it nonetheless.
 | 
			
		||||
	Stop bool `json:"stop,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// Error will be populated with an error message, if any.
 | 
			
		||||
	// This field should be empty if the status code is < 400.
 | 
			
		||||
	Error string `json:"error,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Payload is the data that gets sent to the diagnostics server.
 | 
			
		||||
type Payload struct {
 | 
			
		||||
	// The universally unique ID of the instance
 | 
			
		||||
	InstanceID string `json:"instance_id"`
 | 
			
		||||
 | 
			
		||||
	// The UTC timestamp of the transmission
 | 
			
		||||
	Timestamp time.Time `json:"timestamp"`
 | 
			
		||||
 | 
			
		||||
	// The metrics
 | 
			
		||||
	Data map[string]interface{} `json:"data,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// httpClient should be used for HTTP requests. It
 | 
			
		||||
// is configured with a timeout for reliability.
 | 
			
		||||
var httpClient = http.Client{Timeout: 1 * time.Minute}
 | 
			
		||||
 | 
			
		||||
// buffer holds the data that we are building up to send.
 | 
			
		||||
var buffer = make(map[string]interface{})
 | 
			
		||||
var bufferItemCount = 0
 | 
			
		||||
var bufferMu sync.RWMutex // protects both the buffer and its count
 | 
			
		||||
 | 
			
		||||
// updating is used to ensure only one
 | 
			
		||||
// update happens at a time.
 | 
			
		||||
var updating bool
 | 
			
		||||
var updateMu sync.Mutex
 | 
			
		||||
 | 
			
		||||
// updateTimer fires off the next update.
 | 
			
		||||
// If no update is scheduled, this is nil.
 | 
			
		||||
var updateTimer *time.Timer
 | 
			
		||||
var updateTimerMu sync.Mutex
 | 
			
		||||
 | 
			
		||||
// instanceUUID is the ID of the current instance.
 | 
			
		||||
// This MUST be set to emit diagnostics.
 | 
			
		||||
var instanceUUID uuid.UUID
 | 
			
		||||
 | 
			
		||||
// enabled indicates whether the package has
 | 
			
		||||
// been initialized and can be actively used.
 | 
			
		||||
var enabled bool
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// endpoint is the base URL to remote diagnostics server;
 | 
			
		||||
	// the instance ID will be appended to it.
 | 
			
		||||
	endpoint = "http://localhost:8081/update/"
 | 
			
		||||
 | 
			
		||||
	// defaultUpdateInterval is how long to wait before emitting
 | 
			
		||||
	// more diagnostic data. This value is only used if the
 | 
			
		||||
	// client receives a nonsensical value, or doesn't send one
 | 
			
		||||
	// at all, indicating a likely problem with the server. Thus,
 | 
			
		||||
	// this value should be a long duration to help alleviate
 | 
			
		||||
	// extra load on the server.
 | 
			
		||||
	defaultUpdateInterval = 1 * time.Hour
 | 
			
		||||
 | 
			
		||||
	// maxBufferItems is the maximum number of items we'll allow
 | 
			
		||||
	// in the buffer before we start dropping new ones, in a
 | 
			
		||||
	// rough (simple) attempt to keep memory use under control.
 | 
			
		||||
	maxBufferItems = 100000
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/github.com/klauspost/cpuid/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/klauspost/cpuid/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2015 Klaus Post
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1030
									
								
								vendor/github.com/klauspost/cpuid/cpuid.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1030
									
								
								vendor/github.com/klauspost/cpuid/cpuid.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										42
									
								
								vendor/github.com/klauspost/cpuid/cpuid_386.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/klauspost/cpuid/cpuid_386.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
			
		||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build 386,!gccgo
 | 
			
		||||
 | 
			
		||||
// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
 | 
			
		||||
TEXT ·asmCpuid(SB), 7, $0
 | 
			
		||||
	XORL CX, CX
 | 
			
		||||
	MOVL op+0(FP), AX
 | 
			
		||||
	CPUID
 | 
			
		||||
	MOVL AX, eax+4(FP)
 | 
			
		||||
	MOVL BX, ebx+8(FP)
 | 
			
		||||
	MOVL CX, ecx+12(FP)
 | 
			
		||||
	MOVL DX, edx+16(FP)
 | 
			
		||||
	RET
 | 
			
		||||
 | 
			
		||||
// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
 | 
			
		||||
TEXT ·asmCpuidex(SB), 7, $0
 | 
			
		||||
	MOVL op+0(FP), AX
 | 
			
		||||
	MOVL op2+4(FP), CX
 | 
			
		||||
	CPUID
 | 
			
		||||
	MOVL AX, eax+8(FP)
 | 
			
		||||
	MOVL BX, ebx+12(FP)
 | 
			
		||||
	MOVL CX, ecx+16(FP)
 | 
			
		||||
	MOVL DX, edx+20(FP)
 | 
			
		||||
	RET
 | 
			
		||||
 | 
			
		||||
// func xgetbv(index uint32) (eax, edx uint32)
 | 
			
		||||
TEXT ·asmXgetbv(SB), 7, $0
 | 
			
		||||
	MOVL index+0(FP), CX
 | 
			
		||||
	BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV
 | 
			
		||||
	MOVL AX, eax+4(FP)
 | 
			
		||||
	MOVL DX, edx+8(FP)
 | 
			
		||||
	RET
 | 
			
		||||
 | 
			
		||||
// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
 | 
			
		||||
TEXT ·asmRdtscpAsm(SB), 7, $0
 | 
			
		||||
	BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP
 | 
			
		||||
	MOVL AX, eax+0(FP)
 | 
			
		||||
	MOVL BX, ebx+4(FP)
 | 
			
		||||
	MOVL CX, ecx+8(FP)
 | 
			
		||||
	MOVL DX, edx+12(FP)
 | 
			
		||||
	RET
 | 
			
		||||
							
								
								
									
										42
									
								
								vendor/github.com/klauspost/cpuid/cpuid_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/klauspost/cpuid/cpuid_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
			
		||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
 | 
			
		||||
 | 
			
		||||
//+build amd64,!gccgo
 | 
			
		||||
 | 
			
		||||
// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
 | 
			
		||||
TEXT ·asmCpuid(SB), 7, $0
 | 
			
		||||
	XORQ CX, CX
 | 
			
		||||
	MOVL op+0(FP), AX
 | 
			
		||||
	CPUID
 | 
			
		||||
	MOVL AX, eax+8(FP)
 | 
			
		||||
	MOVL BX, ebx+12(FP)
 | 
			
		||||
	MOVL CX, ecx+16(FP)
 | 
			
		||||
	MOVL DX, edx+20(FP)
 | 
			
		||||
	RET
 | 
			
		||||
 | 
			
		||||
// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
 | 
			
		||||
TEXT ·asmCpuidex(SB), 7, $0
 | 
			
		||||
	MOVL op+0(FP), AX
 | 
			
		||||
	MOVL op2+4(FP), CX
 | 
			
		||||
	CPUID
 | 
			
		||||
	MOVL AX, eax+8(FP)
 | 
			
		||||
	MOVL BX, ebx+12(FP)
 | 
			
		||||
	MOVL CX, ecx+16(FP)
 | 
			
		||||
	MOVL DX, edx+20(FP)
 | 
			
		||||
	RET
 | 
			
		||||
 | 
			
		||||
// func asmXgetbv(index uint32) (eax, edx uint32)
 | 
			
		||||
TEXT ·asmXgetbv(SB), 7, $0
 | 
			
		||||
	MOVL index+0(FP), CX
 | 
			
		||||
	BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV
 | 
			
		||||
	MOVL AX, eax+8(FP)
 | 
			
		||||
	MOVL DX, edx+12(FP)
 | 
			
		||||
	RET
 | 
			
		||||
 | 
			
		||||
// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
 | 
			
		||||
TEXT ·asmRdtscpAsm(SB), 7, $0
 | 
			
		||||
	BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP
 | 
			
		||||
	MOVL AX, eax+0(FP)
 | 
			
		||||
	MOVL BX, ebx+4(FP)
 | 
			
		||||
	MOVL CX, ecx+8(FP)
 | 
			
		||||
	MOVL DX, edx+12(FP)
 | 
			
		||||
	RET
 | 
			
		||||
							
								
								
									
										17
									
								
								vendor/github.com/klauspost/cpuid/detect_intel.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/klauspost/cpuid/detect_intel.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build 386,!gccgo amd64,!gccgo
 | 
			
		||||
 | 
			
		||||
package cpuid
 | 
			
		||||
 | 
			
		||||
func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
 | 
			
		||||
func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
 | 
			
		||||
func asmXgetbv(index uint32) (eax, edx uint32)
 | 
			
		||||
func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
 | 
			
		||||
 | 
			
		||||
func initCPU() {
 | 
			
		||||
	cpuid = asmCpuid
 | 
			
		||||
	cpuidex = asmCpuidex
 | 
			
		||||
	xgetbv = asmXgetbv
 | 
			
		||||
	rdtscpAsm = asmRdtscpAsm
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								vendor/github.com/klauspost/cpuid/detect_ref.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/klauspost/cpuid/detect_ref.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !amd64,!386 gccgo
 | 
			
		||||
 | 
			
		||||
package cpuid
 | 
			
		||||
 | 
			
		||||
func initCPU() {
 | 
			
		||||
	cpuid = func(op uint32) (eax, ebx, ecx, edx uint32) {
 | 
			
		||||
		return 0, 0, 0, 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cpuidex = func(op, op2 uint32) (eax, ebx, ecx, edx uint32) {
 | 
			
		||||
		return 0, 0, 0, 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	xgetbv = func(index uint32) (eax, edx uint32) {
 | 
			
		||||
		return 0, 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rdtscpAsm = func() (eax, ebx, ecx, edx uint32) {
 | 
			
		||||
		return 0, 0, 0, 0
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								vendor/github.com/klauspost/cpuid/generate.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/klauspost/cpuid/generate.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
			
		||||
package cpuid
 | 
			
		||||
 | 
			
		||||
//go:generate go run private-gen.go
 | 
			
		||||
//go:generate gofmt -w ./private
 | 
			
		||||
							
								
								
									
										476
									
								
								vendor/github.com/klauspost/cpuid/private-gen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										476
									
								
								vendor/github.com/klauspost/cpuid/private-gen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,476 @@
 | 
			
		||||
// +build ignore
 | 
			
		||||
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/ast"
 | 
			
		||||
	"go/parser"
 | 
			
		||||
	"go/printer"
 | 
			
		||||
	"go/token"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"unicode"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var inFiles = []string{"cpuid.go", "cpuid_test.go"}
 | 
			
		||||
var copyFiles = []string{"cpuid_amd64.s", "cpuid_386.s", "detect_ref.go", "detect_intel.go"}
 | 
			
		||||
var fileSet = token.NewFileSet()
 | 
			
		||||
var reWrites = []rewrite{
 | 
			
		||||
	initRewrite("CPUInfo -> cpuInfo"),
 | 
			
		||||
	initRewrite("Vendor -> vendor"),
 | 
			
		||||
	initRewrite("Flags -> flags"),
 | 
			
		||||
	initRewrite("Detect -> detect"),
 | 
			
		||||
	initRewrite("CPU -> cpu"),
 | 
			
		||||
}
 | 
			
		||||
var excludeNames = map[string]bool{"string": true, "join": true, "trim": true,
 | 
			
		||||
	// cpuid_test.go
 | 
			
		||||
	"t": true, "println": true, "logf": true, "log": true, "fatalf": true, "fatal": true,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var excludePrefixes = []string{"test", "benchmark"}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	Package := "private"
 | 
			
		||||
	parserMode := parser.ParseComments
 | 
			
		||||
	exported := make(map[string]rewrite)
 | 
			
		||||
	for _, file := range inFiles {
 | 
			
		||||
		in, err := os.Open(file)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatalf("opening input", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		src, err := ioutil.ReadAll(in)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatalf("reading input", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		astfile, err := parser.ParseFile(fileSet, file, src, parserMode)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatalf("parsing input", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, rw := range reWrites {
 | 
			
		||||
			astfile = rw(astfile)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Inspect the AST and print all identifiers and literals.
 | 
			
		||||
		var startDecl token.Pos
 | 
			
		||||
		var endDecl token.Pos
 | 
			
		||||
		ast.Inspect(astfile, func(n ast.Node) bool {
 | 
			
		||||
			var s string
 | 
			
		||||
			switch x := n.(type) {
 | 
			
		||||
			case *ast.Ident:
 | 
			
		||||
				if x.IsExported() {
 | 
			
		||||
					t := strings.ToLower(x.Name)
 | 
			
		||||
					for _, pre := range excludePrefixes {
 | 
			
		||||
						if strings.HasPrefix(t, pre) {
 | 
			
		||||
							return true
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					if excludeNames[t] != true {
 | 
			
		||||
						//if x.Pos() > startDecl && x.Pos() < endDecl {
 | 
			
		||||
						exported[x.Name] = initRewrite(x.Name + " -> " + t)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case *ast.GenDecl:
 | 
			
		||||
				if x.Tok == token.CONST && x.Lparen > 0 {
 | 
			
		||||
					startDecl = x.Lparen
 | 
			
		||||
					endDecl = x.Rparen
 | 
			
		||||
					// fmt.Printf("Decl:%s -> %s\n", fileSet.Position(startDecl), fileSet.Position(endDecl))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if s != "" {
 | 
			
		||||
				fmt.Printf("%s:\t%s\n", fileSet.Position(n.Pos()), s)
 | 
			
		||||
			}
 | 
			
		||||
			return true
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		for _, rw := range exported {
 | 
			
		||||
			astfile = rw(astfile)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var buf bytes.Buffer
 | 
			
		||||
 | 
			
		||||
		printer.Fprint(&buf, fileSet, astfile)
 | 
			
		||||
 | 
			
		||||
		// Remove package documentation and insert information
 | 
			
		||||
		s := buf.String()
 | 
			
		||||
		ind := strings.Index(buf.String(), "\npackage cpuid")
 | 
			
		||||
		s = s[ind:]
 | 
			
		||||
		s = "// Generated, DO NOT EDIT,\n" +
 | 
			
		||||
			"// but copy it to your own project and rename the package.\n" +
 | 
			
		||||
			"// See more at http://github.com/klauspost/cpuid\n" +
 | 
			
		||||
			s
 | 
			
		||||
 | 
			
		||||
		outputName := Package + string(os.PathSeparator) + file
 | 
			
		||||
 | 
			
		||||
		err = ioutil.WriteFile(outputName, []byte(s), 0644)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatalf("writing output: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		log.Println("Generated", outputName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, file := range copyFiles {
 | 
			
		||||
		dst := ""
 | 
			
		||||
		if strings.HasPrefix(file, "cpuid") {
 | 
			
		||||
			dst = Package + string(os.PathSeparator) + file
 | 
			
		||||
		} else {
 | 
			
		||||
			dst = Package + string(os.PathSeparator) + "cpuid_" + file
 | 
			
		||||
		}
 | 
			
		||||
		err := copyFile(file, dst)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatalf("copying file: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		log.Println("Copied", dst)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CopyFile copies a file from src to dst. If src and dst files exist, and are
 | 
			
		||||
// the same, then return success. Copy the file contents from src to dst.
 | 
			
		||||
func copyFile(src, dst string) (err error) {
 | 
			
		||||
	sfi, err := os.Stat(src)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if !sfi.Mode().IsRegular() {
 | 
			
		||||
		// cannot copy non-regular files (e.g., directories,
 | 
			
		||||
		// symlinks, devices, etc.)
 | 
			
		||||
		return fmt.Errorf("CopyFile: non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String())
 | 
			
		||||
	}
 | 
			
		||||
	dfi, err := os.Stat(dst)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if !os.IsNotExist(err) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if !(dfi.Mode().IsRegular()) {
 | 
			
		||||
			return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String())
 | 
			
		||||
		}
 | 
			
		||||
		if os.SameFile(sfi, dfi) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	err = copyFileContents(src, dst)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// copyFileContents copies the contents of the file named src to the file named
 | 
			
		||||
// by dst. The file will be created if it does not already exist. If the
 | 
			
		||||
// destination file exists, all it's contents will be replaced by the contents
 | 
			
		||||
// of the source file.
 | 
			
		||||
func copyFileContents(src, dst string) (err error) {
 | 
			
		||||
	in, err := os.Open(src)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer in.Close()
 | 
			
		||||
	out, err := os.Create(dst)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		cerr := out.Close()
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			err = cerr
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	if _, err = io.Copy(out, in); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	err = out.Sync()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type rewrite func(*ast.File) *ast.File
 | 
			
		||||
 | 
			
		||||
// Mostly copied from gofmt
 | 
			
		||||
func initRewrite(rewriteRule string) rewrite {
 | 
			
		||||
	f := strings.Split(rewriteRule, "->")
 | 
			
		||||
	if len(f) != 2 {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "rewrite rule must be of the form 'pattern -> replacement'\n")
 | 
			
		||||
		os.Exit(2)
 | 
			
		||||
	}
 | 
			
		||||
	pattern := parseExpr(f[0], "pattern")
 | 
			
		||||
	replace := parseExpr(f[1], "replacement")
 | 
			
		||||
	return func(p *ast.File) *ast.File { return rewriteFile(pattern, replace, p) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parseExpr parses s as an expression.
 | 
			
		||||
// It might make sense to expand this to allow statement patterns,
 | 
			
		||||
// but there are problems with preserving formatting and also
 | 
			
		||||
// with what a wildcard for a statement looks like.
 | 
			
		||||
func parseExpr(s, what string) ast.Expr {
 | 
			
		||||
	x, err := parser.ParseExpr(s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "parsing %s %s at %s\n", what, s, err)
 | 
			
		||||
		os.Exit(2)
 | 
			
		||||
	}
 | 
			
		||||
	return x
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Keep this function for debugging.
 | 
			
		||||
/*
 | 
			
		||||
func dump(msg string, val reflect.Value) {
 | 
			
		||||
	fmt.Printf("%s:\n", msg)
 | 
			
		||||
	ast.Print(fileSet, val.Interface())
 | 
			
		||||
	fmt.Println()
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// rewriteFile applies the rewrite rule 'pattern -> replace' to an entire file.
 | 
			
		||||
func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File {
 | 
			
		||||
	cmap := ast.NewCommentMap(fileSet, p, p.Comments)
 | 
			
		||||
	m := make(map[string]reflect.Value)
 | 
			
		||||
	pat := reflect.ValueOf(pattern)
 | 
			
		||||
	repl := reflect.ValueOf(replace)
 | 
			
		||||
 | 
			
		||||
	var rewriteVal func(val reflect.Value) reflect.Value
 | 
			
		||||
	rewriteVal = func(val reflect.Value) reflect.Value {
 | 
			
		||||
		// don't bother if val is invalid to start with
 | 
			
		||||
		if !val.IsValid() {
 | 
			
		||||
			return reflect.Value{}
 | 
			
		||||
		}
 | 
			
		||||
		for k := range m {
 | 
			
		||||
			delete(m, k)
 | 
			
		||||
		}
 | 
			
		||||
		val = apply(rewriteVal, val)
 | 
			
		||||
		if match(m, pat, val) {
 | 
			
		||||
			val = subst(m, repl, reflect.ValueOf(val.Interface().(ast.Node).Pos()))
 | 
			
		||||
		}
 | 
			
		||||
		return val
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := apply(rewriteVal, reflect.ValueOf(p)).Interface().(*ast.File)
 | 
			
		||||
	r.Comments = cmap.Filter(r).Comments() // recreate comments list
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// set is a wrapper for x.Set(y); it protects the caller from panics if x cannot be changed to y.
 | 
			
		||||
func set(x, y reflect.Value) {
 | 
			
		||||
	// don't bother if x cannot be set or y is invalid
 | 
			
		||||
	if !x.CanSet() || !y.IsValid() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if x := recover(); x != nil {
 | 
			
		||||
			if s, ok := x.(string); ok &&
 | 
			
		||||
				(strings.Contains(s, "type mismatch") || strings.Contains(s, "not assignable")) {
 | 
			
		||||
				// x cannot be set to y - ignore this rewrite
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			panic(x)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	x.Set(y)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Values/types for special cases.
 | 
			
		||||
var (
 | 
			
		||||
	objectPtrNil = reflect.ValueOf((*ast.Object)(nil))
 | 
			
		||||
	scopePtrNil  = reflect.ValueOf((*ast.Scope)(nil))
 | 
			
		||||
 | 
			
		||||
	identType     = reflect.TypeOf((*ast.Ident)(nil))
 | 
			
		||||
	objectPtrType = reflect.TypeOf((*ast.Object)(nil))
 | 
			
		||||
	positionType  = reflect.TypeOf(token.NoPos)
 | 
			
		||||
	callExprType  = reflect.TypeOf((*ast.CallExpr)(nil))
 | 
			
		||||
	scopePtrType  = reflect.TypeOf((*ast.Scope)(nil))
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// apply replaces each AST field x in val with f(x), returning val.
 | 
			
		||||
// To avoid extra conversions, f operates on the reflect.Value form.
 | 
			
		||||
func apply(f func(reflect.Value) reflect.Value, val reflect.Value) reflect.Value {
 | 
			
		||||
	if !val.IsValid() {
 | 
			
		||||
		return reflect.Value{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// *ast.Objects introduce cycles and are likely incorrect after
 | 
			
		||||
	// rewrite; don't follow them but replace with nil instead
 | 
			
		||||
	if val.Type() == objectPtrType {
 | 
			
		||||
		return objectPtrNil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// similarly for scopes: they are likely incorrect after a rewrite;
 | 
			
		||||
	// replace them with nil
 | 
			
		||||
	if val.Type() == scopePtrType {
 | 
			
		||||
		return scopePtrNil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch v := reflect.Indirect(val); v.Kind() {
 | 
			
		||||
	case reflect.Slice:
 | 
			
		||||
		for i := 0; i < v.Len(); i++ {
 | 
			
		||||
			e := v.Index(i)
 | 
			
		||||
			set(e, f(e))
 | 
			
		||||
		}
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		for i := 0; i < v.NumField(); i++ {
 | 
			
		||||
			e := v.Field(i)
 | 
			
		||||
			set(e, f(e))
 | 
			
		||||
		}
 | 
			
		||||
	case reflect.Interface:
 | 
			
		||||
		e := v.Elem()
 | 
			
		||||
		set(v, f(e))
 | 
			
		||||
	}
 | 
			
		||||
	return val
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isWildcard(s string) bool {
 | 
			
		||||
	rune, size := utf8.DecodeRuneInString(s)
 | 
			
		||||
	return size == len(s) && unicode.IsLower(rune)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// match returns true if pattern matches val,
 | 
			
		||||
// recording wildcard submatches in m.
 | 
			
		||||
// If m == nil, match checks whether pattern == val.
 | 
			
		||||
func match(m map[string]reflect.Value, pattern, val reflect.Value) bool {
 | 
			
		||||
	// Wildcard matches any expression.  If it appears multiple
 | 
			
		||||
	// times in the pattern, it must match the same expression
 | 
			
		||||
	// each time.
 | 
			
		||||
	if m != nil && pattern.IsValid() && pattern.Type() == identType {
 | 
			
		||||
		name := pattern.Interface().(*ast.Ident).Name
 | 
			
		||||
		if isWildcard(name) && val.IsValid() {
 | 
			
		||||
			// wildcards only match valid (non-nil) expressions.
 | 
			
		||||
			if _, ok := val.Interface().(ast.Expr); ok && !val.IsNil() {
 | 
			
		||||
				if old, ok := m[name]; ok {
 | 
			
		||||
					return match(nil, old, val)
 | 
			
		||||
				}
 | 
			
		||||
				m[name] = val
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Otherwise, pattern and val must match recursively.
 | 
			
		||||
	if !pattern.IsValid() || !val.IsValid() {
 | 
			
		||||
		return !pattern.IsValid() && !val.IsValid()
 | 
			
		||||
	}
 | 
			
		||||
	if pattern.Type() != val.Type() {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Special cases.
 | 
			
		||||
	switch pattern.Type() {
 | 
			
		||||
	case identType:
 | 
			
		||||
		// For identifiers, only the names need to match
 | 
			
		||||
		// (and none of the other *ast.Object information).
 | 
			
		||||
		// This is a common case, handle it all here instead
 | 
			
		||||
		// of recursing down any further via reflection.
 | 
			
		||||
		p := pattern.Interface().(*ast.Ident)
 | 
			
		||||
		v := val.Interface().(*ast.Ident)
 | 
			
		||||
		return p == nil && v == nil || p != nil && v != nil && p.Name == v.Name
 | 
			
		||||
	case objectPtrType, positionType:
 | 
			
		||||
		// object pointers and token positions always match
 | 
			
		||||
		return true
 | 
			
		||||
	case callExprType:
 | 
			
		||||
		// For calls, the Ellipsis fields (token.Position) must
 | 
			
		||||
		// match since that is how f(x) and f(x...) are different.
 | 
			
		||||
		// Check them here but fall through for the remaining fields.
 | 
			
		||||
		p := pattern.Interface().(*ast.CallExpr)
 | 
			
		||||
		v := val.Interface().(*ast.CallExpr)
 | 
			
		||||
		if p.Ellipsis.IsValid() != v.Ellipsis.IsValid() {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p := reflect.Indirect(pattern)
 | 
			
		||||
	v := reflect.Indirect(val)
 | 
			
		||||
	if !p.IsValid() || !v.IsValid() {
 | 
			
		||||
		return !p.IsValid() && !v.IsValid()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch p.Kind() {
 | 
			
		||||
	case reflect.Slice:
 | 
			
		||||
		if p.Len() != v.Len() {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		for i := 0; i < p.Len(); i++ {
 | 
			
		||||
			if !match(m, p.Index(i), v.Index(i)) {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		for i := 0; i < p.NumField(); i++ {
 | 
			
		||||
			if !match(m, p.Field(i), v.Field(i)) {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
 | 
			
		||||
	case reflect.Interface:
 | 
			
		||||
		return match(m, p.Elem(), v.Elem())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Handle token integers, etc.
 | 
			
		||||
	return p.Interface() == v.Interface()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// subst returns a copy of pattern with values from m substituted in place
 | 
			
		||||
// of wildcards and pos used as the position of tokens from the pattern.
 | 
			
		||||
// if m == nil, subst returns a copy of pattern and doesn't change the line
 | 
			
		||||
// number information.
 | 
			
		||||
func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) reflect.Value {
 | 
			
		||||
	if !pattern.IsValid() {
 | 
			
		||||
		return reflect.Value{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Wildcard gets replaced with map value.
 | 
			
		||||
	if m != nil && pattern.Type() == identType {
 | 
			
		||||
		name := pattern.Interface().(*ast.Ident).Name
 | 
			
		||||
		if isWildcard(name) {
 | 
			
		||||
			if old, ok := m[name]; ok {
 | 
			
		||||
				return subst(nil, old, reflect.Value{})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if pos.IsValid() && pattern.Type() == positionType {
 | 
			
		||||
		// use new position only if old position was valid in the first place
 | 
			
		||||
		if old := pattern.Interface().(token.Pos); !old.IsValid() {
 | 
			
		||||
			return pattern
 | 
			
		||||
		}
 | 
			
		||||
		return pos
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Otherwise copy.
 | 
			
		||||
	switch p := pattern; p.Kind() {
 | 
			
		||||
	case reflect.Slice:
 | 
			
		||||
		v := reflect.MakeSlice(p.Type(), p.Len(), p.Len())
 | 
			
		||||
		for i := 0; i < p.Len(); i++ {
 | 
			
		||||
			v.Index(i).Set(subst(m, p.Index(i), pos))
 | 
			
		||||
		}
 | 
			
		||||
		return v
 | 
			
		||||
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		v := reflect.New(p.Type()).Elem()
 | 
			
		||||
		for i := 0; i < p.NumField(); i++ {
 | 
			
		||||
			v.Field(i).Set(subst(m, p.Field(i), pos))
 | 
			
		||||
		}
 | 
			
		||||
		return v
 | 
			
		||||
 | 
			
		||||
	case reflect.Ptr:
 | 
			
		||||
		v := reflect.New(p.Type()).Elem()
 | 
			
		||||
		if elem := p.Elem(); elem.IsValid() {
 | 
			
		||||
			v.Set(subst(m, elem, pos).Addr())
 | 
			
		||||
		}
 | 
			
		||||
		return v
 | 
			
		||||
 | 
			
		||||
	case reflect.Interface:
 | 
			
		||||
		v := reflect.New(p.Type()).Elem()
 | 
			
		||||
		if elem := p.Elem(); elem.IsValid() {
 | 
			
		||||
			v.Set(subst(m, elem, pos))
 | 
			
		||||
		}
 | 
			
		||||
		return v
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pattern
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1024
									
								
								vendor/github.com/klauspost/cpuid/private/cpuid.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1024
									
								
								vendor/github.com/klauspost/cpuid/private/cpuid.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										42
									
								
								vendor/github.com/klauspost/cpuid/private/cpuid_386.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/klauspost/cpuid/private/cpuid_386.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
			
		||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build 386,!gccgo
 | 
			
		||||
 | 
			
		||||
// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
 | 
			
		||||
TEXT ·asmCpuid(SB), 7, $0
 | 
			
		||||
	XORL CX, CX
 | 
			
		||||
	MOVL op+0(FP), AX
 | 
			
		||||
	CPUID
 | 
			
		||||
	MOVL AX, eax+4(FP)
 | 
			
		||||
	MOVL BX, ebx+8(FP)
 | 
			
		||||
	MOVL CX, ecx+12(FP)
 | 
			
		||||
	MOVL DX, edx+16(FP)
 | 
			
		||||
	RET
 | 
			
		||||
 | 
			
		||||
// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
 | 
			
		||||
TEXT ·asmCpuidex(SB), 7, $0
 | 
			
		||||
	MOVL op+0(FP), AX
 | 
			
		||||
	MOVL op2+4(FP), CX
 | 
			
		||||
	CPUID
 | 
			
		||||
	MOVL AX, eax+8(FP)
 | 
			
		||||
	MOVL BX, ebx+12(FP)
 | 
			
		||||
	MOVL CX, ecx+16(FP)
 | 
			
		||||
	MOVL DX, edx+20(FP)
 | 
			
		||||
	RET
 | 
			
		||||
 | 
			
		||||
// func xgetbv(index uint32) (eax, edx uint32)
 | 
			
		||||
TEXT ·asmXgetbv(SB), 7, $0
 | 
			
		||||
	MOVL index+0(FP), CX
 | 
			
		||||
	BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV
 | 
			
		||||
	MOVL AX, eax+4(FP)
 | 
			
		||||
	MOVL DX, edx+8(FP)
 | 
			
		||||
	RET
 | 
			
		||||
 | 
			
		||||
// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
 | 
			
		||||
TEXT ·asmRdtscpAsm(SB), 7, $0
 | 
			
		||||
	BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP
 | 
			
		||||
	MOVL AX, eax+0(FP)
 | 
			
		||||
	MOVL BX, ebx+4(FP)
 | 
			
		||||
	MOVL CX, ecx+8(FP)
 | 
			
		||||
	MOVL DX, edx+12(FP)
 | 
			
		||||
	RET
 | 
			
		||||
							
								
								
									
										42
									
								
								vendor/github.com/klauspost/cpuid/private/cpuid_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/klauspost/cpuid/private/cpuid_amd64.s
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
			
		||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
 | 
			
		||||
 | 
			
		||||
//+build amd64,!gccgo
 | 
			
		||||
 | 
			
		||||
// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
 | 
			
		||||
TEXT ·asmCpuid(SB), 7, $0
 | 
			
		||||
	XORQ CX, CX
 | 
			
		||||
	MOVL op+0(FP), AX
 | 
			
		||||
	CPUID
 | 
			
		||||
	MOVL AX, eax+8(FP)
 | 
			
		||||
	MOVL BX, ebx+12(FP)
 | 
			
		||||
	MOVL CX, ecx+16(FP)
 | 
			
		||||
	MOVL DX, edx+20(FP)
 | 
			
		||||
	RET
 | 
			
		||||
 | 
			
		||||
// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
 | 
			
		||||
TEXT ·asmCpuidex(SB), 7, $0
 | 
			
		||||
	MOVL op+0(FP), AX
 | 
			
		||||
	MOVL op2+4(FP), CX
 | 
			
		||||
	CPUID
 | 
			
		||||
	MOVL AX, eax+8(FP)
 | 
			
		||||
	MOVL BX, ebx+12(FP)
 | 
			
		||||
	MOVL CX, ecx+16(FP)
 | 
			
		||||
	MOVL DX, edx+20(FP)
 | 
			
		||||
	RET
 | 
			
		||||
 | 
			
		||||
// func asmXgetbv(index uint32) (eax, edx uint32)
 | 
			
		||||
TEXT ·asmXgetbv(SB), 7, $0
 | 
			
		||||
	MOVL index+0(FP), CX
 | 
			
		||||
	BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV
 | 
			
		||||
	MOVL AX, eax+8(FP)
 | 
			
		||||
	MOVL DX, edx+12(FP)
 | 
			
		||||
	RET
 | 
			
		||||
 | 
			
		||||
// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
 | 
			
		||||
TEXT ·asmRdtscpAsm(SB), 7, $0
 | 
			
		||||
	BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP
 | 
			
		||||
	MOVL AX, eax+0(FP)
 | 
			
		||||
	MOVL BX, ebx+4(FP)
 | 
			
		||||
	MOVL CX, ecx+8(FP)
 | 
			
		||||
	MOVL DX, edx+12(FP)
 | 
			
		||||
	RET
 | 
			
		||||
							
								
								
									
										17
									
								
								vendor/github.com/klauspost/cpuid/private/cpuid_detect_intel.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/klauspost/cpuid/private/cpuid_detect_intel.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build 386,!gccgo amd64,!gccgo
 | 
			
		||||
 | 
			
		||||
package cpuid
 | 
			
		||||
 | 
			
		||||
func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
 | 
			
		||||
func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
 | 
			
		||||
func asmXgetbv(index uint32) (eax, edx uint32)
 | 
			
		||||
func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
 | 
			
		||||
 | 
			
		||||
func initCPU() {
 | 
			
		||||
	cpuid = asmCpuid
 | 
			
		||||
	cpuidex = asmCpuidex
 | 
			
		||||
	xgetbv = asmXgetbv
 | 
			
		||||
	rdtscpAsm = asmRdtscpAsm
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								vendor/github.com/klauspost/cpuid/private/cpuid_detect_ref.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/klauspost/cpuid/private/cpuid_detect_ref.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
 | 
			
		||||
 | 
			
		||||
// +build !amd64,!386 gccgo
 | 
			
		||||
 | 
			
		||||
package cpuid
 | 
			
		||||
 | 
			
		||||
func initCPU() {
 | 
			
		||||
	cpuid = func(op uint32) (eax, ebx, ecx, edx uint32) {
 | 
			
		||||
		return 0, 0, 0, 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cpuidex = func(op, op2 uint32) (eax, ebx, ecx, edx uint32) {
 | 
			
		||||
		return 0, 0, 0, 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	xgetbv = func(index uint32) (eax, edx uint32) {
 | 
			
		||||
		return 0, 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rdtscpAsm = func() (eax, ebx, ecx, edx uint32) {
 | 
			
		||||
		return 0, 0, 0, 0
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								vendor/manifest
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								vendor/manifest
									
									
									
									
										vendored
									
									
								
							@ -117,6 +117,14 @@
 | 
			
		||||
			"path": "/basic",
 | 
			
		||||
			"notests": true
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"importpath": "github.com/klauspost/cpuid",
 | 
			
		||||
			"repository": "https://github.com/klauspost/cpuid",
 | 
			
		||||
			"vcs": "git",
 | 
			
		||||
			"revision": "ae832f27941af41db13bd6d8efd2493e3b22415a",
 | 
			
		||||
			"branch": "master",
 | 
			
		||||
			"notests": true
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"importpath": "github.com/lucas-clemente/aes12",
 | 
			
		||||
			"repository": "https://github.com/lucas-clemente/aes12",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user