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 + ")"
+}

Reply via email to