1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/process_util.h"
15 #include "base/command_line.h"
16 #include "base/debug_util.h"
17 #include "base/histogram.h"
18 #include "base/logging.h"
19 #include "base/scoped_handle_win.h"
20 #include "base/scoped_ptr.h"
22 // userenv.dll is required for CreateEnvironmentBlock().
23 #pragma comment(lib, "userenv.lib")
29 // System pagesize. This value remains constant on x86/64 architectures.
30 const int PAGESIZE_KB
= 4;
32 // HeapSetInformation function pointer.
33 typedef BOOL (WINAPI
* HeapSetFn
)(HANDLE
, HEAP_INFORMATION_CLASS
, PVOID
, SIZE_T
);
35 // Previous unhandled filter. Will be called if not NULL when we intercept an
36 // exception. Only used in unit tests.
37 LPTOP_LEVEL_EXCEPTION_FILTER g_previous_filter
= NULL
;
39 // Prints the exception call stack.
40 // This is the unit tests exception filter.
41 long WINAPI
StackDumpExceptionFilter(EXCEPTION_POINTERS
* info
) {
42 StackTrace(info
).PrintBacktrace();
43 if (g_previous_filter
)
44 return g_previous_filter(info
);
45 return EXCEPTION_CONTINUE_SEARCH
;
48 // Connects back to a console if available.
49 // Only necessary on Windows, no-op on other platforms.
50 void AttachToConsole() {
51 if (!AttachConsole(ATTACH_PARENT_PROCESS
)) {
52 unsigned int result
= GetLastError();
53 // Was probably already attached.
54 if (result
== ERROR_ACCESS_DENIED
)
57 if (result
== ERROR_INVALID_HANDLE
|| result
== ERROR_INVALID_HANDLE
) {
58 // TODO(maruel): Walk up the process chain if deemed necessary.
60 // Continue even if the function call fails.
63 // http://support.microsoft.com/kb/105305
64 int raw_out
= _open_osfhandle(
65 reinterpret_cast<intptr_t>(GetStdHandle(STD_OUTPUT_HANDLE
)), _O_TEXT
);
66 *stdout
= *_fdopen(raw_out
, "w");
67 setvbuf(stdout
, NULL
, _IONBF
, 0);
69 int raw_err
= _open_osfhandle(
70 reinterpret_cast<intptr_t>(GetStdHandle(STD_ERROR_HANDLE
)), _O_TEXT
);
71 *stderr
= *_fdopen(raw_err
, "w");
72 setvbuf(stderr
, NULL
, _IONBF
, 0);
74 int raw_in
= _open_osfhandle(
75 reinterpret_cast<intptr_t>(GetStdHandle(STD_INPUT_HANDLE
)), _O_TEXT
);
76 *stdin
= *_fdopen(raw_in
, "r");
77 setvbuf(stdin
, NULL
, _IONBF
, 0);
78 // Fix all cout, wcout, cin, wcin, cerr, wcerr, clog and wclog.
79 std::ios::sync_with_stdio();
84 ProcessId
GetCurrentProcId() {
85 return ::GetCurrentProcessId();
88 ProcessHandle
GetCurrentProcessHandle() {
89 return ::GetCurrentProcess();
92 bool OpenProcessHandle(ProcessId pid
, ProcessHandle
* handle
) {
93 // We try to limit privileges granted to the handle. If you need this
94 // for test code, consider using OpenPrivilegedProcessHandle instead of
95 // adding more privileges here.
96 ProcessHandle result
= OpenProcess(PROCESS_DUP_HANDLE
| PROCESS_TERMINATE
,
99 if (result
== INVALID_HANDLE_VALUE
)
106 bool OpenPrivilegedProcessHandle(ProcessId pid
, ProcessHandle
* handle
) {
107 ProcessHandle result
= OpenProcess(PROCESS_DUP_HANDLE
|
109 PROCESS_QUERY_INFORMATION
|
114 if (result
== INVALID_HANDLE_VALUE
)
121 void CloseProcessHandle(ProcessHandle process
) {
122 CloseHandle(process
);
125 ProcessId
GetProcId(ProcessHandle process
) {
126 // Get a handle to |process| that has PROCESS_QUERY_INFORMATION rights.
127 HANDLE current_process
= GetCurrentProcess();
128 HANDLE process_with_query_rights
;
129 if (DuplicateHandle(current_process
, process
, current_process
,
130 &process_with_query_rights
, PROCESS_QUERY_INFORMATION
,
132 DWORD id
= GetProcessId(process_with_query_rights
);
133 CloseHandle(process_with_query_rights
);
142 bool LaunchApp(const std::wstring
& cmdline
,
143 bool wait
, bool start_hidden
, ProcessHandle
* process_handle
) {
144 STARTUPINFO startup_info
= {0};
145 startup_info
.cb
= sizeof(startup_info
);
146 startup_info
.dwFlags
= STARTF_USESHOWWINDOW
;
147 startup_info
.wShowWindow
= start_hidden
? SW_HIDE
: SW_SHOW
;
148 PROCESS_INFORMATION process_info
;
149 if (!CreateProcess(NULL
,
150 const_cast<wchar_t*>(cmdline
.c_str()), NULL
, NULL
,
151 FALSE
, 0, NULL
, NULL
,
152 &startup_info
, &process_info
))
155 // Handles must be closed or they will leak
156 CloseHandle(process_info
.hThread
);
159 WaitForSingleObject(process_info
.hProcess
, INFINITE
);
161 // If the caller wants the process handle, we won't close it.
162 if (process_handle
) {
163 *process_handle
= process_info
.hProcess
;
165 CloseHandle(process_info
.hProcess
);
170 bool LaunchAppAsUser(UserTokenHandle token
, const std::wstring
& cmdline
,
171 bool start_hidden
, ProcessHandle
* process_handle
) {
172 return LaunchAppAsUser(token
, cmdline
, start_hidden
, process_handle
, false);
175 bool LaunchAppAsUser(UserTokenHandle token
, const std::wstring
& cmdline
,
176 bool start_hidden
, ProcessHandle
* process_handle
,
177 bool empty_desktop_name
) {
178 STARTUPINFO startup_info
= {0};
179 startup_info
.cb
= sizeof(startup_info
);
180 if (empty_desktop_name
)
181 startup_info
.lpDesktop
= L
"";
182 PROCESS_INFORMATION process_info
;
184 startup_info
.dwFlags
= STARTF_USESHOWWINDOW
;
185 startup_info
.wShowWindow
= SW_HIDE
;
187 DWORD flags
= CREATE_UNICODE_ENVIRONMENT
;
188 void* enviroment_block
= NULL
;
190 if (!CreateEnvironmentBlock(&enviroment_block
, token
, FALSE
))
194 CreateProcessAsUser(token
, NULL
, const_cast<wchar_t*>(cmdline
.c_str()),
195 NULL
, NULL
, FALSE
, flags
, enviroment_block
,
196 NULL
, &startup_info
, &process_info
);
198 DestroyEnvironmentBlock(enviroment_block
);
203 CloseHandle(process_info
.hThread
);
205 if (process_handle
) {
206 *process_handle
= process_info
.hProcess
;
208 CloseHandle(process_info
.hProcess
);
213 bool LaunchApp(const CommandLine
& cl
,
214 bool wait
, bool start_hidden
, ProcessHandle
* process_handle
) {
215 return LaunchApp(cl
.command_line_string(), wait
,
216 start_hidden
, process_handle
);
219 // Attempts to kill the process identified by the given process
220 // entry structure, giving it the specified exit code.
221 // Returns true if this is successful, false otherwise.
222 bool KillProcessById(ProcessId process_id
, int exit_code
, bool wait
) {
223 HANDLE process
= OpenProcess(PROCESS_TERMINATE
| SYNCHRONIZE
,
224 FALSE
, // Don't inherit handle
227 DLOG(ERROR
) << "Unable to open process " << process_id
<< " : "
231 bool ret
= KillProcess(process
, exit_code
, wait
);
232 CloseHandle(process
);
236 bool GetAppOutput(const CommandLine
& cl
, std::string
* output
) {
237 HANDLE out_read
= NULL
;
238 HANDLE out_write
= NULL
;
240 SECURITY_ATTRIBUTES sa_attr
;
241 // Set the bInheritHandle flag so pipe handles are inherited.
242 sa_attr
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
243 sa_attr
.bInheritHandle
= TRUE
;
244 sa_attr
.lpSecurityDescriptor
= NULL
;
246 // Create the pipe for the child process's STDOUT.
247 if (!CreatePipe(&out_read
, &out_write
, &sa_attr
, 0)) {
248 NOTREACHED() << "Failed to create pipe";
252 // Ensure we don't leak the handles.
253 ScopedHandle
scoped_out_read(out_read
);
254 ScopedHandle
scoped_out_write(out_write
);
256 // Ensure the read handle to the pipe for STDOUT is not inherited.
257 if (!SetHandleInformation(out_read
, HANDLE_FLAG_INHERIT
, 0)) {
258 NOTREACHED() << "Failed to disabled pipe inheritance";
262 // Now create the child process
263 PROCESS_INFORMATION proc_info
= { 0 };
264 STARTUPINFO start_info
= { 0 };
266 start_info
.cb
= sizeof(STARTUPINFO
);
267 start_info
.hStdOutput
= out_write
;
268 // Keep the normal stdin and stderr.
269 start_info
.hStdInput
= GetStdHandle(STD_INPUT_HANDLE
);
270 start_info
.hStdError
= GetStdHandle(STD_ERROR_HANDLE
);
271 start_info
.dwFlags
|= STARTF_USESTDHANDLES
;
273 // Create the child process.
274 if (!CreateProcess(NULL
,
275 const_cast<wchar_t*>(cl
.command_line_string().c_str()),
277 TRUE
, // Handles are inherited.
278 0, NULL
, NULL
, &start_info
, &proc_info
)) {
279 NOTREACHED() << "Failed to start process";
283 // We don't need the thread handle, close it now.
284 CloseHandle(proc_info
.hThread
);
286 // Close our writing end of pipe now. Otherwise later read would not be able
287 // to detect end of child's output.
288 scoped_out_write
.Close();
290 // Read output from the child process's pipe for STDOUT
291 const int kBufferSize
= 1024;
292 char buffer
[kBufferSize
];
295 DWORD bytes_read
= 0;
296 BOOL success
= ReadFile(out_read
, buffer
, kBufferSize
, &bytes_read
, NULL
);
297 if (!success
|| bytes_read
== 0)
299 output
->append(buffer
, bytes_read
);
302 // Let's wait for the process to finish.
303 WaitForSingleObject(proc_info
.hProcess
, INFINITE
);
304 CloseHandle(proc_info
.hProcess
);
309 bool KillProcess(ProcessHandle process
, int exit_code
, bool wait
) {
310 bool result
= (TerminateProcess(process
, exit_code
) != FALSE
);
311 if (result
&& wait
) {
312 // The process may not end immediately due to pending I/O
313 if (WAIT_OBJECT_0
!= WaitForSingleObject(process
, 60 * 1000))
314 DLOG(ERROR
) << "Error waiting for process exit: " << GetLastError();
315 } else if (!result
) {
316 DLOG(ERROR
) << "Unable to terminate process: " << GetLastError();
321 bool DidProcessCrash(bool* child_exited
, ProcessHandle handle
) {
324 if (!::GetExitCodeProcess(handle
, &exitcode
)) {
326 // Assume the child has exited.
328 *child_exited
= true;
331 if (exitcode
== STILL_ACTIVE
) {
332 DWORD wait_result
= WaitForSingleObject(handle
, 0);
333 if (wait_result
== WAIT_TIMEOUT
) {
335 *child_exited
= false;
339 DCHECK_EQ(WAIT_OBJECT_0
, wait_result
);
341 // Strange, the process used 0x103 (STILL_ACTIVE) as exit code.
347 // We're sure the child has exited.
349 *child_exited
= true;
351 // Warning, this is not generic code; it heavily depends on the way
352 // the rest of the code kills a process.
354 if (exitcode
== PROCESS_END_NORMAL_TERMINATION
||
355 exitcode
== PROCESS_END_KILLED_BY_USER
||
356 exitcode
== PROCESS_END_PROCESS_WAS_HUNG
||
357 exitcode
== 0xC0000354 || // STATUS_DEBUGGER_INACTIVE.
358 exitcode
== 0xC000013A || // Control-C/end session.
359 exitcode
== 0x40010004) { // Debugger terminated process/end session.
363 // All other exit codes indicate crashes.
367 bool WaitForExitCode(ProcessHandle handle
, int* exit_code
) {
368 bool success
= WaitForExitCodeWithTimeout(handle
, exit_code
, INFINITE
);
370 CloseProcessHandle(handle
);
374 bool WaitForExitCodeWithTimeout(ProcessHandle handle
, int* exit_code
,
375 int64 timeout_milliseconds
) {
376 if (::WaitForSingleObject(handle
, timeout_milliseconds
) != WAIT_OBJECT_0
)
378 DWORD temp_code
; // Don't clobber out-parameters in case of failure.
379 if (!::GetExitCodeProcess(handle
, &temp_code
))
382 // Only close the handle on success, to give the caller a chance to forcefully
383 // terminate the process if he wants to.
384 CloseProcessHandle(handle
);
386 *exit_code
= temp_code
;
390 ProcessIterator::ProcessIterator(const ProcessFilter
* filter
)
391 : started_iteration_(false),
393 snapshot_
= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS
, 0);
396 ProcessIterator::~ProcessIterator() {
397 CloseHandle(snapshot_
);
400 bool ProcessIterator::CheckForNextProcess() {
401 InitProcessEntry(&entry_
);
403 if (!started_iteration_
) {
404 started_iteration_
= true;
405 return !!Process32First(snapshot_
, &entry_
);
408 return !!Process32Next(snapshot_
, &entry_
);
411 void ProcessIterator::InitProcessEntry(ProcessEntry
* entry
) {
412 memset(entry
, 0, sizeof(*entry
));
413 entry
->dwSize
= sizeof(*entry
);
416 bool NamedProcessIterator::IncludeEntry() {
418 return _wcsicmp(executable_name_
.c_str(), entry().exe_file()) == 0 &&
419 ProcessIterator::IncludeEntry();
422 bool WaitForProcessesToExit(const std::wstring
& executable_name
,
423 int64 wait_milliseconds
,
424 const ProcessFilter
* filter
) {
425 const ProcessEntry
* entry
;
427 DWORD start_time
= GetTickCount();
429 NamedProcessIterator
iter(executable_name
, filter
);
430 while (entry
= iter
.NextProcessEntry()) {
431 DWORD remaining_wait
=
432 std::max
<int64
>(0, wait_milliseconds
- (GetTickCount() - start_time
));
433 HANDLE process
= OpenProcess(SYNCHRONIZE
,
435 entry
->th32ProcessID
);
436 DWORD wait_result
= WaitForSingleObject(process
, remaining_wait
);
437 CloseHandle(process
);
438 result
= result
&& (wait_result
== WAIT_OBJECT_0
);
444 bool WaitForSingleProcess(ProcessHandle handle
, int64 wait_milliseconds
) {
445 bool retval
= WaitForSingleObject(handle
, wait_milliseconds
) == WAIT_OBJECT_0
;
449 bool CrashAwareSleep(ProcessHandle handle
, int64 wait_milliseconds
) {
450 bool retval
= WaitForSingleObject(handle
, wait_milliseconds
) == WAIT_TIMEOUT
;
454 bool CleanupProcesses(const std::wstring
& executable_name
,
455 int64 wait_milliseconds
,
457 const ProcessFilter
* filter
) {
458 bool exited_cleanly
= WaitForProcessesToExit(executable_name
,
462 KillProcesses(executable_name
, exit_code
, filter
);
463 return exited_cleanly
;
466 ///////////////////////////////////////////////////////////////////////////////
469 ProcessMetrics::ProcessMetrics(ProcessHandle process
) : process_(process
),
471 last_system_time_(0) {
472 SYSTEM_INFO system_info
;
473 GetSystemInfo(&system_info
);
474 processor_count_
= system_info
.dwNumberOfProcessors
;
478 ProcessMetrics
* ProcessMetrics::CreateProcessMetrics(ProcessHandle process
) {
479 return new ProcessMetrics(process
);
482 ProcessMetrics::~ProcessMetrics() { }
484 size_t ProcessMetrics::GetPagefileUsage() const {
485 PROCESS_MEMORY_COUNTERS pmc
;
486 if (GetProcessMemoryInfo(process_
, &pmc
, sizeof(pmc
))) {
487 return pmc
.PagefileUsage
;
492 // Returns the peak space allocated for the pagefile, in bytes.
493 size_t ProcessMetrics::GetPeakPagefileUsage() const {
494 PROCESS_MEMORY_COUNTERS pmc
;
495 if (GetProcessMemoryInfo(process_
, &pmc
, sizeof(pmc
))) {
496 return pmc
.PeakPagefileUsage
;
501 // Returns the current working set size, in bytes.
502 size_t ProcessMetrics::GetWorkingSetSize() const {
503 PROCESS_MEMORY_COUNTERS pmc
;
504 if (GetProcessMemoryInfo(process_
, &pmc
, sizeof(pmc
))) {
505 return pmc
.WorkingSetSize
;
510 // Returns the peak working set size, in bytes.
511 size_t ProcessMetrics::GetPeakWorkingSetSize() const {
512 PROCESS_MEMORY_COUNTERS pmc
;
513 if (GetProcessMemoryInfo(process_
, &pmc
, sizeof(pmc
))) {
514 return pmc
.PeakWorkingSetSize
;
519 bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes
,
520 size_t* shared_bytes
) {
521 // PROCESS_MEMORY_COUNTERS_EX is not supported until XP SP2.
522 // GetProcessMemoryInfo() will simply fail on prior OS. So the requested
523 // information is simply not available. Hence, we will return 0 on unsupported
524 // OSes. Unlike most Win32 API, we don't need to initialize the "cb" member.
525 PROCESS_MEMORY_COUNTERS_EX pmcx
;
527 GetProcessMemoryInfo(process_
,
528 reinterpret_cast<PROCESS_MEMORY_COUNTERS
*>(&pmcx
),
530 *private_bytes
= pmcx
.PrivateUsage
;
534 WorkingSetKBytes ws_usage
;
535 if (!GetWorkingSetKBytes(&ws_usage
))
538 *shared_bytes
= ws_usage
.shared
* 1024;
544 void ProcessMetrics::GetCommittedKBytes(CommittedKBytes
* usage
) const {
545 MEMORY_BASIC_INFORMATION mbi
= {0};
546 size_t committed_private
= 0;
547 size_t committed_mapped
= 0;
548 size_t committed_image
= 0;
549 void* base_address
= NULL
;
550 while (VirtualQueryEx(process_
, base_address
, &mbi
, sizeof(mbi
)) ==
552 if (mbi
.State
== MEM_COMMIT
) {
553 if (mbi
.Type
== MEM_PRIVATE
) {
554 committed_private
+= mbi
.RegionSize
;
555 } else if (mbi
.Type
== MEM_MAPPED
) {
556 committed_mapped
+= mbi
.RegionSize
;
557 } else if (mbi
.Type
== MEM_IMAGE
) {
558 committed_image
+= mbi
.RegionSize
;
563 void* new_base
= (static_cast<BYTE
*>(mbi
.BaseAddress
)) + mbi
.RegionSize
;
564 // Avoid infinite loop by weird MEMORY_BASIC_INFORMATION.
565 // If we query 64bit processes in a 32bit process, VirtualQueryEx()
566 // returns such data.
567 if (new_base
<= base_address
) {
573 base_address
= new_base
;
575 usage
->image
= committed_image
/ 1024;
576 usage
->mapped
= committed_mapped
/ 1024;
577 usage
->priv
= committed_private
/ 1024;
580 bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes
* ws_usage
) const {
581 size_t ws_private
= 0;
582 size_t ws_shareable
= 0;
583 size_t ws_shared
= 0;
586 memset(ws_usage
, 0, sizeof(*ws_usage
));
588 DWORD number_of_entries
= 4096; // Just a guess.
589 PSAPI_WORKING_SET_INFORMATION
* buffer
= NULL
;
592 DWORD buffer_size
= sizeof(PSAPI_WORKING_SET_INFORMATION
) +
593 (number_of_entries
* sizeof(PSAPI_WORKING_SET_BLOCK
));
595 // if we can't expand the buffer, don't leak the previous
596 // contents or pass a NULL pointer to QueryWorkingSet
597 PSAPI_WORKING_SET_INFORMATION
* new_buffer
=
598 reinterpret_cast<PSAPI_WORKING_SET_INFORMATION
*>(
599 realloc(buffer
, buffer_size
));
606 // Call the function once to get number of items
607 if (QueryWorkingSet(process_
, buffer
, buffer_size
))
610 if (GetLastError() != ERROR_BAD_LENGTH
) {
615 number_of_entries
= static_cast<DWORD
>(buffer
->NumberOfEntries
);
617 // Maybe some entries are being added right now. Increase the buffer to
618 // take that into account.
619 number_of_entries
= static_cast<DWORD
>(number_of_entries
* 1.25);
621 if (--retries
== 0) {
622 free(buffer
); // If we're looping, eventually fail.
627 // On windows 2000 the function returns 1 even when the buffer is too small.
628 // The number of entries that we are going to parse is the minimum between the
629 // size we allocated and the real number of entries.
631 std::min(number_of_entries
, static_cast<DWORD
>(buffer
->NumberOfEntries
));
632 for (unsigned int i
= 0; i
< number_of_entries
; i
++) {
633 if (buffer
->WorkingSetInfo
[i
].Shared
) {
635 if (buffer
->WorkingSetInfo
[i
].ShareCount
> 1)
642 ws_usage
->priv
= ws_private
* PAGESIZE_KB
;
643 ws_usage
->shareable
= ws_shareable
* PAGESIZE_KB
;
644 ws_usage
->shared
= ws_shared
* PAGESIZE_KB
;
649 static uint64
FileTimeToUTC(const FILETIME
& ftime
) {
651 li
.LowPart
= ftime
.dwLowDateTime
;
652 li
.HighPart
= ftime
.dwHighDateTime
;
656 double ProcessMetrics::GetCPUUsage() {
658 FILETIME creation_time
;
660 FILETIME kernel_time
;
663 GetSystemTimeAsFileTime(&now
);
665 if (!GetProcessTimes(process_
, &creation_time
, &exit_time
,
666 &kernel_time
, &user_time
)) {
667 // We don't assert here because in some cases (such as in the Task Manager)
668 // we may call this function on a process that has just exited but we have
669 // not yet received the notification.
672 int64 system_time
= (FileTimeToUTC(kernel_time
) + FileTimeToUTC(user_time
)) /
674 int64 time
= FileTimeToUTC(now
);
676 if ((last_system_time_
== 0) || (last_time_
== 0)) {
677 // First call, just set the last values.
678 last_system_time_
= system_time
;
683 int64 system_time_delta
= system_time
- last_system_time_
;
684 int64 time_delta
= time
- last_time_
;
685 DCHECK(time_delta
!= 0);
689 // We add time_delta / 2 so the result is rounded.
690 int cpu
= static_cast<int>((system_time_delta
* 100 + time_delta
/ 2) /
693 last_system_time_
= system_time
;
699 bool ProcessMetrics::GetIOCounters(IoCounters
* io_counters
) const {
700 return GetProcessIoCounters(process_
, io_counters
) != FALSE
;
703 bool ProcessMetrics::CalculateFreeMemory(FreeMBytes
* free
) const {
704 const SIZE_T kTopAdress
= 0x7F000000;
705 const SIZE_T kMegabyte
= 1024 * 1024;
706 SIZE_T accumulated
= 0;
708 MEMORY_BASIC_INFORMATION largest
= {0};
710 while (scan
< kTopAdress
) {
711 MEMORY_BASIC_INFORMATION info
;
712 if (!::VirtualQueryEx(process_
, reinterpret_cast<void*>(scan
),
713 &info
, sizeof(info
)))
715 if (info
.State
== MEM_FREE
) {
716 accumulated
+= info
.RegionSize
;
717 UINT_PTR end
= scan
+ info
.RegionSize
;
718 if (info
.RegionSize
> (largest
.RegionSize
))
721 scan
+= info
.RegionSize
;
723 free
->largest
= largest
.RegionSize
/ kMegabyte
;
724 free
->largest_ptr
= largest
.BaseAddress
;
725 free
->total
= accumulated
/ kMegabyte
;
729 bool EnableLowFragmentationHeap() {
730 HMODULE kernel32
= GetModuleHandle(L
"kernel32.dll");
731 HeapSetFn heap_set
= reinterpret_cast<HeapSetFn
>(GetProcAddress(
733 "HeapSetInformation"));
735 // On Windows 2000, the function is not exported. This is not a reason to
740 unsigned number_heaps
= GetProcessHeaps(0, NULL
);
744 // Gives us some extra space in the array in case a thread is creating heaps
745 // at the same time we're querying them.
746 static const int MARGIN
= 8;
747 scoped_array
<HANDLE
> heaps(new HANDLE
[number_heaps
+ MARGIN
]);
748 number_heaps
= GetProcessHeaps(number_heaps
+ MARGIN
, heaps
.get());
752 for (unsigned i
= 0; i
< number_heaps
; ++i
) {
754 // Don't bother with the result code. It may fails on heaps that have the
755 // HEAP_NO_SERIALIZE flag. This is expected and not a problem at all.
757 HeapCompatibilityInformation
,
764 void EnableTerminationOnHeapCorruption() {
765 // Ignore the result code. Supported on XP SP3 and Vista.
766 HeapSetInformation(NULL
, HeapEnableTerminationOnCorruption
, NULL
, 0);
769 bool EnableInProcessStackDumping() {
770 // Add stack dumping support on exception on windows. Similar to OS_POSIX
771 // signal() handling in process_util_posix.cc.
772 g_previous_filter
= SetUnhandledExceptionFilter(&StackDumpExceptionFilter
);
777 void RaiseProcessToHighPriority() {
778 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS
);
781 // GetPerformanceInfo is not available on WIN2K. So we'll
782 // load it on-the-fly.
783 const wchar_t kPsapiDllName
[] = L
"psapi.dll";
784 typedef BOOL (WINAPI
*GetPerformanceInfoFunction
) (
785 PPERFORMANCE_INFORMATION pPerformanceInformation
,
788 // Beware of races if called concurrently from multiple threads.
789 static BOOL
InternalGetPerformanceInfo(
790 PPERFORMANCE_INFORMATION pPerformanceInformation
, DWORD cb
) {
791 static GetPerformanceInfoFunction GetPerformanceInfo_func
= NULL
;
792 if (!GetPerformanceInfo_func
) {
793 HMODULE psapi_dll
= ::GetModuleHandle(kPsapiDllName
);
795 GetPerformanceInfo_func
= reinterpret_cast<GetPerformanceInfoFunction
>(
796 GetProcAddress(psapi_dll
, "GetPerformanceInfo"));
798 if (!GetPerformanceInfo_func
) {
799 // The function could be loaded!
800 memset(pPerformanceInformation
, 0, cb
);
804 return GetPerformanceInfo_func(pPerformanceInformation
, cb
);
807 size_t GetSystemCommitCharge() {
808 // Get the System Page Size.
809 SYSTEM_INFO system_info
;
810 GetSystemInfo(&system_info
);
812 PERFORMANCE_INFORMATION info
;
813 if (!InternalGetPerformanceInfo(&info
, sizeof(info
))) {
814 LOG(ERROR
) << "Failed to fetch internal performance info.";
817 return (info
.CommitTotal
* system_info
.dwPageSize
) / 1024;