This is an automated email from the ASF dual-hosted git repository.
wusheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-go.git
The following commit(s) were added to refs/heads/main by this push:
new adc5da2 Refactor the global ID generator (#45)
adc5da2 is described below
commit adc5da2cecd5c213c400a88b37d9cf7363c1711b
Author: mrproliu <[email protected]>
AuthorDate: Wed May 24 14:55:22 2023 +0000
Refactor the global ID generator (#45)
---
plugins/core/context.go | 3 +
plugins/core/id.go | 98 +++++++++++++++++++++++
plugins/core/id_test.go | 75 +++++++++++++++++
plugins/core/span_tracing.go | 14 ++--
plugins/core/tool.go | 5 --
plugins/core/tracing.go | 16 ++--
tools/go-agent/instrument/agentcore/instrument.go | 8 ++
tools/go-agent/instrument/consts/operator.go | 2 +
tools/go-agent/instrument/runtime/instrument.go | 34 ++++++++
9 files changed, 234 insertions(+), 21 deletions(-)
diff --git a/plugins/core/context.go b/plugins/core/context.go
index cc8ddf4..383afa0 100644
--- a/plugins/core/context.go
+++ b/plugins/core/context.go
@@ -33,6 +33,7 @@ type ContextSnapshoter interface {
type TracingContext struct {
activeSpan TracingSpan
Runtime *RuntimeContext
+ ID *IDContext
}
func (t *TracingContext) TakeSnapShot(val interface{}) interface{} {
@@ -40,6 +41,7 @@ func (t *TracingContext) TakeSnapShot(val interface{})
interface{} {
return &TracingContext{
activeSpan: snapshot,
Runtime: t.Runtime.clone(),
+ ID: NewIDContext(false),
}
}
@@ -67,6 +69,7 @@ func NewTracingContext() *TracingContext {
Runtime: &RuntimeContext{
data: make(map[string]interface{}),
},
+ ID: NewIDContext(true),
}
}
diff --git a/plugins/core/id.go b/plugins/core/id.go
new file mode 100644
index 0000000..5f65367
--- /dev/null
+++ b/plugins/core/id.go
@@ -0,0 +1,98 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 core
+
+import (
+ "fmt"
+ "strings"
+ "time"
+)
+
+var GetGoID = func() int64 {
+ return 0
+}
+
+var (
+ idEpoch time.Time
+ globalInstanceID string
+)
+
+func init() {
+ idEpoch = time.Date(1900, 0, 0, 0, 0, 0, 0, time.UTC)
+ uuid, err := UUID()
+ if err != nil {
+ panic(err)
+ }
+ globalInstanceID = strings.ReplaceAll(uuid, "-", "")
+}
+
+type IDContext struct {
+ goid int64
+ lastTime int64
+ seq int16
+
+ shift int64
+ shitTime int64
+}
+
+func NewIDContext(getGoIDNow bool) *IDContext {
+ var goid int64
+ if getGoIDNow {
+ goid = GetGoID()
+ }
+ return &IDContext{
+ goid: goid,
+ lastTime: 0,
+ seq: 0,
+ }
+}
+
+// GenerateGlobalID generates global unique id
+func GenerateGlobalID(ctx *TracingContext) (globalID string, err error) {
+ idContext := ctx.ID
+ if idContext.goid == 0 {
+ idContext.goid = GetGoID()
+ }
+
+ return fmt.Sprintf("%s.%d.%d", globalInstanceID, idContext.goid,
idContext.nextID()), nil
+}
+
+func (c *IDContext) nextID() int64 {
+ return c.timestamp()*10000 + int64(c.nextSeq())
+}
+
+func (c *IDContext) timestamp() int64 {
+ now := time.Since(idEpoch).Milliseconds()
+ if now < c.lastTime {
+ if c.shitTime != now {
+ c.shift++
+ c.shitTime = now
+ }
+ return c.shift
+ }
+ c.lastTime = now
+ return now
+}
+
+func (c *IDContext) nextSeq() int16 {
+ if c.seq == 10000 {
+ c.seq = 0
+ }
+ c.seq++
+ return c.seq
+}
diff --git a/plugins/core/id_test.go b/plugins/core/id_test.go
new file mode 100644
index 0000000..48083c8
--- /dev/null
+++ b/plugins/core/id_test.go
@@ -0,0 +1,75 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 core
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGenerateID(t *testing.T) {
+ ctx := NewTracingContext()
+ id, err := GenerateGlobalID(ctx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.NotEqual(t, "", id, "id should not be empty")
+}
+
+func BenchmarkGenerateID(b *testing.B) {
+ context := NewTracingContext()
+ for i := 0; i < b.N; i++ {
+ _, err := GenerateGlobalID(context)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func BenchmarkGenerateIDParallels(b *testing.B) {
+ b.RunParallel(func(pb *testing.PB) {
+ context := NewTracingContext()
+ for pb.Next() {
+ _, err := GenerateGlobalID(context)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+ })
+}
+
+func BenchmarkUUID(t *testing.B) {
+ for i := 0; i < t.N; i++ {
+ _, err := UUID()
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+func BenchmarkUUIDParallels(b *testing.B) {
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ _, err := UUID()
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+ })
+}
diff --git a/plugins/core/span_tracing.go b/plugins/core/span_tracing.go
index 8fdac63..87e10d1 100644
--- a/plugins/core/span_tracing.go
+++ b/plugins/core/span_tracing.go
@@ -27,17 +27,17 @@ import (
agentv3 "skywalking.apache.org/repo/goapi/collect/language/agent/v3"
)
-func NewSegmentSpan(defaultSpan *DefaultSpan, parentSpan SegmentSpan) (s
SegmentSpan, err error) {
+func NewSegmentSpan(ctx *TracingContext, defaultSpan *DefaultSpan, parentSpan
SegmentSpan) (s SegmentSpan, err error) {
ssi := &SegmentSpanImpl{
DefaultSpan: *defaultSpan,
}
- err = ssi.createSegmentContext(parentSpan)
+ err = ssi.createSegmentContext(ctx, parentSpan)
if err != nil {
return nil, err
}
if parentSpan == nil || !parentSpan.segmentRegister() {
rs := newSegmentRoot(ssi)
- err = rs.createRootSegmentContext(parentSpan)
+ err = rs.createRootSegmentContext(ctx, parentSpan)
if err != nil {
return nil, err
}
@@ -180,14 +180,14 @@ func (s *SegmentSpanImpl) segmentRegister() bool {
}
}
-func (s *SegmentSpanImpl) createSegmentContext(parent SegmentSpan) (err error)
{
+func (s *SegmentSpanImpl) createSegmentContext(ctx *TracingContext, parent
SegmentSpan) (err error) {
if parent == nil {
s.SegmentContext = SegmentContext{}
if len(s.DefaultSpan.Refs) > 0 {
s.TraceID = s.DefaultSpan.Refs[0].GetTraceID()
s.CorrelationContext =
s.DefaultSpan.Refs[0].(*SpanContext).CorrelationContext
} else {
- s.TraceID, err = GenerateGlobalID()
+ s.TraceID, err = GenerateGlobalID(ctx)
if err != nil {
return err
}
@@ -226,8 +226,8 @@ func (rs *RootSegmentSpan) End() {
}()
}
-func (rs *RootSegmentSpan) createRootSegmentContext(_ SegmentSpan) (err error)
{
- rs.SegmentID, err = GenerateGlobalID()
+func (rs *RootSegmentSpan) createRootSegmentContext(ctx *TracingContext, _
SegmentSpan) (err error) {
+ rs.SegmentID, err = GenerateGlobalID(ctx)
if err != nil {
return err
}
diff --git a/plugins/core/tool.go b/plugins/core/tool.go
index a3b5bc3..29ef72e 100644
--- a/plugins/core/tool.go
+++ b/plugins/core/tool.go
@@ -44,11 +44,6 @@ func UUID() (string, error) {
return strings.ReplaceAll(id.String(), "-", ""), nil
}
-// GenerateGlobalID generates global unique id
-func GenerateGlobalID() (globalID string, err error) {
- return UUID()
-}
-
func ProcessNo() string {
if os.Getpid() > 0 {
return strconv.Itoa(os.Getpid())
diff --git a/plugins/core/tracing.go b/plugins/core/tracing.go
index f3fe871..a09df1d 100644
--- a/plugins/core/tracing.go
+++ b/plugins/core/tracing.go
@@ -60,7 +60,7 @@ func (t *Tracer) CreateEntrySpan(operationName string,
extractor interface{}, op
ref = nil
}
- return t.createSpan0(tracingSpan, opts, withRef(ref),
withSpanType(SpanTypeEntry), withOperationName(operationName))
+ return t.createSpan0(ctx, tracingSpan, opts, withRef(ref),
withSpanType(SpanTypeEntry), withOperationName(operationName))
}
func (t *Tracer) CreateLocalSpan(operationName string, opts ...interface{}) (s
interface{}, err error) {
@@ -72,7 +72,7 @@ func (t *Tracer) CreateLocalSpan(operationName string, opts
...interface{}) (s i
saveSpanToActiveIfNotError(ctx, s, err)
}()
- return t.createSpan0(tracingSpan, opts, withSpanType(SpanTypeLocal),
withOperationName(operationName))
+ return t.createSpan0(ctx, tracingSpan, opts,
withSpanType(SpanTypeLocal), withOperationName(operationName))
}
func (t *Tracer) CreateExitSpan(operationName, peer string, injector
interface{}, opts ...interface{}) (s interface{}, err error) {
@@ -88,7 +88,7 @@ func (t *Tracer) CreateExitSpan(operationName, peer string,
injector interface{}
if tracingSpan != nil && tracingSpan.IsExit() {
return tracingSpan, nil
}
- span, err := t.createSpan0(tracingSpan, opts,
withSpanType(SpanTypeExit), withOperationName(operationName), withPeer(peer))
+ span, err := t.createSpan0(ctx, tracingSpan, opts,
withSpanType(SpanTypeExit), withOperationName(operationName), withPeer(peer))
if err != nil {
return nil, err
}
@@ -152,10 +152,11 @@ func (t *Tracer) createNoop() (*TracingContext,
TracingSpan, bool) {
_, ok := span.(*NoopSpan)
return ctx, span, ok
}
- return nil, nil, false
+ ctx = NewTracingContext()
+ return ctx, nil, false
}
-func (t *Tracer) createSpan0(parent TracingSpan, pluginOpts []interface{},
coreOpts ...interface{}) (s TracingSpan, err error) {
+func (t *Tracer) createSpan0(ctx *TracingContext, parent TracingSpan,
pluginOpts []interface{}, coreOpts ...interface{}) (s TracingSpan, err error) {
ds := NewDefaultSpan(t, parent)
var parentSpan SegmentSpan
if parent != nil {
@@ -179,7 +180,7 @@ func (t *Tracer) createSpan0(parent TracingSpan, pluginOpts
[]interface{}, coreO
for _, opt := range coreOpts {
opt.(tracing.SpanOption).Apply(ds)
}
- s, err = NewSegmentSpan(ds, parentSpan)
+ s, err = NewSegmentSpan(ctx, ds, parentSpan)
if err != nil {
return nil, err
}
@@ -247,9 +248,6 @@ func saveSpanToActiveIfNotError(ctx *TracingContext, span
interface{}, err error
if err != nil || span == nil {
return
}
- if ctx == nil {
- ctx = NewTracingContext()
- }
ctx.SaveActiveSpan(span.(TracingSpan))
SetGLS(ctx)
}
diff --git a/tools/go-agent/instrument/agentcore/instrument.go
b/tools/go-agent/instrument/agentcore/instrument.go
index 0ae0c74..615d1bd 100644
--- a/tools/go-agent/instrument/agentcore/instrument.go
+++ b/tools/go-agent/instrument/agentcore/instrument.go
@@ -177,11 +177,17 @@ var {{.SetGlobalOperatorLinkMethod}} func(interface{})
//go:linkname {{.GetGlobalOperatorLinkMethod}} {{.GetGlobalOperatorLinkMethod}}
var {{.GetGlobalOperatorLinkMethod}} func() interface{}
+//go:linkname {{.GetGoroutineIDLinkMethod}} {{.GetGoroutineIDLinkMethod}}
+var {{.GetGoroutineIDLinkMethod}} func() int64
+
func init() {
if {{.TLSGetLinkMethod}} != nil && {{.TLSSetLinkMethod}} != nil {
GetGLS = {{.TLSGetLinkMethod}}
SetGLS = {{.TLSSetLinkMethod}}
}
+ if {{.GetGoroutineIDLinkMethod}} != nil {
+ GetGoID = {{.GetGoroutineIDLinkMethod}}
+ }
if {{.SetGlobalOperatorLinkMethod}} != nil &&
{{.GetGlobalOperatorLinkMethod}} != nil {
SetGlobalOperator = {{.SetGlobalOperatorLinkMethod}}
GetGlobalOperator = {{.GetGlobalOperatorLinkMethod}}
@@ -193,10 +199,12 @@ func init() {
TLSSetLinkMethod string
SetGlobalOperatorLinkMethod string
GetGlobalOperatorLinkMethod string
+ GetGoroutineIDLinkMethod string
}{
TLSGetLinkMethod: consts.TLSGetMethodName,
TLSSetLinkMethod: consts.TLSSetMethodName,
SetGlobalOperatorLinkMethod: consts.GlobalTracerSetMethodName,
GetGlobalOperatorLinkMethod: consts.GlobalTracerGetMethodName,
+ GetGoroutineIDLinkMethod:
consts.CurrentGoroutineIDGetMethodName,
}))
}
diff --git a/tools/go-agent/instrument/consts/operator.go
b/tools/go-agent/instrument/consts/operator.go
index 01da2ad..2b4ffc6 100644
--- a/tools/go-agent/instrument/consts/operator.go
+++ b/tools/go-agent/instrument/consts/operator.go
@@ -27,4 +27,6 @@ const (
GlobalTracerGetMethodName = "_skywalking_get_global_operator"
GlobalLoggerSetMethodName = "_skywalking_set_global_logger"
GlobalLoggerGetMethodName = "_skywalking_get_global_logger"
+
+ CurrentGoroutineIDGetMethodName = "_skywalking_get_goid"
)
diff --git a/tools/go-agent/instrument/runtime/instrument.go
b/tools/go-agent/instrument/runtime/instrument.go
index 80a290e..dcdbde2 100644
--- a/tools/go-agent/instrument/runtime/instrument.go
+++ b/tools/go-agent/instrument/runtime/instrument.go
@@ -27,6 +27,7 @@ import (
)
type Instrument struct {
+ goIDType string
}
func NewInstrument() *Instrument {
@@ -47,6 +48,11 @@ func (r *Instrument) FilterAndEdit(path string, curFile
*dst.File, cursor *dstut
if !ok {
return false
}
+ for _, f := range st.Fields.List {
+ if len(f.Names) > 0 && f.Names[0].Name == "goid" {
+ r.goIDType = f.Type.(*dst.Ident).Name
+ }
+ }
// append the tls field
st.Fields.List = append(st.Fields.List, &dst.Field{
Names: []*dst.Ident{dst.NewIdent(consts.TLSFieldName)},
@@ -92,6 +98,7 @@ func (r *Instrument) AfterEnhanceFile(fromPath, newPath
string) error {
return nil
}
+// nolint
func (r *Instrument) WriteExtraFiles(dir string) ([]string, error) {
return tools.WriteMultipleFile(dir, map[string]string{
"skywalking_tls_operator.go": tools.ExecuteTemplate(`package
runtime
@@ -122,6 +129,14 @@ var {{.GlobalLoggerSetMethodName}} =
_skywalking_global_logger_set_impl
//go:linkname {{.GlobalLoggerGetMethodName}} {{.GlobalLoggerGetMethodName}}
var {{.GlobalLoggerGetMethodName}} = _skywalking_global_logger_get_impl
+//go:linkname {{.GoroutineIDGetterMethodName}} {{.GoroutineIDGetterMethodName}}
+var {{.GoroutineIDGetterMethodName}} = _skywalking_get_goid_impl
+
+//go:nosplit
+func _skywalking_get_goid_impl() int64 {
+ return {{.GoroutineIDCaster}}
+}
+
//go:nosplit
func _skywalking_tls_get_impl() interface{} {
return getg().m.curg.{{.TLSFiledName}}
@@ -176,6 +191,8 @@ func goroutineChange(tls interface{}) interface{} {
GlobalLoggerFieldName string
GlobalLoggerSetMethodName string
GlobalLoggerGetMethodName string
+ GoroutineIDGetterMethodName string
+ GoroutineIDCaster string
}{
TLSFiledName: consts.TLSFieldName,
TLSGetMethod: consts.TLSGetMethodName,
@@ -187,6 +204,23 @@ func goroutineChange(tls interface{}) interface{} {
GlobalLoggerFieldName:
consts.GlobalLoggerFieldName,
GlobalLoggerSetMethodName:
consts.GlobalLoggerSetMethodName,
GlobalLoggerGetMethodName:
consts.GlobalLoggerGetMethodName,
+ GoroutineIDGetterMethodName:
consts.CurrentGoroutineIDGetMethodName,
+ GoroutineIDCaster:
r.generateCastGoID("getg().m.curg.goid"),
}),
})
}
+
+func (r *Instrument) generateCastGoID(val string) string {
+ switch r.goIDType {
+ case "int64":
+ return val
+ case "uint64":
+ case "int32":
+ case "uint32":
+ case "int":
+ case "uint":
+ default:
+ panic("cannot find goid type in the g struct or the type is not
supported: " + r.goIDType)
+ }
+ return "int64(" + val + ")"
+}