blob: eba177ed50d759278839a145564340f522535764 [file] [log] [blame]
initial.commitd7cae122008-07-26 21:49:381// Copyright 2008, Google Inc.
2// 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 are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include <ctime>
31#include <iomanip>
32#include <cstring>
33#include <windows.h>
34#include <algorithm>
35#include "base/base_switches.h"
36#include "base/command_line.h"
37#include "base/lock_impl.h"
38#include "base/logging.h"
39
40namespace logging {
41
42bool g_enable_dcheck = false;
43
44const char* const log_severity_names[LOG_NUM_SEVERITIES] = {
45 "INFO", "WARNING", "ERROR", "FATAL" };
46
47int min_log_level = 0;
48LogLockingState lock_log_file = LOCK_LOG_FILE;
49LoggingDestination logging_destination = LOG_ONLY_TO_FILE;
50
51const int kMaxFilteredLogLevel = LOG_WARNING;
52char* log_filter_prefix = NULL;
53
54// which log file to use? This is initialized by InitLogging or
55// will be lazily initialized to the default value when it is
56// first needed.
57wchar_t log_file_name[MAX_PATH] = { 0 };
58
59// this file is lazily opened and the handle may be NULL
60HANDLE log_file = NULL;
61
62// what should be prepended to each message?
63bool log_process_id = false;
64bool log_thread_id = false;
65bool log_timestamp = true;
66bool log_tickcount = false;
67
68// An assert handler override specified by the client to be called instead of
69// the debug message dialog.
70LogAssertHandlerFunction log_assert_handler = NULL;
71
72// The lock is used if log file locking is false. It helps us avoid problems
73// with multiple threads writing to the log file at the same time. Use
74// LockImpl directly instead of using Lock, because Lock makes logging calls.
75static LockImpl* log_lock = NULL;
76
77// When we don't use a lock, we are using a global mutex. We need to do this
78// because LockFileEx is not thread safe.
79HANDLE log_mutex = NULL;
80
81// Called by logging functions to ensure that debug_file is initialized
82// and can be used for writing. Returns false if the file could not be
83// initialized. debug_file will be NULL in this case.
84bool InitializeLogFileHandle() {
85 if (log_file)
86 return true;
87
88 if (!log_file_name[0]) {
89 // nobody has called InitLogging to specify a debug log file, so here we
90 // initialize the log file name to the default
91 GetModuleFileName(NULL, log_file_name, MAX_PATH);
92 wchar_t* last_backslash = wcsrchr(log_file_name, '\\');
93 if (last_backslash)
94 last_backslash[1] = 0; // name now ends with the backslash
95 wcscat_s(log_file_name, L"debug.log");
96 }
97
98 log_file = CreateFile(log_file_name, GENERIC_WRITE,
99 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
100 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
101 if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) {
102 // try the current directory
103 log_file = CreateFile(L".\\debug.log", GENERIC_WRITE,
104 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
105 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
106 if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) {
107 log_file = NULL;
108 return false;
109 }
110 }
111 SetFilePointer(log_file, 0, 0, FILE_END);
112 return true;
113}
114
115void InitLogMutex() {
116 if (!log_mutex) {
117 // \ is not a legal character in mutex names so we replace \ with /
118 std::wstring safe_name(log_file_name);
119 std::replace(safe_name.begin(), safe_name.end(), '\\', '/');
120 std::wstring t(L"Global\\");
121 t.append(safe_name);
122 log_mutex = ::CreateMutex(NULL, FALSE, t.c_str());
123 }
124}
125
126void InitLogging(const wchar_t* new_log_file, LoggingDestination logging_dest,
127 LogLockingState lock_log, OldFileDeletionState delete_old) {
128 g_enable_dcheck = CommandLine().HasSwitch(switches::kEnableDCHECK);
129
130 if (log_file) {
131 // calling InitLogging twice or after some log call has already opened the
132 // default log file will re-initialize to the new options
133 CloseHandle(log_file);
134 log_file = NULL;
135 }
136
137 lock_log_file = lock_log;
138 logging_destination = logging_dest;
139
140 // ignore file options if logging is disabled or only to system
141 if (logging_destination == LOG_NONE ||
142 logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG)
143 return;
144
145 wcscpy_s(log_file_name, MAX_PATH, new_log_file);
146 if (delete_old == DELETE_OLD_LOG_FILE)
147 DeleteFile(log_file_name);
148
149 if (lock_log_file == LOCK_LOG_FILE) {
150 InitLogMutex();
151 } else if (!log_lock) {
152 log_lock = new LockImpl();
153 }
154
155 InitializeLogFileHandle();
156}
157
158void SetMinLogLevel(int level) {
159 min_log_level = level;
160}
161
162int GetMinLogLevel() {
163 return min_log_level;
164}
165
166void SetLogFilterPrefix(const char* filter) {
167 if (log_filter_prefix) {
168 delete[] log_filter_prefix;
169 log_filter_prefix = NULL;
170 }
171
172 if (filter) {
173 size_t size = strlen(filter)+1;
174 log_filter_prefix = new char[size];
175 strcpy_s(log_filter_prefix, size, filter);
176 }
177}
178
179void SetLogItems(bool enable_process_id, bool enable_thread_id,
180 bool enable_timestamp, bool enable_tickcount) {
181 log_process_id = enable_process_id;
182 log_thread_id = enable_thread_id;
183 log_timestamp = enable_timestamp;
184 log_tickcount = enable_tickcount;
185}
186
187void SetLogAssertHandler(LogAssertHandlerFunction handler) {
188 log_assert_handler = handler;
189}
190
191// Displays a message box to the user with the error message in it. For
192// Windows programs, it's possible that the message loop is messed up on
193// a fatal error, and creating a MessageBox will cause that message loop
194// to be run. Instead, we try to spawn another process that displays its
195// command line. We look for "Debug Message.exe" in the same directory as
196// the application. If it exists, we use it, otherwise, we use a regular
197// message box.
198void DisplayDebugMessage(const std::string& str) {
199 if (str.empty())
200 return;
201
202 // look for the debug dialog program next to our application
203 wchar_t prog_name[MAX_PATH];
204 GetModuleFileNameW(NULL, prog_name, MAX_PATH);
205 wchar_t* backslash = wcsrchr(prog_name, '\\');
206 if (backslash)
207 backslash[1] = 0;
208 wcscat_s(prog_name, MAX_PATH, L"debug_message.exe");
209
210 // stupid CreateProcess requires a non-const command line and may modify it.
211 // We also want to use the wide string
212 int charcount = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
213 if (!charcount)
214 return;
215 scoped_array<wchar_t> cmdline(new wchar_t[charcount]);
216 if (!MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, cmdline.get(), charcount))
217 return;
218
219 STARTUPINFO startup_info;
220 memset(&startup_info, 0, sizeof(startup_info));
221 startup_info.cb = sizeof(startup_info);
222
223 PROCESS_INFORMATION process_info;
224 if (CreateProcessW(prog_name, cmdline.get(), NULL, NULL, false, 0, NULL,
225 NULL, &startup_info, &process_info)) {
226 WaitForSingleObject(process_info.hProcess, INFINITE);
227 CloseHandle(process_info.hThread);
228 CloseHandle(process_info.hProcess);
229 } else {
230 // debug process broken, let's just do a message box
231 MessageBoxW(NULL, cmdline.get(), L"Fatal error",
232 MB_OK | MB_ICONHAND | MB_TOPMOST);
233 }
234}
235
236LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
237 int ctr)
238 : severity_(severity) {
239 Init(file, line);
240}
241
242LogMessage::LogMessage(const char* file, int line, const CheckOpString& result)
243 : severity_(LOG_FATAL) {
244 Init(file, line);
245 stream_ << "Check failed: " << (*result.str_);
246}
247
248LogMessage::LogMessage(const char* file, int line)
249 : severity_(LOG_INFO) {
250 Init(file, line);
251}
252
253LogMessage::LogMessage(const char* file, int line, LogSeverity severity)
254 : severity_(severity) {
255 Init(file, line);
256}
257
258// writes the common header info to the stream
259void LogMessage::Init(const char* file, int line) {
260 // log only the filename
261 const char* last_slash = strrchr(file, '\\');
262 if (last_slash)
263 file = last_slash + 1;
264
265 // TODO(darin): It might be nice if the columns were fixed width.
266
267 stream_ << '[';
268 if (log_process_id)
269 stream_ << GetCurrentProcessId() << ':';
270 if (log_thread_id)
271 stream_ << GetCurrentThreadId() << ':';
272 if (log_timestamp) {
273 time_t t = time(NULL);
274#if _MSC_VER >= 1400
275 struct tm local_time = {0};
276 localtime_s(&local_time, &t);
277 struct tm* tm_time = &local_time;
278#else
279 struct tm* tm_time = localtime(&t);
280#endif
281 stream_ << std::setfill('0')
282 << std::setw(2) << 1 + tm_time->tm_mon
283 << std::setw(2) << tm_time->tm_mday
284 << '/'
285 << std::setw(2) << tm_time->tm_hour
286 << std::setw(2) << tm_time->tm_min
287 << std::setw(2) << tm_time->tm_sec
288 << ':';
289 }
290 if (log_tickcount)
291 stream_ << GetTickCount() << ':';
292 stream_ << log_severity_names[severity_] << ":" << file << "(" << line << ")] ";
293
294 message_start_ = stream_.tellp();
295}
296
297LogMessage::~LogMessage() {
298 // TODO(brettw) modify the macros so that nothing is executed when the log
299 // level is too high.
300 if (severity_ < min_log_level)
301 return;
302
303 std::string str_newline(stream_.str());
304 str_newline.append("\r\n");
305
306 if (log_filter_prefix && severity_ <= kMaxFilteredLogLevel &&
307 str_newline.compare(message_start_, strlen(log_filter_prefix),
308 log_filter_prefix) != 0) {
309 return;
310 }
311
312 if (logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG ||
313 logging_destination == LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG)
314 OutputDebugStringA(str_newline.c_str());
315
316 // write to log file
317 if (logging_destination != LOG_NONE &&
318 logging_destination != LOG_ONLY_TO_SYSTEM_DEBUG_LOG &&
319 InitializeLogFileHandle()) {
320 // we can have multiple threads and/or processes, so try to prevent them from
321 // clobbering each other's writes
322 if (lock_log_file == LOCK_LOG_FILE) {
323 // Ensure that the mutex is initialized in case the client app did not
324 // call InitLogging. This is not thread safe. See below
325 InitLogMutex();
326
327 DWORD r = ::WaitForSingleObject(log_mutex, INFINITE);
328 DCHECK(r != WAIT_ABANDONED);
329 } else {
330 // use the lock
331 if (!log_lock) {
332 // The client app did not call InitLogging, and so the lock has not
333 // been created. We do this on demand, but if two threads try to do
334 // this at the same time, there will be a race condition to create
335 // the lock. This is why InitLogging should be called from the main
336 // thread at the beginning of execution.
337 log_lock = new LockImpl();
338 }
339 log_lock->Lock();
340 }
341
342 SetFilePointer(log_file, 0, 0, SEEK_END);
343 DWORD num_written;
344 WriteFile(log_file, (void*)str_newline.c_str(), (DWORD)str_newline.length(), &num_written, NULL);
345
346 if (lock_log_file == LOCK_LOG_FILE) {
347 ReleaseMutex(log_mutex);
348 } else {
349 log_lock->Unlock();
350 }
351 }
352
353 if (severity_ == LOG_FATAL) {
354 // display a message or break into the debugger on a fatal error
355 if (::IsDebuggerPresent()) {
356 __debugbreak();
357 } else {
358 if (log_assert_handler) {
359 // make a copy of the string for the handler out of paranoia
360 log_assert_handler(std::string(stream_.str()));
361 } else {
362 // don't use the string with the newline, get a fresh version to send to
363 // the debug message process
364 DisplayDebugMessage(stream_.str());
365 // Crash the process to generate a dump.
366 __debugbreak();
367 }
368 }
369 }
370}
371
372void CloseLogFile() {
373 if (!log_file)
374 return;
375
376 CloseHandle(log_file);
377 log_file = NULL;
378}
379
380} // namespace logging
381
382std::ostream& operator<<(std::ostream& out, const wchar_t* wstr) {
383 if (!wstr || !wstr[0])
384 return out;
385
386 // compute the length of the buffer we'll need
387 int charcount = WideCharToMultiByte(CP_UTF8, 0, wstr, -1,
388 NULL, 0, NULL, NULL);
389 if (charcount == 0)
390 return out;
391
392 // convert
393 scoped_array<char> buf(new char[charcount]);
394 WideCharToMultiByte(CP_UTF8, 0, wstr, -1, buf.get(), charcount, NULL, NULL);
395 return out << buf.get();
396}