blob: 86e8294ada74394cd5669043a38ee9293b9dd741 [file] [log] [blame]
///bin/true ; exec /usr/bin/env go run "$0" "$@"
// 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 (
"bufio"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"sort"
"strings"
)
var dryRun = flag.Bool("n", false, "Dry run - print what would happen but don't actually do it")
var symbolDir = flag.String("symbol-dir", "", "Location for the symbol files")
var upload = flag.Bool("upload", true, "Whether to upload the dumped symbols")
var url = flag.String("url", "https://clients2.google.com/cr/symbol", "Endpoint to use")
var verbose = flag.Bool("v", false, "Verbose output")
var x64Symbols = flag.Bool("x64-symbols", true, "Include symbols from x64 build")
var armSymbols = flag.Bool("arm-symbols", true, "Include symbols from arm64 build")
func getBinariesFromIds(idsFilename string) sort.StringSlice {
var binaries []string
file, err := os.Open(idsFilename)
if err != nil {
log.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
binary := strings.SplitAfterN(scanner.Text(), " ", 2)[1]
binaries = append(binaries, binary)
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
return binaries
}
func dump(bin, fuchsiaRoot string) string {
if *verbose || *dryRun {
fmt.Println("Dumping binary", bin)
}
relToRoot, err := filepath.Rel(fuchsiaRoot, bin)
if err != nil {
log.Fatal("could not filepath.Rel ", bin, ": ", err)
}
symfile := path.Join(*symbolDir, strings.Replace(relToRoot, "/", "#", -1)+".sym")
if *dryRun {
return symfile
}
out, err := exec.Command("buildtools/linux-x64/dump_syms/dump_syms", bin).Output()
if err != nil {
log.Fatal("could not dump_syms ", bin, ": ", err)
}
// Many Fuchsia binaries are built as "something.elf", but then packaged as
// just "something". In the ids.txt file, the name still includes the ".elf"
// extension, which dump_syms emits into the .sym file, and the crash server
// uses as part of the lookup (that is, both the name and the buildid have to
// match). So, if the first header line ends in ".elf" strip it off.
lines := strings.Split(string(out), "\n")
lines[0] = strings.TrimSuffix(lines[0], ".elf")
out = []byte(strings.Join(lines, "\n"))
err = ioutil.WriteFile(symfile, out, 0644)
if err != nil {
log.Fatal("could not write output file", symfile, ": ", err)
}
return symfile
}
func uploadSymbols(symfile string) {
if *verbose || *dryRun {
fmt.Println("Uploading symbols", symfile)
}
if *dryRun {
return
}
out, err := exec.Command("buildtools/linux-x64/symupload/sym_upload", symfile, *url).CombinedOutput()
if err != nil {
log.Fatal("sym_upload for ", symfile, " failed with output ", string(out), " error ", err)
}
}
func mkdir(d string) {
if *verbose || *dryRun {
fmt.Println("Making directory", d)
}
if *dryRun {
return
}
_, err := exec.Command("mkdir", "-p", d).Output()
if err != nil {
log.Fatal("could not create directory", d)
}
}
// Based on https://github.com/xtgo/set/blob/master/mutators.go#L17.
func uniq(data sort.StringSlice) sort.StringSlice {
p, l := 0, data.Len()
if l <= 1 {
return data
}
for i := 1; i < l; i++ {
if !data.Less(p, i) {
continue
}
p++
if p < i {
data.Swap(p, i)
}
}
return data[:p+1]
}
func main() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, `Usage ./upload-symbols.go [flags] /path/to/fuchsia/root
This script converts the symbols for a built tree into a format suitable for the
crash server and then optionally uploads them.
`)
flag.PrintDefaults()
}
flag.Parse()
fuchsiaRoot := flag.Arg(0)
if _, err := os.Stat(fuchsiaRoot); os.IsNotExist(err) {
flag.Usage()
log.Fatalf("Fuchsia root not found at \"%v\"\n", fuchsiaRoot)
}
cwd, err := os.Getwd()
if err != nil {
log.Fatalf("Could not Getwd")
}
cwd, err = filepath.EvalSymlinks(cwd)
if err != nil {
log.Fatalf("Could not EvalSymlinks")
}
fuchsiaRoot = filepath.Join(cwd, fuchsiaRoot)
if *symbolDir == "" {
var err error
*symbolDir, err = ioutil.TempDir("", "crash-symbols")
if err != nil {
log.Fatal("Could not create temporary directory: ", err)
}
defer os.RemoveAll(*symbolDir)
} else if _, err := os.Stat(*symbolDir); os.IsNotExist(err) {
mkdir(*symbolDir)
}
var binaries sort.StringSlice
zxBuildDir := "out/build-zircon"
if *x64Symbols {
x64BuildDir := "out/release-x64"
x64ZxBuildDir := path.Join(zxBuildDir, "build-x64")
binaries = append(binaries, getBinariesFromIds(path.Join(fuchsiaRoot, x64BuildDir, "ids.txt"))...)
binaries = append(binaries, getBinariesFromIds(path.Join(fuchsiaRoot, x64ZxBuildDir, "ids.txt"))...)
}
if *armSymbols {
armBuildDir := "out/release-arm64"
armZxBuildDir := path.Join(zxBuildDir, "build-arm64")
binaries = append(binaries, getBinariesFromIds(path.Join(fuchsiaRoot, armBuildDir, "ids.txt"))...)
binaries = append(binaries, getBinariesFromIds(path.Join(fuchsiaRoot, armZxBuildDir, "ids.txt"))...)
}
sort.Sort(binaries)
binaries = uniq(binaries)
for i, v := range binaries {
binaries[i], err = filepath.EvalSymlinks(v)
if err != nil {
log.Fatalf("Could not EvalSymlinks")
}
}
sem := make(chan bool, len(binaries))
for _, binary := range binaries {
go func(binary string) {
symfile := dump(binary, fuchsiaRoot)
if *upload {
uploadSymbols(symfile)
}
sem <- true
}(binary)
}
for i := 0; i < len(binaries); i++ {
<-sem
}
}