TcpSocketMonitor: add polling loop
TcpSocketMonitor starts a sock_diag polling thread in its ctor whose
polling interval can be controlled with setPollingInterval() and
suspendPolling().
Initially the polling thread will immediately be suspended. The polling
thread is automatically started when 1 or more physical network exists,
and automatically stopped when there is 0 physical networks.
By default the polling interval is set to 30 secs.
Also fix some code indentation issues.
Bug: 64147860
Test: tested manually, watching the result of
$ adb shell dumpsys netd tcp_socket_info
Change-Id: I7fe356a0a073ebc83486bc774a3002648e9dd457
diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp
index 43a55bd..5b2cd89 100644
--- a/server/NetworkController.cpp
+++ b/server/NetworkController.cpp
@@ -355,6 +355,9 @@
}
mNetworks[netId] = physicalNetwork;
+
+ updateTcpSocketMonitorPolling();
+
return 0;
}
@@ -448,6 +451,9 @@
mNetworks.erase(netId);
delete network;
_resolv_delete_cache_for_net(netId);
+
+ updateTcpSocketMonitorPolling();
+
return ret;
}
@@ -730,5 +736,22 @@
return 0;
}
+void NetworkController::updateTcpSocketMonitorPolling() {
+ bool physicalNetworkExists = false;
+ for (const auto& entry : mNetworks) {
+ const auto& network = entry.second;
+ if (network->getType() == Network::PHYSICAL && network->getNetId() >= MIN_NET_ID) {
+ physicalNetworkExists = true;
+ break;
+ }
+ }
+
+ if (physicalNetworkExists) {
+ android::net::gCtls->tcpSocketMonitor.resumePolling();
+ } else {
+ android::net::gCtls->tcpSocketMonitor.suspendPolling();
+ }
+}
+
} // namespace net
} // namespace android
diff --git a/server/NetworkController.h b/server/NetworkController.h
index 378c9f1..627e44d 100644
--- a/server/NetworkController.h
+++ b/server/NetworkController.h
@@ -146,6 +146,7 @@
int modifyRoute(unsigned netId, const char* interface, const char* destination,
const char* nexthop, bool add, bool legacy, uid_t uid) WARN_UNUSED_RESULT;
int modifyFallthroughLocked(unsigned vpnNetId, bool add) WARN_UNUSED_RESULT;
+ void updateTcpSocketMonitorPolling();
class DelegateImpl;
DelegateImpl* const mDelegateImpl;
diff --git a/server/TcpSocketMonitor.cpp b/server/TcpSocketMonitor.cpp
index 3eb8014..2d53119 100644
--- a/server/TcpSocketMonitor.cpp
+++ b/server/TcpSocketMonitor.cpp
@@ -16,19 +16,25 @@
#define LOG_TAG "TcpSocketMonitor"
-#include "TcpSocketMonitor.h"
-#include "DumpWriter.h"
-
-#include "Fwmark.h"
-#include "SockDiag.h"
+#include <iomanip>
+#include <thread>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <linux/tcp.h>
+#include "DumpWriter.h"
+#include "Fwmark.h"
+#include "SockDiag.h"
+#include "TcpSocketMonitor.h"
+
namespace android {
namespace net {
+using std::chrono::duration_cast;
+using std::chrono::milliseconds;
+using std::chrono::steady_clock;
+
constexpr const char* getTcpStateName(int t) {
switch (t) {
case TCP_ESTABLISHED:
@@ -60,41 +66,48 @@
static void tcpInfoPrint(DumpWriter &dw, Fwmark mark, const struct inet_diag_msg *sockinfo,
const struct tcp_info *tcpinfo) {
- char saddr[INET6_ADDRSTRLEN] = {};
- char daddr[INET6_ADDRSTRLEN] = {};
- inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_src), saddr, sizeof(saddr));
- inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_dst), daddr, sizeof(daddr));
+ char saddr[INET6_ADDRSTRLEN] = {};
+ char daddr[INET6_ADDRSTRLEN] = {};
+ inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_src), saddr, sizeof(saddr));
+ inet_ntop(sockinfo->idiag_family, &(sockinfo->id.idiag_dst), daddr, sizeof(daddr));
- dw.println(
- "netId=%d uid=%u mark=0x%x saddr=%s daddr=%s sport=%u dport=%u tcp_state=%s(%u) "
- "rqueue=%u wqueue=%u rtt=%gms var_rtt=%gms rcv_rtt=%gms unacked=%u snd_cwnd=%u",
- mark.netId,
- sockinfo->idiag_uid,
- mark.intValue,
- saddr,
- daddr,
- ntohs(sockinfo->id.idiag_sport),
- ntohs(sockinfo->id.idiag_dport),
- getTcpStateName(sockinfo->idiag_state), sockinfo->idiag_state,
- sockinfo->idiag_rqueue,
- sockinfo->idiag_wqueue,
- tcpinfo != nullptr ? tcpinfo->tcpi_rtt/1000.0 : 0,
- tcpinfo != nullptr ? tcpinfo->tcpi_rttvar/1000.0 : 0,
- tcpinfo != nullptr ? tcpinfo->tcpi_rcv_rtt/1000.0 : 0,
- tcpinfo != nullptr ? tcpinfo->tcpi_unacked : 0,
- tcpinfo != nullptr ? tcpinfo->tcpi_snd_cwnd : 0);
+ dw.println(
+ "netId=%d uid=%u mark=0x%x saddr=%s daddr=%s sport=%u dport=%u tcp_state=%s(%u) "
+ "rqueue=%u wqueue=%u rtt=%gms var_rtt=%gms rcv_rtt=%gms unacked=%u snd_cwnd=%u",
+ mark.netId,
+ sockinfo->idiag_uid,
+ mark.intValue,
+ saddr,
+ daddr,
+ ntohs(sockinfo->id.idiag_sport),
+ ntohs(sockinfo->id.idiag_dport),
+ getTcpStateName(sockinfo->idiag_state), sockinfo->idiag_state,
+ sockinfo->idiag_rqueue,
+ sockinfo->idiag_wqueue,
+ tcpinfo != nullptr ? tcpinfo->tcpi_rtt/1000.0 : 0,
+ tcpinfo != nullptr ? tcpinfo->tcpi_rttvar/1000.0 : 0,
+ tcpinfo != nullptr ? tcpinfo->tcpi_rcv_rtt/1000.0 : 0,
+ tcpinfo != nullptr ? tcpinfo->tcpi_unacked : 0,
+ tcpinfo != nullptr ? tcpinfo->tcpi_snd_cwnd : 0);
}
const String16 TcpSocketMonitor::DUMP_KEYWORD = String16("tcp_socket_info");
+const milliseconds TcpSocketMonitor::kDefaultPollingInterval = milliseconds(30000);
void TcpSocketMonitor::dump(DumpWriter& dw) {
+ std::lock_guard<std::mutex> guard(mLock);
+
dw.println("TcpSocketMonitor");
dw.incIndent();
+ const auto now = steady_clock::now();
+ const auto d = duration_cast<milliseconds>(now - mLastPoll);
+ dw.println("last poll %lld ms ago", d.count());
+
SockDiag sd;
if (!sd.open()) {
- ALOGE("Error opening sock diag for dumping TCP socket info");
- return;
+ ALOGE("Error opening sock diag for dumping TCP socket info");
+ return;
}
const auto tcpInfoReader = [&dw](Fwmark mark, const struct inet_diag_msg *sockinfo,
@@ -110,5 +123,117 @@
dw.decIndent();
}
+void TcpSocketMonitor::setPollingInterval(milliseconds nextSleepDurationMs) {
+ std::lock_guard<std::mutex> guard(mLock);
+
+ mNextSleepDurationMs = nextSleepDurationMs;
+
+ ALOGD("tcpinfo polling interval set to %lld ms", mNextSleepDurationMs.count());
+}
+
+void TcpSocketMonitor::resumePolling() {
+ {
+ std::lock_guard<std::mutex> guard(mLock);
+
+ if (!mIsSuspended) {
+ return;
+ }
+
+ mIsSuspended = false;
+
+ ALOGD("resuming tcpinfo polling with polling interval set to %lld ms",
+ mNextSleepDurationMs.count());
+ }
+
+ mCv.notify_all();
+}
+
+void TcpSocketMonitor::suspendPolling() {
+ std::lock_guard<std::mutex> guard(mLock);
+
+ if (!mIsSuspended) {
+ ALOGD("suspending tcpinfo polling");
+ mIsSuspended = true;
+ }
+}
+
+void TcpSocketMonitor::poll() {
+ std::lock_guard<std::mutex> guard(mLock);
+
+ if (mIsSuspended) {
+ return;
+ }
+
+ const auto now = steady_clock::now();
+
+ SockDiag sd;
+ if (!sd.open()) {
+ ALOGE("Error opening sock diag for polling TCP socket info");
+ return;
+ }
+
+ const auto tcpInfoReader = [](Fwmark mark, const struct inet_diag_msg *sockinfo,
+ const struct tcp_info *tcpinfo) {
+ if (sockinfo == nullptr || tcpinfo == nullptr || mark.intValue == 0) {
+ return;
+ }
+
+ // TODO: process socket stats
+ };
+
+ if (int ret = sd.getLiveTcpInfos(tcpInfoReader)) {
+ ALOGE("Failed to poll TCP socket info: %s", strerror(-ret));
+ return;
+ }
+
+ mLastPoll = now;
+}
+
+void TcpSocketMonitor::waitForNextPoll() {
+ bool isSuspended;
+ milliseconds nextSleepDurationMs;
+ {
+ std::lock_guard<std::mutex> guard(mLock);
+ isSuspended = mIsSuspended;
+ nextSleepDurationMs= mNextSleepDurationMs;
+ }
+
+ std::unique_lock<std::mutex> ul(mLock);
+ if (isSuspended) {
+ mCv.wait(ul);
+ } else {
+ mCv.wait_for(ul, nextSleepDurationMs);
+ }
+}
+
+bool TcpSocketMonitor::isRunning() {
+ std::lock_guard<std::mutex> guard(mLock);
+ return mIsRunning;
+}
+
+TcpSocketMonitor::TcpSocketMonitor() {
+ std::lock_guard<std::mutex> guard(mLock);
+
+ mNextSleepDurationMs = kDefaultPollingInterval;
+ mIsSuspended = true;
+ mIsRunning = true;
+ mPollingThread = std::thread([this] {
+ while (isRunning()) {
+ poll();
+ waitForNextPoll();
+ }
+ });
+}
+
+TcpSocketMonitor::~TcpSocketMonitor() {
+ {
+ std::lock_guard<std::mutex> guard(mLock);
+ mIsRunning = false;
+ mIsSuspended = true;
+ }
+ mCv.notify_all();
+ mPollingThread.join();
+}
+
} // namespace net
} // namespace android
diff --git a/server/TcpSocketMonitor.h b/server/TcpSocketMonitor.h
index 7f1501a..ec39a13 100644
--- a/server/TcpSocketMonitor.h
+++ b/server/TcpSocketMonitor.h
@@ -17,6 +17,11 @@
#ifndef TCP_SOCKET_MONITOR_H
#define TCP_SOCKET_MONITOR_H
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+
+#include <android-base/thread_annotations.h>
#include "utils/String16.h"
namespace android {
@@ -27,13 +32,35 @@
class TcpSocketMonitor {
public:
static const String16 DUMP_KEYWORD;
+ static const std::chrono::milliseconds kDefaultPollingInterval;
- TcpSocketMonitor() = default;
- ~TcpSocketMonitor() = default;
+ TcpSocketMonitor();
+ ~TcpSocketMonitor();
void dump(DumpWriter& dw);
+ void setPollingInterval(std::chrono::milliseconds duration);
+ void resumePolling();
+ void suspendPolling();
private:
+ void poll();
+ void waitForNextPoll();
+ bool isRunning();
+
+ // Lock guarding all reads and writes to member variables.
+ std::mutex mLock;
+ // Used by the polling thread for sleeping between poll operations.
+ std::condition_variable mCv;
+ // The duration of a sleep between polls. Can be updated by the instance owner for dynamically
+ // adjusting the polling rate.
+ std::chrono::milliseconds mNextSleepDurationMs GUARDED_BY(mLock);
+ // The time of the last successful poll operation.
+ std::chrono::time_point<std::chrono::steady_clock> mLastPoll GUARDED_BY(mLock);
+ // True if the polling thread should sleep until notified.
+ bool mIsSuspended GUARDED_BY(mLock);
+ // True while the polling thread should poll.
+ bool mIsRunning GUARDED_BY(mLock);
+ std::thread mPollingThread;
};
} // namespace net