foundry-rs / foundry

Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.
https://getfoundry.sh
Apache License 2.0
8.09k stars 1.67k forks source link

feat: attach a `CoverageInspector` to the `Executor` #6973

Open cvick32 opened 7 months ago

cvick32 commented 7 months ago

Component

Other (please describe)

Describe the feature you would like

Feature: Getting coverage information from anywhere by attaching a CoverageInspector to the Executor.

I'm trying to find coverage during a run of a transaction, so I tried adding a Coverage Inspector to the ExecutorBuilder. I'm able to see that the coverage bool is set in the Executor, but after I run a transaction on the Executor and I run collect on the InspectorStack I get an empty HitMap.

I'm thinking that this is happening because the Analyzer is never called on the source contracts, so there are no Anchors but I'm not sure.

Let me know if this is out of scope or if it's already possible and I've missed something.

Thanks!

Additional context

No response

DaniPopes commented 7 months ago

The coverage inspector is 20 lines of code, are you getting Some() with an empty hitmap when coverage is set to true?

cvick32 commented 7 months ago

Yes, I've set coverage to true in the ExecutorBuilder, and called call_raw_comitting. I figured I would see some information of what was passed through the Interpreter, but I got Some(HitMaps({})).

cvick32 commented 7 months ago

I figured I would try to write my own inspector and pass it to inspect_ref just to see what happens. I have a CoverageInspector that overrides step and just attempts to print out the current opcode. I am unable to see that information. Here's what the call looks like from my rust code (bear in mind this is supposed to just wrap the call_raw call, but I wanted to make sure that my CoverageInspector gets passed in):

        _self: PyRef<'_, Self>,
        caller: &str,
        to: &str,
        value: Option<U256>,
        data: Option<Vec<u8>>,
    ) -> PyResult<String> {
        let res = _self
            .0
            .call_raw(
                addr(caller)?.into(),
                addr(to)?.into(),
                data.unwrap_or_default().into(),
                value.unwrap_or_default().into(),
            )
            .map_err(pyerr)?;

        let mut db: FuzzBackendWrapper<'_> = FuzzBackendWrapper::new(&_self.0.backend);
        let mut env = _self.0.env.clone();
        let mut cov = CoverageInspector::new();
        let result = db.inspect_ref(&mut env, &mut cov);

        print!("RESULT: {:?}", result);
        print!("COVERAGE: {:?}", cov);

    }

So, when I execute run this call_raw I see an empty CoverageInspector is printed. How is that possible if I've just executed the call to inspect_ref? Is there something else I need to call? Or does step never get called from my transaction?