Home | History | Annotate | Line # | 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 
     18 #define UNCONST(ptr) ((void *)(uintptr_t)(ptr))
     19 
     20 /* Return 1 if there is an error, otherwise return  0.  */
     21 static uint32_t fileWriter(ProfDataIOVec *IOVecs, uint32_t NumIOVecs,
     22                            void **WriterCtx) {
     23   uint32_t I;
     24   FILE *File = (FILE *)*WriterCtx;
     25   for (I = 0; I < NumIOVecs; I++) {
     26     if (fwrite(IOVecs[I].Data, IOVecs[I].ElmSize, IOVecs[I].NumElm, File) !=
     27         IOVecs[I].NumElm)
     28       return 1;
     29   }
     30   return 0;
     31 }
     32 
     33 COMPILER_RT_VISIBILITY ProfBufferIO *
     34 llvmCreateBufferIOInternal(void *File, uint32_t BufferSz) {
     35   CallocHook = calloc;
     36   FreeHook = free;
     37   return llvmCreateBufferIO(fileWriter, File, BufferSz);
     38 }
     39 
     40 static int writeFile(FILE *File) {
     41   const char *BufferSzStr = 0;
     42   uint64_t ValueDataSize = 0;
     43   struct ValueProfData **ValueDataArray =
     44       __llvm_profile_gather_value_data(&ValueDataSize);
     45   FreeHook = &free;
     46   CallocHook = &calloc;
     47   BufferSzStr = getenv("LLVM_VP_BUFFER_SIZE");
     48   if (BufferSzStr && BufferSzStr[0])
     49     VPBufferSize = atoi(BufferSzStr);
     50   return llvmWriteProfData(fileWriter, File, ValueDataArray, ValueDataSize);
     51 }
     52 
     53 static int writeFileWithName(const char *OutputName) {
     54   int RetVal;
     55   FILE *OutputFile;
     56   if (!OutputName || !OutputName[0])
     57     return -1;
     58 
     59   /* Append to the file to support profiling multiple shared objects. */
     60   OutputFile = fopen(OutputName, "ab");
     61   if (!OutputFile)
     62     return -1;
     63 
     64   RetVal = writeFile(OutputFile);
     65 
     66   fclose(OutputFile);
     67   return RetVal;
     68 }
     69 
     70 COMPILER_RT_WEAK int __llvm_profile_OwnsFilename = 0;
     71 COMPILER_RT_WEAK const char *__llvm_profile_CurrentFilename = NULL;
     72 
     73 static void truncateCurrentFile(void) {
     74   const char *Filename;
     75   FILE *File;
     76 
     77   Filename = __llvm_profile_CurrentFilename;
     78   if (!Filename || !Filename[0])
     79     return;
     80 
     81   /* Create the directory holding the file, if needed. */
     82   if (strchr(Filename, '/')) {
     83     char *Copy = malloc(strlen(Filename) + 1);
     84     strcpy(Copy, Filename);
     85     __llvm_profile_recursive_mkdir(Copy);
     86     free(Copy);
     87   }
     88 
     89   /* Truncate the file.  Later we'll reopen and append. */
     90   File = fopen(Filename, "w");
     91   if (!File)
     92     return;
     93   fclose(File);
     94 }
     95 
     96 static void setFilename(const char *Filename, int OwnsFilename) {
     97   /* Check if this is a new filename and therefore needs truncation. */
     98   int NewFile = !__llvm_profile_CurrentFilename ||
     99       (Filename && strcmp(Filename, __llvm_profile_CurrentFilename));
    100   if (__llvm_profile_OwnsFilename)
    101     free(UNCONST(__llvm_profile_CurrentFilename));
    102 
    103   __llvm_profile_CurrentFilename = Filename;
    104   __llvm_profile_OwnsFilename = OwnsFilename;
    105 
    106   /* If not a new file, append to support profiling multiple shared objects. */
    107   if (NewFile)
    108     truncateCurrentFile();
    109 }
    110 
    111 static void resetFilenameToDefault(void) { setFilename("default.profraw", 0); }
    112 
    113 int getpid(void);
    114 static int setFilenamePossiblyWithPid(const char *Filename) {
    115 #define MAX_PID_SIZE 16
    116   char PidChars[MAX_PID_SIZE] = {0};
    117   int NumPids = 0, PidLength = 0;
    118   char *Allocated;
    119   int I, J;
    120 
    121   /* Reset filename on NULL, except with env var which is checked by caller. */
    122   if (!Filename) {
    123     resetFilenameToDefault();
    124     return 0;
    125   }
    126 
    127   /* Check the filename for "%p", which indicates a pid-substitution. */
    128   for (I = 0; Filename[I]; ++I)
    129     if (Filename[I] == '%' && Filename[++I] == 'p')
    130       if (!NumPids++) {
    131         PidLength = snprintf(PidChars, MAX_PID_SIZE, "%d", getpid());
    132         if (PidLength <= 0)
    133           return -1;
    134       }
    135   if (!NumPids) {
    136     setFilename(Filename, 0);
    137     return 0;
    138   }
    139 
    140   /* Allocate enough space for the substituted filename. */
    141   Allocated = malloc(I + NumPids*(PidLength - 2) + 1);
    142   if (!Allocated)
    143     return -1;
    144 
    145   /* Construct the new filename. */
    146   for (I = 0, J = 0; Filename[I]; ++I)
    147     if (Filename[I] == '%') {
    148       if (Filename[++I] == 'p') {
    149         memcpy(Allocated + J, PidChars, PidLength);
    150         J += PidLength;
    151       }
    152       /* Drop any unknown substitutions. */
    153     } else
    154       Allocated[J++] = Filename[I];
    155   Allocated[J] = 0;
    156 
    157   /* Use the computed name. */
    158   setFilename(Allocated, 1);
    159   return 0;
    160 }
    161 
    162 static int setFilenameFromEnvironment(void) {
    163   const char *Filename = getenv("LLVM_PROFILE_FILE");
    164 
    165   if (!Filename || !Filename[0])
    166     return -1;
    167 
    168   return setFilenamePossiblyWithPid(Filename);
    169 }
    170 
    171 static void setFilenameAutomatically(void) {
    172   if (!setFilenameFromEnvironment())
    173     return;
    174 
    175   resetFilenameToDefault();
    176 }
    177 
    178 COMPILER_RT_VISIBILITY
    179 void __llvm_profile_initialize_file(void) {
    180   /* Check if the filename has been initialized. */
    181   if (__llvm_profile_CurrentFilename)
    182     return;
    183 
    184   /* Detect the filename and truncate. */
    185   setFilenameAutomatically();
    186 }
    187 
    188 COMPILER_RT_VISIBILITY
    189 void __llvm_profile_set_filename(const char *Filename) {
    190   setFilenamePossiblyWithPid(Filename);
    191 }
    192 
    193 COMPILER_RT_VISIBILITY
    194 void __llvm_profile_override_default_filename(const char *Filename) {
    195   /* If the env var is set, skip setting filename from argument. */
    196   const char *Env_Filename = getenv("LLVM_PROFILE_FILE");
    197   if (Env_Filename && Env_Filename[0])
    198     return;
    199   setFilenamePossiblyWithPid(Filename);
    200 }
    201 
    202 COMPILER_RT_VISIBILITY
    203 int __llvm_profile_write_file(void) {
    204   int rc;
    205 
    206   GetEnvHook = &getenv;
    207   /* Check the filename. */
    208   if (!__llvm_profile_CurrentFilename) {
    209     PROF_ERR("LLVM Profile: Failed to write file : %s\n", "Filename not set");
    210     return -1;
    211   }
    212 
    213   /* Check if there is llvm/runtime version mismatch.  */
    214   if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) {
    215     PROF_ERR("LLVM Profile: runtime and instrumentation version mismatch : "
    216              "expected %d, but get %d\n",
    217              INSTR_PROF_RAW_VERSION,
    218              (int)GET_VERSION(__llvm_profile_get_version()));
    219     return -1;
    220   }
    221 
    222   /* Write the file. */
    223   rc = writeFileWithName(__llvm_profile_CurrentFilename);
    224   if (rc)
    225     PROF_ERR("LLVM Profile: Failed to write file \"%s\": %s\n",
    226             __llvm_profile_CurrentFilename, strerror(errno));
    227   return rc;
    228 }
    229 
    230 static void writeFileWithoutReturn(void) { __llvm_profile_write_file(); }
    231 
    232 COMPILER_RT_VISIBILITY
    233 int __llvm_profile_register_write_file_atexit(void) {
    234   static int HasBeenRegistered = 0;
    235 
    236   if (HasBeenRegistered)
    237     return 0;
    238 
    239   HasBeenRegistered = 1;
    240   return atexit(writeFileWithoutReturn);
    241 }
    242