summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Lucas <bruce.lucas@10gen.com>2018-05-08 07:40:02 -0400
committerBruce Lucas <bruce.lucas@mongodb.com>2018-05-22 11:07:25 -0400
commit0768d9842baea52df153c84c627e63fd3de3683a (patch)
tree079ac0c8ccd69dd5a1248e52590e0acdc7c82865
parentf2e995b3b8b6a688c97720e6fdb286e2e538180c (diff)
downloadmongo-0768d9842baea52df153c84c627e63fd3de3683a.tar.gz
SERVER-31400 Record netstat metrics in ftdc
(cherry picked from commit 68aaf285c35b379a4c81231d86903c78e97d1e76)
-rw-r--r--src/mongo/db/ftdc/ftdc_system_stats_linux.cpp15
-rw-r--r--src/mongo/util/procparser.cpp95
-rw-r--r--src/mongo/util/procparser.h18
-rw-r--r--src/mongo/util/procparser_test.cpp132
4 files changed, 260 insertions, 0 deletions
diff --git a/src/mongo/db/ftdc/ftdc_system_stats_linux.cpp b/src/mongo/db/ftdc/ftdc_system_stats_linux.cpp
index 9c93718c171..fe730879834 100644
--- a/src/mongo/db/ftdc/ftdc_system_stats_linux.cpp
+++ b/src/mongo/db/ftdc/ftdc_system_stats_linux.cpp
@@ -66,6 +66,10 @@ static const std::vector<StringData> kMemKeys{
"Inactive(file)"_sd,
};
+static const std::vector<StringData> kNetstatKeys{
+ "Tcp:"_sd, "Ip:"_sd, "TcpExt:"_sd, "IpExt:"_sd,
+};
+
/**
* Collect metrics from the Linux /proc file system.
*/
@@ -99,6 +103,17 @@ public:
subObjBuilder.doneFast();
}
+ {
+ BSONObjBuilder subObjBuilder(builder.subobjStart("netstat"_sd));
+ processStatusErrors(procparser::parseProcNetstatFile(
+ kNetstatKeys, "/proc/net/netstat"_sd, &subObjBuilder),
+ &subObjBuilder);
+ processStatusErrors(
+ procparser::parseProcNetstatFile(kNetstatKeys, "/proc/net/snmp"_sd, &subObjBuilder),
+ &subObjBuilder);
+ subObjBuilder.doneFast();
+ }
+
// Skip the disks section if we could not find any disks.
// This can happen when we do not have permission to /sys/block for instance.
if (!_disksStringData.empty()) {
diff --git a/src/mongo/util/procparser.cpp b/src/mongo/util/procparser.cpp
index 36f2ae0254e..af7a81fc7a2 100644
--- a/src/mongo/util/procparser.cpp
+++ b/src/mongo/util/procparser.cpp
@@ -406,6 +406,101 @@ Status parseProcMemInfoFile(StringData filename,
return parseProcMemInfo(keys, swString.getValue(), builder);
}
+//
+// Here is an example of the type of string it supports (long lines elided for clarity).
+// > cat /proc/net/netstat
+// TcpExt: SyncookiesSent SyncookiesRecv SyncookiesFailed ...
+// TcpExt: 3437 5938 13368 ...
+// IpExt: InNoRoutes InTruncatedPkts InMcastPkts ...
+// IpExt: 999 1 4819969 ...
+//
+// Parser assumes file consists of alternating lines of keys and values
+// key and value lines consist of space-separated tokens
+// first token is a key prefix that is prepended in the output to each key
+// all prefixed keys and corresponding values are copied to output as-is
+//
+
+Status parseProcNetstat(const std::vector<StringData>& keys,
+ StringData data,
+ BSONObjBuilder* builder) {
+
+ using string_split_iterator = boost::split_iterator<StringData::const_iterator>;
+
+ string_split_iterator keysIt;
+ bool foundKeys = false;
+
+ // Split the file by lines.
+ uint32_t lineNum = 0;
+ 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, ++lineNum) {
+
+ if (lineNum % 2 == 0) {
+
+ // even numbered lines are keys
+ keysIt = string_split_iterator(
+ (*lineIt).begin(),
+ (*lineIt).end(),
+ boost::token_finder([](char c) { return c == ' '; }, boost::token_compress_on));
+
+ } else {
+
+ // odd numbered lines are values
+ string_split_iterator valuesIt = string_split_iterator(
+ (*lineIt).begin(),
+ (*lineIt).end(),
+ boost::token_finder([](char c) { return c == ' '; }, boost::token_compress_on));
+
+ StringData prefix;
+
+ // iterate over the keys and values in parallel
+ for (uint32_t keyNum = 0;
+ keysIt != string_split_iterator() && valuesIt != string_split_iterator();
+ ++keysIt, ++valuesIt, ++keyNum) {
+
+ if (keyNum == 0) {
+
+ // first token is a prefix to be applied to remaining keys
+ prefix = StringData((*keysIt).begin(), (*keysIt).end());
+
+ // ignore line if prefix isn't in requested list
+ if (!keys.empty() && std::find(keys.begin(), keys.end(), prefix) == keys.end())
+ break;
+
+ } else {
+
+ // remaining tokens are key/value pairs
+ StringData key((*keysIt).begin(), (*keysIt).end());
+ StringData stringValue((*valuesIt).begin(), (*valuesIt).end());
+ uint64_t value;
+ if (parseNumberFromString(stringValue, &value).isOK()) {
+ builder->appendNumber(prefix.toString() + key.toString(), value);
+ foundKeys = true;
+ }
+ }
+ }
+ }
+ }
+
+ return foundKeys ? Status::OK()
+ : Status(ErrorCodes::NoSuchKey, "Failed to find any keys in netstats string");
+}
+
+Status parseProcNetstatFile(const std::vector<StringData>& keys,
+ StringData filename,
+ BSONObjBuilder* builder) {
+ auto swString = readFileAsString(filename);
+ if (!swString.isOK()) {
+ return swString.getStatus();
+ }
+ return parseProcNetstat(keys, swString.getValue(), builder);
+}
+
+
// Here is an example of the type of string it supports:
//
// For more information, see:
diff --git a/src/mongo/util/procparser.h b/src/mongo/util/procparser.h
index 2f9e97e343c..bbaf543802a 100644
--- a/src/mongo/util/procparser.h
+++ b/src/mongo/util/procparser.h
@@ -88,6 +88,24 @@ Status parseProcMemInfoFile(StringData filename,
BSONObjBuilder* builder);
/**
+ * Read a string matching /proc/net/netstat format, and write the keys
+ * found in that string into builder.
+ *
+ * data - string to parse
+ * builder - BSON output
+ */
+Status parseProcNetstat(const std::vector<StringData>& keys,
+ StringData data,
+ BSONObjBuilder* builder);
+
+/**
+ * Read from file, and write the keys found in that file into builder.
+ */
+Status parseProcNetstatFile(const std::vector<StringData>& keys,
+ StringData filename,
+ BSONObjBuilder* builder);
+
+/**
* Read a string matching /proc/diskstats format, and write the specified list of disks in builder.
*
* disks - vector of block devices to include in output. For each disk selected, 11 fields are
diff --git a/src/mongo/util/procparser_test.cpp b/src/mongo/util/procparser_test.cpp
index 81bf17ac8df..ff620b8ac61 100644
--- a/src/mongo/util/procparser_test.cpp
+++ b/src/mongo/util/procparser_test.cpp
@@ -85,6 +85,11 @@ StringMap toNestedStringMap(BSONObj& obj) {
ASSERT_OK(procparser::parseProcMemInfo(_keys, _x, &builder)); \
auto obj = builder.obj(); \
auto stringMap = toStringMap(obj);
+#define ASSERT_PARSE_NETSTAT(_keys, _x) \
+ BSONObjBuilder builder; \
+ ASSERT_OK(procparser::parseProcNetstat(_keys, _x, &builder)); \
+ auto obj = builder.obj(); \
+ auto stringMap = toStringMap(obj);
#define ASSERT_PARSE_DISKSTATS(_disks, _x) \
BSONObjBuilder builder; \
ASSERT_OK(procparser::parseProcDiskStats(_disks, _x, &builder)); \
@@ -368,6 +373,133 @@ TEST(FTDCProcMemInfo, TestLocalNonExistentMemInfo) {
ASSERT_NOT_OK(procparser::parseProcMemInfoFile("/proc/does_not_exist", keys, &builder));
}
+TEST(FTDCProcNetstat, TestNetstat) {
+
+ // test keys
+ std::vector<StringData> keys{"pfx1", "pfx2", "pfx3"};
+
+ // Normal case
+ {
+ ASSERT_PARSE_NETSTAT(keys,
+ "pfx1 key1 key2 key3\n"
+ "pfx1 1 2 3\n"
+ "pfxX key1 key2\n"
+ "pfxX key1 key2\n"
+ "pfx2 key4 key5\n"
+ "pfx2 4 5\n");
+ ASSERT_KEY_AND_VALUE("pfx1key1", 1UL);
+ ASSERT_KEY_AND_VALUE("pfx1key2", 2UL);
+ ASSERT_NO_KEY("pfxXkey1");
+ ASSERT_NO_KEY("pfxXkey2");
+ ASSERT_KEY_AND_VALUE("pfx1key3", 3UL)
+ ASSERT_KEY_AND_VALUE("pfx2key4", 4UL);
+ ASSERT_KEY_AND_VALUE("pfx2key5", 5UL);
+ }
+
+ // Mismatched keys and values
+ {
+ ASSERT_PARSE_NETSTAT(keys,
+ "pfx1 key1 key2 key3\n"
+ "pfx1 1 2 3 4\n"
+ "pfx2 key4 key5\n"
+ "pfx2 4\n"
+ "pfx3 key6 key7\n");
+ ASSERT_KEY_AND_VALUE("pfx1key1", 1UL);
+ ASSERT_KEY_AND_VALUE("pfx1key2", 2UL);
+ ASSERT_KEY_AND_VALUE("pfx1key3", 3UL);
+ ASSERT_NO_KEY("pfx1key4");
+ ASSERT_KEY_AND_VALUE("pfx2key4", 4UL);
+ ASSERT_NO_KEY("pfx2key5");
+ ASSERT_NO_KEY("pfx3key6");
+ ASSERT_NO_KEY("pfx3key7");
+ }
+
+ // Non-numeric value
+ {
+ ASSERT_PARSE_NETSTAT(keys,
+ "pfx1 key1 key2 key3\n"
+ "pfx1 1 foo 3\n");
+ ASSERT_KEY_AND_VALUE("pfx1key1", 1UL);
+ ASSERT_NO_KEY("pfx1key2");
+ ASSERT_KEY_AND_VALUE("pfx1key3", 3UL)
+ }
+
+ // No newline
+ {
+ ASSERT_PARSE_NETSTAT(keys,
+ "pfx1 key1 key2 key3\n"
+ "pfx1 1 2 3\n"
+ "pfx2 key4 key5\n"
+ "pfx2 4 5");
+ ASSERT_KEY_AND_VALUE("pfx1key1", 1UL);
+ ASSERT_KEY_AND_VALUE("pfx1key2", 2UL);
+ ASSERT_KEY_AND_VALUE("pfx1key3", 3UL)
+ ASSERT_KEY_AND_VALUE("pfx2key4", 4UL);
+ ASSERT_KEY_AND_VALUE("pfx2key5", 5UL);
+ }
+
+ // Single line only
+ {
+ BSONObjBuilder builder;
+ ASSERT_NOT_OK(procparser::parseProcNetstat(keys, "pfx1 key1 key2 key3\n", &builder));
+ }
+
+ // Empty string
+ {
+ BSONObjBuilder builder;
+ ASSERT_NOT_OK(procparser::parseProcNetstat(keys, "", &builder));
+ }
+}
+
+// Test we can parse the /proc/net/netstat on this machine and assert we have some expected fields
+// Some keys can vary between distros, so we test only for the existence of a few basic ones
+TEST(FTDCProcNetstat, TestLocalNetstat) {
+
+ BSONObjBuilder builder;
+
+ std::vector<StringData> keys{"TcpExt:"_sd, "IpExt:"_sd};
+
+ ASSERT_OK(procparser::parseProcNetstatFile(keys, "/proc/net/netstat", &builder));
+
+ BSONObj obj = builder.obj();
+ auto stringMap = toStringMap(obj);
+ log() << "OBJ:" << obj;
+ ASSERT_KEY("TcpExt:TCPTimeouts");
+ ASSERT_KEY("TcpExt:TCPPureAcks");
+ ASSERT_KEY("TcpExt:TCPAbortOnTimeout");
+ ASSERT_KEY("TcpExt:EmbryonicRsts");
+ ASSERT_KEY("TcpExt:ListenDrops");
+ ASSERT_KEY("TcpExt:ListenOverflows");
+ ASSERT_KEY("TcpExt:DelayedACKs");
+ ASSERT_KEY("IpExt:OutOctets");
+ ASSERT_KEY("IpExt:InOctets");
+}
+
+// Test we can parse the /proc/net/snmp on this machine and assert we have some expected fields
+// Some keys can vary between distros, so we test only for the existence of a few basic ones
+TEST(FTDCProcNetstat, TestLocalNetSnmp) {
+
+ BSONObjBuilder builder;
+
+ std::vector<StringData> keys{"Tcp:"_sd, "Ip:"_sd};
+
+ ASSERT_OK(procparser::parseProcNetstatFile(keys, "/proc/net/snmp", &builder));
+
+ BSONObj obj = builder.obj();
+ auto stringMap = toStringMap(obj);
+ log() << "OBJ:" << obj;
+ ASSERT_KEY("Ip:InReceives");
+ ASSERT_KEY("Ip:OutRequests");
+ ASSERT_KEY("Tcp:InSegs");
+ ASSERT_KEY("Tcp:OutSegs");
+}
+
+TEST(FTDCProcNetstat, TestLocalNonExistentNetstat) {
+ std::vector<StringData> keys{};
+ BSONObjBuilder builder;
+
+ ASSERT_NOT_OK(procparser::parseProcNetstatFile(keys, "/proc/does_not_exist", &builder));
+}
TEST(FTDCProcDiskStats, TestDiskStats) {