gcc with --coverage

3,118 views
Skip to first unread message

Eyal Lotem

unread,
Feb 11, 2012, 4:04:13 PM2/11/12
to tup-...@googlegroups.com
Hey, trying to use the gcc "--coverage" with tup causes some trouble.

The way --coverage is supposed to work:

1) gcc --coverage -c sourcefile.c -o sourcefile.o # will generate sourcefile.gcno as a side-effect

2) gcc --coverage -o executable sourcefile.o # Nothing out of the ordinary. For linkage, --coverage means only -lgcov

3) ./executable # Will now generate sourcefile.gcda as a side-effect

4) gcov sourcefile.c # Reads gcno and gcda files, and generates sourcefile.c.gcov, containing readable coverage information

With tup, 1, 2 and 4 seem to go well, but 3 fails with an error like:

"profiling:.../tupexample/.tup/mnt/@tupjob-9/home/eyal/devel/tupexample/tupexample.gcda:Cannot open"

Apparently, gcc somehow sees through tup's fuse injection, and decides to record the absolute/full path at the time of tupjob-9.
Then it later tries to use the recorded filename when running the executable.

gcc does not seem to have any option to control the "gcno" and "gcda" filenames.

Can the fuse injection be more hidden from gcc here, somehow?

Eyal

tupexample.tgz

Mike Shal

unread,
Feb 13, 2012, 12:19:38 AM2/13/12
to tup-...@googlegroups.com

Hi Eyal,

This is something that is still a bit of a work-in-progress, but if
you make set the userid bit on the tup executable it will run all
sub-processes in a chroot. This way gcc doesn't know that its inside
the fuse file-system and thinks it's just writing to
'/home/user/foo/tupexample.o' instead of the whole path with the
@tupjob marker in it. However, all accesses go through fuse instead of
to the actual file-system so tup can see them. I built your example
with a suid tup and it ran successfully. Unfortunately you will have
to rebuild it from scratch if you switch between a suid tup and a
non-suid tup, since complete support for this isn't finished yet
(eventually it will be configured by an option to select how far
dependency detection & chrooting goes, and rebuilding will be
automatic when necessary).

Let me know if you have any trouble with running it in suid mode.

-Mike

Mike Shal

unread,
Mar 10, 2012, 9:34:11 PM3/10/12
to tup-...@googlegroups.com

Hi Eyal,

I believe this feature is now complete. You will still need tup with
suid root enabled, and you can specify certain processes to run in a
chroot using a ^-flag in the :-rule like so:

: foreach *.c |> ^c^ gcc --coverage -c %f -o %o |> %B.o | %B.gcno

Or if you already use the ^^ markers for display purposes:

: foreach *.c |> ^c CC %f^ gcc --coverage -c %f -o %o |> %B.o | %B.gcno

The 'c' character comes directly after the first carat, and tells tup
to run the sub-process in a chroot. All this really does is let the
sub-process not know that it is running in a funny tup directory, and
has a side effect of running a bit slower. Note that the
update.full_deps option also runs sub-processes in a chroot, but that
is for a different purpose.

I know the syntax is a little funky, but there is unfortunately no
easier way to get the information from the parser stage to the command
running stage. Let me know if you have any issues with it.
-Mike

Eyal Lotem

unread,
Mar 13, 2012, 4:06:01 AM3/13/12
to tup-...@googlegroups.com
Great!

gcc with coverage now works much more nicely.

However, this fix exposes another problem with gcc coverage.

Namely, that gcc --coverage also writes to the gcda files iff they exist (and they are created when executing the resulting program).

This poses two problems:
A) The gcda files are not considered auto-generated by tup
B) tup demands all outputs mentioned be written to ("Error: Expected to write to file '...' from cmd ... but didn't")

For now, I have auto-deletion of all gcda files after every execution as a work-around. Do you have an idea for a nicer way to handle gcda files?

Thanks!
Eyal

Mike Shal

unread,
Mar 15, 2012, 11:38:15 AM3/15/12
to tup-...@googlegroups.com
On Tue, Mar 13, 2012 at 4:06 AM, Eyal Lotem <eyal....@gmail.com> wrote:
> Great!
>
> gcc with coverage now works much more nicely.
>
> However, this fix exposes another problem with gcc coverage.
>
> Namely, that gcc --coverage also writes to the gcda files iff they exist
> (and they are created when executing the resulting program).
>
> This poses two problems:
> A) The gcda files are not considered auto-generated by tup
> B) tup demands all outputs mentioned be written to ("Error: Expected to
> write to file '...' from cmd ... but didn't")
>
> For now, I have auto-deletion of all gcda files after every execution as a
> work-around. Do you have an idea for a nicer way to handle gcda files?
>
> Thanks!
> Eyal

I don't think I understand completely - I tried a simple Tupfile:

: foreach *.c |> ^c^ gcc --coverage -c %f -o %o |> %B.o | %B.gcno

: *.o |> ^c^ gcc --coverage %f -o %o |> prog

And created a main.c and foo.c file. After 'tup upd' I got the .gcno
files and a program. I ran the program at the command line and got two
.gcda files. Now I touch foo.c and 'tup upd', and it runs successfully
- are you seeing it try to write to foo.gcda here?

-Mike

Slawomir Czarko

unread,
Mar 21, 2012, 10:28:48 AM3/21/12
to tup-...@googlegroups.com
Hi,

I've got two, questions about chroot mode:
- have you tried using "setcap cap_sys_chroot+ep tup" instead of using suid?
- I'm using chroot already for doing 32-bit builds on a 64-bit machine, will this prevent tup+chroot from working?

-Slawomir

Mike Shal

unread,
Mar 22, 2012, 9:20:48 AM3/22/12
to tup-...@googlegroups.com
On Wed, Mar 21, 2012 at 10:28 AM, Slawomir Czarko
<slawomi...@gmail.com> wrote:
> Hi,
>
> I've got two, questions about chroot mode:
> - have you tried using "setcap cap_sys_chroot+ep tup" instead of using suid?

In order to actually use the chroot, you have to bind-mount /dev into
the chroot directory (so that .tup/mnt/@tupjob-1/dev mirrors /dev),
otherwise the sub-process can't access things like /dev/null. Mounting
that requires cap_sys_admin anyway, so unfortunately just using
cap_sys_chroot isn't useful here. I would much prefer if tup didn't
have to mess with privileges at all, but I don't know of a better way
to run a sub-process purely inside the tup file-system without chroot.

> - I'm using chroot already for doing 32-bit builds on a 64-bit machine, will
> this prevent tup+chroot from working?

I'm not sure - is it easy for you to try? I don't have such an
environment set up. I have no idea how a series of chroots all the way
down would work :).

-Mike

Slawomir Czarko

unread,
Mar 22, 2012, 10:59:35 AM3/22/12
to tup-...@googlegroups.com


On Thursday, March 22, 2012 2:20:48 PM UTC+1, mar...@gmail.com wrote:
On Wed, Mar 21, 2012 at 10:28 AM, Slawomir Czarko
wrote:
> Hi,
>
> I've got two, questions about chroot mode:
> - have you tried using "setcap cap_sys_chroot+ep tup" instead of using suid?

In order to actually use the chroot, you have to bind-mount /dev into
the chroot directory (so that .tup/mnt/@tupjob-1/dev mirrors /dev),
otherwise the sub-process can't access things like /dev/null. Mounting
that requires cap_sys_admin anyway, so unfortunately just using
cap_sys_chroot isn't useful here. I would much prefer if tup didn't
have to mess with privileges at all, but I don't know of a better way
to run a sub-process purely inside the tup file-system without chroot.

OK
 

> - I'm using chroot already for doing 32-bit builds on a 64-bit machine, will
> this prevent tup+chroot from working?

I'm not sure - is it easy for you to try? I don't have such an
environment set up. I have no idea how a series of chroots all the way
down would work :).


 Is it enough to just add ^c^ in one command or do I need to do more to verify it would work?

-Slawomir

Mike Shal

unread,
Mar 22, 2012, 11:07:03 AM3/22/12
to tup-...@googlegroups.com

Yep, that should be it (and if tup is not privileged, it will give you
an error that it can't run it in a chroot). Assuming it builds
correctly, it might also help to check that changing an input file
actually triggers a re-compile (to make sure they aren't being dropped
somehow). But if it detects the output file being written correctly, I
don't think it will be a problem.

-Mike

Elliott Hird

unread,
Mar 22, 2012, 12:33:20 PM3/22/12
to tup-...@googlegroups.com
On 22 March 2012 13:20, Mike Shal <mar...@gmail.com> wrote:
> In order to actually use the chroot, you have to bind-mount /dev into
> the chroot directory (so that .tup/mnt/@tupjob-1/dev mirrors /dev),
> otherwise the sub-process can't access things like /dev/null.

Why not just individually link a few important devices like null,
zero, etc.? Most builds won't need to access /dev/tty or the like.

Mike Shal

unread,
Mar 22, 2012, 1:40:58 PM3/22/12
to tup-...@googlegroups.com

It looks like mknod() also requires privileged access. Is there some
other way you were thinking of linking them?

Thanks,
-Mike

Elliott Hird

unread,
Mar 22, 2012, 2:51:59 PM3/22/12
to tup-...@googlegroups.com
On 22 March 2012 17:40, Mike Shal <mar...@gmail.com> wrote:
> It looks like mknod() also requires privileged access. Is there some
> other way you were thinking of linking them?

I was thinking a symlink, but I suppose that could mess up programs
expecting a bona fide device. Still, I suspect most programs just try
and use the standard devices without caring too much about what they
actually are.

Mike Shal

unread,
Mar 23, 2012, 7:28:16 PM3/23/12
to tup-...@googlegroups.com

Unfortunately a symlink can't point outside the chroot :(. As far as
the sub-process is concerned, what it sees as "/dev/null" is really
"/home/user/.../.tup/mnt/@tupjob-1/dev/null". There isn't a way for it
to access the real /dev/null directly, so the symlink would be broken
(ignoring the possibility that a sub-process could explicitly break
out of the chroot, but tup isn't using chroot for security so that's
not really an issue).

Thanks,
-Mike

Chris Milburn

unread,
Oct 14, 2013, 5:19:16 PM10/14/13
to tup-...@googlegroups.com
Has a solution been created for the "The gcda files are not considered auto-generated by tup" yet?

The problem arises when you use a script to run your test inside of Tup.

Eg.

"
: foreach *.c |> ^c^ gcc --coverage -c %f -o %o |> %B.o | %B.gcno
: *.o |> ^c^ gcc --coverage %f -o %o |> prog
: prog |> runTestScript $f |>
"

The run test script forces the creation of the .gcda files as it ran the executable, but tup isn't aware of the gcda files, even with adding " | %B.gcda".
All you receive is: "tup error: Expected to write to file '*.gcda' from cmd * but didn't", or without that line it isn't expressed as an output.


- Chris

Mike Shal

unread,
Oct 15, 2013, 9:33:11 PM10/15/13
to tup-...@googlegroups.com
On Mon, Oct 14, 2013 at 5:19 PM, Chris Milburn <milb...@gmail.com> wrote:
Has a solution been created for the "The gcda files are not considered auto-generated by tup" yet?

The problem arises when you use a script to run your test inside of Tup.

Eg.

"
: foreach *.c |> ^c^ gcc --coverage -c %f -o %o |> %B.o | %B.gcno
: *.o |> ^c^ gcc --coverage %f -o %o |> prog
: prog |> runTestScript $f |>
"

The run test script forces the creation of the .gcda files as it ran the executable, but tup isn't aware of the gcda files, even with adding " | %B.gcda".
All you receive is: "tup error: Expected to write to file '*.gcda' from cmd * but didn't", or without that line it isn't expressed as an output.


What is the gcda string that shows up in your program? Here's what I get:

 $ strings prog | grep gcda
/home/marf/test-gcda/prog.gcda

Since it is creating the .gcda file using the full path, the file access unfortunately gets missed by tup (this is an annoying bug). However, I don't get any errors when running it, since tup doesn't even know the file was created. If instead I run the test script with ^c^, I get:

* 1) sh runTestScript                                                             
 *** tup errors ***
tup error: Unspecified output files - A command is writing to files that you didn't specify in the Tupfile. You should add them so tup knows what to expect.
 -- Unspecified output: prog.gcda
 *** Command ID=18 ran successfully, but tup failed to save the dependencies.
 [ ] 100%

When I add prog.gcda as an output for runTestScript, tup complains that I'm adding a generated node when it already exists as a normal file:

* 1) [0.001s] .
tup error: Attempting to insert 'prog.gcda' as a generated node when it already exists as a different type (normal file). You can do one of two things to fix this:
  1) If this file is really supposed to be created from the command, delete the file from the filesystem and try again.
  2) Change your rule in the Tupfile so you aren't trying to overwrite the file.
tup error: Error parsing Tupfile line 3
  Line was: ': prog |> ^c^ sh runTestScript |> prog.gcda'
 [ ] 100%

To proceed from there, I removed prog.gcda, and then updated. Tup detected the prog.gcda output this time, and ran successfully.

Were you possibly confusing the second error message with something else? Are you running the test script in a chroot or not?

Thanks,
-Mike

Chris Milburn

unread,
Oct 15, 2013, 11:49:20 PM10/15/13
to tup-...@googlegroups.com
The problem that I'm having is not just getting it to run successfully on my own machine.

My problem is actually when the CI build is run after I have moved or deleted a directory. I receive 
"tup error: Unable to clean up old directory in the build tree"on a different machine when
it tries to build after the directory change.

Looking further into that error I found out that it's when it had an old .gcda file in the build variant.
Because the .gcda file is not registered with the tup build, it is not able to clean the file correctly
when a different machine tries to pull from the repository and build. So somehow I need tup to know
about those .gcda files, but because they are generated outside of tup, it doesn't like it when they are still
in the build tree when the directory there no longer exists, and that file isn't in the tup database.

It's really strange because it works well on my machine, but it won't work on other machines when
they pull the change with the directory deletion or movement unless they delete the .gcda files,
or just the directory holding the .gcda files.

At least that's what I think is going on.


-Chris
Reply all
Reply to author
Forward
0 new messages