Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/emulator/start/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ type Config struct {
RedisURL string `default:"" flag:"redis-url" info:"redis-server URL for persisting redis storage backend ( redis://[[username:]password@]host[:port][/database] ) "`
SqliteURL string `default:"" flag:"sqlite-url" info:"sqlite db URL for persisting sqlite storage backend "`
CoverageReportingEnabled bool `default:"false" flag:"coverage-reporting" info:"enable Cadence code coverage reporting"`
ComputationProfilingEnabled bool `default:"false" flag:"computation-profiling" info:"enable Cadence computation profiling"`
LegacyContractUpgradeEnabled bool `default:"false" flag:"legacy-upgrade" info:"enable Cadence legacy contract upgrade"`
ForkHost string `default:"" flag:"fork-host" info:"gRPC access node address (host:port) to fork from"`
ForkHeight uint64 `default:"0" flag:"fork-height" info:"height to pin fork; defaults to latest sealed"`
Expand Down Expand Up @@ -225,6 +226,7 @@ func Cmd(config StartConfig) *cobra.Command {
ContractRemovalEnabled: conf.ContractRemovalEnabled,
SqliteURL: conf.SqliteURL,
CoverageReportingEnabled: conf.CoverageReportingEnabled,
ComputationProfilingEnabled: conf.ComputationProfilingEnabled,
ForkHost: conf.ForkHost,
ForkHeight: conf.ForkHeight,
CheckpointPath: conf.CheckpointPath,
Expand Down
53 changes: 46 additions & 7 deletions emulator/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,15 @@ func WithComputationReporting(enabled bool) Option {
}
}

// WithComputationProfile injects a ComputationProfile to collect coverage information.
//
// The default is nil.
func WithComputationProfile(computationProfile *runtime.ComputationProfile) Option {
return func(c *config) {
c.ComputationProfile = computationProfile
}
}

func WithScheduledTransactions(enabled bool) Option {
return func(c *config) {
c.ScheduledTransactionsEnabled = enabled
Expand Down Expand Up @@ -401,6 +410,7 @@ type config struct {
TransactionValidationEnabled bool
ChainID flowgo.ChainID
CoverageReport *runtime.CoverageReport
ComputationProfile *runtime.ComputationProfile
AutoMine bool
Contracts []ContractDescription
ComputationReportingEnabled bool
Expand Down Expand Up @@ -625,17 +635,29 @@ var _ environment.EntropyProvider = &blockHashEntropyProvider{}
func configureFVM(blockchain *Blockchain, conf config, blocks *blocks) (*fvm.VirtualMachine, fvm.Context, error) {
vm := fvm.NewVirtualMachine()

cadenceLogger := conf.Logger.Hook(CadenceHook{MainLogger: &conf.ServerLogger}).Level(zerolog.DebugLevel)
cadenceLogger := conf.Logger.
Hook(CadenceHook{
MainLogger: &conf.ServerLogger,
}).
Level(zerolog.DebugLevel)

if conf.ExecutionEffortWeights != nil &&
conf.ComputationProfile != nil {

conf.ComputationProfile.
WithComputationWeights(conf.ExecutionEffortWeights)
}

runtimeConfig := runtime.Config{
Debugger: blockchain.debugger,
CoverageReport: conf.CoverageReport,
Debugger: blockchain.debugger,
CoverageReport: conf.CoverageReport,
ComputationProfile: conf.ComputationProfile,
}
rt := runtime.NewRuntime(runtimeConfig)
customRuntimePool := reusableRuntime.NewCustomReusableCadenceRuntimePool(
1,
runtimeConfig,
func(config runtime.Config) runtime.Runtime {
func(_ runtime.Config) runtime.Runtime {
return rt
},
)
Expand Down Expand Up @@ -775,6 +797,10 @@ func bootstrapLedger(
fvm.ProcedureOutput,
error,
) {
if conf.ComputationProfile != nil {
conf.ComputationProfile.Reset()
}

accountKey := conf.GetServiceKey().AccountKey()
publicKey, _ := crypto.DecodePublicKey(
accountKey.SigAlgo,
Expand Down Expand Up @@ -807,7 +833,12 @@ func bootstrapLedger(
return executionSnapshot, output, nil
}

func configureBootstrapProcedure(conf config, flowAccountKey flowgo.AccountPublicKey, supply cadence.UFix64) *fvm.BootstrapProcedure {
func configureBootstrapProcedure(
conf config,
flowAccountKey flowgo.AccountPublicKey,
supply cadence.UFix64,
) *fvm.BootstrapProcedure {

options := make([]fvm.BootstrapProcedureOption, 0)
options = append(options,
fvm.WithInitialTokenSupply(supply),
Expand Down Expand Up @@ -1750,12 +1781,20 @@ func (b *Blockchain) CoverageReport() *runtime.CoverageReport {
return b.conf.CoverageReport
}

func (b *Blockchain) ResetCoverageReport() {
b.conf.CoverageReport.Reset()
}

func (b *Blockchain) ComputationReport() *ComputationReport {
return b.computationReport
}

func (b *Blockchain) ResetCoverageReport() {
b.conf.CoverageReport.Reset()
func (b *Blockchain) ComputationProfile() *runtime.ComputationProfile {
return b.conf.ComputationProfile
}

func (b *Blockchain) ResetComputationProfile() {
b.conf.ComputationProfile.Reset()
}

func (b *Blockchain) GetTransactionsByBlockID(blockID flowgo.Identifier) ([]*flowgo.TransactionBody, error) {
Expand Down
97 changes: 97 additions & 0 deletions emulator/computation_profile_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Flow Emulator
*
* Copyright Flow Foundation
*
* 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 emulator_test

import (
"context"
"testing"

"github.com/onflow/cadence"
"github.com/onflow/cadence/common"
"github.com/onflow/cadence/runtime"
flowsdk "github.com/onflow/flow-go-sdk"
"github.com/onflow/flow-go/fvm/meter"
flowgo "github.com/onflow/flow-go/model/flow"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/onflow/flow-emulator/adapters"
"github.com/onflow/flow-emulator/emulator"
)

func TestComputationProfile(t *testing.T) {

t.Parallel()

computationProfile := runtime.NewComputationProfile()
b, err := emulator.New(
emulator.WithComputationProfile(computationProfile),
emulator.WithExecutionEffortWeights(
meter.ExecutionEffortWeights{
common.ComputationKindFunctionInvocation: 1,
common.ComputationKindStatement: 1,
common.ComputationKindLoop: 1,
},
),
)
require.NoError(t, err)

computationProfile.Reset()

logger := zerolog.Nop()
adapter := adapters.NewSDKAdapter(&logger, b)

addTwoScript, counterAddress := DeployAndGenerateAddTwoScript(t, adapter)

tx := flowsdk.NewTransaction().
SetScript([]byte(addTwoScript)).
SetComputeLimit(flowgo.DefaultMaxTransactionGasLimit).
SetProposalKey(b.ServiceKey().Address, b.ServiceKey().Index, b.ServiceKey().SequenceNumber).
SetPayer(b.ServiceKey().Address).
AddAuthorizer(b.ServiceKey().Address)

signer, err := b.ServiceKey().Signer()
require.NoError(t, err)

err = tx.SignEnvelope(b.ServiceKey().Address, b.ServiceKey().Index, signer)
require.NoError(t, err)

callScript := GenerateGetCounterCountScript(counterAddress, b.ServiceKey().Address)

// Sample call (value is 0)
scriptResult, err := b.ExecuteScript([]byte(callScript), nil)
require.NoError(t, err)
assert.Equal(t, cadence.NewInt(0), scriptResult.Value)

// Submit tx (script adds 2)
err = adapter.SendTransaction(context.Background(), *tx)
require.NoError(t, err)

txResult, err := b.ExecuteNextTransaction()
require.NoError(t, err)
AssertTransactionSucceeded(t, txResult)

pprofProfile, err := runtime.NewPProfExporter(computationProfile).Export()
require.NoError(t, err)
require.NotNil(t, pprofProfile)

require.NotEmpty(t, pprofProfile.Function)
require.NotEmpty(t, pprofProfile.Sample)
}
4 changes: 1 addition & 3 deletions emulator/coverage_report_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,8 @@ func TestCoverageReport(t *testing.T) {
require.NoError(t, err)
AssertTransactionSucceeded(t, txResult)

address, err := common.HexToAddress(counterAddress.Hex())
require.NoError(t, err)
location := common.AddressLocation{
Address: address,
Address: common.MustBytesToAddress(counterAddress.Bytes()),
Name: "Counting",
}
coverage := coverageReport.Coverage[location]
Expand Down
6 changes: 6 additions & 0 deletions emulator/emulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ type ComputationReportCapable interface {
ComputationReport() *ComputationReport
}

type ComputationProfileCapable interface {
ComputationProfile() *runtime.ComputationProfile
ResetComputationProfile()
}

type DebuggingCapable interface {
StartDebugger() *interpreter.Debugger
EndDebugging()
Expand Down Expand Up @@ -190,6 +195,7 @@ type Emulator interface {

CoverageReportCapable
ComputationReportCapable
ComputationProfileCapable
DebuggingCapable
SnapshotCapable
RollbackCapable
Expand Down
26 changes: 26 additions & 0 deletions emulator/mocks/emulator.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 13 additions & 3 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ import (
"github.com/psiemens/graceland"
"github.com/rs/zerolog"

flowaccess "github.com/onflow/flow/protobuf/go/flow/access"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"

"github.com/onflow/flow-emulator/adapters"
"github.com/onflow/flow-emulator/emulator"
"github.com/onflow/flow-emulator/server/access"
Expand All @@ -46,9 +50,6 @@ import (
"github.com/onflow/flow-emulator/storage/remote"
"github.com/onflow/flow-emulator/storage/sqlite"
"github.com/onflow/flow-emulator/storage/util"
flowaccess "github.com/onflow/flow/protobuf/go/flow/access"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)

// EmulatorServer is a local server that runs a Flow Emulator instance.
Expand Down Expand Up @@ -142,6 +143,8 @@ type Config struct {
SqliteURL string
// CoverageReportingEnabled enables/disables Cadence code coverage reporting.
CoverageReportingEnabled bool
// ComputationProfilingEnabled enables/disables Cadence computation profiling.
ComputationProfilingEnabled bool
// ForkHost is the gRPC access node address to fork from (host:port).
ForkHost string
// ForkHeight is the height at which to start the emulator when forking.
Expand Down Expand Up @@ -513,6 +516,13 @@ func configureBlockchain(logger *zerolog.Logger, chainID flowgo.ChainID, conf *C
)
}

if conf.ComputationProfilingEnabled {
options = append(
options,
emulator.WithComputationProfile(runtime.NewComputationProfile()),
)
}

if conf.ComputationReportingEnabled {
options = append(
options,
Expand Down
Loading
Loading