From a84b6ae1a64d2ae552d3e03c69d295a166200cd0 Mon Sep 17 00:00:00 2001 From: "Hongli Lai (Phusion)" Date: Sun, 22 Mar 2015 17:03:55 +0100 Subject: [PATCH] Implement logging of file descriptor opening and closing --- .editorconfig | 10 + debian.template/control.erb | 3 - debian.template/rules.erb | 4 - ext/apache2/Hooks.cpp | 6 +- ext/common/AgentsStarter.h | 5 +- ext/common/ApplicationPool2/DirectSpawner.h | 16 +- ext/common/ApplicationPool2/DummySpawner.h | 19 +- .../ApplicationPool2/Implementation.cpp | 2 +- ext/common/ApplicationPool2/Session.h | 4 +- ext/common/ApplicationPool2/SmartSpawner.h | 21 +- ext/common/ApplicationPool2/Socket.h | 22 +- ext/common/ApplicationPool2/Spawner.h | 4 +- ext/common/BackgroundEventLoop.cpp | 7 +- ext/common/EventedClient.h | 2 +- ext/common/EventedServer.h | 4 +- ext/common/FileDescriptor.h | 31 ++- ext/common/Logging.cpp | 85 ++++-- ext/common/Logging.h | 129 +++++++++- ext/common/MessageClient.h | 4 +- ext/common/MessageServer.h | 5 +- ext/common/RandomGenerator.h | 15 +- ext/common/SafeLibev.h | 6 +- ext/common/ServerKit/AcceptLoadBalancer.h | 11 +- ext/common/ServerKit/FileBufferedChannel.h | 2 + ext/common/ServerKit/Server.h | 17 +- ext/common/UnionStation/Connection.h | 4 +- ext/common/UnionStation/Core.h | 10 +- ext/common/Utils.cpp | 7 +- ext/common/Utils/IOUtils.cpp | 75 ++++-- ext/common/Utils/IOUtils.h | 68 ++++- ext/common/Utils/ProcessMetricsCollector.h | 4 +- ext/common/Utils/ScopeGuard.h | 21 +- ext/common/agents/Base.cpp | 41 ++- ext/common/agents/Base.h | 3 +- ext/common/agents/HelperAgent/AdminServer.h | 12 +- ext/common/agents/HelperAgent/Main.cpp | 16 +- ext/common/agents/LoggingAgent/AdminServer.h | 18 +- .../agents/LoggingAgent/LoggingServer.h | 6 +- ext/common/agents/LoggingAgent/Main.cpp | 14 +- ext/common/agents/Watchdog/AdminServer.h | 14 +- ext/common/agents/Watchdog/AgentWatcher.cpp | 6 +- ext/common/agents/Watchdog/Main.cpp | 13 +- ext/libev/ev.c | 12 + ext/libev/ev.h | 3 + test/cxx/ApplicationPool2/ProcessTest.cpp | 16 +- .../cxx/ApplicationPool2/SpawnerTestCases.cpp | 2 +- test/cxx/BufferedIOTest.cpp | 2 +- test/cxx/CxxTestMain.cpp | 2 +- test/cxx/EventedClientTest.cpp | 162 ++++++------ test/cxx/FileDescriptorTest.cpp | 26 +- test/cxx/IOUtilsTest.cpp | 243 +++++++++--------- test/cxx/MessageIOTest.cpp | 92 +++---- test/cxx/ServerKit/HttpServerTest.cpp | 2 +- test/cxx/ServerKit/ServerTest.cpp | 26 +- test/cxx/TestSupport.cpp | 4 +- test/cxx/UnionStationTest.cpp | 9 +- 56 files changed, 896 insertions(+), 471 deletions(-) diff --git a/.editorconfig b/.editorconfig index c0e8bc94a0..2126ebbc0f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -76,6 +76,16 @@ indent_size = 4 indent_style = space indent_size = 4 +[ext/libev/*.c] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = false + +[ext/libev/*.h] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = false + [ext/libeio/*.c] indent_style = space indent_size = 2 diff --git a/debian.template/control.erb b/debian.template/control.erb index b12c71df08..bc9167de06 100644 --- a/debian.template/control.erb +++ b/debian.template/control.erb @@ -15,9 +15,6 @@ Build-Depends: debhelper (>= 7.0.50~), rake, ruby2.0, ruby2.0-dev, <% end -%> apache2-mpm-worker | apache2-mpm, apache2-threaded-dev, -<% if is_distribution?('>= precise') || is_distribution?('>= wheezy') -%> - libev-dev (>= 1:4.0.0), -<% end -%> libapr1-dev, libcurl4-openssl-dev Standards-Version: 3.9.3 Homepage: https://www.phusionpassenger.com/ diff --git a/debian.template/rules.erb b/debian.template/rules.erb index 9eddd9aac2..51f7b267ec 100755 --- a/debian.template/rules.erb +++ b/debian.template/rules.erb @@ -1,10 +1,6 @@ #!/usr/bin/make -f # export DH_VERBOSE=1 -<% if is_distribution?('>= precise') || is_distribution?('>= wheezy') -%> -export USE_VENDORED_LIBEV=false -<% end %> - <% if ['1', 'true', 'on', 'yes'].include?(ENV['USE_CCACHE']) -%> export USE_CCACHE=1 NGINX_CONFIGURE_OPTIONS = CC=/usr/lib/ccache/cc CXX=/usr/lib/ccache/c++ diff --git a/ext/apache2/Hooks.cpp b/ext/apache2/Hooks.cpp index 7aaf1f4dcb..fcb77a27de 100644 --- a/ext/apache2/Hooks.cpp +++ b/ext/apache2/Hooks.cpp @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010-2014 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -246,7 +246,7 @@ class Hooks { FileDescriptor conn; try { - conn = connectToServer(getServerAddress()); + conn.assign(connectToServer(getServerAddress(), __FILE__, __LINE__), NULL, 0); } catch (const SystemException &e) { if (e.code() == EPIPE || e.code() == ECONNREFUSED || e.code() == ENOENT) { UPDATE_TRACE_POINT(); @@ -260,7 +260,7 @@ class Hooks { time_t deadline = time(NULL) + 5; while (!connected && time(NULL) < deadline) { try { - conn = connectToServer(getServerAddress()); + conn.assign(connectToServer(getServerAddress(), __FILE__, __LINE__), NULL, 0); connected = true; } catch (const SystemException &e) { if (e.code() == EPIPE || e.code() == ECONNREFUSED || e.code() == ENOENT) { diff --git a/ext/common/AgentsStarter.h b/ext/common/AgentsStarter.h index 365162c9bd..f29cf6dbab 100644 --- a/ext/common/AgentsStarter.h +++ b/ext/common/AgentsStarter.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010-2014 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -326,7 +326,7 @@ class AgentsStarter { .setInt ("log_level", getLogLevel()); extraParams.addTo(params); - fds = createUnixSocketPair(); + fds = createUnixSocketPair(__FILE__, __LINE__); pid = syscalls::fork(); if (pid == 0) { // Child @@ -388,6 +388,7 @@ class AgentsStarter { ScopeGuard guard(boost::bind(&AgentsStarter::killProcessGroupAndWait, &pid, 0)); fds[1].close(); + P_LOG_FILE_DESCRIPTOR_PURPOSE(feedbackFd, "AgentsStarter: feedback FD"); /****** Send arguments to watchdog through the feedback channel ******/ diff --git a/ext/common/ApplicationPool2/DirectSpawner.h b/ext/common/ApplicationPool2/DirectSpawner.h index 592aab9854..eaa45dcd1a 100644 --- a/ext/common/ApplicationPool2/DirectSpawner.h +++ b/ext/common/ApplicationPool2/DirectSpawner.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2011-2014 Phusion + * Copyright (c) 2011-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -160,8 +160,8 @@ class DirectSpawner: public Spawner { shared_array args; SpawnPreparationInfo preparation = prepareSpawn(options); vector command = createCommand(options, preparation, args); - SocketPair adminSocket = createUnixSocketPair(); - Pipe errorPipe = createPipe(); + SocketPair adminSocket = createUnixSocketPair(__FILE__, __LINE__); + Pipe errorPipe = createPipe(__FILE__, __LINE__); DebugDirPtr debugDir = boost::make_shared(preparation.uid, preparation.gid); pid_t pid; @@ -199,6 +199,16 @@ class DirectSpawner: public Spawner { throw SystemException("Cannot fork a new process", e); } else { + UPDATE_TRACE_POINT(); + P_LOG_FILE_DESCRIPTOR_PURPOSE(adminSocket.first, + "App " << pid << " (" << options.appRoot << ") adminSocket[0]"); + P_LOG_FILE_DESCRIPTOR_PURPOSE(adminSocket.second, + "App " << pid << " (" << options.appRoot << ") adminSocket[1]"); + P_LOG_FILE_DESCRIPTOR_PURPOSE(errorPipe.first, + "App " << pid << " (" << options.appRoot << ") errorPipe[0]"); + P_LOG_FILE_DESCRIPTOR_PURPOSE(errorPipe.second, + "App " << pid << " (" << options.appRoot << ") errorPipe[1]"); + UPDATE_TRACE_POINT(); ScopeGuard guard(boost::bind(nonInterruptableKillAndWaitpid, pid)); P_DEBUG("Process forked for appRoot=" << options.appRoot << ": PID " << pid); diff --git a/ext/common/ApplicationPool2/DummySpawner.h b/ext/common/ApplicationPool2/DummySpawner.h index 41ffadec2e..5a61855b56 100644 --- a/ext/common/ApplicationPool2/DummySpawner.h +++ b/ext/common/ApplicationPool2/DummySpawner.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2011-2014 Phusion + * Copyright (c) 2011-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -54,17 +54,22 @@ class DummySpawner: public Spawner { TRACE_POINT(); possiblyRaiseInternalError(options); - SocketPair adminSocket = createUnixSocketPair(); + pid_t pid; + { + boost::lock_guard l(lock); + count++; + pid = count; + } + + SocketPair adminSocket = createUnixSocketPair(__FILE__, __LINE__); SocketList sockets; - sockets.add("main", "tcp://127.0.0.1:1234", "session", config->concurrency); + sockets.add(pid, "main", "tcp://127.0.0.1:1234", "session", config->concurrency); syscalls::usleep(config->spawnTime); - boost::lock_guard l(lock); - count++; SpawnObject object; - string gupid = "gupid-" + toString(count); + string gupid = "gupid-" + toString(pid); object.process = boost::make_shared( - (pid_t) count, gupid, + pid, gupid, adminSocket.second, FileDescriptor(), sockets, SystemTime::getUsec(), SystemTime::getUsec()); object.process->dummy = true; diff --git a/ext/common/ApplicationPool2/Implementation.cpp b/ext/common/ApplicationPool2/Implementation.cpp index fac2960ab1..0c92d721bd 100644 --- a/ext/common/ApplicationPool2/Implementation.cpp +++ b/ext/common/ApplicationPool2/Implementation.cpp @@ -198,7 +198,7 @@ void processAndLogNewSpawnException(SpawnException &e, const Options &options, getSystemTempDir()); fd = mkstemp(filename); #endif - FdGuard guard(fd, true); + FdGuard guard(fd, NULL, 0, true); if (fd == -1) { int e = errno; throw SystemException("Cannot generate a temporary filename", diff --git a/ext/common/ApplicationPool2/Session.h b/ext/common/ApplicationPool2/Session.h index 00c9617bf9..99aef20542 100644 --- a/ext/common/ApplicationPool2/Session.h +++ b/ext/common/ApplicationPool2/Session.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2011-2014 Phusion + * Copyright (c) 2011-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -150,7 +150,7 @@ class Session { Connection connection = socket->checkoutConnection(); connection.fail = true; if (connection.blocking && !blocking) { - FdGuard g2(connection.fd); + FdGuard g2(connection.fd, NULL, 0); setNonBlocking(connection.fd); g2.clear(); connection.blocking = false; diff --git a/ext/common/ApplicationPool2/SmartSpawner.h b/ext/common/ApplicationPool2/SmartSpawner.h index 728b8f693d..b696c6ac01 100644 --- a/ext/common/ApplicationPool2/SmartSpawner.h +++ b/ext/common/ApplicationPool2/SmartSpawner.h @@ -212,8 +212,8 @@ class SmartSpawner: public Spawner, public boost::enable_shared_from_this args; preparation = prepareSpawn(options); vector command = createRealPreloaderCommand(options, args); - SocketPair adminSocket = createUnixSocketPair(); - Pipe errorPipe = createPipe(); + SocketPair adminSocket = createUnixSocketPair(__FILE__, __LINE__); + Pipe errorPipe = createPipe(__FILE__, __LINE__); DebugDirPtr debugDir = boost::make_shared(preparation.uid, preparation.gid); pid_t pid; @@ -251,6 +251,17 @@ class SmartSpawner: public Spawner, public boost::enable_shared_from_this { public: - void add(const StaticString &name, const StaticString &address, const StaticString &protocol, int concurrency) { - push_back(Socket(name, address, protocol, concurrency)); + void add(pid_t pid, const StaticString &name, const StaticString &address, + const StaticString &protocol, int concurrency) + { + push_back(Socket(pid, name, address, protocol, concurrency)); } const Socket *findSocketWithName(const StaticString &name) const { diff --git a/ext/common/ApplicationPool2/Spawner.h b/ext/common/ApplicationPool2/Spawner.h index a825e60529..718fa1b2ad 100644 --- a/ext/common/ApplicationPool2/Spawner.h +++ b/ext/common/ApplicationPool2/Spawner.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2011-2014 Phusion + * Copyright (c) 2011-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -496,7 +496,7 @@ class Spawner { fixupSocketAddress(*details.options, args[1])); StaticString protocol = psg_pstrdup(result.pool, args[2]); - sockets.add(name, address, protocol, atoi(args[3])); + sockets.add(details.pid, name, address, protocol, atoi(args[3])); } else { throwAppSpawnException("An error occurred while starting the " "web application. It reported a wrongly formatted 'socket'" diff --git a/ext/common/BackgroundEventLoop.cpp b/ext/common/BackgroundEventLoop.cpp index e9d0b8ad3e..51e96bd382 100644 --- a/ext/common/BackgroundEventLoop.cpp +++ b/ext/common/BackgroundEventLoop.cpp @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2011, 2012 Phusion + * Copyright (c) 2011-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -107,10 +108,14 @@ BackgroundEventLoop::BackgroundEventLoop(bool scalable, bool useLibeio) { throw RuntimeException("Cannot create an event loop"); } + P_LOG_FILE_DESCRIPTOR_OPEN2(ev_backend_fd(loop), "libev event loop: backend FD"); + async = (ev_async *) malloc(sizeof(ev_async)); async->data = this; ev_async_init(async, signalBackgroundEventLoopExit); ev_async_start(loop, async); + P_LOG_FILE_DESCRIPTOR_OPEN2(ev_loop_get_pipe(loop, 0), "libev event loop: async pipe 0"); + P_LOG_FILE_DESCRIPTOR_OPEN2(ev_loop_get_pipe(loop, 1), "libev event loop: async pipe 1"); safe = boost::make_shared(loop); priv = new BackgroundEventLoopPrivate(); priv->thr = NULL; diff --git a/ext/common/EventedClient.h b/ext/common/EventedClient.h index 8dae968150..ac14c067b1 100644 --- a/ext/common/EventedClient.h +++ b/ext/common/EventedClient.h @@ -689,7 +689,7 @@ class EventedClient { state = EC_DISCONNECTED; watchReadEvents(false); watchWriteEvents(false); - fd = -1; + fd.assign(-1, __FILE__, __LINE__); emitEvent(onDetach); return oldFd; } diff --git a/ext/common/EventedServer.h b/ext/common/EventedServer.h index e7d9c000d0..4a950a93d4 100644 --- a/ext/common/EventedServer.h +++ b/ext/common/EventedServer.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -221,7 +221,7 @@ class EventedServer { } done = true; } else { - FileDescriptor clientfdGuard(clientfd); + FileDescriptor clientfdGuard(clientfd, __FILE__, __LINE__, "EventedClient"); int optval = 1; setNonBlocking(clientfdGuard); diff --git a/ext/common/FileDescriptor.h b/ext/common/FileDescriptor.h index 3c21485c40..420eb362c8 100644 --- a/ext/common/FileDescriptor.h +++ b/ext/common/FileDescriptor.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010, 2011, 2012 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -34,6 +34,7 @@ #include #include +#include #include namespace Passenger { @@ -79,6 +80,7 @@ class FileDescriptor { if (fd >= 0 && autoClose) { this_thread::disable_syscall_interruption dsi; syscalls::close(fd); + P_LOG_FILE_DESCRIPTOR_CLOSE(fd); } } @@ -88,6 +90,7 @@ class FileDescriptor { int theFd = fd; fd = -1; safelyClose(theFd, !checkErrors); + P_LOG_FILE_DESCRIPTOR_CLOSE(theFd); } } @@ -99,6 +102,15 @@ class FileDescriptor { /** Shared pointer for reference counting on this file descriptor */ boost::shared_ptr data; + /** + * Calling FileDescriptor(otherFileDescriptorObject, __FILE__, __LINE__) + * is always a mistake, so we declare the corresponding constructor as + * private in order to enforce a compiler error. + */ + FileDescriptor(const FileDescriptor &other, const char *file, unsigned int line, bool autoClose = true) { + throw "never reached"; + } + public: /** * Creates a new empty FileDescriptor instance that has no underlying @@ -113,7 +125,7 @@ class FileDescriptor { * * @post *this == fd */ - explicit FileDescriptor(int fd, bool autoClose = true) { + explicit FileDescriptor(int fd, const char *file, unsigned int line, bool autoClose = true) { if (fd >= 0) { /* Make sure that the 'new' operator doesn't overwrite * errno so that we can write code like this: @@ -126,6 +138,7 @@ class FileDescriptor { int e = errno; data = boost::make_shared(fd, autoClose); errno = e; + P_LOG_FILE_DESCRIPTOR_OPEN3(fd, file, line); } } @@ -182,12 +195,12 @@ class FileDescriptor { } } - FileDescriptor &operator=(int fd) { + void assign(int fd, const char *file, unsigned int line) { /* Make sure that the 'new' and 'delete' operators don't * overwrite errno so that we can write code like this: * * FileDescriptor fd; - * fd = open(...); + * fd.assign(open(...)); * if (fd == -1) { * print_error(errno); * } @@ -195,11 +208,13 @@ class FileDescriptor { int e = errno; if (fd >= 0) { data = boost::make_shared(fd, true); + if (file != NULL) { + P_LOG_FILE_DESCRIPTOR_OPEN3(fd, file, line); + } } else { data.reset(); } errno = e; - return *this; } FileDescriptor &operator=(const FileDescriptor &other) { @@ -260,7 +275,7 @@ class EventFd { int writer; public: - EventFd() { + EventFd(const char *file, unsigned int line, const char *purpose) { int fds[2]; if (syscalls::pipe(fds) == -1) { @@ -269,12 +284,16 @@ class EventFd { } reader = fds[0]; writer = fds[1]; + P_LOG_FILE_DESCRIPTOR_OPEN4(fds[0], file, line, purpose); + P_LOG_FILE_DESCRIPTOR_OPEN4(fds[1], file, line, purpose); } ~EventFd() { this_thread::disable_syscall_interruption dsi; syscalls::close(reader); syscalls::close(writer); + P_LOG_FILE_DESCRIPTOR_CLOSE(reader); + P_LOG_FILE_DESCRIPTOR_CLOSE(writer); } void notify() { diff --git a/ext/common/Logging.cpp b/ext/common/Logging.cpp index b4e053213c..b2b5f8280c 100644 --- a/ext/common/Logging.cpp +++ b/ext/common/Logging.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -40,45 +41,77 @@ namespace Passenger { volatile sig_atomic_t _logLevel = DEFAULT_LOG_LEVEL; AssertionFailureInfo lastAssertionFailure; static bool printAppOutputAsDebuggingMessages = false; -static char *logFile = NULL; + +static boost::mutex logFileMutex; +static string logFile; + +static int fileDescriptorLog = -1; +static string fileDescriptorLogFile; #define TRUNCATE_LOGPATHS_TO_MAXCHARS 3 // set to 0 to disable truncation + void setLogLevel(int value) { _logLevel = value; boost::atomic_signal_fence(boost::memory_order_seq_cst); } +string getLogFile() { + boost::lock_guard l(logFileMutex); + return logFile; +} + bool -setLogFile(const char *path) { - int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0644); +setLogFile(const string &path, int *errcode) { + int fd = open(path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0644); if (fd != -1) { - char *newLogFile = strdup(path); - if (newLogFile == NULL) { - P_CRITICAL("Cannot allocate memory"); - abort(); - } - + boost::lock_guard l(logFileMutex); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); close(fd); - - if (logFile != NULL) { - free(logFile); - } - logFile = newLogFile; + logFile = path; return true; } else { + if (errcode != NULL) { + *errcode = errno; + } return false; } } -string getLogFile() { - if (logFile == NULL) { - return string(); +bool +hasFileDescriptorLogFile() { + return fileDescriptorLog != -1; +} + +string +getFileDescriptorLogFile() { + return fileDescriptorLogFile; +} + +int +getFileDescriptorLogFileFd() { + return fileDescriptorLog; +} + +bool +setFileDescriptorLogFile(const string &path, int *errcode) { + int fd = open(path.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0644); + if (fd != -1) { + if (fileDescriptorLog == -1) { + fileDescriptorLog = fd; + } else { + dup2(fd, fileDescriptorLog); + close(fd); + } + fileDescriptorLogFile = path; + return true; } else { - return string(logFile); + if (errcode != NULL) { + *errcode = errno; + } + return false; } } @@ -118,8 +151,8 @@ _prepareLogEntry(FastStringStream<> &sstream, const char *file, unsigned int lin sstream << ":" << line << " ]: "; } -void -_writeLogEntry(const char *str, unsigned int size) { +static void +writeExactWithoutOXT(int fd, const char *str, unsigned int size) { /* We do not use writeExact() here because writeExact() * uses oxt::syscalls::write(), which is an interruption point and * which is slightly more expensive than a plain write(). @@ -133,7 +166,7 @@ _writeLogEntry(const char *str, unsigned int size) { unsigned int written = 0; while (written < size) { do { - ret = write(STDERR_FILENO, str + written, size - written); + ret = write(fd, str + written, size - written); } while (ret == -1 && errno == EINTR); if (ret == -1) { /* The most likely reason why this fails is when the user has setup @@ -150,6 +183,16 @@ _writeLogEntry(const char *str, unsigned int size) { } } +void +_writeLogEntry(const char *str, unsigned int size) { + writeExactWithoutOXT(STDERR_FILENO, str, size); +} + +void +_writeFileDescriptorLogEntry(const char *str, unsigned int size) { + writeExactWithoutOXT(fileDescriptorLog, str, size); +} + const char * _strdupFastStringStream(const FastStringStream<> &stream) { char *buf = (char *) malloc(stream.size() + 1); diff --git a/ext/common/Logging.h b/ext/common/Logging.h index 8d00835c80..28f5dc809c 100644 --- a/ext/common/Logging.h +++ b/ext/common/Logging.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010-2014 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -56,8 +56,6 @@ using namespace boost; using namespace oxt; -/********** Debug logging facilities **********/ - struct AssertionFailureInfo { const char *filename; const char *function; // May be NULL. @@ -69,16 +67,76 @@ extern volatile sig_atomic_t _logLevel; // If assert() or similar fails, we attempt to store its information here. extern AssertionFailureInfo lastAssertionFailure; +/** + * Returns the current log level. This method is thread-safe. + */ inline OXT_FORCE_INLINE int getLogLevel() { return (int) _logLevel; } +/** + * Sets the log level. This method is thread-safe. + */ void setLogLevel(int value); -bool setLogFile(const char *path); // Sets errno on error + +/** + * Returns the general log file that we're using, or the empty string + * if we're not using a log file. + * + * This method is NOT thread-safe. + */ string getLogFile(); + +/** + * Sets the general log file. This method is thread-safe. + * Returns whether the new log file can be opened. If not, + * errcode (if non-NULL) is set to the relevant filesystem + * error code. + */ +bool setLogFile(const string &path, int *errcode = NULL); + +/** + * Returns whether we're using a separate log file for logging file + * descriptor opening and closing. + * + * This method is NOT thread-safe. + */ +bool hasFileDescriptorLogFile(); + +/** + * Returns the file that we're using for logging file descriptor + * opening and closing, or the empty string if we're not using a + * separate log file. + * + * This method is NOT thread-safe. + */ +string getFileDescriptorLogFile(); + +/** + * Returns the file descriptor of the log file that we're using for + * logging file descriptor opening and closing, or -1 if we're not using a + * separate log file. + * + * This method is NOT thread-safe. + */ +int getFileDescriptorLogFileFd(); + +/** + * Sets the log file to use specifically for logging file descriptor + * opening and closing. + * + * This method is NOT thread-safe. + * + * Returns whether the new log file can be opened. If not, + * errcode (if non-NULL) is set to the relevant filesystem + * error code. + */ +bool setFileDescriptorLogFile(const string &path, int *errcode = NULL); + void _prepareLogEntry(FastStringStream<> &sstream, const char *file, unsigned int line); void _writeLogEntry(const char *str, unsigned int size); +void _writeFileDescriptorLogEntry(const char *str, unsigned int size); const char *_strdupFastStringStream(const FastStringStream<> &stream); @@ -166,6 +224,69 @@ enum PassengerLogLevel { #define P_TRACE_WITH_POS(level, file, line, expr) do { /* nothing */ } while (false) #endif + +/** + * Log the fact that a file descriptor has been opened. + */ +#define P_LOG_FILE_DESCRIPTOR_OPEN(fd) \ + P_LOG_FILE_DESCRIPTOR_OPEN3(fd, __FILE__, __LINE__) +#define P_LOG_FILE_DESCRIPTOR_OPEN2(fd, expr) \ + P_LOG_FILE_DESCRIPTOR_OPEN4(fd, __FILE__, __LINE__, expr) +#define P_LOG_FILE_DESCRIPTOR_OPEN3(fd, file, line) \ + do { \ + if (Passenger::hasFileDescriptorLogFile() || Passenger::getLogLevel() >= Passenger::LVL_DEBUG) { \ + Passenger::FastStringStream<> _ostream; \ + Passenger::_prepareLogEntry(_ostream, file, line); \ + _ostream << "File descriptor opened: " << fd << "\n"; \ + if (hasFileDescriptorLogFile()) { \ + Passenger::_writeFileDescriptorLogEntry(_ostream.data(), _ostream.size()); \ + } else { \ + Passenger::_writeLogEntry(_ostream.data(), _ostream.size()); \ + } \ + } \ + } while (false) +#define P_LOG_FILE_DESCRIPTOR_OPEN4(fd, file, line, expr) \ + do { \ + P_LOG_FILE_DESCRIPTOR_OPEN3(fd, file, line); \ + P_LOG_FILE_DESCRIPTOR_PURPOSE(fd, expr); \ + } while (false) + +/** + * Log the purpose of a file descriptor that was recently logged with + * P_LOG_FILE_DESCRIPTOR_OPEN(). You should include information that + * allows a reader to find out what a file descriptor is for. + */ +#define P_LOG_FILE_DESCRIPTOR_PURPOSE(fd, expr) \ + do { \ + if (Passenger::hasFileDescriptorLogFile() || Passenger::getLogLevel() >= Passenger::LVL_DEBUG) { \ + Passenger::FastStringStream<> _ostream; \ + Passenger::_prepareLogEntry(_ostream, __FILE__, __LINE__); \ + _ostream << "File descriptor purpose: " << fd << ": " << expr << "\n"; \ + if (hasFileDescriptorLogFile()) { \ + Passenger::_writeFileDescriptorLogEntry(_ostream.data(), _ostream.size()); \ + } else { \ + Passenger::_writeLogEntry(_ostream.data(), _ostream.size()); \ + } \ + } \ + } while (false) + +/** + * Log the fact that a file descriptor has been closed. + */ +#define P_LOG_FILE_DESCRIPTOR_CLOSE(fd) \ + do { \ + if (Passenger::hasFileDescriptorLogFile() || Passenger::getLogLevel() >= Passenger::LVL_DEBUG) { \ + Passenger::FastStringStream<> _ostream; \ + Passenger::_prepareLogEntry(_ostream, __FILE__, __LINE__); \ + _ostream << "File descriptor closed: " << fd << "\n"; \ + if (hasFileDescriptorLogFile()) { \ + Passenger::_writeFileDescriptorLogEntry(_ostream.data(), _ostream.size()); \ + } else { \ + Passenger::_writeLogEntry(_ostream.data(), _ostream.size()); \ + } \ + } \ + } while (false) + /** * Print a message that was received from an application's stdout/stderr. * diff --git a/ext/common/MessageClient.h b/ext/common/MessageClient.h index 3dbb08e8ab..cad7c8b36f 100644 --- a/ext/common/MessageClient.h +++ b/ext/common/MessageClient.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010-2014 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -135,7 +135,7 @@ class MessageClient { TRACE_POINT(); ScopeGuard g(boost::bind(&MessageClient::autoDisconnect, this)); - fd = connectToServer(serverAddress.c_str()); + fd.assign(connectToServer(serverAddress.c_str(), __FILE__, __LINE__), NULL, 0); vector args; if (!readArrayMessage(fd, args)) { diff --git a/ext/common/MessageServer.h b/ext/common/MessageServer.h index 5c0e63e8bc..d9e51177c9 100644 --- a/ext/common/MessageServer.h +++ b/ext/common/MessageServer.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010, 2011, 2012 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -578,7 +578,8 @@ class MessageServer { FileDescriptor fd; UPDATE_TRACE_POINT(); - fd = syscalls::accept(serverFd, (struct sockaddr *) &addr, &len); + fd.assign(syscalls::accept(serverFd, (struct sockaddr *) &addr, &len), + __FILE__, __LINE__); if (fd == -1) { throw SystemException("Unable to accept a new client", errno); } diff --git a/ext/common/RandomGenerator.h b/ext/common/RandomGenerator.h index 867efa0935..cdbe9537ea 100644 --- a/ext/common/RandomGenerator.h +++ b/ext/common/RandomGenerator.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010-2013 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -31,14 +31,12 @@ #include #include -#include "StaticString.h" -#include "Exceptions.h" -#include "Utils/StrIntUtils.h" +#include +#include +#include +#include -/** - * A random - */ namespace Passenger { using namespace std; @@ -81,10 +79,13 @@ class RandomGenerator: public boost::noncopyable { throw FileSystemException("Cannot open /dev/urandom", errno, "/dev/urandom"); } + P_LOG_FILE_DESCRIPTOR_OPEN4(fileno(handle), __FILE__, __LINE__, + "RandomGenerator"); } void close() { if (handle != NULL) { + P_LOG_FILE_DESCRIPTOR_CLOSE(fileno(handle)); syscalls::fclose(handle); handle = NULL; } diff --git a/ext/common/SafeLibev.h b/ext/common/SafeLibev.h index 793d6c8b28..85fd8b0ee6 100644 --- a/ext/common/SafeLibev.h +++ b/ext/common/SafeLibev.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010-2014 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -34,6 +34,7 @@ #include #include #include +#include namespace Passenger { @@ -144,6 +145,9 @@ class SafeLibev { ~SafeLibev() { destroy(); + P_LOG_FILE_DESCRIPTOR_CLOSE(ev_loop_get_pipe(loop, 0)); + P_LOG_FILE_DESCRIPTOR_CLOSE(ev_loop_get_pipe(loop, 1)); + P_LOG_FILE_DESCRIPTOR_CLOSE(ev_backend_fd(loop)); ev_loop_destroy(loop); } diff --git a/ext/common/ServerKit/AcceptLoadBalancer.h b/ext/common/ServerKit/AcceptLoadBalancer.h index e723e89a15..f52685ed51 100644 --- a/ext/common/ServerKit/AcceptLoadBalancer.h +++ b/ext/common/ServerKit/AcceptLoadBalancer.h @@ -190,13 +190,14 @@ class AcceptLoadBalancer { accept4Available = false; return acceptNonBlockingSocket(serverFd); } else { + P_LOG_FILE_DESCRIPTOR_OPEN(fd); return fd; } } else { int fd = syscalls::accept(serverFd, (struct sockaddr *) &u, &addrlen); - FdGuard guard(fd); + FdGuard guard(fd, __FILE__, __LINE__); if (fd == -1) { return -1; } else { @@ -254,8 +255,10 @@ class AcceptLoadBalancer { int e = errno; throw SystemException("Cannot create pipe", e); } - FdGuard guard1(exitPipe[0]); - FdGuard guard2(exitPipe[1]); + FdGuard guard1(exitPipe[0], __FILE__, __LINE__); + FdGuard guard2(exitPipe[1], __FILE__, __LINE__); + P_LOG_FILE_DESCRIPTOR_PURPOSE(exitPipe[0], "AcceptLoadBalancer: exitPipe[0]"); + P_LOG_FILE_DESCRIPTOR_PURPOSE(exitPipe[1], "AcceptLoadBalancer: exitPipe[1]"); setNonBlocking(exitPipe[0]); setNonBlocking(exitPipe[1]); guard1.clear(); @@ -266,6 +269,8 @@ class AcceptLoadBalancer { shutdown(); close(exitPipe[0]); close(exitPipe[1]); + P_LOG_FILE_DESCRIPTOR_CLOSE(exitPipe[0]); + P_LOG_FILE_DESCRIPTOR_CLOSE(exitPipe[1]); } void listen(int fd) { diff --git a/ext/common/ServerKit/FileBufferedChannel.h b/ext/common/ServerKit/FileBufferedChannel.h index 96b7e7730b..d386defd3d 100644 --- a/ext/common/ServerKit/FileBufferedChannel.h +++ b/ext/common/ServerKit/FileBufferedChannel.h @@ -344,6 +344,7 @@ class FileBufferedChannel: protected Channel { P_ASSERT_EQ(readRequest, 0); P_ASSERT_EQ(writerRequest, 0); if (fd != -1) { + P_LOG_FILE_DESCRIPTOR_CLOSE(fd); eio_close(fd, 0, NULL, NULL); } } @@ -951,6 +952,7 @@ class FileBufferedChannel: protected Channel { if (fd != -1) { FBC_DEBUG("Writer: file created. Deleting file in the background"); + P_LOG_FILE_DESCRIPTOR_OPEN4(fd, __FILE__, __LINE__, "FileBufferedChannel buffer file"); eio_unlink(fcContext->path.c_str(), 0, bufferFileUnlinked, fcContext); inFileMode->fd = fd; moveNextBufferToFile(); diff --git a/ext/common/ServerKit/Server.h b/ext/common/ServerKit/Server.h index 6e5c853d11..36ab8d8f96 100644 --- a/ext/common/ServerKit/Server.h +++ b/ext/common/ServerKit/Server.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2014 Phusion + * Copyright (c) 2014-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -262,7 +262,7 @@ class BaseServer: public HooksImpl { break; } - FdGuard guard(fd); + FdGuard guard(fd, NULL, 0); client = checkoutClientObject(); TAILQ_INSERT_HEAD(&activeClients, client, nextClient.activeOrDisconnectedClient); acceptedClients[acceptCount] = client; @@ -271,6 +271,8 @@ class BaseServer: public HooksImpl { totalClientsAccepted++; client->number = getNextClientNumber(); reinitializeClient(client, fd); + P_LOG_FILE_DESCRIPTOR_PURPOSE(fd, "Server " << getServerName() + << ", client " << getClientName(client)); guard.clear(); } @@ -327,7 +329,7 @@ class BaseServer: public HooksImpl { int fd = syscalls::accept(serverFd, (struct sockaddr *) &u, &addrlen); - FdGuard guard(fd); + FdGuard guard(fd, __FILE__, __LINE__); if (fd == -1) { return -1; } else { @@ -782,6 +784,8 @@ class BaseServer: public HooksImpl { acceptedClients[i] = client; client->number = getNextClientNumber(); reinitializeClient(client, fds[i]); + P_LOG_FILE_DESCRIPTOR_PURPOSE(fds[i], "Server " << getServerName() + << ", client " << getClientName(client)); } SKS_DEBUG(size << " new client(s) accepted; there are now " << @@ -820,6 +824,12 @@ class BaseServer: public HooksImpl { return ret; } + string getClientName(const Client *client) { + char buf[128]; + unsigned int size = getClientName(client, buf, sizeof(buf)); + return string(buf, size); + } + vector getActiveClients() { vector result; Client *client; @@ -900,6 +910,7 @@ class BaseServer: public HooksImpl { SKC_TRACE(c, 2, "Closing client file descriptor: " << fdnum); try { safelyClose(fdnum); + P_LOG_FILE_DESCRIPTOR_CLOSE(fdnum); } catch (const SystemException &e) { SKC_WARN(c, "An error occurred while closing the client file descriptor: " << e.what() << " (errno=" << e.code() << ")"); diff --git a/ext/common/UnionStation/Connection.h b/ext/common/UnionStation/Connection.h index a63aa42e77..ca9087b03a 100644 --- a/ext/common/UnionStation/Connection.h +++ b/ext/common/UnionStation/Connection.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010-2014 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -36,6 +36,7 @@ #include +#include #include #include #include @@ -164,6 +165,7 @@ struct Connection: public boost::noncopyable { this_thread::disable_interruption di; this_thread::disable_syscall_interruption dsi; safelyClose(fd); + P_LOG_FILE_DESCRIPTOR_CLOSE(fd); fd = -1; } } diff --git a/ext/common/UnionStation/Core.h b/ext/common/UnionStation/Core.h index d18f807643..6ae77975a3 100644 --- a/ext/common/UnionStation/Core.h +++ b/ext/common/UnionStation/Core.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010-2014 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -142,9 +142,11 @@ class Core: public boost::enable_shared_from_this { unsigned long long timeout = 15000000; // Create socket. - fd = connectToServer(serverAddress); - FdGuard guard(fd, true); - + fd = connectToServer(serverAddress, __FILE__, __LINE__); + FdGuard guard(fd, NULL, 0, true); + + P_LOG_FILE_DESCRIPTOR_PURPOSE(fd, "Connection to " AGENT_EXE " logger"); + // Handshake: process protocol version number. if (!readArrayMessage(fd, args, &timeout)) { throw IOException("The logging agent closed the connection before sending a version identifier."); diff --git a/ext/common/Utils.cpp b/ext/common/Utils.cpp index fa35f98357..02955a6ee5 100644 --- a/ext/common/Utils.cpp +++ b/ext/common/Utils.cpp @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010-2014 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -165,7 +165,8 @@ createFile(const string &filename, const StaticString &contents, mode_t permissi options |= O_EXCL; } do { - fd = open(filename.c_str(), options, permissions); + fd.assign(open(filename.c_str(), options, permissions), + __FILE__, __LINE__); } while (fd == -1 && errno == EINTR); if (fd != -1) { FileGuard guard(filename); @@ -1036,7 +1037,7 @@ runCommandAndCaptureOutput(const char **command) { int e; Pipe p; - p = createPipe(); + p = createPipe(__FILE__, __LINE__); this_thread::disable_syscall_interruption dsi; pid = syscalls::fork(); diff --git a/ext/common/Utils/IOUtils.cpp b/ext/common/Utils/IOUtils.cpp index ef98b9e4ee..df7cb55e6b 100644 --- a/ext/common/Utils/IOUtils.cpp +++ b/ext/common/Utils/IOUtils.cpp @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010-2013 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -233,18 +233,20 @@ resolveHostname(const string &hostname, unsigned int port, bool shuffle) { } int -createServer(const StaticString &address, unsigned int backlogSize, bool autoDelete) { +createServer(const StaticString &address, unsigned int backlogSize, bool autoDelete, + const char *file, unsigned int line) +{ TRACE_POINT(); switch (getSocketAddressType(address)) { case SAT_UNIX: return createUnixServer(parseUnixSocketAddress(address), - backlogSize, autoDelete); + backlogSize, autoDelete, file, line); case SAT_TCP: { string host; unsigned short port; parseTcpSocketAddress(address, host, port); - return createTcpServer(host.c_str(), port, backlogSize); + return createTcpServer(host.c_str(), port, backlogSize, file, line); } default: throw ArgumentException(string("Unknown address type for '") + address + "'"); @@ -252,7 +254,9 @@ createServer(const StaticString &address, unsigned int backlogSize, bool autoDel } int -createUnixServer(const StaticString &filename, unsigned int backlogSize, bool autoDelete) { +createUnixServer(const StaticString &filename, unsigned int backlogSize, bool autoDelete, + const char *file, unsigned int line) +{ struct sockaddr_un addr; int fd, ret; @@ -269,7 +273,7 @@ createUnixServer(const StaticString &filename, unsigned int backlogSize, bool au throw SystemException("Cannot create a Unix socket file descriptor", e); } - FdGuard guard(fd, true); + FdGuard guard(fd, file, line, true); addr.sun_family = AF_LOCAL; strncpy(addr.sun_path, filename.c_str(), filename.size()); addr.sun_path[filename.size()] = '\0'; @@ -307,7 +311,9 @@ createUnixServer(const StaticString &filename, unsigned int backlogSize, bool au } int -createTcpServer(const char *address, unsigned short port, unsigned int backlogSize) { +createTcpServer(const char *address, unsigned short port, unsigned int backlogSize, + const char *file, unsigned int line) +{ struct sockaddr_in addr; int fd, ret, optval; @@ -334,7 +340,7 @@ createTcpServer(const char *address, unsigned short port, unsigned int backlogSi throw SystemException("Cannot create a TCP socket file descriptor", e); } - FdGuard guard(fd, true); + FdGuard guard(fd, file, line, true); ret = syscalls::bind(fd, (const struct sockaddr *) &addr, sizeof(addr)); if (ret == -1) { int e = errno; @@ -372,17 +378,17 @@ createTcpServer(const char *address, unsigned short port, unsigned int backlogSi } int -connectToServer(const StaticString &address) { +connectToServer(const StaticString &address, const char *file, unsigned int line) { TRACE_POINT(); switch (getSocketAddressType(address)) { case SAT_UNIX: - return connectToUnixServer(parseUnixSocketAddress(address)); + return connectToUnixServer(parseUnixSocketAddress(address), file, line); case SAT_TCP: { string host; unsigned short port; parseTcpSocketAddress(address, host, port); - return connectToTcpServer(host, port); + return connectToTcpServer(host, port, file, line); } default: throw ArgumentException(string("Unknown address type for '") + address + "'"); @@ -390,14 +396,16 @@ connectToServer(const StaticString &address) { } int -connectToUnixServer(const StaticString &filename) { +connectToUnixServer(const StaticString &filename, const char *file, + unsigned int line) +{ int fd = syscalls::socket(PF_UNIX, SOCK_STREAM, 0); if (fd == -1) { int e = errno; throw SystemException("Cannot create a Unix socket file descriptor", e); } - FdGuard guard(fd, true); + FdGuard guard(fd, file, line, true); int ret; struct sockaddr_un addr; @@ -449,8 +457,10 @@ connectToUnixServer(const StaticString &filename) { } void -setupNonBlockingUnixSocket(NUnix_State &state, const StaticString &filename) { - state.fd = syscalls::socket(PF_UNIX, SOCK_STREAM, 0); +setupNonBlockingUnixSocket(NUnix_State &state, const StaticString &filename, + const char *file, unsigned int line) +{ + state.fd.assign(syscalls::socket(PF_UNIX, SOCK_STREAM, 0), file, line); if (state.fd == -1) { int e = errno; throw SystemException("Cannot create a Unix socket file descriptor", e); @@ -494,7 +504,9 @@ connectToUnixServer(NUnix_State &state) { } int -connectToTcpServer(const StaticString &hostname, unsigned int port) { +connectToTcpServer(const StaticString &hostname, unsigned int port, + const char *file, unsigned int line) +{ struct addrinfo hints, *res; int ret, e, fd; @@ -543,11 +555,15 @@ connectToTcpServer(const StaticString &hostname, unsigned int port) { throw SystemException(message, e); } + P_LOG_FILE_DESCRIPTOR_OPEN3(fd, file, line); + return fd; } void -setupNonBlockingTcpSocket(NTCP_State &state, const StaticString &hostname, int port) { +setupNonBlockingTcpSocket(NTCP_State &state, const StaticString &hostname, int port, + const char *file, unsigned int line) +{ int ret; memset(&state.hints, 0, sizeof(state.hints)); @@ -565,7 +581,7 @@ setupNonBlockingTcpSocket(NTCP_State &state, const StaticString &hostname, int p throw IOException(message); } - state.fd = syscalls::socket(PF_INET, SOCK_STREAM, 0); + state.fd.assign(syscalls::socket(PF_INET, SOCK_STREAM, 0), file, line); if (state.fd == -1) { int e = errno; throw SystemException("Cannot create a TCP socket file descriptor", e); @@ -605,19 +621,22 @@ connectToTcpServer(NTCP_State &state) { } void -setupNonBlockingSocket(NConnect_State &state, const StaticString &address) { +setupNonBlockingSocket(NConnect_State &state, const StaticString &address, + const char *file, unsigned int line) +{ TRACE_POINT(); state.type = getSocketAddressType(address); switch (state.type) { case SAT_UNIX: - setupNonBlockingUnixSocket(state.s_unix, parseUnixSocketAddress(address)); + setupNonBlockingUnixSocket(state.s_unix, parseUnixSocketAddress(address), + file, line); break; case SAT_TCP: { string host; unsigned short port; parseTcpSocketAddress(address, host, port); - setupNonBlockingTcpSocket(state.s_tcp, host, port); + setupNonBlockingTcpSocket(state.s_tcp, host, port, file, line); break; } default: @@ -638,7 +657,7 @@ connectToServer(NConnect_State &state) { } SocketPair -createUnixSocketPair() { +createUnixSocketPair(const char *file, unsigned int line) { int fds[2]; FileDescriptor sockets[2]; @@ -646,14 +665,14 @@ createUnixSocketPair() { int e = errno; throw SystemException("Cannot create a Unix socket pair", e); } else { - sockets[0] = fds[0]; - sockets[1] = fds[1]; + sockets[0].assign(fds[0], file, line); + sockets[1].assign(fds[1], file, line); return SocketPair(sockets[0], sockets[1]); } } Pipe -createPipe() { +createPipe(const char *file, unsigned int line) { int fds[2]; FileDescriptor p[2]; @@ -661,8 +680,8 @@ createPipe() { int e = errno; throw SystemException("Cannot create a pipe", e); } else { - p[0] = fds[0]; - p[1] = fds[1]; + p[0].assign(fds[0], file, line); + p[1].assign(fds[1], file, line); return Pipe(p[0], p[1]); } } @@ -1151,7 +1170,7 @@ string readAll(const string &filename) { FILE *f = fopen(filename.c_str(), "rb"); if (f != NULL) { - StdioGuard guard(f); + StdioGuard guard(f, NULL, 0); return readAll(fileno(f)); } else { int e = errno; diff --git a/ext/common/Utils/IOUtils.h b/ext/common/Utils/IOUtils.h index e4488a3307..5a45fdf532 100644 --- a/ext/common/Utils/IOUtils.h +++ b/ext/common/Utils/IOUtils.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -135,6 +135,9 @@ vector resolveHostname(const string &hostname, * @param autoDelete If address is a Unix socket that already exists, * whether that should be deleted. Otherwise this argument * is ignored. + * @param file The name of the source file that called this function, + * for file descriptor logging purposes. + * @param line The line in the source file that called this function. * @return The file descriptor of the newly created server socket. * @throws ArgumentException The given address cannot be parsed. * @throws RuntimeException Something went wrong. @@ -144,7 +147,9 @@ vector resolveHostname(const string &hostname, */ int createServer(const StaticString &address, unsigned int backlogSize = 0, - bool autoDelete = true); + bool autoDelete = true, + const char *file = __FILE__, + unsigned int line = __LINE__); /** * Create a new Unix server socket which is bounded to filename. @@ -153,6 +158,9 @@ int createServer(const StaticString &address, * @param backlogSize The size of the socket's backlog. Specify 0 to use the * platform's maximum allowed backlog size. * @param autoDelete Whether filename should be deleted, if it already exists. + * @param file The name of the source file that called this function, + * for file descriptor logging purposes. + * @param line The line in the source file that called this function. * @return The file descriptor of the newly created Unix server socket. * @throws RuntimeException Something went wrong. * @throws SystemException Something went wrong while creating the Unix server socket. @@ -161,7 +169,9 @@ int createServer(const StaticString &address, */ int createUnixServer(const StaticString &filename, unsigned int backlogSize = 0, - bool autoDelete = true); + bool autoDelete = true, + const char *file = __FILE__, + unsigned int line = __LINE__); /** * Create a new TCP server socket which is bounded to the given address and port. @@ -172,6 +182,9 @@ int createUnixServer(const StaticString &filename, * select a free port. * @param backlogSize The size of the socket's backlog. Specify 0 to use the * platform's maximum allowed backlog size. + * @param file The name of the source file that called this function, + * for file descriptor logging purposes. + * @param line The line in the source file that called this function. * @return The file descriptor of the newly created server socket. * @throws SystemException Something went wrong while creating the server socket. * @throws ArgumentException The given address cannot be parsed. @@ -180,12 +193,17 @@ int createUnixServer(const StaticString &filename, */ int createTcpServer(const char *address = "0.0.0.0", unsigned short port = 0, - unsigned int backlogSize = 0); + unsigned int backlogSize = 0, + const char *file = __FILE__, + unsigned int line = __LINE__); /** * Connect to a server at the given address in a blocking manner. * * @param address An address as accepted by getSocketAddressType(). + * @param file The name of the source file that called this function, + * for file descriptor logging purposes. + * @param line The line in the source file that called this function. * @return The file descriptor of the connected client socket. * @throws ArgumentException Unknown address type. * @throws RuntimeException Something went wrong. @@ -194,32 +212,41 @@ int createTcpServer(const char *address = "0.0.0.0", * @throws boost::thread_interrupted A system call has been interrupted. * @ingroup Support */ -int connectToServer(const StaticString &address); +int connectToServer(const StaticString &address, const char *file, + unsigned int line); /** * Connect to a Unix server socket at filename in a blocking manner. * * @param filename The filename of the socket to connect to. + * @param file The name of the source file that called this function, + * for file descriptor logging purposes. + * @param line The line in the source file that called this function. * @return The file descriptor of the connected client socket. * @throws RuntimeException Something went wrong. * @throws SystemException Something went wrong while connecting to the Unix server. * @throws boost::thread_interrupted A system call has been interrupted. * @ingroup Support */ -int connectToUnixServer(const StaticString &filename); +int connectToUnixServer(const StaticString &filename, const char *file, + unsigned int line); /** * Connect to a TCP server socket at the given host name and port in a blocking manner. * * @param hostname The host name of the TCP server. * @param port The port number of the TCP server. + * @param file The name of the source file that called this function, + * for file descriptor logging purposes. + * @param line The line in the source file that called this function. * @return The file descriptor of the connected client socket. * @throws IOException Something went wrong while connecting to the Unix server. * @throws SystemException Something went wrong while connecting to the server. * @throws boost::thread_interrupted A system call has been interrupted. * @ingroup Support */ -int connectToTcpServer(const StaticString &hostname, unsigned int port); +int connectToTcpServer(const StaticString &hostname, unsigned int port, + const char *file, unsigned int line); /** State structure for non-blocking connectToUnixServer(). */ struct NUnix_State { @@ -233,12 +260,16 @@ struct NUnix_State { * * @param state A state structure. * @param filename The filename of the socket to connect to. + * @param file The name of the source file that called this function, + * for file descriptor logging purposes. + * @param line The line in the source file that called this function. * @throws SystemException Something went wrong. * @throws boost::thread_interrupted A system call has been interrupted. * @ingroup Support */ void setupNonBlockingUnixSocket(NUnix_State & restrict_ref state, - const StaticString & restrict_ref filename); + const StaticString & restrict_ref filename, const char *file, + unsigned int line); /** * Connect a Unix domain socket in non-blocking mode. @@ -280,6 +311,9 @@ struct NTCP_State { * @param state A state structure. * @param hostname The host name of the TCP server. * @param port The port number of the TCP server. + * @param file The name of the source file that called this function, + * for file descriptor logging purposes. + * @param line The line in the source file that called this function. * @throws IOException Something went wrong. * @throws SystemException Something went wrong. * @throws boost::thread_interrupted A system call has been interrupted. @@ -287,7 +321,7 @@ struct NTCP_State { */ void setupNonBlockingTcpSocket(NTCP_State & restrict_ref state, const StaticString & restrict_ref hostname, - int port); + int port, const char *file, unsigned int line); /** * Connect a TCP socket in non-blocking mode. @@ -312,6 +346,9 @@ struct NConnect_State { * * @param A state structure. * @param address An address as accepted by getSocketAddressType(). + * @param file The name of the source file that called this function, + * for file descriptor logging purposes. + * @param line The line in the source file that called this function. * @throws ArgumentException Unknown address type. * @throws RuntimeException Something went wrong. * @throws SystemException Something went wrong. @@ -320,7 +357,8 @@ struct NConnect_State { * @ingroup Support */ void setupNonBlockingSocket(NConnect_State & restrict_ref state, - const StaticString & restrict_ref address); + const StaticString & restrict_ref address, const char *file, + unsigned int line); /** * Connect a socket in non-blocking mode. @@ -338,18 +376,24 @@ bool connectToServer(NConnect_State &state); /** * Creates a Unix domain socket pair. * + * @param file The name of the source file that called this function, + * for file descriptor logging purposes. + * @param line The line in the source file that called this function. * @throws SystemException * @throws boost::thread_interrupted */ -SocketPair createUnixSocketPair(); +SocketPair createUnixSocketPair(const char *file, unsigned int line); /** * Creates a pipe. * + * @param file The name of the source file that called this function, + * for file descriptor logging purposes. + * @param line The line in the source file that called this function. * @throws SystemException * @throws boost::thread_interrupted */ -Pipe createPipe(); +Pipe createPipe(const char *file, unsigned int line); /** * Waits at most *timeout microseconds for the file descriptor to become readable. diff --git a/ext/common/Utils/ProcessMetricsCollector.h b/ext/common/Utils/ProcessMetricsCollector.h index f9a573cf80..6823892a2a 100644 --- a/ext/common/Utils/ProcessMetricsCollector.h +++ b/ext/common/Utils/ProcessMetricsCollector.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010-2013 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -414,7 +414,7 @@ class ProcessMetricsCollector { return; } - StdioGuard guard(f); + StdioGuard guard(f, NULL, 0); bool hasPss = false; bool hasPrivateDirty = false; bool hasSwap = false; diff --git a/ext/common/Utils/ScopeGuard.h b/ext/common/Utils/ScopeGuard.h index f559c71708..2f64a364a6 100644 --- a/ext/common/Utils/ScopeGuard.h +++ b/ext/common/Utils/ScopeGuard.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010, 2011, 2012 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -30,6 +30,7 @@ #include #include #include +#include namespace Passenger { @@ -99,12 +100,17 @@ class StdioGuard: public noncopyable { : f(0) { } - StdioGuard(FILE *_f) + StdioGuard(FILE *_f, const char *file, unsigned int line) : f(_f) - { } + { + if (_f != NULL && file != NULL) { + P_LOG_FILE_DESCRIPTOR_OPEN3(fileno(_f), file, line); + } + } ~StdioGuard() { if (f != NULL) { + P_LOG_FILE_DESCRIPTOR_CLOSE(fileno(f)); fclose(f); } } @@ -116,14 +122,19 @@ class FdGuard: public noncopyable { bool ignoreErrors; public: - FdGuard(int _fd, bool _ignoreErrors = false) + FdGuard(int _fd, const char *file, unsigned int line, bool _ignoreErrors = false) : fd(_fd), ignoreErrors(_ignoreErrors) - { } + { + if (_fd != -1 && file != NULL) { + P_LOG_FILE_DESCRIPTOR_OPEN3(_fd, file, line); + } + } ~FdGuard() { if (fd != -1) { safelyClose(fd, ignoreErrors); + P_LOG_FILE_DESCRIPTOR_CLOSE(fd); } } diff --git a/ext/common/agents/Base.cpp b/ext/common/agents/Base.cpp index 48e77f9e53..90efbe19b6 100644 --- a/ext/common/agents/Base.cpp +++ b/ext/common/agents/Base.cpp @@ -1518,7 +1518,7 @@ initializeAgent(int argc, char **argv[], const char *processName, argc - argStartIndex); } - initializeAgentOptions(options, preinit); + initializeAgentOptions(processName, options, preinit); } catch (const tracable_exception &e) { P_ERROR("*** ERROR: " << e.what() << "\n" << e.backtrace()); exit(1); @@ -1546,7 +1546,9 @@ initializeAgent(int argc, char **argv[], const char *processName, } void -initializeAgentOptions(VariantMap &options, PreinitializationFunc preinit) { +initializeAgentOptions(const char *processName, VariantMap &options, + PreinitializationFunc preinit) +{ ResourceLocator locator; string ruby; @@ -1581,8 +1583,39 @@ initializeAgentOptions(VariantMap &options, PreinitializationFunc preinit) { } options.setDefaultInt("log_level", DEFAULT_LOG_LEVEL); setLogLevel(options.getInt("log_level")); - if (options.has("debug_log_file")) { - setLogFile(options.get("debug_log_file").c_str()); + if (options.has("log_file")) { + setLogFile(options.get("log_file")); + } else if (options.has("debug_log_file")) { + setLogFile(options.get("debug_log_file")); + } + if (options.has("file_descriptor_log_file")) { + setFileDescriptorLogFile(options.get("file_descriptor_log_file").c_str()); + + // This information helps dev/parse_file_descriptor_log. + FastStringStream<> stream; + _prepareLogEntry(stream, __FILE__, __LINE__); + stream << "Starting agent: " << processName << "\n"; + _writeFileDescriptorLogEntry(stream.data(), stream.size()); + + P_LOG_FILE_DESCRIPTOR_OPEN4(getFileDescriptorLogFileFd(), __FILE__, __LINE__, + "file descriptor log file " << options.get("file_descriptor_log_file")); + } else { + // This information helps dev/parse_file_descriptor_log. + P_DEBUG("Starting agent: " << processName); + } + + if (hasEnvOption("PASSENGER_USE_FEEDBACK_FD")) { + P_LOG_FILE_DESCRIPTOR_OPEN2(FEEDBACK_FD, "feedback FD"); + } + if (emergencyPipe1[0] != -1) { + P_LOG_FILE_DESCRIPTOR_OPEN4(emergencyPipe1[0], __FILE__, __LINE__, + "Emergency pipe 1-0"); + P_LOG_FILE_DESCRIPTOR_OPEN4(emergencyPipe1[1], __FILE__, __LINE__, + "Emergency pipe 1-1"); + P_LOG_FILE_DESCRIPTOR_OPEN4(emergencyPipe2[0], __FILE__, __LINE__, + "Emergency pipe 2-0"); + P_LOG_FILE_DESCRIPTOR_OPEN4(emergencyPipe2[1], __FILE__, __LINE__, + "Emergency pipe 2-1"); } } diff --git a/ext/common/agents/Base.h b/ext/common/agents/Base.h index cf14fa34b2..d3f15b41fc 100644 --- a/ext/common/agents/Base.h +++ b/ext/common/agents/Base.h @@ -43,7 +43,8 @@ bool feedbackFdAvailable(); VariantMap initializeAgent(int argc, char **argv[], const char *processName, OptionParserFunc optionParser = NULL, PreinitializationFunc preinit = NULL, int argStartIndex = 1); -void initializeAgentOptions(VariantMap &options, PreinitializationFunc preinit = NULL); +void initializeAgentOptions(const char *processName, VariantMap &options, + PreinitializationFunc preinit = NULL); void installAgentAbortHandler(); void installDiagnosticsDumper(DiagnosticsDumper func, void *userData); diff --git a/ext/common/agents/HelperAgent/AdminServer.h b/ext/common/agents/HelperAgent/AdminServer.h index 0696d0a92c..ba1d0f7cf1 100644 --- a/ext/common/agents/HelperAgent/AdminServer.h +++ b/ext/common/agents/HelperAgent/AdminServer.h @@ -462,6 +462,7 @@ class AdminServer: public ServerKit::HttpServerpool, "content-type", "application/json"); Json::Value doc; @@ -471,6 +472,9 @@ class AdminServer: public ServerKit::HttpServerpool, doc.toStyledString())); @@ -504,8 +508,8 @@ class AdminServer: public ServerKit::HttpServerpool, bufsize); snprintf(message, bufsize, "{ \"status\": \"error\", " @@ -543,8 +547,8 @@ class AdminServer: public ServerKit::HttpServerpool, bufsize); snprintf(message, bufsize, "{ \"status\": \"error\", " diff --git a/ext/common/agents/HelperAgent/Main.cpp b/ext/common/agents/HelperAgent/Main.cpp index 0fb8599326..5c72781242 100644 --- a/ext/common/agents/HelperAgent/Main.cpp +++ b/ext/common/agents/HelperAgent/Main.cpp @@ -146,7 +146,9 @@ namespace ServerAgent { oxt::thread *prestarterThread; WorkingObjects() - : terminationCount(0), + : exitEvent(__FILE__, __LINE__, "WorkingObjects: exitEvent"), + allClientsDisconnectedEvent(__FILE__, __LINE__, "WorkingObjects: allClientsDisconnectedEvent"), + terminationCount(0), shutdownCounter(0) { for (unsigned int i = 0; i < SERVER_KIT_MAX_SERVER_ENDPOINTS; i++) { @@ -274,13 +276,19 @@ startListening() { vector adminAddresses = agentsOptions->getStrSet("server_admin_addresses", false); for (unsigned int i = 0; i < addresses.size(); i++) { - wo->serverFds[i] = createServer(addresses[i]); + wo->serverFds[i] = createServer(addresses[i], 0, true, + __FILE__, __LINE__); + P_LOG_FILE_DESCRIPTOR_PURPOSE(wo->serverFds[i], + "Server address: " << addresses[i]); if (getSocketAddressType(addresses[i]) == SAT_UNIX) { makeFileWorldReadableAndWritable(parseUnixSocketAddress(addresses[i])); } } for (unsigned int i = 0; i < adminAddresses.size(); i++) { - wo->adminServerFds[i] = createServer(adminAddresses[i]); + wo->adminServerFds[i] = createServer(adminAddresses[i], 0, true, + __FILE__, __LINE__); + P_LOG_FILE_DESCRIPTOR_PURPOSE(wo->adminServerFds[i], + "AdminServer address: " << adminAddresses[i]); if (getSocketAddressType(adminAddresses[i]) == SAT_UNIX) { makeFileWorldReadableAndWritable(parseUnixSocketAddress(adminAddresses[i])); } @@ -303,8 +311,8 @@ createPidFile() { } UPDATE_TRACE_POINT(); + FdGuard guard(fd, __FILE__, __LINE__); writeExact(fd, pidStr, strlen(pidStr)); - syscalls::close(fd); } } diff --git a/ext/common/agents/LoggingAgent/AdminServer.h b/ext/common/agents/LoggingAgent/AdminServer.h index 596d4b0556..c70e6ee3e9 100644 --- a/ext/common/agents/LoggingAgent/AdminServer.h +++ b/ext/common/agents/LoggingAgent/AdminServer.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2013-2014 Phusion + * Copyright (c) 2013-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -159,9 +159,17 @@ class AdminServer: public ServerKit::HttpServerpool, "content-type", "application/json"); doc["log_level"] = getLogLevel(); + if (!logFile.empty()) { + doc["log_file"] = logFile; + } + if (!fileDescriptorLogFile.empty()) { + doc["file_descriptor_log_file"] = fileDescriptorLogFile; + } writeSimpleResponse(client, 200, &headers, doc.toStyledString()); if (!req->ended()) { @@ -189,8 +197,8 @@ class AdminServer: public ServerKit::HttpServerpool, bufsize); snprintf(message, bufsize, "{ \"status\": \"error\", " @@ -224,8 +232,8 @@ class AdminServer: public ServerKit::HttpServerpool, bufsize); snprintf(message, bufsize, "{ \"status\": \"error\", " diff --git a/ext/common/agents/LoggingAgent/LoggingServer.h b/ext/common/agents/LoggingAgent/LoggingServer.h index 89b753fdba..451e72eaf8 100644 --- a/ext/common/agents/LoggingAgent/LoggingServer.h +++ b/ext/common/agents/LoggingAgent/LoggingServer.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010-2014 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -157,9 +157,9 @@ class LoggingServer: public EventedMessageServer { } else { this->filename = filename; } - fd = syscalls::open(filename.c_str(), + fd.assign(syscalls::open(filename.c_str(), O_CREAT | O_WRONLY | O_APPEND, - 0600); + 0600), __FILE__, __LINE__); if (fd == -1) { int e = errno; throw FileSystemException("Cannnot open file", e, filename); diff --git a/ext/common/agents/LoggingAgent/Main.cpp b/ext/common/agents/LoggingAgent/Main.cpp index 7c8b2a9f43..9b73afa46b 100644 --- a/ext/common/agents/LoggingAgent/Main.cpp +++ b/ext/common/agents/LoggingAgent/Main.cpp @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010-2014 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -91,6 +91,8 @@ namespace LoggingAgent { bgloop(NULL), serverKitContext(NULL), loggingServer(NULL), + exitEvent(__FILE__, __LINE__, "WorkingObjects: exitEvent"), + allClientsDisconnectedEvent(__FILE__, __LINE__, "WorkingObjects: allClientsDisconnectedEvent"), terminationCount(0) { } }; @@ -201,7 +203,10 @@ startListening() { vector adminAddresses; address = options.get("logging_agent_address"); - wo->serverSocketFd = createServer(address.c_str()); + wo->serverSocketFd.assign(createServer(address, 0, true, + __FILE__, __LINE__), NULL, 0); + P_LOG_FILE_DESCRIPTOR_PURPOSE(wo->serverSocketFd, + "Server address: " << wo->serverSocketFd); if (getSocketAddressType(address) == SAT_UNIX) { makeFileWorldReadableAndWritable(parseUnixSocketAddress(address)); } @@ -210,7 +215,10 @@ startListening() { adminAddresses = options.getStrSet("logging_agent_admin_addresses", false); foreach (address, adminAddresses) { - wo->adminSockets.push_back(createServer(address)); + wo->adminSockets.push_back(createServer(address, 0, true, + __FILE__, __LINE__)); + P_LOG_FILE_DESCRIPTOR_PURPOSE(wo->adminSockets.back(), + "Server address: " << wo->adminSockets.back()); if (getSocketAddressType(address) == SAT_UNIX) { makeFileWorldReadableAndWritable(parseUnixSocketAddress(address)); } diff --git a/ext/common/agents/Watchdog/AdminServer.h b/ext/common/agents/Watchdog/AdminServer.h index d712d38634..97d22d27ff 100644 --- a/ext/common/agents/Watchdog/AdminServer.h +++ b/ext/common/agents/Watchdog/AdminServer.h @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2014 Phusion + * Copyright (c) 2014-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -177,12 +177,16 @@ class AdminServer: public ServerKit::HttpServerpool, "content-type", "application/json"); doc["log_level"] = getLogLevel(); if (!logFile.empty()) { doc["log_file"] = logFile; } + if (!fileDescriptorLogFile.empty()) { + doc["file_descriptor_log_file"] = fileDescriptorLogFile; + } writeSimpleResponse(client, 200, &headers, doc.toStyledString()); if (!req->ended()) { @@ -210,8 +214,8 @@ class AdminServer: public ServerKit::HttpServerpool, bufsize); snprintf(message, bufsize, "{ \"status\": \"error\", " @@ -245,8 +249,8 @@ class AdminServer: public ServerKit::HttpServerpool, bufsize); snprintf(message, bufsize, "{ \"status\": \"error\", " diff --git a/ext/common/agents/Watchdog/AgentWatcher.cpp b/ext/common/agents/Watchdog/AgentWatcher.cpp index 3888f45f63..a39f803651 100644 --- a/ext/common/agents/Watchdog/AgentWatcher.cpp +++ b/ext/common/agents/Watchdog/AgentWatcher.cpp @@ -1,6 +1,6 @@ /* * Phusion Passenger - https://www.phusionpassenger.com/ - * Copyright (c) 2010-2013 Phusion + * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * @@ -265,7 +265,7 @@ class AgentWatcher: public boost::enable_shared_from_this { * arguments to this agent process through this fd, and we'll receive * startup information through it as well. */ - fds = createUnixSocketPair(); + fds = createUnixSocketPair(__FILE__, __LINE__); pid = syscalls::fork(); if (pid == 0) { @@ -334,7 +334,7 @@ class AgentWatcher: public boost::enable_shared_from_this { throw SystemException("Cannot fork a new process", e); } else { // Parent - FileDescriptor feedbackFd = fds[0]; + FileDescriptor feedbackFd(fds[0]); vector args; fds[1].close(); diff --git a/ext/common/agents/Watchdog/Main.cpp b/ext/common/agents/Watchdog/Main.cpp index 9ae8ab3700..4af725b8bc 100644 --- a/ext/common/agents/Watchdog/Main.cpp +++ b/ext/common/agents/Watchdog/Main.cpp @@ -120,7 +120,9 @@ namespace WatchdogAgent { AdminServer *adminServer; WorkingObjects() - : reportFile(-1), + : errorEvent(__FILE__, __LINE__, "WorkingObjects: errorEvent"), + exitEvent(__FILE__, __LINE__, "WorkingObjects: exitEvent"), + reportFile(-1), pidsCleanedUp(false), pidFileCleanedUp(false), bgloop(NULL), @@ -338,6 +340,7 @@ readCleanupPids(const WorkingObjectsPtr &wo) { size_t ret; ret = fread(buf, 1, 32, f); + fclose(f); if (ret > 0) { buf[ret] = '\0'; result.push_back(atoi(buf)); @@ -866,8 +869,8 @@ createPidFile() { } UPDATE_TRACE_POINT(); + FdGuard guard(fd, __FILE__, __LINE__); writeExact(fd, pidStr, strlen(pidStr)); - syscalls::close(fd); } } @@ -882,6 +885,7 @@ openReportFile(const WorkingObjectsPtr &wo) { throw FileSystemException("Cannot open report file " + reportFile, e, reportFile); } + P_LOG_FILE_DESCRIPTOR_OPEN4(fd, __FILE__, __LINE__, "WorkingObjects: reportFile"); wo->reportFile = fd; } } @@ -990,6 +994,7 @@ initializeWorkingObjects(const WorkingObjectsPtr &wo, InstanceDirToucherPtr &ins throw FileSystemException("Cannot open " + lockFilePath + " for reading", e, lockFilePath); } + P_LOG_FILE_DESCRIPTOR_OPEN4(wo->lockFile, __FILE__, __LINE__, "WorkingObjects: lock file"); createFile(wo->instanceDir->getPath() + "/watchdog.pid", toString(getpid())); @@ -1120,7 +1125,8 @@ initializeAdminServer(const WorkingObjectsPtr &wo) { UPDATE_TRACE_POINT(); for (unsigned int i = 0; i < adminAddresses.size(); i++) { P_DEBUG("Admin server will listen on " << adminAddresses[i]); - wo->adminServerFds[i] = createServer(adminAddresses[i]); + wo->adminServerFds[i] = createServer(adminAddresses[i], 0, true, + __FILE__, __LINE__); if (getSocketAddressType(adminAddresses[i]) == SAT_UNIX) { makeFileWorldReadableAndWritable(parseUnixSocketAddress(adminAddresses[i])); } @@ -1216,6 +1222,7 @@ reportAgentsInformation(const WorkingObjectsPtr &wo, const vectorreportFile, str.data(), str.size()); close(wo->reportFile); + P_LOG_FILE_DESCRIPTOR_CLOSE(wo->reportFile); wo->reportFile = -1; } } diff --git a/ext/libev/ev.c b/ext/libev/ev.c index f32866ccca..a240d7a07b 100644 --- a/ext/libev/ev.c +++ b/ext/libev/ev.c @@ -2477,6 +2477,18 @@ ev_backend (EV_P) EV_THROW return backend; } +int +ev_backend_fd (EV_P) EV_THROW +{ + return backend_fd; +} + +int +ev_loop_get_pipe (EV_P_ unsigned int index) EV_THROW +{ + return evpipe[index]; +} + #if EV_FEATURE_API unsigned int ev_iteration (EV_P) EV_THROW diff --git a/ext/libev/ev.h b/ext/libev/ev.h index c5d582e05e..0f5839625b 100644 --- a/ext/libev/ev.h +++ b/ext/libev/ev.h @@ -605,6 +605,9 @@ EV_API_DECL void ev_loop_fork (EV_P) EV_THROW; EV_API_DECL unsigned int ev_backend (EV_P) EV_THROW; /* backend in use by loop */ +EV_API_DECL int ev_backend_fd (EV_P) EV_THROW; +EV_API_DECL int ev_loop_get_pipe (EV_P_ unsigned int index) EV_THROW; + EV_API_DECL void ev_now_update (EV_P) EV_THROW; /* update event loop time */ #if EV_WALK_ENABLE diff --git a/test/cxx/ApplicationPool2/ProcessTest.cpp b/test/cxx/ApplicationPool2/ProcessTest.cpp index 98d52c17da..0326b2ef7a 100644 --- a/test/cxx/ApplicationPool2/ProcessTest.cpp +++ b/test/cxx/ApplicationPool2/ProcessTest.cpp @@ -17,26 +17,26 @@ namespace tut { struct sockaddr_in addr; socklen_t len = sizeof(addr); - server1 = createTcpServer("127.0.0.1", 0); + server1.assign(createTcpServer("127.0.0.1", 0, 0, __FILE__, __LINE__), NULL, 0); getsockname(server1, (struct sockaddr *) &addr, &len); - sockets.add("main1", + sockets.add(1, "main1", "tcp://127.0.0.1:" + toString(addr.sin_port), "session", 3); - server2 = createTcpServer("127.0.0.1", 0); + server2.assign(createTcpServer("127.0.0.1", 0, 0, __FILE__, __LINE__), NULL, 0); getsockname(server2, (struct sockaddr *) &addr, &len); - sockets.add("main2", + sockets.add(2, "main2", "tcp://127.0.0.1:" + toString(addr.sin_port), "session", 3); - server3 = createTcpServer("127.0.0.1", 0); + server3.assign(createTcpServer("127.0.0.1", 0, 0, __FILE__, __LINE__), NULL, 0); getsockname(server3, (struct sockaddr *) &addr, &len); - sockets.add("main3", + sockets.add(3, "main3", "tcp://127.0.0.1:" + toString(addr.sin_port), "session", 3); - adminSocket = createUnixSocketPair(); - errorPipe = createPipe(); + adminSocket = createUnixSocketPair(__FILE__, __LINE__); + errorPipe = createPipe(__FILE__, __LINE__); } ProcessPtr createProcess() { diff --git a/test/cxx/ApplicationPool2/SpawnerTestCases.cpp b/test/cxx/ApplicationPool2/SpawnerTestCases.cpp index 5449af6876..09568f788e 100644 --- a/test/cxx/ApplicationPool2/SpawnerTestCases.cpp +++ b/test/cxx/ApplicationPool2/SpawnerTestCases.cpp @@ -23,7 +23,7 @@ #define RUN_USER_SWITCHING_TEST() \ object = spawner->spawn(options); \ object.process->requiresShutdown = false; \ - BufferedIO io(FileDescriptor(open("/tmp/info.txt", O_RDONLY))); \ + BufferedIO io(FileDescriptor(open("/tmp/info.txt", O_RDONLY), __FILE__, __LINE__)); \ uid_t uid = (uid_t) atol(io.readLine().c_str()); \ gid_t gid = (gid_t) atol(io.readLine().c_str()); \ string groups = strip(io.readLine()); \ diff --git a/test/cxx/BufferedIOTest.cpp b/test/cxx/BufferedIOTest.cpp index e8cda37dd9..07bb0a7198 100644 --- a/test/cxx/BufferedIOTest.cpp +++ b/test/cxx/BufferedIOTest.cpp @@ -19,7 +19,7 @@ namespace tut { BufferedIO::AcceptFunction a_twoBytesRead; BufferedIOTest() { - Pipe p = createPipe(); + Pipe p = createPipe(__FILE__, __LINE__); reader = p.first; writer = p.second; io = BufferedIO(reader); diff --git a/test/cxx/CxxTestMain.cpp b/test/cxx/CxxTestMain.cpp index 96f75156de..fd01c1f2d4 100644 --- a/test/cxx/CxxTestMain.cpp +++ b/test/cxx/CxxTestMain.cpp @@ -138,7 +138,7 @@ installAbortHandler() { options.set("passenger_root", resourceLocator->getRoot()); - initializeAgentOptions(options); + initializeAgentOptions("CxxTestMain", options); installAgentAbortHandler(); } diff --git a/test/cxx/EventedClientTest.cpp b/test/cxx/EventedClientTest.cpp index 306e63b3a9..2db3276f16 100644 --- a/test/cxx/EventedClientTest.cpp +++ b/test/cxx/EventedClientTest.cpp @@ -20,38 +20,38 @@ namespace tut { int lastErrorCode; AtomicInt integer; string data; - + EventedClientTest() : exitWatcher(eventLoop) { - SocketPair sockets = createUnixSocketPair(); + SocketPair sockets = createUnixSocketPair(__FILE__, __LINE__); fd1 = sockets.first; fd2 = sockets.second; setNonBlocking(fd2); - + exitWatcher.set(this); exitWatcher.start(); thr = NULL; lastErrorCode = -1; } - + ~EventedClientTest() { stopEventLoop(); } - + void startEventLoop() { thr = new oxt::thread( boost::bind(&EventedClientTest::runLoop, this) ); } - + void stopEventLoop() { if (thr != NULL) { exitWatcher.send(); waitUntilEventLoopExits(); } } - + void waitUntilEventLoopExits() { if (thr != NULL) { thr->join(); @@ -59,36 +59,36 @@ namespace tut { thr = NULL; } } - + void runLoop() { eventLoop.loop(); } - + void unloop(ev::async &watcher, int revents) { eventLoop.unloop(); } - + static void setIntToOne(EventedClient *client) { EventedClientTest *test = (EventedClientTest *) client->userData; test->integer = 1; } - + static void setIntToTwo(EventedClient *client) { EventedClientTest *test = (EventedClientTest *) client->userData; test->integer = 2; } - + static void saveSystemError(EventedClient *client, const string &message, int code) { EventedClientTest *self = (EventedClientTest *) client->userData; self->lastErrorMessage = message; self->lastErrorCode = code; } - + static void exitEventLoop(EventedClient *client) { EventedClientTest *self = (EventedClientTest *) client->userData; self->eventLoop.unloop(); } - + static void readAndExitOnEof(EventedClient *client) { EventedClientTest *self = (EventedClientTest *) client->userData; char buf[1024]; @@ -102,7 +102,7 @@ namespace tut { }; DEFINE_TEST_GROUP(EventedClientTest); - + #define EVENT_LOOP_GUARD ScopeGuard g(boost::bind(&EventedClientTest::stopEventLoop, this)) TEST_METHOD(1) { @@ -111,15 +111,15 @@ namespace tut { write(fd1, "x", 1); client.userData = this; client.onReadable = EventedClientTest::setIntToOne; - + EVENT_LOOP_GUARD; startEventLoop(); - + SHOULD_NEVER_HAPPEN(100, result = integer == 1; ); } - + TEST_METHOD(2) { // It watches read events after calling notifyReads(true). EventedClient client(eventLoop, fd2); @@ -127,21 +127,21 @@ namespace tut { client.userData = this; client.onReadable = EventedClientTest::setIntToOne; client.notifyReads(true); - + EVENT_LOOP_GUARD; startEventLoop(); - + EVENTUALLY(1, result = integer == 1; ); } - + TEST_METHOD(3) { // I/O is allowed in the initial state. EventedClient client(eventLoop, fd2); ensure(client.ioAllowed()); } - + TEST_METHOD(4) { // EventedClientTest.write() writes all data to the socket // immediately whenever possible. @@ -149,16 +149,16 @@ namespace tut { EventedClient client(eventLoop, fd2); client.write(data, 2); ensure_equals(client.pendingWrites(), 0u); - + EVENT_LOOP_GUARD; startEventLoop(); - + char buf[100]; memset(buf, 0, sizeof(buf)); ensure(readExact(fd1, buf, strlen("hello world"))); ensure_equals(string(buf), "hello world"); } - + TEST_METHOD(5) { // If EventedClientTest.write() cannot write out all data immediately, // and the remaining data is within the outbox limit, then it ensures @@ -170,21 +170,21 @@ namespace tut { client.setOutboxLimit(str.size() + 1); client.write(&data, 1); ensure(client.pendingWrites() > 0); - + client.userData = this; client.onReadable = EventedClientTest::setIntToOne; client.notifyReads(true); ensure(client.readWatcherActive()); - + EVENT_LOOP_GUARD; startEventLoop(); - + char buf[str.size()]; memset(buf, 0, sizeof(buf)); ensure(readExact(fd1, buf, str.size())); ensure(memcmp(buf, str.c_str(), str.size()) == 0); } - + TEST_METHOD(6) { // If EventedClientTest.write() cannot write out all data immediately, // and the remaining data is larger than the outbox limit, then it ensures @@ -196,27 +196,27 @@ namespace tut { client.setOutboxLimit(1); client.userData = this; client.onReadable = EventedClientTest::setIntToOne; - + client.notifyReads(true); client.write(&data, 1); ensure("(1)", client.pendingWrites() > 0); ensure("(2)", !client.readWatcherActive()); client.notifyReads(true); ensure("(3)", !client.readWatcherActive()); - + startEventLoop(); EVENT_LOOP_GUARD; - + char buf[str.size()]; memset(buf, 0, sizeof(buf)); ensure(readExact(fd1, buf, str.size())); ensure(memcmp(buf, str.c_str(), str.size()) == 0); - + // readWatcher will become active again after all pending data has been sent. stopEventLoop(); ensure(client.readWatcherActive()); } - + TEST_METHOD(7) { // disconnect() closes the connection and emits a disconnection event. EventedClient client(eventLoop, fd2); @@ -224,12 +224,12 @@ namespace tut { client.onDisconnect = EventedClientTest::setIntToTwo; client.disconnect(); ensure(!client.ioAllowed()); - + char buf; ensure_equals(read(fd1, &buf, 1), (ssize_t) 0); ensure_equals(integer, 2); } - + TEST_METHOD(8) { // If there's pending outgoing data then disconnect(false) does // not disconnect immediately and does not discard pending @@ -247,34 +247,34 @@ namespace tut { client.onDisconnect = EventedClientTest::setIntToTwo; client.write(&data, 1); client.disconnect(); - + ensure(!client.ioAllowed()); ensure(!client.readWatcherActive()); - + char buf[str.size()]; ensure(!client.ioAllowed()); ensure_equals(read(fd1, buf, 1), (ssize_t) 1); ensure_equals(buf[0], '\1'); - + startEventLoop(); EVENT_LOOP_GUARD; - + // No disconnection event yet. SHOULD_NEVER_HAPPEN(100, result = integer == 2; ); - + memset(buf, 0, sizeof(buf)); ensure(readExact(fd1, buf, str.size() - 1)); ensure(memcmp(buf, str.c_str() + 1, str.size() - 1) == 0); - + ensure_equals(read(fd1, buf, 1), (ssize_t) 0); - + stopEventLoop(); // Disconnection event should now have fired. ensure_equals(integer, 2); } - + TEST_METHOD(9) { // If there's pending outgoing data then disconnect(true) // closes the connection immediately without sending out @@ -287,16 +287,16 @@ namespace tut { client.setOutboxLimit(str.size() + 1); client.write(&data, 1); client.disconnect(true); - + ensure(!client.ioAllowed()); ensure(!client.readWatcherActive()); ensure(client.pendingWrites() > 0); ensure_equals(integer, 2); - + string result = readAll(fd1); ensure_equals(result.size(), str.size() - client.pendingWrites()); } - + TEST_METHOD(10) { // EventedClient.write() doesn't do anything if the client is // already disconnected. @@ -305,11 +305,11 @@ namespace tut { EventedClient client(eventLoop, fd2); client.disconnect(); client.write(&data, 1); - + char buf; ensure_equals(read(fd1, &buf, 1), (ssize_t) 0); } - + TEST_METHOD(11) { // EventedClient.write() doesn't do anything if the client // is being disconnected after pending data is sent out. @@ -317,7 +317,7 @@ namespace tut { string str2(1024 * 256, '\2'); StaticString data; size_t pending; - + EventedClient client(eventLoop, fd2); client.setOutboxLimit(1); data = str1; @@ -327,14 +327,14 @@ namespace tut { data = str2; client.write(&data, 1); ensure_equals(client.pendingWrites(), pending); - + startEventLoop(); EVENT_LOOP_GUARD; - + string result = readAll(fd1); ensure_equals(result, str1); } - + TEST_METHOD(12) { // EventedClient.detach() returns the original file descriptor // and makes I/O to the file descriptor via its own object @@ -345,10 +345,10 @@ namespace tut { ensure_equals(client.fd, -1); ensure(!client.ioAllowed()); ensure(!client.readWatcherActive()); - + StaticString data = "hi"; client.write(&data, 1); - + char buf[2]; ssize_t ret; int e; @@ -358,7 +358,7 @@ namespace tut { ensure_equals(ret, -1); ensure_equals(e, EAGAIN); } - + TEST_METHOD(13) { // EventedClient.detach() emits a detach event. EventedClient client(eventLoop, fd2); @@ -367,7 +367,7 @@ namespace tut { client.detach(); ensure_equals(integer, 2); } - + TEST_METHOD(14) { // Subsequent calls to EventedClient.detach() return -1 // and no longer emit detach events. @@ -378,7 +378,7 @@ namespace tut { ensure_equals(client.detach(), -1); ensure_equals(integer, 0); } - + TEST_METHOD(15) { // EventedClient.write() emits a pending data flushed event // if no outgoing data needs to be scheduled for later. @@ -389,7 +389,7 @@ namespace tut { ensure_equals(client.pendingWrites(), (size_t) 0); ensure_equals(integer, 2); } - + TEST_METHOD(16) { // EventedClient emits a pending data flushed event // after all pending outgoing data is flushed. @@ -400,17 +400,17 @@ namespace tut { client.write(str); ensure(client.pendingWrites() > 0); ensure_equals(integer, 0); - + startEventLoop(); EVENT_LOOP_GUARD; - + char buf[str.size()]; readExact(fd1, buf, str.size()); EVENTUALLY(2, result = integer == 2; ); } - + TEST_METHOD(17) { // EventedClient.write() ensures that the given data is written // after what's already in the outbox. @@ -418,24 +418,24 @@ namespace tut { string header(1024 * 4, 'x'); string body(1024 * 1024, 'y'); char buf[header.size() + body.size() + 1024]; - + client.write(header); client.write(body); ensure("(1)", client.pendingWrites() > 0); - + ensure_equals("(2)", readExact(fd1, buf, header.size()), (unsigned int) header.size()); ensure_equals("(3)", StaticString(buf, header.size()), header); - + client.write("hello world"); - + startEventLoop(); EVENT_LOOP_GUARD; - + unsigned int len = body.size() + strlen("hello world"); ensure_equals("(4)", readExact(fd1, buf, len), len); ensure_equals("(5)", StaticString(buf, len), body + "hello world"); } - + TEST_METHOD(18) { // If writeErrorAction is DISCONNECT_FULL then // EventedClient.write(), upon encountering a write error, @@ -449,7 +449,7 @@ namespace tut { ensure_equals(lastErrorCode, EPIPE); ensure_equals(client.fd, -1); } - + TEST_METHOD(19) { // If writeErrorAction is DISCONNECT_FULL then // the background writer disconnects the connection @@ -458,20 +458,20 @@ namespace tut { client.userData = this; client.writeErrorAction = EventedClient::DISCONNECT_FULL; client.onSystemError = saveSystemError; - + string str(1024 * 1024, 'x'); client.write(str); ensure("(1)", client.pendingWrites() > 0); - + fd1.close(); client.onDisconnect = exitEventLoop; startEventLoop(); waitUntilEventLoopExits(); - + ensure_equals("(2)", lastErrorCode, EPIPE); ensure_equals("(3)", client.fd, -1); } - + TEST_METHOD(20) { // If writeErrorAction is DISCONNECT_WRITE then // EventedClient.write(), upon encountering a write error, @@ -483,18 +483,18 @@ namespace tut { client.onSystemError = saveSystemError; client.onReadable = readAndExitOnEof; client.notifyReads(true); - + writeExact(fd1, "world", 5); fd1.close(); client.write("hello"); - + startEventLoop(); waitUntilEventLoopExits(); - + ensure(client.fd != -1); ensure_equals(data, "world"); } - + TEST_METHOD(21) { // If writeErrorAction is DISCONNECT_WRITE then // the background writer, upon encountering a write error, @@ -506,17 +506,17 @@ namespace tut { client.onSystemError = saveSystemError; client.onReadable = readAndExitOnEof; client.notifyReads(true); - + string str(1024 * 1024, 'x'); client.write(str); ensure("(1)", client.pendingWrites() > 0); - + writeExact(fd1, "world", 5); fd1.close(); - + startEventLoop(); waitUntilEventLoopExits(); - + ensure("(2)", client.fd != -1); ensure_equals("(3)", data, "world"); } diff --git a/test/cxx/FileDescriptorTest.cpp b/test/cxx/FileDescriptorTest.cpp index 947835c159..8dc81e3395 100644 --- a/test/cxx/FileDescriptorTest.cpp +++ b/test/cxx/FileDescriptorTest.cpp @@ -7,11 +7,11 @@ using namespace Passenger; namespace tut { struct FileDescriptorTest { int pipes[2]; - + FileDescriptorTest() { pipe(pipes); } - + ~FileDescriptorTest() { if (pipes[0] != -1) { close(pipes[0]); @@ -21,29 +21,29 @@ namespace tut { } } }; - + DEFINE_TEST_GROUP(FileDescriptorTest); - + TEST_METHOD(1) { // Test constructors. FileDescriptor f; ensure_equals("An empty FileDescriptor has value -1", f, -1); - + int fd = pipes[0]; pipes[0] = -1; - f = FileDescriptor(fd); + f = FileDescriptor(fd, __FILE__, __LINE__); ensure_equals("FileDescriptor takes the value of its constructor argument", f, fd); } - + TEST_METHOD(2) { // It closes the underlying file descriptor when the last // instance is destroyed. int reader = pipes[0]; pipes[0] = -1; { - FileDescriptor f(reader); + FileDescriptor f(reader, __FILE__, __LINE__); { FileDescriptor f2(f); } @@ -53,17 +53,17 @@ namespace tut { ensure("File descriptor is closed if the last live copy is dead", write(pipes[1], "x", 1) == -1); } - + TEST_METHOD(3) { // Calling close() will close the underlying file descriptor for all instances. int reader = pipes[0]; pipes[0] = -1; - - FileDescriptor f(reader); + + FileDescriptor f(reader, __FILE__, __LINE__); FileDescriptor f2(f); f.close(); - ensure_equals(f, -1); - ensure_equals(f2, -1); + ensure_equals("(1)", f, -1); + ensure_equals("(2)", f2, -1); ensure(write(pipes[1], "x", 1) == -1); } } diff --git a/test/cxx/IOUtilsTest.cpp b/test/cxx/IOUtilsTest.cpp index e8aa9ef7f5..753d6d9f8d 100644 --- a/test/cxx/IOUtilsTest.cpp +++ b/test/cxx/IOUtilsTest.cpp @@ -17,7 +17,7 @@ namespace tut { static int writevErrno; static int writevCalled; static string writevData; - + static ssize_t writev_mock(int fildes, const struct iovec *iov, int iovcnt) { if (writevResult >= 0) { string data; @@ -33,10 +33,10 @@ namespace tut { errno = writevErrno; return writevResult; } - + struct IOUtilsTest { string restBuffer; - + IOUtilsTest() { writevResult = 0; writevErrno = 0; @@ -44,17 +44,17 @@ namespace tut { writevData.clear(); setWritevFunction(writev_mock); } - + ~IOUtilsTest() { setWritevFunction(NULL); } - + Pipe createNonBlockingPipe() { - Pipe p = createPipe(); + Pipe p = createPipe(__FILE__, __LINE__); setNonBlocking(p.second); return p; } - + static void writeDataAfterSomeTime(int fd, unsigned int sleepTimeInUsec) { try { syscalls::usleep(sleepTimeInUsec); @@ -63,7 +63,7 @@ namespace tut { // Do nothing. } } - + static void writeDataSlowly(int fd, unsigned int bytesToWrite, unsigned int bytesPerSec) { try { for (unsigned i = 0; i < bytesToWrite && !this_thread::interruption_requested(); i++) { @@ -74,7 +74,7 @@ namespace tut { // Do nothing. } } - + static void readDataAfterSomeTime(int fd, unsigned int sleepTimeInUsec) { try { char buf[1024 * 8]; @@ -84,20 +84,20 @@ namespace tut { // Do nothing. } } - + static void readDataSlowly(int fd, int bytesToRead, int bytesPerSec) { try { unsigned long long start = SystemTime::getUsec(); unsigned long long deadline = start + (bytesToRead * 1000000.0 / bytesPerSec); int alreadyRead = 0; - + while (alreadyRead < bytesToRead && !this_thread::interruption_requested()) { unsigned long long elapsed = SystemTime::getUsec(); double progress = (elapsed - start) / (double) (deadline - start); int shouldHaveRead = progress * bytesToRead; int shouldNowRead = shouldHaveRead - alreadyRead; - + if (shouldNowRead > 0) { char *buf = new char[shouldNowRead]; ssize_t ret = syscalls::read(fd, buf, shouldNowRead); @@ -117,11 +117,11 @@ namespace tut { } } }; - + DEFINE_TEST_GROUP_WITH_LIMIT(IOUtilsTest, 100); - + /***** Test gatheredWrite() with empty input rest buffer *****/ - + TEST_METHOD(1) { // Test complete write of a single data buffer. StaticString data = "hello world"; @@ -130,7 +130,7 @@ namespace tut { ensure_equals(writevData, "hello world"); ensure(restBuffer.empty()); } - + TEST_METHOD(2) { // Test complete write of multiple data buffers. StaticString data[] = { "hello ", "world", "!!!!!!" }; @@ -139,7 +139,7 @@ namespace tut { ensure_equals(writevData, "hello world!!!!!!"); ensure(restBuffer.empty()); } - + TEST_METHOD(3) { // Test partial write of a single data buffer. StaticString data = "hello world"; @@ -148,7 +148,7 @@ namespace tut { ensure_equals(writevData, "hel"); ensure_equals(restBuffer, "lo world"); } - + TEST_METHOD(4) { // Test partial write of multiple data buffers: // first buffer is partially written. @@ -158,7 +158,7 @@ namespace tut { ensure_equals(writevData, "he"); ensure_equals(restBuffer, "llo world!!!!!!"); } - + TEST_METHOD(5) { // Test partial write of multiple data buffers: // first buffer is completely written. @@ -168,7 +168,7 @@ namespace tut { ensure_equals(writevData, "hello "); ensure_equals(restBuffer, "world!!!!!!"); } - + TEST_METHOD(6) { // Test partial write of multiple data buffers: // non-first buffer is partially written. @@ -178,7 +178,7 @@ namespace tut { ensure_equals(writevData, "hello wo"); ensure_equals(restBuffer, "rld!!!!!!"); } - + TEST_METHOD(7) { // Test partial write of multiple data buffers: // non-first buffer is completely written. @@ -188,7 +188,7 @@ namespace tut { ensure_equals(writevData, "hello world"); ensure_equals(restBuffer, "!!!!!!"); } - + TEST_METHOD(8) { // Test failed write of a single data buffer: blocking error. StaticString data = "hello world"; @@ -197,7 +197,7 @@ namespace tut { ensure_equals(gatheredWrite(0, &data, 1, restBuffer), 0); ensure_equals(restBuffer, "hello world"); } - + TEST_METHOD(9) { // Test failed write of a single data buffer: other error. StaticString data = "hello world"; @@ -209,7 +209,7 @@ namespace tut { ensure_equals(e, EBADF); ensure_equals("Rest buffer remains untouched", restBuffer, ""); } - + TEST_METHOD(10) { // Test failed write of multiple data buffers: blocking error. StaticString data[] = { "hello ", "world", "!!!" }; @@ -218,7 +218,7 @@ namespace tut { ensure_equals(gatheredWrite(0, data, 3, restBuffer), 0); ensure_equals(restBuffer, "hello world!!!"); } - + TEST_METHOD(11) { // Test failed write of multiple data buffers: other error. StaticString data[] = { "hello ", "world", "!!!" }; @@ -230,7 +230,7 @@ namespace tut { ensure_equals(e, EBADF); ensure_equals("Rest buffer remains untouched", restBuffer, ""); } - + TEST_METHOD(12) { // Test writing nothing. StaticString data[] = { "", "", "" }; @@ -241,7 +241,7 @@ namespace tut { ensure_equals(writevCalled, 0); ensure_equals(restBuffer, ""); } - + TEST_METHOD(13) { // Test writing multiple buffers where some are empty. StaticString data[] = { "hello ", "", "world" }; @@ -250,9 +250,9 @@ namespace tut { ensure_equals(writevData, "hello world"); ensure_equals(restBuffer, ""); } - + /***** Test gatheredWrite() with non-empty input rest buffer *****/ - + TEST_METHOD(15) { // Test complete write with a single data buffer. restBuffer = "oh "; @@ -262,7 +262,7 @@ namespace tut { ensure_equals(writevData, "oh hello world"); ensure(restBuffer.empty()); } - + TEST_METHOD(16) { // Test complete write with multiple data buffers. restBuffer = "oh "; @@ -272,7 +272,7 @@ namespace tut { ensure_equals(writevData, "oh hello world!!!"); ensure(restBuffer.empty()); } - + TEST_METHOD(17) { // Test partial write of a single data buffer. StaticString data = "hello world"; @@ -281,7 +281,7 @@ namespace tut { ensure_equals(writevData, "hel"); ensure_equals(restBuffer, "lo world"); } - + TEST_METHOD(18) { // Test partial write of multiple data buffers: // rest buffer is partially written. @@ -292,7 +292,7 @@ namespace tut { ensure_equals(writevData, "oh"); ensure_equals(restBuffer, " hello world!!!"); } - + TEST_METHOD(19) { // Test partial write of multiple data buffers: // rest buffer is completely written. @@ -303,7 +303,7 @@ namespace tut { ensure_equals(writevData, "oh "); ensure_equals(restBuffer, "hello world!!!"); } - + TEST_METHOD(20) { // Test partial write of multiple data buffers: // first buffer is partially written. @@ -314,7 +314,7 @@ namespace tut { ensure_equals(writevData, "oh h"); ensure_equals(restBuffer, "ello world!!!"); } - + TEST_METHOD(21) { // Test partial write of multiple data buffers: // first buffer is completely written. @@ -325,7 +325,7 @@ namespace tut { ensure_equals(writevData, "oh hello "); ensure_equals(restBuffer, "world!!!"); } - + TEST_METHOD(22) { // Test partial write of multiple data buffers: // non-first buffer is partially written. @@ -336,7 +336,7 @@ namespace tut { ensure_equals(writevData, "oh hello wo"); ensure_equals(restBuffer, "rld!!!"); } - + TEST_METHOD(23) { // Test partial write of multiple data buffers: // non-first buffer is completely written. @@ -347,7 +347,7 @@ namespace tut { ensure_equals(writevData, "oh hello world"); ensure_equals(restBuffer, "!!!"); } - + TEST_METHOD(24) { // Test failed write of a single data buffer: blocking error. restBuffer = "oh "; @@ -357,7 +357,7 @@ namespace tut { ensure_equals(gatheredWrite(0, &data, 1, restBuffer), 0); ensure_equals(restBuffer, "oh hello world"); } - + TEST_METHOD(25) { // Test failed write of a single data buffer: other error. restBuffer = "oh "; @@ -370,7 +370,7 @@ namespace tut { ensure_equals(e, EBADF); ensure_equals("Rest buffer remains untouched", restBuffer, "oh "); } - + TEST_METHOD(26) { // Test failed write of multiple data buffers: blocking error. restBuffer = "oh "; @@ -380,7 +380,7 @@ namespace tut { ensure_equals(gatheredWrite(0, data, 3, restBuffer), 0); ensure_equals(restBuffer, "oh hello world!!!"); } - + TEST_METHOD(27) { // Test failed write of multiple data buffers: other error. restBuffer = "oh "; @@ -393,7 +393,7 @@ namespace tut { ensure_equals(e, EBADF); ensure_equals("Rest buffer remains untouched", restBuffer, "oh "); } - + TEST_METHOD(28) { // Test writing multiple buffers that are all empty. restBuffer = "oh "; @@ -403,7 +403,7 @@ namespace tut { ensure_equals(writevData, "oh "); ensure_equals(restBuffer, ""); } - + TEST_METHOD(29) { // Test writing multiple buffers where one is empty. restBuffer = "oh "; @@ -413,17 +413,17 @@ namespace tut { ensure_equals(writevData, "oh hello world"); ensure_equals(restBuffer, ""); } - - + + /***** Test gatheredWrite() blocking version *****/ - + TEST_METHOD(35) { // It doesn't call writev() if requested to send 0 bytes. StaticString data[2] = { "", "" }; gatheredWrite(0, data, 2); ensure_equals(writevCalled, 0); } - + TEST_METHOD(36) { // Test sending all data in a single writev() call. StaticString data[] = { "hello", "my", "world" }; @@ -432,7 +432,7 @@ namespace tut { ensure_equals(writevData, "hellomyworld"); ensure_equals(writevCalled, 1); } - + TEST_METHOD(42) { // Test writing byte-by-byte. StaticString data[] = { "hello", "my", "world", "!!" }; @@ -441,7 +441,7 @@ namespace tut { ensure_equals(writevCalled, (int) strlen("hellomyworld!!")); ensure_equals(writevData, "hellomyworld!!"); } - + TEST_METHOD(43) { // Test writev() writing in chunks of 2 bytes. StaticString data[] = { "hello", "my", "world", "!!" }; @@ -450,7 +450,7 @@ namespace tut { ensure_equals(writevCalled, (int) strlen("hellomyworld!!") / 2); ensure_equals(writevData, "hellomyworld!!"); } - + static ssize_t writev_mock_44(int fildes, const struct iovec *iov, int iovcnt) { if (writevCalled == 3) { // Have the last call return 2 instead of 4. @@ -458,7 +458,7 @@ namespace tut { } return writev_mock(fildes, iov, iovcnt); } - + TEST_METHOD(44) { // Test writev() writing in chunks of 4 bytes. setWritevFunction(writev_mock_44); @@ -468,11 +468,11 @@ namespace tut { ensure_equals(writevCalled, 4); ensure_equals(writevData, "hellomyworld!!"); } - + TEST_METHOD(45) { // Test writev() timeout support. setWritevFunction(NULL); - Pipe p = createPipe(); + Pipe p = createPipe(__FILE__, __LINE__); unsigned long long startTime = SystemTime::getUsec(); unsigned long long timeout = 30000; char data1[1024], data2[1024]; @@ -495,55 +495,55 @@ namespace tut { ensure(timeout <= 2000); } } - + /***** Test waitUntilReadable() *****/ - + TEST_METHOD(50) { // waitUntilReadable() waits for the specified timeout if no data is readable. - Pipe p = createPipe(); + Pipe p = createPipe(__FILE__, __LINE__); unsigned long long timeout = 25000; ensure("No data is available", !waitUntilReadable(p.first, &timeout)); ensure("The passed time is deducted from the timeout", timeout < 5000); } - + TEST_METHOD(51) { // waitUntilReadable() waits for less than the specified timeout if data // is not available immediately but still available before the timeout. - Pipe p = createPipe(); + Pipe p = createPipe(__FILE__, __LINE__); TempThread thr(boost::bind(&writeDataAfterSomeTime, p.second, 35000)); - + unsigned long long timeout = 1000000; ensure("Data is available", waitUntilReadable(p.first, &timeout)); ensure("At least 35 msec passed.", timeout <= 1000000 - 35000); ensure("At most 70 msec passed.", timeout >= 1000000 - 70000); // depends on system scheduler though } - + TEST_METHOD(52) { // waitUntilReadable() returns immediately if timeout is 0. - Pipe p = createPipe(); + Pipe p = createPipe(__FILE__, __LINE__); unsigned long long timeout = 0; ensure("No data is available", !waitUntilReadable(p.first, &timeout)); ensure_equals("Timeout is not modified", timeout, 0u); - + write(p.second, "hi", 2); ensure("Data is available", waitUntilReadable(p.first, &timeout)); ensure_equals("Timeout is not modified", timeout, 0u); } - + TEST_METHOD(53) { // waitUntilReadable() returns immediately if there's data immediately available. - Pipe p = createPipe(); + Pipe p = createPipe(__FILE__, __LINE__); unsigned long long timeout = 100000; write(p.second, "hi", 2); ensure("Data is available", waitUntilReadable(p.first, &timeout)); ensure("Timeout is not modified", timeout >= 100000 - 5000); } - + /***** Test readExact() *****/ - + TEST_METHOD(54) { // readExact() throws TimeoutException if no data is received within the timeout. - Pipe p = createPipe(); + Pipe p = createPipe(__FILE__, __LINE__); unsigned long long timeout = 50000; char buf; try { @@ -553,15 +553,15 @@ namespace tut { ensure("The passed time is deducted from timeout", timeout < 5000); } } - + TEST_METHOD(55) { // readExact() throws TimeoutException if not enough data is received within the timeout. - Pipe p = createPipe(); + Pipe p = createPipe(__FILE__, __LINE__); unsigned long long timeout = 20000; char buf[100]; - + TempThread thr(boost::bind(&writeDataSlowly, p.second, sizeof(buf), 1)); - + try { readExact(p.first, &buf, sizeof(buf), &timeout); fail("No TimeoutException thrown."); @@ -569,10 +569,10 @@ namespace tut { ensure("The passed time is deducted from timeout", timeout < 5000); } } - + TEST_METHOD(56) { // readExact() throws TimeoutException if timeout is 0 and no data is immediately available. - Pipe p = createPipe(); + Pipe p = createPipe(__FILE__, __LINE__); unsigned long long timeout = 0; char buf; try { @@ -582,11 +582,11 @@ namespace tut { ensure_equals("Timeout unchanged", timeout, 0u); } } - + TEST_METHOD(57) { // readExact() throws TimeoutException if timeout is 0 and not enough data is // immediately available. - Pipe p = createPipe(); + Pipe p = createPipe(__FILE__, __LINE__); unsigned long long timeout = 0; write(p.second, "hi", 2); try { @@ -597,16 +597,16 @@ namespace tut { ensure_equals("Timeout is unchanged", timeout, 0u); } } - + TEST_METHOD(58) { // readExact() deducts the amount of time spent on waiting from the timeout variable. - Pipe p = createPipe(); + Pipe p = createPipe(__FILE__, __LINE__); unsigned long long timeout = 100000; char buf[3]; - + // Spawn a thread that writes 100 bytes per second, i.e. each byte takes 10 msec. TempThread thr(boost::bind(&writeDataSlowly, p.second, 1000, 100)); - + // We read 3 bytes. ensure_equals(readExact(p.first, &buf, sizeof(buf), &timeout), 3u); ensure("Should have taken at least 20 msec", timeout <= 100000 - 20000); @@ -617,21 +617,21 @@ namespace tut { ensure("Should have taken at most 50 msec", timeout >= 100000 - 40000); #endif } - + TEST_METHOD(59) { // readExact() does not wait and does not modify the timeout variable if there's // immediately enough data available. - Pipe p = createPipe(); + Pipe p = createPipe(__FILE__, __LINE__); unsigned long long timeout = 100000; char buf[2]; - + write(p.second, "hi", 2); ensure_equals(readExact(p.first, &buf, 2, &timeout), 2u); ensure("Timeout not modified", timeout >= 95000); } - + /***** Test waitUntilWritable() *****/ - + TEST_METHOD(60) { // waitUntilWritable() waits for the specified timeout if no data is writable. Pipe p = createNonBlockingPipe(); @@ -640,20 +640,20 @@ namespace tut { ensure("Socket did not become writable", !waitUntilWritable(p.second, &timeout)); ensure("The passed time is deducted from the timeout", timeout < 5000); } - + TEST_METHOD(61) { // waitUntilWritable() waits for less than the specified timeout if the fd // is not immediately writable but still writable before the timeout. Pipe p = createNonBlockingPipe(); writeUntilFull(p.second); TempThread thr(boost::bind(&readDataAfterSomeTime, p.first, 35000)); - + unsigned long long timeout = 1000000; ensure("Socket became writable", waitUntilWritable(p.second, &timeout)); ensure("At least 35 msec passed.", timeout <= 1000000 - 35000); ensure("At most 70 msec passed.", timeout >= 1000000 - 70000); // depends on system scheduler though } - + TEST_METHOD(62) { // waitUntilWritable() returns immediately if timeout is 0. Pipe p = createNonBlockingPipe(); @@ -661,13 +661,13 @@ namespace tut { unsigned long long timeout = 0; ensure("Socket is not writable", !waitUntilWritable(p.second, &timeout)); ensure_equals("Timeout is not modified", timeout, 0u); - + char buf[1024 * 8]; read(p.first, buf, sizeof(buf)); ensure("Socket became writable", waitUntilWritable(p.second, &timeout)); ensure_equals("Timeout is not modified", timeout, 0u); } - + TEST_METHOD(63) { // waitUntilWritable() returns immediately if the fd is immediately writable. Pipe p = createNonBlockingPipe(); @@ -678,9 +678,9 @@ namespace tut { ensure("Socket became writable", waitUntilWritable(p.second, &timeout)); ensure("Timeout is not modified", timeout >= 100000 - 5000); } - + /***** Test readExact() *****/ - + TEST_METHOD(64) { // writeExact() throws TimeoutException if fd does not become writable within the timeout. Pipe p = createNonBlockingPipe(); @@ -693,16 +693,16 @@ namespace tut { ensure("The passed time is deducted from timeout", timeout < 5000); } } - + TEST_METHOD(65) { // writeExact() throws TimeoutException if not enough data is written within the timeout. Pipe p = createNonBlockingPipe(); writeUntilFull(p.second); unsigned long long timeout = 20000; char buf[1024 * 3]; - + TempThread thr(boost::bind(&readDataSlowly, p.first, sizeof(buf), 512)); - + try { writeExact(p.second, "x", 1, &timeout); fail("No TimeoutException thrown."); @@ -710,7 +710,7 @@ namespace tut { ensure("The passed time is deducted from timeout", timeout < 5000); } } - + TEST_METHOD(66) { // writeExact() throws TimeoutException if timeout is 0 and the fd is not immediately writable. Pipe p = createNonBlockingPipe(); @@ -723,19 +723,19 @@ namespace tut { ensure_equals("Timeout unchanged", timeout, 0u); } } - + TEST_METHOD(67) { // writeExact() throws TimeoutException if timeout is 0 not enough data could be written immediately. Pipe p = createNonBlockingPipe(); writeUntilFull(p.second); unsigned long long timeout = 0; - + char buf[1024]; read(p.first, buf, sizeof(buf)); - + char buf2[1024 * 8]; memset(buf2, 0, sizeof(buf2)); - + try { writeExact(p.second, buf2, sizeof(buf2), &timeout); fail("No TimeoutException thrown."); @@ -743,22 +743,22 @@ namespace tut { ensure_equals("Timeout is unchanged", timeout, 0u); } } - + TEST_METHOD(68) { // readExact() deducts the amount of time spent on waiting from the timeout variable. Pipe p = createNonBlockingPipe(); unsigned long long timeout = 100000; - + // Spawn a thread that reads 200000 bytes in 35 msec. TempThread thr(boost::bind(&readDataSlowly, p.first, 5714286, 5714286)); - + // We write 200000 bytes. char buf[200000]; writeExact(p.second, &buf, sizeof(buf), &timeout); ensure("Should have taken at least 20 msec", timeout <= 100000 - 20000); ensure("Should have taken at most 95 msec", timeout >= 100000 - 95000); } - + TEST_METHOD(69) { // writeExact() does not wait and does not modify the timeout variable if // all data can be written immediately. @@ -768,9 +768,9 @@ namespace tut { writeExact(p.second, buf, sizeof(buf), &timeout); ensure("Timeout not modified", timeout >= 95000); } - + /***** Test getSocketAddressType() *****/ - + TEST_METHOD(70) { ensure_equals(getSocketAddressType(""), SAT_UNKNOWN); ensure_equals(getSocketAddressType("/foo.socket"), SAT_UNKNOWN); @@ -783,7 +783,7 @@ namespace tut { ensure_equals(getSocketAddressType("tcp://127.0.0.1"), SAT_TCP); ensure_equals(getSocketAddressType("tcp://127.0.0.1:80"), SAT_TCP); } - + TEST_METHOD(71) { ensure_equals(parseUnixSocketAddress("unix:/foo.socket"), "/foo.socket"); try { @@ -793,15 +793,15 @@ namespace tut { // Pass. } } - + TEST_METHOD(72) { string host; unsigned short port; - + parseTcpSocketAddress("tcp://127.0.0.1:80", host, port); ensure_equals(host, "127.0.0.1"); ensure_equals(port, 80); - + try { parseTcpSocketAddress("tcp://", host, port); fail("ArgumentException expected"); @@ -809,31 +809,32 @@ namespace tut { // Pass. } } - + /***** Test readFileDescriptor() and writeFileDescriptor() *****/ - + TEST_METHOD(80) { // Test whether it works. - SocketPair sockets = createUnixSocketPair(); - Pipe pipes = createPipe(); + SocketPair sockets = createUnixSocketPair(__FILE__, __LINE__); + Pipe pipes = createPipe(__FILE__, __LINE__); writeFileDescriptor(sockets[0], pipes[1]); - FileDescriptor fd(readFileDescriptor(sockets[1])); + FileDescriptor fd(readFileDescriptor(sockets[1]), __FILE__, __LINE__); writeExact(fd, "hello"); char buf[6]; ensure_equals(readExact(pipes[0], buf, 5), 5u); buf[5] = '\0'; ensure_equals(StaticString(buf), "hello"); } - + TEST_METHOD(81) { // Test whether timeout works. - SocketPair sockets = createUnixSocketPair(); - Pipe pipes = createPipe(); - + SocketPair sockets = createUnixSocketPair(__FILE__, __LINE__); + Pipe pipes = createPipe(__FILE__, __LINE__); + unsigned long long timeout = 30000; unsigned long long startTime = SystemTime::getUsec(); try { - FileDescriptor fd(readFileDescriptor(sockets[0], &timeout)); + FileDescriptor fd(readFileDescriptor(sockets[0], &timeout), + __FILE__, __LINE__); fail("TimeoutException expected"); } catch (const TimeoutException &) { unsigned long long elapsed = SystemTime::getUsec() - startTime; @@ -843,9 +844,9 @@ namespace tut { elapsed <= 95000); ensure(timeout <= 2000); } - + writeUntilFull(sockets[0]); - + startTime = SystemTime::getUsec(); timeout = 30000; try { diff --git a/test/cxx/MessageIOTest.cpp b/test/cxx/MessageIOTest.cpp index 60c5ac63d9..b4d79e0ec3 100644 --- a/test/cxx/MessageIOTest.cpp +++ b/test/cxx/MessageIOTest.cpp @@ -10,34 +10,34 @@ using namespace boost; namespace tut { struct MessageIOTest { Pipe pipes; - + MessageIOTest() { - pipes = createPipe(); + pipes = createPipe(__FILE__, __LINE__); } }; DEFINE_TEST_GROUP(MessageIOTest); - + /***** Test readUint16() and writeUint16() *****/ - + TEST_METHOD(1) { // They work. writeUint16(pipes[1], 0x3F56); writeUint16(pipes[1], 0x3F57); writeUint16(pipes[1], 0x3F58); - + unsigned char buf[2]; ensure_equals(readExact(pipes[0], buf, 2), 2u); ensure_equals(buf[0], 0x3F); ensure_equals(buf[1], 0x56); - + ensure_equals(readUint16(pipes[0]), 0x3F57u); - + uint16_t out; ensure(readUint16(pipes[0], out)); ensure_equals(out, 0x3F58); } - + TEST_METHOD(2) { // readUint16() throws EOFException on premature EOF. writeExact(pipes[1], "x", 1); @@ -48,7 +48,7 @@ namespace tut { } catch (const EOFException &) { } } - + TEST_METHOD(3) { // readUint16(uint32_t &) returns false EOFException on premature EOF. writeExact(pipes[1], "x", 1); @@ -56,7 +56,7 @@ namespace tut { uint16_t out; ensure(!readUint16(pipes[0], out)); } - + TEST_METHOD(4) { // Test timeout. unsigned long long timeout = 30000; @@ -69,9 +69,9 @@ namespace tut { ensure("About 30 ms elapsed (1)", elapsed >= 29000 && elapsed <= 95000); ensure("Time is correctly deducted from 'timeout' (1)", timeout <= 2000); } - + writeUntilFull(pipes[1]); - + timeout = 30000; startTime = SystemTime::getUsec(); try { @@ -83,29 +83,29 @@ namespace tut { ensure("Time is correctly deducted from 'timeout' (4)", timeout <= 2000); } } - + /***** Test readUint32() and writeUint32() *****/ - + TEST_METHOD(10) { // They work. writeUint32(pipes[1], 0x12343F56); writeUint32(pipes[1], 0x12343F57); writeUint32(pipes[1], 0x12343F58); - + unsigned char buf[4]; ensure_equals(readExact(pipes[0], buf, 4), 4u); ensure_equals(buf[0], 0x12); ensure_equals(buf[1], 0x34); ensure_equals(buf[2], 0x3F); ensure_equals(buf[3], 0x56); - + ensure_equals(readUint32(pipes[0]), 0x12343F57u); - + uint32_t out; ensure(readUint32(pipes[0], out)); ensure_equals(out, 0x12343F58u); } - + TEST_METHOD(11) { // readUint32() throws EOFException on premature EOF. writeExact(pipes[1], "xyz", 3); @@ -116,7 +116,7 @@ namespace tut { } catch (const EOFException &) { } } - + TEST_METHOD(12) { // readUint16(uint32_t &) returns false EOFException on premature EOF. writeExact(pipes[1], "xyz", 3); @@ -124,7 +124,7 @@ namespace tut { uint32_t out; ensure(!readUint32(pipes[0], out)); } - + TEST_METHOD(13) { // Test timeout. unsigned long long timeout = 30000; @@ -137,9 +137,9 @@ namespace tut { ensure(elapsed >= 29000 && elapsed <= 90000); ensure(timeout <= 2000); } - + writeUntilFull(pipes[1]); - + timeout = 30000; startTime = SystemTime::getUsec(); try { @@ -151,14 +151,14 @@ namespace tut { ensure(timeout <= 2000); } } - + /***** Test readArrayMessage() and writeArrayMessage() *****/ - + TEST_METHOD(20) { // Test <= 10 arguments. writeArrayMessage(pipes[1], "ab", "cd", "efg", NULL); writeArrayMessage(pipes[1], "ab", "cd", "efh", NULL); - + unsigned char buf[12]; readExact(pipes[0], buf, 12); ensure_equals(buf[0], 0u); @@ -173,19 +173,19 @@ namespace tut { ensure_equals(buf[9], 'f'); ensure_equals(buf[10], 'g'); ensure_equals(buf[11], '\0'); - + vector args = readArrayMessage(pipes[0]); ensure_equals(args.size(), 3u); ensure_equals(args[0], "ab"); ensure_equals(args[1], "cd"); ensure_equals(args[2], "efh"); } - + TEST_METHOD(21) { // Test > 10 arguments. writeArrayMessage(pipes[1], "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "a", "b", NULL); writeArrayMessage(pipes[1], "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", NULL); - + unsigned char buf[26]; readExact(pipes[0], buf, 26); ensure_equals(buf[0], 0u); @@ -214,7 +214,7 @@ namespace tut { ensure_equals(buf[23], '\0'); ensure_equals(buf[24], 'b'); ensure_equals(buf[25], '\0'); - + vector args = readArrayMessage(pipes[0]); ensure_equals(args.size(), 12u); ensure_equals(args[0], "c"); @@ -230,7 +230,7 @@ namespace tut { ensure_equals(args[10], "m"); ensure_equals(args[11], "n"); } - + TEST_METHOD(22) { // readArrayMessage() throws EOFException on premature EOF. writeExact(pipes[1], "\x00"); @@ -240,8 +240,8 @@ namespace tut { fail("EOFException expected (1)"); } catch (const EOFException &) { } - - pipes = createPipe(); + + pipes = createPipe(__FILE__, __LINE__); writeExact(pipes[1], "\x00\x04a\x00b"); pipes[1].close(); try { @@ -250,7 +250,7 @@ namespace tut { } catch (const EOFException &) { } } - + TEST_METHOD(23) { // Test timeout. unsigned long long timeout = 30000; @@ -263,9 +263,9 @@ namespace tut { ensure(elapsed >= 29000 && elapsed <= 90000); ensure(timeout <= 2000); } - + writeUntilFull(pipes[1]); - + timeout = 30000; startTime = SystemTime::getUsec(); try { @@ -277,14 +277,14 @@ namespace tut { ensure(timeout <= 2000); } } - + /***** Test readScalarMessage() and writeScalarMessage() *****/ - + TEST_METHOD(30) { // They work. writeScalarMessage(pipes[1], "hello"); writeScalarMessage(pipes[1], "world"); - + unsigned char buf[4 + 5]; readExact(pipes[0], buf, 4 + 5); ensure_equals(buf[0], 0u); @@ -296,10 +296,10 @@ namespace tut { ensure_equals(buf[6], 'l'); ensure_equals(buf[7], 'l'); ensure_equals(buf[8], 'o'); - + ensure_equals(readScalarMessage(pipes[0]), "world"); } - + TEST_METHOD(31) { // readScalarMessage() throws EOFException on premature EOF. writeExact(pipes[1], StaticString("\x00", 1)); @@ -309,8 +309,8 @@ namespace tut { fail("EOFException expected (1)"); } catch (const EOFException &) { } - - pipes = createPipe(); + + pipes = createPipe(__FILE__, __LINE__); writeExact(pipes[1], StaticString("\x00\x00\x00\x04" "abc", 4 + 3)); pipes[1].close(); try { @@ -319,7 +319,7 @@ namespace tut { } catch (const EOFException &) { } } - + TEST_METHOD(32) { // readScalarMessage() throws SecurityException if the // body larger than the limit @@ -330,7 +330,7 @@ namespace tut { } catch (const SecurityException &) { } } - + TEST_METHOD(33) { // Test timeout. unsigned long long timeout = 30000; @@ -343,9 +343,9 @@ namespace tut { ensure(elapsed >= 29000 && elapsed <= 90000); ensure(timeout <= 2000); } - + writeUntilFull(pipes[1]); - + timeout = 30000; startTime = SystemTime::getUsec(); try { diff --git a/test/cxx/ServerKit/HttpServerTest.cpp b/test/cxx/ServerKit/HttpServerTest.cpp index ec16463ad0..c3097965a4 100644 --- a/test/cxx/ServerKit/HttpServerTest.cpp +++ b/test/cxx/ServerKit/HttpServerTest.cpp @@ -273,7 +273,7 @@ namespace tut { FileDescriptor &connectToServer() { startLoop(); - fd = FileDescriptor(connectToUnixServer("tmp.server")); + fd = FileDescriptor(connectToUnixServer("tmp.server", __FILE__, __LINE__), NULL, 0); io = BufferedIO(fd); return fd; } diff --git a/test/cxx/ServerKit/ServerTest.cpp b/test/cxx/ServerKit/ServerTest.cpp index cdf0c1d754..6c607bc888 100644 --- a/test/cxx/ServerKit/ServerTest.cpp +++ b/test/cxx/ServerKit/ServerTest.cpp @@ -57,11 +57,11 @@ namespace tut { } FileDescriptor connectToServer1() { - return FileDescriptor(connectToUnixServer("tmp.server1")); + return FileDescriptor(connectToUnixServer("tmp.server1", __FILE__, __LINE__), NULL, 0); } FileDescriptor connectToServer2() { - return FileDescriptor(connectToUnixServer("tmp.server2")); + return FileDescriptor(connectToUnixServer("tmp.server2", __FILE__, __LINE__), NULL, 0); } unsigned int getActiveClientCount() { @@ -144,7 +144,7 @@ namespace tut { set_test_name("Accepting a new client works"); startServer(); - FileDescriptor fd = connectToServer1(); + FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); @@ -162,7 +162,7 @@ namespace tut { ensure_equals(getDisconnectedClientCount(), 0u); ensure_equals(getFreeClientCount(), 1u); - FileDescriptor fd = connectToServer1(); + FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); @@ -175,7 +175,7 @@ namespace tut { "the object is allocated"); startServer(); - FileDescriptor fd = connectToServer1(); + FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); @@ -190,7 +190,7 @@ namespace tut { server->clientFreelistLimit = 10; startServer(); - FileDescriptor fd = connectToServer1(); + FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); @@ -209,7 +209,7 @@ namespace tut { server->clientFreelistLimit = 0; startServer(); - FileDescriptor fd = connectToServer1(); + FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); @@ -230,7 +230,7 @@ namespace tut { server->clientFreelistLimit = 10; startServer(); - FileDescriptor fd = connectToServer1(); + FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); @@ -260,7 +260,7 @@ namespace tut { server->clientFreelistLimit = 0; startServer(); - FileDescriptor fd = connectToServer1(); + FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); @@ -329,7 +329,7 @@ namespace tut { USE_CUSTOM_SERVER_CLASS(Test25Server); startServer(); - FileDescriptor fd = connectToServer1(); + FileDescriptor fd(connectToServer1()); writeExact(fd, "hello", 5); EVENTUALLY(5, @@ -364,7 +364,7 @@ namespace tut { USE_CUSTOM_SERVER_CLASS(Test26Server); startServer(); - FileDescriptor fd = connectToServer1(); + FileDescriptor fd(connectToServer1()); writeExact(fd, "hello", 5); syscalls::shutdown(fd, SHUT_WR); ensure_equals(readAll(fd), "hello"); @@ -375,7 +375,7 @@ namespace tut { startServer(); - FileDescriptor fd = connectToServer1(); + FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); @@ -393,7 +393,7 @@ namespace tut { startServer(); - FileDescriptor fd = connectToServer1(); + FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); diff --git a/test/cxx/TestSupport.cpp b/test/cxx/TestSupport.cpp index e6f962da1d..41b05e7cc6 100644 --- a/test/cxx/TestSupport.cpp +++ b/test/cxx/TestSupport.cpp @@ -119,7 +119,7 @@ replaceStringInFile(const char *filename, const string &toFind, const string &re message.append("' for writing"); throw FileSystemException(message, e, filename); } else { - StdioGuard guard(f); + StdioGuard guard(f, __FILE__, __LINE__); content = replaceString(content, toFind, replaceWith); fwrite(content.data(), 1, content.size(), f); } @@ -140,7 +140,7 @@ writeFile(const string &filename, const string &contents) { message.append("' for writing"); throw FileSystemException(message, e, filename); } else { - StdioGuard guard(f); + StdioGuard guard(f, __FILE__, __LINE__); fwrite(contents.data(), 1, contents.size(), f); } } diff --git a/test/cxx/UnionStationTest.cpp b/test/cxx/UnionStationTest.cpp index 6ff451efe4..71cad76201 100644 --- a/test/cxx/UnionStationTest.cpp +++ b/test/cxx/UnionStationTest.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -63,7 +64,7 @@ namespace tut { void startLoggingServer(const boost::function &initFunc = boost::function()) { VariantMap options; options.set("analytics_dump_file", dumpFile); - serverFd = createUnixServer(socketFilename.c_str()); + serverFd.assign(createUnixServer(socketFilename.c_str(), 0, true, __FILE__, __LINE__), NULL, 0); server = ptr(new LoggingServer(eventLoop, serverFd, accountsDatabase, options)); if (initFunc) { @@ -603,7 +604,7 @@ namespace tut { SystemTime::forceAll(YESTERDAY + 1000000); SHOULD_NEVER_HAPPEN(250, try { - close(connectToUnixServer(socketFilename)); + FdGuard(connectToUnixServer(socketFilename, __FILE__, __LINE__), NULL, 0); result = false; } catch (const SystemException &) { result = true; @@ -616,7 +617,7 @@ namespace tut { SystemTime::forceAll(YESTERDAY + 1000000 + 5000000); usleep(100000); // Give server some time to run the timer. try { - close(connectToUnixServer(socketFilename)); + FdGuard(connectToUnixServer(socketFilename, __FILE__, __LINE__), NULL, 0); fail("(4)"); } catch (const SystemException &) { // Success @@ -650,7 +651,7 @@ namespace tut { SystemTime::forceAll(YESTERDAY + 1000000 + 5000000); usleep(100000); // Give server some time to run the timer. try { - close(connectToUnixServer(socketFilename)); + FdGuard(connectToUnixServer(socketFilename, __FILE__, __LINE__), NULL, 0); fail("(2)"); } catch (const SystemException &) { // Success