mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-28 09:06:06 +00:00

New change on top of [reviewed patch](https://github.com/llvm/llvm-project/pull/81691) are [in commits after this one](d0757f46b3
). Previous commits are restored from the remote branch with timestamps. 1. Fix build breakage for non-ELF platforms, by defining the missing functions {`__llvm_profile_begin_vtables`, `__llvm_profile_end_vtables`, `__llvm_profile_begin_vtabnames `, `__llvm_profile_end_vtabnames`} everywhere. * Tested on mac laptop (for darwins) and Windows. Specifically, functions in `InstrProfilingPlatformWindows.c` returns `NULL` to make it more explicit that type prof isn't supported; see comments for the reason. * For the rest (AIX, other), mostly follow existing examples (like this [one](f95b2f1acf
)) 2. Rename `__llvm_prf_vtabnames` -> `__llvm_prf_vns` for shorter section name, and make returned pointers [const](a825d2a4ec (diff-4de780ce726d76b7abc9d3353aef95013e7b21e7bda01be8940cc6574fb0b5ffR120-R121)
) **Original Description** * Raw profile format - Header: records the byte size of compressed vtable names, and the number of profiled vtable entries (call it `VTableProfData`). Header also records padded bytes of each section. - Payload: adds a section for compressed vtable names, and a section to store `VTableProfData`. Both sections are padded so the size is a multiple of 8. * Indexed profile format - Header: records the byte offset of compressed vtable names. - Payload: adds a section to store compressed vtable names. This section is used by `llvm-profdata` to show the list of vtables profiled for an instrumented site. [The originally reviewed patch](https://github.com/llvm/llvm-project/pull/66825) will have profile reader/write change and llvm-profdata change. - To ensure this PR has all the necessary profile format change along with profile version bump, created a copy of the originally reviewed patch in https://github.com/llvm/llvm-project/pull/80761. The copy doesn't have profile format change, but it has the set of tests which covers type profile generation, profile read and profile merge. Tests pass there. rfc in https://discourse.llvm.org/t/rfc-dynamic-type-profiling-and-optimizations-in-llvm/74600 --------- Co-authored-by: modiking <modiking213@gmail.com>
222 lines
7.7 KiB
C
222 lines
7.7 KiB
C
/*===- InstrProfilingPlatformAIX.c - Profile data AIX platform ------------===*\
|
|
|*
|
|
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|* See https://llvm.org/LICENSE.txt for license information.
|
|
|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|*
|
|
\*===----------------------------------------------------------------------===*/
|
|
|
|
#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);
|
|
if (BufPtr != 0)
|
|
RC = loadquery(L_GETXINFO | L_IGNOREUNLOAD, BufPtr, (unsigned int)BufSize);
|
|
}
|
|
|
|
if (RC == -1)
|
|
goto done;
|
|
|
|
// 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
|
|
void __llvm_profile_register_function(void *Data_) {}
|
|
|
|
COMPILER_RT_VISIBILITY
|
|
void __llvm_profile_register_names_function(void *NamesStart,
|
|
uint64_t NamesSize) {}
|
|
|
|
// The __start_SECNAME and __stop_SECNAME symbols (for SECNAME \in
|
|
// {"__llvm_prf_cnts", "__llvm_prf_data", "__llvm_prf_name", "__llvm_prf_vnds",
|
|
// "__llvm_prf_vns", "__llvm_prf_vtab"})
|
|
// are always live when linking on AIX, regardless if the .o's being linked
|
|
// reference symbols from the profile library (for example when no files were
|
|
// compiled with -fprofile-generate). That's because these symbols are kept
|
|
// alive through references in constructor functions that are always live in the
|
|
// default linking model on AIX (-bcdtors:all). The __start_SECNAME and
|
|
// __stop_SECNAME symbols are only resolved by the linker when the SECNAME
|
|
// section exists. So for the scenario where the user objects have no such
|
|
// section (i.e. when they are compiled with -fno-profile-generate), we always
|
|
// define these zero length variables in each of the above 4 sections.
|
|
static int dummy_cnts[0] COMPILER_RT_SECTION(
|
|
COMPILER_RT_SEG INSTR_PROF_CNTS_SECT_NAME);
|
|
static int dummy_bits[0] COMPILER_RT_SECTION(
|
|
COMPILER_RT_SEG INSTR_PROF_BITS_SECT_NAME);
|
|
static int dummy_data[0] COMPILER_RT_SECTION(
|
|
COMPILER_RT_SEG INSTR_PROF_DATA_SECT_NAME);
|
|
static const int dummy_name[0] COMPILER_RT_SECTION(
|
|
COMPILER_RT_SEG INSTR_PROF_NAME_SECT_NAME);
|
|
static int dummy_vnds[0] COMPILER_RT_SECTION(
|
|
COMPILER_RT_SEG INSTR_PROF_VNODES_SECT_NAME);
|
|
static int dummy_orderfile[0] COMPILER_RT_SECTION(
|
|
COMPILER_RT_SEG INSTR_PROF_ORDERFILE_SECT_NAME);
|
|
static int dummy_vname[0] COMPILER_RT_SECTION(
|
|
COMPILER_RT_SEG INSTR_PROF_VNAME_SECT_NAME);
|
|
static int dummy_vtab[0] COMPILER_RT_SECTION(
|
|
COMPILER_RT_SEG INSTR_PROF_VTAB_SECT_NAME);
|
|
|
|
// To avoid GC'ing of the dummy variables by the linker, reference them in an
|
|
// array and reference the array in the runtime registration code
|
|
// (InstrProfilingRuntime.cpp)
|
|
#ifdef __GNUC__
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
|
#endif
|
|
COMPILER_RT_VISIBILITY
|
|
void *__llvm_profile_keep[] = {(void *)&dummy_cnts, (void *)&dummy_bits,
|
|
(void *)&dummy_data, (void *)&dummy_name,
|
|
(void *)&dummy_vnds, (void *)&dummy_orderfile,
|
|
(void *)&dummy_vname, (void *)&dummy_vtab};
|
|
#ifdef __GNUC__
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
#endif
|