blob: 079ca58d750cb5697047cae532e3702c44b1f3a1 [file]
// Copyright 2019 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 (
"archive/tar"
"compress/bzip2"
"encoding/json"
"flag"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"go.fuchsia.dev/fuchsia/tools/debug/elflib"
"go.fuchsia.dev/fuchsia/tools/lib/color"
"go.fuchsia.dev/fuchsia/tools/lib/logger"
)
var (
debugArchive string
buildIDDir string
outputManifest string
cpu string
osName string
colors color.EnableColor
level logger.LogLevel
)
func init() {
colors = color.ColorAuto
level = logger.WarningLevel
flag.StringVar(&debugArchive, "debug-archive", "", "path to archive of debug binaries")
flag.StringVar(&buildIDDir, "build-id-dir", "", "path to .build-id directory to add debug binaries to")
flag.StringVar(&outputManifest, "output-manifest", "", "path to output a json manifest of debug binaries to")
flag.StringVar(&cpu, "cpu", "", "the architecture of the binaries in the archive")
flag.StringVar(&osName, "os", "", "the os of the binaries in the archive")
flag.Var(&colors, "color", "use color in output, can be never, auto, always")
flag.Var(&level, "level", "output verbosity, can be fatal, error, warning, info, debug or trace")
}
type binary struct {
CPU string `json:"cpu"`
Debug string `json:"debug"`
BuildID string `json:"elf_build_id"`
OS string `json:"os"`
}
var buildIDFileRE = regexp.MustCompile("^([0-9a-f][0-9a-f])/([0-9a-f]+).debug$")
func unpack(log *logger.Logger) ([]elflib.BinaryFileRef, error) {
// unpack each debug binary into buildIDDir
file, err := os.Open(debugArchive)
if err != nil {
return nil, fmt.Errorf("while unpacking %s: %v", debugArchive, err)
}
defer file.Close()
// The file is bzip2 compressed
tr := tar.NewReader(bzip2.NewReader(file))
out := []elflib.BinaryFileRef{}
for {
hdr, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return nil, fmt.Errorf("while reading %s: %v", debugArchive, err)
}
matches := buildIDFileRE.FindStringSubmatch(hdr.Name)
if matches == nil {
log.Warningf("%s in %s was not a debug binary", hdr.Name, debugArchive)
continue
}
log.Tracef("Reading %s from %s", hdr.Name, debugArchive)
if len(matches) != 3 {
panic("The list of matches isn't as expected")
}
buildID := matches[1] + matches[2]
unpackFilePath := filepath.Join(buildIDDir, hdr.Name)
if err = os.MkdirAll(filepath.Dir(unpackFilePath), os.ModePerm); err != nil {
return nil, fmt.Errorf("while attempting to write %s from %s to %s: %v", hdr.Name, debugArchive, unpackFilePath, err)
}
outFile, err := os.Create(unpackFilePath)
if err != nil {
return nil, fmt.Errorf("while attempting to write %s from %s to %s: %v", hdr.Name, debugArchive, unpackFilePath, err)
}
if _, err := io.Copy(outFile, tr); err != nil {
outFile.Close()
return nil, fmt.Errorf("while attempting to write %s from %s to %s: %v", hdr.Name, debugArchive, unpackFilePath, err)
}
outFile.Close()
bfr := elflib.NewBinaryFileRef(unpackFilePath, buildID)
if err := bfr.Verify(); err != nil {
return nil, fmt.Errorf("while attempting to verify %s copied from %s: %v", unpackFilePath, debugArchive, err)
}
out = append(out, bfr)
}
return out, nil
}
func writeManifest(bfrs []elflib.BinaryFileRef) error {
out := []binary{}
for _, bfr := range bfrs {
out = append(out, binary{
CPU: cpu,
Debug: bfr.Filepath,
BuildID: bfr.BuildID,
OS: osName,
})
}
file, err := os.Create(outputManifest)
if err != nil {
return fmt.Errorf("while writing json to %s: %v", outputManifest, err)
}
defer file.Close()
if err = json.NewEncoder(file).Encode(out); err != nil {
return fmt.Errorf("while writing json to %s: %v", outputManifest, err)
}
return nil
}
func main() {
log := logger.NewLogger(level, color.NewColor(colors), os.Stdout, os.Stderr, "")
flag.Parse()
if debugArchive == "" {
log.Fatalf("-debug-archive is required.")
}
if buildIDDir == "" {
log.Fatalf("-build-id-dir is required.")
}
if outputManifest == "" {
log.Fatalf("-output-manifest is required.")
}
bfrs, err := unpack(log)
if err != nil {
log.Fatalf("%v", err)
}
if err = writeManifest(bfrs); err != nil {
log.Fatalf("%v", err)
}
}