blob: 1ed0a424359cc95d889181d22022096f33a9256a [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package main
import (
// TODO(kjharland): change crypto/sha1 to a safer hash algorithm. sha256 or sha2, etc.
"archive/tar"
"compress/gzip"
"context"
"flag"
"fmt"
"log"
"os"
"go.fuchsia.dev/tools/debug/breakpad/generator"
"go.fuchsia.dev/tools/debug/elflib"
"go.fuchsia.dev/tools/lib/tarutil"
)
const usage = `usage: dump_breakpad_symbols [options] paths...
Generates breakpad symbol files by running dump_syms on a collection of binaries. Produces
a tar archive containing all generated files. Expects a list of paths to .build-id dirs as
input.
Example invocation:
$ dump_breakpad_symbols -dump-syms-path=dump_syms -tar-file=out.tar.gz -depfile=dep.out ./.build-ids
`
// Command line flag values
var (
depFilepath string
dumpSymsPath string
tarFilepath string
// Flag to switch to using .build-id directory paths as input instead of ids.txt
// files.
//
// TODO(1068): Make this the default and delete support for ids.txt.
useBuildIDInput bool
)
func init() {
flag.Usage = func() {
fmt.Fprint(os.Stderr, usage)
flag.PrintDefaults()
os.Exit(0)
}
var unused string
flag.StringVar(&unused, "out-dir", "", "DEPRECATED. This is not used")
flag.StringVar(&dumpSymsPath, "dump-syms-path", "", "Path to the breakpad tools `dump_syms` executable")
flag.StringVar(&depFilepath, "depfile", "", "Path to the ninja depfile to generate")
flag.StringVar(&tarFilepath, "tar-file", "", "Path where the tar archive containing symbol files is written")
flag.BoolVar(&useBuildIDInput, "use-build-id", false, "Use .build-id dir inputs instead of ids.txt")
}
func main() {
flag.Parse()
if err := execute(context.Background(), flag.Args()...); err != nil {
log.Fatal(err)
}
}
func execute(ctx context.Context, paths ...string) error {
// Collect all binary file refs from each directory
var bfrs []elflib.BinaryFileRef
var err error
if useBuildIDInput {
bfrs, err = bfrsFromBuildIDs(paths...)
} else {
bfrs, err = bfrsFromIdsTxt(paths...)
}
if err != nil {
return err
}
// Skip files without .debug_info header.
bfrs = filterEmptyDebugSymbolFiles(bfrs)
// Generate all symbol files.
path, err := generator.Generate(bfrs, dumpSymsPath)
if err != nil {
return fmt.Errorf("failed to generate symbols: %v", err)
}
// Write all files to the specified tar archive.
tarfd, err := os.Create(tarFilepath)
if err != nil {
return fmt.Errorf("failed to create %q: %v", tarFilepath, err)
}
gzw := gzip.NewWriter(tarfd)
defer gzw.Close()
tw := tar.NewWriter(gzw)
defer tw.Close()
log.Printf("archiving %q to %q", path, tarFilepath)
if err := tarutil.TarDirectory(tw, path); err != nil {
return fmt.Errorf("failed to write %q: %v", tarFilepath, err)
}
// Write the Ninja dep file.
depfile := depfile{outputPath: tarFilepath, inputPaths: paths}
depfd, err := os.Create(depFilepath)
if err != nil {
return fmt.Errorf("failed to create %q: %v", depFilepath, err)
}
n, err := depfile.WriteTo(depfd)
if err != nil {
return fmt.Errorf("failed to write %q: %v", depFilepath, err)
}
if n == 0 {
return fmt.Errorf("wrote 0 bytes to %q", depFilepath)
}
return nil
}
// TODO(1068): Delete this after updating the build to use .build-id directories.
func bfrsFromIdsTxt(paths ...string) ([]elflib.BinaryFileRef, error) {
var bfrs []elflib.BinaryFileRef
for _, path := range paths {
fd, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("failed to open %q: %v", path, err)
}
defer fd.Close()
newbfrs, err := elflib.ReadIDsFile(fd)
if err != nil {
return nil, fmt.Errorf("failed to read %q: %v", path, err)
}
bfrs = append(bfrs, newbfrs...)
}
return bfrs, nil
}
func bfrsFromBuildIDs(dirs ...string) ([]elflib.BinaryFileRef, error) {
var bfrs []elflib.BinaryFileRef
for _, dir := range dirs {
newbfrs, err := elflib.WalkBuildIDDir(dir)
if err != nil {
return nil, err
}
bfrs = append(bfrs, newbfrs...)
}
return bfrs, nil
}
// Returns filtered input of BinaryFileRefs, skipping files without .debug_info header.
func filterEmptyDebugSymbolFiles(bfrs []elflib.BinaryFileRef) []elflib.BinaryFileRef {
var filteredBfrs []elflib.BinaryFileRef
for _, bfr := range bfrs {
hasDebugInfo, err := bfr.HasDebugInfo()
if err != nil {
log.Printf("WARNING: cannot read file %s: %v, skipping\n", bfr.Filepath, err)
} else if !hasDebugInfo {
log.Printf("WARNING: file %s missing .debug_info section, skipping\n", bfr.Filepath)
} else {
filteredBfrs = append(filteredBfrs, bfr)
}
}
return filteredBfrs
}