summaryrefslogtreecommitdiff
path: root/lib/profile/InstrProfilingFile.c
diff options
context:
space:
mode:
authorXinliang David Li <davidxl@google.com>2016-06-08 23:43:56 +0000
committerXinliang David Li <davidxl@google.com>2016-06-08 23:43:56 +0000
commit8be0a4ba49d80b27d9a1e3906bef25f674f1c195 (patch)
tree8272179a1ea12f96e1f507a4bddbcd1a86774421 /lib/profile/InstrProfilingFile.c
parentef5e5ab4ba851cacd6b4cbf3b83a27373e62badb (diff)
[profile] in-process merging support part-3
Differential Revision: http://reviews.llvm.org/D21056 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@272227 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/profile/InstrProfilingFile.c')
-rw-r--r--lib/profile/InstrProfilingFile.c168
1 files changed, 157 insertions, 11 deletions
diff --git a/lib/profile/InstrProfilingFile.c b/lib/profile/InstrProfilingFile.c
index 856d8f7e6..880059a99 100644
--- a/lib/profile/InstrProfilingFile.c
+++ b/lib/profile/InstrProfilingFile.c
@@ -18,6 +18,18 @@
/* For _alloca. */
#include <malloc.h>
#endif
+#if defined(_WIN32)
+#include "WindowsMMap.h"
+/* For _chsize_s */
+#include <io.h>
+#else
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#if defined(__linux__)
+#include <sys/types.h>
+#endif
+#endif
#define MAX_PID_SIZE 16
/* Data structure holding the result of parsed filename pattern. */
@@ -28,13 +40,22 @@ typedef struct lprofFilename {
char Hostname[COMPILER_RT_MAX_HOSTLEN];
unsigned NumPids;
unsigned NumHosts;
+ /* When in-process merging is enabled, this parameter specifies
+ * the total number of profile data files shared by all the processes
+ * spawned from the same binary. By default the value is 1. If merging
+ * is not enabled, its value should be 0. This parameter is specified
+ * by the %[0-9]m specifier. For instance %2m enables merging using
+ * 2 profile data files. %1m is equivalent to %m. Also %m specifier
+ * can only appear once at the end of the name pattern. */
+ unsigned MergePoolSize;
} lprofFilename;
-lprofFilename lprofCurFilename = {0, {0}, {0}, 0, 0};
+lprofFilename lprofCurFilename = {0, {0}, {0}, 0, 0, 0};
int getpid(void);
static int getCurFilenameLength();
static const char *getCurFilename(char *FilenameBuf);
+static unsigned doMerging() { return lprofCurFilename.MergePoolSize; }
/* Return 1 if there is an error, otherwise return 0. */
static uint32_t fileWriter(ProfDataIOVec *IOVecs, uint32_t NumIOVecs,
@@ -66,13 +87,96 @@ static void setupIOBuffer() {
}
}
+/* Read profile data in \c ProfileFile and merge with in-memory
+ profile counters. Returns -1 if there is fatal error, otheriwse
+ 0 is returned.
+*/
+static int doProfileMerging(FILE *ProfileFile) {
+ uint64_t ProfileFileSize;
+ char *ProfileBuffer;
+
+ if (fseek(ProfileFile, 0L, SEEK_END) == -1) {
+ PROF_ERR("Unable to merge profile data, unable to get size: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ ProfileFileSize = ftell(ProfileFile);
+
+ /* Restore file offset. */
+ if (fseek(ProfileFile, 0L, SEEK_SET) == -1) {
+ PROF_ERR("Unable to merge profile data, unable to rewind: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ /* Nothing to merge. */
+ if (ProfileFileSize < sizeof(__llvm_profile_header)) {
+ if (ProfileFileSize)
+ PROF_WARN("Unable to merge profile data: %s\n",
+ "source profile file is too small.");
+ return 0;
+ }
+
+ ProfileBuffer = mmap(NULL, ProfileFileSize, PROT_READ, MAP_SHARED | MAP_FILE,
+ fileno(ProfileFile), 0);
+ if (ProfileBuffer == MAP_FAILED) {
+ PROF_ERR("Unable to merge profile data, mmap failed: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ if (__llvm_profile_check_compatibility(ProfileBuffer, ProfileFileSize)) {
+ (void)munmap(ProfileBuffer, ProfileFileSize);
+ PROF_WARN("Unable to merge profile data: %s\n",
+ "source profile file is not compatible.");
+ return 0;
+ }
+
+ /* Now start merging */
+ __llvm_profile_merge_from_buffer(ProfileBuffer, ProfileFileSize);
+ (void)munmap(ProfileBuffer, ProfileFileSize);
+
+ return 0;
+}
+
+/* Open the profile data for merging. It opens the file in r+b mode with
+ * file locking. If the file has content which is compatible with the
+ * current process, it also reads in the profile data in the file and merge
+ * it with in-memory counters. After the profile data is merged in memory,
+ * the original profile data is truncated and gets ready for the profile
+ * dumper. With profile merging enabled, each executable as well as any of
+ * its instrumented shared libraries dump profile data into their own data file.
+*/
+static FILE *openFileForMerging(const char *ProfileFileName) {
+ FILE *ProfileFile;
+ int rc;
+
+ ProfileFile = lprofOpenFileEx(ProfileFileName);
+ if (!ProfileFile)
+ return NULL;
+
+ rc = doProfileMerging(ProfileFile);
+ if (rc || COMPILER_RT_FTRUNCATE(ProfileFile, 0L) ||
+ fseek(ProfileFile, 0L, SEEK_SET) == -1) {
+ PROF_ERR("Profile Merging of file %s failed: %s\n", ProfileFileName,
+ strerror(errno));
+ fclose(ProfileFile);
+ return NULL;
+ }
+ fseek(ProfileFile, 0L, SEEK_SET);
+ return ProfileFile;
+}
+
/* 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 (!doMerging())
+ OutputFile = fopen(OutputName, "ab");
+ else
+ OutputFile = openFileForMerging(OutputName);
+
if (!OutputFile)
return -1;
@@ -115,13 +219,21 @@ static void resetFilenameToDefault(void) {
lprofCurFilename.FilenamePat = "default.profraw";
}
-/* Parses the pattern string \p FilenamePat and store the result to
- * lprofcurFilename structure. */
+static int containsMergeSpecifier(const char *FilenamePat, int I) {
+ return (FilenamePat[I] == 'm' ||
+ (FilenamePat[I] >= '1' && FilenamePat[I] <= '9' &&
+ /* If FilenamePat[I] is not '\0', the next byte is guaranteed
+ * to be in-bound as the string is null terminated. */
+ FilenamePat[I + 1] == 'm'));
+}
+/* Parses the pattern string \p FilenamePat and stores the result to
+ * lprofcurFilename structure. */
static int parseFilenamePattern(const char *FilenamePat) {
int NumPids = 0, NumHosts = 0, I;
char *PidChars = &lprofCurFilename.PidChars[0];
char *Hostname = &lprofCurFilename.Hostname[0];
+ int MergingEnabled = 0;
lprofCurFilename.FilenamePat = FilenamePat;
/* Check the filename for "%p", which indicates a pid-substitution. */
@@ -144,6 +256,20 @@ static int parseFilenamePattern(const char *FilenamePat) {
FilenamePat);
return -1;
}
+ } else if (containsMergeSpecifier(FilenamePat, I)) {
+ if (MergingEnabled) {
+ PROF_WARN(
+ "%%m specifier can only be specified once at the end of %s.\n",
+ FilenamePat);
+ return -1;
+ }
+ MergingEnabled = 1;
+ if (FilenamePat[I] == 'm')
+ lprofCurFilename.MergePoolSize = 1;
+ else {
+ lprofCurFilename.MergePoolSize = FilenamePat[I] - '0';
+ I++; /* advance to 'm' */
+ }
}
}
@@ -162,22 +288,29 @@ static void parseAndSetFilename(const char *FilenamePat) {
NewFile =
!OldFilenamePat || (strcmp(OldFilenamePat, lprofCurFilename.FilenamePat));
- if (NewFile)
+ if (NewFile && !lprofCurFilename.MergePoolSize)
truncateCurrentFile();
}
/* Return buffer length that is required to store the current profile
* filename with PID and hostname substitutions. */
+/* The length to hold uint64_t followed by 2 digit pool id including '_' */
+#define SIGLEN 24
static int getCurFilenameLength() {
+ int Len;
if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0])
return 0;
- if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts))
+ if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts ||
+ lprofCurFilename.MergePoolSize))
return strlen(lprofCurFilename.FilenamePat);
- return strlen(lprofCurFilename.FilenamePat) +
- lprofCurFilename.NumPids * (strlen(lprofCurFilename.PidChars) - 2) +
- lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2);
+ Len = strlen(lprofCurFilename.FilenamePat) +
+ lprofCurFilename.NumPids * (strlen(lprofCurFilename.PidChars) - 2) +
+ lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2);
+ if (lprofCurFilename.MergePoolSize)
+ Len += SIGLEN;
+ return Len;
}
/* Return the pointer to the current profile file name (after substituting
@@ -191,7 +324,8 @@ static const char *getCurFilename(char *FilenameBuf) {
if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0])
return 0;
- if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts))
+ if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts ||
+ lprofCurFilename.MergePoolSize))
return lprofCurFilename.FilenamePat;
PidLength = strlen(lprofCurFilename.PidChars);
@@ -205,6 +339,18 @@ static const char *getCurFilename(char *FilenameBuf) {
} else if (FilenamePat[I] == 'h') {
memcpy(FilenameBuf + J, lprofCurFilename.Hostname, HostNameLength);
J += HostNameLength;
+ } else if (containsMergeSpecifier(FilenamePat, I)) {
+ char LoadModuleSignature[SIGLEN];
+ int S;
+ int ProfilePoolId = getpid() % lprofCurFilename.MergePoolSize;
+ S = snprintf(LoadModuleSignature, SIGLEN, "%" PRIu64 "_%d",
+ lprofGetLoadModuleSignature(), ProfilePoolId);
+ if (S == -1 || S > SIGLEN)
+ S = SIGLEN;
+ memcpy(FilenameBuf + J, LoadModuleSignature, S);
+ J += S;
+ if (FilenamePat[I] != 'm')
+ I++;
}
/* Drop any unknown substitutions. */
} else