package debuginfod import ( "bufio" "bytes" "context" "os/exec" "slices" "strings" "time" ) const debuginfodFind = "debuginfod-find" const notificationThrottle time.Duration = 1 * time.Second func execFind(ctx context.Context, notify func(string), args ...string) (string, error) { if _, err := exec.LookPath(debuginfodFind); err != nil { return "", err } if ctx == nil { ctx = context.Background() } cmd := exec.CommandContext(ctx, debuginfodFind, args...) if notify != nil { cmd.Args = slices.Insert(cmd.Args, 1, "--verbose") stderr, err := cmd.StderrPipe() if err != nil { return "", err } s := bufio.NewScanner(stderr) s.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) { if atEOF && len(data) == 0 { return 0, nil, nil } if i := bytes.IndexAny(data, "\n\r"); i >= 0 { return i + 1, dropCR(data[0:i]), nil } if atEOF { return len(data), dropCR(data), nil } return 0, nil, nil }) go func() { var tlast time.Time for s.Scan() { if time.Since(tlast) > notificationThrottle { tlast = time.Now() notify(string(s.Text())) } } }() } out, err := cmd.Output() // ignore stderr if err != nil { return "", err } return strings.TrimSpace(string(out)), err } func dropCR(data []byte) []byte { r, _ := bytes.CutSuffix(data, []byte{'\r'}) return r } func GetSource(buildid, filename string) (string, error) { return execFind(context.Background(), nil, "source", buildid, filename) } func GetDebuginfo(ctx context.Context, notify func(string), buildid string) (string, error) { return execFind(ctx, notify, "debuginfo", buildid) }