How do I disable Devel::Cover for forked child processes?
Asked Answered
L

2

7

I noticed, that when I run my program with perl -MDevel::Cover=-silent,-nogcov foo.pl to collect coverage information for foo.pl, I am getting massive slowdowns from parts of my program that fork and exec non-perl programs like tar, gzip or dpkg-deb. Thanks to this question I figured out how to disable Devel::Cover selectively, so I'm now writing:

my $is_covering = !!(eval 'Devel::Cover::get_coverage()');
my $pid = fork();
if ($pid == 0) {
    eval 'Devel::Cover::set_coverage("none")' if $is_covering;
    exec 'tar', '-cf', ...
}

Doing so, shaves off five minutes of runtime per test which, for 122 tests saves me 10 hours of computation time.

Unfortunately, I cannot always add this eval statement into the forked child process. For example it's impossible to do so when I use system(). I want to avoid rewriting each of my system() calls to a manual fork/exec.

Is there a way to disable Devel::Cover for my forked processes or basically for everything that is not my script foo.pl?

Thanks!

Ligan answered 7/1, 2020 at 7:25 Comment(0)
S
1

Forks::Super is kind of heavy, but it has the feature of post-fork callbacks that are executed after each fork but before any other code in a child process is executed.

use Forks::Super;
my $is_covering = !!(eval 'Devel::Cover::get_coverage()');
POSTFORK_CHILD {
    # runs in every child process immediately after fork()
    eval 'Devel::Cover::set_coverage("none")' if $is_covering;
};
...
Serving answered 7/1, 2020 at 18:12 Comment(2)
My script right now does not depend on anything besides perl Core modules. This module is not even packaged in Debian or Ubuntu... :(Ligan
Additionally, this technique has the disadvantage that it would then disable Devel::Cover even in cases where I'm forking myself without calling exec. In these cases I obviously do not want to disable Devel::Cover. The perfect solution would specifically tell Devel::Cover: "trace only this file but stop tracing if exec-ing another". So instead of a POSTFORK hook, I'd rather need a pre-exec hook.Ligan
S
1

I suspect your problem is not the fork per se, but rather the exec. The difference is somewhat academic but might lead to a possible solution. If you don't mind compiling your own version of Devel::Cover you could try commenting out this line: https://github.com/pjcj/Devel--Cover/blob/05392f3062dd2bdbf019d9a8fbae1b152b97d862/Cover.xs#L1140

This will cause any coverage data collected before an exec call to be lost and speed up the exec call.

If you can't compile your own version, adding local *Devel::Cover::_report = sub { }; before the exec calls should also speed up the execs but this is ultimately a similar solution to what you have already with the disadvantage of not using a published API.

Schonthal answered 9/1, 2020 at 20:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.