| // Copyright 2020 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 fuzz |
| |
| import ( |
| "fmt" |
| "io" |
| "time" |
| |
| "github.com/golang/glog" |
| ) |
| |
| // An Instance is a specific combination of build, connector, and launcher, |
| // representable by a handle. Most methods of this interface map directly to |
| // the ClusterFuchsia API. |
| type Instance interface { |
| Start() error |
| Stop() error |
| ListFuzzers() []string |
| Get(fuzzerName, targetSrc, hostDst string) error |
| Put(fuzzerName, hostSrc, targetDst string) error |
| RunFuzzer(out io.Writer, name, hostArtifactDir string, args ...string) error |
| Handle() (Handle, error) |
| Close() |
| } |
| |
| // BaseInstance groups the core subobjects, to which most work will be delegated |
| type BaseInstance struct { |
| Build Build |
| Connector Connector |
| Launcher Launcher |
| } |
| |
| // NewInstance creates a fresh instance |
| func NewInstance() (Instance, error) { |
| build, err := NewBuild() |
| if err != nil { |
| return nil, fmt.Errorf("Error configuring build: %s", err) |
| } |
| |
| if err := build.Prepare(); err != nil { |
| return nil, fmt.Errorf("Error preparing build: %s", err) |
| } |
| |
| // TODO(fxbug.dev/47479): should the user be able to choose connector/launcher types? |
| launcher := NewQemuLauncher(build) |
| |
| // Note: We can't get a Connector until the Launcher has started |
| return &BaseInstance{build, nil, launcher}, nil |
| } |
| |
| func loadInstanceFromHandle(handle Handle) (Instance, error) { |
| // TODO(fxbug.dev/47320): Store build info in the handle too |
| build, err := NewBuild() |
| if err != nil { |
| return nil, fmt.Errorf("Error configuring build: %s", err) |
| } |
| |
| connector, err := loadConnectorFromHandle(handle) |
| if err != nil { |
| return nil, err |
| } |
| |
| launcher, err := loadLauncherFromHandle(build, handle) |
| if err != nil { |
| return nil, err |
| } |
| |
| return &BaseInstance{build, connector, launcher}, nil |
| } |
| |
| // Close releases the Instance, but doesn't Stop it |
| func (i *BaseInstance) Close() { |
| i.Connector.Close() |
| } |
| |
| // Start boots up the instance and waits for connectivity to be established |
| func (i *BaseInstance) Start() error { |
| conn, err := i.Launcher.Start() |
| if err != nil { |
| return err |
| } |
| i.Connector = conn |
| |
| glog.Infof("Waiting for connectivity...") |
| |
| sleep := 3 * time.Second |
| success := false |
| for i := 0; i < 3; i++ { |
| cmd := conn.Command("echo", "hello") |
| cmd.SetTimeout(5 * time.Second) |
| if err := cmd.Start(); err != nil { |
| return err |
| } |
| |
| if err := cmd.Wait(); err != nil { |
| // If you forgot to --with-base the devtools: |
| // error: 2 (/boot/bin/sh: 1: Cannot create child process: -1 (ZX_ERR_INTERNAL): |
| // failed to resolve fuchsia-pkg://fuchsia.com/ls#bin/ls |
| glog.Warningf("Got error during attempt %d: %s", i, err) |
| glog.Warningf("Retrying in %s...", sleep) |
| time.Sleep(sleep) |
| } else { |
| glog.Info("Instance is now online.") |
| success = true |
| |
| if sc, ok := conn.(*SSHConnector); ok { |
| glog.Infof("Access via: ssh -i'%s' %s:%d", sc.Key, sc.Host, sc.Port) |
| } |
| break |
| } |
| } |
| if !success { |
| return fmt.Errorf("error establishing connectivity to instance") |
| } |
| |
| return nil |
| } |
| |
| // RunFuzzer runs the named fuzzer on the Instance. If `hostArtifactDir` is |
| // specified and the run generated any output artifacts, they will be copied to |
| // that directory. `args` is an optional list of arguments in the form |
| // `-key=value` that will be passed to libFuzzer. |
| func (i *BaseInstance) RunFuzzer(out io.Writer, name, hostArtifactDir string, args ...string) error { |
| fuzzer, err := i.Build.Fuzzer(name) |
| if err != nil { |
| return err |
| } |
| |
| fuzzer.Parse(args) |
| artifacts, err := fuzzer.Run(i.Connector, out, hostArtifactDir) |
| if err != nil { |
| return err |
| } |
| |
| if hostArtifactDir != "" { |
| for _, art := range artifacts { |
| if err := i.Get(name, art, hostArtifactDir); err != nil { |
| return err |
| } |
| } |
| } |
| |
| return nil |
| } |
| |
| // Get copies files from a fuzzer namespace on the Instance to the host |
| func (i *BaseInstance) Get(fuzzerName, targetSrc, hostDst string) error { |
| fuzzer, err := i.Build.Fuzzer(fuzzerName) |
| if err != nil { |
| return err |
| } |
| return i.Connector.Get(fuzzer.AbsPath(targetSrc), hostDst) |
| } |
| |
| // Put copies files from the host to a fuzzer namespace on the Instance |
| func (i *BaseInstance) Put(fuzzerName, hostSrc, targetDst string) error { |
| fuzzer, err := i.Build.Fuzzer(fuzzerName) |
| if err != nil { |
| return err |
| } |
| return i.Connector.Put(hostSrc, fuzzer.AbsPath(targetDst)) |
| } |
| |
| // Stop shuts down the Instance |
| func (i *BaseInstance) Stop() error { |
| return i.Launcher.Kill() |
| } |
| |
| // Handle returns a Handle representing the Instance |
| func (i *BaseInstance) Handle() (Handle, error) { |
| handle, err := NewHandleFromObjects(i.Connector, i.Launcher) |
| if err != nil { |
| return nil, fmt.Errorf("error constructing instance handle: %s", err) |
| } |
| |
| return handle, nil |
| } |
| |
| // ListFuzzers lists fuzzers available on the Instance |
| func (i *BaseInstance) ListFuzzers() []string { |
| return i.Build.ListFuzzers() |
| } |