1515package main
1616
1717import (
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
7785func 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 ())
0 commit comments