• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2009 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.
4 
5 #include "base/process_util.h"
6 
7 #include <fcntl.h>
8 #include <io.h>
9 #include <windows.h>
10 #include <userenv.h>
11 #include <psapi.h>
12 
13 #include <ios>
14 
15 #include "base/debug_util.h"
16 #include "base/histogram.h"
17 #include "base/logging.h"
18 #include "base/scoped_handle_win.h"
19 #include "base/scoped_ptr.h"
20 
21 // userenv.dll is required for CreateEnvironmentBlock().
22 #pragma comment(lib, "userenv.lib")
23 
24 namespace base {
25 
26 namespace {
27 
28 // System pagesize. This value remains constant on x86/64 architectures.
29 const int PAGESIZE_KB = 4;
30 
31 // HeapSetInformation function pointer.
32 typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T);
33 
34 // Previous unhandled filter. Will be called if not NULL when we intercept an
35 // exception. Only used in unit tests.
36 LPTOP_LEVEL_EXCEPTION_FILTER g_previous_filter = NULL;
37 
38 // Prints the exception call stack.
39 // This is the unit tests exception filter.
StackDumpExceptionFilter(EXCEPTION_POINTERS * info)40 long WINAPI StackDumpExceptionFilter(EXCEPTION_POINTERS* info) {
41   StackTrace(info).PrintBacktrace();
42   if (g_previous_filter)
43     return g_previous_filter(info);
44   return EXCEPTION_CONTINUE_SEARCH;
45 }
46 
47 // Connects back to a console if available.
48 // Only necessary on Windows, no-op on other platforms.
AttachToConsole()49 void AttachToConsole() {
50   if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
51     unsigned int result = GetLastError();
52     // Was probably already attached.
53     if (result == ERROR_ACCESS_DENIED)
54       return;
55 
56     if (result == ERROR_INVALID_HANDLE || result == ERROR_INVALID_HANDLE) {
57       // TODO(maruel): Walk up the process chain if deemed necessary.
58     }
59     // Continue even if the function call fails.
60     AllocConsole();
61   }
62   // http://support.microsoft.com/kb/105305
63   int raw_out = _open_osfhandle(
64       reinterpret_cast<intptr_t>(GetStdHandle(STD_OUTPUT_HANDLE)), _O_TEXT);
65   *stdout = *_fdopen(raw_out, "w");
66   setvbuf(stdout, NULL, _IONBF, 0);
67 
68   int raw_err = _open_osfhandle(
69       reinterpret_cast<intptr_t>(GetStdHandle(STD_ERROR_HANDLE)), _O_TEXT);
70   *stderr = *_fdopen(raw_err, "w");
71   setvbuf(stderr, NULL, _IONBF, 0);
72 
73   int raw_in = _open_osfhandle(
74       reinterpret_cast<intptr_t>(GetStdHandle(STD_INPUT_HANDLE)), _O_TEXT);
75   *stdin = *_fdopen(raw_in, "r");
76   setvbuf(stdin, NULL, _IONBF, 0);
77   // Fix all cout, wcout, cin, wcin, cerr, wcerr, clog and wclog.
78   std::ios::sync_with_stdio();
79 }
80 
81 }  // namespace
82 
GetCurrentProcId()83 ProcessId GetCurrentProcId() {
84   return ::GetCurrentProcessId();
85 }
86 
GetCurrentProcessHandle()87 ProcessHandle GetCurrentProcessHandle() {
88   return ::GetCurrentProcess();
89 }
90 
OpenProcessHandle(ProcessId pid,ProcessHandle * handle)91 bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) {
92   // We try to limit privileges granted to the handle. If you need this
93   // for test code, consider using OpenPrivilegedProcessHandle instead of
94   // adding more privileges here.
95   ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_TERMINATE,
96                                      FALSE, pid);
97 
98   if (result == INVALID_HANDLE_VALUE)
99     return false;
100 
101   *handle = result;
102   return true;
103 }
104 
OpenPrivilegedProcessHandle(ProcessId pid,ProcessHandle * handle)105 bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) {
106   ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE |
107                                          PROCESS_TERMINATE |
108                                          PROCESS_QUERY_INFORMATION |
109                                          PROCESS_VM_READ |
110                                          SYNCHRONIZE,
111                                      FALSE, pid);
112 
113   if (result == INVALID_HANDLE_VALUE)
114     return false;
115 
116   *handle = result;
117   return true;
118 }
119 
CloseProcessHandle(ProcessHandle process)120 void CloseProcessHandle(ProcessHandle process) {
121   CloseHandle(process);
122 }
123 
GetProcId(ProcessHandle process)124 ProcessId GetProcId(ProcessHandle process) {
125   // Get a handle to |process| that has PROCESS_QUERY_INFORMATION rights.
126   HANDLE current_process = GetCurrentProcess();
127   HANDLE process_with_query_rights;
128   if (DuplicateHandle(current_process, process, current_process,
129                       &process_with_query_rights, PROCESS_QUERY_INFORMATION,
130                       false, 0)) {
131     DWORD id = GetProcessId(process_with_query_rights);
132     CloseHandle(process_with_query_rights);
133     return id;
134   }
135 
136   // We're screwed.
137   NOTREACHED();
138   return 0;
139 }
140 
LaunchApp(const std::wstring & cmdline,bool wait,bool start_hidden,ProcessHandle * process_handle)141 bool LaunchApp(const std::wstring& cmdline,
142                bool wait, bool start_hidden, ProcessHandle* process_handle) {
143   STARTUPINFO startup_info = {0};
144   startup_info.cb = sizeof(startup_info);
145   startup_info.dwFlags = STARTF_USESHOWWINDOW;
146   startup_info.wShowWindow = start_hidden ? SW_HIDE : SW_SHOW;
147   PROCESS_INFORMATION process_info;
148   if (!CreateProcess(NULL,
149                      const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL,
150                      FALSE, 0, NULL, NULL,
151                      &startup_info, &process_info))
152     return false;
153 
154   // Handles must be closed or they will leak
155   CloseHandle(process_info.hThread);
156 
157   if (wait)
158     WaitForSingleObject(process_info.hProcess, INFINITE);
159 
160   // If the caller wants the process handle, we won't close it.
161   if (process_handle) {
162     *process_handle = process_info.hProcess;
163   } else {
164     CloseHandle(process_info.hProcess);
165   }
166   return true;
167 }
168 
169 
LaunchAppAsUser(UserTokenHandle token,const std::wstring & cmdline,bool start_hidden,ProcessHandle * process_handle)170 bool LaunchAppAsUser(UserTokenHandle token, const std::wstring& cmdline,
171                      bool start_hidden, ProcessHandle* process_handle) {
172   STARTUPINFO startup_info = {0};
173   startup_info.cb = sizeof(startup_info);
174   PROCESS_INFORMATION process_info;
175   if (start_hidden) {
176     startup_info.dwFlags = STARTF_USESHOWWINDOW;
177     startup_info.wShowWindow = SW_HIDE;
178   }
179   DWORD flags = CREATE_UNICODE_ENVIRONMENT;
180   void* enviroment_block = NULL;
181 
182   if(!CreateEnvironmentBlock(&enviroment_block, token, FALSE))
183     return false;
184 
185   BOOL launched =
186       CreateProcessAsUser(token, NULL, const_cast<wchar_t*>(cmdline.c_str()),
187                           NULL, NULL, FALSE, flags, enviroment_block,
188                           NULL, &startup_info, &process_info);
189 
190   DestroyEnvironmentBlock(enviroment_block);
191 
192   if (!launched)
193     return false;
194 
195   CloseHandle(process_info.hThread);
196 
197   if (process_handle) {
198     *process_handle = process_info.hProcess;
199   } else {
200     CloseHandle(process_info.hProcess);
201   }
202   return true;
203 }
204 
LaunchApp(const CommandLine & cl,bool wait,bool start_hidden,ProcessHandle * process_handle)205 bool LaunchApp(const CommandLine& cl,
206                bool wait, bool start_hidden, ProcessHandle* process_handle) {
207   return LaunchApp(cl.command_line_string(), wait,
208                    start_hidden, process_handle);
209 }
210 
211 // Attempts to kill the process identified by the given process
212 // entry structure, giving it the specified exit code.
213 // Returns true if this is successful, false otherwise.
KillProcessById(ProcessId process_id,int exit_code,bool wait)214 bool KillProcessById(ProcessId process_id, int exit_code, bool wait) {
215   HANDLE process = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE,
216                                FALSE,  // Don't inherit handle
217                                process_id);
218   if (!process)
219     return false;
220 
221   bool ret = KillProcess(process, exit_code, wait);
222   CloseHandle(process);
223   return ret;
224 }
225 
GetAppOutput(const CommandLine & cl,std::string * output)226 bool GetAppOutput(const CommandLine& cl, std::string* output) {
227   HANDLE out_read = NULL;
228   HANDLE out_write = NULL;
229 
230   SECURITY_ATTRIBUTES sa_attr;
231   // Set the bInheritHandle flag so pipe handles are inherited.
232   sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
233   sa_attr.bInheritHandle = TRUE;
234   sa_attr.lpSecurityDescriptor = NULL;
235 
236   // Create the pipe for the child process's STDOUT.
237   if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) {
238     NOTREACHED() << "Failed to create pipe";
239     return false;
240   }
241 
242   // Ensure we don't leak the handles.
243   ScopedHandle scoped_out_read(out_read);
244   ScopedHandle scoped_out_write(out_write);
245 
246   // Ensure the read handle to the pipe for STDOUT is not inherited.
247   if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
248     NOTREACHED() << "Failed to disabled pipe inheritance";
249     return false;
250   }
251 
252   // Now create the child process
253   PROCESS_INFORMATION proc_info = { 0 };
254   STARTUPINFO start_info = { 0 };
255 
256   start_info.cb = sizeof(STARTUPINFO);
257   start_info.hStdOutput = out_write;
258   // Keep the normal stdin and stderr.
259   start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
260   start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
261   start_info.dwFlags |= STARTF_USESTDHANDLES;
262 
263   // Create the child process.
264   if (!CreateProcess(NULL,
265                      const_cast<wchar_t*>(cl.command_line_string().c_str()),
266                      NULL, NULL,
267                      TRUE,  // Handles are inherited.
268                      0, NULL, NULL, &start_info, &proc_info)) {
269     NOTREACHED() << "Failed to start process";
270     return false;
271   }
272 
273   // We don't need the thread handle, close it now.
274   CloseHandle(proc_info.hThread);
275 
276   // Close our writing end of pipe now. Otherwise later read would not be able
277   // to detect end of child's output.
278   scoped_out_write.Close();
279 
280   // Read output from the child process's pipe for STDOUT
281   const int kBufferSize = 1024;
282   char buffer[kBufferSize];
283 
284   for (;;) {
285     DWORD bytes_read = 0;
286     BOOL success = ReadFile(out_read, buffer, kBufferSize, &bytes_read, NULL);
287     if (!success || bytes_read == 0)
288       break;
289     output->append(buffer, bytes_read);
290   }
291 
292   // Let's wait for the process to finish.
293   WaitForSingleObject(proc_info.hProcess, INFINITE);
294   CloseHandle(proc_info.hProcess);
295 
296   return true;
297 }
298 
KillProcess(ProcessHandle process,int exit_code,bool wait)299 bool KillProcess(ProcessHandle process, int exit_code, bool wait) {
300   bool result = (TerminateProcess(process, exit_code) != FALSE);
301   if (result && wait) {
302     // The process may not end immediately due to pending I/O
303     if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000))
304       DLOG(ERROR) << "Error waiting for process exit: " << GetLastError();
305   } else if (!result) {
306     DLOG(ERROR) << "Unable to terminate process: " << GetLastError();
307   }
308   return result;
309 }
310 
DidProcessCrash(bool * child_exited,ProcessHandle handle)311 bool DidProcessCrash(bool* child_exited, ProcessHandle handle) {
312   DWORD exitcode = 0;
313 
314   if (child_exited)
315     *child_exited = true;  // On Windows it an error to call this function if
316                            // the child hasn't already exited.
317   if (!::GetExitCodeProcess(handle, &exitcode)) {
318     NOTREACHED();
319     return false;
320   }
321   if (exitcode == STILL_ACTIVE) {
322     // The process is likely not dead or it used 0x103 as exit code.
323     NOTREACHED();
324     return false;
325   }
326 
327   // Warning, this is not generic code; it heavily depends on the way
328   // the rest of the code kills a process.
329 
330   if (exitcode == PROCESS_END_NORMAL_TERMINATON ||
331       exitcode == PROCESS_END_KILLED_BY_USER ||
332       exitcode == PROCESS_END_PROCESS_WAS_HUNG ||
333       exitcode == 0xC0000354 ||     // STATUS_DEBUGGER_INACTIVE.
334       exitcode == 0xC000013A ||     // Control-C/end session.
335       exitcode == 0x40010004) {     // Debugger terminated process/end session.
336     return false;
337   }
338 
339   // All other exit codes indicate crashes.
340   return true;
341 }
342 
WaitForExitCode(ProcessHandle handle,int * exit_code)343 bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
344   ScopedHandle closer(handle);  // Ensure that we always close the handle.
345   if (::WaitForSingleObject(handle, INFINITE) != WAIT_OBJECT_0) {
346     NOTREACHED();
347     return false;
348   }
349   DWORD temp_code;  // Don't clobber out-parameters in case of failure.
350   if (!::GetExitCodeProcess(handle, &temp_code))
351     return false;
352   *exit_code = temp_code;
353   return true;
354 }
355 
NamedProcessIterator(const std::wstring & executable_name,const ProcessFilter * filter)356 NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name,
357                                            const ProcessFilter* filter)
358     : started_iteration_(false),
359       executable_name_(executable_name),
360       filter_(filter) {
361   snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
362 }
363 
~NamedProcessIterator()364 NamedProcessIterator::~NamedProcessIterator() {
365   CloseHandle(snapshot_);
366 }
367 
368 
NextProcessEntry()369 const ProcessEntry* NamedProcessIterator::NextProcessEntry() {
370   bool result = false;
371   do {
372     result = CheckForNextProcess();
373   } while (result && !IncludeEntry());
374 
375   if (result) {
376     return &entry_;
377   }
378 
379   return NULL;
380 }
381 
CheckForNextProcess()382 bool NamedProcessIterator::CheckForNextProcess() {
383   InitProcessEntry(&entry_);
384 
385   if (!started_iteration_) {
386     started_iteration_ = true;
387     return !!Process32First(snapshot_, &entry_);
388   }
389 
390   return !!Process32Next(snapshot_, &entry_);
391 }
392 
IncludeEntry()393 bool NamedProcessIterator::IncludeEntry() {
394   return _wcsicmp(executable_name_.c_str(), entry_.szExeFile) == 0 &&
395                   (!filter_ || filter_->Includes(entry_.th32ProcessID,
396                                                  entry_.th32ParentProcessID));
397 }
398 
InitProcessEntry(ProcessEntry * entry)399 void NamedProcessIterator::InitProcessEntry(ProcessEntry* entry) {
400   memset(entry, 0, sizeof(*entry));
401   entry->dwSize = sizeof(*entry);
402 }
403 
GetProcessCount(const std::wstring & executable_name,const ProcessFilter * filter)404 int GetProcessCount(const std::wstring& executable_name,
405                     const ProcessFilter* filter) {
406   int count = 0;
407 
408   NamedProcessIterator iter(executable_name, filter);
409   while (iter.NextProcessEntry())
410     ++count;
411   return count;
412 }
413 
KillProcesses(const std::wstring & executable_name,int exit_code,const ProcessFilter * filter)414 bool KillProcesses(const std::wstring& executable_name, int exit_code,
415                    const ProcessFilter* filter) {
416   bool result = true;
417   const ProcessEntry* entry;
418 
419   NamedProcessIterator iter(executable_name, filter);
420   while (entry = iter.NextProcessEntry()) {
421     if (!KillProcessById((*entry).th32ProcessID, exit_code, true))
422       result = false;
423   }
424 
425   return result;
426 }
427 
WaitForProcessesToExit(const std::wstring & executable_name,int64 wait_milliseconds,const ProcessFilter * filter)428 bool WaitForProcessesToExit(const std::wstring& executable_name,
429                             int64 wait_milliseconds,
430                             const ProcessFilter* filter) {
431   const ProcessEntry* entry;
432   bool result = true;
433   DWORD start_time = GetTickCount();
434 
435   NamedProcessIterator iter(executable_name, filter);
436   while (entry = iter.NextProcessEntry()) {
437     DWORD remaining_wait =
438         std::max<int64>(0, wait_milliseconds - (GetTickCount() - start_time));
439     HANDLE process = OpenProcess(SYNCHRONIZE,
440                                  FALSE,
441                                  entry->th32ProcessID);
442     DWORD wait_result = WaitForSingleObject(process, remaining_wait);
443     CloseHandle(process);
444     result = result && (wait_result == WAIT_OBJECT_0);
445   }
446 
447   return result;
448 }
449 
WaitForSingleProcess(ProcessHandle handle,int64 wait_milliseconds)450 bool WaitForSingleProcess(ProcessHandle handle, int64 wait_milliseconds) {
451   bool retval = WaitForSingleObject(handle, wait_milliseconds) == WAIT_OBJECT_0;
452   return retval;
453 }
454 
CrashAwareSleep(ProcessHandle handle,int64 wait_milliseconds)455 bool CrashAwareSleep(ProcessHandle handle, int64 wait_milliseconds) {
456   bool retval = WaitForSingleObject(handle, wait_milliseconds) == WAIT_TIMEOUT;
457   return retval;
458 }
459 
CleanupProcesses(const std::wstring & executable_name,int64 wait_milliseconds,int exit_code,const ProcessFilter * filter)460 bool CleanupProcesses(const std::wstring& executable_name,
461                       int64 wait_milliseconds,
462                       int exit_code,
463                       const ProcessFilter* filter) {
464   bool exited_cleanly = WaitForProcessesToExit(executable_name,
465                                                wait_milliseconds,
466                                                filter);
467   if (!exited_cleanly)
468     KillProcesses(executable_name, exit_code, filter);
469   return exited_cleanly;
470 }
471 
472 ///////////////////////////////////////////////////////////////////////////////
473 // ProcesMetrics
474 
ProcessMetrics(ProcessHandle process)475 ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process),
476                                                         last_time_(0),
477                                                         last_system_time_(0) {
478   SYSTEM_INFO system_info;
479   GetSystemInfo(&system_info);
480   processor_count_ = system_info.dwNumberOfProcessors;
481 }
482 
483 // static
CreateProcessMetrics(ProcessHandle process)484 ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
485   return new ProcessMetrics(process);
486 }
487 
~ProcessMetrics()488 ProcessMetrics::~ProcessMetrics() { }
489 
GetPagefileUsage() const490 size_t ProcessMetrics::GetPagefileUsage() const {
491   PROCESS_MEMORY_COUNTERS pmc;
492   if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
493     return pmc.PagefileUsage;
494   }
495   return 0;
496 }
497 
498 // Returns the peak space allocated for the pagefile, in bytes.
GetPeakPagefileUsage() const499 size_t ProcessMetrics::GetPeakPagefileUsage() const {
500   PROCESS_MEMORY_COUNTERS pmc;
501   if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
502     return pmc.PeakPagefileUsage;
503   }
504   return 0;
505 }
506 
507 // Returns the current working set size, in bytes.
GetWorkingSetSize() const508 size_t ProcessMetrics::GetWorkingSetSize() const {
509   PROCESS_MEMORY_COUNTERS pmc;
510   if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
511     return pmc.WorkingSetSize;
512   }
513   return 0;
514 }
515 
516 // Returns the peak working set size, in bytes.
GetPeakWorkingSetSize() const517 size_t ProcessMetrics::GetPeakWorkingSetSize() const {
518   PROCESS_MEMORY_COUNTERS pmc;
519   if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
520     return pmc.PeakWorkingSetSize;
521   }
522   return 0;
523 }
524 
GetPrivateBytes() const525 size_t ProcessMetrics::GetPrivateBytes() const {
526   // PROCESS_MEMORY_COUNTERS_EX is not supported until XP SP2.
527   // GetProcessMemoryInfo() will simply fail on prior OS. So the requested
528   // information is simply not available. Hence, we will return 0 on unsupported
529   // OSes. Unlike most Win32 API, we don't need to initialize the "cb" member.
530   PROCESS_MEMORY_COUNTERS_EX pmcx;
531   if (GetProcessMemoryInfo(process_,
532                           reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmcx),
533                           sizeof(pmcx))) {
534       return pmcx.PrivateUsage;
535   }
536   return 0;
537 }
538 
GetCommittedKBytes(CommittedKBytes * usage) const539 void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const {
540   MEMORY_BASIC_INFORMATION mbi = {0};
541   size_t committed_private = 0;
542   size_t committed_mapped = 0;
543   size_t committed_image = 0;
544   void* base_address = NULL;
545   while (VirtualQueryEx(process_, base_address, &mbi, sizeof(mbi)) ==
546       sizeof(mbi)) {
547     if (mbi.State == MEM_COMMIT) {
548       if (mbi.Type == MEM_PRIVATE) {
549         committed_private += mbi.RegionSize;
550       } else if (mbi.Type == MEM_MAPPED) {
551         committed_mapped += mbi.RegionSize;
552       } else if (mbi.Type == MEM_IMAGE) {
553         committed_image += mbi.RegionSize;
554       } else {
555         NOTREACHED();
556       }
557     }
558     void* new_base = (static_cast<BYTE*>(mbi.BaseAddress)) + mbi.RegionSize;
559     // Avoid infinite loop by weird MEMORY_BASIC_INFORMATION.
560     // If we query 64bit processes in a 32bit process, VirtualQueryEx()
561     // returns such data.
562     if (new_base <= base_address) {
563       usage->image = 0;
564       usage->mapped = 0;
565       usage->priv = 0;
566       return;
567     }
568     base_address = new_base;
569   }
570   usage->image = committed_image / 1024;
571   usage->mapped = committed_mapped / 1024;
572   usage->priv = committed_private / 1024;
573 }
574 
GetWorkingSetKBytes(WorkingSetKBytes * ws_usage) const575 bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
576   size_t ws_private = 0;
577   size_t ws_shareable = 0;
578   size_t ws_shared = 0;
579 
580   DCHECK(ws_usage);
581   memset(ws_usage, 0, sizeof(*ws_usage));
582 
583   DWORD number_of_entries = 4096;  // Just a guess.
584   PSAPI_WORKING_SET_INFORMATION* buffer = NULL;
585   int retries = 5;
586   for (;;) {
587     DWORD buffer_size = sizeof(PSAPI_WORKING_SET_INFORMATION) +
588                         (number_of_entries * sizeof(PSAPI_WORKING_SET_BLOCK));
589 
590     // if we can't expand the buffer, don't leak the previous
591     // contents or pass a NULL pointer to QueryWorkingSet
592     PSAPI_WORKING_SET_INFORMATION* new_buffer =
593         reinterpret_cast<PSAPI_WORKING_SET_INFORMATION*>(
594             realloc(buffer, buffer_size));
595     if (!new_buffer) {
596       free(buffer);
597       return false;
598     }
599     buffer = new_buffer;
600 
601     // Call the function once to get number of items
602     if (QueryWorkingSet(process_, buffer, buffer_size))
603       break;  // Success
604 
605     if (GetLastError() != ERROR_BAD_LENGTH) {
606       free(buffer);
607       return false;
608     }
609 
610     number_of_entries = static_cast<DWORD>(buffer->NumberOfEntries);
611 
612     // Maybe some entries are being added right now. Increase the buffer to
613     // take that into account.
614     number_of_entries = static_cast<DWORD>(number_of_entries * 1.25);
615 
616     if (--retries == 0) {
617       free(buffer);  // If we're looping, eventually fail.
618       return false;
619     }
620   }
621 
622   // On windows 2000 the function returns 1 even when the buffer is too small.
623   // The number of entries that we are going to parse is the minimum between the
624   // size we allocated and the real number of entries.
625   number_of_entries =
626       std::min(number_of_entries, static_cast<DWORD>(buffer->NumberOfEntries));
627   for (unsigned int i = 0; i < number_of_entries; i++) {
628     if (buffer->WorkingSetInfo[i].Shared) {
629       ws_shareable++;
630       if (buffer->WorkingSetInfo[i].ShareCount > 1)
631         ws_shared++;
632     } else {
633       ws_private++;
634     }
635   }
636 
637   ws_usage->priv = ws_private * PAGESIZE_KB;
638   ws_usage->shareable = ws_shareable * PAGESIZE_KB;
639   ws_usage->shared = ws_shared * PAGESIZE_KB;
640   free(buffer);
641   return true;
642 }
643 
FileTimeToUTC(const FILETIME & ftime)644 static uint64 FileTimeToUTC(const FILETIME& ftime) {
645   LARGE_INTEGER li;
646   li.LowPart = ftime.dwLowDateTime;
647   li.HighPart = ftime.dwHighDateTime;
648   return li.QuadPart;
649 }
650 
GetCPUUsage()651 double ProcessMetrics::GetCPUUsage() {
652   FILETIME now;
653   FILETIME creation_time;
654   FILETIME exit_time;
655   FILETIME kernel_time;
656   FILETIME user_time;
657 
658   GetSystemTimeAsFileTime(&now);
659 
660   if (!GetProcessTimes(process_, &creation_time, &exit_time,
661                        &kernel_time, &user_time)) {
662     // We don't assert here because in some cases (such as in the Task Manager)
663     // we may call this function on a process that has just exited but we have
664     // not yet received the notification.
665     return 0;
666   }
667   int64 system_time = (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) /
668                         processor_count_;
669   int64 time = FileTimeToUTC(now);
670 
671   if ((last_system_time_ == 0) || (last_time_ == 0)) {
672     // First call, just set the last values.
673     last_system_time_ = system_time;
674     last_time_ = time;
675     return 0;
676   }
677 
678   int64 system_time_delta = system_time - last_system_time_;
679   int64 time_delta = time - last_time_;
680   DCHECK(time_delta != 0);
681   if (time_delta == 0)
682     return 0;
683 
684   // We add time_delta / 2 so the result is rounded.
685   int cpu = static_cast<int>((system_time_delta * 100 + time_delta / 2) /
686                              time_delta);
687 
688   last_system_time_ = system_time;
689   last_time_ = time;
690 
691   return cpu;
692 }
693 
GetIOCounters(IO_COUNTERS * io_counters) const694 bool ProcessMetrics::GetIOCounters(IO_COUNTERS* io_counters) const {
695   return GetProcessIoCounters(process_, io_counters) != FALSE;
696 }
697 
CalculateFreeMemory(FreeMBytes * free) const698 bool ProcessMetrics::CalculateFreeMemory(FreeMBytes* free) const {
699   const SIZE_T kTopAdress = 0x7F000000;
700   const SIZE_T kMegabyte = 1024 * 1024;
701   SIZE_T accumulated = 0;
702 
703   MEMORY_BASIC_INFORMATION largest = {0};
704   UINT_PTR scan = 0;
705   while (scan < kTopAdress) {
706     MEMORY_BASIC_INFORMATION info;
707     if (!::VirtualQueryEx(process_, reinterpret_cast<void*>(scan),
708                           &info, sizeof(info)))
709       return false;
710     if (info.State == MEM_FREE) {
711       accumulated += info.RegionSize;
712       UINT_PTR end = scan + info.RegionSize;
713       if (info.RegionSize > (largest.RegionSize))
714         largest = info;
715     }
716     scan += info.RegionSize;
717   }
718   free->largest = largest.RegionSize / kMegabyte;
719   free->largest_ptr = largest.BaseAddress;
720   free->total = accumulated / kMegabyte;
721   return true;
722 }
723 
EnableLowFragmentationHeap()724 bool EnableLowFragmentationHeap() {
725   HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
726   HeapSetFn heap_set = reinterpret_cast<HeapSetFn>(GetProcAddress(
727       kernel32,
728       "HeapSetInformation"));
729 
730   // On Windows 2000, the function is not exported. This is not a reason to
731   // fail.
732   if (!heap_set)
733     return true;
734 
735   unsigned number_heaps = GetProcessHeaps(0, NULL);
736   if (!number_heaps)
737     return false;
738 
739   // Gives us some extra space in the array in case a thread is creating heaps
740   // at the same time we're querying them.
741   static const int MARGIN = 8;
742   scoped_array<HANDLE> heaps(new HANDLE[number_heaps + MARGIN]);
743   number_heaps = GetProcessHeaps(number_heaps + MARGIN, heaps.get());
744   if (!number_heaps)
745     return false;
746 
747   for (unsigned i = 0; i < number_heaps; ++i) {
748     ULONG lfh_flag = 2;
749     // Don't bother with the result code. It may fails on heaps that have the
750     // HEAP_NO_SERIALIZE flag. This is expected and not a problem at all.
751     heap_set(heaps[i],
752              HeapCompatibilityInformation,
753              &lfh_flag,
754              sizeof(lfh_flag));
755   }
756   return true;
757 }
758 
EnableTerminationOnHeapCorruption()759 void EnableTerminationOnHeapCorruption() {
760   // Ignore the result code. Supported on XP SP3 and Vista.
761   HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
762 }
763 
EnableInProcessStackDumping()764 bool EnableInProcessStackDumping() {
765   // Add stack dumping support on exception on windows. Similar to OS_POSIX
766   // signal() handling in process_util_posix.cc.
767   g_previous_filter = SetUnhandledExceptionFilter(&StackDumpExceptionFilter);
768   AttachToConsole();
769   return true;
770 }
771 
RaiseProcessToHighPriority()772 void RaiseProcessToHighPriority() {
773   SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
774 }
775 
776 // GetPerformanceInfo is not available on WIN2K.  So we'll
777 // load it on-the-fly.
778 const wchar_t kPsapiDllName[] = L"psapi.dll";
779 typedef BOOL (WINAPI *GetPerformanceInfoFunction) (
780     PPERFORMANCE_INFORMATION pPerformanceInformation,
781     DWORD cb);
782 
783 // Beware of races if called concurrently from multiple threads.
InternalGetPerformanceInfo(PPERFORMANCE_INFORMATION pPerformanceInformation,DWORD cb)784 static BOOL InternalGetPerformanceInfo(
785     PPERFORMANCE_INFORMATION pPerformanceInformation, DWORD cb) {
786   static GetPerformanceInfoFunction GetPerformanceInfo_func = NULL;
787   if (!GetPerformanceInfo_func) {
788     HMODULE psapi_dll = ::GetModuleHandle(kPsapiDllName);
789     if (psapi_dll)
790       GetPerformanceInfo_func = reinterpret_cast<GetPerformanceInfoFunction>(
791           GetProcAddress(psapi_dll, "GetPerformanceInfo"));
792 
793     if (!GetPerformanceInfo_func) {
794       // The function could be loaded!
795       memset(pPerformanceInformation, 0, cb);
796       return FALSE;
797     }
798   }
799   return GetPerformanceInfo_func(pPerformanceInformation, cb);
800 }
801 
GetSystemCommitCharge()802 size_t GetSystemCommitCharge() {
803   // Get the System Page Size.
804   SYSTEM_INFO system_info;
805   GetSystemInfo(&system_info);
806 
807   PERFORMANCE_INFORMATION info;
808   if (!InternalGetPerformanceInfo(&info, sizeof(info))) {
809     LOG(ERROR) << "Failed to fetch internal performance info.";
810     return 0;
811   }
812   return (info.CommitTotal * system_info.dwPageSize) / 1024;
813 }
814 
815 }  // namespace base
816