[tools] Delete all tools.
They have been moved to fuchsia.git.
Bug: 10321
Change-Id: Iacc8f2ece7a5b070a5727ddb6e393b6cb8e45fcf
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index f821f1c..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright 2018 The Fuchsia Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/PATENTS b/PATENTS
deleted file mode 100644
index 2746e78..0000000
--- a/PATENTS
+++ /dev/null
@@ -1,22 +0,0 @@
-Additional IP Rights Grant (Patents)
-
-"This implementation" means the copyrightable works distributed by
-Google as part of the Fuchsia project.
-
-Google hereby grants to you a perpetual, worldwide, non-exclusive,
-no-charge, royalty-free, irrevocable (except as stated in this
-section) patent license to make, have made, use, offer to sell, sell,
-import, transfer, and otherwise run, modify and propagate the contents
-of this implementation of Fuchsia, where such license applies only to
-those patent claims, both currently owned by Google and acquired in
-the future, licensable by Google that are necessarily infringed by
-this implementation. This grant does not include claims that would be
-infringed only as a consequence of further modification of this
-implementation. If you or your agent or exclusive licensee institute
-or order or agree to the institution of patent litigation or any other
-patent enforcement activity against any entity (including a
-cross-claim or counterclaim in a lawsuit) alleging that this
-implementation of Fuchsia constitutes direct or contributory patent
-infringement, or inducement of patent infringement, then any patent
-rights granted to you under this License for this implementation of
-Fuchsia shall terminate as of the date such litigation is filed.
diff --git a/README.md b/README.md
index 1745a5c..5b3a962 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,3 @@
# tools
-This repo contains tools used in Fuchsia build and development.
-
-Go packages from here are automatically built and uploaded to CIPD and Google
-Storage by bots using the [tools](https://fuchsia.googlesource.com/infra/recipes/+/master/recipes/tools.py) recipe.
+All tools have been moved to fuchsia.git at https://fuchsia.googlesource.com/fuchsia/+/refs/heads/master/tools.
diff --git a/botanist/boot.go b/botanist/boot.go
deleted file mode 100644
index 22f01e4..0000000
--- a/botanist/boot.go
+++ /dev/null
@@ -1,307 +0,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 botanist
-
-import (
- "bytes"
- "context"
- "errors"
- "fmt"
- "io"
- "log"
- "net"
- "os"
- "sort"
- "strings"
- "time"
-
- "go.fuchsia.dev/tools/build/api"
- "go.fuchsia.dev/tools/lib/retry"
- "go.fuchsia.dev/tools/net/netboot"
- "go.fuchsia.dev/tools/net/tftp"
- "golang.org/x/crypto/ssh"
-)
-
-const (
- // ModePave is a directive to pave when booting.
- ModePave int = iota
- // ModeNetboot is a directive to netboot when booting.
- ModeNetboot
-)
-
-const (
- // Special image names recognized by fuchsia's netsvc.
- authorizedKeysNetsvcName = "<<image>>authorized_keys"
- bootloaderNetsvcName = "<<image>>bootloader.img"
- cmdlineNetsvcName = "<<netboot>>cmdline"
- efiNetsvcName = "<<image>>efi.img"
- fvmNetsvcName = "<<image>>sparse.fvm"
- kerncNetsvcName = "<<image>>kernc.img"
- kernelNetsvcName = "<<netboot>>kernel.bin"
- vbmetaANetsvcName = "<<image>>vbmetaa.img"
- vbmetaBNetsvcName = "<<image>>vbmetab.img"
- zirconANetsvcName = "<<image>>zircona.img"
- zirconBNetsvcName = "<<image>>zirconb.img"
- zirconRNetsvcName = "<<image>>zirconr.img"
-)
-
-// Maps bootserver argument to a corresponding netsvc name.
-var bootserverArgToName = map[string]string{
- "--boot": kernelNetsvcName,
- "--bootloader": bootloaderNetsvcName,
- "--efi": efiNetsvcName,
- "--fvm": fvmNetsvcName,
- "--kernc": kerncNetsvcName,
- "--vbmetaa": vbmetaANetsvcName,
- "--vbmetab": vbmetaBNetsvcName,
- "--zircona": zirconANetsvcName,
- "--zirconb": zirconBNetsvcName,
- "--zirconr": zirconRNetsvcName,
-}
-
-// Maps netsvc name to the index at which the corresponding file should be transferred if
-// present. The indices correspond to the ordering given in
-// https://go.fuchsia.dev/zircon/+/master/system/host/bootserver/bootserver.c
-var transferOrder = map[string]int{
- cmdlineNetsvcName: 1,
- fvmNetsvcName: 2,
- bootloaderNetsvcName: 3,
- efiNetsvcName: 4,
- kerncNetsvcName: 5,
- zirconANetsvcName: 6,
- zirconBNetsvcName: 7,
- zirconRNetsvcName: 8,
- vbmetaANetsvcName: 9,
- vbmetaBNetsvcName: 10,
- authorizedKeysNetsvcName: 11,
- kernelNetsvcName: 12,
-}
-
-// Boot prepares and boots a device at the given IP address. Depending on bootMode, the
-// device will either be paved or netbooted with the provided images, command-line
-// arguments and a public SSH user key.
-func Boot(ctx context.Context, addr *net.UDPAddr, bootMode int, imgs []build.Image, cmdlineArgs []string, signers []ssh.Signer) error {
- var bootArgs func(build.Image) []string
- switch bootMode {
- case ModePave:
- bootArgs = func(img build.Image) []string { return img.PaveArgs }
- case ModeNetboot:
- bootArgs = func(img build.Image) []string { return img.NetbootArgs }
- default:
- return fmt.Errorf("invalid boot mode: %d", bootMode)
- }
-
- var files []*netsvcFile
- if len(cmdlineArgs) > 0 {
- var buf bytes.Buffer
- for _, arg := range cmdlineArgs {
- fmt.Fprintf(&buf, "%s\n", arg)
- }
- cmdlineFile, err := newNetsvcFile(cmdlineNetsvcName, buf.Bytes())
- if err != nil {
- return err
- }
- files = append(files, cmdlineFile)
- }
-
- for _, img := range imgs {
- for _, arg := range bootArgs(img) {
- name, ok := bootserverArgToName[arg]
- if !ok {
- return fmt.Errorf("unrecognized bootserver argument found: %s", arg)
- }
- imgFile, err := openNetsvcFile(name, img.Path)
- if err != nil {
- return err
- }
- defer imgFile.close()
- files = append(files, imgFile)
- }
- }
-
- if bootMode == ModePave && len(signers) > 0 {
- var authorizedKeys []byte
- for _, s := range signers {
- authorizedKey := ssh.MarshalAuthorizedKey(s.PublicKey())
- authorizedKeys = append(authorizedKeys, authorizedKey...)
- }
- authorizedKeysFile, err := newNetsvcFile(authorizedKeysNetsvcName, authorizedKeys)
- if err != nil {
- return err
- }
- files = append(files, authorizedKeysFile)
- }
-
- sort.Slice(files, func(i, j int) bool {
- return files[i].index < files[j].index
- })
-
- if len(files) == 0 {
- return errors.New("no files to transfer")
- }
- if err := transfer(ctx, addr, files); err != nil {
- return err
- }
-
- // If we do not load a kernel into RAM, then we reboot back into the first kernel
- // partition; else we boot directly from RAM.
- // TODO(ZX-2069): Eventually, no such kernel should be present.
- hasRAMKernel := files[len(files)-1].name == kernelNetsvcName
- n := netboot.NewClient(time.Second)
- if hasRAMKernel {
- // Try to send the boot command a few times, as there's no ack, so it's
- // not possible to tell if it's successfully booted or not.
- for i := 0; i < 5; i++ {
- n.Boot(addr)
- }
- }
- return n.Reboot(addr)
-}
-
-// BootZedbootShim extracts the Zircon-R image that is intended to be paved to the device
-// and mexec()'s it, it is intended to be executed before calling Boot().
-// This function serves to emulate zero-state, and will eventually be superseded by an
-// infra implementation.
-func BootZedbootShim(ctx context.Context, addr *net.UDPAddr, imgs []build.Image) error {
- netsvcName := kernelNetsvcName
- zirconRImg := build.Image{}
- for _, img := range imgs {
- for _, arg := range img.PaveArgs {
- // Find name by bootserver arg to ensure we are extracting the correct zircon-r.
- // There may be more than one in images.json but only one should be passed to
- // the bootserver for paving.
- name, ok := bootserverArgToName[arg]
- if !ok {
- return fmt.Errorf("unrecognized bootserver argument found: %q", arg)
- }
- if name == zirconRNetsvcName {
- zirconRImg = img
- // Signed ZBIs cannot be mexec()'d, so pave them to A and boot instead.
- if strings.HasSuffix(img.Name, ".signed") {
- netsvcName = zirconANetsvcName
- }
- break
- }
- }
- if zirconRImg.Name != "" {
- break
- }
- }
-
- if zirconRImg.Name != "" {
- imgFile, err := openNetsvcFile(netsvcName, zirconRImg.Path)
- if err != nil {
- return err
- }
- defer imgFile.close()
- if err := transfer(ctx, addr, []*netsvcFile{imgFile}); err != nil {
- return err
- }
- n := netboot.NewClient(time.Second)
- if netsvcName == kernelNetsvcName {
- return n.Boot(addr)
- }
- return n.Reboot(addr)
- }
-
- return fmt.Errorf("no zircon-r image found in: %v", imgs)
-}
-
-// A file to send to netsvc.
-type netsvcFile struct {
- name string
- reader io.Reader
- size int64
- index int
-}
-
-func (f netsvcFile) close() error {
- closer, ok := (f.reader).(io.Closer)
- if ok {
- return closer.Close()
- }
- return nil
-}
-
-func openNetsvcFile(name, path string) (*netsvcFile, error) {
- idx, ok := transferOrder[name]
- if !ok {
- return nil, fmt.Errorf("unrecognized name: %s", name)
- }
- fd, err := os.Open(path)
- if err != nil {
- fd.Close()
- return nil, err
- }
- fi, err := fd.Stat()
- if err != nil {
- fd.Close()
- return nil, err
- }
- return &netsvcFile{name: name, reader: fd, size: fi.Size(), index: idx}, nil
-}
-
-func newNetsvcFile(name string, buf []byte) (*netsvcFile, error) {
- idx, ok := transferOrder[name]
- if !ok {
- return nil, fmt.Errorf("unrecognized name: %s", name)
- }
- return &netsvcFile{
- reader: bytes.NewReader(buf),
- size: int64(len(buf)),
- name: name,
- index: idx,
- }, nil
-}
-
-// Transfers files over TFTP to a node at a given address.
-func transfer(ctx context.Context, addr *net.UDPAddr, files []*netsvcFile) error {
- t := tftp.NewClient()
- tftpAddr := &net.UDPAddr{
- IP: addr.IP,
- Port: tftp.ClientPort,
- Zone: addr.Zone,
- }
-
- // Attempt the whole process of sending every file over and retry on failure of any file.
- // This behavior more closely aligns with that of the bootserver.
- return retry.Retry(ctx, retry.WithMaxRetries(retry.NewConstantBackoff(time.Second), 20), func() error {
- for _, f := range files {
- // Attempt to send a file. If the server tells us we need to wait, then try
- // again as long as it keeps telling us this. ErrShouldWait implies the server
- // is still responding and will eventually be able to handle our request.
- for {
- select {
- case <-ctx.Done():
- return nil
- default:
- }
- log.Printf("attempting to send %s...\n", f.name)
- reader, err := t.Send(tftpAddr, f.name, f.size)
- switch {
- case err == tftp.ErrShouldWait:
- // The target is busy, so let's sleep for a bit before
- // trying again, otherwise we'll be wasting cycles and
- // printing too often.
- log.Printf("target is busy, retrying in one second\n")
- select {
- case <-time.After(time.Second):
- continue
- }
- case err != nil:
- log.Printf("failed to send %s; starting from the top: %v\n", f.name, err)
- return err
- }
- if _, err := reader.ReadFrom(f.reader); err != nil {
- log.Printf("unable to read from %s; retrying: %v\n", f.name, err)
- return err
- }
- break
- }
- log.Printf("done\n")
- }
- return nil
- }, nil)
-}
diff --git a/botanist/cmd/main.go b/botanist/cmd/main.go
deleted file mode 100644
index e749d42..0000000
--- a/botanist/cmd/main.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2017 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 (
- "context"
- "flag"
- "os"
- "syscall"
-
- "github.com/google/subcommands"
-
- "go.fuchsia.dev/tools/lib/color"
- "go.fuchsia.dev/tools/lib/command"
- "go.fuchsia.dev/tools/lib/logger"
-)
-
-var (
- colors color.EnableColor
- level logger.LogLevel
-)
-
-func init() {
- colors = color.ColorAuto
- level = logger.InfoLevel
-
- 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")
-}
-
-func main() {
- subcommands.Register(subcommands.HelpCommand(), "")
- subcommands.Register(subcommands.CommandsCommand(), "")
- subcommands.Register(subcommands.FlagsCommand(), "")
- subcommands.Register(&ZedbootCommand{}, "")
- subcommands.Register(&QEMUCommand{}, "")
- subcommands.Register(&RunCommand{}, "")
-
- flag.Parse()
-
- log := logger.NewLogger(level, color.NewColor(colors), os.Stdout, os.Stderr, "botanist ")
- ctx := logger.WithLogger(context.Background(), log)
- ctx = command.CancelOnSignals(ctx, syscall.SIGTERM)
- os.Exit(int(subcommands.Execute(ctx)))
-}
diff --git a/botanist/cmd/qemu.go b/botanist/cmd/qemu.go
deleted file mode 100644
index 9ef6850..0000000
--- a/botanist/cmd/qemu.go
+++ /dev/null
@@ -1,115 +0,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 (
- "context"
- "flag"
- "fmt"
-
- "github.com/google/subcommands"
- "go.fuchsia.dev/tools/botanist/target"
- "go.fuchsia.dev/tools/build/api"
- "go.fuchsia.dev/tools/lib/logger"
-)
-
-// QEMUBinPrefix is the prefix of the QEMU binary name, which is of the form
-// qemu-system-<QEMU arch suffix>.
-const qemuBinPrefix = "qemu-system"
-
-// QEMUCommand is a Command implementation for running the testing workflow on an emulated
-// target within QEMU.
-type QEMUCommand struct {
- // ImageManifest is the path an image manifest specifying a QEMU kernel, a zircon
- // kernel, and block image to back QEMU storage.
- imageManifest string
-
- // QEMUBinDir is a path to a directory of QEMU binaries.
- qemuBinDir string
-
- // MinFSImage is a path to a minFS image to be mounted on target, and to where test
- // results will be written.
- minFSImage string
-
- // MinFSBlkDevPCIAddr is a minFS block device PCI address.
- minFSBlkDevPCIAddr string
-
- // TargetArch is the target architecture to be emulated within QEMU
- targetArch string
-
- // EnableKVM dictates whether to enable KVM.
- enableKVM bool
-
- // CPU is the number of processors to emulate.
- cpu int
-
- // Memory is the amount of memory (in MB) to provide.
- memory int
-}
-
-func (*QEMUCommand) Name() string {
- return "qemu"
-}
-
-func (*QEMUCommand) Usage() string {
- return "qemu [flags...] [kernel command-line arguments...]\n\nflags:\n"
-}
-
-func (*QEMUCommand) Synopsis() string {
- return "boots a QEMU device with a MinFS image as a block device."
-}
-
-func (cmd *QEMUCommand) SetFlags(f *flag.FlagSet) {
- f.StringVar(&cmd.imageManifest, "images", "", "path to an image manifest")
- f.StringVar(&cmd.qemuBinDir, "qemu-dir", "", "")
- f.StringVar(&cmd.minFSImage, "minfs", "", "path to minFS image")
- f.StringVar(&cmd.minFSBlkDevPCIAddr, "pci-addr", "06.0", "minFS block device PCI address")
- f.StringVar(&cmd.targetArch, "arch", "", "target architecture (x64 or arm64)")
- f.BoolVar(&cmd.enableKVM, "use-kvm", false, "whether to enable KVM")
- f.IntVar(&cmd.cpu, "cpu", 4, "number of processors to emulate")
- f.IntVar(&cmd.memory, "memory", 4096, "amount of memory (in MB) to provide")
-}
-
-func (cmd *QEMUCommand) execute(ctx context.Context, cmdlineArgs []string) error {
- if cmd.qemuBinDir == "" {
- return fmt.Errorf("-qemu-dir must be set")
- }
-
- imgs, err := build.LoadImages(cmd.imageManifest)
- if err != nil {
- return err
- }
-
- // TODO: pass this directly from a file.
- config := target.QEMUConfig{
- CPU: cmd.cpu,
- Memory: cmd.memory,
- Path: cmd.qemuBinDir,
- Target: cmd.targetArch,
- KVM: cmd.enableKVM,
- UserNetworking: true,
- }
- if cmd.minFSImage != "" {
- config.MinFS = &target.MinFS{
- Image: cmd.minFSImage,
- PCIAddress: cmd.minFSBlkDevPCIAddr,
- }
- }
-
- t := target.NewQEMUTarget(config, target.Options{})
- if err := t.Start(ctx, imgs, cmdlineArgs); err != nil {
- return err
- }
-
- return t.Wait(ctx)
-}
-
-func (cmd *QEMUCommand) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
- if err := cmd.execute(ctx, f.Args()); err != nil {
- logger.Errorf(ctx, "%v\n", err)
- return subcommands.ExitFailure
- }
- return subcommands.ExitSuccess
-}
diff --git a/botanist/cmd/run.go b/botanist/cmd/run.go
deleted file mode 100644
index 7337f5b..0000000
--- a/botanist/cmd/run.go
+++ /dev/null
@@ -1,387 +0,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 (
- "context"
- "encoding/json"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "net"
- "os"
- "path/filepath"
- "sync"
- "time"
-
- "go.fuchsia.dev/tools/botanist"
- "go.fuchsia.dev/tools/botanist/target"
- "go.fuchsia.dev/tools/build/api"
- "go.fuchsia.dev/tools/lib/command"
- "go.fuchsia.dev/tools/lib/logger"
- "go.fuchsia.dev/tools/lib/runner"
- "go.fuchsia.dev/tools/net/sshutil"
-
- "github.com/google/subcommands"
-)
-
-const (
- netstackTimeout time.Duration = 1 * time.Minute
-)
-
-// Target represents a fuchsia instance.
-type Target interface {
- // Nodename returns the name of the target node.
- Nodename() string
-
- // IPv4Addr returns the IPv4 address of the target.
- IPv4Addr() (net.IP, error)
-
- // Serial returns the serial device associated with the target for serial i/o.
- Serial() io.ReadWriteCloser
-
- // SSHKey returns the private key corresponding an authorized SSH key of the target.
- SSHKey() string
-
- // Start starts the target.
- Start(ctx context.Context, images build.Images, args []string) error
-
- // Restart restarts the target.
- Restart(ctx context.Context) error
-
- // Stop stops the target.
- Stop(ctx context.Context) error
-
- // Wait waits for the target to finish running.
- Wait(ctx context.Context) error
-}
-
-// RunCommand is a Command implementation for booting a device and running a
-// given command locally.
-type RunCommand struct {
- // ConfigFile is the path to the target configurations.
- configFile string
-
- // ImageManifests is a list of paths to image manifests (e.g., images.json)
- imageManifests command.StringsFlag
-
- // Netboot tells botanist to netboot (and not to pave).
- netboot bool
-
- // ZirconArgs are kernel command-line arguments to pass on boot.
- zirconArgs command.StringsFlag
-
- // Timeout is the duration allowed for the command to finish execution.
- timeout time.Duration
-
- // CmdStdout is the file to which the command's stdout will be redirected.
- cmdStdout string
-
- // CmdStderr is the file to which the command's stderr will be redirected.
- cmdStderr string
-
- // SysloggerFile, if nonempty, is the file to where the system's logs will be written.
- syslogFile string
-
- // SshKey is the path to a private SSH user key.
- sshKey string
-
- // SerialLogFile, if nonempty, is the file where the system's serial logs will be written.
- serialLogFile string
-}
-
-func (*RunCommand) Name() string {
- return "run"
-}
-
-func (*RunCommand) Usage() string {
- return `
-botanist run [flags...] [command...]
-
-flags:
-`
-}
-
-func (*RunCommand) Synopsis() string {
- return "boots a device and runs a local command"
-}
-
-func (r *RunCommand) SetFlags(f *flag.FlagSet) {
- f.StringVar(&r.configFile, "config", "", "path to file of device config")
- f.Var(&r.imageManifests, "images", "paths to image manifests")
- f.BoolVar(&r.netboot, "netboot", false, "if set, botanist will not pave; but will netboot instead")
- f.Var(&r.zirconArgs, "zircon-args", "kernel command-line arguments")
- f.DurationVar(&r.timeout, "timeout", 10*time.Minute, "duration allowed for the command to finish execution.")
- f.StringVar(&r.cmdStdout, "stdout", "", "file to redirect the command's stdout into; if unspecified, it will be redirected to the process' stdout")
- f.StringVar(&r.cmdStderr, "stderr", "", "file to redirect the command's stderr into; if unspecified, it will be redirected to the process' stderr")
- f.StringVar(&r.syslogFile, "syslog", "", "file to write the systems logs to")
- f.StringVar(&r.sshKey, "ssh", "", "file containing a private SSH user key; if not provided, a private key will be generated.")
- f.StringVar(&r.serialLogFile, "serial-log", "", "file to write the serial logs to.")
-}
-
-func (r *RunCommand) runCmd(ctx context.Context, args []string, t Target) error {
- nodename := t.Nodename()
- ip, err := t.IPv4Addr()
- if err == nil {
- logger.Infof(ctx, "IPv4 address of %s found: %s", nodename, ip)
- } else {
- logger.Errorf(ctx, "could not resolve IPv4 address of %s: %v", nodename, err)
- }
-
- env := append(
- os.Environ(),
- fmt.Sprintf("FUCHSIA_NODENAME=%s", nodename),
- fmt.Sprintf("FUCHSIA_IPV4_ADDR=%v", ip),
- fmt.Sprintf("FUCHSIA_SSH_KEY=%s", t.SSHKey()),
- )
-
- ctx, cancel := context.WithTimeout(ctx, r.timeout)
- defer cancel()
-
- stdout := os.Stdout
- if r.cmdStdout != "" {
- f, err := os.Create(r.cmdStdout)
- if err != nil {
- return err
- }
- defer f.Close()
- stdout = f
- }
- stderr := os.Stderr
- if r.cmdStderr != "" {
- f, err := os.Create(r.cmdStderr)
- if err != nil {
- return err
- }
- defer f.Close()
- stderr = f
- }
-
- runner := runner.SubprocessRunner{
- Env: env,
- }
- if err := runner.Run(ctx, args, stdout, stderr); err != nil {
- if ctx.Err() != nil {
- return fmt.Errorf("command timed out after %v", r.timeout)
- }
- return err
- }
- return nil
-}
-
-func getIndexedFilename(filename string, index int) string {
- ext := filepath.Ext(filename)
- name := filename[:len(filename)-len(ext)]
- return fmt.Sprintf("%s-%d%s", name, index, ext)
-}
-
-func (r *RunCommand) execute(ctx context.Context, args []string) error {
- imgs, err := build.LoadImages(r.imageManifests...)
- if err != nil {
- return fmt.Errorf("failed to load images: %v", err)
- }
-
- opts := target.Options{
- Netboot: r.netboot,
- SSHKey: r.sshKey,
- }
-
- data, err := ioutil.ReadFile(r.configFile)
- if err != nil {
- return fmt.Errorf("could not open config file: %v", err)
- }
- var objs []json.RawMessage
- if err := json.Unmarshal(data, &objs); err != nil {
- return fmt.Errorf("could not unmarshal config file as a JSON list: %v", err)
- }
-
- var targets []Target
- for _, obj := range objs {
- t, err := DeriveTarget(ctx, obj, opts)
- if err != nil {
- return err
- }
- targets = append(targets, t)
- }
-
- ctx, cancel := context.WithCancel(ctx)
- defer cancel()
- errs := make(chan error)
- var wg sync.WaitGroup
-
- for i, t := range targets {
- defer func() {
- logger.Debugf(ctx, "stopping or rebooting the node %q\n", t.Nodename())
- if err := t.Stop(ctx); err == target.ErrUnimplemented {
- t.Restart(ctx)
- }
- }()
-
- var syslogFile, serialLogFile string
- if r.syslogFile != "" {
- syslogFile = r.syslogFile
- if len(targets) > 1 {
- syslogFile = getIndexedFilename(r.syslogFile, i)
- }
- }
- if r.serialLogFile != "" {
- serialLogFile = r.serialLogFile
- if len(targets) > 1 {
- serialLogFile = getIndexedFilename(r.serialLogFile, i)
- }
- }
-
- wg.Add(1)
- go func(t Target, syslogFile string, serialLogFile string) {
- defer wg.Done()
- var syslog io.Writer
- if syslogFile != "" {
- syslog, err := os.Create(syslogFile)
- if err != nil {
- errs <- err
- return
- }
- defer syslog.Close()
- }
-
- zirconArgs := r.zirconArgs
- if t.Serial() != nil {
- if serialLogFile != "" {
- serialLog, err := os.Create(serialLogFile)
- if err != nil {
- errs <- err
- return
- }
- defer serialLog.Close()
-
- // Here we invoke the `dlog` command over serial to tail the existing log buffer into the
- // output file. This should give us everything since Zedboot boot, and new messages should
- // be written to directly to the serial port without needing to tail with `dlog -f`.
- if _, err = io.WriteString(t.Serial(), "\ndlog\n"); err != nil {
- logger.Errorf(ctx, "failed to tail zedboot dlog: %v", err)
- }
-
- go func(t Target) {
- for {
- _, err := io.Copy(serialLog, t.Serial())
- if err != nil && err != io.EOF {
- logger.Errorf(ctx, "failed to write serial log: %v", err)
- return
- }
- }
- }(t)
- zirconArgs = append(zirconArgs, "kernel.bypass-debuglog=true")
- }
- // Modify the zirconArgs passed to the kernel on boot to enable serial on x64.
- // arm64 devices should already be enabling kernel.serial at compile time.
- zirconArgs = append(zirconArgs, "kernel.serial=legacy")
- }
-
- if err := t.Start(ctx, imgs, zirconArgs); err != nil {
- errs <- err
- return
- }
- nodename := t.Nodename()
- // If having paved, SSH in and stream syslogs back to a file sink.
- if !r.netboot && syslog != nil {
- p, err := ioutil.ReadFile(t.SSHKey())
- if err != nil {
- errs <- err
- return
- }
- config, err := sshutil.DefaultSSHConfig(p)
- if err != nil {
- errs <- err
- return
- }
- client, err := sshutil.ConnectToNode(ctx, nodename, config)
- if err != nil {
- errs <- err
- return
- }
- syslogger, err := botanist.NewSyslogger(client)
- if err != nil {
- errs <- err
- return
- }
- go func() {
- syslogger.Stream(ctx, syslog)
- syslogger.Close()
- }()
- }
- }(t, syslogFile, serialLogFile)
- }
- // Wait for all targets to finish starting.
- wg.Wait()
- // We can close the channel on the receiver end since we wait for all target goroutines to finish.
- close(errs)
- err, ok := <-errs
- if ok {
- return err
- }
-
- // Since errs was closed, reset it to reuse it again.
- errs = make(chan error)
-
- go func() {
- // Target doesn't matter for multi-device host tests. Just use first one.
- errs <- r.runCmd(ctx, args, targets[0])
- }()
-
- for _, t := range targets {
- go func(t Target) {
- if err := t.Wait(ctx); err != nil && err != target.ErrUnimplemented {
- errs <- err
- }
- }(t)
- }
-
- select {
- case err := <-errs:
- return err
- case <-ctx.Done():
- }
- return nil
-}
-
-func (r *RunCommand) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
- args := f.Args()
- if len(args) == 0 {
- return subcommands.ExitUsageError
- }
- if err := r.execute(ctx, args); err != nil {
- logger.Errorf(ctx, "%v\n", err)
- return subcommands.ExitFailure
- }
- return subcommands.ExitSuccess
-}
-
-func DeriveTarget(ctx context.Context, obj []byte, opts target.Options) (Target, error) {
- type typed struct {
- Type string `json:"type"`
- }
- var x typed
-
- if err := json.Unmarshal(obj, &x); err != nil {
- return nil, fmt.Errorf("object in list has no \"type\" field: %v", err)
- }
- switch x.Type {
- case "qemu":
- var cfg target.QEMUConfig
- if err := json.Unmarshal(obj, &cfg); err != nil {
- return nil, fmt.Errorf("invalid QEMU config found: %v", err)
- }
- return target.NewQEMUTarget(cfg, opts), nil
- case "device":
- var cfg target.DeviceConfig
- if err := json.Unmarshal(obj, &cfg); err != nil {
- return nil, fmt.Errorf("invalid device config found: %v", err)
- }
- t, err := target.NewDeviceTarget(ctx, cfg, opts)
- return t, err
- default:
- return nil, fmt.Errorf("unknown type found: %q", x.Type)
- }
-}
diff --git a/botanist/cmd/zedboot.go b/botanist/cmd/zedboot.go
deleted file mode 100644
index 2a8bcee..0000000
--- a/botanist/cmd/zedboot.go
+++ /dev/null
@@ -1,162 +0,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 (
- "context"
- "flag"
- "fmt"
- "io/ioutil"
- "net"
- "strings"
- "time"
-
- "go.fuchsia.dev/tools/botanist/target"
- "go.fuchsia.dev/tools/build/api"
- "go.fuchsia.dev/tools/lib/command"
- "go.fuchsia.dev/tools/lib/logger"
- "go.fuchsia.dev/tools/net/netutil"
- "go.fuchsia.dev/tools/testing/runtests"
-
- "github.com/google/subcommands"
-)
-
-// ZedbootCommand is a Command implementation for running the testing workflow on a device
-// that boots with Zedboot.
-type ZedbootCommand struct {
- // ImageManifests is a list of paths to image manifests (e.g., images.json)
- imageManifests command.StringsFlag
-
- // Netboot tells botanist to netboot (and not to pave).
- netboot bool
-
- // ConfigFile is the path to a file containing the target config.
- configFile string
-
- // TestResultsDir is the directory on target to where test results will be written.
- testResultsDir string
-
- // SummaryFilename is the name of the test summary JSON file to be written to
- // testResultsDir.
- summaryFilename string
-
- // FilePollInterval is the duration waited between checking for test summary file
- // on the target to be written.
- filePollInterval time.Duration
-
- // OutputArchive is a path on host to where the tarball containing the test results
- // will be output.
- outputArchive string
-
- // CmdlineFile is the path to a file of additional kernel command-line arguments.
- cmdlineFile string
-}
-
-func (*ZedbootCommand) Name() string {
- return "zedboot"
-}
-
-func (*ZedbootCommand) Usage() string {
- return "zedboot [flags...] [kernel command-line arguments...]\n\nflags:\n"
-}
-
-func (*ZedbootCommand) Synopsis() string {
- return "boots a Zedboot device and collects test results"
-}
-
-func (cmd *ZedbootCommand) SetFlags(f *flag.FlagSet) {
- f.Var(&cmd.imageManifests, "images", "paths to image manifests")
- f.BoolVar(&cmd.netboot, "netboot", false, "if set, botanist will not pave; but will netboot instead")
- f.StringVar(&cmd.testResultsDir, "results-dir", "/test", "path on target to where test results will be written")
- f.StringVar(&cmd.outputArchive, "out", "output.tar", "path on host to output tarball of test results")
- f.StringVar(&cmd.summaryFilename, "summary-name", runtests.TestSummaryFilename, "name of the file in the test directory")
- f.DurationVar(&cmd.filePollInterval, "poll-interval", 1*time.Minute, "time between checking for summary.json on the target")
- f.StringVar(&cmd.configFile, "config", "", "path to file of device config")
- f.StringVar(&cmd.cmdlineFile, "cmdline-file", "", "path to a file containing additional kernel command-line arguments")
-}
-
-func (cmd *ZedbootCommand) runTests(ctx context.Context, imgs build.Images, addr *net.UDPAddr, cmdlineArgs []string) error {
- logger.Debugf(ctx, "waiting for %q\n", cmd.summaryFilename)
- return runtests.PollForSummary(ctx, addr, cmd.summaryFilename, cmd.testResultsDir, cmd.outputArchive, cmd.filePollInterval)
-}
-
-func (cmd *ZedbootCommand) execute(ctx context.Context, cmdlineArgs []string) error {
- configs, err := target.LoadDeviceConfigs(cmd.configFile)
-
- if err != nil {
- return fmt.Errorf("failed to load target config file %q", cmd.configFile)
- }
- opts := target.Options{
- Netboot: cmd.netboot,
- }
-
- var devices []*target.DeviceTarget
- for _, config := range configs {
- device, err := target.NewDeviceTarget(ctx, config, opts)
- if err != nil {
- return err
- }
- devices = append(devices, device)
- }
-
- for _, device := range devices {
- defer device.Restart(ctx)
- }
-
- imgs, err := build.LoadImages(cmd.imageManifests...)
- if err != nil {
- return err
- }
-
- ctx, cancel := context.WithCancel(ctx)
- defer cancel()
- errs := make(chan error)
-
- for _, device := range devices {
- go func(device *target.DeviceTarget) {
- if err := device.Start(ctx, imgs, cmdlineArgs); err != nil {
- errs <- err
- }
- }(device)
- }
- go func() {
- addr, err := netutil.GetNodeAddress(ctx, devices[0].Nodename(), false)
- if err != nil {
- errs <- err
- return
- }
- errs <- cmd.runTests(ctx, imgs, addr, cmdlineArgs)
- }()
-
- select {
- case err := <-errs:
- return err
- case <-ctx.Done():
- }
-
- return nil
-}
-
-func (cmd *ZedbootCommand) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
- configFlag := f.Lookup("config")
- logger.Debugf(ctx, "config flag: %v\n", configFlag.Value)
-
- // Aggregate command-line arguments.
- cmdlineArgs := f.Args()
- if cmd.cmdlineFile != "" {
- args, err := ioutil.ReadFile(cmd.cmdlineFile)
- if err != nil {
- logger.Errorf(ctx, "failed to read command-line args file %q: %v\n", cmd.cmdlineFile, err)
- return subcommands.ExitFailure
- }
- cmdlineArgs = append(cmdlineArgs, strings.Split(string(args), "\n")...)
- }
-
- if err := cmd.execute(ctx, cmdlineArgs); err != nil {
- logger.Errorf(ctx, "%v\n", err)
- return subcommands.ExitFailure
- }
-
- return subcommands.ExitSuccess
-}
diff --git a/botanist/fileutil.go b/botanist/fileutil.go
deleted file mode 100644
index 4950d4c..0000000
--- a/botanist/fileutil.go
+++ /dev/null
@@ -1,32 +0,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 botanist
-
-import (
- "archive/tar"
- "fmt"
- "net"
-
- "go.fuchsia.dev/tools/net/tftp"
-)
-
-// FetchAndArchiveFile fetches a remote file via TFTP from a given node, and
-// writes it an archive.
-func FetchAndArchiveFile(t *tftp.Client, addr *net.UDPAddr, tw *tar.Writer, path, name string) error {
- receiver, err := t.Receive(addr, path)
- if err != nil {
- return fmt.Errorf("failed to receive file %s: %v\n", path, err)
- }
- hdr := &tar.Header{
- Name: name,
- Size: receiver.(tftp.Session).Size(),
- Mode: 0666,
- }
- if err := tw.WriteHeader(hdr); err != nil {
- return err
- }
- _, err = receiver.WriteTo(tw)
- return err
-}
diff --git a/botanist/ip.go b/botanist/ip.go
deleted file mode 100644
index 30fcd46..0000000
--- a/botanist/ip.go
+++ /dev/null
@@ -1,76 +0,0 @@
-// 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 botanist
-
-import (
- "context"
- "fmt"
- "net"
- "time"
-
- "go.fuchsia.dev/tools/lib/logger"
- "go.fuchsia.dev/tools/lib/retry"
- "go.fuchsia.dev/tools/net/mdns"
-)
-
-// Interval at which ResolveIP will wait for a response to a question packet.
-const mDNSTimeout time.Duration = 2 * time.Second
-
-func getLocalDomain(nodename string) string {
- return nodename + ".local"
-}
-
-// ResolveIP returns the IPv4 address of a fuchsia node via mDNS.
-//
-// TODO(joshuaseaton): Refactor dev_finder to share 'resolve' logic with botanist.
-func ResolveIPv4(ctx context.Context, nodename string, timeout time.Duration) (net.IP, error) {
- m := mdns.NewMDNS()
- m.EnableIPv4()
- out := make(chan net.IP)
- domain := getLocalDomain(nodename)
- m.AddHandler(func(iface net.Interface, addr net.Addr, packet mdns.Packet) {
- for _, a := range packet.Answers {
- if a.Class == mdns.IN && a.Type == mdns.A && a.Domain == domain {
- out <- net.IP(a.Data)
- return
- }
- }
- })
- m.AddWarningHandler(func(addr net.Addr, err error) {
- logger.Infof(ctx, "from: %v; warn: %v", addr, err)
- })
- errs := make(chan error)
- m.AddErrorHandler(func(err error) {
- errs <- err
- })
-
- ctx, cancel := context.WithTimeout(ctx, timeout)
- defer cancel()
-
- if err := m.Start(ctx, mdns.DefaultPort); err != nil {
- return nil, fmt.Errorf("could not start mDNS client: %v", err)
- }
-
- // Send question packets to the mDNS server at intervals of mDNSTimeout for a total of
- // |timeout|; retry, as it takes time for the netstack and server to be brought up.
- var ip net.IP
- var err error
- err = retry.Retry(ctx, &retry.ZeroBackoff{}, func() error {
- m.Send(mdns.QuestionPacket(domain))
- ctx, cancel := context.WithTimeout(context.Background(), mDNSTimeout)
- defer cancel()
-
- select {
- case <-ctx.Done():
- return fmt.Errorf("timeout")
- case err = <-errs:
- return err
- case ip = <-out:
- return nil
- }
- }, nil)
-
- return ip, err
-}
diff --git a/botanist/power/amt/amt.go b/botanist/power/amt/amt.go
deleted file mode 100644
index fb20b5e..0000000
--- a/botanist/power/amt/amt.go
+++ /dev/null
@@ -1,98 +0,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 amt
-
-import (
- "fmt"
- "io/ioutil"
- "net/http"
- "net/url"
- "strings"
-
- "github.com/google/uuid"
- "go.fuchsia.dev/tools/net/digest"
-)
-
-const (
- // https://software.intel.com/en-us/node/645995
- PowerStateOn = 2
- PowerStateLightSleep = 3
- PowerStateDeepSleep = 4
- PowerStatePowerCycleSoft = 5
- PowerStateOffHard = 6
- PowerStateHibernate = 7
- PowerStateOffSoft = 8
- PowerStatePowerCycleHard = 9
- PowerStateMasterBusReset = 10
-)
-
-// Printf string with placeholders for destination uri, message uuid
-const payloadTmpl = `
-<?xml version="1.0" encoding="UTF-8"?>
-<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsman="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns:pms="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService">
-<s:Header>
- <wsa:Action s:mustUnderstand="true">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService/RequestPowerStateChange</wsa:Action>
- <wsa:To s:mustUnderstand="true">%s</wsa:To>
- <wsman:ResourceURI s:mustUnderstand="true">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService</wsman:ResourceURI>
- <wsa:MessageID s:mustUnderstand="true">uuid:%s</wsa:MessageID>
- <wsa:ReplyTo><wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address></wsa:ReplyTo>
- <wsman:SelectorSet>
- <wsman:Selector Name="Name">Intel(r) AMT Power Management Service</wsman:Selector>
- <wsman:Selector Name="SystemName">Intel(r) AMT</wsman:Selector>
- <wsman:Selector Name="CreationClassName">CIM_PowerManagementService</wsman:Selector>
- <wsman:Selector Name="SystemCreationClassName">CIM_ComputerSystem</wsman:Selector>
- </wsman:SelectorSet>
-</s:Header>
-<s:Body>
- <pms:RequestPowerStateChange_INPUT>
- <pms:PowerState>%d</pms:PowerState>
- <pms:ManagedElement>
- <wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
- <wsa:ReferenceParameters>
- <wsman:ResourceURI>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem</wsman:ResourceURI>
- <wsman:SelectorSet>
- <wsman:Selector Name="Name">ManagedSystem</wsman:Selector>
- <wsman:Selector Name="CreationClassName">CIM_ComputerSystem</wsman:Selector>
- </wsman:SelectorSet>
- </wsa:ReferenceParameters>
- </pms:ManagedElement>
- </pms:RequestPowerStateChange_INPUT>
-</s:Body>
-</s:Envelope>
-`
-
-// Reboot sends a Master Bus Reset to an AMT compatible device at host:port.
-func Reboot(host, username, password string) error {
- // AMT over http always uses port 16992
- uri, err := url.Parse(fmt.Sprintf("http://%s:16992/wsman", host))
- if err != nil {
- return err
- }
- // Generate MessageID
- uuid := uuid.New()
- payload := fmt.Sprintf(payloadTmpl, uri.String(), uuid, PowerStatePowerCycleSoft)
-
- t := digest.NewTransport(username, password)
- req, err := http.NewRequest("POST", uri.String(), strings.NewReader(payload))
- if err != nil {
- return err
- }
- res, err := t.RoundTrip(req)
- if err != nil {
- return err
- }
- defer res.Body.Close()
-
- body, err := ioutil.ReadAll(res.Body)
- if err != nil {
- return err
- }
- returnValue := string(strings.Split(string(body), "ReturnValue>")[1][0])
- if returnValue != "0" {
- return fmt.Errorf("amt reboot ReturnValue=%s", returnValue)
- }
-
- return nil
-}
diff --git a/botanist/power/power.go b/botanist/power/power.go
deleted file mode 100644
index a8aa3bc..0000000
--- a/botanist/power/power.go
+++ /dev/null
@@ -1,143 +0,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 power
-
-import (
- "context"
- "io"
-
- "go.fuchsia.dev/tools/botanist/power/amt"
- "go.fuchsia.dev/tools/botanist/power/wol"
- "go.fuchsia.dev/tools/lib/logger"
- "go.fuchsia.dev/tools/net/sshutil"
-
- "golang.org/x/crypto/ssh"
-)
-
-// TODO(IN-977) Clean this up per suggestions in go/fxr/251550
-
-const (
- // Controller machines use 192.168.42.1/24 for swarming bots
- // This will broadcast to that entire subnet.
- botBroadcastAddr = "192.168.42.255:9"
-
- // Controller machines have multiple interfaces, currently
- // 'eno2' is used for swarming bots.
- botInterface = "eno2"
-)
-
-// Client represents a power management configuration for a particular device.
-type Client struct {
- // Type is the type of manager to use.
- Type string `json:"type"`
-
- // Host is the network hostname of the manager, e.g. fuchsia-tests-pdu-001.
- Host string `json:"host"`
-
- // HostHwAddr is the ethernet MAC address of the manager, e.g. 10:10:10:10:10:10
- HostMACAddr string `json:"host_mac_addr"`
-
- // Username is the username used to log in to the manager.
- Username string `json:"username"`
-
- // Password is the password used to log in to the manager..
- Password string `json:"password"`
-}
-
-type Rebooter interface {
- reboot() error
-}
-
-type SshRebooter struct {
- nodename string
- signers []ssh.Signer
-}
-
-type SerialRebooter struct {
- serial io.ReadWriter
-}
-
-// RebootDevice attempts to reboot the specified device into recovery, and
-// additionally uses the given configuration to reboot the device if specified.
-func (c Client) RebootDevice(signers []ssh.Signer, nodename string, serial io.ReadWriter) error {
- var rebooter Rebooter
- if serial != nil {
- rebooter = NewSerialRebooter(serial)
- } else {
- rebooter = NewSSHRebooter(nodename, signers)
- }
- // Always attempt to soft reboot the device to recovery.
- err := rebooter.reboot()
- if err != nil {
- logger.Warningf(context.Background(), "soft reboot failed: %v", err)
- }
-
- // Hard reboot the device if specified in the config.
- switch c.Type {
- case "amt":
- return amt.Reboot(c.Host, c.Username, c.Password)
- case "wol":
- return wol.Reboot(botBroadcastAddr, botInterface, c.HostMACAddr)
- default:
- return err
- }
-}
-
-func NewSerialRebooter(serial io.ReadWriter) *SerialRebooter {
- return &SerialRebooter{
- serial: serial,
- }
-}
-
-func NewSSHRebooter(nodename string, signers []ssh.Signer) *SshRebooter {
- return &SshRebooter{
- nodename: nodename,
- signers: signers,
- }
-}
-
-func (s *SerialRebooter) reboot() error {
- _, err := io.WriteString(s.serial, "\ndm reboot-recovery\n")
- return err
-}
-
-func (s *SshRebooter) reboot() error {
- config, err := sshutil.DefaultSSHConfigFromSigners(s.signers...)
- if err != nil {
- return err
- }
-
- ctx := context.Background()
- client, err := sshutil.ConnectToNode(ctx, s.nodename, config)
- if err != nil {
- return err
- }
-
- defer client.Close()
-
- session, err := client.NewSession()
- if err != nil {
- return err
- }
-
- defer session.Close()
-
- // Invoke `dm reboot-recovery` with a 2 second delay in the background, then exit the SSH shell.
- // This prevents the SSH connection from hanging waiting for `dm reboot-recovery` to return.
- err = session.Start("{ sleep 2; dm reboot-recovery; } >/dev/null & exit")
- if err != nil {
- return err
- }
-
- done := make(chan error)
- go func() {
- done <- session.Wait()
- }()
-
- select {
- case err := <-done:
- return err
- }
-}
diff --git a/botanist/power/wol/wol.go b/botanist/power/wol/wol.go
deleted file mode 100644
index f05d503..0000000
--- a/botanist/power/wol/wol.go
+++ /dev/null
@@ -1,107 +0,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 wol
-
-import (
- "errors"
- "fmt"
- "net"
- "regexp"
- "time"
-)
-
-var (
- macAddrRegex = regexp.MustCompile(`(?i)^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$`)
- // Magic Packet header is 0xFF repeated 6 times.
- magicPacketHeader = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
-)
-
-const (
- magicPacketLength = 102
-)
-
-// Reboot sends a WakeOnLAN magic packet {magicPacketHeader + macAddr x 16}
-// using the specified network interface to the broadcast address
-func Reboot(broadcastAddr, interfaceName, macAddr string) error {
- if !macAddrRegex.Match([]byte(macAddr)) {
- return fmt.Errorf("Invalid MAC: %s", macAddr)
- }
-
- remoteHwAddr, err := net.ParseMAC(macAddr)
- if err != nil {
- return err
- }
-
- localAddr, err := getUDPAddrFromIFace(interfaceName)
- if err != nil {
- return err
- }
- remoteAddr, err := net.ResolveUDPAddr("udp", broadcastAddr)
- if err != nil {
- return err
- }
-
- return sendMagicPacket(localAddr, remoteAddr, remoteHwAddr)
-}
-
-func getUDPAddrFromIFace(ifaceName string) (*net.UDPAddr, error) {
- iface, err := net.InterfaceByName(ifaceName)
- if err != nil {
- return nil, err
- }
-
- addrs, err := iface.Addrs()
- if err != nil {
- return nil, err
- }
-
- for _, addr := range addrs {
- if ipAddr, ok := addr.(*net.IPNet); ok {
- // Need an IPv4, non-loopback address to send on
- if !ipAddr.IP.IsLoopback() && ipAddr.IP.To4() != nil {
- return &net.UDPAddr{
- IP: ipAddr.IP,
- }, nil
- }
- }
- }
-
- return nil, errors.New("No UDPAddr found on interface")
-}
-
-func sendMagicPacket(localAddr, remoteAddr *net.UDPAddr, remoteHwAddr net.HardwareAddr) error {
- packet := magicPacketHeader
- for i := 0; i < 16; i++ {
- packet = append(packet, remoteHwAddr...)
- }
-
- if len(packet) != magicPacketLength {
- return fmt.Errorf("Wake-On-LAN packet incorrect length: %d", len(packet))
- }
-
- conn, err := net.DialUDP("udp", localAddr, remoteAddr)
- if err != nil {
- return err
- }
- defer conn.Close()
-
- // Attempt to send the Magic Packet TEN times in a row. The UDP packet sometimes
- // does not make it to the DUT and this is the simplest way to increase the chance
- // the device reboots.
- for i := 0; i < 10; i++ {
- n, err := conn.Write(packet)
-
- if n != magicPacketLength {
- return errors.New("Failed to send correct Wake-On-LAN packet length")
- }
-
- if err != nil {
- return err
- }
- time.Sleep(1 * time.Second)
- }
-
- return nil
-}
diff --git a/botanist/syslog.go b/botanist/syslog.go
deleted file mode 100644
index 35916fe..0000000
--- a/botanist/syslog.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// 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 botanist
-
-import (
- "context"
- "io"
-
- "golang.org/x/crypto/ssh"
-
- "go.fuchsia.dev/tools/lib/runner"
-)
-
-// The program on fuchsia used to stream system logs through a shell, not to be confused
-// with the zircon host tool "loglistener" (no underscore) used to zircon-level logs to host.
-const logListener = "bin/log_listener"
-
-// Syslogger represents an session with a Fuchsia instance through which system logs may be streamed.
-type Syslogger struct {
- r *runner.SSHRunner
-}
-
-// NewSyslogger creates a new Syslogger, given an SSH session with a Fuchsia instance.
-func NewSyslogger(client *ssh.Client) (*Syslogger, error) {
- session, err := client.NewSession()
- if err != nil {
- return nil, err
- }
- return &Syslogger{r: &runner.SSHRunner{Session: session}}, nil
-}
-
-// Stream writes system logs to a given writer; it blocks until the stream is
-// is terminated or a Done is signaled.
-func (s *Syslogger) Stream(ctx context.Context, output io.Writer) error {
- // Note: Fuchsia's log_listener does not write to stderr.
- return s.r.Run(ctx, []string{logListener}, output, nil)
-}
-
-// Close tidies up the system logging session with the corresponding fuchsia instance.
-func (s *Syslogger) Close() error {
- s.r.Session.Signal(ssh.SIGKILL)
- return s.r.Session.Close()
-}
diff --git a/botanist/target/device.go b/botanist/target/device.go
deleted file mode 100644
index 58a71d2..0000000
--- a/botanist/target/device.go
+++ /dev/null
@@ -1,222 +0,0 @@
-// 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 target
-
-import (
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "io/ioutil"
- "net"
- "time"
-
- "go.fuchsia.dev/tools/botanist"
- "go.fuchsia.dev/tools/botanist/power"
- "go.fuchsia.dev/tools/build/api"
- "go.fuchsia.dev/tools/lib/logger"
- "go.fuchsia.dev/tools/net/netboot"
- "go.fuchsia.dev/tools/net/netutil"
- "go.fuchsia.dev/tools/net/serial"
-
- "golang.org/x/crypto/ssh"
-)
-
-const (
- // The duration we allow for the netstack to come up when booting.
- netstackTimeout = 90 * time.Second
-)
-
-// DeviceConfig contains the static properties of a target device.
-type DeviceConfig struct {
- // Network is the network properties of the target.
- Network NetworkProperties `json:"network"`
-
- // Power is the attached power management configuration.
- Power *power.Client `json:"power,omitempty"`
-
- // SSHKeys are the default system keys to be used with the device.
- SSHKeys []string `json:"keys,omitempty"`
-
- // Serial is the path to the device file for serial i/o.
- Serial string `json:"serial,omitempty"`
-}
-
-// NetworkProperties are the static network properties of a target.
-type NetworkProperties struct {
- // Nodename is the hostname of the device that we want to boot on.
- Nodename string `json:"nodename"`
-
- // IPv4Addr is the IPv4 address, if statically given. If not provided, it may be
- // resolved via the netstack's MDNS server.
- IPv4Addr string `json:"ipv4"`
-}
-
-// LoadDeviceConfigs unmarshalls a slice of device configs from a given file.
-func LoadDeviceConfigs(path string) ([]DeviceConfig, error) {
- data, err := ioutil.ReadFile(path)
- if err != nil {
- return nil, fmt.Errorf("failed to read device properties file %q", path)
- }
-
- var configs []DeviceConfig
- if err := json.Unmarshal(data, &configs); err != nil {
- return nil, fmt.Errorf("failed to unmarshal configs: %v", err)
- }
- return configs, nil
-}
-
-// DeviceTarget represents a target device.
-type DeviceTarget struct {
- config DeviceConfig
- opts Options
- signers []ssh.Signer
- serial io.ReadWriteCloser
-}
-
-// NewDeviceTarget returns a new device target with a given configuration.
-func NewDeviceTarget(ctx context.Context, config DeviceConfig, opts Options) (*DeviceTarget, error) {
- // If an SSH key is specified in the options, prepend it the configs list so that it
- // corresponds to the authorized key that would be paved.
- if opts.SSHKey != "" {
- config.SSHKeys = append([]string{opts.SSHKey}, config.SSHKeys...)
- }
- signers, err := parseOutSigners(config.SSHKeys)
- if err != nil {
- return nil, fmt.Errorf("could not parse out signers from private keys: %v", err)
- }
- var s io.ReadWriteCloser
- if config.Serial != "" {
- s, err = serial.Open(config.Serial)
- if err != nil {
- // TODO(IN-????): This should be returned as an error, but we don't want to fail any
- // test runs for misconfigured serial until it is actually required to complete certain
- // tasks.
- logger.Errorf(ctx, "unable to open %s: %v", config.Serial, err)
- }
- }
- return &DeviceTarget{
- config: config,
- opts: opts,
- signers: signers,
- serial: s,
- }, nil
-}
-
-// Nodename returns the name of the node.
-func (t *DeviceTarget) Nodename() string {
- return t.config.Network.Nodename
-}
-
-// IPv4Addr returns the IPv4 address of the node. If not provided in the config, then it
-// will be resolved against the target-side MDNS server.
-func (t *DeviceTarget) IPv4Addr() (net.IP, error) {
- if t.config.Network.IPv4Addr != "" {
- return net.ParseIP(t.config.Network.IPv4Addr), nil
- }
- return botanist.ResolveIPv4(context.Background(), t.Nodename(), netstackTimeout)
-}
-
-// Serial returns the serial device associated with the target for serial i/o.
-func (t *DeviceTarget) Serial() io.ReadWriteCloser {
- return t.serial
-}
-
-// SSHKey returns the private SSH key path associated with the authorized key to be paved.
-func (t *DeviceTarget) SSHKey() string {
- return t.config.SSHKeys[0]
-}
-
-// Start starts the device target.
-func (t *DeviceTarget) Start(ctx context.Context, images build.Images, args []string) error {
- // Set up log listener and dump kernel output to stdout.
- l, err := netboot.NewLogListener(t.Nodename())
- if err != nil {
- return fmt.Errorf("cannot listen: %v", err)
- }
- go func() {
- defer l.Close()
- for {
- data, err := l.Listen()
- if err != nil {
- continue
- }
- fmt.Print(data)
- select {
- case <-ctx.Done():
- return
- default:
- }
- }
- }()
-
- addr, err := netutil.GetNodeAddress(ctx, t.Nodename(), false)
- if err != nil {
- return err
- }
-
- // Mexec Zedboot
- err = botanist.BootZedbootShim(ctx, addr, images)
- if err != nil {
- return err
- }
-
- // Boot Fuchsia.
- var bootMode int
- if t.opts.Netboot {
- bootMode = botanist.ModeNetboot
- } else {
- bootMode = botanist.ModePave
- }
- return botanist.Boot(ctx, addr, bootMode, images, args, t.signers)
-}
-
-// Restart restarts the target.
-func (t *DeviceTarget) Restart(ctx context.Context) error {
- if t.serial != nil {
- defer t.serial.Close()
- }
- if t.config.Power != nil {
- if err := t.config.Power.RebootDevice(t.signers, t.Nodename(), t.serial); err != nil {
- return fmt.Errorf("failed to reboot the device: %v", err)
- }
- }
- return nil
-}
-
-// Stop stops the device.
-func (t *DeviceTarget) Stop(ctx context.Context) error {
- return ErrUnimplemented
-}
-
-// Wait waits for the device target to stop.
-func (t *DeviceTarget) Wait(ctx context.Context) error {
- return ErrUnimplemented
-}
-
-func parseOutSigners(keyPaths []string) ([]ssh.Signer, error) {
- if len(keyPaths) == 0 {
- return nil, errors.New("must supply SSH keys in the config")
- }
- var keys [][]byte
- for _, keyPath := range keyPaths {
- p, err := ioutil.ReadFile(keyPath)
- if err != nil {
- return nil, fmt.Errorf("could not read SSH key file %q: %v", keyPath, err)
- }
- keys = append(keys, p)
- }
-
- var signers []ssh.Signer
- for _, p := range keys {
- signer, err := ssh.ParsePrivateKey(p)
- if err != nil {
- return nil, err
- }
- signers = append(signers, signer)
- }
- return signers, nil
-}
diff --git a/botanist/target/device_test.go b/botanist/target/device_test.go
deleted file mode 100644
index 30687ab..0000000
--- a/botanist/target/device_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// 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 target
-
-import (
- "io/ioutil"
- "os"
- "testing"
-)
-
-func TestLoadConfigs(t *testing.T) {
- tests := []struct {
- name string
- jsonStr string
- expectedLen int
- expectErr bool
- }{
- // Valid configs.
- {"ValidConfig", `[{"nodename":"upper-drank-wick-creek"},{"nodename":"siren-swoop-wick-hasty"}]`, 2, false},
- // Invalid configs.
- {"InvalidConfig", `{{"nodename":"upper-drank-wick-creek"},{"nodename":"siren-swoop-wick-hasty"}}`, 0, true},
- }
- for _, test := range tests {
- tmpfile, err := ioutil.TempFile(os.TempDir(), "common_test")
- if err != nil {
- t.Fatalf("Failed to create test device properties file: %s", err)
- }
- defer os.Remove(tmpfile.Name())
-
- content := []byte(test.jsonStr)
- if _, err := tmpfile.Write(content); err != nil {
- t.Fatalf("Failed to write to test device properties file: %s", err)
- }
-
- configs, err := LoadDeviceConfigs(tmpfile.Name())
-
- if test.expectErr && err == nil {
- t.Errorf("Test%v: Exepected errors; no errors found", test.name)
- }
-
- if !test.expectErr && err != nil {
- t.Errorf("Test%v: Exepected no errors; found error - %v", test.name, err)
- }
-
- if len(configs) != test.expectedLen {
- t.Errorf("Test%v: Expected %d nodes; found %d", test.name, test.expectedLen, len(configs))
- }
-
- if err := tmpfile.Close(); err != nil {
- t.Fatal(err)
- }
- }
-}
diff --git a/botanist/target/errors.go b/botanist/target/errors.go
deleted file mode 100644
index bdb49f7..0000000
--- a/botanist/target/errors.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// 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 target
-
-import (
- "errors"
-)
-
-var (
- // ErrUnimplemented is an error for unimplemented methods.
- ErrUnimplemented error = errors.New("method unimplemented")
-)
diff --git a/botanist/target/options.go b/botanist/target/options.go
deleted file mode 100644
index 8440c0d..0000000
--- a/botanist/target/options.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// 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 target
-
-// Options represents lifecycle options for a target. The options will not necessarily make
-// sense for all target types.
-type Options struct {
- // Netboot gives whether to netboot or pave. Netboot here is being used in the
- // colloquial sense of only sending netsvc a kernel to mexec. If false, the target
- // will be paved. Ignored for QEMUTarget.
- Netboot bool
-
- // SSHKey is a private SSH key file, corresponding to an authorized key to be paved or
- // to one baked into a boot image.
- SSHKey string
-}
diff --git a/botanist/target/qemu.go b/botanist/target/qemu.go
deleted file mode 100644
index 9a650df..0000000
--- a/botanist/target/qemu.go
+++ /dev/null
@@ -1,285 +0,0 @@
-// 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 target
-
-import (
- "context"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "net"
- "os"
- "os/exec"
- "path/filepath"
-
- "go.fuchsia.dev/tools/build/api"
- "go.fuchsia.dev/tools/qemu"
-)
-
-const (
- // qemuSystemPrefix is the prefix of the QEMU binary name, which is of the
- // form qemu-system-<QEMU arch suffix>.
- qemuSystemPrefix = "qemu-system"
-
- // DefaultInterfaceName is the name given to the emulated tap interface.
- defaultInterfaceName = "qemu"
-
- // DefaultMACAddr is the default MAC address given to a QEMU target.
- defaultMACAddr = "52:54:00:63:5e:7a"
-
- // DefaultNodename is the default nodename given to an target with the default QEMU MAC address.
- defaultNodename = "step-atom-yard-juicy"
-)
-
-// qemuTargetMapping maps the Fuchsia target name to the name recognized by QEMU.
-var qemuTargetMapping = map[string]string{
- "x64": qemu.TargetX86_64,
- "arm64": qemu.TargetAArch64,
-}
-
-// MinFS is the configuration for the MinFS filesystem image.
-type MinFS struct {
- // Image is the path to the filesystem image.
- Image string `json:"image"`
-
- // PCIAddress is the PCI address to map the device at.
- PCIAddress string `json:"pci_address"`
-}
-
-// QEMUConfig is a QEMU configuration.
-type QEMUConfig struct {
- // Path is a path to a directory that contains QEMU system binary.
- Path string `json:"path"`
-
- // Target is the QEMU target to emulate.
- Target string `json:"target"`
-
- // CPU is the number of processors to emulate.
- CPU int `json:"cpu"`
-
- // Memory is the amount of memory (in MB) to provide.
- Memory int `json:"memory"`
-
- // KVM specifies whether to enable hardware virtualization acceleration.
- KVM bool `json:"kvm"`
-
- // Whether User networking is enabled; if false, a Tap interface will be used.
- UserNetworking bool `json:"user_networking"`
-
- // MinFS is the filesystem to mount as a device.
- MinFS *MinFS `json:"minfs,omitempty"`
-}
-
-// QEMUTarget is a QEMU target.
-type QEMUTarget struct {
- config QEMUConfig
- opts Options
-
- c chan error
-
- cmd *exec.Cmd
- status error
-}
-
-// NewQEMUTarget returns a new QEMU target with a given configuration.
-func NewQEMUTarget(config QEMUConfig, opts Options) *QEMUTarget {
- return &QEMUTarget{
- config: config,
- opts: opts,
- c: make(chan error),
- }
-}
-
-// Nodename returns the name of the target node.
-func (t *QEMUTarget) Nodename() string {
- return defaultNodename
-}
-
-// IPv4Addr returns a nil address, as DHCP is not currently configured.
-func (t *QEMUTarget) IPv4Addr() (net.IP, error) {
- return nil, nil
-}
-
-// Serial returns the serial device associated with the target for serial i/o.
-func (t *QEMUTarget) Serial() io.ReadWriteCloser {
- return nil
-}
-
-// SSHKey returns the private SSH key path associated with the authorized key to be pavet.
-func (t *QEMUTarget) SSHKey() string {
- return t.opts.SSHKey
-}
-
-// Start starts the QEMU target.
-func (t *QEMUTarget) Start(ctx context.Context, images build.Images, args []string) error {
- qemuTarget, ok := qemuTargetMapping[t.config.Target]
- if !ok {
- return fmt.Errorf("invalid target %q", t.config.Target)
- }
-
- if t.config.Path == "" {
- return fmt.Errorf("directory must be set")
- }
- qemuSystem := filepath.Join(t.config.Path, fmt.Sprintf("%s-%s", qemuSystemPrefix, qemuTarget))
- if _, err := os.Stat(qemuSystem); err != nil {
- return fmt.Errorf("could not find qemu-system binary %q: %v", qemuSystem, err)
- }
-
- qemuKernel := images.Get("qemu-kernel")
- if qemuKernel == nil {
- return fmt.Errorf("could not find qemu-kernel")
- }
- zirconA := images.Get("zircon-a")
- if zirconA == nil {
- return fmt.Errorf("could not find zircon-a")
- }
-
- var drives []qemu.Drive
- if storageFull := images.Get("storage-full"); storageFull != nil {
- drives = append(drives, qemu.Drive{
- ID: "maindisk",
- File: storageFull.Path,
- })
- }
- if t.config.MinFS != nil {
- if _, err := os.Stat(t.config.MinFS.Image); err != nil {
- return fmt.Errorf("could not find minfs image %q: %v", t.config.MinFS.Image, err)
- }
- file, err := filepath.Abs(t.config.MinFS.Image)
- if err != nil {
- return err
- }
- // Swarming hard-links Isolate downloads with a cache and the very same
- // cached minfs image will be used across multiple tasks. To ensure
- // that it remains blank, we must break its link.
- if err := overwriteFileWithCopy(file); err != nil {
- return err
- }
- drives = append(drives, qemu.Drive{
- ID: "testdisk",
- File: file,
- Addr: t.config.MinFS.PCIAddress,
- })
- }
-
- netdev := qemu.Netdev{
- ID: "net0",
- MAC: defaultMACAddr,
- }
- if t.config.UserNetworking {
- netdev.User = &qemu.NetdevUser{}
- } else {
- netdev.Tap = &qemu.NetdevTap{
- Name: defaultInterfaceName,
- }
- }
- networks := []qemu.Netdev{netdev}
-
- config := qemu.Config{
- Binary: qemuSystem,
- Target: qemuTarget,
- CPU: t.config.CPU,
- Memory: t.config.Memory,
- KVM: t.config.KVM,
- Kernel: qemuKernel.Path,
- Initrd: zirconA.Path,
- Drives: drives,
- Networks: networks,
- }
-
- // The system will halt on a kernel panic instead of rebooting.
- args = append(args, "kernel.halt-on-panic=true")
- // Print a message if `dm poweroff` times out.
- args = append(args, "devmgr.suspend-timeout-debug=true")
- // Do not print colors.
- args = append(args, "TERM=dumb")
- if t.config.Target == "x64" {
- // Necessary to redirect to stdout.
- args = append(args, "kernel.serial=legacy")
- }
-
- invocation, err := qemu.CreateInvocation(config, args)
- if err != nil {
- return err
- }
-
- // The QEMU command needs to be invoked within an empty directory, as QEMU
- // will attempt to pick up files from its working directory, one notable
- // culprit being multiboot.bin. This can result in strange behavior.
- workdir, err := ioutil.TempDir("", "qemu-working-dir")
- if err != nil {
- return err
- }
-
- t.cmd = &exec.Cmd{
- Path: invocation[0],
- Args: invocation,
- Dir: workdir,
- Stdout: os.Stdout,
- Stderr: os.Stderr,
- }
- log.Printf("QEMU invocation:\n%s", invocation)
-
- if err := t.cmd.Start(); err != nil {
- os.RemoveAll(workdir)
- return fmt.Errorf("failed to start: %v", err)
- }
-
- // Ensure that the working directory when QEMU finishes whether the Wait
- // method is invoked or not.
- go func() {
- defer os.RemoveAll(workdir)
- t.c <- qemu.CheckExitCode(t.cmd.Wait())
- }()
-
- return nil
-}
-
-// Wait waits for the QEMU target to stop.
-func (t *QEMUTarget) Restart(ctx context.Context) error {
- return ErrUnimplemented
-}
-
-// Stop stops the QEMU target.
-func (t *QEMUTarget) Stop(ctx context.Context) error {
- return t.cmd.Process.Kill()
-}
-
-// Wait waits for the QEMU target to stop.
-func (t *QEMUTarget) Wait(ctx context.Context) error {
- return <-t.c
-}
-
-func overwriteFileWithCopy(path string) error {
- tmpfile, err := ioutil.TempFile(filepath.Dir(path), "botanist")
- if err != nil {
- return err
- }
- defer tmpfile.Close()
- if err := copyFile(path, tmpfile.Name()); err != nil {
- return err
- }
- return os.Rename(tmpfile.Name(), path)
-}
-
-func copyFile(src, dest string) error {
- in, err := os.Open(src)
- if err != nil {
- return err
- }
- defer in.Close()
- info, err := in.Stat()
- if err != nil {
- return err
- }
- out, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE, info.Mode().Perm())
- if err != nil {
- return err
- }
- defer out.Close()
- _, err = io.Copy(out, in)
- return err
-}
diff --git a/build/api/build.go b/build/api/build.go
deleted file mode 100644
index bb19532..0000000
--- a/build/api/build.go
+++ /dev/null
@@ -1,16 +0,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.
-
-// This file includes build-specific concepts.
-
-package build
-
-const (
- // TestSpecManifestName is the name of the manifest of test specs produced by the build.
- TestSpecManifestName = "tests.json"
-
- // PlatformManifestName is the name of the manifest of available test
- // platforms.
- PlatformManifestName = "platforms.json"
-)
diff --git a/build/api/images.go b/build/api/images.go
deleted file mode 100644
index 94b55b5..0000000
--- a/build/api/images.go
+++ /dev/null
@@ -1,81 +0,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 build
-
-import (
- "encoding/json"
- "fmt"
- "os"
- "path/filepath"
-)
-
-// Image represents an entry in an image manifest.
-type Image struct {
- // Name is the canonical name of the image.
- Name string `json:"name"`
-
- // Path is the absolute path to the image.
- // Note: when unmarshalled from a manifest this entry actually gives the relative
- // location from the manifest's directory; we prepend that directory when loading. See
- // LoadImages() below.
- Path string `json:"path"`
-
- // Type is the shorthand for the type of the image (e.g., "zbi" or "blk").
- Type string `json:"type"`
-
- // PaveArgs is the list of associated arguments to pass to the bootserver
- // when paving.
- PaveArgs []string `json:"bootserver_pave"`
-
- // NetbootArgs is the list of associated arguments to pass to the bootserver
- // when netbooting.
- NetbootArgs []string `json:"bootserver_netboot"`
-}
-
-// Images is list of images produced by the build, as may be read in from an image manifest.
-type Images []Image
-
-// Get returns the first image in a list with the given name, or nil if no such image exists.
-func (imgs Images) Get(name string) *Image {
- for _, img := range imgs {
- if img.Name == name {
- return &img
- }
- }
- return nil
-}
-
-// LoadImages reads in the entries indexed in the given image manifests.
-func LoadImages(imageManifests ...string) (Images, error) {
- decodeImages := func(manifest string) (Images, error) {
- f, err := os.Open(manifest)
- if err != nil {
- return nil, fmt.Errorf("failed to open %s: %v", manifest, err)
- }
- defer f.Close()
- var imgs Images
- if err := json.NewDecoder(f).Decode(&imgs); err != nil {
- return nil, fmt.Errorf("failed to decode %s: %v", manifest, err)
- }
- manifestDir, err := filepath.Abs(filepath.Dir(manifest))
- if err != nil {
- return nil, err
- }
- for i, _ := range imgs {
- imgs[i].Path = filepath.Join(manifestDir, imgs[i].Path)
- }
- return imgs, nil
- }
-
- var imgs Images
- for _, manifest := range imageManifests {
- decoded, err := decodeImages(manifest)
- if err != nil {
- return nil, err
- }
- imgs = append(imgs, decoded...)
- }
- return imgs, nil
-}
diff --git a/build/gndoc/argmap.go b/build/gndoc/argmap.go
deleted file mode 100644
index 24888a6..0000000
--- a/build/gndoc/argmap.go
+++ /dev/null
@@ -1,100 +0,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 gndoc
-
-import (
- "fmt"
- "io"
- "sort"
- "strings"
-)
-
-type ArgMap struct {
- argLookup map[string][]string // map of arg names to keys for lookup
- args map[string][]Arg // map of key names to relevant args
- sources *SourceMap // map of existing source locations for linkifying
-}
-
-// NewArgMap returns a pointer to an
-func NewArgMap(sources *SourceMap) *ArgMap {
- return &ArgMap{
- argLookup: make(map[string][]string),
- args: make(map[string][]Arg),
- sources: sources,
- }
-}
-
-// AddArgs creates args from GNArgs and adds them to the maps
-func (a *ArgMap) AddArgs(gnArgs <-chan Arg) {
- for gnArg := range gnArgs {
- a.AddArg(gnArg)
- }
-}
-
-// AddArg adds Arg to the maps
-func (a *ArgMap) AddArg(gnArg Arg) {
- a.args[gnArg.Key] = append(a.args[gnArg.Key], gnArg)
- a.argLookup[gnArg.Name] = append(a.argLookup[gnArg.Name], gnArg.Key)
-}
-
-// EmitMarkdown emits Markdown text for the map of arguments.
-func (a *ArgMap) EmitMarkdown(out io.Writer) {
- type mappedArgs struct {
- m map[string][]Arg
- k []string
- }
- sortedArgs := struct {
- m map[string]*mappedArgs
- k []string
- }{
- m: make(map[string]*mappedArgs),
- k: make([]string, 0),
- }
-
- numKeys := len(a.args)
- for _, gnArgs := range a.args {
- for _, gnArg := range gnArgs {
- // Lookup the keys associated with this arg & sort & stringify.
- keys, _ := a.argLookup[gnArg.Name]
- if len(keys) == numKeys {
- // Incoming keys will always have an `=`, and so this is an
- // okay value.
- keys = []string{"all"}
- }
- sort.Strings(keys)
- key := strings.Join(keys, ", ")
- if _, ok := sortedArgs.m[key]; !ok {
- sortedArgs.m[key] = &mappedArgs{
- m: make(map[string][]Arg),
- k: make([]string, 0)}
- }
- sortedArgs.m[key].m[gnArg.Name] = append(sortedArgs.m[key].m[gnArg.Name], gnArg)
- }
- }
- for k := range sortedArgs.m {
- for argName := range sortedArgs.m[k].m {
- sort.Slice(sortedArgs.m[k].m[argName], func(i, j int) bool {
- return sortedArgs.m[k].m[argName][i].Key < sortedArgs.m[k].m[argName][j].Key
- })
- sortedArgs.m[k].k = append(sortedArgs.m[k].k, argName)
- }
- sort.Strings(sortedArgs.m[k].k)
- sortedArgs.k = append(sortedArgs.k, k)
- }
- sort.Strings(sortedArgs.k)
- // Emit a header.
- fmt.Fprintf(out, "# %s\n\n", pageTitle)
- for _, name := range sortedArgs.k {
- if name == "all" {
- fmt.Fprintf(out, "## All builds\n\n")
- } else {
- fmt.Fprintf(out, "## `%s`\n\n", name)
- }
- for _, argsKey := range sortedArgs.m[name].k {
- gnArgs := sortedArgs.m[name].m[argsKey]
- writeArgs(gnArgs, out, a.sources)
- }
- }
-}
diff --git a/build/gndoc/argmap_test.go b/build/gndoc/argmap_test.go
deleted file mode 100644
index 005c97b..0000000
--- a/build/gndoc/argmap_test.go
+++ /dev/null
@@ -1,136 +0,0 @@
-// 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 gndoc
-
-import (
- "bytes"
- "math/rand"
- "testing"
- "time"
-)
-
-func genArgMapInRandomOrder() *ArgMap {
- testArgMap := NewArgMap(Sources())
- gnArgs := []Arg{
- defaultx64Arg,
- defaultarm64Arg,
- defaultarm64ArgWithCurrent,
- defaultx64ArgWithCurrent,
- x64Arg,
- arm64Arg,
- twoKeyarm64TestArg,
- twoKeyx64TestArg,
- twoKeyarm64OtherArg,
- twoKeyx64OtherArg,
- newLineValueArg,
- }
- // Shuffle the gnArgs.
- r := rand.New(rand.NewSource(time.Now().Unix()))
- shuffledGnArgs := make([]Arg, 0)
- for _, j := range r.Perm(len(gnArgs)) {
- shuffledGnArgs = append(shuffledGnArgs, gnArgs[j])
- }
- for _, arg := range shuffledGnArgs {
- testArgMap.AddArg(arg)
- }
- return testArgMap
-}
-
-func TestArgMapEmitMarkdown(t *testing.T) {
- expectedOutput := `# GN Build Arguments
-
-## ` + "`target_cpu = arm64, package='other/package/default'`" + `
-
-### arm64Other
-Description of arm64 arg.
-
-**Current value for ` + "`target_cpu = arm64, package='other/package/default'`:** `arg`" + `
-
-**Overridden from the default:** ` + "`value`" + `
-
-## ` + "`target_cpu = arm64, target_cpu = arm64, package='test/package/default'`" + `
-
-### arm64
-Description of arm64 arg.
-
-**Current value for ` + "`target_cpu = arm64`:** `arg`" + `
-
-**Overridden from the default:** ` + "`value`" + `
-
-**Current value for ` + "`target_cpu = arm64, package='test/package/default'`:** `arg`" + `
-
-**Overridden from the default:** ` + "`value`" + `
-
-## ` + "`target_cpu = arm64, target_cpu = x64`" + `
-
-### default
-Description of default arg.
-
-**Current value (from the default):** ` + "`false`" + `
-
-From //test/BUILD.gn:2
-
-### default_current
-Description of default_current arg.
-
-**Current value for ` + "`target_cpu = arm64`:** `[1, 2]`" + `
-
-From [//build/BUILD.gn:24](http://fuchsia.com/build/BUILD.gn#24)
-
-**Overridden from the default:** ` + "`[3, 4]`" + `
-
-From [//base/BUILD.gn:4](http://fuchsia.com/base/BUILD.gn#4)
-
-**Current value for ` + "`target_cpu = x64`:** `3`" + `
-
-From [//build/BUILD.gn:24](http://fuchsia.com/build/BUILD.gn#24)
-
-**Overridden from the default:** ` + "`4`" + `
-
-From [//base/BUILD.gn:2](http://fuchsia.com/base/BUILD.gn#2)
-
-## ` + "`target_cpu = x64, package='other/package/default'`" + `
-
-### NewLine
-Description of newline arg.
-
-**Current value (from the default):**
-` + "```" + `
-{
- base = "//build/toolchain/fuchsia:x64"
-}
-` + "```" + `
-
-### x64Other
-Description of x64 arg.
-
-**Current value for ` + "`target_cpu = x64, package='other/package/default'`:** `arg`" + `
-
-**Overridden from the default:** ` + "`value`" + `
-
-## ` + "`target_cpu = x64, target_cpu = x64, package='test/package/default'`" + `
-
-### x64
-Description of x64 arg that references [//build/path.py](http://fuchsia.com/build/path.py), //sources, and [//base](http://fuchsia.com/base).
-
-**Current value for ` + "`target_cpu = x64`:** `1`" + `
-
-**Overridden from the default:** ` + "`2`" + `
-
-**Current value for ` + "`target_cpu = x64, package='test/package/default'`:** `arg`" + `
-
-**Overridden from the default:** ` + "`value`" + `
-
-`
- for i := 0; i < 10; i++ {
- testArgMap := genArgMapInRandomOrder()
- var testOutput bytes.Buffer
- testArgMap.EmitMarkdown(&testOutput)
- if expectedOutput != testOutput.String() {
- t.Errorf("expecting output:\n%s\n, got:\n%s\n", expectedOutput, testOutput.String())
- break
- }
- }
-}
diff --git a/build/gndoc/argparser.go b/build/gndoc/argparser.go
deleted file mode 100644
index a21ad0e..0000000
--- a/build/gndoc/argparser.go
+++ /dev/null
@@ -1,93 +0,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 gndoc
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "os"
- "strings"
-)
-
-// Arg holds the information directly parsed from the json output of `gn args <build> --list --json`.
-type Arg struct {
- Name string `json:"name"`
- CurrentVal argValue `json:"current"`
- DefaultVal argValue `json:"default"`
- Comment string `json:"comment"`
- Key string `json: "-"`
-}
-
-// ArgValue holds a value, its filepath and line number, and the build associated with the value.
-type argValue struct {
- Val string `json:"value"`
- File string `json:"file"`
- Line int `json:"line"`
-}
-
-// ParseGNArgs runs the necessary gn commands and decodes the json output into a channel of GNArgs.
-func ParseGNArgs(ctx context.Context, inputFiles []string, keyArgs []string) (<-chan Arg, <-chan error) {
- args := make(chan Arg)
- errs := make(chan error, 1)
- go func() {
- defer func() {
- close(args)
- close(errs)
- }()
- for _, input := range inputFiles {
- select {
- case <-ctx.Done():
- return
- default:
- err := parseJson(input, keyArgs, args)
- if err != nil {
- errs <- err
- return
- }
- }
- }
- }()
- return args, errs
-}
-
-func parseJson(input string, keyArgs []string, out chan<- Arg) error {
- // Open the json file.
- file, err := os.Open(input)
- if err != nil {
- return err
- }
- defer file.Close()
-
- // Decode the json into GNArgs.
- var gnArgs []Arg
- decoder := json.NewDecoder(file)
- if err := decoder.Decode(&gnArgs); err != nil {
- return err
- }
-
- // Mark the args with the relevant key and send to channel.
- key := getKey(gnArgs, keyArgs)
- for _, arg := range gnArgs {
- arg.Key = key
- out <- arg
- }
-
- return nil
-}
-
-// TODO: make sure this is sorted before stringifying
-// getKey searches the decoded json for the flagged keys and builds the marker string.
-func getKey(args []Arg, keys []string) string {
- keyVals := make([]string, len(keys))
- for _, arg := range args {
- for idx, key := range keys {
- if arg.Name == key {
- keyVals[idx] = fmt.Sprintf("%s = %v", key, arg.CurrentVal.Val)
- }
- }
- }
- return strings.Join(keyVals, ", ")
-}
diff --git a/build/gndoc/cmd/main.go b/build/gndoc/cmd/main.go
deleted file mode 100644
index 06aedc1..0000000
--- a/build/gndoc/cmd/main.go
+++ /dev/null
@@ -1,89 +0,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 (
- "context"
- "flag"
- "io"
- "log"
- "os"
- "path/filepath"
- "strings"
-
- "go.fuchsia.dev/tools/build/gndoc"
-)
-
-type stringsFlag []string
-
-func (s *stringsFlag) String() string {
- return strings.Join(*s, ", ")
-}
-
-func (s *stringsFlag) Set(value string) error {
- *s = append(*s, value)
- return nil
-}
-
-var (
- keyArgs stringsFlag
- inputFiles stringsFlag
- outFile string
- sourcesFile string
-)
-
-func init() {
- flag.Var(&keyArgs, "key", "a label for output")
- flag.Var(&inputFiles, "in", "path to an input file")
- flag.StringVar(&outFile, "out", "", "path to output file (default stdout)")
- flag.StringVar(&sourcesFile, "s", "", "path to json file generated by Jiri projects")
-}
-
-func main() {
- flag.Parse()
- if flag.NArg() != 0 {
- flag.PrintDefaults()
- }
-
- if sourcesFile == "" {
- log.Fatalf("Error: no Jiri project reference file specified.\nTo generate, use jiri project -json-output=<file>.\n")
- }
-
- // Open the json file.
- sources, err := os.Open(sourcesFile)
- if err != nil {
- log.Fatalf("Error: unable to open %s", sourcesFile)
- }
- defer sources.Close()
-
- ctx := context.Background()
- sourceMap, err := gndoc.NewSourceMap(sources)
- if err != nil {
- log.Fatalf("Error: %s", err)
- }
- argMap := gndoc.NewArgMap(sourceMap)
-
- args, errs := gndoc.ParseGNArgs(ctx, inputFiles, keyArgs)
- argMap.AddArgs(args)
- if err := <-errs; err != nil {
- log.Fatalf("Error: %s\n", err)
- }
-
- var out io.Writer
- if outFile != "" {
- var err error
- if dirErr := os.MkdirAll(filepath.Dir(outFile), os.ModePerm); dirErr != nil {
- log.Fatalf("Error creating directories: %s", dirErr)
- }
- out, err = os.Create(outFile)
- if err != nil {
- log.Fatalf("Error opening file: %s", err)
- }
- } else {
- out = os.Stdout
- }
-
- argMap.EmitMarkdown(out)
-}
diff --git a/build/gndoc/formatter.go b/build/gndoc/formatter.go
deleted file mode 100644
index 15119f0..0000000
--- a/build/gndoc/formatter.go
+++ /dev/null
@@ -1,85 +0,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 gndoc
-
-import (
- "fmt"
- "io"
- "regexp"
- "sort"
- "strings"
-)
-
-const (
- pageTitle = "GN Build Arguments"
- nameDepth = 3
-)
-
-var (
- linkRegexp = regexp.MustCompile("//([/A-Za-z-_]+)([.][/A-Za-z-_]+)?")
-)
-
-// writeArg emits the name, comment description, and value(s) of the argument in Markdown.
-func writeArgs(args []Arg, out io.Writer, sources *SourceMap) {
- if len(args) == 0 {
- return
- }
- sort.Slice(args, func(i, j int) bool {
- return args[i].Name < args[j].Name
- })
-
- fmt.Fprintf(out, "%s %s\n", strings.Repeat("#", nameDepth), args[0].Name)
- // TODO (juliehockett): Make sure that *all* comments get emitted.
- writeLinkifiedComment(&args[0], out, sources)
- writeAllValues(args, out, sources)
-}
-
-// writeValue emits the value of a given argument value, along with the associated Markdown link to its declaration and build (if present).
-func writeValue(a *argValue, out io.Writer, sources *SourceMap) {
- var value string
- if strings.Contains(a.Val, "\n") {
- value = fmt.Sprintf("\n```\n%s\n```", a.Val)
- } else {
- value = fmt.Sprintf(" `%s`", a.Val)
- }
-
- if a.File == "" {
- // If there is no declaration file, emit just the value.
- fmt.Fprintf(out, "%s\n\n", value)
- } else {
- // Otherwise, emit the value with a link to the declaration.
- link := sources.GetSourceLink(a.File, a.Line)
- if link == "" {
- fmt.Fprintf(out, "%s\n\nFrom %s:%d\n\n", value, a.File, a.Line)
- return
- }
- fmt.Fprintf(out, "%s\n\nFrom [%s:%d](%s)\n\n", value, a.File, a.Line, link)
- }
-}
-
-func writeLinkifiedComment(a *Arg, out io.Writer, sources *SourceMap) {
- replFunc := func(str string) string {
- if link := sources.GetSourceLink(str, 0); link != "" {
- return fmt.Sprintf("[%s](%s)", str, link)
- }
- return str
- }
- fmt.Fprintf(out, "%s\n", linkRegexp.ReplaceAllStringFunc(a.Comment, replFunc))
-}
-
-func writeAllValues(args []Arg, out io.Writer, sources *SourceMap) {
- emptyArgValue := argValue{}
- for _, a := range args {
- if a.CurrentVal == emptyArgValue || a.CurrentVal == a.DefaultVal {
- fmt.Fprintf(out, "**Current value (from the default):**")
- writeValue(&a.DefaultVal, out, sources)
- return
- }
- fmt.Fprintf(out, "**Current value for `%s`:**", a.Key)
- writeValue(&a.CurrentVal, out, sources)
- fmt.Fprintf(out, "**Overridden from the default:**")
- writeValue(&a.DefaultVal, out, sources)
- }
-}
diff --git a/build/gndoc/formatter_test.go b/build/gndoc/formatter_test.go
deleted file mode 100644
index a72757c..0000000
--- a/build/gndoc/formatter_test.go
+++ /dev/null
@@ -1,360 +0,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 gndoc
-
-import (
- "bytes"
- "testing"
-)
-
-const (
- numKeys = 2
-)
-
-var (
- defaultx64Arg = Arg{
- Name: "default",
- DefaultVal: argValue{
- Val: "false",
- File: "//test/BUILD.gn",
- Line: 2,
- },
- Comment: "Description of default arg.\n",
- Key: "target_cpu = x64",
- }
-
- defaultarm64Arg = Arg{
- Name: "default",
- DefaultVal: argValue{
- Val: "false",
- File: "//test/BUILD.gn",
- Line: 2,
- },
- Comment: "Description of default arg.\n",
- Key: "target_cpu = arm64",
- }
-
- defaultarm64ArgWithCurrent = Arg{
- Name: "default_current",
- CurrentVal: argValue{
- Val: "[1, 2]",
- File: "//build/BUILD.gn",
- Line: 24,
- },
- DefaultVal: argValue{
- Val: "[3, 4]",
- File: "//base/BUILD.gn",
- Line: 4,
- },
- Comment: "Description of default_current arg.\n",
- Key: "target_cpu = arm64",
- }
-
- defaultx64ArgWithCurrent = Arg{
- Name: "default_current",
- CurrentVal: argValue{
- Val: "3",
- File: "//build/BUILD.gn",
- Line: 24,
- },
- DefaultVal: argValue{
- Val: "4",
- File: "//base/BUILD.gn",
- Line: 2,
- },
- Comment: "Description of default_current arg.\n",
- Key: "target_cpu = x64",
- }
-
- x64Arg = Arg{
- Name: "x64",
- CurrentVal: argValue{
- Val: "1",
- },
- DefaultVal: argValue{
- Val: "2",
- },
- Comment: "Description of x64 arg that references //build/path.py, //sources, and //base.\n",
- Key: "target_cpu = x64",
- }
-
- arm64Arg = Arg{
- Name: "arm64",
- CurrentVal: argValue{
- Val: "arg",
- },
- DefaultVal: argValue{
- Val: "value",
- },
- Comment: "Description of arm64 arg.\n",
- Key: "target_cpu = arm64",
- }
-
- twoKeyarm64TestArg = Arg{
- Name: "arm64",
- CurrentVal: argValue{
- Val: "arg",
- },
- DefaultVal: argValue{
- Val: "value",
- },
- Comment: "Description of arm64 arg.\n",
- Key: "target_cpu = arm64, package='test/package/default'",
- }
-
- twoKeyx64TestArg = Arg{
- Name: "x64",
- CurrentVal: argValue{
- Val: "arg",
- },
- DefaultVal: argValue{
- Val: "value",
- },
- Comment: "Description of x64 arg.\n",
- Key: "target_cpu = x64, package='test/package/default'",
- }
-
- twoKeyarm64OtherArg = Arg{
- Name: "arm64Other",
- CurrentVal: argValue{
- Val: "arg",
- },
- DefaultVal: argValue{
- Val: "value",
- },
- Comment: "Description of arm64 arg.\n",
- Key: "target_cpu = arm64, package='other/package/default'",
- }
-
- twoKeyx64OtherArg = Arg{
- Name: "x64Other",
- CurrentVal: argValue{
- Val: "arg",
- },
- DefaultVal: argValue{
- Val: "value",
- },
- Comment: "Description of x64 arg.\n",
- Key: "target_cpu = x64, package='other/package/default'",
- }
-
- newLineValueArg = Arg{
- Name: "NewLine",
- DefaultVal: argValue{
- Val: "{\n base = \"//build/toolchain/fuchsia:x64\"\n}",
- },
- Comment: "Description of newline arg.\n",
- Key: "target_cpu = x64, package='other/package/default'",
- }
-)
-
-func Sources() *SourceMap {
- s := SourceMap(make(map[string]string))
- s["base"] = "http://fuchsia.com/base"
- s["build"] = "http://fuchsia.com/build"
- return &s
-}
-
-func TestDefault(t *testing.T) {
-
- gnArgs := []Arg{defaultx64Arg, defaultarm64Arg}
- argMap := NewArgMap(Sources())
- for _, arg := range gnArgs {
- argMap.AddArg(arg)
- }
-
- // No file name emits to stdout.
- var buffer bytes.Buffer
- argMap.EmitMarkdown(&buffer)
-
- actual := buffer.String()
- expected := `# GN Build Arguments
-
-## All builds
-
-### default
-Description of default arg.
-
-**Current value (from the default):** ` + "`false`" + `
-
-From //test/BUILD.gn:2
-
-`
- if expected != actual {
- t.Fatalf("In TestDefault, expected \n%s but got \n%s", expected, actual)
- }
-}
-
-func TestDefaultWithCurrent(t *testing.T) {
-
- gnArgs := []Arg{defaultx64ArgWithCurrent, defaultarm64ArgWithCurrent}
- argMap := NewArgMap(Sources())
- for _, arg := range gnArgs {
- argMap.AddArg(arg)
- }
-
- // No file name emits to stdout.
- var buffer bytes.Buffer
- argMap.EmitMarkdown(&buffer)
-
- actual := buffer.String()
- expected := `# GN Build Arguments
-
-## All builds
-
-### default_current
-Description of default_current arg.
-
-**Current value for ` + "`target_cpu = arm64`:** `[1, 2]`" + `
-
-From [//build/BUILD.gn:24](http://fuchsia.com/build/BUILD.gn#24)
-
-**Overridden from the default:** ` + "`[3, 4]`" + `
-
-From [//base/BUILD.gn:4](http://fuchsia.com/base/BUILD.gn#4)
-
-**Current value for ` + "`target_cpu = x64`:** `3`" + `
-
-From [//build/BUILD.gn:24](http://fuchsia.com/build/BUILD.gn#24)
-
-**Overridden from the default:**` + " `4`" + `
-
-From [//base/BUILD.gn:2](http://fuchsia.com/base/BUILD.gn#2)
-
-`
-
- if expected != actual {
- t.Fatalf("In TestDefaultWithCurrent, expected \n%s but got \n%s", expected, actual)
- }
-}
-
-func TestUnique(t *testing.T) {
-
- gnArgs := []Arg{x64Arg, arm64Arg}
- argMap := NewArgMap(Sources())
- for _, arg := range gnArgs {
- argMap.AddArg(arg)
- }
-
- // No file name emits to stdout.
- var buffer bytes.Buffer
- argMap.EmitMarkdown(&buffer)
-
- actual := buffer.String()
- expected := `# GN Build Arguments
-
-## ` + "`target_cpu = arm64`" + `
-
-### arm64
-Description of arm64 arg.
-
-**Current value for ` + "`target_cpu = arm64`:** `arg`" + `
-
-**Overridden from the default:** ` + "`value`" + `
-
-## ` + "`target_cpu = x64`" + `
-
-### x64
-Description of x64 arg that references [//build/path.py](http://fuchsia.com/build/path.py), //sources, and [//base](http://fuchsia.com/base).
-
-**Current value for ` + "`target_cpu = x64`:** `1`" + `
-
-**Overridden from the default:** ` + "`2`" + `
-
-`
- if expected != actual {
- t.Fatalf("In TestUnique, expected \n%s but got \n%s", expected, actual)
- }
-}
-
-func TestTwoKeys(t *testing.T) {
-
- gnArgs := []Arg{twoKeyarm64TestArg, twoKeyx64TestArg, twoKeyarm64OtherArg, twoKeyx64OtherArg}
- argMap := NewArgMap(Sources())
- for _, arg := range gnArgs {
- argMap.AddArg(arg)
- }
-
- // No file name emits to stdout.
- var buffer bytes.Buffer
- argMap.EmitMarkdown(&buffer)
-
- actual := buffer.String()
- expected := `# GN Build Arguments
-
-## ` + "`target_cpu = arm64, package='other/package/default'`" + `
-
-### arm64Other
-Description of arm64 arg.
-
-**Current value for ` + "`target_cpu = arm64, package='other/package/default'`:** `arg`" + `
-
-**Overridden from the default:** ` + "`value`" + `
-
-## ` + "`target_cpu = arm64, package='test/package/default'`" + `
-
-### arm64
-Description of arm64 arg.
-
-**Current value for ` + "`target_cpu = arm64, package='test/package/default'`:** `arg`" + `
-
-**Overridden from the default:** ` + "`value`" + `
-
-## ` + "`target_cpu = x64, package='other/package/default'`" + `
-
-### x64Other
-Description of x64 arg.
-
-**Current value for ` + "`target_cpu = x64, package='other/package/default'`:** `arg`" + `
-
-**Overridden from the default:** ` + "`value`" + `
-
-## ` + "`target_cpu = x64, package='test/package/default'`" + `
-
-### x64
-Description of x64 arg.
-
-**Current value for ` + "`target_cpu = x64, package='test/package/default'`:** `arg`" + `
-
-**Overridden from the default:** ` + "`value`" + `
-
-`
- if expected != actual {
- t.Fatalf("In TestUnique, expected \n%s but got \n%s", expected, actual)
- }
-}
-
-func TestValueNewLine(t *testing.T) {
-
- gnArgs := []Arg{newLineValueArg}
- argMap := NewArgMap(Sources())
- for _, arg := range gnArgs {
- argMap.AddArg(arg)
- }
-
- // No file name emits to stdout.
- var buffer bytes.Buffer
- argMap.EmitMarkdown(&buffer)
-
- actual := buffer.String()
- expected := `# GN Build Arguments
-
-## All builds
-
-### NewLine
-Description of newline arg.
-
-**Current value (from the default):**
-` + "```" + `
-{
- base = "//build/toolchain/fuchsia:x64"
-}
-` + "```" + `
-
-`
- if expected != actual {
- t.Fatalf("In TestDefault, expected \n%s but got \n%s", expected, actual)
- }
-}
diff --git a/build/gndoc/sourceparser.go b/build/gndoc/sourceparser.go
deleted file mode 100644
index 02c8611..0000000
--- a/build/gndoc/sourceparser.go
+++ /dev/null
@@ -1,64 +0,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 gndoc
-
-import (
- "encoding/json"
- "fmt"
- "io"
- "path/filepath"
- "strings"
-)
-
-type SourceMap map[string]string
-
-func NewSourceMap(input io.Reader) (*SourceMap, error) {
- s := SourceMap(make(map[string]string))
- err := s.parseJson(input)
- if err != nil {
- return nil, err
- }
- return &s, nil
-}
-
-// parseJson extracts the source project information into the map.
-func (s SourceMap) parseJson(input io.Reader) error {
- // Decode the json into the map.
- var sources []map[string]interface{}
- decoder := json.NewDecoder(input)
- if err := decoder.Decode(&sources); err != nil {
- return err
- }
-
- for _, source := range sources {
- if revision, ok := source["revision"]; ok {
- s[source["name"].(string)] = fmt.Sprintf("%v/+/%s", source["remote"], revision)
- } else {
- s[source["name"].(string)] = fmt.Sprintf("%v/+/master", source["remote"])
- }
- }
-
- return nil
-}
-
-// GetSourceLink returns a URL based on a file and a line number.
-func (s SourceMap) GetSourceLink(file string, line int) string {
- // We need to trim off first slashes in the filename.
- file = strings.TrimPrefix(file, "//")
- project := file
- for {
- if _, exists := s[project]; exists {
- break
- }
- project = filepath.Dir(project)
- if project == "." {
- return ""
- }
- }
- if line == 0 {
- return fmt.Sprintf("%s%s", s[project], file[len(project):])
- }
- return fmt.Sprintf("%s%s#%d", s[project], file[len(project):], line)
-}
diff --git a/build/gndoc/sourceparser_test.go b/build/gndoc/sourceparser_test.go
deleted file mode 100644
index e5db803..0000000
--- a/build/gndoc/sourceparser_test.go
+++ /dev/null
@@ -1,77 +0,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 gndoc
-
-import (
- "strings"
- "testing"
-)
-
-func TestNewSourceMap(t *testing.T) {
- sources := `[
- {
- "name": "build",
- "path": "/path/to/fuchsia/build",
- "relativePath": "build",
- "remote": "https://fuchsia.googlesource.com/build",
- "revision": "43d2b55675d428d460fe6f91092bbf3c39552caf",
- "branches": [
- "mybranch",
- "master"
- ]
- },
- {
- "name": "buildtools",
- "path": "/path/to/fuchsia/buildtools",
- "relativePath": "buildtools",
- "remote": "https://fuchsia.googlesource.com/buildtools"
- }
-]
-`
- actual, err := NewSourceMap(strings.NewReader(sources))
- if err != nil {
- t.Fatalf("In TestNewSourceMap, unable to create source map: %s", err)
- }
-
- expected := SourceMap(make(map[string]string))
- expected["build"] = "https://fuchsia.googlesource.com/build/+/43d2b55675d428d460fe6f91092bbf3c39552caf"
- expected["buildtools"] = "https://fuchsia.googlesource.com/buildtools/+/master"
-
- if len(expected) != len(*actual) {
- t.Fatalf("In TestNewSourceMap, expected \n%d but got \n%d", len(expected), len(*actual))
- }
-
- if expected["build"] != (*actual)["build"] {
- t.Fatalf("In TestNewSourceMap, expected \n%s but got \n%s", expected["build"], (*actual)["build"])
- }
-
- if expected["buildtools"] != (*actual)["buildtools"] {
- t.Fatalf("In TestNewSourceMap, expected \n%s but got \n%s", expected["buildtools"], (*actual)["buildtools"])
- }
-}
-
-func TestGetSourceLink(t *testing.T) {
- sourceMap := SourceMap(make(map[string]string))
- sourceMap["build"] = "https://fuchsia.googlesource.com/build/+/43d2b55675d428d460fe6f91092bbf3c39552caf"
- sourceMap["buildtools"] = "https://fuchsia.googlesource.com/buildtools/+/master"
-
- expected := "https://fuchsia.googlesource.com/build/+/43d2b55675d428d460fe6f91092bbf3c39552caf/BUILD.gn#27"
- actual := sourceMap.GetSourceLink("//build/BUILD.gn", 27)
- if expected != actual {
- t.Fatalf("In TestNewSourceMap, expected \n%s but got \n%s", expected, actual)
- }
-
- expected = "https://fuchsia.googlesource.com/build/+/43d2b55675d428d460fe6f91092bbf3c39552caf/BUILD.gn"
- actual = sourceMap.GetSourceLink("//build/BUILD.gn", 0)
- if expected != actual {
- t.Fatalf("In TestNewSourceMap, expected \n%s but got \n%s", expected, actual)
- }
-
- expected = ""
- actual = sourceMap.GetSourceLink("//base/BUILD.gn", 0)
- if expected != actual {
- t.Fatalf("In TestNewSourceMap, expected \n%s but got \n%s", expected, actual)
- }
-}
diff --git a/build/godepfile/cmd/godepfile.go b/build/godepfile/cmd/godepfile.go
deleted file mode 100644
index 0793697..0000000
--- a/build/godepfile/cmd/godepfile.go
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2016 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.
-
-// Generates a depfile for a Go package that can be consumed by Ninja.
-package main
-
-import (
- "flag"
- "fmt"
- "go/build"
- "log"
- "os"
- "runtime"
- "sort"
- "strings"
- "sync"
-)
-
-type stringsFlag []string
-
-func (v *stringsFlag) String() string { return strings.Join(*v, " ") }
-
-func (v *stringsFlag) Set(s string) error {
- *v = strings.Split(s, " ")
- if *v == nil {
- *v = []string{}
- }
- return nil
-}
-
-var (
- ctx = build.Default
- output string
- test bool
-)
-
-func init() {
- flag.Var((*stringsFlag)(&ctx.BuildTags), "tags", "build tags")
- flag.StringVar(&output, "o", "", "name of the resulting executable")
- flag.BoolVar(&test, "test", false, "whether this is a test target")
-
- flag.Usage = func() {
- fmt.Fprintf(os.Stderr, "usage: godepfile [packages]\n")
- flag.PrintDefaults()
- }
-}
-
-func main() {
- flag.Parse()
-
- if len(flag.Args()) == 0 {
- flag.Usage()
- os.Exit(1)
- }
-
- var mu sync.Mutex
- deps := make(map[string]bool)
- paths := make(map[string]bool)
-
- fdlimit := make(chan struct{}, 128)
- var wg sync.WaitGroup
- var scan func(path, srcDir string)
- scan = func(path, srcDir string) {
- defer wg.Done()
-
- mu.Lock()
- _, done := paths[path]
- if !done {
- paths[path] = true
- }
- mu.Unlock()
-
- if done {
- return
- }
-
- if path == "C" {
- return
- }
-
- fdlimit <- struct{}{}
- defer func() { <-fdlimit }()
-
- pkg, err := ctx.Import(path, srcDir, 0)
- if err != nil {
- log.Fatalf("%s: %v", path, err)
- }
-
- var files []string
- srcdir := pkg.Dir + "/"
- files = appendAndPrefix(files, srcdir, pkg.GoFiles)
- files = appendAndPrefix(files, srcdir, pkg.CgoFiles)
- files = appendAndPrefix(files, srcdir, pkg.CFiles)
- files = appendAndPrefix(files, srcdir, pkg.CXXFiles)
- files = appendAndPrefix(files, srcdir, pkg.HFiles)
- files = appendAndPrefix(files, srcdir, pkg.SFiles)
- files = appendAndPrefix(files, srcdir, pkg.SwigFiles)
- files = appendAndPrefix(files, srcdir, pkg.SwigCXXFiles)
-
- if test {
- files = appendAndPrefix(files, srcdir, pkg.TestGoFiles)
- files = appendAndPrefix(files, srcdir, pkg.XTestGoFiles)
- }
-
- mu.Lock()
- for _, file := range files {
- deps[file] = true
- }
- mu.Unlock()
-
- if pkg.Name == "main" && output == "" {
- bindir := os.Getenv("GOBIN")
- if bindir == "" {
- bindir = pkg.BinDir
- }
- if ctx.GOOS == runtime.GOOS && ctx.GOARCH == runtime.GOARCH {
- output = ctx.JoinPath(bindir, path)
- } else {
- output = ctx.JoinPath(bindir, ctx.GOOS+"_"+ctx.GOARCH, path)
- }
- }
-
- for _, imp := range pkg.Imports {
- wg.Add(1)
- go scan(imp, pkg.Dir)
- }
- }
-
- for _, root := range flag.Args() {
- wg.Add(1)
- go scan(root, "")
- }
- wg.Wait()
-
- fmt.Printf("%s:", output)
- var depnames []string
- for path := range deps {
- depnames = append(depnames, path)
- }
- sort.Strings(depnames)
- for path := range deps {
- fmt.Printf(" %s", path)
- }
- fmt.Printf("\n")
-}
-
-func appendAndPrefix(slice []string, prefix string, src []string) []string {
- for _, s := range src {
- slice = append(slice, prefix+s)
- }
- return slice
-}
diff --git a/build/ninjalog/doc.go b/build/ninjalog/doc.go
deleted file mode 100644
index 4c2b075..0000000
--- a/build/ninjalog/doc.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2014 The Chromium 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 ninjalog provides ninja_log parser
-
-It support ninja log v5.
-
- # ninja log v5
- <start> <end> <restat> <target> <cmdhash>
-
-where
- <start> = start time since ninja starts in msec.
- <end> = end time since ninja starts in msec.
- <restat> = restat time in epoch.
- <target> = target (output) filename
- <cmdhash> = hash of command line (?)
-
-It assumes steps in the last build will be ascendent order of <end>.
-
-It also supports metadata added by chromium's buildbot compile.py.
-metadata is added after
-
- # end of ninja log
-
-and written in json format.
-
-*/
-package ninjalog
diff --git a/build/ninjalog/ninjalog.go b/build/ninjalog/ninjalog.go
deleted file mode 100644
index 09261ac..0000000
--- a/build/ninjalog/ninjalog.go
+++ /dev/null
@@ -1,449 +0,0 @@
-// Copyright 2014 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 ninjalog
-
-import (
- "bufio"
- "encoding/json"
- "fmt"
- "io"
- "sort"
- "strconv"
- "strings"
- "time"
-)
-
-// Step is one step in ninja_log file.
-// time is measured from ninja start time.
-type Step struct {
- Start time.Duration
- End time.Duration
- // modification time, but not convertable to absolute real time.
- // on POSIX, time_t is used, but on Windows different type is used.
- // htts://github.com/martine/ninja/blob/master/src/timestamp.h
- Restat int
- Out string
- CmdHash string
-
- // other outs for the same CmdHash if dedup'ed.
- Outs []string
-}
-
-// Duration reports step's duration.
-func (s Step) Duration() time.Duration {
- return s.End - s.Start
-}
-
-// Steps is a list of Step.
-// It could be used to sort by start time.
-type Steps []Step
-
-func (s Steps) Len() int { return len(s) }
-func (s Steps) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-func (s Steps) Less(i, j int) bool {
- if s[i].Start != s[j].Start {
- return s[i].Start < s[j].Start
- }
- if s[i].End != s[j].End {
- return s[i].End < s[j].End
- }
- return s[i].Out < s[j].Out
-}
-
-// Reverse reverses steps.
-// It would be more efficient if steps is already sorted than using sort.Reverse.
-func (s Steps) Reverse() {
- for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
- s[i], s[j] = s[j], s[i]
- }
-}
-
-// ByEnd is used to sort by end time.
-type ByEnd struct{ Steps }
-
-func (s ByEnd) Less(i, j int) bool { return s.Steps[i].End < s.Steps[j].End }
-
-// ByDuration is used to sort by duration.
-type ByDuration struct{ Steps }
-
-func (s ByDuration) Less(i, j int) bool { return s.Steps[i].Duration() < s.Steps[j].Duration() }
-
-// ByWeightedTime is used to sort by weighted time.
-type ByWeightedTime struct {
- Weighted map[string]time.Duration
- Steps
-}
-
-func (s ByWeightedTime) Less(i, j int) bool {
- return s.Weighted[s.Steps[i].Out] < s.Weighted[s.Steps[j].Out]
-}
-
-// Metadata is data added by compile.py.
-type Metadata struct {
- // Platform is platform of buildbot.
- Platform string `json:"platform"`
-
- // Argv is argv of compile.py
- Argv []string `json:"argv"`
-
- // Cwd is current working directory of compile.py
- Cwd string `json:"cwd"`
-
- // Compiler is compiler used.
- Compiler string `json:"compiler"`
-
- // Cmdline is command line of ninja.
- Cmdline []string `json:"cmdline"`
-
- // Exit is exit status of ninja.
- Exit int `json:"exit"`
-
- // Env is environment variables.
- Env map[string]string `json:"env"`
-
- // CompilerProxyInfo is a path name of associated compiler_proxy.INFO log.
- CompilerProxyInfo string `json:"compiler_proxy_info"`
-
- // Raw is raw string for metadata.
- Raw string
- // Error is error message of parsing metadata.
- Error string
-}
-
-// NinjaLog is parsed data of ninja_log file.
-type NinjaLog struct {
- // Filename is a filename of ninja_log.
- Filename string
-
- // Start is start line of the last build in ninja_log file.
- Start int
-
- // Steps contains steps in the last build in ninja_log file.
- Steps []Step
-
- // Metadata is additional data found in ninja_log file.
- Metadata Metadata
-}
-
-// Parse parses .ninja_log file, with chromium's compile.py metadata.
-func Parse(fname string, r io.Reader) (*NinjaLog, error) {
- b := bufio.NewReader(r)
- scanner := bufio.NewScanner(b)
- nlog := &NinjaLog{Filename: fname}
- lineno := 0
- if !scanner.Scan() {
- if err := scanner.Err(); err != nil {
- return nil, err
- }
- return nil, fmt.Errorf("empty file?")
- }
- lineno++
- line := scanner.Text()
- if line != "# ninja log v5" {
- return nil, fmt.Errorf("unexpected format: %s", line)
- }
- nlog.Start = lineno
- var lastStep Step
- for scanner.Scan() {
- line := scanner.Text()
- if line == "# end of ninja log" {
- break
- }
- if line == "" {
- continue
- }
- step, err := lineToStep(line)
- if err != nil {
- return nil, fmt.Errorf("error at %d: %v", lineno, err)
- }
- if step.End < lastStep.End {
- nlog.Start = lineno
- nlog.Steps = nil
- }
- nlog.Steps = append(nlog.Steps, step)
- lastStep = step
- lineno++
- }
- if err := scanner.Err(); err != nil {
- return nil, fmt.Errorf("error at %d: %v", lineno, err)
- }
- if !scanner.Scan() {
- if err := scanner.Err(); err != nil {
- return nil, fmt.Errorf("error at %d: %v", lineno, err)
- }
- // missing metadata?
- return nlog, nil
- }
- lineno++
- nlog.Metadata.Raw = scanner.Text()
- if err := parseMetadata([]byte(nlog.Metadata.Raw), &nlog.Metadata); err != nil {
- nlog.Metadata.Error = fmt.Sprintf("error at %d: %v", lineno, err)
- }
- return nlog, nil
-}
-
-func lineToStep(line string) (Step, error) {
- var step Step
-
- // Due to slowness of strings.Split in App Engine Go,
- // we use more faster implementation.
- fields := make([]string, 0, 5)
- for i := 0; i < 5; i += 1 {
- m := strings.IndexByte(line, '\t')
- if m < 0 {
- m = len(line)
- }
- fields = append(fields, line[:m])
- if m < len(line) {
- line = line[m+1:]
- }
- }
-
- if len(fields) < 5 {
- return step, fmt.Errorf("few fields:%d", len(fields))
- }
- s, err := strconv.ParseUint(fields[0], 10, 0)
- if err != nil {
- return step, fmt.Errorf("bad start %s:%v", fields[0], err)
- }
- e, err := strconv.ParseUint(fields[1], 10, 0)
- if err != nil {
- return step, fmt.Errorf("bad end %s:%v", fields[1], err)
- }
- rs, err := strconv.ParseUint(fields[2], 10, 0)
- if err != nil {
- return step, fmt.Errorf("bad restat %s:%v", fields[2], err)
- }
- step.Start = time.Duration(s) * time.Millisecond
- step.End = time.Duration(e) * time.Millisecond
- step.Restat = int(rs)
- step.Out = fields[3]
- step.CmdHash = fields[4]
- return step, nil
-}
-
-func stepToLine(s Step) string {
- return fmt.Sprintf("%d\t%d\t%d\t%s\t%s",
- s.Start.Nanoseconds()/int64(time.Millisecond),
- s.End.Nanoseconds()/int64(time.Millisecond),
- s.Restat,
- s.Out,
- s.CmdHash)
-}
-
-func parseMetadata(buf []byte, metadata *Metadata) error {
- return json.Unmarshal(buf, metadata)
-}
-
-// Dump dumps steps as ninja log v5 format in w.
-func Dump(w io.Writer, steps []Step) error {
- _, err := fmt.Fprintf(w, "# ninja log v5\n")
- if err != nil {
- return err
- }
- for _, s := range steps {
- _, err = fmt.Fprintln(w, stepToLine(s))
- if err != nil {
- return err
- }
- }
- return nil
-}
-
-// Dedup dedupes steps. step may have the same cmd hash.
-// Dedup only returns the first step for these steps.
-// steps will be sorted by start time.
-func Dedup(steps []Step) []Step {
- m := make(map[string]*Step)
- sort.Sort(Steps(steps))
- var dedup []Step
- for _, s := range steps {
- if os := m[s.CmdHash]; os != nil {
- os.Outs = append(os.Outs, s.Out)
- continue
- }
- dedup = append(dedup, s)
- m[s.CmdHash] = &dedup[len(dedup)-1]
- }
- return dedup
-}
-
-// TotalTime returns startup time and end time of ninja, and accumulated time
-// of all tasks.
-func TotalTime(steps []Step) (startupTime, endTime, cpuTime time.Duration) {
- if len(steps) == 0 {
- return 0, 0, 0
- }
- steps = Dedup(steps)
- startup := steps[0].Start
- var end time.Duration
- for _, s := range steps {
- if s.Start < startup {
- startup = s.Start
- }
- if s.End > end {
- end = s.End
- }
- cpuTime += s.Duration()
- }
- return startup, end, cpuTime
-}
-
-// Flow returns concurrent steps by time.
-// steps in every []Step will not have time overlap.
-// steps will be sorted by start time.
-func Flow(steps []Step) [][]Step {
- sort.Sort(Steps(steps))
- var threads [][]Step
-
- for _, s := range steps {
- tid := -1
- for i, th := range threads {
- if len(th) == 0 {
- panic(fmt.Errorf("thread %d has no entry", i))
- }
- if th[len(th)-1].End <= s.Start {
- tid = i
- break
- }
- }
- if tid == -1 {
- threads = append(threads, nil)
- tid = len(threads) - 1
- }
- threads[tid] = append(threads[tid], s)
- }
- return threads
-}
-
-// action represents an event's action. "start" or "end".
-type action string
-
-const (
- unknownAction action = ""
- startAction action = "start"
- stopAction action = "stop"
-)
-
-// event is an event of steps.
-type event struct {
- time time.Duration
- action action
- target string
-}
-
-// toEvent converts steps into events.
-// events are sorted by its time.
-func toEvent(steps []Step) []event {
- var events []event
- for _, s := range steps {
- events = append(events,
- event{
- time: s.Start,
- action: startAction,
- target: s.Out,
- },
- event{
- time: s.End,
- action: stopAction,
- target: s.Out,
- },
- )
- }
- sort.Slice(events, func(i, j int) bool {
- if events[i].time == events[j].time {
- // If a task starts and stops on the same time stamp
- // then the start will come first.
- return events[i].action < events[j].action
- }
- return events[i].time < events[j].time
- })
- return events
-}
-
-// WeightedTime calculates weighted time, which is elapsed time with
-// each segment divided by the number of tasks that were running in paralle.
-// This makes it a much better approximation of how "important" a slow step was.
-// For example, A link that is entirely or mostly serialized will have a
-// weighted time that is the same or similar to its elapsed time.
-// A compile that runs in parallel with 999 other compiles will have a weighted
-// time that is tiny.
-func WeightedTime(steps []Step) map[string]time.Duration {
- if len(steps) == 0 {
- return nil
- }
- steps = Dedup(steps)
- events := toEvent(steps)
- weightedDuration := make(map[string]time.Duration)
-
- // Track the tasks which are currently running.
- runningTasks := make(map[string]time.Duration)
-
- // Record the time we have processed up to so we know how to calculate
- // time deltas.
- lastTime := events[0].time
-
- // Track the accumulated weighted time so that it can efficiently be
- // added to individual tasks.
- var lastWeightedTime time.Duration
-
- for _, event := range events {
- numRunning := len(runningTasks)
- if numRunning > 0 {
- // Update the total weighted time up to this moment.
- lastWeightedTime += (event.time - lastTime) / time.Duration(numRunning)
- }
- switch event.action {
- case startAction:
- // Record the total weighted task time when this task starts.
- runningTasks[event.target] = lastWeightedTime
- case stopAction:
- // Record the change in the total weighted task time while this task ran.
- weightedDuration[event.target] = lastWeightedTime - runningTasks[event.target]
- delete(runningTasks, event.target)
- }
- lastTime = event.time
- }
- return weightedDuration
-}
-
-// Stat represents statistics for build step.
-type Stat struct {
- Type string
- Count int
- Time time.Duration
- Weighted time.Duration
-}
-
-// StatsByType summarizes build step statistics with weighted and typeOf.
-// Stats is sorted by Weighted, longer first.
-func StatsByType(steps []Step, weighted map[string]time.Duration, typeOf func(Step) string) []Stat {
- if len(steps) == 0 {
- return nil
- }
- steps = Dedup(steps)
- m := make(map[string]int) // type to index of stats.
- var stats []Stat
- for _, step := range steps {
- t := typeOf(step)
- if i, ok := m[t]; ok {
- stats[i].Count++
- stats[i].Time += step.Duration()
- stats[i].Weighted += weighted[step.Out]
- continue
- }
- stats = append(stats, Stat{
- Type: t,
- Count: 1,
- Time: step.Duration(),
- Weighted: weighted[step.Out],
- })
- m[t] = len(stats) - 1
- }
- sort.Slice(stats, func(i, j int) bool {
- return stats[i].Weighted > stats[j].Weighted
- })
- return stats
-}
diff --git a/build/ninjalog/ninjalog_test.go b/build/ninjalog/ninjalog_test.go
deleted file mode 100644
index bc5306d..0000000
--- a/build/ninjalog/ninjalog_test.go
+++ /dev/null
@@ -1,553 +0,0 @@
-// Copyright 2014 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 ninjalog
-
-import (
- "bytes"
- "io/ioutil"
- "reflect"
- "sort"
- "strings"
- "testing"
- "time"
-)
-
-var (
- logTestCase = `# ninja log v5
-76 187 0 resources/inspector/devtools_extension_api.js 75430546595be7c2
-80 284 0 gen/autofill_regex_constants.cc fa33c8d7ce1d8791
-78 286 0 gen/angle/commit_id.py 4ede38e2c1617d8c
-79 287 0 gen/angle/copy_compiler_dll.bat 9fb635ad5d2c1109
-141 287 0 PepperFlash/manifest.json 324f0a0b77c37ef
-142 288 0 PepperFlash/libpepflashplayer.so 1e2c2b7845a4d4fe
-287 290 0 obj/third_party/angle/src/copy_scripts.actions_rules_copies.stamp b211d373de72f455
-`
-
- stepsTestCase = []Step{
- Step{
- Start: 76 * time.Millisecond,
- End: 187 * time.Millisecond,
- Out: "resources/inspector/devtools_extension_api.js",
- CmdHash: "75430546595be7c2",
- },
- Step{
- Start: 80 * time.Millisecond,
- End: 284 * time.Millisecond,
- Out: "gen/autofill_regex_constants.cc",
- CmdHash: "fa33c8d7ce1d8791",
- },
- Step{
- Start: 78 * time.Millisecond,
- End: 286 * time.Millisecond,
- Out: "gen/angle/commit_id.py",
- CmdHash: "4ede38e2c1617d8c",
- },
- Step{
- Start: 79 * time.Millisecond,
- End: 287 * time.Millisecond,
- Out: "gen/angle/copy_compiler_dll.bat",
- CmdHash: "9fb635ad5d2c1109",
- },
- Step{
- Start: 141 * time.Millisecond,
- End: 287 * time.Millisecond,
- Out: "PepperFlash/manifest.json",
- CmdHash: "324f0a0b77c37ef",
- },
- Step{
- Start: 142 * time.Millisecond,
- End: 288 * time.Millisecond,
- Out: "PepperFlash/libpepflashplayer.so",
- CmdHash: "1e2c2b7845a4d4fe",
- },
- Step{
- Start: 287 * time.Millisecond,
- End: 290 * time.Millisecond,
- Out: "obj/third_party/angle/src/copy_scripts.actions_rules_copies.stamp",
- CmdHash: "b211d373de72f455",
- },
- }
-
- stepsSorted = []Step{
- Step{
- Start: 76 * time.Millisecond,
- End: 187 * time.Millisecond,
- Out: "resources/inspector/devtools_extension_api.js",
- CmdHash: "75430546595be7c2",
- },
- Step{
- Start: 78 * time.Millisecond,
- End: 286 * time.Millisecond,
- Out: "gen/angle/commit_id.py",
- CmdHash: "4ede38e2c1617d8c",
- },
- Step{
- Start: 79 * time.Millisecond,
- End: 287 * time.Millisecond,
- Out: "gen/angle/copy_compiler_dll.bat",
- CmdHash: "9fb635ad5d2c1109",
- },
- Step{
- Start: 80 * time.Millisecond,
- End: 284 * time.Millisecond,
- Out: "gen/autofill_regex_constants.cc",
- CmdHash: "fa33c8d7ce1d8791",
- },
- Step{
- Start: 141 * time.Millisecond,
- End: 287 * time.Millisecond,
- Out: "PepperFlash/manifest.json",
- CmdHash: "324f0a0b77c37ef",
- },
- Step{
- Start: 142 * time.Millisecond,
- End: 288 * time.Millisecond,
- Out: "PepperFlash/libpepflashplayer.so",
- CmdHash: "1e2c2b7845a4d4fe",
- },
- Step{
- Start: 287 * time.Millisecond,
- End: 290 * time.Millisecond,
- Out: "obj/third_party/angle/src/copy_scripts.actions_rules_copies.stamp",
- CmdHash: "b211d373de72f455",
- },
- }
-
- metadataTestCase = Metadata{
- Platform: "linux",
- Argv: []string{"../../../scripts/slave/compile.py", "--target", "Release", "--clobber", "--compiler=goma", "--", "all"},
- Cwd: "/b/build/slave/Linux_x64/build/src",
- Compiler: "goma",
- Cmdline: []string{"ninja", "-C", "/b/build/slave/Linux_x64/build/src/out/Release", "all", "-j50"},
- Exit: 0,
- Env: map[string]string{
- "LANG": "en_US.UTF-8",
- "SHELL": "/bin/bash",
- "HOME": "/home/chrome-bot",
- "PWD": "/b/build/slave/Linux_x64/build",
- "LOGNAME": "chrome-bot",
- "USER": "chrome-bot",
- "PATH": "/home/chrome-bot/slavebin:/b/depot_tools:/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin",
- },
- CompilerProxyInfo: "/tmp/compiler_proxy.build48-m1.chrome-bot.log.INFO.20140907-203827.14676",
- }
-)
-
-func TestStepsSort(t *testing.T) {
- steps := append([]Step{}, stepsTestCase...)
- sort.Sort(Steps(steps))
- if !reflect.DeepEqual(steps, stepsSorted) {
- t.Errorf("sort Steps=%v; want=%v", steps, stepsSorted)
- }
-}
-
-func TestStepsReverse(t *testing.T) {
- steps := []Step{
- Step{Out: "0"},
- Step{Out: "1"},
- Step{Out: "2"},
- Step{Out: "3"},
- }
- Steps(steps).Reverse()
- want := []Step{
- Step{Out: "3"},
- Step{Out: "2"},
- Step{Out: "1"},
- Step{Out: "0"},
- }
- if !reflect.DeepEqual(steps, want) {
- t.Errorf("steps.Reverse=%v; want=%v", steps, want)
- }
-}
-
-func TestParseBadVersion(t *testing.T) {
- _, err := Parse(".ninja_log", strings.NewReader(`# ninja log v4
-0 1 0 foo touch foo
-`))
- if err == nil {
- t.Error("Parse()=_, <nil>; want=_, error")
- }
-}
-
-func TestParseSimple(t *testing.T) {
- njl, err := Parse(".ninja_log", strings.NewReader(logTestCase))
- if err != nil {
- t.Errorf(`Parse()=_, %v; want=_, <nil>`, err)
- }
-
- want := &NinjaLog{
- Filename: ".ninja_log",
- Start: 1,
- Steps: stepsTestCase,
- }
- if !reflect.DeepEqual(njl, want) {
- t.Errorf("Parse()=%v; want=%v", njl, want)
- }
-}
-
-func TestParseEmptyLine(t *testing.T) {
- njl, err := Parse(".ninja_log", strings.NewReader(logTestCase+"\n"))
- if err != nil {
- t.Errorf(`Parse()=_, %v; want=_, <nil>`, err)
- }
- want := &NinjaLog{
- Filename: ".ninja_log",
- Start: 1,
- Steps: stepsTestCase,
- }
- if !reflect.DeepEqual(njl, want) {
- t.Errorf("Parse()=%v; want=%v", njl, want)
- }
-}
-
-func TestParseLast(t *testing.T) {
- njl, err := Parse(".ninja_log", strings.NewReader(`# ninja log v5
-1020807 1020916 0 chrome.1 e101fd46be020cfc
-84 9489 0 gen/libraries.cc 9001f3182fa8210e
-1024369 1041522 0 chrome aee9d497d56c9637
-76 187 0 resources/inspector/devtools_extension_api.js 75430546595be7c2
-80 284 0 gen/autofill_regex_constants.cc fa33c8d7ce1d8791
-78 286 0 gen/angle/commit_id.py 4ede38e2c1617d8c
-79 287 0 gen/angle/copy_compiler_dll.bat 9fb635ad5d2c1109
-141 287 0 PepperFlash/manifest.json 324f0a0b77c37ef
-142 288 0 PepperFlash/libpepflashplayer.so 1e2c2b7845a4d4fe
-287 290 0 obj/third_party/angle/src/copy_scripts.actions_rules_copies.stamp b211d373de72f455
-`))
- if err != nil {
- t.Errorf(`Parse()=_, %v; want=_, <nil>`, err)
- }
-
- want := &NinjaLog{
- Filename: ".ninja_log",
- Start: 4,
- Steps: stepsTestCase,
- }
- if !reflect.DeepEqual(njl, want) {
- t.Errorf("Parse()=%v; want=%v", njl, want)
- }
-}
-
-func TestParseMetadata(t *testing.T) {
- njl, err := Parse(".ninja_log", strings.NewReader(`# ninja log v5
-1020807 1020916 0 chrome.1 e101fd46be020cfc
-84 9489 0 gen/libraries.cc 9001f3182fa8210e
-1024369 1041522 0 chrome aee9d497d56c9637
-76 187 0 resources/inspector/devtools_extension_api.js 75430546595be7c2
-80 284 0 gen/autofill_regex_constants.cc fa33c8d7ce1d8791
-78 286 0 gen/angle/commit_id.py 4ede38e2c1617d8c
-79 287 0 gen/angle/copy_compiler_dll.bat 9fb635ad5d2c1109
-141 287 0 PepperFlash/manifest.json 324f0a0b77c37ef
-142 288 0 PepperFlash/libpepflashplayer.so 1e2c2b7845a4d4fe
-287 290 0 obj/third_party/angle/src/copy_scripts.actions_rules_copies.stamp b211d373de72f455
-
-# end of ninja log
-{"platform": "linux", "argv": ["../../../scripts/slave/compile.py", "--target", "Release", "--clobber", "--compiler=goma", "--", "all"], "cmdline": ["ninja", "-C", "/b/build/slave/Linux_x64/build/src/out/Release", "all", "-j50"], "exit": 0, "env": {"LANG": "en_US.UTF-8", "SHELL": "/bin/bash", "HOME": "/home/chrome-bot", "PWD": "/b/build/slave/Linux_x64/build", "LOGNAME": "chrome-bot", "USER": "chrome-bot", "PATH": "/home/chrome-bot/slavebin:/b/depot_tools:/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin" }, "compiler_proxy_info": "/tmp/compiler_proxy.build48-m1.chrome-bot.log.INFO.20140907-203827.14676", "cwd": "/b/build/slave/Linux_x64/build/src", "compiler": "goma"}
-`))
- if err != nil {
- t.Errorf(`Parse()=_, %#v; want=_, <nil>`, err)
- }
-
- want := &NinjaLog{
- Filename: ".ninja_log",
- Start: 4,
- Steps: stepsTestCase,
- Metadata: metadataTestCase,
- }
- njl.Metadata.Raw = ""
- if !reflect.DeepEqual(njl, want) {
- t.Errorf("Parse()=%#v; want=%#v", njl, want)
- }
-}
-
-func TestParseBadMetadata(t *testing.T) {
- // https://bugs.chromium.org/p/chromium/issues/detail?id=667571
- njl, err := Parse(".ninja_log", strings.NewReader(`# ninja log v5
-1020807 1020916 0 chrome.1 e101fd46be020cfc
-84 9489 0 gen/libraries.cc 9001f3182fa8210e
-1024369 1041522 0 chrome aee9d497d56c9637
-76 187 0 resources/inspector/devtools_extension_api.js 75430546595be7c2
-80 284 0 gen/autofill_regex_constants.cc fa33c8d7ce1d8791
-78 286 0 gen/angle/commit_id.py 4ede38e2c1617d8c
-79 287 0 gen/angle/copy_compiler_dll.bat 9fb635ad5d2c1109
-141 287 0 PepperFlash/manifest.json 324f0a0b77c37ef
-142 288 0 PepperFlash/libpepflashplayer.so 1e2c2b7845a4d4fe
-287 290 0 obj/third_party/angle/src/copy_scripts.actions_rules_copies.stamp b211d373de72f455
-# end of ninja log
-{"platform": "linux", "argv": ["/b/build/scripts/slave/upload_goma_logs.py", "--upload-compiler-proxy-info", "--json-status", "/b/build/slave/cache/cipd/goma/jsonstatus", "--ninja-log-outdir", "/b/build/slave/pdfium/build/pdfium/out/debug_xfa_v8", "--ninja-log-compiler", "unknown", "--ninja-log-command", "['ninja', '-C', Path('checkout', 'out','debug_xfa_v8'), '-j', 80]", "--ninja-log-exit-status", "0", "--goma-stats-file", "/b/build/slave/pdfium/.recipe_runtime/tmpOgwx97/build_data/goma_stats_proto", "--goma-crash-report-id-file", "/b/build/slave/pdfium/.recipe_runtime/tmpOgwx97/build_data/crash_report_id_file", "--build-data-dir", "/b/build/slave/pdfium/.recipe_runtime/tmpOgwx97/build_data", "--buildbot-buildername", "linux_xfa", "--buildbot-mastername", "tryserver.client.pdfium", "--buildbot-slavename", "slave1386-c4"], "cmdline": "['ninja', '-C', Path('checkout','out','debug_xfa_v8'), '-j', 80]", "exit": 0, "env": {"GOMA_SERVICE_ACCOUNT_JSON_FILE": "/creds/service_accounts/service-account-goma-client.json", "BUILDBOT_BUILDERNAME": "linux_xfa", "USER": "chrome-bot", "HOME": "/home/chrome-bot", "BOTO_CONFIG": "/b/build/scripts/slave/../../site_config/.boto", "PATH": "/home/chrome-bot/slavebin:/b/depot_tools:/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin", "PYTHONUNBUFFERED": "1", "BUILDBOT_BUILDBOTURL": "https://build.chromium.org/p/tryserver.client.pdfium/", "DISPLAY": ":0.0", "LANG": "en_US.UTF-8", "BUILDBOT_BLAMELIST": "[u'dsinclair@chromium.org']", "BUILDBOT_MASTERNAME": "tryserver.client.pdfium", "GOMACTL_CRASH_REPORT_ID_FILE": "/b/build/slave/pdfium/.recipe_runtime/tmpOgwx97/build_data/crash_report_id_file", "USERNAME": "chrome-bot", "BUILDBOT_GOT_REVISION": "None", "PYTHONPATH": "/b/build/site_config:/b/build/scripts:/b/build/scripts/release:/b/build/third_party:/b/build/third_party/requests_2_10_0:/b/build_internal/site_config:/b/build_internal/symsrc:/b/build/slave:/b/build/third_party/buildbot_slave_8_4:/b/build/third_party/twisted_10_2:", "BUILDBOT_SCHEDULER": "None", "BUILDBOT_REVISION": "", "AWS_CREDENTIAL_FILE": "/b/build/scripts/slave/../../site_config/.boto", "CHROME_HEADLESS": "1", "BUILDBOT_BRANCH": "", "GIT_USER_AGENT": "linux2 git/2.10.2 slave1386-c4.c.chromecompute.google.com.internal", "TESTING_SLAVENAME": "slave1386-c4", "GOMA_DUMP_STATS_FILE": "/b/build/slave/pdfium/.recipe_runtime/tmpOgwx97/build_data/goma_stats_proto", "BUILDBOT_BUILDNUMBER": "2937", "PWD": "/b/build/slave/pdfium/build", "BUILDBOT_SLAVENAME": "slave1386-c4", "BUILDBOT_CLOBBER": "", "PAGER": "cat"}, "compiler_proxy_info": "/tmp/compiler_proxy.slave1386-c4.chrome-bot.log.INFO.20161121-165459.5790", "cwd": "/b/build/slave/pdfium/build", "compiler": "unknown"}
-`))
- if err != nil {
- t.Errorf(`Parse()=_, %#v; want=_, <nil>`, err)
- }
-
- if njl.Metadata.Error == "" {
- t.Errorf("Parse().Metadata.Error='', want some error")
- }
- njl.Metadata = Metadata{}
-
- want := &NinjaLog{
- Filename: ".ninja_log",
- Start: 4,
- Steps: stepsTestCase,
- }
- if !reflect.DeepEqual(njl, want) {
- t.Errorf("Parse()=%#v; want=%#v", njl, want)
- }
-}
-
-func TestDump(t *testing.T) {
- var b bytes.Buffer
- err := Dump(&b, stepsTestCase)
- if err != nil {
- t.Errorf("Dump()=%v; want=<nil>", err)
- }
- if b.String() != logTestCase {
- t.Errorf("Dump %q; want %q", b.String(), logTestCase)
- }
-}
-
-func TestDedup(t *testing.T) {
- steps := append([]Step{}, stepsTestCase...)
- for _, out := range []string{
- "gen/ui/keyboard/webui/keyboard.mojom.cc",
- "gen/ui/keyboard/webui/keyboard.mojom.h",
- "gen/ui/keyboard/webui/keyboard.mojom.js",
- "gen/ui/keyboard/webui/keyboard.mojom-internal.h",
- } {
- steps = append(steps, Step{
- Start: 302 * time.Millisecond,
- End: 5764 * time.Millisecond,
- Out: out,
- CmdHash: "a551cc46f8c21e5a",
- })
- }
- got := Dedup(steps)
- want := append([]Step{}, stepsSorted...)
- want = append(want, Step{
- Start: 302 * time.Millisecond,
- End: 5764 * time.Millisecond,
- Out: "gen/ui/keyboard/webui/keyboard.mojom-internal.h",
- Outs: []string{
- "gen/ui/keyboard/webui/keyboard.mojom.cc",
- "gen/ui/keyboard/webui/keyboard.mojom.h",
- "gen/ui/keyboard/webui/keyboard.mojom.js",
- },
- CmdHash: "a551cc46f8c21e5a",
- })
- if !reflect.DeepEqual(got, want) {
- t.Errorf("Dedup=%v; want=%v", got, want)
- }
-}
-
-func TestFlow(t *testing.T) {
- steps := append([]Step{}, stepsTestCase...)
- steps = append(steps, Step{
- Start: 187 * time.Millisecond,
- End: 21304 * time.Millisecond,
- Out: "obj/third_party/pdfium/core/src/fpdfdoc/fpdfdoc.doc_formfield.o",
- CmdHash: "2ac7111aa1ae86af",
- })
-
- flow := Flow(steps)
-
- want := [][]Step{
- []Step{
- Step{
- Start: 76 * time.Millisecond,
- End: 187 * time.Millisecond,
- Out: "resources/inspector/devtools_extension_api.js",
- CmdHash: "75430546595be7c2",
- },
- Step{
- Start: 187 * time.Millisecond,
- End: 21304 * time.Millisecond,
- Out: "obj/third_party/pdfium/core/src/fpdfdoc/fpdfdoc.doc_formfield.o",
- CmdHash: "2ac7111aa1ae86af",
- },
- },
- []Step{
- Step{
- Start: 78 * time.Millisecond,
- End: 286 * time.Millisecond,
- Out: "gen/angle/commit_id.py",
- CmdHash: "4ede38e2c1617d8c",
- },
- Step{
- Start: 287 * time.Millisecond,
- End: 290 * time.Millisecond,
- Out: "obj/third_party/angle/src/copy_scripts.actions_rules_copies.stamp",
- CmdHash: "b211d373de72f455",
- },
- },
- []Step{
- Step{
- Start: 79 * time.Millisecond,
- End: 287 * time.Millisecond,
- Out: "gen/angle/copy_compiler_dll.bat",
- CmdHash: "9fb635ad5d2c1109",
- },
- },
- []Step{
- Step{
- Start: 80 * time.Millisecond,
- End: 284 * time.Millisecond,
- Out: "gen/autofill_regex_constants.cc",
- CmdHash: "fa33c8d7ce1d8791",
- },
- },
- []Step{
- Step{
- Start: 141 * time.Millisecond,
- End: 287 * time.Millisecond,
- Out: "PepperFlash/manifest.json",
- CmdHash: "324f0a0b77c37ef",
- },
- },
- []Step{
- Step{
- Start: 142 * time.Millisecond,
- End: 288 * time.Millisecond,
- Out: "PepperFlash/libpepflashplayer.so",
- CmdHash: "1e2c2b7845a4d4fe",
- },
- },
- }
-
- if !reflect.DeepEqual(flow, want) {
- t.Errorf("Flow()=%v; want=%v", flow, want)
- }
-}
-
-func TestWeightedTime(t *testing.T) {
- steps := []Step{
- Step{
- Start: 0 * time.Millisecond,
- End: 3 * time.Millisecond,
- Out: "target-a",
- CmdHash: "hash-target-a",
- },
- Step{
- Start: 2 * time.Millisecond,
- End: 5 * time.Millisecond,
- Out: "target-b",
- CmdHash: "hash-target-b",
- },
- Step{
- Start: 2 * time.Millisecond,
- End: 8 * time.Millisecond,
- Out: "target-c",
- CmdHash: "hash-target-c",
- },
- Step{
- Start: 2 * time.Millisecond,
- End: 3 * time.Millisecond,
- Out: "target-d",
- CmdHash: "hash-target-d",
- },
- }
-
- // 0 1 2 3 4 5 6 7 8
- // +-+-+-+-+-+-+-+-+
- // <--A-->
- // <--B-->
- // <------C---->
- // <D>
- got := WeightedTime(steps)
- want := map[string]time.Duration{
- "target-a": 2*time.Millisecond + 1*time.Millisecond/4,
- "target-b": 1*time.Millisecond/4 + 2*time.Millisecond/2,
- "target-c": 1*time.Millisecond/4 + 2*time.Millisecond/2 + 3*time.Millisecond,
- "target-d": 1 * time.Millisecond / 4,
- }
- if !reflect.DeepEqual(got, want) {
- t.Errorf("WeightedTime(%v)=%v; want=%v", steps, got, want)
- }
-}
-
-func BenchmarkParse(b *testing.B) {
- data, err := ioutil.ReadFile("testdata/ninja_log")
- if err != nil {
- b.Errorf(`ReadFile("testdata/ninja_log")=_, %v; want_, <nil>`, err)
- }
-
- for i := 0; i < b.N; i++ {
- _, err := Parse(".ninja_log", bytes.NewReader(data))
- if err != nil {
- b.Errorf(`Parse()=_, %v; want=_, <nil>`, err)
- }
- }
-}
-
-func BenchmarkDedup(b *testing.B) {
- data, err := ioutil.ReadFile("testdata/ninja_log")
- if err != nil {
- b.Errorf(`ReadFile("testdata/ninja_log")=_, %v; want_, <nil>`, err)
- }
-
- njl, err := Parse(".ninja_log", bytes.NewReader(data))
- if err != nil {
- b.Errorf(`Parse()=_, %v; want=_, <nil>`, err)
- }
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- steps := make([]Step, len(njl.Steps))
- copy(steps, njl.Steps)
- Dedup(steps)
- }
-}
-
-func BenchmarkFlow(b *testing.B) {
- data, err := ioutil.ReadFile("testdata/ninja_log")
- if err != nil {
- b.Errorf(`ReadFile("testdata/ninja_log")=_, %v; want_, <nil>`, err)
- }
-
- njl, err := Parse(".ninja_log", bytes.NewReader(data))
- if err != nil {
- b.Errorf(`Parse()=_, %v; want=_, <nil>`, err)
- }
- steps := Dedup(njl.Steps)
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- flowInput := make([]Step, len(steps))
- copy(flowInput, steps)
- Flow(flowInput)
- }
-}
-
-func BenchmarkToTraces(b *testing.B) {
- data, err := ioutil.ReadFile("testdata/ninja_log")
- if err != nil {
- b.Errorf(`ReadFile("testdata/ninja_log")=_, %v; want_, <nil>`, err)
- }
-
- njl, err := Parse(".ninja_log", bytes.NewReader(data))
- if err != nil {
- b.Errorf(`Parse()=_, %v; want=_, <nil>`, err)
- }
- steps := Dedup(njl.Steps)
- flow := Flow(steps)
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- ToTraces(flow, 1)
- }
-}
-
-func BenchmarkDedupFlowToTraces(b *testing.B) {
- data, err := ioutil.ReadFile("testdata/ninja_log")
- if err != nil {
- b.Errorf(`ReadFile("testdata/ninja_log")=_, %v; want_, <nil>`, err)
- }
-
- for i := 0; i < b.N; i++ {
- njl, err := Parse(".ninja_log", bytes.NewReader(data))
- if err != nil {
- b.Errorf(`Parse()=_, %v; want=_, <nil>`, err)
- }
-
- steps := Dedup(njl.Steps)
- flow := Flow(steps)
- ToTraces(flow, 1)
- }
-}
diff --git a/build/ninjalog/testdata/ninja_log b/build/ninjalog/testdata/ninja_log
deleted file mode 100644
index ac90cab..0000000
--- a/build/ninjalog/testdata/ninja_log
+++ /dev/null
@@ -1,326 +0,0 @@
-# ninja log v5
-932 1887 0 obj/aura_builder.stamp 7f299500e7cc72e4
-933 1887 0 obj/chromium_builder_tests.stamp 3b269cc5e106d1cb
-935 1889 0 obj/chromium_swarm_tests.stamp 228817dbc2838ed
-935 1889 0 obj/breakpad/stackwalk_common/ia32_operand.o e910d286882099db
-936 1890 0 obj/breakpad/stackwalk_common/ia32_implicit.o 5f70baee81295512
-936 1891 0 obj/breakpad/stackwalk_common/ia32_insn.o f5a4396053248fe2
-940 1892 0 obj/breakpad/stackwalk_common/ia32_settings.o defcb1700387b35b
-941 1900 0 obj/breakpad/stackwalk_common/x86_imm.o a9d30af1b7950290
-944 1901 0 obj/build/config/posix/posix.stamp 6d0ca2a0eaf18f67
-944 1902 0 obj/build/config/nacl/nacl_base.stamp ef27934affc9c112
-945 1903 0 obj/build/config/sanitizers/deps_no_options.stamp 5a49d397d6442ac3
-947 1903 0 obj/build/config/linux/linux.stamp f77230b5eb5afaf2
-947 1910 0 obj/build/config/linux/gtk3/gtk3.stamp 9b784fd2e50164d4
-947 1910 0 obj/build/config/linux/gtk3/gtkprint3.stamp 630941a61f5a6923
-948 1914 0 obj/base/base_static/switches.o b013922ef6f8aaab
-948 1915 0 gen/base/histogram_unittest_nc.cc dd29c0c48a525644
-949 1915 0 gen/base/callback_unittest_nc.cc 475066177ce401ff
-949 1916 0 gen/base/callback_list_unittest_nc.cc 16824df464b694f
-953 1920 0 obj/base/test/native_library_test_utils/native_library_test_utils.o c33b12415d139603
-953 1920 0 obj/base/allocator/tcmalloc/low_level_alloc.o 1fc5cc88e3e16e23
-957 1921 0 obj/base/allocator/tcmalloc/sysinfo.o 45a91a4d87d1aa9e
-957 1922 0 obj/base/allocator/tcmalloc/vdso_support.o 3e46bc0f32a5f33c
-962 1922 0 obj/base/allocator/tcmalloc/common.o 13a060d08e39d509
-962 1923 0 obj/base/allocator/tcmalloc/free_list.o 4e42356197e89a16
-965 1925 0 obj/base/allocator/tcmalloc/malloc_extension.o 76679f18029232e7
-965 1925 0 obj/base/allocator/tcmalloc/malloc_hook.o c627b7675ebfb1b0
-968 1925 0 obj/base/allocator/tcmalloc/page_heap.o d94de6badd6f30e6
-968 1926 0 obj/base/allocator/tcmalloc/raw_printer.o 9a2e6e0c1a365182
-968 1927 0 obj/base/allocator/tcmalloc/sampler.o ab3fb9ade346e5bc
-969 1927 0 obj/base/allocator/tcmalloc/stacktrace.o 7ac392099df854df
-969 1928 0 obj/base/allocator/tcmalloc/static_vars.o 5a7c9b34e682f72f
-973 1929 0 obj/base/allocator/tcmalloc/system-alloc.o f7e40e31b72b4fed
-977 1929 0 gen/base/debug/debugging_flags.h 9596e291177bcaa7
-977 1932 0 obj/base/base_unittests_bundle_data.stamp ccc2010a4884995f
-980 1932 0 obj/base/build_date.inputdeps.stamp 44b53a9dd8e06c36
-980 1932 0 gen/base/allocator/features.h f9894386c01c41c2
-980 1935 0 obj/base/allocator/tcmalloc/spinlock_internal.o 52eb3163ecfc72c
-980 1935 0 obj/base/allocator/tcmalloc/abort.o 318d8d2c8df20f6c
-987 1935 0 obj/base/allocator/tcmalloc/atomicops-internals-x86.o 3d3c2a4b6e9e91fa
-1005 1936 0 obj/base/allocator/tcmalloc/logging.o 861b3c21231da430
-1023 1937 0 obj/base/third_party/xdg_mime/xdg_mime/xdgmimeicon.o 64baf98d444024dd
-1029 1937 0 obj/base/third_party/libevent/libevent/strlcpy.o be9686387d30188e
-1171 1938 0 minidump_fuzzer.dict 2f759dc54b10c5a5
-1195 1939 0 minidump_fuzzer.options 12fb1704c79cd108
-1204 1940 0 gen/library_loaders/libgio.h 3f42a994f91c90ad
-1204 1940 0 gen/library_loaders/libgio_loader.cc 3f42a994f91c90ad
-1220 1944 0 obj/breakpad/stackwalk_common/dump_object.o e6678006ee215cf6
-1253 1944 0 obj/breakpad/stackwalk_common/stackwalker_sparc.o 634f5cec2de7ad50
-1253 1944 0 chrome.VisualElementsManifest.xml 11019a3b23390133
-1258 1944 0 gen/library_loaders/libudev0.h 6e9ea2f9a6d08cc5
-1258 1944 0 gen/library_loaders/libudev0_loader.cc 6e9ea2f9a6d08cc5
-1259 1945 0 gen/library_loaders/libudev1.h 5cff2c8da0269aa3
-1259 1945 0 gen/library_loaders/libudev1_loader.cc 5cff2c8da0269aa3
-1259 1945 0 obj/build/util/chrome_version_json.inputdeps.stamp 4a98b379bd3a95e1
-1259 1945 0 obj/build/util/webkit_version.inputdeps.stamp b4f2c6e2e759ee6d
-1259 1945 0 obj/build/win/default_exe_manifest.stamp 5749e6404e11fdd6
-1259 1947 0 obj/cc/ipc/interfaces__is_mojom.stamp 4f4afff0f6093dda
-1259 1948 0 obj/chrome/browser/preferences_manifest.inputdeps.stamp 373d9f2344667069
-1261 1948 0 obj/cc/ipc/test_interfaces__is_mojom.stamp 11f28ad8101031f9
-1268 1948 0 product_logo_48.png b4486b27ac8de19e
-1269 1950 0 chrome-wrapper c30fdbd2f2925001
-1270 1951 0 obj/chrome/manpage.inputdeps.stamp 307252fd80755cae
-1271 1951 0 xdg-mime 472f1619df53cd2b
-1272 1951 0 xdg-settings 325658a140ad44c1
-1272 1952 0 obj/chrome/app/chrome_content_gpu_manifest__is_service_manifest.stamp bc54c4907b6aae93
-1273 1952 0 obj/chrome/app/chrome_content_browser_manifest_overlay__is_service_manifest.stamp f6b6ad1752883df7
-1273 1952 0 obj/chrome/app/chrome_content_browser_manifest__is_service_manifest.stamp 679b1f0fffefa65f
-1273 1954 0 obj/chrome/app/chrome_content_gpu_manifest_overlay.inputdeps.stamp 87835f17ee6d9ad8
-1273 1954 0 obj/chrome/app/chrome_content_gpu_manifest_overlay__is_service_manifest.stamp b8219a49d409a88e
-1274 1955 0 obj/chrome/app/chrome_content_packaged_services_manifest__is_service_manifest.stamp 162af6abc2708239
-1274 1956 0 obj/chrome/app/chrome_content_packaged_services_manifest_for_mash__is_service_manifest.stamp b1395a7bf61f2e1a
-1274 1956 0 obj/chrome/app/chrome_content_packaged_services_manifest_overlay__is_service_manifest.stamp da61f69dd6c92351
-1274 1956 0 obj/chrome/app/chrome_content_packaged_services_manifest_overlay_for_mash__is_service_manifest.stamp 2a19ea80ead0568a
-1275 1957 0 obj/chrome/app/chrome_content_plugin_manifest_overlay.inputdeps.stamp a6733dbc50dc0b4
-1275 1957 0 obj/chrome/app/chrome_content_plugin_manifest__is_service_manifest.stamp df4185965232a7e6
-1275 1957 0 obj/chrome/app/chrome_content_plugin_manifest_overlay__is_service_manifest.stamp bd0fb97237c5074d
-1275 1957 0 obj/chrome/app/chrome_content_renderer_manifest_overlay.inputdeps.stamp 1605b840beae701c
-1275 1958 0 obj/chrome/app/chrome_content_renderer_manifest__is_service_manifest.stamp 5173642d314f2d18
-1276 1959 0 obj/chrome/app/chrome_content_renderer_manifest_overlay__is_service_manifest.stamp dab5a7d3eaadd85a
-1276 1960 0 obj/chrome/app/chrome_content_utility_manifest_overlay.inputdeps.stamp bb61a6eae7e90d98
-1276 1962 0 obj/chrome/app/chrome_content_utility_manifest__is_service_manifest.stamp d102efc7228156fc
-1276 1962 0 obj/chrome/app/chrome_content_utility_manifest_overlay__is_service_manifest.stamp e7c510cfbe2d9088
-1277 1962 0 obj/chrome/app/chrome_dll_resources.stamp 608c7174590ae48c
-1280 1963 0 obj/chrome/app/chrome_manifest.inputdeps.stamp 26a230f094ee6e25
-1281 1964 0 obj/chrome/app/chrome_manifest__is_service_manifest.stamp 392035a75b781481
-1281 1965 0 obj/chrome/app/command_ids.stamp a2bf13bf4ee69ea3
-1282 1966 0 obj/chrome/browser/ui/webui/omnibox/mojo_bindings__check_deps_are_all_mojom.stamp bd7ff06038f3ec7f
-1282 1966 0 obj/chrome/app/vector_icons/chrome_vector_icons.inputdeps.stamp f47ee6fec73e05b0
-1282 1967 0 cast_remoting_connector_fuzzer.options 930134b769ba9f73
-1282 1967 0 obj/chrome/browser/media/router/mojo_bindings__is_mojom.stamp 466fc9a439fbb2ec
-1283 1970 0 obj/chrome/browser/media/router/mojo_bindings_common__is_mojom.stamp 8b490270cdafecf
-1283 1971 0 obj/chrome/browser/media/router/mojo_test_interfaces__is_mojom.stamp 4c893ba3099cdaf7
-1283 1971 0 obj/chrome/browser/resources/md_downloads/vulcanize.inputdeps.stamp b463bc7f9833e4eb
-1283 1973 0 obj/chrome/browser/resources/md_history/vulcanize_app.inputdeps.stamp 5da5a62ad1dcb31f
-1283 1975 0 obj/chrome/browser/resources/md_history/vulcanize_lazy_load.inputdeps.stamp 3962d76366774295
-1284 1976 0 obj/components/domain_reliability/bake_in_configs.inputdeps.stamp 7fb2b7da8b55926b
-1284 1976 0 obj/chrome/browser/chrome_internal_resources_gen.stamp 1eae97aecd17386c
-1284 1976 0 obj/chrome/browser/preferences_manifest__is_service_manifest.stamp 33d9abfb726981c4
-1284 1978 0 obj/chrome/browser/theme_properties.stamp 6aa373278541af71
-1288 1978 0 gen/chrome/common/features.h 1380d988824bf2ae
-1289 1982 0 obj/chrome/common/mojo_bindings__is_mojom.stamp 58677facfc55a6c3
-1289 1983 0 obj/chrome/common/instant_mojom__is_mojom.stamp eca52cd264c479c6
-1289 1983 0 obj/chrome/browser/ui/webui/engagement/mojo_bindings__is_mojom.stamp 9f26b0e2b5750aa6
-1289 1984 0 obj/chrome/browser/ui/webui/omnibox/mojo_bindings__type_mappings.inputdeps.stamp a92cfc87812fc28e
-1289 1984 0 obj/chrome/browser/ui/webui/omnibox/mojo_bindings__is_mojom.stamp 1f292b08a229fcff
-1289 1984 0 obj/chrome/browser/ui/webui/omnibox/mojo_bindings_blink__type_mappings.inputdeps.stamp a7ef1a514c41174f
-1289 1985 0 obj/chrome/browser/ui/webui/usb_internals/mojo_bindings__is_mojom.stamp 9f2140f824793e44
-1290 1986 0 obj/chrome/common/extensions/mojo_bindings__is_mojom.stamp 697cc1f72aa3c9e9
-1290 1987 0 obj/chrome/common/version_header_action.inputdeps.stamp 6b9baa8063a6a905
-1291 1987 0 obj/chrome/common/extensions/api/api_bundle_generator_schema.inputdeps.stamp 417a733a1b1dc1b8
-1291 1988 0 obj/chrome/common/extensions/api/api_schema_generator.inputdeps.stamp c78d2148e660f04b
-1291 1990 0 obj/chrome/common/extensions/api/api_registration_bundle_generator_registration.inputdeps.stamp d87a40660f2526e8
-1292 1990 0 obj/chrome/common/importer/interfaces__is_mojom.stamp fbba48a63930fad8
-1292 1991 0 obj/components/password_manager/core/browser/unit_tests_bundle_data.stamp a1221f6a41bbc716
-1292 1993 0 obj/components/visitedlink/common/interfaces__is_mojom.stamp 2c49363bec81305d
-1292 1994 0 obj/components/visitedlink/common/interfaces__type_mappings.inputdeps.stamp a0d0f67f50be78ae
-1294 1994 0 obj/chrome/installer/installer.stamp fdbc934e2b94627c
-1294 1996 0 obj/chrome/installer/util/generate_strings.inputdeps.stamp c21387fd9dd578b7
-1294 1996 0 obj/components/payments/content/payment_app__is_mojom.stamp a8f1f182179a2e4a
-1294 1996 0 obj/components/autofill/core/browser/unit_tests_bundle_data.stamp 76813fb9ac300ccd
-1295 1997 0 obj/components/dom_distiller/core/unit_tests_bundle_data.stamp 6a0f970508ef2822
-1295 1998 0 obj/mash/public/interfaces/interfaces__type_mappings.inputdeps.stamp 8fc53baa86393f2c
-1295 1999 0 obj/components/dom_distiller/content/common/mojo_bindings__type_mappings.inputdeps.stamp 7cf1b8d92cfbe99d
-1295 2002 0 obj/chrome/test/test.stamp 740bf2e94b308511
-1295 2005 0 obj/extensions/browser/api/api_registration_bundle_generator_registration.inputdeps.stamp 16ba782e33efde8a
-1295 2009 0 obj/components/autofill/content/common/mojo_interfaces__is_mojom.stamp 9e5b625f65094c9f
-1295 2009 0 obj/components/autofill/content/common/mojo_types__is_mojom.stamp dcb5dbec6d55a6a7
-1296 2011 0 obj/components/autofill/content/common/mojo_test_types__is_mojom.stamp d9c798289ba46425
-1297 2011 0 obj/components/password_manager/content/common/mojo_interfaces__is_mojom.stamp 626b36fa82d1da58
-1298 2012 0 obj/components/bookmarks/browser/unit_tests_bundle_data.stamp 597321f908035e7b
-1298 2029 0 test_data/chrome/browser/resources/print_preview/print_preview_utils.js 3da6f440b12423f5
-1298 2045 0 test_data/chrome/browser/resources/print_preview/data/measurement_system.js 276da1cf0e264513
-1298 2061 0 test_data/chrome/browser/resources/md_downloads/action_service.js ab7d4417cd0ee295
-1298 2097 0 test_data/chrome/renderer/resources/extensions/notifications_custom_bindings.js a6480bafe57a2747
-1299 2097 0 test_data/chrome/renderer/resources/extensions/notifications_test_util.js 21534713a862f9f0
-1300 2106 0 media_router/browser_test_resources/no_provider.json 3966753c5d416bb
-1300 2120 0 media_router/browser_test_resources/no_sinks.json 8a565aa71679478b
-1300 2121 0 media_router/test_extension/manifest.json 8f65bc8b627dcc57
-1300 2126 0 media_router/test_extension/script.js 107afb97bad014f
-1300 2127 0 media_router/browser_test_resources/close_route_with_error_on_send.json b9facf84d2e8c445
-1301 2129 0 media_router/browser_test_resources/basic_test.html f95507df99df541
-1301 2134 0 media_router/browser_test_resources/common.js d07c9ad75bf70cf5
-1301 2137 0 media_router/browser_test_resources/route_creation_timed_out.json e5dc7c74ac436fe5
-1302 2141 0 media_router/browser_test_resources/fail_create_route.json f41da6c16c17fcce
-1302 2157 0 media_router/browser_test_resources/fail_reconnect_session.json 581f4aec17f01054
-1302 2177 0 media_router/browser_test_resources/no_supported_sinks.json 9a1378949ae8c576
-1303 2181 0 media_router/browser_test_resources/fail_reconnect_session.html 63cebfae6ed2384e
-1303 2181 0 obj/content/test/content_test_mojo_bindings__check_deps_are_all_mojom.stamp ffff60eb9ffb675
-1303 2183 0 test_data/ui/webui/resources/js/cr.js b6fec657a251987d
-1303 2184 0 obj/chrome/test/chromedriver/embed_js_in_cpp.inputdeps.stamp decaaac784c17e4a
-1303 2184 0 obj/chrome/test/chromedriver/embed_user_data_dir_in_cpp.inputdeps.stamp e92600aa087bbb93
-1304 2185 0 obj/chrome/test/chromedriver/embed_version_in_cpp.inputdeps.stamp 3972b73cb5c02713
-1304 2186 0 obj/chrome/test/chromedriver/embed_extension_in_cpp.inputdeps.stamp b9ccb10c5ebe688b
-1304 2189 0 obj/components/content_settings/core/common/mojo_bindings__check_deps_are_all_mojom.stamp da375d2ecca40368
-1305 2201 0 obj/components/history/core/browser/unit_tests_bundle_data.stamp a3b912d12f688623
-1305 2201 0 obj/components/contextual_search/mojo_bindings__check_deps_are_all_mojom.stamp 18fd737f0687941e
-1305 2201 0 obj/components/contextual_search/mojo_bindings__is_mojom.stamp d187e414f8925477
-1308 2205 0 obj/components/contextual_search/mojo_bindings__type_mappings.inputdeps.stamp 1af9b91fb39cf2c
-1309 2221 0 hid_report_descriptor_fuzzer.options 580f064a68005230
-1309 2224 0 obj/components/content_settings/core/common/mojo_bindings__type_mappings.inputdeps.stamp 5d22a3eb8a588726
-1309 2249 0 obj/components/content_settings/core/common/mojo_bindings__is_mojom.stamp 7750f7c67582c124
-1309 2258 0 obj/components/content_settings/core/common/mojo_bindings_blink__type_mappings.inputdeps.stamp 2431763c76fe6e5a
-1309 2265 0 obj/components/contextual_search/mojo_bindings_blink__type_mappings.inputdeps.stamp e7473d8c2e1dcae2
-1311 2265 0 obj/components/data_reduction_proxy/core/common/version_header_action.inputdeps.stamp def55c35f16728c2
-1311 2277 0 obj/components/data_reduction_proxy/core/browser/unit_tests_bundle_data.stamp 7cb412d497616df3
-1311 2297 0 obj/components/discardable_memory/public/interfaces/interfaces__type_mappings.inputdeps.stamp dca59ab4f91c2ee8
-1311 2309 0 obj/components/discardable_memory/public/interfaces/interfaces__is_mojom.stamp 9f0a161334664221
-1311 2333 0 obj/components/discardable_memory/public/interfaces/interfaces_blink__type_mappings.inputdeps.stamp 6e36ef3a17f3de02
-1312 2345 0 obj/components/discardable_memory/public/interfaces/interfaces__check_deps_are_all_mojom.stamp ec112dbb3de63c54
-1312 2345 0 obj/components/dom_distiller/content/common/mojo_bindings__is_mojom.stamp e9cb079e59ba260e
-1312 2348 0 obj/components/dom_distiller/content/common/mojo_bindings__check_deps_are_all_mojom.stamp dbece267a76395df
-1314 2353 0 obj/ui/base/ime/text_input_types.stamp 7a2d9dbcc77cfda6
-1314 2359 0 obj/components/dom_distiller/content/common/mojo_bindings_blink__type_mappings.inputdeps.stamp 1e36ef36dd3398c9
-1315 2360 0 obj/components/filesystem/test_manifest__is_service_manifest.stamp 49dd9970c19ba5e
-1315 2369 0 obj/components/filesystem/manifest__is_service_manifest.stamp 95c95da367c72984
-1315 2369 0 obj/components/filesystem/test_manifest.inputdeps.stamp ff8428f5f54eeb48
-1315 2393 0 obj/components/filesystem/manifest.inputdeps.stamp 43bef0c83d0b0886
-1315 2393 0 obj/components/filesystem/public/interfaces/interfaces__is_mojom.stamp 4c28486b416c7fd2
-1316 2394 0 obj/components/font_service/manifest.inputdeps.stamp d8a2303746c6b516
-1317 2395 0 obj/components/font_service/manifest__is_service_manifest.stamp 1fc5b5ae92a492b7
-1317 2396 0 obj/components/font_service/public/interfaces/interfaces__is_mojom.stamp 2f17d19bcb97eb84
-1318 2404 0 obj/components/handoff/handoff.stamp a1ecd0e1f95f0461
-1318 2409 0 obj/components/visitedlink/common/interfaces_blink__type_mappings.inputdeps.stamp 22bdb1b353356929
-1318 2425 0 obj/components/json_schema/unit_tests_bundle_data.stamp 55e44fc60b39fe05
-1318 2427 0 obj/components/leveldb/public/interfaces/interfaces__is_mojom.stamp a4c4f4afa51e0970
-1319 2427 0 obj/components/leveldb/test_manifest__is_service_manifest.stamp a5060450ad053f48
-1319 2429 0 obj/components/leveldb/manifest__is_service_manifest.stamp 98ae837e0acd335
-1319 2429 0 obj/components/leveldb/test_manifest.inputdeps.stamp 68a6f2adcbd52597
-1319 2433 0 obj/components/leveldb/manifest.inputdeps.stamp ab10e912a19cf2df
-1320 2433 0 obj/content/public/common/service_names__type_mappings.inputdeps.stamp 373bbbad6ead5f34
-1321 2453 0 obj/content/public/common/service_names__is_mojom.stamp 8357cc3b4f2c0123
-1323 2461 0 obj/content/renderer/for_content_tests.stamp 700a6a50f029f439
-1323 2461 0 obj/components/metrics/call_stack_profile_params/call_stack_profile_params.o d0898cd3269c4685
-1324 2461 0 obj/components/metrics/public/interfaces/call_stack_mojo_bindings__is_mojom.stamp d71fb0ea06f4ff50
-1324 2463 0 obj/components/metrics/public/interfaces/call_stack_mojo_test_bindings__is_mojom.stamp f343bc355006eda9
-1324 2465 0 obj/content/test/content_test_mojo_bindings__is_mojom.stamp dca0e4ba7b52130d
-1325 2466 0 obj/content/test/content_test_mojo_bindings__type_mappings.inputdeps.stamp 1b21e422740b76bd
-1325 2466 0 obj/device/bluetooth/public/interfaces/interfaces__type_mappings.inputdeps.stamp bb0aa06fc65ada2
-1325 2486 0 obj/device/bluetooth/public/interfaces/interfaces__is_mojom.stamp 5e31bc7b9644b45e
-1325 2486 0 obj/components/omnibox/browser/unit_tests_bundle_data.stamp e6f22eb311c939d0
-1326 2489 0 obj/components/omnibox/common/common.stamp c3620bb786809ce6
-1326 2489 0 obj/components/omnibox/browser/omnibox_vector_icons.inputdeps.stamp fc4caa96333b1f49
-1328 2525 0 obj/components/payments/content/payment_request__type_mappings.inputdeps.stamp 4138c73edd8d9967
-1329 2525 0 obj/components/payments/content/payment_request__is_mojom.stamp 4dbc6bdd61021ca0
-1330 2537 0 obj/components/payments/content/payment_request__check_deps_are_all_mojom.stamp d922943f5977c2dd
-1330 2541 0 obj/components/payments/content/payment_request_blink__type_mappings.inputdeps.stamp 43997c6ac87963a4
-1330 2541 0 obj/components/policy/full_runtime_code_generate.inputdeps.stamp 8207acd4fc2c0e50
-1331 2542 0 obj/device/wake_lock/public/interfaces/interfaces_blink__type_mappings.inputdeps.stamp 6b4c989d4e204ee3
-1332 2545 0 obj/components/policy/cloud_policy_code_generate.inputdeps.stamp 80d997b008445f27
-1332 2559 0 obj/components/rappor/public/interfaces/interfaces__is_mojom.stamp f74ddb5b30de7283
-1332 2585 0 gen/components/reading_list/core/reading_list_enable_flags.h f18d0b5d02cf9f78
-1334 2586 0 obj/headless/version_header_action.inputdeps.stamp c1023777f5460270
-1334 2587 0 obj/components/resources/about_credits.inputdeps.stamp a42c859c1e84aafc
-1334 2587 0 obj/gpu/ipc/common/test_interfaces__is_mojom.stamp 6c211a01634aee8e
-1335 2587 0 obj/components/search_engines/prepopulated_engines_action.inputdeps.stamp 563322a485255768
-1335 2588 0 obj/components/safe_json/public/interfaces/interfaces__is_mojom.stamp b1c4ffd92ad2d62
-1337 2593 0 gpu_fuzzer.options 357a45b5220e5ae6
-1337 2593 0 gen/components/spellcheck/spellcheck_build_features.h ffbfeb2acac5fefb
-1338 2594 0 net_url_request_ftp_fuzzer.dict d2a573f9850594ae
-1338 2594 0 net_url_request_ftp_fuzzer.options ebf89f03483d7889
-1338 2596 0 obj/components/startup_metric_utils/common/interfaces__is_mojom.stamp 402b01730c095796
-1339 2597 0 obj/services/image_decoder/public/interfaces/constants__check_deps_are_all_mojom.stamp 79541de2b563448d
-1339 2598 0 remoting/chrome-remote-desktop-host 645e67669422d2f4
-1339 2598 0 obj/services/ui/ws/mus_ws_unittests_app_manifest.inputdeps.stamp 387fe8854b2e7630
-1339 2599 0 obj/skia/public/interfaces/test_interfaces__is_mojom.stamp 8e9419d22031d89a
-1340 2606 0 obj/services/shape_detection/manifest.inputdeps.stamp 2245ddb863b954c
-1341 2621 0 colorenhancer/src/cvd.js bfff4c53b86e67fa
-1342 2621 0 colorenhancer/src/common.js 273a0f544599a6f
-1343 2633 0 colorenhancer/src/background.js 8b6bc1aa16c16bc8
-1343 2633 0 obj/ui/base/mojo/mojo_bindings_blink__type_mappings.inputdeps.stamp 565a2b79604e9caa
-1344 2633 0 obj/services/service_manager/manifest.inputdeps.stamp 89d35163db05eef
-1344 2634 0 obj/services/service_manager/manifest__is_service_manifest.stamp 1cbc8d6f5f4b48ff
-1350 2634 0 obj/services/service_manager/background/tests/test_manifest__is_service_manifest.stamp d6300a614424bac5
-1351 2636 0 obj/services/service_manager/background/tests/test_service_interfaces__check_deps_are_all_mojom.stamp 17f819c51d0bbcba
-1351 2636 0 obj/services/service_manager/background/tests/test_manifest.inputdeps.stamp ac6756d58fa02be5
-1351 2636 0 obj/services/service_manager/background/tests/test_service_interfaces__is_mojom.stamp d9304a27f20ef777
-1351 2636 0 obj/services/service_manager/background/tests/test_service_interfaces__type_mappings.inputdeps.stamp 99a4b65a446a96c6
-1352 2637 0 obj/services/service_manager/background/tests/test_service_interfaces_blink__type_mappings.inputdeps.stamp fc8744ed09af87be
-1352 2637 0 obj/services/service_manager/background/tests/test_service_manifest.inputdeps.stamp c24cd0627664d8f
-1352 2637 0 obj/services/service_manager/background/tests/test_service_manifest__is_service_manifest.stamp b5e86b588a28fb5c
-1353 2641 0 obj/services/service_manager/public/interfaces/constants__is_mojom.stamp e062038719022d9a
-1354 2642 0 obj/services/service_manager/public/interfaces/constants__check_deps_are_all_mojom.stamp 419f0ddba4c080d6
-1356 2643 0 obj/services/service_manager/public/interfaces/constants__type_mappings.inputdeps.stamp d69a5fd3bc55a6fa
-1357 2643 0 obj/services/service_manager/public/interfaces/constants_blink__type_mappings.inputdeps.stamp e5d64366e8ff03ac
-1358 2645 0 obj/services/service_manager/public/interfaces/interfaces__is_mojom.stamp 8008271190de7995
-1358 2649 0 obj/services/service_manager/tests/interfaces__type_mappings.inputdeps.stamp 3b926ed55307693f
-1359 2650 0 obj/services/service_manager/tests/interfaces__is_mojom.stamp bbf5a998c440e482
-1359 2652 0 obj/services/service_manager/runner/host/host_test_service_manifest__is_service_manifest.stamp b3629c78e87569ea
-1360 2652 0 obj/services/service_manager/runner/host/host_test_service_manifest.inputdeps.stamp 881ed42edc54279
-1360 2654 0 obj/services/service_manager/tests/interfaces__check_deps_are_all_mojom.stamp 3e745013b5c3a7d4
-1361 2654 0 obj/ui/keyboard/mojom__is_mojom.stamp ac83212aef984ca6
-1362 2655 0 obj/components/toolbar/toolbar_vector_icons.inputdeps.stamp 999190b061be2b18
-1363 2656 0 obj/components/translate/content/common/common__is_mojom.stamp e77a47adf41e78ca
-1363 2658 0 obj/components/ui_devtools/protocol_compatibility.inputdeps.stamp a262a48d447d407c
-1364 2660 0 obj/components/update_client/unit_tests_bundle_data.stamp e34a9f29a7ddf9da
-1364 2660 0 obj/components/variations/field_trial_config/field_trial_testing_config_action.inputdeps.stamp 434946957497ef1e
-1364 2660 0 obj/testing/buildbot/filters/browser_tests_filters.stamp 3b9fd46dc80e103
-1365 2660 0 obj/components/version_info/generate_version_info_action.inputdeps.stamp e320dedf7f041bb8
-1365 2662 0 obj/components/web_cache/public/interfaces/interfaces__type_mappings.inputdeps.stamp 1d8798130ad180c8
-1365 2662 0 obj/components/web_cache/public/interfaces/interfaces__is_mojom.stamp 5173eb112c5d56f1
-1366 2669 0 obj/mash/public/interfaces/interfaces__is_mojom.stamp 6dd113bd4ce0134a
-1366 2669 0 obj/components/web_cache/public/interfaces/interfaces_blink__type_mappings.inputdeps.stamp defee36e55d65320
-1367 2669 0 obj/components/visitedlink/common/interfaces__check_deps_are_all_mojom.stamp 12c5f8f404f07144
-1367 2669 0 obj/components/web_cache/public/interfaces/interfaces__check_deps_are_all_mojom.stamp 4d22832bbd44e332
-1368 2673 0 obj/content/sandbox_helper_win.stamp 53556bd1952574d0
-1369 2681 0 obj/services/ui/demo/manifest.inputdeps.stamp d261cb8b5832ad36
-1370 2681 0 obj/components/webdata/common/unit_tests_bundle_data.stamp bea6c98642e32d4e
-1370 2681 0 obj/content/app/both_for_content_tests.stamp 61ebf49fd98b90cc
-1370 2681 0 obj/content/export.stamp 4a060d841250382b
-1370 2681 0 obj/content/common/mojo_bindings__is_mojom.stamp 41eea74b73e02dfb
-1371 2682 0 obj/content/child/for_content_tests.stamp 44aaf3e748abc1ea
-1371 2682 0 gen/content/public/common/features.h f5ec1a1276c73150
-1372 2683 0 obj/content/common/for_content_tests.stamp b3f0afc35633fbca
-1373 2684 0 gen/content/common/features.h f14dec80fcf32baa
-1373 2685 0 obj/services/ui/demo/test_manifest.inputdeps.stamp 76824c99aa6bf355
-1373 2685 0 obj/content/public/app/renderer_manifest__is_service_manifest.stamp fd7f4a85ccacf1b7
-1374 2686 0 obj/content/public/app/renderer_manifest.inputdeps.stamp 48032c8ab25a6aec
-1374 2688 0 obj/content/public/app/plugin_manifest__is_service_manifest.stamp 99d43b55bb330fae
-1374 2690 0 obj/content/public/app/plugin_manifest.inputdeps.stamp fd40843720182461
-1374 2690 0 obj/content/public/app/packaged_services_manifest__is_service_manifest.stamp 4813dd4c7cfe0dd4
-1375 2691 0 obj/content/public/app/gpu_manifest__is_service_manifest.stamp ae2b882bbd8bb292
-1375 2692 0 obj/content/public/app/gpu_manifest.inputdeps.stamp d316339c888e8cbf
-1375 2692 0 obj/content/public/app/browser_manifest__is_service_manifest.stamp 83671be596ae3bf9
-1377 2692 0 obj/content/public/app/utility_manifest__is_service_manifest.stamp c875df2d90c2c80
-1378 2693 0 obj/content/public/app/utility_manifest.inputdeps.stamp 88bb6e963b00b0c1
-1378 2694 0 obj/content/public/common/service_names_blink__type_mappings.inputdeps.stamp 1b2338947af3a1b
-1378 2694 0 obj/content/public/common/interfaces__check_deps_are_all_mojom.stamp 97afcae8baadc77a
-1379 2694 0 obj/content/public/common/result_codes.stamp 7e3d25132dcedc3b
-1380 2694 0 obj/content/public/common/service_names__check_deps_are_all_mojom.stamp 1b497d9c4f1d8650
-1380 2695 0 obj/content/public/common/interfaces__type_mappings.inputdeps.stamp 359a82b755d6dfbc
-1380 2696 0 obj/content/public/common/interfaces__is_mojom.stamp 4a4e8a0c32c5f938
-1381 2696 0 obj/content/public/common/interfaces_blink__type_mappings.inputdeps.stamp ee602eacc9b045e9
-1381 2697 0 GardinerModCat.ttf 3ce72a7c357259c0
-1381 2699 0 GardinerModBug.ttf 69f4c57ee21e3d45
-1382 2699 0 fonts.conf 903c92c674848f27
-1383 2699 0 AHEM____.TTF d9be98d3534e71be
-1384 2699 0 obj/content/shell/mojo_bindings__is_mojom.stamp 41a503d4088b8a6d
-1384 2700 0 obj/content/test/content_test_mojo_bindings_blink__type_mappings.inputdeps.stamp cdbad946535f7511
-1385 2701 0 obj/content/test/content_unittests_manifest__is_service_manifest.stamp f72e2413607b90b6
-1385 2701 0 obj/content/utility/for_content_tests.stamp 144e86637a119ab7
-1385 2701 0 obj/content/test/web_ui_test_mojo_bindings__check_deps_are_all_mojom.stamp 70bf70f78a81f092
-1386 2703 0 obj/third_party/webrtc/base/gtest_prod.stamp c7ba3618f71670fe
-1387 2713 0 obj/device/battery/mojo_bindings__is_mojom.stamp d1000202725cdd3a
-1387 2713 0 obj/device/battery/mojo_bindings_blink__type_mappings.inputdeps.stamp b5a2ba69c2ffe9f1
-1388 2714 0 obj/device/battery/mojo_bindings__check_deps_are_all_mojom.stamp ec7d3f5902ab03b8
-1388 2715 0 obj/device/battery/mojo_bindings__type_mappings.inputdeps.stamp 8f755a1226416498
-1388 2716 0 obj/content/test/web_ui_test_mojo_bindings_blink__type_mappings.inputdeps.stamp 491d4795773c54c8
-1388 2716 0 obj/content/test/web_ui_test_mojo_bindings__type_mappings.inputdeps.stamp de7637397f63b2ec
-1389 2716 0 obj/content/test/web_ui_test_mojo_bindings__is_mojom.stamp 1e0ab0348c2e1ee0
-1390 2717 0 obj/content/test/fuzzer/fuzzer.stamp bbc6f5317d29aa65
-1390 2718 0 obj/device/bluetooth/public/interfaces/experimental_interfaces__is_mojom.stamp 1c79665bed98ab04
-1390 2719 0 obj/device/bluetooth/public/interfaces/interfaces__check_deps_are_all_mojom.stamp 89c2f14913b32f85
-1391 2719 0 obj/device/bluetooth/public/interfaces/interfaces_blink__type_mappings.inputdeps.stamp 2b05f29f4adaa33
-1392 2720 0 obj/device/gamepad/public/interfaces/gamepad_struct_traits_test__is_mojom.stamp 33daf38dd323965b
-1393 2721 0 obj/device/gamepad/public/interfaces/interfaces__check_deps_are_all_mojom.stamp 35111b8a42281362
-1393 2721 0 obj/device/gamepad/public/interfaces/interfaces__is_mojom.stamp 60c295fb5928248a
-1394 2722 0 obj/device/gamepad/public/interfaces/interfaces__type_mappings.inputdeps.stamp 3b2c48390830aa2b
-1395 2725 0 obj/device/gamepad/public/interfaces/interfaces_blink__type_mappings.inputdeps.stamp 1e19f40cc0de3a70
-1395 2733 0 obj/device/generic_sensor/public/interfaces/interfaces__check_deps_are_all_mojom.stamp 4314a13b8c276e18
-1395 2733 0 obj/device/generic_sensor/public/interfaces/interfaces__is_mojom.stamp 69989e0cd8e35ad4
-1396 2735 0 obj/device/generic_sensor/public/interfaces/interfaces__type_mappings.inputdeps.stamp 5ddddfa704d022e6
-1396 2735 0 obj/device/generic_sensor/public/interfaces/interfaces_blink__type_mappings.inputdeps.stamp e1fa941c54fc56cc
-1397 2735 0 obj/device/geolocation/public/interfaces/interfaces__is_mojom.stamp 1c29a8f30eb15f9d
-1397 2736 0 obj/device/geolocation/public/interfaces/interfaces__type_mappings.inputdeps.stamp 286d95a305d8b211
-1398 2737 0 obj/device/geolocation/public/interfaces/interfaces__check_deps_are_all_mojom.stamp a9256d80d904d268
diff --git a/build/ninjalog/trace.go b/build/ninjalog/trace.go
deleted file mode 100644
index 0313d4c..0000000
--- a/build/ninjalog/trace.go
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2014 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 ninjalog
-
-import (
- "sort"
-)
-
-// Trace is an entry of trace format.
-// https://code.google.com/p/trace-viewer/
-type Trace struct {
- Name string `json:"name"`
- Category string `json:"cat"`
- EventType string `json:"ph"`
- Timestamp int `json:"ts"` // microsecond
- Duration int `json:"dur"` // microsecond
- ProcessID int `json:"pid"`
- ThreadID int `json:"tid"`
- Args map[string]interface{} `json:"args"`
-}
-
-type traceByStart []Trace
-
-func (t traceByStart) Len() int { return len(t) }
-func (t traceByStart) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
-func (t traceByStart) Less(i, j int) bool { return t[i].Timestamp < t[j].Timestamp }
-
-func toTrace(step Step, pid int, tid int) Trace {
- return Trace{
- Name: step.Out,
- Category: "target",
- EventType: "X",
- Timestamp: int(step.Start.Nanoseconds() / 1000),
- Duration: int(step.Duration().Nanoseconds() / 1000),
- ProcessID: pid,
- ThreadID: tid,
- Args: make(map[string]interface{}),
- }
-}
-
-// ToTraces converts Flow outputs into trace log.
-func ToTraces(steps [][]Step, pid int) []Trace {
- traceNum := 0
- for _, thread := range steps {
- traceNum += len(thread)
- }
-
- traces := make([]Trace, 0, traceNum)
- for tid, thread := range steps {
- for _, step := range thread {
- traces = append(traces, toTrace(step, pid, tid))
- }
- }
- sort.Sort(traceByStart(traces))
- return traces
-}
diff --git a/build/ninjalog/trace_test.go b/build/ninjalog/trace_test.go
deleted file mode 100644
index 9417587..0000000
--- a/build/ninjalog/trace_test.go
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2014 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 ninjalog
-
-import (
- "reflect"
- "testing"
- "time"
-)
-
-func TestTrace(t *testing.T) {
- flow := [][]Step{
- []Step{
- Step{
- Start: 76 * time.Millisecond,
- End: 187 * time.Millisecond,
- Out: "resources/inspector/devtools_extension_api.js",
- CmdHash: "75430546595be7c2",
- },
- Step{
- Start: 187 * time.Millisecond,
- End: 21304 * time.Millisecond,
- Out: "obj/third_party/pdfium/core/src/fpdfdoc/fpdfdoc.doc_formfield.o",
- CmdHash: "2ac7111aa1ae86af",
- },
- },
- []Step{
- Step{
- Start: 78 * time.Millisecond,
- End: 286 * time.Millisecond,
- Out: "gen/angle/commit_id.py",
- CmdHash: "4ede38e2c1617d8c",
- },
- Step{
- Start: 287 * time.Millisecond,
- End: 290 * time.Millisecond,
- Out: "obj/third_party/angle/src/copy_scripts.actions_rules_copies.stamp",
- CmdHash: "b211d373de72f455",
- },
- },
- []Step{
- Step{
- Start: 79 * time.Millisecond,
- End: 287 * time.Millisecond,
- Out: "gen/angle/copy_compiler_dll.bat",
- CmdHash: "9fb635ad5d2c1109",
- },
- },
- []Step{
- Step{
- Start: 80 * time.Millisecond,
- End: 284 * time.Millisecond,
- Out: "gen/autofill_regex_constants.cc",
- CmdHash: "fa33c8d7ce1d8791",
- },
- },
- []Step{
- Step{
- Start: 141 * time.Millisecond,
- End: 287 * time.Millisecond,
- Out: "PepperFlash/manifest.json",
- CmdHash: "324f0a0b77c37ef",
- },
- },
- []Step{
- Step{
- Start: 142 * time.Millisecond,
- End: 288 * time.Millisecond,
- Out: "PepperFlash/libpepflashplayer.so",
- CmdHash: "1e2c2b7845a4d4fe",
- },
- },
- }
-
- traces := ToTraces(flow, 1)
- want := []Trace{
- Trace{
- Name: "resources/inspector/devtools_extension_api.js",
- Category: "target",
- EventType: "X",
- Timestamp: 76 * 1000,
- Duration: (187 - 76) * 1000,
- ProcessID: 1,
- ThreadID: 0,
- Args: map[string]interface{}{},
- },
- Trace{
- Name: "gen/angle/commit_id.py",
- Category: "target",
- EventType: "X",
- Timestamp: 78 * 1000,
- Duration: (286 - 78) * 1000,
- ProcessID: 1,
- ThreadID: 1,
- Args: map[string]interface{}{},
- },
- Trace{
- Name: "gen/angle/copy_compiler_dll.bat",
- Category: "target",
- EventType: "X",
- Timestamp: 79 * 1000,
- Duration: (287 - 79) * 1000,
- ProcessID: 1,
- ThreadID: 2,
- Args: map[string]interface{}{},
- },
- Trace{
- Name: "gen/autofill_regex_constants.cc",
- Category: "target",
- EventType: "X",
- Timestamp: 80 * 1000,
- Duration: (284 - 80) * 1000,
- ProcessID: 1,
- ThreadID: 3,
- Args: map[string]interface{}{},
- },
- Trace{
- Name: "PepperFlash/manifest.json",
- Category: "target",
- EventType: "X",
- Timestamp: 141 * 1000,
- Duration: (287 - 141) * 1000,
- ProcessID: 1,
- ThreadID: 4,
- Args: map[string]interface{}{},
- },
- Trace{
- Name: "PepperFlash/libpepflashplayer.so",
- Category: "target",
- EventType: "X",
- Timestamp: 142 * 1000,
- Duration: (288 - 142) * 1000,
- ProcessID: 1,
- ThreadID: 5,
- Args: map[string]interface{}{},
- },
- Trace{
- Name: "obj/third_party/pdfium/core/src/fpdfdoc/fpdfdoc.doc_formfield.o",
- Category: "target",
- EventType: "X",
- Timestamp: 187 * 1000,
- Duration: (21304 - 187) * 1000,
- ProcessID: 1,
- ThreadID: 0,
- Args: map[string]interface{}{},
- },
- Trace{
- Name: "obj/third_party/angle/src/copy_scripts.actions_rules_copies.stamp",
- Category: "target",
- EventType: "X",
- Timestamp: 287 * 1000,
- Duration: (290 - 287) * 1000,
- ProcessID: 1,
- ThreadID: 1,
- Args: map[string]interface{}{},
- },
- }
-
- if !reflect.DeepEqual(traces, want) {
- t.Errorf("ToTrace()=%v; want=%v", traces, want)
- }
-}
diff --git a/build/ninjatrace/cmd/ninjatrace.go b/build/ninjatrace/cmd/ninjatrace.go
deleted file mode 100644
index 633d44a..0000000
--- a/build/ninjatrace/cmd/ninjatrace.go
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2014 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.
-
-// ninjatrace converts .ninja_log into trace-viewer formats.
-//
-// usage:
-// $ go run ninjatrace.go --filename out/debug-x64/.ninja_log
-//
-package main
-
-import (
- "bufio"
- "compress/gzip"
- "encoding/json"
- "flag"
- "io"
- "log"
- "os"
- "path/filepath"
- "runtime/pprof"
-
- "go.fuchsia.dev/tools/build/ninjalog"
-)
-
-var (
- filename = flag.String("filename", ".ninja_log", "filename of .ninja_log")
- traceJSON = flag.String("trace-json", "trace.json", "output filename of trace.json")
- cpuprofile = flag.String("cpuprofile", "", "file to write cpu profile")
-)
-
-func reader(fname string, rd io.Reader) (io.Reader, error) {
- if filepath.Ext(fname) != ".gz" {
- return bufio.NewReaderSize(rd, 512*1024), nil
- }
- return gzip.NewReader(bufio.NewReaderSize(rd, 512*1024))
-}
-
-func convert(fname string) ([]ninjalog.Trace, error) {
- f, err := os.Open(fname)
- if err != nil {
- return nil, err
- }
- defer f.Close()
- rd, err := reader(fname, f)
- if err != nil {
- return nil, err
- }
-
- njl, err := ninjalog.Parse(fname, rd)
- if err != nil {
- return nil, err
- }
- steps := ninjalog.Dedup(njl.Steps)
- flow := ninjalog.Flow(steps)
- return ninjalog.ToTraces(flow, 1), nil
-}
-
-func output(fname string, traces []ninjalog.Trace) (err error) {
- f, err := os.Create(fname)
- if err != nil {
- return err
- }
- defer func() {
- cerr := f.Close()
- if err == nil {
- err = cerr
- }
- }()
- js, err := json.Marshal(traces)
- if err != nil {
- return err
- }
- _, err = f.Write(js)
- return err
-}
-
-func main() {
- flag.Parse()
-
- if *cpuprofile != "" {
- f, err := os.Create(*cpuprofile)
- if err != nil {
- log.Fatal(err)
- }
- pprof.StartCPUProfile(f)
- defer pprof.StopCPUProfile()
- }
-
- traces, err := convert(*filename)
- if err != nil {
- log.Fatal(err)
- }
- if *traceJSON != "" {
- err = output(*traceJSON, traces)
- if err != nil {
- log.Fatal(err)
- }
- }
-}
diff --git a/debroot/cmd/debian-archive-keyring.gpg b/debroot/cmd/debian-archive-keyring.gpg
deleted file mode 100644
index b13cf79..0000000
--- a/debroot/cmd/debian-archive-keyring.gpg
+++ /dev/null
Binary files differ
diff --git a/debroot/cmd/main.go b/debroot/cmd/main.go
deleted file mode 100644
index d8d1552..0000000
--- a/debroot/cmd/main.go
+++ /dev/null
@@ -1,727 +0,0 @@
-// Copyright 2017 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.
-
-// Generates a Debian based sysroot.
-package main
-
-import (
- "bufio"
- "bytes"
- "compress/gzip"
- "context"
- "crypto/sha256"
- "encoding/hex"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
- "net/url"
- "os"
- "os/exec"
- "path"
- "path/filepath"
- "regexp"
- "sort"
- "strings"
- "time"
-
- "github.com/google/subcommands"
- "golang.org/x/crypto/openpgp"
- "gopkg.in/yaml.v2"
-)
-
-// gpg keyring file generated using:
-// export KEYS="46925553 2B90D010 518E17E1 1A7B6500"
-// gpg --keyserver keys.gnupg.net --recv-keys $KEYS
-// gpg --output ./debian-archive-stretch-stable.gpg --export $KEYS
-
-const (
- // http://http.us.debian.org/debian no longer contains arm64 packages
- // for jessie so we instead use the last snapshot that still has them.
- aptRepo = "https://snapshot.debian.org/archive/debian/20190324T093412Z"
-)
-
-type stringsValue []string
-
-func (i *stringsValue) String() string {
- return strings.Join(*i, ",")
-}
-
-func (i *stringsValue) Set(value string) error {
- *i = strings.Split(value, ",")
- return nil
-}
-
-type Config struct {
- Dists []string `yaml:"dists"`
- Components []string `yaml:"components"`
- Keyring string `yaml:"keyring"`
- Architectures []string `yaml:"architectures"`
- Packages []Package `yaml:"packages"`
-}
-
-type Package struct {
- Name string `yaml:"package"`
- Architectures []string `yaml:"architectures,omitempty"`
-}
-
-type Lockfile struct {
- Hash string `yaml:"hash"`
- Updated time.Time `yaml:"updated"`
- Packages []Lock `yaml:"packages"`
-}
-
-type Lock struct {
- Name string `yaml:"package"`
- Version string `yaml:"version"`
- Filename string `yaml:"filename"`
- Hash string `yaml:"hash"`
-}
-
-type Locks []Lock
-
-func (l Locks) Len() int {
- return len(l)
-}
-
-func (l Locks) Swap(i, j int) {
- l[i], l[j] = l[j], l[i]
-}
-
-func (l Locks) Less(i, j int) bool {
- if l[i].Name == l[j].Name {
- return l[i].Filename < l[j].Filename
- }
- return l[i].Name < l[j].Name
-}
-
-// Descriptor represents a Debian package description.
-type Descriptor map[string]string
-
-// parsePackages parses Debian's control file which described packages.
-//
-// See chapter 5.1 (Syntax of control files) of the Debian Policy Manual:
-// http://www.debian.org/doc/debian-policy/ch-controlfields.html
-func parsePackages(r io.Reader) ([]Descriptor, error) {
- // Packages are separated by double newline, use scanner to split them.
- scanner := bufio.NewScanner(r)
- scanner.Split(func(data []byte, atEOF bool) (int, []byte, error) {
- const separator = "\n\n"
- if i := bytes.Index(data, []byte(separator)); i != -1 {
- return i + len(separator), data[:i], nil
- }
- return 0, nil, nil
- })
- exp := regexp.MustCompile(`(?m)^(?P<key>\S+): (?P<value>(.*)(?:$\s^ .*)*)$`)
- var descriptors []Descriptor
- for scanner.Scan() {
- descriptor := make(Descriptor)
- for _, m := range exp.FindAllStringSubmatch(scanner.Text(), -1) {
- descriptor[m[1]] = m[2]
- }
- descriptors = append(descriptors, descriptor)
- }
- if err := scanner.Err(); err != nil {
- return nil, err
- }
- return descriptors, nil
-}
-
-func downloadPackageList(config *Config, depends bool) ([]Lock, error) {
- descriptors := map[string]map[string]Descriptor{}
-
- file, err := os.Open(config.Keyring)
- if err != nil {
- return nil, err
- }
- defer file.Close()
- keyring, err := openpgp.ReadKeyRing(file)
- if err != nil {
- return nil, err
- }
-
- for _, dist := range config.Dists {
- u, err := url.Parse(aptRepo)
- u.Path = path.Join(u.Path, "dists", dist, "Release")
- r, err := http.Get(u.String())
- if err != nil {
- return nil, err
- }
- defer r.Body.Close()
-
- b, err := ioutil.ReadAll(r.Body)
- if err != nil {
- return nil, err
- }
- var lines []string
- sha256section := false
- for _, l := range strings.Split(string(b), "\n") {
- if sha256section {
- if strings.HasPrefix(l, " ") {
- lines = append(lines, l[1:])
- } else {
- sha256section = false
- }
- } else if strings.HasPrefix(l, "SHA256:") {
- sha256section = true
- }
- }
-
- u, err = url.Parse(aptRepo)
- u.Path = path.Join(u.Path, "dists", dist, "Release.gpg")
- r, err = http.Get(u.String())
- if err != nil {
- return nil, err
- }
- defer r.Body.Close()
-
- _, err = openpgp.CheckArmoredDetachedSignature(keyring, bytes.NewReader(b), r.Body)
- if err != nil {
- return nil, err
- }
-
- for _, a := range config.Architectures {
- for _, c := range config.Components {
- u, err := url.Parse(aptRepo)
- u.Path = path.Join(u.Path, "dists", dist, c, "binary-"+a, "Packages.gz")
- r, err := http.Get(u.String())
- if err != nil {
- return nil, err
- }
- defer r.Body.Close()
-
- buf, err := ioutil.ReadAll(r.Body)
- if err != nil {
- return nil, err
- }
-
- var checksum string
- f := path.Join(c, "binary-"+a, "Packages.gz")
- for _, l := range lines {
- if strings.HasSuffix(l, f) {
- checksum = strings.Fields(l)[0]
- break
- }
- }
- if checksum == "" {
- return nil, fmt.Errorf("%s: checksum missing", f)
- }
-
- sum := sha256.Sum256(buf)
- if checksum != hex.EncodeToString(sum[:]) {
- return nil, fmt.Errorf("%s: checksum doesn't match", f)
- }
-
- g, err := gzip.NewReader(bytes.NewReader(buf))
- if err != nil {
- return nil, err
- }
-
- ps, err := parsePackages(g)
- if err != nil {
- return nil, err
- }
-
- // We only want development libraries, filter out everything else.
- for _, p := range ps {
- // Use sections as a coarse grained filter.
- var section bool
- switch p["Section"] {
- case "libs", "libdevel", "devel", "x11":
- section = true
- }
- // Use tags as a more fine-grained filter.
- var tag bool
- for _, n := range strings.Split(p["Tag"], ", ") {
- t := strings.Split(strings.TrimSpace(n), " ")[0]
- switch t {
- case "devel::library", "x11::library", "role::devel-lib", "role::shared-lib":
- tag = true
- }
- }
- // Skip everything that doesn't match.
- if section && tag {
- n := p["Package"]
- if _, ok := descriptors[n]; !ok {
- descriptors[n] = map[string]Descriptor{}
- }
- descriptors[n][a] = p
- }
- }
- }
- }
- }
-
- type dependency struct {
- name string
- architecture string
- }
-
- // Place the initial set of packages into queue.
- var queue []dependency
- for _, p := range config.Packages {
- if len(p.Architectures) > 0 {
- for _, a := range p.Architectures {
- queue = append(queue, dependency{
- name: p.Name,
- architecture: a,
- })
- }
- } else {
- for _, a := range config.Architectures {
- queue = append(queue, dependency{
- name: p.Name,
- architecture: a,
- })
- }
- }
- }
-
- // Process all dependencies until we drain the queue.
- locks := map[string]map[string]Lock{}
- for len(queue) > 0 {
- p := queue[0]
- queue = queue[1:]
- if lock, ok := locks[p.name]; ok {
- if _, ok := lock[p.architecture]; ok {
- continue
- }
- }
- if ds, ok := descriptors[p.name]; ok {
- if _, ok := locks[p.name]; !ok {
- locks[p.name] = map[string]Lock{}
- }
- if pkg, ok := ds[p.architecture]; ok {
- locks[p.name][p.architecture] = Lock{
- Name: pkg["Package"],
- Version: pkg["Version"],
- Filename: pkg["Filename"],
- Hash: pkg["SHA256"],
- }
- if depends {
- for _, n := range strings.Split(pkg["Depends"], ", ") {
- d := strings.Split(strings.TrimSpace(n), " ")[0]
- queue = append(queue, dependency{
- name: d,
- architecture: p.architecture,
- })
- }
- }
- } else {
- return nil, fmt.Errorf("package %q not found for architecture %q", p.name, p.architecture)
- }
- } else {
- return nil, fmt.Errorf("package %q not found", p.name)
- }
- }
-
- // Eliminate all duplicates.
- hashes := map[string]Lock{}
- for _, l := range locks {
- for _, p := range l {
- hashes[p.Hash] = p
- }
- }
-
- // Flatten into a list.
- var list []Lock
- for _, p := range hashes {
- list = append(list, p)
- }
-
- return list, nil
-}
-
-func relativize(link, target, dir string, patterns []string) error {
- for _, p := range patterns {
- matches, err := filepath.Glob(filepath.Join(dir, p))
- if err != nil {
- return err
- }
- for _, m := range matches {
- if link == m {
- if err := os.Remove(link); err != nil {
- return err
- }
- relDir := ".." + strings.Repeat("/..", strings.Count(p, "/")-1)
- if err := os.Symlink(relDir+target, link); err != nil {
- return err
- }
- return nil
- }
- }
- }
- return nil
-}
-
-func installSysroot(list []Lock, installDir, debsCache string) error {
- if err := os.MkdirAll(debsCache, 0777); err != nil {
- return err
- }
-
- if err := os.RemoveAll(installDir); err != nil {
- return err
- }
-
- // This is only needed when running dpkg-shlibdeps.
- if err := os.MkdirAll(filepath.Join(installDir, "debian"), 0777); err != nil {
- return err
- }
-
- // An empty control file is necessary to run dpkg-shlibdeps.
- if file, err := os.OpenFile(filepath.Join(installDir, "debian", "control"), os.O_RDONLY|os.O_CREATE, 0644); err != nil {
- return err
- } else {
- file.Close()
- }
-
- for _, pkg := range list {
- filename := filepath.Base(pkg.Filename)
- deb := filepath.Join(debsCache, filename)
- if _, err := os.Stat(deb); os.IsNotExist(err) {
- fmt.Printf("Downloading %s...\n", filename)
-
- u, err := url.Parse(aptRepo)
- u.Path = path.Join(u.Path, pkg.Filename)
- r, err := http.Get(u.String())
- if err != nil {
- return err
- }
- defer r.Body.Close()
-
- buf, err := ioutil.ReadAll(r.Body)
- if err != nil {
- return err
- }
-
- sum := sha256.Sum256(buf)
- if pkg.Hash != hex.EncodeToString(sum[:]) {
- return fmt.Errorf("%s: checksum doesn't match", filename)
- }
-
- if err := ioutil.WriteFile(deb, buf, 0644); err != nil {
- return err
- }
- }
-
- fmt.Printf("Installing %s...\n", filename)
- // Extract the content of the package into the install directory.
- err := exec.Command("dpkg-deb", "-x", deb, installDir).Run()
- if err != nil {
- return err
- }
- // Get the package name.
- cmd := exec.Command("dpkg-deb", "--field", deb, "Package")
- stdout, err := cmd.StdoutPipe()
- if err != nil {
- return err
- }
- if err := cmd.Start(); err != nil {
- return err
- }
- r := bufio.NewReader(stdout)
- baseDir, _, err := r.ReadLine()
- if err != nil {
- return err
- }
- if err := cmd.Wait(); err != nil {
- return err
- }
- // Construct the path which contains the control information files.
- controlDir := filepath.Join(installDir, "debian", string(baseDir), "DEBIAN")
- if err := os.MkdirAll(controlDir, 0777); err != nil {
- return err
- }
- // Extract the control information files.
- err = exec.Command("dpkg-deb", "-e", deb, controlDir).Run()
- if err != nil {
- return err
- }
- }
-
- // Prune /usr/share, leave only pkgconfig files.
- files, err := ioutil.ReadDir(filepath.Join(installDir, "usr", "share"))
- if err != nil {
- return err
- }
- for _, file := range files {
- if file.Name() != "pkgconfig" {
- if err := os.RemoveAll(filepath.Join(installDir, "usr", "share", file.Name())); err != nil {
- return err
- }
- }
- }
-
- // Ensure that we don't have duplicate file names that only differ in case.
- type rename struct{ oldpath, newpath string }
- renames := []rename{}
-
- for _, d := range []string{"usr/include/linux"} {
- p := filepath.Join(installDir, d)
- if _, err := os.Stat(p); os.IsNotExist(err) {
- continue
- }
- paths := make(map[string][]string)
- if err := filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
- if info.Mode().IsRegular() && filepath.Ext(path) == ".h" {
- name := strings.ToLower(path)
- if _, ok := paths[name]; !ok {
- paths[name] = []string{}
- }
- paths[name] = append(paths[name], path)
- }
- return nil
- }); err != nil {
- return err
- }
-
- for _, ps := range paths {
- if len(ps) > 1 {
- sort.Sort(sort.Reverse(sort.StringSlice(ps)))
- for i, p := range ps {
- if i > 0 {
- ext := filepath.Ext(p)
- renames = append(renames, rename{p, p[:len(p)-len(ext)] + strings.Repeat("_", i) + ext})
- }
- }
- }
- }
- }
-
- for _, r := range renames {
- fmt.Printf("Renaming %s to %s\n", r.oldpath, r.newpath)
- if err := os.Rename(r.oldpath, r.newpath); err != nil {
- return err
- }
- }
-
- for _, d := range []string{"usr/include/linux"} {
- p := filepath.Join(installDir, d)
- if _, err := os.Stat(p); os.IsNotExist(err) {
- continue
- }
- if err := filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
- update := false
- if info.Mode().IsRegular() && filepath.Ext(path) == ".h" {
- content, err := ioutil.ReadFile(path)
- if err != nil {
- return err
- }
- for _, r := range renames {
- oldbase := filepath.Base(r.oldpath)
- newbase := filepath.Base(r.newpath)
- if strings.Contains(string(content), oldbase) {
- content = bytes.Replace(content, []byte(oldbase), []byte(newbase), 1)
- update = true
- }
- }
- if update {
- fmt.Printf("Updating %s...\n", path)
- if err := ioutil.WriteFile(path, content, info.Mode()); err != nil {
- return err
- }
- }
- }
- return nil
- }); err != nil {
- return err
- }
- }
-
- // Relativize all symlinks within the sysroot.
- for _, d := range []string{"usr/lib", "lib64", "lib"} {
- p := filepath.Join(installDir, d)
- if _, err := os.Stat(p); os.IsNotExist(err) {
- continue
- }
- if err := filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
- if info.Mode()&os.ModeSymlink == os.ModeSymlink {
- target, err := os.Readlink(path)
- if err != nil {
- return err
- }
- if !filepath.IsAbs(target) {
- return nil
- }
- patterns := []string{
- "usr/lib/gcc/*-linux-gnu*/*/*",
- "usr/lib/*-linux-gnu*/*",
- "usr/lib/*",
- "lib64/*",
- "lib/*",
- }
- if err := relativize(path, target, installDir, patterns); err != nil {
- return err
- }
- if _, err := os.Stat(path); os.IsNotExist(err) {
- return fmt.Errorf("%s: broken link", path)
- }
- }
- return nil
- }); err != nil {
- return err
- }
- }
-
- // Rewrite and relativize all linkerscripts.
- linkerscripts := []string{
- "usr/lib/*-linux-gnu*/libpthread.so",
- "usr/lib/*-linux-gnu*/libc.so",
- }
- for _, l := range linkerscripts {
- matches, err := filepath.Glob(filepath.Join(installDir, l))
- if err != nil {
- return err
- }
- for _, path := range matches {
- read, err := ioutil.ReadFile(path)
- if err != nil {
- return err
- }
- sub := regexp.MustCompile(`(/usr)?/lib/[a-z0-9_]+-linux-gnu[a-z0-9]*/`)
- contents := sub.ReplaceAllString(string(read), "")
- if err := ioutil.WriteFile(path, []byte(contents), 0644); err != nil {
- return err
- }
- }
- }
-
- if err := os.RemoveAll(filepath.Join(installDir, "debian")); err != nil {
- return err
- }
-
- return nil
-}
-
-type updateCmd struct {
- config string
- lockfile string
- depends bool
-}
-
-func (*updateCmd) Name() string { return "update" }
-func (*updateCmd) Synopsis() string { return "Update the lock file." }
-func (*updateCmd) Usage() string {
- return `update [-config] [-lock] [-depends]:
- Update the lock file to include specific package versions.
-`
-}
-
-func (c *updateCmd) SetFlags(f *flag.FlagSet) {
- f.StringVar(&c.config, "config", "packages.yml", "Package configuration")
- f.StringVar(&c.lockfile, "lock", "packages.lock", "Lockfile filename")
- f.BoolVar(&c.depends, "depends", false, "Transitively include dependencies")
-}
-
-func (c *updateCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
- d, err := ioutil.ReadFile(c.config)
- if err != nil {
- fmt.Fprintf(os.Stderr, "failed to read config: %v\n", err)
- return subcommands.ExitFailure
- }
- config := &Config{}
- if err := yaml.Unmarshal(d, &config); err != nil {
- fmt.Fprintf(os.Stderr, "failed to unmarshal config: %v\n", err)
- return subcommands.ExitFailure
- }
-
- if _, err := os.Stat(config.Keyring); os.IsNotExist(err) {
- fmt.Fprintf(os.Stderr, "keyring file '%s' missing\n", config.Keyring)
- return subcommands.ExitUsageError
- }
-
- list, err := downloadPackageList(config, c.depends)
- if err != nil {
- fmt.Fprintf(os.Stderr, "failed to download package list: %v\n", err)
- return subcommands.ExitFailure
- }
- sort.Sort(Locks(list))
-
- hash := sha256.New()
- hash.Write(d)
-
- lockfile := Lockfile{
- Updated: time.Now(),
- Hash: fmt.Sprintf("%x", hash.Sum(nil)),
- Packages: list,
- }
-
- l, err := yaml.Marshal(&lockfile)
- if err != nil {
- fmt.Fprintf(os.Stderr, "failed to marshal lockfile: %v\n", err)
- return subcommands.ExitFailure
- }
-
- if err := ioutil.WriteFile(c.lockfile, l, 0666); err != nil {
- fmt.Fprintf(os.Stderr, "failed to write lockfile: %v\n", err)
- return subcommands.ExitFailure
- }
-
- return subcommands.ExitSuccess
-}
-
-type installCmd struct {
- outDir string
- debsCache string
-}
-
-func (*installCmd) Name() string { return "install" }
-func (*installCmd) Synopsis() string { return "Install packages" }
-func (*installCmd) Usage() string {
- return `install [-out] [-cache] <lockfile>:
- Install the specific versions from the lock file.
-`
-}
-
-func (c *installCmd) SetFlags(f *flag.FlagSet) {
- f.StringVar(&c.outDir, "out", "sysroot", "Output directory")
- f.StringVar(&c.debsCache, "cache", "debs", "Cache for .deb files")
-}
-
-func (c *installCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
- if len(f.Args()) != 1 {
- fmt.Fprintln(os.Stderr, "missing lockfile argument")
- return subcommands.ExitUsageError
- }
- lockfile := f.Args()[0]
-
- d, err := ioutil.ReadFile(lockfile)
- if err != nil {
- fmt.Fprintf(os.Stderr, "failed to read lockfile: %v\n", err)
- return subcommands.ExitFailure
- }
- var lock Lockfile
- if err := yaml.Unmarshal(d, &lock); err != nil {
- fmt.Fprintf(os.Stderr, "failed to unmarshal lockfile: %v\n", err)
- return subcommands.ExitFailure
- }
-
- if err := installSysroot(lock.Packages, c.outDir, c.debsCache); err != nil {
- fmt.Fprintf(os.Stderr, "failed to install sysroot: %v\n", err)
- return subcommands.ExitFailure
- }
- return subcommands.ExitSuccess
-}
-
-func main() {
- subcommands.Register(subcommands.HelpCommand(), "")
- subcommands.Register(subcommands.FlagsCommand(), "")
- subcommands.Register(subcommands.CommandsCommand(), "")
- subcommands.Register(&updateCmd{}, "")
- subcommands.Register(&installCmd{}, "")
-
- flag.Parse()
- ctx := context.Background()
- os.Exit(int(subcommands.Execute(ctx)))
-}
diff --git a/debroot/cmd/packages.lock b/debroot/cmd/packages.lock
deleted file mode 100644
index bd08182..0000000
--- a/debroot/cmd/packages.lock
+++ /dev/null
@@ -1,403 +0,0 @@
-hash: 8727abfc278cd94cc0b6d4285f92eca18f7495bf7650a26e416ad585c9b3f65a
-updated: 2019-08-14T21:38:48.608392147-07:00
-packages:
-- package: libc6
- version: 2.19-18+deb8u10
- filename: pool/main/g/glibc/libc6_2.19-18+deb8u10_amd64.deb
- hash: 0a95ee1c5bff7f73c1279b2b78f32d40da9025a76f93cb67c03f2867a7133e61
-- package: libc6
- version: 2.19-18+deb8u10
- filename: pool/main/g/glibc/libc6_2.19-18+deb8u10_arm64.deb
- hash: b52eab4f1beeb020e20fb44bc2dc02a9ccf12fc70213d6a56921e08308bc3e9c
-- package: libc6
- version: 2.19-18+deb8u10
- filename: pool/main/g/glibc/libc6_2.19-18+deb8u10_armhf.deb
- hash: ccfc4a10a1654454ad07ac381d55dc1bfe0787ebbcd87b42ac05402777ce41b5
-- package: libc6
- version: 2.19-18+deb8u10
- filename: pool/main/g/glibc/libc6_2.19-18+deb8u10_i386.deb
- hash: 1d1f82f905386ff6a1dab08ec69b3b1d8bddbfd7c41b68e295e0a204232ad1ae
-- package: libc6-dev
- version: 2.19-18+deb8u10
- filename: pool/main/g/glibc/libc6-dev_2.19-18+deb8u10_amd64.deb
- hash: 00ba98ce8879bdfde13eac699da718d3aaba904ea6a88d5e21e77d1e298d65eb
-- package: libc6-dev
- version: 2.19-18+deb8u10
- filename: pool/main/g/glibc/libc6-dev_2.19-18+deb8u10_arm64.deb
- hash: 9cf8ae92bb508873e19b43975b5841166fc2476664d9de52eb62ae3a1a50abb3
-- package: libc6-dev
- version: 2.19-18+deb8u10
- filename: pool/main/g/glibc/libc6-dev_2.19-18+deb8u10_armhf.deb
- hash: b5af7102716127343a82dfdb42e5b9cd9dc28e28fc14653e0db8f9efdd1fe0a9
-- package: libc6-dev
- version: 2.19-18+deb8u10
- filename: pool/main/g/glibc/libc6-dev_2.19-18+deb8u10_i386.deb
- hash: 287ea40a96c4eafb94843169f6bf802335182b68f78a1b4bab95851c3385866d
-- package: libgl1-mesa-dev
- version: 10.3.2-1+deb8u1
- filename: pool/main/m/mesa/libgl1-mesa-dev_10.3.2-1+deb8u1_amd64.deb
- hash: b790d56135725193fc38216d4c6017003209d23e4250147882f075b5622e5e06
-- package: libgl1-mesa-dev
- version: 10.3.2-1+deb8u1
- filename: pool/main/m/mesa/libgl1-mesa-dev_10.3.2-1+deb8u1_arm64.deb
- hash: b363bbe43937a7f23a868e64b278e29f3f45b7608cdc41f295f6088b566b045c
-- package: libgl1-mesa-dev
- version: 10.3.2-1+deb8u1
- filename: pool/main/m/mesa/libgl1-mesa-dev_10.3.2-1+deb8u1_armhf.deb
- hash: 81f7f98b50e513b5b21f0265542a5544b2c97a2e8f059c79b4e6208dc5d5e656
-- package: libgl1-mesa-dev
- version: 10.3.2-1+deb8u1
- filename: pool/main/m/mesa/libgl1-mesa-dev_10.3.2-1+deb8u1_i386.deb
- hash: 754a9968ea861842a289fa34ca68123723170eb308bd5c64cc7cdb69a939fdbd
-- package: libgl1-mesa-glx
- version: 10.3.2-1+deb8u1
- filename: pool/main/m/mesa/libgl1-mesa-glx_10.3.2-1+deb8u1_amd64.deb
- hash: de91fad0ffb35ba497ec146ccfa261df79dced33ffc6f9e229435dadeac49e64
-- package: libgl1-mesa-glx
- version: 10.3.2-1+deb8u1
- filename: pool/main/m/mesa/libgl1-mesa-glx_10.3.2-1+deb8u1_arm64.deb
- hash: e22ec22e5a46920c6340ea3224a43ec677f728d368fe34421d51d304046a62a0
-- package: libgl1-mesa-glx
- version: 10.3.2-1+deb8u1
- filename: pool/main/m/mesa/libgl1-mesa-glx_10.3.2-1+deb8u1_armhf.deb
- hash: f62a51c6968c9b7b0312081816c538cc4ff49c3a799722f06e492bf0e13ca259
-- package: libgl1-mesa-glx
- version: 10.3.2-1+deb8u1
- filename: pool/main/m/mesa/libgl1-mesa-glx_10.3.2-1+deb8u1_i386.deb
- hash: 11aee5275de4a92d297a785f79ffc1696db99e2761af63355aa7ebe34139ecaa
-- package: libglapi-mesa
- version: 10.3.2-1+deb8u1
- filename: pool/main/m/mesa/libglapi-mesa_10.3.2-1+deb8u1_amd64.deb
- hash: 8f77452524501072c09676bd1acf4b71d864d7d2a18fa8158aa5a5cb0bc3ade3
-- package: libglapi-mesa
- version: 10.3.2-1+deb8u1
- filename: pool/main/m/mesa/libglapi-mesa_10.3.2-1+deb8u1_arm64.deb
- hash: 24007141988b34aab55abf03a606ea590ac71d62e6cd0aac015427cfab6c0df4
-- package: libglapi-mesa
- version: 10.3.2-1+deb8u1
- filename: pool/main/m/mesa/libglapi-mesa_10.3.2-1+deb8u1_armhf.deb
- hash: 7098825f957f7bcf216f65fbb8e1c3efe6365041103f77a412f0c8c1145de17f
-- package: libglapi-mesa
- version: 10.3.2-1+deb8u1
- filename: pool/main/m/mesa/libglapi-mesa_10.3.2-1+deb8u1_i386.deb
- hash: 3b39319fbb66afcf17ba1e6bef9c5583eb873cbd14621821143babb0f855529e
-- package: libx11-6
- version: 2:1.6.2-3+deb8u1
- filename: pool/main/libx/libx11/libx11-6_1.6.2-3+deb8u1_amd64.deb
- hash: 380aa38372e949f74b57e962146626aae847850ed4f1fcb83784086f239d50c3
-- package: libx11-6
- version: 2:1.6.2-3+deb8u1
- filename: pool/main/libx/libx11/libx11-6_1.6.2-3+deb8u1_arm64.deb
- hash: 20ce433137bd35b1f131dadf8dd0f4bedfeac804ae875d495d572b52b9f238cd
-- package: libx11-6
- version: 2:1.6.2-3+deb8u1
- filename: pool/main/libx/libx11/libx11-6_1.6.2-3+deb8u1_armhf.deb
- hash: 29b2da94d351278e0682f86b290724998b6aace8dd730a23930feb5ad11662e7
-- package: libx11-6
- version: 2:1.6.2-3+deb8u1
- filename: pool/main/libx/libx11/libx11-6_1.6.2-3+deb8u1_i386.deb
- hash: 7f55cb1a60fdd6a02bda2c00d6a438f836404681d0e7a8ce7c93cee1545ef7ca
-- package: libx11-dev
- version: 2:1.6.2-3+deb8u1
- filename: pool/main/libx/libx11/libx11-dev_1.6.2-3+deb8u1_amd64.deb
- hash: cd141e5c95d7b5f05df08a8e66abbbdb9a2a7213f9f4354b862925fac6106334
-- package: libx11-dev
- version: 2:1.6.2-3+deb8u1
- filename: pool/main/libx/libx11/libx11-dev_1.6.2-3+deb8u1_arm64.deb
- hash: fb9e44a7520dd7cc9ec911954fc4896c31aff683c56a8fbf64867b1e184dd7f2
-- package: libx11-dev
- version: 2:1.6.2-3+deb8u1
- filename: pool/main/libx/libx11/libx11-dev_1.6.2-3+deb8u1_armhf.deb
- hash: d5b0c2b3e8939e896da089757151a9f1f202b0e65134ed453b37d465566acd25
-- package: libx11-dev
- version: 2:1.6.2-3+deb8u1
- filename: pool/main/libx/libx11/libx11-dev_1.6.2-3+deb8u1_i386.deb
- hash: 82fd52f4db77855cbf76f274550ff07d2a4bfe62a9c9d10e0345e7c8dece4188
-- package: libxcursor-dev
- version: 1:1.1.14-1+deb8u1
- filename: pool/main/libx/libxcursor/libxcursor-dev_1.1.14-1+deb8u1_amd64.deb
- hash: 101d83f1368cef54594100b849843df0d1952b0155b707f2256914d4e2d9debc
-- package: libxcursor-dev
- version: 1:1.1.14-1+deb8u1
- filename: pool/main/libx/libxcursor/libxcursor-dev_1.1.14-1+deb8u1_arm64.deb
- hash: 5c5125291e5258deac4442fe363c7da162329125e46e9d18add6efee5a6a7ba3
-- package: libxcursor-dev
- version: 1:1.1.14-1+deb8u1
- filename: pool/main/libx/libxcursor/libxcursor-dev_1.1.14-1+deb8u1_armhf.deb
- hash: 3836920c14f8e9ecd3aabe31b4b3526e135125f3712cba4b4a030a218be1699b
-- package: libxcursor-dev
- version: 1:1.1.14-1+deb8u1
- filename: pool/main/libx/libxcursor/libxcursor-dev_1.1.14-1+deb8u1_i386.deb
- hash: 61a4618472acb9cd464c1f42b304d5250cf0fe71212682453031f7fde172d799
-- package: libxcursor1
- version: 1:1.1.14-1+deb8u1
- filename: pool/main/libx/libxcursor/libxcursor1_1.1.14-1+deb8u1_amd64.deb
- hash: 31c05ccd419d5c255643e3d21c871204e154a06af25300c2bd2b721989d038f0
-- package: libxcursor1
- version: 1:1.1.14-1+deb8u1
- filename: pool/main/libx/libxcursor/libxcursor1_1.1.14-1+deb8u1_arm64.deb
- hash: ce62dcf433498d25f2534213f48184e08ca72abe11454041cf96d845bc32d062
-- package: libxcursor1
- version: 1:1.1.14-1+deb8u1
- filename: pool/main/libx/libxcursor/libxcursor1_1.1.14-1+deb8u1_armhf.deb
- hash: 87a99212079783b499b402426796d2ceb07e14f1b972e4e73f7e25ec3046fa9a
-- package: libxcursor1
- version: 1:1.1.14-1+deb8u1
- filename: pool/main/libx/libxcursor/libxcursor1_1.1.14-1+deb8u1_i386.deb
- hash: 771530e98891da87de8c15156d2a2d06ab293086a35f5167f9116548f41900c4
-- package: libxext-dev
- version: 2:1.3.3-1
- filename: pool/main/libx/libxext/libxext-dev_1.3.3-1_amd64.deb
- hash: 35d62e8b6c72ea9ae67488f06360224a918f6d87ceb27575f49a9e712bf8b70a
-- package: libxext-dev
- version: 2:1.3.3-1
- filename: pool/main/libx/libxext/libxext-dev_1.3.3-1_arm64.deb
- hash: c8abe305d5b35eb2f6b342f72e6fd2294748eb6254b21fb3d68e288c864a70e9
-- package: libxext-dev
- version: 2:1.3.3-1
- filename: pool/main/libx/libxext/libxext-dev_1.3.3-1_armhf.deb
- hash: 0a23e3dd6f6fcb51219b75654bac5df8ff92ef029d8203e6efebcf0c9047501c
-- package: libxext-dev
- version: 2:1.3.3-1
- filename: pool/main/libx/libxext/libxext-dev_1.3.3-1_i386.deb
- hash: 5f1e821fcc241d642e15c0a966536e12320aaf319dbd9738a7715108f3ad22ab
-- package: libxext6
- version: 2:1.3.3-1
- filename: pool/main/libx/libxext/libxext6_1.3.3-1_amd64.deb
- hash: 5105f77289a7e08faaf3bb4c16c6945cafca65b9bdb4792fef820045ab43e568
-- package: libxext6
- version: 2:1.3.3-1
- filename: pool/main/libx/libxext/libxext6_1.3.3-1_arm64.deb
- hash: 2d9b203682b2ac5e3cb3e585e6a51e59bd7ac32486827586a4ca121e84c8d794
-- package: libxext6
- version: 2:1.3.3-1
- filename: pool/main/libx/libxext/libxext6_1.3.3-1_armhf.deb
- hash: c98094a2dcc1965bb04ed8929fca6899e1cad3ad1e0f29a5c20a79338dacb702
-- package: libxext6
- version: 2:1.3.3-1
- filename: pool/main/libx/libxext/libxext6_1.3.3-1_i386.deb
- hash: d9025901af98bcda89028753583e146989f6f25eacbb7b073f2bfe4619a0fb79
-- package: libxfixes-dev
- version: 1:5.0.1-2+deb8u1
- filename: pool/main/libx/libxfixes/libxfixes-dev_5.0.1-2+deb8u1_amd64.deb
- hash: 4dc7467a9dbc531d95884217437e2e8e856c8f211fe3fcebb2297998f72901b3
-- package: libxfixes-dev
- version: 1:5.0.1-2+deb8u1
- filename: pool/main/libx/libxfixes/libxfixes-dev_5.0.1-2+deb8u1_arm64.deb
- hash: 3ce4c58fd7a97cf3217a4ac35c495e444d944dedb22b3893cffeecd475fe8f60
-- package: libxfixes-dev
- version: 1:5.0.1-2+deb8u1
- filename: pool/main/libx/libxfixes/libxfixes-dev_5.0.1-2+deb8u1_armhf.deb
- hash: 7c0fecdc8973056fb32aa9255b58536236bfde0b2fe0bfdbfbad8beff79b2216
-- package: libxfixes-dev
- version: 1:5.0.1-2+deb8u1
- filename: pool/main/libx/libxfixes/libxfixes-dev_5.0.1-2+deb8u1_i386.deb
- hash: ae0692bbc186ec19d610b5bc5a027bdd33563887f19d780b1b2024c3702802aa
-- package: libxfixes3
- version: 1:5.0.1-2+deb8u1
- filename: pool/main/libx/libxfixes/libxfixes3_5.0.1-2+deb8u1_amd64.deb
- hash: bc30bf139964c7505776374c7ba8a7f9bd7c6bd101823f3d6a1f3b0bf11e41ee
-- package: libxfixes3
- version: 1:5.0.1-2+deb8u1
- filename: pool/main/libx/libxfixes/libxfixes3_5.0.1-2+deb8u1_arm64.deb
- hash: 8e9d0fbc96e12fa7a3400901b7deb755ca46b21e52c2c4852c59e28c95b20c04
-- package: libxfixes3
- version: 1:5.0.1-2+deb8u1
- filename: pool/main/libx/libxfixes/libxfixes3_5.0.1-2+deb8u1_armhf.deb
- hash: fda5f4b0cd09388acab581e59c2064e0398e23f3677c06ab82477ac615759016
-- package: libxfixes3
- version: 1:5.0.1-2+deb8u1
- filename: pool/main/libx/libxfixes/libxfixes3_5.0.1-2+deb8u1_i386.deb
- hash: 44f2d8cb572ff8e2a5c6dcd9d0aa42eda7a0eed0c50a403cc9239d6d3a2bf308
-- package: libxi-dev
- version: 2:1.7.4-1+deb8u1
- filename: pool/main/libx/libxi/libxi-dev_1.7.4-1+deb8u1_amd64.deb
- hash: 77dd8c34c69d86dec841028c0fca646544cea079ee95643db3fdc15cbdf9d8bf
-- package: libxi-dev
- version: 2:1.7.4-1+deb8u1
- filename: pool/main/libx/libxi/libxi-dev_1.7.4-1+deb8u1_arm64.deb
- hash: 5aee0969b1792e41ab7e3b4dfe0bd1e2657cb32fe3e880a25bf87a3a645f9cdc
-- package: libxi-dev
- version: 2:1.7.4-1+deb8u1
- filename: pool/main/libx/libxi/libxi-dev_1.7.4-1+deb8u1_armhf.deb
- hash: de4bb8afb8f2fc3e083cc89e39aa839b7af3c40ab312bb1ac7342bdfb2f605f2
-- package: libxi-dev
- version: 2:1.7.4-1+deb8u1
- filename: pool/main/libx/libxi/libxi-dev_1.7.4-1+deb8u1_i386.deb
- hash: 4ed707f9a73f7381fe7e16c4f4099f47b025700465dfada8aa7c8678f4e8a657
-- package: libxi6
- version: 2:1.7.4-1+deb8u1
- filename: pool/main/libx/libxi/libxi6_1.7.4-1+deb8u1_amd64.deb
- hash: a99de627a07f54bc40e5dc77e3ca7e9d9b5619e068f1bb303f86edd138d6d037
-- package: libxi6
- version: 2:1.7.4-1+deb8u1
- filename: pool/main/libx/libxi/libxi6_1.7.4-1+deb8u1_arm64.deb
- hash: fddbfdce740bd5a74cb34d69d10d0e42a9abff78cd89e7119cdf21a8e3dd5d3a
-- package: libxi6
- version: 2:1.7.4-1+deb8u1
- filename: pool/main/libx/libxi/libxi6_1.7.4-1+deb8u1_armhf.deb
- hash: 79c53ea34b9a7c239a75aac28386ca2614f7add0be3578ca4710705ba3e0bd66
-- package: libxi6
- version: 2:1.7.4-1+deb8u1
- filename: pool/main/libx/libxi/libxi6_1.7.4-1+deb8u1_i386.deb
- hash: d6f5e16a2ea39f045273b8ab1ac64436ce6df0b21fb77f3839d298ebdac297e5
-- package: libxinerama-dev
- version: 2:1.1.3-1+b1
- filename: pool/main/libx/libxinerama/libxinerama-dev_1.1.3-1+b1_amd64.deb
- hash: 0e2504bbe3501aae376691e2519336533309c8a97715f8a2b060cdcd6abce15f
-- package: libxinerama-dev
- version: 2:1.1.3-1+b1
- filename: pool/main/libx/libxinerama/libxinerama-dev_1.1.3-1+b1_arm64.deb
- hash: 9094a4298c357455ea4a4094ecdf93a85a1ab404630e35da1dedad5b08e30f27
-- package: libxinerama-dev
- version: 2:1.1.3-1+b1
- filename: pool/main/libx/libxinerama/libxinerama-dev_1.1.3-1+b1_armhf.deb
- hash: 49d378db7c271db6b3befc7cc73448177b4515424d17e400b053f199df4b2a77
-- package: libxinerama-dev
- version: 2:1.1.3-1+b1
- filename: pool/main/libx/libxinerama/libxinerama-dev_1.1.3-1+b1_i386.deb
- hash: 490439d9ebff2e4bc324e5d418d4d9a245a223b5d211de3189cabd78ff7b47d7
-- package: libxinerama1
- version: 2:1.1.3-1+b1
- filename: pool/main/libx/libxinerama/libxinerama1_1.1.3-1+b1_amd64.deb
- hash: 3668c1178ac649879338192759e79879e0f5797764b448dcac6aed4b24ef495b
-- package: libxinerama1
- version: 2:1.1.3-1+b1
- filename: pool/main/libx/libxinerama/libxinerama1_1.1.3-1+b1_arm64.deb
- hash: e573c84cb609a8799db03b19f1529153048d9e04307e8721fa4f0b516cb47171
-- package: libxinerama1
- version: 2:1.1.3-1+b1
- filename: pool/main/libx/libxinerama/libxinerama1_1.1.3-1+b1_armhf.deb
- hash: 9fcc2234beb0eb4e0ca3a38d93d38655a433551c04b3d73aa06ca020121d5d2f
-- package: libxinerama1
- version: 2:1.1.3-1+b1
- filename: pool/main/libx/libxinerama/libxinerama1_1.1.3-1+b1_i386.deb
- hash: a1f29a26a81405df89d2ddf64990d9e32d7b642130334db66f063d78c5574633
-- package: libxrandr-dev
- version: 2:1.4.2-1+deb8u1
- filename: pool/main/libx/libxrandr/libxrandr-dev_1.4.2-1+deb8u1_amd64.deb
- hash: d64ac2abe410fe6e25c6de2b90fd2a65dbfdf027c117a574714a0a60c019738f
-- package: libxrandr-dev
- version: 2:1.4.2-1+deb8u1
- filename: pool/main/libx/libxrandr/libxrandr-dev_1.4.2-1+deb8u1_arm64.deb
- hash: d1c1c132da1d809acc8e3b11f173c5a28ba28e76d70dd81123cbf41c6fbb90eb
-- package: libxrandr-dev
- version: 2:1.4.2-1+deb8u1
- filename: pool/main/libx/libxrandr/libxrandr-dev_1.4.2-1+deb8u1_armhf.deb
- hash: 712357a9065f49f4174e04ddb49e2e1eac63a168c27ae9737f04a463e309757d
-- package: libxrandr-dev
- version: 2:1.4.2-1+deb8u1
- filename: pool/main/libx/libxrandr/libxrandr-dev_1.4.2-1+deb8u1_i386.deb
- hash: 727435c3854fec93401eb2421842a5749cc113797799dccfa246d6f9b2f22f84
-- package: libxrandr2
- version: 2:1.4.2-1+deb8u1
- filename: pool/main/libx/libxrandr/libxrandr2_1.4.2-1+deb8u1_amd64.deb
- hash: e59996ac0e993331799aa7bc768f7edd7b0baec0fafdbeebeef658834ba96b63
-- package: libxrandr2
- version: 2:1.4.2-1+deb8u1
- filename: pool/main/libx/libxrandr/libxrandr2_1.4.2-1+deb8u1_arm64.deb
- hash: 713b2b8a05bc05dc61fa158b2fb4f665272d2f60a86f4bcb15722aec3a02c9fd
-- package: libxrandr2
- version: 2:1.4.2-1+deb8u1
- filename: pool/main/libx/libxrandr/libxrandr2_1.4.2-1+deb8u1_armhf.deb
- hash: 9c379b71846de6d3cb35191962caa5f15d0964fdfd277c3f39b56275dbd1566e
-- package: libxrandr2
- version: 2:1.4.2-1+deb8u1
- filename: pool/main/libx/libxrandr/libxrandr2_1.4.2-1+deb8u1_i386.deb
- hash: c14134e213a1020e4848f927612377bce319a6a4640b5d572009cb8929b0142a
-- package: libxrender-dev
- version: 1:0.9.8-1+b1
- filename: pool/main/libx/libxrender/libxrender-dev_0.9.8-1+b1_amd64.deb
- hash: d17bd9a32eaecdd75d465d38bfc6318aa00e92fa5289ed36a8853f94feb18cbe
-- package: libxrender-dev
- version: 1:0.9.8-1+b1
- filename: pool/main/libx/libxrender/libxrender-dev_0.9.8-1+b1_arm64.deb
- hash: 7149bd62f79f8f0d5ef8981722e3f1e7d3090b037bf850a607c2ca69f66cf2ad
-- package: libxrender-dev
- version: 1:0.9.8-1+b1
- filename: pool/main/libx/libxrender/libxrender-dev_0.9.8-1+b1_armhf.deb
- hash: 031a2ebffde76e4c201f43c1b66a62a1af7c488877e541623fa5ab6b790abb07
-- package: libxrender-dev
- version: 1:0.9.8-1+b1
- filename: pool/main/libx/libxrender/libxrender-dev_0.9.8-1+b1_i386.deb
- hash: e399b94ab1c07f7f1d8e72a43630f9e83ae439a279d81a51c9cd82848b986355
-- package: libxrender1
- version: 1:0.9.8-1+b1
- filename: pool/main/libx/libxrender/libxrender1_0.9.8-1+b1_amd64.deb
- hash: 8980934c84c0ebbca4158023d91ced3ddbfa028a2be25959ff4566b37eceb8f4
-- package: libxrender1
- version: 1:0.9.8-1+b1
- filename: pool/main/libx/libxrender/libxrender1_0.9.8-1+b1_arm64.deb
- hash: c2607cd67ff2eb60f88b2532e6999d1a3d31c55afa20ac9be431148a76374908
-- package: libxrender1
- version: 1:0.9.8-1+b1
- filename: pool/main/libx/libxrender/libxrender1_0.9.8-1+b1_armhf.deb
- hash: 8bb5f345e6603bbdd151619e045637512e9c3dad27bd670983a50c15b4d98fb0
-- package: libxrender1
- version: 1:0.9.8-1+b1
- filename: pool/main/libx/libxrender/libxrender1_0.9.8-1+b1_i386.deb
- hash: df3f4d61be300203ffde82c829e2bb0a72eceb4991161f419dc5a7ec061d92f9
-- package: linux-libc-dev
- version: 3.16.51-3
- filename: pool/main/l/linux/linux-libc-dev_3.16.51-3_amd64.deb
- hash: 8cdc63378b6dfe8995f40cd6f04701e89dbca588e9a9c6fbbe524d1db4cb4f08
-- package: linux-libc-dev
- version: 3.16.51-3
- filename: pool/main/l/linux/linux-libc-dev_3.16.51-3_arm64.deb
- hash: f2e7ab5f0cc3dd03cd7278e5574dc25b744a399f75cb2890c346a064954779dd
-- package: linux-libc-dev
- version: 3.16.51-3
- filename: pool/main/l/linux/linux-libc-dev_3.16.51-3_armhf.deb
- hash: 14bfef400b83e2f3a587dc564b7d13bf72f27f739d6a74d77a9326f83b3d9460
-- package: linux-libc-dev
- version: 3.16.51-3
- filename: pool/main/l/linux/linux-libc-dev_3.16.51-3_i386.deb
- hash: 35def1939fccfd26335d000528a0053e6092a488abce9bbb47d94eb161115d1b
-- package: mesa-common-dev
- version: 10.3.2-1+deb8u1
- filename: pool/main/m/mesa/mesa-common-dev_10.3.2-1+deb8u1_amd64.deb
- hash: 6e617bfdbe9b87a82981b33d2be0963d3cba28160565658420859d84d07ef7a0
-- package: mesa-common-dev
- version: 10.3.2-1+deb8u1
- filename: pool/main/m/mesa/mesa-common-dev_10.3.2-1+deb8u1_arm64.deb
- hash: f22b5fa98c4e367d1753a18b2feabb9a1c8185abc727357a783567fe523d1e96
-- package: mesa-common-dev
- version: 10.3.2-1+deb8u1
- filename: pool/main/m/mesa/mesa-common-dev_10.3.2-1+deb8u1_armhf.deb
- hash: 19d7b6756d27ddca44356cb4cb3fcaee19dd8166cf64e52d0dfe51302ea39a21
-- package: mesa-common-dev
- version: 10.3.2-1+deb8u1
- filename: pool/main/m/mesa/mesa-common-dev_10.3.2-1+deb8u1_i386.deb
- hash: 9d2de0e90cca097d380da9e5bd1fdfc34f15b71ba8da5de1289a930b79de5ac9
-- package: x11proto-core-dev
- version: 7.0.26-1
- filename: pool/main/x/x11proto-core/x11proto-core-dev_7.0.26-1_all.deb
- hash: 9d60c962746bf2d1b1eb69e872a57eb21e13b341329ca592fd8eca527bab1df6
-- package: x11proto-fixes-dev
- version: 1:5.0-2
- filename: pool/main/x/x11proto-fixes/x11proto-fixes-dev_5.0-2_all.deb
- hash: e08e0aa9b58ffccc7685c6715df77a9c00781575a6229b4497b06b32bd61336c
-- package: x11proto-gl-dev
- version: 1.4.17-1
- filename: pool/main/x/x11proto-gl/x11proto-gl-dev_1.4.17-1_all.deb
- hash: 6ce92444e2fda684d5071667adb776152291f940696e547d73022d5ed66d3f10
-- package: x11proto-input-dev
- version: 2.3.1-1
- filename: pool/main/x/x11proto-input/x11proto-input-dev_2.3.1-1_all.deb
- hash: a61a10b34cb0ffac6a2603971be1afd4908ed2830b6a1c554e74ac3321bb1e35
-- package: x11proto-kb-dev
- version: 1.0.6-2
- filename: pool/main/x/x11proto-kb/x11proto-kb-dev_1.0.6-2_all.deb
- hash: b7ff8eceb9f05eb470700807c73fdae88f5f9f8b8fbef869029882264dbebd81
-- package: x11proto-randr-dev
- version: 1.4.0-2
- filename: pool/main/x/x11proto-randr/x11proto-randr-dev_1.4.0-2_all.deb
- hash: 3d49d2e205da5d2ca4cdca8d5782e47da5c11ad762c046ffb6173beb5f0d837e
-- package: x11proto-render-dev
- version: 2:0.11.1-2
- filename: pool/main/x/x11proto-render/x11proto-render-dev_0.11.1-2_all.deb
- hash: 606d63fab57421afe305e4866edb1948e4f9090409d6f63199838ac269d8cfba
-- package: x11proto-xext-dev
- version: 7.3.0-1
- filename: pool/main/x/x11proto-xext/x11proto-xext-dev_7.3.0-1_all.deb
- hash: bb505470525281ad330093a9d9d23f4a36b00d0b8502dcbc05e44781375abdf0
diff --git a/debroot/cmd/packages.yml b/debroot/cmd/packages.yml
deleted file mode 100644
index b84421c..0000000
--- a/debroot/cmd/packages.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-dists:
- - jessie
- - jessie-updates
-architectures:
- - amd64
- - arm64
- - armhf
- - i386
-components:
- - main
-keyring: debian-archive-keyring.gpg
-packages:
- - package: libc6
- - package: libc6-dev
- - package: libgl1-mesa-dev
- - package: libgl1-mesa-glx
- - package: libglapi-mesa
- - package: libx11-6
- - package: libx11-dev
- - package: libxcursor1
- - package: libxcursor-dev
- - package: libxext6
- - package: libxext-dev
- - package: libxfixes3
- - package: libxfixes-dev
- - package: libxi6
- - package: libxi-dev
- - package: libxrandr2
- - package: libxrandr-dev
- - package: libxrender1
- - package: libxrender-dev
- - package: libxinerama1
- - package: libxinerama-dev
- - package: linux-libc-dev
- - package: mesa-common-dev
- - package: x11proto-core-dev
- - package: x11proto-gl-dev
- - package: x11proto-input-dev
- - package: x11proto-kb-dev
- - package: x11proto-randr-dev
- - package: x11proto-render-dev
- - package: x11proto-xext-dev
- - package: x11proto-fixes-dev
diff --git a/debug/bloatalyzer/cmd/main.go b/debug/bloatalyzer/cmd/main.go
deleted file mode 100644
index fb161e8..0000000
--- a/debug/bloatalyzer/cmd/main.go
+++ /dev/null
@@ -1,128 +0,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 (
- "context"
- "encoding/json"
- "flag"
- "fmt"
- "io"
- "os"
- "runtime"
- "strings"
-
- "go.fuchsia.dev/tools/debug/bloaty"
- "go.fuchsia.dev/tools/lib/logger"
-)
-
-type Format int
-
-const (
- FormatJSON Format = iota
- FormatHTML
-)
-
-func (f *Format) Set(value string) error {
- switch strings.ToLower(value) {
- case "json":
- *f = FormatJSON
- case "html":
- *f = FormatHTML
- default:
- return fmt.Errorf("Unknown format type.")
- }
- return nil
-}
-
-func (f *Format) String() string {
- switch *f {
- case FormatJSON:
- return "json"
- case FormatHTML:
- return "html"
- default:
- return "unknown"
- }
-}
-
-var (
- bloatyPath string
- idsPath string
- output string
- format Format
- title string
- topFiles uint64
- topSyms uint64
- jobs int
-)
-
-func init() {
- flag.StringVar(&bloatyPath, "bloaty", "", "path to bloaty executable")
- flag.StringVar(&idsPath, "input", "", "path to ids.txt")
- flag.StringVar(&output, "output", "", "output path")
- flag.Var(&format, "format", "output format (options: json, html)")
- flag.StringVar(&title, "title", "Bloaty Analysis", "title for html page")
- flag.Uint64Var(&topFiles, "top-files", 0, "max number of files to keep")
- flag.Uint64Var(&topSyms, "top-syms", 0, "max number of symbols to keep per file")
- flag.IntVar(&jobs, "jobs", runtime.NumCPU(), "max number of concurrent bloaty runs")
-}
-
-func main() {
- flag.Parse()
- if flag.NArg() != 0 {
- flag.PrintDefaults()
- }
-
- ctx := context.Background()
-
- if bloatyPath == "" {
- logger.Fatalf(ctx, "%s", "must provide path to bloaty executable.")
- }
-
- if idsPath == "" {
- logger.Fatalf(ctx, "%s", "must provide path to ids.txt file.")
- }
-
- if jobs <= 0 {
- logger.Fatalf(ctx, "%s", "jobs (j) must be greater than 0.")
- }
-
- data, err := bloaty.RunBloaty(bloatyPath, idsPath, topFiles, topSyms, jobs)
- if err != nil {
- logger.Fatalf(ctx, "%v", err)
- }
-
- var out io.Writer
- if output != "" {
- var err error
- f, err := os.Create(output)
- if err != nil {
- logger.Fatalf(ctx, "uanble to open file: %v", err)
- }
- defer f.Close()
- out = f
- } else {
- out = os.Stdout
- }
-
- switch format {
- case FormatHTML:
- logger.Debugf(ctx, "Writing HTML...")
- err = bloaty.Chart(data, title, out)
- if err != nil {
- logger.Fatalf(ctx, "%v", err)
- }
- case FormatJSON:
- logger.Debugf(ctx, "Marshalling JSON...")
- enc := json.NewEncoder(out)
- err := enc.Encode(data)
- if err != nil {
- logger.Fatalf(ctx, "%v", err)
- }
- default:
- logger.Fatalf(ctx, "Unknown format.")
- }
-}
diff --git a/debug/bloaty/bloaty.go b/debug/bloaty/bloaty.go
deleted file mode 100644
index 27d0354..0000000
--- a/debug/bloaty/bloaty.go
+++ /dev/null
@@ -1,220 +0,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 bloaty
-
-import (
- "bufio"
- "bytes"
- "fmt"
- "os"
- "os/exec"
- "sort"
- "strings"
- "sync"
-)
-
-type bloatyOutput struct {
- data row
- file string
- err error
-}
-
-// TODO(jakehehrlich): Add reading ids.txt to elflib, since there are now three
-// different tools that each need to read it for mostly unrelated reasons
-func getFiles(idsPath string) ([]string, error) {
- out := []string{}
- idsFile, err := os.Open(idsPath)
- if err != nil {
- return nil, err
- }
- defer idsFile.Close()
- scanner := bufio.NewScanner(idsFile)
- for line := 1; scanner.Scan(); line++ {
- parts := strings.SplitN(scanner.Text(), " ", 2)
- if len(parts) != 2 {
- return nil, fmt.Errorf("invalid ids.txt: error on line %d", line)
- }
- out = append(out, parts[1])
- }
- return out, nil
-}
-
-func run(bloatyPath, file string, out chan<- bloatyOutput) {
- args := []string{
- "-d", "segments,compileunits,symbols",
- "-s", "file",
- "--tsv",
- "-n", "0",
- file,
- }
- cmd := exec.Command(bloatyPath, args...)
- fmt.Printf("running: %s\n", file)
- stdout, err := cmd.StdoutPipe()
- if err != nil {
- out <- bloatyOutput{err: fmt.Errorf("pipe: %s: %s", file, err)}
- return
- }
-
- var stderr bytes.Buffer
- cmd.Stderr = &stderr
-
- if err := cmd.Start(); err != nil {
- out <- bloatyOutput{err: fmt.Errorf("start (%s): %s: %s\n", err, file, cmd.Stderr)}
- stdout.Close()
- return
- }
-
- err = ReadCSV(stdout, out, file)
- if err != nil {
- out <- bloatyOutput{err: fmt.Errorf("csv: %s: %s", file, err)}
- stdout.Close()
- return
- }
-
- if err := cmd.Wait(); err != nil {
- out <- bloatyOutput{err: fmt.Errorf("wait (%s): %s: %s\n", err, file, cmd.Stderr)}
- return
- }
-}
-
-func updateSymbol(newSym *row, file string, sym *Symbol) {
- sym.Name = newSym.Symbol
- sym.Vmsz += newSym.Vmsz
- sym.Filesz += newSym.Filesz
- sym.Binaries = append(sym.Binaries, file)
-}
-
-func addRowToOutput(r *row, file string, output map[string]*Segment) {
- if _, ok := output[r.Seg]; !ok {
- output[r.Seg] = &Segment{make(map[string]*File)}
- }
- seg := output[r.Seg]
-
- if _, ok := seg.Files[r.File]; !ok {
- seg.Files[r.File] = &File{Symbols: make(map[string]*Symbol)}
- }
- f := seg.Files[r.File]
-
- if _, ok := f.Symbols[r.Symbol]; !ok {
- f.Symbols[r.Symbol] = &Symbol{}
- }
- updateSymbol(r, file, f.Symbols[r.Symbol])
- seg.Files[r.File] = f
- output[r.Seg] = seg
-}
-
-func getTopN(fileSizes map[string]uint64, topFiles, topSyms uint64, output *map[string]*Segment) {
- // If both topFiles and topSyms are 0, bail early because we're returning everything.
- if topFiles == 0 && topSyms == 0 {
- return
- }
- type sortedFile struct {
- name string
- size uint64
- }
-
- smallFiles := make(map[string]uint64)
- if topFiles > 0 && topFiles < uint64(len(fileSizes)) {
- var all []struct {
- name string
- size uint64
- }
- for name, size := range fileSizes {
- all = append(all, sortedFile{name, size})
- }
- sort.Slice(all, func(i, j int) bool {
- return all[i].size < all[j].size
- })
-
- for _, d := range all[:uint64(len(all))-topFiles] {
- smallFiles[d.name] = d.size
- }
- }
-
- for _, segData := range *output {
- smallFilesSize := uint64(0)
- for file, fileData := range segData.Files {
- smallSyms := Symbol{Name: "all small syms"}
- // If the file labeled a small file, add to small files size and delete the sym data.
- if size, exists := smallFiles[file]; exists {
- smallFilesSize += size
- delete(segData.Files, file)
- } else if topSyms > 0 && topSyms < uint64(len(fileData.Symbols)) {
- var all []*Symbol
- for _, sym := range fileData.Symbols {
- all = append(all, sym)
- }
- sort.Slice(all, func(i, j int) bool {
- return all[i].Filesz < all[j].Filesz
- })
-
- for _, d := range all[:uint64(len(all))-topSyms] {
- if sym, exists := fileData.Symbols[d.Name]; exists {
- smallSyms.Vmsz += sym.Vmsz
- smallSyms.Filesz += sym.Filesz
- delete(fileData.Symbols, d.Name)
- }
- }
- }
-
- if topSyms > 0 {
- fileData.Symbols["all small syms"] = &smallSyms
- }
- }
-
- if topFiles > 0 {
- segData.Files["all small files"] = &File{TotalFilesz: smallFilesSize}
- }
- }
-}
-
-// RunBloaty runs bloaty on all files in ids.txt, and returns a mapping of the
-// symbols and files by segment.
-func RunBloaty(bloatyPath, idsPath string, topFiles, topSyms uint64, jobs int) (map[string]*Segment, error) {
- files, err := getFiles(idsPath)
- if err != nil {
- return nil, err
- }
-
- var wg sync.WaitGroup
- data := make(chan bloatyOutput)
- output := make(map[string]*Segment)
- fileSizes := make(map[string]uint64)
-
- // Start up the data collection process.
- dataComplete := make(chan struct{}, 1)
- go func() {
- for d := range data {
- if d.err != nil {
- fmt.Printf("%v", d.err)
- continue
- }
- addRowToOutput(&d.data, d.file, output)
- fileSizes[d.data.File] += d.data.Filesz
- }
- dataComplete <- struct{}{}
- }()
-
- // Only allow up to max jobs concurrent executions.
- sem := make(chan struct{}, jobs)
-
- // Start a bloaty run on each file.
- for _, file := range files {
- wg.Add(1)
- sem <- struct{}{}
- go func(file string) {
- defer wg.Done()
- run(bloatyPath, file, data)
- <-sem
- }(file)
- }
-
- wg.Wait()
- close(data)
- <-dataComplete
-
- getTopN(fileSizes, topFiles, topSyms, &output)
- return output, nil
-}
diff --git a/debug/bloaty/bloaty_test.go b/debug/bloaty/bloaty_test.go
deleted file mode 100644
index 2acf8cd..0000000
--- a/debug/bloaty/bloaty_test.go
+++ /dev/null
@@ -1,285 +0,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 bloaty
-
-import (
- "fmt"
- "io/ioutil"
- "testing"
-)
-
-const (
- ids = `0071116f87e52dab0c21bd1cabc590fab5b06348 /out/libc.so
-01885627175ee9996b08af6c64c95a6e30787269 /out/libpc-ps2.so
-`
-)
-
-func WriteTempIdsTxt() (string, error) {
- idsPath, err := ioutil.TempFile("", "ids")
- if err != nil {
- return "", err
- }
- fmt.Fprintf(idsPath, ids)
- idsPath.Close()
- return idsPath.Name(), nil
-}
-
-func TestGetFiles(t *testing.T) {
- idsPath, errIds := WriteTempIdsTxt()
- if errIds != nil {
- t.Fatal(errIds)
- }
-
- actual, errConfig := getFiles(idsPath)
- if errConfig != nil {
- t.Fatal(errConfig)
- }
-
- expected := []string{"/out/libc.so", "/out/libpc-ps2.so"}
-
- if len(actual) != len(expected) {
- t.Fatalf("In TestGenConfig, expected \n%s but got \n%s", expected, actual)
- }
-
- for i, val := range expected {
- if actual[i] != val {
- t.Fatalf("In TestGenConfig, expected \n%s but got \n%s", expected, actual)
- }
- }
-}
-
-func TestGetTopNFiles(t *testing.T) {
- input := map[string]*Segment{
- "LOAD [R]": {
- Files: map[string]*File{
- "file.c": {
- TotalFilesz: 14,
- Symbols: map[string]*Symbol{
- "ecm_bind": {
- Name: "ecm_bind",
- Vmsz: 14,
- Filesz: 14,
- Binaries: []string{"lib.so"},
- },
- },
- },
- "different.c": {
- TotalFilesz: 5,
- Symbols: map[string]*Symbol{
- "ecm_bind": {
- Name: "ecm_bind",
- Vmsz: 23,
- Filesz: 5,
- Binaries: []string{"lib.so"},
- },
- },
- },
- },
- },
- "LOAD [RX]": {
- Files: map[string]*File{
- "other.c": {
- TotalFilesz: 1,
- Symbols: map[string]*Symbol{
- "ecm_bind": {
- Name: "ecm_bind",
- Vmsz: 1,
- Filesz: 1,
- Binaries: []string{"lib.so"},
- },
- },
- },
- "test.c": {
- TotalFilesz: 4,
- Symbols: map[string]*Symbol{
- "test": {
- Name: "test",
- Vmsz: 4,
- Filesz: 4,
- Binaries: []string{"lib.so"},
- },
- },
- },
- },
- },
- }
-
- fileSizes := map[string]uint64{
- "file.c": 14,
- "different.c": 5,
- "other.c": 1,
- "test.c": 4,
- }
-
- getTopN(fileSizes, 1, 0, &input)
- if len(input) != 2 {
- t.Fatalf("In TestGetTopN, len is wrong: \n%+v", input)
- }
-
- if _, ok := input["LOAD [R]"]; !ok {
- t.Fatalf("In TestGetTopN, missing LOAD [R]: \n%+v", input)
- }
-
- if len(input["LOAD [R]"].Files) != 2 {
- t.Fatalf("In TestGetTopN, len is wrong: \n%+v", input["LOAD [R]"].Files)
- }
-
- if val, ok := input["LOAD [R]"].Files["file.c"]; !ok {
- t.Fatalf("In TestGetTopN, missing file.c: \n%+v", input["LOAD [R]"].Files)
- } else if val.TotalFilesz != 14 {
- t.Fatalf("In TestGetTopN, filesz is wrong: \n%+v", val)
- }
-
- if val, ok := input["LOAD [R]"].Files["all small files"]; !ok {
- t.Fatalf("In TestGetTopN, missing all small files: \n%+v", input["LOAD [R]"].Files)
- } else if val.TotalFilesz != 5 {
- t.Fatalf("In TestGetTopN, filesz is wrong: \n%+v", val)
- }
-
- if _, ok := input["LOAD [RX]"]; !ok {
- t.Fatalf("In TestGetTopN, missing LOAD [RX]: \n%+v", input)
- }
-
- if len(input["LOAD [RX]"].Files) != 1 {
- t.Fatalf("In TestGetTopN, len is wrong: \n%+v", input["LOAD [R]"].Files)
- }
-
- if val, ok := input["LOAD [RX]"].Files["all small files"]; !ok {
- t.Fatalf("In TestGetTopN, missing all small files: \n%+v", input["LOAD [R]"].Files)
- } else if val.TotalFilesz != 5 {
- t.Fatalf("In TestGetTopN, filesz is wrong: \n%+v", val)
- }
-}
-
-func TestGetTopNSymbols(t *testing.T) {
- input := map[string]*Segment{
- "LOAD [R]": {
- Files: map[string]*File{
- "file.c": {
- TotalFilesz: 14,
- Symbols: map[string]*Symbol{
- "ecm_bind": {
- Name: "ecm_bind",
- Vmsz: 14,
- Filesz: 14,
- Binaries: []string{"lib.so"},
- },
- "test": {
- Name: "test",
- Vmsz: 23,
- Filesz: 5,
- Binaries: []string{"lib.so"},
- },
- "other": {
- Name: "other",
- Vmsz: 5,
- Filesz: 5,
- Binaries: []string{"lib.so"},
- },
- },
- },
- },
- },
- }
-
- fileSizes := map[string]uint64{
- "file.c": 14,
- "different.c": 5,
- "other.c": 1,
- "test.c": 4,
- }
-
- getTopN(fileSizes, 0, 1, &input)
- if len(input) != 1 {
- t.Fatalf("In TestGetTopNSymbols, len is wrong: \n%+v", input)
- }
-
- if _, ok := input["LOAD [R]"]; !ok {
- t.Fatalf("In TestGetTopNSymbols, missing LOAD [R]: \n%+v", input)
- }
-
- if len(input["LOAD [R]"].Files) != 1 {
- t.Fatalf("In TestGetTopNSymbols, len is wrong: \n%+v", input["LOAD [R]"].Files)
- }
-
- if val, ok := input["LOAD [R]"].Files["file.c"]; !ok {
- t.Fatalf("In TestGetTopNSymbols, missing file.c: \n%+v", input["LOAD [R]"].Files)
- } else if val.TotalFilesz != 14 {
- t.Fatalf("In TestGetTopNSymbols, filesz is wrong: \n%+v", val)
- }
-
- if len(input["LOAD [R]"].Files["file.c"].Symbols) != 2 {
- t.Fatalf("In TestGetTopNSymbols, len is wrong: \n%+v", input["LOAD [R]"].Files["file.c"].Symbols)
- }
-
- if val, ok := input["LOAD [R]"].Files["file.c"].Symbols["ecm_bind"]; !ok {
- t.Fatalf("In TestGetTopNSymbols, missing ecm_bind: \n%+v", input["LOAD [R]"].Files)
- } else if val.Filesz != 14 {
- t.Fatalf("In TestGetTopNSymbols, filesz is wrong: \n%+v", val)
- }
-
- if val, ok := input["LOAD [R]"].Files["file.c"].Symbols["all small syms"]; !ok {
- t.Fatalf("In TestGetTopNSymbols, missing ecm_bind: \n%+v", input["LOAD [R]"].Files["file.c"].Symbols["all small syms"])
- } else if val.Filesz != 10 {
- t.Fatalf("In TestGetTopNSymbols, filesz is wrong: \n%+v", val)
- }
-}
-
-func TestAddRowToOutput(t *testing.T) {
- rows := []row{
- {"ecm_bind", "other.c", "LOAD [RX]", 7, 7},
- {"test", "other.c", "LOAD [RX]", 12, 2},
- {"ecm_bind", "other.c", "LOAD [R]", 23, 5},
- {"ecm_bind", "file.c", "LOAD [R]", 3, 3},
- }
-
- actual := make(map[string]*Segment)
- for _, row := range rows {
- addRowToOutput(&row, row.File, actual)
- }
-
- // {"ecm_bind", "other.c", "LOAD [RX]", 7, 7},
- if _, ok := actual["LOAD [RX]"]; !ok {
- t.Fatalf("In TestAddRowToOutput, got \n%+v", actual)
- }
- if _, ok := actual["LOAD [RX]"].Files["other.c"]; !ok {
- t.Fatalf("In TestAddRowToOutput, missing LOAD[RX] other.c")
- }
- if val, ok := actual["LOAD [RX]"].Files["other.c"].Symbols["ecm_bind"]; !ok {
- t.Fatalf("In TestAddRowToOutput, missing LOAD[RX] other.c ecm_bind")
- } else if val.Name != "ecm_bind" || val.Vmsz != 7 || val.Filesz != 7 {
- t.Fatalf("In TestAddRowToOutput, got \n%+v", val)
- }
-
- // {"test", "other.c", "LOAD [RX]", 12, 2},
- if val, ok := actual["LOAD [RX]"].Files["other.c"].Symbols["test"]; !ok {
- t.Fatalf("In TestAddRowToOutput, missing LOAD[RX] other.c test")
- } else if val.Name != "test" || val.Vmsz != 12 || val.Filesz != 2 {
- t.Fatalf("In TestAddRowToOutput, got \n%+v", val)
- }
-
- // {"ecm_bind", "other.c", "LOAD [R]", 23, 5},
- if _, ok := actual["LOAD [R]"]; !ok {
- t.Fatalf("In TestAddRowToOutput, missing LOAD[R]")
- }
- if _, ok := actual["LOAD [R]"].Files["other.c"]; !ok {
- t.Fatalf("In TestAddRowToOutput, missing LOAD[R]:other.c")
- }
- if val, ok := actual["LOAD [R]"].Files["other.c"].Symbols["ecm_bind"]; !ok {
- t.Fatalf("In TestAddRowToOutput, missing LOAD[R] other.c ecm_bind")
- } else if val.Name != "ecm_bind" || val.Vmsz != 23 || val.Filesz != 5 {
- t.Fatalf("In TestAddRowToOutput, got \n%+v", val)
- }
-
- // {"ecm_bind", "file.c", "LOAD [R]", 3, 3},
- if _, ok := actual["LOAD [R]"].Files["file.c"]; !ok {
- t.Fatalf("In TestAddRowToOutput, missing LOAD[R] file.c")
- }
- if val, ok := actual["LOAD [R]"].Files["file.c"].Symbols["ecm_bind"]; !ok {
- t.Fatalf("In TestAddRowToOutput, missing LOAD[R] file.c ecm_bind")
- } else if val.Name != "ecm_bind" || val.Vmsz != 3 || val.Filesz != 3 {
- t.Fatalf("In TestAddRowToOutput, got \n%+v", val)
- }
-}
diff --git a/debug/bloaty/chart.go b/debug/bloaty/chart.go
deleted file mode 100644
index 242b436..0000000
--- a/debug/bloaty/chart.go
+++ /dev/null
@@ -1,99 +0,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 bloaty
-
-import (
- "fmt"
- "html/template"
- "io"
-)
-
-const gchart = `
-<html>
- <head>
- <title>{{.Title}}</title>
- <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
- <script type="text/javascript">
- google.charts.load('current', {'packages':['treemap']});
- google.charts.setOnLoadCallback(drawChart);
- function drawChart() {
- var data = google.visualization.arrayToDataTable({{.Data}});
-
- tree = new google.visualization.TreeMap(document.getElementById('chart_div'));
-
- var options = {
- highlightOnMouseOver: true,
- maxDepth: 0,
- maxPostDepth: 0,
- minHighlightColor: '#8c6bb1',
- midHighlightColor: '#9ebcda',
- maxHighlightColor: '#edf8fb',
- minColor: '#009688',
- midColor: '#f7f7f7',
- maxColor: '#ee8100',
- headerHeight: 15,
- showScale: true,
- useWeightedAverageForAggregation: true
- };
-
- tree.draw(data, options);
- }
- </script>
- </head>
- <body>
- <div id="chart_div" style="width: 100%; height: 100%;"></div>
- </body>
-</html>`
-
-type htmlData struct {
- Data *[][]interface{}
- Title string
-}
-
-// Data format:
-// Column 0 - [string] An ID for this node. It can be any valid JavaScript string,
-// including spaces, and any length that a string can hold. This value is
-// displayed as the node header.
-// Column 1 - [string] - The ID of the parent node. If this is a root node,
-// leave this blank. Only one root is allowed per treemap.
-// Column 2 - [number] - The size of the node. Any positive value is allowed.
-// This value determines the size of the node, computed relative to all other
-// nodes currently shown. For non-leaf nodes, this value is ignored and
-// calculated from the size of all its children.
-func toTable(bloatyData map[string]*Segment) *[][]interface{} {
- var data [][]interface{}
- data = append(data, []interface{}{"ID", "Parent", "File Size", "Color"})
- data = append(data, []interface{}{"Build", nil, uint64(0), 0})
-
- for seg, segData := range bloatyData {
- data = append(data, []interface{}{seg, "Build", uint64(0), 0})
- for file, fileData := range segData.Files {
- data = append(data, []interface{}{fmt.Sprintf("%s (%s)", file, seg), seg, fileData.TotalFilesz, 0})
- for sym, symData := range fileData.Symbols {
- data = append(data, []interface{}{
- fmt.Sprintf("%s:%s (%s)", file, sym, seg),
- fmt.Sprintf("%s (%s)", file, seg),
- symData.Filesz,
- len(symData.Binaries),
- })
- }
- }
- }
- return &data
-}
-
-// Chart converts the map of symbols to the format expected by GChart, and then
-// generates an HTML page from that data.
-func Chart(bloatyData map[string]*Segment, title string, out io.Writer) error {
- temp, err := template.New("T").Parse(gchart)
- if err != nil {
- return err
- }
- err = temp.Execute(out, htmlData{toTable(bloatyData), title})
- if err != nil {
- return err
- }
- return nil
-}
diff --git a/debug/bloaty/chart_test.go b/debug/bloaty/chart_test.go
deleted file mode 100644
index f38a84c..0000000
--- a/debug/bloaty/chart_test.go
+++ /dev/null
@@ -1,108 +0,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 bloaty
-
-import (
- "testing"
-)
-
-func checkRow(t *testing.T, row []interface{}, expected []interface{}) {
- if len(expected) != 4 {
- t.Fatalf("In TestToChart, len is wrong. expected: \n%+v\n but got: \n%+v", expected, row)
- }
- if row[0] != expected[0] {
- t.Fatalf("In TestToChart, len is wrong. expected: \n%+v\n but got: \n%+v", expected, row)
- }
- if len(row) != 4 {
- t.Fatalf("In TestToChart, id is wrong. expected: \n%+v\n but got: \n%+v", expected, row)
- }
- if row[1] != expected[1] {
- t.Fatalf("In TestToChart, parent is wrong. expected: \n%+v\n but got: \n%+v", expected, row)
- }
- if row[2] != expected[2] {
- t.Fatalf("In TestToChart, size is wrong. expected: \n%+v\n but got: \n%+v", expected, row)
- }
- if row[3] != expected[3] {
- t.Fatalf("In TestToChart, color is wrong. expected: \n%+v\n but got: \n%+v", expected, row)
- }
-}
-
-func TestToChart(t *testing.T) {
- input := map[string]*Segment{
- "LOAD [R]": {
- Files: map[string]*File{
- "file.c": {
- TotalFilesz: 3,
- Symbols: map[string]*Symbol{
- "ecm_bind": {
- Name: "ecm_bind",
- Vmsz: 3,
- Filesz: 3,
- Binaries: []string{"lib.so"},
- },
- },
- },
- "other.c": {
- TotalFilesz: 5,
- Symbols: map[string]*Symbol{
- "ecm_bind": {
- Name: "ecm_bind",
- Vmsz: 23,
- Filesz: 5,
- Binaries: []string{"lib.so", "other.so"},
- },
- },
- },
- },
- },
- "LOAD [RX]": {
- Files: map[string]*File{
- "other.c": {
- TotalFilesz: 14,
- Symbols: map[string]*Symbol{
- "ecm_bind": {
- Name: "ecm_bind",
- Vmsz: 7,
- Filesz: 7,
- Binaries: []string{"lib.so"},
- },
- "test": {
- Name: "test",
- Vmsz: 12,
- Filesz: 7,
- Binaries: []string{"lib.so", "other.so"},
- },
- },
- },
- },
- },
- }
-
- actual := *toTable(input)
-
- ids_to_expected_rows := map[string][]interface{}{
- "ID": {"ID", "Parent", "File Size", "Color"},
- "Build": {"Build", nil, uint64(0), 0},
- "LOAD [R]": {"LOAD [R]", "Build", uint64(0), 0},
- "LOAD [RX]": {"LOAD [RX]", "Build", uint64(0), 0},
- "file.c (LOAD [R])": {"file.c (LOAD [R])", "LOAD [R]", uint64(3), 0},
- "other.c (LOAD [R])": {"other.c (LOAD [R])", "LOAD [R]", uint64(5), 0},
- "other.c (LOAD [RX])": {"other.c (LOAD [RX])", "LOAD [RX]", uint64(14), 0},
- "file.c:ecm_bind (LOAD [R])": {"file.c:ecm_bind (LOAD [R])", "file.c (LOAD [R])", uint64(3), 1},
- "other.c:ecm_bind (LOAD [R])": {"other.c:ecm_bind (LOAD [R])", "other.c (LOAD [R])", uint64(5), 2},
- "other.c:ecm_bind (LOAD [RX])": {"other.c:ecm_bind (LOAD [RX])", "other.c (LOAD [RX])", uint64(7), 1},
- "other.c:test (LOAD [RX])": {"other.c:test (LOAD [RX])", "other.c (LOAD [RX])", uint64(7), 2},
- }
-
- if len(actual) != 11 {
- t.Fatalf("In TestToChart, len is wrong: \n%+v", actual)
- }
- for _, row := range actual {
- if len(row) != 4 {
- t.Fatalf("In TestToChart, len is wrong: \n%+v", row)
- }
- checkRow(t, row, ids_to_expected_rows[row[0].(string)])
- }
-}
diff --git a/debug/bloaty/symbols.go b/debug/bloaty/symbols.go
deleted file mode 100644
index 8d61094..0000000
--- a/debug/bloaty/symbols.go
+++ /dev/null
@@ -1,115 +0,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 bloaty
-
-import (
- "encoding/csv"
- "fmt"
- "io"
- "strconv"
-)
-
-type row struct {
- Symbol string
- File string
- Seg string
- Vmsz uint64
- Filesz uint64
-}
-
-// Symbol represents data about one symbol in the produced Bloaty output.
-type Symbol struct {
- Name string `json:"Name"`
- Vmsz uint64 `json:"Vmsz"`
- Filesz uint64 `json:"Filesz"`
- Binaries []string `json:"Binaries"`
-}
-
-// File represents all data about one file in the produced Bloaty output
-type File struct {
- Symbols map[string]*Symbol `json:"Symbols"`
- TotalFilesz uint64 `json:"TotalFilesz"`
-}
-
-// Segment represents all data about one segment in the produced Bloaty output
-type Segment struct {
- Files map[string]*File
-}
-
-func parseRow(rawRow []string) (row, bool, error) {
- var out row
- if len(rawRow) != 5 {
- return out, false, fmt.Errorf("row did not match format")
- }
-
- if rawRow[0] == "segments" {
- return out, true, nil
- }
-
- out.Seg = rawRow[0]
- out.File = rawRow[1]
- out.Symbol = rawRow[2]
-
- var err error
- out.Vmsz, err = strconv.ParseUint(rawRow[3], 0, 64)
- if err != nil {
- return out, false, err
- }
-
- out.Filesz, err = strconv.ParseUint(rawRow[4], 0, 64)
- if err != nil {
- return out, false, err
- }
-
- return out, false, nil
-}
-
-func filterRow(r row) bool {
- // These rows mean nothing to us
- if r.Vmsz == 0 || r.Filesz == 0 {
- return false
- }
- // These are things that get stripped away
- if r.Seg == "[Unmapped]" {
- return false
- }
- return true
-}
-
-func ReadCSV(data io.Reader, out chan<- bloatyOutput, file string) error {
- reader := csv.NewReader(data)
- reader.Comma = '\t'
-
- var line int
- for {
- r, err := reader.Read()
- if err == io.EOF {
- return nil
- }
- if err != nil {
- return fmt.Errorf("%v\n", err)
- }
-
- // If it's empty, ignore it
- if len(r) == 0 {
- line += 1
- continue
- }
-
- properRow, isHeader, err := parseRow(r)
- if !isHeader {
- if err != nil {
- return fmt.Errorf("unable to decode line %d:%v %v\n", line, r, err)
- }
- if filterRow(properRow) {
- out <- bloatyOutput{
- data: properRow,
- file: file,
- }
- }
- }
- line += 1
- }
-}
diff --git a/debug/bloaty/symbols_test.go b/debug/bloaty/symbols_test.go
deleted file mode 100644
index edb83af..0000000
--- a/debug/bloaty/symbols_test.go
+++ /dev/null
@@ -1,69 +0,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 bloaty
-
-import (
- "bytes"
- "testing"
-)
-
-const (
- tsv = `segments compileunits symbols vmsize filesize
-[Unmapped] file.c [section .debug_info] 0 15550
-LOAD [RX] other.c ecm_bind 4142 4142
-[ELF Headers] 0 1920
-LOAD [RX] [LOAD [RX]] 530 0`
-)
-
-func TestReadCSV(t *testing.T) {
- reader := bytes.NewReader([]byte(tsv))
-
- out := make(chan bloatyOutput)
- go func() {
- err := ReadCSV(reader, out, "file.c")
- if err != nil {
- t.Fatal(err)
- }
- close(out)
- }()
-
- actual := []row{}
- for o := range out {
- actual = append(actual, o.data)
- }
-
- expected := []row{
- {"ecm_bind", "other.c", "LOAD [RX]", 4142, 4142},
- }
-
- if len(actual) != len(expected) {
- t.Fatalf("In TestReadCSV, expected \n%v but got \n%v", expected, actual)
- }
-
- for i, val := range expected {
- if actual[i] != val {
- t.Fatalf("In TestReadCSV, expected \n%v but got \n%v", expected, actual)
- }
- }
-}
-
-func TestFilterRow(t *testing.T) {
- goodRow := row{"ecm_bind", "other.c", "LOAD [RX]", 4142, 4142}
- badRow := row{"[section .debug_info]", "file.c", "[Unmapped]", 0, 15550}
-
- actual := filterRow(goodRow)
- expected := true
-
- if actual != expected {
- t.Fatalf("In TestFilterRows, expected \n%v but got \n%v", expected, actual)
- }
-
- actual = filterRow(badRow)
- expected = false
-
- if actual != expected {
- t.Fatalf("In TestFilterRows, expected \n%v but got \n%v", expected, actual)
- }
-}
diff --git a/debug/breakpad/generator/generator.go b/debug/breakpad/generator/generator.go
deleted file mode 100644
index 84f9b44..0000000
--- a/debug/breakpad/generator/generator.go
+++ /dev/null
@@ -1,164 +0,0 @@
-// 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 generator
-
-import (
- "bytes"
- "fmt"
- "io/ioutil"
- "log"
- "os"
- "os/exec"
- "path/filepath"
- "sync"
-
- "go.fuchsia.dev/tools/debug/breakpad"
- "go.fuchsia.dev/tools/debug/elflib"
-)
-
-// The default module name for modules that don't have a soname, e.g., executables and
-// loadable modules. This allows us to use the same module name at runtime as sonames are
-// the only names that are guaranteed to be available at build and run times. This value
-// must be kept in sync with what Crashpad uses at run time for symbol resolution to work
-// properly.
-const defaultModuleName = "<_>"
-
-// The module OS used to overwrite existing OS values in generated symbol files, even if
-// they're already set to something else.
-const replacementModuleOS = "Fuchsia"
-
-// Generate generates breakpad symbol data for each of the input elflib.BinaryFileRefs.
-// Returns the path to a directory containing the generated files, or the empty string if
-// an error occurred.
-func Generate(bfrs []elflib.BinaryFileRef, dumpSymsPath string) (path string, err error) {
- outc := make(chan string)
- errc := make(chan error)
- defer close(outc)
- defer close(errc)
-
- g := &generator{
- dumpSymsPath: dumpSymsPath,
- visited: make(map[string]bool),
- visitedMutex: &sync.Mutex{},
- }
-
- jobs := make(chan elflib.BinaryFileRef)
- go g.run(jobs, outc, errc)
- for _, bfr := range bfrs {
- jobs <- bfr
- }
- close(jobs)
-
- select {
- case err = <-errc:
- return "", err
- case path = <-outc:
- return path, nil
- }
-}
-
-// Generator is a helper class for executing Breakpad's dump_syms tool.
-//
-// The run method is meant to be executed as a go-routine. It will manage its own working
-// directory, and publish the path to that directory only on success.
-//
-// The run method is threadsafe, and will skip files that have already been processed.
-type generator struct {
- // The path to the Breakpad dump_syms executable.
- dumpSymsPath string
-
- // Filepaths that have already been processed by this generator.
- visited map[string]bool
- visitedMutex *sync.Mutex
-}
-
-// Run executes this generator on the given channel of elflib.BinarFileRefs.
-//
-// A temp directory is created to store generated files. On success, the directory is
-// emitted on out. On the first encountered error, the generator will emit the error on
-// errs, delete the output directory, and exit.
-func (g *generator) run(in <-chan elflib.BinaryFileRef, out chan<- string, errs chan<- error) {
- outdir, err := ioutil.TempDir("", "breakpad")
- if err != nil {
- errs <- err
- return
- }
- if err := g.generate(in, outdir); err != nil {
- errs <- err
- os.RemoveAll(outdir)
- return
- }
- out <- outdir
-}
-
-func (g *generator) generate(in <-chan elflib.BinaryFileRef, outdir string) error {
- for bfr := range in {
- if !g.markVisited(bfr.Filepath) {
- continue
- }
- sf, err := g.genFromBinaryFileRef(bfr)
- if err != nil {
- return err
- }
- fd, err := os.Create(filepath.Join(outdir, sf.ModuleSection.BuildID))
- if err != nil {
- return err
- }
- defer fd.Close()
- if _, err := sf.WriteTo(fd); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (g *generator) genFromBinaryFileRef(bfr elflib.BinaryFileRef) (*breakpad.SymbolFile, error) {
- log.Printf("generating symbols for %q", bfr.Filepath)
- sf, err := g.genSymbolFile(bfr)
- if err != nil {
- return nil, err
- }
- sf.ModuleSection.OS = replacementModuleOS
- sf.ModuleSection.ModuleName = defaultModuleName
- soname, err := g.readSoName(bfr.Filepath)
- if err == nil && soname != "" {
- sf.ModuleSection.ModuleName = soname
- }
- return sf, nil
-}
-
-func (g *generator) genSymbolFile(bfr elflib.BinaryFileRef) (*breakpad.SymbolFile, error) {
- var stdout bytes.Buffer
- cmd := exec.Cmd{
- Path: g.dumpSymsPath,
- Args: []string{g.dumpSymsPath, bfr.Filepath},
- Stdout: &stdout,
- }
- if err := cmd.Run(); err != nil {
- return nil, fmt.Errorf("command failed %v: %v", cmd.Args, err)
- }
- return breakpad.ParseSymbolFile(&stdout)
-}
-
-func (g *generator) readSoName(path string) (string, error) {
- fd, err := os.Open(path)
- if err != nil {
- return "", fmt.Errorf("open failed %q: %v", path, err)
- }
- defer fd.Close()
- return elflib.GetSoName(path, fd)
-}
-
-// Marks that path has been visited and returs true iff this generator has not alread
-// visited path. Otherwise returns false.
-func (g *generator) markVisited(path string) (succeeded bool) {
- g.visitedMutex.Lock()
- defer g.visitedMutex.Unlock()
- if g.visited[path] {
- return false
- }
- g.visited[path] = true
- return true
-}
diff --git a/debug/breakpad/symbol_file.go b/debug/breakpad/symbol_file.go
deleted file mode 100644
index 495ab66..0000000
--- a/debug/breakpad/symbol_file.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// 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 breakpad
-
-import (
- "bytes"
- "fmt"
- "io"
- "io/ioutil"
- "strings"
-)
-
-// ParseSymbolFile reads a breakpad symbol file.
-func ParseSymbolFile(r io.Reader) (*SymbolFile, error) {
- bytes, err := ioutil.ReadAll(r)
- if err != nil {
- return nil, fmt.Errorf("failed to read symbol file: %v", err)
- }
- lines := strings.SplitN(string(bytes), "\n", 2)
- if len(lines) != 2 {
- return nil, fmt.Errorf("got < 2 lines in symbol file")
- }
- moduleSection := strings.TrimSpace(lines[0])
- if moduleSection == "" {
- return nil, fmt.Errorf("unexpected blank first line in symbol file")
- }
- module, err := parseModuleLine(moduleSection)
- if err != nil {
- return nil, fmt.Errorf("failed to parse module section: %v", err)
- }
- return &SymbolFile{
- ModuleSection: module,
- remainder: lines[1],
- }, nil
-}
-
-// SymbolFile represents a Breakpad symbol file, typically produced by the dump_syms tool.
-// This is only a partial implementation; the ModuleSection is all that is parsed. Add fields
-// as needed.
-type SymbolFile struct {
- ModuleSection *ModuleSection
-
- // Unparsed contents from the input file excluding the ModuleSection.
- remainder string
-}
-
-func (f SymbolFile) WriteTo(w io.Writer) (int64, error) {
- return io.Copy(w, f.Reader())
-}
-
-// Reader returns an io.Reader for this SymbolFile data as it would be written to a file.
-func (f SymbolFile) Reader() io.Reader {
- var buf bytes.Buffer
- buf.WriteString(f.ModuleSection.String())
- buf.WriteString("\n")
- buf.WriteString(f.remainder)
- return &buf
-}
-
-// ModuleSection represents the first section/line of a symbol file.
-type ModuleSection struct {
- OS string
- Arch string
- BuildID string
- ModuleName string
-}
-
-// String formats this ModuleSection as it would be written to a file.
-func (mod *ModuleSection) String() string {
- content := fmt.Sprintf("MODULE %s %s %s %s", mod.OS, mod.Arch, mod.BuildID, mod.ModuleName)
- return strings.TrimSpace(content)
-}
-
-func parseModuleLine(source string) (*ModuleSection, error) {
- format := "MODULE $OS $ARCH $BUILD_ID $MODULE_NAME"
- parts := strings.Split(source, " ")
- if len(parts) != 5 {
- return nil, fmt.Errorf("wanted line 1 with format %q but got %q", format, source)
- }
- return &ModuleSection{
- OS: parts[1],
- Arch: parts[2],
- BuildID: parts[3],
- ModuleName: parts[4],
- }, nil
-}
diff --git a/debug/breakpad/symbol_file_test.go b/debug/breakpad/symbol_file_test.go
deleted file mode 100644
index 5ea195f..0000000
--- a/debug/breakpad/symbol_file_test.go
+++ /dev/null
@@ -1,192 +0,0 @@
-// 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 breakpad
-
-import (
- "bytes"
- "reflect"
- "strings"
- "testing"
-)
-
-func TestParseSymbolFile(t *testing.T) {
- tests := []struct {
- name string
- input string
- output *SymbolFile
- expectErr bool
- }{
- {
- name: "should parse when the module section has all values",
- input: strings.Join([]string{
- "MODULE os arch buildid modulename",
- "garbage1",
- }, "\n"),
- output: &SymbolFile{
- ModuleSection: &ModuleSection{
- OS: "os",
- Arch: "arch",
- BuildID: "buildid",
- ModuleName: "modulename",
- },
- remainder: "garbage1",
- },
- },
- {
- name: "should fail to parse a file with less than two lines",
- input: strings.Join([]string{
- "MODULE os arch buildid modulename",
- }, "\n"),
- expectErr: true,
- },
- {
- name: "should fail to parse a file with a leading blank line",
- input: strings.Join([]string{
- "",
- "MODULE os arch buildid modulename",
- }, "\n"),
- expectErr: true,
- },
- {
- name: "should fail to parse when the module section is missing one value",
- input: strings.Join([]string{
- "MODULE os arch buildid",
- "INFO CODE_ID ACBDEF",
- "FILE 9 /tmp/test/out/source.cpp",
- }, "\n"),
- expectErr: true,
- },
- {
- name: "should fail to parse when the module section is missing two values",
- input: strings.Join([]string{
- "MODULE os arch",
- "INFO CODE_ID ACBDEF",
- "FILE 9 /tmp/test/out/source.cpp",
- }, "\n"),
- expectErr: true,
- },
- {
- name: "should fail to parse when the module section is missing three values",
- input: strings.Join([]string{
- "MODULE os",
- "INFO CODE_ID ACBDEF",
- "FILE 9 /tmp/test/out/source.cpp",
- }, "\n"),
- expectErr: true,
- },
- {
- name: "should fail to parse when the module section is missing all values",
- input: strings.Join([]string{
- "MODULE",
- "INFO CODE_ID ACBDEF",
- "FILE 9 /tmp/test/out/source.cpp",
- }, "\n"),
- expectErr: true,
- },
- {
- name: "should fail to parse when the module section is missing",
- input: strings.Join([]string{
- "INFO CODE_ID ACBDEF",
- "FILE 9 /tmp/test/out/source.cpp",
- }, "\n"),
- expectErr: true,
- },
- {
- name: "should fail to parse when the module section has extra values",
- input: strings.Join([]string{
- "MODULE os arch buildid modulename whatisthis",
- "INFO CODE_ID ACBDEF",
- "FILE 9 /tmp/test/out/source.cpp",
- }, "\n"),
- expectErr: true,
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- output, err := ParseSymbolFile(strings.NewReader(tt.input))
- if err != nil && !tt.expectErr {
- t.Errorf("unexpected parse error: %v", err)
- return
- } else if err == nil && tt.expectErr {
- t.Errorf("exected error but got nil with output: %v", output)
- return
- }
- if tt.expectErr {
- return
- }
- expected := tt.output
- actual := output
- if !reflect.DeepEqual(expected.ModuleSection, actual.ModuleSection) {
- t.Errorf("wanted module section:\n\n%v\n\ngot:\n\n%v\n\n", expected.ModuleSection, actual.ModuleSection)
- }
- if expected.remainder != actual.remainder {
- t.Errorf("wanted remainder file contents:\n\n%v\n\ngot:\n\n%v\n\n", expected.remainder, actual.remainder)
- }
- })
- }
-}
-
-func TestSymbolFileWriteTo(t *testing.T) {
- tests := []struct {
- name string
- input SymbolFile
- output string
- }{
- {
- name: "when module section has all fields",
- input: SymbolFile{
- ModuleSection: &ModuleSection{
- OS: "os",
- Arch: "arch",
- BuildID: "buildid",
- ModuleName: "modulename",
- },
- remainder: strings.Join([]string{
- "remainder line 1",
- "remainder line 2",
- }, "\n"),
- },
- output: strings.Join([]string{
- "MODULE os arch buildid modulename",
- "remainder line 1",
- "remainder line 2",
- }, "\n"),
- },
- {
- // This is not a valid module section, but we still want to verify that it
- // writes the exected data.
- name: "when module section is missing some fields",
- input: SymbolFile{
- ModuleSection: &ModuleSection{
- OS: "os",
- Arch: "arch",
- BuildID: "buildid",
- },
- remainder: strings.Join([]string{
- "remainder line 1",
- }, "\n"),
- },
- output: strings.Join([]string{
- "MODULE os arch buildid",
- "remainder line 1",
- }, "\n"),
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- var output bytes.Buffer
- if _, err := tt.input.WriteTo(&output); err != nil {
- t.Errorf("unexpected error: %v. input: %v", err, tt.input)
- }
- expected := tt.output
- actual := output.String()
- if expected != actual {
- t.Errorf("wanted:\n\n%v\n\ngot:\n\n%v\n\n", expected, actual)
- }
- })
- }
-}
diff --git a/debug/buildidtool/cmd/main.go b/debug/buildidtool/cmd/main.go
deleted file mode 100644
index fbb5584..0000000
--- a/debug/buildidtool/cmd/main.go
+++ /dev/null
@@ -1,215 +0,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 (
- "crypto/rand"
- "encoding/hex"
- "flag"
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
-
- "go.fuchsia.dev/tools/debug/elflib"
- "go.fuchsia.dev/tools/lib/color"
- "go.fuchsia.dev/tools/lib/logger"
-)
-
-type entry struct {
- suffix string
- file string
-}
-
-type entryList []entry
-
-func (a *entryList) String() string {
- return fmt.Sprintf("%v", []entry(*a))
-}
-
-func (a *entryList) Set(value string) error {
- args := strings.SplitN(value, "=", 2)
- if len(args) != 2 {
- return fmt.Errorf("'%s' is not a valid entry. Must be in format <suffix>=<file>", value)
- }
- *a = append(*a, entry{args[0], args[1]})
- return nil
-}
-
-var (
- buildIDDir string
- stamp string
- entries entryList
- colors color.EnableColor
- level logger.LogLevel
-)
-
-func init() {
- colors = color.ColorAuto
- level = logger.FatalLevel
-
- flag.StringVar(&buildIDDir, "build-id-dir", "", "path to .build-id dirctory")
- flag.StringVar(&stamp, "stamp", "", "path to stamp file which acts as a stand in for the .build-id file")
- flag.Var(&entries, "entry", "supply <suffix>=<file> to link <file> into .build-id with the given suffix")
- 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")
-}
-
-func getTmpFile(path string, name string) (string, error) {
- out := make([]byte, 16)
- if _, err := rand.Read(out); err != nil {
- return "", nil
- }
- return filepath.Join(path, name+"-"+hex.EncodeToString(out)) + ".tmp", nil
-}
-
-func atomicLink(from, to string) error {
- // First make sure the directory already exists
- if err := os.MkdirAll(filepath.Dir(to), 0700); err != nil {
- return err
- }
- // Make a tmpFile in the same directory as 'from'
- dir, file := filepath.Split(from)
- tmpFile, err := getTmpFile(dir, file)
- if err != nil {
- return err
- }
- if err := os.Link(from, tmpFile); err != nil {
- return err
- }
- if err := os.Rename(tmpFile, to); err != nil {
- return err
- }
- // If "tmpFile" and "to" are already links to the same inode Rename does not remove tmpFile.
- if err := os.Remove(tmpFile); !os.IsNotExist(err) {
- return err
- }
- return nil
-}
-
-func atomicWrite(file, fmtStr string, args ...interface{}) error {
- dir, base := filepath.Split(file)
- tmpFile, err := getTmpFile(dir, base)
- if err != nil {
- return err
- }
- f, err := os.Create(tmpFile)
- if err != nil {
- return err
- }
- defer f.Close()
- _, err = fmt.Fprintf(f, fmtStr, args...)
- if err != nil {
- return err
- }
- return os.Rename(tmpFile, file)
-}
-
-func removeOldFile(newBuildID, suffix string) error {
- data, err := ioutil.ReadFile(stamp)
- if err != nil {
- if !os.IsNotExist(err) {
- return err
- }
- return nil
- }
- oldBuildID := string(data)
- // We don't want to remove what we just added!
- if newBuildID == oldBuildID {
- return nil
- }
- oldPath := filepath.Join(buildIDDir, oldBuildID[:2], oldBuildID[2:]) + suffix
- // If the file has already been removed (perhaps by another process) then
- // just keep going.
- if err := os.Remove(oldPath); !os.IsNotExist(err) {
- return err
- }
- return nil
-}
-
-type entryInfo struct {
- ref elflib.BinaryFileRef
- suffix string
-}
-
-func getEntriesInfo() ([]entryInfo, error) {
- var outs []entryInfo
- for _, entry := range entries {
- f, err := os.Open(entry.file)
- if err != nil {
- return nil, fmt.Errorf("opening %s to read build ID: %v", entry.file, err)
- }
- defer f.Close()
- buildIDs, err := elflib.GetBuildIDs(entry.file, f)
- if err != nil {
- return nil, fmt.Errorf("reading build ID from %s: %v", entry.file, err)
- }
- // Ignore entries where the binary doesn't have a build ID.
- if len(buildIDs) == 0 {
- continue
- }
- if len(buildIDs) != 1 {
- return nil, fmt.Errorf("unexpected number of build IDs in %s. Expected 1 but found %v", entry.file, buildIDs)
- }
- if len(buildIDs[0]) < 2 {
- return nil, fmt.Errorf("build ID (%s) is too short in %s", buildIDs[0], entry.file)
- }
- buildID := hex.EncodeToString(buildIDs[0])
- outs = append(outs, entryInfo{elflib.BinaryFileRef{BuildID: buildID, Filepath: entry.file}, entry.suffix})
- }
- return outs, nil
-}
-
-func main() {
- l := logger.NewLogger(level, color.NewColor(colors), os.Stderr, os.Stderr, "")
- // Parse flags and check for errors.
- flag.Parse()
- if buildIDDir == "" {
- l.Fatalf("-build-id-dir is required.")
- }
- if stamp == "" {
- l.Fatalf("-stamp file is required.")
- }
- if len(entries) == 0 {
- l.Fatalf("Need at least one -entry arg")
- }
- // Get the build IDs
- infos, err := getEntriesInfo()
- if err != nil {
- l.Fatalf("Parsing entries: %v", err)
- }
- if len(infos) != 0 {
- // It could occur that we have ignored all files and need to return.
- return
- }
- buildID := infos[0].ref.BuildID
- for _, info := range infos {
- if buildID != info.ref.BuildID {
- l.Fatalf("%s and %s do not have the same build ID", info.ref.Filepath, infos[0].ref.Filepath)
- }
- if err := info.ref.Verify(); err != nil {
- l.Fatalf("Could not verify build ID of %s: %v", info.ref.Filepath, err)
- }
- }
- // Now that we know all the build IDs are in order perform operations.
- // Make sure to not output the stamp file until all of these operations are
- // performed to ensure that this tool is re-run if it fails mid-run.
- buildIDRunes := []rune(buildID)
- buildIDPathPrefix := filepath.Join(buildIDDir, string(buildIDRunes[:2]), string(buildIDRunes[2:]))
- for _, info := range infos {
- buildIDPath := buildIDPathPrefix + info.suffix
- if err = atomicLink(info.ref.Filepath, buildIDPath); err != nil {
- l.Fatalf("atomically linking %s to %s: %v", info.ref.Filepath, buildIDPath, err)
- }
- if err = removeOldFile(buildID, info.suffix); err != nil {
- l.Fatalf("removing old file referenced by %s: %v", stamp, err)
- }
- }
- // Update the stamp last atomically to commit all the above operations.
- if err = atomicWrite(stamp, buildID); err != nil {
- l.Fatalf("emitting final stamp %s: %v", stamp, err)
- }
-}
diff --git a/debug/covargs/cmd/main.go b/debug/covargs/cmd/main.go
deleted file mode 100644
index 30ff15e..0000000
--- a/debug/covargs/cmd/main.go
+++ /dev/null
@@ -1,363 +0,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 (
- "bytes"
- "context"
- "debug/elf"
- "encoding/json"
- "flag"
- "fmt"
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
-
- "go.fuchsia.dev/tools/debug/elflib"
- "go.fuchsia.dev/tools/debug/symbolize"
- "go.fuchsia.dev/tools/lib/color"
- "go.fuchsia.dev/tools/lib/command"
- "go.fuchsia.dev/tools/lib/logger"
- "go.fuchsia.dev/tools/testing/runtests"
-)
-
-var (
- colors color.EnableColor
- level logger.LogLevel
- summaryFile command.StringsFlag
- idsFile string
- symbolizeDumpFile command.StringsFlag
- dryRun bool
- outputDir string
- llvmCov string
- llvmProfdata string
- outputFormat string
- jsonOutput string
-)
-
-func init() {
- colors = color.ColorAuto
- level = logger.InfoLevel
-
- flag.Var(&colors, "color", "can be never, auto, always")
- flag.Var(&level, "level", "can be fatal, error, warning, info, debug or trace")
- flag.Var(&summaryFile, "summary", "path to summary.json file")
- flag.StringVar(&idsFile, "ids", "", "path to ids.txt")
- flag.Var(&symbolizeDumpFile, "symbolize-dump", "path to the json emited from the symbolizer")
- flag.BoolVar(&dryRun, "dry-run", false, "if set the system prints out commands that would be run instead of running them")
- flag.StringVar(&outputDir, "output-dir", "", "the directory to output results to")
- flag.StringVar(&llvmProfdata, "llvm-profdata", "llvm-profdata", "the location of llvm-profdata")
- flag.StringVar(&llvmCov, "llvm-cov", "llvm-cov", "the location of llvm-cov")
- flag.StringVar(&outputFormat, "format", "html", "the output format used for llvm-cov")
- flag.StringVar(&jsonOutput, "json-output", "", "outputs profile information to the specified file")
-}
-
-const llvmProfileSinkType = "llvm-profile"
-
-// Output is indexed by dump name
-func readSummary(summaryFiles []string) (map[string][]runtests.DataSink, error) {
- sinks := make(map[string][]runtests.DataSink)
-
- for _, summaryFile := range summaryFiles {
- // TODO(phosek): process these in parallel using goroutines.
- file, err := os.Open(summaryFile)
- if err != nil {
- return nil, fmt.Errorf("cannot open %q: %v", summaryFile, err)
- }
- defer file.Close()
-
- var summary runtests.TestSummary
- if err := json.NewDecoder(file).Decode(&summary); err != nil {
- return nil, fmt.Errorf("cannot decode %q: %v", summaryFile, err)
- }
-
- dir := filepath.Dir(summaryFile)
- for _, detail := range summary.Tests {
- for name, data := range detail.DataSinks {
- for _, sink := range data {
- sinks[name] = append(sinks[name], runtests.DataSink{
- Name: sink.Name,
- File: filepath.Join(dir, sink.File),
- })
- }
- }
- }
- }
-
- return sinks, nil
-}
-
-type SymbolizerDump struct {
- Modules []symbolize.Module `json:"modules"`
- SinkType string `json:"type"`
- DumpName string `json:"name"`
-}
-
-type SymbolizerOutput []SymbolizerDump
-
-func readSymbolizerOutput(outputFiles []string) (map[string]SymbolizerDump, error) {
- dumps := make(map[string]SymbolizerDump)
-
- for _, outputFile := range outputFiles {
- // TODO(phosek): process these in parallel using goroutines.
- file, err := os.Open(outputFile)
- if err != nil {
- return nil, fmt.Errorf("cannot open %q: %v", outputFile, err)
- }
- defer file.Close()
- var output SymbolizerOutput
- if err := json.NewDecoder(file).Decode(&output); err != nil {
- return nil, fmt.Errorf("cannot decode %q: %v", outputFile, err)
- }
-
- for _, dump := range output {
- dumps[dump.DumpName] = dump
- }
- }
-
- return dumps, nil
-}
-
-// Output is indexed by build id
-func readIDsTxt(idsFile string) (map[string]elflib.BinaryFileRef, error) {
- file, err := os.Open(idsFile)
- if err != nil {
- return nil, fmt.Errorf("cannot open %q: %s", idsFile, err)
- }
- defer file.Close()
- refs, err := elflib.ReadIDsFile(file)
- if err != nil {
- return nil, fmt.Errorf("cannot read %q: %v", idsFile, err)
- }
- out := make(map[string]elflib.BinaryFileRef)
- for _, ref := range refs {
- out[ref.BuildID] = ref
- }
- return out, nil
-}
-
-type indexedInfo struct {
- dumps map[string]SymbolizerDump
- summary map[string][]runtests.DataSink
- ids map[string]elflib.BinaryFileRef
-}
-
-type ProfileEntry struct {
- ProfileData string `json:"profile"`
- ModuleFiles []string `json:"modules"`
-}
-
-func readInfo(dumpFiles, summaryFiles []string, idsFile string) (*indexedInfo, error) {
- summary, err := readSummary(summaryFile)
- if err != nil {
- return nil, err
- }
- dumps, err := readSymbolizerOutput(symbolizeDumpFile)
- if err != nil {
- return nil, err
- }
- ids, err := readIDsTxt(idsFile)
- if err != nil {
- return nil, err
- }
- return &indexedInfo{
- dumps: dumps,
- summary: summary,
- ids: ids,
- }, nil
-}
-
-func mergeInfo(ctx context.Context, info *indexedInfo) ([]ProfileEntry, error) {
- entries := []ProfileEntry{}
-
- for _, sink := range info.summary[llvmProfileSinkType] {
- dump, ok := info.dumps[sink.Name]
- if !ok {
- logger.Warningf(ctx, "%s not found in summary file\n", sink.Name)
- continue
- }
-
- // This is going to go in a covDataEntry as the list of paths to the modules for the data
- moduleFiles := []string{}
- for _, mod := range dump.Modules {
- if ref, ok := info.ids[mod.Build]; ok {
- moduleFiles = append(moduleFiles, ref.Filepath)
- } else {
- logger.Warningf(ctx, "module with build id %s not found in ids.txt file\n", mod.Build)
- continue
- }
- }
-
- // Finally we can add all the data
- entries = append(entries, ProfileEntry{
- ModuleFiles: moduleFiles,
- ProfileData: sink.File,
- })
- }
-
- return entries, nil
-}
-
-type Action struct {
- Path string `json:"cmd"`
- Args []string `json:"args"`
-}
-
-func (a Action) Run(ctx context.Context) ([]byte, error) {
- logger.Debugf(ctx, "%s\n", a.String())
- if !dryRun {
- return exec.Command(a.Path, a.Args...).CombinedOutput()
- }
- return nil, nil
-}
-
-func (a Action) String() string {
- var buf bytes.Buffer
- fmt.Fprint(&buf, a.Path)
- for _, arg := range a.Args {
- fmt.Fprintf(&buf, " %s", arg)
- }
- return buf.String()
-}
-
-func isInstrumented(filepath string) bool {
- sections := []string{"__llvm_covmap", "__llvm_prf_names"}
- file, err := os.Open(filepath)
- if err != nil {
- return false
- }
- defer file.Close()
- elfFile, err := elf.NewFile(file)
- if err != nil {
- return false
- }
- for _, section := range sections {
- if sec := elfFile.Section(section); sec == nil {
- return false
- }
- }
- return true
-}
-
-func process(ctx context.Context) error {
- // Make the output directory
- err := os.MkdirAll(outputDir, os.ModePerm)
- if err != nil {
- return fmt.Errorf("creating output dir %s: %v", outputDir, err)
- }
-
- // Read in all the data
- info, err := readInfo(symbolizeDumpFile, summaryFile, idsFile)
- if err != nil {
- return fmt.Errorf("parsing info: %v", err)
- }
-
- // Merge all the information
- entries, err := mergeInfo(ctx, info)
- if err != nil {
- return fmt.Errorf("merging info: %v", err)
- }
-
- if jsonOutput != "" {
- file, err := os.Create(jsonOutput)
- if err != nil {
- return fmt.Errorf("creating profile output file: %v", err)
- }
- defer file.Close()
- if err := json.NewEncoder(file).Encode(entries); err != nil {
- return fmt.Errorf("writing profile information: %v", err)
- }
- }
-
- // Gather the set of modules and coverage files
- modSet := make(map[string]struct{})
- var mods []string
- var covFiles []string
- for _, entry := range entries {
- for _, mod := range entry.ModuleFiles {
- if _, ok := modSet[mod]; !ok && isInstrumented(mod) {
- mods = append(mods, mod)
- modSet[mod] = struct{}{}
- }
- }
- // TODO(https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=34796): ideally this would
- // be handled by llvm-profdata tool itself.
- cmd := exec.Command(llvmProfdata, "show", entry.ProfileData)
- if err := cmd.Run(); err != nil {
- if _, ok := err.(*exec.ExitError); ok {
- logger.Warningf(ctx, "profile %q is corrupted\n", entry.ProfileData)
- continue
- } else {
- return fmt.Errorf("llvm-profdata show %s failed: %v", entry.ProfileData, err)
- }
- }
- covFiles = append(covFiles, entry.ProfileData)
- }
-
- dir, err := ioutil.TempDir("", "covargs")
- if err != nil {
- return fmt.Errorf("cannot create temporary dir: %v", err)
- }
- defer os.RemoveAll(dir)
-
- // Make the llvm-profdata response file
- profdataFile, err := os.Create(filepath.Join(dir, "llvm-profdata.rsp"))
- if err != nil {
- return fmt.Errorf("creating llvm-profdata.rsp file: %v", err)
- }
- for _, covFile := range covFiles {
- fmt.Fprintf(profdataFile, "%s\n", covFile)
- }
- profdataFile.Close()
-
- // Merge everything
- mergedFile := filepath.Join(dir, "merged.profdata")
- mergeCmd := Action{Path: llvmProfdata, Args: []string{
- "merge",
- "-o", mergedFile,
- "@" + profdataFile.Name(),
- }}
- data, err := mergeCmd.Run(ctx)
- if err != nil {
- return fmt.Errorf("%v:\n%s", err, string(data))
- }
-
- // Make the llvm-cov response file
- covFile, err := os.Create(filepath.Join(dir, "llvm-cov.rsp"))
- if err != nil {
- return fmt.Errorf("creating llvm-cov.rsp file: %v", err)
- }
- for _, mod := range mods {
- fmt.Fprintf(covFile, "-object %s\n", mod)
- }
- covFile.Close()
-
- // Produce output
- showCmd := Action{Path: llvmCov, Args: []string{
- "show",
- "-format", outputFormat,
- "-instr-profile", mergedFile,
- "-output-dir", outputDir,
- "@" + covFile.Name(),
- }}
- data, err = showCmd.Run(ctx)
- if err != nil {
- return fmt.Errorf("%v:\n%s", err, string(data))
- }
- return nil
-}
-
-func main() {
- flag.Parse()
-
- log := logger.NewLogger(level, color.NewColor(colors), os.Stdout, os.Stderr, "")
- ctx := logger.WithLogger(context.Background(), log)
-
- if err := process(ctx); err != nil {
- log.Errorf("%v\n", err)
- os.Exit(1)
- }
-}
diff --git a/debug/covargs/cmd/testdata/build/dl.c b/debug/covargs/cmd/testdata/build/dl.c
deleted file mode 100644
index 77b2aae..0000000
--- a/debug/covargs/cmd/testdata/build/dl.c
+++ /dev/null
@@ -1,7 +0,0 @@
-int main() {
- if (0) {
- return 1;
- } else {
- return 0;
- }
-}
diff --git a/debug/covargs/cmd/testdata/build/dl.profraw b/debug/covargs/cmd/testdata/build/dl.profraw
deleted file mode 100644
index 3100c8d..0000000
--- a/debug/covargs/cmd/testdata/build/dl.profraw
+++ /dev/null
Binary files differ
diff --git a/debug/covargs/cmd/testdata/build/fbl.c b/debug/covargs/cmd/testdata/build/fbl.c
deleted file mode 100644
index 807d0f5..0000000
--- a/debug/covargs/cmd/testdata/build/fbl.c
+++ /dev/null
@@ -1,6 +0,0 @@
-void puts(const char*);
-
-int main() {
- puts("Hello, World!");
- return 0;
-}
diff --git a/debug/covargs/cmd/testdata/build/fbl.profraw b/debug/covargs/cmd/testdata/build/fbl.profraw
deleted file mode 100644
index ed0bc96..0000000
--- a/debug/covargs/cmd/testdata/build/fbl.profraw
+++ /dev/null
Binary files differ
diff --git a/debug/covargs/cmd/testdata/build/foo.c b/debug/covargs/cmd/testdata/build/foo.c
deleted file mode 100644
index 79c88fd..0000000
--- a/debug/covargs/cmd/testdata/build/foo.c
+++ /dev/null
@@ -1,7 +0,0 @@
-int foo() {
- return 42;
-}
-
-int main() {
- return foo();
-}
diff --git a/debug/covargs/cmd/testdata/build/foo.profraw b/debug/covargs/cmd/testdata/build/foo.profraw
deleted file mode 100644
index 1d491e9..0000000
--- a/debug/covargs/cmd/testdata/build/foo.profraw
+++ /dev/null
Binary files differ
diff --git a/debug/covargs/cmd/testdata/build/libc.c b/debug/covargs/cmd/testdata/build/libc.c
deleted file mode 100644
index 33ab303..0000000
--- a/debug/covargs/cmd/testdata/build/libc.c
+++ /dev/null
@@ -1,9 +0,0 @@
-#include <stdio.h>
-
-int main() {
- if (printf("this is a test")) {
- return 0;
- } else {
- return 1;
- }
-}
diff --git a/debug/covargs/cmd/testdata/build/libc.profraw b/debug/covargs/cmd/testdata/build/libc.profraw
deleted file mode 100644
index 9825399..0000000
--- a/debug/covargs/cmd/testdata/build/libc.profraw
+++ /dev/null
Binary files differ
diff --git a/debug/covargs/cmd/testdata/dump.json b/debug/covargs/cmd/testdata/dump.json
deleted file mode 100644
index 475b589..0000000
--- a/debug/covargs/cmd/testdata/dump.json
+++ /dev/null
@@ -1,12 +0,0 @@
-[
- {"modules":[
- {"name": "fbl.so", "build":"deadbeef", "id":0},
- {"name": "a.out", "build":"deadcafe", "id":2}],
- "segments":[], "type":"llvm-profile", "name": "llvm-profile.3190"},
- {"modules":[
- {"name": "dl.so", "build":"89abcdef", "id":3}],
- "segments":[], "type":"llvm-profile", "name": "llvm-profile.1234"},
- {"modules":[
- {"name": "foo.so", "build":"ffffffff", "id":1}],
- "segments":[], "type":"llvm-profile", "name": "llvm-profile.4567"}
-]
diff --git a/debug/covargs/cmd/testdata/ids.txt b/debug/covargs/cmd/testdata/ids.txt
deleted file mode 100644
index 5dc3a0d..0000000
--- a/debug/covargs/cmd/testdata/ids.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-deadbeef build/fbl.so
-12345678 build/libc.so
-ffffffff build/foo.so
-89abcdef build/dl.so
-deadcafe build/a.out
diff --git a/debug/covargs/cmd/testdata/summary.json b/debug/covargs/cmd/testdata/summary.json
deleted file mode 100644
index 7e91d53..0000000
--- a/debug/covargs/cmd/testdata/summary.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{"tests": [
- {"name": "fbl-test",
- "output_file": "stdout-and-stderr.txt",
- "result": "PASS",
- "data_sinks": {"llvm-profile": [
- {"name": "llvm-profile.3190", "file": "build/fbl.profraw"}
- ]}},
- {"name": "foo-test",
- "output_file": "stdout-and-stderr.txt",
- "result": "PASS",
- "data_sinks": {"llvm-profile": [
- {"name": "llvm-profile.4567", "file": "build/foo.profraw"}
- ]}},
- {"name": "all-test",
- "output_file": "stdout-and-stderr.txt",
- "result": "PASS",
- "data_sinks": {"llvm-profile": [
- {"name": "llvm-profile.1234", "file": "build/dl.profraw"}
- ]}}
- ],
- "outputs": {"syslog_file": "syslog.txt"}}
diff --git a/debug/dump_breakpad_symbols/cmd/depfile.go b/debug/dump_breakpad_symbols/cmd/depfile.go
deleted file mode 100644
index ee2dfb7..0000000
--- a/debug/dump_breakpad_symbols/cmd/depfile.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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 (
- "errors"
- "fmt"
- "io"
- "strings"
-)
-
-// depfile writes Ninja dep files.
-type depfile struct {
- // outputPath is the path to some new filesystem entity.
- outputPath string
-
- // inputPaths are the sources that were used to generate outputPath.
- inputPaths []string
-}
-
-// WriteTo writes this depfile to the given io.Writer. Returns an error if the output is
-// empty, there are no input paths, or any of the inputs is empty.
-func (df *depfile) WriteTo(w io.Writer) (int64, error) {
- if df.outputPath == "" {
- return 0, errors.New("depfile is missing output")
- }
- if len(df.inputPaths) == 0 {
- return 0, errors.New("depfile is missing inputs")
- }
- for _, input := range df.inputPaths {
- if input == "" {
- return 0, fmt.Errorf("got empty dep file input path: %v", df.inputPaths)
- }
- }
-
- contents := fmt.Sprintf("%s: %s\n", df.outputPath, strings.Join(df.inputPaths, " "))
- n, err := w.Write([]byte(contents))
- return int64(n), err
-}
diff --git a/debug/dump_breakpad_symbols/cmd/depfile_test.go b/debug/dump_breakpad_symbols/cmd/depfile_test.go
deleted file mode 100644
index e7296e2..0000000
--- a/debug/dump_breakpad_symbols/cmd/depfile_test.go
+++ /dev/null
@@ -1,101 +0,0 @@
-// 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 (
- "bytes"
- "reflect"
- "testing"
-)
-
-func TestDepfile(t *testing.T) {
- tests := []struct {
- // A name for this test case
- name string
-
- // The input depfile.
- depfile *depfile
-
- // The expected contents of the file produced by WriteTo.
- output string
-
- // Whether to expect an error when creating the dep file.
- expectErr bool
- }{
- {
- name: "should create a dep file with a single input",
- depfile: &depfile{
- outputPath: "a",
- inputPaths: []string{"b"},
- },
- output: "a: b\n",
- },
- {
- name: "should create a dep file with multiple inputs",
- depfile: &depfile{
- outputPath: "a",
- inputPaths: []string{"b", "c"},
- },
- output: "a: b c\n",
- },
- {
- name: "should err if the output path is missing",
- depfile: &depfile{
- inputPaths: []string{"b"},
- },
- expectErr: true,
- },
- {
- name: "should err if the input paths are missing",
- depfile: &depfile{
- outputPath: "a",
- },
- expectErr: true,
- },
- {
- name: "should err if any of the input paths is empty (first)",
- depfile: &depfile{
- outputPath: "a",
- inputPaths: []string{"", "b"},
- },
- expectErr: true,
- },
- {
- name: "should err if any of the input paths is empty (last)",
- depfile: &depfile{
- outputPath: "a",
- inputPaths: []string{"b", ""},
- },
- expectErr: true,
- },
- {
- name: "should err if any of the input paths is empty (nth)",
- depfile: &depfile{
- outputPath: "a",
- inputPaths: []string{"b", "c", "", "e"},
- },
- expectErr: true,
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- var buf bytes.Buffer
- _, err := tt.depfile.WriteTo(&buf)
- if tt.expectErr && err == nil {
- t.Errorf("wanted an error but got nil")
- return
- }
- if !tt.expectErr && err != nil {
- t.Errorf("got an unexpected error: %v", err)
- }
- actual := buf.String()
- expected := tt.output
- if !reflect.DeepEqual(actual, expected) {
- t.Errorf("got:\n\n%v\n\nwanted:\n\n%v\n\n", actual, expected)
- }
- })
- }
-}
diff --git a/debug/dump_breakpad_symbols/cmd/main.go b/debug/dump_breakpad_symbols/cmd/main.go
deleted file mode 100755
index 1ed0a42..0000000
--- a/debug/dump_breakpad_symbols/cmd/main.go
+++ /dev/null
@@ -1,164 +0,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 (
- // 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
-}
diff --git a/debug/elflib/elflib.go b/debug/elflib/elflib.go
deleted file mode 100644
index bfb3a67..0000000
--- a/debug/elflib/elflib.go
+++ /dev/null
@@ -1,276 +0,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 elflib provides methods for handling ELF files.
-package elflib
-
-import (
- "bufio"
- "bytes"
- "debug/elf"
- "encoding/binary"
- "encoding/hex"
- "fmt"
- "io"
- "os"
- "path/filepath"
- "strings"
-)
-
-const NT_GNU_BUILD_ID uint32 = 3
-
-// DebugFileSuffix is the file suffix used by unstripped debug binaries.
-const DebugFileSuffix = ".debug"
-
-// BinaryFileRef represents a reference to an ELF file. The build id
-// and filepath are stored here. BinaryFileRefs can verify that their
-// build id matches their contents.
-type BinaryFileRef struct {
- BuildID string
- Filepath string
-}
-
-// NewBinaryFileRef returns a BinaryFileRef with the given build id and filepath.
-func NewBinaryFileRef(filepath, build string) BinaryFileRef {
- return BinaryFileRef{BuildID: build, Filepath: filepath}
-}
-
-type buildIDError struct {
- err error
- filename string
-}
-
-func newBuildIDError(err error, filename string) *buildIDError {
- return &buildIDError{err: err, filename: filename}
-}
-
-func (b buildIDError) Error() string {
- return fmt.Sprintf("error reading %s: %v", b.filename, b.err)
-}
-
-// Verify verifies that the build id of b matches the build id found in the file.
-func (b BinaryFileRef) Verify() error {
- file, err := os.Open(b.Filepath)
- if err != nil {
- return newBuildIDError(err, b.Filepath)
- }
- defer file.Close()
- buildIDs, err := GetBuildIDs(b.Filepath, file)
- if err != nil {
- return newBuildIDError(err, b.Filepath)
- }
- binBuild, err := hex.DecodeString(b.BuildID)
- if err != nil {
- return newBuildIDError(fmt.Errorf("build ID `%s` is not a hex string: %v", b.BuildID, err), b.Filepath)
- }
- for _, buildID := range buildIDs {
- if bytes.Equal(buildID, binBuild) {
- return nil
- }
- }
- return newBuildIDError(fmt.Errorf("build ID `%s` could not be found", b.BuildID), b.Filepath)
-}
-
-// HasDebugInfo checks if file contains debug_info section.
-func (b BinaryFileRef) HasDebugInfo() (bool, error) {
- elfFile, err := elf.Open(b.Filepath)
- if err != nil {
- return false, err
- }
- defer elfFile.Close()
- for _, section := range elfFile.Sections {
- if section != nil && section.Name == ".debug_info" {
- return true, nil
- }
- }
- return false, nil
-}
-
-// rounds 'x' up to the next 'to' aligned value
-func alignTo(x, to uint32) uint32 {
- return (x + to - 1) & -to
-}
-
-type noteEntry struct {
- noteType uint32
- name string
- desc []byte
-}
-
-func forEachNote(note []byte, endian binary.ByteOrder, entryFn func(noteEntry)) error {
- for {
- var out noteEntry
- // If there isn't enough to parse set n to nil.
- if len(note) < 12 {
- return nil
- }
- namesz := endian.Uint32(note[0:4])
- descsz := endian.Uint32(note[4:8])
- out.noteType = endian.Uint32(note[8:12])
- if namesz+12 > uint32(len(note)) {
- return fmt.Errorf("invalid name length in note entry")
- }
- out.name = string(note[12 : 12+namesz])
- // We need to account for padding at the end.
- descoff := alignTo(12+namesz, 4)
- if descoff+descsz > uint32(len(note)) {
- return fmt.Errorf("invalid desc length in note entry")
- }
- out.desc = note[descoff : descoff+descsz]
- next := alignTo(descoff+descsz, 4)
- // If the final padding isn't in the entry, don't throw error.
- if next >= uint32(len(note)) {
- note = note[len(note):]
- } else {
- note = note[next:]
- }
- entryFn(out)
- }
-}
-
-func getBuildIDs(filename string, endian binary.ByteOrder, data io.ReaderAt, size uint64) ([][]byte, error) {
- noteBytes := make([]byte, size)
- _, err := data.ReadAt(noteBytes, 0)
- if err != nil {
- return nil, fmt.Errorf("error parsing section header in %s: %v", filename, err)
- }
- out := [][]byte{}
- err = forEachNote(noteBytes, endian, func(entry noteEntry) {
- if entry.noteType != NT_GNU_BUILD_ID || entry.name != "GNU\000" {
- return
- }
- out = append(out, entry.desc)
- })
- return out, err
-}
-
-// GetBuildIDs parses and returns all the build ids from file's section/program headers.
-func GetBuildIDs(filename string, file io.ReaderAt) ([][]byte, error) {
- elfFile, err := elf.NewFile(file)
- if err != nil {
- return nil, fmt.Errorf("could not parse ELF file %s: %v", filename, err)
- }
- if len(elfFile.Progs) == 0 && len(elfFile.Sections) == 0 {
- return nil, fmt.Errorf("no program headers or sections in %s", filename)
- }
- var endian binary.ByteOrder
- if elfFile.Data == elf.ELFDATA2LSB {
- endian = binary.LittleEndian
- } else {
- endian = binary.BigEndian
- }
- out := [][]byte{}
- // Check every SHT_NOTE section.
- for _, section := range elfFile.Sections {
- if section == nil || section.Type != elf.SHT_NOTE {
- continue
- }
- buildIDs, err := getBuildIDs(filename, endian, section, section.Size)
- if err != nil {
- return out, err
- }
- out = append(out, buildIDs...)
- }
- // If we found what we were looking for with sections, don't reparse the program
- // headers.
- if len(out) > 0 {
- return out, nil
- }
- // Check every PT_NOTE segment.
- for _, prog := range elfFile.Progs {
- if prog == nil || prog.Type != elf.PT_NOTE {
- continue
- }
- buildIDs, err := getBuildIDs(filename, endian, prog, prog.Filesz)
- if err != nil {
- return out, err
- }
- out = append(out, buildIDs...)
- }
- return out, nil
-}
-
-// GetSoName returns the soname of the ELF file.
-func GetSoName(filename string, file io.ReaderAt) (string, error) {
- elfFile, err := elf.NewFile(file)
- if err != nil {
- return "", fmt.Errorf("could not parse ELF file %s: %v", filename, err)
- }
- strings, err := elfFile.DynString(elf.DT_SONAME)
- if err != nil {
- return "", fmt.Errorf("when parsing .dynamic from %s: %v", filename, err)
- }
- if len(strings) > 1 {
- return "", fmt.Errorf("expected at most one DT_SONAME entry in %s", filename)
- }
- if len(strings) == 0 {
- return "", nil
- }
- return strings[0], nil
-}
-
-// ReadIDsFile reads a list of build ids and corresponding filenames from an ids file
-// and returns a list of BinaryFileRefs.
-func ReadIDsFile(file io.Reader) ([]BinaryFileRef, error) {
- scanner := bufio.NewScanner(file)
- out := []BinaryFileRef{}
- for line := 1; scanner.Scan(); line++ {
- text := scanner.Text()
- parts := strings.SplitN(text, " ", 2)
- if len(parts) != 2 {
- return nil, fmt.Errorf("error parsing on line %d: found `%s`", line, text)
- }
- build := strings.TrimSpace(parts[0])
- filename := strings.TrimSpace(parts[1])
- out = append(out, BinaryFileRef{Filepath: filename, BuildID: build})
- }
- return out, nil
-}
-
-// WalkBuildIDDir walks the directory containing symbol files at dirpath and creates a
-// BinaryFileRef for each symbol file it finds. Files without a ".debug" suffix are
-// skipped. Each output BinaryFileRef's BuildID is formed by concatenating the file's
-// basename with its parent directory's basename. For example:
-//
-// Input directory:
-//
-// de/
-// adbeef.debug
-// admeat.debug
-//
-// Output BuildIDs:
-//
-// deadbeef.debug
-// deadmeat.debug
-func WalkBuildIDDir(dirpath string) ([]BinaryFileRef, error) {
- info, err := os.Stat(dirpath)
- if err != nil {
- return nil, fmt.Errorf("failed to stat %q: %v", dirpath, err)
- }
- if !info.IsDir() {
- return nil, fmt.Errorf("%q is not a directory", dirpath)
- }
- var refs []BinaryFileRef
- if err := filepath.Walk(dirpath, func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return fmt.Errorf("%q: %v", path, err)
- }
- if info.IsDir() {
- return nil
- }
- if !strings.HasSuffix(path, DebugFileSuffix) {
- return nil
- }
- dir, basename := filepath.Split(path)
- buildID := filepath.Base(dir) + strings.TrimSuffix(basename, DebugFileSuffix)
- refs = append(refs, BinaryFileRef{
- Filepath: path,
- BuildID: buildID,
- })
- return nil
- }); err != nil {
- return nil, err
- }
- return refs, nil
-}
diff --git a/debug/elflib/elflib_test.go b/debug/elflib/elflib_test.go
deleted file mode 100644
index 8c01747..0000000
--- a/debug/elflib/elflib_test.go
+++ /dev/null
@@ -1,47 +0,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 elflib
-
-import (
- "encoding/hex"
- "os"
- "testing"
-)
-
-func TestBuildIDs(t *testing.T) {
- f, err := os.Open("testdata/libc.elf.section-only")
- if err != nil {
- t.Fatal("from os.Open: ", err)
- }
- buildIDs, err := GetBuildIDs("testdata/libc.elf.section-only", f)
- if err != nil {
- t.Fatal("from GetBuildIDs: ", err)
- }
- if len(buildIDs) != 1 {
- t.Fatal("expected one build ID but got ", buildIDs)
- }
- expected := "4fcb712aa6387724a9f465a32cd8c14b"
- if hex.EncodeToString(buildIDs[0]) != expected {
- t.Fatal("expected ", expected, " but got ", buildIDs[0])
- }
-}
-
-func TestStrippedBuildIDs(t *testing.T) {
- f, err := os.Open("testdata/libc.elf.stripped")
- if err != nil {
- t.Fatal("from os.Open: ", err)
- }
- buildIDs, err := GetBuildIDs("testdata/libc.elf.stripped", f)
- if err != nil {
- t.Fatal("from GetBuildIDs: ", err)
- }
- if len(buildIDs) != 1 {
- t.Fatal("expected one build ID but got ", buildIDs)
- }
- expected := "4fcb712aa6387724a9f465a32cd8c14b"
- if hex.EncodeToString(buildIDs[0]) != expected {
- t.Fatal("expected ", expected, " but got ", buildIDs[0])
- }
-}
diff --git a/debug/elflib/testdata/libc.elf.section-only b/debug/elflib/testdata/libc.elf.section-only
deleted file mode 100644
index cbced7c..0000000
--- a/debug/elflib/testdata/libc.elf.section-only
+++ /dev/null
Binary files differ
diff --git a/debug/elflib/testdata/libc.elf.stripped b/debug/elflib/testdata/libc.elf.stripped
deleted file mode 100755
index 0071813..0000000
--- a/debug/elflib/testdata/libc.elf.stripped
+++ /dev/null
Binary files differ
diff --git a/debug/elflib/testdata/libc.yaml b/debug/elflib/testdata/libc.yaml
deleted file mode 100644
index e33cfd0..0000000
--- a/debug/elflib/testdata/libc.yaml
+++ /dev/null
@@ -1,45 +0,0 @@
---- !ELF
-FileHeader:
- Class: ELFCLASS64
- Data: ELFDATA2LSB
- Type: ET_EXEC
- Machine: EM_X86_64
-Sections:
- - Name: .text
- Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC ]
- - Name: .dynsym
- Type: SHT_DYNSYM
- - Name: .dynstr
- Type: SHT_STRTAB
- - Name: .note.gnu.build-id
- Type: SHT_NOTE
- Flags: [ SHF_ALLOC ]
- AddressAlign: 0x0000000000000004
- Content: 040000001000000003000000474E55004FCB712AA6387724A9F465A32CD8C14B
-DynamicSymbols:
- Global:
- - Name: atan2
- Type: STT_FUNC
- Section: .text
- - Name: pow
- Type: STT_FUNC
- Section: .text
- - Name: memcpy
- Type: STT_FUNC
- Section: .text
-ProgramHeaders:
- - Type: PT_LOAD
- Flags: [ PF_X, PF_R ]
- Sections:
- - Section: .text
- - Type: PT_LOAD
- Flags: [ PF_R ]
- Sections:
- - Section: .dynsym
- - Section: .dynstr
- - Section: .note.gnu.build-id
- - Type: PT_NOTE
- Flags: [ PF_R ]
- Sections:
- - Section: .note.gnu.build-id
diff --git a/debug/elflib/testdata/libc.yaml.section-only b/debug/elflib/testdata/libc.yaml.section-only
deleted file mode 100644
index 5bcd6ca..0000000
--- a/debug/elflib/testdata/libc.yaml.section-only
+++ /dev/null
@@ -1,12 +0,0 @@
---- !ELF
-FileHeader:
- Class: ELFCLASS64
- Data: ELFDATA2LSB
- Type: ET_EXEC
- Machine: EM_X86_64
-Sections:
- - Name: .note.gnu.build-id
- Type: SHT_NOTE
- Flags: [ SHF_ALLOC ]
- AddressAlign: 0x0000000000000004
- Content: 040000001000000003000000474E55004FCB712AA6387724A9F465A32CD8C14B
diff --git a/debug/symbolize/ast.go b/debug/symbolize/ast.go
deleted file mode 100644
index 4c9d9b8..0000000
--- a/debug/symbolize/ast.go
+++ /dev/null
@@ -1,156 +0,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 symbolize
-
-import (
- "encoding/json"
- "fmt"
-)
-
-// TODO (jakehehrlich): Add methods to make these node types usable
-
-// NodeVisitor is a visitor for all Node types
-type NodeVisitor interface {
- VisitBt(elem *BacktraceElement)
- VisitPc(elem *PCElement)
- VisitColor(node *ColorCode)
- VisitText(node *Text)
- VisitDump(elem *DumpfileElement)
- VisitReset(elem *ResetElement)
- VisitModule(elem *ModuleElement)
- VisitMapping(elem *MappingElement)
-}
-type Node interface {
- Accept(visitor NodeVisitor)
-}
-
-// OptionalString implements possibly missing strings from llvm-symbolizer
-type OptStr struct {
- val *string
-}
-
-func EmptyOptStr() OptStr {
- return OptStr{}
-}
-
-func NewOptStr(s string) OptStr {
- return OptStr{&s}
-}
-
-func (o *OptStr) Unwrap(def string) string {
- if o.val != nil {
- return *o.val
- }
- return def
-}
-
-func (o OptStr) IsEmpty() bool {
- return o.val == nil
-}
-
-func (o OptStr) String() string {
- return o.Unwrap("??")
-}
-
-func (o OptStr) MarshalJSON() ([]byte, error) {
- return json.Marshal(o.String())
-}
-
-func (o OptStr) Format(f fmt.State, c rune) {
- f.Write([]byte(o.String()))
-}
-
-// SourceLocation represents a location in a source file
-type SourceLocation struct {
- file OptStr
- line int
- function OptStr
-}
-
-type BacktraceElement struct {
- vaddr uint64
- num uint64
- msg string
- info addressInfo
-}
-
-func (b *BacktraceElement) Accept(visitor NodeVisitor) {
- visitor.VisitBt(b)
-}
-
-// PcElement is an AST node representing a pc element in the markup
-type PCElement struct {
- vaddr uint64
- info addressInfo
-}
-
-func (p *PCElement) Accept(visitor NodeVisitor) {
- visitor.VisitPc(p)
-}
-
-// TODO(jakehehrlich): Make this semantic rather than literal (e.g. keep track of color/bold information directly)
-// ColorCode is an AST node representing a colored part of the markup
-type ColorCode struct {
- color uint64
-}
-
-func (c *ColorCode) Accept(visitor NodeVisitor) {
- visitor.VisitColor(c)
-}
-
-// Text represents text between the special parts of the markup
-type Text struct {
- text string
-}
-
-func (t *Text) Accept(visitor NodeVisitor) {
- visitor.VisitText(t)
-}
-
-type DumpfileElement struct {
- sinkType string
- name string
- context *TriggerContext
-}
-
-func (d *DumpfileElement) Context() TriggerContext {
- return *d.context
-}
-
-func (d *DumpfileElement) SinkType() string {
- return d.sinkType
-}
-
-func (d *DumpfileElement) Name() string {
- return d.name
-}
-
-func (d *DumpfileElement) Accept(visitor NodeVisitor) {
- visitor.VisitDump(d)
-}
-
-type ResetElement struct{}
-
-func (r *ResetElement) Accept(visitor NodeVisitor) {
- visitor.VisitReset(r)
-}
-
-// ModuleElement represents a module element in the markup
-type ModuleElement struct {
- mod Module
-}
-
-func (m *ModuleElement) Accept(visitor NodeVisitor) {
- visitor.VisitModule(m)
-}
-
-// MappingElement represents an mmap element in the markup
-type MappingElement struct {
- seg Segment
-}
-
-func (s *MappingElement) Accept(visitor NodeVisitor) {
- visitor.VisitMapping(s)
-}
diff --git a/debug/symbolize/cmd/main.go b/debug/symbolize/cmd/main.go
deleted file mode 100644
index 77763bf..0000000
--- a/debug/symbolize/cmd/main.go
+++ /dev/null
@@ -1,138 +0,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 (
- "context"
- "encoding/json"
- "flag"
- "fmt"
- "io"
- "os"
-
- "go.fuchsia.dev/tools/debug/symbolize"
- "go.fuchsia.dev/tools/lib/color"
- "go.fuchsia.dev/tools/lib/logger"
-)
-
-type argList []string
-
-func (a *argList) String() string {
- return fmt.Sprintf("%s", []string(*a))
-}
-
-func (a *argList) Set(value string) error {
- *a = append(*a, value)
- return nil
-}
-
-var (
- buildIDDirPaths argList
- colors color.EnableColor
- jsonOutput string
- idsPaths argList
- // TODO(jakehehrlich): Make idsRel always true and remove this flag.
- idsRel bool
- level logger.LogLevel
- llvmSymboPath string
-)
-
-func init() {
- colors = color.ColorAuto
- level = logger.InfoLevel
-
- flag.Var(&buildIDDirPaths, "build-id-dir", "path to .build-id directory")
- flag.StringVar(&llvmSymboPath, "llvm-symbolizer", "llvm-symbolizer", "path to llvm-symbolizer")
- flag.Var(&idsPaths, "ids", "path to ids.txt")
- 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")
- flag.StringVar(&jsonOutput, "json-output", "", "outputs trigger information to the specified file")
- flag.BoolVar(&idsRel, "ids-rel", false, "tells the symbolizer to always use ids.txt relative paths")
-}
-
-type dumpEntry struct {
- Modules []symbolize.Module `json:"modules"`
- Segments []symbolize.Segment `json:"segments"`
- Type string `json:"type"`
- Name string `json:"name"`
-}
-
-type dumpHandler struct {
- dumps []dumpEntry
-}
-
-func (d *dumpHandler) HandleDump(dump *symbolize.DumpfileElement) {
- triggerCtx := dump.Context()
- d.dumps = append(d.dumps, dumpEntry{
- Modules: triggerCtx.Mods,
- Segments: triggerCtx.Segs,
- Type: dump.SinkType(),
- Name: dump.Name(),
- })
-}
-
-func (d *dumpHandler) Write(buf io.Writer) error {
- enc := json.NewEncoder(buf)
- enc.SetIndent("", " ")
- err := enc.Encode(d.dumps)
- if err != nil {
- return err
- }
- return nil
-}
-
-func main() {
- // Parse flags and setup helpers
- flag.Parse()
- var jsonTriggerHandler *dumpHandler
- if jsonOutput != "" {
- jsonTriggerHandler = &dumpHandler{}
- }
-
- // Setup logger and context
- painter := color.NewColor(colors)
- log := logger.NewLogger(level, painter, os.Stdout, os.Stderr, "")
- ctx := logger.WithLogger(context.Background(), log)
-
- // Construct the nodes of the pipeline
- symbolizer := symbolize.NewLLVMSymbolizer(llvmSymboPath)
- var repo symbolize.CompositeRepo
- for _, dir := range buildIDDirPaths {
- repo.AddRepo(symbolize.NewBuildIDRepo(dir))
- }
- for _, idsPath := range idsPaths {
- repo.AddRepo(symbolize.NewIDsTxtRepo(idsPath, idsRel))
- }
- demuxer := symbolize.NewDemuxer(&repo, symbolizer)
- tap := symbolize.NewTriggerTap()
- if jsonTriggerHandler != nil {
- tap.AddHandler(jsonTriggerHandler.HandleDump)
- }
- presenter := symbolize.NewBasicPresenter(os.Stdout, painter.Enabled())
-
- // Build the pipeline to start presenting.
- if err := symbolizer.Start(ctx); err != nil {
- log.Fatalf("%v\n", err)
- }
- inputLines := symbolize.StartParsing(ctx, os.Stdin)
- outputLines := demuxer.Start(ctx, inputLines)
- trash := symbolize.ComposePostProcessors(ctx, outputLines,
- tap,
- &symbolize.ContextPresenter{},
- &symbolize.OptimizeColor{},
- symbolize.NewBacktracePresenter(os.Stdout, presenter))
- symbolize.Consume(trash)
-
- // Once the pipeline has finished output all triggers
- if jsonTriggerHandler != nil {
- file, err := os.Create(jsonOutput)
- if err != nil {
- log.Fatalf("%v\n", err)
- }
- if err := jsonTriggerHandler.Write(file); err != nil {
- log.Fatalf("%v\n", err)
- }
- }
-}
diff --git a/debug/symbolize/demuxer.go b/debug/symbolize/demuxer.go
deleted file mode 100644
index 81c8c51..0000000
--- a/debug/symbolize/demuxer.go
+++ /dev/null
@@ -1,128 +0,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 symbolize
-
-import (
- "context"
- "fmt"
-
- "go.fuchsia.dev/tools/lib/logger"
-)
-
-// TODO (jakehehrlich): Make as much of this private as possible.
-
-type remuxer struct {
- seq chan (<-chan OutputLine)
- out chan<- OutputLine
-}
-
-func newRemuxer() *remuxer {
- return &remuxer{seq: make(chan (<-chan OutputLine), 1024)}
-}
-
-func (r *remuxer) sequence(in <-chan OutputLine) {
- r.seq <- in
-}
-
-func (r *remuxer) start(ctx context.Context) (<-chan OutputLine, error) {
- if r.out != nil {
- return nil, fmt.Errorf("Attempt to start an already running remuxer")
- }
- out := make(chan OutputLine)
- r.out = out
- go func() {
- defer close(r.out)
- for {
- select {
- case <-ctx.Done():
- return
- case in, ok := <-r.seq:
- if !ok {
- return
- }
- r.out <- (<-in)
- }
- }
- }()
- return out, nil
-}
-
-func (r *remuxer) stop() error {
- close(r.seq)
- if r.seq == nil {
- return fmt.Errorf("Attempt to stop a remuxer that hasn't been started")
- }
- return nil
-}
-
-type pipe struct {
- in chan<- InputLine
- out <-chan OutputLine
-}
-
-// Demuxer demultiplexes incomming input lines, spins up new filters, and sends them input lines.
-type Demuxer struct {
- // Spin up new filters as new procsses are found.
- filters map[LineSource]pipe
- // Use same symbolizer for all
- symbolizer Symbolizer
- // Use same repo for all
- repo Repository
-}
-
-// NewDemuxer creates a new demuxer.
-func NewDemuxer(repo Repository, symbo Symbolizer) *Demuxer {
- return &Demuxer{
- repo: repo,
- symbolizer: symbo,
- filters: make(map[LineSource]pipe),
- }
-}
-
-// Start tells the demuxer to start consuming input and dispatching to the filters.
-func (d *Demuxer) Start(ctx context.Context, input <-chan InputLine) <-chan OutputLine {
- // Create the remuxer.
- remux := newRemuxer()
- out, err := remux.start(ctx)
- if err != nil {
- logger.Fatalf(ctx, "Failed to start remuxer in case where that should never happen.")
- }
- go func() {
- // Clean up the channels/goroutines when we're done
- defer func() {
- for _, pipe := range d.filters {
- close(pipe.in)
- }
- err := remux.stop()
- if err != nil {
- logger.Warningf(ctx, "%v", err)
- }
- }()
- // Start multiplexing things out
- for {
- select {
- case <-ctx.Done():
- return
- case line, ok := <-input:
- if !ok {
- return
- }
- var fpipe pipe
- if fpipe, ok = d.filters[line.source]; !ok {
- fin := make(chan InputLine)
- // Spin up a new Filter for this process.
- filt := NewFilter(d.repo, d.symbolizer)
- fout := filt.Start(ctx, fin)
- fpipe = pipe{in: fin, out: fout}
- d.filters[line.source] = fpipe
- }
- // sequence a read from fpipe.out
- remux.sequence(fpipe.out)
- fpipe.in <- line
- }
- }
- }()
- return out
-}
diff --git a/debug/symbolize/demuxer_test.go b/debug/symbolize/demuxer_test.go
deleted file mode 100644
index 106e52f..0000000
--- a/debug/symbolize/demuxer_test.go
+++ /dev/null
@@ -1,415 +0,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 symbolize
-
-import (
- "bytes"
- "context"
- "os"
- "strings"
- "testing"
-)
-
-type triggerTester struct {
- count uint64
- sinkType string
- name string
- src LineSource
- mod Module
- seg Segment
- t *testing.T
-}
-
-func (t *triggerTester) HandleDump(dump *DumpfileElement) {
- t.count += 1
- if dump == nil {
- t.t.Fatal("dump was nil")
- }
- t.sinkType = dump.SinkType()
- t.name = dump.Name()
- t.src = dump.Context().Source
- mods := dump.Context().Mods
- segs := dump.Context().Segs
- if len(mods) != 1 {
- t.t.Fatal("expected exactly 1 module")
- }
- t.mod = mods[0]
- if len(segs) != 1 {
- t.t.Fatal("expected exactly 1 segment")
- }
- t.seg = segs[0]
-}
-
-func TestDumpfile(t *testing.T) {
- msg := "[123.456] 01234.5678> {{{module:1:libc.so:elf:4fcb712aa6387724a9f465a32cd8c14b}}}\n" +
- "[123.456] 1234:05678> {{{mmap:0x12345000:849596:load:1:rx:0x0}}}\n" +
- "[123.456] 01234.05678> {{{dumpfile:llvm-cov:test}}}\n"
-
- symbo := newMockSymbolizer([]mockModule{})
- demuxer := NewDemuxer(testBinaries, symbo)
- tap := NewTriggerTap()
- tHandler := &triggerTester{t: t}
- tap.AddHandler(tHandler.HandleDump)
-
- ctx := context.Background()
- in := StartParsing(ctx, strings.NewReader(msg))
- out := demuxer.Start(ctx, in)
- buf := new(bytes.Buffer)
- Consume(ComposePostProcessors(ctx, out, tap, &ContextPresenter{}, &OptimizeColor{}, NewBasicPresenter(buf, true)))
-
- expectedSrc := Process(1234)
- expectedMod := Module{
- Name: "libc.so",
- Build: "4fcb712aa6387724a9f465a32cd8c14b",
- Id: 1,
- }
- expectedSeg := Segment{
- Mod: 1,
- Vaddr: 0x12345000,
- Size: 849596,
- Flags: "rx",
- ModRelAddr: 0x0,
- }
- expectedSink := "llvm-cov"
- expectedName := "test"
- expected := "[123.456] 01234.05678> [[[ELF module #0x1 \"libc.so\" BuildID=4fcb712aa6387724a9f465a32cd8c14b 0x12345000]]]\n" +
- "[123.456] 01234.05678> {{{dumpfile:llvm-cov:test}}}\n"
-
- actual := buf.String()
- if actual != expected {
- t.Error("expected", expected, "got", actual)
- }
- if tHandler.sinkType != expectedSink {
- t.Error("expected", expectedSink, "got", tHandler.sinkType)
- }
- if tHandler.name != expectedName {
- t.Error("expected", expectedName, "got", tHandler.name)
- }
- if tHandler.src != expectedSrc {
- t.Error("expected", expectedSrc, "got", tHandler.src)
- }
- if tHandler.mod != expectedMod {
- t.Error("expected", expectedMod, "got", tHandler.mod)
- }
- if tHandler.seg != expectedSeg {
- t.Error("expected", expectedSeg, "got", tHandler.seg)
- }
- if tHandler.count == 0 {
- t.Error("trigger handler was not called")
- }
- if tHandler.count > 1 {
- t.Error("trigger handler was called more than once")
- }
-}
-
-func TestSyslog(t *testing.T) {
- msg := "[123.456][1234][05678][klog] INFO: Blarg\n"
- symbo := newMockSymbolizer([]mockModule{})
- demuxer := NewDemuxer(testBinaries, symbo)
- ctx := context.Background()
- in := StartParsing(ctx, strings.NewReader(msg))
- out := demuxer.Start(ctx, in)
- buf := new(bytes.Buffer)
- Consume(ComposePostProcessors(ctx, out, &ContextPresenter{}, &OptimizeColor{}, NewBasicPresenter(buf, true)))
- expected := "[00123.456000][1234][5678][klog] INFO: Blarg\n"
- actual := buf.String()
- if actual != expected {
- t.Error("expected", expected, "got", actual)
- }
-}
-
-func TestColor(t *testing.T) {
- // TODO(jakehehrlich): Change presenter so that redundent resets are not used when user input already contains them.
- msg := "[0.0] 01234.5678> \033[1mThis is bold \033[31mThis is red and bold \033[37mThis is bold white\n" +
- "[0.0] 1234:05678> This is just normal and has no trailing ANSI code\n" +
- "[0.0] 1234:5678> \033[1m\033[31m this line tests adjacent state changes\n" +
- "[0.0] 01234.5678> \033[1m\033[31m this line will eventully test non-redundent reset \033[1m\n"
- symbo := newMockSymbolizer([]mockModule{})
- demuxer := NewDemuxer(testBinaries, symbo)
- ctx := context.Background()
- in := StartParsing(ctx, strings.NewReader(msg))
- out := demuxer.Start(ctx, in)
- buf := new(bytes.Buffer)
- Consume(ComposePostProcessors(ctx, out, &ContextPresenter{}, &OptimizeColor{}, NewBasicPresenter(buf, true)))
- expected := "[0.000] 01234.05678> \033[1mThis is bold \033[31mThis is red and bold \033[37mThis is bold white\033[0m\n" +
- "[0.000] 01234.05678> This is just normal and has no trailing ANSI code\n" +
- "[0.000] 01234.05678> \033[31m\033[1m this line tests adjacent state changes\033[0m\n" +
- "[0.000] 01234.05678> \033[31m\033[1m this line will eventully test non-redundent reset \033[0m\n"
- actual := buf.String()
- if actual != expected {
- t.Errorf("expected %#v got %#v", expected, actual)
- }
-}
-
-func TestKeepLeadingSpace(t *testing.T) {
- // mock the input and outputs of llvm-symbolizer
- symbo := newMockSymbolizer([]mockModule{})
- // make a demuxer
- demuxer := NewDemuxer(testBinaries, symbo)
-
- // define a little message that will need to be parsed
- msg := "[0.000] 00000.00000> \tkeep\n"
-
- // start sending InputLines to the demuxer
- ctx := context.Background()
- in := StartParsing(ctx, strings.NewReader(msg))
- // start the demuxer which will cause filters to send output lines to 'out'
- out := demuxer.Start(ctx, in)
- var buf bytes.Buffer
- Consume(ComposePostProcessors(ctx, out, NewBasicPresenter(&buf, false)))
- if string(buf.Bytes()) != msg {
- t.Fatal("expected", msg, "got", string(buf.Bytes()))
- }
-}
-
-func ExampleDummyProcess() {
- // mock the input and outputs of llvm-symbolizer
- symbo := newMockSymbolizer([]mockModule{
- {"testdata/libc.elf", map[uint64][]SourceLocation{
- 0x429c0: {{NewOptStr("atan2.c"), 33, NewOptStr("atan2")}},
- }},
- })
-
- // make a demuxer
- demuxer := NewDemuxer(testBinaries, symbo)
-
- // define a little message that will need to be parsed
- msg := "{{{module:1:libc.so:elf:4fcb712aa6387724a9f465a32cd8c14b}}}\n" +
- "{{{mmap:0x12345000:849596:load:1:rx:0x0}}}\n" +
- "{{{pc:0x123879c0}}}\n" +
- "blarg[0.0] 0.0> This should be on it's own line\n"
-
- // start sending InputLines to the demuxer
- ctx := context.Background()
- in := StartParsing(ctx, strings.NewReader(msg))
- // start the demuxer which will cause filters to send output lines to 'out'
- out := demuxer.Start(ctx, in)
-
- Consume(ComposePostProcessors(ctx, out, &ContextPresenter{}, &OptimizeColor{}, NewBasicPresenter(os.Stdout, false)))
-
- //Output:
- //[[[ELF module #0x1 "libc.so" BuildID=4fcb712aa6387724a9f465a32cd8c14b 0x12345000]]]
- //atan2 at atan2.c:33
- //blarg
- //[0.000] 00000.00000> This should be on it's own line
-}
-
-func ExampleDemux() {
- // mock the input and outputs of llvm-symbolizer
- symbo := newMockSymbolizer([]mockModule{
- {"testdata/libc.elf", map[uint64][]SourceLocation{
- 0x429c0: {{NewOptStr("atan2.c"), 49, NewOptStr("atan2")}, {NewOptStr("math.h"), 51, NewOptStr("__DOUBLE_FLOAT")}},
- 0x43680: {{NewOptStr("pow.c"), 23, NewOptStr("pow")}},
- 0x44987: {{NewOptStr("memcpy.c"), 76, NewOptStr("memcpy")}},
- }},
- {"testdata/libcrypto.elf", map[uint64][]SourceLocation{
- 0x81000: {{NewOptStr("rsa.c"), 101, NewOptStr("mod_exp")}},
- 0x82000: {{NewOptStr("aes.c"), 17, NewOptStr("gf256_mul")}},
- 0x83000: {{NewOptStr("aes.c"), 560, NewOptStr("gf256_div")}},
- }},
- })
-
- // make a demuxer
- demuxer := NewDemuxer(testBinaries, symbo)
-
- // define a little message that will need to be parsed
- msg := "[131.200] 1234.5678> keep {{{module:1:libc.so:elf:4fcb712aa6387724a9f465a32cd8c14b}}}\n" +
- "[131.301] 1234.5678> {{{module:2:libcrypto.so:elf:b4b6c520ccf0aa11ff71d8ded7d6a2bc03037aa1}}} keep\n" +
- "[131.402] 1234.5678> {{{mmap:0x12345000:0xcf6bc:load:1:rx:0x0}}}\n" +
- "[131.503] 1234.5678> {{{mmap:0x23456000:0x83c80:load:2:rx:0x80000}}}\n" +
- "[131.604] 1234.5678> \033[1mError at {{{pc:0x123879c0}}}\n"
-
- // start sending InputLines to the demuxer
- ctx := context.Background()
- in := StartParsing(ctx, strings.NewReader(msg))
- // start the demuxer which will cause filters to send output lines to 'out'
- out := demuxer.Start(ctx, in)
-
- Consume(ComposePostProcessors(ctx, out, &ContextPresenter{}, &OptimizeColor{}, NewBasicPresenter(os.Stdout, false)))
-
- //Output:
- //[131.200] 01234.05678> [[[ELF module #0x1 "libc.so" BuildID=4fcb712aa6387724a9f465a32cd8c14b]]]
- //[131.200] 01234.05678> keep {{{module:4fcb712aa6387724a9f465a32cd8c14b:libc.so:1}}}
- //[131.301] 01234.05678> [[[ELF module #0x2 "libcrypto.so" BuildID=b4b6c520ccf0aa11ff71d8ded7d6a2bc03037aa1]]]
- //[131.301] 01234.05678> {{{module:b4b6c520ccf0aa11ff71d8ded7d6a2bc03037aa1:libcrypto.so:2}}} keep
- //[131.402] 01234.05678> [[[ELF seg #0x1 0x12345000]]]
- //[131.503] 01234.05678> [[[ELF seg #0x2 0x233d6000]]]
- //[131.604] 01234.05678> Error at atan2 at atan2.c:49
-}
-
-func TestMsgSimpleBacktrace(t *testing.T) {
- msg := "{{{bt:0:0xdeadbeef:this is a message}}}\n"
- symbo := newMockSymbolizer([]mockModule{})
- demuxer := NewDemuxer(testBinaries, symbo)
- ctx := context.Background()
- in := StartParsing(ctx, strings.NewReader(msg))
- out := demuxer.Start(ctx, in)
- buf := new(bytes.Buffer)
- Consume(ComposePostProcessors(ctx, out, &ContextPresenter{},
- NewBacktracePresenter(buf, NewBasicPresenter(buf, false))))
- expected := " #0 0x00000000deadbeef in <>+0xdeadbeef this is a message\n"
- actual := buf.String()
- if actual != expected {
- t.Errorf("want %q got %q", expected, actual)
- }
-}
-
-func ExampleMsgBacktrace() {
- // mock the input and outputs of llvm-symbolizer
- symbo := newMockSymbolizer([]mockModule{
- {"testdata/libc.elf", map[uint64][]SourceLocation{
- 0x43680: {{NewOptStr("pow.c"), 23, NewOptStr("pow")}},
- }},
- })
-
- // make a demuxer
- demuxer := NewDemuxer(testBinaries, symbo)
-
- // define a little message that will need to be parsed
- msg := "{{{module:1:libc.so:elf:4fcb712aa6387724a9f465a32cd8c14b}}}\n" +
- "{{{mmap:0x12345000:0xcf6bc:load:1:rx:0x0}}}\n" +
- "{{{bt:0:0x12388680:sp 0xdeadbaaf bp 0xdeadbeef}}}\n"
-
- // start sending InputLines to the demuxer
- ctx := context.Background()
- in := StartParsing(ctx, strings.NewReader(msg))
- // start the demuxer which will cause filters to send output lines to 'out'
- out := demuxer.Start(ctx, in)
-
- Consume(ComposePostProcessors(ctx, out,
- &ContextPresenter{},
- NewBacktracePresenter(os.Stdout, NewBasicPresenter(os.Stdout, false))))
-
- //Output:
- //[[[ELF module #0x1 "libc.so" BuildID=4fcb712aa6387724a9f465a32cd8c14b 0x12345000]]]
- // #0 0x0000000012388680 in pow pow.c:23 <libc.so>+0x43680 sp 0xdeadbaaf bp 0xdeadbeef
-}
-
-func ExampleNoHeaderBacktrace() {
- // mock the input and outputs of llvm-symbolizer
- symbo := newMockSymbolizer([]mockModule{
- {"testdata/libc.elf", map[uint64][]SourceLocation{
- 0x429c0: {{NewOptStr("math.h"), 51, NewOptStr("__DOUBLE_FLOAT")}, {NewOptStr("atan2.c"), 49, NewOptStr("atan2")}},
- 0x43680: {{NewOptStr("pow.c"), 23, NewOptStr("pow")}},
- 0x44987: {{NewOptStr("memcpy.c"), 76, NewOptStr("memcpy")}},
- }},
- })
-
- // make a demuxer
- demuxer := NewDemuxer(testBinaries, symbo)
-
- // define a little message that will need to be parsed
- msg := "{{{module:1:libc.so:elf:4fcb712aa6387724a9f465a32cd8c14b}}}\n" +
- "{{{mmap:0x12345000:0xcf6bc:load:1:rx:0x0}}}\n" +
- "Backtrace:\n" +
- "{{{bt:0:0x12388680}}}\n" +
- "{{{bt:1:0x123879c0}}}\n"
-
- // start sending InputLines to the demuxer
- ctx := context.Background()
- in := StartParsing(ctx, strings.NewReader(msg))
- // start the demuxer which will cause filters to send output lines to 'out'
- out := demuxer.Start(ctx, in)
-
- Consume(ComposePostProcessors(ctx, out,
- &ContextPresenter{},
- NewBacktracePresenter(os.Stdout, NewBasicPresenter(os.Stdout, false))))
-
- //Output:
- //[[[ELF module #0x1 "libc.so" BuildID=4fcb712aa6387724a9f465a32cd8c14b 0x12345000]]]
- //Backtrace:
- // #0 0x0000000012388680 in pow pow.c:23 <libc.so>+0x43680
- // #1.1 0x00000000123879c0 in __DOUBLE_FLOAT math.h:51 <libc.so>+0x429c0
- // #1 0x00000000123879c0 in atan2 atan2.c:49 <libc.so>+0x429c0
-}
-
-func ExampleNewBacktracePresenter() {
- // mock the input and outputs of llvm-symbolizer
- symbo := newMockSymbolizer([]mockModule{
- {"testdata/libc.elf", map[uint64][]SourceLocation{
- 0x429c0: {{NewOptStr("math.h"), 51, NewOptStr("__DOUBLE_FLOAT")}, {NewOptStr("atan2.c"), 49, NewOptStr("atan2")}},
- 0x43680: {{NewOptStr("pow.c"), 23, NewOptStr("pow")}},
- 0x44987: {{NewOptStr("memcpy.c"), 76, NewOptStr("memcpy")}},
- }},
- {"testdata/libcrypto.elf", map[uint64][]SourceLocation{
- 0x81000: {{NewOptStr("rsa.c"), 101, NewOptStr("mod_exp")}},
- 0x82000: {{NewOptStr("aes.c"), 17, NewOptStr("gf256_mul")}},
- 0x83000: {{NewOptStr("aes.c"), 560, NewOptStr("gf256_div")}},
- }},
- })
-
- // make a demuxer
- demuxer := NewDemuxer(testBinaries, symbo)
-
- // define a little message that will need to be parsed
- msg := "[131.200] 1234.5678> {{{module:1:libc.so:elf:4fcb712aa6387724a9f465a32cd8c14b}}}\n" +
- "[131.301] 1234.5678> {{{module:9:libcrypto.so:elf:12ef5c50b3ed3599c07c02d4509311be}}}\n" +
- "[131.301] 1234.5678> {{{module:3:libmissing.no:elf:deadbeef}}}\n" +
- "[131.402] 1234.5678> {{{mmap:0x12345000:0xcf6bc:load:1:rx:0x0}}}\n" +
- "[131.503] 1234.5678> {{{mmap:0x23456000:0x83c80:load:09:rx:0x80000}}}\n" +
- "[131.503] 1234.5678> {{{mmap:0x34567000:0x1000:load:3:rx:0x0}}}\n" +
- "[131.604] 1234.5678> Backtrace:\n" +
- "[131.604] 1234.5678> {{{bt:0:0x12388680}}}\n" +
- "[131.604] 1234.5678> {{{bt:1:0x23457000}}}\n" +
- "[131.604] 1234.5678> {{{bt:2:0x123879c0}}}\n" +
- "[131.705] 1234.5678> {{{bt:3:0x34567042}}}\n"
-
- // start sending InputLines to the demuxer
- ctx := context.Background()
- in := StartParsing(ctx, strings.NewReader(msg))
- // start the demuxer which will cause filters to send output lines to 'out'
- out := demuxer.Start(ctx, in)
-
- Consume(ComposePostProcessors(ctx, out,
- &ContextPresenter{},
- NewBacktracePresenter(os.Stdout, NewBasicPresenter(os.Stdout, false))))
-
- //Output:
- //[131.604] 01234.05678> [[[ELF module #0x1 "libc.so" BuildID=4fcb712aa6387724a9f465a32cd8c14b 0x12345000]]]
- //[131.604] 01234.05678> [[[ELF module #0x3 "libmissing.no" BuildID=deadbeef 0x34567000]]]
- //[131.604] 01234.05678> [[[ELF module #0x9 "libcrypto.so" BuildID=12ef5c50b3ed3599c07c02d4509311be 0x23456000]]]
- //[131.604] 01234.05678> Backtrace:
- //[131.604] 01234.05678> #0 0x0000000012388680 in pow pow.c:23 <libc.so>+0x43680
- //[131.604] 01234.05678> #1 0x0000000023457000 in mod_exp rsa.c:101 <libcrypto.so>+0x81000
- //[131.604] 01234.05678> #2.1 0x00000000123879c0 in __DOUBLE_FLOAT math.h:51 <libc.so>+0x429c0
- //[131.604] 01234.05678> #2 0x00000000123879c0 in atan2 atan2.c:49 <libc.so>+0x429c0
- //[131.705] 01234.05678> #3 0x0000000034567042 in <libmissing.no>+0x42
-}
-
-func ExampleBadAddr() {
- // mock the input and outputs of llvm-symbolizer
- symbo := newMockSymbolizer([]mockModule{
- {"testdata/libc.elf", map[uint64][]SourceLocation{
- 0x429c0: {{EmptyOptStr(), 0, NewOptStr("atan2")}, {NewOptStr("math.h"), 51, NewOptStr("__DOUBLE_FLOAT")}},
- 0x43680: {{NewOptStr("pow.c"), 67, EmptyOptStr()}},
- 0x44987: {{NewOptStr("memcpy.c"), 76, NewOptStr("memcpy")}},
- }},
- })
-
- // make a demuxer
- demuxer := NewDemuxer(testBinaries, symbo)
-
- // define a little message that will need to be parsed
- msg := "[131.200] 1234.5678> {{{module:1:libc.so:elf:4fcb712aa6387724a9f465a32cd8c14b}}}\n" +
- "[131.402] 1234.5678> {{{mmap:0x12345000:0xcf6bc:load:1:rx:0x0}}}\n" +
- "[131.402] 1234.5678> {{{mmap:0x12345000:0xcf6bc:load:01:rx:0x0}}}\n" +
- "[131.604] 1234.5678> {{{pc:0x123879ff}}}\n" +
- "[131.605] 1234.5678> {{{pc:0x123879c0}}}\n" +
- "[131.606] 1234.5678> {{{pc:0x12388680}}}\n"
-
- // start sending InputLines to the demuxer
- ctx := context.Background()
- in := StartParsing(ctx, strings.NewReader(msg))
- // start the demuxer which will cause filters to send output lines to 'out'
- out := demuxer.Start(ctx, in)
-
- Consume(ComposePostProcessors(ctx, out, &ContextPresenter{}, &OptimizeColor{}, NewBasicPresenter(os.Stdout, true)))
-
- //Output:
- //[131.604] 01234.05678> [[[ELF module #0x1 "libc.so" BuildID=4fcb712aa6387724a9f465a32cd8c14b 0x12345000]]]
- //[131.604] 01234.05678> <libc.so>+0x429ff
- //[131.605] 01234.05678> atan2 at <libc.so>+0x429c0
- //[131.606] 01234.05678> pow.c:67
-}
diff --git a/debug/symbolize/filter.go b/debug/symbolize/filter.go
deleted file mode 100644
index 07918df..0000000
--- a/debug/symbolize/filter.go
+++ /dev/null
@@ -1,293 +0,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 symbolize
-
-import (
- "context"
- "fmt"
-
- "go.fuchsia.dev/tools/lib/logger"
-)
-
-// TODO (jakehehrlich): LineSource is now a part of the public interface. This is needed to
-// allow for the proper construction of triggers since triggers need to know
-// where a triggering element came from. Right now this is just an empty
-// interface. It would be nice if the user could do soemthing other than cast this.
-type LineSource interface{}
-
-type Process uint64
-
-type DummySource struct{}
-
-type LineHeader interface {
- Present() string
-}
-
-type logHeader struct {
- time float64
- process uint64
- thread uint64
-}
-
-func (l logHeader) Present() string {
- return fmt.Sprintf("[%.3f] %05d.%05d>", l.time, l.process, l.thread)
-}
-
-type sysLogHeader struct {
- time float64
- process uint64
- thread uint64
- tags string
- typ string
-}
-
-func (s sysLogHeader) Present() string {
- return fmt.Sprintf("[%012.6f][%d][%d][%s] %s:", s.time, s.process, s.thread, s.tags, s.typ)
-}
-
-type LogLine struct {
- lineno uint64
- header LineHeader
- source LineSource
-}
-
-type InputLine struct {
- LogLine
- msg string
-}
-
-// Later this will be more general.
-type Segment struct {
- Mod uint64 `json:"mod"`
- Vaddr uint64 `json:"vaddr"`
- Size uint64 `json:"size"`
- Flags string `json:"flags"`
- ModRelAddr uint64 `json:"modRelAddr"`
-}
-
-type addressInfo struct {
- locs []SourceLocation
- mod Module
- seg Segment
- addr uint64
-}
-
-type Module struct {
- Name string `json:"name"`
- Build string `json:"build"`
- Id uint64 `json:"id"`
-}
-
-type OutputLine struct {
- LogLine
- line []Node
-}
-
-type missingObjError struct {
- name string
- buildid string
- err error
-}
-
-func (m *missingObjError) Error() string {
- return fmt.Sprintf("could not find file for module %s with build ID %s: %v", m.name, m.buildid, m.err)
-}
-
-type Repository interface {
- GetBuildObject(buildID string) (string, error)
-}
-
-// Filter represents the state needed to process a log.
-type Filter struct {
- // handles for llvm-symbolizer
- symbolizer Symbolizer
- // Symbolizer context
- symContext mappingStore
- modules map[uint64]Module
- modNamesByBuildID map[string]string
- // Symbolizer repository
- repo Repository
-}
-
-// TODO (jakehehrlich): Consider making FindInfoForAddress private.
-
-// FindInfoForAddress takes a process an in memory address and converts it to a source location.
-func (s *Filter) findInfoForAddress(vaddr uint64) (addressInfo, error) {
- info := addressInfo{addr: vaddr}
- // Sometimes zero will be requested as an address. We should not warn in this case.
- if vaddr == 0 {
- return info, nil
- }
- seg := s.symContext.find(vaddr)
- if seg == nil {
- return info, fmt.Errorf("could not find segment that covers 0x%x", vaddr)
- }
- info.seg = *seg
- if mod, ok := s.modules[info.seg.Mod]; ok {
- info.mod = mod
- } else {
- return info, fmt.Errorf("could not find module for 0x%x", vaddr)
- }
- modRelAddr := vaddr - seg.Vaddr + seg.ModRelAddr
- mod, ok := s.modules[seg.Mod]
- if !ok {
- return info, fmt.Errorf("could not find module with module ID %d", seg.Mod)
- }
- modPath, err := s.repo.GetBuildObject(mod.Build)
- if err != nil {
- out := &missingObjError{mod.Name, mod.Build, err}
- return info, out
- }
- result := <-s.symbolizer.FindSrcLoc(modPath, mod.Build, modRelAddr)
- if result.Err != nil {
- return info, fmt.Errorf("in module %s with build ID %s: %v", mod.Name, mod.Build, result.Err)
- }
- info.locs = result.Locs
- return info, nil
-}
-
-// NewFilter creates a new filter
-func NewFilter(repo Repository, symbo Symbolizer) *Filter {
- return &Filter{
- modules: make(map[uint64]Module),
- modNamesByBuildID: make(map[string]string),
- repo: repo,
- symbolizer: symbo,
- }
-}
-
-// Reset resets the filter so that it can work for a new process
-func (s *Filter) reset() {
- s.modules = make(map[uint64]Module)
- s.symContext.clear()
-}
-
-// AddModule updates the filter state to inform it of a new module
-func (s *Filter) addModule(m Module) error {
- var err error
- // TODO(jakehehrlich): Add check to see if two modules with the same
- // build IDs but different names are added and emit a warning if they
- // are. Keep in mind that the notion of "same name" has to be carefully
- // defined so that cases like <VMO#87378=ld.so.l> and <VMO#87393=ld.so.l>
- // do not trigger the warning.
-
- // Keep track of modules by build ID.
- s.modNamesByBuildID[m.Build] = m.Name
- s.modules[m.Id] = m
- return err
-}
-
-// AddSegment updates the filter state to inform it of a new memory mapped location.
-func (s *Filter) addSegment(seg Segment) {
- s.symContext.add(seg)
-}
-
-// Start tells the filter to start consuming input and produce output.
-func (f *Filter) Start(ctx context.Context, input <-chan InputLine) <-chan OutputLine {
- out := make(chan OutputLine)
- parseLine := GetLineParser()
- go func() {
- for {
- select {
- case <-ctx.Done():
- return
- case elem, ok := <-input:
- if !ok {
- return
- }
- var res OutputLine
- if res.line = parseLine(elem.msg); res.line == nil {
- res.line = []Node{&Text{text: elem.msg}}
- }
- // Update AST with source locations.
- for _, token := range res.line {
- token.Accept(&filterVisitor{filter: f, lineno: elem.lineno, ctx: ctx, source: elem.source})
- }
- res.LogLine = elem.LogLine
- out <- res
- }
- }
- }()
- return out
-}
-
-type filterVisitor struct {
- filter *Filter
- lineno uint64
- ctx context.Context
- source LineSource
-}
-
-func (f *filterVisitor) warn(err error) {
- logger.Warningf(f.ctx, "on line %d: %v", f.lineno, err)
-}
-
-func (f *filterVisitor) debug(err error) {
- logger.Debugf(f.ctx, "on line %d: %v", f.lineno, err)
-}
-
-func (f *filterVisitor) VisitBt(elem *BacktraceElement) {
- info, err := f.filter.findInfoForAddress(elem.vaddr)
- if err != nil {
- // Don't be noisy about missing objects.
- if _, ok := err.(*missingObjError); ok {
- f.debug(err)
- } else {
- f.warn(err)
- }
- }
- elem.info = info
-}
-
-func (f *filterVisitor) VisitPc(elem *PCElement) {
- info, err := f.filter.findInfoForAddress(elem.vaddr)
- if err != nil {
- // Don't be noisy about missing objects.
- if _, ok := err.(*missingObjError); !ok {
- f.warn(err)
- }
- }
- elem.info = info
-}
-
-func (f *filterVisitor) VisitColor(group *ColorCode) {
-
-}
-
-func (f *filterVisitor) VisitText(_ *Text) {
- // This must be implemented in order to meet the interface but it has no effect.
- // This visitor is supposed to do all of the non-parsing parts of constructing the AST.
- // There is nothing to do for Text however.
-}
-
-func (f *filterVisitor) VisitDump(elem *DumpfileElement) {
- // Defensive copies must be made here for two reasons:
- // 1) We don't want the trigger handler to change internal state
- // 2) The trigger handler is allowed to store/process the context
- // at a later date and we might have modified either of these
- // in the interim.
- segs := f.filter.symContext.GetSegments()
- mods := []Module{}
- for _, mod := range f.filter.modules {
- mods = append(mods, mod)
- }
- elem.context = &TriggerContext{Source: f.source, Mods: mods, Segs: segs}
-}
-
-func (f *filterVisitor) VisitReset(elem *ResetElement) {
- // TODO: Check if Reset had an effect and output that a pid reuse occured.
- f.filter.reset()
-}
-
-func (f *filterVisitor) VisitModule(elem *ModuleElement) {
- err := f.filter.addModule(elem.mod)
- if err != nil {
- f.warn(err)
- }
-}
-
-func (f *filterVisitor) VisitMapping(elem *MappingElement) {
- f.filter.addSegment(elem.seg)
-}
diff --git a/debug/symbolize/filter_test.go b/debug/symbolize/filter_test.go
deleted file mode 100644
index cb24770..0000000
--- a/debug/symbolize/filter_test.go
+++ /dev/null
@@ -1,238 +0,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 symbolize
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "reflect"
- "testing"
-)
-
-type mockModule struct {
- name string
- addr2line map[uint64][]SourceLocation
-}
-
-type mockSymbolizer struct {
- modules map[string]mockModule
-}
-
-func newMockSymbolizer(modules []mockModule) Symbolizer {
- var out mockSymbolizer
- out.modules = make(map[string]mockModule)
- for _, mod := range modules {
- out.modules[mod.name] = mod
- }
- return &out
-}
-
-func (s *mockSymbolizer) FindSrcLoc(file, build string, modRelAddr uint64) <-chan LLVMSymbolizeResult {
- out := make(chan LLVMSymbolizeResult, 1)
- if mod, ok := s.modules[file]; ok {
- if locs, ok := mod.addr2line[modRelAddr]; ok {
- out <- LLVMSymbolizeResult{locs, nil}
- } else {
- out <- LLVMSymbolizeResult{nil, fmt.Errorf("0x%x was not a valid address in %s", modRelAddr, file)}
- }
- } else {
- out <- LLVMSymbolizeResult{nil, fmt.Errorf("%s could not be found", file)}
- }
- return out
-}
-
-type symbolizerRepo struct {
- builds map[string]string
-}
-
-func TestBasic(t *testing.T) {
- // mock the input and outputs of llvm-symbolizer
- symbo := newMockSymbolizer([]mockModule{
- {"testdata/libc.elf", map[uint64][]SourceLocation{
- 0x429c0: {{NewOptStr("atan2.c"), 49, NewOptStr("atan2")}, {NewOptStr("math.h"), 51, NewOptStr("__DOUBLE_FLOAT")}},
- 0x43680: {{NewOptStr("pow.c"), 23, NewOptStr("pow")}},
- 0x44987: {{NewOptStr("memcpy.c"), 76, NewOptStr("memcpy")}},
- }},
- {"testdata/libcrypto.elf", map[uint64][]SourceLocation{
- 0x81000: {{NewOptStr("rsa.c"), 101, NewOptStr("mod_exp")}},
- 0x82000: {{NewOptStr("aes.c"), 17, NewOptStr("gf256_mul")}},
- 0x83000: {{NewOptStr("aes.c"), 560, NewOptStr("gf256_div")}},
- }},
- })
- // Get a line parser
- parseLine := GetLineParser()
-
- // make an actual filter using those two mock objects
- filter := NewFilter(testBinaries, symbo)
-
- // parse some example lines
- err := filter.addModule(Module{"libc.elf", "4fcb712aa6387724a9f465a32cd8c14b", 1})
- if err != nil {
- t.Fatal(err)
- }
- err = filter.addModule(Module{"libcrypto.elf", "12ef5c50b3ed3599c07c02d4509311be", 2})
- if err != nil {
- t.Fatal(err)
- }
- filter.addSegment(Segment{1, 0x12345000, 849596, "rx", 0x0})
- filter.addSegment(Segment{2, 0x23456000, 539776, "rx", 0x80000})
- line := parseLine("\033[1m Error at {{{pc:0x123879c0}}}")
- // print out a more precise form
- for _, token := range line {
- token.Accept(&filterVisitor{filter, 1, context.Background(), DummySource{}})
- }
- json, err := GetLineJson(line)
- if err != nil {
- t.Fatalf("json did not parse correctly: %v", err)
- }
-
- expectedJson := []byte(`[
- {"type": "color", "color": 1},
- {"type": "text", "text": " Error at "},
- {"type": "pc", "vaddr": 305691072, "file": "atan2.c",
- "line": 49, "function": "atan2"}
- ]`)
-
- if !EqualJson(json, expectedJson) {
- t.Error("expected", expectedJson, "got", json)
- }
-}
-
-func TestMalformed(t *testing.T) {
- parseLine := GetLineParser()
- // Parse a bad line
- line := parseLine("\033[1m Error at {{{pc:0x123879c0")
- // Malformed lines should still parse
- if line == nil {
- t.Error("expected", "not nil", "got", line)
- }
-}
-
-func EqualJson(a, b []byte) bool {
- var j1, j2 interface{}
- err := json.Unmarshal(a, &j1)
- if err != nil {
- panic(err.Error())
- }
- err = json.Unmarshal(b, &j2)
- if err != nil {
- panic(err.Error())
- }
- return reflect.DeepEqual(j1, j2)
-}
-
-func TestBacktrace(t *testing.T) {
- parseLine := GetLineParser()
- line := parseLine("Error at {{{bt:0:0x12389988}}}")
-
- if line == nil {
- t.Error("got", nil, "expected", "not nil")
- return
- }
-
- // mock the input and outputs of llvm-symbolizer
- symbo := newMockSymbolizer([]mockModule{
- {"testdata/libc.elf", map[uint64][]SourceLocation{
- 0x44988: {{NewOptStr("duff.h"), 64, NewOptStr("duffcopy")}, {NewOptStr("memcpy.c"), 76, NewOptStr("memcpy")}},
- }},
- })
-
- // make an actual filter using those two mock objects
- filter := NewFilter(testBinaries, symbo)
-
- // add some context
- err := filter.addModule(Module{"libc.so", "4fcb712aa6387724a9f465a32cd8c14b", 1})
- if err != nil {
- t.Fatal(err)
- }
- filter.addSegment(Segment{1, 0x12345000, 849596, "rx", 0x0})
- for _, token := range line {
- token.Accept(&filterVisitor{filter, 1, context.Background(), DummySource{}})
- }
-
- json, err := GetLineJson(line)
- if err != nil {
- t.Error("json did not parse correctly", err)
- }
-
- expectedJson := []byte(`[
- {"type": "text", "text": "Error at "},
- {"type": "bt", "vaddr": 305699208, "num": 0, "locs":[
- {"line": 64, "function": "duffcopy", "file": "duff.h"},
- {"line": 76, "function": "memcpy", "file": "memcpy.c"}
- ]}
- ]`)
-
- if !EqualJson(json, expectedJson) {
- t.Error("unexpected json output", "got", string(json), "expected", string(expectedJson))
- }
-}
-
-func TestReset(t *testing.T) {
- parseLine := GetLineParser()
- line := parseLine("{{{reset}}}")
-
- json, err := GetLineJson(line)
- if err != nil {
- t.Error("json did not parse correctly", err)
- }
-
- expectedJson := []byte(`[{"type":"reset"}]`)
- if !EqualJson(json, expectedJson) {
- t.Error("unexpected json output", "got", string(json), "expected", string(expectedJson))
- }
-
- // mock the input and outputs of llvm-symbolizer
- symbo := newMockSymbolizer([]mockModule{
- {"testdata/libc.elf", map[uint64][]SourceLocation{
- 0x44987: {{NewOptStr("memcpy.c"), 76, NewOptStr("memcpy")}},
- }},
- })
-
- // make an actual filter using those two mock objects
- filter := NewFilter(testBinaries, symbo)
-
- // add some context
- mod := Module{"libc.so", "4fcb712aa6387724a9f465a32cd8c14b", 1}
- err = filter.addModule(mod)
- if err != nil {
- t.Fatal(err)
- }
- seg := Segment{1, 0x12345000, 849596, "rx", 0x0}
- filter.addSegment(seg)
-
- addr := uint64(0x12389987)
-
- if info, err := filter.findInfoForAddress(addr); err != nil {
- t.Error("expected", nil, "got", err)
- if len(info.locs) != 1 {
- t.Error("expected", 1, "source location but got", len(info.locs))
- } else {
- loc := SourceLocation{NewOptStr("memcpy.c"), 76, NewOptStr("memcpy")}
- if info.locs[0] != loc {
- t.Error("expected", loc, "got", info.locs[0])
- }
- }
- if info.mod != mod {
- t.Error("expected", mod, "got", info.mod)
- }
- if info.seg != seg {
- t.Error("expected", seg, "got", info.seg)
- }
- if info.addr != addr {
- t.Error("expected", addr, "got", info.addr)
- }
- }
-
- // now forget the context
- for _, token := range line {
- token.Accept(&filterVisitor{filter, 1, context.Background(), DummySource{}})
- }
-
- if _, err := filter.findInfoForAddress(addr); err == nil {
- t.Error("expected non-nil error but got", err)
- }
-}
diff --git a/debug/symbolize/json.go b/debug/symbolize/json.go
deleted file mode 100644
index cf02455..0000000
--- a/debug/symbolize/json.go
+++ /dev/null
@@ -1,150 +0,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 symbolize
-
-import (
- "encoding/json"
-)
-
-type jsonVisitor struct {
- stack []json.RawMessage
-}
-
-func GetLineJson(line []Node) ([]byte, error) {
- var j jsonVisitor
- for _, token := range line {
- token.Accept(&j)
- }
- return j.getJson()
-}
-
-func (j *jsonVisitor) getJson() ([]byte, error) {
- return json.MarshalIndent(j.stack, "", "\t")
-}
-
-func (j *jsonVisitor) VisitBt(elem *BacktraceElement) {
- type loc struct {
- File OptStr `json:"file"`
- Line int `json:"line"`
- Function OptStr `json:"function"`
- }
- var locs []loc
- for _, srcloc := range elem.info.locs {
- locs = append(locs, loc{
- File: srcloc.file,
- Line: srcloc.line,
- Function: srcloc.function,
- })
- }
- msg, _ := json.Marshal(struct {
- Type string `json:"type"`
- Vaddr uint64 `json:"vaddr"`
- Num uint64 `json:"num"`
- Locs []loc `json:"locs"`
- }{
- Type: "bt",
- Vaddr: elem.vaddr,
- Num: elem.num,
- Locs: locs,
- })
- j.stack = append(j.stack, msg)
-}
-
-func (j *jsonVisitor) VisitPc(elem *PCElement) {
- loc := elem.info.locs[0]
- msg, _ := json.Marshal(struct {
- Type string `json:"type"`
- Vaddr uint64 `json:"vaddr"`
- File OptStr `json:"file"`
- Line int `json:"line"`
- Function OptStr `json:"function"`
- }{
- Type: "pc",
- Vaddr: elem.vaddr,
- File: loc.file,
- Line: loc.line,
- Function: loc.function,
- })
- j.stack = append(j.stack, msg)
-}
-
-func (j *jsonVisitor) VisitColor(elem *ColorCode) {
- out := j.stack
- msg, _ := json.Marshal(struct {
- Type string `json:"type"`
- Color uint64 `json:"color"`
- }{
- Type: "color",
- Color: elem.color,
- })
- j.stack = append(out, msg)
-}
-
-func (j *jsonVisitor) VisitText(elem *Text) {
- msg, _ := json.Marshal(struct {
- Type string `json:"type"`
- Text string `json:"text"`
- }{
- Type: "text",
- Text: elem.text,
- })
- j.stack = append(j.stack, msg)
-}
-
-func (j *jsonVisitor) VisitDump(elem *DumpfileElement) {
- msg, _ := json.Marshal(struct {
- Type string `json:"type"`
- SinkType string `json:"sinkType"`
- DumpName string `json:"name"`
- }{
- Type: "dumpfile",
- SinkType: elem.sinkType,
- DumpName: elem.name,
- })
- j.stack = append(j.stack, msg)
-}
-
-// TODO: update this for generalized modules
-func (j *jsonVisitor) VisitModule(elem *ModuleElement) {
- msg, _ := json.Marshal(struct {
- Type string `json:"type"`
- Name string `json:"name"`
- Build string `json:"build"`
- Id uint64 `json:"id"`
- }{
- Type: "module",
- Name: elem.mod.Name,
- Build: elem.mod.Build,
- Id: elem.mod.Id,
- })
- j.stack = append(j.stack, msg)
-}
-
-func (j *jsonVisitor) VisitReset(elem *ResetElement) {
- msg, _ := json.Marshal(map[string]string{
- "type": "reset",
- })
- j.stack = append(j.stack, msg)
-}
-
-// TODO: update this for generalized loads
-func (j *jsonVisitor) VisitMapping(elem *MappingElement) {
- msg, _ := json.Marshal(struct {
- Type string `json:"type"`
- Mod uint64 `json:"mod"`
- Size uint64 `json:"size"`
- Vaddr uint64 `json:"vaddr"`
- Flags string `json:"flags"`
- ModRelAddr uint64 `json:"modRelAddr"`
- }{
- Type: "mmap",
- Mod: elem.seg.Mod,
- Size: elem.seg.Size,
- Vaddr: elem.seg.Vaddr,
- Flags: elem.seg.Flags,
- ModRelAddr: elem.seg.ModRelAddr,
- })
- j.stack = append(j.stack, msg)
-}
diff --git a/debug/symbolize/mapstore.go b/debug/symbolize/mapstore.go
deleted file mode 100644
index d20f832..0000000
--- a/debug/symbolize/mapstore.go
+++ /dev/null
@@ -1,106 +0,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 symbolize
-
-import (
- "sort"
-)
-
-// TODO (jakehehrlich): This whole file should be private.
-
-type byVAddr []Segment
-
-func (b byVAddr) Len() int {
- return len(b)
-}
-
-func (b byVAddr) Swap(i, j int) {
- b[i], b[j] = b[j], b[i]
-}
-
-func (b byVAddr) Less(i, j int) bool {
- return b[i].Vaddr < b[j].Vaddr
-}
-
-// TODO: replace with skip list
-// I call it a "lazy flat map". It stores mapped regions of memory in a way that
-// allows them to be efficentily looked up by an address within them.
-
-// MappingStore quasi-efficently indexes segments by their start address.
-type mappingStore struct {
- segments []Segment
- sorted int
-}
-
-func merge(a []Segment, b []Segment) []Segment {
- out := []Segment{}
- for len(a) != 0 && len(b) != 0 {
- var min Segment
- if a[0].Vaddr < b[0].Vaddr {
- min = a[0]
- a = a[1:]
- } else {
- min = b[0]
- b = b[1:]
- }
- out = append(out, min)
- }
- out = append(out, a...)
- out = append(out, b...)
- return out
-}
-
-// sortAndFind is meant to be called if an element couldn't be found.
-// sortAndFind sorts the unsorted range of segments, finds the missing element
-// and then merges the two sorted ranges so that sortAndFind won't have to be
-// called again for the same element.
-func (m *mappingStore) sortAndFind(vaddr uint64) *Segment {
- sort.Sort(byVAddr(m.segments[m.sorted:]))
- seg := findSegment(m.segments[m.sorted:], vaddr)
- newMods := merge(m.segments[m.sorted:], m.segments[:m.sorted])
- m.segments = newMods
- m.sorted = len(newMods)
- return seg
-}
-
-// findSegment finds a segment in sorted slice of segments.
-func findSegment(sorted []Segment, vaddr uint64) *Segment {
- idx := sort.Search(len(sorted), func(i int) bool {
- seg := sorted[i]
- return seg.Vaddr+seg.Size >= vaddr
- })
- if idx < len(sorted) && sorted[idx].Vaddr <= vaddr {
- return &sorted[idx]
- }
- return nil
-}
-
-// Find first trys to find the desired segment in the sorted segment. If the segment
-// can't be found we consult the unsorted part and update the structure.
-func (m *mappingStore) find(vaddr uint64) *Segment {
- out := findSegment(m.segments[:m.sorted], vaddr)
- if out == nil {
- out = m.sortAndFind(vaddr)
- }
- return out
-}
-
-// Add adds a segment to the segment.
-func (m *mappingStore) add(seg Segment) {
- m.segments = append(m.segments, seg)
-}
-
-// Clear clears the mapping store of all previous information
-func (m *mappingStore) clear() {
- m.segments = nil
- m.sorted = 0
-}
-
-// GetSegments returns a new slice containing all the segments
-func (m *mappingStore) GetSegments() []Segment {
- out := make([]Segment, len(m.segments))
- copy(out, m.segments)
- return out
-}
diff --git a/debug/symbolize/mock_elf.go b/debug/symbolize/mock_elf.go
deleted file mode 100644
index 0cb7975..0000000
--- a/debug/symbolize/mock_elf.go
+++ /dev/null
@@ -1,32 +0,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 symbolize
-
-import (
- "fmt"
-
- "go.fuchsia.dev/tools/debug/elflib"
-)
-
-type mockSource []elflib.BinaryFileRef
-
-// Common binaries used for tests in this package.
-var testBinaries = mockSource{
- {Filepath: "testdata/gobug.elf", BuildID: "5bf6a28a259b95b4f20ffbcea0cbb149"},
- {Filepath: "testdata/libc.elf", BuildID: "4fcb712aa6387724a9f465a32cd8c14b"},
- {Filepath: "testdata/libcrypto.elf", BuildID: "12ef5c50b3ed3599c07c02d4509311be"},
-}
-
-func (m mockSource) GetBuildObject(buildID string) (string, error) {
- for _, file := range testBinaries {
- if file.BuildID == buildID {
- if err := file.Verify(); err != nil {
- return "", err
- }
- return file.Filepath, nil
- }
- }
- return "", fmt.Errorf("could not find file associated with %s", buildID)
-}
diff --git a/debug/symbolize/parser.go b/debug/symbolize/parser.go
deleted file mode 100644
index ca73ab4..0000000
--- a/debug/symbolize/parser.go
+++ /dev/null
@@ -1,180 +0,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 symbolize
-
-import (
- "bufio"
- "context"
- "fmt"
- "io"
- "strconv"
- "strings"
-)
-
-// TODO: Implement a reflection based means of automatically doing these conversions.
-func str2dec(what string) uint64 {
- what = strings.TrimLeft(what, "0")
- if len(what) == 0 {
- return 0
- }
- out, err := strconv.ParseUint(what, 10, 64)
- if err != nil {
- panic(err.Error())
- }
- return out
-}
-
-func str2int(what string) uint64 {
- // str2int assumes that the |what| matches either decRegex or ptrRegex.
- // If we come across a hex value, we don't want to trim the leading zero.
- // If we come across anything else and it still matched one of dec or ptr
- // regexes then we want to trim leading zeros. This lets us match things like
- // "01234" which is important for pids and things like that but also prevents
- // panics like those seen in TC-273.
- if !strings.HasPrefix(what, "0x") {
- what = strings.TrimLeft(what, "0")
- if len(what) == 0 {
- return 0
- }
- }
- out, err := strconv.ParseUint(what, 0, 64)
- if err != nil {
- panic(err.Error())
- }
- return out
-}
-
-func str2float(what string) float64 {
- out, err := strconv.ParseFloat(what, 64)
- if err != nil {
- panic(err.Error())
- }
- return out
-}
-
-const (
- decRegexp = "(?:[[:digit:]]+)"
- ptrRegexp = "(?:0|0x[[:xdigit:]]{1,16})"
- strRegexp = "(?:[^{}:]*)"
- spaceRegexp = `(?:\s*)`
- floatRegexp = `(?:[[:digit:]]+)\.(?:[[:digit:]]+)`
-)
-
-type ParseLineFunc func(msg string) []Node
-
-func GetLineParser() ParseLineFunc {
- var b regexpTokenizerBuilder
- out := []Node{}
- dec := decRegexp
- ptr := ptrRegexp
- str := strRegexp
- num := fmt.Sprintf("(?:%s|%s)", dec, ptr)
- b.addRule(fmt.Sprintf("{{{bt:(%s):(%s)(?::(%s))?}}}", dec, ptr, str), func(args ...string) {
- out = append(out, &BacktraceElement{
- num: str2dec(args[1]),
- vaddr: str2int(args[2]),
- msg: args[3],
- })
- })
- b.addRule(fmt.Sprintf("{{{pc:(%s)}}}", ptr), func(args ...string) {
- out = append(out, &PCElement{vaddr: str2int(args[1])})
- })
- b.addRule(fmt.Sprintf("\033\\[(%s)m", dec), func(args ...string) {
- out = append(out, &ColorCode{color: str2dec(args[1])})
- })
- b.addRule(fmt.Sprintf("{{{dumpfile:(%s):(%s)}}}", str, str), func(args ...string) {
- out = append(out, &DumpfileElement{sinkType: args[1], name: args[2]})
- })
- b.addRule(fmt.Sprintf(`{{{module:(%s):(%s):elf:(%s)}}}`, num, str, str), func(args ...string) {
- out = append(out, &ModuleElement{mod: Module{
- Id: str2int(args[1]),
- Name: args[2],
- Build: args[3],
- }})
- })
- b.addRule(fmt.Sprintf(`{{{mmap:(%s):(%s):load:(%s):(%s):(%s)}}}`, ptr, num, num, str, ptr), func(args ...string) {
- out = append(out, &MappingElement{seg: Segment{
- Vaddr: str2int(args[1]),
- Size: str2int(args[2]),
- Mod: str2int(args[3]),
- Flags: args[4],
- ModRelAddr: str2int(args[5]),
- }})
- })
- b.addRule(`{{{reset}}}`, func(args ...string) {
- out = append(out, &ResetElement{})
- })
- tokenizer, err := b.compile(func(text string) {
- out = append(out, &Text{text: text})
- })
- if err != nil {
- panic(err.Error())
- }
- return func(msg string) []Node {
- out = nil
- tokenizer.run(msg)
- return out
- }
-}
-
-func StartParsing(ctx context.Context, reader io.Reader) <-chan InputLine {
- out := make(chan InputLine)
- // This is not used for demuxing. It is a human readable line number.
- var lineno uint64 = 1
- var b regexpTokenizerBuilder
- space := spaceRegexp
- float := floatRegexp
- dec := decRegexp
- tags := `[^\[\]]*`
- b.addRule(fmt.Sprintf(`\[(%s)\]%s(%s)[.:](%s)>(.*)$`, float, space, dec, dec), func(args ...string) {
- var hdr logHeader
- var line InputLine
- hdr.time = str2float(args[1])
- hdr.process = str2dec(args[2])
- hdr.thread = str2dec(args[3])
- line.header = hdr
- line.source = Process(hdr.process)
- line.lineno = lineno
- line.msg = args[4]
- out <- line
- })
- b.addRule(fmt.Sprintf(`\[(%s)\]\[(%s)\]\[(%s)\]\[(%s)\] ([A-Z]+):?(.*)$`, float, dec, dec, tags), func(args ...string) {
- var hdr sysLogHeader
- var line InputLine
- hdr.time = str2float(args[1])
- hdr.process = str2dec(args[2])
- hdr.thread = str2dec(args[3])
- hdr.tags = args[4]
- hdr.typ = args[5]
- line.header = hdr
- line.source = Process(hdr.process)
- line.lineno = lineno
- line.msg = args[6]
- out <- line
- })
- tokenizer, err := b.compile(func(text string) {
- var line InputLine
- line.source = DummySource{}
- line.msg = text
- line.lineno = lineno
- out <- line
- })
- if err != nil {
- panic(err.Error())
- }
- go func() {
- defer close(out)
- scanner := bufio.NewScanner(reader)
- for ; scanner.Scan(); lineno++ {
- select {
- case <-ctx.Done():
- return
- default:
- tokenizer.run(scanner.Text())
- }
- }
- }()
- return out
-}
diff --git a/debug/symbolize/pipeline.go b/debug/symbolize/pipeline.go
deleted file mode 100644
index 8b6bd9f..0000000
--- a/debug/symbolize/pipeline.go
+++ /dev/null
@@ -1,127 +0,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 symbolize
-
-import (
- "context"
-)
-
-// PostProcessor is the type of modifications to a stream of OutputLines
-type PostProcessor interface {
- Process(OutputLine, chan<- OutputLine)
-}
-
-// PreProcessor is the type of modifications to a stream of InputLines
-type PreProcessor interface {
- Process(InputLine, chan<- InputLine)
-}
-
-// Processor is the type of transformations from OutputLines to InputLines
-type Processor interface {
- Process(InputLine, chan<- OutputLine)
-}
-
-type postProcessorNode struct {
- postProc PostProcessor
-}
-
-func (p postProcessorNode) run(ctx context.Context, input <-chan OutputLine) <-chan OutputLine {
- out := make(chan OutputLine)
- go func() {
- defer func() {
- close(out)
- }()
- for {
- select {
- case <-ctx.Done():
- return
- case elem, ok := <-input:
- if !ok {
- return
- }
- p.postProc.Process(elem, out)
- }
- }
- }()
- return out
-}
-
-type preProcessorNode struct {
- preProc PreProcessor
-}
-
-func (p preProcessorNode) run(ctx context.Context, input <-chan InputLine) <-chan InputLine {
- out := make(chan InputLine)
- go func() {
- defer func() {
- close(out)
- }()
- for {
- select {
- case <-ctx.Done():
- return
- case elem, ok := <-input:
- if !ok {
- return
- }
- p.preProc.Process(elem, out)
- }
- }
- }()
- return out
-}
-
-type processorNode struct {
- proc Processor
-}
-
-func (p processorNode) run(ctx context.Context, input <-chan InputLine) <-chan OutputLine {
- out := make(chan OutputLine)
- go func() {
- defer func() {
- close(out)
- }()
- for {
- select {
- case <-ctx.Done():
- return
- case elem, ok := <-input:
- if !ok {
- return
- }
- p.proc.Process(elem, out)
- }
- }
- }()
- return out
-}
-
-// Consume eats and forgets all input from a channel. This is useful for keeping a goroutine alive
-// until a pipeline has finished processing to completion.
-func Consume(input <-chan OutputLine) {
- for range input {
- }
-}
-
-// ComposePreProcessors takes several PreProcessors and runs them in sequence
-func ComposePreProcessors(ctx context.Context, input <-chan InputLine, pres ...PreProcessor) <-chan InputLine {
- for _, pre := range pres {
- input = preProcessorNode{pre}.run(ctx, input)
- }
- return input
-}
-
-// ComposePostProcessors takes several PostProcessors and runs them in sequence
-func ComposePostProcessors(ctx context.Context, input <-chan OutputLine, posts ...PostProcessor) <-chan OutputLine {
- for _, post := range posts {
- input = postProcessorNode{post}.run(ctx, input)
- }
- return input
-}
-
-// Compose takes a single Processor and causes it to start transforming Input into Output
-func ComposeProcessors(ctx context.Context, input <-chan InputLine, proc Processor) <-chan OutputLine {
- return processorNode{proc}.run(ctx, input)
-}
diff --git a/debug/symbolize/presenter.go b/debug/symbolize/presenter.go
deleted file mode 100644
index 3d23a93..0000000
--- a/debug/symbolize/presenter.go
+++ /dev/null
@@ -1,303 +0,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 symbolize
-
-import (
- "fmt"
- "io"
- "sort"
- "unicode"
-)
-
-// BacktracePresenter intercepts backtrace elements on their own line and
-// presents them in text. Inlines are output as separate lines.
-// A PostProcessor is taken as an input to synchronously compose another
-// PostProcessor
-type BacktracePresenter struct {
- out io.Writer
- next PostProcessor
-}
-
-// NewBacktracePresenter constructs a BacktracePresenter.
-func NewBacktracePresenter(out io.Writer, next PostProcessor) *BacktracePresenter {
- return &BacktracePresenter{
- out: out,
- next: next,
- }
-}
-
-func printBacktrace(out io.Writer, hdr LineHeader, frame uint64, msg string, info addressInfo) {
- modRelAddr := info.addr - info.seg.Vaddr + info.seg.ModRelAddr
- var hdrString string
- if hdr != nil {
- hdrString = hdr.Present()
- }
- if len(info.locs) == 0 {
- fmt.Fprintf(out, "%s #%-4d %#016x in <%s>+%#x %s\n", hdrString, frame, info.addr, info.mod.Name, modRelAddr, msg)
- return
- }
- for i, loc := range info.locs {
- i = len(info.locs) - i - 1
- fmt.Fprintf(out, "%s ", hdrString)
- var frameStr string
- if i == 0 {
- frameStr = fmt.Sprintf("#%d", frame)
- } else {
- frameStr = fmt.Sprintf("#%d.%d", frame, i)
- }
- fmt.Fprintf(out, "%-5s", frameStr)
- fmt.Fprintf(out, " %#016x", info.addr)
- if !loc.function.IsEmpty() {
- fmt.Fprintf(out, " in %v", loc.function)
- }
- if !loc.file.IsEmpty() {
- fmt.Fprintf(out, " %s:%d", loc.file, loc.line)
- }
- fmt.Fprintf(out, " <%s>+%#x", info.mod.Name, modRelAddr)
- if msg != "" {
- fmt.Fprintf(out, " %s", msg)
- }
- fmt.Fprintf(out, "\n")
- }
-}
-
-func isSpace(s string) bool {
- for _, r := range s {
- if !unicode.IsSpace(r) {
- return false
- }
- }
- return true
-}
-
-func (b *BacktracePresenter) Process(line OutputLine, out chan<- OutputLine) {
- if len(line.line) == 1 {
- if bt, ok := line.line[0].(*BacktraceElement); ok {
- printBacktrace(b.out, line.header, bt.num, bt.msg, bt.info)
- // Don't process a backtrace we've already output.
- return
- }
- }
- if len(line.line) == 2 {
- // Note that we're going to discard the text in front.
- if txt, ok := line.line[0].(*Text); ok && isSpace(txt.text) {
- if bt, ok := line.line[1].(*BacktraceElement); ok {
- printBacktrace(b.out, line.header, bt.num, bt.msg, bt.info)
- // Don't process a backtrace we've already output.
- return
- }
- }
- }
- b.next.Process(line, out)
-}
-
-func printLine(line LogLine, fmtStr string, args ...interface{}) OutputLine {
- node := Text{text: fmt.Sprintf(fmtStr, args...)}
- return OutputLine{LogLine: line, line: []Node{&node}}
-}
-
-// Because apparently this is the world we live in, I have to write my own
-// min/max function.
-func min(x, y uint64) uint64 {
- if x < y {
- return x
- }
- return y
-}
-
-type dsoInfo struct {
- id uint64
- name string
- build string
- addr *uint64
-}
-
-type ContextPresenter map[LineSource]map[uint64]dsoInfo
-
-func (c ContextPresenter) Process(line OutputLine, out chan<- OutputLine) {
- if _, ok := c[line.source]; !ok {
- c[line.source] = make(map[uint64]dsoInfo)
- }
- info := c[line.source]
- blank := true
- skip := false
- for _, token := range line.line {
- switch t := token.(type) {
- case *ResetElement:
- skip = true
- delete(c, line.source)
- break
- case *ModuleElement:
- skip = true
- if _, ok := info[t.mod.Id]; !ok {
- info[t.mod.Id] = dsoInfo{id: t.mod.Id, name: t.mod.Name, build: t.mod.Build}
- }
- break
- case *MappingElement:
- skip = true
- dInfo, ok := info[t.seg.Mod]
- if !ok {
- // We might be missing the module because a non-context element was interleaved.
- // It could also be missing but that isn't this function's job to point out.
- out <- printLine(line.LogLine, " [[[ELF seg #%#x %#x]]]", t.seg.Mod, t.seg.Vaddr-t.seg.ModRelAddr)
- break
- }
- if dInfo.addr == nil {
- dInfo.addr = &t.seg.Vaddr
- } else {
- newAddr := min(*dInfo.addr, t.seg.Vaddr-t.seg.ModRelAddr)
- dInfo.addr = &newAddr
- }
- info[t.seg.Mod] = dInfo
- break
- default:
- // Save this token for output later
- if t, ok := token.(*Text); !ok || !isSpace(t.text) {
- blank = false
- }
- }
- }
- if !skip || !blank {
- // Output all contextual information we've thus far consumed.
- sortedInfo := []dsoInfo{}
- for _, dInfo := range info {
- sortedInfo = append(sortedInfo, dInfo)
- }
- sort.Slice(sortedInfo, func(i, j int) bool {
- return sortedInfo[i].id < sortedInfo[j].id
- })
- // TODO(TC-615): We'd really like something more like the following:
- // [[[ELF module #0 "libc.so" BuildID=1234abcdef 0x12345000(r)-0x12356000(rx)-0x12378000(rw)-0x12389000]]]
- // but this requires a fair bit more work to track.
- for _, dInfo := range sortedInfo {
- if dInfo.addr != nil {
- out <- printLine(line.LogLine, " [[[ELF module #%#x \"%s\" BuildID=%s %#x]]]", dInfo.id, dInfo.name, dInfo.build, *dInfo.addr)
- } else {
- out <- printLine(line.LogLine, " [[[ELF module #%#x \"%s\" BuildID=%s]]]", dInfo.id, dInfo.name, dInfo.build)
- }
- }
- // Now so that we don't print this information out again, forget it all.
- delete(c, line.source)
- out <- line
- }
-}
-
-// OptimizeColor attempts to transform output elements to use as few color
-// transisitions as is possible
-type OptimizeColor struct {
-}
-
-func (o *OptimizeColor) Process(line OutputLine, out chan<- OutputLine) {
- // Maintain a current simulated color state
- curColor := uint64(0)
- curBold := false
- // Keep track of the color state at the end of 'out'
- color := uint64(0)
- bold := false
- // The new list of tokens we will output
- newLine := []Node{}
- // Go though each token
- for _, token := range line.line {
- if colorCode, ok := token.(*ColorCode); ok {
- // If we encounter a color update the simulated color state
- if colorCode.color == 1 {
- curBold = true
- } else if colorCode.color == 0 {
- curColor = 0
- curBold = false
- } else {
- curColor = colorCode.color
- }
- } else {
- // If we encounter a non-color token make sure we output
- // colors to handle the transition from the last color to the
- // new color.
- if curColor == 0 && color != 0 {
- color = 0
- bold = false
- newLine = append(newLine, &ColorCode{color: 0})
- } else if curColor != color {
- color = curColor
- newLine = append(newLine, &ColorCode{color: curColor})
- }
- // Make sure to bold the output even if a color 0 code was just output
- if curBold && !bold {
- bold = true
- newLine = append(newLine, &ColorCode{color: 1})
- }
- // Append all non-color nodes
- newLine = append(newLine, token)
- }
- }
- // If the color state isn't already clear, clear it
- if color != 0 || bold != false {
- newLine = append(newLine, &ColorCode{color: 0})
- }
- line.line = newLine
- out <- line
-}
-
-// BasicPresenter is a presenter to output very basic uncolored output
-type BasicPresenter struct {
- enableColor bool
- output io.Writer
-}
-
-func NewBasicPresenter(output io.Writer, enableColor bool) *BasicPresenter {
- return &BasicPresenter{output: output, enableColor: enableColor}
-}
-
-func (b *BasicPresenter) printSrcLoc(loc SourceLocation, info addressInfo) {
- modRelAddr := info.addr - info.seg.Vaddr + info.seg.ModRelAddr
- if !loc.function.IsEmpty() {
- fmt.Fprintf(b.output, "%s at ", loc.function)
- }
- if !loc.file.IsEmpty() {
- fmt.Fprintf(b.output, "%s:%d", loc.file, loc.line)
- } else {
- fmt.Fprintf(b.output, "<%s>+0x%x", info.mod.Name, modRelAddr)
- }
-}
-
-func (b *BasicPresenter) Process(res OutputLine, out chan<- OutputLine) {
- if res.header != nil {
- fmt.Fprintf(b.output, "%s", res.header.Present())
- }
- for _, token := range res.line {
- switch node := token.(type) {
- case *BacktraceElement:
- if len(node.info.locs) == 0 {
- b.printSrcLoc(SourceLocation{}, node.info)
- }
- for i, loc := range node.info.locs {
- b.printSrcLoc(loc, node.info)
- if i != len(node.info.locs)-1 {
- fmt.Fprintf(b.output, " inlined from ")
- }
- }
- case *PCElement:
- if len(node.info.locs) > 0 {
- b.printSrcLoc(node.info.locs[0], node.info)
- } else {
- b.printSrcLoc(SourceLocation{}, node.info)
- }
- case *ColorCode:
- if b.enableColor {
- fmt.Fprintf(b.output, "\033[%dm", node.color)
- }
- case *Text:
- fmt.Fprintf(b.output, "%s", node.text)
- case *DumpfileElement:
- fmt.Fprintf(b.output, "{{{dumpfile:%s:%s}}}", node.sinkType, node.name)
- case *ResetElement:
- fmt.Fprintf(b.output, "{{{reset}}}")
- case *ModuleElement:
- fmt.Fprintf(b.output, "{{{module:%s:%s:%d}}}", node.mod.Build, node.mod.Name, node.mod.Id)
- case *MappingElement:
- fmt.Fprintf(b.output, "{{{mmap:0x%x:0x%x:load:%d:%s:0x%x}}}", node.seg.Vaddr, node.seg.Size, node.seg.Mod, node.seg.Flags, node.seg.ModRelAddr)
- }
- }
- fmt.Fprintf(b.output, "\n")
-}
diff --git a/debug/symbolize/regextokenizer.go b/debug/symbolize/regextokenizer.go
deleted file mode 100644
index f94a871..0000000
--- a/debug/symbolize/regextokenizer.go
+++ /dev/null
@@ -1,111 +0,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 symbolize
-
-import (
- "fmt"
- "regexp"
- "strings"
-)
-
-// TODO: Make this whole file private
-
-type actionFunc func(...string)
-
-type regexInfo struct {
- regex *regexp.Regexp // the regex for this rule
- groupCount int // the number of groups in this regex
- index int // the group index of this regex in the master regex
- action actionFunc // the action to execute if this rule succeeds
-}
-
-// RegexpTokenizer allows for the splitting of input into tokens based on a list
-// of regexs a la (f)lex.
-type regexpTokenizer struct {
- regexs []regexInfo
- master *regexp.Regexp
- defaultAction func(string)
-}
-
-type rule struct {
- regexStr string
- action actionFunc
-}
-
-// RegexpTokenizerBuilder is the means by which a RegexpTokenizer can be constructed.
-type regexpTokenizerBuilder struct {
- rules []rule
-}
-
-// TODO: Add a way to infer the automatic conversions that need to happen from
-// a user supplied function's type via reflection.
-// Rule adds a new regex to the builder
-func (r *regexpTokenizerBuilder) addRule(regex string, action actionFunc) {
- r.rules = append(r.rules, rule{regex, action})
-}
-
-// End compiles the list of regular expressions and actions into a RegexpTokenizer
-func (r *regexpTokenizerBuilder) compile(defaultAction func(string)) (*regexpTokenizer, error) {
- out := regexpTokenizer{defaultAction: defaultAction}
- // Start groupIndex at 1 to account for the master regexp
- groupIndex := 1
- regexStrs := []string{}
- for _, rule := range r.rules {
- regex, err := regexp.Compile(rule.regexStr)
- if err != nil {
- return nil, err
- }
- // Add all needed information to an regexInfo for this rule.
- toAdd := regexInfo{regex, len(regex.SubexpNames()), groupIndex, rule.action}
- // Advance the groupIndex by the subgroups of this regex plus the additional group we add for the whole thing.
- groupIndex += toAdd.groupCount
- regexStrs = append(regexStrs, fmt.Sprintf("(%s)", rule.regexStr))
- out.regexs = append(out.regexs, toAdd)
- }
- // Create the master regex
- masterRegexp, err := regexp.Compile(strings.Join(regexStrs, "|"))
- if err != nil {
- return nil, err
- }
- out.master = masterRegexp
- return &out, nil
-}
-
-// Run tokenizes 'input'
-func (r *regexpTokenizer) run(input string) {
- for len(input) > 0 {
- locs := r.master.FindStringSubmatchIndex(input)
- if locs == nil {
- // There are no more matches so parse the rest of the input and return
- r.defaultAction(input)
- return
- }
- // If there is anything before the match we need to pass it to the default case.
- if locs[0] != 0 {
- r.defaultAction(input[:locs[0]])
- }
- // If we have a match however find which regex produced the match
- for _, regex := range r.regexs {
- if locs[2*regex.index] >= 0 {
- groups := []string{}
- for i := 0; i < regex.groupCount; i++ {
- groupBeginIdx := locs[2*(regex.index+i)]
- groupEndIdx := locs[2*(regex.index+i)+1]
- // When a group is optional it may not be included. Check for that.
- if groupBeginIdx == -1 || groupEndIdx == -1 {
- groups = append(groups, "")
- } else {
- groups = append(groups, input[groupBeginIdx:groupEndIdx])
- }
- }
- // Pass the regex's groups to it
- regex.action(groups...)
- break
- }
- }
- // Now we need to advance the input to not cover anything in the match.
- input = input[locs[1]:]
- }
-}
diff --git a/debug/symbolize/regextokenizer_test.go b/debug/symbolize/regextokenizer_test.go
deleted file mode 100644
index 0c6dca2..0000000
--- a/debug/symbolize/regextokenizer_test.go
+++ /dev/null
@@ -1,45 +0,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 symbolize
-
-import (
- "bytes"
- "fmt"
- "testing"
-)
-
-func TestRegexTokenize(t *testing.T) {
- var builder regexpTokenizerBuilder
- var out bytes.Buffer
- builder.addRule("{a+}", func(args ...string) {
- fmt.Fprintf(&out, "a+ case: %v\n", args)
- })
- builder.addRule("{b+}", func(args ...string) {
- fmt.Fprintf(&out, "b+ case: %v\n", args)
- })
- builder.addRule("{(x)(y)(z)}", func(args ...string) {
- fmt.Fprintf(&out, "xyz case: %v\n", args)
- })
- tokenizer, err := builder.compile(func(str string) {
- fmt.Fprintf(&out, "default case: %s\n", str)
- })
- if err != nil {
- t.Fatal(err)
- }
- tokenizer.run("blarg{a}foo{bbbbb}{xyz}{aa}{aa}baz[test]rest")
- expected := `default case: blarg
-a+ case: [{a}]
-default case: foo
-b+ case: [{bbbbb}]
-xyz case: [{xyz} x y z]
-a+ case: [{aa}]
-a+ case: [{aa}]
-default case: baz[test]rest
-`
- actual := string(out.Bytes())
- if expected != string(out.Bytes()) {
- t.Error("expected\n", expected, "\ngot\n", actual)
- }
-}
diff --git a/debug/symbolize/repo.go b/debug/symbolize/repo.go
deleted file mode 100644
index 1f5fcc4..0000000
--- a/debug/symbolize/repo.go
+++ /dev/null
@@ -1,127 +0,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 symbolize
-
-import (
- "fmt"
- "os"
- "path/filepath"
- "sync"
-
- "go.fuchsia.dev/tools/debug/elflib"
-)
-
-// idsSource is a BinaryFileSource parsed from ids.txt
-type IDsTxtRepo struct {
- lock sync.RWMutex
- cached map[string]elflib.BinaryFileRef
- pathToIDs string
- rel bool
-}
-
-func NewIDsTxtRepo(pathToIDs string, rel bool) *IDsTxtRepo {
- return &IDsTxtRepo{
- cached: make(map[string]elflib.BinaryFileRef),
- pathToIDs: pathToIDs,
- rel: rel,
- }
-}
-
-func (i *IDsTxtRepo) getBinaries() ([]elflib.BinaryFileRef, error) {
- file, err := os.Open(i.pathToIDs)
- if err != nil {
- return nil, err
- }
- defer file.Close()
- out, err := elflib.ReadIDsFile(file)
- if err != nil {
- return nil, err
- }
- if i.rel {
- base := filepath.Dir(i.pathToIDs)
- for idx, ref := range out {
- if !filepath.IsAbs(ref.Filepath) {
- out[idx].Filepath = filepath.Join(base, ref.Filepath)
- }
- }
- }
- return out, nil
-}
-
-func (i *IDsTxtRepo) readFromCache(buildID string) (elflib.BinaryFileRef, bool) {
- i.lock.RLock()
- defer i.lock.RUnlock()
- info, ok := i.cached[buildID]
- return info, ok
-}
-
-func (i *IDsTxtRepo) updateCache() error {
- i.lock.Lock()
- defer i.lock.Unlock()
- bins, err := i.getBinaries()
- if err != nil {
- return err
- }
- newCache := make(map[string]elflib.BinaryFileRef)
- // TODO(jakehehrlich): Do this in parallel.
- for _, bin := range bins {
- newCache[bin.BuildID] = bin
- }
- i.cached = newCache
- return nil
-}
-
-func (i *IDsTxtRepo) GetBuildObject(buildID string) (string, error) {
- if file, ok := i.readFromCache(buildID); ok && file.Verify() != nil {
- return file.Filepath, nil
- }
- if err := i.updateCache(); err != nil {
- return "", err
- }
- if file, ok := i.readFromCache(buildID); ok {
- if err := file.Verify(); err != nil {
- return "", err
- }
- return file.Filepath, nil
- }
- return "", fmt.Errorf("could not find file for %s", buildID)
-}
-
-type CompositeRepo struct {
- repos []Repository
-}
-
-// AddRepo adds a repo to be checked that has lower priority than any other
-// previouslly added repo. This operation is not thread safe.
-func (c *CompositeRepo) AddRepo(repo Repository) {
- c.repos = append(c.repos, repo)
-}
-
-func (c *CompositeRepo) GetBuildObject(buildID string) (string, error) {
- for _, repo := range c.repos {
- file, err := repo.GetBuildObject(buildID)
- if err != nil {
- continue
- }
- return file, nil
- }
- return "", fmt.Errorf("could not find file for %s", buildID)
-}
-
-type NewBuildIDRepo string
-
-func (b NewBuildIDRepo) GetBuildObject(buildID string) (string, error) {
- if len(buildID) < 4 {
- return "", fmt.Errorf("build ID must be the hex representation of at least 2 bytes")
- }
- bin := elflib.BinaryFileRef{
- Filepath: filepath.Join(string(b), buildID[:2], buildID[2:]) + ".debug",
- BuildID: buildID,
- }
- if err := bin.Verify(); err != nil {
- return "", err
- }
- return bin.Filepath, nil
-}
diff --git a/debug/symbolize/repo_test.go b/debug/symbolize/repo_test.go
deleted file mode 100644
index 5a45f2a..0000000
--- a/debug/symbolize/repo_test.go
+++ /dev/null
@@ -1,65 +0,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 symbolize
-
-import (
- "bytes"
- "encoding/hex"
- "os"
- "testing"
-
- "go.fuchsia.dev/tools/debug/elflib"
-)
-
-func hexEqual(a []byte, b string) bool {
- bBytes, _ := hex.DecodeString(b)
- return bytes.Equal(a, bBytes)
-}
-
-type gobugMockSource struct{}
-
-func (g *gobugMockSource) GetBinaries() ([]elflib.BinaryFileRef, error) {
- out := []elflib.BinaryFileRef{
- {BuildID: "5bf6a28a259b95b4f20ffbcea0cbb149", Filepath: "testdata/gobug.elf"},
- {BuildID: "4FCB712AA6387724A9F465A3DEADBEEF", Filepath: "testdata/gobug.elf"},
- {BuildID: "DEADBEEFA6387724A9F465A32CD8C14B", Filepath: "testdata/gobug.elf"},
- }
- for _, bin := range out {
- if err := bin.Verify(); err != nil {
- return nil, err
- }
- }
- return out, nil
-}
-
-// The current go toolchain used in Fuchsia has a bug that causes multiple build ids
-// to wind up in go binaries. This tests to make sure we can handle that case and
-// still ensure that our mentioned build id matches any of the build ids in the file.
-func TestGoBug(t *testing.T) {
- source := &gobugMockSource{}
- data, err := os.Open("testdata/gobug.elf")
- if err != nil {
- t.Fatal(err)
- }
- defer data.Close()
- buildids, err := elflib.GetBuildIDs("testdata/gobug.elf", data)
- if err != nil {
- t.Fatal(err)
- }
- if len(buildids) != 3 {
- t.Error("expected", 3, "build IDs but got", len(buildids))
- return
- }
- // Test that we get exactly the build IDs we expect
- bins, err := source.GetBinaries()
- if err != nil {
- t.Fatal(err)
- }
- for i, bin := range bins {
- if !hexEqual(buildids[i], bin.BuildID) {
- t.Error("expected", bin.BuildID, "got", hex.EncodeToString(buildids[i]))
- }
- }
-}
diff --git a/debug/symbolize/symbolizer.go b/debug/symbolize/symbolizer.go
deleted file mode 100644
index 3bcff11..0000000
--- a/debug/symbolize/symbolizer.go
+++ /dev/null
@@ -1,156 +0,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 symbolize
-
-import (
- "bufio"
- "context"
- "fmt"
- "io"
- "os/exec"
- "strconv"
- "strings"
-
- "go.fuchsia.dev/tools/debug/elflib"
- "go.fuchsia.dev/tools/lib/cache"
-)
-
-// Symbolizer is an interface to an object that maps addresses in a bianry to source locations
-type Symbolizer interface {
- FindSrcLoc(file, build string, modRelAddr uint64) <-chan LLVMSymbolizeResult
-}
-
-type llvmSymbolizeArgsKey struct {
- build string
- modRelAddr uint64
-}
-
-type llvmSymboArgs struct {
- file string
- build string
- modRelAddr uint64
- output chan LLVMSymbolizeResult
-}
-
-const maxCacheSize = 128
-
-type LLVMSymbolizeResult struct {
- Locs []SourceLocation
- Err error
-}
-
-type LLVMSymbolizer struct {
- path string
- stdin io.WriteCloser
- stdout io.ReadCloser
- symbolizer *exec.Cmd
- input chan llvmSymboArgs
- cache cache.Cache
-}
-
-func NewLLVMSymbolizer(llvmSymboPath string) *LLVMSymbolizer {
- var out LLVMSymbolizer
- out.path = llvmSymboPath
- out.symbolizer = exec.Command(llvmSymboPath)
- out.input = make(chan llvmSymboArgs)
- out.cache = &cache.LRUCache{Size: maxCacheSize}
- return &out
-}
-
-func unknownStr(str string) OptStr {
- if str == "??" || str == "" {
- return EmptyOptStr()
- }
- return NewOptStr(str)
-}
-
-func (s *LLVMSymbolizer) handle(ctx context.Context) {
- for {
- select {
- case <-ctx.Done():
- return
- case args, ok := <-s.input:
- if !ok {
- return
- }
- // See if we've seen this before and send off the result
- key := llvmSymbolizeArgsKey{args.build, args.modRelAddr}
- if res, ok := s.cache.Get(key); ok {
- args.output <- res.(LLVMSymbolizeResult)
- continue
- }
- if len(strings.TrimSpace(args.file)) == 0 {
- args.output <- LLVMSymbolizeResult{
- nil, fmt.Errorf("Attempt to request code location of unnamed file with build ID %x", args.build)}
- continue
- }
- // Before sending a binary off to llvm-symbolizer, verify the binary
- if err := elflib.NewBinaryFileRef(args.file, args.build).Verify(); err != nil {
- args.output <- LLVMSymbolizeResult{nil, err}
- continue
- }
- // From //zircon/docs/symbolizer_markup.md:
- // In frames after frame zero, this code location identifies a call site.
- // Some emitters may subtract one byte or one instruction length from the
- // actual return address for the call site, with the intent that the address
- // logged can be translated directly to a source location for the call site
- // and not for the apparent return site thereafter (which can be confusing).
- // It‘s recommended that emitters not do this, so that each frame’s code
- // location is the exact return address given to its callee and e.g. could be
- // highlighted in instruction-level disassembly. The symbolizing filter can do
- // the adjustment to the address it translates into a source location. Assuming
- // that a call instruction is longer than one byte on all supported machines,
- // applying the "subtract one byte" adjustment a second time still results in an
- // address somewhere in the call instruction, so a little sloppiness here does
- // no harm.
- fmt.Fprintf(s.stdin, "%s 0x%x\n", args.file, args.modRelAddr-1)
- out := []SourceLocation{}
- scanner := bufio.NewScanner(s.stdout)
- for scanner.Scan() {
- function := scanner.Text()
- if len(function) == 0 {
- break
- }
- good := scanner.Scan()
- if !good {
- panic(fmt.Sprintf("%s output ended too soon", s.path))
- }
- location := scanner.Text()
- parts := strings.SplitN(location, ":", 3)
- if len(parts) < 2 {
- panic(fmt.Sprintf("%s output unrecgonized format", s.path))
- }
- line, _ := strconv.Atoi(parts[1])
- out = append(out, SourceLocation{unknownStr(parts[0]), line, unknownStr(function)})
- }
- outputRes := LLVMSymbolizeResult{out, nil}
- s.cache.Add(key, outputRes)
- args.output <- outputRes
- }
- }
-}
-
-func (s *LLVMSymbolizer) Start(ctx context.Context) error {
- var err error
- if s.stdin, err = s.symbolizer.StdinPipe(); err != nil {
- return err
- }
- if s.stdout, err = s.symbolizer.StdoutPipe(); err != nil {
- return err
- }
- if err = s.symbolizer.Start(); err != nil {
- return err
- }
- go s.handle(ctx)
- return nil
-}
-
-func (s *LLVMSymbolizer) FindSrcLoc(file, build string, modRelAddr uint64) <-chan LLVMSymbolizeResult {
- // Buffer the return chanel so we don't block handle().
- out := make(chan LLVMSymbolizeResult, 1)
- args := llvmSymboArgs{file: file, build: build, modRelAddr: modRelAddr, output: out}
- s.input <- args
- return out
-}
diff --git a/debug/symbolize/testdata/gobug.elf b/debug/symbolize/testdata/gobug.elf
deleted file mode 100644
index 15522ec..0000000
--- a/debug/symbolize/testdata/gobug.elf
+++ /dev/null
Binary files differ
diff --git a/debug/symbolize/testdata/gobug.yaml b/debug/symbolize/testdata/gobug.yaml
deleted file mode 100644
index b751320..0000000
--- a/debug/symbolize/testdata/gobug.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
---- !ELF
-FileHeader:
- Class: ELFCLASS64
- Data: ELFDATA2LSB
- Type: ET_EXEC
- Machine: EM_X86_64
-Sections:
- - Name: .text
- Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC ]
- - Name: .dynsym
- Type: SHT_DYNSYM
- - Name: .dynstr
- Type: SHT_STRTAB
- - Name: .note.gnu.build-id
- Type: SHT_NOTE
- Flags: [ SHF_ALLOC ]
- AddressAlign: 0x0000000000000004
- Content: 040000001000000003000000474E55005bf6a28a259b95b4f20ffbcea0cbb149040000001000000003000000474E55004FCB712AA6387724A9F465A3DEADBEEF040000001000000003000000474E5500DEADBEEFA6387724A9F465A32CD8C14B
-ProgramHeaders:
- - Type: PT_LOAD
- Flags: [ PF_X, PF_R ]
- Sections:
- - Section: .text
- - Type: PT_LOAD
- Flags: [ PF_R ]
- Sections:
- - Section: .dynsym
- - Section: .dynstr
- - Section: .note.gnu.build-id
- - Type: PT_NOTE
- Flags: [ PF_R ]
- Sections:
- - Section: .note.gnu.build-id
diff --git a/debug/symbolize/testdata/libc.elf b/debug/symbolize/testdata/libc.elf
deleted file mode 100644
index 8322c66..0000000
--- a/debug/symbolize/testdata/libc.elf
+++ /dev/null
Binary files differ
diff --git a/debug/symbolize/testdata/libc.yaml b/debug/symbolize/testdata/libc.yaml
deleted file mode 100644
index e33cfd0..0000000
--- a/debug/symbolize/testdata/libc.yaml
+++ /dev/null
@@ -1,45 +0,0 @@
---- !ELF
-FileHeader:
- Class: ELFCLASS64
- Data: ELFDATA2LSB
- Type: ET_EXEC
- Machine: EM_X86_64
-Sections:
- - Name: .text
- Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC ]
- - Name: .dynsym
- Type: SHT_DYNSYM
- - Name: .dynstr
- Type: SHT_STRTAB
- - Name: .note.gnu.build-id
- Type: SHT_NOTE
- Flags: [ SHF_ALLOC ]
- AddressAlign: 0x0000000000000004
- Content: 040000001000000003000000474E55004FCB712AA6387724A9F465A32CD8C14B
-DynamicSymbols:
- Global:
- - Name: atan2
- Type: STT_FUNC
- Section: .text
- - Name: pow
- Type: STT_FUNC
- Section: .text
- - Name: memcpy
- Type: STT_FUNC
- Section: .text
-ProgramHeaders:
- - Type: PT_LOAD
- Flags: [ PF_X, PF_R ]
- Sections:
- - Section: .text
- - Type: PT_LOAD
- Flags: [ PF_R ]
- Sections:
- - Section: .dynsym
- - Section: .dynstr
- - Section: .note.gnu.build-id
- - Type: PT_NOTE
- Flags: [ PF_R ]
- Sections:
- - Section: .note.gnu.build-id
diff --git a/debug/symbolize/testdata/libcrypto.elf b/debug/symbolize/testdata/libcrypto.elf
deleted file mode 100644
index 6278d90..0000000
--- a/debug/symbolize/testdata/libcrypto.elf
+++ /dev/null
Binary files differ
diff --git a/debug/symbolize/testdata/libcrypto.yaml b/debug/symbolize/testdata/libcrypto.yaml
deleted file mode 100644
index 95654ea..0000000
--- a/debug/symbolize/testdata/libcrypto.yaml
+++ /dev/null
@@ -1,45 +0,0 @@
---- !ELF
-FileHeader:
- Class: ELFCLASS64
- Data: ELFDATA2LSB
- Type: ET_EXEC
- Machine: EM_X86_64
-Sections:
- - Name: .text
- Type: SHT_PROGBITS
- Flags: [ SHF_ALLOC ]
- - Name: .dynsym
- Type: SHT_DYNSYM
- - Name: .dynstr
- Type: SHT_STRTAB
- - Name: .note.gnu.build-id
- Type: SHT_NOTE
- Flags: [ SHF_ALLOC ]
- AddressAlign: 0x0000000000000004
- Content: 040000001000000003000000474E550012ef5c50b3ed3599c07c02d4509311be
-DynamicSymbols:
- Global:
- - Name: mod_exp
- Type: STT_FUNC
- Section: .text
- - Name: gf256_mul
- Type: STT_FUNC
- Section: .text
- - Name: gf256_div
- Type: STT_FUNC
- Section: .text
-ProgramHeaders:
- - Type: PT_LOAD
- Flags: [ PF_X, PF_R ]
- Sections:
- - Section: .text
- - Type: PT_LOAD
- Flags: [ PF_R ]
- Sections:
- - Section: .dynsym
- - Section: .dynstr
- - Section: .note.gnu.build-id
- - Type: PT_NOTE
- Flags: [ PF_R ]
- Sections:
- - Section: .note.gnu.build-id
diff --git a/debug/symbolize/triggers.go b/debug/symbolize/triggers.go
deleted file mode 100644
index ab35467..0000000
--- a/debug/symbolize/triggers.go
+++ /dev/null
@@ -1,36 +0,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 symbolize
-
-// TriggerHandler is a visitor for Node types that can trigger actions
-type TriggerContext struct {
- Source LineSource
- Mods []Module
- Segs []Segment
-}
-
-// TriggerTap is a nop on the pipeline that reads trigger information out
-type TriggerTap struct {
- handlers []func(*DumpfileElement)
-}
-
-func NewTriggerTap() *TriggerTap {
- return &TriggerTap{}
-}
-
-func (t *TriggerTap) AddHandler(handler func(*DumpfileElement)) {
- t.handlers = append(t.handlers, handler)
-}
-
-func (t *TriggerTap) Process(line OutputLine, out chan<- OutputLine) {
- for _, node := range line.line {
- if dumpElem, ok := node.(*DumpfileElement); ok {
- for _, handler := range t.handlers {
- handler(dumpElem)
- }
- }
- }
- out <- line
-}
diff --git a/debug/upload_debug_symbols/cmd/gcs_bucket.go b/debug/upload_debug_symbols/cmd/gcs_bucket.go
deleted file mode 100644
index d910d80..0000000
--- a/debug/upload_debug_symbols/cmd/gcs_bucket.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// 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 (
- "context"
- "fmt"
- "io"
- "strings"
-
- "cloud.google.com/go/storage"
-)
-
-// GCSBucket provides access to a cloud storage bucket.
-type GCSBucket struct {
- bkt *storage.BucketHandle
-}
-
-func (bkt *GCSBucket) upload(ctx context.Context, object string, r io.Reader) error {
- wc := bkt.bkt.Object(object).If(storage.Conditions{DoesNotExist: true}).NewWriter(ctx)
- if _, err := io.Copy(wc, r); err != nil {
- return fmt.Errorf("failed to write object %q: %v", object, err)
- }
- // Close completes the write operation and flushes any buffered data.
- if err := wc.Close(); err != nil {
- // Error 412 means the precondition of DoesNotExist doesn't match.
- // It is the expected behavior since we don't want to upload duplicated files.
- if !strings.Contains(err.Error(), "Error 412") {
- return fmt.Errorf("failed in close: %v", err)
- }
- }
- return nil
-}
-
-// newGCSBucket returns a new GCSBucket object for the given bucket name.
-func newGCSBucket(ctx context.Context, name string) (*GCSBucket, error) {
- client, err := storage.NewClient(ctx)
- if err != nil {
- return nil, fmt.Errorf("failed to create client: %v", err)
- }
- bkt := client.Bucket(gcsBucket)
- return &GCSBucket{
- bkt: bkt,
- }, nil
-}
diff --git a/debug/upload_debug_symbols/cmd/job.go b/debug/upload_debug_symbols/cmd/job.go
deleted file mode 100644
index 7886c35..0000000
--- a/debug/upload_debug_symbols/cmd/job.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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 (
- "context"
- "fmt"
- "os"
-
- "go.fuchsia.dev/tools/debug/elflib"
-)
-
-// job is a description of some BinaryFileRef to upload to GCS. The object's name in GCS
-// is formed by concatenating the ref's BuildID with elflib.DebugFileSuffix.
-type job struct {
- // The BinaryFileRef to upload to GCS.
- bfr elflib.BinaryFileRef
-
- // dstBucket is the destination GCS bucket.
- dstBucket string
-
- // gcsPath is the derived destination GCS path.
- gcsPath string
-
- // name is a human-readable display name for this job.
- name string
-}
-
-func newJob(bfr elflib.BinaryFileRef, dstBucket string) job {
- gcsPath := gcsPath(bfr, dstBucket)
- name := name(bfr, gcsPath)
- return job{
- bfr: bfr,
- dstBucket: dstBucket,
- gcsPath: gcsPath,
- name: name,
- }
-}
-
-// Returns the GCS path for a bfr upload.
-func gcsPath(bfr elflib.BinaryFileRef, dstBucket string) string {
- return fmt.Sprintf("gs://%s/%s%s", dstBucket, bfr.BuildID, elflib.DebugFileSuffix)
-}
-
-// Returns a human-readable display name for a bfr upload.
-func name(bfr elflib.BinaryFileRef, gcsPath string) string {
- return fmt.Sprintf("upload %q to %s", bfr.BuildID, gcsPath)
-}
-
-func (j *job) execute(ctx context.Context, bkt *GCSBucket) error {
- object := j.bfr.BuildID + elflib.DebugFileSuffix
- filepath := j.bfr.Filepath
- reader, err := os.Open(filepath)
- if err != nil {
- return fmt.Errorf("failed to open %q: %v", filepath, err)
- }
- return bkt.upload(ctx, object, reader)
-}
diff --git a/debug/upload_debug_symbols/cmd/main.go b/debug/upload_debug_symbols/cmd/main.go
deleted file mode 100644
index 51cff0d..0000000
--- a/debug/upload_debug_symbols/cmd/main.go
+++ /dev/null
@@ -1,202 +0,0 @@
-// 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.
-
-// Uploads binary debug symbols to Google Cloud Storage.
-//
-// Example Usage:
-//
-// $ upload_debug_symbols -j 20 -bucket bucket-name -upload-record /path/to/record /path/to/.build-id
-
-package main
-
-import (
- "context"
- "errors"
- "flag"
- "fmt"
- "io"
- "log"
- "os"
- "strings"
- "sync"
-
- "go.fuchsia.dev/tools/debug/elflib"
-)
-
-const (
- usage = `upload_debug_symbols [flags] [paths..]
-
- Uploads binary debug symbols to Google Cloud Storage.
- `
-
- // The default number of files to upload at once. The storage API returns 4xx errors
- // when we spawn too many go routines at once, and we can't predict the number of
- // symbol files we'll have to upload. 100 is chosen as a sensible default. This can be
- // overriden on the command line.
- defaultConccurrentUploadCount = 100
-)
-
-// Command line flags.
-var (
- // The GCS bucket to upload files to.
- gcsBucket string
-
- // GCS path to record of uploaded files.
- uploadRecord string
-
- // The maximum number of files to upload at once.
- concurrentUploadCount int
-)
-
-func init() {
- flag.Usage = func() {
- fmt.Fprint(os.Stderr, usage)
- flag.PrintDefaults()
- os.Exit(1)
- }
- flag.StringVar(&gcsBucket, "bucket", "", "GCS bucket to upload symbols to")
- flag.StringVar(&uploadRecord, "upload-record", "", "Path to write record of uploaded symbols")
- flag.IntVar(&concurrentUploadCount, "j", defaultConccurrentUploadCount, "Number of concurrent threads to use to upload files")
-}
-
-func main() {
- flag.Parse()
- if flag.NArg() == 0 {
- log.Fatal("expected at least one path to a .build-id directory")
- }
- if gcsBucket == "" {
- log.Fatal("missing -bucket")
- }
- if err := execute(context.Background(), flag.Args()); err != nil {
- log.Fatal(err)
- }
-}
-
-func execute(ctx context.Context, paths []string) error {
- bfrs, err := collectDebugSymbolFiles(paths)
- if err != nil {
- return fmt.Errorf("failed to collect symbol files: %v", err)
- }
- bfrs = filterInvalidDebugSymbolFiles(bfrs)
- jobs, err := queueJobs(bfrs)
- if err != nil {
- return fmt.Errorf("failed to queue jobs: %v", err)
- }
- bkt, err := newGCSBucket(ctx, gcsBucket)
- if err != nil {
- return err
- }
- succeeded, uploadPaths := upload(ctx, bkt, jobs)
- if !succeeded {
- return errors.New("completed with errors")
- }
- if uploadRecord != "" {
- if err = writeUploadRecord(uploadRecord, uploadPaths); err != nil {
- return fmt.Errorf("failed to write record of uploaded symbols: %v", err)
- }
- log.Printf("wrote record of uploaded symbols to %s\n", uploadRecord)
- }
- return nil
-}
-
-// Returns filtered input of BinaryFileRefs, skipping files without .debug_info header or valid build ID.
-func filterInvalidDebugSymbolFiles(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 if err := bfr.Verify(); err != nil {
- log.Printf("WARNING: validation failed for %s: %v, skipping\n", bfr.Filepath, err)
- } else {
- filteredBfrs = append(filteredBfrs, bfr)
- }
- }
- return filteredBfrs
-}
-
-// Creates BinaryFileRefs for all debug symbol files in the directories named in dirs.
-func collectDebugSymbolFiles(dirs []string) ([]elflib.BinaryFileRef, error) {
- var out []elflib.BinaryFileRef
- for _, dir := range dirs {
- refs, err := elflib.WalkBuildIDDir(dir)
- if err != nil {
- return nil, err
- }
- out = append(out, refs...)
- }
- return out, nil
-}
-
-// Returns a read-only channel of jobs to upload each file referenced in bfrs.
-func queueJobs(bfrs []elflib.BinaryFileRef) (<-chan job, error) {
- jobs := make(chan job, len(bfrs))
- for _, bfr := range bfrs {
- jobs <- newJob(bfr, gcsBucket)
- }
- close(jobs)
- return jobs, nil
-}
-
-// Upload executes all of the jobs to upload files from the input channel. Returns true
-// iff all uploads succeeded without error, and a record of all uploads as a string.
-func upload(ctx context.Context, bkt *GCSBucket, jobs <-chan job) (bool, string) {
- errs := make(chan error, concurrentUploadCount)
- defer close(errs)
- uploadPaths := make(chan string, concurrentUploadCount)
- defer close(uploadPaths)
-
- // Spawn workers to execute the uploads.
- workerCount := concurrentUploadCount
- var wg sync.WaitGroup
- wg.Add(workerCount)
- for i := 0; i < workerCount; i++ {
- go worker(ctx, bkt, &wg, jobs, errs, uploadPaths)
- }
-
- // Let the caller know whether any errors were emitted.
- succeeded := true
- go func() {
- for e := range errs {
- succeeded = false
- log.Printf("error: %v", e)
- }
- }()
- // Receive from uploadPaths channel to build upload record.
- var builder strings.Builder
- go func() {
- for uploadPath := range uploadPaths {
- fmt.Fprintf(&builder, "%s\n", uploadPath)
- }
- }()
- wg.Wait()
- return succeeded, builder.String()
-}
-
-// worker processes all jobs on the input channel, emitting any errors on errs.
-func worker(ctx context.Context, bkt *GCSBucket, wg *sync.WaitGroup, jobs <-chan job, errs chan<- error, uploadPaths chan<- string) {
- defer wg.Done()
- for job := range jobs {
- log.Printf("executing %s", job.name)
- err := job.execute(context.Background(), bkt)
- if err != nil {
- errs <- fmt.Errorf("job %s failed: %v", job.name, err)
- } else {
- uploadPaths <- job.gcsPath
- }
- }
-}
-
-// Write upload paths to local file.
-func writeUploadRecord(uploadRecord string, uploadPaths string) error {
- file, err := os.Create(uploadRecord)
- if err != nil {
- return err
- }
- defer file.Close()
- _, err = io.WriteString(file, uploadPaths)
- return err
-}
diff --git a/go.mod b/go.mod
deleted file mode 100644
index d3ea3ed..0000000
--- a/go.mod
+++ /dev/null
@@ -1,14 +0,0 @@
-module go.fuchsia.dev/tools
-
-require (
- cloud.google.com/go v0.44.3
- github.com/google/go-cmp v0.3.1
- github.com/google/subcommands v1.0.1
- github.com/google/uuid v1.1.1
- golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586
- golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
- golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a
- gopkg.in/yaml.v2 v2.2.2
-)
-
-go 1.13
diff --git a/go.sum b/go.sum
deleted file mode 100644
index b42d0ed..0000000
--- a/go.sum
+++ /dev/null
@@ -1,134 +0,0 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
-cloud.google.com/go v0.44.3 h1:0sMegbmn/8uTwpNkB0q9cLEpZ2W5a6kl+wtBQgPWBJQ=
-cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
-github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
-github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/subcommands v1.0.1 h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k=
-github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
-github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
-github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
-github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
-go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
-go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0=
-golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
-golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
-golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
-golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
-google.golang.org/api v0.8.0 h1:VGGbLNyPF7dvYHhcUGYBBGCRDDK0RRJAI6KCvo0CL+E=
-google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
-google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 h1:iKtrH9Y8mcbADOP0YFaEMth7OfuHY9xHOwNj4znpM1A=
-google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
-google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
diff --git a/integration/testsharder/cmd/main.go b/integration/testsharder/cmd/main.go
deleted file mode 100644
index 092718c..0000000
--- a/integration/testsharder/cmd/main.go
+++ /dev/null
@@ -1,102 +0,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 (
- "encoding/json"
- "flag"
- "fmt"
- "log"
- "os"
-
- "go.fuchsia.dev/tools/integration/testsharder"
- "go.fuchsia.dev/tools/lib/command"
-)
-
-var (
- // The path to the Fuchsia build directory root.
- buildDir string
-
- // The filepath to write output to. If unspecified, stdout is used.
- outputFile string
-
- // The mode in which to run the testsharder.
- mode testsharder.Mode = testsharder.Normal
-
- // Tags are keys on which to filter environments, which are labeled.
- tags command.StringsFlag
-
- // The path to the json manifest file containing the tests to mutiply.
- multipliersPath string
-
- // Maximum number of tests per shard.
- maxShardSize int
-)
-
-func usage() {
- fmt.Printf(`testsharder [flags]
-
-Shards tests produced by a build.
-For more information on the modes in which the testsharder may be run, see
-See https://go.fuchsia.dev/tools/+/master/testsharder/mode.go.
-`)
-}
-
-func init() {
- flag.StringVar(&buildDir, "build-dir", "", "path to the fuchsia build directory root (required)")
- flag.StringVar(&outputFile, "output-file", "", "path to a file which will contain the shards as JSON, default is stdout")
- flag.Var(&mode, "mode", "mode in which to run the testsharder (e.g., normal or restricted).")
- flag.Var(&tags, "tag", "environment tags on which to filter; only the tests that match all tags will be sharded")
- flag.StringVar(&multipliersPath, "multipliers", "", "path to the json manifest containing tests to multiply")
- flag.IntVar(&maxShardSize, "max-shard-size", 0, "maximum number of tests per shard. If <= 0, will be ignored. Otherwise, tests will be placed into more, smaller shards")
- flag.Usage = usage
-}
-
-func main() {
- flag.Parse()
-
- if buildDir == "" {
- log.Fatal("must specify a Fuchsia build output directory")
- }
-
- specs, err := testsharder.LoadTestSpecs(buildDir)
- if err != nil {
- log.Fatal(err)
- }
- platforms, err := testsharder.LoadPlatforms(buildDir)
- if err != nil {
- log.Fatal(err)
- }
-
- // Verify that the produced specs specify valid test environments.
- if err = testsharder.ValidateTestSpecs(specs, platforms); err != nil {
- log.Fatal(err)
- }
-
- // Create shards and write them to an output file if specifed, else stdout.
- shards := testsharder.MakeShards(specs, mode, tags)
- if multipliersPath != "" {
- multipliers, err := testsharder.LoadTestModifiers(multipliersPath)
- if err != nil {
- log.Fatal(err)
- }
- shards = testsharder.MultiplyShards(shards, multipliers)
- }
- shards = testsharder.WithMaxSize(shards, maxShardSize)
- f := os.Stdout
- if outputFile != "" {
- var err error
- f, err = os.Create(outputFile)
- if err != nil {
- log.Fatalf("unable to create %s: %v", outputFile, err)
- }
- defer f.Close()
- }
-
- encoder := json.NewEncoder(f)
- encoder.SetIndent("", " ")
- if err := encoder.Encode(&shards); err != nil {
- log.Fatal("failed to encode shards: ", err)
- }
-}
diff --git a/integration/testsharder/doc.go b/integration/testsharder/doc.go
deleted file mode 100644
index d9a8b45..0000000
--- a/integration/testsharder/doc.go
+++ /dev/null
@@ -1,16 +0,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 testsharder creates shards based on tests and specified environments.
-//
-// Test authors in the Fuchsia source will specify `environments`in GN within
-// the packages they define their tests. These specifications will be printed
-// to disk in JSON-form during a build.
-//
-// This package is concerned with reading in those specifications, validating
-// that they correspond to valid test environments supported by the
-// infrastructure, and ultimately sharding the associated tests along the lines
-// of those environments.
-
-package testsharder
diff --git a/integration/testsharder/environment.go b/integration/testsharder/environment.go
deleted file mode 100644
index 8b8b6b1..0000000
--- a/integration/testsharder/environment.go
+++ /dev/null
@@ -1,105 +0,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 testsharder
-
-import (
- "encoding/json"
- "io/ioutil"
- "path/filepath"
- "strings"
-
- "go.fuchsia.dev/tools/build/api"
-)
-
-// Environment describes the full environment a test requires.
-// The GN environments specified by test authors in the Fuchsia source
-// correspond directly to the Environment struct defined here.
-type Environment struct {
- // Dimensions gives the Swarming dimensions a test wishes to target.
- Dimensions DimensionSet `json:"dimensions"`
-
- // Tags are keys given to an environment on which the testsharder may filter.
- Tags []string `json:"tags,omitempty"`
-
- // ServiceAccount gives a service account to attach to Swarming task.
- ServiceAccount string `json:"service_account,omitempty"`
-}
-
-// Name returns a name calculated from its specfied properties.
-func (env Environment) Name() string {
- tokens := []string{}
- addToken := func(s string) {
- if s != "" {
- // s/-/_, so there is no ambiguity among the tokens
- // making up a name.
- s = strings.Replace(s, "-", "_", -1)
- tokens = append(tokens, s)
- }
- }
-
- addToken(env.Dimensions.DeviceType)
- addToken(env.Dimensions.OS)
- addToken(env.Dimensions.Testbed)
- addToken(env.Dimensions.Pool)
- if env.ServiceAccount != "" {
- addToken(strings.Split(env.ServiceAccount, "@")[0])
- }
- return strings.Join(tokens, "-")
-}
-
-// DimensionSet encapsulates the Swarming dimensions a test wishes to target.
-type DimensionSet struct {
- // DeviceType represents the class of device the test should run on.
- // This is a required field.
- DeviceType string `json:"device_type,omitempty"`
-
- // The OS to run the test on (e.g., "Linux" or "Mac"). Used for host-side testing.
- OS string `json:"os,omitempty"`
-
- // The CPU type that the test is meant to run on.
- CPU string `json:"cpu,omitempty"`
-
- // Testbed denotes a physical test device configuration to run a test on (e.g., multi-device set-ups or devices inside chambers for connectivity testing).
- Testbed string `json:"testbed,omitempty"`
-
- // Pool denotes the swarming pool to run a test in.
- Pool string `json:"pool,omitempty"`
-}
-
-// resolvesTo gives a partial ordering on DimensionSets in which one resolves to
-// another if the former's dimensions are given the latter.
-func (dims DimensionSet) resolvesTo(other DimensionSet) bool {
- if dims.DeviceType != "" && dims.DeviceType != other.DeviceType {
- return false
- }
- if dims.OS != "" && dims.OS != other.OS {
- return false
- }
- if dims.Testbed != "" && dims.Testbed != other.Testbed {
- return false
- }
- if dims.Pool != "" && dims.Pool != other.Pool {
- return false
- }
- return true
-}
-
-// LoadPlatforms loads the list of test platforms specified as a JSON list
-// produced by a build, given the root of the build directory.
-// Note that by "platforms" we mean a specific group of dimension sets which
-// correspond to the currently available test platforms supported by the
-// infrastructure.
-func LoadPlatforms(fuchsiaBuildDir string) ([]DimensionSet, error) {
- platformManifestPath := filepath.Join(fuchsiaBuildDir, build.PlatformManifestName)
- bytes, err := ioutil.ReadFile(platformManifestPath)
- if err != nil {
- return nil, err
- }
- var platforms []DimensionSet
- if err = json.Unmarshal(bytes, &platforms); err != nil {
- return nil, err
- }
- return platforms, err
-}
diff --git a/integration/testsharder/mode.go b/integration/testsharder/mode.go
deleted file mode 100644
index 088cb90..0000000
--- a/integration/testsharder/mode.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// 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 testsharder
-
-import (
- "fmt"
-)
-
-// Mode is a mode in which the testsharder can be run.
-type Mode int
-
-const (
- // Normal is the default mode in which all tests are sharded, except
- // those excluded by differing tags.
- Normal Mode = iota
-
- // Restricted is the mode in which auth-needing tests (i.e., those that
- // specify service accounts) are ignored. This mode is useful for
- // running untrusted code.
- Restricted
-)
-
-// String implements flag.Var.String.
-func (m *Mode) String() string {
- switch *m {
- case Normal:
- return "normal"
- case Restricted:
- return "restricted"
- }
- return ""
-}
-
-// Set implements flag.Var.Set.
-func (m *Mode) Set(s string) error {
- switch s {
- case "normal":
- *m = Normal
- case "restricted":
- *m = Restricted
- default:
- return fmt.Errorf("%s is not a valid mode", s)
- }
- return nil
-}
diff --git a/integration/testsharder/shard.go b/integration/testsharder/shard.go
deleted file mode 100644
index 3fd6a50..0000000
--- a/integration/testsharder/shard.go
+++ /dev/null
@@ -1,190 +0,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 testsharder
-
-import (
- "fmt"
- "sort"
- "strings"
-)
-
-// Shard represents a set of tests with a common execution environment.
-type Shard struct {
- // Name is the identifier for the shard.
- Name string `json:"name"`
-
- // Tests is the set of tests to be executed in this shard.
- Tests []Test `json:"tests"`
-
- // Env is a generalized notion of the execution environment for the shard.
- Env Environment `json:"environment"`
-}
-
-// MakeShards is the core algorithm to this tool. It takes a set of test specs and produces
-// a set of shards which may then be converted into Swarming tasks.
-// A single output Shard will contain only tests that have the same Envs.
-//
-// Environments that do not match all tags will be ignored.
-//
-// In Restricted mode, environments that don't specify a ServiceAccount will be ignored.
-func MakeShards(specs []TestSpec, mode Mode, tags []string) []*Shard {
- // Collect the order of the shards so our shard ordering is deterministic with
- // respect to the input.
- envToSuites := newEnvMap()
- envs := []Environment{}
- for _, spec := range specs {
- for _, env := range spec.Envs {
- if !stringSlicesEq(tags, env.Tags) {
- continue
- }
- if mode == Restricted && env.ServiceAccount != "" {
- continue
- }
-
- // Tags should not differ by ordering.
- sortableTags := sort.StringSlice(tags)
- sortableTags.Sort()
- env.Tags = []string(sortableTags)
-
- specs, ok := envToSuites.get(env)
- if !ok {
- envs = append(envs, env)
- }
- envToSuites.set(env, append(specs, spec))
- }
- }
- shards := make([]*Shard, 0, len(envs))
- for _, env := range envs {
- specs, _ := envToSuites.get(env)
- sort.Slice(specs, func(i, j int) bool {
- return specs[i].Test.Name < specs[j].Test.Name
- })
- var tests []Test
- for _, spec := range specs {
- tests = append(tests, spec.Test)
- }
- shards = append(shards, &Shard{
- Name: env.Name(),
- Tests: tests,
- Env: env,
- })
- }
- return shards
-}
-
-// MultiplyShards appends new shards to shards where each new shard contains one test
-// repeated multiple times according to the specifications in multipliers.
-func MultiplyShards(shards []*Shard, multipliers []TestModifier) []*Shard {
- for _, shard := range shards {
- for _, multiplier := range multipliers {
- for _, test := range shard.Tests {
- if multiplier.Target == test.Name && multiplier.OS == test.OS {
- shards = append(shards, &Shard{
- Name: shard.Name + "-" + normalizeTestName(test.Name),
- Tests: multiplyTest(test, multiplier.TotalRuns),
- Env: shard.Env,
- })
- }
- }
- }
- }
- return shards
-}
-
-func min(a, b int) int {
- if a < b {
- return a
- }
- return b
-}
-
-func divRoundUp(a, b int) int {
- if a%b == 0 {
- return a / b
- }
- return (a / b) + 1
-}
-
-// WithMaxSize returns a list of shards such that each shard contains fewer than maxShardSize tests.
-// If maxShardSize <= 0, just returns its input.
-func WithMaxSize(shards []*Shard, maxShardSize int) []*Shard {
- if maxShardSize <= 0 {
- return shards
- }
- output := make([]*Shard, 0, len(shards))
- for _, shard := range shards {
- numNewShards := divRoundUp(len(shard.Tests), maxShardSize)
- // Evenly distribute the tests between the new shards.
- maxTestsPerNewShard := divRoundUp(len(shard.Tests), numNewShards)
- for i := 0; i < numNewShards; i++ {
- sliceStart := i * maxTestsPerNewShard
- sliceLimit := min((i+1)*maxTestsPerNewShard, len(shard.Tests))
- newName := shard.Name
- if numNewShards > 1 {
- newName = fmt.Sprintf("%s-(%d)", shard.Name, i+1)
- }
- output = append(output, &Shard{
- Name: newName,
- Tests: shard.Tests[sliceStart:sliceLimit],
- Env: shard.Env,
- })
- }
- }
- return output
-}
-
-// Removes leading slashes and replaces all other `/` with `_`. This allows the
-// shard name to appear in filepaths.
-func normalizeTestName(name string) string {
- trimmedName := strings.TrimLeft(name, "/")
- return strings.ReplaceAll(trimmedName, "/", "_")
-}
-
-// Returns a list of Tests containing the same test multiplied by the number of runs.
-func multiplyTest(test Test, runs int) []Test {
- var tests []Test
- for i := 1; i <= runs; i++ {
- testCopy := test
- testCopy.Name = fmt.Sprintf("%s (%d)", test.Name, i)
- tests = append(tests, testCopy)
- }
- return tests
-}
-
-// Abstracts a mapping Environment -> []string, as Environment contains non-comparable
-// members (e.g., string slices), which makes it invalid for a map key.
-type envMap struct {
- m map[string][]TestSpec
-}
-
-func newEnvMap() envMap {
- return envMap{m: make(map[string][]TestSpec)}
-}
-
-func (em envMap) get(e Environment) ([]TestSpec, bool) {
- specs, ok := em.m[fmt.Sprintf("%v", e)]
- return specs, ok
-}
-
-func (em *envMap) set(e Environment, specs []TestSpec) {
- em.m[fmt.Sprintf("%v", e)] = specs
-}
-
-func stringSlicesEq(s []string, t []string) bool {
- if len(s) != len(t) {
- return false
- }
- seen := make(map[string]int)
- for i := range s {
- seen[s[i]]++
- seen[t[i]]--
- }
- for _, v := range seen {
- if v != 0 {
- return false
- }
- }
- return true
-}
diff --git a/integration/testsharder/shard_test.go b/integration/testsharder/shard_test.go
deleted file mode 100644
index 2185c5d..0000000
--- a/integration/testsharder/shard_test.go
+++ /dev/null
@@ -1,315 +0,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 testsharder
-
-import (
- "fmt"
- "reflect"
- "testing"
-)
-
-// Note that just printing a list of shard pointers will print a list of memory addresses,
-// which would make for an unhelpful error message.
-func assertEqual(t *testing.T, expected, actual []*Shard) {
- if !reflect.DeepEqual(expected, actual) {
- errMsg := "\nexpected:\n"
- for _, shard := range expected {
- errMsg += fmt.Sprintf("%v,\n", shard)
- }
- errMsg += "\nactual:\n"
- for _, shard := range actual {
- errMsg += fmt.Sprintf("%v,\n", shard)
- }
- t.Fatalf(errMsg)
- }
-}
-
-func spec(id int, envs ...Environment) TestSpec {
- return TestSpec{
- Test: Test{
- Location: fmt.Sprintf("/path/to/test/%d", id),
- },
- Envs: envs,
- }
-}
-
-func shard(env Environment, ids ...int) *Shard {
- return namedShard(env, env.Name(), ids...)
-}
-
-func namedShard(env Environment, name string, ids ...int) *Shard {
- var tests []Test
- for _, id := range ids {
- tests = append(tests, spec(id, env).Test)
- }
- return &Shard{
- Name: name,
- Tests: tests,
- Env: env,
- }
-}
-
-func TestMakeShards(t *testing.T) {
- env1 := Environment{
- Dimensions: DimensionSet{DeviceType: "QEMU"},
- Tags: []string{},
- }
- env2 := Environment{
- Dimensions: DimensionSet{DeviceType: "NUC"},
- Tags: []string{},
- }
- env3 := Environment{
- Dimensions: DimensionSet{OS: "Linux"},
- Tags: []string{},
- }
- t.Run("environments have nonempty names", func(t *testing.T) {
- envs := []Environment{env1, env2, env3}
- for _, env := range envs {
- if env.Name() == "" {
- t.Fatalf("Environment\n%+v\n has an empty name", env)
- }
- }
- })
-
- t.Run("tests of same environment are grouped", func(t *testing.T) {
- actual := MakeShards(
- []TestSpec{spec(1, env1, env2), spec(2, env1, env3), spec(3, env3)},
- Normal,
- []string{},
- )
- expected := []*Shard{shard(env1, 1, 2), shard(env2, 1), shard(env3, 2, 3)}
- assertEqual(t, expected, actual)
- })
-
- t.Run("there is no deduplication of tests", func(t *testing.T) {
- actual := MakeShards(
- []TestSpec{spec(1, env1), spec(1, env1), spec(1, env1)},
- Normal,
- []string{},
- )
- expected := []*Shard{shard(env1, 1, 1, 1)}
- assertEqual(t, expected, actual)
- })
-
- // Ensure that the order of the shards is the order in which their
- // corresponding environments appear in the input. This is the simplest
- // deterministic order we can produce for the shards.
- t.Run("shards are ordered", func(t *testing.T) {
- actual := MakeShards(
- []TestSpec{spec(1, env2, env3), spec(2, env1), spec(3, env3)},
- Normal,
- []string{},
- )
- expected := []*Shard{shard(env2, 1), shard(env3, 1, 3), shard(env1, 2)}
- assertEqual(t, expected, actual)
- })
-
- t.Run("tags are respected", func(t *testing.T) {
- tagger := func(env Environment, tags ...string) Environment {
- env2 := env
- env2.Tags = tags
- return env2
- }
-
- actual := MakeShards(
- []TestSpec{
- spec(1, tagger(env1, "A")),
- spec(2, tagger(env1, "A", "B", "C")),
- spec(3, tagger(env2, "B", "C")),
- spec(4, tagger(env3, "C", "A")),
- spec(5, tagger(env3, "A", "C")),
- },
- Normal,
- []string{"A", "C"},
- )
- expected := []*Shard{
- // "C", "A" and "A", "C" should define the same tags.
- shard(tagger(env3, "A", "C"), 4, 5),
- }
- assertEqual(t, expected, actual)
- })
-
- t.Run("different service accounts get different shards", func(t *testing.T) {
- withAcct := func(env Environment, acct string) Environment {
- env2 := env
- env2.ServiceAccount = acct
- return env2
- }
-
- actual := MakeShards(
- []TestSpec{
- spec(1, env1),
- spec(1, withAcct(env1, "acct1")),
- spec(1, withAcct(env1, "acct2")),
- },
- Normal,
- []string{},
- )
- expected := []*Shard{
- shard(env1, 1),
- shard(withAcct(env1, "acct1"), 1),
- shard(withAcct(env1, "acct2"), 1),
- }
- assertEqual(t, expected, actual)
- })
-
- t.Run("restricted mode is respected", func(t *testing.T) {
- withAcct := func(env Environment, acct string) Environment {
- env2 := env
- env2.ServiceAccount = acct
- return env2
- }
-
- actual := MakeShards(
- []TestSpec{
- spec(1, env1),
- spec(2, withAcct(env1, "acct1")),
- spec(3, withAcct(env1, "acct2")),
- },
- Restricted,
- []string{},
- )
- expected := []*Shard{
- shard(env1, 1),
- }
- assertEqual(t, expected, actual)
- })
-}
-
-func TestMultiplyShards(t *testing.T) {
- env1 := Environment{
- Dimensions: DimensionSet{DeviceType: "QEMU"},
- Tags: []string{},
- }
- env2 := Environment{
- Dimensions: DimensionSet{DeviceType: "NUC"},
- Tags: []string{},
- }
- env3 := Environment{
- Dimensions: DimensionSet{OS: "Linux"},
- Tags: []string{},
- }
- makeTest := func(id int, os OS) Test {
- return Test{
- Name: fmt.Sprintf("test%d", id),
- Location: fmt.Sprintf("/path/to/test/%d", id),
- OS: os,
- }
- }
-
- shard := func(env Environment, os OS, ids ...int) *Shard {
- var tests []Test
- for _, id := range ids {
- tests = append(tests, makeTest(id, os))
- }
- return &Shard{
- Name: env.Name(),
- Tests: tests,
- Env: env,
- }
- }
-
- makeTestModifier := func(id int, os OS, runs int) TestModifier {
- return TestModifier{
- Target: fmt.Sprintf("test%d", id),
- OS: os,
- TotalRuns: runs,
- }
- }
-
- multShard := func(env Environment, os OS, id int, runs int) *Shard {
- var tests []Test
- test := makeTest(id, os)
- for i := 1; i <= runs; i++ {
- testCopy := test
- testCopy.Name = fmt.Sprintf("%s (%d)", test.Name, i)
- tests = append(tests, testCopy)
- }
- return &Shard{
- Name: env.Name() + "-" + test.Name,
- Tests: tests,
- Env: env,
- }
- }
-
- t.Run("multiply tests in shards", func(t *testing.T) {
- shards := []*Shard{
- shard(env1, Fuchsia, 1),
- shard(env2, Fuchsia, 1, 2),
- shard(env3, Linux, 3),
- }
- multipliers := []TestModifier{
- makeTestModifier(1, Fuchsia, 2),
- makeTestModifier(3, Linux, 3),
- }
- actual := MultiplyShards(
- shards,
- multipliers,
- )
- expected := append(
- shards,
- multShard(env1, Fuchsia, 1, 2),
- multShard(env2, Fuchsia, 1, 2),
- multShard(env3, Linux, 3, 3),
- )
- assertEqual(t, expected, actual)
- })
-}
-
-func max(a, b int) int {
- if a > b {
- return a
- }
- return b
-}
-
-func TestWithMaxSize(t *testing.T) {
- env1 := Environment{
- Tags: []string{"env1"},
- }
- env2 := Environment{
- Dimensions: DimensionSet{DeviceType: "env2"},
- Tags: []string{"env2"},
- }
- input := []*Shard{namedShard(env1, "env1", 1, 2, 3, 4, 5), namedShard(env2, "env2", 6, 7, 8)}
- t.Run("does nothing if max is 0", func(t *testing.T) {
- assertEqual(t, input, WithMaxSize(input, 0))
- })
- t.Run("does nothing if max is < 0", func(t *testing.T) {
- assertEqual(t, input, WithMaxSize(input, -7))
- })
- assertShardsLessThanSize := func(t *testing.T, actual []*Shard, maxSize int) {
- for _, s := range actual {
- if len(s.Tests) > maxSize {
- t.Errorf("Shard %s has %d tests, expected at most %d", s.Name, len(s.Tests), maxSize)
- }
- }
- }
- t.Run("max is larger greater or equal to all shards", func(t *testing.T) {
- maxSize := max(len(input[0].Tests), len(input[1].Tests))
- actual := WithMaxSize(input, maxSize)
- assertEqual(t, input, actual)
- assertShardsLessThanSize(t, actual, maxSize)
- })
-
- t.Run("applies max", func(t *testing.T) {
- maxSize := 2
- actual := WithMaxSize(input, maxSize)
- assertEqual(t, []*Shard{
- namedShard(env1, "env1-(1)", 1, 2), namedShard(env1, "env1-(2)", 3, 4),
- namedShard(env1, "env1-(3)", 5),
- namedShard(env2, "env2-(1)", 6, 7), namedShard(env2, "env2-(2)", 8)},
- actual)
- assertShardsLessThanSize(t, actual, maxSize)
- })
- t.Run("evenly distributes tests", func(t *testing.T) {
- maxSize := 4
- actual := WithMaxSize(input, maxSize)
- assertEqual(t, []*Shard{
- namedShard(env1, "env1-(1)", 1, 2, 3), namedShard(env1, "env1-(2)", 4, 5),
- namedShard(env2, "env2", 6, 7, 8)},
- actual)
- assertShardsLessThanSize(t, actual, maxSize)
- })
-}
diff --git a/integration/testsharder/test_modifier.go b/integration/testsharder/test_modifier.go
deleted file mode 100644
index 36d8a59..0000000
--- a/integration/testsharder/test_modifier.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// 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 testsharder
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
-)
-
-// TestModifier is the specification for a single test and the number of
-// times it should be run.
-type TestModifier struct {
- // Target is the GN target name of the test.
- Target string `json:"target"`
-
- // OS is the operating system in which this test must be executed; treated as "fuchsia" if not present.
- OS OS `json:"os,omitempty"`
-
- // TotalRuns is the number of times to run the test; treated as 1 if not present.
- TotalRuns int `json:"total_runs,omitempty"`
-}
-
-// LoadTestModifiers loads a set of test modifiers from a json manifest.
-func LoadTestModifiers(manifestPath string) ([]TestModifier, error) {
- bytes, err := ioutil.ReadFile(manifestPath)
- if err != nil {
- return nil, err
- }
- var specs []TestModifier
- if err = json.Unmarshal(bytes, &specs); err != nil {
- return nil, err
- }
-
- for i := range specs {
- if specs[i].Target == "" {
- return nil, fmt.Errorf("A test spec's target must have a non-empty name")
- }
- if specs[i].TotalRuns == 0 {
- specs[i].TotalRuns = 1
- }
- if specs[i].OS == "" {
- specs[i].OS = Fuchsia
- }
- }
- return specs, nil
-}
diff --git a/integration/testsharder/test_modifier_test.go b/integration/testsharder/test_modifier_test.go
deleted file mode 100644
index 59785b1..0000000
--- a/integration/testsharder/test_modifier_test.go
+++ /dev/null
@@ -1,76 +0,0 @@
-// 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 testsharder
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "reflect"
- "sort"
- "testing"
-)
-
-var barTestModifier = TestModifier{
- Target: "//obsidian/lib/bar:bar_tests",
- TotalRuns: 2,
-}
-
-var bazTestModifier = TestModifier{
- Target: "//obsidian/public/lib/baz:baz_host_tests",
- OS: Linux,
-}
-
-func TestLoadTestModifiers(t *testing.T) {
- areEqual := func(a, b []TestModifier) bool {
- stringify := func(modifier TestModifier) string {
- return fmt.Sprintf("%#v", modifier)
- }
- sort := func(list []TestModifier) {
- sort.Slice(list[:], func(i, j int) bool {
- return stringify(list[i]) < stringify(list[j])
- })
- }
- sort(a)
- sort(b)
- return reflect.DeepEqual(a, b)
- }
-
- tmpDir, err := ioutil.TempDir("", "test-spec")
- if err != nil {
- t.Fatalf("failed to create temp dir: %v", err)
- }
- defer os.RemoveAll(tmpDir)
-
- initial := []TestModifier{barTestModifier, bazTestModifier}
-
- modifiersPath := filepath.Join(tmpDir, "test_modifiers.json")
- m, err := os.Create(modifiersPath)
- if err != nil {
- t.Fatal(err)
- }
- defer m.Close()
- if err := json.NewEncoder(m).Encode(&initial); err != nil {
- t.Fatal(err)
- }
-
- actual, err := LoadTestModifiers(modifiersPath)
- if err != nil {
- t.Fatalf("failed to load test modifiers: %v", err)
- }
-
- bazOut := bazTestModifier
- // If TotalRuns is missing, it gets set to default 1.
- bazOut.TotalRuns = 1
- barOut := barTestModifier
- // If OS is missing, it gets set to default Fuchsia.
- barOut.OS = Fuchsia
- expected := []TestModifier{barOut, bazOut}
-
- if !areEqual(expected, actual) {
- t.Fatalf("test modifiers not properly loaded:\nexpected:\n%+v\nactual:\n%+v", expected, actual)
- }
-}
diff --git a/integration/testsharder/test_spec.go b/integration/testsharder/test_spec.go
deleted file mode 100644
index e05a35c..0000000
--- a/integration/testsharder/test_spec.go
+++ /dev/null
@@ -1,164 +0,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 testsharder
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
-
- "go.fuchsia.dev/tools/build/api"
-)
-
-// OS is an operating system that a test may run in.
-type OS string
-
-// Acceptable OS constants.
-const (
- Linux OS = "linux"
- Fuchsia OS = "fuchsia"
- Mac OS = "mac"
-)
-
-// TestSpec is the specification for a single test and the environments it
-// should be executed in.
-type TestSpec struct {
- // Test is the test that this specification is for.
- Test `json:"test"`
-
- // Envs is a set of environments that the test should be executed in.
- Envs []Environment `json:"environments"`
-}
-
-// Test encapsulates details about a particular test.
-type Test struct {
- // Name is the full, GN source-relative target name of the test
- // (e.g., //garnet/bin/foo/tests:foo_tests).
- Name string `json:"name"`
-
- // TODO(joshuaseaton): Remove and replace by `Path`.
- //
- // Location is a unique reference to a test: for example, a filesystem
- // path or a Fuchsia URI.
- Location string `json:"location"`
-
- // Path is the path to the test.
- Path string `json:"path"`
-
- // OS is the operating system in which this test must be executed.
- OS OS `json:"os"`
-
- // Command is the command line to run to execute this test.
- Command []string `json:"command,omitempty"`
-
- // DepsFile is a relative path within the build directory to a file containing a JSON
- // list of the test's runtime dependencies,
- // Currently this field only makes sense for Linux and Mac tests.
- DepsFile string `json:"deps_file,omitempty"`
-
- // Deps is the list of paths to the test's runtime dependencies within the build
- // directory. It is read out of DepsFile.
- Deps []string `json:"deps,omitempty"`
-}
-
-func (spec TestSpec) validateAgainst(platforms []DimensionSet) error {
- if spec.Test.Name == "" {
- return fmt.Errorf("A test spec's test must have a non-empty name")
- }
- if len(spec.Command) == 0 && spec.Test.Path == "" && spec.Test.Location == "" {
- return fmt.Errorf("A test spec's test must have one of a non-empty path, non-empty location, or non-empty command")
- }
- if spec.Test.OS == "" {
- return fmt.Errorf("A test spec's test must have a non-empty OS")
- }
-
- resolvesToOneOf := func(env Environment, platforms []DimensionSet) bool {
- for _, platform := range platforms {
- if env.Dimensions.resolvesTo(platform) {
- return true
- }
- }
- return false
- }
-
- var badEnvs []Environment
- for _, env := range spec.Envs {
- if !resolvesToOneOf(env, platforms) {
- badEnvs = append(badEnvs, env)
- }
- }
- if len(badEnvs) > 0 {
- return fmt.Errorf(
- `the following environments of test\n%+v were malformed
- or did not match any available test platforms:\n%+v`,
- spec.Test, badEnvs)
- }
- return nil
-}
-
-// ValidateTestSpecs validates a list of test specs against a list of test
-// platform dimension sets.
-func ValidateTestSpecs(specs []TestSpec, platforms []DimensionSet) error {
- errMsg := ""
- for _, spec := range specs {
- if err := spec.validateAgainst(platforms); err != nil {
- errMsg += fmt.Sprintf("\n%v", err)
- }
- }
- if errMsg != "" {
- return fmt.Errorf(errMsg)
- }
- return nil
-}
-
-// LoadTestSpecs loads a set of test specifications from a build.
-func LoadTestSpecs(fuchsiaBuildDir string) ([]TestSpec, error) {
- manifestPath := filepath.Join(fuchsiaBuildDir, build.TestSpecManifestName)
- bytes, err := ioutil.ReadFile(manifestPath)
- if err != nil {
- return nil, err
- }
- var specs []TestSpec
- if err = json.Unmarshal(bytes, &specs); err != nil {
- return nil, err
- }
-
- for i := range specs {
- if specs[i].Path == "" {
- specs[i].Path = specs[i].Location
- }
-
- if specs[i].DepsFile == "" {
- continue
- }
- path := filepath.Join(fuchsiaBuildDir, specs[i].DepsFile)
- f, err := os.Open(path)
- if err != nil {
- return nil, err
- }
- if err = json.NewDecoder(f).Decode(&specs[i].Deps); err != nil {
- return nil, err
- }
- specs[i].DepsFile = "" // No longer needed.
- }
- return specs, nil
-}
-
-// LoadTests loads the list of tests from the given path.
-func LoadTests(path string) ([]Test, error) {
- bytes, err := ioutil.ReadFile(path)
- if err != nil {
- return nil, fmt.Errorf("failed to read %q: %v", path, err)
- }
-
- var tests []Test
- if err := json.Unmarshal(bytes, &tests); err != nil {
- return nil, fmt.Errorf("failed to unmarshal %q: %v", path, err)
- }
-
- return tests, nil
-}
diff --git a/integration/testsharder/test_spec_test.go b/integration/testsharder/test_spec_test.go
deleted file mode 100644
index 3d38a0b..0000000
--- a/integration/testsharder/test_spec_test.go
+++ /dev/null
@@ -1,211 +0,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 testsharder
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "reflect"
- "sort"
- "testing"
-
- "go.fuchsia.dev/tools/build/api"
-)
-
-var qemuPlatform = DimensionSet{
- DeviceType: "QEMU",
-}
-
-var nucPlatform = DimensionSet{
- DeviceType: "NUC",
-}
-
-var linuxPlatform = DimensionSet{
- OS: "Linux",
-}
-
-var macPlatform = DimensionSet{
- OS: "Mac",
-}
-
-var qemuEnv = Environment{
- Dimensions: qemuPlatform,
-}
-
-var nucEnv = Environment{
- Dimensions: nucPlatform,
-}
-
-var linuxEnv = Environment{
- Dimensions: linuxPlatform,
-}
-
-var macEnv = Environment{
- Dimensions: macPlatform,
-}
-
-var specFoo1 = TestSpec{
- Test: Test{
- Name: "//obsidian/bin/foo:foo_unittests",
- Path: "/system/test/foo_unittests",
- OS: Fuchsia,
- Command: []string{"/system/test/foo_unittests", "bar", "baz"},
- },
- Envs: []Environment{qemuEnv},
-}
-
-var specFoo2 = TestSpec{
- Test: Test{
- Name: "//obsidian/bin/foo:foo_integration_tests",
- Path: "/system/test/foo_integration_tests",
- OS: Fuchsia,
- },
- Envs: []Environment{qemuEnv, nucEnv},
-}
-
-var specBar = TestSpec{
- Test: Test{
- Name: "//obsidian/lib/bar:bar_tests",
- Path: "/system/test/bar_tests",
- OS: Fuchsia,
- },
- Envs: []Environment{qemuEnv},
-}
-
-var specBaz = TestSpec{
- Test: Test{
- Name: "//obsidian/public/lib/baz:baz_host_tests",
- Path: "/$root_build_dir/baz_host_tests",
- OS: Linux,
- },
- Envs: []Environment{linuxEnv, macEnv},
-}
-
-func TestLoadTestSpecs(t *testing.T) {
- areEqual := func(a, b []TestSpec) bool {
- stringify := func(spec TestSpec) string {
- return fmt.Sprintf("%#v", spec)
- }
- sort := func(list []TestSpec) {
- sort.Slice(list[:], func(i, j int) bool {
- return stringify(list[i]) < stringify(list[j])
- })
- }
- sort(a)
- sort(b)
- return reflect.DeepEqual(a, b)
- }
-
- tmpDir, err := ioutil.TempDir("", "test-spec")
- if err != nil {
- t.Fatalf("failed to create temp dir: %v", err)
- }
- defer os.RemoveAll(tmpDir)
-
- deps := []string{"path/to/first/dep", "path/to/second"}
- depsFilepath := filepath.Join(tmpDir, "deps.json")
- df, err := os.Create(depsFilepath)
- if err != nil {
- t.Fatal(err)
- }
- defer df.Close()
- if err := json.NewEncoder(df).Encode(&deps); err != nil {
- t.Fatalf("failed to create JSON encoder: %v", err)
- }
-
- specBazIn := specBaz
- specBazIn.DepsFile = "deps.json"
- initial := []TestSpec{specBar, specBazIn}
-
- manifest := filepath.Join(tmpDir, build.TestSpecManifestName)
- m, err := os.Create(manifest)
- if err != nil {
- t.Fatal(err)
- }
- defer m.Close()
- if err := json.NewEncoder(m).Encode(&initial); err != nil {
- t.Fatal(err)
- }
-
- actual, err := LoadTestSpecs(tmpDir)
- if err != nil {
- t.Fatalf("failed to load test specs: %v", err)
- }
-
- specBazOut := specBaz
- specBazOut.Deps = deps
- expected := []TestSpec{specBar, specBazOut}
-
- if !areEqual(expected, actual) {
- t.Fatalf("test specs not properly loaded:\nexpected:\n%+v\nactual:\n%+v", expected, actual)
- }
-}
-
-func TestValidateTestSpecs(t *testing.T) {
- noTestNameSpec := TestSpec{
- Test: Test{
- Path: "/system/test/baz_tests",
- OS: Linux,
- },
- Envs: []Environment{qemuEnv},
- }
- noTestPathSpec := TestSpec{
- Test: Test{
- Name: "//obsidian/public/lib/baz:baz_tests",
- OS: Linux,
- },
- Envs: []Environment{qemuEnv},
- }
- noOSSpec := TestSpec{
- Test: Test{
- Name: "//obsidian/bin/foo:foo_unittests",
- Path: "/system/test/foo_unittests",
- },
- }
- badEnvSpec := TestSpec{
- Test: Test{
- Name: "//obsidian/public/lib/baz:baz_tests",
- Path: "/system/test/baz_tests",
- OS: Linux,
- },
- Envs: []Environment{
- Environment{
- Dimensions: DimensionSet{
- DeviceType: "NON-EXISTENT-DEVICE",
- },
- },
- },
- }
- platforms := []DimensionSet{qemuPlatform, nucPlatform}
-
- t.Run("valid specs are validated", func(t *testing.T) {
- validSpecLists := [][]TestSpec{
- {specFoo1}, {specFoo2}, {specBar},
- {specFoo1, specFoo2}, {specFoo1, specBar}, {specFoo2, specBar},
- {specFoo1, specFoo2, specBar},
- }
- for _, list := range validSpecLists {
- if err := ValidateTestSpecs(list, platforms); err != nil {
- t.Fatalf("valid specs marked as invalid: %+v: %v", list, err)
- }
- }
- })
-
- t.Run("invalid specs are invalidated", func(t *testing.T) {
- invalidSpecLists := [][]TestSpec{
- {noOSSpec}, {noTestNameSpec}, {noTestPathSpec}, {badEnvSpec},
- {noTestNameSpec, noTestPathSpec}, {noTestNameSpec, badEnvSpec},
- {noTestPathSpec, badEnvSpec},
- {noTestNameSpec, noTestPathSpec, badEnvSpec},
- }
- for _, list := range invalidSpecLists {
- if err := ValidateTestSpecs(list, platforms); err == nil {
- t.Fatalf("invalid specs marked as valid: %+v", list)
- }
- }
- })
-}
diff --git a/lib/cache/cache.go b/lib/cache/cache.go
deleted file mode 100644
index 6a5ce6f..0000000
--- a/lib/cache/cache.go
+++ /dev/null
@@ -1,115 +0,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 cache provides methods for creating and using a cache.
-package cache
-
-import "container/list"
-
-// A Key may be any value that is comparable.
-type Key interface{}
-
-// Cache is the interface for cache.
-type Cache interface {
- // Adds a value to the cache.
- Add(key Key, value interface{}) interface{}
-
- // Returns key's value from the cache.
- Get(key Key) (interface{}, bool)
-
- // Checks if a key exists in cache.
- Contains(key Key) bool
-
- // Removes a key from the cache.
- Remove(key Key) interface{}
-
- // Returns the number of items in the cache.
- Len() int
-
- // Clears all cache entries.
- Clear()
-}
-
-// LRUCache is a simple LRU cache.
-type LRUCache struct {
- // Size is the maximum number of entries before an item is evicted.
- // Zero means no limit on the number of entries.
- Size uint
-
- ll *list.List
- cache map[interface{}]*list.Element
-}
-
-type entry struct {
- key Key
- value interface{}
-}
-
-// Add adds a value to the cache and updates the "recently used"-ness of the key.
-func (c *LRUCache) Add(key Key, value interface{}) interface{} {
- if c.cache == nil {
- c.cache = make(map[interface{}]*list.Element)
- c.ll = list.New()
- }
- if e, ok := c.cache[key]; ok {
- c.ll.MoveToFront(e)
- value, e.Value.(*entry).value = e.Value.(*entry).value, value
- return value
- }
- e := c.ll.PushFront(&entry{key, value})
- c.cache[key] = e
- if c.Size != 0 && uint(c.ll.Len()) > c.Size {
- v := c.ll.Remove(c.ll.Back())
- delete(c.cache, v.(*entry).key)
- }
- return nil
-}
-
-// Get returns key's value from the cache and updates the "recently used"-ness.
-func (c *LRUCache) Get(key Key) (interface{}, bool) {
- if c.cache == nil {
- return nil, false
- }
- if e, ok := c.cache[key]; ok {
- c.ll.MoveToFront(e)
- return e.Value.(*entry).value, true
- }
- return nil, false
-}
-
-// Contains checks if a key exists in cache without updating the recent-ness.
-func (c *LRUCache) Contains(key Key) bool {
- if c.cache == nil {
- return false
- }
- _, ok := c.cache[key]
- return ok
-}
-
-// Remove removes a key from the cache.
-func (c *LRUCache) Remove(key Key) interface{} {
- if c.cache == nil {
- return nil
- }
- if e, ok := c.cache[key]; ok {
- c.ll.Remove(e)
- delete(c.cache, key)
- return e.Value.(*entry).value
- }
- return nil
-}
-
-// Len returns the number of items in the cache.
-func (c *LRUCache) Len() int {
- if c.cache == nil {
- return 0
- }
- return c.ll.Len()
-}
-
-// Clear clears all cache entries.
-func (c *LRUCache) Clear() {
- c.ll = nil
- c.cache = nil
-}
diff --git a/lib/cache/cache_test.go b/lib/cache/cache_test.go
deleted file mode 100644
index 389a530..0000000
--- a/lib/cache/cache_test.go
+++ /dev/null
@@ -1,51 +0,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 cache
-
-import (
- "fmt"
- "testing"
-)
-
-func init() {
-}
-
-var testCases = []struct {
- key string
- val string
-}{
- {"foo", "aaa"},
- {"bar", "bbb"},
- {"baz", "ccc"},
-}
-
-func TestAdd(t *testing.T) {
- var cache Cache = &LRUCache{}
- for _, tc := range testCases {
- t.Run(fmt.Sprintf("add %s", tc.key), func(t *testing.T) {
- cache.Add(tc.key, tc.val)
- if val, ok := cache.Get(tc.key); !ok {
- t.Fatalf("not found")
- } else if ok && val != tc.val {
- t.Fatalf("got %v; want %v", tc.val, val)
- }
- })
- }
-}
-
-func TestRemove(t *testing.T) {
- var cache Cache = &LRUCache{}
- for _, tc := range testCases {
- cache.Add(tc.key, tc.val)
- }
- for _, tc := range testCases {
- t.Run(fmt.Sprintf("remove %s", tc.key), func(t *testing.T) {
- cache.Remove(tc.key)
- if _, ok := cache.Get(tc.key); ok {
- t.Fatalf("not removed")
- }
- })
- }
-}
diff --git a/lib/color/color.go b/lib/color/color.go
deleted file mode 100644
index 4a5558e..0000000
--- a/lib/color/color.go
+++ /dev/null
@@ -1,164 +0,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 color provides functions for printing in different colors.
-package color
-
-import (
- "fmt"
- "os"
-
- "go.fuchsia.dev/tools/lib/isatty"
-)
-
-// Colorfn is a function type that takes a formatted string and returns it in a certain color.
-type Colorfn func(format string, a ...interface{}) string
-
-const (
- escape = "\033["
- clear = escape + "0m"
-)
-
-// ColorCode represents the int used for coloring formatted strings in a certain color.
-type ColorCode int
-
-// Foreground text colors
-const (
- BlackFg ColorCode = iota + 30
- RedFg
- GreenFg
- YellowFg
- BlueFg
- MagentaFg
- CyanFg
- WhiteFg
- DefaultFg
-)
-
-// Color provides an interface for a color or monochrome type that will be returned by NewColor()
-// based on whether coloring is enabled or not.
-type Color interface {
- Black(format string, a ...interface{}) string
- Red(format string, a ...interface{}) string
- Green(format string, a ...interface{}) string
- Yellow(format string, a ...interface{}) string
- Blue(format string, a ...interface{}) string
- Magenta(format string, a ...interface{}) string
- Cyan(format string, a ...interface{}) string
- White(format string, a ...interface{}) string
- DefaultColor(format string, a ...interface{}) string
- WithColor(code ColorCode, format string, a ...interface{}) string
- Enabled() bool
-}
-
-type color struct{}
-
-func (color) Black(format string, a ...interface{}) string { return colorString(BlackFg, format, a...) }
-func (color) Red(format string, a ...interface{}) string { return colorString(RedFg, format, a...) }
-func (color) Green(format string, a ...interface{}) string { return colorString(GreenFg, format, a...) }
-func (color) Yellow(format string, a ...interface{}) string {
- return colorString(YellowFg, format, a...)
-}
-func (color) Blue(format string, a ...interface{}) string { return colorString(BlueFg, format, a...) }
-func (color) Magenta(format string, a ...interface{}) string {
- return colorString(MagentaFg, format, a...)
-}
-func (color) Cyan(format string, a ...interface{}) string { return colorString(CyanFg, format, a...) }
-func (color) White(format string, a ...interface{}) string { return colorString(WhiteFg, format, a...) }
-func (color) DefaultColor(format string, a ...interface{}) string {
- return colorString(DefaultFg, format, a...)
-}
-func (color) WithColor(code ColorCode, format string, a ...interface{}) string {
- return colorString(code, format, a...)
-}
-func (color) Enabled() bool {
- return true
-}
-
-func colorString(c ColorCode, format string, a ...interface{}) string {
- if c == DefaultFg {
- return fmt.Sprintf(format, a...)
- }
- return fmt.Sprintf("%v%vm%v%v", escape, c, fmt.Sprintf(format, a...), clear)
-}
-
-type monochrome struct{}
-
-func (monochrome) Black(format string, a ...interface{}) string { return fmt.Sprintf(format, a...) }
-func (monochrome) Red(format string, a ...interface{}) string { return fmt.Sprintf(format, a...) }
-func (monochrome) Green(format string, a ...interface{}) string { return fmt.Sprintf(format, a...) }
-func (monochrome) Yellow(format string, a ...interface{}) string { return fmt.Sprintf(format, a...) }
-func (monochrome) Blue(format string, a ...interface{}) string { return fmt.Sprintf(format, a...) }
-func (monochrome) Magenta(format string, a ...interface{}) string { return fmt.Sprintf(format, a...) }
-func (monochrome) Cyan(format string, a ...interface{}) string { return fmt.Sprintf(format, a...) }
-func (monochrome) White(format string, a ...interface{}) string { return fmt.Sprintf(format, a...) }
-func (monochrome) DefaultColor(format string, a ...interface{}) string {
- return fmt.Sprintf(format, a...)
-}
-func (monochrome) WithColor(_ ColorCode, format string, a ...interface{}) string {
- return fmt.Sprintf(format, a...)
-}
-func (monochrome) Enabled() bool {
- return false
-}
-
-// EnableColor represents whether or not to return colored strings.
-type EnableColor int
-
-const (
- ColorNever EnableColor = iota
- ColorAuto
- ColorAlways
-)
-
-func isColorAvailable() bool {
- term := os.Getenv("TERM")
- switch term {
- case "dumb", "":
- return false
- }
- return isatty.IsTerminal()
-}
-
-// NewColor returns a color or monochrome type depending on the value of enableColor.
-// A monochrome type will always return the string in the default color.
-func NewColor(enableColor EnableColor) Color {
- ec := enableColor != ColorNever
- if enableColor == ColorAuto {
- ec = isColorAvailable()
- }
- if ec {
- return color{}
- } else {
- return monochrome{}
- }
-}
-
-// String returns the string value of the EnableColor type.
-func (ec *EnableColor) String() string {
- switch *ec {
- case ColorNever:
- return "never"
- case ColorAuto:
- return "auto"
- case ColorAlways:
- return "always"
- }
- return ""
-}
-
-// Set sets the EnableColor type based on the string value.
-func (ec *EnableColor) Set(s string) error {
- switch s {
- case "never":
- *ec = ColorNever
- case "auto":
- *ec = ColorAuto
- case "always":
- *ec = ColorAlways
- default:
- return fmt.Errorf("%s is not a valid color value", s)
- }
- return nil
-}
diff --git a/lib/color/color_test.go b/lib/color/color_test.go
deleted file mode 100644
index c643d07..0000000
--- a/lib/color/color_test.go
+++ /dev/null
@@ -1,52 +0,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 color
-
-import (
- "fmt"
- "testing"
-)
-
-func TestColors(t *testing.T) {
- c := NewColor(ColorAlways)
- colorFns := []Colorfn{c.Black, c.Red, c.Green, c.Yellow, c.Magenta, c.Cyan, c.White, c.DefaultColor}
- colorCodes := []ColorCode{BlackFg, RedFg, GreenFg, YellowFg, MagentaFg, CyanFg, WhiteFg, DefaultFg}
-
- for i, colorCode := range colorCodes {
- fn := colorFns[i]
- str := fmt.Sprintf("test string: %d", i)
- coloredStr := fn("test string: %d", i)
- withColorStr := c.WithColor(colorCode, "test string: %d", i)
- expectedStr := fmt.Sprintf("%v%vm%v%v", escape, colorCode, str, clear)
- if colorCode == DefaultFg {
- expectedStr = str
- }
- if coloredStr != expectedStr {
- t.Fatalf("Expected string:%v\n, got: %v", expectedStr, coloredStr)
- }
- if withColorStr != expectedStr {
- t.Fatalf("Expected string:%v\n, got: %v", expectedStr, withColorStr)
- }
- }
-}
-
-func TestColorsDisabled(t *testing.T) {
- c := NewColor(ColorNever)
- colorFns := []Colorfn{c.Black, c.Red, c.Green, c.Yellow, c.Magenta, c.Cyan, c.White, c.DefaultColor}
- colorCodes := []ColorCode{BlackFg, RedFg, GreenFg, YellowFg, MagentaFg, CyanFg, WhiteFg, DefaultFg}
-
- for i, colorCode := range colorCodes {
- fn := colorFns[i]
- str := fmt.Sprintf("test string: %d", i)
- coloredStr := fn("test string: %d", i)
- withColorStr := c.WithColor(colorCode, "test string: %d", i)
- if coloredStr != str {
- t.Fatalf("Expected string:%v\n, got: %v", str, coloredStr)
- }
- if withColorStr != str {
- t.Fatalf("Expected string:%v\n, got: %v", str, withColorStr)
- }
- }
-}
diff --git a/lib/command/cancelable.go b/lib/command/cancelable.go
deleted file mode 100644
index 47e10fe..0000000
--- a/lib/command/cancelable.go
+++ /dev/null
@@ -1,75 +0,0 @@
-// 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 command
-
-import (
- "context"
- "flag"
- "log"
-
- "github.com/google/subcommands"
-)
-
-// Disposer is an object that performs tear down. This is used by Cancelable to gracefully
-// terminate a delegate Command before exiting.
-type Disposer interface {
- Dispose()
-}
-
-// Cancelable wraps a subcommands.Command so that it is canceled if its input execution
-// context emits a Done event before execution is finished. If the given Command
-// implements the Disposer interface, Dispose is called before the program exits.
-func Cancelable(sub subcommands.Command) subcommands.Command {
- return &cancelable{sub}
-}
-
-// cancelable wraps a subcommands.Command so that it is canceled if the input execution
-// context emits a Done event before execution is finished. cancelable "masquerades" as
-// the underlying Command. Example Registration:
-//
-// subcommands.Register(command.Cancelable(&OtherSubcommand{}))
-type cancelable struct {
- sub subcommands.Command
-}
-
-// Name forwards to the underlying Command.
-func (cmd *cancelable) Name() string {
- return cmd.sub.Name()
-}
-
-// Usage forwards to the underlying Command.
-func (cmd *cancelable) Usage() string {
- return cmd.sub.Usage()
-}
-
-// Synopsis forwards to the underlying Command.
-func (cmd *cancelable) Synopsis() string {
- return cmd.sub.Synopsis()
-}
-
-// SetFlags forwards to the underlying Command.
-func (cmd *cancelable) SetFlags(f *flag.FlagSet) {
- cmd.sub.SetFlags(f)
-}
-
-// Execute runs the underlying Command in a goroutine. If the input context is canceled
-// before execution finishes, execution is canceled and the context's error is logged.
-func (cmd *cancelable) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
- status := make(chan subcommands.ExitStatus)
- go func() {
- status <- cmd.sub.Execute(ctx, f, args...)
- }()
- select {
- case <-ctx.Done():
- if d, ok := cmd.sub.(Disposer); ok {
- d.Dispose()
- }
- log.Println(ctx.Err())
- return subcommands.ExitFailure
- case s := <-status:
- close(status)
- return s
- }
-}
diff --git a/lib/command/cancelable_test.go b/lib/command/cancelable_test.go
deleted file mode 100644
index 22b14c7..0000000
--- a/lib/command/cancelable_test.go
+++ /dev/null
@@ -1,85 +0,0 @@
-// 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 command_test
-
-import (
- "context"
- "flag"
- "testing"
- "time"
-
- "github.com/google/subcommands"
- "go.fuchsia.dev/tools/lib/command"
-)
-
-func TestCancelableExecute(t *testing.T) {
- tests := []struct {
- // The name of this test case
- name string
-
- // Whether to cancel the execution context early.
- cancelContextEarly bool
-
- // Whether the underlying subcommand is expected to finish
- expectToFinish bool
- }{
- {"when context is canceled early", true, false},
- {"when context is never canceled", false, true},
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- tcmd := &TestCommand{}
- cmd := command.Cancelable(tcmd)
- ctx, cancel := context.WithCancel(context.Background())
- if tt.cancelContextEarly {
- cancel()
- cmd.Execute(ctx, flag.NewFlagSet("test", flag.ContinueOnError))
- } else {
- cmd.Execute(ctx, flag.NewFlagSet("test", flag.ContinueOnError))
- cancel()
- }
-
- if tcmd.DidFinish && !tt.expectToFinish {
- t.Errorf("wanted command to exit early but it finished")
- } else if !tcmd.DidFinish && tt.expectToFinish {
- t.Errorf("wanted command to finish but it exited early")
- }
- })
- }
-}
-
-// TestCancelableDelegation verifies that Cancelable() returns a subcommand.Command that
-// delegates to the input subcommand.Command.
-func TestCancelableDelegation(t *testing.T) {
- expectEq := func(t *testing.T, name, expected, actual string) {
- if expected != actual {
- t.Errorf("wanted %s to be %q but got %q", name, expected, actual)
- }
- }
- cmd := command.Cancelable(&TestCommand{
- name: "test_name",
- usage: "test_usage",
- synopsis: "test_synopsis",
- })
- expectEq(t, "Name", "test_name", cmd.Name())
- expectEq(t, "Usage", "test_usage", cmd.Usage())
- expectEq(t, "Synopsis", "test_synopsis", cmd.Synopsis())
-}
-
-type TestCommand struct {
- name, usage, synopsis string
- DidFinish bool
-}
-
-func (cmd *TestCommand) Name() string { return cmd.name }
-func (cmd *TestCommand) Usage() string { return cmd.usage }
-func (cmd *TestCommand) Synopsis() string { return cmd.synopsis }
-func (cmd *TestCommand) SetFlags(f *flag.FlagSet) {}
-func (cmd *TestCommand) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
- time.Sleep(time.Millisecond)
- cmd.DidFinish = true
- return subcommands.ExitSuccess
-}
diff --git a/lib/command/doc.go b/lib/command/doc.go
deleted file mode 100644
index bc6b9bf..0000000
--- a/lib/command/doc.go
+++ /dev/null
@@ -1,6 +0,0 @@
-// 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 command defines common code for writing Fuchsia command line tools.
-package command
diff --git a/lib/command/flags.go b/lib/command/flags.go
deleted file mode 100644
index 8647927..0000000
--- a/lib/command/flags.go
+++ /dev/null
@@ -1,25 +0,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 command
-
-import (
- "strings"
-)
-
-// StringsFlag implements flag.Value so it may be treated as a flag type.
-type StringsFlag []string
-
-// Set implements flag.Value.Set.
-func (s *StringsFlag) Set(val string) error {
- *s = append(*s, val)
- return nil
-}
-
-// String implements flag.Value.String.
-func (s *StringsFlag) String() string {
- if s == nil {
- return ""
- }
- return strings.Join([]string(*s), ", ")
-}
diff --git a/lib/command/signals.go b/lib/command/signals.go
deleted file mode 100644
index 298517c..0000000
--- a/lib/command/signals.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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 command
-
-import (
- "context"
- "os"
- "os/signal"
-)
-
-// CancelOnSignals returns a Context that emits a Done event when any of the input signals
-// are received, assuming those signals can be handled by the current process.
-func CancelOnSignals(ctx context.Context, sigs ...os.Signal) context.Context {
- ctx, cancel := context.WithCancel(ctx)
- signals := make(chan os.Signal)
- signal.Notify(signals, sigs...)
- go func() {
- select {
- case s := <-signals:
- if s != nil {
- cancel()
- close(signals)
- }
- }
- }()
- return ctx
-}
diff --git a/lib/isatty/isatty.go b/lib/isatty/isatty.go
deleted file mode 100644
index a468ae0..0000000
--- a/lib/isatty/isatty.go
+++ /dev/null
@@ -1,20 +0,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.
-
-// +build linux darwin
-
-package isatty
-
-import (
- "os"
- "syscall"
- "unsafe"
-)
-
-// IsTerminal returns whether the system is in a terminal.
-func IsTerminal() bool {
- var termios syscall.Termios
- _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, os.Stdout.Fd(), ioctlTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
- return err == 0
-}
diff --git a/lib/isatty/isatty_darwin.go b/lib/isatty/isatty_darwin.go
deleted file mode 100644
index e044c21..0000000
--- a/lib/isatty/isatty_darwin.go
+++ /dev/null
@@ -1,11 +0,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.
-
-// +build darwin
-
-package isatty
-
-import "syscall"
-
-const ioctlTermios = syscall.TIOCGETA
diff --git a/lib/isatty/isatty_linux.go b/lib/isatty/isatty_linux.go
deleted file mode 100644
index f510507..0000000
--- a/lib/isatty/isatty_linux.go
+++ /dev/null
@@ -1,11 +0,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.
-
-// +build linux
-
-package isatty
-
-import "syscall"
-
-const ioctlTermios = syscall.TCGETS
diff --git a/lib/logger/logger.go b/lib/logger/logger.go
deleted file mode 100644
index e7ada50..0000000
--- a/lib/logger/logger.go
+++ /dev/null
@@ -1,206 +0,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 logger provides methods for logging with different levels.
-package logger
-
-import (
- "context"
- "fmt"
- "io"
- goLog "log"
- "os"
-
- "go.fuchsia.dev/tools/lib/color"
-)
-
-type globalLoggerKeyType struct{}
-
-// WithLogger returns the context with its logger set as the provided Logger.
-func WithLogger(ctx context.Context, logger *Logger) context.Context {
- return context.WithValue(ctx, globalLoggerKeyType{}, logger)
-}
-
-// Logger represents a specific LogLevel with a specified color and prefix.
-type Logger struct {
- LoggerLevel LogLevel
- goLogger *goLog.Logger
- goErrorLogger *goLog.Logger
- color color.Color
- prefix string
-}
-
-// LogLevel represents different levels for logging depending on the amount of detail wanted.
-type LogLevel int
-
-const (
- NoLogLevel LogLevel = iota
- FatalLevel
- ErrorLevel
- WarningLevel
- InfoLevel
- DebugLevel
- TraceLevel
-)
-
-// String returns the string representation of the LogLevel.
-func (l *LogLevel) String() string {
- switch *l {
- case NoLogLevel:
- return "no"
- case FatalLevel:
- return "fatal"
- case ErrorLevel:
- return "error"
- case WarningLevel:
- return "warning"
- case InfoLevel:
- return "info"
- case DebugLevel:
- return "debug"
- case TraceLevel:
- return "trace"
- }
- return ""
-}
-
-// Set sets the LogLevel based on its string value.
-func (l *LogLevel) Set(s string) error {
- switch s {
- case "fatal":
- *l = FatalLevel
- case "error":
- *l = ErrorLevel
- case "warning":
- *l = WarningLevel
- case "info":
- *l = InfoLevel
- case "debug":
- *l = DebugLevel
- case "trace":
- *l = TraceLevel
- default:
- return fmt.Errorf("%s is not a valid level", s)
- }
- return nil
-}
-
-// NewLogger creates a new logger instance. The loggerLevel variable sets the log level for the logger.
-// The color variable specifies the visual color of displayed log output.
-// The outWriter and errWriter variables set the destination to which non-error and error data will be written.
-// The prefix appears on the same line directly preceding any log data.
-func NewLogger(loggerLevel LogLevel, color color.Color, outWriter, errWriter io.Writer, prefix string) *Logger {
- if outWriter == nil {
- outWriter = os.Stdout
- }
- if errWriter == nil {
- errWriter = os.Stderr
- }
- l := &Logger{
- LoggerLevel: loggerLevel,
- goLogger: goLog.New(outWriter, "", goLog.LstdFlags),
- goErrorLogger: goLog.New(errWriter, "", goLog.LstdFlags),
- color: color,
- prefix: prefix,
- }
- return l
-}
-
-func (l *Logger) log(prefix, format string, a ...interface{}) {
- l.goLogger.Printf("%s%s%s", l.prefix, prefix, fmt.Sprintf(format, a...))
-}
-
-// Logf logs the string based on the loglevel of the string and the LogLevel of the logger.
-func (l *Logger) Logf(loglevel LogLevel, format string, a ...interface{}) {
- switch loglevel {
- case InfoLevel:
- l.Infof(format, a...)
- case DebugLevel:
- l.Debugf(format, a...)
- case TraceLevel:
- l.Tracef(format, a...)
- case WarningLevel:
- l.Warningf(format, a...)
- case ErrorLevel:
- l.Errorf(format, a...)
- case FatalLevel:
- l.Fatalf(format, a...)
- default:
- panic(fmt.Sprintf("Undefined loglevel: %v, log message: %s", loglevel, fmt.Sprintf(format, a...)))
- }
-}
-
-func Logf(ctx context.Context, logLevel LogLevel, format string, a ...interface{}) {
- if v, ok := ctx.Value(globalLoggerKeyType{}).(*Logger); ok && v != nil {
- v.Logf(logLevel, format, a...)
- } else {
- goLog.Printf(format, a...)
- }
-}
-
-// Infof logs the string if the logger is at least InfoLevel.
-func (l *Logger) Infof(format string, a ...interface{}) {
- if l.LoggerLevel >= InfoLevel {
- l.log("", format, a...)
- }
-}
-
-func Infof(ctx context.Context, format string, a ...interface{}) {
- Logf(ctx, InfoLevel, format, a...)
-}
-
-// Debugf logs the string if the logger is at least DebugLevel.
-func (l *Logger) Debugf(format string, a ...interface{}) {
- if l.LoggerLevel >= DebugLevel {
- l.log(l.color.Cyan("DEBUG: "), format, a...)
- }
-}
-
-func Debugf(ctx context.Context, format string, a ...interface{}) {
- Logf(ctx, DebugLevel, format, a...)
-}
-
-// Tracef logs the string if the logger is at least TraceLevel.
-func (l *Logger) Tracef(format string, a ...interface{}) {
- if l.LoggerLevel >= TraceLevel {
- l.log(l.color.Blue("TRACE: "), format, a...)
- }
-}
-
-func Tracef(ctx context.Context, format string, a ...interface{}) {
- Logf(ctx, TraceLevel, format, a...)
-}
-
-// Warningf logs the string if the logger is at least WarningLevel.
-func (l *Logger) Warningf(format string, a ...interface{}) {
- if l.LoggerLevel >= WarningLevel {
- l.log(l.color.Yellow("WARN: "), format, a...)
- }
-}
-
-func Warningf(ctx context.Context, format string, a ...interface{}) {
- Logf(ctx, WarningLevel, format, a...)
-}
-
-// Errorf logs the string if the logger is at least ErrorLevel.
-func (l *Logger) Errorf(format string, a ...interface{}) {
- if l.LoggerLevel >= ErrorLevel {
- l.goErrorLogger.Printf("%s%s%s", l.prefix, l.color.Red("ERROR: "), fmt.Sprintf(format, a...))
- }
-}
-
-func Errorf(ctx context.Context, format string, a ...interface{}) {
- Logf(ctx, ErrorLevel, format, a...)
-}
-
-// Fatalf logs the string if the logger is at least FatalLevel.
-func (l *Logger) Fatalf(format string, a ...interface{}) {
- if l.LoggerLevel >= FatalLevel {
- l.goErrorLogger.Fatalf("%s%s%s", l.prefix, l.color.Red("FATAL: "), fmt.Sprintf(format, a...))
- }
-}
-
-func Fatalf(ctx context.Context, format string, a ...interface{}) {
- Logf(ctx, FatalLevel, format, a...)
-}
diff --git a/lib/logger/logger_test.go b/lib/logger/logger_test.go
deleted file mode 100644
index 60ac5a8..0000000
--- a/lib/logger/logger_test.go
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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 logger
-
-import (
- "context"
- goLog "log"
- "os"
- "testing"
-
- "go.fuchsia.dev/tools/lib/color"
-)
-
-func TestWithContext(t *testing.T) {
- logger := NewLogger(DebugLevel, color.NewColor(color.ColorAuto), os.Stdout, os.Stderr, "")
- ctx := context.Background()
- if v, ok := ctx.Value(globalLoggerKeyType{}).(*Logger); ok || v != nil {
- t.Fatalf("Default context should not have globalLoggerKeyType. Expected: \nnil\n but got: \n%+v ", v)
- }
- ctx = WithLogger(ctx, logger)
- if v, ok := ctx.Value(globalLoggerKeyType{}).(*Logger); !ok || v == nil {
- t.Fatalf("Updated context should have globalLoggerKeyType, but got nil")
- }
-
-}
-
-func TestNewLogger(t *testing.T) {
- prefix := "testprefix "
-
- logger := NewLogger(InfoLevel, color.NewColor(color.ColorAuto), nil, nil, prefix)
- logFlags, errFlags := logger.goLogger.Flags(), logger.goErrorLogger.Flags()
-
- if logFlags != goLog.LstdFlags || errFlags != goLog.LstdFlags {
- t.Fatalf("New loggers should have the proper flags set for both standard and error logging. Expected: \n%+v and %+v\n but got: \n%+v and %+v", goLog.LstdFlags, goLog.LstdFlags, logFlags, errFlags)
- }
-
- logPrefix := logger.prefix
- if logPrefix != prefix {
- t.Fatalf("New loggers should use the specified prefix on creation. Expected: \n%+v\n but got: \n%+v", prefix, logPrefix)
- }
-}
diff --git a/lib/retry/backoff.go b/lib/retry/backoff.go
deleted file mode 100644
index cfc163c..0000000
--- a/lib/retry/backoff.go
+++ /dev/null
@@ -1,92 +0,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 retry
-
-import (
- "time"
-)
-
-// Stop indicates that no more retries should be made.
-const Stop time.Duration = -1
-
-type Backoff interface {
- // Next gets the duration to wait before retrying the operation or |Stop|
- // to indicate that no retries should be made.
- Next() time.Duration
-
- // Reset resets to initial state.
- Reset()
-}
-
-// ZeroBackoff is a fixed policy whose back-off time is always zero, meaning
-// that the operation is retried immediately without waiting.
-type ZeroBackoff struct{}
-
-func (b *ZeroBackoff) Reset() {}
-
-func (b *ZeroBackoff) Next() time.Duration { return 0 }
-
-// ConstantBackoff is a fixed policy that always returns the same backoff delay.
-type ConstantBackoff struct {
- interval time.Duration
-}
-
-func (b *ConstantBackoff) Reset() {}
-
-func (b *ConstantBackoff) Next() time.Duration { return b.interval }
-
-func NewConstantBackoff(d time.Duration) *ConstantBackoff {
- return &ConstantBackoff{interval: d}
-}
-
-type maxTriesBackoff struct {
- backOff Backoff
- maxTries uint64
- numTries uint64
-}
-
-func (b *maxTriesBackoff) Next() time.Duration {
- if b.maxTries > 0 {
- if b.maxTries <= b.numTries {
- return Stop
- }
- b.numTries++
- }
- return b.backOff.Next()
-}
-
-func (b *maxTriesBackoff) Reset() {
- b.numTries = 0
- b.backOff.Reset()
-}
-
-// WithMaxRetries wraps a back-off which stops after |max| retries.
-func WithMaxRetries(b Backoff, max uint64) Backoff {
- return &maxTriesBackoff{backOff: b, maxTries: max}
-}
-
-type maxDurationBackoff struct {
- backOff Backoff
- maxDuration time.Duration
- startTime time.Time
- c clock
-}
-
-func (b *maxDurationBackoff) Next() time.Duration {
- if b.c.Since(b.startTime) < b.maxDuration {
- return b.backOff.Next()
- }
- return Stop
-}
-
-func (b *maxDurationBackoff) Reset() {
- b.startTime = b.c.Now()
- b.backOff.Reset()
-}
-
-// WithMaxDuration wraps a back-off which stops attempting retries after |max|
-// duration.
-func WithMaxDuration(b Backoff, max time.Duration) Backoff {
- return &maxDurationBackoff{backOff: b, maxDuration: max, c: &systemClock{}}
-}
diff --git a/lib/retry/backoff_test.go b/lib/retry/backoff_test.go
deleted file mode 100644
index d55780c..0000000
--- a/lib/retry/backoff_test.go
+++ /dev/null
@@ -1,57 +0,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 retry
-
-import (
- "testing"
- "time"
-)
-
-func TestZeroBackoff(t *testing.T) {
- backoff := ZeroBackoff{}
- backoff.Reset()
- if backoff.Next() != 0 {
- t.Error("invalid interval")
- }
-}
-
-func TestConstantBackoff(t *testing.T) {
- backoff := NewConstantBackoff(time.Second)
- backoff.Reset()
- if backoff.Next() != time.Second {
- t.Error("invalid interval")
- }
-}
-
-func TestMaxTriesBackoff(t *testing.T) {
- backoff := WithMaxRetries(&ZeroBackoff{}, 10)
- backoff.Reset()
- for i := 0; i < 10; i++ {
- if backoff.Next() != 0 {
- t.Error("invalid interval")
- }
- }
- if backoff.Next() != Stop {
- t.Error("did not stop")
- }
-}
-
-func TestMaxDurationBackoff(t *testing.T) {
- c := &fakeClock{t: time.Now()}
- backoff := &maxDurationBackoff{backOff: &ZeroBackoff{}, maxDuration: 10 * time.Second, c: c}
- backoff.Reset()
- if backoff.Next() != 0 {
- t.Error("invalid interval")
- }
-
- c.Tick(9 * time.Second)
- if backoff.Next() != 0 {
- t.Error("invalid interval")
- }
-
- c.Tick(1 * time.Second)
- if backoff.Next() != Stop {
- t.Error("did not stop")
- }
-}
diff --git a/lib/retry/clock.go b/lib/retry/clock.go
deleted file mode 100644
index f062574..0000000
--- a/lib/retry/clock.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// 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 retry
-
-import "time"
-
-type clock interface {
- Now() time.Time
- Since(time.Time) time.Duration
-}
-
-type fakeClock struct {
- t time.Time
-}
-
-func (c fakeClock) Now() time.Time {
- return c.t
-}
-
-func (c fakeClock) Since(startTime time.Time) time.Duration {
- return c.t.Sub(startTime)
-}
-
-func (c *fakeClock) Tick(d time.Duration) {
- c.t = c.t.Add(d)
-}
-
-type systemClock struct{}
-
-func (c systemClock) Now() time.Time {
- return time.Now()
-}
-
-func (c systemClock) Since(startTime time.Time) time.Duration {
- return time.Since(startTime)
-}
diff --git a/lib/retry/retry.go b/lib/retry/retry.go
deleted file mode 100644
index 43e6d2a..0000000
--- a/lib/retry/retry.go
+++ /dev/null
@@ -1,41 +0,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 retry
-
-import (
- "context"
- "time"
-)
-
-// Retry the operation using the provided back-off policy until it succeeds,
-// or the context is cancelled.
-func Retry(ctx context.Context, b Backoff, f func() error, c chan<- error) error {
- var err error
- var next time.Duration
-
- b.Reset()
- for {
- if err = f(); err == nil {
- break
- }
-
- if next = b.Next(); next == Stop {
- return err
- }
-
- timer := time.NewTimer(next)
-
- select {
- case <-ctx.Done():
- timer.Stop()
- return err
- case <-timer.C:
- if c != nil {
- c <- err
- }
- }
- }
-
- return err
-}
diff --git a/lib/retry/retry_test.go b/lib/retry/retry_test.go
deleted file mode 100644
index fef9b7c..0000000
--- a/lib/retry/retry_test.go
+++ /dev/null
@@ -1,52 +0,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 retry
-
-import (
- "context"
- "fmt"
- "testing"
-)
-
-func TestRetry(t *testing.T) {
- const tries = 5
- t.Run("error", func(t *testing.T) {
- var i int
- err := Retry(context.Background(), &ZeroBackoff{}, func() error {
- i++
- if i == tries {
- return nil
- }
- return fmt.Errorf("try %d", i)
- }, nil)
-
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if i != tries {
- t.Errorf("invalid number of tries: %d", i)
- }
- })
- t.Run("cancel", func(t *testing.T) {
- var i int
- ctx, cancel := context.WithCancel(context.Background())
- err := Retry(ctx, &ZeroBackoff{}, func() error {
- i++
- if i == tries {
- cancel()
- }
- return fmt.Errorf("try %d", i)
- }, nil)
-
- if err == nil {
- t.Error("error is nil")
- }
- if err.Error() != "try 5" {
- t.Errorf("unexpected error: %v", err)
- }
- if i != tries {
- t.Errorf("invalid number of tries: %d", i)
- }
- })
-}
diff --git a/lib/runner/ssh_runner.go b/lib/runner/ssh_runner.go
deleted file mode 100644
index 1917ce5..0000000
--- a/lib/runner/ssh_runner.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// 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 runner
-
-import (
- "context"
- "io"
- "strings"
-
- "golang.org/x/crypto/ssh"
-)
-
-// SSHRunner runs commands over SSH.
-type SSHRunner struct {
- Session *ssh.Session
-}
-
-// Run executes the given command.
-func (r *SSHRunner) Run(ctx context.Context, command []string, stdout io.Writer, stderr io.Writer) error {
- r.Session.Stdout = stdout
- r.Session.Stderr = stderr
-
- // TERM-dumb, to avoid a loop fetching a cursor position.
- r.Session.Setenv("TERM", "dumb")
- cmd := strings.Join(command, " ")
-
- if err := r.Session.Start(cmd); err != nil {
- return err
- }
-
- done := make(chan error)
- go func() {
- done <- r.Session.Wait()
- }()
-
- select {
- case err := <-done:
- return err
- case <-ctx.Done():
- r.Session.Signal(ssh.SIGKILL)
- }
-
- return nil
-}
diff --git a/lib/runner/subprocess_runner.go b/lib/runner/subprocess_runner.go
deleted file mode 100644
index 4bc991c..0000000
--- a/lib/runner/subprocess_runner.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// 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 runner provides implementations for running commands in different environments.
-package runner
-
-import (
- "context"
- "io"
- "os/exec"
- "syscall"
-
- "go.fuchsia.dev/tools/lib/logger"
-)
-
-// SubprocessRunner is a Runner that runs commands as local subprocesses.
-type SubprocessRunner struct {
- // Dir is the working directory of the subprocesses; if unspecified, that
- // of the current process will be used.
- Dir string
-
- // Env is the environment of the subprocess, following the usual convention of a list of
- // strings of the form "<environment variable name>=<value>".
- Env []string
-}
-
-// Run runs a command until completion or until a context is canceled, in
-// which case the subprocess is killed so that no subprocesses it spun up are
-// orphaned.
-func (r *SubprocessRunner) Run(ctx context.Context, command []string, stdout io.Writer, stderr io.Writer) error {
- cmd := exec.Cmd{
- Path: command[0],
- Args: command,
- Stdout: stdout,
- Stderr: stderr,
- Dir: r.Dir,
- Env: r.Env,
- SysProcAttr: &syscall.SysProcAttr{Setpgid: true},
- }
- logger.Infof(ctx, "environment of subprocess:\n%v", cmd.Env)
- logger.Infof(ctx, "starting:\n%v", cmd.Args)
- if err := cmd.Start(); err != nil {
- return err
- }
- done := make(chan error)
- go func() {
- done <- cmd.Wait()
- }()
- select {
- case err := <-done:
- return err
- case <-ctx.Done():
- syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
- return ctx.Err()
- }
-}
diff --git a/lib/runner/subprocess_runner_test.go b/lib/runner/subprocess_runner_test.go
deleted file mode 100644
index b322436..0000000
--- a/lib/runner/subprocess_runner_test.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// 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 runner
-
-import (
- "bytes"
- "context"
- "fmt"
- "strings"
- "testing"
- "time"
-)
-
-const (
- skipMessage = "runner tests are meant for local testing only"
- defaultIOTimeout = 100 * time.Millisecond
-)
-
-func TestSubprocessRunner(t *testing.T) {
- t.Skip(skipMessage)
-
- t.Run("Run", func(t *testing.T) {
- t.Run("should execute a commmand", func(t *testing.T) {
- r := SubprocessRunner{}
- message := "Hello, World!"
- command := []string{"/bin/echo", message}
-
- stdout := new(bytes.Buffer)
- stderr := new(bytes.Buffer)
- if err := r.Run(context.Background(), command, stdout, stderr); err != nil {
- t.Fatalf("failed to run test. Got an error %v", err)
- }
-
- stdoutS := strings.TrimSpace(stdout.String())
- if stdoutS != "Hello, World!" {
- t.Fatalf("expected output '%s', but got %s", message, stdoutS)
- }
-
- stderrS := strings.TrimSpace(stderr.String())
- if stderrS != "" {
- t.Fatalf("expected empty stderr, but got %s", stderrS)
- }
- })
-
- t.Run("should error if the context Completes before the commmand", func(t *testing.T) {
- ctx, cancel := context.WithCancel(context.Background())
- cancel()
- r := SubprocessRunner{}
- command := []string{"/bin/sleep", "5"}
-
- stdout := new(bytes.Buffer)
- stderr := new(bytes.Buffer)
-
- err := r.Run(ctx, command, stdout, stderr)
- stdoutS := strings.TrimSpace(stdout.String())
- stderrS := strings.TrimSpace(stderr.String())
-
- if err == nil {
- t.Fatal(strings.Join([]string{
- "expected command to terminate early but it completed:",
- fmt.Sprintf("(stdout): %s\n", stdoutS),
- fmt.Sprintf("(stderr): %s\n", stderrS),
- }, "\n"))
- }
- })
-
- t.Run("should return an error if the command fails", func(t *testing.T) {
- r := SubprocessRunner{}
- command := []string{"not_a_command_12345"}
-
- stdout := new(bytes.Buffer)
- stderr := new(bytes.Buffer)
-
- err := r.Run(context.Background(), command, stdout, stderr)
- stdoutS := strings.TrimSpace(stdout.String())
- stderrS := strings.TrimSpace(stderr.String())
-
- if err == nil {
- t.Fatal(strings.Join([]string{
- "expected command to terminate early but it completed:",
- fmt.Sprintf("(stdout): %s\n", stdoutS),
- fmt.Sprintf("(stderr): %s\n", stderrS),
- }, "\n"))
- }
- })
- })
-
-}
diff --git a/lib/tarutil/tar.go b/lib/tarutil/tar.go
deleted file mode 100644
index 3abe83b..0000000
--- a/lib/tarutil/tar.go
+++ /dev/null
@@ -1,64 +0,0 @@
-// 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 tarutil provides methods for creating tar packages.
-package tarutil
-
-import (
- "archive/tar"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
-)
-
-// TarDirectory archives the given directory.
-func TarDirectory(tw *tar.Writer, dir string) error {
- return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
- if info.IsDir() {
- return nil
- }
-
- hdr, err := tar.FileInfoHeader(info, path)
- if err != nil {
- return err
- }
- hdr.Name = path[len(dir)+1:]
- if err := tw.WriteHeader(hdr); err != nil {
- return err
- }
- fi, err := os.Open(path)
- if err != nil {
- return err
- }
- _, err = io.Copy(tw, fi)
- return err
- })
-}
-
-// TarBuffer writes the given bytes to a given path within an archive.
-func TarBuffer(tw *tar.Writer, buf []byte, path string) error {
- hdr := &tar.Header{
- Name: path,
- Size: int64(len(buf)),
- Mode: 0666,
- }
- if err := tw.WriteHeader(hdr); err != nil {
- return err
- }
- _, err := tw.Write(buf)
- return err
-}
-
-// TarReader writes data from the given Reader to the given tar.Writer.
-func TarReader(tw *tar.Writer, r io.Reader, path string) error {
- bytes, err := ioutil.ReadAll(r)
- if err != nil {
- return err
- }
- return TarBuffer(tw, bytes, path)
-}
diff --git a/lib/tarutil/tar_test.go b/lib/tarutil/tar_test.go
deleted file mode 100644
index 5e2b5d0..0000000
--- a/lib/tarutil/tar_test.go
+++ /dev/null
@@ -1,101 +0,0 @@
-// 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 tarutil_test
-
-import (
- "archive/tar"
- "bytes"
- "fmt"
- "io"
- "reflect"
- "testing"
-
- "go.fuchsia.dev/tools/lib/tarutil"
-)
-
-func TestTarBuffer(t *testing.T) {
- type entry struct {
- name, data string
- }
-
- tests := []struct {
- // A name for this test case.
- name string
-
- // The input tarball.
- input []entry
-
- // The expected contents of the archive written by WriteTo.
- output map[string]string
- }{
- {
- name: "should handle an empty buffer",
- input: []entry{{"", ""}},
- output: map[string]string{"": ""},
- },
- {
- name: "should handle a non-empty buffer",
- input: []entry{{"a", string("a data")}},
- output: map[string]string{
- "a": string("a data"),
- },
- },
- {
- name: "should handle multiple non-empty buffers",
- input: []entry{
- {"a", string("a data")},
- {"b", string("b data")},
- },
- output: map[string]string{
- "a": string("a data"),
- "b": string("b data"),
- },
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- var buf bytes.Buffer
- tw := tar.NewWriter(&buf)
- for _, ent := range tt.input {
- tarutil.TarBuffer(tw, []byte(ent.data), ent.name)
- }
- actual, err := readTar(&buf)
- if err != nil {
- t.Errorf("failed to read tar archive: %v", err)
- return
- }
- expected := tt.output
- if !reflect.DeepEqual(actual, expected) {
- t.Errorf("got:\n\n%v\n\nwanted:\n\n%v\n\n", actual, expected)
- }
- })
- }
-
-}
-
-// Helper function to read data from a gzipped tar archive. The output maps each header's
-// name within the archive to its data.
-func readTar(r io.Reader) (map[string]string, error) {
- tr := tar.NewReader(r)
- output := make(map[string]string)
- for {
- hdr, err := tr.Next()
- if err == io.EOF {
- break // End of archive.
- }
- if err != nil {
- return nil, fmt.Errorf("reading tarball failed, %v", err)
-
- }
- data := make([]byte, hdr.Size)
- if _, err := tr.Read(data); err != nil && err != io.EOF {
- return nil, fmt.Errorf("reading tarball data failed, %v", err)
- }
- output[hdr.Name] = string(data)
- }
-
- return output, nil
-}
diff --git a/manifest b/manifest
deleted file mode 100644
index 32733a1..0000000
--- a/manifest
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<manifest>
- <projects>
- <project name="tools"
- path="go/src/go.fuchsia.dev/tools"
- remote="https://fuchsia.googlesource.com/tools"
- gerrithost="https://fuchsia-review.googlesource.com"/>
- </projects>
-</manifest>
diff --git a/net/dev_finder/cmd/.gitignore b/net/dev_finder/cmd/.gitignore
deleted file mode 100644
index f9cecca..0000000
--- a/net/dev_finder/cmd/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/dev_finder
diff --git a/net/dev_finder/cmd/common.go b/net/dev_finder/cmd/common.go
deleted file mode 100644
index ac60e83..0000000
--- a/net/dev_finder/cmd/common.go
+++ /dev/null
@@ -1,267 +0,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 (
- "context"
- "encoding/json"
- "errors"
- "flag"
- "fmt"
- "io"
- "log"
- "net"
- "os"
- "sort"
- "strconv"
- "strings"
- "time"
-
- "go.fuchsia.dev/tools/net/mdns"
-)
-
-type mDNSResponse struct {
- rxIface net.Interface
- devAddr net.Addr
- rxPacket mdns.Packet
-}
-
-func (m *mDNSResponse) getReceiveIP() (net.IP, error) {
- if unicastAddrs, err := m.rxIface.Addrs(); err != nil {
- return nil, err
- } else {
- for _, addr := range unicastAddrs {
- var ip net.IP
- switch v := addr.(type) {
- case *net.IPNet:
- ip = v.IP
- case *net.IPAddr:
- ip = v.IP
- }
- if ip == nil || ip.To4() == nil {
- continue
- }
- return ip, nil
- }
- }
- return nil, fmt.Errorf("no IPv4 unicast addresses found on iface %v", m.rxIface)
-}
-
-type mDNSHandler func(mDNSResponse, bool, chan<- *fuchsiaDevice, chan<- error)
-
-type mdnsInterface interface {
- AddHandler(f func(net.Interface, net.Addr, mdns.Packet))
- AddWarningHandler(f func(net.Addr, error))
- AddErrorHandler(f func(error))
- SendTo(packet mdns.Packet, dst *net.UDPAddr) error
- Send(packet mdns.Packet) error
- Start(ctx context.Context, port int) error
-}
-
-type newMDNSFunc func(address string) mdnsInterface
-
-// Contains common command information for embedding in other dev_finder commands.
-type devFinderCmd struct {
- // Outputs in JSON format if true.
- json bool
- // The mDNS addresses to connect to.
- mdnsAddrs string
- // The mDNS ports to connect to.
- mdnsPorts string
- // The timeout in ms to either give up or to exit the program after finding at least one
- // device.
- timeout int
- // Determines whether to return the address of the address of the interface that
- // established a connection to the Fuchsia device (rather than the address of the
- // Fuchsia device on its own).
- localResolve bool
- // Determines whether to accept incoming unicast mDNS responses. This can happen if the
- // receiving device is on a different subnet, or the receiving device's listener port
- // has been forwarded to from a non-standard port.
- acceptUnicast bool
- // The limit of devices to discover. If this number of devices has been discovered before
- // the timeout has been reached the program will exit successfully.
- deviceLimit int
-
- mdnsHandler mDNSHandler
-
- // Only for testing.
- newMDNSFunc newMDNSFunc
- output io.Writer
-}
-
-type fuchsiaDevice struct {
- addr net.IP
- domain string
-}
-
-func (cmd *devFinderCmd) SetCommonFlags(f *flag.FlagSet) {
- f.BoolVar(&cmd.json, "json", false, "Outputs in JSON format.")
- f.StringVar(&cmd.mdnsAddrs, "addr", "224.0.0.251,224.0.0.250,ff02::fb", "Comma separated list of addresses to issue mDNS queries to.")
- f.StringVar(&cmd.mdnsPorts, "port", "5353,5356", "Comma separated list of ports to issue mDNS queries to.")
- f.IntVar(&cmd.timeout, "timeout", 2000, "The number of milliseconds before declaring a timeout.")
- f.BoolVar(&cmd.localResolve, "local", false, "Returns the address of the interface to the host when doing service lookup/domain resolution.")
- f.BoolVar(&cmd.acceptUnicast, "accept-unicast", false, "Accepts unicast responses. For if the receiving device responds from a different subnet or behind port forwarding.")
- f.IntVar(&cmd.deviceLimit, "device-limit", 0, "Exits before the timeout at this many devices per resolution (zero means no limit).")
-}
-
-func (cmd *devFinderCmd) Output() io.Writer {
- if cmd.output == nil {
- return os.Stdout
- }
- return cmd.output
-}
-
-// Extracts the IP from its argument, returning an error if the type is unsupported.
-func addrToIP(addr net.Addr) (net.IP, error) {
- switch v := addr.(type) {
- case *net.IPNet:
- return v.IP, nil
- case *net.IPAddr:
- return v.IP, nil
- case *net.UDPAddr:
- return v.IP, nil
- }
- return nil, errors.New("unsupported address type")
-}
-
-func (cmd *devFinderCmd) newMDNS(address string) mdnsInterface {
- if cmd.newMDNSFunc != nil {
- return cmd.newMDNSFunc(address)
- }
- m := mdns.NewMDNS()
- ip := net.ParseIP(address)
- if ip.To4() != nil {
- m.EnableIPv4()
- } else {
- m.EnableIPv6()
- }
- m.SetAddress(address)
- m.SetAcceptUnicastResponses(cmd.acceptUnicast)
- return m
-}
-
-func sortDeviceMap(deviceMap map[string]*fuchsiaDevice) []*fuchsiaDevice {
- keys := make([]string, 0)
- for k := range deviceMap {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- res := make([]*fuchsiaDevice, 0)
- for _, k := range keys {
- res = append(res, deviceMap[k])
- }
- return res
-}
-
-func (cmd *devFinderCmd) sendMDNSPacket(ctx context.Context, packet mdns.Packet) ([]*fuchsiaDevice, error) {
- if cmd.mdnsHandler == nil {
- return nil, fmt.Errorf("packet handler is nil")
- }
- if cmd.timeout <= 0 {
- return nil, fmt.Errorf("invalid timeout value: %v", cmd.timeout)
- }
-
- addrs := strings.Split(cmd.mdnsAddrs, ",")
- var ports []int
- for _, s := range strings.Split(cmd.mdnsPorts, ",") {
- p, err := strconv.ParseUint(s, 10, 16)
- if err != nil {
- return nil, fmt.Errorf("Could not parse port number %v: %v\n", s, err)
- }
- ports = append(ports, int(p))
- }
-
- ctx, cancel := context.WithTimeout(ctx, time.Duration(cmd.timeout)*time.Millisecond)
- defer cancel()
- errChan := make(chan error)
- devChan := make(chan *fuchsiaDevice)
- for _, addr := range addrs {
- for _, p := range ports {
- m := cmd.newMDNS(addr)
- m.AddHandler(func(recv net.Interface, addr net.Addr, rxPacket mdns.Packet) {
- response := mDNSResponse{recv, addr, rxPacket}
- cmd.mdnsHandler(response, cmd.localResolve, devChan, errChan)
- })
- m.AddErrorHandler(func(err error) {
- errChan <- err
- })
- m.AddWarningHandler(func(addr net.Addr, err error) {
- log.Printf("from: %v warn: %v\n", addr, err)
- })
- if err := m.Start(ctx, p); err != nil {
- return nil, fmt.Errorf("starting mdns: %v", err)
- }
- m.Send(packet)
- }
- }
-
- devices := make(map[string]*fuchsiaDevice)
- for {
- select {
- case <-ctx.Done():
- if len(devices) == 0 {
- return nil, fmt.Errorf("timeout")
- }
- // Devices are returned in sorted order to ensure that results are
- // deterministic despite using a hashmap (which is non-deterministically
- // ordered).
- return sortDeviceMap(devices), nil
- case err := <-errChan:
- return nil, err
- case device := <-devChan:
- // Creates a hashable string to remove duplicate devices.
- //
- // There should only be one of each domain on the network, but given how
- // mcast is sent on each interface, multiple responses with different IP
- // addresses can be returned for a single device in the case of a device
- // running on the host in an emulator (this is a special case). Each
- // IP would be point to the localhost in this case.
- devices[fmt.Sprintf("%s", device.domain)] = device
- if cmd.deviceLimit != 0 && len(devices) == cmd.deviceLimit {
- return sortDeviceMap(devices), nil
- }
- }
- }
-}
-
-// jsonOutput represents the output in JSON format.
-type jsonOutput struct {
- // List of devices found.
- Devices []jsonDevice `json:"devices"`
-}
-
-type jsonDevice struct {
- // Device IP address.
- Addr string `json:"addr"`
- // Device domain name. Can be omitted.
- Domain string `json:"domain,omitempty"`
-}
-
-func (cmd *devFinderCmd) outputNormal(filteredDevices []*fuchsiaDevice, includeDomain bool) error {
- for _, device := range filteredDevices {
- if includeDomain {
- fmt.Fprintf(cmd.Output(), "%v %v\n", device.addr, device.domain)
- } else {
- fmt.Fprintf(cmd.Output(), "%v\n", device.addr)
- }
- }
- return nil
-}
-
-func (cmd *devFinderCmd) outputJSON(filteredDevices []*fuchsiaDevice, includeDomain bool) error {
- jsonOut := jsonOutput{Devices: make([]jsonDevice, 0, len(filteredDevices))}
-
- for _, device := range filteredDevices {
- dev := jsonDevice{Addr: device.addr.String()}
- if includeDomain {
- dev.Domain = device.domain
- }
- jsonOut.Devices = append(jsonOut.Devices, dev)
- }
-
- e := json.NewEncoder(cmd.Output())
- e.SetIndent("", " ")
- return e.Encode(jsonOut)
-}
diff --git a/net/dev_finder/cmd/dev_finder_test.go b/net/dev_finder/cmd/dev_finder_test.go
deleted file mode 100644
index 944d6f9..0000000
--- a/net/dev_finder/cmd/dev_finder_test.go
+++ /dev/null
@@ -1,403 +0,0 @@
-// 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 (
- "bytes"
- "context"
- "encoding/json"
- "net"
- "strings"
- "testing"
-
- "github.com/google/go-cmp/cmp"
-
- "go.fuchsia.dev/tools/net/mdns"
-)
-
-// fakeMDNS is a fake implementation of MDNS for testing.
-type fakeMDNS struct {
- answer *fakeAnswer
- handlers []func(net.Interface, net.Addr, mdns.Packet)
- sendEmptyData bool
- sendTooShortData bool
-}
-
-type fakeAnswer struct {
- ip string
- domains []string
-}
-
-func (m *fakeMDNS) AddHandler(f func(net.Interface, net.Addr, mdns.Packet)) {
- m.handlers = append(m.handlers, f)
-}
-func (m *fakeMDNS) AddWarningHandler(func(net.Addr, error)) {}
-func (m *fakeMDNS) AddErrorHandler(func(error)) {}
-func (m *fakeMDNS) SendTo(mdns.Packet, *net.UDPAddr) error { return nil }
-func (m *fakeMDNS) Send(packet mdns.Packet) error {
- if m.answer != nil {
- go func() {
- ifc := net.Interface{}
- ip := net.IPAddr{IP: net.ParseIP(m.answer.ip).To4()}
- for _, q := range packet.Questions {
- switch {
- case q.Type == mdns.PTR && q.Class == mdns.IN:
- // 'list' command
- answers := make([]mdns.Record, len(m.answer.domains))
- for _, d := range m.answer.domains {
- // Cases for malformed response.
- if m.sendEmptyData {
- answers = append(answers, mdns.Record{
- Class: mdns.IN,
- Type: mdns.PTR,
- Data: nil, // Empty data
- })
- continue
- }
- if m.sendTooShortData {
- data := make([]byte, len(d)) // One byte shorter
- data[0] = byte(len(d))
- copy(data[1:], []byte(d[1:]))
- answers = append(answers, mdns.Record{
- Class: mdns.IN,
- Type: mdns.PTR,
- Data: data,
- })
- continue
- }
-
- data := make([]byte, len(d)+1)
- data[0] = byte(len(d))
- copy(data[1:], []byte(d))
- answers = append(answers, mdns.Record{
- Class: mdns.IN,
- Type: mdns.PTR,
- Data: data,
- })
- }
- pkt := mdns.Packet{Answers: answers}
- for _, h := range m.handlers {
- h(ifc, &ip, pkt)
- }
- case q.Type == mdns.A && q.Class == mdns.IN:
- // 'resolve' command
- answers := make([]mdns.Record, len(m.answer.domains))
- for _, d := range m.answer.domains {
- answers = append(answers, mdns.Record{
- Class: mdns.IN,
- Type: mdns.A,
- Data: net.ParseIP(m.answer.ip).To4(),
- Domain: d,
- })
- }
- pkt := mdns.Packet{Answers: answers}
- for _, h := range m.handlers {
- h(ifc, &ip, pkt)
- }
- }
- }
- }()
- }
- return nil
-}
-func (m *fakeMDNS) Start(context.Context, int) error { return nil }
-
-func newDevFinderCmd(handler mDNSHandler, answerDomains []string, sendEmptyData bool, sendTooShortData bool) devFinderCmd {
- // Because mdnsAddrs have two addresses specified and mdnsPorts have
- // two ports specified, four MDNS objects are created. To emulate the
- // case where only one of them responds, create only one fake MDNS
- // object with answers. The others wouldn't respond at all. See the
- // Send() method above.
- mdnsCount := 0
- return devFinderCmd{
- mdnsHandler: handler,
- mdnsAddrs: "224.0.0.251,224.0.0.250",
- mdnsPorts: "5353,5356",
- timeout: 10,
- newMDNSFunc: func(addr string) mdnsInterface {
- mdnsCount++
- switch mdnsCount {
- case 1:
- return &fakeMDNS{
- answer: &fakeAnswer{
- ip: "192.168.0.42",
- domains: answerDomains,
- },
- sendEmptyData: sendEmptyData,
- sendTooShortData: sendTooShortData,
- }
- default:
- return &fakeMDNS{}
- }
- },
- }
-}
-
-func compareFuchsiaDevices(d1, d2 *fuchsiaDevice) bool {
- return cmp.Equal(d1.addr, d2.addr) && cmp.Equal(d1.domain, d2.domain)
-}
-
-//// Tests for the `list` command.
-
-func TestListDevices(t *testing.T) {
- cmd := listCmd{
- devFinderCmd: newDevFinderCmd(
- listMDNSHandler,
- []string{
- "some.domain",
- "another.domain",
- }, false, false),
- }
-
- got, err := cmd.listDevices(context.Background())
- if err != nil {
- t.Fatalf("listDevices: %v", err)
- }
- want := []*fuchsiaDevice{
- {
- addr: net.ParseIP("192.168.0.42").To4(),
- domain: "another.domain",
- },
- {
- addr: net.ParseIP("192.168.0.42").To4(),
- domain: "some.domain",
- },
- }
- if d := cmp.Diff(want, got, cmp.Comparer(compareFuchsiaDevices)); d != "" {
- t.Errorf("listDevices mismatch: (-want +got):\n%s", d)
- }
-}
-
-func TestListDevices_domainFilter(t *testing.T) {
- cmd := listCmd{
- devFinderCmd: newDevFinderCmd(
- listMDNSHandler,
- []string{
- "some.domain",
- "another.domain",
- }, false, false),
- domainFilter: "some",
- }
-
- got, err := cmd.listDevices(context.Background())
- if err != nil {
- t.Fatalf("listDevices: %v", err)
- }
- want := []*fuchsiaDevice{
- {
- addr: net.ParseIP("192.168.0.42").To4(),
- domain: "some.domain",
- },
- }
- if d := cmp.Diff(want, got, cmp.Comparer(compareFuchsiaDevices)); d != "" {
- t.Errorf("listDevices mismatch: (-want +got):\n%s", d)
- }
-}
-
-func TestListDevices_emptyData(t *testing.T) {
- cmd := listCmd{
- devFinderCmd: newDevFinderCmd(
- listMDNSHandler,
- []string{
- "some.domain",
- "another.domain",
- },
- true, // sendEmptyData
- false),
- }
-
- // Must not crash.
- cmd.listDevices(context.Background())
-}
-
-func TestListDevices_duplicateDevices(t *testing.T) {
- cmd := listCmd{
- devFinderCmd: newDevFinderCmd(
- listMDNSHandler,
- []string{
- "some.domain",
- "some.domain",
- "some.domain",
- "some.domain",
- "some.domain",
- "another.domain",
- },
- false,
- false),
- }
- got, err := cmd.listDevices(context.Background())
- if err != nil {
- t.Fatalf("listDevices: %v", err)
- }
- want := []*fuchsiaDevice{
- {
- addr: net.ParseIP("192.168.0.42").To4(),
- domain: "another.domain",
- },
- {
- addr: net.ParseIP("192.168.0.42").To4(),
- domain: "some.domain",
- },
- }
- if d := cmp.Diff(want, got, cmp.Comparer(compareFuchsiaDevices)); d != "" {
- t.Errorf("listDevices mismatch: (-want +got):\n%s", d)
- }
-}
-
-func TestListDevices_tooShortData(t *testing.T) {
- cmd := listCmd{
- devFinderCmd: newDevFinderCmd(
- listMDNSHandler,
- []string{
- "some.domain",
- "another.domain",
- },
- false,
- true, // sendTooShortData
- ),
- }
-
- // Must not crash.
- cmd.listDevices(context.Background())
-}
-
-//// Tests for the `resolve` command.
-
-func TestResolveDevices(t *testing.T) {
- cmd := resolveCmd{
- devFinderCmd: newDevFinderCmd(
- resolveMDNSHandler,
- []string{
- "some.domain.local",
- "another.domain.local",
- }, false, false),
- }
-
- got, err := cmd.resolveDevices(context.Background(), "some.domain")
- if err != nil {
- t.Fatalf("resolveDevices: %v", err)
- }
- want := []*fuchsiaDevice{
- {
- addr: net.ParseIP("192.168.0.42").To4(),
- domain: "some.domain.local",
- },
- }
- if d := cmp.Diff(want, got, cmp.Comparer(compareFuchsiaDevices)); d != "" {
- t.Errorf("resolveDevices mismatch: (-want +got):\n%s", d)
- }
-}
-
-//// Tests for output functions.
-
-func TestOutputNormal(t *testing.T) {
- devs := []*fuchsiaDevice{
- {
- addr: net.ParseIP("123.12.234.23").To4(),
- domain: "hello.world",
- },
- {
- addr: net.ParseIP("11.22.33.44").To4(),
- domain: "fuchsia.rocks",
- },
- }
-
- {
- var buf strings.Builder
- cmd := devFinderCmd{output: &buf}
-
- cmd.outputNormal(devs, false)
-
- got := buf.String()
- want := `123.12.234.23
-11.22.33.44
-`
- if d := cmp.Diff(want, got); d != "" {
- t.Errorf("outputNormal mismatch: (-want +got):\n%s", d)
- }
- }
-
- {
- var buf strings.Builder
- cmd := devFinderCmd{output: &buf}
- cmd.outputNormal(devs, true)
-
- got := buf.String()
- want := `123.12.234.23 hello.world
-11.22.33.44 fuchsia.rocks
-`
- if d := cmp.Diff(want, got); d != "" {
- t.Errorf("outputNormal(includeDomain) mismatch: (-want +got):\n%s", d)
- }
- }
-}
-
-func TestOutputJSON(t *testing.T) {
- devs := []*fuchsiaDevice{
- {
- addr: net.ParseIP("123.12.234.23").To4(),
- domain: "hello.world",
- },
- {
- addr: net.ParseIP("11.22.33.44").To4(),
- domain: "fuchsia.rocks",
- },
- }
-
- {
- var buf bytes.Buffer
- cmd := devFinderCmd{
- json: true,
- output: &buf,
- }
-
- cmd.outputJSON(devs, false)
-
- var got jsonOutput
- if err := json.Unmarshal(buf.Bytes(), &got); err != nil {
- t.Fatalf("json.Unmarshal: %v", err)
- }
- want := jsonOutput{
- Devices: []jsonDevice{
- {Addr: "123.12.234.23"},
- {Addr: "11.22.33.44"},
- },
- }
- if d := cmp.Diff(want, got); d != "" {
- t.Errorf("outputNormal mismatch: (-want +got):\n%s", d)
- }
- }
-
- {
- var buf bytes.Buffer
- cmd := devFinderCmd{
- json: true,
- output: &buf,
- }
-
- cmd.outputJSON(devs, true)
-
- var got jsonOutput
- if err := json.Unmarshal(buf.Bytes(), &got); err != nil {
- t.Fatalf("json.Unmarshal: %v", err)
- }
-
- want := jsonOutput{
- Devices: []jsonDevice{
- {
- Addr: "123.12.234.23",
- Domain: "hello.world",
- },
- {
- Addr: "11.22.33.44",
- Domain: "fuchsia.rocks",
- },
- },
- }
- if d := cmp.Diff(want, got); d != "" {
- t.Errorf("outputNormal(includeDomain) mismatch: (-want +got):\n%s", d)
- }
- }
-}
diff --git a/net/dev_finder/cmd/list.go b/net/dev_finder/cmd/list.go
deleted file mode 100644
index 4a5e749..0000000
--- a/net/dev_finder/cmd/list.go
+++ /dev/null
@@ -1,136 +0,0 @@
-// 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 (
- "context"
- "flag"
- "fmt"
- "log"
- "strings"
-
- "github.com/google/subcommands"
-
- "go.fuchsia.dev/tools/net/mdns"
-)
-
-const (
- fuchsiaService = "_fuchsia._udp.local"
-)
-
-type listCmd struct {
- devFinderCmd
-
- // Determines whether or not to print the full device info.
- fullInfo bool
- // Filters domains that match this string when listing devices.
- domainFilter string
-}
-
-func (*listCmd) Name() string {
- return "list"
-}
-
-func (*listCmd) Usage() string {
- return "list [flags...]\n\nflags:\n"
-}
-
-func (*listCmd) Synopsis() string {
- return "lists all Fuchsia devices on the network"
-}
-
-func (cmd *listCmd) SetFlags(f *flag.FlagSet) {
- cmd.SetCommonFlags(f)
- f.StringVar(&cmd.domainFilter, "domain-filter", "", "When using the \"list\" command, returns only devices that match this domain name.")
- f.BoolVar(&cmd.fullInfo, "full", false, "Print device address and domain")
-}
-
-func listMDNSHandler(resp mDNSResponse, localResolve bool, devChan chan<- *fuchsiaDevice, errChan chan<- error) {
- for _, a := range resp.rxPacket.Answers {
- if a.Class == mdns.IN && a.Type == mdns.PTR {
- // DX-1498: Some protection against malformed responses.
- if len(a.Data) == 0 {
- log.Print("Empty data in response. Ignoring...")
- continue
- }
- nameLength := int(a.Data[0])
- if len(a.Data) < nameLength+1 {
- log.Printf("Too short data in response. Got %d bytes; expected %d", len(a.Data), nameLength+1)
- continue
- }
-
- // This is a bit convoluted: the domain param is being used
- // as a "service", and the Data field actually contains the
- // domain of the device.
- fuchsiaDomain := string(a.Data[1 : nameLength+1])
- if localResolve {
- recvIP, err := resp.getReceiveIP()
- if err != nil {
- errChan <- err
- return
- }
- devChan <- &fuchsiaDevice{recvIP, fuchsiaDomain}
- continue
- }
- if ip, err := addrToIP(resp.devAddr); err != nil {
- errChan <- fmt.Errorf("could not find addr for %v: %v", resp.devAddr, err)
- } else {
- devChan <- &fuchsiaDevice{
- addr: ip,
- domain: fuchsiaDomain,
- }
- }
- }
- }
-}
-
-func (cmd *listCmd) listDevices(ctx context.Context) ([]*fuchsiaDevice, error) {
- listPacket := mdns.Packet{
- Header: mdns.Header{QDCount: 1},
- Questions: []mdns.Question{
- {
- Domain: fuchsiaService,
- Type: mdns.PTR,
- Class: mdns.IN,
- Unicast: false,
- },
- },
- }
- devices, err := cmd.sendMDNSPacket(ctx, listPacket)
- if err != nil {
- return nil, fmt.Errorf("sending/receiving mdns packets: %v", err)
- }
- var filteredDevices []*fuchsiaDevice
- for _, device := range devices {
- if strings.Contains(device.domain, cmd.domainFilter) {
- filteredDevices = append(filteredDevices, device)
- }
- }
- if len(filteredDevices) == 0 {
- return nil, fmt.Errorf("no devices with domain matching '%v'", cmd.domainFilter)
- }
- return filteredDevices, nil
-}
-
-func (cmd *listCmd) execute(ctx context.Context) error {
- filteredDevices, err := cmd.listDevices(ctx)
- if err != nil {
- return err
- }
-
- if cmd.json {
- return cmd.outputJSON(filteredDevices, cmd.fullInfo)
- }
- return cmd.outputNormal(filteredDevices, cmd.fullInfo)
-}
-
-func (cmd *listCmd) Execute(ctx context.Context, _ *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
- cmd.mdnsHandler = listMDNSHandler
- if err := cmd.execute(ctx); err != nil {
- log.Print(err)
- return subcommands.ExitFailure
- }
- return subcommands.ExitSuccess
-}
diff --git a/net/dev_finder/cmd/main.go b/net/dev_finder/cmd/main.go
deleted file mode 100644
index 72525e3..0000000
--- a/net/dev_finder/cmd/main.go
+++ /dev/null
@@ -1,25 +0,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
-
-// Uses mDNS for Fuchsia device discovery.
-
-import (
- "context"
- "flag"
- "os"
-
- "github.com/google/subcommands"
-)
-
-func main() {
- subcommands.Register(subcommands.HelpCommand(), "")
- subcommands.Register(subcommands.CommandsCommand(), "")
- subcommands.Register(subcommands.FlagsCommand(), "")
- subcommands.Register(&listCmd{}, "")
- subcommands.Register(&resolveCmd{}, "")
-
- flag.Parse()
- os.Exit(int(subcommands.Execute(context.Background())))
-}
diff --git a/net/dev_finder/cmd/resolve.go b/net/dev_finder/cmd/resolve.go
deleted file mode 100644
index 691c697..0000000
--- a/net/dev_finder/cmd/resolve.go
+++ /dev/null
@@ -1,109 +0,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 (
- "context"
- "errors"
- "flag"
- "fmt"
- "log"
- "net"
-
- "github.com/google/subcommands"
-
- "go.fuchsia.dev/tools/net/mdns"
-)
-
-const (
- ipv4AddrLength = 4
-)
-
-type resolveCmd struct {
- devFinderCmd
-}
-
-func (*resolveCmd) Name() string {
- return "resolve"
-}
-
-func (*resolveCmd) Usage() string {
- return "resolve [flags...] [domains...]\n\nflags:\n"
-}
-
-func (*resolveCmd) Synopsis() string {
- return "attempts to resolve all passed Fuchsia domain names on the network"
-}
-
-func (cmd *resolveCmd) SetFlags(f *flag.FlagSet) {
- cmd.SetCommonFlags(f)
-}
-
-func resolveMDNSHandler(resp mDNSResponse, localResolve bool, devChan chan<- *fuchsiaDevice, errChan chan<- error) {
- for _, a := range resp.rxPacket.Answers {
- if a.Class == mdns.IN && a.Type == mdns.A &&
- len(a.Data) == ipv4AddrLength {
- if localResolve {
- recvIP, err := resp.getReceiveIP()
- if err != nil {
- errChan <- err
- return
- }
- devChan <- &fuchsiaDevice{recvIP, a.Domain}
- continue
- }
- devChan <- &fuchsiaDevice{net.IP(a.Data), a.Domain}
- }
- }
-}
-
-func (cmd *resolveCmd) resolveDevices(ctx context.Context, domains ...string) ([]*fuchsiaDevice, error) {
- if len(domains) == 0 {
- return nil, errors.New("no domains supplied")
- }
-
- var outDevices []*fuchsiaDevice
- for _, domain := range domains {
- mDNSDomain := fmt.Sprintf("%s.local", domain)
- devices, err := cmd.sendMDNSPacket(ctx, mdns.QuestionPacket(mDNSDomain))
- if err != nil {
- return nil, fmt.Errorf("sending/receiving mdns packets during resolve of domain '%s': %v", domain, err)
- }
- filteredDevices := make([]*fuchsiaDevice, 0)
- for _, device := range devices {
- if device.domain == mDNSDomain {
- filteredDevices = append(filteredDevices, device)
- }
- }
- if len(filteredDevices) == 0 {
- return nil, fmt.Errorf("no devices with domain %v", domain)
- }
-
- for _, device := range filteredDevices {
- outDevices = append(outDevices, device)
- }
- }
- return outDevices, nil
-}
-
-func (cmd *resolveCmd) execute(ctx context.Context, domains ...string) error {
- outDevices, err := cmd.resolveDevices(ctx, domains...)
- if err != nil {
- return err
- }
-
- if cmd.json {
- return cmd.outputJSON(outDevices, false /* includeDomain */)
- }
- return cmd.outputNormal(outDevices, false /* includeDomain */)
-}
-
-func (cmd *resolveCmd) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
- cmd.mdnsHandler = resolveMDNSHandler
- if err := cmd.execute(ctx, f.Args()...); err != nil {
- log.Print(err)
- return subcommands.ExitFailure
- }
- return subcommands.ExitSuccess
-}
diff --git a/net/digest/digest.go b/net/digest/digest.go
deleted file mode 100644
index 607135e..0000000
--- a/net/digest/digest.go
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright 2013 M-Lab
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package digest
-
-import (
- "crypto/md5"
- "crypto/rand"
- "errors"
- "fmt"
- "io"
- "net/http"
- "strings"
-)
-
-var (
- ErrNilTransport = errors.New("transport is nil")
- ErrBadChallenge = errors.New("challenge is bad")
- ErrAlgNotImplemented = errors.New("algorithm not implemented")
-)
-
-// Transport is an implementation of http.RoundTripper that supports HTTP
-// digest authentication.
-type Transport struct {
- Username string
- Password string
- Transport http.RoundTripper
-}
-
-// NewTransport creates a new digest transport using http.DefaultTransport.
-func NewTransport(username, password string) *Transport {
- return &Transport{
- Username: username,
- Password: password,
- Transport: http.DefaultTransport,
- }
-}
-
-// RoundTrip makes a request expecting a 401 response that will require digest
-// authentication. It creates the credentials it needs and makes a follow-up
-// request.
-func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
- if t.Transport == nil {
- return nil, ErrNilTransport
- }
-
- body, err := r.GetBody()
- if err != nil {
- return nil, err
- }
- req, err := http.NewRequest(r.Method, r.URL.String(), body)
- if err != nil {
- return nil, err
- }
-
- req.Header = make(http.Header)
- for k, s := range r.Header {
- req.Header[k] = s
- }
-
- // Make a request to get the 401 that contains the challenge.
- res, err := t.Transport.RoundTrip(r)
- if err != nil || res.StatusCode != 401 {
- return res, err
- }
- defer res.Body.Close()
-
- chal := res.Header.Get("WWW-Authenticate")
- c, err := parseChallenge(chal)
- if err != nil {
- return res, err
- }
-
- // Generate credentials based on the challenge.
- cr := t.authenticate(req, c)
- auth, err := cr.authorize()
- if err != nil {
- return res, err
- }
-
- // Make authenticated request.
- req.Header.Set("Authorization", auth)
- return t.Transport.RoundTrip(req)
-}
-
-type challenge struct {
- realm string
- domain string
- nonce string
- opaque string
- stale string
- algorithm string
- qop string
-}
-
-func parseChallenge(input string) (*challenge, error) {
- const ws = " \n\r\t"
- const qs = `"`
- s := strings.Trim(input, ws)
- if !strings.HasPrefix(s, "Digest ") {
- return nil, ErrBadChallenge
- }
- s = strings.Trim(s[7:], ws)
- sl := strings.Split(s, ",")
- c := &challenge{
- algorithm: "MD5",
- }
- var r []string
- for i := range sl {
- r = strings.SplitN(strings.Trim(sl[i], ws), "=", 2)
- switch r[0] {
- case "realm":
- c.realm = strings.Trim(r[1], qs)
- case "domain":
- c.domain = strings.Trim(r[1], qs)
- case "nonce":
- c.nonce = strings.Trim(r[1], qs)
- case "opaque":
- c.opaque = strings.Trim(r[1], qs)
- case "stale":
- c.stale = strings.Trim(r[1], qs)
- case "algorithm":
- c.algorithm = strings.Trim(r[1], qs)
- case "qop":
- c.qop = strings.Trim(r[1], qs)
- default:
- return nil, ErrBadChallenge
- }
- }
- return c, nil
-}
-
-type credentials struct {
- userhash bool
- username string
- realm string
- nonce string
- uri string
- algorithm string
- cnonce string
- opaque string
- qop string
- nc int
- method string
- password string
-}
-
-func h(data string) string {
- return fmt.Sprintf("%x", md5.Sum([]byte(data)))
-}
-
-func (c *credentials) ha1() string {
- return h(fmt.Sprintf("%s:%s:%s", c.username, c.realm, c.password))
-}
-
-func (c *credentials) ha2() string {
- return h(fmt.Sprintf("%s:%s", c.method, c.uri))
-}
-
-func (c *credentials) response(cnonce string) (string, error) {
- c.nc++
- if c.qop == "auth" {
- if cnonce != "" {
- c.cnonce = cnonce
- } else {
- b := make([]byte, 8)
- io.ReadFull(rand.Reader, b)
- c.cnonce = fmt.Sprintf("%x", b)[:16]
- }
- return h(fmt.Sprintf("%s:%s:%08x:%s:%s:%s",
- c.ha1(), c.nonce, c.nc, c.cnonce, c.qop, c.ha2())), nil
- } else if c.qop == "" {
- return h(fmt.Sprintf("%s:%s:%s", c.ha1(), c.nonce, c.ha2())), nil
- }
- return "", ErrAlgNotImplemented
-}
-
-func (c *credentials) authorize() (string, error) {
- if c.algorithm != "MD5" {
- return "", ErrAlgNotImplemented
- }
- if c.qop != "auth" && c.qop != "" {
- return "", ErrAlgNotImplemented
- }
- response, err := c.response("")
- if err != nil {
- return "", err
- }
- sl := []string{}
- sl = append(sl, fmt.Sprintf(`username="%s"`, c.username))
- sl = append(sl, fmt.Sprintf(`realm="%s"`, c.realm))
- sl = append(sl, fmt.Sprintf(`nonce="%s"`, c.nonce))
- sl = append(sl, fmt.Sprintf(`uri="%s"`, c.uri))
- sl = append(sl, fmt.Sprintf(`response="%s"`, response))
- sl = append(sl, fmt.Sprintf(`algorithm="%s"`, c.algorithm))
- sl = append(sl, fmt.Sprintf(`cnonce="%s"`, c.cnonce))
- if c.opaque != "" {
- sl = append(sl, fmt.Sprintf(`opaque="%s"`, c.opaque))
- }
- if c.qop != "" {
- sl = append(sl, fmt.Sprintf(`qop=%s`, c.qop))
- }
- sl = append(sl, fmt.Sprintf("nc=%08x", c.nc))
- if c.userhash {
- sl = append(sl, `userhash="true"`)
- }
- return fmt.Sprintf("Digest %s", strings.Join(sl, ", ")), nil
-}
-
-func (t *Transport) authenticate(req *http.Request, c *challenge) *credentials {
- return &credentials{
- username: t.Username,
- realm: c.realm,
- nonce: c.nonce,
- uri: req.URL.RequestURI(),
- algorithm: c.algorithm,
- opaque: c.opaque,
- qop: c.qop,
- nc: 0,
- method: req.Method,
- password: t.Password,
- }
-}
diff --git a/net/digest/digest_test.go b/net/digest/digest_test.go
deleted file mode 100644
index 8607a48..0000000
--- a/net/digest/digest_test.go
+++ /dev/null
@@ -1,46 +0,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 digest
-
-import (
- "testing"
-)
-
-var c = &credentials{
- username: "admin",
- realm: "Digest:4C1F0000000000000000000000000000",
- nonce: "GZHoABAHAAAAAAAAtejSfCEQLbW+c/fM",
- uri: "/index",
- algorithm: "MD5",
- qop: "auth",
- method: "POST",
- password: "password",
-}
-
-var cnonce = "0a4f113b"
-
-func TestHa1(t *testing.T) {
- r := c.ha1()
- if r != "e00fd2f74e4bb1ccd5c3f359e13822ce" {
- t.Fail()
- }
-}
-
-func TestHa2(t *testing.T) {
- r := c.ha2()
- if r != "f272ccec928f9de4e8e0bc6319ab2c66" {
- t.Fail()
- }
-}
-
-func TestResponse(t *testing.T) {
- r, err := c.response(cnonce)
- if err != nil {
- t.Fail()
- }
- if r != "ce25c065de2d1c900b21ed6d6fbe886b" {
- t.Fail()
- }
-}
diff --git a/net/mdns/BUILD.gn b/net/mdns/BUILD.gn
deleted file mode 100644
index 35ff521..0000000
--- a/net/mdns/BUILD.gn
+++ /dev/null
@@ -1,13 +0,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.
-
-import ("//build/go/go_library.gni")
-
-go_library("mdns_lib") {
- name = "mdns_lib"
-
- deps = [
- "//garnet/public/go/third_party:golang.org/x/net",
- ]
-}
diff --git a/net/mdns/mdns.go b/net/mdns/mdns.go
deleted file mode 100644
index c304d94..0000000
--- a/net/mdns/mdns.go
+++ /dev/null
@@ -1,1102 +0,0 @@
-// 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 mdns
-
-import (
- "bytes"
- "context"
- "encoding/binary"
- "fmt"
- "io"
- "net"
- "strings"
- "syscall"
- "unicode/utf8"
-
- "golang.org/x/net/ipv4"
- "golang.org/x/net/ipv6"
- "golang.org/x/sys/unix"
-)
-
-// DefaultPort is the mDNS port required of the spec, though this library is port-agnostic.
-const DefaultPort int = 5353
-
-type Header struct {
- ID uint16
- Flags uint16
- QDCount uint16
- ANCount uint16
- NSCount uint16
- ARCount uint16
-}
-
-type Record struct {
- Domain string
- Type uint16
- Class uint16
- Flush bool
- TTL uint32
- Data []byte
-}
-
-type Question struct {
- Domain string
- Type uint16
- Class uint16
- Unicast bool
-}
-
-type Packet struct {
- Header Header
- Questions []Question
- Answers []Record
- Authority []Record
- Additional []Record
-}
-
-// A small struct used to send received UDP packets and
-// information about their interface / source address through a channel.
-type receivedPacketInfo struct {
- data []byte
- iface *net.Interface
- src net.Addr
- err error
-}
-
-func writeUint16(out io.Writer, val uint16) error {
- buf := make([]byte, 2)
- binary.BigEndian.PutUint16(buf, val)
- _, err := out.Write(buf)
- return err
-}
-
-func (h Header) serialize(out io.Writer) error {
- if err := writeUint16(out, h.ID); err != nil {
- return err
- }
- if err := writeUint16(out, h.Flags); err != nil {
- return err
- }
- if err := writeUint16(out, h.QDCount); err != nil {
- return err
- }
- if err := writeUint16(out, h.ANCount); err != nil {
- return err
- }
- if err := writeUint16(out, h.NSCount); err != nil {
- return err
- }
- if err := writeUint16(out, h.ARCount); err != nil {
- return err
- }
- return nil
-}
-
-func writeDomain(out io.Writer, domain string) error {
- domain = strings.TrimSuffix(domain, ".")
- parts := strings.Split(domain, ".")
- // TODO(jakehehrlich): Add check that each label is ASCII.
- // TODO(jakehehrlich): Add check that each label is <= 63 in length.
- // TODO(jakehehrlich): Add support for compression.
- for _, dpart := range parts {
- ascii := []byte(dpart)
- if _, err := out.Write([]byte{byte(len(ascii))}); err != nil {
- return err
- }
- if _, err := out.Write(ascii); err != nil {
- return err
- }
- }
- _, err := out.Write([]byte{0})
- return err
-}
-
-func (q Question) serialize(out io.Writer) error {
- if err := writeDomain(out, q.Domain); err != nil {
- return err
- }
- if err := writeUint16(out, q.Type); err != nil {
- return err
- }
- var unicast uint16
- if q.Unicast {
- unicast = 1 << 15
- }
- if err := writeUint16(out, unicast|q.Class); err != nil {
- return err
- }
- return nil
-}
-
-func writeUint32(out io.Writer, val uint32) error {
- buf := make([]byte, 4)
- binary.BigEndian.PutUint32(buf, val)
- _, err := out.Write(buf)
- return err
-}
-
-func (r Record) serialize(out io.Writer) error {
- if err := writeDomain(out, r.Domain); err != nil {
- return err
- }
- if err := writeUint16(out, r.Type); err != nil {
- return err
- }
- var flush uint16
- if r.Flush {
- flush = 1 << 15
- }
- if err := writeUint16(out, flush|r.Class); err != nil {
- return err
- }
- if err := writeUint32(out, r.TTL); err != nil {
- return err
- }
- if err := writeUint16(out, uint16(len(r.Data))); err != nil {
- return err
- }
- if _, err := out.Write(r.Data); err != nil {
- return err
- }
- return nil
-}
-
-func (p Packet) serialize(out io.Writer) error {
- if err := p.Header.serialize(out); err != nil {
- return err
- }
- for _, question := range p.Questions {
- if err := question.serialize(out); err != nil {
- return err
- }
- }
- for _, answer := range p.Answers {
- if err := answer.serialize(out); err != nil {
- return err
- }
- }
- for _, authority := range p.Authority {
- if err := authority.serialize(out); err != nil {
- return err
- }
- }
- for _, addon := range p.Additional {
- if err := addon.serialize(out); err != nil {
- return err
- }
- }
- return nil
-}
-
-func readUint16(in io.Reader, out *uint16) error {
- buf := make([]byte, 2)
- _, err := in.Read(buf)
- if err != nil {
- return err
- }
- *out = binary.BigEndian.Uint16(buf)
- return nil
-}
-
-func (h *Header) deserialize(data []byte, in io.Reader) error {
- if err := readUint16(in, &h.ID); err != nil {
- return err
- }
- if err := readUint16(in, &h.Flags); err != nil {
- return err
- }
- if err := readUint16(in, &h.QDCount); err != nil {
- return err
- }
- if err := readUint16(in, &h.ANCount); err != nil {
- return err
- }
- if err := readUint16(in, &h.NSCount); err != nil {
- return err
- }
- if err := readUint16(in, &h.ARCount); err != nil {
- return err
- }
- return nil
-}
-
-func readDomain(data []byte, in io.Reader, domain *string) error {
- // TODO(jakehehrlich): Don't stack overflow when domain contains cycle.
-
- var d bytes.Buffer
- for {
- sizeBuf := make([]byte, 1)
- if _, err := in.Read(sizeBuf); err != nil {
- return err
- }
- size := sizeBuf[0]
- // A size of zero indicates that we're done.
- if size == 0 {
- break
- }
- // We don't support compressed domains right now.
- if size > 63 {
- if size < 192 {
- return fmt.Errorf("invalid size for label")
- }
- if _, err := in.Read(sizeBuf); err != nil {
- return err
- }
- offset := ((size & 0x3f) << 8) | sizeBuf[0]
- var pDomain string
- readDomain(data, bytes.NewBuffer(data[offset:]), &pDomain)
- if _, err := d.WriteString(pDomain); err != nil {
- return err
- }
- if err := d.WriteByte(byte('.')); err != nil {
- return err
- }
- break
- }
- // Read in the specified bytes (max length 256)
- buf := make([]byte, size)
- if _, err := in.Read(buf); err != nil {
- return err
- }
- // Make sure the string is ASCII
- for _, b := range buf {
- if b >= utf8.RuneSelf {
- return fmt.Errorf("Found non-ASCII byte %v in domain", b)
- }
- }
- // Now add this to a temporary domain
- if _, err := d.Write(buf); err != nil {
- return err
- }
- // Add the trailing "." as seen in the RFC.
- if err := d.WriteByte(byte('.')); err != nil {
- return err
- }
- }
- *domain = string(d.Bytes())
- // Remove the trailing '.' to canonicalize.
- *domain = strings.TrimSuffix(*domain, ".")
- return nil
-}
-
-func (q *Question) deserialize(data []byte, in io.Reader) error {
- if err := readDomain(data, in, &q.Domain); err != nil {
- return fmt.Errorf("reading domain: %v", err)
- }
- if err := readUint16(in, &q.Type); err != nil {
- return err
- }
- var tmp uint16
- if err := readUint16(in, &tmp); err != nil {
- return err
- }
- // Extract class and unicast bit.
- q.Unicast = (tmp >> 15) != 0
- q.Class = (tmp << 1) >> 1
- return nil
-}
-
-func readUint32(in io.Reader, out *uint32) error {
- buf := make([]byte, 4)
- _, err := in.Read(buf)
- if err != nil {
- return err
- }
- *out = binary.BigEndian.Uint32(buf)
- return nil
-}
-
-func (r *Record) deserialize(data []byte, in io.Reader) error {
- if err := readDomain(data, in, &r.Domain); err != nil {
- return err
- }
- if err := readUint16(in, &r.Type); err != nil {
- return err
- }
- var tmp uint16
- if err := readUint16(in, &tmp); err != nil {
- return err
- }
- // Extract class and flush bit.
- r.Flush = (tmp >> 15) != 0
- r.Class = (tmp << 1) >> 1
- if err := readUint32(in, &r.TTL); err != nil {
- return err
- }
-
- var dataLength uint16
- if err := readUint16(in, &dataLength); err != nil {
- return err
- }
- // Now read the data (max allocation size of 64k)
- r.Data = make([]byte, dataLength)
- if _, err := in.Read(r.Data); err != nil {
- return err
- }
- return nil
-}
-
-// TODO(jakehehrlich): Handle truncation.
-func (p *Packet) deserialize(data []byte, in io.Reader) error {
- if err := p.Header.deserialize(data, in); err != nil {
- return err
- }
- p.Questions = make([]Question, p.Header.QDCount)
- for i := uint16(0); i < p.Header.QDCount; i++ {
- if err := p.Questions[i].deserialize(data, in); err != nil {
- return err
- }
- }
- p.Answers = make([]Record, p.Header.ANCount)
- for i := uint16(0); i < p.Header.ANCount; i++ {
- if err := p.Answers[i].deserialize(data, in); err != nil {
- return err
- }
- }
- p.Authority = make([]Record, p.Header.NSCount)
- for i := uint16(0); i < p.Header.NSCount; i++ {
- if err := p.Authority[i].deserialize(data, in); err != nil {
- return err
- }
- }
- p.Additional = make([]Record, p.Header.ARCount)
- for i := uint16(0); i < p.Header.ARCount; i++ {
- if err := p.Additional[i].deserialize(data, in); err != nil {
- return err
- }
- }
- return nil
-}
-
-// getFlag constructs the flag field of a header for the tiny subset of
-// flag options that we need.
-// TODO(jakehehrlich): Implement response code error handling.
-// TODO(jakehehrlich): Implement truncation.
-func getFlag(query bool, authority bool) uint16 {
- var out uint16
- if !query {
- out |= 1
- }
- if authority {
- out |= 1 << 5
- }
- return out
-}
-
-const (
- // A is the DNS Type for ipv4
- A = 1
- // AAAA is the DNS Type for ipv6
- AAAA = 28
- // PTR is the DNS Type for domain name pointers
- PTR = 12
- // SRV is the DNS Type for services
- SRV = 33
- // IN is the Internet DNS Class
- IN = 1
-)
-
-// IpToDnsRecordType returns either A or AAAA based on the type of ip.
-func IpToDnsRecordType(ip net.IP) uint16 {
- if ip4 := ip.To4(); ip4 != nil {
- return A
- } else {
- return AAAA
- }
-}
-
-// MDNS is the central interface through which requests are sent and received.
-// This implementation is agnostic to use case and asynchronous.
-// To handle various responses add Handlers. To send a packet you may use
-// either SendTo (generally used for unicast) or Send (generally used for
-// multicast).
-type MDNS interface {
- // EnableIPv4 enables listening on IPv4 network interfaces.
- EnableIPv4()
-
- // EnableIPv6 enables listening on IPv6 network interfaces.
- EnableIPv6()
-
- // SetAddress sets a non-default listen address.
- SetAddress(address string) error
-
- // Sets whether to accept unicast responses. These can be received when
- // the receiving device is in a different subnet, or if there is port
- // forwarding occurring between the host and the device.
- SetAcceptUnicastResponses(accept bool)
-
- // ipToSend returns the IP corresponding to the current address.
- ipToSend() net.IP
-
- // AddHandler calls f on every Packet received.
- AddHandler(f func(net.Interface, net.Addr, Packet))
-
- // AddWarningHandler calls f on every non-fatal error.
- AddWarningHandler(f func(net.Addr, error))
-
- // AddErrorHandler calls f on every fatal error. After
- // all active handlers are called, m will stop listening and
- // close it's connection so this function will not be called twice.
- AddErrorHandler(f func(error))
-
- // Start causes m to start listening for mDNS packets on all interfaces on
- // the specified port. Listening will stop if ctx is done.
- Start(ctx context.Context, port int) error
-
- // Send serializes and sends packet out as a multicast to all interfaces
- // using the port that m is listening on. Note that Start must be
- // called prior to making this call.
- Send(packet Packet) error
-
- // SendTo serializes and sends packet to dst. If dst is a multicast
- // address then packet is multicast to the corresponding group on
- // all interfaces. Note that start must be called prior to making this
- // call.
- SendTo(packet Packet, dst *net.UDPAddr) error
-
- // Close closes all connections.
- Close()
-}
-
-// Interface used to read packets into a caller-owned buffer.
-type packetReceiver interface {
- // Reads a packet from the connection into |buf|. On success, returns
- // the packet size in bytes, the interface |iface| on which it was
- // received, and the source address |src|.
- //
- // On error, returns a non-nil |err|.
- ReadPacket(buf []byte) (size int, iface *net.Interface, src net.Addr, err error)
- // Implements ipv4/ipv6 JoinGroup functionality, joining a group address
- // on the interface |iface|.
- JoinGroup(iface *net.Interface, group net.Addr) error
- Close() error
-}
-
-type packetReceiver4 struct {
- conn *ipv4.PacketConn
-}
-
-func (p *packetReceiver4) ReadPacket(buf []byte) (size int, iface *net.Interface, src net.Addr, err error) {
- var cm *ipv4.ControlMessage
- size, cm, src, err = p.conn.ReadFrom(buf)
- if err != nil {
- return
- }
- iface, err = net.InterfaceByIndex(cm.IfIndex)
- return
-}
-
-func (p *packetReceiver4) JoinGroup(iface *net.Interface, group net.Addr) error {
- return p.conn.JoinGroup(iface, group)
-}
-
-func (p *packetReceiver4) Close() error {
- err := p.conn.Close()
- p.conn = nil
- return err
-}
-
-type packetReceiver6 struct {
- conn *ipv6.PacketConn
-}
-
-func (p *packetReceiver6) ReadPacket(buf []byte) (size int, iface *net.Interface, src net.Addr, err error) {
- var cm *ipv6.ControlMessage
- size, cm, src, err = p.conn.ReadFrom(buf)
- if err != nil {
- return
- }
- iface, err = net.InterfaceByIndex(cm.IfIndex)
- return
-}
-
-func (p *packetReceiver6) JoinGroup(iface *net.Interface, group net.Addr) error {
- return p.conn.JoinGroup(iface, group)
-}
-
-func (p *packetReceiver6) Close() error {
- err := p.conn.Close()
- p.conn = nil
- return err
-}
-
-type mDNSConn interface {
- Close() error
- SetIp(ip net.IP) error
- getIp() net.IP
- SetAcceptUnicastResponses(accept bool)
- SendTo(buf bytes.Buffer, dst *net.UDPAddr) error
- Send(buf bytes.Buffer) error
- InitReceiver(port int) error
- // Starts a goroutine to listen for packets incoming on the multicast
- // connection.
- //
- // If |SetAcceptUnicastResponses| has been set to true, starts a
- // separate goroutine to listen to incoming unicast packets for each
- // of the machine's interfaces.
- //
- // Returns a read-only channel on which received packets are written.
- Listen() <-chan receivedPacketInfo
- JoinGroup(iface net.Interface) error
- // Connects to the specific port and interface.
- //
- // This is primarily for sending packets, but if
- // |SetAcceptUnicastResponses(true)| has been called, then this
- // connection becomes bidirectional, and is also used for reading
- // packets when |Listen()| is later invoked.
- //
- // If |SetAcceptUnicastResponses| is called between multiple invocations
- // of this function, then not all connections will be read from when
- // |Listen| is called.
- ConnectTo(port int, ip net.IP, iface *net.Interface) error
- ReadFrom(buf []byte) (size int, iface *net.Interface, src net.Addr, err error)
- NewPacketReceiver(net.PacketConn) packetReceiver
-}
-
-type mDNSConnBase struct {
- dst net.UDPAddr
- acceptUnicast bool
- receiver packetReceiver
- senders []net.PacketConn
- ucastReceivers []packetReceiver
-}
-
-func (c *mDNSConnBase) Close() error {
- if c.receiver != nil {
- err := c.receiver.Close()
- c.receiver = nil
- if err != nil {
- return err
- }
- }
- for _, receiver := range c.ucastReceivers {
- if err := receiver.Close(); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (c *mDNSConnBase) Listen() <-chan receivedPacketInfo {
- ch := make(chan receivedPacketInfo, 1)
- startListenLoop(c.receiver, ch)
- for _, receiver := range c.ucastReceivers {
- startListenLoop(receiver, ch)
- }
- return ch
-}
-
-func (c *mDNSConnBase) getIp() net.IP {
- return c.dst.IP
-}
-
-func (c *mDNSConnBase) SendTo(buf bytes.Buffer, dst *net.UDPAddr) error {
- for _, sender := range c.senders {
- if _, err := sender.WriteTo(buf.Bytes(), dst); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (c *mDNSConnBase) SetAcceptUnicastResponses(accept bool) {
- c.acceptUnicast = accept
-}
-
-func (c *mDNSConnBase) Send(buf bytes.Buffer) error {
- return c.SendTo(buf, &c.dst)
-}
-
-type mDNSConn4 struct {
- mDNSConnBase
-}
-
-var defaultMDNSMulticastIPv4 = net.ParseIP("224.0.0.251")
-
-// Helper function for the |Listen| method in the |mDNSConn| interface.
-//
-// Accepts write-only channel of receivePacketInfo items.
-//
-// Starts a goroutine which will stop when it cannot read anymore from
-// |receiver|, which will happen in the case of an error (like the connection
-// being closed). In this case it will send a final receivedPacketInfo instance
-// with only an error code before exiting.
-func startListenLoop(receiver packetReceiver, ch chan<- receivedPacketInfo) {
- go func() {
- payloadBuf := make([]byte, 1<<16)
- for {
- size, iface, src, err := receiver.ReadPacket(payloadBuf)
- if err != nil {
- ch <- receivedPacketInfo{err: err}
- return
- }
- data := make([]byte, size)
- copy(data, payloadBuf[:size])
- ch <- receivedPacketInfo{
- data: data,
- iface: iface,
- src: src,
- err: nil}
- }
- }()
-}
-
-func newMDNSConn4() mDNSConn {
- c := mDNSConn4{}
- c.SetIp(defaultMDNSMulticastIPv4)
- return &c
-}
-
-func (c *mDNSConn4) SetIp(ip net.IP) error {
- if ip4 := ip.To4(); ip4 == nil {
- panic(fmt.Errorf("Not an IPv-4 address: %v", ip))
- }
- c.dst.IP = ip
- return nil
-}
-
-func (c *mDNSConn4) InitReceiver(port int) error {
- c.dst.Port = port
- conn, err := net.ListenUDP("udp4", &c.dst)
- if err != nil {
- return err
- }
- c.receiver = c.NewPacketReceiver(conn)
- return nil
-}
-
-func (c *mDNSConn4) NewPacketReceiver(conn net.PacketConn) packetReceiver {
- conn4 := ipv4.NewPacketConn(conn)
- conn4.SetControlMessage(ipv4.FlagDst|ipv4.FlagInterface, true)
- return &packetReceiver4{conn4}
-}
-
-// This allows us to listen on this specific interface.
-func (c *mDNSConn4) JoinGroup(iface net.Interface) error {
- if err := c.receiver.JoinGroup(&iface, &c.dst); err != nil {
- c.Close()
- return fmt.Errorf("joining %v%%%v: %v", iface, c.dst, err)
- }
- return nil
-}
-
-func (c *mDNSConn4) ConnectTo(port int, ip net.IP, iface *net.Interface) error {
- ip4 := ip.To4()
- if ip4 == nil {
- return fmt.Errorf("Not a valid IPv4 address: %v", ip)
- }
- conn, err := makeUdpSocketWithReusePort(port, ip4, iface)
- if err != nil {
- return err
- }
- c.senders = append(c.senders, conn)
- if c.acceptUnicast {
- c.ucastReceivers = append(c.ucastReceivers, c.NewPacketReceiver(conn))
- }
- return nil
-}
-
-func (c *mDNSConn4) ReadFrom(buf []byte) (int, *net.Interface, net.Addr, error) {
- return c.receiver.ReadPacket(buf)
-}
-
-type mDNSConn6 struct {
- mDNSConnBase
-}
-
-var defaultMDNSMulticastIPv6 = net.ParseIP("ff02::fb")
-
-func newMDNSConn6() mDNSConn {
- c := mDNSConn6{}
- c.SetIp(defaultMDNSMulticastIPv6)
- return &c
-}
-
-func (c *mDNSConn6) SetIp(ip net.IP) error {
- if ip6 := ip.To16(); ip6 == nil {
- panic(fmt.Errorf("Not an IPv6 address: %v", ip))
- }
- c.dst.IP = ip
- return nil
-}
-
-func (c *mDNSConn6) InitReceiver(port int) error {
- c.dst.Port = port
- conn, err := net.ListenUDP("udp6", &c.dst)
- if err != nil {
- return err
- }
- c.receiver = c.NewPacketReceiver(conn)
- return nil
-}
-
-func (c *mDNSConn6) NewPacketReceiver(conn net.PacketConn) packetReceiver {
- conn6 := ipv6.NewPacketConn(conn)
- conn6.SetControlMessage(ipv6.FlagDst|ipv6.FlagInterface, true)
- return &packetReceiver6{conn6}
-}
-
-// This allows us to listen on this specific interface.
-func (c *mDNSConn6) JoinGroup(iface net.Interface) error {
- if err := c.receiver.JoinGroup(&iface, &c.dst); err != nil {
- c.Close()
- return fmt.Errorf("joining %v%%%v: %v", iface, c.dst, err)
- }
- return nil
-}
-
-func (c *mDNSConn6) ConnectTo(port int, ip net.IP, iface *net.Interface) error {
- ip6 := ip.To16()
- if ip6 == nil {
- return fmt.Errorf("Not a valid IPv6 address: %v", ip)
- }
- conn, err := makeUdpSocketWithReusePort(port, ip6, iface)
- if err != nil {
- return err
- }
- c.senders = append(c.senders, conn)
- if c.acceptUnicast {
- c.ucastReceivers = append(c.ucastReceivers, c.NewPacketReceiver(conn))
- }
- return nil
-}
-
-func (c *mDNSConn6) ReadFrom(buf []byte) (int, *net.Interface, net.Addr, error) {
- return c.receiver.ReadPacket(buf)
-}
-
-type mDNS struct {
- conn4 mDNSConn
- conn6 mDNSConn
- port int
- pHandlers []func(net.Interface, net.Addr, Packet)
- wHandlers []func(net.Addr, error)
- eHandlers []func(error)
-}
-
-// NewMDNS creates a new object implementing the MDNS interface. Do not forget
-// to call EnableIPv4() or EnableIPv6() to enable listening on interfaces of
-// the corresponding type, or nothing will work.
-func NewMDNS() MDNS {
- m := mDNS{}
- m.conn4 = nil
- m.conn6 = nil
- return &m
-}
-
-func (m *mDNS) EnableIPv4() {
- if m.conn4 == nil {
- m.conn4 = newMDNSConn4()
- }
-}
-
-func (m *mDNS) EnableIPv6() {
- if m.conn6 == nil {
- m.conn6 = newMDNSConn6()
- }
-}
-
-func (m *mDNS) SetAcceptUnicastResponses(accept bool) {
- if m.conn4 != nil {
- m.conn4.SetAcceptUnicastResponses(accept)
- }
- if m.conn6 != nil {
- m.conn6.SetAcceptUnicastResponses(accept)
- }
-}
-
-func (m *mDNS) Close() {
- if m.conn4 != nil {
- m.conn4.Close()
- m.conn4 = nil
- }
- if m.conn6 != nil {
- m.conn6.Close()
- m.conn6 = nil
- }
-}
-
-func (m *mDNS) SetAddress(address string) error {
- ip := net.ParseIP(address)
- if ip == nil {
- return fmt.Errorf("Not a valid IP address: %v", address)
- }
- if ip4 := ip.To4(); ip4 != nil {
- if m.conn4 == nil {
- return fmt.Errorf("mDNS IPv4 support is disabled")
- }
- return m.conn4.SetIp(ip4)
- } else {
- if m.conn6 == nil {
- return fmt.Errorf("mDNS IPv6 support is disabled")
- }
- return m.conn6.SetIp(ip.To16())
- }
-}
-
-func (m *mDNS) ipToSend() net.IP {
- if m.conn4 != nil {
- return m.conn4.getIp()
- }
- if m.conn6 != nil {
- return m.conn6.getIp()
- }
- return nil
-}
-
-// AddHandler calls f on every Packet received.
-func (m *mDNS) AddHandler(f func(net.Interface, net.Addr, Packet)) {
- m.pHandlers = append(m.pHandlers, f)
-}
-
-// AddWarningHandler calls f on every non-fatal error.
-func (m *mDNS) AddWarningHandler(f func(net.Addr, error)) {
- m.wHandlers = append(m.wHandlers, f)
-}
-
-// AddErrorHandler calls f on every fatal error. After
-// all active handlers are called, m will stop listening and
-// close it's connection so this function will not be called twice.
-func (m *mDNS) AddErrorHandler(f func(error)) {
- m.eHandlers = append(m.eHandlers, f)
-}
-
-// SendTo serializes and sends packet to dst. If dst is a multicast
-// address then packet is multicast to the corresponding group on
-// all interfaces. Note that start must be called prior to making this
-// call.
-func (m *mDNS) SendTo(packet Packet, dst *net.UDPAddr) error {
- var buf bytes.Buffer
- // TODO(jakehehrlich): Add checking that the packet is well formed.
- if err := packet.serialize(&buf); err != nil {
- return err
- }
- if dst.IP.To4() != nil {
- if m.conn4 != nil {
- return m.conn4.SendTo(buf, dst)
- } else {
- return fmt.Errorf("IPv4 was not enabled!")
- }
- } else {
- if m.conn6 != nil {
- return m.conn6.SendTo(buf, dst)
- } else {
- return fmt.Errorf("IPv6 was not enabled!")
- }
- }
-}
-
-// Send serializes and sends packet out as a multicast to all interfaces
-// using the port that m is listening on. Note that Start must be
-// called prior to making this call.
-func (m *mDNS) Send(packet Packet) error {
- var buf bytes.Buffer
- // TODO(jakehehrlich): Add checking that the packet is well formed.
- if err := packet.serialize(&buf); err != nil {
- return err
- }
- var err4 error
- if m.conn4 != nil {
- err4 = m.conn4.Send(buf)
- }
- var err6 error
- if m.conn6 != nil {
- err6 = m.conn6.Send(buf)
- }
- if err4 != nil {
- return err4
- }
- return err6
-}
-
-func makeUdpSocketWithReusePort(port int, ip net.IP, iface *net.Interface) (net.PacketConn, error) {
- is_ipv6 := ip.To4() == nil
- network := "udp4"
- var zone string
- if is_ipv6 {
- network = "udp6"
- zone = iface.Name
- }
- address := (&net.UDPAddr{IP: ip, Port: port, Zone: zone}).String()
-
- // A net.ListenConfig control function to set both SO_REUSEADDR and SO_REUSEPORT
- // on a given socket fd. Only works on Unix-based systems, not Windows. For portability
- // an alternative might be to use something like the go-reuseport library.
- control := func(network, address string, c syscall.RawConn) error {
- var err error
- c.Control(func(fd uintptr) {
- err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
- if err != nil {
- return
- }
-
- err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
- if err != nil {
- return
- }
- })
- return err
- }
-
- listenConfig := net.ListenConfig{Control: control}
- return listenConfig.ListenPacket(context.Background(), network, address)
-}
-
-// Start causes m to start listening for MDNS packets on all interfaces on
-// the specified port. Listening will stop if ctx is done.
-func (m *mDNS) Start(ctx context.Context, port int) error {
- if m.conn4 != nil {
- if err := m.conn4.InitReceiver(port); err != nil {
- return err
- }
- }
- if m.conn6 != nil {
- if err := m.conn6.InitReceiver(port); err != nil {
- return err
- }
- }
- // Now we need to join this connection to every interface that supports
- // Multicast.
- ifaces, err := net.Interfaces()
- if err != nil {
- m.Close()
- return fmt.Errorf("listing interfaces: %v", err)
- }
- // We need to make sure to handle each interface.
- for _, iface := range ifaces {
- if iface.Flags&net.FlagMulticast == 0 || iface.Flags&net.FlagUp == 0 {
- continue
- }
- if m.conn4 != nil {
- if err := m.conn4.JoinGroup(iface); err != nil {
- m.Close()
- return fmt.Errorf("joining %v: %v", iface, err)
- }
- }
- if m.conn6 != nil {
- if err := m.conn6.JoinGroup(iface); err != nil {
- m.Close()
- return fmt.Errorf("joining %v: %v", iface, err)
- }
- }
- addrs, err := iface.Addrs()
- if err != nil {
- return fmt.Errorf("getting addresses of %v: %v", iface, err)
- }
- // When both IPv4 and IPv6 are enabled, only connect to the interface
- // through its IPv6 address if it doesn't have an IPv4 one.
- for _, addr := range addrs {
- var ip net.IP
- switch v := addr.(type) {
- case *net.IPNet:
- ip = v.IP
- case *net.IPAddr:
- ip = v.IP
- }
- if ip == nil {
- continue
- }
- ip4 := ip.To4()
- if m.conn4 != nil && ip4 != nil {
- if err := m.conn4.ConnectTo(port, ip4, &iface); err != nil {
- return fmt.Errorf("creating socket for %v via %v: %v", iface, ip, err)
- }
- break
- }
- if m.conn6 != nil && ip4 == nil {
- if err := m.conn6.ConnectTo(port, ip, &iface); err != nil {
- return fmt.Errorf("creating socket for %v via %v: %v", iface, ip, err)
- }
- break
- }
- }
- }
- go func() {
- // NOTE: This defer statement will close connections, which will force
- // the goroutines started by startListenLoop() to exit.
- defer m.Close()
-
- var chan4 <-chan receivedPacketInfo
- var chan6 <-chan receivedPacketInfo
-
- if m.conn4 != nil {
- chan4 = m.conn4.Listen()
- }
- if m.conn6 != nil {
- chan6 = m.conn6.Listen()
- }
- for {
- var received receivedPacketInfo
-
- select {
- case <-ctx.Done():
- return
- case received = <-chan4:
- break
- case received = <-chan6:
- break
- }
-
- if received.err != nil {
- for _, e := range m.eHandlers {
- go e(received.err)
- }
- return
- }
-
- var packet Packet
- if err := packet.deserialize(received.data, bytes.NewBuffer(received.data)); err != nil {
- for _, w := range m.wHandlers {
- go w(received.src, err)
- }
- continue
- }
-
- for _, p := range m.pHandlers {
- go p(*received.iface, received.src, packet)
- }
- }
- }()
- return nil
-}
-
-// QuestionPacket constructs and returns a packet that
-// requests the ip address associated with domain.
-func QuestionPacket(domain string) Packet {
- return Packet{
- Header: Header{QDCount: 2},
- Questions: []Question{
- {
- Domain: domain,
- Type: AAAA,
- Class: IN,
- Unicast: false,
- },
- {
- Domain: domain,
- Type: A,
- Class: IN,
- Unicast: false,
- },
- },
- }
-}
-
-// AnswerPacket constructs and returns a packet that
-// gives a response to the
-func AnswerPacket(domain string, ip net.IP) Packet {
- return Packet{
- Header: Header{ANCount: 1},
- Answers: []Record{
- {
- Domain: domain,
- Type: IpToDnsRecordType(ip),
- Class: IN,
- Flush: false,
- Data: []byte(ip),
- },
- },
- }
-}
diff --git a/net/mdns/mdns_test.go b/net/mdns/mdns_test.go
deleted file mode 100644
index fec008a..0000000
--- a/net/mdns/mdns_test.go
+++ /dev/null
@@ -1,149 +0,0 @@
-// 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 mdns
-
-import (
- "bytes"
- "net"
- "testing"
-
- "github.com/google/go-cmp/cmp"
-)
-
-func TestUint16(t *testing.T) {
- var buf bytes.Buffer
- v := uint16(6857)
- writeUint16(&buf, v)
- var v2 uint16
- readUint16(&buf, &v2)
- if d := cmp.Diff(v, v2); d != "" {
- t.Errorf("read/writeUint16: mismatch (-wrote +read)\n%s", d)
- }
-}
-
-func TestUint32(t *testing.T) {
- var buf bytes.Buffer
- v := uint32(6857)
- writeUint32(&buf, v)
- var v2 uint32
- readUint32(&buf, &v2)
- if d := cmp.Diff(v, v2); d != "" {
- t.Errorf("read/writeUint32: mismatch (-wrote +read)\n%s", d)
- }
-}
-
-func TestHeader(t *testing.T) {
- var buf bytes.Buffer
- v := Header{
- ID: 593,
- Flags: 795,
- QDCount: 5839,
- ANCount: 9009,
- NSCount: 8583,
- ARCount: 7764,
- }
- v.serialize(&buf)
- var v2 Header
- v2.deserialize(buf.Bytes(), &buf)
- if d := cmp.Diff(v, v2); d != "" {
- t.Errorf("header (de)serialize: mismatch (-serialize +deserialize)\n%s", d)
- }
-}
-
-func TestDomain(t *testing.T) {
- var buf bytes.Buffer
- v := "this.is.a.random.domain.to.check"
- writeDomain(&buf, v)
- var v2 string
- readDomain(buf.Bytes(), &buf, &v2)
- if d := cmp.Diff(v, v2); d != "" {
- t.Errorf("read/writeDomain: mismatch (-wrote +read)\n%s", d)
- }
-}
-
-func TestQuestion(t *testing.T) {
- var buf bytes.Buffer
- v := Question{
- Domain: "some.random.thing.local",
- Type: 5954,
- Unicast: true,
- }
- v.serialize(&buf)
- var v2 Question
- v2.deserialize(buf.Bytes(), &buf)
- if d := cmp.Diff(v, v2); d != "" {
- t.Errorf("question (de)serialize: mismatch (-serialize +deserialize)\n%s", d)
- }
-}
-
-func TestRecord(t *testing.T) {
- var buf bytes.Buffer
- v := Record{
- Domain: "some.random.thing",
- Type: 1234,
- Class: 8765,
- Flush: true,
- TTL: 18656,
- Data: []byte{45, 145, 253, 167, 34, 74},
- }
- v.serialize(&buf)
- var v2 Record
- v2.deserialize(buf.Bytes(), &buf)
- if d := cmp.Diff(v, v2); d != "" {
- t.Errorf("record (de)serialize: mismatch (-serialize +deserialize)\n%s", d)
- }
-}
-
-func TestIpToDnsRecordType(t *testing.T) {
- ip1 := "224.0.0.251"
- if addrType := IpToDnsRecordType(net.ParseIP(ip1)); addrType != A {
- t.Errorf("IpToDnsRecordType(%s) mismatch %v, wanted %v", ip1, addrType, A)
- }
- ip2 := "ff2e::fb"
- if addrType := IpToDnsRecordType(net.ParseIP(ip2)); addrType != AAAA {
- t.Errorf("IpToDnsRecordType(%s) mismatch %v, wanted %v", ip2, addrType, AAAA)
- }
-}
-
-func TestSetAddress(t *testing.T) {
- m := NewMDNS()
- m.EnableIPv4()
- got := m.ipToSend()
- // Should send to the default address.
- want := net.ParseIP("224.0.0.251")
- if d := cmp.Diff(want, got); d != "" {
- t.Errorf("ipToSend (default): mismatch (-want +got)\n%s", d)
- }
-
- if err := m.SetAddress("11.22.33.44"); err != nil {
- t.Errorf("SetAddress() returned error: %s", err)
- } else {
- got = m.ipToSend()
- // Should send to the given custom address.
- want = net.ParseIP("11.22.33.44")
- if d := cmp.Diff(want, got); d != "" {
- t.Errorf("ipToSend (custom): mismatch (-want +got)\n%s", d)
- }
- }
-
- m = NewMDNS()
- m.EnableIPv6()
- got = m.ipToSend()
- want = net.ParseIP("ff02::fb")
- if d := cmp.Diff(want, got); d != "" {
- t.Errorf("ipToSend (default): mismatch (-want +got)\n%s", d)
- }
-
- if err := m.SetAddress("11:22::33:44"); err != nil {
- t.Errorf("SetAddress() returned error: %s", err)
- } else {
- got = m.ipToSend()
- // Should send to the given custom address.
- want = net.ParseIP("11:22::33:44")
- if d := cmp.Diff(want, got); d != "" {
- t.Errorf("ipToSend (custom): mismatch (-want +got)\n%s", d)
- }
- }
-}
diff --git a/net/mdnstool/cmd/main.go b/net/mdnstool/cmd/main.go
deleted file mode 100644
index 12a35be..0000000
--- a/net/mdnstool/cmd/main.go
+++ /dev/null
@@ -1,205 +0,0 @@
-// 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 (
- "context"
- "flag"
- "fmt"
- "log"
- "net"
- "os"
- "time"
-
- "go.fuchsia.dev/tools/net/mdns"
-)
-
-// TODO(jakehehrlich): This doesn't retry or anything, it just times out. It would
-// be nice to make this more robust.
-// sends out a request for the ip of domain. Waits for up to |dur| amount of time.
-// Will stop waiting for a response if ctx is done. The default port for mDNS is
-// 5353 but you're allowed to specify via port.
-func mDNSResolve(ctx context.Context, domain string, port int, dur time.Duration) (net.IP, error) {
- m := mdns.NewMDNS()
- m.EnableIPv4()
- m.EnableIPv6()
- out := make(chan net.IP)
- // Add all of our handlers
- m.AddHandler(func(iface net.Interface, addr net.Addr, packet mdns.Packet) {
- for _, a := range packet.Answers {
- if a.Class != mdns.IN || a.Domain != domain {
- continue
- }
- if a.Type == mdns.A || a.Type == mdns.AAAA {
- out <- net.IP(a.Data)
- return
- }
- }
- })
- m.AddWarningHandler(func(addr net.Addr, err error) {
- log.Printf("from: %v warn: %v", addr, err)
- })
- errs := make(chan error)
- m.AddErrorHandler(func(err error) {
- errs <- err
- })
- // Before we start we need to add a context to timeout with
- ctx, cancel := context.WithTimeout(ctx, dur)
- defer cancel()
- // Start up the mdns loop
- if err := m.Start(ctx, port); err != nil {
- return nil, fmt.Errorf("starting mdns: %v", err)
- }
- // Send a packet requesting an answer to "what is the IP of |domain|?"
- m.Send(mdns.QuestionPacket(domain))
- // Now wait for either a timeout, an error, or an answer.
- select {
- case <-ctx.Done():
- return nil, fmt.Errorf("timeout")
- case err := <-errs:
- return nil, err
- case ip := <-out:
- return ip, nil
- }
-}
-
-// TODO(jakehehrlich): Add support for unicast.
-// mDNSPublish will respond to requests for the ip of domain by responding with ip.
-// Note that this responds on both IPv4 and IPv6 interfaces, independent on the type
-// of ip itself. You can stop the server by canceling ctx. Even though mDNS is
-// generally on 5353 you can specify any port via port.
-func mDNSPublish(ctx context.Context, domain string, port int, ip net.IP) error {
- // Now create and mDNS server
- m := mdns.NewMDNS()
- m.EnableIPv4()
- m.EnableIPv6()
- addrType := mdns.IpToDnsRecordType(ip)
- m.AddHandler(func(iface net.Interface, addr net.Addr, packet mdns.Packet) {
- log.Printf("from %v packet %v", addr, packet)
- for _, q := range packet.Questions {
- if q.Class == mdns.IN && q.Type == addrType && q.Domain == domain {
- // We ignore the Unicast bit here but in theory this could be handled via SendTo and addr.
- m.Send(mdns.AnswerPacket(domain, ip))
- }
- }
- })
- m.AddWarningHandler(func(addr net.Addr, err error) {
- log.Printf("from: %v warn: %v", addr, err)
- })
- errs := make(chan error)
- m.AddErrorHandler(func(err error) {
- errs <- err
- })
- // Now start the server.
- if err := m.Start(ctx, port); err != nil {
- return fmt.Errorf("starting mdns: %v", err)
- }
- // Now wait for either a timeout, an error, or an answer.
- select {
- case <-ctx.Done():
- return nil
- case err := <-errs:
- return err
- }
-}
-
-// This function makes a faulty assumption. It assumes that the first
-// multicast interface it finds with an ipv4 address will be the
-// address the user wants. There isn't really a way to guess exactly
-// the address that the user will want. If an IPv6 address is needed, then
-// using the -ip <address> option is required.
-func getMulticastIP() net.IP {
- ifaces, err := net.Interfaces()
- if err != nil {
- return nil
- }
- for _, i := range ifaces {
- if i.Flags&net.FlagMulticast == 0 {
- continue
- }
- addrs, err := i.Addrs()
- if err != nil {
- return nil
- }
- for _, addr := range addrs {
- switch v := addr.(type) {
- case *net.IPNet:
- if ip4 := v.IP.To4(); ip4 != nil {
- return ip4
- }
- }
- }
- }
- return nil
-}
-
-var (
- port int
- timeout int
- ipAddr string
-)
-
-func init() {
- flag.StringVar(&ipAddr, "ip", "", "the ip to respond with when serving.")
- flag.IntVar(&port, "port", 5353, "the port your mDNS servers operate on")
- flag.IntVar(&timeout, "timeout", 2000, "the number of milliseconds before declaring a timeout")
-}
-
-func publish(args ...string) error {
- if len(args) < 1 {
- return fmt.Errorf("missing domain to serve")
- }
- var ip net.IP
- if ipAddr == "" {
- ip = getMulticastIP()
- if ip = getMulticastIP(); ip == nil {
- return fmt.Errorf("could not find a suitable ip")
- }
- } else {
- if ip = net.ParseIP(ipAddr); ip == nil {
- return fmt.Errorf("'%s' is not a valid ip address", ipAddr)
- }
- }
- domain := args[0]
- if err := mDNSPublish(context.Background(), domain, port, ip); err != nil {
- return err
- }
- return nil
-}
-
-func resolve(args ...string) error {
- if len(args) < 1 {
- return fmt.Errorf("missing domain to request")
- }
- domain := args[0]
- ip, err := mDNSResolve(context.Background(), domain, port, time.Duration(timeout)*time.Millisecond)
- if err != nil {
- return err
- }
- fmt.Printf("%v\n", ip)
- return nil
-}
-
-func main() {
- flag.Parse()
- args := flag.Args()
- if len(args) < 1 {
- log.Printf("error: no command given")
- os.Exit(1)
- }
- mp := map[string]func(...string) error{
- "publish": publish,
- "resolve": resolve,
- }
- if f, ok := mp[args[0]]; ok {
- if err := f(args[1:]...); err != nil {
- log.Printf("error: %v", err)
- }
- return
- } else {
- log.Printf("error: %s is not a command", args[0])
- }
- os.Exit(1)
-}
diff --git a/net/netboot/loglistener.go b/net/netboot/loglistener.go
deleted file mode 100644
index 96a9661..0000000
--- a/net/netboot/loglistener.go
+++ /dev/null
@@ -1,134 +0,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.
-
-// This file implements the Zircon loglistener protocol.
-package netboot
-
-import (
- "bytes"
- "encoding/binary"
- "errors"
- "net"
- "os"
- "syscall"
-
- "golang.org/x/sys/unix"
-)
-
-// Magic constants used by the netboot protocol.
-const (
- debugMagic = 0xAEAE1123 // see system/public/zircon/boot/netboot.h
-)
-
-// Port numbers used by the netboot protocol.
-const (
- debugPort = 33337 // debugging log port
-)
-
-// logpacket is the network logging protocol packet.
-type logpacket struct {
- Magic uint32
- Seqno uint32
- Nodename [64]byte
- Data [1216]byte
-}
-
-// LogListener is Zircon's debug log listener.
-type LogListener struct {
- seq uint32
- conn net.PacketConn
- nodename string
-}
-
-// NewLogListener creates and connects a new instance of debug log listener.
-func NewLogListener(nodename string) (*LogListener, error) {
- syscall.ForkLock.RLock()
- fd, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP)
- if err == nil {
- unix.CloseOnExec(fd)
- }
- syscall.ForkLock.RUnlock()
- if err != nil {
- return nil, err
- }
-
- // SO_REUSEADDR and SO_REUSEPORT allows binding to the same port multiple
- // times which is necessary in the case when there are multiple instances.
- if err := syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, unix.SO_REUSEADDR, 1); err != nil {
- syscall.Close(fd)
- return nil, err
- }
-
- if err := syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil {
- syscall.Close(fd)
- return nil, err
- }
-
- // Bind the socket to the default debug log listener port.
- if err := syscall.Bind(fd, &syscall.SockaddrInet6{Port: debugPort}); err != nil {
- syscall.Close(fd)
- return nil, err
- }
-
- f := os.NewFile(uintptr(fd), "")
- conn, err := net.FilePacketConn(f)
- f.Close()
- if err != nil {
- return nil, err
- }
-
- return &LogListener{
- conn: conn,
- nodename: nodename,
- }, nil
-}
-
-// Listen receive a single debug log packet.
-func (l *LogListener) Listen() (string, error) {
- b := make([]byte, 4096)
-
- // Wait for logpacket.
- _, addr, err := l.conn.ReadFrom(b)
- if err != nil {
- return "", err
- }
-
- var pkt logpacket
- if err := binary.Read(bytes.NewReader(b), binary.LittleEndian, &pkt); err != nil {
- return "", err
- }
- if pkt.Magic != debugMagic {
- // Not a valid debug packet.
- return "", errors.New("invalid magic")
- }
- name, err := netbootString(pkt.Nodename[:])
- if err != nil {
- return "", err
- }
- if name != l.nodename {
- // Not a packet from our node.
- return "", errors.New("invalid nodename")
- }
-
- var data string
- if pkt.Seqno != l.seq {
- data, err = netbootString(pkt.Data[:])
- if err != nil {
- return data, err
- }
- l.seq = pkt.Seqno
- }
-
- // Acknowledge the packet.
- if _, err = l.conn.WriteTo(b[:8], addr); err != nil {
- return "", err
- }
-
- return data, nil
-}
-
-// Close shuts down the log listener underlying connection.
-func (l *LogListener) Close() error {
- return l.conn.Close()
-}
diff --git a/net/netboot/netboot.go b/net/netboot/netboot.go
deleted file mode 100644
index 49ac636..0000000
--- a/net/netboot/netboot.go
+++ /dev/null
@@ -1,276 +0,0 @@
-// Copyright 2017 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.
-
-// This package implements the Zircon netboot protocol.
-//
-// TODO(fxb/35957): Add testing for this package.
-package netboot
-
-import (
- "bytes"
- "encoding/binary"
- "errors"
- "fmt"
- "net"
- "strings"
- "time"
-)
-
-// Magic constants used by the netboot protocol.
-const (
- magic = 0xAA774217 // see system/public/zircon/boot/netboot.h
-)
-
-// Port numbers used by the netboot protocol.
-const (
- serverPort = 33330 // netboot server port
- advertPort = 33331 // advertisement port
-)
-
-// Commands supported by the netboot protocol.
-const (
- cmdCommand = uint32(1) // command
- cmdSendFile = uint32(2) // send file
- cmdData = uint32(3) // data
- cmdBoot = uint32(4) // boot command
- cmdQuery = uint32(5) // query command
- cmdShell = uint32(6) // shell command
- cmdOpen = uint32(7) // open file
- cmdRead = uint32(8) // read data
- cmdWrite = uint32(9) // write data
- cmdClose = uint32(10) // close file
- cmdLastData = uint32(11) //
- cmdReboot = uint32(12) // reboot command
-)
-
-// Client implements the netboot protocol.
-type Client struct {
- Port int
- Cookie uint32
- Timeout time.Duration
- Wait bool
-}
-
-// netbootHeader is the netboot protocol message header.
-type netbootHeader struct {
- Magic uint32
- Cookie uint32
- Cmd uint32
- Arg uint32
-}
-
-// netbootMessage is the netboot protocol message.
-type netbootMessage struct {
- Header netbootHeader
- Data [1024]byte
-}
-
-// NewClient creates a new Client instance.
-func NewClient(timeout time.Duration) *Client {
- return &Client{
- Timeout: timeout,
- Cookie: uint32(0x12345678),
- }
-}
-
-// Discover resolves the address of host and returns either the netsvc or
-// Fuchsia address dependending on the value of fuchsia.
-func (n *Client) Discover(nodename string, fuchsia bool) (*net.UDPAddr, error) {
- conn, err := net.ListenUDP("udp6", &net.UDPAddr{IP: net.IPv6zero})
- if err != nil {
- return nil, fmt.Errorf("failed to bind to udp6 port: %v\n", err)
- }
- defer conn.Close()
-
- n.Cookie++
- req := netbootMessage{
- Header: netbootHeader{
- Magic: magic,
- Cookie: n.Cookie,
- Cmd: cmdQuery,
- Arg: 0,
- },
- }
- copy(req.Data[:], nodename)
-
- var buf bytes.Buffer
- if err := binary.Write(&buf, binary.LittleEndian, req); err != nil {
- return nil, err
- }
-
- // Enumerate all available network interfaces.
- ifaces, err := net.Interfaces()
- if err != nil {
- return nil, fmt.Errorf("failed to enumerate network interfaces: %v\n", err)
- }
- for _, iface := range ifaces {
- // Skip interfaces that are down.
- if iface.Flags&net.FlagUp == 0 {
- continue
- }
- // Skip loopback interfaces.
- if iface.Flags&net.FlagLoopback != 0 {
- continue
- }
- addrs, err := iface.Addrs()
- if err != nil {
- return nil, err
- }
-
- // Enumerate all interface addresses and find the usable ones.
- for _, addr := range addrs {
- var ip net.IP
- switch v := addr.(type) {
- case *net.IPNet:
- ip = v.IP
- case *net.IPAddr:
- ip = v.IP
- }
- if ip == nil || ip.To16() == nil {
- continue
- }
-
- // Send a broadcast query command using the interface.
- conn.WriteToUDP(buf.Bytes(), &net.UDPAddr{
- IP: net.IPv6linklocalallnodes,
- Port: serverPort,
- Zone: iface.Name,
- })
- }
- }
-
- // Wait for response, this is a best effort approach so we may not receive
- // any response if there're no usable interfaces or no devices connected.
- conn.SetReadDeadline(time.Now().Add(n.Timeout))
-
- b := make([]byte, 4096)
- _, addr, err := conn.ReadFromUDP(b)
- if err != nil {
- return nil, err
- }
-
- r := bytes.NewReader(b)
- var res netbootMessage
- if err := binary.Read(r, binary.LittleEndian, &res); err != nil {
- return nil, err
- }
-
- data, err := netbootString(res.Data[:])
- if err != nil {
- return nil, err
- }
- // The query packet payload contains the nodename.
- if data != nodename {
- return nil, fmt.Errorf("invalid nodename `%s`", data)
- }
-
- if fuchsia {
- // The netstack link-local address has 11th byte always set to 0xff, set
- // this byte to transform netsvc address to netstack address if needed.
- addr.IP[11] = 0xff
- }
-
- return addr, nil
-}
-
-// Beacon receives the beacon packet, returning the address of the sender.
-func (n *Client) Beacon() (*net.UDPAddr, error) {
- conn, err := net.ListenUDP("udp6", &net.UDPAddr{
- IP: net.IPv6zero,
- Port: advertPort,
- })
- defer conn.Close()
-
- conn.SetReadDeadline(time.Now().Add(n.Timeout))
-
- b := make([]byte, 4096)
- _, addr, err := conn.ReadFromUDP(b)
- if err != nil {
- return nil, err
- }
-
- r := bytes.NewReader(b)
- var res netbootMessage
- if err := binary.Read(r, binary.LittleEndian, &res); err != nil {
- return nil, err
- }
-
- data, err := netbootString(res.Data[:])
- if err != nil {
- return nil, err
- }
- // The query packet payload contains fields separated by ;.
- for _, f := range strings.Split(string(data[:]), ";") {
- // The field has a key=value format.
- vars := strings.SplitN(f, "=", 2)
- // The field with the "nodename" key contains the name of the device.
- if vars[0] == "nodename" {
- return addr, nil
- }
- }
-
- return nil, errors.New("no valid beacon")
-}
-
-// Boot sends a boot packet to the address.
-func (n *Client) Boot(addr *net.UDPAddr) error {
- n.Cookie++
- msg := &netbootHeader{
- Magic: magic,
- Cookie: n.Cookie,
- Cmd: cmdBoot,
- Arg: 0,
- }
- if err := sendPacket(msg, addr); err != nil {
- return fmt.Errorf("failed to send boot command: %v\n", err)
- }
- return nil
-}
-
-// Reboot sends a reboot packet the address.
-func (n *Client) Reboot(addr *net.UDPAddr) error {
- n.Cookie++
- msg := &netbootHeader{
- Magic: magic,
- Cookie: n.Cookie,
- Cmd: cmdReboot,
- Arg: 0,
- }
- if err := sendPacket(msg, addr); err != nil {
- return fmt.Errorf("failed to send reboot command: %v\n", err)
- }
- return nil
-}
-
-func sendPacket(msg *netbootHeader, addr *net.UDPAddr) error {
- if msg == nil {
- return errors.New("no message provided")
- }
- var buf bytes.Buffer
- if err := binary.Write(&buf, binary.LittleEndian, *msg); err != nil {
- return err
- }
-
- conn, err := net.ListenUDP("udp6", &net.UDPAddr{IP: net.IPv6zero})
- if err != nil {
- return fmt.Errorf("failed to create a socket: %v\n", err)
- }
- defer conn.Close()
-
- _, err = conn.WriteToUDP(buf.Bytes(), &net.UDPAddr{
- IP: addr.IP,
- Port: serverPort,
- Zone: addr.Zone,
- })
- return err
-}
-
-func netbootString(bs []byte) (string, error) {
- for i, b := range bs {
- if b == 0 {
- return string(bs[:i]), nil
- }
- }
- return "", errors.New("no null terminated string found")
-}
diff --git a/net/netutil/netutil.go b/net/netutil/netutil.go
deleted file mode 100644
index 7d3c840..0000000
--- a/net/netutil/netutil.go
+++ /dev/null
@@ -1,32 +0,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 netutil
-
-import (
- "context"
- "fmt"
- "net"
- "time"
-
- "go.fuchsia.dev/tools/lib/retry"
- "go.fuchsia.dev/tools/net/netboot"
-)
-
-// GetNodeAddress returns the UDP address corresponding to a given node, specifically
-// the netsvc or fuchsia address dependending on the value of `fuchsia`.
-func GetNodeAddress(ctx context.Context, nodename string, fuchsia bool) (*net.UDPAddr, error) {
- // Retry, as the netstack might not yet be up.
- var addr *net.UDPAddr
- var err error
- n := netboot.NewClient(time.Second)
- err = retry.Retry(ctx, retry.WithMaxDuration(&retry.ZeroBackoff{}, time.Minute), func() error {
- addr, err = n.Discover(nodename, fuchsia)
- return err
- }, nil)
- if err != nil {
- return nil, fmt.Errorf("cannot find node %q: %v", nodename, err)
- }
- return addr, nil
-}
diff --git a/net/serial/serial.go b/net/serial/serial.go
deleted file mode 100644
index da33de2..0000000
--- a/net/serial/serial.go
+++ /dev/null
@@ -1,25 +0,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 serial provides support for serial connections.
-package serial
-
-import (
- "io"
-)
-
-const (
- defaultBaudRate = 115200
- defaultTimeoutSecs = 10
-)
-
-// Open opens a new serial port using defaults.
-func Open(name string) (io.ReadWriteCloser, error) {
- return OpenWithOptions(name, defaultBaudRate, defaultTimeoutSecs)
-}
-
-// OpenWithOptions opens a new serial port with the given name and baud rate.
-func OpenWithOptions(name string, baudRate int, timeoutSecs int) (io.ReadWriteCloser, error) {
- return open(name, baudRate, timeoutSecs)
-}
diff --git a/net/serial/serial_darwin.go b/net/serial/serial_darwin.go
deleted file mode 100644
index 7e6f0ce..0000000
--- a/net/serial/serial_darwin.go
+++ /dev/null
@@ -1,14 +0,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 serial
-
-import (
- "errors"
- "io"
-)
-
-func open(name string, baudRate int, timeoutSecs int) (io.ReadWriteCloser, error) {
- return nil, errors.New("not supported")
-}
diff --git a/net/serial/serial_linux.go b/net/serial/serial_linux.go
deleted file mode 100644
index 22e7e17..0000000
--- a/net/serial/serial_linux.go
+++ /dev/null
@@ -1,84 +0,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 serial
-
-import (
- "fmt"
- "io"
- "os"
- "unsafe"
-
- "golang.org/x/sys/unix"
-)
-
-var supportedBaudRates = map[int]uint32{
- 50: unix.B50,
- 75: unix.B75,
- 110: unix.B110,
- 134: unix.B134,
- 150: unix.B150,
- 200: unix.B200,
- 300: unix.B300,
- 600: unix.B600,
- 1200: unix.B1200,
- 1800: unix.B1800,
- 2400: unix.B2400,
- 4800: unix.B4800,
- 9600: unix.B9600,
- 19200: unix.B19200,
- 38400: unix.B38400,
- 57600: unix.B57600,
- 115200: unix.B115200,
- 230400: unix.B230400,
- 460800: unix.B460800,
- 500000: unix.B500000,
- 576000: unix.B576000,
- 921600: unix.B921600,
- 1000000: unix.B1000000,
- 1152000: unix.B1152000,
- 1500000: unix.B1500000,
- 2000000: unix.B2000000,
- 2500000: unix.B2500000,
- 3000000: unix.B3000000,
- 3500000: unix.B3500000,
- 4000000: unix.B4000000,
-}
-
-func open(name string, baudRate int, timeoutSecs int) (io.ReadWriteCloser, error) {
- rate, ok := supportedBaudRates[baudRate]
- if !ok {
- return nil, fmt.Errorf("unsupported baud rate: %d", baudRate)
- }
- if timeoutSecs < 0 || (timeoutSecs*10) > (1<<8) {
- return nil, fmt.Errorf("timeout must be between 0 and 25, got: %d", timeoutSecs)
- }
- f, err := os.OpenFile(name, unix.O_RDWR|unix.O_NOCTTY|unix.O_NONBLOCK, 0666)
- if err != nil {
- return nil, err
- }
- t := unix.Termios{
- Iflag: unix.IGNPAR,
- Cflag: unix.CREAD | unix.CLOCAL | unix.CS8 | rate,
- Ispeed: rate,
- Ospeed: rate,
- }
- t.Cc[unix.VTIME] = uint8(timeoutSecs * 10)
- _, _, errno := unix.Syscall6(
- unix.SYS_IOCTL,
- uintptr(f.Fd()),
- uintptr(unix.TCSETS),
- uintptr(unsafe.Pointer(&t)),
- 0,
- 0,
- 0,
- )
- if errno != 0 {
- return nil, errno
- }
- if err := unix.SetNonblock(int(f.Fd()), false); err != nil {
- return nil, err
- }
- return f, nil
-}
diff --git a/net/sshutil/sshutil.go b/net/sshutil/sshutil.go
deleted file mode 100644
index 49dbc50..0000000
--- a/net/sshutil/sshutil.go
+++ /dev/null
@@ -1,151 +0,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 sshutil
-
-import (
- "context"
- "crypto/rand"
- "crypto/rsa"
- "crypto/x509"
- "encoding/pem"
- "fmt"
- "net"
- "time"
-
- "go.fuchsia.dev/tools/lib/retry"
- "go.fuchsia.dev/tools/net/netutil"
-
- "golang.org/x/crypto/ssh"
-)
-
-const (
- // Default SSH server port.
- SSHPort = 22
-
- // Default RSA key size.
- RSAKeySize = 2048
-
- // The allowed timeout to establish an SSH connection.
- connTimeout = 5 * time.Second
- // The total allowed timeout to establish an SSH connection and complete an auth handshake.
- totalDialTimeout = 10 * time.Second
-
- sshUser = "fuchsia"
-)
-
-// GeneratePrivateKey generates a private SSH key.
-func GeneratePrivateKey() ([]byte, error) {
- key, err := rsa.GenerateKey(rand.Reader, RSAKeySize)
- if err != nil {
- return nil, err
- }
- privateKey := &pem.Block{
- Type: "RSA PRIVATE KEY",
- Bytes: x509.MarshalPKCS1PrivateKey(key),
- }
- buf := pem.EncodeToMemory(privateKey)
-
- return buf, nil
-}
-
-func Connect(ctx context.Context, address net.Addr, config *ssh.ClientConfig) (*ssh.Client, error) {
- network, err := network(address)
- if err != nil {
- return nil, err
- }
-
- var client *ssh.Client
- // TODO: figure out optimal backoff time and number of retries
- if err := retry.Retry(ctx, retry.WithMaxDuration(&retry.ZeroBackoff{}, time.Minute), func() error {
- var err error
- client, err = dialWithTimeout(network, address.String(), config, totalDialTimeout)
- return err
- }, nil); err != nil {
- return nil, fmt.Errorf("cannot connect to address %q: %v", address, err)
- }
-
- return client, nil
-}
-
-// ssh.Dial can hang during authentication, the 'timeout' being set in the config only
-// applying to establishment of the initial connection. This function is effectively
-// ssh.Dial with the ability to set a deadline on the underlying connection.
-//
-// See https://github.com/golang/go/issues/21941 for more details on the hang.
-func dialWithTimeout(network, addr string, config *ssh.ClientConfig, timeout time.Duration) (*ssh.Client, error) {
- conn, err := net.DialTimeout(network, addr, config.Timeout)
- if err != nil {
- return nil, err
- }
- if err := conn.SetDeadline(time.Now().Add(timeout)); err != nil {
- conn.Close()
- return nil, err
- }
- c, chans, reqs, err := ssh.NewClientConn(conn, addr, config)
- if err != nil {
- conn.Close()
- return nil, err
- }
- if err := conn.SetDeadline(time.Time{}); err != nil {
- c.Close()
- return nil, err
- }
- return ssh.NewClient(c, chans, reqs), nil
-}
-
-// ConnectToNode connects to the device with the given nodename.
-func ConnectToNode(ctx context.Context, nodename string, config *ssh.ClientConfig) (*ssh.Client, error) {
- addr, err := netutil.GetNodeAddress(ctx, nodename, true)
- if err != nil {
- return nil, err
- }
- addr.Port = SSHPort
- return Connect(ctx, addr, config)
-}
-
-// DefaultSSHConfig returns a basic SSH client configuration.
-func DefaultSSHConfig(privateKey []byte) (*ssh.ClientConfig, error) {
- signer, err := ssh.ParsePrivateKey(privateKey)
- if err != nil {
- return nil, err
- }
- return DefaultSSHConfigFromSigners(signer)
-}
-
-// DefaultSSHConfigFromSigners returns a basic SSH client configuration.
-func DefaultSSHConfigFromSigners(signers ...ssh.Signer) (*ssh.ClientConfig, error) {
- return &ssh.ClientConfig{
- User: sshUser,
- Auth: []ssh.AuthMethod{ssh.PublicKeys(signers...)},
- Timeout: connTimeout,
- HostKeyCallback: ssh.InsecureIgnoreHostKey(),
- }, nil
-}
-
-// Returns the network to use to SSH into a device.
-func network(address net.Addr) (string, error) {
- var ip *net.IP
-
- // We need these type assertions because the net package (annoyingly) doesn't provide
- // an interface for objects that have an IP address.
- switch addr := address.(type) {
- case *net.UDPAddr:
- ip = &addr.IP
- case *net.TCPAddr:
- ip = &addr.IP
- case *net.IPAddr:
- ip = &addr.IP
- default:
- return "", fmt.Errorf("unsupported address type: %T", address)
- }
-
- if ip.To4() != nil {
- return "tcp", nil // IPv4
- }
- if ip.To16() != nil {
- return "tcp6", nil // IPv6
- }
- return "", fmt.Errorf("cannot infer network for IP address %s", ip.String())
-}
diff --git a/net/sshutil/sshutil_test.go b/net/sshutil/sshutil_test.go
deleted file mode 100644
index 458098b..0000000
--- a/net/sshutil/sshutil_test.go
+++ /dev/null
@@ -1,48 +0,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 sshutil
-
-import (
- "net"
- "testing"
-)
-
-func TestNetwork(t *testing.T) {
- tests := []struct {
- id int
- addr net.Addr
- family string
- wantErr bool
- }{
- // Valid tcp addresses.
- {1, &net.TCPAddr{IP: net.IPv4(1, 2, 3, 4)}, "tcp", false},
- {2, &net.UDPAddr{IP: net.IPv4(5, 6, 7, 8)}, "tcp", false},
- {3, &net.IPAddr{IP: net.IPv4(9, 10, 11, 12)}, "tcp", false},
-
- // Valid tcp6 addresses.
- {4, &net.TCPAddr{IP: net.IPv6loopback}, "tcp6", false},
- {5, &net.UDPAddr{IP: net.ParseIP("2001:db8::1")}, "tcp6", false},
- {6, &net.IPAddr{IP: net.IPv6linklocalallrouters}, "tcp6", false},
-
- // Invalid IP addresses
- {7, &net.TCPAddr{IP: net.IP("")}, "", true},
- {8, &net.UDPAddr{IP: net.IP("123456")}, "", true},
- {9, &net.IPAddr{IP: nil}, "", true},
-
- // Invalid net.AddrType
- {10, &net.UnixAddr{}, "", true},
- }
-
- for _, test := range tests {
- n, err := network(test.addr)
- if test.wantErr && err == nil {
- t.Errorf("Test %d: got no error; want error", test.id)
- } else if !test.wantErr && err != nil {
- t.Errorf("Test %d: got error %q; want no error", test.id, err)
- } else if n != test.family {
- t.Errorf("Test %d: got %q; want %q", test.id, n, test.family)
- }
- }
-}
diff --git a/net/tftp/tftp.go b/net/tftp/tftp.go
deleted file mode 100644
index 246808b..0000000
--- a/net/tftp/tftp.go
+++ /dev/null
@@ -1,563 +0,0 @@
-// Copyright 2017 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.
-
-// TODO(fxb/35958): Add testing for this package.
-package tftp
-
-import (
- "bytes"
- "encoding/binary"
- "errors"
- "fmt"
- "io"
- "net"
- "strconv"
- "strings"
- "time"
-)
-
-const (
- ServerPort = 33340 // tftp server port
- ClientPort = 33341 // tftp client port
-)
-
-const (
- timeout = 2 * time.Second // default client timeout
- retries = 5 // default number of retries
- blockSize = 1024 // default block size
- datagramSize = 1028 // default datagram size
- windowSize = 256 // default window size
-)
-
-const (
- opRrq = uint8(1) // read request (RRQ)
- opWrq = uint8(2) // write request (WRQ)
- opData = uint8(3) // data
- opAck = uint8(4) // acknowledgement
- opError = uint8(5) // error
- opOack = uint8(6) // option acknowledgment
-)
-
-const (
- ErrorUndefined = uint16(0) // Not defined, see error message (if any)
- ErrorFileNotFound = uint16(1) // File not found
- ErrorAccessViolation = uint16(2) // Access violation
- ErrorDiskFull = uint16(3) // Disk full or allocation exceeded
- ErrorIllegalOperation = uint16(4) // Illegal TFTP operation
- ErrorUnknownID = uint16(5) // Unknown transfer ID
- ErrorFileExists = uint16(6) // File already exists
- ErrorNoSuchUser = uint16(7) // No such user
- ErrorBadOptions = uint16(8) // Bad options
-
- // ErrorBusy is a Fuchsia-specific extension to the set of TFTP error
- // codes, meant to indicate that the server cannot currently handle a
- // request, but may be able to at some future time.
- ErrorBusy = uint16(0x143) // 'B' + 'U' + 'S' + 'Y'
-)
-
-var (
- ErrShouldWait = fmt.Errorf("target is busy")
-)
-
-type Client struct {
- Timeout time.Duration // duration to wait for the client to ack a packet
- Retries int // how many times a packet will be resent
- BlockSize uint16 // maximum block size used for file transfers
- WindowSize uint16 // window size used for file transfers.
-}
-
-func NewClient() *Client {
- return &Client{
- Timeout: timeout,
- Retries: retries,
- BlockSize: blockSize,
- WindowSize: windowSize,
- }
-}
-
-type options struct {
- timeout time.Duration
- blockSize uint16
- windowSize uint16
- transferSize int64
-}
-
-type Session interface {
- // Size returns the transfer size, if set.
- Size() int64
-
- // RemoteAddr returns the remote adderss.
- RemoteAddr() net.UDPAddr
-}
-
-// Send sends the filename of length size to server addr with the content being
-// read from the reader.
-//
-// If the server being talked to is running on Fuchsia, this method may return
-// ErrShouldWait, which indicates that the server is currently not ready to
-// handle a new request, but will be in the future. This case should be
-// explicitly handled by the caller.
-func (c *Client) Send(addr *net.UDPAddr, filename string, size int64) (io.ReaderFrom, error) {
- conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv6zero})
- if err != nil {
- return nil, fmt.Errorf("creating socket: %s", err)
- }
-
- var b bytes.Buffer
- writeRequest(&b, opWrq, filename, &options{
- timeout: c.Timeout,
- blockSize: c.BlockSize,
- windowSize: c.WindowSize,
- transferSize: size,
- })
-
- opts, addr, err := c.sendRequest(conn, addr, b.Bytes())
- if err != nil {
- conn.Close()
- if err == ErrShouldWait {
- return nil, ErrShouldWait
- }
- return nil, fmt.Errorf("sending WRQ: %s", err)
- }
- b.Reset()
- if opts.transferSize != size {
- err := errors.New("transfer size mismatch")
- abort(conn, addr, ErrorBadOptions, err)
- return nil, err
- }
-
- return &sender{
- addr: addr,
- conn: conn,
- opts: opts,
- retry: c.Retries,
- }, nil
-}
-
-type sender struct {
- addr *net.UDPAddr
- conn *net.UDPConn
- opts *options
- retry int
-}
-
-func (s *sender) Size() (n int64) {
- return s.opts.transferSize
-}
-
-func (s *sender) RemoteAddr() net.UDPAddr {
- return *s.addr
-}
-
-func (s *sender) ReadFrom(r io.Reader) (int64, error) {
- l, err := s.send(r)
- if err != nil {
- abort(s.conn, s.addr, ErrorUndefined, err)
- return l, err
- }
- s.conn.Close()
- return l, nil
-}
-
-func (s *sender) send(r io.Reader) (int64, error) {
- var n int64 // total number of bytes read
- var seq uint64 // sequence number denoting the beginning of window
- b := make([]byte, s.opts.blockSize+4)
- ra, ok := r.(io.ReaderAt)
- if !ok {
- return n, fmt.Errorf("%t does not implement io.ReaderAt", r)
- }
-
-Loop:
- for {
- Attempt:
- for attempt := 0; attempt < s.retry; attempt++ {
- for i := uint16(0); uint16(i) < s.opts.windowSize; i++ {
- block := uint16(seq + uint64(i+1))
- // Fill the first byte of the command with an arbitrary value to
- // work around the issue where certain ethernet adapters would
- // refuse to send the packet on retransmission.
- b[0] = uint8(attempt)
- b[1] = opData
- binary.BigEndian.PutUint16(b[2:], block)
- off := (seq + uint64(i)) * uint64(s.opts.blockSize)
- l, err := ra.ReadAt(b[4:], int64(off))
- n += int64(l)
- if err != nil && err != io.EOF {
- return n, fmt.Errorf("reading bytes for block %d: %s", block, err)
- }
- isEOF := err == io.EOF
- if _, err := s.conn.WriteToUDP(b[:l+4], s.addr); err != nil {
- return n, fmt.Errorf("sending block %d: %s", block, err)
- }
- if isEOF {
- break
- }
- }
-
- s.conn.SetReadDeadline(time.Now().Add(s.opts.timeout))
-
- for {
- m, addr, err := s.conn.ReadFromUDP(b[:])
- if err != nil {
- if t, ok := err.(net.Error); ok && t.Timeout() {
- continue Attempt
- }
- return n, err
- }
- if m < 4 { // packet too small
- continue
- }
- if !addr.IP.Equal(s.addr.IP) || addr.Port != s.addr.Port {
- continue
- }
- break
- }
-
- switch b[1] {
- case opAck:
- num := binary.BigEndian.Uint16(b[2:4])
- off := num - uint16(seq) // offset from the start of the window
- if off > 0 && off <= s.opts.windowSize {
- seq += uint64(off)
- if seq*uint64(s.opts.blockSize) >= uint64(s.opts.transferSize) {
- return n, nil // all data transferred, return number of bytes
- }
- }
- continue Loop
- case opError:
- msg, _, _ := netasciiString(b[4:])
- return n, fmt.Errorf("server aborted transfer: %s", msg)
- }
- }
- return n, errors.New("timeout waiting for ACK")
- }
-}
-
-// Receive reads the data from filename at the server addr with the content
-// being written to the writer.
-//
-// If the server being talked to is running on a Fuchsia, this method may return
-// ErrShouldWait, which indicates that the server is currently not ready to
-// handle a new request, but will be in the future. This case should be
-// explicitly handled by the caller.
-func (c *Client) Receive(addr *net.UDPAddr, filename string) (io.WriterTo, error) {
- conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv6zero})
- if err != nil {
- return nil, fmt.Errorf("creating socket: %s", err)
- }
-
- var b bytes.Buffer
- writeRequest(&b, opRrq, filename, &options{
- blockSize: c.BlockSize,
- timeout: c.Timeout,
- windowSize: c.WindowSize,
- })
- opts, addr, err := c.sendRequest(conn, addr, b.Bytes())
- if err != nil {
- conn.Close()
- if err == ErrShouldWait {
- return nil, ErrShouldWait
- }
- return nil, fmt.Errorf("sending RRQ: %s", err)
- }
- b.Reset()
- if err := acknowledge(conn, addr, uint16(0)); err != nil {
- return nil, fmt.Errorf("sending ACK: %s", err)
- }
-
- return &receiver{
- addr: addr,
- conn: conn,
- opts: opts,
- retry: c.Retries,
- }, nil
-}
-
-type receiver struct {
- addr *net.UDPAddr
- conn *net.UDPConn
- opts *options
- retry int
-}
-
-func (r *receiver) Size() (n int64) {
- return r.opts.transferSize
-}
-
-func (r *receiver) RemoteAddr() net.UDPAddr {
- return *r.addr
-}
-
-func (r *receiver) WriteTo(w io.Writer) (int64, error) {
- l, err := r.receive(w)
- if err != nil {
- abort(r.conn, r.addr, ErrorUndefined, err)
- return l, err
- }
- r.conn.Close()
- return l, nil
-}
-
-func (r *receiver) receive(w io.Writer) (int64, error) {
- var n int64
- var seq uint64
- recv := make([]byte, r.opts.blockSize+4)
-
-Loop:
- for {
- Attempt:
- for attempt := 0; attempt < r.retry; attempt++ {
- for i := uint16(0); i < uint16(r.opts.windowSize); i++ {
- r.conn.SetReadDeadline(time.Now().Add(r.opts.timeout))
-
- var m int
- for {
- var addr *net.UDPAddr
- var err error
- m, addr, err = r.conn.ReadFromUDP(recv[m:])
- if err != nil {
- if t, ok := err.(net.Error); ok && t.Timeout() {
- acknowledge(r.conn, r.addr, uint16(seq))
- continue Attempt
- }
- return n, err
- }
- if m < 4 { // packet too small
- continue
- }
- if !addr.IP.Equal(r.addr.IP) || addr.Port != r.addr.Port {
- continue
- }
- break
- }
-
- n += int64(m)
-
- switch recv[1] {
- case opData:
- if num := binary.BigEndian.Uint16(recv[2:4]); num != uint16(seq)+1 {
- if num-uint16(seq) < uint16(r.opts.windowSize) {
- acknowledge(r.conn, r.addr, uint16(seq))
- }
- continue Loop
- }
- seq++
- if _, err := w.Write(recv[4:m]); err != nil {
- return n, fmt.Errorf("writing bytes for block %d: %v", seq, err)
- }
- if m < int(r.opts.blockSize+4) {
- acknowledge(r.conn, r.addr, uint16(seq))
- return n, nil
- }
- case opError:
- msg, _, _ := netasciiString(recv[4:])
- return n, fmt.Errorf("server aborted transfer: %s", msg)
- }
- }
- // TODO: this should be addr
- acknowledge(r.conn, r.addr, uint16(seq))
- continue Loop
- }
- return n, errors.New("timeout waiting for DATA")
- }
-}
-
-func writeRequest(b *bytes.Buffer, op uint8, filename string, o *options) {
- b.WriteByte(0)
- b.WriteByte(op)
- writeString(b, filename)
- // Only support octet mode, because in practice that's the
- // only remaining sensible use of TFTP.
- writeString(b, "octet")
- writeOption(b, "tsize", o.transferSize)
- writeOption(b, "blksize", int64(o.blockSize))
- writeOption(b, "timeout", int64(o.timeout/time.Second))
- writeOption(b, "windowsize", int64(o.windowSize))
-}
-
-func writeOption(b *bytes.Buffer, name string, value int64) {
- writeString(b, name)
- writeString(b, strconv.FormatInt(value, 10))
-}
-
-func writeString(b *bytes.Buffer, s string) {
- b.WriteString(s)
- b.WriteByte(0)
-}
-
-func (c *Client) sendRequest(conn *net.UDPConn, addr *net.UDPAddr, b []byte) (*options, *net.UDPAddr, error) {
- for attempt := 0; attempt < c.Retries; attempt++ {
- if _, err := conn.WriteToUDP(b, addr); err != nil {
- return nil, nil, err
- }
-
- conn.SetReadDeadline(time.Now().Add(c.Timeout))
-
- var recv [256]byte
- for {
- n, addr, err := conn.ReadFromUDP(recv[:])
- if err != nil {
- if t, ok := err.(net.Error); ok && t.Timeout() {
- break
- }
- return nil, nil, err
- }
-
- if n < 4 { // packet too small
- continue
- }
- switch recv[1] {
- case opError:
- // Handling ErrorBusy here is a Fuchsia-specific extension.
- if code := binary.BigEndian.Uint16(recv[2:4]); code == ErrorBusy {
- return nil, addr, ErrShouldWait
- }
- msg, _, _ := netasciiString(recv[4:n])
- return nil, addr, fmt.Errorf("server aborted transfer: %s", msg)
- case opOack:
- options, err := parseOACK(recv[2:n])
- return options, addr, err
- }
- }
- }
-
- return nil, nil, errors.New("timeout waiting for OACK")
-}
-
-func parseOACK(bs []byte) (*options, error) {
- var o options
-
- for len(bs) > 0 {
- opt, rest, err := netasciiString(bs)
- if err != nil {
- return nil, fmt.Errorf("reading option name: %s", err)
- }
- bs = rest
- val, rest, err := netasciiString(bs)
- if err != nil {
- return nil, fmt.Errorf("reading option %q value: %s", opt, err)
- }
- bs = rest
- switch strings.ToLower(opt) {
- case "blksize":
- size, err := strconv.ParseUint(val, 10, 16)
- if err != nil {
- return nil, fmt.Errorf("unsupported block size %q", val)
- }
- o.blockSize = uint16(size)
- case "timeout":
- seconds, err := strconv.ParseUint(val, 10, 8)
- if err != nil {
- return nil, fmt.Errorf("unsupported timeout %q", val)
- }
- o.timeout = time.Second * time.Duration(seconds)
- case "tsize":
- size, err := strconv.ParseUint(val, 10, 64)
- if err != nil {
- return nil, fmt.Errorf("unsupported transfer size %q", val)
- }
- o.transferSize = int64(size)
- case "windowsize":
- size, err := strconv.ParseUint(val, 10, 16)
- if err != nil {
- return nil, fmt.Errorf("unsupported window size %q", val)
- }
- o.windowSize = uint16(size)
- }
- }
-
- return &o, nil
-}
-
-func acknowledge(conn *net.UDPConn, addr *net.UDPAddr, seq uint16) error {
- var b bytes.Buffer
- b.WriteByte(0)
- b.WriteByte(opAck)
- if err := binary.Write(&b, binary.BigEndian, seq); err != nil {
- return fmt.Errorf("writing seqnum: %v", err)
- }
- if _, err := conn.WriteToUDP(b.Bytes(), addr); err != nil {
- return err
- }
- return nil
-}
-
-func abort(conn *net.UDPConn, addr *net.UDPAddr, code uint16, err error) error {
- var b bytes.Buffer
- b.WriteByte(0)
- b.WriteByte(opError)
- if binary.Write(&b, binary.BigEndian, code); err != nil {
- return fmt.Errorf("writing code: %v", err)
- }
- b.WriteString(err.Error())
- b.WriteByte(0)
- if _, err := conn.WriteToUDP(b.Bytes(), addr); err != nil {
- return err
- }
- conn.Close()
- return nil
-}
-
-func netasciiString(bs []byte) (string, []byte, error) {
- for i, b := range bs {
- if b == 0 {
- return string(bs[:i]), bs[i+1:], nil
- } else if b < 0x20 || b > 0x7e {
- return "", nil, fmt.Errorf("invalid netascii byte %q at offset %d", b, i)
- }
- }
- return "", nil, errors.New("no null terminated string found")
-}
-
-// WindowReader supports reading bytes from an underlying stream using
-// fixed sized blocks and rewinding back up to slots blocks.
-type WindowReader struct {
- buf []byte // buffer space
- len []int // len of data written to each slot
- reader io.Reader // underlying reader object
- current int // current block to be read
- head int // head of buffer
- slots int // number of slots
- size int // size of the slot
-}
-
-// NewWindowReader creates a new reader with slots blocks of size.
-func NewWindowReader(reader io.Reader, slots int, size int) *WindowReader {
- return &WindowReader{
- buf: make([]byte, slots*size),
- len: make([]int, slots),
- reader: reader,
- slots: slots,
- size: size,
- }
-}
-
-func (r *WindowReader) Read(p []byte) (int, error) {
- slot := r.current % r.slots
- offset := slot * r.size
-
- if r.current != r.head {
- len := offset + r.len[slot]
- n := copy(p, r.buf[offset:len])
- r.current++
- return n, nil
- }
-
- n, err := r.reader.Read(p)
- if err != nil {
- return n, err
- }
- n = copy(r.buf[offset:offset+n], p[:n])
- r.len[slot] = n
-
- r.current++
- r.head = r.current
- return n, err
-}
-
-// Unread rewinds the reader back by n blocks.
-func (r *WindowReader) Unread(n int) {
- r.current -= n
-}
diff --git a/qemu/config.go b/qemu/config.go
deleted file mode 100644
index 3ca096d..0000000
--- a/qemu/config.go
+++ /dev/null
@@ -1,230 +0,0 @@
-// 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 qemu
-
-import (
- "fmt"
- "os"
- "path/filepath"
- "strings"
-)
-
-const (
- DefaultNetwork = "10.0.2.0/24"
- DefaultDHCPStart = "10.0.2.15"
- DefaultGateway = "10.0.2.2"
- DefaultDNS = "10.0.2.3"
-)
-
-const (
- TargetAArch64 = "aarch64"
- TargetX86_64 = "x86_64"
-)
-
-type Drive struct {
- // ID is the block device identifier.
- ID string
-
- // File is the disk image file.
- File string
-
- // Addr is the PCI address of the block device.
- Addr string
-}
-
-type Forward struct {
- // HostPort is the port on the host.
- HostPort int
-
- // GuestPort is the port on the guest.
- GuestPort int
-}
-
-type Netdev struct {
- // ID is the network device identifier.
- ID string
-
- // User is a netdev user backend.
- User *NetdevUser
-
- // Tap is a netdev tap backend.
- Tap *NetdevTap
-
- // MAC is the network device MAC address.
- MAC string
-}
-
-// NetdevUser defines a netdev backend giving user networking.
-type NetdevUser struct {
- // Network is the network block.
- Network string
-
- // DHCPStart is the address at which the DHCP allocation starts.
- DHCPStart string
-
- // DNS is the address of the builtin DNS server.
- DNS string
-
- // Host is the host IP address.
- Host string
-
- // Forwards are the host forwardings.
- Forwards []Forward
-}
-
-// NetdevTap defines a netdev backend giving a tap interface.
-type NetdevTap struct {
- // Name is the name of the interface.
- Name string
-}
-
-// Config gives a high-level configuration for QEMU on Fuchsia.
-type Config struct {
- // QEMUBin is a path to the QEMU binary.
- Binary string
-
- // Target is the QEMU target (e.g., "x86_64" or "aarch64").
- Target string
-
- // CPU is the number of CPUs.
- CPU int
-
- // Memory is the amount of RAM.
- Memory int
-
- // KVM gives whether to enable KVM.
- KVM bool
-
- // Kernel is the path to the kernel image.
- Kernel string
-
- // Initrd is the path to the initrd image.
- Initrd string
-
- // Drives are drives to mount inside the QEMU instance.
- Drives []Drive
-
- // Networks are networks to set up inside the QEMU instance.
- Networks []Netdev
-}
-
-// CreateInvocation creates a QEMU invocation given a particular configuration, a list of
-// images, and any specified command-line arguments.
-func CreateInvocation(cfg Config, cmdlineArgs []string) ([]string, error) {
- if _, err := os.Stat(cfg.Binary); err != nil {
- return nil, fmt.Errorf("QEMU binary not found: %v", err)
- }
- absBinaryPath, err := filepath.Abs(cfg.Binary)
- if err != nil {
- return nil, err
- }
-
- invocation := []string{absBinaryPath}
-
- switch cfg.Target {
- case TargetAArch64:
- if cfg.KVM {
- invocation = append(invocation, "-machine", "virt,gic_version=host")
- invocation = append(invocation, "-cpu", "host")
- invocation = append(invocation, "-enable-kvm")
- } else {
- invocation = append(invocation, "-machine", "virt,gic_version=3")
- invocation = append(invocation, "-machine", "virtualization=true")
- invocation = append(invocation, "-cpu", "cortex-a53")
- }
- case TargetX86_64:
- invocation = append(invocation, "-machine", "q35")
- // TODO: this is Fuchsia specific, factor it out as another device struct.
- // Necessary for userboot.shutdown to trigger properly, since it writes
- // to 0xf4 to debug-exit in QEMU.
- invocation = append(invocation, "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04")
- if cfg.KVM {
- invocation = append(invocation, "-cpu", "host")
- invocation = append(invocation, "-enable-kvm")
- } else {
- invocation = append(invocation, "-cpu", "Haswell,+smap,-check,-fsgsbase")
- }
- default:
- return nil, fmt.Errorf("cpu %q not recognized", cfg.Target)
- }
-
- invocation = append(invocation, "-m", fmt.Sprintf("%d", cfg.Memory))
- invocation = append(invocation, "-smp", fmt.Sprintf("%d", cfg.CPU))
- invocation = append(invocation, "-nographic")
- invocation = append(invocation, "-serial", "stdio")
- invocation = append(invocation, "-monitor", "none")
-
- invocation = append(invocation, "-kernel", cfg.Kernel)
- invocation = append(invocation, "-initrd", cfg.Initrd)
-
- // TODO: maybe we should introduce Device interface with three different
- // implementations: Drive, Netdev and ISADebugExit to cleanup the code
- // below a bit.
-
- for i, d := range cfg.Drives {
- invocation = append(invocation, "-object", fmt.Sprintf("iothread,id=iothread%d", i))
-
- var drive strings.Builder
- fmt.Fprintf(&drive, "id=%s,file=%s,format=raw,if=none,cache=unsafe,aio=threads", d.ID, d.File)
- invocation = append(invocation, "-drive", drive.String())
-
- var device strings.Builder
- fmt.Fprintf(&device, "virtio-blk-pci,drive=%s,iothread=iothread%d", d.ID, i)
- if d.Addr != "" {
- fmt.Fprintf(&device, ",addr=%s", d.Addr)
- }
- invocation = append(invocation, "-device", device.String())
- }
-
- for _, n := range cfg.Networks {
- if n.ID == "" {
- return nil, fmt.Errorf("a network must have an ID")
- }
-
- var netdev strings.Builder
- if n.Tap != nil {
- if n.Tap.Name == "" {
- return nil, fmt.Errorf("network %q must specify a TAP interface name", n.ID)
- }
- // Overwrite any default configuration scripts with none; there is not currently a
- // good use case for these parameters.
- fmt.Fprintf(&netdev, "tap,id=%s,ifname=%s,script=no,downscript=no", n.ID, n.Tap.Name)
- } else if n.User != nil {
- fmt.Fprintf(&netdev, "user,id=%s", n.ID)
- if n.User.Network != "" {
- fmt.Fprintf(&netdev, ",net=%s", n.User.Network)
- }
- if n.User.DHCPStart != "" {
- fmt.Fprintf(&netdev, ",dhcpstart=%s", n.User.DHCPStart)
- }
- if n.User.DNS != "" {
- fmt.Fprintf(&netdev, ",dns=%s", n.User.DNS)
- }
- if n.User.Host != "" {
- fmt.Fprintf(&netdev, ",host=%s", n.User.Host)
- }
- for _, f := range n.User.Forwards {
- fmt.Fprintf(&netdev, ",hostfwd=tcp::%d-:%d", f.HostPort, f.GuestPort)
- }
- } else {
- return nil, fmt.Errorf("network %q must specify a netdev backend", n.ID)
- }
- invocation = append(invocation, "-netdev", netdev.String())
-
- var device strings.Builder
- fmt.Fprintf(&device, "virtio-net-pci,netdev=%s", n.ID)
- if n.MAC != "" {
- fmt.Fprintf(&device, ",mac=%s", n.MAC)
- }
- invocation = append(invocation, "-device", device.String())
- }
- // Treat the absense of specified networks as a directive to disable networking entirely.
- if len(cfg.Networks) == 0 {
- invocation = append(invocation, "-net", "none")
- }
-
- invocation = append(invocation, "-append", strings.Join(cmdlineArgs, " "))
- return invocation, nil
-}
diff --git a/qemu/exit.go b/qemu/exit.go
deleted file mode 100644
index 9d1a76a..0000000
--- a/qemu/exit.go
+++ /dev/null
@@ -1,49 +0,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 qemu
-
-import (
- "fmt"
- "log"
- "os/exec"
- "syscall"
-)
-
-// QEMUSuccessCodes encodes all possible QEMU exit codes that signify successful shutdown.
-var qemuSuccessCodes = map[int]bool{
- 0: true,
-
- // QEMU returns 31 to signify a graceful shutdown when running Zircon's core-tests on
- // x64 architecture. It writes a value to a specially declared I/O port, which is used
- // to compute the return code as `(<value> << 1) | 1` (always odd and non-zero):
- // 31 is that magic return code.
- 31: true,
-}
-
-// CheckExitCode checks whether a given error encodes a successful QEMU exit code.
-func CheckExitCode(err error) error {
- if err == nil {
- return nil
- }
-
- if exitErr, ok := err.(*exec.ExitError); ok {
- // This works on both Unix and Windows. Although the syscall package is generally
- // platform dependent, WaitStatus has a void-to-int ExitStatus() method in both cases.
- if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
- exitCode := status.ExitStatus()
- _, ok = qemuSuccessCodes[exitCode]
- if ok {
- log.Printf("successful QEMU exit status: %d", exitCode)
- return nil
- } else {
- return fmt.Errorf("unsuccessful QEMU exit status: %d", exitCode)
- }
- } else {
- return fmt.Errorf("could not derive exit code from associated os.ProcessState: %v", err)
- }
- } else {
- return fmt.Errorf("provided error is not of type *exec.ExitError: %v", err)
- }
-}
diff --git a/testing/runtests/poll.go b/testing/runtests/poll.go
deleted file mode 100644
index 0fbb21b..0000000
--- a/testing/runtests/poll.go
+++ /dev/null
@@ -1,115 +0,0 @@
-// 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 runtests
-
-import (
- "archive/tar"
- "bytes"
- "context"
- "encoding/json"
- "fmt"
- "io"
- "net"
- "os"
- "path"
- "path/filepath"
- "time"
-
- "go.fuchsia.dev/tools/botanist"
- "go.fuchsia.dev/tools/lib/logger"
- "go.fuchsia.dev/tools/lib/retry"
- "go.fuchsia.dev/tools/lib/tarutil"
- "go.fuchsia.dev/tools/net/tftp"
-)
-
-// PollForSummary polls a node waiting for a summary.json to be written; this relies on
-// runtests having been run on target.
-func PollForSummary(ctx context.Context, addr *net.UDPAddr, summaryFilename, testResultsDir, outputArchive string, filePollInterval time.Duration) error {
- t := tftp.NewClient()
- tftpAddr := &net.UDPAddr{
- IP: addr.IP,
- Port: tftp.ClientPort,
- Zone: addr.Zone,
- }
- var buffer bytes.Buffer
- var writer io.WriterTo
- var err error
- err = retry.Retry(ctx, retry.NewConstantBackoff(filePollInterval), func() error {
- writer, err = t.Receive(tftpAddr, path.Join(testResultsDir, summaryFilename))
- return err
- }, nil)
- if err != nil {
- return fmt.Errorf("timed out waiting for tests to complete: %v", err)
- }
-
- logger.Debugf(ctx, "reading %q\n", summaryFilename)
-
- if _, err := writer.WriteTo(&buffer); err != nil {
- return fmt.Errorf("failed to receive summary file: %v", err)
- }
-
- // Parse and save the summary.json file.
- var result TestSummary
- if err := json.Unmarshal(buffer.Bytes(), &result); err != nil {
- return fmt.Errorf("cannot unmarshall test results: %v", err)
- }
-
- outFile, err := os.OpenFile(outputArchive, os.O_WRONLY|os.O_CREATE, 0666)
- if err != nil {
- return fmt.Errorf("failed to create file %s: %v", outputArchive, err)
- }
-
- tw := tar.NewWriter(outFile)
- defer tw.Close()
-
- if err = tarutil.TarBuffer(tw, buffer.Bytes(), summaryFilename); err != nil {
- return err
- }
-
- logger.Debugf(ctx, "copying test output\n")
-
- // Tar in a subroutine while busy-printing so that we do not hit an i/o timeout when
- // dealing with large files.
- c := make(chan error)
- go func() {
- // Copy test output from the node.
- for _, output := range result.Outputs {
- remote := filepath.Join(testResultsDir, output)
- if err = botanist.FetchAndArchiveFile(t, tftpAddr, tw, remote, output); err != nil {
- c <- err
- return
- }
- }
- for _, test := range result.Tests {
- remote := filepath.Join(testResultsDir, test.OutputFile)
- if err = botanist.FetchAndArchiveFile(t, tftpAddr, tw, remote, test.OutputFile); err != nil {
- c <- err
- return
- }
- // Copy data sinks if any are present.
- for _, sinks := range test.DataSinks {
- for _, sink := range sinks {
- remote := filepath.Join(testResultsDir, sink.File)
- if err = botanist.FetchAndArchiveFile(t, tftpAddr, tw, remote, sink.File); err != nil {
- c <- err
- return
- }
- }
- }
- }
- c <- nil
- }()
-
- logger.Debugf(ctx, "tarring test output...\n")
- ticker := time.NewTicker(5 * time.Second)
- for {
- select {
- case err := <-c:
- ticker.Stop()
- return err
- case <-ticker.C:
- logger.Debugf(ctx, "tarring test output...\n")
- }
- }
-}
diff --git a/testing/runtests/runtests.go b/testing/runtests/runtests.go
deleted file mode 100644
index bcb426e..0000000
--- a/testing/runtests/runtests.go
+++ /dev/null
@@ -1,62 +0,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 runtests contains specifics related to the runtests command.
-package runtests
-
-// TestResult is the exit result of a test.
-type TestResult string
-
-const (
- // TestSummaryFilename is the summary file name expected by the fuchsia
- // recipe module.
- TestSummaryFilename = "summary.json"
-
- // TestOutputFilename is the default output file name for a test.
- TestOutputFilename = "stdout-and-stderr.txt"
-
- // TestSuccess represents a passed test.
- TestSuccess TestResult = "PASS"
-
- // TestFailure represents a failed test.
- TestFailure TestResult = "FAIL"
-)
-
-// TestSummary is a summary of a suite of test runs. It represents the output
-// file format of a runtests invocation.
-type TestSummary struct {
- // Tests is a list of the details of the test runs.
- Tests []TestDetails `json:"tests"`
-
- // Outputs gives the suite-wide outputs, mapping canonical name of the
- // output to its path.
- Outputs map[string]string `json:"outputs,omitempty"`
-}
-
-// DataSink is a data sink exported by the test.
-type DataSink struct {
- // Name is the name of the sink.
- Name string `json:"name"`
-
- // File is the file containing the sink data.
- File string `json:"file"`
-}
-
-// TestDetails contains the details of a test run.
-type TestDetails struct {
- // Name is the name of the test.
- Name string `json:"name"`
-
- // OutputFile is a file containing the test's output (stdout/stderr).
- OutputFile string `json:"output_file"`
-
- // Result is the result of the test.
- Result TestResult `json:"result"`
-
- // DataSinks gives the data sinks attached to a test.
- DataSinks map[string][]DataSink `json:"data_sinks,omitempty"`
-
- // Duration is how long the test execution took.
- DurationMillis int64 `json:"duration_milliseconds"`
-}
diff --git a/testing/seriallistener/cmd/main.go b/testing/seriallistener/cmd/main.go
deleted file mode 100644
index d61ef89..0000000
--- a/testing/seriallistener/cmd/main.go
+++ /dev/null
@@ -1,141 +0,0 @@
-// 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
-
-// Program to watch for a specific string to appear in a socket and
-// then exits with code 0. As long as the file is being actively written
-// to the tool will keep watching, but after ioTimeout of inactivity it
-// will fail.
-
-// To manually test, do something like the following.
-//
-// $ SOCKET=socket
-// $ rm $SOCKET &> /dev/null || true
-// $ go run main.go -socket $SOCKET -success-string FOO &
-// $ nc -U $SOCKET -l
-//
-// Any lines typed into stdin of nc will be sent to the this program. When
-// the success string is sent, this program will exit, closing the socket,
-// and causing nc to exit. Likewise, both programs will exit when the
-// timeout is reached.
-
-import (
- "context"
- "errors"
- "flag"
- "fmt"
- "net"
- "os"
- "strings"
- "time"
-
- "go.fuchsia.dev/tools/lib/color"
- "go.fuchsia.dev/tools/lib/logger"
- "go.fuchsia.dev/tools/lib/retry"
-)
-
-var (
- ioTimeout time.Duration
- socket string
- successString string
- colors color.EnableColor
- level logger.LogLevel
-)
-
-const serialSocketVar = "FUCHSIA_SERIAL_SOCKET"
-
-func init() {
- flag.DurationVar(&ioTimeout, "io-timeout", 30*time.Second,
- "amount of time to wait for new data (seconds)")
-
- defaultSocket := os.Getenv(serialSocketVar)
- flag.StringVar(&socket, "socket", defaultSocket,
- fmt.Sprintf("unix socket to watch (required if %s not set)",
- serialSocketVar))
-
- flag.StringVar(&successString, "success-string", "",
- "string which means the test passed")
-
- colors = color.ColorAuto
- flag.Var(&colors, "color",
- "use color in output, can be never, auto, always")
-
- level = logger.TraceLevel
- flag.Var(&level, "level",
- "output verbosity, can be fatal, error, warning, info, "+
- "debug or trace")
-}
-
-func max(x int, y int) int {
- if x < y {
- return y
- }
- return x
-}
-
-func Main(ctx context.Context) error {
- if socket == "" {
- flag.Usage()
- return errors.New("no socket specified")
- }
- logger.Infof(ctx, "socket: %s", socket)
-
- if successString == "" {
- flag.Usage()
- return errors.New("no success string specified")
- }
- logger.Infof(ctx, "success string: %#v", successString)
-
- logger.Infof(ctx, "io-timeout: %v", ioTimeout)
-
- // Connect to the socket, retry for up to a minute.
- var err error
- var conn net.Conn
- backoff := retry.WithMaxDuration(
- retry.NewConstantBackoff(time.Second), time.Minute)
- retry.Retry(ctx, backoff, func() error {
- conn, err = net.DialTimeout("unix", socket, 5*time.Second)
- return err
- }, nil)
- if err != nil {
- return err
- }
- defer conn.Close()
- logger.Infof(ctx, "connection successful")
-
- buf := make([]byte, 4096)
- currPage := ""
-
- // Repeatedly read from the socket, setting a timeout before every Read().
- for {
- conn.SetReadDeadline(time.Now().Add(ioTimeout))
- n, err := conn.Read(buf)
- if err != nil {
- return err
- }
-
- currPage = currPage[max(0, len(currPage)-len(successString)):]
- currPage += string(buf[0:n])
-
- logger.Infof(ctx, "currPage: %#v", currPage)
- if strings.Contains(currPage, successString) {
- logger.Infof(ctx, "success string found")
- return nil
- }
- }
-}
-
-func main() {
- flag.Parse()
-
- log := logger.NewLogger(level, color.NewColor(colors),
- os.Stdout, os.Stderr, "seriallistener ")
- ctx := logger.WithLogger(context.Background(), log)
-
- err := Main(ctx)
- if err != nil {
- logger.Fatalf(ctx, "%s", err)
- }
-}
diff --git a/testing/tap/parser.go b/testing/tap/parser.go
deleted file mode 100644
index 3bbdc01..0000000
--- a/testing/tap/parser.go
+++ /dev/null
@@ -1,262 +0,0 @@
-// 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 tap
-
-import (
- "errors"
- "fmt"
- "log"
- "strconv"
- "strings"
-
- "go.fuchsia.dev/tools/testing/tap/tokenizer"
-)
-
-// Parse parses the given input string into a Document. The input is allowed to contain
-// garbage lines; The parser will skip them and parse as much of the input as possible.
-// The only execption is that the first line of input must be a TAP version header of the
-// form "TAP version XXX".
-func Parse(input []byte) (*Document, error) {
- output := make(chan *Document)
- go parse(tokenizer.NewTokenStream(input), output)
- return <-output, nil
-}
-
-// State represents a parser state. Each state takes the current stream of input tokens
-// and the current Document and attempts to parse the next line of input. A state must
-// return the next state to use, even when an error is encountered. If nil is returned,
-// parsing stops.
-type state func(*tokenizer.TokenStream, *Document) (state, error)
-
-// Parse parses a Document from the given Token stream. The result is emitted on the
-// output channel.
-func parse(tokens *tokenizer.TokenStream, output chan<- *Document) {
- document := &Document{}
-
- for state := parseVersion; state != nil; {
- next, err := state(tokens, document)
- if err != nil {
- // Garbage lines are allowed; Treat errors as non-fatal.
- log.Println(err)
- }
- state = next
- }
-
- output <- document
-}
-
-// DiscardLine is a parser state that throws away every token until a newline or EOF.
-func discardLine(tokens *tokenizer.TokenStream, _ *Document) (state, error) {
- for {
- token := tokens.Peek()
- switch {
- case token.Type == tokenizer.TypeEOF:
- return nil, nil
- case token.Type != tokenizer.TypeNewline:
- tokens.Next()
- default:
- tokens.Next() // Skip the newline.
- return parseNextLine, nil
- }
- }
-}
-
-func parseNextLine(tokens *tokenizer.TokenStream, doc *Document) (state, error) {
- rtokens := tokens.Raw()
- if rtokens.Peek().Type == tokenizer.TypeEOF {
- return nil, nil
- }
-
- if rtokens.Peek().Type == tokenizer.TypeSpace {
- return parseYAMLBlock, nil
- }
-
- if rtokens.Peek().Type == tokenizer.TypeNumber {
- return parsePlan, nil
- }
-
- if rtokens.Peek().Value == "ok" || rtokens.Peek().Value == "not" {
- return parseTestLine, nil
- }
-
- return parseNextLine, unexpectedTokenError("one of 'ok', 'not' or a number", tokens.Next())
-}
-
-func parseVersion(tokens *tokenizer.TokenStream, doc *Document) (state, error) {
- token := tokens.Next()
- if token.Value != "TAP" {
- return nil, unexpectedTokenError("'TAP'", token)
- }
-
- token = tokens.Next()
- if token.Value != "version" {
- return nil, unexpectedTokenError("'version'", token)
- }
-
- token = tokens.Next()
- if token.Type != tokenizer.TypeNumber {
- return nil, unexpectedTokenError("a version number", token)
- }
-
- version, err := strconv.ParseInt(token.Value, 10, 64)
- if err != nil {
- return nil, parserError(err.Error())
- }
-
- doc.Version = Version(version)
- return parseNextLine, tokens.Eat(tokenizer.TypeNewline)
-}
-
-func parsePlan(tokens *tokenizer.TokenStream, doc *Document) (state, error) {
- if doc.Plan.Start != 0 || doc.Plan.End != 0 {
- return discardLine, errors.New("plan has already been parsed")
- }
-
- token := tokens.Peek()
- if token.Type != tokenizer.TypeNumber {
- return discardLine, unexpectedTokenError("a number", token)
- }
-
- start, err := strconv.ParseInt(tokens.Next().Value, 10, 64)
- if err != nil {
- return discardLine, parserError(err.Error())
- }
-
- if err := tokens.Eat(tokenizer.TypeDot); err != nil {
- return discardLine, err
- }
-
- if err := tokens.Eat(tokenizer.TypeDot); err != nil {
- return discardLine, err
- }
-
- token = tokens.Peek()
- if token.Type != tokenizer.TypeNumber {
- return discardLine, unexpectedTokenError("a number > 1", token)
- }
-
- end, err := strconv.ParseInt(tokens.Next().Value, 10, 64)
- if err != nil {
- return discardLine, parserError(err.Error())
- }
-
- doc.Plan = Plan{Start: int(start), End: int(end)}
- return parseNextLine, tokens.Eat(tokenizer.TypeNewline)
-}
-
-func parseTestLine(tokens *tokenizer.TokenStream, doc *Document) (state, error) {
- var testLine TestLine
-
- // Parse test status.
- token := tokens.Next()
- switch token.Value {
- case "not":
- testLine.Ok = false
- token = tokens.Next()
- if token.Value != "ok" {
- return discardLine, unexpectedTokenError("'ok'", token)
- }
- case "ok":
- testLine.Ok = true
- default:
- return discardLine, unexpectedTokenError("'ok' or 'not ok'", token)
- }
-
- // Parse optional test number.
- testLine.Count = len(doc.TestLines) + 1
- if tokens.Peek().Type == tokenizer.TypeNumber {
- count, err := strconv.ParseInt(tokens.Next().Value, 10, 64)
- if err != nil {
- return discardLine, parserError(err.Error())
- }
- testLine.Count = int(count)
- }
-
- // Parse optional description. Stop at a TypePound token which marks the start of a
- // diagnostic.
- description := tokens.Raw().ConcatUntil(tokenizer.TypePound, tokenizer.TypeNewline)
- testLine.Description = strings.TrimSpace(description)
-
- switch tokens.Peek().Type {
- case tokenizer.TypeEOF:
- doc.TestLines = append(doc.TestLines, testLine)
- return nil, nil
- case tokenizer.TypeNewline:
- doc.TestLines = append(doc.TestLines, testLine)
- return discardLine, nil
- case tokenizer.TypePound:
- tokens.Eat(tokenizer.TypePound)
- }
-
- // Parse optional directive.
- token = tokens.Next()
- switch token.Value {
- case "TODO":
- testLine.Directive = Todo
- case "SKIP":
- testLine.Directive = Skip
- default:
- return discardLine, unexpectedTokenError("a directive", token)
- }
-
- // Parse explanation.
- explanation := tokens.Raw().ConcatUntil(tokenizer.TypeNewline)
- testLine.Explanation = strings.TrimSpace(explanation)
- doc.TestLines = append(doc.TestLines, testLine)
-
- if tokens.Peek().Type == tokenizer.TypeEOF {
- return nil, nil
- }
- tokens.Eat(tokenizer.TypeNewline)
- return parseNextLine, nil
-}
-
-// Parses a YAML block. The block must begin as a line containing three dashes and end
-// with a line containing three dots.
-func parseYAMLBlock(tokens *tokenizer.TokenStream, doc *Document) (state, error) {
- rtokens := tokens.Raw()
- if len(doc.TestLines) == 0 {
- return discardLine, parserError("found YAML with no parent test line")
- }
- testLine := &doc.TestLines[len(doc.TestLines)-1]
- if len(testLine.YAML) > 0 {
- return discardLine, parserError("found YAML with no parent test line")
- }
-
- // Expect the header to match /\s+---/
- header := rtokens.ConcatUntil(tokenizer.TypeNewline)
- if len(header) < 4 || !strings.HasPrefix(strings.TrimSpace(header), "---") {
- return discardLine, fmt.Errorf("expected line matching /^\\s+---/ but got %q", header)
- }
- if err := rtokens.Eat(tokenizer.TypeNewline); err != nil {
- return discardLine, unexpectedTokenError("a newline", rtokens.Peek())
- }
-
- var body string
- for {
- line := rtokens.ConcatUntil(tokenizer.TypeNewline)
- // Expect the footer to match /\s+.../
- if len(line) >= 4 && strings.HasPrefix(strings.TrimSpace(line), "...") {
- break
- }
-
- body += strings.TrimSpace(line) + "\n"
- if rtokens.Peek().Type == tokenizer.TypeEOF {
- break
- }
- rtokens.Eat(tokenizer.TypeNewline)
- }
-
- testLine.YAML = body
- return parseNextLine, nil
-}
-
-func unexpectedTokenError(wanted string, token tokenizer.Token) error {
- return parserError("got %q but wanted %s", token, wanted)
-}
-
-func parserError(format string, args ...interface{}) error {
- return fmt.Errorf("parse error: "+format, args...)
-}
diff --git a/testing/tap/parser_test.go b/testing/tap/parser_test.go
deleted file mode 100644
index 0d2a337..0000000
--- a/testing/tap/parser_test.go
+++ /dev/null
@@ -1,317 +0,0 @@
-// 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 tap
-
-import (
- "reflect"
- "strings"
- "testing"
-)
-
-func TestParse(t *testing.T) {
- tests := []struct {
- name string
- input string
- expected *Document
- }{
- {
- name: "should parse a document containing only the version",
- input: strings.TrimSpace(`TAP version 13`),
- expected: &Document{
- Version: 13,
- },
- }, {
- name: "should parse a document containing only the version and plan",
- input: strings.TrimSpace(`
-TAP version 13
-1..2
-`),
- expected: &Document{
- Version: 13,
- Plan: Plan{Start: 1, End: 2},
- },
- }, {
- name: "should parse a basic TAP document",
- input: strings.TrimSpace(`
-TAP version 13
-1..4
-ok 1 - This test passed
-ok 2 # TODO this test is disabled
-not ok 3 - This test failed
-ok 4 - This test passed also # TODO congratulate the author
-`),
- expected: &Document{
- Version: 13,
- Plan: Plan{Start: 1, End: 4},
- TestLines: []TestLine{
- {Ok: true, Count: 1, Description: "- This test passed"},
- {Ok: true, Count: 2, Directive: Todo, Explanation: "this test is disabled"},
- {Ok: false, Count: 3, Description: "- This test failed"},
- {Ok: true, Count: 4, Description: "- This test passed also", Directive: Todo, Explanation: "congratulate the author"},
- },
- },
- },
- {
- name: "should parse a plan at the end of the document",
- input: strings.TrimSpace(`
-TAP version 13
-ok 1 - This test passed
-ok 2 # TODO this test is disabled
-not ok 3 - This test failed
-1..3
-`),
- expected: &Document{
- Version: 13,
- Plan: Plan{
- Start: 1,
- End: 3,
- },
- TestLines: []TestLine{
- {Ok: true, Count: 1, Description: "- This test passed"},
- {Ok: true, Count: 2, Directive: Todo, Explanation: "this test is disabled"},
- {Ok: false, Count: 3, Description: "- This test failed"},
- },
- },
- },
- {
- name: "should skip garbage output",
- input: strings.TrimSpace(`
-TAP version 13
-ERROR: segfault at 0x33123. print stackdump;
-0x00001fff: 0x88881
-0x00001ffe: 0x88881
-0x00001ffd: 0x88881
-1..3
-0x00001ffc: 0x88881
-ok 1 - This test passed
-ok 2 # TODO this test is disabled
-exiting
-not ok 3 - This test failed
-`),
- expected: &Document{
- Version: 13,
- Plan: Plan{
- Start: 1,
- End: 3,
- },
- TestLines: []TestLine{
- {Ok: true, Count: 1, Description: "- This test passed"},
- {Ok: true, Count: 2, Directive: Todo, Explanation: "this test is disabled"},
- {Ok: false, Count: 3, Description: "- This test failed"},
- },
- },
- },
- {
- name: "should skip a line with an incomplete test plan",
- input: strings.TrimSpace(`
-TAP version 13
-1..
-not ok 3 - This test failed
-`),
- expected: &Document{
- Version: 13,
- TestLines: []TestLine{
- {Ok: false, Count: 3, Description: "- This test failed"},
- },
- },
- },
- {
- name: "should preserve spaces in description",
- input: strings.TrimSpace(`
-TAP version 13
-1..1
-ok 1 - This test passed
-`),
- expected: &Document{
- Version: 13,
- Plan: Plan{Start: 1, End: 1},
- TestLines: []TestLine{
- {Ok: true, Count: 1, Description: "- This test passed"},
- },
- },
- },
- {
- name: "should preserve spaces in directive explanation",
- input: strings.TrimSpace(`
-TAP version 13
-1..1
-ok 1 # SKIP this is disabled
-`),
- expected: &Document{
- Version: 13,
- Plan: Plan{Start: 1, End: 1},
- TestLines: []TestLine{
- {Ok: true, Count: 1, Directive: Skip, Explanation: "this is disabled"},
- },
- },
- },
- {
- name: "should parse a YAML block",
- input: strings.TrimSpace(`
-TAP version 13
-1..1
-ok 1
- ---
- name: foo
- start: 1
- end: 2
- ...
-`),
- expected: &Document{
- Version: 13,
- Plan: Plan{Start: 1, End: 1},
- TestLines: []TestLine{
- {
- Ok: true,
- Count: 1,
- YAML: "name: foo\nstart: 1\nend: 2\n",
- },
- },
- },
- },
- {
- name: "should parse a YAML block whose header contains trailing characters",
- input: strings.TrimSpace(`
-TAP version 13
-1..1
-ok 1
- ---trailing chars
- name: foo
- start: 1
- end: 2
- ...
-`),
- expected: &Document{
- Version: 13,
- Plan: Plan{Start: 1, End: 1},
- TestLines: []TestLine{
- {
- Ok: true,
- Count: 1,
- YAML: "name: foo\nstart: 1\nend: 2\n",
- },
- },
- },
- },
- {
- name: "should parse a YAML block whose footer contains trailing characters",
- input: strings.TrimSpace(`
-TAP version 13
-1..1
-ok 1
- ---
- name: foo
- start: 1
- end: 2
- ...trailing chars
-`),
- expected: &Document{
- Version: 13,
- Plan: Plan{Start: 1, End: 1},
- TestLines: []TestLine{
- {
- Ok: true,
- Count: 1,
- YAML: "name: foo\nstart: 1\nend: 2\n",
- },
- },
- },
- },
- {
- name: "should parse a YAML block whose header and footer contain trailing characters",
- input: strings.TrimSpace(`
-TAP version 13
-1..1
-ok 1
- --- trailing chars
- name: foo
- start: 1
- end: 2
- ... even more trailing chars
-`),
- expected: &Document{
- Version: 13,
- Plan: Plan{Start: 1, End: 1},
- TestLines: []TestLine{
- {
- Ok: true,
- Count: 1,
- YAML: "name: foo\nstart: 1\nend: 2\n",
- },
- },
- },
- },
- {
- name: "should skip a YAML block at the start of the output",
- input: strings.TrimSpace(`
-TAP version 13
-1..1
- ---
- name: foo
- start: 1
- end: 2
- ...
-`),
- expected: &Document{
- Version: 13,
- Plan: Plan{Start: 1, End: 1},
- },
- },
- {
- name: "should skip a YAML block that does not follow a test line",
- input: strings.TrimSpace(`
-TAP version 13
-1..1
-ok 1
- ---
- name: foo_test
- start: 1
- end: 2
- ...
- ---
- name: bar_test
- start: 3
- end: 4
- ...
-`),
- expected: &Document{
- Version: 13,
- Plan: Plan{Start: 1, End: 1},
- TestLines: []TestLine{{Ok: true, Count: 1, YAML: "name: foo_test\nstart: 1\nend: 2\n"}},
- },
- },
- {
- name: "should parse a YAML block with no trailing /\\s+.../",
- input: strings.TrimSpace(`
-TAP version 13
-1..1
-ok 1
- ---
- name: foo_test
- start: 1
- end: 2
-`),
- expected: &Document{
- Version: 13,
- Plan: Plan{Start: 1, End: 1},
- TestLines: []TestLine{
- {Ok: true, Count: 1, YAML: "name: foo_test\nstart: 1\nend: 2\n"},
- },
- },
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- doc, err := Parse([]byte(tt.input))
- if err != nil {
- t.Fatal(err)
- }
- if !reflect.DeepEqual(doc, tt.expected) {
- t.Errorf("got\n%+v\nbut wanted\n%+v", doc, tt.expected)
- }
- })
- }
-}
diff --git a/testing/tap/producer.go b/testing/tap/producer.go
deleted file mode 100644
index f203a0c..0000000
--- a/testing/tap/producer.go
+++ /dev/null
@@ -1,117 +0,0 @@
-// 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 tap
-
-import (
- "fmt"
- "io"
- "os"
- "strings"
-)
-
-// Producer produces TAP output.
-//
-// This producer always includes test numbers.
-type Producer struct {
- output io.Writer
- directive Directive
- testNumber int
-}
-
-// NewProducer creates a new Producer that writes to the given Writer.
-func NewProducer(w io.Writer) *Producer {
- p := &Producer{
- output: w,
- directive: None,
- }
- // Output the TAP version line.
- p.writeln("TAP version 13")
- return p
-}
-
-// Plan outputs the TAP plan line: 1..count. If count <= 0, nothing is printed
-func (p *Producer) Plan(count int) {
- if count > 0 {
- p.writeln("1..%d", count)
- }
-}
-
-// Ok outputs a test line containing the given description and starting with either "ok"
-// if test is true or "not ok" if false. If this producer was created using
-// Todo or Skip, then the corresponding directive is also printed, and the description is
-// used as the explanation of that directive.
-func (p *Producer) Ok(test bool, description string) {
- p.testNumber++
-
- ok := "ok"
- if !test {
- ok = "not ok"
- }
-
- switch p.directive {
- case None:
- p.writeln("%s %d %s", ok, p.testNumber, description)
- case Todo:
- p.writeln("%s %d # TODO %s", ok, p.testNumber, description)
- case Skip:
- p.writeln("%s %d # SKIP %s", ok, p.testNumber, description)
- }
-
- p.directive = None
-}
-
-// YAML produces a YAML block from the given input. This will indent the input data. The
-// caller should not do this themselves.
-func (p *Producer) YAML(input []byte) {
- // Chomp empty lines from the end of the document.
- content := strings.TrimSuffix(string(input), "\n")
- p.writeln(p.indent("---"))
- p.writeln(p.indent(content))
- p.writeln(p.indent("..."))
-}
-
-// Todo returns a new Producer that prints TODO directives.
-func (p *Producer) Todo() *Producer {
- p.directive = Todo
- return p
-}
-
-// Skip returns a new Producer that prints SKIP directives.
-func (p *Producer) Skip() *Producer {
- p.directive = Skip
- return p
-}
-
-func (p *Producer) writeln(format string, args ...interface{}) {
- fmt.Fprintln(p.writer(), fmt.Sprintf(format, args...))
-}
-
-// writer initializes the Writer to use for this Producer, in case the Producer was
-// initialized with nil output.
-func (p *Producer) writer() io.Writer {
- if p.output == nil {
- p.output = os.Stdout
- }
- return p.output
-}
-
-// Indent indents every line of the input text with a single space.
-func (p *Producer) indent(input string) string {
- return string(p.indentBytes([]byte(input)))
-}
-
-// IndentBytes indents every line of the input text with a single space.
-func (p *Producer) indentBytes(input []byte) []byte {
- var output []byte
- startOfLine := true
- for _, c := range input {
- if startOfLine && c != '\n' {
- output = append(output, ' ')
- }
- output = append(output, c)
- startOfLine = c == '\n'
- }
- return output
-}
diff --git a/testing/tap/producer_test.go b/testing/tap/producer_test.go
deleted file mode 100644
index 86819f3..0000000
--- a/testing/tap/producer_test.go
+++ /dev/null
@@ -1,99 +0,0 @@
-// 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 tap_test
-
-import (
- "os"
- "time"
-
- "go.fuchsia.dev/tools/testing/tap"
- "gopkg.in/yaml.v2"
-)
-
-func ExampleProducer_single_test() {
- p := tap.NewProducer(os.Stdout)
- p.Plan(1)
- p.Ok(true, "- this test passed")
- // Output:
- // TAP version 13
- // 1..1
- // ok 1 - this test passed
-}
-
-func ExampleProducer_Todo() {
- p := tap.NewProducer(os.Stdout)
- p.Plan(1)
- p.Todo().Ok(true, "implement this test")
- // Output:
- // TAP version 13
- // 1..1
- // ok 1 # TODO implement this test
-}
-
-func ExampleProducer_Skip() {
- p := tap.NewProducer(os.Stdout)
- p.Plan(1)
- p.Skip().Ok(true, "implement this test")
- // Output:
- // TAP version 13
- // 1..1
- // ok 1 # SKIP implement this test
-}
-
-func ExampleProducer_many_test() {
- p := tap.NewProducer(os.Stdout)
- p.Plan(4)
- p.Ok(true, "- this test passed")
- p.Ok(false, "")
- // Output:
- // TAP version 13
- // 1..4
- // ok 1 - this test passed
- // not ok 2
-}
-
-func ExampleProducer_skip_todo_alternating() {
- p := tap.NewProducer(os.Stdout)
- p.Plan(4)
- p.Skip().Ok(true, "implement this test")
- p.Todo().Ok(false, "oh no!")
- p.Skip().Ok(false, "skipped another")
- p.Todo().Skip().Todo().Ok(true, "please don't write code like this")
- // Output:
- // TAP version 13
- // 1..4
- // ok 1 # SKIP implement this test
- // not ok 2 # TODO oh no!
- // not ok 3 # SKIP skipped another
- // ok 4 # TODO please don't write code like this
-}
-
-func ExampleProducer_YAML() {
- p := tap.NewProducer(os.Stdout)
- p.Plan(1)
- p.Ok(true, "passed")
- bytes, err := yaml.Marshal(struct {
- Name string `yaml:"name"`
- Start time.Time `yaml:"start_time"`
- End time.Time `yaml:"end_time"`
- }{
- Name: "foo_test",
- Start: time.Date(2019, 1, 1, 12, 30, 0, 0, time.UTC),
- End: time.Date(2019, 1, 1, 12, 40, 0, 0, time.UTC),
- })
- if err != nil {
- panic(err)
- }
- p.YAML(bytes)
- // Output:
- // TAP version 13
- // 1..1
- // ok 1 passed
- // ---
- // name: foo_test
- // start_time: 2019-01-01T12:30:00Z
- // end_time: 2019-01-01T12:40:00Z
- // ...
-}
diff --git a/testing/tap/tap.go b/testing/tap/tap.go
deleted file mode 100644
index 1f2f99a..0000000
--- a/testing/tap/tap.go
+++ /dev/null
@@ -1,132 +0,0 @@
-// 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 tap
-
-import (
- "bytes"
- "fmt"
- "io"
- "strings"
-)
-
-// Type describes the type of TAP Object returned by the Parser.
-type Type int
-
-const (
- // VersionType is the type returned by a Version Object.
- VersionType Type = iota
-
- // PlanType is the type returned by a Plan Object.
- PlanType
-
- // TestLineType is the type returned by a TestLine.
- TestLineType
-)
-
-// Object is a TAP element such as a test line, plan, version header, or Yaml block.
-type Object interface {
- Type() Type
-}
-
-// Document represents a complete TAP document.
-type Document struct {
- Version Version
- Plan Plan
- TestLines []TestLine
-}
-
-// WriteTo writes this document to the given Writer as a formatted TAP output stream.
-func (d *Document) WriteTo(w io.Writer) (int64, error) {
- n, err := w.Write([]byte(d.format()))
- return int64(n), err
-}
-
-// Format renders this document as thought it were a TAP output stream.
-func (d *Document) format() string {
- output := new(bytes.Buffer)
- output.WriteString(fmt.Sprintf("TAP version %d\n", d.Version))
- output.WriteString(fmt.Sprintf("%d..%d\n", d.Plan.Start, d.Plan.End))
-
- for _, line := range d.TestLines {
- var parts []string
- ok := "ok"
- if !line.Ok {
- ok = "not ok"
- }
- parts = append(parts, ok)
-
- if line.Count != 0 {
- parts = append(parts, fmt.Sprintf("%d", line.Count))
- }
-
- if line.Description != "" {
- parts = append(parts, line.Description)
- }
-
- switch line.Directive {
- case Todo:
- parts = append(parts, "# TODO", line.Explanation)
- case Skip:
- parts = append(parts, "# SKIP", line.Explanation)
- }
-
- output.WriteString(strings.Join(parts, " ") + "\n")
- }
-
- return output.String()
-}
-
-// Version represents a TAP version line.
-type Version int
-
-// Type implements Object.
-func (v Version) Type() Type {
- return VersionType
-}
-
-func (v Version) String() string {
- return fmt.Sprintf("TAP version %d", v)
-}
-
-// Plan represents a TAP plan line.
-type Plan struct {
- Start int
- End int
-}
-
-// Type implements Object.
-func (p Plan) Type() Type {
- return PlanType
-}
-
-func (p Plan) String() string {
- return fmt.Sprintf("%d..%d", p.Start, p.End)
-}
-
-// Directive represents a TAP directive (TODO|SKIP|<none>)
-type Directive int
-
-// Valid Tap directives.
-const (
- None Directive = iota
- Todo
- Skip
-)
-
-// TestLine represents a TAP test line beginning with "ok" or "not ok".
-type TestLine struct {
- Ok bool
- Count int
- Description string
- Directive Directive
- Explanation string
- Diagnostic string
- YAML string
-}
-
-// Type implements Object.
-func (t TestLine) Type() Type {
- return TestLineType
-}
diff --git a/testing/tap/tokenizer/iterator.go b/testing/tap/tokenizer/iterator.go
deleted file mode 100644
index 6a7f0ea..0000000
--- a/testing/tap/tokenizer/iterator.go
+++ /dev/null
@@ -1,85 +0,0 @@
-// 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 tokenizer
-
-// noSpacesIterator iterates over a stream of tokens and skips whitespace characters, with
-// the exception of newlines.
-type noSpacesIterator struct {
- raw *rawIterator
-}
-
-// Next consumes the next token in the stream.
-func (s *noSpacesIterator) Next() Token {
- for {
- next := s.raw.Next()
- if next.Type != TypeSpace {
- return next
- }
- }
-}
-
-// Peek returns a read-only copy of the next token in the stream, without consuming it.
-func (s *noSpacesIterator) Peek() Token {
- for {
- next := s.raw.Peek()
- if next.Type == TypeSpace {
- s.raw.Next()
- continue
- }
- return next
- }
-}
-
-// Raw returns a rawIterator using the same underlying channel of Tokens.
-func (s noSpacesIterator) Raw() *rawIterator {
- return s.raw
-}
-
-// rawIterator iterates over a stream of tokens, including whitespace characters.
-type rawIterator struct {
- stream <-chan Token
- eof bool
- lookahead *Token
-}
-
-// Next consumes the next token in the stream.
-func (s *rawIterator) Next() Token {
- if s.eof {
- return EOFToken()
- }
-
- next := new(Token)
- if s.lookahead == nil {
- *next = <-s.stream
- } else {
- next = s.lookahead
- s.lookahead = nil
- }
-
- if next.Type == TypeEOF {
- s.eof = true
- }
-
- return *next
-}
-
-// Peek returns a read-only copy of the next token in the stream, without consuming it.
-func (s *rawIterator) Peek() Token {
- if s.eof {
- return EOFToken()
- }
-
- if s.lookahead == nil {
- s.lookahead = new(Token)
- *s.lookahead = <-s.stream
- }
-
- return *s.lookahead
-}
-
-// Raw returns a rawIterator using the same underlying channel of Tokens.
-func (s rawIterator) Raw() *rawIterator {
- return &s
-}
diff --git a/testing/tap/tokenizer/lexer.go b/testing/tap/tokenizer/lexer.go
deleted file mode 100644
index ca1c1c8..0000000
--- a/testing/tap/tokenizer/lexer.go
+++ /dev/null
@@ -1,212 +0,0 @@
-// 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 tokenizer
-
-import (
- "log"
- "strconv"
- "unicode"
- "unicode/utf8"
-)
-
-// TokenType describes the category a Token belongs to.
-type TokenType string
-
-// TokenType values recognized by the lexer.
-const (
- TypePound TokenType = "POUND" // '#'
- TypeNumber TokenType = "NUMBER" // A number
- TypeText TokenType = "TEXT" // Catch-all type
- TypeDot TokenType = "DOT" // '.'
- TypeNewline TokenType = "NEWLINE" // '\n'
- TypeEOF TokenType = "EOF" // Psuedo token to signal the end of input.
- TypeSpace TokenType = "SPACE" // A whitespace character
- TypeDash TokenType = "DASH" // '-'
-)
-
-// Token represents some atomic TAP output string.
-type Token struct {
- Type TokenType
- Value string
-}
-
-// Tokenize generates a channel of Tokens read from the given input.
-func Tokenize(input []byte) <-chan Token {
- l := &lexer{
- input: input,
- Tokens: make(chan Token, 1),
- }
- go l.run()
- return l.Tokens
-}
-
-// EOFToken is emitted to signal the end of input.
-func EOFToken() Token {
- return Token{
- Type: TypeEOF,
- Value: "",
- }
-}
-
-// The rune emitted when the end of input has been reached.
-const eof = rune(-1)
-
-// State represents a lexical analysis state. Each state accepts a lexer as input and
-// returns the next lexer state. If the output state is nil, lexing stops.
-type state func(*lexer) state
-
-// Lexer manages the position of a lexical analysis on some TAP output string.
-type lexer struct {
- input []byte
- start int
- pos int
- width int
- Tokens chan Token
-}
-
-func (l *lexer) run() {
- for state := lexAny; state != nil; {
- state = state(l)
- }
- close(l.Tokens)
-}
-
-func (l *lexer) emit(t TokenType) {
- l.Tokens <- Token{Type: t, Value: string(l.input[l.start:l.pos])}
- l.start = l.pos
-}
-
-func (l *lexer) next() rune {
- if l.pos >= len(l.input) {
- l.width = 0
- return eof
- }
-
- // Read the next rune, skipping over all invalid utf8 sequences.
- var rn rune
- rn, l.width = utf8.DecodeRune(l.input[l.pos:])
- for rn == utf8.RuneError && l.pos < len(l.input) {
- log.Printf("invalid UTF-8 found at pos %d:\n\n%s", l.pos, string(l.input))
- l.pos++
- rn, l.width = utf8.DecodeRune(l.input[l.pos:])
- }
- l.pos += l.width
- return rn
-}
-
-// Returns the current lexeme.
-func (l *lexer) lexeme() lexeme {
- if l.pos >= len(l.input) {
- return lexeme(eof)
- }
- return lexeme(l.input[l.pos : l.pos+1][0])
-}
-
-// LexAny is the lexer start state. It's job is to put the lexer into the proper state
-// according to the next input rune. Other states should return to this state after
-// emitting their lexemes. They should also not consume runes using l.next() immediately
-// before entering this state.
-func lexAny(l *lexer) state {
- lxm := l.lexeme()
- if lxm.isEOF() {
- l.emit(TypeEOF)
- return nil
- }
-
- l.start = l.pos
-
- switch {
- case lxm.isDash():
- l.next()
- l.emit(TypeDash)
- return lexAny
- case lxm.isNewline():
- l.next()
- l.emit(TypeNewline)
- return lexAny
- case lxm.isDot():
- l.next()
- l.emit(TypeDot)
- return lexAny
- case lxm.isPound():
- l.next()
- l.emit(TypePound)
- return lexAny
- case lxm.isSpace():
- return lexSpace
- case lxm.isDigit():
- return lexNumber
- }
-
- return lexText
-}
-
-func lexSpace(l *lexer) state {
- return lexUntil(l, TypeSpace, func(lxm lexeme) bool { return !lxm.isSpace() })
-}
-
-func lexNumber(l *lexer) state {
- return lexUntil(l, TypeNumber, func(lxm lexeme) bool { return !lxm.isDigit() })
-}
-
-func lexText(l *lexer) state {
- return lexUntil(l, TypeText, func(lxm lexeme) bool { return lxm.isNonText() })
-}
-
-// LexUntil consumes all runes into a token of the given type while `stop` is false.
-// Returns lexAny when complete or nil if the end of input was reached.
-func lexUntil(l *lexer, typ TokenType, stop func(lexeme) bool) state {
- for {
- lxm := l.lexeme()
- if lxm.isEOF() || stop(lxm) {
- l.emit(typ)
- return lexAny
- }
- if l.next() == eof {
- break
- }
- }
- // Reached EOF
- if l.pos > l.start {
- l.emit(typ)
- }
- l.emit(TypeEOF)
- return nil
-}
-
-type lexeme rune
-
-func (l lexeme) isSpace() bool {
- return l != '\n' && unicode.IsSpace(rune(l))
-}
-
-func (l lexeme) isNewline() bool {
- return l == '\n'
-}
-
-func (l lexeme) isDigit() bool {
- _, err := strconv.Atoi(string(l))
- return err == nil
-}
-
-func (l lexeme) isDot() bool {
- return l == '.'
-}
-
-func (l lexeme) isDash() bool {
- return l == '-'
-}
-
-func (l lexeme) isPound() bool {
- return l == '#'
-}
-
-func (l lexeme) isEOF() bool {
- return rune(l) == eof
-}
-
-func (l lexeme) isNonText() bool {
- return l.isEOF() || l.isSpace() || l.isNewline() || l.isDigit() || l.isDot() || l.isPound() || l.isDash()
-}
diff --git a/testing/tap/tokenizer/stream.go b/testing/tap/tokenizer/stream.go
deleted file mode 100644
index ab04954..0000000
--- a/testing/tap/tokenizer/stream.go
+++ /dev/null
@@ -1,75 +0,0 @@
-// 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 tokenizer
-
-import "fmt"
-
-// Iterator iterates over a stream of tokens. This provides the core functionality for a
-// TokenStream.
-type iterator interface {
- Next() Token
- Peek() Token
- Raw() *rawIterator
-}
-
-// NewTokenStream creates a stream of Token values read from input.
-func NewTokenStream(input []byte) *TokenStream {
- return &TokenStream{
- iter: &noSpacesIterator{
- raw: &rawIterator{
- stream: Tokenize(input),
- },
- },
- }
-}
-
-// TokenStream is a read-only queue of Token values. The next Token in the stream can be
-// consumed by calling Next(). The next token can be inspected without being consumed by
-// calling Peek(). By default, TokenStream discards whitespace characters as though they
-// are not part of the stream. Use Raw get a TokenStream that respects whitespace.
-type TokenStream struct {
- iter iterator
-}
-
-// Next consumes the next token in the stream.
-func (s *TokenStream) Next() Token {
- return s.iter.Next()
-}
-
-// Peek returns a read-only copy of the next token in the stream, without consuming it.
-func (s *TokenStream) Peek() Token {
- return s.iter.Peek()
-}
-
-// Raw returns a TokenStream that includes whitespace characters.
-func (s *TokenStream) Raw() *TokenStream {
- return &TokenStream{iter: s.iter.Raw()}
-}
-
-// Eat consumes the next token from the stream iff it's type matches typ. If the types
-// are different, an error is returned.
-func (s *TokenStream) Eat(typ TokenType) error {
- token := s.iter.Peek()
- if token.Type != typ {
- return fmt.Errorf("unexpected token: %q", token.Type)
- }
- s.iter.Next()
- return nil
-}
-
-// ConcatUntil concatenates the values of the next tokens in this stream as long as their
-// types are not anyOf. TypeEOF is implied and need not be specified. Returns the
-// contatenated output with outer spaces trimmed.
-func (s *TokenStream) ConcatUntil(anyOf ...TokenType) string {
- var values string
- stopAtType := map[TokenType]bool{TypeEOF: true}
- for i := range anyOf {
- stopAtType[anyOf[i]] = true
- }
- for !stopAtType[s.iter.Peek().Type] {
- values += s.iter.Next().Value
- }
- return values
-}
diff --git a/testing/testrunner/cmd/main.go b/testing/testrunner/cmd/main.go
deleted file mode 100644
index 157fd1e..0000000
--- a/testing/testrunner/cmd/main.go
+++ /dev/null
@@ -1,199 +0,0 @@
-// 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 (
- "bytes"
- "context"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "os"
- "time"
-
- "go.fuchsia.dev/tools/integration/testsharder"
- "go.fuchsia.dev/tools/testing/runtests"
- "go.fuchsia.dev/tools/testing/testrunner"
-)
-
-const (
- // Default amount of time to wait before failing to perform any IO action.
- defaultIOTimeout = 1 * time.Minute
-
- // The username used to authenticate with the Fuchsia device.
- sshUser = "fuchsia"
-)
-
-// Command-line flags
-var (
- // Whether to show Usage and exit.
- help bool
-
- // The path where a tar archive containing test results should be created.
- archive string
-
- // Working directory of the local testing subprocesses.
- localWD string
-)
-
-// Fuchsia-specific environment variables possibly exposed to the testrunner.
-const (
- nodenameEnvVar = "FUCHSIA_NODENAME"
- sshKeyEnvVar = "FUCHSIA_SSH_KEY"
-)
-
-func usage() {
- fmt.Printf(`testrunner [flags] tests-file
-
-Executes all tests found in the JSON [tests-file]
-Fuchsia tests require both the nodename of the fuchsia instance and a private
-SSH key corresponding to a authorized key to be set in the environment under
-%s and %s respectively.
-`, nodenameEnvVar, sshKeyEnvVar)
-}
-
-func init() {
- flag.BoolVar(&help, "help", false, "Whether to show Usage and exit.")
- flag.StringVar(&archive, "archive", "", "Optional path where a tar archive containing test results should be created.")
- flag.StringVar(&localWD, "C", "", "Working directory of local testing subprocesses; if unset the current working directory will be used.")
- flag.Usage = usage
-}
-
-func main() {
- flag.Parse()
-
- if help || flag.NArg() != 1 {
- flag.Usage()
- flag.PrintDefaults()
- return
- }
-
- // Load tests.
- testsPath := flag.Arg(0)
- tests, err := testsharder.LoadTests(testsPath)
- if err != nil {
- log.Fatalf("failed to load tests from %q: %v", testsPath, err)
- }
-
- // Prepare test output drivers.
- output := new(Output)
- defer output.Complete()
- output.SetupTAP(os.Stdout, len(tests))
- output.SetupSummary()
- if archive != "" {
- if err := output.SetupTar(archive); err != nil {
- log.Fatalf("failed to initialize tar output: %v", err)
- }
- }
-
- // Execute.
- nodename := os.Getenv(nodenameEnvVar)
- sshKeyFile := os.Getenv(sshKeyEnvVar)
- if err := execute(tests, output, nodename, sshKeyFile); err != nil {
- log.Fatal(err)
- }
-}
-
-func execute(tests []testsharder.Test, output *Output, nodename, sshKeyFile string) error {
- var linux, mac, fuchsia, unknown []testsharder.Test
- for _, test := range tests {
- switch test.OS {
- case testsharder.Fuchsia:
- fuchsia = append(fuchsia, test)
- case testsharder.Linux:
- linux = append(linux, test)
- case testsharder.Mac:
- mac = append(mac, test)
- default:
- unknown = append(unknown, test)
- }
- }
-
- if len(unknown) > 0 {
- return fmt.Errorf("could not determine the runtime system for following tests %v", unknown)
- }
-
- localTester := &SubprocessTester{
- dir: localWD,
- env: os.Environ(),
- }
-
- if err := runTests(linux, localTester.Test, output); err != nil {
- return err
- }
-
- if err := runTests(mac, localTester.Test, output); err != nil {
- return err
- }
-
- return runFuchsiaTests(fuchsia, output, nodename, sshKeyFile)
-}
-
-func runFuchsiaTests(tests []testsharder.Test, output *Output, nodename, sshKeyFile string) error {
- if len(tests) == 0 {
- return nil
- } else if nodename == "" {
- return fmt.Errorf("%s must be set", nodenameEnvVar)
- } else if sshKeyFile == "" {
- return fmt.Errorf("%s must be set", sshKeyEnvVar)
- }
-
- sshKey, err := ioutil.ReadFile(sshKeyFile)
- if err != nil {
- return err
- }
- tester, err := NewFuchsiaTester(nodename, sshKey)
- if err != nil {
- return fmt.Errorf("failed to initialize fuchsia tester: %v", err)
- }
- defer tester.Close()
- return runTests(tests, tester.Test, output)
-}
-
-func runTests(tests []testsharder.Test, tester Tester, output *Output) error {
- for _, test := range tests {
- result, err := runTest(context.Background(), test, tester)
- if err != nil {
- log.Println(err)
- }
- if result != nil {
- output.Record(*result)
- }
- }
- return nil
-}
-
-func runTest(ctx context.Context, test testsharder.Test, tester Tester) (*testrunner.TestResult, error) {
- result := runtests.TestSuccess
- stdout := new(bytes.Buffer)
- stderr := new(bytes.Buffer)
-
- // Fork the test's stdout and stderr streams to the test runner's stderr stream to
- // make local debugging easier. Writing both to stderr ensures that stdout only
- // contains the test runner's TAP output stream.
- multistdout := io.MultiWriter(stdout, os.Stderr)
- multistderr := io.MultiWriter(stderr, os.Stderr)
-
- startTime := time.Now()
-
- if err := tester(ctx, test, multistdout, multistderr); err != nil {
- result = runtests.TestFailure
- log.Println(err)
- }
-
- endTime := time.Now()
-
- // Record the test details in the summary.
- return &testrunner.TestResult{
- Name: test.Name,
- Stdout: stdout.Bytes(),
- Stderr: stderr.Bytes(),
- Result: result,
- StartTime: startTime,
- EndTime: endTime,
- }, nil
-}
diff --git a/testing/testrunner/cmd/output.go b/testing/testrunner/cmd/output.go
deleted file mode 100644
index bad9f72..0000000
--- a/testing/testrunner/cmd/output.go
+++ /dev/null
@@ -1,73 +0,0 @@
-// 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 (
- "encoding/json"
- "fmt"
- "io"
- "os"
-
- "go.fuchsia.dev/tools/testing/runtests"
- "go.fuchsia.dev/tools/testing/testrunner"
- "go.fuchsia.dev/tools/testing/testrunner/cmd/outputs"
-)
-
-// Output manages the test runner's output drivers. Upon completion, if tar output is
-// initialized, a TAR archive containing all other outputs is produced.
-type Output struct {
- Summary *outputs.SummaryOutput
- TAP *outputs.TAPOutput
- Tar *outputs.TarOutput
-}
-
-// SetupTAP intializes a TAPOutput stream. out is where the stream is written. testCount
-// is used to print the TAP plan (e.g. 1..testCount). See outputs.TAPOutput for full docs.
-func (o *Output) SetupTAP(out io.Writer, testCount int) {
- o.TAP = outputs.NewTAPOutput(out, testCount)
-}
-
-// SetupSummary initializes a Test Summary output. See outputs.SummaryOutput for docs.
-func (o *Output) SetupSummary() {
- o.Summary = &outputs.SummaryOutput{}
-}
-
-// SetupTar initializes a TarOutput. See outputs.TarOutput for docs.
-func (o *Output) SetupTar(archivePath string) error {
- fd, err := os.Create(archivePath)
- if err != nil {
- return fmt.Errorf("failed to open %q: %v", archivePath, err)
- }
- o.Tar = outputs.NewTarOutput(fd)
- return nil
-}
-
-// Record writes the test result to initialized outputs.
-func (o *Output) Record(result testrunner.TestResult) {
- if o.Summary != nil {
- o.Summary.Record(result)
- }
- if o.TAP != nil {
- o.TAP.Record(result)
- }
- if o.Tar != nil {
- o.Tar.Record(result)
- }
-}
-
-// Complete finishes producing output for the test run.
-func (o *Output) Complete() error {
- if o.Tar == nil {
- return nil
- }
- bytes, err := json.Marshal(o.Summary.Summary)
- if err != nil {
- return err
- }
- if err := o.Tar.TarFile(bytes, runtests.TestSummaryFilename); err != nil {
- return err
- }
- return o.Tar.Close()
-}
diff --git a/testing/testrunner/cmd/outputs/summary.go b/testing/testrunner/cmd/outputs/summary.go
deleted file mode 100644
index db60a4d..0000000
--- a/testing/testrunner/cmd/outputs/summary.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// 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 outputs
-
-import (
- "path"
- "strings"
-
- "go.fuchsia.dev/tools/testing/runtests"
- "go.fuchsia.dev/tools/testing/testrunner"
-)
-
-// SummaryOutput records test results in a TestSummary object.
-type SummaryOutput struct {
- Summary runtests.TestSummary
-}
-
-// Record writes a TestDetails entry for the given test into a Summary.
-func (o *SummaryOutput) Record(result testrunner.TestResult) {
- pathInArchive := path.Join(result.Name, runtests.TestOutputFilename)
- // Strip any leading //, contributed by Linux/Mac test names, so that
- // pathInArchive gives a valid relative path.
- pathInArchive = strings.TrimLeft(pathInArchive, "//")
- o.Summary.Tests = append(o.Summary.Tests, runtests.TestDetails{
- Name: result.Name,
- OutputFile: pathInArchive,
- Result: result.Result,
- DurationMillis: result.EndTime.Sub(result.StartTime).Nanoseconds() / 1000 / 1000,
- })
-}
-
-// AddFile registers a file on disk as a file to include in the summary.
-func (o *SummaryOutput) AddFile(name, path string) {
- if o.Summary.Outputs == nil {
- o.Summary.Outputs = make(map[string]string)
- }
- o.Summary.Outputs[name] = path
-}
diff --git a/testing/testrunner/cmd/outputs/summary_test.go b/testing/testrunner/cmd/outputs/summary_test.go
deleted file mode 100644
index 2bb6327..0000000
--- a/testing/testrunner/cmd/outputs/summary_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// 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 outputs_test
-
-import (
- "reflect"
- "testing"
- "time"
-
- "go.fuchsia.dev/tools/testing/runtests"
- "go.fuchsia.dev/tools/testing/testrunner"
- "go.fuchsia.dev/tools/testing/testrunner/cmd/outputs"
-)
-
-func TestSummaryOutput(t *testing.T) {
- start := time.Now()
- inputs := []testrunner.TestResult{{
- Name: "test_a",
- Result: runtests.TestFailure,
- StartTime: start,
- EndTime: start.Add(10 * time.Millisecond),
- }, {
- Name: "test_b",
- Result: runtests.TestSuccess,
- }}
-
- var output outputs.SummaryOutput
- for _, input := range inputs {
- output.Record(input)
- }
-
- expectedSummary := runtests.TestSummary{
- Tests: []runtests.TestDetails{{
- Name: "test_a",
- OutputFile: "test_a/stdout-and-stderr.txt",
- Result: runtests.TestFailure,
- DurationMillis: 10,
- }, {
- Name: "test_b",
- OutputFile: "test_b/stdout-and-stderr.txt",
- Result: runtests.TestSuccess,
- // Unspecified start and end times == 0
- DurationMillis: 0,
- }},
- }
-
- actualSummary := output.Summary
-
- if !reflect.DeepEqual(actualSummary, expectedSummary) {
- t.Errorf("got\n%q\nbut wanted\n%q\n", actualSummary, expectedSummary)
- }
-}
diff --git a/testing/testrunner/cmd/outputs/tap.go b/testing/testrunner/cmd/outputs/tap.go
deleted file mode 100644
index aad89ef..0000000
--- a/testing/testrunner/cmd/outputs/tap.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// 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 outputs
-
-import (
- "fmt"
- "io"
-
- "go.fuchsia.dev/tools/testing/runtests"
- "go.fuchsia.dev/tools/testing/tap"
- "go.fuchsia.dev/tools/testing/testrunner"
-)
-
-// TAPOutput records test results as a TAP output stream.
-type TAPOutput struct {
- producer *tap.Producer
-}
-
-// NewTAPOutput creates a new TAPOutput that streams to the given writer. testCount is
-// used to print the TAP plan line.
-func NewTAPOutput(output io.Writer, testCount int) *TAPOutput {
- producer := tap.NewProducer(output)
- producer.Plan(testCount)
- return &TAPOutput{producer}
-}
-
-// Record writes the test's result and name to the output given at construction time.
-func (o *TAPOutput) Record(result testrunner.TestResult) {
- desc := fmt.Sprintf("%s (%v)", result.Name, result.EndTime.Sub(result.StartTime))
- o.producer.Ok(result.Result == runtests.TestSuccess, desc)
-}
diff --git a/testing/testrunner/cmd/outputs/tap_test.go b/testing/testrunner/cmd/outputs/tap_test.go
deleted file mode 100644
index 620994e..0000000
--- a/testing/testrunner/cmd/outputs/tap_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// 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 outputs_test
-
-import (
- "bytes"
- "strings"
- "testing"
- "time"
-
- "go.fuchsia.dev/tools/testing/runtests"
- "go.fuchsia.dev/tools/testing/testrunner"
- "go.fuchsia.dev/tools/testing/testrunner/cmd/outputs"
-)
-
-func TestTapOutput(t *testing.T) {
- s := time.Unix(0, 0)
- inputs := []testrunner.TestResult{{
- Name: "test_a",
- Result: runtests.TestSuccess,
- StartTime: s,
- EndTime: s.Add(time.Second * 2),
- }, {
- Name: "test_b",
- Result: runtests.TestFailure,
- StartTime: s.Add(time.Minute * 1),
- EndTime: s.Add(time.Minute * 2),
- }}
-
- var buf bytes.Buffer
- output := outputs.NewTAPOutput(&buf, 10)
- for _, input := range inputs {
- output.Record(input)
- }
-
- expectedOutput := strings.TrimSpace(`
-TAP version 13
-1..10
-ok 1 test_a (2s)
-not ok 2 test_b (1m0s)
-`)
-
- actualOutput := strings.TrimSpace(buf.String())
- if actualOutput != expectedOutput {
- t.Errorf("got\n%q\nbut wanted\n%q\n", actualOutput, expectedOutput)
- }
-}
diff --git a/testing/testrunner/cmd/outputs/tar.go b/testing/testrunner/cmd/outputs/tar.go
deleted file mode 100644
index fe9ce2c..0000000
--- a/testing/testrunner/cmd/outputs/tar.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// 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 outputs
-
-import (
- "archive/tar"
- "bytes"
- "io"
- "path"
-
- "go.fuchsia.dev/tools/lib/tarutil"
- "go.fuchsia.dev/tools/testing/runtests"
- "go.fuchsia.dev/tools/testing/testrunner"
-)
-
-// TarOutput records test stdout and stderr streams in a TAR archive.
-type TarOutput struct {
- w *tar.Writer
-}
-
-func NewTarOutput(w io.Writer) *TarOutput {
- tw := tar.NewWriter(w)
- return &TarOutput{w: tw}
-}
-
-// Record writes the given test result's stdout and stderr streams to the same file within
-// a Tar archive.
-func (o *TarOutput) Record(result testrunner.TestResult) {
- pathInArchive := path.Join(result.Name, runtests.TestOutputFilename)
- stdout := bytes.NewReader(result.Stdout)
- stderr := bytes.NewReader(result.Stderr)
- tarutil.TarReader(o.w, io.MultiReader(stdout, stderr), pathInArchive)
-}
-
-// TarFile adds a file to the underlying archive.
-func (o *TarOutput) TarFile(bytes []byte, filename string) error {
- return tarutil.TarBuffer(o.w, bytes, filename)
-}
-
-// Close flushes all data to the archive.
-func (o *TarOutput) Close() error {
- return o.w.Close()
-}
diff --git a/testing/testrunner/cmd/outputs/tar_test.go b/testing/testrunner/cmd/outputs/tar_test.go
deleted file mode 100644
index 7f7bd04..0000000
--- a/testing/testrunner/cmd/outputs/tar_test.go
+++ /dev/null
@@ -1,102 +0,0 @@
-// 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 outputs_test
-
-import (
- "archive/tar"
- "bytes"
- "io"
- "testing"
-
- "go.fuchsia.dev/tools/testing/runtests"
- "go.fuchsia.dev/tools/testing/testrunner"
- "go.fuchsia.dev/tools/testing/testrunner/cmd/outputs"
-)
-
-func TestTarOutput(t *testing.T) {
- tests := []struct {
- name string
- input testrunner.TestResult
- expectedHeader *tar.Header
- expectedContents string
- }{
- {
- name: "archive entry for test_a",
- input: testrunner.TestResult{
- Name: "test_a",
- Stdout: []byte("the test passed"),
- Result: runtests.TestSuccess,
- },
- expectedHeader: &tar.Header{
- Typeflag: tar.TypeReg,
- Name: "test_a/stdout-and-stderr.txt",
- Size: int64(len("the test passed")),
- Mode: 0666,
- Format: tar.FormatUSTAR,
- },
- expectedContents: "the test passed",
- },
- {
- name: "archive entry for test_b",
- input: testrunner.TestResult{
- Name: "test_b",
- Stdout: []byte("the test failed"),
- Result: runtests.TestSuccess,
- },
- expectedHeader: &tar.Header{
- Typeflag: tar.TypeReg,
- Name: "test_b/stdout-and-stderr.txt",
- Size: int64(len("the test failed")),
- Mode: 0666,
- Format: tar.FormatUSTAR,
- },
- expectedContents: "the test failed",
- },
- }
-
- // Helper to compare archive headers. Certain fields are ignored.
- headersEqual := func(a *tar.Header, b *tar.Header) bool {
- return a.Format == b.Format &&
- a.Gid == b.Gid &&
- a.Gname == b.Gname &&
- a.Linkname == b.Linkname &&
- a.Mode == b.Mode &&
- a.Name == b.Name &&
- a.Size == b.Size &&
- a.Typeflag == b.Typeflag &&
- a.Uid == b.Uid &&
- a.Uname == b.Uname
- }
-
- for _, tt := range tests {
- // Record output.
- t.Run(tt.name, func(t *testing.T) {
- var buf bytes.Buffer
- to := outputs.NewTarOutput(&buf)
- to.Record(tt.input)
- to.Close()
-
- // Check the contents of the tar archive.
- tr := tar.NewReader(&buf)
- hdr, err := tr.Next()
- if err != nil {
- t.Fatalf("got an error, wanted a header: %v", err)
- }
-
- if !headersEqual(hdr, tt.expectedHeader) {
- t.Errorf("got:\n%+v\nwanted:\n%+v", hdr, tt.expectedHeader)
- }
-
- var actualContents bytes.Buffer
- if _, err := io.Copy(&actualContents, tr); err != nil {
- t.Fatalf("failed to read from the Tar Reader: %v", err)
- }
-
- if tt.expectedContents != actualContents.String() {
- t.Errorf("got: %q, but wanted: %q", actualContents.String(), tt.expectedContents)
- }
- })
- }
-}
diff --git a/testing/testrunner/cmd/tester.go b/testing/testrunner/cmd/tester.go
deleted file mode 100644
index f79c1f3..0000000
--- a/testing/testrunner/cmd/tester.go
+++ /dev/null
@@ -1,152 +0,0 @@
-// 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 (
- "context"
- "fmt"
- "io"
- "path"
-
- "go.fuchsia.dev/tools/integration/testsharder"
- "go.fuchsia.dev/tools/lib/logger"
- "go.fuchsia.dev/tools/lib/runner"
- "go.fuchsia.dev/tools/net/sshutil"
- "golang.org/x/crypto/ssh"
-)
-
-const (
- // The test output directory to create on the Fuchsia device.
- fuchsiaOutputDir = "/data/infra/testrunner"
-
- // A conventionally used global request name for checking the status of a client
- // connection to an OpenSSH server.
- keepAliveOpenSSH = "keepalive@openssh.com"
-)
-
-// Tester is executes a Test.
-type Tester func(context.Context, testsharder.Test, io.Writer, io.Writer) error
-
-// SubprocessTester executes tests in local subprocesses.
-type SubprocessTester struct {
- dir string
- env []string
-}
-
-func (t *SubprocessTester) Test(ctx context.Context, test testsharder.Test, stdout io.Writer, stderr io.Writer) error {
- command := test.Command
- if len(test.Command) == 0 {
- if test.Path == "" {
- return fmt.Errorf("test %q has no `command` or `path` set", test.Name)
- }
- command = []string{test.Path}
- }
-
- runner := &runner.SubprocessRunner{
- Dir: t.dir,
- Env: t.env,
- }
-
- return runner.Run(ctx, command, stdout, stderr)
-}
-
-// SSHTester executes tests over an SSH connection. It assumes the test.Command
-// contains the command line to execute on the remote machine. The caller should Close() the
-// tester when finished. Once closed, this object can no longer be used.
-type SSHTester struct {
- client *ssh.Client
- newClient func(ctx context.Context) (*ssh.Client, error)
-}
-
-func NewSSHTester(newClient func(context.Context) (*ssh.Client, error)) (*SSHTester, error) {
- client, err := newClient(context.Background())
- if err != nil {
- return nil, err
- }
- return &SSHTester{client: client, newClient: newClient}, nil
-}
-
-func (t *SSHTester) Test(ctx context.Context, test testsharder.Test, stdout io.Writer, stderr io.Writer) error {
- if _, _, err := t.client.Conn.SendRequest(keepAliveOpenSSH, true, nil); err != nil {
- logger.Errorf(ctx, "SSH client not responsive: %v", err)
- client, err := t.newClient(ctx)
- if err != nil {
- return fmt.Errorf("failed to create new SSH client: %v", err)
- }
- t.client.Close()
- t.client = client
- }
-
- session, err := t.client.NewSession()
- if err != nil {
- return err
- }
- defer session.Close()
-
- runner := &runner.SSHRunner{Session: session}
- return runner.Run(ctx, test.Command, stdout, stderr)
-}
-
-// Close stops this SSHTester. The underlying SSH connection is terminated. The object
-// is no longer usable after calling this method.
-func (t *SSHTester) Close() error {
- return t.client.Close()
-}
-
-// FuchsiaTester executes tests on remote Fuchsia devices. The caller should Close() the
-// tester when finished. Once closed, this object can no longer be used.
-//
-// This is a hack. We have to run Fuchsia tests using `runtests` on the remote device
-// because there are many ways to execute Fuchsia tests and runtests already does this
-// correctly. This wrapper around SSHTester is meant to keep SSHTester free of OS-specific
-// behavior. Later we'll delete this and use SSHTester directly.
-type FuchsiaTester struct {
- remoteOutputDir string
- delegate *SSHTester
-}
-
-// NewFuchsiaTester creates a FuchsiaTester object and starts a log_listener process on
-// the remote device. The log_listener output can be read from SysLogOutput().
-func NewFuchsiaTester(nodename string, sshKey []byte) (*FuchsiaTester, error) {
- newClient := func(ctx context.Context) (*ssh.Client, error) {
- config, err := sshutil.DefaultSSHConfig(sshKey)
- if err != nil {
- return nil, fmt.Errorf("failed to create an SSH client config: %v", err)
- }
- client, err := sshutil.ConnectToNode(ctx, nodename, config)
- if err != nil {
- return nil, fmt.Errorf("failed to connect to node %q: %v", nodename, err)
- }
- return client, nil
- }
-
- delegate, err := NewSSHTester(newClient)
- if err != nil {
- return nil, err
- }
- tester := &FuchsiaTester{
- remoteOutputDir: fuchsiaOutputDir,
- delegate: delegate,
- }
- return tester, nil
-}
-
-func (t *FuchsiaTester) Test(ctx context.Context, test testsharder.Test, stdout io.Writer, stderr io.Writer) error {
- if len(test.Command) == 0 {
- name := path.Base(test.Path)
- test.Command = []string{"runtests", "-t", name, "-o", t.remoteOutputDir + "runtests"}
- }
- return t.delegate.Test(ctx, test, stdout, stderr)
-}
-
-// Close stops this FuchsiaTester. The remote log_listener process is terminated along
-// with the underlying SSH connection. The object is no longer usable after calling this
-// method.
-func (t *FuchsiaTester) Close() error {
- if err := t.delegate.Close(); err != nil {
- return fmt.Errorf("failed to close delegate ssh runner: %v", err)
- }
- return nil
-}
diff --git a/testing/testrunner/cmd/tester_test.go b/testing/testrunner/cmd/tester_test.go
deleted file mode 100644
index b6d49b1..0000000
--- a/testing/testrunner/cmd/tester_test.go
+++ /dev/null
@@ -1,171 +0,0 @@
-// 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 (
- "bytes"
- "context"
- "fmt"
- "io/ioutil"
- "os"
- "strings"
- "testing"
-
- "go.fuchsia.dev/tools/integration/testsharder"
- "go.fuchsia.dev/tools/net/sshutil"
-
- "golang.org/x/crypto/ssh"
-)
-
-func TestTester(t *testing.T) {
- cases := []struct {
- name string
- test testsharder.Test
- stdout string
- stderr string
- }{
- {
- name: "should run a command a local subprocess",
- test: testsharder.Test{
- Name: "hello_world_test",
- // Assumes that we're running on a Unix system.
- Command: []string{"/bin/echo", "Hello world!"},
- },
- stdout: "Hello world!",
- },
- }
-
- for _, tt := range cases {
- t.Run(tt.name, func(t *testing.T) {
- tester := &SubprocessTester{}
-
- stdout := new(bytes.Buffer)
- stderr := new(bytes.Buffer)
-
- if err := tester.Test(context.Background(), tt.test, stdout, stderr); err != nil {
- t.Errorf("failed to exected test %q: %v", tt.test.Name, err)
- return
- }
-
- // Compare stdout
- actual := strings.TrimSpace(stdout.String())
- expected := tt.stdout
- if actual != expected {
- t.Errorf("got stdout %q but wanted %q", actual, expected)
- }
-
- // Compare stderr
- actual = strings.TrimSpace(stderr.String())
- expected = tt.stderr
- if actual != expected {
- t.Errorf("got stderr %q but wanted %q", actual, expected)
- }
- })
- }
-}
-
-// Verifies that SSHTester can execute tests on a remote device. These tests are
-// only meant for local verification. You can execute them like this:
-//
-// FUCHSIA_NODENAME=<my nodename> FUCHSIA_SSH_KEY=<my key> go test ./...
-func TestSSHTester(t *testing.T) {
- t.Skip("ssh tests are meant for local testing only")
-
- nodename := os.Getenv("FUCHSIA_NODENAME")
- if nodename == "" {
- t.Fatal("FUCHSIA_NODENAME not set")
- }
- sshKeyFile := os.Getenv("FUCHSIA_SSH_KEY")
- if sshKeyFile == "" {
- t.Fatal("FUCHSIA_SSH_KEY not set")
- }
- sshKey, err := ioutil.ReadFile(sshKeyFile)
- if err != nil {
- t.Fatalf("could not read file %q", sshKeyFile)
- }
-
- cases := []struct {
- name string
- tests []testsharder.Test
- stdout string
- stderr string
- }{
- {
- name: "should run a command over SSH",
- tests: []testsharder.Test{
- {
- Name: "hello_world_test",
- // Just 'echo' and not '/bin/echo' because this assumes we're running on
- // Fuchsia.
- Command: []string{"echo", "Hello world!"},
- },
- },
- stdout: "Hello world!",
- },
- {
- name: "should run successive commands over SSH",
- tests: []testsharder.Test{
- {
- Name: "test_1",
- Command: []string{"echo", "this is test 1"},
- },
- {
- Name: "test_2",
- Command: []string{"echo", "this is test 2"},
- },
- },
- stdout: "this is test 1\nthis is test 2",
- },
- }
-
- for _, tt := range cases {
- t.Run(tt.name, func(t *testing.T) {
- newClient := func(ctx context.Context) (*ssh.Client, error) {
- config, err := sshutil.DefaultSSHConfig(sshKey)
- if err != nil {
- return nil, fmt.Errorf("failed to create an SSH client config: %v", err)
- }
- client, err := sshutil.ConnectToNode(ctx, nodename, config)
- if err != nil {
- return nil, fmt.Errorf("failed to connect to node %q: %v", nodename, err)
- }
- return client, nil
- }
-
- tester, err := NewSSHTester(newClient)
- if err != nil {
- t.Errorf("failed to intialize tester: %v", err)
- return
- }
-
- stdout := new(bytes.Buffer)
- stderr := new(bytes.Buffer)
- for _, test := range tt.tests {
- if err := tester.Test(context.Background(), test, stdout, stderr); err != nil {
- t.Error(err)
- return
- }
- }
-
- if err := tester.Close(); err != nil {
- t.Fatalf("failed to close tester: %v", err)
- }
-
- // Compare stdout
- actual := strings.TrimSpace(stdout.String())
- expected := tt.stdout
- if actual != expected {
- t.Errorf("got stdout %q but wanted %q", actual, expected)
- }
-
- // Compare stderr
- actual = strings.TrimSpace(stderr.String())
- expected = tt.stderr
- if actual != expected {
- t.Errorf("got stderr %q but wanted %q", actual, expected)
- }
- })
- }
-}
diff --git a/testing/testrunner/result.go b/testing/testrunner/result.go
deleted file mode 100644
index e252c2c..0000000
--- a/testing/testrunner/result.go
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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 testrunner handles specifics related to the testrunner tool.
-package testrunner
-
-import (
- "time"
-
- "go.fuchsia.dev/tools/testing/runtests"
-)
-
-// TestResult is the result of executing a test.
-type TestResult struct {
- // Name is the name of the test that was executed.
- Name string
-
- // Result describes whether the test passed or failed.
- Result runtests.TestResult
-
- Stdout []byte
- Stderr []byte
- StartTime time.Time
- EndTime time.Time
-}