Add bpf support for NetworkStatsFactory

Add the native method used to read the detail information of network
stats from bpf maps. The native method of NetworkStatsFactory should
choose the correct implementation to get the stats detail depending on
the kernel version. Currently the bpf result is printed as a reference
and the actual behavior of NetworkStatsFactory should not change.

Test: NetworkStatsFactory related cts test should not fail.
Bug: 30950746
Change-Id: I4715a23559b5b2306bd556cea0431f0ed172a993
This commit is contained in:
Chenbo Feng
2017-11-20 17:03:59 -08:00
parent 7d2d414fd8
commit adfda6945d
2 changed files with 56 additions and 42 deletions

View File

@@ -62,6 +62,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 +79,15 @@ 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) {
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;
} }
/** /**
@@ -252,7 +255,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 +349,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 }
}; };