Skip to content
This repository was archived by the owner on Aug 17, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion tracer/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var tags []string
func init() {
tags = make([]string, 1000)
for j := 0; j < len(tags); j++ {
tags[j] = fmt.Sprintf("%d", randomID())
tags[j] = fmt.Sprintf("%d", getRandomId())
}
}

Expand Down
5 changes: 3 additions & 2 deletions tracer/tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ ReferencesLoop:

refCtx := ref.ReferencedContext.(SpanContext)
sp.raw.Context.TraceID = refCtx.TraceID
sp.raw.Context.SpanID = randomID()
sp.raw.Context.SpanID = getRandomId()
sp.raw.Context.Sampled = refCtx.Sampled
sp.raw.ParentSpanID = refCtx.SpanID

Expand All @@ -198,7 +198,8 @@ ReferencesLoop:
if sp.raw.Context.TraceID == 0 {
// No parent Span found; allocate new trace and span ids and determine
// the Sampled status.
sp.raw.Context.TraceID, sp.raw.Context.SpanID = randomID2()
sp.raw.Context.TraceID = getRandomId()
sp.raw.Context.SpanID = getRandomId()
sp.raw.Context.Sampled = t.options.ShouldSample(sp.raw.Context.TraceID)
}

Expand Down
77 changes: 56 additions & 21 deletions tracer/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package tracer

import (
cryptorand "crypto/rand"
"encoding/binary"
"math"
"math/big"
"math/rand"
"sync"
"time"
Expand All @@ -11,32 +12,66 @@ import (
)

var (
seededIDGen = rand.New(rand.NewSource(generateSeed()))
// The golang rand generators are *not* intrinsically thread-safe.
seededIDLock sync.Mutex
random *rand.Rand
mu sync.Mutex
)

func generateSeed() int64 {
var b [8]byte
_, err := cryptorand.Read(b[:])
if err != nil {
instrumentation.Logger().Printf("cryptorand error: %v. \n falling back to time.Now()", err)
// Cannot seed math/rand package with cryptographically secure random number generator
// Fallback to time.Now()
return time.Now().UnixNano()
func getRandomId() uint64 {
ensureRandom()
return random.Uint64()
}

func ensureRandom() {
mu.Lock()
defer mu.Unlock()
if random == nil {
random = rand.New(&safeSource{
source: rand.NewSource(getSeed()),
})
}
}

//go:noinline
func getSeed() int64 {
var seed int64
n, err := cryptorand.Int(cryptorand.Reader, big.NewInt(math.MaxInt64))
if err == nil {
seed = n.Int64()
} else {
instrumentation.Logger().Printf("cryptorand error generating seed: %v. \n falling back to time.Now()", err)

// Adding some jitter to the clock seed using golang channels and goroutines
jitterStart := time.Now()
cb := make(chan time.Time, 0)
go func() { cb <- <-time.After(time.Nanosecond) }()
now := <-cb
jitter := time.Since(jitterStart)

// Seed based on the clock + some jitter
seed = now.Add(jitter).UnixNano()
}
instrumentation.Logger().Printf("seed: %d", seed)
return seed
}

return int64(binary.LittleEndian.Uint64(b[:]))
// safeSource holds a thread-safe implementation of rand.Source64.
type safeSource struct {
source rand.Source
sync.Mutex
}

func randomID() uint64 {
seededIDLock.Lock()
defer seededIDLock.Unlock()
return uint64(seededIDGen.Int63())
func (rs *safeSource) Int63() int64 {
rs.Lock()
n := rs.source.Int63()
rs.Unlock()

return n
}

func randomID2() (uint64, uint64) {
seededIDLock.Lock()
defer seededIDLock.Unlock()
return uint64(seededIDGen.Int63()), uint64(seededIDGen.Int63())
func (rs *safeSource) Uint64() uint64 { return uint64(rs.Int63()) }

func (rs *safeSource) Seed(seed int64) {
rs.Lock()
rs.source.Seed(seed)
rs.Unlock()
}