| // 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 agentcore |
| |
| import ( |
| "fmt" |
| "html" |
| "io/fs" |
| "path/filepath" |
| "regexp" |
| "runtime" |
| "strings" |
| |
| "github.com/apache/skywalking-go/plugins/core" |
| "github.com/apache/skywalking-go/tools/go-agent/config" |
| "github.com/apache/skywalking-go/tools/go-agent/instrument/api" |
| "github.com/apache/skywalking-go/tools/go-agent/instrument/consts" |
| "github.com/apache/skywalking-go/tools/go-agent/tools" |
| |
| "github.com/dave/dst" |
| "github.com/dave/dst/dstutil" |
| ) |
| |
| var ( |
| ProjectBasePackage = "github.com/apache/skywalking-go/" |
| EnhanceBasePackage = ProjectBasePackage + "agent/core" |
| EnhanceFromBasePackage = ProjectBasePackage + "plugins/core" |
| ReporterFromBasePackage = "reporter" |
| ReporterBasePackage = "agent/reporter" |
| |
| CopiedBasePackage = `skywalking-go(@[\d\w\.\-]+)?\/agent\/core` |
| CopiedSubPackages = []string{"", "tracing", "operator"} |
| ) |
| |
| type Instrument struct { |
| hasCopyPath bool |
| needsCopyDir string |
| compileOpts *api.CompileOptions |
| } |
| |
| func NewInstrument() *Instrument { |
| return &Instrument{} |
| } |
| |
| func (i *Instrument) CouldHandle(opts *api.CompileOptions) bool { |
| i.compileOpts = opts |
| return strings.HasPrefix(opts.Package, EnhanceBasePackage) |
| } |
| |
| func (i *Instrument) FilterAndEdit(path string, curFile *dst.File, cursor *dstutil.Cursor, allFiles []*dst.File) bool { |
| if i.hasCopyPath { |
| return false |
| } |
| targetDir := filepath.Dir(path) |
| for _, sub := range CopiedSubPackages { |
| // On os=windows, we need to escape the characters, |
| // and we can't just use "filepath.Join", |
| // which would cause the regex to throw an error |
| var prefix string |
| if runtime.GOOS == "windows" { |
| prefix = strings.ReplaceAll(CopiedBasePackage, `\/`, `\\`) |
| if sub != "" { |
| prefix = fmt.Sprintf(`%s\\%s`, prefix, sub) |
| } |
| } else { |
| prefix = filepath.Join(CopiedBasePackage, sub) |
| } |
| |
| res := prefix + "$" |
| if regexp.MustCompile(res).MatchString(targetDir) { |
| i.needsCopyDir = sub |
| i.hasCopyPath = true |
| return true |
| } |
| } |
| return false |
| } |
| |
| func (i *Instrument) AfterEnhanceFile(fromPath, newPath string) error { |
| return nil |
| } |
| |
| func (i *Instrument) WriteExtraFiles(dir string) ([]string, error) { |
| if !i.hasCopyPath { |
| return nil, nil |
| } |
| sub := i.needsCopyDir |
| results := make([]string, 0) |
| if sub == "" { |
| sub = "." |
| } |
| |
| pkgUpdates := make(map[string]string) |
| for _, p := range CopiedSubPackages { |
| pkgUpdates[filepath.Join(EnhanceFromBasePackage, p)] = filepath.Join(EnhanceBasePackage, p) |
| } |
| pkgUpdates[filepath.Join(EnhanceFromBasePackage, ReporterFromBasePackage)] = filepath.Join(ProjectBasePackage, ReporterBasePackage) |
| |
| for k, v := range pkgUpdates { |
| newKey := strings.ReplaceAll(k, `\`, `/`) |
| newVal := strings.ReplaceAll(v, `\`, `/`) |
| |
| pkgUpdates[newKey] = newVal |
| } |
| |
| copiedFiles, err := tools.CopyGoFiles(core.FS, sub, dir, i.buildDSTDebugInfo, func(file *dst.File) { |
| tools.ChangePackageImportPath(file, pkgUpdates) |
| }) |
| if err != nil { |
| return nil, err |
| } |
| results = append(results, copiedFiles...) |
| |
| // write extra file to link the operator and TLS methods |
| if sub == "." { |
| file, err := i.writeLinkerFile(dir) |
| if err != nil { |
| return nil, err |
| } |
| results = append(results, file) |
| |
| file1, err := i.writeTracerInitLink(dir) |
| if err != nil { |
| return nil, err |
| } |
| results = append(results, file1) |
| } |
| |
| return results, nil |
| } |
| |
| func (i *Instrument) buildDSTDebugInfo(entry fs.DirEntry, _ *dst.File) (*tools.DebugInfo, error) { |
| if i.compileOpts.DebugDir == "" { |
| return nil, nil |
| } |
| debugPath := filepath.Join(i.compileOpts.DebugDir, "plugins", "core", entry.Name()) |
| return tools.BuildDSTDebugInfo(debugPath, nil) |
| } |
| |
| func (i *Instrument) writeTracerInitLink(dir string) (string, error) { |
| return tools.WriteFile(dir, "tracer_init.go", html.UnescapeString(tools.ExecuteTemplate(`package core |
| |
| import ( |
| "github.com/apache/skywalking-go/agent/reporter" |
| "github.com/apache/skywalking-go/agent/core/operator" |
| "fmt" |
| "os" |
| "strconv" |
| _ "unsafe" |
| ) |
| |
| //go:linkname {{.GetGlobalLoggerLinkMethod}} {{.GetGlobalLoggerLinkMethod}} |
| var {{.GetGlobalLoggerLinkMethod}} func() interface{} |
| |
| func (t *Tracer) InitTracer(extend map[string]interface{}) { |
| rep, err := reporter.{{.GRPCReporterFuncName}}(t.Log) |
| if err != nil { |
| t.Log.Errorf("cannot initialize the reporter: %v", err) |
| return |
| } |
| entity := NewEntity({{.Config.Agent.ServiceName.ToGoStringValue}}, {{.Config.Agent.InstanceEnvName.ToGoStringValue}}) |
| samp := NewDynamicSampler({{.Config.Agent.Sampler.ToGoFloatValue "loading the agent sampler error"}}, t) |
| meterCollectInterval := {{.Config.Agent.Meter.CollectInterval.ToGoIntValue "loading the agent meter interval error"}} |
| var logger operator.LogOperator |
| if {{.GetGlobalLoggerLinkMethod}} != nil { |
| if l, ok := {{.GetGlobalLoggerLinkMethod}}().(operator.LogOperator); ok && l != nil { |
| logger = l |
| } |
| } |
| correlation := &CorrelationConfig{ |
| MaxKeyCount : {{.Config.Agent.Correlation.MaxKeyCount.ToGoIntValue "loading the agent correlation maxKeyCount error"}}, |
| MaxValueSize : {{.Config.Agent.Correlation.MaxValueSize.ToGoIntValue "loading the agent correlation maxValueSize error"}}, |
| } |
| ignoreSuffixStr := {{.Config.Agent.IgnoreSuffix.ToGoStringValue}} |
| ignorePath := {{.Config.Agent.TraceIgnorePath.ToGoStringValue}} |
| if err := t.Init(entity, rep, samp, logger, meterCollectInterval, correlation, ignoreSuffixStr, ignorePath); err != nil { |
| t.Log.Errorf("cannot initialize the SkyWalking Tracer: %v", err) |
| } |
| }`, struct { |
| GRPCReporterFuncName string |
| GetGlobalLoggerLinkMethod string |
| Config *config.Config |
| }{ |
| GRPCReporterFuncName: consts.GRPCInitFuncName, |
| GetGlobalLoggerLinkMethod: consts.GlobalLoggerGetMethodName, |
| Config: config.GetConfig(), |
| }))) |
| } |
| |
| func (i *Instrument) writeLinkerFile(dir string) (string, error) { |
| return tools.WriteFile(dir, "runtime_linker.go", tools.ExecuteTemplate(`package core |
| |
| import ( |
| _ "unsafe" |
| ) |
| |
| //go:linkname {{.TLSGetLinkMethod}} {{.TLSGetLinkMethod}} |
| var {{.TLSGetLinkMethod}} func() interface{} |
| |
| //go:linkname {{.TLSSetLinkMethod}} {{.TLSSetLinkMethod}} |
| var {{.TLSSetLinkMethod}} func(interface{}) |
| |
| //go:linkname {{.SetGlobalOperatorLinkMethod}} {{.SetGlobalOperatorLinkMethod}} |
| var {{.SetGlobalOperatorLinkMethod}} func(interface{}) |
| |
| //go:linkname {{.GetGlobalOperatorLinkMethod}} {{.GetGlobalOperatorLinkMethod}} |
| var {{.GetGlobalOperatorLinkMethod}} func() interface{} |
| |
| //go:linkname {{.GetGoroutineIDLinkMethod}} {{.GetGoroutineIDLinkMethod}} |
| var {{.GetGoroutineIDLinkMethod}} func() int64 |
| |
| //go:linkname {{.GetInitNotifyLinkMethod}} {{.GetInitNotifyLinkMethod}} |
| var {{.GetInitNotifyLinkMethod}} func() []func() |
| |
| //go:linkname {{.MetricsObtainMethodName}} {{.MetricsObtainMethodName}} |
| var {{.MetricsObtainMethodName}} func() ([]interface{}, []func()) |
| |
| 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}} |
| SetGlobalOperator(newTracer()) // setting the global tracer when init the agent core |
| } |
| if {{.GetInitNotifyLinkMethod}} != nil { |
| GetInitNotify = {{.GetInitNotifyLinkMethod}} |
| } |
| if {{.MetricsObtainMethodName}} != nil { |
| MetricsObtain = {{.MetricsObtainMethodName}} |
| } |
| } |
| `, struct { |
| TLSGetLinkMethod string |
| TLSSetLinkMethod string |
| SetGlobalOperatorLinkMethod string |
| GetGlobalOperatorLinkMethod string |
| GetGoroutineIDLinkMethod string |
| GetInitNotifyLinkMethod string |
| MetricsObtainMethodName string |
| }{ |
| TLSGetLinkMethod: consts.TLSGetMethodName, |
| TLSSetLinkMethod: consts.TLSSetMethodName, |
| SetGlobalOperatorLinkMethod: consts.GlobalTracerSetMethodName, |
| GetGlobalOperatorLinkMethod: consts.GlobalTracerGetMethodName, |
| GetGoroutineIDLinkMethod: consts.CurrentGoroutineIDGetMethodName, |
| GetInitNotifyLinkMethod: consts.GlobalTracerInitGetNotifyMethodName, |
| MetricsObtainMethodName: consts.MetricsObtainMethodName, |
| })) |
| } |