summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-22 01:15:47 +0000
committerestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-22 01:15:47 +0000
commitfb7f9bef3ceecd5a3efb9252410f6850320462b8 (patch)
tree95cefb13965daa20689cebec67e48352be1ccfbf
parent0cbd643274f17abca0efaa82036ac8ccbc695be3 (diff)
downloadchromium_src-fb7f9bef3ceecd5a3efb9252410f6850320462b8.zip
chromium_src-fb7f9bef3ceecd5a3efb9252410f6850320462b8.tar.gz
chromium_src-fb7f9bef3ceecd5a3efb9252410f6850320462b8.tar.bz2
Added linux process utilities and tests.
Review URL: http://codereview.chromium.org/7202 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@3715 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/base_unittest_exported_symbols.version5
-rw-r--r--base/base_unittests.scons29
-rw-r--r--base/multiprocess_test.h38
-rw-r--r--base/port.h2
-rw-r--r--base/process_util.h25
-rw-r--r--base/process_util_linux.cc46
-rw-r--r--base/process_util_unittest.cc32
-rw-r--r--base/process_util_win.cc13
-rw-r--r--base/stats_table_unittest.cc63
-rw-r--r--base/test_suite.h20
10 files changed, 225 insertions, 48 deletions
diff --git a/base/base_unittest_exported_symbols.version b/base/base_unittest_exported_symbols.version
new file mode 100644
index 0000000..357ed6b
--- /dev/null
+++ b/base/base_unittest_exported_symbols.version
@@ -0,0 +1,5 @@
+# list of functions exported from the Linux base_unittests executable.
+{
+ SimpleChildProcess;
+ StatsTableMultipleProcessMain;
+};
diff --git a/base/base_unittests.scons b/base/base_unittests.scons
index fbb7c0c..3fcd89c 100644
--- a/base/base_unittests.scons
+++ b/base/base_unittests.scons
@@ -59,6 +59,22 @@ if env['PLATFORM'] == 'win32':
],
)
+if env['PLATFORM'] == 'posix':
+ # Explicity list the functions we want to export from the base_unittest
+ # executable in the following file.
+ exported_symbols_filename = \
+ env.Dir('#').abspath + '/base_unittest_exported_symbols.version'
+
+ env.Append(
+ LIBS = [
+ 'event',
+ ],
+ LINKFLAGS = [
+ '-Xlinker',
+ '--dynamic-list=' + exported_symbols_filename,
+ ],
+ )
+
# These test files work on *all* platforms; tests that don't work
# cross-platform live below.
input_files = [
@@ -127,8 +143,6 @@ if env['PLATFORM'] in ('posix', 'darwin'):
to_be_ported_files = [
'clipboard_unittest.cc',
'idletimer_unittest.cc',
- 'process_util_unittest.cc',
- 'stats_table_unittest.cc',
'watchdog_unittest.cc',
'gfx/native_theme_unittest.cc',
'gfx/uniscribe_unittest.cc',
@@ -137,6 +151,17 @@ if env['PLATFORM'] in ('posix', 'darwin'):
for remove in to_be_ported_files:
input_files.remove(remove)
+if env['PLATFORM'] == 'darwin':
+ # Remove files that still need to be ported from the input_files list.
+ # TODO(port): delete files from this list as they get ported.
+ to_be_ported_files = [
+ 'process_util_unittest.cc',
+ 'stats_table_unittest.cc',
+ ]
+ for remove in to_be_ported_files:
+ input_files.remove(remove)
+
+
if env['PLATFORM'] == 'win32':
# Windows-specific tests.
input_files.extend([
diff --git a/base/multiprocess_test.h b/base/multiprocess_test.h
index 93bc5e7..15085d9 100644
--- a/base/multiprocess_test.h
+++ b/base/multiprocess_test.h
@@ -7,8 +7,14 @@
#include "base/command_line.h"
#include "base/process_util.h"
+#include "base/string_util.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if defined(OS_POSIX)
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
// Command line switch to invoke a child process rather than
// to run the normal test suite.
static const wchar_t kRunClientProcess[] = L"client";
@@ -24,13 +30,15 @@ static const wchar_t kRunClientProcess[] = L"client";
// 3) Create a mainline function for the child processes
// 4) Call SpawnChild("foo"), where "foo" is the name of
// the function you wish to run in the child processes.
+// 5) On posix, add the function's name to the list in the file
+// base_unittests_exported_symbols.version
// That's it!
//
class MultiProcessTest : public testing::Test {
public:
// Prototype function for a client function. Multi-process
// clients must provide a callback with this signature to run.
- typedef int (__cdecl *ChildFunctionPtr)();
+ typedef int (*ChildFunctionPtr)();
protected:
// Run a child process.
@@ -44,13 +52,27 @@ class MultiProcessTest : public testing::Test {
// }
//
// Returns the handle to the child, or NULL on failure
- HANDLE SpawnChild(const std::wstring& procname) {
- std::wstring cl(GetCommandLineW());
- CommandLine::AppendSwitchWithValue(&cl, kRunClientProcess, procname);
- // TODO(darin): re-enable this once we have base/debug_util.h
- //ProcessDebugFlags(&cl, DebugUtil::UNKNOWN, false);
- HANDLE handle = NULL;
- process_util::LaunchApp(cl, false, true, &handle);
+ //
+ // TODO(darin): re-enable this once we have base/debug_util.h
+ // ProcessDebugFlags(&cl, DebugUtil::UNKNOWN, false);
+ ProcessHandle SpawnChild(const std::wstring& procname) {
+ CommandLine cl;
+ ProcessHandle handle = static_cast<ProcessHandle>(NULL);
+
+#if defined(OS_WIN)
+ std::wstring clstr = cl.command_line_string();
+ CommandLine::AppendSwitchWithValue(&clstr, kRunClientProcess, procname);
+ process_util::LaunchApp(clstr, false, true, &handle);
+#elif defined(OS_POSIX)
+ std::vector<std::string> clvec(cl.argv());
+ std::wstring wswitchstr =
+ CommandLine::PrefixedSwitchStringWithValue(kRunClientProcess,
+ procname);
+ std::string switchstr = WideToUTF8(wswitchstr);
+ clvec.push_back(switchstr.c_str());
+ process_util::LaunchApp(clvec, false, &handle);
+#endif
+
return handle;
}
};
diff --git a/base/port.h b/base/port.h
index 102651f..9207192 100644
--- a/base/port.h
+++ b/base/port.h
@@ -53,8 +53,10 @@ inline void va_copy(va_list& a, va_list& b) {
// Define an OS-neutral wrapper for shared library entry points
#if defined(OS_WIN)
#define API_CALL __stdcall
+#define DYNAMIC_EXPORT __declspec(dllexport)
#elif defined(OS_POSIX)
#define API_CALL
+#define DYNAMIC_EXPORT
#endif
#endif // BASE_PORT_H_
diff --git a/base/process_util.h b/base/process_util.h
index 4f33bb0..503c2ec 100644
--- a/base/process_util.h
+++ b/base/process_util.h
@@ -17,6 +17,7 @@
#include <string>
+#include "base/command_line.h"
#include "base/process.h"
#if defined(OS_WIN)
@@ -47,6 +48,7 @@ ProcessHandle GetCurrentProcessHandle();
// Win XP SP1 as well.
int GetProcId(ProcessHandle process);
+#if defined(OS_WIN)
// Runs the given application name with the given command line. Normally, the
// first command line argument should be the path to the process, and don't
// forget to quote it.
@@ -63,6 +65,24 @@ int GetProcId(ProcessHandle process);
// that it doesn't leak!
bool LaunchApp(const std::wstring& cmdline,
bool wait, bool start_hidden, ProcessHandle* process_handle);
+#elif defined(OS_POSIX)
+// Runs the application specified in argv[0] with the command line argv.
+// Both the elements of argv and argv itself must be terminated with a null
+// byte.
+//
+// As above, if wait is true, execute synchronously. The pid will be stored
+// in process_handle if that pointer is non-null.
+//
+// Note that the first argument in argv must point to the filename,
+// and must be fully specified.
+bool LaunchApp(const std::vector<std::string>& argv,
+ bool wait, ProcessHandle* process_handle);
+#endif
+
+// Execute the application specified by cl. This function delegates to one
+// of the above two platform-specific functions.
+bool LaunchApp(const CommandLine& cl,
+ bool wait, bool start_hidden, ProcessHandle* process_handle);
// Used to filter processes by process ID.
class ProcessFilter {
@@ -106,6 +126,11 @@ bool WaitForProcessesToExit(const std::wstring& executable_name,
int wait_milliseconds,
const ProcessFilter* filter);
+// Wait for a single process to exit. Return true if it exited cleanly within
+// the given time limit.
+bool WaitForSingleProcess(ProcessHandle handle,
+ int wait_milliseconds);
+
// Waits a certain amount of time (can be 0) for all the processes with a given
// executable name to exit, then kills off any of them that are still around.
// If filter is non-null, then only processes selected by the filter are waited
diff --git a/base/process_util_linux.cc b/base/process_util_linux.cc
index e5bc5db..f53ba3b 100644
--- a/base/process_util_linux.cc
+++ b/base/process_util_linux.cc
@@ -5,6 +5,8 @@
#include "base/process_util.h"
#include <string>
+#include <sys/types.h>
+#include <sys/wait.h>
#include "base/file_util.h"
#include "base/logging.h"
@@ -22,6 +24,50 @@ enum ParsingState {
namespace process_util {
+bool LaunchApp(const std::vector<std::string>& argv,
+ bool wait, ProcessHandle* process_handle) {
+ bool retval = true;
+
+ char* argv_copy[argv.size() + 1];
+ for (size_t i = 0; i < argv.size(); i++) {
+ argv_copy[i] = new char[argv[i].size() + 1];
+ strcpy(argv_copy[i], argv[i].c_str());
+ }
+ argv_copy[argv.size()] = NULL;
+
+ int pid = vfork();
+ if (pid == 0) {
+ execv(argv_copy[0], argv_copy);
+ } else if (pid < 0) {
+ retval = false;
+ } else {
+ if (wait)
+ waitpid(pid, 0, 0);
+
+ if(process_handle)
+ *process_handle = pid;
+ }
+
+ for (size_t i = 0; i < argv.size(); i++)
+ delete[] argv_copy[i];
+
+ return retval;
+}
+
+bool LaunchApp(const CommandLine& cl,
+ bool wait, bool start_hidden, ProcessHandle* process_handle) {
+ return LaunchApp(cl.argv(), wait, process_handle);
+}
+
+bool WaitForSingleProcess(ProcessHandle handle, int wait_milliseconds) {
+ int status;
+ waitpid(handle, &status, 0);
+ return WIFEXITED(status);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//// ProcessMetrics
+
// To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING
// in your kernel configuration.
bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) {
diff --git a/base/process_util_unittest.cc b/base/process_util_unittest.cc
index de51fc2..32595c9 100644
--- a/base/process_util_unittest.cc
+++ b/base/process_util_unittest.cc
@@ -4,10 +4,35 @@
#define _CRT_SECURE_NO_WARNINGS
-#include "testing/gtest/include/gtest/gtest.h"
+#include "base/multiprocess_test.h"
#include "base/process_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_LINUX)
+#include <dlfcn.h>
+#endif
+
+namespace {
+class ProcessUtilTest : public MultiProcessTest {
+};
+}
+
+extern "C" int DYNAMIC_EXPORT SimpleChildProcess() {
+ return 0;
+}
+
+TEST_F(ProcessUtilTest, SpawnChild) {
+ ProcessHandle handle = this->SpawnChild(L"SimpleChildProcess");
+
+ ASSERT_NE(static_cast<ProcessHandle>(NULL), handle);
+ EXPECT_TRUE(process_util::WaitForSingleProcess(handle, 1000));
+}
-TEST(ProcessUtilTest, EnableLFH) {
+// TODO(estade): if possible, port these 2 tests.
+#if defined(OS_WIN)
+TEST_F(ProcessUtilTest, EnableLFH) {
ASSERT_TRUE(process_util::EnableLowFragmentationHeap());
if (IsDebuggerPresent()) {
// Under these conditions, LFH can't be enabled. There's no point to test
@@ -38,7 +63,7 @@ TEST(ProcessUtilTest, EnableLFH) {
}
}
-TEST(ProcessUtilTest, CalcFreeMemory) {
+TEST_F(ProcessUtilTest, CalcFreeMemory) {
process_util::ProcessMetrics* metrics =
process_util::ProcessMetrics::CreateProcessMetrics(::GetCurrentProcess());
ASSERT_TRUE(NULL != metrics);
@@ -72,4 +97,5 @@ TEST(ProcessUtilTest, CalcFreeMemory) {
delete[] alloc;
delete metrics;
}
+#endif // defined(OS_WIN)
diff --git a/base/process_util_win.cc b/base/process_util_win.cc
index b464336..b13cbea 100644
--- a/base/process_util_win.cc
+++ b/base/process_util_win.cc
@@ -144,6 +144,12 @@ bool LaunchApp(const std::wstring& cmdline,
return true;
}
+bool LaunchApp(CommandLine& cl,
+ bool wait, bool start_hidden, ProcessHandle* process_handle) {
+ return LaunchApp(cl.command_line_string(), wait,
+ start_hidden, process_handle);
+}
+
// 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.
@@ -316,6 +322,12 @@ bool WaitForProcessesToExit(const std::wstring& executable_name,
return result;
}
+bool WaitForSingleProcess(ProcessHandle handle, int wait_milliseconds) {
+ bool retval = WaitForSingleObject(handle, wait_milliseconds) == WAIT_OBJECT_0;
+ CloseHandle(handle);
+ return retval;
+}
+
bool CleanupProcesses(const std::wstring& executable_name,
int wait_milliseconds,
int exit_code,
@@ -328,7 +340,6 @@ bool CleanupProcesses(const std::wstring& executable_name,
return exited_cleanly;
}
-
///////////////////////////////////////////////////////////////////////////////
// ProcesMetrics
diff --git a/base/stats_table_unittest.cc b/base/stats_table_unittest.cc
index 649fd07..7d5f015 100644
--- a/base/stats_table_unittest.cc
+++ b/base/stats_table_unittest.cc
@@ -2,15 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <process.h>
-#include <windows.h>
-
#include "base/multiprocess_test.h"
+#include "base/platform_thread.h"
#include "base/stats_table.h"
#include "base/stats_counters.h"
#include "base/string_util.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if defined(OS_WIN)
+#include <process.h>
+#include <windows.h>
+#endif
+
namespace {
class StatsTableTest : public MultiProcessTest {
};
@@ -61,6 +64,8 @@ const std::wstring kCounterMixed = L"CounterMixed";
// The number of thread loops that we will do.
const int kThreadLoops = 1000;
+// TODO(estade): port this test
+#if defined(OS_WIN)
unsigned __stdcall StatsTableMultipleThreadMain(void* param) {
// Each thread will open the shared memory and set counters
// concurrently in a loop. We'll use some pauses to
@@ -81,10 +86,11 @@ unsigned __stdcall StatsTableMultipleThreadMain(void* param) {
mixed_counter.Decrement();
else
mixed_counter.Increment();
- Sleep(index % 10); // short wait
+ PlatformThread::Sleep(index % 10); // short wait
}
return 0;
}
+
// Create a few threads and have them poke on their counters.
TEST_F(StatsTableTest, MultipleThreads) {
// Create a stats table.
@@ -140,10 +146,11 @@ TEST_F(StatsTableTest, MultipleThreads) {
table.GetCounterValue(name));
EXPECT_EQ(0, table.CountThreadsRegistered());
}
+#endif // defined(OS_WIN)
const std::wstring kTableName = L"MultipleProcessStatTable";
-extern "C" int __declspec(dllexport) ChildProcessMain() {
+extern "C" int DYNAMIC_EXPORT StatsTableMultipleProcessMain() {
// Each process will open the shared memory and set counters
// concurrently in a loop. We'll use some pauses to
// mixup the scheduling.
@@ -159,38 +166,38 @@ extern "C" int __declspec(dllexport) ChildProcessMain() {
lucky13_counter.Set(1313);
increment_counter.Increment();
decrement_counter.Decrement();
- Sleep(index % 10); // short wait
+ PlatformThread::Sleep(index % 10); // short wait
}
return 0;
}
-// Create a few threads and have them poke on their counters.
+// Create a few processes and have them poke on their counters.
TEST_F(StatsTableTest, MultipleProcesses) {
// Create a stats table.
const std::wstring kTableName = L"MultipleProcessStatTable";
- const int kMaxThreads = 20;
+ const int kMaxProcs = 20;
const int kMaxCounter = 5;
- StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable table(kTableName, kMaxProcs, kMaxCounter);
StatsTable::set_current(&table);
EXPECT_EQ(0, table.CountThreadsRegistered());
- // Spin up a set of threads to go bang on the various counters.
- // After we join the threads, we'll make sure the counters
+ // Spin up a set of processes to go bang on the various counters.
+ // After we join the processes, we'll make sure the counters
// contain the values we expected.
- HANDLE threads[kMaxThreads];
+ ProcessHandle procs[kMaxProcs];
// Spawn the processes.
- for (int16 index = 0; index < kMaxThreads; index++) {
- threads[index] = this->SpawnChild(L"ChildProcessMain");
- EXPECT_NE((HANDLE)NULL, threads[index]);
+ for (int16 index = 0; index < kMaxProcs; index++) {
+ procs[index] = this->SpawnChild(L"StatsTableMultipleProcessMain");
+ EXPECT_NE(static_cast<ProcessHandle>(NULL), procs[index]);
}
- // Wait for the threads to finish.
- for (int index = 0; index < kMaxThreads; index++) {
- DWORD rv = WaitForSingleObject(threads[index], 60 * 1000);
- EXPECT_EQ(rv, WAIT_OBJECT_0); // verify all threads finished
+ // Wait for the processes to finish.
+ for (int index = 0; index < kMaxProcs; index++) {
+ EXPECT_TRUE(process_util::WaitForSingleProcess(procs[index], 60 * 1000));
}
+
StatsCounter zero_counter(kCounterZero);
StatsCounter lucky13_counter(kCounter1313);
StatsCounter increment_counter(kCounterIncrement);
@@ -201,13 +208,13 @@ TEST_F(StatsTableTest, MultipleProcesses) {
name = L"c:" + kCounterZero;
EXPECT_EQ(0, table.GetCounterValue(name));
name = L"c:" + kCounter1313;
- EXPECT_EQ(1313 * kMaxThreads,
+ EXPECT_EQ(1313 * kMaxProcs,
table.GetCounterValue(name));
name = L"c:" + kCounterIncrement;
- EXPECT_EQ(kMaxThreads * kThreadLoops,
+ EXPECT_EQ(kMaxProcs * kThreadLoops,
table.GetCounterValue(name));
name = L"c:" + kCounterDecrement;
- EXPECT_EQ(-kMaxThreads * kThreadLoops,
+ EXPECT_EQ(-kMaxProcs * kThreadLoops,
table.GetCounterValue(name));
EXPECT_EQ(0, table.CountThreadsRegistered());
}
@@ -290,13 +297,13 @@ TEST_F(StatsTableTest, StatsCounterTimer) {
// Do some timing.
bar.Start();
- Sleep(500);
+ PlatformThread::Sleep(500);
bar.Stop();
EXPECT_LE(500, table.GetCounterValue(L"t:bar"));
// Verify that timing again is additive.
bar.Start();
- Sleep(500);
+ PlatformThread::Sleep(500);
bar.Stop();
EXPECT_LE(1000, table.GetCounterValue(L"t:bar"));
}
@@ -319,14 +326,14 @@ TEST_F(StatsTableTest, StatsRate) {
// Do some timing.
baz.Start();
- Sleep(500);
+ PlatformThread::Sleep(500);
baz.Stop();
EXPECT_EQ(1, table.GetCounterValue(L"c:baz"));
EXPECT_LE(500, table.GetCounterValue(L"t:baz"));
// Verify that timing again is additive.
baz.Start();
- Sleep(500);
+ PlatformThread::Sleep(500);
baz.Stop();
EXPECT_EQ(2, table.GetCounterValue(L"c:baz"));
EXPECT_LE(1000, table.GetCounterValue(L"t:baz"));
@@ -353,7 +360,7 @@ TEST_F(StatsTableTest, StatsScope) {
{
StatsScope<StatsCounterTimer> timer(foo);
StatsScope<StatsRate> timer2(bar);
- Sleep(500);
+ PlatformThread::Sleep(500);
}
EXPECT_LE(500, table.GetCounterValue(L"t:foo"));
EXPECT_LE(500, table.GetCounterValue(L"t:bar"));
@@ -363,7 +370,7 @@ TEST_F(StatsTableTest, StatsScope) {
{
StatsScope<StatsCounterTimer> timer(foo);
StatsScope<StatsRate> timer2(bar);
- Sleep(500);
+ PlatformThread::Sleep(500);
}
EXPECT_LE(1000, table.GetCounterValue(L"t:foo"));
EXPECT_LE(1000, table.GetCounterValue(L"t:bar"));
diff --git a/base/test_suite.h b/base/test_suite.h
index 6b6dfd5..9104a54 100644
--- a/base/test_suite.h
+++ b/base/test_suite.h
@@ -14,12 +14,14 @@
#include "base/debug_on_start.h"
#include "base/icu_util.h"
#include "base/logging.h"
+#include "base/multiprocess_test.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_WIN)
#include <windows.h>
#include "base/multiprocess_test.h"
#elif defined(OS_LINUX)
+#include <dlfcn.h>
#include <gtk/gtk.h>
#endif
@@ -37,25 +39,31 @@ class TestSuite {
int Run() {
Initialize();
-
-#if defined(OS_WIN)
- // Check to see if we are being run as a client process.
+#if defined(OS_WIN) || defined(OS_LINUX)
std::wstring client_func = CommandLine().GetSwitchValue(kRunClientProcess);
+ // Check to see if we are being run as a client process.
if (!client_func.empty()) {
// Convert our function name to a usable string for GetProcAddress.
std::string func_name(client_func.begin(), client_func.end());
+#if defined(OS_WIN)
// Get our module handle and search for an exported function
// which we can use as our client main.
MultiProcessTest::ChildFunctionPtr func =
reinterpret_cast<MultiProcessTest::ChildFunctionPtr>(
GetProcAddress(GetModuleHandle(NULL), func_name.c_str()));
+#elif defined(OS_LINUX)
+ void* exobj = dlopen(0, RTLD_LAZY);
+ MultiProcessTest::ChildFunctionPtr func =
+ reinterpret_cast<MultiProcessTest::ChildFunctionPtr>(
+ dlsym(exobj, func_name.c_str()));
+#endif // defined(OS_LINUX)
+
if (func)
- return func();
+ return (*func)();
return -1;
}
-#endif
-
+#endif // defined(OS_WIN) || defined(OS_LINUX)
int result = RUN_ALL_TESTS();
Shutdown();