diff --git a/ebpftracer/elf.go b/ebpftracer/elf.go new file mode 100644 index 0000000..00e3966 --- /dev/null +++ b/ebpftracer/elf.go @@ -0,0 +1,169 @@ +package ebpftracer + +import ( + "debug/elf" + "fmt" + "io" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/link" + "golang.org/x/arch/arm64/arm64asm" + "golang.org/x/arch/x86/x86asm" +) + +type Symbol struct { + s *elf.Symbol + f *ELFFile + address uint64 +} + +func (s *Symbol) Name() string { + return s.s.Name +} + +func (s *Symbol) Address() uint64 { + if s.address == 0 { + s.address = s.s.Value + for _, p := range s.f.elf.Progs { + if p.Type != elf.PT_LOAD || (p.Flags&elf.PF_X) == 0 { + continue + } + if p.Vaddr <= s.s.Value && s.s.Value < (p.Vaddr+p.Memsz) { + s.address = s.s.Value - p.Vaddr + p.Off + break + } + } + } + return s.address +} + +func (s *Symbol) ReturnOffsets() ([]int, error) { + text, reader, err := s.f.getTextSectionAndReader() + if err != nil { + return nil, err + } + + sStart := s.s.Value - text.Addr + _, err = reader.Seek(int64(sStart), io.SeekStart) + if err != nil { + return nil, err + } + sBytes := make([]byte, s.s.Size) + _, err = reader.Read(sBytes) + if err != nil { + return nil, err + } + + offsets := getReturnOffsets(s.f.elf.Machine, sBytes) + if len(offsets) == 0 { + return nil, fmt.Errorf("no offsets found") + } + return offsets, nil +} + +func (s *Symbol) AttachUprobe(exe *link.Executable, prog *ebpf.Program, pid uint32) (link.Link, error) { + return exe.Uprobe(s.Name(), prog, &link.UprobeOptions{Address: s.Address(), PID: int(pid)}) +} + +func (s *Symbol) AttachUretprobes(exe *link.Executable, prog *ebpf.Program, pid uint32) ([]link.Link, error) { + returnOffsets, err := s.ReturnOffsets() + if err != nil { + return nil, err + } + var links []link.Link + for _, offset := range returnOffsets { + l, err := exe.Uprobe("pthread_cond_timedwait", prog, &link.UprobeOptions{Address: s.Address(), Offset: uint64(offset), PID: int(pid)}) + if err != nil { + return links, err + } + links = append(links, l) + } + + return links, nil +} + +type ELFFile struct { + elf *elf.File + symbols []elf.Symbol + textSection *elf.Section + textSectionReader io.ReadSeeker +} + +func OpenELFFile(path string) (*ELFFile, error) { + file, err := elf.Open(path) + if err != nil { + return nil, err + } + return &ELFFile{elf: file}, nil +} + +func (f *ELFFile) readSymbols() error { + symbols, _ := f.elf.Symbols() + dyn, _ := f.elf.DynamicSymbols() + + if len(symbols) == 0 && len(dyn) == 0 { + return fmt.Errorf("no symbols found") + } + f.symbols = append(symbols, dyn...) + return nil +} + +func (f *ELFFile) GetSymbol(name string) (*Symbol, error) { + if f.symbols == nil { + if err := f.readSymbols(); err != nil { + return nil, err + } + } + var es *elf.Symbol + for _, s := range f.symbols { + if elf.ST_TYPE(s.Info) != elf.STT_FUNC || s.Size == 0 || s.Value == 0 { + continue + } + if s.Name == name && s.VersionIndex&0x8000 == 0 { + es = &s + break + } + } + if es == nil { + return nil, fmt.Errorf("symbol %s not found", name) + } + return &Symbol{s: es, f: f}, nil +} + +func (f *ELFFile) getTextSectionAndReader() (*elf.Section, io.ReadSeeker, error) { + if f.textSection == nil { + f.textSection = f.elf.Section(".text") + if f.textSection == nil { + return nil, nil, fmt.Errorf("no .text") + } + f.textSectionReader = f.textSection.Open() + } + return f.textSection, f.textSectionReader, nil +} + +func (f *ELFFile) Close() error { + return f.elf.Close() +} + +func getReturnOffsets(machine elf.Machine, instructions []byte) []int { + var res []int + switch machine { + case elf.EM_X86_64: + for i := 0; i < len(instructions); { + ins, err := x86asm.Decode(instructions[i:], 64) + if err == nil && ins.Op == x86asm.RET { + res = append(res, i) + } + i += ins.Len + } + case elf.EM_AARCH64: + for i := 0; i < len(instructions); { + ins, err := arm64asm.Decode(instructions[i:]) + if err == nil && ins.Op == arm64asm.RET { + res = append(res, i) + } + i += 4 + } + } + return res +} diff --git a/ebpftracer/nodejs.go b/ebpftracer/nodejs.go index ae7ac06..78cef98 100644 --- a/ebpftracer/nodejs.go +++ b/ebpftracer/nodejs.go @@ -25,55 +25,65 @@ func (t *Tracer) AttachNodejsProbes(pid uint32, exe string) []link.Link { klog.InfofDepth(1, "pid=%d lib=%s: %s", pid, libPath, msg) } - var ( - lastErr error - links []link.Link - libPath string - ) - for _, libPath = range append(getLibuv(pid), proc.Path(pid, "root", exe)) { - exe, err := link.OpenExecutable(libPath) - if err != nil { - log(libPath, "failed to open executable", err) - return nil - } - options := &link.UprobeOptions{PID: int(pid)} - var uprobe, uretprobe link.Link - uprobe, lastErr = exe.Uprobe("uv__io_poll", t.uprobes["uv_io_poll_enter"], options) - if lastErr != nil { - continue + for _, libPath := range append(getLibuv(pid), proc.Path(pid, "root", exe)) { + if links, err := t.attachNodejsUprobes(libPath, pid); err == nil { + log(libPath, "nodejs uprobes attached", nil) + return links + } else { + log(libPath, "failed to attach nodejs uprobes", err) } + } + return nil +} - links = append(links, uprobe) - uretprobe, lastErr = exe.Uretprobe("uv__io_poll", t.uprobes["uv_io_poll_exit"], options) - if lastErr != nil { - continue - } +func (t *Tracer) attachNodejsUprobes(libPath string, pid uint32) ([]link.Link, error) { + exe, err := link.OpenExecutable(libPath) + if err != nil { + return nil, err + } + ef, err := OpenELFFile(libPath) + if err != nil { + return nil, err + } + defer ef.Close() + + s, err := ef.GetSymbol("uv__io_poll") + if err != nil { + return nil, err + } + l, err := s.AttachUprobe(exe, t.uprobes["uv_io_poll_enter"], pid) + if err != nil { + return nil, err + } + var links []link.Link + links = append(links, l) - links = append(links, uretprobe) + ls, err := s.AttachUretprobes(exe, t.uprobes["uv_io_poll_exit"], pid) + links = append(links, ls...) + if err != nil { + for _, l := range links { + _ = l.Close() + } + return nil, err + } - for _, cb := range []string{"uv__stream_io", "uv__async_io", "uv__poll_io", "uv__server_io", "uv__udp_io"} { - uprobe, lastErr = exe.Uprobe(cb, t.uprobes["uv_io_cb_enter"], options) - if lastErr != nil { - break - } - links = append(links, uprobe) - uretprobe, lastErr = exe.Uretprobe(cb, t.uprobes["uv_io_cb_exit"], options) - if lastErr != nil { - break - } - links = append(links, uretprobe) + for _, cb := range []string{"uv__stream_io", "uv__async_io", "uv__poll_io", "uv__server_io", "uv__udp_io"} { + s, err = ef.GetSymbol(cb) + if err != nil { + break } - if lastErr != nil { - continue + l, err = s.AttachUprobe(exe, t.uprobes["uv_io_cb_enter"], pid) + if err != nil { + break + } + links = append(links, l) + ls, err = s.AttachUretprobes(exe, t.uprobes["uv_io_cb_exit"], pid) + links = append(links, ls...) + if err != nil { + break } - - log(libPath, "nodejs uprobes attached", nil) - break - } - if lastErr != nil { - log(libPath, "failed to attach uprobe", lastErr) } - return links + return links, nil } func getLibuv(pid uint32) []string { diff --git a/ebpftracer/python.go b/ebpftracer/python.go index 875c1a5..a481449 100644 --- a/ebpftracer/python.go +++ b/ebpftracer/python.go @@ -14,7 +14,7 @@ import ( var ( libcRegexp = regexp.MustCompile(`libc[\.-]`) - muslRegexp = regexp.MustCompile(`musl[\.-]`) + muslRegexp = regexp.MustCompile(`ld-musl[\.-]`) ) func (t *Tracer) AttachPythonThreadLockProbes(pid uint32) []link.Link { @@ -32,38 +32,55 @@ func (t *Tracer) AttachPythonThreadLockProbes(pid uint32) []link.Link { } var ( - lastErr error - links []link.Link - libPath string + links []link.Link + err error ) - for _, libPath = range getPthreadLibs(pid) { - exe, err := link.OpenExecutable(libPath) - if err != nil { - log(libPath, "failed to open executable", err) - return nil - } - options := &link.UprobeOptions{PID: int(pid)} - var uprobe, uretprobe link.Link - uprobe, lastErr = exe.Uprobe("pthread_cond_timedwait", t.uprobes["pthread_cond_timedwait_enter"], options) - if lastErr != nil { - continue - } - links = append(links, uprobe) - uretprobe, lastErr = exe.Uretprobe("pthread_cond_timedwait", t.uprobes["pthread_cond_timedwait_exit"], options) - if lastErr != nil { - continue + for _, libPath := range getPthreadLibs(pid) { + if links, err = t.attachPythonUprobes(libPath, pid); err == nil { + log(libPath, "python uprobes attached", nil) + return links + } else { + log(libPath, "failed to attach python uprobes", err) } - links = append(links, uretprobe) - log(libPath, "python uprobes attached", nil) - break } - if lastErr != nil { - log(libPath, "failed to attach uprobe", lastErr) + if len(links) > 0 { + } return links } +func (t *Tracer) attachPythonUprobes(libPath string, pid uint32) ([]link.Link, error) { + exe, err := link.OpenExecutable(libPath) + if err != nil { + return nil, err + } + ef, err := OpenELFFile(libPath) + if err != nil { + return nil, err + } + defer ef.Close() + + s, err := ef.GetSymbol("pthread_cond_timedwait") + if err != nil { + return nil, err + } + l, err := s.AttachUprobe(exe, t.uprobes["pthread_cond_timedwait_enter"], pid) + if err != nil { + return nil, err + } + links := []link.Link{l} + ls, err := s.AttachUretprobes(exe, t.uprobes["pthread_cond_timedwait_exit"], pid) + links = append(links, ls...) + if err != nil { + for _, l := range links { + _ = l.Close() + } + return nil, err + } + return links, nil +} + func getPthreadLibs(pid uint32) []string { f, err := os.Open(proc.Path(pid, "maps")) if err != nil { diff --git a/ebpftracer/tls.go b/ebpftracer/tls.go index 6a348f2..874cd51 100644 --- a/ebpftracer/tls.go +++ b/ebpftracer/tls.go @@ -5,9 +5,6 @@ import ( "bytes" "debug/buildinfo" "debug/elf" - "errors" - "fmt" - "io" "os" "regexp" "strings" @@ -15,8 +12,6 @@ import ( "github.com/cilium/ebpf/link" "github.com/coroot/coroot-node-agent/common" "github.com/coroot/coroot-node-agent/proc" - "golang.org/x/arch/arm64/arm64asm" - "golang.org/x/arch/x86/x86asm" "k8s.io/klog/v2" ) @@ -57,6 +52,11 @@ func (t *Tracer) AttachOpenSslUprobes(pid uint32) []link.Link { return nil } var links []link.Link + closeLinks := func() { + for _, l := range links { + l.Close() + } + } writeEnter := "openssl_SSL_write_enter" readEnter := "openssl_SSL_read_enter" readExEnter := "openssl_SSL_read_ex_enter" @@ -94,26 +94,43 @@ func (t *Tracer) AttachOpenSslUprobes(pid uint32) []link.Link { {symbol: "SSL_read_ex", uretprobe: readExit}, }...) } + + ef, err := OpenELFFile(libPath) + if err != nil { + log("open elf", err) + return nil + } + defer ef.Close() + for _, p := range progs { + s, err := ef.GetSymbol(p.symbol) + if err != nil { + log("failed to get symbol", err) + closeLinks() + return nil + } if p.uprobe != "" { - l, err := exe.Uprobe(p.symbol, t.uprobes[p.uprobe], nil) + l, err := s.AttachUprobe(exe, t.uprobes[p.uprobe], pid) if err != nil { log("failed to attach uprobe", err) + closeLinks() return nil } links = append(links, l) } if p.uretprobe != "" { - l, err := exe.Uretprobe(p.symbol, t.uprobes[p.uretprobe], nil) + ls, err := s.AttachUretprobes(exe, t.uprobes[p.uretprobe], pid) + links = append(links, ls...) if err != nil { - log("failed to attach uretprobe", err) + log("failed to attach exit uprobe", err) + closeLinks() return nil } - links = append(links, l) } } - - log("libssl uprobes attached", nil) + if len(links) > 0 { + log("libssl uprobes attached", nil) + } return links } @@ -162,98 +179,57 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32) ([]link.Link, bool) { return nil, isGolangApp } - ef, err := elf.Open(path) + ef, err := OpenELFFile(path) if err != nil { log("failed to open as elf binary", err) return nil, isGolangApp } defer ef.Close() - symbols, err := ef.Symbols() + exe, err := link.OpenExecutable(path) if err != nil { - if errors.Is(err, elf.ErrNoSymbols) { - log("no symbol section", nil) - return nil, isGolangApp - } - log("failed to read symbols", err) + log("failed to open executable", err) return nil, isGolangApp } - textSection := ef.Section(".text") - if textSection == nil { - log("no text section", nil) - return nil, isGolangApp + var links []link.Link + closeLinks := func() { + for _, l := range links { + l.Close() + } } - textReader := textSection.Open() - exe, err := link.OpenExecutable(path) + ws, err := ef.GetSymbol(goTlsWriteSymbol) if err != nil { - log("failed to open executable", err) + log("failed to get symbol", err) + return nil, isGolangApp + } + l, err := ws.AttachUprobe(exe, t.uprobes["go_crypto_tls_write_enter"], pid) + if err != nil { + log("failed to attach write_enter uprobe", err) return nil, isGolangApp } + links = append(links, l) - var links []link.Link - for _, s := range symbols { - if elf.ST_TYPE(s.Info) != elf.STT_FUNC || s.Size == 0 { - continue - } - switch s.Name { - case goTlsWriteSymbol, goTlsReadSymbol: - default: - continue - } - address := s.Value - for _, p := range ef.Progs { - if p.Type != elf.PT_LOAD || (p.Flags&elf.PF_X) == 0 { - continue - } + rs, err := ef.GetSymbol(goTlsReadSymbol) + if err != nil { + log("failed to get symbol", err) + return nil, isGolangApp + } + l, err = rs.AttachUprobe(exe, t.uprobes["go_crypto_tls_read_enter"], pid) + if err != nil { + log("failed to attach read_enter uprobe", err) + closeLinks() + return nil, isGolangApp + } + links = append(links, l) - if p.Vaddr <= s.Value && s.Value < (p.Vaddr+p.Memsz) { - address = s.Value - p.Vaddr + p.Off - break - } - } - switch s.Name { - case goTlsWriteSymbol: - l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_write_enter"], &link.UprobeOptions{Address: address}) - if err != nil { - log("failed to attach write_enter uprobe", err) - return nil, isGolangApp - } - links = append(links, l) - case goTlsReadSymbol: - l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_read_enter"], &link.UprobeOptions{Address: address}) - if err != nil { - log("failed to attach read_enter uprobe", err) - return nil, isGolangApp - } - links = append(links, l) - sStart := s.Value - textSection.Addr - _, err = textReader.Seek(int64(sStart), io.SeekStart) - if err != nil { - log("failed to seek", err) - return nil, isGolangApp - } - sBytes := make([]byte, s.Size) - _, err = textReader.Read(sBytes) - if err != nil { - log("failed to read", err) - return nil, isGolangApp - } - returnOffsets := getReturnOffsets(ef.Machine, sBytes) - if len(returnOffsets) == 0 { - log("failed to attach read_exit uprobe", fmt.Errorf("no return offsets found")) - return nil, isGolangApp - } - for _, offset := range returnOffsets { - l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_read_exit"], &link.UprobeOptions{Address: address, Offset: uint64(offset)}) - if err != nil { - log("failed to attach read_exit uprobe", err) - return nil, isGolangApp - } - links = append(links, l) - } - } + ls, err := rs.AttachUretprobes(exe, t.uprobes["go_crypto_tls_read_exit"], pid) + links = append(links, ls...) + if err != nil { + log("failed to attach read_exit uprobe", err) + closeLinks() + return nil, isGolangApp } if len(links) == 0 { return nil, isGolangApp @@ -327,26 +303,3 @@ func getSslLibPathAndVersion(pid uint32) (string, string) { } return libsslPath, "v" + version } - -func getReturnOffsets(machine elf.Machine, instructions []byte) []int { - var res []int - switch machine { - case elf.EM_X86_64: - for i := 0; i < len(instructions); { - ins, err := x86asm.Decode(instructions[i:], 64) - if err == nil && ins.Op == x86asm.RET { - res = append(res, i) - } - i += ins.Len - } - case elf.EM_AARCH64: - for i := 0; i < len(instructions); { - ins, err := arm64asm.Decode(instructions[i:]) - if err == nil && ins.Op == arm64asm.RET { - res = append(res, i) - } - i += 4 - } - } - return res -} diff --git a/go.mod b/go.mod index 6126dc3..bb054b2 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/NVIDIA/go-nvml v0.12.4-1 github.com/agoda-com/opentelemetry-logs-go v0.4.1 github.com/cilium/cilium v1.17.2 - github.com/cilium/ebpf v0.19.0 + github.com/cilium/ebpf v0.20.0 github.com/containerd/cgroups v1.0.4 github.com/containerd/containerd v1.6.38 github.com/coreos/go-systemd/v22 v22.5.0 @@ -40,8 +40,8 @@ require ( go.opentelemetry.io/otel/trace v1.34.0 golang.org/x/arch v0.4.0 golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa - golang.org/x/net v0.38.0 - golang.org/x/sys v0.36.0 + golang.org/x/net v0.46.0 + golang.org/x/sys v0.37.0 golang.org/x/time v0.8.0 gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/yaml.v2 v2.4.0 @@ -174,10 +174,10 @@ require ( go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect golang.org/x/oauth2 v0.27.0 // indirect - golang.org/x/sync v0.12.0 // indirect - golang.org/x/term v0.30.0 // indirect - golang.org/x/text v0.23.0 // indirect - golang.org/x/tools v0.28.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/term v0.36.0 // indirect + golang.org/x/text v0.30.0 // indirect + golang.org/x/tools v0.38.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect google.golang.org/grpc v1.69.2 // indirect diff --git a/go.sum b/go.sum index f9b7c99..03da0f8 100644 --- a/go.sum +++ b/go.sum @@ -138,8 +138,8 @@ github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.19.0 h1:Ro/rE64RmFBeA9FGjcTc+KmCeY6jXmryu6FfnzPRIao= -github.com/cilium/ebpf v0.19.0/go.mod h1:fLCgMo3l8tZmAdM3B2XqdFzXBpwkcSTroaVqN08OWVY= +github.com/cilium/ebpf v0.20.0 h1:atwWj9d3NffHyPZzVlx3hmw1on5CLe9eljR8VuHTwhM= +github.com/cilium/ebpf v0.20.0/go.mod h1:pzLjFymM+uZPLk/IXZUL63xdx5VXEo+enTzxkZXdycw= github.com/cilium/fake v0.6.1 h1:cLkNx1nkF0b0pPW79JaQxaI5oG2/rBzRKpp0YUg1fTA= github.com/cilium/fake v0.6.1/go.mod h1:V9lCbbcsnSf3vB6sdOP7Q0bsUUJ/jyHPZxnFAw5nPUc= github.com/cilium/hive v0.0.0-20250121145729-e67f66eb0375 h1:EhoCO0AI3qJavnhfAls4w7VpVVpAr12wIh293sNA0hQ= @@ -1099,8 +1099,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1119,8 +1119,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1205,11 +1205,11 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= +golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1217,8 +1217,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1272,8 +1272,8 @@ golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4X golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= -golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=