On 08/29/2018 01:18 PM, Richard Biener wrote:
> On Wed, Aug 29, 2018 at 12:44 PM Martin Liška <[email protected]> wrote:
>>
>> On 08/29/2018 11:17 AM, Richard Biener wrote:
>>> On Wed, Aug 29, 2018 at 10:31 AM Martin Liška <[email protected]> wrote:
>>>>
>>>> Hello.
>>>>
>>>> Moving the thread from gcc ML into gcc-patches. That's first implementation
>>>> of shared libgcov library. Currently inclusion of t-libgcov is added only
>>>> to *-linux targets. Is it fine to add to all configurations that already
>>>> include 't-slibgcc t-slibgcc-elf-ver'?
>>>>
>>>> Patch can bootstrap on ppc64le-redhat-linux and survives regression tests.
>>>
>>> I understand this is to make profiling work with multiple DSOs (all
>>> instrumented).
>>
>> Yes.
>>
>>> But does this also make the case work when those DSOs are dlopened/dlclosed
>>> multiple times?
>>
>> That works fine even now, when a DSO is loaded, __gcov_init is called and
>> it registers gcov_info into __gcov_root.
>>
>> Following sample:
>>
>> #include <dlfcn.h>
>> #include <assert.h>
>> #include <unistd.h>
>>
>> int main()
>> {
>> for (int i = 0; i < 10; i++)
>> {
>> void *handle = dlopen ("libfoo.so", RTLD_NOW);
>> assert (handle);
>>
>> void (*fn) (void) = dlsym (handle, "foo");
>> assert (fn);
>>
>> fn ();
>> sleep (1);
>> }
>>
>> return 0;
>> }
>
> That doesn't dlclose ;)
Yep, I tested that but with compiler (static libgcov) :/
I noticed that make check for postgres generated invalid *.gcda files.
If's caused by fact that:
libgcov-driver.c:
/* Per-dynamic-object gcov state. */
struct gcov_root __gcov_root;
is not longer per-DSO, but global. So when a shared library is unloaded
with dlclose, it computes histogram of all gcov_info files. As the program
still runs, arcs counters are being updated.
>
>> has:
>>
>> $ gcov-dump -l foo.gcda
>> foo.gcda:data:magic `gcda':version `A82*'
>> foo.gcda:stamp 2235622999
>> foo.gcda: a3000000: 22:PROGRAM_SUMMARY checksum=0x02bfe640
>> foo.gcda: counts=2, runs=1, sum_all=20, run_max=10, sum_max=10
>> foo.gcda: counter histogram:
>> foo.gcda: 9: num counts=2, min counter=10, cum_counter=20
>> foo.gcda: 01000000: 3:FUNCTION ident=1636255671,
>> lineno_checksum=0x2172766e, cfg_checksum=0xc0bbb23e
>> foo.gcda: 01a10000: 4:COUNTERS arcs 2 counts
>> foo.gcda: 0: 10 10
>> foo.gcda: 01af0000: 2:COUNTERS time_profiler 1 counts
>> foo.gcda: 0: 1
>>
>> which is fine, runs == 1 and arcs counter executed 10x.
>>
>>
>> I understand that with the shared libgcov implementation this
>>> library must be finalized last?
>>
>> If you do profiling, then you depend on libgcov, thus it's loaded before a
>> DSO (or executable)
>> is loaded.
>>
>> Wouldn't it be better to have some additional
>>> object that lives in an executable only? Or do we want profiled DSOs
>>> without
>>> profiling the executable using it?
>>
>> That should be rare, but it will be possible as __gcov_exit will be
>> eventually called.
>
> I'm not too familiar but I guess each DSO _fini will call __gcov_exit?
Yes.
>
>> Does that case work with the shared libgcov
>>> (with dlopen/dlclose)?
>>
>> Yes, I've just tested that.
>>
>> Does it work when DSO1 is compiled with GCC N
>>> and DSO2 is compiled with GCC N+1 where the libgcov ABI changed?
>>
>> For such case we have:
>>
>> /* Exactly one of these will be live in the process image. */
>> struct gcov_master __gcov_master =
>> {GCOV_VERSION, 0};
>> ...
>> and it's check in __gcov_init.
>
> So it doesn't work. Too bad, in the past we shipped a profiling (-pg)
> glibc build
> but that would then only work for the same compiler.
>
> I guess using a shared libgcov makes profiling the dynamic loader DSO
> impossible?
You mean ld.so?
> So we'd likely want a -static-libgcov option as well?
If you use -static, that works now.
>
>>>
>>> I think we need a better understanding of the situation before jumping to
>>> the conclusion that a shared libgcov is the solution. Maybe better
>>> isolating
>>> the individual instances is better?
>>
>> It's undesired as seen in the PR. When doing profiling of indirect jumps
>> we need have exactly one __gcov_indirect_call_callee. That's because one can
>> do indirect calls that cross DSO boundaries.
>
> But usually you don't inline across DSO boundaries and after devirtualizing
> you still need to go through the PLT...
>
> Btw, when you dlopen/dlcose a DSO multiple times the same function can
> end up at different addresses, does gcov somehow deal with this? It seems
> that the profile functions get the callee address.
That's fine, profile_id is saved:
if (__gcov_indirect_call_callee != NULL)
__gcov_indirect_call_profiler_v2 (profile_id, ¤t_function_decl);
tree_uid = build_int_cst
(gcov_type_node,
cgraph_node::get (current_function_decl)->profile_id);
>
> Can you shortly tell why the testcase in the PR segfaults? Does the issue
> only affect indirect call profiling?
What happens is that there will exist 2 instances of:
void * __gcov_indirect_call_callee;
one in main executable, and one another in DSO loaded via dlopen.
Then caller sets __gcov_indirect_call_callee to a function pointer, but when
the actual function is called it has it's own __gcov_indirect_call_callee
instance
and it's zero. Then following dereferencing happens:
if (cur_func == __gcov_indirect_call_callee
|| (__LIBGCC_VTABLE_USES_DESCRIPTORS__
&& *(void **) cur_func == *(void **) __gcov_indirect_call_callee))
__gcov_one_value_profiler_body (__gcov_indirect_call_counters, value, 0);
Well, yes, it's only related to indirect profiling. And only benefit is that we
maybe
miss a target of an indirect call. Profile for indirect call is beneficial only
for speculative inlining, which will not be possible because the callee lives in
a different DSO. Thus the code of such function will not be probably available
in compilation unit of a caller.
That said, it looks the shared library libgcov.so is overkill.
Alexander may explain how that can be beneficial?
Note that I can fix the segfault being caused by inter-DSO calls.
Martin
>
> Richard.
>
>> Martin
>>
>>>
>>> Richard.
>>>
>>>> Martin
>>>>
>>>> libgcc/ChangeLog:
>>>>
>>>> 2018-08-28 Martin Liska <[email protected]>
>>>>
>>>> PR gcov-profile/84107
>>>> * Makefile.in: Build libgcov.so objects separately,
>>>> add rule for libgcov.map and how the library
>>>> is linked.
>>>> * config.host: Add t-libgcov and t-libgcov-elf-ver
>>>> to *-linux targets.
>>>> * config/t-libgcov: New file.
>>>> * config/t-libgcov-elf-ver: New file.
>>>> * libgcov-driver.c: Declare function if new L_gcov_shared
>>>> is defined.
>>>> * libgcov-interface.c: Likewise.
>>>> * libgcov-merge.c: Likewise.
>>>> * libgcov-profiler.c: Likewise.
>>>> * libgcov-std.ver.in: New file.
>>>> * libgcov.h (__gcov_init): Remove ATTRIBUTE_HIDDEN.
>>>> (__gcov_exit): Likewise.
>>>> (__gcov_merge_add): Likewise.
>>>> (__gcov_merge_time_profile): Likewise.
>>>> (__gcov_merge_single): Likewise.
>>>> (__gcov_merge_ior): Likewise.
>>>> (__gcov_merge_icall_topn): Likewise.
>>>> (gcov_sort_n_vals): Likewise.
>>>> (__gcov_fork): Likewise.
>>>> (__gcov_execl): Likewise.
>>>> (__gcov_execlp): Likewise.
>>>> (__gcov_execle): Likewise.
>>>> (__gcov_execv): Likewise.
>>>> (__gcov_execvp): Likewise.
>>>> (__gcov_execve): Likewise.
>>>> ---
>>>> libgcc/Makefile.in | 56 ++++++++++++++++++++++++++++++---
>>>> libgcc/config.host | 2 +-
>>>> libgcc/config/t-libgcov | 46 +++++++++++++++++++++++++++
>>>> libgcc/config/t-libgcov-elf-ver | 3 ++
>>>> libgcc/libgcov-driver.c | 4 +--
>>>> libgcc/libgcov-interface.c | 28 ++++++++---------
>>>> libgcc/libgcov-merge.c | 14 ++++-----
>>>> libgcc/libgcov-profiler.c | 34 ++++++++++----------
>>>> libgcc/libgcov-std.ver.in | 53 +++++++++++++++++++++++++++++++
>>>> libgcc/libgcov.h | 31 +++++++++---------
>>>> 10 files changed, 209 insertions(+), 62 deletions(-)
>>>> create mode 100644 libgcc/config/t-libgcov
>>>> create mode 100644 libgcc/config/t-libgcov-elf-ver
>>>> create mode 100644 libgcc/libgcov-std.ver.in
>>>>
>>>>
>>