Skip to content

Commit 9698205

Browse files
authored
Merge pull request #18 from parca-dev/src-cmd
Add source command to build source archive from debuginfo
2 parents ace8012 + 0adf2c5 commit 9698205

File tree

3 files changed

+105
-1
lines changed

3 files changed

+105
-1
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,8 @@ Commands:
2727
buildid <path>
2828
Extract buildid.
2929
30+
source <debuginfo-path> [<out-path>]
31+
Build a source archive by discovering files from a given debuginfo file.
32+
3033
Run "parca-debuginfo <command> --help" for more information on a command.
3134
```

cmd/parca-debuginfo/main.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
package main
1616

1717
import (
18+
"archive/tar"
1819
"context"
1920
"crypto/tls"
21+
"debug/dwarf"
2022
"debug/elf"
2123
"errors"
2224
"fmt"
@@ -28,6 +30,7 @@ import (
2830
"github.com/alecthomas/kong"
2931
"github.com/go-kit/log"
3032
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
33+
"github.com/klauspost/compress/zstd"
3134
grun "github.com/oklog/run"
3235
"github.com/parca-dev/parca-agent/pkg/buildid"
3336
"github.com/parca-dev/parca-agent/pkg/debuginfo"
@@ -72,6 +75,11 @@ type flags struct {
7275
Buildid struct {
7376
Path string `kong:"required,arg,name='path',help='Paths to extract buildid.',type:'path'"`
7477
} `cmd:"" help:"Extract buildid."`
78+
79+
Source struct {
80+
DebuginfoPath string `kong:"required,arg,name='debuginfo-path',help='Path to debuginfo file',type:'path'"`
81+
OutPath string `kong:"arg,name='out-path',help='Path to output archive file',type:'path',default='source.tar.zstd'"`
82+
} `cmd:"" help:"Build a source archive by discovering files from a given debuginfo file."`
7583
}
7684

7785
func main() {
@@ -335,6 +343,99 @@ func run(kongCtx *kong.Context, flags flags) error {
335343
cancel()
336344
})
337345

346+
case "source <debuginfo-path>":
347+
g.Add(func() error {
348+
f, err := elf.Open(flags.Source.DebuginfoPath)
349+
if err != nil {
350+
return fmt.Errorf("open elf: %w", err)
351+
}
352+
defer f.Close()
353+
354+
sf, err := os.Create(flags.Source.OutPath)
355+
if err != nil {
356+
return fmt.Errorf("create source archive: %w", err)
357+
}
358+
defer sf.Close()
359+
360+
zw, err := zstd.NewWriter(sf)
361+
if err != nil {
362+
return fmt.Errorf("create zstd writer: %w", err)
363+
}
364+
365+
tw := tar.NewWriter(zw)
366+
367+
d, err := f.DWARF()
368+
if err != nil {
369+
return fmt.Errorf("get dwarf data: %w", err)
370+
}
371+
372+
r := d.Reader()
373+
seen := map[string]struct{}{}
374+
for {
375+
e, err := r.Next()
376+
if err != nil {
377+
return fmt.Errorf("read DWARF entry: %w", err)
378+
}
379+
if e == nil {
380+
break
381+
}
382+
383+
if e.Tag == dwarf.TagCompileUnit {
384+
lr, err := d.LineReader(e)
385+
if err != nil {
386+
return fmt.Errorf("get line reader: %w", err)
387+
}
388+
389+
if lr == nil {
390+
continue
391+
}
392+
393+
for _, lineFile := range lr.Files() {
394+
if lineFile == nil {
395+
continue
396+
}
397+
if _, ok := seen[lineFile.Name]; !ok {
398+
sourceFile, err := os.Open(lineFile.Name)
399+
if errors.Is(err, os.ErrNotExist) {
400+
fmt.Fprintf(os.Stderr, "skipping file %q: does not exist\n", lineFile.Name)
401+
seen[lineFile.Name] = struct{}{}
402+
continue
403+
}
404+
if err != nil {
405+
return fmt.Errorf("open file: %w", err)
406+
}
407+
408+
stat, err := sourceFile.Stat()
409+
if err != nil {
410+
return fmt.Errorf("stat file: %w", err)
411+
}
412+
413+
if err := tw.WriteHeader(&tar.Header{
414+
Name: lineFile.Name,
415+
Size: stat.Size(),
416+
}); err != nil {
417+
return fmt.Errorf("write tar header: %w", err)
418+
}
419+
420+
if _, err = io.Copy(tw, sourceFile); err != nil {
421+
return fmt.Errorf("copy file to tar: %w", err)
422+
}
423+
424+
if err := sourceFile.Close(); err != nil {
425+
return fmt.Errorf("close file: %w", err)
426+
}
427+
428+
seen[lineFile.Name] = struct{}{}
429+
}
430+
}
431+
}
432+
}
433+
434+
return nil
435+
}, func(error) {
436+
cancel()
437+
})
438+
338439
default:
339440
cancel()
340441
return errors.New("unknown command: " + kongCtx.Command())

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/alecthomas/kong v0.8.0
77
github.com/go-kit/log v0.2.1
88
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
9+
github.com/klauspost/compress v1.16.7
910
github.com/oklog/run v1.1.0
1011
github.com/parca-dev/parca v0.18.1-0.20230816074650-c9b9bed904c3
1112
github.com/parca-dev/parca-agent v0.12.1-0.20230216133018-8dd5ccaeef0f
@@ -64,7 +65,6 @@ require (
6465
github.com/hashicorp/go-multierror v1.1.1 // indirect
6566
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.23.3+incompatible // indirect
6667
github.com/json-iterator/go v1.1.12 // indirect
67-
github.com/klauspost/compress v1.16.7 // indirect
6868
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
6969
github.com/kylelemons/godebug v1.1.0 // indirect
7070
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect

0 commit comments

Comments
 (0)