summaryrefslogtreecommitdiffstats
path: root/base/process_util.cc
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 21:49:38 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 21:49:38 +0000
commitd7cae12696b96500c05dd2d430f6238922c20c96 (patch)
treeecff27b367735535b2a66477f8cd89d3c462a6c0 /base/process_util.cc
parentee2815e28d408216cf94e874825b6bcf76c69083 (diff)
downloadchromium_src-d7cae12696b96500c05dd2d430f6238922c20c96.zip
chromium_src-d7cae12696b96500c05dd2d430f6238922c20c96.tar.gz
chromium_src-d7cae12696b96500c05dd2d430f6238922c20c96.tar.bz2
Add base to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@8 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/process_util.cc')
-rw-r--r--base/process_util.cc581
1 files changed, 581 insertions, 0 deletions
diff --git a/base/process_util.cc b/base/process_util.cc
new file mode 100644
index 0000000..09631ac
--- /dev/null
+++ b/base/process_util.cc
@@ -0,0 +1,581 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/process_util.h"
+
+#include <windows.h>
+#include <winternl.h>
+#include <psapi.h>
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+
+namespace {
+
+// System pagesize. This value remains constant on x86/64 architectures.
+const int PAGESIZE_KB = 4;
+
+// HeapSetInformation function pointer.
+typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T);
+
+} // namespace
+
+namespace process_util {
+
+int GetCurrentProcId() {
+ return ::GetCurrentProcessId();
+}
+
+// Helper for GetProcId()
+bool GetProcIdViaGetProcessId(ProcessHandle process, DWORD* id) {
+ // Dynamically get a pointer to GetProcessId().
+ typedef DWORD (WINAPI *GetProcessIdFunction)(HANDLE);
+ static GetProcessIdFunction GetProcessIdPtr = NULL;
+ static bool initialize_get_process_id = true;
+ if (initialize_get_process_id) {
+ initialize_get_process_id = false;
+ HMODULE kernel32_handle = GetModuleHandle(L"kernel32.dll");
+ if (!kernel32_handle) {
+ NOTREACHED();
+ return false;
+ }
+ GetProcessIdPtr = reinterpret_cast<GetProcessIdFunction>(GetProcAddress(
+ kernel32_handle, "GetProcessId"));
+ }
+ if (!GetProcessIdPtr)
+ return false;
+ // Ask for the process ID.
+ *id = (*GetProcessIdPtr)(process);
+ return true;
+}
+
+// Helper for GetProcId()
+bool GetProcIdViaNtQueryInformationProcess(ProcessHandle process, DWORD* id) {
+ // Dynamically get a pointer to NtQueryInformationProcess().
+ typedef NTSTATUS (WINAPI *NtQueryInformationProcessFunction)(
+ HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
+ static NtQueryInformationProcessFunction NtQueryInformationProcessPtr = NULL;
+ static bool initialize_query_information_process = true;
+ if (initialize_query_information_process) {
+ initialize_query_information_process = false;
+ // According to nsylvain, ntdll.dll is guaranteed to be loaded, even though
+ // the Windows docs seem to imply that you should LoadLibrary() it.
+ HMODULE ntdll_handle = GetModuleHandle(L"ntdll.dll");
+ if (!ntdll_handle) {
+ NOTREACHED();
+ return false;
+ }
+ NtQueryInformationProcessPtr =
+ reinterpret_cast<NtQueryInformationProcessFunction>(GetProcAddress(
+ ntdll_handle, "NtQueryInformationProcess"));
+ }
+ if (!NtQueryInformationProcessPtr)
+ return false;
+ // Ask for the process ID.
+ PROCESS_BASIC_INFORMATION info;
+ ULONG bytes_returned;
+ NTSTATUS status = (*NtQueryInformationProcessPtr)(process,
+ ProcessBasicInformation,
+ &info, sizeof info,
+ &bytes_returned);
+ if (!SUCCEEDED(status) || (bytes_returned != (sizeof info)))
+ return false;
+
+ *id = static_cast<DWORD>(info.UniqueProcessId);
+ return true;
+}
+
+int GetProcId(ProcessHandle process) {
+ // Get a handle to |process| that has PROCESS_QUERY_INFORMATION rights.
+ HANDLE current_process = GetCurrentProcess();
+ HANDLE process_with_query_rights;
+ if (DuplicateHandle(current_process, process, current_process,
+ &process_with_query_rights, PROCESS_QUERY_INFORMATION,
+ false, 0)) {
+ // Try to use GetProcessId(), if it exists. Fall back on
+ // NtQueryInformationProcess() otherwise (< Win XP SP1).
+ DWORD id;
+ bool success =
+ GetProcIdViaGetProcessId(process_with_query_rights, &id) ||
+ GetProcIdViaNtQueryInformationProcess(process_with_query_rights, &id);
+ CloseHandle(process_with_query_rights);
+ if (success)
+ return id;
+ }
+
+ // We're screwed.
+ NOTREACHED();
+ return 0;
+}
+
+bool LaunchApp(const std::wstring& cmdline,
+ bool wait, bool start_hidden, ProcessHandle* process_handle) {
+ STARTUPINFO startup_info = {0};
+ startup_info.cb = sizeof(startup_info);
+ if (start_hidden) {
+ startup_info.dwFlags = STARTF_USESHOWWINDOW;
+ startup_info.wShowWindow = SW_HIDE;
+ }
+ PROCESS_INFORMATION process_info;
+ if (!CreateProcess(NULL,
+ const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL,
+ FALSE, 0, NULL, NULL,
+ &startup_info, &process_info))
+ return false;
+
+ // Handles must be closed or they will leak
+ CloseHandle(process_info.hThread);
+
+ if (wait)
+ WaitForSingleObject(process_info.hProcess, INFINITE);
+
+ // If the caller wants the process handle, we won't close it.
+ if (process_handle) {
+ *process_handle = process_info.hProcess;
+ } else {
+ CloseHandle(process_info.hProcess);
+ }
+ return true;
+}
+
+// Attempts to kill the process identified by the given process
+// entry structure, giving it the specified exit code.
+// Returns true if this is successful, false otherwise.
+bool KillProcess(int process_id, int exit_code, bool wait) {
+ bool result = false;
+ HANDLE process = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE,
+ FALSE, // Don't inherit handle
+ process_id);
+ if (process) {
+ result = !!TerminateProcess(process, exit_code);
+ if (result && wait) {
+ // The process may not end immediately due to pending I/O
+ if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000))
+ DLOG(ERROR) << "Error waiting for process exit: " << GetLastError();
+ } else {
+ DLOG(ERROR) << "Unable to terminate process: " << GetLastError();
+ }
+ CloseHandle(process);
+ }
+ return result;
+}
+
+bool DidProcessCrash(ProcessHandle handle) {
+ DWORD exitcode = 0;
+ BOOL success = ::GetExitCodeProcess(handle, &exitcode);
+ DCHECK(success);
+ DCHECK(exitcode != STILL_ACTIVE);
+
+ if (exitcode == 0 || // Normal termination.
+ exitcode == 1 || // Killed by task manager.
+ exitcode == 0xC0000354 || // STATUS_DEBUGGER_INACTIVE
+ exitcode == 0xC000013A || // Control-C/end session.
+ exitcode == 0x40010004) { // Debugger terminated process/end session.
+ return false;
+ }
+
+ // All other exit codes indicate crashes.
+ return true;
+}
+
+NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name,
+ const ProcessFilter* filter) :
+ started_iteration_(false),
+ executable_name_(executable_name),
+ filter_(filter) {
+ snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ }
+
+NamedProcessIterator::~NamedProcessIterator() {
+ CloseHandle(snapshot_);
+}
+
+
+const ProcessEntry* NamedProcessIterator::NextProcessEntry() {
+ bool result = false;
+ do {
+ result = CheckForNextProcess();
+ } while (result && !IncludeEntry());
+
+ if (result) {
+ return &entry_;
+ }
+
+ return NULL;
+}
+
+bool NamedProcessIterator::CheckForNextProcess() {
+ InitProcessEntry(&entry_);
+
+ if (!started_iteration_) {
+ started_iteration_ = true;
+ return !!Process32First(snapshot_, &entry_);
+ }
+
+ return !!Process32Next(snapshot_, &entry_);
+}
+
+bool NamedProcessIterator::IncludeEntry() {
+ return _wcsicmp(executable_name_.c_str(), entry_.szExeFile) == 0 &&
+ (!filter_ || filter_->Includes(entry_.th32ProcessID,
+ entry_.th32ParentProcessID));
+}
+
+void NamedProcessIterator::InitProcessEntry(ProcessEntry* entry) {
+ memset(entry, 0, sizeof(*entry));
+ entry->dwSize = sizeof(*entry);
+}
+
+int GetProcessCount(const std::wstring& executable_name,
+ const ProcessFilter* filter) {
+ int count = 0;
+
+ NamedProcessIterator iter(executable_name, filter);
+ while (iter.NextProcessEntry())
+ ++count;
+ return count;
+}
+
+bool KillProcesses(const std::wstring& executable_name, int exit_code,
+ const ProcessFilter* filter) {
+ bool result = true;
+ const ProcessEntry* entry;
+
+ NamedProcessIterator iter(executable_name, filter);
+ while (entry = iter.NextProcessEntry())
+ result = KillProcess((*entry).th32ProcessID, exit_code, true) && result;
+
+ return result;
+}
+
+bool WaitForProcessesToExit(const std::wstring& executable_name,
+ int wait_milliseconds,
+ const ProcessFilter* filter) {
+ const ProcessEntry* entry;
+ bool result = true;
+ DWORD start_time = GetTickCount();
+
+ NamedProcessIterator iter(executable_name, filter);
+ while (entry = iter.NextProcessEntry()) {
+ DWORD remaining_wait =
+ std::max(0, wait_milliseconds -
+ static_cast<int>(GetTickCount() - start_time));
+ HANDLE process = OpenProcess(SYNCHRONIZE,
+ FALSE,
+ entry->th32ProcessID);
+ DWORD wait_result = WaitForSingleObject(process, remaining_wait);
+ CloseHandle(process);
+ result = result && (wait_result == WAIT_OBJECT_0);
+ }
+
+ return result;
+}
+
+bool CleanupProcesses(const std::wstring& executable_name,
+ int wait_milliseconds,
+ int exit_code,
+ const ProcessFilter* filter) {
+ bool exited_cleanly =
+ process_util::WaitForProcessesToExit(executable_name, wait_milliseconds,
+ filter);
+ if (!exited_cleanly)
+ process_util::KillProcesses(executable_name, exit_code, filter);
+ return exited_cleanly;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// ProcesMetrics
+
+ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process),
+ last_time_(0),
+ last_system_time_(0) {
+ SYSTEM_INFO system_info;
+ GetSystemInfo(&system_info);
+ processor_count_ = system_info.dwNumberOfProcessors;
+}
+
+// static
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
+ return new ProcessMetrics(process);
+}
+
+ProcessMetrics::~ProcessMetrics() { }
+
+size_t ProcessMetrics::GetPagefileUsage() {
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
+ return pmc.PagefileUsage;
+ }
+ return 0;
+}
+
+// Returns the peak space allocated for the pagefile, in bytes.
+size_t ProcessMetrics::GetPeakPagefileUsage() {
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
+ return pmc.PeakPagefileUsage;
+ }
+ return 0;
+}
+
+// Returns the current working set size, in bytes.
+size_t ProcessMetrics::GetWorkingSetSize() {
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
+ return pmc.WorkingSetSize;
+ }
+ return 0;
+}
+
+size_t ProcessMetrics::GetPrivateBytes() {
+ // PROCESS_MEMORY_COUNTERS_EX is not supported until XP SP2.
+ // GetProcessMemoryInfo() will simply fail on prior OS. So the requested
+ // information is simply not available. Hence, we will return 0 on unsupported
+ // OSes. Unlike most Win32 API, we don't need to initialize the "cb" member.
+ PROCESS_MEMORY_COUNTERS_EX pmcx;
+ if (GetProcessMemoryInfo(process_,
+ reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmcx),
+ sizeof(pmcx))) {
+ return pmcx.PrivateUsage;
+ }
+ return 0;
+}
+
+void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) {
+ MEMORY_BASIC_INFORMATION mbi = {0};
+ size_t committed_private = 0;
+ size_t committed_mapped = 0;
+ size_t committed_image = 0;
+ void* base_address = NULL;
+ while (VirtualQueryEx(process_, base_address, &mbi,
+ sizeof(MEMORY_BASIC_INFORMATION)) ==
+ sizeof(MEMORY_BASIC_INFORMATION)) {
+ if(mbi.State == MEM_COMMIT) {
+ if (mbi.Type == MEM_PRIVATE) {
+ committed_private += mbi.RegionSize;
+ } else if (mbi.Type == MEM_MAPPED) {
+ committed_mapped += mbi.RegionSize;
+ } else if (mbi.Type == MEM_IMAGE) {
+ committed_image += mbi.RegionSize;
+ } else {
+ NOTREACHED();
+ }
+ }
+ base_address = (static_cast<BYTE*>(mbi.BaseAddress)) + mbi.RegionSize;
+ }
+ usage->image = committed_image / 1024;
+ usage->mapped = committed_mapped / 1024;
+ usage->priv = committed_private / 1024;
+}
+
+bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) {
+ size_t ws_private = 0;
+ size_t ws_shareable = 0;
+ size_t ws_shared = 0;
+
+ DCHECK(ws_usage);
+ memset(ws_usage, 0, sizeof(*ws_usage));
+
+ DWORD number_of_entries = 4096; // Just a guess.
+ PSAPI_WORKING_SET_INFORMATION* buffer = NULL;
+ int retries = 5;
+ for(;;) {
+ DWORD buffer_size = sizeof(PSAPI_WORKING_SET_INFORMATION) +
+ (number_of_entries * sizeof(PSAPI_WORKING_SET_BLOCK));
+
+ // if we can't expand the buffer, don't leak the previous
+ // contents or pass a NULL pointer to QueryWorkingSet
+ PSAPI_WORKING_SET_INFORMATION* new_buffer = reinterpret_cast<PSAPI_WORKING_SET_INFORMATION*>(
+ realloc(buffer, buffer_size));
+ if (!new_buffer) {
+ free(buffer);
+ return false;
+ }
+ buffer = new_buffer;
+
+ // Call the function once to get number of items
+ if (QueryWorkingSet(process_, buffer, buffer_size))
+ break; // Success
+
+ if (GetLastError() != ERROR_BAD_LENGTH) {
+ free(buffer);
+ return false;
+ }
+
+ number_of_entries = static_cast<DWORD>(buffer->NumberOfEntries);
+
+ // Maybe some entries are being added right now. Increase the buffer to
+ // take that into account.
+ number_of_entries = static_cast<DWORD>(number_of_entries * 1.25);
+
+ if (--retries == 0) {
+ free(buffer); // If we're looping, eventually fail.
+ return false;
+ }
+ }
+
+ // On windows 2000 the function returns 1 even when the buffer is too small.
+ // The number of entries that we are going to parse is the minimum between the
+ // size we allocated and the real number of entries.
+ number_of_entries =
+ std::min(number_of_entries, static_cast<DWORD>(buffer->NumberOfEntries));
+ for (unsigned int i = 0; i < number_of_entries; i++) {
+ if (buffer->WorkingSetInfo[i].Shared) {
+ ws_shareable++;
+ if (buffer->WorkingSetInfo[i].ShareCount > 1)
+ ws_shared++;
+ } else {
+ ws_private++;
+ }
+ }
+
+ ws_usage->priv = ws_private * PAGESIZE_KB;
+ ws_usage->shareable = ws_shareable * PAGESIZE_KB;
+ ws_usage->shared = ws_shared * PAGESIZE_KB;
+ free(buffer);
+ return true;
+}
+
+static uint64 FileTimeToUTC(const FILETIME& ftime) {
+ LARGE_INTEGER li;
+ li.LowPart = ftime.dwLowDateTime;
+ li.HighPart = ftime.dwHighDateTime;
+ return li.QuadPart;
+}
+
+int ProcessMetrics::GetCPUUsage() {
+ FILETIME now;
+ FILETIME creation_time;
+ FILETIME exit_time;
+ FILETIME kernel_time;
+ FILETIME user_time;
+
+ GetSystemTimeAsFileTime(&now);
+
+ if (!GetProcessTimes(process_, &creation_time, &exit_time,
+ &kernel_time, &user_time)) {
+ // We don't assert here because in some cases (such as in the Task Manager)
+ // we may call this function on a process that has just exited but we have
+ // not yet received the notification.
+ return 0;
+ }
+ int64 system_time = (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) /
+ processor_count_;
+ int64 time = FileTimeToUTC(now);
+
+ if ((last_system_time_ == 0) || (last_time_ == 0)) {
+ // First call, just set the last values.
+ last_system_time_ = system_time;
+ last_time_ = time;
+ return 0;
+ }
+
+ int64 system_time_delta = system_time - last_system_time_;
+ int64 time_delta = time - last_time_;
+ DCHECK(time_delta != 0);
+ if (time_delta == 0)
+ return 0;
+
+ // We add time_delta / 2 so the result is rounded.
+ int cpu = static_cast<int>((system_time_delta * 100 + time_delta / 2) /
+ time_delta);
+
+ last_system_time_ = system_time;
+ last_time_ = time;
+
+ return cpu;
+}
+
+bool ProcessMetrics::GetIOCounters(IO_COUNTERS* io_counters) {
+ return GetProcessIoCounters(process_, io_counters) != FALSE;
+}
+
+bool ProcessMetrics::CalculateFreeMemory(FreeMBytes* free) {
+ const SIZE_T kTopAdress = 0x7F000000;
+ const SIZE_T kMegabyte = 1024 * 1024;
+ SIZE_T accumulated = 0;
+
+ MEMORY_BASIC_INFORMATION largest = {0};
+ UINT_PTR scan = 0;
+ while (scan < kTopAdress) {
+ MEMORY_BASIC_INFORMATION info;
+ if (!::VirtualQueryEx(process_, reinterpret_cast<void*>(scan),
+ &info, sizeof(info)))
+ return false;
+ if (info.State == MEM_FREE) {
+ accumulated += info.RegionSize;
+ UINT_PTR end = scan + info.RegionSize;
+ if (info.RegionSize > (largest.RegionSize))
+ largest = info;
+ }
+ scan += info.RegionSize;
+ }
+ free->largest = largest.RegionSize / kMegabyte;
+ free->largest_ptr = largest.BaseAddress;
+ free->total = accumulated / kMegabyte;
+ return true;
+}
+
+bool EnableLowFragmentationHeap() {
+ HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
+ HeapSetFn heap_set = reinterpret_cast<HeapSetFn>(GetProcAddress(
+ kernel32,
+ "HeapSetInformation"));
+
+ // On Windows 2000, the function is not exported. This is not a reason to
+ // fail.
+ if (!heap_set)
+ return true;
+
+ unsigned number_heaps = GetProcessHeaps(0, NULL);
+ if (!number_heaps)
+ return false;
+
+ // Gives us some extra space in the array in case a thread is creating heaps
+ // at the same time we're querying them.
+ static const int MARGIN = 8;
+ scoped_array<HANDLE> heaps(new HANDLE[number_heaps + MARGIN]);
+ number_heaps = GetProcessHeaps(number_heaps + MARGIN, heaps.get());
+ if (!number_heaps)
+ return false;
+
+ for (unsigned i = 0; i < number_heaps; ++i) {
+ ULONG lfh_flag = 2;
+ // Don't bother with the result code. It may fails on heaps that have the
+ // HEAP_NO_SERIALIZE flag. This is expected and not a problem at all.
+ heap_set(heaps[i],
+ HeapCompatibilityInformation,
+ &lfh_flag,
+ sizeof(lfh_flag));
+ }
+ return true;
+}
+
+} // namespace process_util