From: Wael Yehia Date: Mon, 20 Mar 2023 19:51:17 +0000 (-0400) Subject: [AIX][PGO] Teach profile runtime to read build-id X-Git-Tag: upstream/17.0.6~13317 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=0e2ed90516ee834ca78f4974a7aebcba1a302eb0;p=platform%2Fupstream%2Fllvm.git [AIX][PGO] Teach profile runtime to read build-id On AIX, the build-id can be embedded in a binary using the -mxcoff-build-id compiler option. When present, the build id is stored as an ascii string at the beginning of the string table in the loader section of the XCOFF file. Reviewed By: stephenpeckham, daltenty Differential Revision: https://reviews.llvm.org/D146976 --- diff --git a/compiler-rt/lib/profile/InstrProfilingInternal.h b/compiler-rt/lib/profile/InstrProfilingInternal.h index b2ce110..360165e 100644 --- a/compiler-rt/lib/profile/InstrProfilingInternal.h +++ b/compiler-rt/lib/profile/InstrProfilingInternal.h @@ -198,4 +198,12 @@ extern void (*VPMergeHook)(struct ValueProfData *, __llvm_profile_data *); */ int __llvm_write_binary_ids(ProfDataWriter *Writer); +/* + * Write binary id length and then its data, because binary id does not + * have a fixed length. + */ +int lprofWriteOneBinaryId(ProfDataWriter *Writer, uint64_t BinaryIdLen, + const uint8_t *BinaryIdData, + uint64_t BinaryIdPadding); + #endif diff --git a/compiler-rt/lib/profile/InstrProfilingPlatformAIX.c b/compiler-rt/lib/profile/InstrProfilingPlatformAIX.c index 1035894..ce03766 100644 --- a/compiler-rt/lib/profile/InstrProfilingPlatformAIX.c +++ b/compiler-rt/lib/profile/InstrProfilingPlatformAIX.c @@ -8,7 +8,162 @@ #if defined(_AIX) +#ifdef __64BIT__ +#define __XCOFF64__ +#endif +#include +#include +#include +#include +#include + #include "InstrProfiling.h" +#include "InstrProfilingInternal.h" + +#define BIN_ID_PREFIX "xcoff_binary_id:" + +// If found, write the build-id into the Result buffer. +static size_t FindBinaryId(char *Result, size_t Size) { + unsigned long EntryAddr = (unsigned long)__builtin_return_address(0); + + // Use loadquery to get information about loaded modules; loadquery writes + // its result into a buffer of unknown size. + char Buf[1024]; + size_t BufSize = sizeof(Buf); + char *BufPtr = Buf; + int RC = -1; + + errno = 0; + RC = loadquery(L_GETXINFO | L_IGNOREUNLOAD, BufPtr, (unsigned int)BufSize); + if (RC == -1 && errno == ENOMEM) { + BufSize = 64000; // should be plenty for any program. + BufPtr = malloc(BufSize); + RC = loadquery(L_GETXINFO | L_IGNOREUNLOAD, BufPtr, (unsigned int)BufSize); + } + + if (RC == -1) + return RC; + + // Locate the ld_xinfo corresponding to this module. + struct ld_xinfo *CurInfo = (struct ld_xinfo *)BufPtr; + while (1) { + unsigned long CurTextStart = (uint64_t)CurInfo->ldinfo_textorg; + unsigned long CurTextEnd = CurTextStart + CurInfo->ldinfo_textsize; + if (CurTextStart <= EntryAddr && EntryAddr < CurTextEnd) { + // Found my slot. Now search for the build-id. + char *p = (char *)CurInfo->ldinfo_textorg; + + FILHDR *f = (FILHDR *)p; + AOUTHDR *a = (AOUTHDR *)(p + FILHSZ); + SCNHDR *s = + (SCNHDR *)(p + FILHSZ + f->f_opthdr + SCNHSZ * (a->o_snloader - 1)); + LDHDR *ldhdr = (LDHDR *)(p + s->s_scnptr); + // This is the loader string table + char *lstr = (char *)ldhdr + ldhdr->l_stoff; + + // If the build-id exists, it's the first entry. + // Each entry is comprised of a 2-byte size component, followed by the + // data. + size_t len = *(short *)lstr; + char *str = (char *)(lstr + 2); + size_t PrefixLen = sizeof(BIN_ID_PREFIX) - 1; + if (len > PrefixLen && (len - PrefixLen) <= Size && + strncmp(str, BIN_ID_PREFIX, PrefixLen) == 0) { + memcpy(Result, str + PrefixLen, len - PrefixLen); + RC = len - PrefixLen; + goto done; + } + break; + } + if (CurInfo->ldinfo_next == 0u) + break; + CurInfo = (struct ld_xinfo *)((char *)CurInfo + CurInfo->ldinfo_next); + } +done: + if (BufSize != sizeof(Buf) && BufPtr != 0) + free(BufPtr); + return RC; +} + +static int StrToHexError = 0; +static uint8_t StrToHex(char c) { + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 0xa; + if (c >= 'A' && c <= 'F') + return c - 'A' + 0xa; + StrToHexError = 1; + return 0; +} + +COMPILER_RT_VISIBILITY int __llvm_write_binary_ids(ProfDataWriter *Writer) { + // 200 bytes should be enough for the build-id hex string. + static char Buf[200]; + // Profile reading tools expect this to be 8-bytes long. + static int64_t BinaryIdLen = 0; + static uint8_t *BinaryIdData = 0; + + // -1 means we already checked for a BinaryId and didn't find one. + if (BinaryIdLen == -1) + return 0; + + // Are we being called for the first time? + if (BinaryIdLen == 0) { + if (getenv("LLVM_PROFILE_NO_BUILD_ID")) + goto fail; + + int BuildIdLen = FindBinaryId(Buf, sizeof(Buf)); + if (BuildIdLen <= 0) + goto fail; + + if (Buf[BuildIdLen - 1] == '\0') + BuildIdLen--; + + // assume even number of digits/chars, so 0xabc must be 0x0abc + if ((BuildIdLen % 2) != 0 || BuildIdLen == 0) + goto fail; + + // The numeric ID is represented as an ascii string in the loader section, + // so convert it to raw binary. + BinaryIdLen = BuildIdLen / 2; + BinaryIdData = (uint8_t *)Buf; + + // Skip "0x" prefix if it exists. + if (Buf[0] == '0' && Buf[1] == 'x') { + BinaryIdLen -= 1; + BinaryIdData += 2; + } + + StrToHexError = 0; + for (int i = 0; i < BinaryIdLen; i++) + BinaryIdData[i] = (StrToHex(BinaryIdData[2 * i]) << 4) + + StrToHex(BinaryIdData[2 * i + 1]); + + if (StrToHexError) + goto fail; + + if (getenv("LLVM_PROFILE_VERBOSE")) { + char *StrBuf = (char *)COMPILER_RT_ALLOCA(2 * BinaryIdLen + 1); + for (int i = 0; i < (int)BinaryIdLen; i++) + sprintf(&StrBuf[2 * i], "%02x", BinaryIdData[i]); + PROF_NOTE("Writing binary id: %s\n", StrBuf); + } + } + + uint8_t BinaryIdPadding = __llvm_profile_get_num_padding_bytes(BinaryIdLen); + if (Writer && lprofWriteOneBinaryId(Writer, BinaryIdLen, BinaryIdData, + BinaryIdPadding) == -1) + return -1; // Return -1 rather goto fail to match the NT_GNU_BUILD_ID path. + + return sizeof(BinaryIdLen) + BinaryIdLen + BinaryIdPadding; + +fail: + if (getenv("LLVM_PROFILE_VERBOSE")) + fprintf(stderr, "no or invalid binary id: %.*s\n", (int)sizeof(Buf), Buf); + BinaryIdLen = -1; + return 0; +} // Empty stubs to allow linking object files using the registration-based scheme COMPILER_RT_VISIBILITY diff --git a/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c b/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c index f17e543..2cce0a4 100644 --- a/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c +++ b/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c @@ -94,26 +94,6 @@ static size_t RoundUp(size_t size, size_t align) { } /* - * Write binary id length and then its data, because binary id does not - * have a fixed length. - */ -static int WriteOneBinaryId(ProfDataWriter *Writer, uint64_t BinaryIdLen, - const uint8_t *BinaryIdData, - uint64_t BinaryIdPadding) { - ProfDataIOVec BinaryIdIOVec[] = { - {&BinaryIdLen, sizeof(uint64_t), 1, 0}, - {BinaryIdData, sizeof(uint8_t), BinaryIdLen, 0}, - {NULL, sizeof(uint8_t), BinaryIdPadding, 1}, - }; - if (Writer->Write(Writer, BinaryIdIOVec, - sizeof(BinaryIdIOVec) / sizeof(*BinaryIdIOVec))) - return -1; - - /* Successfully wrote binary id, report success. */ - return 0; -} - -/* * Look for the note that has the name "GNU\0" and type NT_GNU_BUILD_ID * that contains build id. If build id exists, write binary id. * @@ -135,8 +115,9 @@ static int WriteBinaryIdForNote(ProfDataWriter *Writer, const uint8_t *BinaryIdData = (const uint8_t *)(NoteName + RoundUp(Note->n_namesz, 4)); uint8_t BinaryIdPadding = __llvm_profile_get_num_padding_bytes(BinaryIdLen); - if (Writer != NULL && WriteOneBinaryId(Writer, BinaryIdLen, BinaryIdData, - BinaryIdPadding) == -1) + if (Writer != NULL && + lprofWriteOneBinaryId(Writer, BinaryIdLen, BinaryIdData, + BinaryIdPadding) == -1) return -1; BinaryIdSize = sizeof(BinaryIdLen) + BinaryIdLen + BinaryIdPadding; @@ -220,7 +201,7 @@ COMPILER_RT_VISIBILITY int __llvm_write_binary_ids(ProfDataWriter *Writer) { return TotalBinaryIdsSize; } -#else /* !NT_GNU_BUILD_ID */ +#elif !defined(_AIX) /* !NT_GNU_BUILD_ID */ /* * Fallback implementation for targets that don't support the GNU * extensions NT_GNU_BUILD_ID and __ehdr_start. diff --git a/compiler-rt/lib/profile/InstrProfilingWriter.c b/compiler-rt/lib/profile/InstrProfilingWriter.c index 366451a..4a39298 100644 --- a/compiler-rt/lib/profile/InstrProfilingWriter.c +++ b/compiler-rt/lib/profile/InstrProfilingWriter.c @@ -336,3 +336,24 @@ lprofWriteDataImpl(ProfDataWriter *Writer, const __llvm_profile_data *DataBegin, return writeValueProfData(Writer, VPDataReader, DataBegin, DataEnd); } + +/* + * Write binary id length and then its data, because binary id does not + * have a fixed length. + */ +COMPILER_RT_VISIBILITY +int lprofWriteOneBinaryId(ProfDataWriter *Writer, uint64_t BinaryIdLen, + const uint8_t *BinaryIdData, + uint64_t BinaryIdPadding) { + ProfDataIOVec BinaryIdIOVec[] = { + {&BinaryIdLen, sizeof(uint64_t), 1, 0}, + {BinaryIdData, sizeof(uint8_t), BinaryIdLen, 0}, + {NULL, sizeof(uint8_t), BinaryIdPadding, 1}, + }; + if (Writer->Write(Writer, BinaryIdIOVec, + sizeof(BinaryIdIOVec) / sizeof(*BinaryIdIOVec))) + return -1; + + /* Successfully wrote binary id, report success. */ + return 0; +} diff --git a/compiler-rt/test/profile/AIX/binary-id-shared.c b/compiler-rt/test/profile/AIX/binary-id-shared.c new file mode 100644 index 0000000..55ba9d8 --- /dev/null +++ b/compiler-rt/test/profile/AIX/binary-id-shared.c @@ -0,0 +1,35 @@ +// RUN: split-file %s %t +// RUN: cd %t +// RUN: %clang_pgogen -c shr1.c -o shr1.o -Xclang -fprofile-instrument-path=default_1.profraw +// RUN: %clang_pgogen -shared shr1.o -o shr1.so -mxcoff-build-id=0x01 +// +// RUN: %clangxx_pgogen -c shr2.cpp -o shr2.o -Xclang -fprofile-instrument-path=default_2.profraw +// RUN: %clangxx_pgogen -shared shr2.o -o shr2.so -mxcoff-build-id=0x02 +// +// RUN: %clang_pgogen -c main.c -o main.o -Xclang -fprofile-instrument-path=default_main.profraw +// RUN: %clang_pgogen main.o -L%t shr1.so shr2.so -o a.out -mxcoff-build-id=0xFFFFFFFFFFFFFFFF +// +// RUN: %run ./a.out +// +// RUN: llvm-profdata show --binary-ids default_1.profraw | FileCheck %s --check-prefix=SHARED1 +// RUN: llvm-profdata show --binary-ids default_2.profraw | FileCheck %s --check-prefix=SHARED2 +// RUN: llvm-profdata show --binary-ids default_main.profraw | FileCheck %s --check-prefix=MAIN + +// SHARED1: Binary IDs: +// SHARED1-NEXT: {{^}}01{{$}} +// SHARED2: Binary IDs: +// SHARED2-NEXT: {{^}}02{{$}} +// MAIN: Binary IDs: +// MAIN-NEXT: {{^}}ffffffffffffffff{{$}} + +//--- shr1.c +int shr1() { return 1; } + +//--- shr2.cpp +int helper() { return 3; } +extern "C" int shr2() { return helper(); } + +//--- main.c +int shr1(); +int shr2(); +int main() { return 4 - shr1() - shr2(); } diff --git a/compiler-rt/test/profile/AIX/binary-id.c b/compiler-rt/test/profile/AIX/binary-id.c new file mode 100644 index 0000000..b60bfd8 --- /dev/null +++ b/compiler-rt/test/profile/AIX/binary-id.c @@ -0,0 +1,47 @@ +// RUN: %clang_pgogen -c -o %t.o %s +// +// Test valid IDs: +// (20-byte, ends with 2 zeroes, upper case) +// RUN: %clang_pgogen -mxcoff-build-id=0x8d7AEC8b900dce6c14afe557dc8889230518be00 -o %t %t.o +// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t +// RUN: llvm-profdata show --binary-ids %t.profraw | FileCheck %s --check-prefix=LONG + +// (all zeroes) +// RUN: %clang_pgogen -mxcoff-build-id=0x00 -o %t %t.o +// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t +// RUN: llvm-profdata show --binary-ids %t.profraw | FileCheck %s --check-prefix=00 + +// (starts with one zero) +// RUN: %clang_pgogen -mxcoff-build-id=0x0120 -o %t %t.o +// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t +// RUN: llvm-profdata show --binary-ids %t.profraw | FileCheck %s --check-prefix=0120 + +// (starts with 8 zeroes == 4 bytes) +// RUN: %clang_pgogen -mxcoff-build-id=0x0000000012 -o %t %t.o +// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t +// RUN: llvm-profdata show --binary-ids %t.profraw | FileCheck %s --check-prefix=0000000012 + +// (starts with 16 zeroes == 8 bytes) +// RUN: %clang_pgogen -mxcoff-build-id=0x0000000000000000ff -o %t %t.o +// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t +// RUN: llvm-profdata show --binary-ids %t.profraw | FileCheck %s --check-prefix=0000000000000000ff + +// (starts with 17 zeroes) +// RUN: %clang_pgogen -mxcoff-build-id=0x00000000000000000f -o %t %t.o +// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t +// RUN: llvm-profdata show --binary-ids %t.profraw | FileCheck %s --check-prefix=00000000000000000f + +// LONG: Binary IDs: +// LONG-NEXT: 8d7aec8b900dce6c14afe557dc8889230518be00 +// 00: Binary IDs: +// 00-NEXT: {{^}}00{{$}} +// 0120: Binary IDs: +// 0120-NEXT: {{^}}0120{{$}} +// 0000000012: Binary IDs: +// 0000000012-NEXT: {{^}}0000000012{{$}} +// 0000000000000000ff: Binary IDs: +// 0000000000000000ff-NEXT: {{^}}0000000000000000ff{{$}} +// 00000000000000000f: Binary IDs: +// 00000000000000000f-NEXT: {{^}}00000000000000000f{{$}} + +int main() { return 0; }