summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Benvenuto <mark.benvenuto@mongodb.com>2016-07-14 15:36:32 -0400
committerMark Benvenuto <mark.benvenuto@mongodb.com>2016-07-14 15:52:50 -0400
commitd1e1b5b80c1ae83c0180fef1fde093e3f1e24cae (patch)
treea6555e1797069785d06bbe6df1159c047ee40eb6
parent472ac6b5bcb50e8869d03ecb044ce8ed3d779954 (diff)
downloadmongo-d1e1b5b80c1ae83c0180fef1fde093e3f1e24cae.tar.gz
SERVER-24572 Add support for collecting information from /proc/stat
-rw-r--r--src/mongo/base/error_codes.err1
-rw-r--r--src/mongo/base/string_data.h24
-rw-r--r--src/mongo/util/SConscript21
-rw-r--r--src/mongo/util/procparser.cpp283
-rw-r--r--src/mongo/util/procparser.h73
-rw-r--r--src/mongo/util/procparser_test.cpp216
6 files changed, 615 insertions, 3 deletions
diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err
index 3979f9bdf59..aa3c0b24803 100644
--- a/src/mongo/base/error_codes.err
+++ b/src/mongo/base/error_codes.err
@@ -172,6 +172,7 @@ error_code("TooManyMatchingDocuments", 170)
error_code("CannotIndexParallelArrays", 171)
error_code("TransportSessionNotFound", 172)
error_code("QueryPlanKilled", 173)
+error_code("FileOpenFailed", 174)
# Non-sequential error codes (for compatibility only)
error_code("SocketException", 9001)
diff --git a/src/mongo/base/string_data.h b/src/mongo/base/string_data.h
index b1491b919e6..98a4dc6f7f1 100644
--- a/src/mongo/base/string_data.h
+++ b/src/mongo/base/string_data.h
@@ -36,6 +36,7 @@
#include <stdexcept>
#include <string>
+#include "mongo/stdx/type_traits.h"
#define MONGO_INCLUDE_INVARIANT_H_WHITELISTED
#include "mongo/util/invariant.h"
#undef MONGO_INCLUDE_INVARIANT_H_WHITELISTED
@@ -63,6 +64,9 @@ public:
// Declared in string_data_comparator_interface.h.
class ComparatorInterface;
+ // Iterator type
+ using const_iterator = const char*;
+
/** Constructs an empty StringData. */
constexpr StringData() = default;
@@ -98,6 +102,23 @@ public:
constexpr friend StringData operator"" _sd(const char* c, std::size_t len);
/**
+ * Constructs a StringData with begin and end iterators. begin points to the beginning of the
+ * string. end points to the position past the end of the string. In a null-terminated string,
+ * end points to the null-terminator.
+ *
+ * We template the second parameter to ensure if StringData is called with 0 in the second
+ * parameter, the (ptr,len) constructor is chosen instead.
+ */
+ template <
+ typename InputIt,
+ typename = stdx::enable_if_t<std::is_same<StringData::const_iterator, InputIt>::value>>
+ StringData(InputIt begin, InputIt end) {
+ invariant(begin && end);
+ _data = begin;
+ _size = std::distance(begin, end);
+ }
+
+ /**
* Returns -1, 0, or 1 if 'this' is less, equal, or greater than 'other' in
* lexicographical order.
*/
@@ -170,9 +191,6 @@ public:
//
// iterators
//
-
- typedef const char* const_iterator;
-
const_iterator begin() const {
return rawData();
}
diff --git a/src/mongo/util/SConscript b/src/mongo/util/SConscript
index 31f2d017eda..0a4864eea40 100644
--- a/src/mongo/util/SConscript
+++ b/src/mongo/util/SConscript
@@ -529,3 +529,24 @@ env.CppUnitTest(
'$BUILD_DIR/mongo/base',
]
)
+
+if env.TargetOSIs('linux'):
+ env.Library(
+ target='procparser',
+ source=[
+ "procparser.cpp",
+ ],
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/base',
+ ],
+ )
+
+ env.CppUnitTest(
+ target='procparser_test',
+ source=[
+ 'procparser_test.cpp',
+ ],
+ LIBDEPS=[
+ 'procparser',
+ ],
+ )
diff --git a/src/mongo/util/procparser.cpp b/src/mongo/util/procparser.cpp
new file mode 100644
index 00000000000..b0fa73bd122
--- /dev/null
+++ b/src/mongo/util/procparser.cpp
@@ -0,0 +1,283 @@
+/**
+ * Copyright (C) 2016 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.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kFTDC
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/util/procparser.h"
+
+#include <algorithm>
+#include <array>
+#include <boost/algorithm/string/finder.hpp>
+#include <boost/algorithm/string/split.hpp>
+#include <fcntl.h>
+#include <string>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <type_traits>
+#include <unistd.h>
+
+#include "mongo/base/parse_number.h"
+#include "mongo/base/status.h"
+#include "mongo/base/status_with.h"
+#include "mongo/base/string_data.h"
+#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/util/log.h"
+#include "mongo/util/mongoutils/str.h"
+#include "mongo/util/scopeguard.h"
+#include "mongo/util/text.h"
+namespace mongo {
+
+namespace {
+
+/**
+ * Get USER_HZ for the machine. See time(7) for an explanation.
+ */
+int64_t getTicksPerSecond() {
+ int64_t ret = sysconf(_SC_CLK_TCK);
+ return ret;
+}
+
+/**
+ * Convert USER_HZ to milliseconds.
+ */
+double convertTicksToMilliSeconds(const int64_t ticks, const int64_t ticksPerSecond) {
+ return static_cast<double>(ticks) / (static_cast<double>(ticksPerSecond) / 1000.0);
+}
+
+const size_t kFileBufferSize = 16384;
+const size_t kFileReadRetryCount = 5;
+
+/**
+ * Read a file from disk as a string with a null-terminating byte using the POSIX file api.
+ *
+ * This function is designed to get all the data it needs from small /proc files in a single read.
+ * The /proc/stat and /proc/diskstats files can vary in size, but 16kb will cover most cases.
+ *
+ * Finally, we return errors instead of throwing to ensure that FTDC can return partial information
+ * on failure instead of no information. Some container filesystems may overlay /proc so we may not
+ * be reading directly from the kernel.
+ */
+StatusWith<std::string> readFileAsString(StringData filename) {
+ int fd = open(filename.toString().c_str(), 0);
+ if (fd == -1) {
+ int err = errno;
+ return Status(ErrorCodes::FileOpenFailed,
+ str::stream() << "Failed to open file " << filename << " with error: "
+ << errnoWithDescription(err));
+ }
+ auto scopedGuard = MakeGuard([fd] { close(fd); });
+
+ BufBuilder builder(kFileBufferSize);
+ std::array<char, kFileBufferSize> buf;
+
+ ssize_t size_read = 0;
+
+ // Read until the end as needed
+ do {
+
+ // Retry if interrupted
+ size_t retry = 0;
+
+ do {
+ size_read = read(fd, buf.data(), kFileBufferSize);
+
+ if (size_read == -1) {
+ int err = errno;
+
+ // Retry if we hit EGAIN or EINTR a few times before giving up
+ if (retry < kFileReadRetryCount && (err == EAGAIN || err == EINTR)) {
+ ++retry;
+ continue;
+ }
+
+ return Status(ErrorCodes::FileStreamFailed,
+ str::stream() << "Failed to read file " << filename << " with error: "
+ << errnoWithDescription(err));
+ }
+
+ break;
+ } while (true);
+
+ if (size_read != 0) {
+ builder.appendBuf(buf.data(), size_read);
+ }
+ } while (size_read != 0);
+
+ // Null terminate the buffer since we are about to convert it to a string
+ builder.appendChar(0);
+
+ return std::string(builder.buf(), builder.len());
+}
+
+
+const char* const kAdditionCpuFields[] = {"user_ms",
+ "nice_ms",
+ "system_ms",
+ "idle_ms",
+ "iowait_ms",
+ "irq_ms",
+ "softirq_ms",
+ "steal_ms",
+ "guest_ms",
+ "guest_nice_ms"};
+const size_t kAdditionCpuFieldCount = std::extent<decltype(kAdditionCpuFields)>::value;
+
+} // namespace
+
+namespace procparser {
+// Here is an example of the type of string it supports.
+// Note: intr output has been trimmed
+//
+// The cpu field maps up to 10 individual fields depending on the kernel version. For other views,
+// this code assumes there is only a single value.
+//
+// For more information, see:
+// Documentation/filesystems/proc.txt in the Linux kernel
+// proc(5) man page
+//
+// > cat /proc/stat
+// cpu 41801 9179 32206 831134223 34279 0 947 0 0 0
+// cpu0 2977 450 2475 69253074 1959 0 116 0 0 0
+// cpu1 6213 4261 9400 69177349 845 0 539 0 0 0
+// cpu2 1949 831 3699 69261035 645 0 0 0 0 0
+// cpu3 2222 644 3283 69264801 783 0 0 0 0 0
+// cpu4 16576 607 4757 69232589 8195 0 291 0 0 0
+// cpu5 3742 391 4571 69257332 2322 0 0 0 0 0
+// cpu6 2173 376 743 69284308 400 0 0 0 0 0
+// cpu7 1232 375 704 69285753 218 0 0 0 0 0
+// cpu8 960 127 576 69262851 18107 0 0 0 0 0
+// cpu9 1755 227 744 69283938 362 0 0 0 0 0
+// cpu10 1380 641 678 69285193 219 0 0 0 0 0
+// cpu11 618 244 572 69285995 218 0 0 0 0 0
+// intr 54084718 135 2 ....
+// ctxt 190305514
+// btime 1463584038
+// processes 47438
+// procs_running 1
+// procs_blocked 0
+// softirq 102690251 8 26697410 115481 23345078 816026 0 2296 26068778 0 25645174
+//
+Status parseProcStat(const std::vector<StringData>& keys,
+ StringData data,
+ int64_t ticksPerSecond,
+ BSONObjBuilder* builder) {
+ bool foundKeys = false;
+
+ using string_split_iterator = boost::split_iterator<StringData::const_iterator>;
+
+ // Split the file by lines.
+ // token_compress_on means the iterator skips over consecutive '\n'. This should not be a
+ // problem in normal /proc/stat output.
+ for (string_split_iterator lineIt = string_split_iterator(
+ data.begin(),
+ data.end(),
+ boost::token_finder([](char c) { return c == '\n'; }, boost::token_compress_on));
+ lineIt != string_split_iterator();
+ ++lineIt) {
+
+ StringData line((*lineIt).begin(), (*lineIt).end());
+
+ // Split the line by spaces since that is the only delimiter for stat files.
+ // token_compress_on means the iterator skips over consecutive ' '. This is needed for the
+ // first line which is "cpu <number>".
+ string_split_iterator partIt = string_split_iterator(
+ line.begin(),
+ line.end(),
+ boost::token_finder([](char c) { return c == ' '; }, boost::token_compress_on));
+
+ // Skip processing this line if we do not have a key.
+ if (partIt == string_split_iterator()) {
+ continue;
+ }
+
+ StringData key((*partIt).begin(), (*partIt).end());
+
+ ++partIt;
+
+ // Skip processing this line if we only have a key, and no number.
+ if (partIt == string_split_iterator()) {
+ continue;
+ }
+
+ // Check if the key is in the list. /proc/stat will have extra keys, and
+ // may not have the keys we want.
+ if (keys.empty() || std::find(keys.begin(), keys.end(), key) != keys.end()) {
+
+ foundKeys = true;
+
+ if (key == "cpu") {
+ // Cpu is 10 fields, we need to chew through all of them.
+ // Some kernels we support lack the last field or two: guest and/or guest_nice.
+ for (size_t index = 0;
+ partIt != string_split_iterator() && index < kAdditionCpuFieldCount;
+ ++partIt, ++index) {
+
+ StringData stringValue((*partIt).begin(), (*partIt).end() - (*partIt).begin());
+
+ uint64_t value;
+
+ if (!parseNumberFromString(stringValue, &value).isOK()) {
+ value = 0;
+ }
+
+ builder->appendNumber(kAdditionCpuFields[index],
+ convertTicksToMilliSeconds(value, ticksPerSecond));
+ }
+ } else {
+ StringData stringValue((*partIt).begin(), (*partIt).end() - (*partIt).begin());
+
+ uint64_t value;
+
+ if (!parseNumberFromString(stringValue, &value).isOK()) {
+ value = 0;
+ }
+
+ builder->appendNumber(key, value);
+ }
+ }
+ }
+
+ return foundKeys ? Status::OK()
+ : Status(ErrorCodes::NoSuchKey, "Failed to find any keys in stat string");
+}
+
+Status parseProcStatFile(StringData filename,
+ const std::vector<StringData>& keys,
+ BSONObjBuilder* builder) {
+ auto swString = readFileAsString(filename);
+ if (!swString.isOK()) {
+ return swString.getStatus();
+ }
+
+ return parseProcStat(keys, swString.getValue(), getTicksPerSecond(), builder);
+}
+
+} // namespace procparser
+} // namespace mongo
diff --git a/src/mongo/util/procparser.h b/src/mongo/util/procparser.h
new file mode 100644
index 00000000000..3bbb0ece16e
--- /dev/null
+++ b/src/mongo/util/procparser.h
@@ -0,0 +1,73 @@
+/**
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "mongo/base/status.h"
+#include "mongo/base/string_data.h"
+
+namespace mongo {
+
+class BSONObjBuilder;
+
+namespace procparser {
+
+/**
+ * Reads a string matching /proc/stat format, and writes the values of the specified keys into
+ * builder. If fields are not in the "data" parameter, they are omitted. Converts fields
+ * from USER_HZ to milliseconds, and names fields with a "_ms" suffix. If the string is empty,
+ * corrupt, or missing fields, the builder will simply be missing fields.
+ *
+ * keys - sorted vector of field names to include in the output, "cpu" will include the 11 fields
+ * that make up cpu. If keys is empty, all keys are outputed.
+ * data - string to parsee
+ * ticksPerSecond - USER_HZ value
+ * builder - BSON output
+ */
+Status parseProcStat(const std::vector<StringData>& keys,
+ StringData data,
+ int64_t ticksPerSecond,
+ BSONObjBuilder* builder);
+
+/**
+* Read from file, and write the specified list of keys into builder.
+*
+* See parseProcStat.
+*
+* Returns Status errors on file reading issues.
+*/
+Status parseProcStatFile(StringData filename,
+ const std::vector<StringData>& keys,
+ BSONObjBuilder* builder);
+
+} // namespace procparser
+} // namespace mongo
diff --git a/src/mongo/util/procparser_test.cpp b/src/mongo/util/procparser_test.cpp
new file mode 100644
index 00000000000..f21c9683025
--- /dev/null
+++ b/src/mongo/util/procparser_test.cpp
@@ -0,0 +1,216 @@
+/**
+ * Copyright (C) 2016 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.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kFTDC
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/util/procparser.h"
+
+#include <boost/filesystem.hpp>
+#include <map>
+
+#include "mongo/bson/bsonobj.h"
+#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/unittest/unittest.h"
+#include "mongo/util/log.h"
+
+namespace mongo {
+
+namespace {
+using StringMap = std::map<std::string, uint64_t>;
+
+StringMap toStringMap(BSONObj& obj) {
+ StringMap map;
+
+ for (const auto& e : obj) {
+ map[e.fieldName()] = e.numberLong();
+ }
+
+ return map;
+}
+
+#define ASSERT_KEY(_key) ASSERT_TRUE(stringMap.find(_key) != stringMap.end());
+#define ASSERT_NO_KEY(_key) ASSERT_TRUE(stringMap.find(_key) == stringMap.end());
+#define ASSERT_KEY_AND_VALUE(_key, _value) ASSERT_EQUALS(stringMap.at(_key), _value);
+
+#define ASSERT_PARSE_STAT(_keys, _x) \
+ BSONObjBuilder builder; \
+ ASSERT_OK(procparser::parseProcStat(_keys, _x, 1000, &builder)); \
+ auto obj = builder.obj(); \
+ auto stringMap = toStringMap(obj);
+
+TEST(FTDCProcStat, TestStat) {
+
+ std::vector<StringData> keys{"cpu", "ctxt", "processes"};
+
+ // Normal case
+ {
+ ASSERT_PARSE_STAT(
+ keys,
+ "cpu 41801 9179 32206 831134223 34279 0 947 0 0 0\n"
+ "cpu0 2977 450 2475 69253074 1959 0 116 0 0 0\n"
+ "cpu1 6213 4261 9400 69177349 845 0 539 0 0 0\n"
+ "cpu2 1949 831 3699 69261035 645 0 0 0 0 0\n"
+ "cpu3 2222 644 3283 69264801 783 0 0 0 0 0\n"
+ "cpu4 16576 607 4757 69232589 8195 0 291 0 0 0\n"
+ "cpu5 3742 391 4571 69257332 2322 0 0 0 0 0\n"
+ "cpu6 2173 376 743 69284308 400 0 0 0 0 0\n"
+ "cpu7 1232 375 704 69285753 218 0 0 0 0 0\n"
+ "cpu8 960 127 576 69262851 18107 0 0 0 0 0\n"
+ "cpu9 1755 227 744 69283938 362 0 0 0 0 0\n"
+ "cpu10 1380 641 678 69285193 219 0 0 0 0 0\n"
+ "cpu11 618 244 572 69285995 218 0 0 0 0 0\n"
+ "intr 54084718 135 2 ....\n"
+ "ctxt 190305514\n"
+ "btime 1463584038\n"
+ "processes 47438\n"
+ "procs_running 1\n"
+ "procs_blocked 0\n"
+ "softirq 102690251 8 26697410 115481 23345078 816026 0 2296 26068778 0 25645174\n");
+ ASSERT_KEY_AND_VALUE("user_ms", 41801UL);
+ ASSERT_KEY_AND_VALUE("nice_ms", 9179UL);
+ ASSERT_KEY_AND_VALUE("system_ms", 32206UL);
+ ASSERT_KEY_AND_VALUE("idle_ms", 831134223UL);
+ ASSERT_KEY_AND_VALUE("iowait_ms", 34279UL);
+ ASSERT_KEY_AND_VALUE("irq_ms", 0UL);
+ ASSERT_KEY_AND_VALUE("softirq_ms", 947UL);
+ ASSERT_KEY_AND_VALUE("steal_ms", 0UL);
+ ASSERT_KEY_AND_VALUE("guest_ms", 0UL);
+ ASSERT_KEY_AND_VALUE("guest_nice_ms", 0UL);
+ ASSERT_KEY_AND_VALUE("ctxt", 190305514UL);
+ ASSERT_KEY_AND_VALUE("processes", 47438UL);
+ }
+
+ // Missing fields in cpu and others
+ {
+ ASSERT_PARSE_STAT(keys,
+ "cpu 41801 9179 32206\n"
+ "ctxt 190305514\n");
+ ASSERT_KEY_AND_VALUE("user_ms", 41801UL);
+ ASSERT_KEY_AND_VALUE("nice_ms", 9179UL);
+ ASSERT_KEY_AND_VALUE("system_ms", 32206UL);
+ ASSERT_NO_KEY("idle_ms");
+ ASSERT_KEY_AND_VALUE("ctxt", 190305514UL);
+ ASSERT_NO_KEY("processes");
+ }
+
+ // Missing fields in cpu and others
+ {
+ ASSERT_PARSE_STAT(keys,
+ "cpu 41801\n"
+ "ctxt 190305514\n");
+ ASSERT_KEY_AND_VALUE("user_ms", 41801UL);
+ ASSERT_NO_KEY("nice_ms");
+ ASSERT_KEY_AND_VALUE("ctxt", 190305514UL);
+ ASSERT_NO_KEY("processes");
+ }
+
+ // Missing fields in cpu
+ {
+ ASSERT_PARSE_STAT(keys,
+ "cpu \n"
+ "ctxt 190305514\n");
+ ASSERT_KEY_AND_VALUE("ctxt", 190305514UL);
+ ASSERT_NO_KEY("processes");
+ }
+
+ // Single string with only cpu and numbers
+ {
+ ASSERT_PARSE_STAT(keys, "cpu 41801 9179 32206");
+ ASSERT_KEY_AND_VALUE("user_ms", 41801UL);
+ ASSERT_KEY_AND_VALUE("nice_ms", 9179UL);
+ ASSERT_KEY_AND_VALUE("system_ms", 32206UL);
+ ASSERT_NO_KEY("idle_ms");
+ }
+
+ // Single string with only cpu
+ {
+ BSONObjBuilder builder;
+ ASSERT_NOT_OK(procparser::parseProcStat(keys, "cpu", 1000, &builder));
+ }
+
+ // Single string with only cpu and a number, and empty ctxt
+ {
+ ASSERT_PARSE_STAT(keys,
+ "cpu 123\n"
+ "ctxt");
+ ASSERT_KEY_AND_VALUE("user_ms", 123UL);
+ }
+
+ // Empty String
+ {
+ BSONObjBuilder builder;
+ ASSERT_NOT_OK(procparser::parseProcStat(keys, "", 1000, &builder));
+ }
+}
+
+// Test we can parse the /proc/stat on this machine. Also assert we have the expected fields
+// This tests is designed to exercise our parsing code on various Linuxes and fail
+// Normally when run in the FTDC loop we return a non-fatal error so we may not notice the failure
+// otherwise.
+TEST(FTDCProcStat, TestLocalStat) {
+ std::vector<StringData> keys{
+ "btime", "cpu", "ctxt", "processes", "procs_blocked", "procs_running",
+ };
+
+ BSONObjBuilder builder;
+
+ ASSERT_OK(procparser::parseProcStatFile("/proc/stat", keys, &builder));
+
+ BSONObj obj = builder.obj();
+ auto stringMap = toStringMap(obj);
+ log() << "OBJ:" << obj;
+ ASSERT_KEY("user_ms");
+ ASSERT_KEY("nice_ms");
+ ASSERT_KEY("idle_ms");
+ ASSERT_KEY("system_ms");
+ ASSERT_KEY("iowait_ms");
+ ASSERT_KEY("irq_ms");
+ ASSERT_KEY("softirq_ms");
+ ASSERT_KEY("steal_ms");
+ // Needs 2.6.24 - ASSERT_KEY("guest_ms");
+ // Needs 2.6.33 - ASSERT_KEY("guest_nice_ms");
+ ASSERT_KEY("ctxt");
+ ASSERT_KEY("btime");
+ ASSERT_KEY("processes");
+ ASSERT_KEY("procs_running");
+ ASSERT_KEY("procs_blocked");
+}
+
+TEST(FTDCProcStat, TestLocalNonExistentStat) {
+ std::vector<StringData> keys{
+ "btime", "cpu", "ctxt", "processes", "procs_blocked", "procs_running",
+ };
+ BSONObjBuilder builder;
+
+ ASSERT_NOT_OK(procparser::parseProcStatFile("/proc/does_not_exist", keys, &builder));
+}
+
+} // namespace
+} // namespace mongo