[llvm-dev] Using source-based code coverage on baremetal
Friedman, Eli via llvm-dev
llvm-dev at lists.llvm.org
Tue Sep 5 18:55:45 PDT 2017
I think using code coverage on baremetal has come up once or twice on
llvmdev, but I don't think anyone has actually written up how the
workflow works, or what issues come up. This description is based on
work done together with my colleague Weiming Zhao.
By "baremetal" here, I mean an embedded environment without an operating
system. We specifically used a ARM target with semihosting, but similar
steps should work elsewhere.
1. First, you need a copy of libclangrt_profile.a, stripped down for the
baremetal target. (More on this below.)
2. Then, you need to change the source code to call into it; since a
baremetal image doesn't exit like an operating system process, you need
to insert code somewhere to write out the profile data yourself. We
used __llvm_profile_get_size_for_buffer() and __llvm_profile_write_buf()
for this (and semihosting APIs to transfer the resulting buffer to the
3. Then, you have to edit your linker script to include the necessary
sections. __llvm_prf_names, __llvm_prf_data, and __llvm_prf_cnts need
to be included in the final image. And __llvm_covmap needs to be in a
non-allocatable section in the ELF output. And depending on how your
linker behaves, you might need to explicitly mark all of these sections
KEEP so parts don't get dropped. This is actually the trickiest part,
in the sense that messing it up lead to obscure issues which are
difficult to debug. At best, you get an error message like "No coverage
data found" or "Malformed instrumentation profile data". Or, if you're
using a build of LLVM more than a few months old, coverage data can be
4. Then, you add "-fprofile-instr-generate -fcoverage-mapping -mllvm
-enable-value-profiling=false" to your CFLAGS.
5. Then, you build and run the image in the semihosted environment. If
everything works, you get a file with raw profile data, and use the
normal workflow to convert it into a report.
Areas that required LLVM changes:
1. The copy of libclangrt_profile.a for the target. Given that we
already were using builtins from compiler-rt, the primary changes
required are enabling the profile library and excluding a bunch of files
from the build (since baremetal doesn't have a filesystem, system calls,
etc.). I'll look into posting patches when I have time, but it might
take me a little while for me to figure out how to cleanly modify the
build, and verify everything actually works on trunk. It looks like
there's a CMake variable COMPILER_RT_BAREMETAL_BUILD which is supposed
to be turned on for this sort of environment?
2. Changing the compiler and compiler-rt to use __start and __end
symbols to find the sections, rather than .init code. This isn't
strictly necessary, but our linker supports __start and __end, and this
was easier than changing the baremetal image to handle a .init section.
See needsRuntimeRegistrationOfSectionRange in
lib/Transforms/Instrumentation/InstrProfiling.cpp; we currently only
whitelist a few platforms. Not sure what would be appropriate here;
maybe we could assume any *-none-* triple supports __start and __end
symbols? Or maybe control it with a flag somehow? Or something else I'm
not thinking of?
Other problem areas:
1. We turned value profiling off because we were running into runtime
issues; specifically, we had infinite recursion because we instrumented
malloc. It isn't really important to have in this context (coverage
reports currently don't use the data at all), but is there some way to
improve this workflow to involve fewer magic command-line flags?
2. The error messages produced by llvm-profdata and llvm-cov tools could
probably be improved, to describe the actual issue in more detail.
The next steps here depend on community interest, I guess... has anyone
else tried something like this? Is anyone interested in my patches?
Should we add a section to the coverage documentation?
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
More information about the llvm-dev