blob: ca9905d9405497739364a60284d9655b1e45e00a [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>
[email protected]dd88bf22008-08-06 17:02:3433#include <windows.h>
initial.commitd7cae122008-07-26 21:49:3834#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"
[email protected]15af80e2008-08-07 03:11:4239#include "base/sys_string_conversions.h"
initial.commitd7cae122008-07-26 21:49:3840
41namespace logging {
42
43bool g_enable_dcheck = false;
44
45const char* const log_severity_names[LOG_NUM_SEVERITIES] = {
46 "INFO", "WARNING", "ERROR", "FATAL" };
47
48int min_log_level = 0;
49LogLockingState lock_log_file = LOCK_LOG_FILE;
50LoggingDestination logging_destination = LOG_ONLY_TO_FILE;
51
52const int kMaxFilteredLogLevel = LOG_WARNING;
53char* log_filter_prefix = NULL;
54
55// which log file to use? This is initialized by InitLogging or
56// will be lazily initialized to the default value when it is
57// first needed.
[email protected]dd88bf22008-08-06 17:02:3458wchar_t log_file_name[MAX_PATH] = { 0 };
initial.commitd7cae122008-07-26 21:49:3859
60// this file is lazily opened and the handle may be NULL
[email protected]dd88bf22008-08-06 17:02:3461HANDLE log_file = NULL;
initial.commitd7cae122008-07-26 21:49:3862
63// what should be prepended to each message?
64bool log_process_id = false;
65bool log_thread_id = false;
66bool log_timestamp = true;
67bool log_tickcount = false;
68
69// An assert handler override specified by the client to be called instead of
70// the debug message dialog.
71LogAssertHandlerFunction log_assert_handler = NULL;
72
73// The lock is used if log file locking is false. It helps us avoid problems
74// with multiple threads writing to the log file at the same time. Use
75// LockImpl directly instead of using Lock, because Lock makes logging calls.
76static LockImpl* log_lock = NULL;
77
78// When we don't use a lock, we are using a global mutex. We need to do this
79// because LockFileEx is not thread safe.
[email protected]dd88bf22008-08-06 17:02:3480HANDLE log_mutex = NULL;
initial.commitd7cae122008-07-26 21:49:3881
82// Called by logging functions to ensure that debug_file is initialized
83// and can be used for writing. Returns false if the file could not be
84// initialized. debug_file will be NULL in this case.
85bool InitializeLogFileHandle() {
86 if (log_file)
87 return true;
88
89 if (!log_file_name[0]) {
90 // nobody has called InitLogging to specify a debug log file, so here we
91 // initialize the log file name to the default
92 GetModuleFileName(NULL, log_file_name, MAX_PATH);
93 wchar_t* last_backslash = wcsrchr(log_file_name, '\\');
94 if (last_backslash)
[email protected]dd88bf22008-08-06 17:02:3495 last_backslash[1] = 0; // name now ends with the backslash
initial.commitd7cae122008-07-26 21:49:3896 wcscat_s(log_file_name, L"debug.log");
97 }
98
99 log_file = CreateFile(log_file_name, GENERIC_WRITE,
100 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
101 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
102 if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) {
103 // try the current directory
104 log_file = CreateFile(L".\\debug.log", GENERIC_WRITE,
105 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
106 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
107 if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) {
108 log_file = NULL;
109 return false;
110 }
111 }
112 SetFilePointer(log_file, 0, 0, FILE_END);
113 return true;
114}
115
116void InitLogMutex() {
117 if (!log_mutex) {
118 // \ is not a legal character in mutex names so we replace \ with /
119 std::wstring safe_name(log_file_name);
120 std::replace(safe_name.begin(), safe_name.end(), '\\', '/');
121 std::wstring t(L"Global\\");
122 t.append(safe_name);
123 log_mutex = ::CreateMutex(NULL, FALSE, t.c_str());
124 }
125}
126
[email protected]dd88bf22008-08-06 17:02:34127void InitLogging(const wchar_t* new_log_file, LoggingDestination logging_dest,
initial.commitd7cae122008-07-26 21:49:38128 LogLockingState lock_log, OldFileDeletionState delete_old) {
129 g_enable_dcheck = CommandLine().HasSwitch(switches::kEnableDCHECK);
130
131 if (log_file) {
132 // calling InitLogging twice or after some log call has already opened the
133 // default log file will re-initialize to the new options
[email protected]dd88bf22008-08-06 17:02:34134 CloseHandle(log_file);
initial.commitd7cae122008-07-26 21:49:38135 log_file = NULL;
136 }
137
138 lock_log_file = lock_log;
139 logging_destination = logging_dest;
140
141 // ignore file options if logging is disabled or only to system
142 if (logging_destination == LOG_NONE ||
143 logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG)
144 return;
145
146 wcscpy_s(log_file_name, MAX_PATH, new_log_file);
147 if (delete_old == DELETE_OLD_LOG_FILE)
[email protected]dd88bf22008-08-06 17:02:34148 DeleteFile(log_file_name);
initial.commitd7cae122008-07-26 21:49:38149
150 if (lock_log_file == LOCK_LOG_FILE) {
151 InitLogMutex();
152 } else if (!log_lock) {
153 log_lock = new LockImpl();
154 }
155
156 InitializeLogFileHandle();
157}
158
159void SetMinLogLevel(int level) {
160 min_log_level = level;
161}
162
163int GetMinLogLevel() {
164 return min_log_level;
165}
166
167void SetLogFilterPrefix(const char* filter) {
168 if (log_filter_prefix) {
169 delete[] log_filter_prefix;
170 log_filter_prefix = NULL;
171 }
172
173 if (filter) {
174 size_t size = strlen(filter)+1;
175 log_filter_prefix = new char[size];
176 strcpy_s(log_filter_prefix, size, filter);
177 }
178}
179
180void SetLogItems(bool enable_process_id, bool enable_thread_id,
181 bool enable_timestamp, bool enable_tickcount) {
182 log_process_id = enable_process_id;
183 log_thread_id = enable_thread_id;
184 log_timestamp = enable_timestamp;
185 log_tickcount = enable_tickcount;
186}
187
188void SetLogAssertHandler(LogAssertHandlerFunction handler) {
189 log_assert_handler = handler;
190}
191
192// Displays a message box to the user with the error message in it. For
193// Windows programs, it's possible that the message loop is messed up on
194// a fatal error, and creating a MessageBox will cause that message loop
195// to be run. Instead, we try to spawn another process that displays its
196// command line. We look for "Debug Message.exe" in the same directory as
197// the application. If it exists, we use it, otherwise, we use a regular
198// message box.
199void DisplayDebugMessage(const std::string& str) {
200 if (str.empty())
201 return;
202
203 // look for the debug dialog program next to our application
204 wchar_t prog_name[MAX_PATH];
205 GetModuleFileNameW(NULL, prog_name, MAX_PATH);
206 wchar_t* backslash = wcsrchr(prog_name, '\\');
207 if (backslash)
208 backslash[1] = 0;
209 wcscat_s(prog_name, MAX_PATH, L"debug_message.exe");
210
[email protected]15af80e2008-08-07 03:11:42211 // Stupid CreateProcess requires a non-const command line and may modify it.
212 // We also want to use the wide string.
213 std::wstring cmdline_string = base::SysUTF8ToWide(str);
214 wchar_t* cmdline = const_cast<wchar_t*>(cmdline_string.c_str());
initial.commitd7cae122008-07-26 21:49:38215
216 STARTUPINFO startup_info;
217 memset(&startup_info, 0, sizeof(startup_info));
218 startup_info.cb = sizeof(startup_info);
219
220 PROCESS_INFORMATION process_info;
[email protected]15af80e2008-08-07 03:11:42221 if (CreateProcessW(prog_name, cmdline, NULL, NULL, false, 0, NULL,
initial.commitd7cae122008-07-26 21:49:38222 NULL, &startup_info, &process_info)) {
223 WaitForSingleObject(process_info.hProcess, INFINITE);
224 CloseHandle(process_info.hThread);
225 CloseHandle(process_info.hProcess);
226 } else {
227 // debug process broken, let's just do a message box
[email protected]15af80e2008-08-07 03:11:42228 MessageBoxW(NULL, cmdline, L"Fatal error",
initial.commitd7cae122008-07-26 21:49:38229 MB_OK | MB_ICONHAND | MB_TOPMOST);
230 }
231}
232
233LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
234 int ctr)
235 : severity_(severity) {
236 Init(file, line);
237}
238
239LogMessage::LogMessage(const char* file, int line, const CheckOpString& result)
240 : severity_(LOG_FATAL) {
241 Init(file, line);
242 stream_ << "Check failed: " << (*result.str_);
243}
244
245LogMessage::LogMessage(const char* file, int line)
246 : severity_(LOG_INFO) {
247 Init(file, line);
248}
249
250LogMessage::LogMessage(const char* file, int line, LogSeverity severity)
251 : severity_(severity) {
252 Init(file, line);
253}
254
255// writes the common header info to the stream
256void LogMessage::Init(const char* file, int line) {
257 // log only the filename
258 const char* last_slash = strrchr(file, '\\');
259 if (last_slash)
260 file = last_slash + 1;
261
262 // TODO(darin): It might be nice if the columns were fixed width.
263
264 stream_ << '[';
265 if (log_process_id)
[email protected]dd88bf22008-08-06 17:02:34266 stream_ << GetCurrentProcessId() << ':';
initial.commitd7cae122008-07-26 21:49:38267 if (log_thread_id)
[email protected]dd88bf22008-08-06 17:02:34268 stream_ << GetCurrentThreadId() << ':';
initial.commitd7cae122008-07-26 21:49:38269 if (log_timestamp) {
270 time_t t = time(NULL);
271#if _MSC_VER >= 1400
272 struct tm local_time = {0};
273 localtime_s(&local_time, &t);
274 struct tm* tm_time = &local_time;
275#else
276 struct tm* tm_time = localtime(&t);
277#endif
278 stream_ << std::setfill('0')
279 << std::setw(2) << 1 + tm_time->tm_mon
280 << std::setw(2) << tm_time->tm_mday
281 << '/'
282 << std::setw(2) << tm_time->tm_hour
283 << std::setw(2) << tm_time->tm_min
284 << std::setw(2) << tm_time->tm_sec
285 << ':';
286 }
287 if (log_tickcount)
[email protected]dd88bf22008-08-06 17:02:34288 stream_ << GetTickCount() << ':';
initial.commitd7cae122008-07-26 21:49:38289 stream_ << log_severity_names[severity_] << ":" << file << "(" << line << ")] ";
290
291 message_start_ = stream_.tellp();
292}
293
294LogMessage::~LogMessage() {
295 // TODO(brettw) modify the macros so that nothing is executed when the log
296 // level is too high.
297 if (severity_ < min_log_level)
298 return;
299
300 std::string str_newline(stream_.str());
301 str_newline.append("\r\n");
302
303 if (log_filter_prefix && severity_ <= kMaxFilteredLogLevel &&
304 str_newline.compare(message_start_, strlen(log_filter_prefix),
305 log_filter_prefix) != 0) {
306 return;
307 }
308
309 if (logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG ||
[email protected]dd88bf22008-08-06 17:02:34310 logging_destination == LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG)
initial.commitd7cae122008-07-26 21:49:38311 OutputDebugStringA(str_newline.c_str());
[email protected]dd88bf22008-08-06 17:02:34312
initial.commitd7cae122008-07-26 21:49:38313 // write to log file
314 if (logging_destination != LOG_NONE &&
315 logging_destination != LOG_ONLY_TO_SYSTEM_DEBUG_LOG &&
316 InitializeLogFileHandle()) {
317 // we can have multiple threads and/or processes, so try to prevent them from
318 // clobbering each other's writes
319 if (lock_log_file == LOCK_LOG_FILE) {
320 // Ensure that the mutex is initialized in case the client app did not
321 // call InitLogging. This is not thread safe. See below
322 InitLogMutex();
323
324 DWORD r = ::WaitForSingleObject(log_mutex, INFINITE);
325 DCHECK(r != WAIT_ABANDONED);
326 } else {
327 // use the lock
328 if (!log_lock) {
329 // The client app did not call InitLogging, and so the lock has not
330 // been created. We do this on demand, but if two threads try to do
331 // this at the same time, there will be a race condition to create
332 // the lock. This is why InitLogging should be called from the main
333 // thread at the beginning of execution.
334 log_lock = new LockImpl();
335 }
336 log_lock->Lock();
337 }
338
339 SetFilePointer(log_file, 0, 0, SEEK_END);
340 DWORD num_written;
341 WriteFile(log_file, (void*)str_newline.c_str(), (DWORD)str_newline.length(), &num_written, NULL);
342
343 if (lock_log_file == LOCK_LOG_FILE) {
344 ReleaseMutex(log_mutex);
345 } else {
346 log_lock->Unlock();
347 }
348 }
349
350 if (severity_ == LOG_FATAL) {
351 // display a message or break into the debugger on a fatal error
352 if (::IsDebuggerPresent()) {
353 __debugbreak();
[email protected]dd88bf22008-08-06 17:02:34354 } else {
initial.commitd7cae122008-07-26 21:49:38355 if (log_assert_handler) {
356 // make a copy of the string for the handler out of paranoia
357 log_assert_handler(std::string(stream_.str()));
358 } else {
359 // don't use the string with the newline, get a fresh version to send to
360 // the debug message process
361 DisplayDebugMessage(stream_.str());
362 // Crash the process to generate a dump.
363 __debugbreak();
364 }
365 }
366 }
367}
368
369void CloseLogFile() {
370 if (!log_file)
371 return;
372
[email protected]dd88bf22008-08-06 17:02:34373 CloseHandle(log_file);
initial.commitd7cae122008-07-26 21:49:38374 log_file = NULL;
375}
376
377} // namespace logging
378
379std::ostream& operator<<(std::ostream& out, const wchar_t* wstr) {
[email protected]15af80e2008-08-07 03:11:42380 return out << base::SysWideToUTF8(std::wstring(wstr));
initial.commitd7cae122008-07-26 21:49:38381}