[compiler-rt] 0e2ed90 - [AIX][PGO] Teach profile runtime to read build-id
Wael Yehia via llvm-commits
llvm-commits at lists.llvm.org
Wed Mar 29 08:17:44 PDT 2023
Author: Wael Yehia
Date: 2023-03-29T15:15:07Z
New Revision: 0e2ed90516ee834ca78f4974a7aebcba1a302eb0
URL: https://github.com/llvm/llvm-project/commit/0e2ed90516ee834ca78f4974a7aebcba1a302eb0
DIFF: https://github.com/llvm/llvm-project/commit/0e2ed90516ee834ca78f4974a7aebcba1a302eb0.diff
LOG: [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
Added:
compiler-rt/test/profile/AIX/binary-id-shared.c
compiler-rt/test/profile/AIX/binary-id.c
Modified:
compiler-rt/lib/profile/InstrProfilingInternal.h
compiler-rt/lib/profile/InstrProfilingPlatformAIX.c
compiler-rt/lib/profile/InstrProfilingPlatformLinux.c
compiler-rt/lib/profile/InstrProfilingWriter.c
Removed:
################################################################################
diff --git a/compiler-rt/lib/profile/InstrProfilingInternal.h b/compiler-rt/lib/profile/InstrProfilingInternal.h
index b2ce11067abdd..360165e32ab3f 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 1035894b8ed1c..ce03766569e1e 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ldr.h>
+#include <xcoff.h>
+
#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 f17e543ddb5d8..2cce0a4b2c48d 100644
--- a/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c
+++ b/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c
@@ -93,26 +93,6 @@ static size_t RoundUp(size_t size, size_t align) {
return (size + align - 1) & ~(align - 1);
}
-/*
- * 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 366451a686c13..4a392984fe6ba 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 0000000000000..55ba9d85c5ee1
--- /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 0000000000000..b60bfd8d8dde4
--- /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; }
More information about the llvm-commits
mailing list