diff options
author | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2016-07-14 15:36:32 -0400 |
---|---|---|
committer | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2016-07-14 15:52:50 -0400 |
commit | d1e1b5b80c1ae83c0180fef1fde093e3f1e24cae (patch) | |
tree | a6555e1797069785d06bbe6df1159c047ee40eb6 | |
parent | 472ac6b5bcb50e8869d03ecb044ce8ed3d779954 (diff) | |
download | mongo-d1e1b5b80c1ae83c0180fef1fde093e3f1e24cae.tar.gz |
SERVER-24572 Add support for collecting information from /proc/stat
-rw-r--r-- | src/mongo/base/error_codes.err | 1 | ||||
-rw-r--r-- | src/mongo/base/string_data.h | 24 | ||||
-rw-r--r-- | src/mongo/util/SConscript | 21 | ||||
-rw-r--r-- | src/mongo/util/procparser.cpp | 283 | ||||
-rw-r--r-- | src/mongo/util/procparser.h | 73 | ||||
-rw-r--r-- | src/mongo/util/procparser_test.cpp | 216 |
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 |