llvm-project/compiler-rt/lib/profile/InstrProfilingUtil.h
Wael Yehia 2abed78b4d
[AIX][PGO] Handle atexit functions when dlclose'ing shared libraries (#102940)
Problem:
On AIX, functions registered by atexit in a shared library are not run
when the library is dlclosed, but instead run (and fail because the
function pointer is no longer valid) during main program exit.

The profile-rt registers some functions with atexit:

 1. writeFileWithoutReturn that writes out the profile file
 2. llvm_delete_reset_function_list that does some cleanup in the gcov 
    instrumentation library (not sure)

And so right now, we get an "Illegal instruction (core dumped)" when an
instrumented shared object is dlopen'ed and dlclosed.

Solution:
  When a shared library is dlclose'd, destructors from the library are
  called. So create a destructor function that iterates over all known
  functions that profile-rt registers with atexit, and unregister the ones
  that have been registered and execute them.

Scenarios tested:
(0) gcov dlopen/dlclose                                       (AIX/gcov-dlopen-dlclose.test)
(1) multiple dlopen/dlclose of the same lib and multiple libs (instrprof-dlopen-dlclose.test)
(2) dlopen but no dlclose                                     (exists: Posix/instrprof-dlopen.test)
(3) a simple fork testcase with dlopen/dlclose                (instrprof-dlopen-dlclose.test)
(4) dlopen/dlclose by multiple threads.                       (instrprof-dlopen-dlclose.test)
(5) regular dynamic-linking of instrumented shared libs       (exists: AIX/shared-bexpall-pgo.c)
(6) a simple fork testcase produces correct profile           (instrprof-fork.c)


---------

Co-authored-by: Hubert Tong <hstong@ca.ibm.com>
2024-08-27 14:30:32 -04:00

93 lines
3.4 KiB
C

/*===- InstrProfilingUtil.h - Support library for PGO instrumentation -----===*\
|*
|* 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
|*
\*===----------------------------------------------------------------------===*/
#ifndef PROFILE_INSTRPROFILINGUTIL_H
#define PROFILE_INSTRPROFILINGUTIL_H
#include <inttypes.h>
#include <stddef.h>
#include <stdio.h>
/*! \brief Create a directory tree. */
void __llvm_profile_recursive_mkdir(char *Pathname);
/*! Set the mode used when creating profile directories. */
void __llvm_profile_set_dir_mode(unsigned Mode);
/*! Return the directory creation mode. */
unsigned __llvm_profile_get_dir_mode(void);
int lprofLockFd(int fd);
int lprofUnlockFd(int fd);
int lprofLockFileHandle(FILE *F);
int lprofUnlockFileHandle(FILE *F);
/*! Open file \c Filename for read+write with write
* lock for exclusive access. The caller will block
* if the lock is already held by another process. */
FILE *lprofOpenFileEx(const char *Filename);
/* PS4 doesn't have setenv/getenv/fork. Define a shim. */
#if __ORBIS__
#include <sys/types.h>
static inline char *getenv(const char *name) { return NULL; }
static inline int setenv(const char *name, const char *value, int overwrite)
{ return 0; }
static pid_t fork() { return -1; }
#endif /* #if __ORBIS__ */
/* GCOV_PREFIX and GCOV_PREFIX_STRIP support */
/* Return the path prefix specified by GCOV_PREFIX environment variable.
* If GCOV_PREFIX_STRIP is also specified, the strip level (integer value)
* is returned via \c *PrefixStrip. The prefix length is stored in *PrefixLen.
*/
const char *lprofGetPathPrefix(int *PrefixStrip, size_t *PrefixLen);
/* Apply the path prefix specified in \c Prefix to path string in \c PathStr,
* and store the result to buffer pointed to by \c Buffer. If \c PrefixStrip
* is not zero, path prefixes are stripped from \c PathStr (the level of
* stripping is specified by \c PrefixStrip) before \c Prefix is added.
*/
void lprofApplyPathPrefix(char *Dest, const char *PathStr, const char *Prefix,
size_t PrefixLen, int PrefixStrip);
/* Returns a pointer to the first occurrence of \c DIR_SEPARATOR char in
* the string \c Path, or NULL if the char is not found. */
const char *lprofFindFirstDirSeparator(const char *Path);
/* Returns a pointer to the last occurrence of \c DIR_SEPARATOR char in
* the string \c Path, or NULL if the char is not found. */
const char *lprofFindLastDirSeparator(const char *Path);
int lprofGetHostName(char *Name, int Len);
unsigned lprofBoolCmpXchg(void **Ptr, void *OldV, void *NewV);
void *lprofPtrFetchAdd(void **Mem, long ByteIncr);
/* Temporarily suspend SIGKILL. Return value of 1 means a restore is needed.
* Other return values mean no restore is needed.
*/
int lprofSuspendSigKill();
/* Restore previously suspended SIGKILL. */
void lprofRestoreSigKill();
static inline size_t lprofRoundUpTo(size_t x, size_t boundary) {
return (x + boundary - 1) & ~(boundary - 1);
}
static inline size_t lprofRoundDownTo(size_t x, size_t boundary) {
return x & ~(boundary - 1);
}
int lprofReleaseMemoryPagesToOS(uintptr_t Begin, uintptr_t End);
typedef void (*AtExit_Fn_ptr)(void);
/* Call atexit and perform other platform-specific bookkeeping. */
int lprofAtExit(AtExit_Fn_ptr);
#endif /* PROFILE_INSTRPROFILINGUTIL_H */