summaryrefslogtreecommitdiff
path: root/src/mongo/platform
diff options
context:
space:
mode:
authorAndrew Morrow <acm@mongodb.com>2015-10-26 13:01:53 -0400
committerAndrew Morrow <acm@mongodb.com>2015-10-27 08:06:42 -0400
commite556e4763ad88c3308347b56b4831c46b014627e (patch)
tree31eb5f821d77b50a7d7185ef571d3824dfea0d03 /src/mongo/platform
parent17a0b2a3be87fb1a1a86db2ee0bf1871e4b8d6fc (diff)
downloadmongo-e556e4763ad88c3308347b56b4831c46b014627e.tar.gz
SERVER-19614 Implement stack bounds detection
Diffstat (limited to 'src/mongo/platform')
-rw-r--r--src/mongo/platform/SConscript3
-rw-r--r--src/mongo/platform/stack_locator.cpp73
-rw-r--r--src/mongo/platform/stack_locator.h103
-rw-r--r--src/mongo/platform/stack_locator_freebsd.cpp29
-rw-r--r--src/mongo/platform/stack_locator_linux.cpp29
-rw-r--r--src/mongo/platform/stack_locator_openbsd.cpp29
-rw-r--r--src/mongo/platform/stack_locator_osx.cpp51
-rw-r--r--src/mongo/platform/stack_locator_pthread_getattr_np.cpp62
-rw-r--r--src/mongo/platform/stack_locator_solaris.cpp51
-rw-r--r--src/mongo/platform/stack_locator_test.cpp171
-rw-r--r--src/mongo/platform/stack_locator_unknown.cpp39
-rw-r--r--src/mongo/platform/stack_locator_windows.cpp129
12 files changed, 769 insertions, 0 deletions
diff --git a/src/mongo/platform/SConscript b/src/mongo/platform/SConscript
index 021fd0990dc..59bdff21e9c 100644
--- a/src/mongo/platform/SConscript
+++ b/src/mongo/platform/SConscript
@@ -10,6 +10,8 @@ source_files = [
'process_id.cpp',
'shared_library.cpp',
'shared_library_${TARGET_OS_FAMILY}.cpp',
+ 'stack_locator.cpp',
+ 'stack_locator_${TARGET_OS}.cpp',
'strcasestr.cpp',
]
@@ -24,6 +26,7 @@ env.CppUnitTest('bits_test', 'bits_test.cpp')
env.CppUnitTest('endian_test', 'endian_test.cpp')
env.CppUnitTest('process_id_test', 'process_id_test.cpp', LIBDEPS=['platform'])
env.CppUnitTest('random_test', 'random_test.cpp', LIBDEPS=['$BUILD_DIR/mongo/base'])
+env.CppUnitTest('stack_locator_test', 'stack_locator_test.cpp', LIBDEPS=['platform'])
if GetOption('experimental-decimal-support') == 'on':
env.CppUnitTest('decimal128_test', 'decimal128_test.cpp', LIBDEPS=['$BUILD_DIR/mongo/base'])
diff --git a/src/mongo/platform/stack_locator.cpp b/src/mongo/platform/stack_locator.cpp
new file mode 100644
index 00000000000..4654d6c1977
--- /dev/null
+++ b/src/mongo/platform/stack_locator.cpp
@@ -0,0 +1,73 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/platform/stack_locator.h"
+
+#include "mongo/util/assert_util.h"
+
+namespace mongo {
+
+boost::optional<std::size_t> StackLocator::available() const {
+ if (!begin() || !end())
+ return boost::none;
+
+ // Technically, it is undefined behavior to compare or subtract
+ // two pointers that do not point into the same
+ // aggregate. However, we know that these are both pointers within
+ // the same stack, and it seems unlikely that the compiler will
+ // see that it can elide the comparison here.
+
+ const auto cbegin = reinterpret_cast<const char*>(begin());
+ const auto cthis = reinterpret_cast<const char*>(this);
+ const auto cend = reinterpret_cast<const char*>(end());
+
+ // TODO: Assumes that stack grows downward
+ invariant(cthis <= cbegin);
+ invariant(cthis > cend);
+
+ std::size_t avail = cthis - cend;
+
+ return avail;
+}
+
+boost::optional<size_t> StackLocator::size() const {
+ if (!begin() || !end())
+ return boost::none;
+
+ const auto cbegin = reinterpret_cast<const char*>(begin());
+ const auto cend = reinterpret_cast<const char*>(end());
+
+ // TODO: Assumes that stack grows downward
+ invariant(cbegin > cend);
+
+ return static_cast<size_t>(cbegin - cend);
+}
+
+} // namespace mongo
diff --git a/src/mongo/platform/stack_locator.h b/src/mongo/platform/stack_locator.h
new file mode 100644
index 00000000000..9fb387580fd
--- /dev/null
+++ b/src/mongo/platform/stack_locator.h
@@ -0,0 +1,103 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include <boost/optional.hpp>
+#include <cstddef>
+
+namespace mongo {
+
+/**
+ * Provides access to the current stack bounds and remaining
+ * available stack space.
+ *
+ * To use one, create it on the stack, like this:
+ *
+ * // Construct a new locator
+ * const StackLocator locator;
+ *
+ * // Get the start of the stack
+ * auto b = locator.begin();
+ *
+ * // Get the end of the stack
+ * auto e = locator.end();
+ *
+ * // Get the remaining space after 'locator' on the stack.
+ * auto avail = locator.available();
+ */
+class StackLocator {
+public:
+ /**
+ * Constructs a new StackLocator. The locator must have automatic
+ * storage duration or the behavior is undefined.
+ */
+ StackLocator();
+
+ /**
+ * Returns the address of the beginning of the stack, or nullptr
+ * if this cannot be done. Beginning here means those addresses
+ * that represent values of automatic duration found earlier in
+ * the call chain. Returns nullptr if the beginning of the stack
+ * could not be found.
+ */
+ void* begin() const {
+ return _begin;
+ }
+
+ /**
+ * Returns the address of the end of the stack, or nullptr if
+ * this cannot be done. End here means those addresses that
+ * represent values of automatic duration allocated deeper in the
+ * call chain. Returns nullptr if the end of the stack could not
+ * be found.
+ */
+ void* end() const {
+ return _end;
+ }
+
+ /**
+ * Returns the apparent size of the stack. Returns a disengaged
+ * optional if the size of the stack could not be determined.
+ */
+ boost::optional<size_t> size() const;
+
+ /**
+ * Returns the remaining stack available after the location of
+ * this StackLocator. Obviously, the StackLocator must have been
+ * constructed on the stack. Calling 'available' on a heap
+ * allocated StackAllocator will have undefined behavior. Returns
+ * a disengaged optional if the remaining stack cannot be
+ * determined.
+ */
+ boost::optional<std::size_t> available() const;
+
+private:
+ void* _begin = nullptr;
+ void* _end = nullptr;
+};
+
+} // namespace mongo
diff --git a/src/mongo/platform/stack_locator_freebsd.cpp b/src/mongo/platform/stack_locator_freebsd.cpp
new file mode 100644
index 00000000000..af46f763783
--- /dev/null
+++ b/src/mongo/platform/stack_locator_freebsd.cpp
@@ -0,0 +1,29 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "stack_locator_unknown.cpp"
diff --git a/src/mongo/platform/stack_locator_linux.cpp b/src/mongo/platform/stack_locator_linux.cpp
new file mode 100644
index 00000000000..d3bf42542af
--- /dev/null
+++ b/src/mongo/platform/stack_locator_linux.cpp
@@ -0,0 +1,29 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "stack_locator_pthread_getattr_np.cpp"
diff --git a/src/mongo/platform/stack_locator_openbsd.cpp b/src/mongo/platform/stack_locator_openbsd.cpp
new file mode 100644
index 00000000000..af46f763783
--- /dev/null
+++ b/src/mongo/platform/stack_locator_openbsd.cpp
@@ -0,0 +1,29 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "stack_locator_unknown.cpp"
diff --git a/src/mongo/platform/stack_locator_osx.cpp b/src/mongo/platform/stack_locator_osx.cpp
new file mode 100644
index 00000000000..2c9b855e9b0
--- /dev/null
+++ b/src/mongo/platform/stack_locator_osx.cpp
@@ -0,0 +1,51 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/platform/stack_locator.h"
+
+#include <pthread.h>
+
+#include "mongo/util/assert_util.h"
+
+namespace mongo {
+
+StackLocator::StackLocator() {
+ const auto self = pthread_self();
+ _begin = pthread_get_stackaddr_np(self);
+ invariant(_begin);
+
+ const auto size = pthread_get_stacksize_np(self);
+ invariant(size);
+
+ // TODO: Assumes stack grows downward on OS X.
+ _end = static_cast<char*>(_begin) - size;
+}
+
+} // namespace mongo
diff --git a/src/mongo/platform/stack_locator_pthread_getattr_np.cpp b/src/mongo/platform/stack_locator_pthread_getattr_np.cpp
new file mode 100644
index 00000000000..00961d06286
--- /dev/null
+++ b/src/mongo/platform/stack_locator_pthread_getattr_np.cpp
@@ -0,0 +1,62 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/platform/stack_locator.h"
+
+#include <pthread.h>
+
+#include "mongo/util/assert_util.h"
+
+namespace mongo {
+
+StackLocator::StackLocator() {
+ pthread_t self = pthread_self();
+ pthread_attr_t selfAttrs;
+ pthread_attr_init(&selfAttrs);
+ pthread_getattr_np(self, &selfAttrs);
+
+ void* base = nullptr;
+ size_t size = 0;
+
+ auto result = pthread_attr_getstack(&selfAttrs, &base, &size);
+ pthread_attr_destroy(&selfAttrs);
+
+ invariant(result == 0);
+ invariant(base != nullptr);
+ invariant(size != 0);
+
+ // TODO: Assumes a downward growing stack. Note here that
+ // getstack returns the stack *base*, being the bottom of the
+ // stack, so we need to add size to it.
+ _end = base;
+ _begin = static_cast<char*>(_end) + size;
+}
+
+} // namespace mongo
diff --git a/src/mongo/platform/stack_locator_solaris.cpp b/src/mongo/platform/stack_locator_solaris.cpp
new file mode 100644
index 00000000000..055f25505ba
--- /dev/null
+++ b/src/mongo/platform/stack_locator_solaris.cpp
@@ -0,0 +1,51 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/platform/stack_locator.h"
+
+#include <thread.h>
+
+#include "mongo/util/assert_util.h"
+
+namespace mongo {
+
+StackLocator::StackLocator() {
+ stack_t stack;
+ invariant(thr_stksegment(&stack) == 0);
+
+ invariant(stack.ss_sp != nullptr);
+ invariant(stack.ss_size != 0);
+
+ // TODO: Assumes stack grows downward on Solaris
+ _begin = stack.ss_sp;
+ _end = static_cast<char*>(_begin) - stack.ss_size;
+}
+
+} // namespace mongo
diff --git a/src/mongo/platform/stack_locator_test.cpp b/src/mongo/platform/stack_locator_test.cpp
new file mode 100644
index 00000000000..356b726135e
--- /dev/null
+++ b/src/mongo/platform/stack_locator_test.cpp
@@ -0,0 +1,171 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include <thread>
+
+#include "mongo/platform/stack_locator.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+namespace {
+
+TEST(StackLocator, StacLocatorFindsStackOfTestExecutorThread) {
+ const StackLocator locator;
+
+ const auto begin = locator.begin();
+ ASSERT_TRUE(nullptr != begin);
+
+ const auto end = locator.end();
+ ASSERT_TRUE(nullptr != end);
+
+ ASSERT_TRUE(begin != end);
+
+ const auto available = locator.available();
+ ASSERT_TRUE(available);
+ ASSERT_TRUE(available.get() > 0);
+
+ const auto size = locator.size();
+ ASSERT_TRUE(size);
+ ASSERT_TRUE(size.get() > 0);
+ ASSERT_TRUE(size.get() > available.get());
+}
+
+TEST(StackLocator, StacksGrowsDown) {
+ // The current implementation assumes a downward growing stack. Write a test
+ // that confirms that the current platform is downward growing, so that if the
+ // system is ever ported to an upward growing stack, we get a test failure.
+
+ const StackLocator locator;
+ ASSERT_TRUE(nullptr != locator.begin());
+ ASSERT_TRUE(nullptr != locator.end());
+
+ // NOTE: Technically, comparing pointers for ordering is UB if
+ // they aren't in the same aggregate, but we are already out
+ // with the dragons at the edge of the map.
+ ASSERT_TRUE(locator.begin() > locator.end());
+}
+
+TEST(StackLocator, StackLocatorFindsStackOfStdThread) {
+ bool foundBounds = false;
+
+ std::thread thr([&] {
+ const StackLocator locator;
+ auto avail = locator.available();
+ foundBounds = static_cast<bool>(avail);
+ });
+ thr.join();
+ ASSERT_TRUE(foundBounds);
+}
+
+struct LocatorThreadHelper {
+#ifdef _WIN32
+ static DWORD WINAPI run(LPVOID arg) {
+ static_cast<LocatorThreadHelper*>(arg)->_run();
+ return 0;
+ }
+#else
+ static void* run(void* arg) {
+ static_cast<LocatorThreadHelper*>(arg)->_run();
+ return nullptr;
+ }
+#endif
+
+ void _run() {
+ const StackLocator locator;
+ located = static_cast<bool>(locator.available());
+ if (located)
+ size = locator.size().get();
+ }
+
+ bool located = false;
+ size_t size = 0;
+};
+
+TEST(StackLocator, StackLocatorFindsStackOfNativeThreadWithDefaultStack) {
+ LocatorThreadHelper helper;
+
+#ifdef _WIN32
+
+ HANDLE thread = CreateThread(nullptr, 0, &LocatorThreadHelper::run, &helper, 0, nullptr);
+ ASSERT_NE(WAIT_FAILED, WaitForSingleObject(thread, INFINITE));
+
+#else
+
+ pthread_attr_t attrs;
+ ASSERT_EQ(0, pthread_attr_init(&attrs));
+ pthread_t thread;
+ ASSERT_EQ(0, pthread_create(&thread, &attrs, &LocatorThreadHelper::run, &helper));
+ ASSERT_EQ(0, pthread_join(thread, nullptr));
+
+#endif
+
+ ASSERT_TRUE(helper.located);
+}
+
+TEST(StackLocator, StackLocatorFindStackOfNativeThreadWithCustomStack) {
+ const size_t kThreadStackSize = 64 * 1024 * 1024;
+
+#ifdef _WIN32
+
+ LocatorThreadHelper helperNoCommit;
+ HANDLE thread = CreateThread(nullptr,
+ kThreadStackSize,
+ &LocatorThreadHelper::run,
+ &helperNoCommit,
+ STACK_SIZE_PARAM_IS_A_RESERVATION,
+ nullptr);
+ ASSERT_NE(WAIT_FAILED, WaitForSingleObject(thread, INFINITE));
+ ASSERT_TRUE(helperNoCommit.located);
+ ASSERT_EQ(kThreadStackSize, helperNoCommit.size);
+
+ LocatorThreadHelper helperCommit;
+ thread = CreateThread(
+ nullptr, kThreadStackSize, &LocatorThreadHelper::run, &helperCommit, 0, nullptr);
+ ASSERT_NE(WAIT_FAILED, WaitForSingleObject(thread, INFINITE));
+ ASSERT_TRUE(helperCommit.located);
+ ASSERT_TRUE(kThreadStackSize <= helperCommit.size);
+
+#else
+
+ LocatorThreadHelper helper;
+ pthread_attr_t attrs;
+ ASSERT_EQ(0, pthread_attr_init(&attrs));
+ ASSERT_EQ(0, pthread_attr_setstacksize(&attrs, kThreadStackSize));
+ pthread_t thread;
+ ASSERT_EQ(0, pthread_create(&thread, &attrs, &LocatorThreadHelper::run, &helper));
+ ASSERT_EQ(0, pthread_join(thread, nullptr));
+ ASSERT_TRUE(helper.located);
+ ASSERT_TRUE(kThreadStackSize <= helper.size);
+
+#endif
+}
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/platform/stack_locator_unknown.cpp b/src/mongo/platform/stack_locator_unknown.cpp
new file mode 100644
index 00000000000..fd3fb62c993
--- /dev/null
+++ b/src/mongo/platform/stack_locator_unknown.cpp
@@ -0,0 +1,39 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/platform/stack_locator.h"
+
+#include "mongo/util/assert_util.h"
+
+namespace mongo {
+
+StackLocator::StackLocator() = default;
+
+// namespace mongo
diff --git a/src/mongo/platform/stack_locator_windows.cpp b/src/mongo/platform/stack_locator_windows.cpp
new file mode 100644
index 00000000000..2ead30c0bf0
--- /dev/null
+++ b/src/mongo/platform/stack_locator_windows.cpp
@@ -0,0 +1,129 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/platform/stack_locator.h"
+
+#include "mongo/util/assert_util.h"
+
+namespace mongo {
+
+StackLocator::StackLocator() {
+ // Please see
+ //
+ // http://stackoverflow.com/questions/1740888/determining-stack-space-with-visual-studio/1747499#1747499
+ //
+ // for notes on the following arcana.
+
+ // TODO: _WIN32_WINNT >= 0x0602 (windows 8 / 2012 server) may be
+ // able to use GetCurrentThreadStackLimits
+
+ // Put something on the stack, convieniently the variable we are
+ // going to write into, and ask the VM system for information
+ // about the memory region it inhabits, which is the committed
+ // part of the stack.
+ MEMORY_BASIC_INFORMATION committedMbi = {};
+ invariant(VirtualQuery(&committedMbi, &committedMbi, sizeof(committedMbi)) != 0);
+ invariant(committedMbi.State == MEM_COMMIT);
+
+ // Now committedMbi.AllocationBase points to the reserved stack
+ // memory base address (the real bottom of the stack), and
+ // committedMbi.BaseAddress points to base address of the
+ // committed region, and committedMbi.RegionSize is the size of
+ // the commit region. Since the stack grows downward, the top of
+ // the stack is at the base address for the commit region plus the
+ // region size.
+ _begin = static_cast<char*>(committedMbi.BaseAddress) + committedMbi.RegionSize;
+
+ // Now, we skip down to the bottom, where the uncommitted memory
+ // is, and get its size. So, we ask for the region at the
+ // allocation base (the real bottom of the stack), after which
+ // uncommitedMbi will have a BaseAddress and a RegionSize that
+ // describes the uncommited area. The memory should have the
+ // RESERVE state set.
+ MEMORY_BASIC_INFORMATION uncommittedMbi = {};
+ invariant(VirtualQuery(committedMbi.AllocationBase, &uncommittedMbi, sizeof(uncommittedMbi)) !=
+ 0);
+
+ invariant(committedMbi.AllocationBase == uncommittedMbi.AllocationBase);
+ invariant(uncommittedMbi.RegionSize > 0);
+
+ // If the stack was created with CreateThread with dwStackSize
+ // non-zero and without the STACK_SIZE_PARAM_IS_A_RESERVATION
+ // flag, then the whole AllocationBase is our stack base, and
+ // there is no guard page.
+ if (uncommittedMbi.State == MEM_COMMIT) {
+ // NOTE: Originally, it seemed to make sense that what you would get back
+ // here would be the same information as in committedMbi. After all, the whole
+ // stack is committed. So, querying AllocationBase should give you back
+ // the same region.
+ //
+ // Bizzarely, that doesn't seem to be the case. Even though
+ // it has the same protections, state, etc., VirtualQuery seems
+ // to consider the stack that has been used distinct from the part
+ // that hasn't.
+ //
+ // invariant(uncommittedMbi.BaseAddress == committedMbi.BaseAddress);
+ // invariant(uncommittedMbi.RegionSize == committedMbi.RegionSize);
+ //
+
+ _end = static_cast<char*>(committedMbi.AllocationBase);
+ return;
+ }
+ invariant(uncommittedMbi.State == MEM_RESERVE);
+
+ if (kDebugBuild) {
+ // Locate the guard page, which sits bewteen the uncommitted
+ // region (which we know is not empty!), and the committed
+ // region. We can count the guard page as usable stack space,
+ // but it is good to find it so we can validate that we walked
+ // the stack correctly.
+
+ // The end of the guard page is right after the uncommitted
+ // area. Form a pointer into the guard page by skipping over the
+ // committed region.
+ const auto guard =
+ static_cast<char*>(uncommittedMbi.BaseAddress) + uncommittedMbi.RegionSize;
+
+ // With that pointer in hand, get the info about the guard
+ // region. We really only care about its size here (and we
+ // validate that it has the right bits).
+ MEMORY_BASIC_INFORMATION guardMbi = {};
+ invariant(VirtualQuery(guard, &guardMbi, sizeof(guardMbi)) != 0);
+
+ invariant(committedMbi.AllocationBase == guardMbi.AllocationBase);
+ invariant((guardMbi.Protect & PAGE_GUARD) != 0);
+ invariant(guardMbi.RegionSize > 0);
+ }
+
+ // The end of our stack is the allocation base for the whole stack.
+ _end = static_cast<char*>(committedMbi.AllocationBase);
+}
+
+} // namespace mongo