summaryrefslogtreecommitdiff
path: root/src/mongo/db/operation_cpu_timer.cpp
diff options
context:
space:
mode:
authorAmirsaman Memaripour <amirsaman.memaripour@mongodb.com>2020-10-02 02:40:31 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-10-14 15:29:48 +0000
commit1f0009a389042c24360509625d50a9e3812658c7 (patch)
treedfd754ceb4fa7c0a92ddd919e9759f9fe307528c /src/mongo/db/operation_cpu_timer.cpp
parent687a70903fc5f10de9066ac92e1b9e0806924327 (diff)
downloadmongo-1f0009a389042c24360509625d50a9e3812658c7.tar.gz
SERVER-47446 Measure cpu time taken by operations
Diffstat (limited to 'src/mongo/db/operation_cpu_timer.cpp')
-rw-r--r--src/mongo/db/operation_cpu_timer.cpp173
1 files changed, 173 insertions, 0 deletions
diff --git a/src/mongo/db/operation_cpu_timer.cpp b/src/mongo/db/operation_cpu_timer.cpp
new file mode 100644
index 00000000000..7627c8af11c
--- /dev/null
+++ b/src/mongo/db/operation_cpu_timer.cpp
@@ -0,0 +1,173 @@
+/**
+ * Copyright (C) 2020-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 <boost/optional.hpp>
+#include <fmt/format.h>
+
+#if defined(__linux__)
+#include <time.h>
+#endif // defined(__linux__)
+
+#include "mongo/db/operation_cpu_timer.h"
+
+#include "mongo/base/error_codes.h"
+#include "mongo/db/client.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/stdx/thread.h"
+#include "mongo/util/assert_util.h"
+#include "mongo/util/errno_util.h"
+#include "mongo/util/fail_point.h"
+
+namespace mongo {
+
+#if defined(__linux__)
+
+namespace {
+
+using namespace fmt::literals;
+
+MONGO_FAIL_POINT_DEFINE(hangCPUTimerAfterOnThreadAttach);
+MONGO_FAIL_POINT_DEFINE(hangCPUTimerAfterOnThreadDetach);
+
+class PosixTimer final : public OperationCPUTimer {
+public:
+ Nanoseconds getElapsed() const override;
+
+ void start() override;
+ void stop() override;
+
+ void onThreadAttach() override;
+ void onThreadDetach() override;
+
+private:
+ bool _timerIsRunning() const;
+ bool _isAttachedToCurrentThread() const;
+
+ // Returns the elapsed time since the creation of the current thread.
+ Nanoseconds _getThreadTime() const;
+
+ // Holds the value returned by `_getThreadTime()` at the time of starting/resuming the timer.
+ boost::optional<Nanoseconds> _startedOn;
+ boost::optional<stdx::thread::id> _threadId;
+ Nanoseconds _elapsedBeforeInterrupted = Nanoseconds(0);
+};
+
+Nanoseconds PosixTimer::getElapsed() const {
+ invariant(_isAttachedToCurrentThread(), "Not attached to current thread");
+ auto elapsed = _elapsedBeforeInterrupted;
+ if (_timerIsRunning())
+ elapsed += _getThreadTime() - _startedOn.get();
+ return elapsed;
+}
+
+bool PosixTimer::_timerIsRunning() const {
+ return _startedOn.has_value();
+}
+
+bool PosixTimer::_isAttachedToCurrentThread() const {
+ return _threadId.has_value() && _threadId.get() == stdx::this_thread::get_id();
+}
+
+void PosixTimer::start() {
+ invariant(!_timerIsRunning(), "Timer has already started");
+
+ _startedOn = _getThreadTime();
+ _threadId = stdx::this_thread::get_id();
+ _elapsedBeforeInterrupted = Nanoseconds(0);
+}
+
+void PosixTimer::stop() {
+ invariant(_timerIsRunning(), "Timer is not running");
+ invariant(_isAttachedToCurrentThread());
+
+ _elapsedBeforeInterrupted = getElapsed();
+ _startedOn.reset();
+}
+
+void PosixTimer::onThreadAttach() {
+ if (!_timerIsRunning())
+ return;
+
+ invariant(!_threadId.has_value(), "Timer has already been attached");
+ _threadId = stdx::this_thread::get_id();
+ _startedOn = _getThreadTime();
+
+ hangCPUTimerAfterOnThreadAttach.pauseWhileSet();
+}
+
+void PosixTimer::onThreadDetach() {
+ if (!_timerIsRunning())
+ return;
+
+ invariant(_threadId.has_value(), "Timer is not attached");
+ _threadId.reset();
+ _elapsedBeforeInterrupted = _getThreadTime() - _startedOn.get();
+
+ hangCPUTimerAfterOnThreadDetach.pauseWhileSet();
+}
+
+Nanoseconds PosixTimer::_getThreadTime() const {
+ struct timespec t;
+ if (auto ret = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t); ret != 0) {
+ int ec = errno;
+ internalAssert(Status(ErrorCodes::InternalError,
+ "Unable to get time: {}"_format(errnoWithDescription(ec))));
+ }
+ return Seconds(t.tv_sec) + Nanoseconds(t.tv_nsec);
+}
+
+static auto getCPUTimer = OperationContext::declareDecoration<PosixTimer>();
+
+} // namespace
+
+OperationCPUTimer* OperationCPUTimer::get(OperationContext* opCtx) {
+ invariant(Client::getCurrent() && Client::getCurrent()->getOperationContext() == opCtx,
+ "Operation not attached to the current thread");
+
+ // Checks for time support on POSIX platforms. In particular, it checks for support in presence
+ // of SMP systems.
+ static bool isTimeSupported = [] {
+ clockid_t cid;
+ return clock_getcpuclockid(0, &cid) == 0;
+ }();
+
+ if (!isTimeSupported)
+ return nullptr;
+ return &getCPUTimer(opCtx);
+}
+
+#else // not defined(__linux__)
+
+OperationCPUTimer* OperationCPUTimer::get(OperationContext*) {
+ return nullptr;
+}
+
+#endif // defined(__linux__)
+
+} // namespace mongo