summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Caimano <ben.caimano@mongodb.com>2019-10-17 20:59:44 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-03-03 18:12:45 +0000
commit781853e5021171e713fe226c31df88f3b7542530 (patch)
treebf95f21b38819c090ef542fc081dfba3e463d2af
parent68f523a474106c6fc0f0688b030c7b271725ece2 (diff)
downloadmongo-781853e5021171e713fe226c31df88f3b7542530.tar.gz
SERVER-42894 Create SourceLocation type and related macros
(cherry picked from commit a01e4d53caeb92cf07faa2507cefcfa8acbf017c)
-rw-r--r--src/mongo/platform/SConscript1
-rw-r--r--src/mongo/platform/source_location.h158
-rw-r--r--src/mongo/platform/source_location_test.cpp167
-rw-r--r--src/mongo/platform/source_location_test.h59
4 files changed, 385 insertions, 0 deletions
diff --git a/src/mongo/platform/SConscript b/src/mongo/platform/SConscript
index 62805813b64..4d3daed97a5 100644
--- a/src/mongo/platform/SConscript
+++ b/src/mongo/platform/SConscript
@@ -11,6 +11,7 @@ env.CppUnitTest('endian_test', 'endian_test.cpp')
env.CppUnitTest('mutex_test', 'mutex_test.cpp')
env.CppUnitTest('process_id_test', 'process_id_test.cpp')
env.CppUnitTest('random_test', 'random_test.cpp')
+env.CppUnitTest('source_location_test', 'source_location_test.cpp')
env.CppUnitTest('stack_locator_test', 'stack_locator_test.cpp')
env.CppUnitTest('decimal128_test', 'decimal128_test.cpp')
env.CppUnitTest('decimal128_bson_test', 'decimal128_bson_test.cpp')
diff --git a/src/mongo/platform/source_location.h b/src/mongo/platform/source_location.h
new file mode 100644
index 00000000000..272029635cd
--- /dev/null
+++ b/src/mongo/platform/source_location.h
@@ -0,0 +1,158 @@
+/**
+ * Copyright (C) 2019-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * 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
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * 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 Server Side 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <string>
+
+#if !defined(_MSC_VER) && !defined(__clang__) // not windows or clang
+#include <experimental/source_location>
+#endif // windows
+
+#include <fmt/format.h>
+
+namespace mongo {
+
+/**
+ * A SourceLocation is a constexpr type that captures a source location
+ *
+ * This class mimics the api signature of C++20 std::source_location
+ *
+ * It is intended to be constructed with MONGO_SOURCE_LOCATION() below.
+ */
+class SourceLocation {
+public:
+ constexpr SourceLocation(uint_least32_t line,
+ uint_least32_t column,
+ const char* file_name,
+ const char* function_name) noexcept
+ : _line(line), _column(column), _file_name(file_name), _function_name(function_name) {}
+
+ constexpr uint_least32_t line() const noexcept {
+ return _line;
+ }
+
+ constexpr uint_least32_t column() const noexcept {
+ return _column;
+ }
+
+ constexpr const char* file_name() const noexcept {
+ return _file_name;
+ }
+
+ constexpr const char* function_name() const noexcept {
+ return _function_name;
+ }
+
+private:
+ uint_least32_t _line;
+ uint_least32_t _column; // column will be 0 if there isn't compiler support
+ const char* _file_name;
+ const char* _function_name;
+};
+
+/**
+ * SourceLocationHolder is intended for convenient io of SourceLocation
+ */
+class SourceLocationHolder {
+public:
+ constexpr SourceLocationHolder(SourceLocation&& loc) noexcept
+ : _loc(std::forward<SourceLocation>(loc)) {}
+
+ constexpr uint_least32_t line() const noexcept {
+ return _loc.line();
+ }
+
+ constexpr uint_least32_t column() const noexcept {
+ return _loc.column();
+ }
+
+ constexpr const char* file_name() const noexcept {
+ return _loc.file_name();
+ }
+
+ constexpr const char* function_name() const noexcept {
+ return _loc.function_name();
+ }
+
+ std::string toString() const {
+ using namespace fmt::literals;
+ return R"({{fileName:"{}", line:{}, functionName:"{}"}})"_format(
+ _loc.file_name(), _loc.line(), _loc.function_name());
+ }
+
+ friend std::ostream& operator<<(std::ostream& out, const SourceLocationHolder& context) {
+ return out << context.toString();
+ }
+
+private:
+ SourceLocation _loc;
+};
+
+/**
+ * MONGO_SOURCE_LOCATION() either:
+ * - captures std::experimental::source_location::current()
+ * - makes a best effort with various macros and local static constants
+ *
+ * Since __FUNCSIG__ and __PRETTY_FUNCTION__ aren't defined outside of functions, there is also
+ * MONGO_SOURCE_LOCATION_NO_FUNC() for use with a default member initializatizer or constant
+ * initialization.
+ */
+#if defined(_MSC_VER) // windows
+
+// MSVC does not have any of N4810 yet. (see
+// https://developercommunity.visualstudio.com/idea/354069/implement-c-library-fundamentals-ts-v2.html)
+#define MONGO_SOURCE_LOCATION() SourceLocation(__LINE__, 0ul, __FILE__, __func__)
+#define MONGO_SOURCE_LOCATION_NO_FUNC() SourceLocation(__LINE__, 0ul, __FILE__, "")
+
+#elif defined(__clang__) // windows -> clang
+
+// Clang got __builtin_FILE et al as of 8.0.1 (see https://reviews.llvm.org/D37035)
+#define MONGO_SOURCE_LOCATION() SourceLocation(__LINE__, 0ul, __FILE__, __func__)
+#define MONGO_SOURCE_LOCATION_NO_FUNC() SourceLocation(__LINE__, 0ul, __FILE__, "")
+
+#elif defined(__GNUG__) // clang -> gcc
+
+constexpr auto toSourceLocation(std::experimental::source_location loc) {
+ // Note that std::experimental::source_location captures __func__, not __PRETTY_FUNC__
+ return SourceLocation(loc.line(), loc.column(), loc.file_name(), loc.function_name());
+}
+
+#define MONGO_SOURCE_LOCATION() toSourceLocation(std::experimental::source_location::current())
+#define MONGO_SOURCE_LOCATION_NO_FUNC() \
+ toSourceLocation(std::experimental::source_location::current())
+
+#else // gcc -> ?
+
+#error "Unknown compiler, cannot approximate std::source_location"
+
+#endif // ?
+
+} // namespace mongo
diff --git a/src/mongo/platform/source_location_test.cpp b/src/mongo/platform/source_location_test.cpp
new file mode 100644
index 00000000000..a702eda827e
--- /dev/null
+++ b/src/mongo/platform/source_location_test.cpp
@@ -0,0 +1,167 @@
+/**
+ * Copyright (C) 2019-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * 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
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * 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 Server Side 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/source_location_test.h"
+
+namespace mongo {
+namespace {
+
+constexpr SourceLocation makeLocalFunctionSourceLocationForTest() {
+ return MONGO_SOURCE_LOCATION();
+}
+
+const SourceLocationHolder kLocation = MONGO_SOURCE_LOCATION_NO_FUNC();
+
+struct StructWithDefaultInitContextMember {
+ const SourceLocationHolder location = MONGO_SOURCE_LOCATION_NO_FUNC();
+};
+
+#define CALL_MONGO_SOURCE_LOCATION() MONGO_SOURCE_LOCATION()
+
+TEST(SourceLocation, CorrectLineNumber) {
+ using LineT = std::invoke_result_t<decltype(&SourceLocation::line), SourceLocation>;
+ ASSERT_EQ(MONGO_SOURCE_LOCATION().line(), static_cast<LineT>(__LINE__));
+}
+
+TEST(SourceLocation, InlineVariable) {
+ SourceLocationHolder inlineLocation1 = MONGO_SOURCE_LOCATION();
+ SourceLocationHolder inlineLocation2 = MONGO_SOURCE_LOCATION();
+ SourceLocationHolder inlineLocation3 = MONGO_SOURCE_LOCATION();
+
+ // Each location should have the same filename
+ ASSERT_EQ(inlineLocation1.file_name(), inlineLocation2.file_name());
+ ASSERT_EQ(inlineLocation1.file_name(), inlineLocation3.file_name());
+
+ // The line numbers for each location should increase monotonically when inline
+ ASSERT_LT(inlineLocation1.line(), inlineLocation2.line());
+ ASSERT_LT(inlineLocation2.line(), inlineLocation3.line());
+
+ unittest::log() << inlineLocation1;
+ unittest::log() << inlineLocation2;
+ unittest::log() << inlineLocation3;
+}
+
+TEST(SourceLocation, LocalFunction) {
+ SourceLocationHolder inlineLocation1 = MONGO_SOURCE_LOCATION();
+ SourceLocationHolder localFunctionLocation1 = makeLocalFunctionSourceLocationForTest();
+ SourceLocationHolder localFunctionLocation2 = makeLocalFunctionSourceLocationForTest();
+
+ // The inline location should have the same file name but a later line
+ ASSERT_EQ(inlineLocation1.file_name(), localFunctionLocation1.file_name());
+ ASSERT_GT(inlineLocation1.line(), localFunctionLocation1.line());
+
+ // The two local function locations should be identical
+ ASSERT_EQ(localFunctionLocation1, localFunctionLocation2);
+
+ unittest::log() << inlineLocation1;
+ unittest::log() << localFunctionLocation1;
+ unittest::log() << localFunctionLocation2;
+}
+
+TEST(SourceLocation, HeaderFunction) {
+ SourceLocationHolder inlineLocation1 = MONGO_SOURCE_LOCATION();
+ SourceLocationHolder headerLocation1 = makeHeaderSourceLocationForTest();
+ SourceLocationHolder headerLocation2 = makeHeaderSourceLocationForTest();
+
+ // The inline location should have a different file name
+ ASSERT_NE(inlineLocation1.file_name(), headerLocation1.file_name());
+
+ // The two header locations should be identical
+ ASSERT_EQ(headerLocation1, headerLocation2);
+
+ unittest::log() << inlineLocation1;
+ unittest::log() << headerLocation1;
+ unittest::log() << headerLocation2;
+}
+
+TEST(SourceLocation, GlobalVariable) {
+ SourceLocationHolder inlineLocation1 = MONGO_SOURCE_LOCATION();
+
+ // The inline location should have the same file name but a later line
+ ASSERT_EQ(inlineLocation1.file_name(), kLocation.file_name());
+ ASSERT_GT(inlineLocation1.line(), kLocation.line());
+
+ unittest::log() << inlineLocation1;
+ unittest::log() << kLocation;
+}
+
+TEST(SourceLocation, DefaultStructMember) {
+ SourceLocationHolder inlineLocation1 = MONGO_SOURCE_LOCATION();
+ StructWithDefaultInitContextMember obj1;
+ StructWithDefaultInitContextMember obj2;
+
+ // The inline location should have the same file name but a later line
+ ASSERT_EQ(inlineLocation1.file_name(), obj1.location.file_name());
+ ASSERT_GT(inlineLocation1.line(), obj1.location.line());
+
+ // The two default ctor'd struct member locations should be identical
+ ASSERT_EQ(obj1.location, obj2.location);
+
+ unittest::log() << inlineLocation1;
+ unittest::log() << obj1.location;
+ unittest::log() << obj2.location;
+}
+
+TEST(SourceLocation, Macro) {
+ SourceLocationHolder inlineLocation1 = MONGO_SOURCE_LOCATION();
+ SourceLocationHolder inlineLocation2 = CALL_MONGO_SOURCE_LOCATION();
+
+ // Each location should have the same filename
+ ASSERT_EQ(inlineLocation1.file_name(), inlineLocation2.file_name());
+
+ // The line numbers for each location should increase monotonically when inline
+ ASSERT_LT(inlineLocation1.line(), inlineLocation2.line());
+
+ unittest::log() << inlineLocation1;
+ unittest::log() << inlineLocation2;
+}
+
+TEST(SourceLocation, Constexpr) {
+ constexpr SourceLocationHolder inlineLocation1 = MONGO_SOURCE_LOCATION();
+ constexpr SourceLocationHolder inlineLocation2 = MONGO_SOURCE_LOCATION();
+ static_assert((inlineLocation1.line() + 1) == inlineLocation2.line());
+ static_assert(inlineLocation1.column() == inlineLocation2.column());
+ static_assert(areEqual(inlineLocation1.file_name(), inlineLocation2.file_name()));
+ static_assert(areEqual(inlineLocation1.function_name(), inlineLocation2.function_name()));
+
+ constexpr auto localFunctionLocation = makeLocalFunctionSourceLocationForTest();
+ static_assert(inlineLocation1.line() > localFunctionLocation.line());
+ static_assert(areEqual(inlineLocation1.file_name(), localFunctionLocation.file_name()));
+ static_assert(
+ !areEqual(inlineLocation1.function_name(), localFunctionLocation.function_name()));
+
+ constexpr auto headerLocation = makeHeaderSourceLocationForTest();
+ static_assert(!areEqual(inlineLocation1.file_name(), headerLocation.file_name()));
+ static_assert(!areEqual(inlineLocation1.function_name(), headerLocation.function_name()));
+}
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/platform/source_location_test.h b/src/mongo/platform/source_location_test.h
new file mode 100644
index 00000000000..4b97c72777e
--- /dev/null
+++ b/src/mongo/platform/source_location_test.h
@@ -0,0 +1,59 @@
+/**
+ * Copyright (C) 2019-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * 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
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * 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 Server Side 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.
+ */
+
+#pragma once
+
+#include "mongo/unittest/unittest.h"
+
+#include "mongo/platform/source_location.h"
+
+namespace mongo {
+inline bool operator==(const SourceLocationHolder& lhs, const SourceLocationHolder& rhs) {
+ return lhs.line() == rhs.line() //
+ && lhs.column() == rhs.column() //
+ && lhs.file_name() == rhs.file_name() //
+ && lhs.function_name() == rhs.function_name();
+}
+
+inline bool operator!=(const SourceLocationHolder& lhs, const SourceLocationHolder& rhs) {
+ return !(lhs == rhs);
+}
+
+// Simple recursive constexpr string comparison to play nice with static_assert
+constexpr bool areEqual(const char* string1, const char* string2) {
+ return (string1 != nullptr) //
+ && (string2 != nullptr) //
+ && *string1 == *string2 //
+ && (*string1 == '\0' || areEqual(string1 + 1, string2 + 1));
+}
+
+inline constexpr SourceLocation makeHeaderSourceLocationForTest() {
+ return MONGO_SOURCE_LOCATION();
+}
+} // namespace mongo