Skip to content

Commit cd11146

Browse files
committed
[profile] Add %b LLVM_PROFILE_FILE option for binary ID
Add support for expanding `%b` in `LLVM_PROFILE_FILE` to the binary ID (build ID). It can be used with `%m` to avoid its signature collisions. This is supported on all platforms where writing binary IDs into profiles is implemented, as the `__llvm_write_binary_ids` function is used. Fixes #51560.
1 parent d80eb92 commit cd11146

File tree

4 files changed

+97
-7
lines changed

4 files changed

+97
-7
lines changed

clang/docs/SourceBasedCodeCoverage.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ directory structure will be created. Additionally, the following special
9494
not specified (i.e the pattern is "%m"), it's assumed that ``N = 1``. The
9595
merge pool specifier can only occur once per filename pattern.
9696

97+
* "%b" expands out to the binary ID (build ID). It can be used with "%Nm" to
98+
avoid binary signature collisions. To use it, the program should be compiled
99+
with the build ID linker option (``--build-id`` for GNU ld or LLD). Linux,
100+
Windows and AIX are supported.
101+
97102
* "%c" expands out to nothing, but enables a mode in which profile counter
98103
updates are continuously synced to a file. This means that if the
99104
instrumented program crashes, or is killed by a signal, perfect coverage

clang/docs/UsersManual.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2880,7 +2880,8 @@ instrumentation:
28802880
environment variable to specify an alternate file. If non-default file name
28812881
is specified by both the environment variable and the command line option,
28822882
the environment variable takes precedence. The file name pattern specified
2883-
can include different modifiers: ``%p``, ``%h``, ``%m``, ``%t``, and ``%c``.
2883+
can include different modifiers: ``%p``, ``%h``, ``%m``, ``%b``, ``%t``, and
2884+
``%c``.
28842885

28852886
Any instance of ``%p`` in that file name will be replaced by the process
28862887
ID, so that you can easily distinguish the profile output from multiple
@@ -2917,7 +2918,7 @@ instrumentation:
29172918
$ LLVM_PROFILE_FILE="code-%m.profraw" ./code
29182919
29192920
See `this <SourceBasedCodeCoverage.html#running-the-instrumented-program>`_ section
2920-
about the ``%t``, and ``%c`` modifiers.
2921+
about the ``%b``, ``%t``, and ``%c`` modifiers.
29212922

29222923
3. Combine profiles from multiple runs and convert the "raw" profile format to
29232924
the input expected by clang. Use the ``merge`` command of the

compiler-rt/lib/profile/InstrProfilingFile.c

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ typedef struct lprofFilename {
7777
char Hostname[COMPILER_RT_MAX_HOSTLEN];
7878
unsigned NumPids;
7979
unsigned NumHosts;
80+
unsigned NumBinaryIds;
8081
/* When in-process merging is enabled, this parameter specifies
8182
* the total number of profile data files shared by all the processes
8283
* spawned from the same binary. By default the value is 1. If merging
@@ -88,8 +89,8 @@ typedef struct lprofFilename {
8889
ProfileNameSpecifier PNS;
8990
} lprofFilename;
9091

91-
static lprofFilename lprofCurFilename = {0, 0, 0, {0}, NULL,
92-
{0}, 0, 0, 0, PNS_unknown};
92+
static lprofFilename lprofCurFilename = {0, 0, 0, {0}, NULL, {0},
93+
0, 0, 0, 0, PNS_unknown};
9394

9495
static int ProfileMergeRequested = 0;
9596
static int getProfileFileSizeForMerging(FILE *ProfileFile,
@@ -790,7 +791,7 @@ static int checkBounds(int Idx, int Strlen) {
790791
* lprofcurFilename structure. */
791792
static int parseFilenamePattern(const char *FilenamePat,
792793
unsigned CopyFilenamePat) {
793-
int NumPids = 0, NumHosts = 0, I;
794+
int NumPids = 0, NumHosts = 0, NumBinaryIds = 0, I;
794795
char *PidChars = &lprofCurFilename.PidChars[0];
795796
char *Hostname = &lprofCurFilename.Hostname[0];
796797
int MergingEnabled = 0;
@@ -855,6 +856,15 @@ static int parseFilenamePattern(const char *FilenamePat,
855856
FilenamePat);
856857
return -1;
857858
}
859+
} else if (FilenamePat[I] == 'b') {
860+
if (!NumBinaryIds++) {
861+
if (__llvm_write_binary_ids(NULL) <= 0) {
862+
PROF_WARN("Unable to get binary ID for filename pattern %s. Using "
863+
"the default name.",
864+
FilenamePat);
865+
return -1;
866+
}
867+
}
858868
} else if (FilenamePat[I] == 'c') {
859869
if (__llvm_profile_is_continuous_mode_enabled()) {
860870
PROF_WARN("%%c specifier can only be specified once in %s.\n",
@@ -887,6 +897,7 @@ static int parseFilenamePattern(const char *FilenamePat,
887897

888898
lprofCurFilename.NumPids = NumPids;
889899
lprofCurFilename.NumHosts = NumHosts;
900+
lprofCurFilename.NumBinaryIds = NumBinaryIds;
890901
return 0;
891902
}
892903

@@ -934,24 +945,53 @@ static void parseAndSetFilename(const char *FilenamePat,
934945
* filename with PID and hostname substitutions. */
935946
/* The length to hold uint64_t followed by 3 digits pool id including '_' */
936947
#define SIGLEN 24
948+
/* The length to hold 160-bit hash in hexadecimal form */
949+
#define BINARY_ID_LEN 40
937950
static int getCurFilenameLength(void) {
938951
int Len;
939952
if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0])
940953
return 0;
941954

942955
if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts ||
943-
lprofCurFilename.TmpDir || lprofCurFilename.MergePoolSize))
956+
lprofCurFilename.NumBinaryIds || lprofCurFilename.TmpDir ||
957+
lprofCurFilename.MergePoolSize))
944958
return strlen(lprofCurFilename.FilenamePat);
945959

946960
Len = strlen(lprofCurFilename.FilenamePat) +
947961
lprofCurFilename.NumPids * (strlen(lprofCurFilename.PidChars) - 2) +
948962
lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2) +
963+
lprofCurFilename.NumBinaryIds * BINARY_ID_LEN +
949964
(lprofCurFilename.TmpDir ? (strlen(lprofCurFilename.TmpDir) - 1) : 0);
950965
if (lprofCurFilename.MergePoolSize)
951966
Len += SIGLEN;
952967
return Len;
953968
}
954969

970+
typedef struct lprofBinaryIdsBuffer {
971+
char String[BINARY_ID_LEN + 1];
972+
int Length;
973+
} lprofBinaryIdsBuffer;
974+
975+
/* Reads binary ID length and then its data, writes it into lprofBinaryIdsBuffer
976+
* in hexadecimal form. */
977+
static uint32_t binaryIdsStringWriter(ProfDataWriter *This,
978+
ProfDataIOVec *IOVecs,
979+
uint32_t NumIOVecs) {
980+
if (NumIOVecs < 2 || IOVecs[0].ElmSize != sizeof(uint64_t))
981+
return -1;
982+
uint64_t BinaryIdLen = *(const uint64_t *)IOVecs[0].Data;
983+
if (IOVecs[1].ElmSize != sizeof(uint8_t) || IOVecs[1].NumElm != BinaryIdLen)
984+
return -1;
985+
const uint8_t *BinaryIdData = (const uint8_t *)IOVecs[1].Data;
986+
lprofBinaryIdsBuffer *Data = (lprofBinaryIdsBuffer *)This->WriterCtx;
987+
for (uint64_t I = 0; I < BinaryIdLen; I++) {
988+
Data->Length +=
989+
snprintf(Data->String + Data->Length, BINARY_ID_LEN + 1 - Data->Length,
990+
"%02hhx", BinaryIdData[I]);
991+
}
992+
return 0;
993+
}
994+
955995
/* Return the pointer to the current profile file name (after substituting
956996
* PIDs and Hostnames in filename pattern. \p FilenameBuf is the buffer
957997
* to store the resulting filename. If no substitution is needed, the
@@ -965,7 +1005,8 @@ static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf) {
9651005
return 0;
9661006

9671007
if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts ||
968-
lprofCurFilename.TmpDir || lprofCurFilename.MergePoolSize ||
1008+
lprofCurFilename.NumBinaryIds || lprofCurFilename.TmpDir ||
1009+
lprofCurFilename.MergePoolSize ||
9691010
__llvm_profile_is_continuous_mode_enabled())) {
9701011
if (!ForceUseBuf)
9711012
return lprofCurFilename.FilenamePat;
@@ -992,6 +1033,12 @@ static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf) {
9921033
memcpy(FilenameBuf + J, lprofCurFilename.TmpDir, TmpDirLength);
9931034
FilenameBuf[J + TmpDirLength] = DIR_SEPARATOR;
9941035
J += TmpDirLength + 1;
1036+
} else if (FilenamePat[I] == 'b') {
1037+
lprofBinaryIdsBuffer Data = {{0}, 0};
1038+
ProfDataWriter Writer = {binaryIdsStringWriter, &Data};
1039+
__llvm_write_binary_ids(&Writer);
1040+
memcpy(FilenameBuf + J, Data.String, Data.Length);
1041+
J += Data.Length;
9951042
} else {
9961043
if (!getMergePoolSize(FilenamePat, &I))
9971044
continue;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// REQUIRES: linux
2+
// RUN: split-file %s %t.dir
3+
// RUN: %clang_profgen -Wl,--build-id=sha1 -o %t.dir/foo %t.dir/foo.c
4+
// RUN: %clang_profgen -Wl,--build-id=sha1 -o %t.dir/bar %t.dir/bar.c
5+
6+
// Check that foo and bar have the same signatures.
7+
// RUN: rm -rf %t.profdir
8+
// RUN: env LLVM_PROFILE_FILE=%t.profdir/%m.profraw %run %t.dir/foo
9+
// RUN: env LLVM_PROFILE_FILE=%t.profdir/%m.profraw %run %t.dir/bar 2>&1 | FileCheck %s --check-prefix=MERGE-ERROR
10+
11+
// Check that foo and bar have different binary IDs.
12+
// RUN: rm -rf %t.profdir %t.profdata
13+
// RUN: env LLVM_PROFILE_FILE=%t.profdir/%b.profraw %run %t.dir/foo
14+
// RUN: env LLVM_PROFILE_FILE=%t.profdir/%b.profraw %run %t.dir/bar
15+
// RUN: llvm-profdata merge -o %t.profdata %t.profdir
16+
// RUN: llvm-profdata show %t.profdata | FileCheck %s --check-prefix=BINARY-ID
17+
18+
// Check fallback to the default name if binary ID is missing.
19+
// RUN: %clang_profgen -Wl,--build-id=none -o %t.dir/foo %t.dir/foo.c
20+
// RUN: env LLVM_PROFILE_FILE=%t.profdir/%b.profraw %run %t.dir/foo 2>&1 | FileCheck %s --check-prefix=MISSING
21+
22+
// MERGE-ERROR: LLVM Profile Error: Profile Merging of file {{.*}}.profraw failed: File exists
23+
24+
// BINARY-ID: Instrumentation level: Front-end
25+
// BINARY-ID-NEXT: Total functions: 3
26+
// BINARY-ID-NEXT: Maximum function count: 2
27+
// BINARY-ID-NEXT: Maximum internal block count: 0
28+
29+
// MISSING: Unable to get binary ID for filename pattern {{.*}}.profraw. Using the default name.
30+
31+
//--- foo.c
32+
int main(void) { return 0; }
33+
void foo(void) {}
34+
35+
//--- bar.c
36+
int main(void) { return 0; }
37+
void bar(int *a) { *a += 10; }

0 commit comments

Comments
 (0)