| /*===- InstrProfilingFile.c - Write instrumentation to a file -------------===*\ |
| |* |
| |* The LLVM Compiler Infrastructure |
| |* |
| |* This file is distributed under the University of Illinois Open Source |
| |* License. See LICENSE.TXT for details. |
| |* |
| \*===----------------------------------------------------------------------===*/ |
| |
| #include "InstrProfiling.h" |
| #include "InstrProfilingInternal.h" |
| #include "InstrProfilingUtil.h" |
| #include <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #ifdef _MSC_VER |
| /* For _alloca. */ |
| #include <malloc.h> |
| #endif |
| |
| #define MAX_PID_SIZE 16 |
| /* Data structure holding the result of parsed filename pattern. */ |
| typedef struct lprofFilename { |
| /* File name string possibly with %p or %h specifiers. */ |
| const char *FilenamePat; |
| char PidChars[MAX_PID_SIZE]; |
| char Hostname[COMPILER_RT_MAX_HOSTLEN]; |
| unsigned NumPids; |
| unsigned NumHosts; |
| } lprofFilename; |
| |
| lprofFilename lprofCurFilename = {0}; |
| |
| int getpid(void); |
| static int getCurFilenameLength(); |
| static const char *getCurFilename(char *FilenameBuf); |
| |
| /* Return 1 if there is an error, otherwise return 0. */ |
| static uint32_t fileWriter(ProfDataIOVec *IOVecs, uint32_t NumIOVecs, |
| void **WriterCtx) { |
| uint32_t I; |
| FILE *File = (FILE *)*WriterCtx; |
| for (I = 0; I < NumIOVecs; I++) { |
| if (fwrite(IOVecs[I].Data, IOVecs[I].ElmSize, IOVecs[I].NumElm, File) != |
| IOVecs[I].NumElm) |
| return 1; |
| } |
| return 0; |
| } |
| |
| COMPILER_RT_VISIBILITY ProfBufferIO * |
| lprofCreateBufferIOInternal(void *File, uint32_t BufferSz) { |
| FreeHook = &free; |
| DynamicBufferIOBuffer = (uint8_t *)calloc(BufferSz, 1); |
| VPBufferSize = BufferSz; |
| return lprofCreateBufferIO(fileWriter, File); |
| } |
| |
| static void setupIOBuffer() { |
| const char *BufferSzStr = 0; |
| BufferSzStr = getenv("LLVM_VP_BUFFER_SIZE"); |
| if (BufferSzStr && BufferSzStr[0]) { |
| VPBufferSize = atoi(BufferSzStr); |
| DynamicBufferIOBuffer = (uint8_t *)calloc(VPBufferSize, 1); |
| } |
| } |
| |
| /* Write profile data to file \c OutputName. */ |
| static int writeFile(const char *OutputName) { |
| int RetVal; |
| FILE *OutputFile; |
| |
| /* Append to the file to support profiling multiple shared objects. */ |
| OutputFile = fopen(OutputName, "ab"); |
| if (!OutputFile) |
| return -1; |
| |
| FreeHook = &free; |
| setupIOBuffer(); |
| RetVal = lprofWriteData(fileWriter, OutputFile, lprofGetVPDataReader()); |
| |
| fclose(OutputFile); |
| return RetVal; |
| } |
| |
| static void truncateCurrentFile(void) { |
| const char *Filename; |
| char *FilenameBuf; |
| FILE *File; |
| int Length; |
| |
| Length = getCurFilenameLength(); |
| FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1); |
| Filename = getCurFilename(FilenameBuf); |
| if (!Filename) |
| return; |
| |
| /* Create the directory holding the file, if needed. */ |
| if (strchr(Filename, '/') || strchr(Filename, '\\')) { |
| char *Copy = (char *)COMPILER_RT_ALLOCA(Length + 1); |
| strncpy(Copy, Filename, Length + 1); |
| __llvm_profile_recursive_mkdir(Copy); |
| } |
| |
| /* Truncate the file. Later we'll reopen and append. */ |
| File = fopen(Filename, "w"); |
| if (!File) |
| return; |
| fclose(File); |
| } |
| |
| /* Set the result of the file name parsing. If \p FilenamePat pattern is seen |
| * the first time, also truncate the file associated with that name. |
| */ |
| static void setFilename(const char *FilenamePat, const char *PidStr, |
| unsigned NumPids, const char *HostStr, |
| unsigned NumHosts) { |
| /* Check if this is a new filename and therefore needs truncation. */ |
| int NewFile = |
| !lprofCurFilename.FilenamePat || |
| (FilenamePat && strcmp(FilenamePat, lprofCurFilename.FilenamePat)); |
| |
| lprofCurFilename.FilenamePat = FilenamePat; |
| lprofCurFilename.NumPids = NumPids; |
| if (NumPids) |
| strncpy(lprofCurFilename.PidChars, PidStr, MAX_PID_SIZE); |
| lprofCurFilename.NumHosts = NumHosts; |
| if (NumHosts) |
| strncpy(lprofCurFilename.Hostname, HostStr, COMPILER_RT_MAX_HOSTLEN); |
| |
| /* If not a new file, append to support profiling multiple shared objects. */ |
| if (NewFile) |
| truncateCurrentFile(); |
| } |
| |
| static void resetFilenameToDefault(void) { |
| setFilename("default.profraw", 0, 0, 0, 0); |
| } |
| |
| /* Parses the pattern string \p FilenamePat and store the result to |
| * lprofcurFilename structure. */ |
| static int parseFilenamePattern(const char *FilenamePat) { |
| int NumPids = 0, NumHosts = 0, I; |
| char PidChars[MAX_PID_SIZE]; |
| char Hostname[COMPILER_RT_MAX_HOSTLEN]; |
| |
| /* Check the filename for "%p", which indicates a pid-substitution. */ |
| for (I = 0; FilenamePat[I]; ++I) |
| if (FilenamePat[I] == '%') { |
| if (FilenamePat[++I] == 'p') { |
| if (!NumPids++) { |
| if (snprintf(PidChars, MAX_PID_SIZE, "%d", getpid()) <= 0) { |
| PROF_WARN( |
| "Unable to parse filename pattern %s. Using the default name.", |
| FilenamePat); |
| return -1; |
| } |
| } |
| } else if (FilenamePat[I] == 'h') { |
| if (!NumHosts++) |
| if (COMPILER_RT_GETHOSTNAME(Hostname, COMPILER_RT_MAX_HOSTLEN)) { |
| PROF_WARN( |
| "Unable to parse filename pattern %s. Using the default name.", |
| FilenamePat); |
| return -1; |
| } |
| } |
| } |
| |
| setFilename(FilenamePat, PidChars, NumPids, Hostname, NumHosts); |
| return 0; |
| } |
| |
| /* Return buffer length that is required to store the current profile |
| * filename with PID and hostname substitutions. */ |
| static int getCurFilenameLength() { |
| if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0]) |
| return 0; |
| |
| if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts)) |
| return strlen(lprofCurFilename.FilenamePat); |
| |
| return strlen(lprofCurFilename.FilenamePat) + |
| lprofCurFilename.NumPids * (strlen(lprofCurFilename.PidChars) - 2) + |
| lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2); |
| } |
| |
| /* Return the pointer to the current profile file name (after substituting |
| * PIDs and Hostnames in filename pattern. \p FilenameBuf is the buffer |
| * to store the resulting filename. If no substitution is needed, the |
| * current filename pattern string is directly returned. */ |
| static const char *getCurFilename(char *FilenameBuf) { |
| int I, J, PidLength, HostNameLength; |
| const char *FilenamePat = lprofCurFilename.FilenamePat; |
| |
| if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0]) |
| return 0; |
| |
| if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts)) |
| return lprofCurFilename.FilenamePat; |
| |
| PidLength = strlen(lprofCurFilename.PidChars); |
| HostNameLength = strlen(lprofCurFilename.Hostname); |
| /* Construct the new filename. */ |
| for (I = 0, J = 0; FilenamePat[I]; ++I) |
| if (FilenamePat[I] == '%') { |
| if (FilenamePat[++I] == 'p') { |
| memcpy(FilenameBuf + J, lprofCurFilename.PidChars, PidLength); |
| J += PidLength; |
| } else if (FilenamePat[I] == 'h') { |
| memcpy(FilenameBuf + J, lprofCurFilename.Hostname, HostNameLength); |
| J += HostNameLength; |
| } |
| /* Drop any unknown substitutions. */ |
| } else |
| FilenameBuf[J++] = FilenamePat[I]; |
| FilenameBuf[J] = 0; |
| |
| return FilenameBuf; |
| } |
| |
| /* Returns the pointer to the environment variable |
| * string. Returns null if the env var is not set. */ |
| static const char *getFilenamePatFromEnv(void) { |
| const char *Filename = getenv("LLVM_PROFILE_FILE"); |
| if (!Filename || !Filename[0]) |
| return 0; |
| return Filename; |
| } |
| |
| /* This method is invoked by the runtime initialization hook |
| * InstrProfilingRuntime.o if it is linked in. Both user specified |
| * profile path via -fprofile-instr-generate= and LLVM_PROFILE_FILE |
| * environment variable can override this default value. */ |
| COMPILER_RT_VISIBILITY |
| void __llvm_profile_initialize_file(void) { |
| const char *FilenamePat; |
| /* Check if the filename has been initialized. */ |
| if (lprofCurFilename.FilenamePat) |
| return; |
| |
| /* Detect the filename and truncate. */ |
| FilenamePat = getFilenamePatFromEnv(); |
| if (!FilenamePat || parseFilenamePattern(FilenamePat)) |
| resetFilenameToDefault(); |
| } |
| |
| /* This API is directly called by the user application code. It has the |
| * highest precedence compared with LLVM_PROFILE_FILE environment variable |
| * and command line option -fprofile-instr-generate=<profile_name>. |
| */ |
| COMPILER_RT_VISIBILITY |
| void __llvm_profile_set_filename(const char *FilenamePat) { |
| if (!FilenamePat || parseFilenamePattern(FilenamePat)) |
| resetFilenameToDefault(); |
| } |
| |
| /* |
| * This API is invoked by the global initializers emitted by Clang/LLVM when |
| * -fprofile-instr-generate=<..> is specified (vs -fprofile-instr-generate |
| * without an argument). This option has lower precedence than the |
| * LLVM_PROFILE_FILE environment variable. |
| */ |
| COMPILER_RT_VISIBILITY |
| void __llvm_profile_override_default_filename(const char *FilenamePat) { |
| /* If the env var is set, skip setting filename from argument. */ |
| const char *Env_Filename = getFilenamePatFromEnv(); |
| if (Env_Filename) |
| return; |
| if (!FilenamePat || parseFilenamePattern(FilenamePat)) |
| resetFilenameToDefault(); |
| } |
| |
| /* The public API for writing profile data into the file with name |
| * set by previous calls to __llvm_profile_set_filename or |
| * __llvm_profile_override_default_filename or |
| * __llvm_profile_initialize_file. */ |
| COMPILER_RT_VISIBILITY |
| int __llvm_profile_write_file(void) { |
| int rc, Length; |
| const char *Filename; |
| char *FilenameBuf; |
| |
| Length = getCurFilenameLength(); |
| FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1); |
| Filename = getCurFilename(FilenameBuf); |
| |
| /* Check the filename. */ |
| if (!Filename) { |
| PROF_ERR("Failed to write file : %s\n", "Filename not set"); |
| return -1; |
| } |
| |
| /* Check if there is llvm/runtime version mismatch. */ |
| if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) { |
| PROF_ERR("Runtime and instrumentation version mismatch : " |
| "expected %d, but get %d\n", |
| INSTR_PROF_RAW_VERSION, |
| (int)GET_VERSION(__llvm_profile_get_version())); |
| return -1; |
| } |
| |
| /* Write profile data to the file. */ |
| rc = writeFile(Filename); |
| if (rc) |
| PROF_ERR("Failed to write file \"%s\": %s\n", Filename, strerror(errno)); |
| return rc; |
| } |
| |
| static void writeFileWithoutReturn(void) { __llvm_profile_write_file(); } |
| |
| COMPILER_RT_VISIBILITY |
| int __llvm_profile_register_write_file_atexit(void) { |
| static int HasBeenRegistered = 0; |
| |
| if (HasBeenRegistered) |
| return 0; |
| |
| lprofSetupValueProfiler(); |
| |
| HasBeenRegistered = 1; |
| return atexit(writeFileWithoutReturn); |
| } |