Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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"`
StartBlockHeight uint64 `default:"0" flag:"start-block-height" info:"block height to start the emulator at. only valid when forking Mainnet or Testnet"`
RPCHost string `default:"" flag:"rpc-host" info:"rpc host to query when forking Mainnet or Testnet"`
Expand Down Expand Up @@ -217,6 +218,7 @@ func Cmd(config StartConfig) *cobra.Command {
ContractRemovalEnabled: conf.ContractRemovalEnabled,
SqliteURL: conf.SqliteURL,
CoverageReportingEnabled: conf.CoverageReportingEnabled,
ComputationProfilingEnabled: conf.ComputationProfilingEnabled,
StartBlockHeight: conf.StartBlockHeight,
RPCHost: conf.RPCHost,
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 @@ -315,6 +315,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 @@ -402,6 +411,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 @@ -627,17 +637,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 @@ -776,6 +798,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 @@ -808,7 +834,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 @@ -1712,12 +1743,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
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 @@ -182,6 +187,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.

9 changes: 9 additions & 0 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ type Config struct {
SqliteURL string
// CoverageReportingEnabled enables/disables Cadence code coverage reporting.
CoverageReportingEnabled bool
// ComputationProfilingEnabled enables/disables Cadence computation profiling.
ComputationProfilingEnabled bool
// RPCHost is the address of the access node to use when using a forked network.
RPCHost string
// StartBlockHeight is the height at which to start the emulator.
Expand Down Expand Up @@ -463,6 +465,13 @@ func configureBlockchain(logger *zerolog.Logger, conf *Config, store storage.Sto
)
}

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

if conf.ComputationReportingEnabled {
options = append(
options,
Expand Down
39 changes: 34 additions & 5 deletions server/utils/emulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import (
"strconv"

"github.com/gorilla/mux"
"github.com/onflow/cadence/runtime"
flowgo "github.com/onflow/flow-go/model/flow"

"github.com/onflow/flow-emulator/adapters"
"github.com/onflow/flow-emulator/emulator"
)
Expand Down Expand Up @@ -64,6 +66,9 @@ func NewEmulatorAPIServer(emulator emulator.Emulator, adapter *adapters.AccessAd
router.HandleFunc("/emulator/codeCoverage", r.CodeCoverage).Methods("GET")
router.HandleFunc("/emulator/codeCoverage/reset", r.ResetCodeCoverage).Methods("PUT")

router.HandleFunc("/emulator/computationProfile", r.ComputationProfile).Methods("GET")
router.HandleFunc("/emulator/computationProfile/reset", r.ResetComputationProfile).Methods("PUT")

router.HandleFunc("/emulator/computationReport", r.ComputationReport).Methods("GET")

return r
Expand All @@ -86,7 +91,7 @@ func (m EmulatorAPIServer) Config(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write(s)
}

func (m EmulatorAPIServer) CommitBlock(w http.ResponseWriter, r *http.Request) {
func (m EmulatorAPIServer) CommitBlock(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, err := m.emulator.CommitBlock()
if err != nil {
Expand Down Expand Up @@ -230,7 +235,7 @@ func (m EmulatorAPIServer) SnapshotCreate(w http.ResponseWriter, r *http.Request
m.latestBlockResponse(name, w)
}

func (m EmulatorAPIServer) CodeCoverage(w http.ResponseWriter, r *http.Request) {
func (m EmulatorAPIServer) CodeCoverage(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")

err := json.NewEncoder(w).Encode(m.emulator.CoverageReport())
Expand All @@ -240,7 +245,13 @@ func (m EmulatorAPIServer) CodeCoverage(w http.ResponseWriter, r *http.Request)
}
}

func (m EmulatorAPIServer) ComputationReport(w http.ResponseWriter, r *http.Request) {
func (m EmulatorAPIServer) ResetCodeCoverage(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
m.emulator.ResetCoverageReport()
w.WriteHeader(http.StatusOK)
}

func (m EmulatorAPIServer) ComputationReport(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")

err := json.NewEncoder(w).Encode(m.emulator.ComputationReport())
Expand All @@ -250,9 +261,27 @@ func (m EmulatorAPIServer) ComputationReport(w http.ResponseWriter, r *http.Requ
}
}

func (m EmulatorAPIServer) ResetCodeCoverage(w http.ResponseWriter, r *http.Request) {
func (m EmulatorAPIServer) ComputationProfile(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/gzip")

computationProfile := m.emulator.ComputationProfile()

pprofProfile, err := runtime.NewPProfExporter(computationProfile).Export()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}

err = pprofProfile.Write(w)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
}

func (m EmulatorAPIServer) ResetComputationProfile(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
m.emulator.ResetCoverageReport()
m.emulator.ResetComputationProfile()
w.WriteHeader(http.StatusOK)
}

Expand Down
Loading