Home | History | Annotate | Download | only in profile
      1 /*===- InstrProfilingFile.c - Write instrumentation to a file -------------===*\
      2 |*
      3 |*                     The LLVM Compiler Infrastructure
      4 |*
      5 |* This file is distributed under the University of Illinois Open Source
      6 |* License. See LICENSE.TXT for details.
      7 |*
      8 \*===----------------------------------------------------------------------===*/
      9 
     10 #include "InstrProfiling.h"
     11 #include "InstrProfilingInternal.h"
     12 #include "InstrProfilingUtil.h"
     13 #include <errno.h>
     14 #include <stdio.h>
     15 #include <stdlib.h>
     16 #include <string.h>
     17 #ifdef _MSC_VER
     18 /* For _alloca. */
     19 #include <malloc.h>
     20 #endif
     21 #if defined(_WIN32)
     22 #include "WindowsMMap.h"
     23 /* For _chsize_s */
     24 #include <io.h>
     25 #else
     26 #include <sys/file.h>
     27 #include <sys/mman.h>
     28 #include <unistd.h>
     29 #if defined(__linux__)
     30 #include <sys/types.h>
     31 #endif
     32 #endif
     33 
     34 /* From where is profile name specified.
     35  * The order the enumerators define their
     36  * precedence. Re-order them may lead to
     37  * runtime behavior change. */
     38 typedef enum ProfileNameSpecifier {
     39   PNS_unknown = 0,
     40   PNS_default,
     41   PNS_command_line,
     42   PNS_environment,
     43   PNS_runtime_api
     44 } ProfileNameSpecifier;
     45 
     46 static const char *getPNSStr(ProfileNameSpecifier PNS) {
     47   switch (PNS) {
     48   case PNS_default:
     49     return "default setting";
     50   case PNS_command_line:
     51     return "command line";
     52   case PNS_environment:
     53     return "environment variable";
     54   case PNS_runtime_api:
     55     return "runtime API";
     56   default:
     57     return "Unknown";
     58   }
     59 }
     60 
     61 #define MAX_PID_SIZE 16
     62 /* Data structure holding the result of parsed filename pattern.  */
     63 typedef struct lprofFilename {
     64   /* File name string possibly with %p or %h specifiers. */
     65   const char *FilenamePat;
     66   char PidChars[MAX_PID_SIZE];
     67   char Hostname[COMPILER_RT_MAX_HOSTLEN];
     68   unsigned NumPids;
     69   unsigned NumHosts;
     70   /* When in-process merging is enabled, this parameter specifies
     71    * the total number of profile data files shared by all the processes
     72    * spawned from the same binary. By default the value is 1. If merging
     73    * is not enabled, its value should be 0. This parameter is specified
     74    * by the %[0-9]m specifier. For instance %2m enables merging using
     75    * 2 profile data files. %1m is equivalent to %m. Also %m specifier
     76    * can only appear once at the end of the name pattern. */
     77   unsigned MergePoolSize;
     78   ProfileNameSpecifier PNS;
     79 } lprofFilename;
     80 
     81 lprofFilename lprofCurFilename = {0, {0}, {0}, 0, 0, 0, PNS_unknown};
     82 
     83 int getpid(void);
     84 static int getCurFilenameLength();
     85 static const char *getCurFilename(char *FilenameBuf);
     86 static unsigned doMerging() { return lprofCurFilename.MergePoolSize; }
     87 
     88 /* Return 1 if there is an error, otherwise return  0.  */
     89 static uint32_t fileWriter(ProfDataIOVec *IOVecs, uint32_t NumIOVecs,
     90                            void **WriterCtx) {
     91   uint32_t I;
     92   FILE *File = (FILE *)*WriterCtx;
     93   for (I = 0; I < NumIOVecs; I++) {
     94     if (fwrite(IOVecs[I].Data, IOVecs[I].ElmSize, IOVecs[I].NumElm, File) !=
     95         IOVecs[I].NumElm)
     96       return 1;
     97   }
     98   return 0;
     99 }
    100 
    101 COMPILER_RT_VISIBILITY ProfBufferIO *
    102 lprofCreateBufferIOInternal(void *File, uint32_t BufferSz) {
    103   FreeHook = &free;
    104   DynamicBufferIOBuffer = (uint8_t *)calloc(BufferSz, 1);
    105   VPBufferSize = BufferSz;
    106   return lprofCreateBufferIO(fileWriter, File);
    107 }
    108 
    109 static void setupIOBuffer() {
    110   const char *BufferSzStr = 0;
    111   BufferSzStr = getenv("LLVM_VP_BUFFER_SIZE");
    112   if (BufferSzStr && BufferSzStr[0]) {
    113     VPBufferSize = atoi(BufferSzStr);
    114     DynamicBufferIOBuffer = (uint8_t *)calloc(VPBufferSize, 1);
    115   }
    116 }
    117 
    118 /* Read profile data in \c ProfileFile and merge with in-memory
    119    profile counters. Returns -1 if there is fatal error, otheriwse
    120    0 is returned.
    121 */
    122 static int doProfileMerging(FILE *ProfileFile) {
    123   uint64_t ProfileFileSize;
    124   char *ProfileBuffer;
    125 
    126   if (fseek(ProfileFile, 0L, SEEK_END) == -1) {
    127     PROF_ERR("Unable to merge profile data, unable to get size: %s\n",
    128              strerror(errno));
    129     return -1;
    130   }
    131   ProfileFileSize = ftell(ProfileFile);
    132 
    133   /* Restore file offset.  */
    134   if (fseek(ProfileFile, 0L, SEEK_SET) == -1) {
    135     PROF_ERR("Unable to merge profile data, unable to rewind: %s\n",
    136              strerror(errno));
    137     return -1;
    138   }
    139 
    140   /* Nothing to merge.  */
    141   if (ProfileFileSize < sizeof(__llvm_profile_header)) {
    142     if (ProfileFileSize)
    143       PROF_WARN("Unable to merge profile data: %s\n",
    144                 "source profile file is too small.");
    145     return 0;
    146   }
    147 
    148   ProfileBuffer = mmap(NULL, ProfileFileSize, PROT_READ, MAP_SHARED | MAP_FILE,
    149                        fileno(ProfileFile), 0);
    150   if (ProfileBuffer == MAP_FAILED) {
    151     PROF_ERR("Unable to merge profile data, mmap failed: %s\n",
    152              strerror(errno));
    153     return -1;
    154   }
    155 
    156   if (__llvm_profile_check_compatibility(ProfileBuffer, ProfileFileSize)) {
    157     (void)munmap(ProfileBuffer, ProfileFileSize);
    158     PROF_WARN("Unable to merge profile data: %s\n",
    159               "source profile file is not compatible.");
    160     return 0;
    161   }
    162 
    163   /* Now start merging */
    164   __llvm_profile_merge_from_buffer(ProfileBuffer, ProfileFileSize);
    165   (void)munmap(ProfileBuffer, ProfileFileSize);
    166 
    167   return 0;
    168 }
    169 
    170 /* Open the profile data for merging. It opens the file in r+b mode with
    171  * file locking.  If the file has content which is compatible with the
    172  * current process, it also reads in the profile data in the file and merge
    173  * it with in-memory counters. After the profile data is merged in memory,
    174  * the original profile data is truncated and gets ready for the profile
    175  * dumper. With profile merging enabled, each executable as well as any of
    176  * its instrumented shared libraries dump profile data into their own data file.
    177 */
    178 static FILE *openFileForMerging(const char *ProfileFileName) {
    179   FILE *ProfileFile;
    180   int rc;
    181 
    182   ProfileFile = lprofOpenFileEx(ProfileFileName);
    183   if (!ProfileFile)
    184     return NULL;
    185 
    186   rc = doProfileMerging(ProfileFile);
    187   if (rc || COMPILER_RT_FTRUNCATE(ProfileFile, 0L) ||
    188       fseek(ProfileFile, 0L, SEEK_SET) == -1) {
    189     PROF_ERR("Profile Merging of file %s failed: %s\n", ProfileFileName,
    190              strerror(errno));
    191     fclose(ProfileFile);
    192     return NULL;
    193   }
    194   fseek(ProfileFile, 0L, SEEK_SET);
    195   return ProfileFile;
    196 }
    197 
    198 /* Write profile data to file \c OutputName.  */
    199 static int writeFile(const char *OutputName) {
    200   int RetVal;
    201   FILE *OutputFile;
    202 
    203   if (!doMerging())
    204     OutputFile = fopen(OutputName, "ab");
    205   else
    206     OutputFile = openFileForMerging(OutputName);
    207 
    208   if (!OutputFile)
    209     return -1;
    210 
    211   FreeHook = &free;
    212   setupIOBuffer();
    213   RetVal = lprofWriteData(fileWriter, OutputFile, lprofGetVPDataReader());
    214 
    215   fclose(OutputFile);
    216   return RetVal;
    217 }
    218 
    219 static void truncateCurrentFile(void) {
    220   const char *Filename;
    221   char *FilenameBuf;
    222   FILE *File;
    223   int Length;
    224 
    225   Length = getCurFilenameLength();
    226   FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1);
    227   Filename = getCurFilename(FilenameBuf);
    228   if (!Filename)
    229     return;
    230 
    231   /* Create the directory holding the file, if needed. */
    232   if (strchr(Filename, '/') || strchr(Filename, '\\')) {
    233     char *Copy = (char *)COMPILER_RT_ALLOCA(Length + 1);
    234     strncpy(Copy, Filename, Length + 1);
    235     __llvm_profile_recursive_mkdir(Copy);
    236   }
    237 
    238   /* Truncate the file.  Later we'll reopen and append. */
    239   File = fopen(Filename, "w");
    240   if (!File)
    241     return;
    242   fclose(File);
    243 }
    244 
    245 static const char *DefaultProfileName = "default.profraw";
    246 static void resetFilenameToDefault(void) {
    247   memset(&lprofCurFilename, 0, sizeof(lprofCurFilename));
    248   lprofCurFilename.FilenamePat = DefaultProfileName;
    249   lprofCurFilename.PNS = PNS_default;
    250 }
    251 
    252 static int containsMergeSpecifier(const char *FilenamePat, int I) {
    253   return (FilenamePat[I] == 'm' ||
    254           (FilenamePat[I] >= '1' && FilenamePat[I] <= '9' &&
    255            /* If FilenamePat[I] is not '\0', the next byte is guaranteed
    256             * to be in-bound as the string is null terminated. */
    257            FilenamePat[I + 1] == 'm'));
    258 }
    259 
    260 /* Parses the pattern string \p FilenamePat and stores the result to
    261  * lprofcurFilename structure. */
    262 static int parseFilenamePattern(const char *FilenamePat) {
    263   int NumPids = 0, NumHosts = 0, I;
    264   char *PidChars = &lprofCurFilename.PidChars[0];
    265   char *Hostname = &lprofCurFilename.Hostname[0];
    266   int MergingEnabled = 0;
    267 
    268   lprofCurFilename.FilenamePat = FilenamePat;
    269   /* Check the filename for "%p", which indicates a pid-substitution. */
    270   for (I = 0; FilenamePat[I]; ++I)
    271     if (FilenamePat[I] == '%') {
    272       if (FilenamePat[++I] == 'p') {
    273         if (!NumPids++) {
    274           if (snprintf(PidChars, MAX_PID_SIZE, "%d", getpid()) <= 0) {
    275             PROF_WARN(
    276                 "Unable to parse filename pattern %s. Using the default name.",
    277                 FilenamePat);
    278             return -1;
    279           }
    280         }
    281       } else if (FilenamePat[I] == 'h') {
    282         if (!NumHosts++)
    283           if (COMPILER_RT_GETHOSTNAME(Hostname, COMPILER_RT_MAX_HOSTLEN)) {
    284             PROF_WARN(
    285                 "Unable to parse filename pattern %s. Using the default name.",
    286                 FilenamePat);
    287             return -1;
    288           }
    289       } else if (containsMergeSpecifier(FilenamePat, I)) {
    290         if (MergingEnabled) {
    291           PROF_WARN("%%m specifier can only be specified once in %s.\n",
    292                     FilenamePat);
    293           return -1;
    294         }
    295         MergingEnabled = 1;
    296         if (FilenamePat[I] == 'm')
    297           lprofCurFilename.MergePoolSize = 1;
    298         else {
    299           lprofCurFilename.MergePoolSize = FilenamePat[I] - '0';
    300           I++; /* advance to 'm' */
    301         }
    302       }
    303     }
    304 
    305   lprofCurFilename.NumPids = NumPids;
    306   lprofCurFilename.NumHosts = NumHosts;
    307   return 0;
    308 }
    309 
    310 static void parseAndSetFilename(const char *FilenamePat,
    311                                 ProfileNameSpecifier PNS) {
    312 
    313   const char *OldFilenamePat = lprofCurFilename.FilenamePat;
    314   ProfileNameSpecifier OldPNS = lprofCurFilename.PNS;
    315 
    316   if (PNS < OldPNS)
    317     return;
    318 
    319   if (!FilenamePat)
    320     FilenamePat = DefaultProfileName;
    321 
    322   /* When -fprofile-instr-generate=<path> is specified on the
    323    * command line, each module will be instrumented with runtime
    324    * init call to __llvm_profile_init function which calls
    325    * __llvm_profile_override_default_filename. In most of the cases,
    326    * the path will be identical, so bypass the parsing completely.
    327    */
    328   if (OldFilenamePat && !strcmp(OldFilenamePat, FilenamePat)) {
    329     lprofCurFilename.PNS = PNS;
    330     return;
    331   }
    332 
    333   /* When PNS >= OldPNS, the last one wins. */
    334   if (!FilenamePat || parseFilenamePattern(FilenamePat))
    335     resetFilenameToDefault();
    336   lprofCurFilename.PNS = PNS;
    337 
    338   if (!OldFilenamePat) {
    339     PROF_NOTE("Set profile file path to \"%s\" via %s.\n",
    340               lprofCurFilename.FilenamePat, getPNSStr(PNS));
    341   } else {
    342     PROF_NOTE("Override old profile path \"%s\" via %s to \"%s\" via %s.\n",
    343               OldFilenamePat, getPNSStr(OldPNS), lprofCurFilename.FilenamePat,
    344               getPNSStr(PNS));
    345   }
    346 
    347   if (!lprofCurFilename.MergePoolSize)
    348     truncateCurrentFile();
    349 }
    350 
    351 /* Return buffer length that is required to store the current profile
    352  * filename with PID and hostname substitutions. */
    353 /* The length to hold uint64_t followed by 2 digit pool id including '_' */
    354 #define SIGLEN 24
    355 static int getCurFilenameLength() {
    356   int Len;
    357   if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0])
    358     return 0;
    359 
    360   if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts ||
    361         lprofCurFilename.MergePoolSize))
    362     return strlen(lprofCurFilename.FilenamePat);
    363 
    364   Len = strlen(lprofCurFilename.FilenamePat) +
    365         lprofCurFilename.NumPids * (strlen(lprofCurFilename.PidChars) - 2) +
    366         lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2);
    367   if (lprofCurFilename.MergePoolSize)
    368     Len += SIGLEN;
    369   return Len;
    370 }
    371 
    372 /* Return the pointer to the current profile file name (after substituting
    373  * PIDs and Hostnames in filename pattern. \p FilenameBuf is the buffer
    374  * to store the resulting filename. If no substitution is needed, the
    375  * current filename pattern string is directly returned. */
    376 static const char *getCurFilename(char *FilenameBuf) {
    377   int I, J, PidLength, HostNameLength;
    378   const char *FilenamePat = lprofCurFilename.FilenamePat;
    379 
    380   if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0])
    381     return 0;
    382 
    383   if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts ||
    384         lprofCurFilename.MergePoolSize))
    385     return lprofCurFilename.FilenamePat;
    386 
    387   PidLength = strlen(lprofCurFilename.PidChars);
    388   HostNameLength = strlen(lprofCurFilename.Hostname);
    389   /* Construct the new filename. */
    390   for (I = 0, J = 0; FilenamePat[I]; ++I)
    391     if (FilenamePat[I] == '%') {
    392       if (FilenamePat[++I] == 'p') {
    393         memcpy(FilenameBuf + J, lprofCurFilename.PidChars, PidLength);
    394         J += PidLength;
    395       } else if (FilenamePat[I] == 'h') {
    396         memcpy(FilenameBuf + J, lprofCurFilename.Hostname, HostNameLength);
    397         J += HostNameLength;
    398       } else if (containsMergeSpecifier(FilenamePat, I)) {
    399         char LoadModuleSignature[SIGLEN];
    400         int S;
    401         int ProfilePoolId = getpid() % lprofCurFilename.MergePoolSize;
    402         S = snprintf(LoadModuleSignature, SIGLEN, "%" PRIu64 "_%d",
    403                      lprofGetLoadModuleSignature(), ProfilePoolId);
    404         if (S == -1 || S > SIGLEN)
    405           S = SIGLEN;
    406         memcpy(FilenameBuf + J, LoadModuleSignature, S);
    407         J += S;
    408         if (FilenamePat[I] != 'm')
    409           I++;
    410       }
    411       /* Drop any unknown substitutions. */
    412     } else
    413       FilenameBuf[J++] = FilenamePat[I];
    414   FilenameBuf[J] = 0;
    415 
    416   return FilenameBuf;
    417 }
    418 
    419 /* Returns the pointer to the environment variable
    420  * string. Returns null if the env var is not set. */
    421 static const char *getFilenamePatFromEnv(void) {
    422   const char *Filename = getenv("LLVM_PROFILE_FILE");
    423   if (!Filename || !Filename[0])
    424     return 0;
    425   return Filename;
    426 }
    427 
    428 /* This method is invoked by the runtime initialization hook
    429  * InstrProfilingRuntime.o if it is linked in. Both user specified
    430  * profile path via -fprofile-instr-generate= and LLVM_PROFILE_FILE
    431  * environment variable can override this default value. */
    432 COMPILER_RT_VISIBILITY
    433 void __llvm_profile_initialize_file(void) {
    434   const char *FilenamePat;
    435 
    436   FilenamePat = getFilenamePatFromEnv();
    437   parseAndSetFilename(FilenamePat, FilenamePat ? PNS_environment : PNS_default);
    438 }
    439 
    440 /* This API is directly called by the user application code. It has the
    441  * highest precedence compared with LLVM_PROFILE_FILE environment variable
    442  * and command line option -fprofile-instr-generate=<profile_name>.
    443  */
    444 COMPILER_RT_VISIBILITY
    445 void __llvm_profile_set_filename(const char *FilenamePat) {
    446   parseAndSetFilename(FilenamePat, PNS_runtime_api);
    447 }
    448 
    449 /*
    450  * This API is invoked by the global initializers emitted by Clang/LLVM when
    451  * -fprofile-instr-generate=<..> is specified (vs -fprofile-instr-generate
    452  *  without an argument). This option has lower precedence than the
    453  *  LLVM_PROFILE_FILE environment variable.
    454  */
    455 COMPILER_RT_VISIBILITY
    456 void __llvm_profile_override_default_filename(const char *FilenamePat) {
    457   parseAndSetFilename(FilenamePat, PNS_command_line);
    458 }
    459 
    460 /* The public API for writing profile data into the file with name
    461  * set by previous calls to __llvm_profile_set_filename or
    462  * __llvm_profile_override_default_filename or
    463  * __llvm_profile_initialize_file. */
    464 COMPILER_RT_VISIBILITY
    465 int __llvm_profile_write_file(void) {
    466   int rc, Length;
    467   const char *Filename;
    468   char *FilenameBuf;
    469 
    470   Length = getCurFilenameLength();
    471   FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1);
    472   Filename = getCurFilename(FilenameBuf);
    473 
    474   /* Check the filename. */
    475   if (!Filename) {
    476     PROF_ERR("Failed to write file : %s\n", "Filename not set");
    477     return -1;
    478   }
    479 
    480   /* Check if there is llvm/runtime version mismatch.  */
    481   if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) {
    482     PROF_ERR("Runtime and instrumentation version mismatch : "
    483              "expected %d, but get %d\n",
    484              INSTR_PROF_RAW_VERSION,
    485              (int)GET_VERSION(__llvm_profile_get_version()));
    486     return -1;
    487   }
    488 
    489   /* Write profile data to the file. */
    490   rc = writeFile(Filename);
    491   if (rc)
    492     PROF_ERR("Failed to write file \"%s\": %s\n", Filename, strerror(errno));
    493   return rc;
    494 }
    495 
    496 static void writeFileWithoutReturn(void) { __llvm_profile_write_file(); }
    497 
    498 COMPILER_RT_VISIBILITY
    499 int __llvm_profile_register_write_file_atexit(void) {
    500   static int HasBeenRegistered = 0;
    501 
    502   if (HasBeenRegistered)
    503     return 0;
    504 
    505   lprofSetupValueProfiler();
    506 
    507   HasBeenRegistered = 1;
    508   return atexit(writeFileWithoutReturn);
    509 }
    510