diff options
author | Andras Becsi <andras.becsi@digia.com> | 2014-03-18 13:16:26 +0100 |
---|---|---|
committer | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2014-03-20 15:55:39 +0100 |
commit | 3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch) | |
tree | 92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/base/process | |
parent | e90d7c4b152c56919d963987e2503f9909a666d2 (diff) |
Update to new stable branch 1750
This also includes an updated ninja and chromium dependencies
needed on Windows.
Change-Id: Icd597d80ed3fa4425933c9f1334c3c2e31291c42
Reviewed-by: Zoltan Arvai <zarvai@inf.u-szeged.hu>
Reviewed-by: Zeno Albisser <zeno.albisser@digia.com>
Diffstat (limited to 'chromium/base/process')
18 files changed, 783 insertions, 343 deletions
diff --git a/chromium/base/process/internal_linux.h b/chromium/base/process/internal_linux.h index a10cee36e56..0cacd3f3a84 100644 --- a/chromium/base/process/internal_linux.h +++ b/chromium/base/process/internal_linux.h @@ -8,6 +8,8 @@ #ifndef BASE_PROCESS_LINUX_INTERNAL_H_ #define BASE_PROCESS_LINUX_INTERNAL_H_ +#include <unistd.h> + #include "base/files/file_path.h" namespace base { diff --git a/chromium/base/process/kill.h b/chromium/base/process/kill.h index c828c718def..de72d7a8dca 100644 --- a/chromium/base/process/kill.h +++ b/chromium/base/process/kill.h @@ -25,6 +25,13 @@ enum TerminationStatus { TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill TERMINATION_STATUS_PROCESS_CRASHED, // e.g. Segmentation fault TERMINATION_STATUS_STILL_RUNNING, // child hasn't exited yet +#if defined(OS_ANDROID) + // On Android processes are spawned from the system Zygote and we do not get + // the termination status. We can't know if the termination was a crash or an + // oom kill for sure, but we can use status of the strong process bindings as + // a hint. + TERMINATION_STATUS_OOM_PROTECTED, // child was protected from oom kill +#endif TERMINATION_STATUS_MAX_ENUM }; diff --git a/chromium/base/process/launch.cc b/chromium/base/process/launch.cc index 1329a5af23e..0c9f3a88fef 100644 --- a/chromium/base/process/launch.cc +++ b/chromium/base/process/launch.cc @@ -10,6 +10,7 @@ LaunchOptions::LaunchOptions() : wait(false), #if defined(OS_WIN) start_hidden(false), + handles_to_inherit(NULL), inherit_handles(false), as_user(NULL), empty_desktop_name(false), diff --git a/chromium/base/process/launch.h b/chromium/base/process/launch.h index ac2df5eee5f..336bfba16e9 100644 --- a/chromium/base/process/launch.h +++ b/chromium/base/process/launch.h @@ -1,4 +1,4 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -16,17 +16,23 @@ #include "base/basictypes.h" #include "base/environment.h" #include "base/process/process_handle.h" +#include "base/strings/string_piece.h" #if defined(OS_POSIX) #include "base/posix/file_descriptor_shuffle.h" #elif defined(OS_WIN) #include <windows.h> +#include "base/win/scoped_handle.h" #endif class CommandLine; namespace base { +#if defined(OS_WIN) +typedef std::vector<HANDLE> HandlesToInheritVector; +#endif +// TODO(viettrungluu): Only define this on POSIX? typedef std::vector<std::pair<int, int> > FileHandleMappingVector; // Options for launching a subprocess that are passed to LaunchProcess(). @@ -41,13 +47,19 @@ struct BASE_EXPORT LaunchOptions { #if defined(OS_WIN) bool start_hidden; + // If non-null, inherit exactly the list of handles in this vector (these + // handles must be inheritable). This is only supported on Vista and higher. + HandlesToInheritVector* handles_to_inherit; + // If true, the new process inherits handles from the parent. In production // code this flag should be used only when running short-lived, trusted // binaries, because open handles from other libraries and subsystems will // leak to the child process, causing errors such as open socket hangs. + // Note: If |handles_to_inherit| is non-null, this flag is ignored and only + // those handles will be inherited (on Vista and higher). bool inherit_handles; - // If non-NULL, runs as if the user represented by the token had launched it. + // If non-null, runs as if the user represented by the token had launched it. // Whether the application is visible on the interactive desktop depends on // the token belonging to an interactive logon session. // @@ -59,7 +71,7 @@ struct BASE_EXPORT LaunchOptions { // If true, use an empty string for the desktop name. bool empty_desktop_name; - // If non-NULL, launches the application in that job object. The process will + // If non-null, launches the application in that job object. The process will // be terminated immediately and LaunchProcess() will fail if assignment to // the job object fails. HANDLE job_handle; @@ -81,7 +93,7 @@ struct BASE_EXPORT LaunchOptions { // the same environment. See AlterEnvironment(). EnvironmentMap environ; - // If non-NULL, remap file descriptors according to the mapping of + // If non-null, remap file descriptors according to the mapping of // src fd->dest fd to propagate FDs into the child process. // This pointer is owned by the caller and must live through the // call to LaunchProcess(). @@ -116,7 +128,7 @@ struct BASE_EXPORT LaunchOptions { // // Returns true upon success. // -// Upon success, if |process_handle| is non-NULL, it will be filled in with the +// Upon success, if |process_handle| is non-null, it will be filled in with the // handle of the launched process. NOTE: In this case, the caller is // responsible for closing the handle so that it doesn't leak! // Otherwise, the process handle will be implicitly closed. @@ -146,7 +158,7 @@ BASE_EXPORT bool LaunchProcess(const CommandLine& cmdline, // cmdline = "c:\windows\explorer.exe" -foo "c:\bar\" BASE_EXPORT bool LaunchProcess(const string16& cmdline, const LaunchOptions& options, - ProcessHandle* process_handle); + win::ScopedHandle* process_handle); #elif defined(OS_POSIX) // A POSIX-specific version of LaunchProcess that takes an argv array @@ -179,6 +191,13 @@ BASE_EXPORT void RouteStdioToConsole(); // indicating success). BASE_EXPORT bool GetAppOutput(const CommandLine& cl, std::string* output); +#if defined(OS_WIN) +// A Windows-specific version of GetAppOutput that takes a command line string +// instead of a CommandLine object. Useful for situations where you need to +// control the command line arguments directly. +BASE_EXPORT bool GetAppOutput(const StringPiece16& cl, std::string* output); +#endif + #if defined(OS_POSIX) // A POSIX-specific version of GetAppOutput that takes an argv array // instead of a CommandLine. Useful for situations where you need to diff --git a/chromium/base/process/launch_posix.cc b/chromium/base/process/launch_posix.cc index de6286da26b..8dc8f9e215d 100644 --- a/chromium/base/process/launch_posix.cc +++ b/chromium/base/process/launch_posix.cc @@ -230,7 +230,7 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) { // Since we're just trying to close anything we can find, // ignore any error return values of close(). - ignore_result(HANDLE_EINTR(close(fd))); + close(fd); } return; } @@ -264,7 +264,7 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) { // these FDs are >= |max_fds|, so we can check against that here // before closing. See https://bugs.kde.org/show_bug.cgi?id=191758 if (fd < static_cast<int>(max_fds)) { - int ret = HANDLE_EINTR(close(fd)); + int ret = IGNORE_EINTR(close(fd)); DPCHECK(ret == 0); } } diff --git a/chromium/base/process/launch_win.cc b/chromium/base/process/launch_win.cc index da913efba74..0c831cf7b4a 100644 --- a/chromium/base/process/launch_win.cc +++ b/chromium/base/process/launch_win.cc @@ -11,6 +11,7 @@ #include <psapi.h> #include <ios> +#include <limits> #include "base/bind.h" #include "base/bind_helpers.h" @@ -25,6 +26,7 @@ #include "base/win/object_watcher.h" #include "base/win/scoped_handle.h" #include "base/win/scoped_process_information.h" +#include "base/win/startup_information.h" #include "base/win/windows_version.h" // userenv.dll is required for CreateEnvironmentBlock(). @@ -103,27 +105,62 @@ void RouteStdioToConsole() { bool LaunchProcess(const string16& cmdline, const LaunchOptions& options, - ProcessHandle* process_handle) { - STARTUPINFO startup_info = {}; - startup_info.cb = sizeof(startup_info); + win::ScopedHandle* process_handle) { + win::StartupInformation startup_info_wrapper; + STARTUPINFO* startup_info = startup_info_wrapper.startup_info(); + + bool inherit_handles = options.inherit_handles; + DWORD flags = 0; + if (options.handles_to_inherit) { + if (options.handles_to_inherit->empty()) { + inherit_handles = false; + } else { + if (base::win::GetVersion() < base::win::VERSION_VISTA) { + DLOG(ERROR) << "Specifying handles to inherit requires Vista or later."; + return false; + } + + if (options.handles_to_inherit->size() > + std::numeric_limits<DWORD>::max() / sizeof(HANDLE)) { + DLOG(ERROR) << "Too many handles to inherit."; + return false; + } + + if (!startup_info_wrapper.InitializeProcThreadAttributeList(1)) { + DPLOG(ERROR); + return false; + } + + if (!startup_info_wrapper.UpdateProcThreadAttribute( + PROC_THREAD_ATTRIBUTE_HANDLE_LIST, + const_cast<HANDLE*>(&options.handles_to_inherit->at(0)), + static_cast<DWORD>(options.handles_to_inherit->size() * + sizeof(HANDLE)))) { + DPLOG(ERROR); + return false; + } + + inherit_handles = true; + flags |= EXTENDED_STARTUPINFO_PRESENT; + } + } + if (options.empty_desktop_name) - startup_info.lpDesktop = L""; - startup_info.dwFlags = STARTF_USESHOWWINDOW; - startup_info.wShowWindow = options.start_hidden ? SW_HIDE : SW_SHOW; + startup_info->lpDesktop = L""; + startup_info->dwFlags = STARTF_USESHOWWINDOW; + startup_info->wShowWindow = options.start_hidden ? SW_HIDE : SW_SHOW; if (options.stdin_handle || options.stdout_handle || options.stderr_handle) { - DCHECK(options.inherit_handles); + DCHECK(inherit_handles); DCHECK(options.stdin_handle); DCHECK(options.stdout_handle); DCHECK(options.stderr_handle); - startup_info.dwFlags |= STARTF_USESTDHANDLES; - startup_info.hStdInput = options.stdin_handle; - startup_info.hStdOutput = options.stdout_handle; - startup_info.hStdError = options.stderr_handle; + startup_info->dwFlags |= STARTF_USESTDHANDLES; + startup_info->hStdInput = options.stdin_handle; + startup_info->hStdOutput = options.stdout_handle; + startup_info->hStdError = options.stderr_handle; } - DWORD flags = 0; - if (options.job_handle) { flags |= CREATE_SUSPENDED; @@ -136,7 +173,7 @@ bool LaunchProcess(const string16& cmdline, if (options.force_breakaway_from_job_) flags |= CREATE_BREAKAWAY_FROM_JOB; - base::win::ScopedProcessInformation process_info; + PROCESS_INFORMATION temp_process_info = {}; if (options.as_user) { flags |= CREATE_UNICODE_ENVIRONMENT; @@ -150,9 +187,9 @@ bool LaunchProcess(const string16& cmdline, BOOL launched = CreateProcessAsUser(options.as_user, NULL, const_cast<wchar_t*>(cmdline.c_str()), - NULL, NULL, options.inherit_handles, flags, - enviroment_block, NULL, &startup_info, - process_info.Receive()); + NULL, NULL, inherit_handles, flags, + enviroment_block, NULL, startup_info, + &temp_process_info); DestroyEnvironmentBlock(enviroment_block); if (!launched) { DPLOG(ERROR); @@ -161,12 +198,13 @@ bool LaunchProcess(const string16& cmdline, } else { if (!CreateProcess(NULL, const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL, - options.inherit_handles, flags, NULL, NULL, - &startup_info, process_info.Receive())) { + inherit_handles, flags, NULL, NULL, + startup_info, &temp_process_info)) { DPLOG(ERROR); return false; } } + base::win::ScopedProcessInformation process_info(temp_process_info); if (options.job_handle) { if (0 == AssignProcessToJobObject(options.job_handle, @@ -184,7 +222,7 @@ bool LaunchProcess(const string16& cmdline, // If the caller wants the process handle, we won't close it. if (process_handle) - *process_handle = process_info.TakeProcessHandle(); + process_handle->Set(process_info.TakeProcessHandle()); return true; } @@ -192,7 +230,13 @@ bool LaunchProcess(const string16& cmdline, bool LaunchProcess(const CommandLine& cmdline, const LaunchOptions& options, ProcessHandle* process_handle) { - return LaunchProcess(cmdline.GetCommandLineString(), options, process_handle); + if (!process_handle) + return LaunchProcess(cmdline.GetCommandLineString(), options, NULL); + + win::ScopedHandle process; + bool rv = LaunchProcess(cmdline.GetCommandLineString(), options, &process); + *process_handle = process.Take(); + return rv; } bool SetJobObjectLimitFlags(HANDLE job_object, DWORD limit_flags) { @@ -206,6 +250,10 @@ bool SetJobObjectLimitFlags(HANDLE job_object, DWORD limit_flags) { } bool GetAppOutput(const CommandLine& cl, std::string* output) { + return GetAppOutput(cl.GetCommandLineString(), output); +} + +bool GetAppOutput(const StringPiece16& cl, std::string* output) { HANDLE out_read = NULL; HANDLE out_write = NULL; @@ -231,10 +279,10 @@ bool GetAppOutput(const CommandLine& cl, std::string* output) { return false; } - FilePath::StringType writable_command_line_string(cl.GetCommandLineString()); + FilePath::StringType writable_command_line_string; + writable_command_line_string.assign(cl.data(), cl.size()); - base::win::ScopedProcessInformation proc_info; - STARTUPINFO start_info = { 0 }; + STARTUPINFO start_info = {}; start_info.cb = sizeof(STARTUPINFO); start_info.hStdOutput = out_write; @@ -244,14 +292,16 @@ bool GetAppOutput(const CommandLine& cl, std::string* output) { start_info.dwFlags |= STARTF_USESTDHANDLES; // Create the child process. + PROCESS_INFORMATION temp_process_info = {}; if (!CreateProcess(NULL, &writable_command_line_string[0], NULL, NULL, TRUE, // Handles are inherited. - 0, NULL, NULL, &start_info, proc_info.Receive())) { + 0, NULL, NULL, &start_info, &temp_process_info)) { NOTREACHED() << "Failed to start process"; return false; } + base::win::ScopedProcessInformation proc_info(temp_process_info); // Close our writing end of pipe now. Otherwise later read would not be able // to detect end of child's output. diff --git a/chromium/base/process/memory.h b/chromium/base/process/memory.h index de79477e95d..e6696cb8a70 100644 --- a/chromium/base/process/memory.h +++ b/chromium/base/process/memory.h @@ -62,6 +62,7 @@ BASE_EXPORT bool AdjustOOMScore(ProcessId process, int score); // IF YOU USE THIS WITHOUT CONSULTING YOUR FRIENDLY OSX DEVELOPER, // YOUR CODE IS LIKELY TO BE REVERTED. THANK YOU. BASE_EXPORT void* UncheckedMalloc(size_t size); +BASE_EXPORT void* UncheckedCalloc(size_t num_items, size_t size); #endif // defined(OS_MACOSX) } // namespace base diff --git a/chromium/base/process/memory_linux.cc b/chromium/base/process/memory_linux.cc index f81429b2ac0..6bed68bd832 100644 --- a/chromium/base/process/memory_linux.cc +++ b/chromium/base/process/memory_linux.cc @@ -18,6 +18,7 @@ size_t g_oom_size = 0U; namespace { +#if !defined(OS_ANDROID) void OnNoMemorySize(size_t size) { g_oom_size = size; @@ -29,6 +30,7 @@ void OnNoMemorySize(size_t size) { void OnNoMemory() { OnNoMemorySize(0); } +#endif // !defined(OS_ANDROID) } // namespace diff --git a/chromium/base/process/memory_mac.mm b/chromium/base/process/memory_mac.mm index dd30e704c8f..3e281cd8e3c 100644 --- a/chromium/base/process/memory_mac.mm +++ b/chromium/base/process/memory_mac.mm @@ -108,7 +108,7 @@ class ThreadLocalBooleanAutoReset { }; base::LazyInstance<ThreadLocalBoolean>::Leaky - g_unchecked_malloc = LAZY_INSTANCE_INITIALIZER; + g_unchecked_alloc = LAZY_INSTANCE_INITIALIZER; // NOTE(shess): This is called when the malloc library noticed that the heap // is fubar. Avoid calls which will re-enter the malloc library. @@ -117,10 +117,23 @@ void CrMallocErrorBreak() { // Out of memory is certainly not heap corruption, and not necessarily // something for which the process should be terminated. Leave that decision - // to the OOM killer. The EBADF case comes up because the malloc library - // attempts to log to ASL (syslog) before calling this code, which fails - // accessing a Unix-domain socket because of sandboxing. - if (errno == ENOMEM || (errno == EBADF && g_unchecked_malloc.Get().Get())) + // to the OOM killer. + if (errno == ENOMEM) + return; + + // The malloc library attempts to log to ASL (syslog) before calling this + // code, which fails accessing a Unix-domain socket when sandboxed. The + // failed socket results in writing to a -1 fd, leaving EBADF in errno. If + // UncheckedMalloc() is on the stack, for large allocations (15k and up) only + // an OOM failure leads here. Smaller allocations could also arrive here due + // to freelist corruption, but there is no way to distinguish that from OOM at + // this point. + // + // NOTE(shess): I hypothesize that EPERM case in 10.9 is the same root cause + // as EBADF. Unfortunately, 10.9's opensource releases don't include malloc + // source code at this time. + // <http://crbug.com/312234> + if ((errno == EBADF || errno == EPERM) && g_unchecked_alloc.Get().Get()) return; // A unit test checks this error message, so it needs to be in release builds. @@ -422,7 +435,7 @@ void oom_killer_new() { // === Core Foundation CFAllocators === bool CanGetContextForCFAllocator() { - return !base::mac::IsOSLaterThanMountainLion_DontCallThis(); + return !base::mac::IsOSLaterThanMavericks_DontCallThis(); } CFAllocatorContext* ContextForCFAllocator(CFAllocatorRef allocator) { @@ -431,7 +444,9 @@ CFAllocatorContext* ContextForCFAllocator(CFAllocatorRef allocator) { const_cast<ChromeCFAllocatorLeopards*>( reinterpret_cast<const ChromeCFAllocatorLeopards*>(allocator)); return &our_allocator->_context; - } else if (base::mac::IsOSLion() || base::mac::IsOSMountainLion()) { + } else if (base::mac::IsOSLion() || + base::mac::IsOSMountainLion() || + base::mac::IsOSMavericks()) { ChromeCFAllocatorLions* our_allocator = const_cast<ChromeCFAllocatorLions*>( reinterpret_cast<const ChromeCFAllocatorLions*>(allocator)); @@ -491,13 +506,24 @@ void* UncheckedMalloc(size_t size) { if (g_old_malloc) { #if ARCH_CPU_32_BITS ScopedClearErrno clear_errno; - ThreadLocalBooleanAutoReset flag(g_unchecked_malloc.Pointer(), true); + ThreadLocalBooleanAutoReset flag(g_unchecked_alloc.Pointer(), true); #endif // ARCH_CPU_32_BITS return g_old_malloc(malloc_default_zone(), size); } return malloc(size); } +void* UncheckedCalloc(size_t num_items, size_t size) { + if (g_old_calloc) { +#if ARCH_CPU_32_BITS + ScopedClearErrno clear_errno; + ThreadLocalBooleanAutoReset flag(g_unchecked_alloc.Pointer(), true); +#endif // ARCH_CPU_32_BITS + return g_old_calloc(malloc_default_zone(), num_items, size); + } + return calloc(num_items, size); +} + void EnableTerminationOnOutOfMemory() { if (g_oom_killer_enabled) return; diff --git a/chromium/base/process/memory_unittest.cc b/chromium/base/process/memory_unittest.cc index a1f30526aa3..e5c759d5711 100644 --- a/chromium/base/process/memory_unittest.cc +++ b/chromium/base/process/memory_unittest.cc @@ -10,6 +10,7 @@ #include "base/compiler_specific.h" #include "base/debug/alias.h" +#include "base/strings/stringprintf.h" #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_WIN) @@ -23,7 +24,6 @@ #include "base/process/memory_unittest_mac.h" #endif #if defined(OS_LINUX) -#include <glib.h> #include <malloc.h> #endif @@ -260,14 +260,13 @@ TEST_F(OutOfMemoryDeathTest, Memalign) { } TEST_F(OutOfMemoryDeathTest, ViaSharedLibraries) { - // g_try_malloc is documented to return NULL on failure. (g_malloc is the - // 'safe' default that crashes if allocation fails). However, since we have - // hopefully overridden malloc, even g_try_malloc should fail. This tests - // that the run-time symbol resolution is overriding malloc for shared - // libraries as well as for our code. + // This tests that the run-time symbol resolution is overriding malloc for + // shared libraries (including libc itself) as well as for our code. + std::string format = base::StringPrintf("%%%zud", test_size_); + char *value = NULL; ASSERT_DEATH({ SetUpInDeathAssert(); - value_ = g_try_malloc(test_size_); + EXPECT_EQ(-1, asprintf(&value, format.c_str(), 0)); }, ""); } #endif // OS_LINUX diff --git a/chromium/base/process/process_handle_linux.cc b/chromium/base/process/process_handle_linux.cc index 91441f7b38c..0f7ccd348a8 100644 --- a/chromium/base/process/process_handle_linux.cc +++ b/chromium/base/process/process_handle_linux.cc @@ -20,7 +20,7 @@ ProcessId GetParentProcessId(ProcessHandle process) { FilePath GetProcessExecutablePath(ProcessHandle process) { FilePath stat_file = internal::GetProcPidDir(process).Append("exe"); FilePath exe_name; - if (!file_util::ReadSymbolicLink(stat_file, &exe_name)) { + if (!ReadSymbolicLink(stat_file, &exe_name)) { // No such process. Happens frequently in e.g. TerminateAllChromeProcesses return FilePath(); } diff --git a/chromium/base/process/process_metrics.cc b/chromium/base/process/process_metrics.cc index 127fb4649f7..83289b8c78b 100644 --- a/chromium/base/process/process_metrics.cc +++ b/chromium/base/process/process_metrics.cc @@ -42,4 +42,12 @@ scoped_ptr<Value> SystemMetrics::ToValue() const { return res.PassAs<Value>(); } +double ProcessMetrics::GetPlatformIndependentCPUUsage() { +#if defined(OS_WIN) + return GetCPUUsage() * processor_count_; +#else + return GetCPUUsage(); +#endif +} + } // namespace base diff --git a/chromium/base/process/process_metrics.h b/chromium/base/process/process_metrics.h index f6b225fa94f..560490a9b8c 100644 --- a/chromium/base/process/process_metrics.h +++ b/chromium/base/process/process_metrics.h @@ -160,14 +160,19 @@ class BASE_EXPORT ProcessMetrics { // load and fragmentation. bool CalculateFreeMemory(FreeMBytes* free) const; - // Returns the CPU usage in percent since the last time this method was - // called. The first time this method is called it returns 0 and will return - // the actual CPU info on subsequent calls. - // On Windows, the CPU usage value is for all CPUs. So if you have 2 CPUs and - // your process is using all the cycles of 1 CPU and not the other CPU, this - // method returns 50. + // Returns the CPU usage in percent since the last time this method or + // GetPlatformIndependentCPUUsage() was called. The first time this method + // is called it returns 0 and will return the actual CPU info on subsequent + // calls. On Windows, the CPU usage value is for all CPUs. So if you have + // 2 CPUs and your process is using all the cycles of 1 CPU and not the other + // CPU, this method returns 50. double GetCPUUsage(); + // Same as GetCPUUsage(), but will return consistent values on all platforms + // (cancelling the Windows exception mentioned above) by returning a value in + // the range of 0 to (100 * numCPUCores) everywhere. + double GetPlatformIndependentCPUUsage(); + // Retrieves accounting information for all I/O operations performed by the // process. // If IO information is retrieved successfully, the function returns true @@ -273,6 +278,16 @@ struct BASE_EXPORT SystemMemoryInfoKB { #endif }; +// Parses a string containing the contents of /proc/meminfo +// returns true on success or false for a parsing error +BASE_EXPORT bool ParseProcMeminfo(const std::string& input, + SystemMemoryInfoKB* meminfo); + +// Parses a string containing the contents of /proc/vmstat +// returns true on success or false for a parsing error +BASE_EXPORT bool ParseProcVmstat(const std::string& input, + SystemMemoryInfoKB* meminfo); + // Retrieves data from /proc/meminfo and /proc/vmstat // about system-wide memory consumption. // Fills in the provided |meminfo| structure. Returns true on success. diff --git a/chromium/base/process/process_metrics_linux.cc b/chromium/base/process/process_metrics_linux.cc index 7f46639e4a4..afa88486a0b 100644 --- a/chromium/base/process/process_metrics_linux.cc +++ b/chromium/base/process/process_metrics_linux.cc @@ -405,33 +405,6 @@ int GetNumberOfThreads(ProcessHandle process) { namespace { -// The format of /proc/meminfo is: -// -// MemTotal: 8235324 kB -// MemFree: 1628304 kB -// Buffers: 429596 kB -// Cached: 4728232 kB -// ... -const size_t kMemTotalIndex = 1; -const size_t kMemFreeIndex = 4; -const size_t kMemBuffersIndex = 7; -const size_t kMemCachedIndex = 10; -const size_t kMemActiveAnonIndex = 22; -const size_t kMemInactiveAnonIndex = 25; -const size_t kMemActiveFileIndex = 28; -const size_t kMemInactiveFileIndex = 31; - -// The format of /proc/vmstat is: -// -// nr_free_pages 299878 -// nr_inactive_anon 239863 -// nr_active_anon 1318966 -// nr_inactive_file 2015629 -// ... -const size_t kVMPagesSwappedIn = 75; -const size_t kVMPagesSwappedOut = 77; -const size_t kVMPageMajorFaults = 95; - // The format of /proc/diskstats is: // Device major number // Device minor number @@ -538,6 +511,123 @@ scoped_ptr<Value> SystemMemoryInfoKB::ToValue() const { return res.PassAs<Value>(); } +// exposed for testing +bool ParseProcMeminfo(const std::string& meminfo_data, + SystemMemoryInfoKB* meminfo) { + // The format of /proc/meminfo is: + // + // MemTotal: 8235324 kB + // MemFree: 1628304 kB + // Buffers: 429596 kB + // Cached: 4728232 kB + // ... + // There is no guarantee on the ordering or position + // though it doesn't appear to change very often + + // As a basic sanity check, let's make sure we at least get non-zero + // MemTotal value + meminfo->total = 0; + + std::vector<std::string> meminfo_lines; + Tokenize(meminfo_data, "\n", &meminfo_lines); + for (std::vector<std::string>::iterator it = meminfo_lines.begin(); + it != meminfo_lines.end(); ++it) { + std::vector<std::string> tokens; + SplitStringAlongWhitespace(*it, &tokens); + // HugePages_* only has a number and no suffix so we can't rely on + // there being exactly 3 tokens. + if (tokens.size() > 1) { + if (tokens[0] == "MemTotal:") { + StringToInt(tokens[1], &meminfo->total); + continue; + } if (tokens[0] == "MemFree:") { + StringToInt(tokens[1], &meminfo->free); + continue; + } if (tokens[0] == "Buffers:") { + StringToInt(tokens[1], &meminfo->buffers); + continue; + } if (tokens[0] == "Cached:") { + StringToInt(tokens[1], &meminfo->cached); + continue; + } if (tokens[0] == "Active(anon):") { + StringToInt(tokens[1], &meminfo->active_anon); + continue; + } if (tokens[0] == "Inactive(anon):") { + StringToInt(tokens[1], &meminfo->inactive_anon); + continue; + } if (tokens[0] == "Active(file):") { + StringToInt(tokens[1], &meminfo->active_file); + continue; + } if (tokens[0] == "Inactive(file):") { + StringToInt(tokens[1], &meminfo->inactive_file); + continue; + } if (tokens[0] == "SwapTotal:") { + StringToInt(tokens[1], &meminfo->swap_total); + continue; + } if (tokens[0] == "SwapFree:") { + StringToInt(tokens[1], &meminfo->swap_free); + continue; + } if (tokens[0] == "Dirty:") { + StringToInt(tokens[1], &meminfo->dirty); + continue; +#if defined(OS_CHROMEOS) + // Chrome OS has a tweaked kernel that allows us to query Shmem, which is + // usually video memory otherwise invisible to the OS. + } if (tokens[0] == "Shmem:") { + StringToInt(tokens[1], &meminfo->shmem); + continue; + } if (tokens[0] == "Slab:") { + StringToInt(tokens[1], &meminfo->slab); + continue; +#endif + } + } else + DLOG(WARNING) << "meminfo: tokens: " << tokens.size() + << " malformed line: " << *it; + } + + // Make sure we got a valid MemTotal. + if (!meminfo->total) + return false; + + return true; +} + +// exposed for testing +bool ParseProcVmstat(const std::string& vmstat_data, + SystemMemoryInfoKB* meminfo) { + // The format of /proc/vmstat is: + // + // nr_free_pages 299878 + // nr_inactive_anon 239863 + // nr_active_anon 1318966 + // nr_inactive_file 2015629 + // ... + // + // We iterate through the whole file because the position of the + // fields are dependent on the kernel version and configuration. + + std::vector<std::string> vmstat_lines; + Tokenize(vmstat_data, "\n", &vmstat_lines); + for (std::vector<std::string>::iterator it = vmstat_lines.begin(); + it != vmstat_lines.end(); ++it) { + std::vector<std::string> tokens; + SplitString(*it, ' ', &tokens); + if (tokens.size() == 2) { + if (tokens[0] == "pswpin") { + StringToInt(tokens[1], &meminfo->pswpin); + continue; + } if (tokens[0] == "pswpout") { + StringToInt(tokens[1], &meminfo->pswpout); + continue; + } if (tokens[0] == "pgmajfault") + StringToInt(tokens[1], &meminfo->pgmajfault); + } + } + + return true; +} + bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) { // Synchronously reading files in /proc is safe. ThreadRestrictions::ScopedAllowIO allow_io; @@ -549,55 +639,13 @@ bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) { DLOG(WARNING) << "Failed to open " << meminfo_file.value(); return false; } - std::vector<std::string> meminfo_fields; - SplitStringAlongWhitespace(meminfo_data, &meminfo_fields); - if (meminfo_fields.size() < kMemCachedIndex) { - DLOG(WARNING) << "Failed to parse " << meminfo_file.value() - << ". Only found " << meminfo_fields.size() << " fields."; + if (!ParseProcMeminfo(meminfo_data, meminfo)) { + DLOG(WARNING) << "Failed to parse " << meminfo_file.value(); return false; } - DCHECK_EQ(meminfo_fields[kMemTotalIndex-1], "MemTotal:"); - DCHECK_EQ(meminfo_fields[kMemFreeIndex-1], "MemFree:"); - DCHECK_EQ(meminfo_fields[kMemBuffersIndex-1], "Buffers:"); - DCHECK_EQ(meminfo_fields[kMemCachedIndex-1], "Cached:"); - DCHECK_EQ(meminfo_fields[kMemActiveAnonIndex-1], "Active(anon):"); - DCHECK_EQ(meminfo_fields[kMemInactiveAnonIndex-1], "Inactive(anon):"); - DCHECK_EQ(meminfo_fields[kMemActiveFileIndex-1], "Active(file):"); - DCHECK_EQ(meminfo_fields[kMemInactiveFileIndex-1], "Inactive(file):"); - - StringToInt(meminfo_fields[kMemTotalIndex], &meminfo->total); - StringToInt(meminfo_fields[kMemFreeIndex], &meminfo->free); - StringToInt(meminfo_fields[kMemBuffersIndex], &meminfo->buffers); - StringToInt(meminfo_fields[kMemCachedIndex], &meminfo->cached); - StringToInt(meminfo_fields[kMemActiveAnonIndex], &meminfo->active_anon); - StringToInt(meminfo_fields[kMemInactiveAnonIndex], &meminfo->inactive_anon); - StringToInt(meminfo_fields[kMemActiveFileIndex], &meminfo->active_file); - StringToInt(meminfo_fields[kMemInactiveFileIndex], &meminfo->inactive_file); - - // We don't know when these fields appear, so we must search for them. - for (size_t i = kMemCachedIndex+2; i < meminfo_fields.size(); i += 3) { - if (meminfo_fields[i] == "SwapTotal:") - StringToInt(meminfo_fields[i+1], &meminfo->swap_total); - if (meminfo_fields[i] == "SwapFree:") - StringToInt(meminfo_fields[i+1], &meminfo->swap_free); - if (meminfo_fields[i] == "Dirty:") - StringToInt(meminfo_fields[i+1], &meminfo->dirty); - } - #if defined(OS_CHROMEOS) - // Chrome OS has a tweaked kernel that allows us to query Shmem, which is - // usually video memory otherwise invisible to the OS. Unfortunately, the - // meminfo format varies on different hardware so we have to search for the - // string. It always appears after "Cached:". - for (size_t i = kMemCachedIndex+2; i < meminfo_fields.size(); i += 3) { - if (meminfo_fields[i] == "Shmem:") - StringToInt(meminfo_fields[i+1], &meminfo->shmem); - if (meminfo_fields[i] == "Slab:") - StringToInt(meminfo_fields[i+1], &meminfo->slab); - } - // Report on Chrome OS GEM object graphics memory. /var/run/debugfs_gpu is a // bind mount into /sys/kernel/debug and synchronously reading the in-memory // files in /sys is fast. @@ -640,15 +688,10 @@ bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) { DLOG(WARNING) << "Failed to open " << vmstat_file.value(); return false; } - - std::vector<std::string> vmstat_fields; - SplitStringAlongWhitespace(vmstat_data, &vmstat_fields); - if (vmstat_fields[kVMPagesSwappedIn-1] == "pswpin") - StringToInt(vmstat_fields[kVMPagesSwappedIn], &meminfo->pswpin); - if (vmstat_fields[kVMPagesSwappedOut-1] == "pswpout") - StringToInt(vmstat_fields[kVMPagesSwappedOut], &meminfo->pswpout); - if (vmstat_fields[kVMPageMajorFaults-1] == "pgmajfault") - StringToInt(vmstat_fields[kVMPageMajorFaults], &meminfo->pgmajfault); + if (!ParseProcVmstat(vmstat_data, meminfo)) { + DLOG(WARNING) << "Failed to parse " << vmstat_file.value(); + return false; + } return true; } diff --git a/chromium/base/process/process_metrics_unittest.cc b/chromium/base/process/process_metrics_unittest.cc new file mode 100644 index 00000000000..5d365d9a203 --- /dev/null +++ b/chromium/base/process/process_metrics_unittest.cc @@ -0,0 +1,397 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/process/process_metrics.h" + +#include <sstream> +#include <string> + +#include "base/threading/thread.h" +#include "testing/gtest/include/gtest/gtest.h" + + +namespace base { +namespace debug { + +// Tests for SystemMetrics. +// Exists as a class so it can be a friend of SystemMetrics. +class SystemMetricsTest : public testing::Test { + public: + SystemMetricsTest() {} + + private: + DISALLOW_COPY_AND_ASSIGN(SystemMetricsTest); +}; + +///////////////////////////////////////////////////////////////////////////// + +#if defined(OS_LINUX) || defined(OS_ANDROID) +TEST_F(SystemMetricsTest, IsValidDiskName) { + std::string invalid_input1 = ""; + std::string invalid_input2 = "s"; + std::string invalid_input3 = "sdz+"; + std::string invalid_input4 = "hda0"; + std::string invalid_input5 = "mmcbl"; + std::string invalid_input6 = "mmcblka"; + std::string invalid_input7 = "mmcblkb"; + std::string invalid_input8 = "mmmblk0"; + + EXPECT_FALSE(IsValidDiskName(invalid_input1)); + EXPECT_FALSE(IsValidDiskName(invalid_input2)); + EXPECT_FALSE(IsValidDiskName(invalid_input3)); + EXPECT_FALSE(IsValidDiskName(invalid_input4)); + EXPECT_FALSE(IsValidDiskName(invalid_input5)); + EXPECT_FALSE(IsValidDiskName(invalid_input6)); + EXPECT_FALSE(IsValidDiskName(invalid_input7)); + EXPECT_FALSE(IsValidDiskName(invalid_input8)); + + std::string valid_input1 = "sda"; + std::string valid_input2 = "sdaaaa"; + std::string valid_input3 = "hdz"; + std::string valid_input4 = "mmcblk0"; + std::string valid_input5 = "mmcblk999"; + + EXPECT_TRUE(IsValidDiskName(valid_input1)); + EXPECT_TRUE(IsValidDiskName(valid_input2)); + EXPECT_TRUE(IsValidDiskName(valid_input3)); + EXPECT_TRUE(IsValidDiskName(valid_input4)); + EXPECT_TRUE(IsValidDiskName(valid_input5)); +} + +TEST_F(SystemMetricsTest, ParseMeminfo) { + struct SystemMemoryInfoKB meminfo; + std::string invalid_input1 = "abc"; + std::string invalid_input2 = "MemTotal:"; + // Partial file with no MemTotal + std::string invalid_input3 = + "MemFree: 3913968 kB\n" + "Buffers: 2348340 kB\n" + "Cached: 49071596 kB\n" + "SwapCached: 12 kB\n" + "Active: 36393900 kB\n" + "Inactive: 21221496 kB\n" + "Active(anon): 5674352 kB\n" + "Inactive(anon): 633992 kB\n"; + EXPECT_FALSE(ParseProcMeminfo(invalid_input1, &meminfo)); + EXPECT_FALSE(ParseProcMeminfo(invalid_input2, &meminfo)); + EXPECT_FALSE(ParseProcMeminfo(invalid_input3, &meminfo)); + + std::string valid_input1 = + "MemTotal: 3981504 kB\n" + "MemFree: 140764 kB\n" + "Buffers: 116480 kB\n" + "Cached: 406160 kB\n" + "SwapCached: 21304 kB\n" + "Active: 3152040 kB\n" + "Inactive: 472856 kB\n" + "Active(anon): 2972352 kB\n" + "Inactive(anon): 270108 kB\n" + "Active(file): 179688 kB\n" + "Inactive(file): 202748 kB\n" + "Unevictable: 0 kB\n" + "Mlocked: 0 kB\n" + "SwapTotal: 5832280 kB\n" + "SwapFree: 3672368 kB\n" + "Dirty: 184 kB\n" + "Writeback: 0 kB\n" + "AnonPages: 3101224 kB\n" + "Mapped: 142296 kB\n" + "Shmem: 140204 kB\n" + "Slab: 54212 kB\n" + "SReclaimable: 30936 kB\n" + "SUnreclaim: 23276 kB\n" + "KernelStack: 2464 kB\n" + "PageTables: 24812 kB\n" + "NFS_Unstable: 0 kB\n" + "Bounce: 0 kB\n" + "WritebackTmp: 0 kB\n" + "CommitLimit: 7823032 kB\n" + "Committed_AS: 7973536 kB\n" + "VmallocTotal: 34359738367 kB\n" + "VmallocUsed: 375940 kB\n" + "VmallocChunk: 34359361127 kB\n" + "DirectMap4k: 72448 kB\n" + "DirectMap2M: 4061184 kB\n"; + // output from a much older kernel where the Active and Inactive aren't + // broken down into anon and file and Huge Pages are enabled + std::string valid_input2 = + "MemTotal: 255908 kB\n" + "MemFree: 69936 kB\n" + "Buffers: 15812 kB\n" + "Cached: 115124 kB\n" + "SwapCached: 0 kB\n" + "Active: 92700 kB\n" + "Inactive: 63792 kB\n" + "HighTotal: 0 kB\n" + "HighFree: 0 kB\n" + "LowTotal: 255908 kB\n" + "LowFree: 69936 kB\n" + "SwapTotal: 524280 kB\n" + "SwapFree: 524200 kB\n" + "Dirty: 4 kB\n" + "Writeback: 0 kB\n" + "Mapped: 42236 kB\n" + "Slab: 25912 kB\n" + "Committed_AS: 118680 kB\n" + "PageTables: 1236 kB\n" + "VmallocTotal: 3874808 kB\n" + "VmallocUsed: 1416 kB\n" + "VmallocChunk: 3872908 kB\n" + "HugePages_Total: 0\n" + "HugePages_Free: 0\n" + "Hugepagesize: 4096 kB\n"; + + EXPECT_TRUE(ParseProcMeminfo(valid_input1, &meminfo)); + EXPECT_TRUE(meminfo.total == 3981504); + EXPECT_TRUE(meminfo.free == 140764); + EXPECT_TRUE(meminfo.buffers == 116480); + EXPECT_TRUE(meminfo.cached == 406160); + EXPECT_TRUE(meminfo.active_anon == 2972352); + EXPECT_TRUE(meminfo.active_file == 179688); + EXPECT_TRUE(meminfo.inactive_anon == 270108); + EXPECT_TRUE(meminfo.inactive_file == 202748); + EXPECT_TRUE(meminfo.swap_total == 5832280); + EXPECT_TRUE(meminfo.swap_free == 3672368); + EXPECT_TRUE(meminfo.dirty == 184); +#if defined(OS_CHROMEOS) + EXPECT_TRUE(meminfo.shmem == 140204); + EXPECT_TRUE(meminfo.slab == 54212); +#endif + EXPECT_TRUE(ParseProcMeminfo(valid_input2, &meminfo)); + EXPECT_TRUE(meminfo.total == 255908); + EXPECT_TRUE(meminfo.free == 69936); + EXPECT_TRUE(meminfo.buffers == 15812); + EXPECT_TRUE(meminfo.cached == 115124); + EXPECT_TRUE(meminfo.swap_total == 524280); + EXPECT_TRUE(meminfo.swap_free == 524200); + EXPECT_TRUE(meminfo.dirty == 4); +} + +TEST_F(SystemMetricsTest, ParseVmstat) { + struct SystemMemoryInfoKB meminfo; + // part of vmstat from a 3.2 kernel with numa enabled + std::string valid_input1 = + "nr_free_pages 905104\n" + "nr_inactive_anon 142478" + "nr_active_anon 1520046\n" + "nr_inactive_file 4481001\n" + "nr_active_file 8313439\n" + "nr_unevictable 5044\n" + "nr_mlock 5044\n" + "nr_anon_pages 1633780\n" + "nr_mapped 104742\n" + "nr_file_pages 12828218\n" + "nr_dirty 245\n" + "nr_writeback 0\n" + "nr_slab_reclaimable 831609\n" + "nr_slab_unreclaimable 41164\n" + "nr_page_table_pages 31470\n" + "nr_kernel_stack 1735\n" + "nr_unstable 0\n" + "nr_bounce 0\n" + "nr_vmscan_write 406\n" + "nr_vmscan_immediate_reclaim 281\n" + "nr_writeback_temp 0\n" + "nr_isolated_anon 0\n" + "nr_isolated_file 0\n" + "nr_shmem 28820\n" + "nr_dirtied 84674644\n" + "nr_written 75307109\n" + "nr_anon_transparent_hugepages 0\n" + "nr_dirty_threshold 1536206\n" + "nr_dirty_background_threshold 768103\n" + "pgpgin 30777108\n" + "pgpgout 319023278\n" + "pswpin 179\n" + "pswpout 406\n" + "pgalloc_dma 0\n" + "pgalloc_dma32 20833399\n" + "pgalloc_normal 1622609290\n" + "pgalloc_movable 0\n" + "pgfree 1644355583\n" + "pgactivate 75391882\n" + "pgdeactivate 4121019\n" + "pgfault 2542879679\n" + "pgmajfault 487192\n"; + std::string valid_input2 = + "nr_free_pages 180125\n" + "nr_inactive_anon 51\n" + "nr_active_anon 38832\n" + "nr_inactive_file 50171\n" + "nr_active_file 47510\n" + "nr_unevictable 0\n" + "nr_mlock 0\n" + "nr_anon_pages 38825\n" + "nr_mapped 24043\n" + "nr_file_pages 97733\n" + "nr_dirty 0\n" + "nr_writeback 0\n" + "nr_slab_reclaimable 4032\n" + "nr_slab_unreclaimable 2848\n" + "nr_page_table_pages 1505\n" + "nr_kernel_stack 626\n" + "nr_unstable 0\n" + "nr_bounce 0\n" + "nr_vmscan_write 0\n" + "nr_vmscan_immediate_reclaim 0\n" + "nr_writeback_temp 0\n" + "nr_isolated_anon 0\n" + "nr_isolated_file 0\n" + "nr_shmem 58\n" + "nr_dirtied 435358\n" + "nr_written 401258\n" + "nr_anon_transparent_hugepages 0\n" + "nr_dirty_threshold 18566\n" + "nr_dirty_background_threshold 4641\n" + "pgpgin 299464\n" + "pgpgout 2437788\n" + "pswpin 12\n" + "pswpout 901\n" + "pgalloc_normal 144213030\n" + "pgalloc_high 164501274\n" + "pgalloc_movable 0\n" + "pgfree 308894908\n" + "pgactivate 239320\n" + "pgdeactivate 1\n" + "pgfault 716044601\n" + "pgmajfault 2023\n" + "pgrefill_normal 0\n" + "pgrefill_high 0\n" + "pgrefill_movable 0\n"; + EXPECT_TRUE(ParseProcVmstat(valid_input1, &meminfo)); + EXPECT_TRUE(meminfo.pswpin == 179); + EXPECT_TRUE(meminfo.pswpout == 406); + EXPECT_TRUE(meminfo.pgmajfault == 487192); + EXPECT_TRUE(ParseProcVmstat(valid_input2, &meminfo)); + EXPECT_TRUE(meminfo.pswpin == 12); + EXPECT_TRUE(meminfo.pswpout == 901); + EXPECT_TRUE(meminfo.pgmajfault == 2023); +} +#endif // defined(OS_LINUX) || defined(OS_ANDROID) + +#if defined(OS_LINUX) || defined(OS_ANDROID) +TEST(SystemMetrics2Test, GetSystemMemoryInfo) { + base::SystemMemoryInfoKB info; + EXPECT_TRUE(base::GetSystemMemoryInfo(&info)); + + // Ensure each field received a value. + EXPECT_GT(info.total, 0); + EXPECT_GT(info.free, 0); + EXPECT_GT(info.buffers, 0); + EXPECT_GT(info.cached, 0); + EXPECT_GT(info.active_anon, 0); + EXPECT_GT(info.inactive_anon, 0); + EXPECT_GT(info.active_file, 0); + EXPECT_GT(info.inactive_file, 0); + + // All the values should be less than the total amount of memory. + EXPECT_LT(info.free, info.total); + EXPECT_LT(info.buffers, info.total); + EXPECT_LT(info.cached, info.total); + EXPECT_LT(info.active_anon, info.total); + EXPECT_LT(info.inactive_anon, info.total); + EXPECT_LT(info.active_file, info.total); + EXPECT_LT(info.inactive_file, info.total); + +#if defined(OS_CHROMEOS) + // Chrome OS exposes shmem. + EXPECT_GT(info.shmem, 0); + EXPECT_LT(info.shmem, info.total); + // Chrome unit tests are not run on actual Chrome OS hardware, so gem_objects + // and gem_size cannot be tested here. +#endif +} +#endif // defined(OS_LINUX) || defined(OS_ANDROID) + +#if defined(OS_WIN) +// TODO(estade): if possible, port this test. +TEST(ProcessMetricsTest, CalcFreeMemory) { + scoped_ptr<base::ProcessMetrics> metrics( + base::ProcessMetrics::CreateProcessMetrics(::GetCurrentProcess())); + ASSERT_TRUE(NULL != metrics.get()); + + bool using_tcmalloc = false; + + // Detect if we are using tcmalloc +#if !defined(NO_TCMALLOC) + const char* chrome_allocator = getenv("CHROME_ALLOCATOR"); + if (!chrome_allocator || _stricmp(chrome_allocator, "tcmalloc") == 0) + using_tcmalloc = true; +#endif + + // Typical values here is ~1900 for total and ~1000 for largest. Obviously + // it depends in what other tests have done to this process. + base::FreeMBytes free_mem1 = {0}; + EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem1)); + EXPECT_LT(10u, free_mem1.total); + EXPECT_LT(10u, free_mem1.largest); + EXPECT_GT(2048u, free_mem1.total); + EXPECT_GT(2048u, free_mem1.largest); + EXPECT_GE(free_mem1.total, free_mem1.largest); + EXPECT_TRUE(NULL != free_mem1.largest_ptr); + + // Allocate 20M and check again. It should have gone down. + const int kAllocMB = 20; + scoped_ptr<char[]> alloc(new char[kAllocMB * 1024 * 1024]); + size_t expected_total = free_mem1.total - kAllocMB; + size_t expected_largest = free_mem1.largest; + + base::FreeMBytes free_mem2 = {0}; + EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem2)); + EXPECT_GE(free_mem2.total, free_mem2.largest); + // This test is flaky when using tcmalloc, because tcmalloc + // allocation strategy sometimes results in less than the + // full drop of 20Mb of free memory. + if (!using_tcmalloc) + EXPECT_GE(expected_total, free_mem2.total); + EXPECT_GE(expected_largest, free_mem2.largest); + EXPECT_TRUE(NULL != free_mem2.largest_ptr); +} +#endif // defined(OS_WIN) + +#if defined(OS_LINUX) || defined(OS_ANDROID) +TEST(ProcessMetricsTest, ParseProcStatCPU) { + // /proc/self/stat for a process running "top". + const char kTopStat[] = "960 (top) S 16230 960 16230 34818 960 " + "4202496 471 0 0 0 " + "12 16 0 0 " // <- These are the goods. + "20 0 1 0 121946157 15077376 314 18446744073709551615 4194304 " + "4246868 140733983044336 18446744073709551615 140244213071219 " + "0 0 0 138047495 0 0 0 17 1 0 0 0 0 0"; + EXPECT_EQ(12 + 16, base::ParseProcStatCPU(kTopStat)); + + // cat /proc/self/stat on a random other machine I have. + const char kSelfStat[] = "5364 (cat) R 5354 5364 5354 34819 5364 " + "0 142 0 0 0 " + "0 0 0 0 " // <- No CPU, apparently. + "16 0 1 0 1676099790 2957312 114 4294967295 134512640 134528148 " + "3221224832 3221224344 3086339742 0 0 0 0 0 0 0 17 0 0 0"; + + EXPECT_EQ(0, base::ParseProcStatCPU(kSelfStat)); +} +#endif // defined(OS_LINUX) || defined(OS_ANDROID) + +// Disable on Android because base_unittests runs inside a Dalvik VM that +// starts and stop threads (crbug.com/175563). +#if defined(OS_LINUX) +TEST(ProcessMetricsTest, GetNumberOfThreads) { + const base::ProcessHandle current = base::GetCurrentProcessHandle(); + const int initial_threads = base::GetNumberOfThreads(current); + ASSERT_GT(initial_threads, 0); + const int kNumAdditionalThreads = 10; + { + scoped_ptr<base::Thread> my_threads[kNumAdditionalThreads]; + for (int i = 0; i < kNumAdditionalThreads; ++i) { + my_threads[i].reset(new base::Thread("GetNumberOfThreadsTest")); + my_threads[i]->Start(); + ASSERT_EQ(base::GetNumberOfThreads(current), initial_threads + 1 + i); + } + } + // The Thread destructor will stop them. + ASSERT_EQ(initial_threads, base::GetNumberOfThreads(current)); +} +#endif // defined(OS_LINUX) + +} // namespace debug +} // namespace base diff --git a/chromium/base/process/process_util_unittest_ios.cc b/chromium/base/process/process_metrics_unittest_ios.cc index cad0f1b09f0..3e1ca35b18c 100644 --- a/chromium/base/process/process_util_unittest_ios.cc +++ b/chromium/base/process/process_metrics_unittest_ios.cc @@ -1,12 +1,13 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/memory/scoped_ptr.h" #include "base/process/process_metrics.h" + +#include "base/memory/scoped_ptr.h" #include "testing/gtest/include/gtest/gtest.h" -TEST(ProcessUtilTestIos, Memory) { +TEST(ProcessMetricsTestIos, Memory) { scoped_ptr<base::ProcessMetrics> process_metrics( base::ProcessMetrics::CreateProcessMetrics( base::GetCurrentProcessHandle())); diff --git a/chromium/base/process/process_metrics_unittests.cc b/chromium/base/process/process_metrics_unittests.cc deleted file mode 100644 index 387d860eda5..00000000000 --- a/chromium/base/process/process_metrics_unittests.cc +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/process/process_metrics.h" - -#include <sstream> -#include <string> - -#include "testing/gtest/include/gtest/gtest.h" - - -namespace base { -namespace debug { - -// Tests for SystemMetrics. -// Exists as a class so it can be a friend of SystemMetrics. -class SystemMetricsTest : public testing::Test { - public: - SystemMetricsTest() {} - - private: - DISALLOW_COPY_AND_ASSIGN(SystemMetricsTest); -}; - -///////////////////////////////////////////////////////////////////////////// - -#if defined(OS_LINUX) || defined(OS_ANDROID) -TEST_F(SystemMetricsTest, IsValidDiskName) { - std::string invalid_input1 = ""; - std::string invalid_input2 = "s"; - std::string invalid_input3 = "sdz+"; - std::string invalid_input4 = "hda0"; - std::string invalid_input5 = "mmcbl"; - std::string invalid_input6 = "mmcblka"; - std::string invalid_input7 = "mmcblkb"; - std::string invalid_input8 = "mmmblk0"; - - EXPECT_FALSE(IsValidDiskName(invalid_input1)); - EXPECT_FALSE(IsValidDiskName(invalid_input2)); - EXPECT_FALSE(IsValidDiskName(invalid_input3)); - EXPECT_FALSE(IsValidDiskName(invalid_input4)); - EXPECT_FALSE(IsValidDiskName(invalid_input5)); - EXPECT_FALSE(IsValidDiskName(invalid_input6)); - EXPECT_FALSE(IsValidDiskName(invalid_input7)); - EXPECT_FALSE(IsValidDiskName(invalid_input8)); - - std::string valid_input1 = "sda"; - std::string valid_input2 = "sdaaaa"; - std::string valid_input3 = "hdz"; - std::string valid_input4 = "mmcblk0"; - std::string valid_input5 = "mmcblk999"; - - EXPECT_TRUE(IsValidDiskName(valid_input1)); - EXPECT_TRUE(IsValidDiskName(valid_input2)); - EXPECT_TRUE(IsValidDiskName(valid_input3)); - EXPECT_TRUE(IsValidDiskName(valid_input4)); - EXPECT_TRUE(IsValidDiskName(valid_input5)); -} -#endif // defined(OS_LINUX) || defined(OS_ANDROID) - -} // namespace debug -} // namespace base diff --git a/chromium/base/process/process_util_unittest.cc b/chromium/base/process/process_util_unittest.cc index 44be9f4a273..6bfc1d07388 100644 --- a/chromium/base/process/process_util_unittest.cc +++ b/chromium/base/process/process_util_unittest.cc @@ -19,7 +19,9 @@ #include "base/process/memory.h" #include "base/process/process.h" #include "base/process/process_metrics.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" +#include "base/synchronization/waitable_event.h" #include "base/test/multiprocess_test.h" #include "base/test/test_timeouts.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" @@ -29,7 +31,6 @@ #include "testing/multiprocess_func_list.h" #if defined(OS_LINUX) -#include <glib.h> #include <malloc.h> #include <sched.h> #endif @@ -44,6 +45,7 @@ #endif #if defined(OS_WIN) #include <windows.h> +#include "base/win/windows_version.h" #endif #if defined(OS_MACOSX) #include <mach/vm_param.h> @@ -54,12 +56,6 @@ using base::FilePath; namespace { -#if defined(OS_WIN) -const wchar_t kProcessName[] = L"base_unittests.exe"; -#else -const wchar_t kProcessName[] = L"base_unittests"; -#endif // defined(OS_WIN) - #if defined(OS_ANDROID) const char kShellPath[] = "/system/bin/sh"; const char kPosixShell[] = "sh"; @@ -69,7 +65,6 @@ const char kPosixShell[] = "bash"; #endif const char kSignalFileSlow[] = "SlowChildProcess.die"; -const char kSignalFileCrash[] = "CrashingChildProcess.die"; const char kSignalFileKill[] = "KilledChildProcess.die"; #if defined(OS_WIN) @@ -221,6 +216,7 @@ TEST_F(ProcessUtilTest, GetProcId) { // TODO(gspencer): turn this test process into a very small program // with no symbols (instead of using the multiprocess testing // framework) to reduce the ReportCrash overhead. +const char kSignalFileCrash[] = "CrashingChildProcess.die"; MULTIPROCESS_TEST_MAIN(CrashingChildProcess) { WaitToDie(ProcessUtilTest::GetSignalFilePath(kSignalFileCrash).c_str()); @@ -360,85 +356,8 @@ TEST_F(ProcessUtilTest, SetProcessBackgroundedSelf) { EXPECT_EQ(old_priority, new_priority); } -#if defined(OS_LINUX) || defined(OS_ANDROID) -TEST_F(ProcessUtilTest, GetSystemMemoryInfo) { - base::SystemMemoryInfoKB info; - EXPECT_TRUE(base::GetSystemMemoryInfo(&info)); - - // Ensure each field received a value. - EXPECT_GT(info.total, 0); - EXPECT_GT(info.free, 0); - EXPECT_GT(info.buffers, 0); - EXPECT_GT(info.cached, 0); - EXPECT_GT(info.active_anon, 0); - EXPECT_GT(info.inactive_anon, 0); - EXPECT_GT(info.active_file, 0); - EXPECT_GT(info.inactive_file, 0); - - // All the values should be less than the total amount of memory. - EXPECT_LT(info.free, info.total); - EXPECT_LT(info.buffers, info.total); - EXPECT_LT(info.cached, info.total); - EXPECT_LT(info.active_anon, info.total); - EXPECT_LT(info.inactive_anon, info.total); - EXPECT_LT(info.active_file, info.total); - EXPECT_LT(info.inactive_file, info.total); - -#if defined(OS_CHROMEOS) - // Chrome OS exposes shmem. - EXPECT_GT(info.shmem, 0); - EXPECT_LT(info.shmem, info.total); - // Chrome unit tests are not run on actual Chrome OS hardware, so gem_objects - // and gem_size cannot be tested here. -#endif -} -#endif // defined(OS_LINUX) || defined(OS_ANDROID) - -// TODO(estade): if possible, port these 2 tests. #if defined(OS_WIN) -TEST_F(ProcessUtilTest, CalcFreeMemory) { - scoped_ptr<base::ProcessMetrics> metrics( - base::ProcessMetrics::CreateProcessMetrics(::GetCurrentProcess())); - ASSERT_TRUE(NULL != metrics.get()); - - bool using_tcmalloc = false; - - // Detect if we are using tcmalloc -#if !defined(NO_TCMALLOC) - const char* chrome_allocator = getenv("CHROME_ALLOCATOR"); - if (!chrome_allocator || _stricmp(chrome_allocator, "tcmalloc") == 0) - using_tcmalloc = true; -#endif - - // Typical values here is ~1900 for total and ~1000 for largest. Obviously - // it depends in what other tests have done to this process. - base::FreeMBytes free_mem1 = {0}; - EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem1)); - EXPECT_LT(10u, free_mem1.total); - EXPECT_LT(10u, free_mem1.largest); - EXPECT_GT(2048u, free_mem1.total); - EXPECT_GT(2048u, free_mem1.largest); - EXPECT_GE(free_mem1.total, free_mem1.largest); - EXPECT_TRUE(NULL != free_mem1.largest_ptr); - - // Allocate 20M and check again. It should have gone down. - const int kAllocMB = 20; - scoped_ptr<char[]> alloc(new char[kAllocMB * 1024 * 1024]); - size_t expected_total = free_mem1.total - kAllocMB; - size_t expected_largest = free_mem1.largest; - - base::FreeMBytes free_mem2 = {0}; - EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem2)); - EXPECT_GE(free_mem2.total, free_mem2.largest); - // This test is flaky when using tcmalloc, because tcmalloc - // allocation strategy sometimes results in less than the - // full drop of 20Mb of free memory. - if (!using_tcmalloc) - EXPECT_GE(expected_total, free_mem2.total); - EXPECT_GE(expected_largest, free_mem2.largest); - EXPECT_TRUE(NULL != free_mem2.largest_ptr); -} - +// TODO(estade): if possible, port this test. TEST_F(ProcessUtilTest, GetAppOutput) { // Let's create a decently long message. std::string message; @@ -468,16 +387,64 @@ TEST_F(ProcessUtilTest, GetAppOutput) { EXPECT_EQ("", output); } +// TODO(estade): if possible, port this test. TEST_F(ProcessUtilTest, LaunchAsUser) { base::UserTokenHandle token; ASSERT_TRUE(OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)); - std::wstring cmdline = - this->MakeCmdLine("SimpleChildProcess", false).GetCommandLineString(); base::LaunchOptions options; options.as_user = token; - EXPECT_TRUE(base::LaunchProcess(cmdline, options, NULL)); + EXPECT_TRUE(base::LaunchProcess( + this->MakeCmdLine("SimpleChildProcess", false), options, NULL)); +} + +static const char kEventToTriggerHandleSwitch[] = "event-to-trigger-handle"; + +MULTIPROCESS_TEST_MAIN(TriggerEventChildProcess) { + std::string handle_value_string = + CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + kEventToTriggerHandleSwitch); + CHECK(!handle_value_string.empty()); + + uint64 handle_value_uint64; + CHECK(base::StringToUint64(handle_value_string, &handle_value_uint64)); + // Give ownership of the handle to |event|. + base::WaitableEvent event(reinterpret_cast<HANDLE>(handle_value_uint64)); + + event.Signal(); + + return 0; } +TEST_F(ProcessUtilTest, InheritSpecifiedHandles) { + // Manually create the event, so that it can be inheritable. + SECURITY_ATTRIBUTES security_attributes = {}; + security_attributes.nLength = static_cast<DWORD>(sizeof(security_attributes)); + security_attributes.lpSecurityDescriptor = NULL; + security_attributes.bInheritHandle = true; + + // Takes ownership of the event handle. + base::WaitableEvent event( + CreateEvent(&security_attributes, true, false, NULL)); + base::HandlesToInheritVector handles_to_inherit; + handles_to_inherit.push_back(event.handle()); + base::LaunchOptions options; + options.handles_to_inherit = &handles_to_inherit; + + CommandLine cmd_line = MakeCmdLine("TriggerEventChildProcess", false); + cmd_line.AppendSwitchASCII(kEventToTriggerHandleSwitch, + base::Uint64ToString(reinterpret_cast<uint64>(event.handle()))); + + // This functionality actually requires Vista or later. Make sure that it + // fails properly on XP. + if (base::win::GetVersion() < base::win::VERSION_VISTA) { + EXPECT_FALSE(base::LaunchProcess(cmd_line, options, NULL)); + return; + } + + // Launch the process and wait for it to trigger the event. + ASSERT_TRUE(base::LaunchProcess(cmd_line, options, NULL)); + EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout())); +} #endif // defined(OS_WIN) #if defined(OS_POSIX) @@ -525,7 +492,7 @@ MULTIPROCESS_TEST_MAIN(ProcessUtilsLeakFDChildProcess) { int written = HANDLE_EINTR(write(write_pipe, &num_open_files, sizeof(num_open_files))); DCHECK_EQ(static_cast<size_t>(written), sizeof(num_open_files)); - int ret = HANDLE_EINTR(close(write_pipe)); + int ret = IGNORE_EINTR(close(write_pipe)); DPCHECK(ret == 0); return 0; @@ -541,7 +508,7 @@ int ProcessUtilTest::CountOpenFDsInChild() { base::ProcessHandle handle = this->SpawnChild( "ProcessUtilsLeakFDChildProcess", fd_mapping_vec, false); CHECK(handle); - int ret = HANDLE_EINTR(close(fds[1])); + int ret = IGNORE_EINTR(close(fds[1])); DPCHECK(ret == 0); // Read number of open files in client process from pipe; @@ -557,7 +524,7 @@ int ProcessUtilTest::CountOpenFDsInChild() { CHECK(base::WaitForSingleProcess(handle, base::TimeDelta::FromSeconds(1))); #endif base::CloseProcessHandle(handle); - ret = HANDLE_EINTR(close(fds[0])); + ret = IGNORE_EINTR(close(fds[0])); DPCHECK(ret == 0); return num_open_files; @@ -585,11 +552,11 @@ TEST_F(ProcessUtilTest, MAYBE_FDRemapping) { ASSERT_EQ(fds_after, fds_before); int ret; - ret = HANDLE_EINTR(close(sockets[0])); + ret = IGNORE_EINTR(close(sockets[0])); DPCHECK(ret == 0); - ret = HANDLE_EINTR(close(sockets[1])); + ret = IGNORE_EINTR(close(sockets[1])); DPCHECK(ret == 0); - ret = HANDLE_EINTR(close(dev_null)); + ret = IGNORE_EINTR(close(dev_null)); DPCHECK(ret == 0); } @@ -618,13 +585,13 @@ std::string TestLaunchProcess(const base::EnvironmentMap& env_changes, CHECK_EQ(0, clone_flags); #endif // OS_LINUX EXPECT_TRUE(base::LaunchProcess(args, options, NULL)); - PCHECK(HANDLE_EINTR(close(fds[1])) == 0); + PCHECK(IGNORE_EINTR(close(fds[1])) == 0); char buf[512]; const ssize_t n = HANDLE_EINTR(read(fds[0], buf, sizeof(buf))); PCHECK(n > 0); - PCHECK(HANDLE_EINTR(close(fds[0])) == 0); + PCHECK(IGNORE_EINTR(close(fds[0])) == 0); return std::string(buf, n); } @@ -775,7 +742,16 @@ TEST_F(ProcessUtilTest, GetAppOutputRestrictedSIGPIPE) { } #endif -TEST_F(ProcessUtilTest, GetAppOutputRestrictedNoZombies) { +#if defined(ADDRESS_SANITIZER) && defined(OS_MACOSX) && \ + defined(ARCH_CPU_64_BITS) +// Times out under AddressSanitizer on 64-bit OS X, see +// http://crbug.com/298197. +#define MAYBE_GetAppOutputRestrictedNoZombies \ + DISABLED_GetAppOutputRestrictedNoZombies +#else +#define MAYBE_GetAppOutputRestrictedNoZombies GetAppOutputRestrictedNoZombies +#endif +TEST_F(ProcessUtilTest, MAYBE_GetAppOutputRestrictedNoZombies) { std::vector<std::string> argv; argv.push_back(std::string(kShellPath)); // argv[0] @@ -826,50 +802,6 @@ TEST_F(ProcessUtilTest, GetParentProcessId) { EXPECT_EQ(ppid, getppid()); } -#if defined(OS_LINUX) || defined(OS_ANDROID) -TEST_F(ProcessUtilTest, ParseProcStatCPU) { - // /proc/self/stat for a process running "top". - const char kTopStat[] = "960 (top) S 16230 960 16230 34818 960 " - "4202496 471 0 0 0 " - "12 16 0 0 " // <- These are the goods. - "20 0 1 0 121946157 15077376 314 18446744073709551615 4194304 " - "4246868 140733983044336 18446744073709551615 140244213071219 " - "0 0 0 138047495 0 0 0 17 1 0 0 0 0 0"; - EXPECT_EQ(12 + 16, base::ParseProcStatCPU(kTopStat)); - - // cat /proc/self/stat on a random other machine I have. - const char kSelfStat[] = "5364 (cat) R 5354 5364 5354 34819 5364 " - "0 142 0 0 0 " - "0 0 0 0 " // <- No CPU, apparently. - "16 0 1 0 1676099790 2957312 114 4294967295 134512640 134528148 " - "3221224832 3221224344 3086339742 0 0 0 0 0 0 0 17 0 0 0"; - - EXPECT_EQ(0, base::ParseProcStatCPU(kSelfStat)); -} - -// Disable on Android because base_unittests runs inside a Dalvik VM that -// starts and stop threads (crbug.com/175563). -#if !defined(OS_ANDROID) -TEST_F(ProcessUtilTest, GetNumberOfThreads) { - const base::ProcessHandle current = base::GetCurrentProcessHandle(); - const int initial_threads = base::GetNumberOfThreads(current); - ASSERT_GT(initial_threads, 0); - const int kNumAdditionalThreads = 10; - { - scoped_ptr<base::Thread> my_threads[kNumAdditionalThreads]; - for (int i = 0; i < kNumAdditionalThreads; ++i) { - my_threads[i].reset(new base::Thread("GetNumberOfThreadsTest")); - my_threads[i]->Start(); - ASSERT_EQ(base::GetNumberOfThreads(current), initial_threads + 1 + i); - } - } - // The Thread destructor will stop them. - ASSERT_EQ(initial_threads, base::GetNumberOfThreads(current)); -} -#endif // !defined(OS_ANDROID) - -#endif // defined(OS_LINUX) || defined(OS_ANDROID) - // TODO(port): port those unit tests. bool IsProcessDead(base::ProcessHandle child) { // waitpid() will actually reap the process which is exactly NOT what we |