Merge changes from topic "bpfStats"

* changes:
  Use /proc/net/dev to get stats summary
  Add bpf support for NetworkStatsFactory
  Add BPF support for NetworkStatsService
This commit is contained in:
Treehugger Robot
2018-01-24 04:08:29 +00:00
committed by Gerrit Code Review
4 changed files with 164 additions and 63 deletions

View File

@@ -31,13 +31,17 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils; import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ProcFileReader; import com.android.internal.util.ProcFileReader;
import com.google.android.collect.Lists;
import libcore.io.IoUtils; import libcore.io.IoUtils;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.net.ProtocolException; import java.net.ProtocolException;
import java.util.ArrayList;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -55,6 +59,8 @@ public class NetworkStatsFactory {
// Used for correct stats accounting on clatd interfaces. // Used for correct stats accounting on clatd interfaces.
private static final int IPV4V6_HEADER_DELTA = 20; private static final int IPV4V6_HEADER_DELTA = 20;
/** Path to {@code /proc/net/dev}. */
private final File mStatsIfaceDev;
/** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */ /** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
private final File mStatsXtIfaceAll; private final File mStatsXtIfaceAll;
/** Path to {@code /proc/net/xt_qtaguid/iface_stat_fmt}. */ /** Path to {@code /proc/net/xt_qtaguid/iface_stat_fmt}. */
@@ -62,6 +68,8 @@ public class NetworkStatsFactory {
/** Path to {@code /proc/net/xt_qtaguid/stats}. */ /** Path to {@code /proc/net/xt_qtaguid/stats}. */
private final File mStatsXtUid; private final File mStatsXtUid;
private boolean mUseBpfStats;
// TODO: to improve testability and avoid global state, do not use a static variable. // TODO: to improve testability and avoid global state, do not use a static variable.
@GuardedBy("sStackedIfaces") @GuardedBy("sStackedIfaces")
private static final ArrayMap<String, String> sStackedIfaces = new ArrayMap<>(); private static final ArrayMap<String, String> sStackedIfaces = new ArrayMap<>();
@@ -77,14 +85,54 @@ public class NetworkStatsFactory {
} }
public NetworkStatsFactory() { public NetworkStatsFactory() {
this(new File("/proc/")); this(new File("/proc/"), new File("/sys/fs/bpf/traffic_uid_stats_map").exists());
} }
@VisibleForTesting @VisibleForTesting
public NetworkStatsFactory(File procRoot) { public NetworkStatsFactory(File procRoot, boolean useBpfStats) {
mStatsIfaceDev = new File(procRoot, "net/dev");
mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all"); mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt"); mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats"); mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
mUseBpfStats = useBpfStats;
}
@VisibleForTesting
public NetworkStats readNetworkStatsIfaceDev() throws IOException {
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
final NetworkStats.Entry entry = new NetworkStats.Entry();
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(mStatsIfaceDev));
// skip first two header lines
reader.readLine();
reader.readLine();
// parse remaining lines
String line;
while ((line = reader.readLine()) != null) {
String[] values = line.trim().split("\\:?\\s+");
entry.iface = values[0];
entry.uid = UID_ALL;
entry.set = SET_ALL;
entry.tag = TAG_NONE;
entry.rxBytes = Long.parseLong(values[1]);
entry.rxPackets = Long.parseLong(values[2]);
entry.txBytes = Long.parseLong(values[9]);
entry.txPackets = Long.parseLong(values[10]);
stats.addValues(entry);
}
} catch (NullPointerException|NumberFormatException e) {
throw new ProtocolException("problem parsing stats", e);
} finally {
IoUtils.closeQuietly(reader);
StrictMode.setThreadPolicy(savedPolicy);
}
return stats;
} }
/** /**
@@ -96,6 +144,11 @@ public class NetworkStatsFactory {
* @throws IllegalStateException when problem parsing stats. * @throws IllegalStateException when problem parsing stats.
*/ */
public NetworkStats readNetworkStatsSummaryDev() throws IOException { public NetworkStats readNetworkStatsSummaryDev() throws IOException {
// Return the stats get from /proc/net/dev if switched to bpf module.
if (mUseBpfStats)
return readNetworkStatsIfaceDev();
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6); final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
@@ -147,6 +200,11 @@ public class NetworkStatsFactory {
* @throws IllegalStateException when problem parsing stats. * @throws IllegalStateException when problem parsing stats.
*/ */
public NetworkStats readNetworkStatsSummaryXt() throws IOException { public NetworkStats readNetworkStatsSummaryXt() throws IOException {
// Return the stats get from /proc/net/dev if qtaguid module is replaced.
if (mUseBpfStats)
return readNetworkStatsIfaceDev();
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
// return null when kernel doesn't support // return null when kernel doesn't support
@@ -252,7 +310,7 @@ public class NetworkStatsFactory {
stats = new NetworkStats(SystemClock.elapsedRealtime(), -1); stats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
} }
if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid, if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid,
limitIfaces, limitTag) != 0) { limitIfaces, limitTag, mUseBpfStats) != 0) {
throw new IOException("Failed to parse network stats"); throw new IOException("Failed to parse network stats");
} }
if (SANITY_CHECK_NATIVE) { if (SANITY_CHECK_NATIVE) {
@@ -346,6 +404,6 @@ public class NetworkStatsFactory {
* are expected to monotonically increase since device boot. * are expected to monotonically increase since device boot.
*/ */
@VisibleForTesting @VisibleForTesting
public static native int nativeReadNetworkStatsDetail( public static native int nativeReadNetworkStatsDetail(NetworkStats stats, String path,
NetworkStats stats, String path, int limitUid, String[] limitIfaces, int limitTag); int limitUid, String[] limitIfaces, int limitTag, boolean useBpfStats);
} }

View File

@@ -30,7 +30,14 @@
#include <utils/Log.h> #include <utils/Log.h>
#include <utils/misc.h> #include <utils/misc.h>
#include <utils/Vector.h>
#include "android-base/unique_fd.h"
#include "bpf/BpfNetworkStats.h"
#include "bpf/BpfUtils.h"
using android::bpf::hasBpfSupport;
using android::bpf::parseBpfNetworkStatsDetail;
using android::bpf::stats_line;
namespace android { namespace android {
@@ -53,17 +60,6 @@ static struct {
jfieldID operations; jfieldID operations;
} gNetworkStatsClassInfo; } gNetworkStatsClassInfo;
struct stats_line {
char iface[32];
int32_t uid;
int32_t set;
int32_t tag;
int64_t rxBytes;
int64_t rxPackets;
int64_t txBytes;
int64_t txPackets;
};
static jobjectArray get_string_array(JNIEnv* env, jobject obj, jfieldID field, int size, bool grow) static jobjectArray get_string_array(JNIEnv* env, jobject obj, jfieldID field, int size, bool grow)
{ {
if (!grow) { if (!grow) {
@@ -97,33 +93,14 @@ static jlongArray get_long_array(JNIEnv* env, jobject obj, jfieldID field, int s
return env->NewLongArray(size); return env->NewLongArray(size);
} }
static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, static int legacyReadNetworkStatsDetail(std::vector<stats_line>* lines,
jstring path, jint limitUid, jobjectArray limitIfacesObj, jint limitTag) { const std::vector<std::string>& limitIfaces,
ScopedUtfChars path8(env, path); int limitTag, int limitUid, const char* path) {
if (path8.c_str() == NULL) { FILE* fp = fopen(path, "r");
return -1;
}
FILE *fp = fopen(path8.c_str(), "r");
if (fp == NULL) { if (fp == NULL) {
return -1; return -1;
} }
Vector<String8> limitIfaces;
if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) {
int num = env->GetArrayLength(limitIfacesObj);
limitIfaces.setCapacity(num);
for (int i=0; i<num; i++) {
jstring string = (jstring)env->GetObjectArrayElement(limitIfacesObj, i);
ScopedUtfChars string8(env, string);
if (string8.c_str() != NULL) {
limitIfaces.add(String8(string8.c_str()));
}
}
}
Vector<stats_line> lines;
int lastIdx = 1; int lastIdx = 1;
int idx; int idx;
char buffer[384]; char buffer[384];
@@ -215,7 +192,7 @@ static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats,
//ALOGI("skipping due to uid: %s", buffer); //ALOGI("skipping due to uid: %s", buffer);
continue; continue;
} }
lines.push_back(s); lines->push_back(s);
} else { } else {
//ALOGI("skipping due to bad remaining fields: %s", pos); //ALOGI("skipping due to bad remaining fields: %s", pos);
} }
@@ -225,8 +202,42 @@ static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats,
ALOGE("Failed to close netstats file"); ALOGE("Failed to close netstats file");
return -1; return -1;
} }
return 0;
}
static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, jstring path,
jint limitUid, jobjectArray limitIfacesObj, jint limitTag,
jboolean useBpfStats) {
ScopedUtfChars path8(env, path);
if (path8.c_str() == NULL) {
return -1;
}
std::vector<std::string> limitIfaces;
if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) {
int num = env->GetArrayLength(limitIfacesObj);
for (int i = 0; i < num; i++) {
jstring string = (jstring)env->GetObjectArrayElement(limitIfacesObj, i);
ScopedUtfChars string8(env, string);
if (string8.c_str() != NULL) {
limitIfaces.push_back(std::string(string8.c_str()));
}
}
}
std::vector<stats_line> lines;
if (useBpfStats) {
if (parseBpfNetworkStatsDetail(&lines, limitIfaces, limitTag, limitUid) < 0)
return -1;
} else {
if (legacyReadNetworkStatsDetail(&lines, limitIfaces, limitTag,
limitUid, path8.c_str()) < 0)
return -1;
}
int size = lines.size(); int size = lines.size();
bool grow = size > env->GetIntField(stats, gNetworkStatsClassInfo.capacity); bool grow = size > env->GetIntField(stats, gNetworkStatsClassInfo.capacity);
ScopedLocalRef<jobjectArray> iface(env, get_string_array(env, stats, ScopedLocalRef<jobjectArray> iface(env, get_string_array(env, stats,
@@ -303,7 +314,7 @@ static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats,
static const JNINativeMethod gMethods[] = { static const JNINativeMethod gMethods[] = {
{ "nativeReadNetworkStatsDetail", { "nativeReadNetworkStatsDetail",
"(Landroid/net/NetworkStats;Ljava/lang/String;I[Ljava/lang/String;I)I", "(Landroid/net/NetworkStats;Ljava/lang/String;I[Ljava/lang/String;IZ)I",
(void*) readNetworkStatsDetail } (void*) readNetworkStatsDetail }
}; };

View File

@@ -887,17 +887,21 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override @Override
public long getUidStats(int uid, int type) { public long getUidStats(int uid, int type) {
return nativeGetUidStat(uid, type); return nativeGetUidStat(uid, type, checkBpfStatsEnable());
} }
@Override @Override
public long getIfaceStats(String iface, int type) { public long getIfaceStats(String iface, int type) {
return nativeGetIfaceStat(iface, type); return nativeGetIfaceStat(iface, type, checkBpfStatsEnable());
} }
@Override @Override
public long getTotalStats(int type) { public long getTotalStats(int type) {
return nativeGetTotalStat(type); return nativeGetTotalStat(type, checkBpfStatsEnable());
}
private boolean checkBpfStatsEnable() {
return new File("/sys/fs/bpf/traffic_uid_stats_map").exists();
} }
/** /**
@@ -1668,7 +1672,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private static int TYPE_TCP_RX_PACKETS; private static int TYPE_TCP_RX_PACKETS;
private static int TYPE_TCP_TX_PACKETS; private static int TYPE_TCP_TX_PACKETS;
private static native long nativeGetTotalStat(int type); private static native long nativeGetTotalStat(int type, boolean useBpfStats);
private static native long nativeGetIfaceStat(String iface, int type); private static native long nativeGetIfaceStat(String iface, int type, boolean useBpfStats);
private static native long nativeGetUidStat(int uid, int type); private static native long nativeGetUidStat(int uid, int type, boolean useBpfStats);
} }

View File

@@ -29,6 +29,15 @@
#include <utils/misc.h> #include <utils/misc.h>
#include <utils/Log.h> #include <utils/Log.h>
#include "android-base/unique_fd.h"
#include "bpf/BpfNetworkStats.h"
#include "bpf/BpfUtils.h"
using android::bpf::Stats;
using android::bpf::hasBpfSupport;
using android::bpf::bpfGetUidStats;
using android::bpf::bpfGetIfaceStats;
namespace android { namespace android {
static const char* QTAGUID_IFACE_STATS = "/proc/net/xt_qtaguid/iface_stat_fmt"; static const char* QTAGUID_IFACE_STATS = "/proc/net/xt_qtaguid/iface_stat_fmt";
@@ -46,15 +55,6 @@ enum StatsType {
TCP_TX_PACKETS = 5 TCP_TX_PACKETS = 5
}; };
struct Stats {
uint64_t rxBytes;
uint64_t rxPackets;
uint64_t txBytes;
uint64_t txPackets;
uint64_t tcpRxPackets;
uint64_t tcpTxPackets;
};
static uint64_t getStatsType(struct Stats* stats, StatsType type) { static uint64_t getStatsType(struct Stats* stats, StatsType type) {
switch (type) { switch (type) {
case RX_BYTES: case RX_BYTES:
@@ -150,9 +150,18 @@ static int parseUidStats(const uint32_t uid, struct Stats* stats) {
return 0; return 0;
} }
static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type) { static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type, jboolean useBpfStats) {
struct Stats stats; struct Stats stats;
memset(&stats, 0, sizeof(Stats)); memset(&stats, 0, sizeof(Stats));
if (useBpfStats) {
if (bpfGetIfaceStats(NULL, &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
} else {
return UNKNOWN;
}
}
if (parseIfaceStats(NULL, &stats) == 0) { if (parseIfaceStats(NULL, &stats) == 0) {
return getStatsType(&stats, (StatsType) type); return getStatsType(&stats, (StatsType) type);
} else { } else {
@@ -160,7 +169,8 @@ static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type) {
} }
} }
static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) { static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type,
jboolean useBpfStats) {
ScopedUtfChars iface8(env, iface); ScopedUtfChars iface8(env, iface);
if (iface8.c_str() == NULL) { if (iface8.c_str() == NULL) {
return UNKNOWN; return UNKNOWN;
@@ -168,6 +178,15 @@ static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) {
struct Stats stats; struct Stats stats;
memset(&stats, 0, sizeof(Stats)); memset(&stats, 0, sizeof(Stats));
if (useBpfStats) {
if (bpfGetIfaceStats(iface8.c_str(), &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
} else {
return UNKNOWN;
}
}
if (parseIfaceStats(iface8.c_str(), &stats) == 0) { if (parseIfaceStats(iface8.c_str(), &stats) == 0) {
return getStatsType(&stats, (StatsType) type); return getStatsType(&stats, (StatsType) type);
} else { } else {
@@ -175,9 +194,18 @@ static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) {
} }
} }
static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type) { static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type, jboolean useBpfStats) {
struct Stats stats; struct Stats stats;
memset(&stats, 0, sizeof(Stats)); memset(&stats, 0, sizeof(Stats));
if (useBpfStats) {
if (bpfGetUidStats(uid, &stats) == 0) {
return getStatsType(&stats, (StatsType) type);
} else {
return UNKNOWN;
}
}
if (parseUidStats(uid, &stats) == 0) { if (parseUidStats(uid, &stats) == 0) {
return getStatsType(&stats, (StatsType) type); return getStatsType(&stats, (StatsType) type);
} else { } else {
@@ -186,9 +214,9 @@ static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type) {
} }
static const JNINativeMethod gMethods[] = { static const JNINativeMethod gMethods[] = {
{"nativeGetTotalStat", "(I)J", (void*) getTotalStat}, {"nativeGetTotalStat", "(IZ)J", (void*) getTotalStat},
{"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*) getIfaceStat}, {"nativeGetIfaceStat", "(Ljava/lang/String;IZ)J", (void*) getIfaceStat},
{"nativeGetUidStat", "(II)J", (void*) getUidStat}, {"nativeGetUidStat", "(IIZ)J", (void*) getUidStat},
}; };
int register_android_server_net_NetworkStatsService(JNIEnv* env) { int register_android_server_net_NetworkStatsService(JNIEnv* env) {