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