Skip to content

Commit d3db13a

Browse files
committed
[profile] Support counter relocation at runtime
This is an alternative to the continous mode that was implemented in D68351. This mode relies on padding and the ability to mmap a file over the existing mapping which is generally only available on POSIX systems and isn't suitable for other platforms. This change instead introduces the ability to relocate counters at runtime using a level of indirection. On every counter access, we add a bias to the counter address. This bias is stored in a symbol that's provided by the profile runtime and is initially set to zero, meaning no relocation. The runtime can mmap the profile into memory at abitrary location, and set bias to the offset between the original and the new counter location, at which point every subsequent counter access will be to the new location, which allows updating profile directly akin to the continous mode. The advantage of this implementation is that doesn't require any special OS support. The disadvantage is the extra overhead due to additional instructions required for each counter access (overhead both in terms of binary size and performance) plus duplication of counters (i.e. one copy in the binary itself and another copy that's mmapped). Differential Revision: https://reviews.llvm.org/D69740
1 parent 383ff4e commit d3db13a

File tree

21 files changed

+361
-82
lines changed

21 files changed

+361
-82
lines changed

clang/docs/SourceBasedCodeCoverage.rst

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,42 @@ directory structure will be created. Additionally, the following special
9292
instrumented program crashes, or is killed by a signal, perfect coverage
9393
information can still be recovered. Continuous mode does not support value
9494
profiling for PGO, and is only supported on Darwin at the moment. Support for
95-
Linux may be mostly complete but requires testing, and support for
96-
Fuchsia/Windows may require more extensive changes: please get involved if
97-
you are interested in porting this feature.
95+
Linux may be mostly complete but requires testing, and support for Windows
96+
may require more extensive changes: please get involved if you are interested
97+
in porting this feature.
9898

9999
.. code-block:: console
100100
101101
# Step 2: Run the program.
102102
% LLVM_PROFILE_FILE="foo.profraw" ./foo
103103
104+
Note that continuous mode is also used on Fuchsia where it's the only supported
105+
mode, but the implementation is different. The Darwin and Linux implementation
106+
relies on padding and the ability to map a file over the existing memory
107+
mapping which is generally only available on POSIX systems and isn't suitable
108+
for other platforms.
109+
110+
On Fuchsia, we rely on the the ability to relocate counters at runtime using a
111+
level of indirection. On every counter access, we add a bias to the counter
112+
address. This bias is stored in ``__llvm_profile_counter_bias`` symbol that's
113+
provided by the profile runtime and is initially set to zero, meaning no
114+
relocation. The runtime can map the profile into memory at abitrary location,
115+
and set bias to the offset between the original and the new counter location,
116+
at which point every subsequent counter access will be to the new location,
117+
which allows updating profile directly akin to the continous mode.
118+
119+
The advantage of this approach is that doesn't require any special OS support.
120+
The disadvantage is the extra overhead due to additional instructions required
121+
for each counter access (overhead both in terms of binary size and performance)
122+
plus duplication of counters (i.e. one copy in the binary itself and another
123+
copy that's mapped into memory). This implementation can be also enabled for
124+
other platforms by passing the ``-runtime-counter-relocation`` option to the
125+
backend during compilation.
126+
127+
.. code-block:: console
128+
129+
% clang++ -fprofile-instr-generate -fcoverage-mapping -mllvm -runtime-counter-relocation foo.cc -o foo
130+
104131
Creating coverage reports
105132
=========================
106133

clang/lib/Driver/ToolChains/Darwin.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,6 +1149,7 @@ void Darwin::addProfileRTLibs(const ArgList &Args,
11491149
} else {
11501150
addExportedSymbol(CmdArgs, "___llvm_profile_filename");
11511151
addExportedSymbol(CmdArgs, "___llvm_profile_raw_version");
1152+
addExportedSymbol(CmdArgs, "___llvm_profile_counter_bias");
11521153
}
11531154
addExportedSymbol(CmdArgs, "_lprofDirMode");
11541155
}

compiler-rt/lib/profile/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ set(PROFILE_SOURCES
5252
GCDAProfiling.c
5353
InstrProfiling.c
5454
InstrProfilingValue.c
55+
InstrProfilingBiasVar.c
5556
InstrProfilingBuffer.c
5657
InstrProfilingFile.c
5758
InstrProfilingMerge.c

compiler-rt/lib/profile/InstrProfiling.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,4 +307,11 @@ extern uint64_t INSTR_PROF_RAW_VERSION_VAR; /* __llvm_profile_raw_version */
307307
*/
308308
extern char INSTR_PROF_PROFILE_NAME_VAR[1]; /* __llvm_profile_filename. */
309309

310+
/*!
311+
* This variable is a weak symbol defined in InstrProfilingBiasVar.c. It
312+
* allows compiler instrumentation to provide overriding definition with
313+
* value from compiler command line. This variable has hidden visibility.
314+
*/
315+
COMPILER_RT_VISIBILITY extern intptr_t __llvm_profile_counter_bias;
316+
310317
#endif /* PROFILE_INSTRPROFILING_H_ */
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*===- InstrProfilingBiasVar.c - profile counter bias variable setup ------===*\
2+
|*
3+
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
|* See https://llvm.org/LICENSE.txt for license information.
5+
|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
|*
7+
\*===----------------------------------------------------------------------===*/
8+
9+
#include "InstrProfiling.h"
10+
11+
/* The runtime should only provide its own definition of this symbol when the
12+
* user has not specified one. Set this up by moving the runtime's copy of this
13+
* symbol to an object file within the archive.
14+
*/
15+
COMPILER_RT_WEAK intptr_t __llvm_profile_counter_bias = -1;

compiler-rt/lib/profile/InstrProfilingBuffer.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
#include "InstrProfilingInternal.h"
1111
#include "InstrProfilingPort.h"
1212

13+
/* When counters are being relocated at runtime, this parameter is set to 1. */
14+
COMPILER_RT_VISIBILITY int RuntimeCounterRelocation = 0;
15+
1316
/* When continuous mode is enabled (%c), this parameter is set to 1.
1417
*
1518
* This parameter is defined here in InstrProfilingBuffer.o, instead of in
@@ -62,7 +65,8 @@ void __llvm_profile_get_padding_sizes_for_counters(
6265
uint64_t DataSize, uint64_t CountersSize, uint64_t NamesSize,
6366
uint64_t *PaddingBytesBeforeCounters, uint64_t *PaddingBytesAfterCounters,
6467
uint64_t *PaddingBytesAfterNames) {
65-
if (!__llvm_profile_is_continuous_mode_enabled()) {
68+
if (!__llvm_profile_is_continuous_mode_enabled() ||
69+
RuntimeCounterRelocation) {
6670
*PaddingBytesBeforeCounters = 0;
6771
*PaddingBytesAfterCounters = 0;
6872
*PaddingBytesAfterNames = __llvm_profile_get_num_padding_bytes(NamesSize);

compiler-rt/lib/profile/InstrProfilingFile.c

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,98 @@ static void unlockProfile(int *ProfileRequiresUnlock, FILE *File) {
448448
}
449449
#endif // !defined(__Fuchsia__) && !defined(_WIN32)
450450

451+
static int writeMMappedFile(FILE *OutputFile, char **Profile) {
452+
if (!OutputFile)
453+
return -1;
454+
455+
/* Write the data into a file. */
456+
setupIOBuffer();
457+
ProfDataWriter fileWriter;
458+
initFileWriter(&fileWriter, OutputFile);
459+
if (lprofWriteData(&fileWriter, NULL, 0)) {
460+
PROF_ERR("Failed to write profile: %s\n", strerror(errno));
461+
return -1;
462+
}
463+
fflush(OutputFile);
464+
465+
/* Get the file size. */
466+
uint64_t FileSize = ftell(OutputFile);
467+
468+
/* Map the profile. */
469+
*Profile = (char *)mmap(
470+
NULL, FileSize, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(OutputFile), 0);
471+
if (*Profile == MAP_FAILED) {
472+
PROF_ERR("Unable to mmap profile: %s\n", strerror(errno));
473+
return -1;
474+
}
475+
476+
return 0;
477+
}
478+
479+
static void relocateCounters(void) {
480+
if (!__llvm_profile_is_continuous_mode_enabled() || !RuntimeCounterRelocation)
481+
return;
482+
483+
/* Get the sizes of various profile data sections. Taken from
484+
* __llvm_profile_get_size_for_buffer(). */
485+
const __llvm_profile_data *DataBegin = __llvm_profile_begin_data();
486+
const __llvm_profile_data *DataEnd = __llvm_profile_end_data();
487+
uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd);
488+
const uint64_t CountersOffset = sizeof(__llvm_profile_header) +
489+
(DataSize * sizeof(__llvm_profile_data));
490+
491+
int Length = getCurFilenameLength();
492+
char *FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1);
493+
const char *Filename = getCurFilename(FilenameBuf, 0);
494+
if (!Filename)
495+
return;
496+
497+
FILE *File = NULL;
498+
char *Profile = NULL;
499+
500+
if (!doMerging()) {
501+
File = fopen(Filename, "w+b");
502+
if (!File)
503+
return;
504+
505+
if (writeMMappedFile(File, &Profile) == -1) {
506+
fclose(File);
507+
return;
508+
}
509+
} else {
510+
File = lprofOpenFileEx(Filename);
511+
if (!File)
512+
return;
513+
514+
uint64_t ProfileFileSize = 0;
515+
if (getProfileFileSizeForMerging(File, &ProfileFileSize) == -1) {
516+
lprofUnlockFileHandle(File);
517+
fclose(File);
518+
return;
519+
}
520+
521+
if (!ProfileFileSize) {
522+
if (writeMMappedFile(File, &Profile) == -1) {
523+
fclose(File);
524+
return;
525+
}
526+
} else {
527+
/* The merged profile has a non-zero length. Check that it is compatible
528+
* with the data in this process. */
529+
if (mmapProfileForMerging(File, ProfileFileSize, &Profile) == -1) {
530+
fclose(File);
531+
return;
532+
}
533+
}
534+
535+
lprofUnlockFileHandle(File);
536+
}
537+
538+
/* Update the profile fields based on the current mapping. */
539+
__llvm_profile_counter_bias = (intptr_t)Profile -
540+
(uintptr_t)__llvm_profile_begin_counters() + CountersOffset;
541+
}
542+
451543
static void initializeProfileForContinuousMode(void) {
452544
if (!__llvm_profile_is_continuous_mode_enabled())
453545
return;
@@ -715,7 +807,12 @@ static void parseAndSetFilename(const char *FilenamePat,
715807
}
716808

717809
truncateCurrentFile();
718-
initializeProfileForContinuousMode();
810+
if (__llvm_profile_is_continuous_mode_enabled()) {
811+
if (RuntimeCounterRelocation)
812+
relocateCounters();
813+
else
814+
initializeProfileForContinuousMode();
815+
}
719816
}
720817

721818
/* Return buffer length that is required to store the current profile
@@ -865,6 +962,9 @@ void __llvm_profile_initialize_file(void) {
865962
ProfileNameSpecifier PNS = PNS_unknown;
866963
int hasCommandLineOverrider = (INSTR_PROF_PROFILE_NAME_VAR[0] != 0);
867964

965+
if (__llvm_profile_counter_bias != -1)
966+
RuntimeCounterRelocation = 1;
967+
868968
EnvFilenamePat = getFilenamePatFromEnv();
869969
if (EnvFilenamePat) {
870970
/* Pass CopyFilenamePat = 1, to ensure that the filename would be valid

compiler-rt/lib/profile/InstrProfilingInternal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ uint64_t lprofGetLoadModuleSignature();
184184
unsigned lprofProfileDumped();
185185
void lprofSetProfileDumped();
186186

187+
COMPILER_RT_VISIBILITY extern int RuntimeCounterRelocation;
187188
COMPILER_RT_VISIBILITY extern void (*FreeHook)(void *);
188189
COMPILER_RT_VISIBILITY extern uint8_t *DynamicBufferIOBuffer;
189190
COMPILER_RT_VISIBILITY extern uint32_t VPBufferSize;

0 commit comments

Comments
 (0)