github.com/firecracker-microvm/firecracker-go-sdk@v1.0.0/benchmark_test.go (about)

     1  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"). You may
     4  // not use this file except in compliance with the License. A copy of the
     5  // License is located at
     6  //
     7  //	http://aws.amazon.com/apache2.0/
     8  //
     9  // or in the "license" file accompanying this file. This file is distributed
    10  // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
    11  // express or implied. See the License for the specific language governing
    12  // permissions and limitations under the License.
    13  package firecracker
    14  
    15  import (
    16  	"bufio"
    17  	"context"
    18  	"fmt"
    19  	"io/ioutil"
    20  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/sirupsen/logrus"
    26  
    27  	models "github.com/firecracker-microvm/firecracker-go-sdk/client/models"
    28  )
    29  
    30  const numberOfVMs = 200
    31  
    32  func createMachine(ctx context.Context, name string, forwardSignals []os.Signal) (*Machine, func(), error) {
    33  	dir, err := ioutil.TempDir("", name)
    34  	if err != nil {
    35  		return nil, nil, err
    36  	}
    37  	cleanup := func() {
    38  		os.RemoveAll(dir)
    39  	}
    40  
    41  	socketPath := filepath.Join(dir, "api.sock")
    42  	vmlinuxPath := filepath.Join(testDataPath, "./vmlinux")
    43  	logFifo := filepath.Join(dir, "log.fifo")
    44  	metrics := filepath.Join(dir, "metrics.fifo")
    45  
    46  	config := Config{
    47  		SocketPath:      socketPath,
    48  		KernelImagePath: vmlinuxPath,
    49  		LogFifo:         logFifo,
    50  		MetricsFifo:     metrics,
    51  		LogLevel:        "Info",
    52  		MachineCfg: models.MachineConfiguration{
    53  			VcpuCount:   Int64(1),
    54  			CPUTemplate: models.CPUTemplate(models.CPUTemplateT2),
    55  			MemSizeMib:  Int64(256),
    56  			Smt:         Bool(false),
    57  		},
    58  		Drives: []models.Drive{
    59  			{
    60  				DriveID:      String("root"),
    61  				IsRootDevice: Bool(true),
    62  				IsReadOnly:   Bool(true),
    63  				PathOnHost:   String(testRootfs),
    64  			},
    65  		},
    66  		ForwardSignals: forwardSignals,
    67  	}
    68  
    69  	cmd := VMCommandBuilder{}.
    70  		WithSocketPath(socketPath).
    71  		WithBin(getFirecrackerBinaryPath()).
    72  		Build(ctx)
    73  
    74  	log := logrus.New()
    75  	log.SetLevel(logrus.FatalLevel)
    76  	machine, err := NewMachine(ctx, config, WithProcessRunner(cmd), WithLogger(logrus.NewEntry(log)))
    77  	if err != nil {
    78  		return nil, cleanup, err
    79  	}
    80  
    81  	return machine, cleanup, nil
    82  }
    83  
    84  func startAndWaitVM(ctx context.Context, m *Machine) error {
    85  	err := m.Start(ctx)
    86  	if err != nil {
    87  		return err
    88  	}
    89  
    90  	file, err := os.Open(m.LogFile())
    91  	if err != nil {
    92  		return err
    93  	}
    94  
    95  	scanner := bufio.NewScanner(file)
    96  	for scanner.Scan() {
    97  		line := scanner.Text()
    98  		if strings.Contains(line, "Guest-boot-time") {
    99  			break
   100  		}
   101  	}
   102  	err = m.StopVMM()
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	err = m.Wait(ctx)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  func benchmarkForwardSignals(b *testing.B, forwardSignals []os.Signal) {
   116  	ctx := context.Background()
   117  
   118  	b.Logf("%s: %d", b.Name(), b.N)
   119  
   120  	for i := 0; i < b.N; i++ {
   121  		errCh := make(chan error, numberOfVMs)
   122  		for j := 0; j < numberOfVMs; j++ {
   123  			go func() {
   124  				var err error
   125  				defer func() { errCh <- err }()
   126  
   127  				machine, cleanup, err := createMachine(ctx, b.Name(), forwardSignals)
   128  				if err != nil {
   129  					err = fmt.Errorf("failed to create a VM: %v", err)
   130  					return // anonymous defer func() will deliver the error
   131  				}
   132  				defer cleanup()
   133  
   134  				err = startAndWaitVM(ctx, machine)
   135  				if err != nil && !strings.Contains(err.Error(), "signal: terminated") {
   136  					err = fmt.Errorf("failed to start the VM: %v", err)
   137  					return // anonymous defer func() will deliver the error
   138  				}
   139  				return // anonymous defer func() will deliver this nil error
   140  			}()
   141  		}
   142  		for k := 0; k < numberOfVMs; k++ {
   143  			err := <-errCh
   144  			if err != nil {
   145  				b.Fatal(err)
   146  			}
   147  		}
   148  		close(errCh)
   149  	}
   150  }
   151  func BenchmarkForwardSignalsDefault(t *testing.B) {
   152  	benchmarkForwardSignals(t, nil)
   153  }
   154  
   155  func BenchmarkForwardSignalsDisable(t *testing.B) {
   156  	benchmarkForwardSignals(t, []os.Signal{})
   157  }