[WTF] Add environment variable helpers
[WebKit-https.git] / Source / WTF / wtf / DataLog.cpp
1 /*
2  * Copyright (C) 2012 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include <wtf/DataLog.h>
28
29 #include <mutex>
30 #include <stdarg.h>
31 #include <string.h>
32 #include <thread>
33 #include <wtf/Environment.h>
34 #include <wtf/FilePrintStream.h>
35 #include <wtf/LockedPrintStream.h>
36 #include <wtf/ProcessID.h>
37 #include <wtf/Threading.h>
38
39 #if OS(UNIX) || OS(DARWIN)
40 #include <unistd.h>
41 #endif
42
43 #define DATA_LOG_TO_FILE 0
44
45 // Set to 1 to use the temp directory from confstr instead of hardcoded directory.
46 // The last component of DATA_LOG_FILENAME will still be used.
47 #define DATA_LOG_TO_DARWIN_TEMP_DIR 0
48
49 // Uncomment to force logging to the given file regardless of what the environment variable says.
50 // Note that we will append ".<pid>.txt" where <pid> is the PID.
51 // This path won't work on Windows, make sure to change to something like C:\\Users\\<more path>\\log.txt.
52 #define DATA_LOG_FILENAME "/tmp/WTFLog"
53
54 namespace WTF {
55
56 static const size_t maxPathLength = 1024;
57
58 static PrintStream* s_file;
59 static uint64_t s_fileData[(sizeof(FilePrintStream) + 7) / 8];
60 static uint64_t s_lockedFileData[(sizeof(LockedPrintStream) + 7) / 8];
61
62 static void initializeLogFileOnce()
63 {
64     const char* filename = nullptr;
65
66     if (s_file)
67         return;
68
69 #if DATA_LOG_TO_FILE
70 #if DATA_LOG_TO_DARWIN_TEMP_DIR
71     char filenameBuffer[maxPathLength + 1];
72 #if defined(DATA_LOG_FILENAME)
73     const char* logBasename = strrchr(DATA_LOG_FILENAME, '/');
74     if (!logBasename)
75         logBasename = (char*)DATA_LOG_FILENAME;
76 #else
77     const char* logBasename = "WTFLog";
78 #endif
79
80     bool success = confstr(_CS_DARWIN_USER_TEMP_DIR, filenameBuffer, sizeof(filenameBuffer));
81     if (success) {
82         // FIXME: Assert that the path ends with a slash instead of adding a slash if it does not exist
83         // once <rdar://problem/23579077> is fixed in all iOS Simulator versions that we use.
84         size_t lastComponentLength = strlen(logBasename) + 20; // More than enough for ".<pid>.txt"
85         size_t dirnameLength = strlen(filenameBuffer);
86         bool shouldAddPathSeparator = filenameBuffer[dirnameLength - 1] != '/' && logBasename[0] != '/';
87         if (lastComponentLength + shouldAddPathSeparator <= sizeof(filenameBuffer) - dirnameLength - 1) {
88             if (shouldAddPathSeparator)
89                 strncat(filenameBuffer, "/", 1);
90             strncat(filenameBuffer, logBasename, sizeof(filenameBuffer) - strlen(filenameBuffer) - 1);
91             filename = filenameBuffer;
92         }
93     }
94 #elif defined(DATA_LOG_FILENAME)
95     filename = DATA_LOG_FILENAME;
96 #else
97     filename = Environment::getRaw("WTF_DATA_LOG_FILENAME");
98 #endif
99     char actualFilename[maxPathLength + 1];
100
101     if (filename && !strstr(filename, "%pid")) {
102         snprintf(actualFilename, sizeof(actualFilename), "%s.%%pid.txt", filename);
103         filename = actualFilename;
104     }
105 #endif // DATA_LOG_TO_FILE
106
107     setDataFile(filename);
108 }
109
110 static void initializeLogFile()
111 {
112     static std::once_flag once;
113     std::call_once(
114         once,
115         [] {
116             initializeLogFileOnce();
117         });
118 }
119
120 void setDataFile(const char* path)
121 {
122     FilePrintStream* file = nullptr;
123     char formattedPath[maxPathLength + 1];
124     const char* pathToOpen = path;
125
126     if (path) {
127         const char* pidFormat = strstr(path, "%pid");
128         if (pidFormat) {
129             size_t leadingPathLength = pidFormat - path;
130             size_t pathCharactersAvailable = std::min(maxPathLength, leadingPathLength);
131             strncpy(formattedPath, path, pathCharactersAvailable);
132             char* nextDest = formattedPath + pathCharactersAvailable;
133             pathCharactersAvailable = maxPathLength - pathCharactersAvailable;
134             if (pathCharactersAvailable) {
135                 int pidTextLength = snprintf(nextDest, pathCharactersAvailable, "%d", getCurrentProcessID());
136
137                 if (pidTextLength < 0 || static_cast<size_t>(pidTextLength) >= pathCharactersAvailable)
138                     pathCharactersAvailable = 0;
139                 else {
140                     pathCharactersAvailable -= static_cast<size_t>(pidTextLength);
141                     nextDest += pidTextLength;
142                     strncpy(nextDest, pidFormat + 4, pathCharactersAvailable);
143                 }
144             }
145             formattedPath[maxPathLength] = '\0';
146             pathToOpen = formattedPath;
147         }
148
149         file = FilePrintStream::open(pathToOpen, "w").release();
150         if (file)
151             WTFLogAlways("*** DataLog output to \"%s\" ***\n", pathToOpen);
152         else
153             WTFLogAlways("Warning: Could not open DataLog file %s for writing.\n", pathToOpen);
154     }
155
156     if (!file) {
157         // Use placement new; this makes it easier to use dataLog() to debug
158         // fastMalloc.
159         file = new (s_fileData) FilePrintStream(stderr, FilePrintStream::Borrow);
160     }
161
162     setvbuf(file->file(), 0, _IONBF, 0); // Prefer unbuffered output, so that we get a full log upon crash or deadlock.
163
164     if (s_file)
165         s_file->flush();
166
167     s_file = new (s_lockedFileData) LockedPrintStream(std::unique_ptr<FilePrintStream>(file));
168 }
169
170 PrintStream& dataFile()
171 {
172     initializeLogFile();
173     return *s_file;
174 }
175
176 void dataLogFV(const char* format, va_list argList)
177 {
178     dataFile().vprintf(format, argList);
179 }
180
181 void dataLogF(const char* format, ...)
182 {
183     va_list argList;
184     va_start(argList, format);
185     dataLogFV(format, argList);
186     va_end(argList);
187 }
188
189 void dataLogFString(const char* str)
190 {
191     dataFile().printf("%s", str);
192 }
193
194 } // namespace WTF
195