summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2013-01-14 16:48:51 -0800
committerJeff Sharkey <jsharkey@android.com>2013-02-13 17:40:49 -0800
commit9a2c2a6da90abbcc9a064c20e93ed885651f4ae1 (patch)
tree424065d447332bcbc2f95782abcedf897d9a22a2 /core
parent336fcac31ddccc7e7a6773d03e7cb17967ebb898 (diff)
downloadframeworks_base-9a2c2a6da90abbcc9a064c20e93ed885651f4ae1.zip
frameworks_base-9a2c2a6da90abbcc9a064c20e93ed885651f4ae1.tar.gz
frameworks_base-9a2c2a6da90abbcc9a064c20e93ed885651f4ae1.tar.bz2
Parse network stats using native code.
Switch to parsing detailed network stats with native code, which is 71% faster than ProcFileReader. Change-Id: I2525aaee74d227ce187ba3a74dd08a2b06514deb
Diffstat (limited to 'core')
-rw-r--r--core/java/android/net/NetworkStats.java12
-rw-r--r--core/java/com/android/internal/net/NetworkStatsFactory.java84
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java4
-rw-r--r--core/jni/Android.mk5
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/com_android_internal_net_NetworkStatsFactory.cpp187
-rw-r--r--core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java54
7 files changed, 321 insertions, 27 deletions
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index c757605..9cb904d 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -135,6 +135,18 @@ public class NetworkStats implements Parcelable {
builder.append(" operations=").append(operations);
return builder.toString();
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof Entry) {
+ final Entry e = (Entry) o;
+ return uid == e.uid && set == e.set && tag == e.tag && rxBytes == e.rxBytes
+ && rxPackets == e.rxPackets && txBytes == e.txBytes
+ && txPackets == e.txPackets && operations == e.operations
+ && iface.equals(e.iface);
+ }
+ return false;
+ }
}
public NetworkStats(long elapsedRealtime, int initialSize) {
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index c517a68..8282d23 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -31,6 +31,7 @@ import com.android.internal.util.ProcFileReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.net.ProtocolException;
import libcore.io.IoUtils;
@@ -41,7 +42,8 @@ import libcore.io.IoUtils;
public class NetworkStatsFactory {
private static final String TAG = "NetworkStatsFactory";
- // TODO: consider moving parsing to native code
+ private static final boolean USE_NATIVE_PARSING = true;
+ private static final boolean SANITY_CHECK_NATIVE = false;
/** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
private final File mStatsXtIfaceAll;
@@ -69,7 +71,7 @@ public class NetworkStatsFactory {
*
* @throws IllegalStateException when problem parsing stats.
*/
- public NetworkStats readNetworkStatsSummaryDev() throws IllegalStateException {
+ public NetworkStats readNetworkStatsSummaryDev() throws IOException {
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
@@ -105,11 +107,9 @@ public class NetworkStatsFactory {
reader.finishLine();
}
} catch (NullPointerException e) {
- throw new IllegalStateException("problem parsing stats: " + e);
+ throw new ProtocolException("problem parsing stats", e);
} catch (NumberFormatException e) {
- throw new IllegalStateException("problem parsing stats: " + e);
- } catch (IOException e) {
- throw new IllegalStateException("problem parsing stats: " + e);
+ throw new ProtocolException("problem parsing stats", e);
} finally {
IoUtils.closeQuietly(reader);
StrictMode.setThreadPolicy(savedPolicy);
@@ -124,7 +124,7 @@ public class NetworkStatsFactory {
*
* @throws IllegalStateException when problem parsing stats.
*/
- public NetworkStats readNetworkStatsSummaryXt() throws IllegalStateException {
+ public NetworkStats readNetworkStatsSummaryXt() throws IOException {
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
// return null when kernel doesn't support
@@ -154,11 +154,9 @@ public class NetworkStatsFactory {
reader.finishLine();
}
} catch (NullPointerException e) {
- throw new IllegalStateException("problem parsing stats: " + e);
+ throw new ProtocolException("problem parsing stats", e);
} catch (NumberFormatException e) {
- throw new IllegalStateException("problem parsing stats: " + e);
- } catch (IOException e) {
- throw new IllegalStateException("problem parsing stats: " + e);
+ throw new ProtocolException("problem parsing stats", e);
} finally {
IoUtils.closeQuietly(reader);
StrictMode.setThreadPolicy(savedPolicy);
@@ -166,17 +164,33 @@ public class NetworkStatsFactory {
return stats;
}
- public NetworkStats readNetworkStatsDetail() {
+ public NetworkStats readNetworkStatsDetail() throws IOException {
return readNetworkStatsDetail(UID_ALL);
}
+ public NetworkStats readNetworkStatsDetail(int limitUid) throws IOException {
+ if (USE_NATIVE_PARSING) {
+ final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
+ if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid) != 0) {
+ throw new IOException("Failed to parse network stats");
+ }
+ if (SANITY_CHECK_NATIVE) {
+ final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid, limitUid);
+ assertEquals(javaStats, stats);
+ }
+ return stats;
+ } else {
+ return javaReadNetworkStatsDetail(mStatsXtUid, limitUid);
+ }
+ }
+
/**
- * Parse and return {@link NetworkStats} with UID-level details. Values
- * monotonically increase since device boot.
- *
- * @throws IllegalStateException when problem parsing stats.
+ * Parse and return {@link NetworkStats} with UID-level details. Values are
+ * expected to monotonically increase since device boot.
*/
- public NetworkStats readNetworkStatsDetail(int limitUid) throws IllegalStateException {
+ @VisibleForTesting
+ public static NetworkStats javaReadNetworkStatsDetail(File detailPath, int limitUid)
+ throws IOException {
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
@@ -188,13 +202,13 @@ public class NetworkStatsFactory {
ProcFileReader reader = null;
try {
// open and consume header line
- reader = new ProcFileReader(new FileInputStream(mStatsXtUid));
+ reader = new ProcFileReader(new FileInputStream(detailPath));
reader.finishLine();
while (reader.hasMoreData()) {
idx = reader.nextInt();
if (idx != lastIdx + 1) {
- throw new IllegalStateException(
+ throw new ProtocolException(
"inconsistent idx=" + idx + " after lastIdx=" + lastIdx);
}
lastIdx = idx;
@@ -215,11 +229,9 @@ public class NetworkStatsFactory {
reader.finishLine();
}
} catch (NullPointerException e) {
- throw new IllegalStateException("problem parsing idx " + idx, e);
+ throw new ProtocolException("problem parsing idx " + idx, e);
} catch (NumberFormatException e) {
- throw new IllegalStateException("problem parsing idx " + idx, e);
- } catch (IOException e) {
- throw new IllegalStateException("problem parsing idx " + idx, e);
+ throw new ProtocolException("problem parsing idx " + idx, e);
} finally {
IoUtils.closeQuietly(reader);
StrictMode.setThreadPolicy(savedPolicy);
@@ -227,4 +239,30 @@ public class NetworkStatsFactory {
return stats;
}
+
+ public void assertEquals(NetworkStats expected, NetworkStats actual) {
+ if (expected.size() != actual.size()) {
+ throw new AssertionError(
+ "Expected size " + expected.size() + ", actual size " + actual.size());
+ }
+
+ NetworkStats.Entry expectedRow = null;
+ NetworkStats.Entry actualRow = null;
+ for (int i = 0; i < expected.size(); i++) {
+ expectedRow = expected.getValues(i, expectedRow);
+ actualRow = actual.getValues(i, actualRow);
+ if (!expectedRow.equals(actualRow)) {
+ throw new AssertionError(
+ "Expected row " + i + ": " + expectedRow + ", actual row " + actualRow);
+ }
+ }
+ }
+
+ /**
+ * Parse statistics from file into given {@link NetworkStats} object. Values
+ * are expected to monotonically increase since device boot.
+ */
+ @VisibleForTesting
+ public static native int nativeReadNetworkStatsDetail(
+ NetworkStats stats, String path, int limitUid);
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4d35a6b..04b9884 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5956,7 +5956,7 @@ public final class BatteryStatsImpl extends BatteryStats {
if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) {
try {
mNetworkSummaryCache = mNetworkStatsFactory.readNetworkStatsSummaryDev();
- } catch (IllegalStateException e) {
+ } catch (IOException e) {
Log.wtf(TAG, "problem reading network stats", e);
}
}
@@ -5980,7 +5980,7 @@ public final class BatteryStatsImpl extends BatteryStats {
try {
mNetworkDetailCache = mNetworkStatsFactory
.readNetworkStatsDetail().groupedByUid();
- } catch (IllegalStateException e) {
+ } catch (IOException e) {
Log.wtf(TAG, "problem reading network stats", e);
}
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index d705024..5337329 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -145,7 +145,8 @@ LOCAL_SRC_FILES:= \
android_app_backup_FullBackup.cpp \
android_content_res_ObbScanner.cpp \
android_content_res_Configuration.cpp \
- android_animation_PropertyValuesHolder.cpp
+ android_animation_PropertyValuesHolder.cpp \
+ com_android_internal_net_NetworkStatsFactory.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
@@ -155,7 +156,7 @@ LOCAL_C_INCLUDES += \
$(call include-path-for, bluedroid) \
$(call include-path-for, libhardware)/hardware \
$(call include-path-for, libhardware_legacy)/hardware_legacy \
- $(TOP)/frameworks/av/include \
+ $(TOP)/frameworks/av/include \
external/skia/include/core \
external/skia/include/effects \
external/skia/include/images \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 94324f8..aa59b43 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -173,6 +173,7 @@ extern int register_android_content_res_ObbScanner(JNIEnv* env);
extern int register_android_content_res_Configuration(JNIEnv* env);
extern int register_android_animation_PropertyValuesHolder(JNIEnv *env);
extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
+extern int register_com_android_internal_net_NetworkStatsFactory(JNIEnv *env);
static AndroidRuntime* gCurRuntime = NULL;
@@ -1204,6 +1205,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_animation_PropertyValuesHolder),
REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
+ REG_JNI(register_com_android_internal_net_NetworkStatsFactory),
};
/*
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
new file mode 100644
index 0000000..0906593
--- /dev/null
+++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "NetworkStats"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android_runtime/AndroidRuntime.h>
+#include <cutils/logger.h>
+#include <jni.h>
+
+#include <ScopedUtfChars.h>
+#include <ScopedLocalRef.h>
+#include <ScopedPrimitiveArray.h>
+
+#include <utils/Log.h>
+#include <utils/misc.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+static jclass gStringClass;
+
+static struct {
+ jfieldID size;
+ jfieldID iface;
+ jfieldID uid;
+ jfieldID set;
+ jfieldID tag;
+ jfieldID rxBytes;
+ jfieldID rxPackets;
+ jfieldID txBytes;
+ jfieldID txPackets;
+ jfieldID operations;
+} gNetworkStatsClassInfo;
+
+struct stats_line {
+ int32_t idx;
+ 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 int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats,
+ jstring path, jint limitUid) {
+ ScopedUtfChars path8(env, path);
+ if (path8.c_str() == NULL) {
+ return -1;
+ }
+
+ FILE *fp = fopen(path8.c_str(), "r");
+ if (fp == NULL) {
+ return -1;
+ }
+
+ Vector<stats_line> lines;
+
+ int lastIdx = 1;
+ char buffer[384];
+ while (fgets(buffer, sizeof(buffer), fp) != NULL) {
+ stats_line s;
+ int64_t rawTag;
+ if (sscanf(buffer, "%d %31s 0x%llx %u %u %llu %llu %llu %llu", &s.idx,
+ &s.iface, &rawTag, &s.uid, &s.set, &s.rxBytes, &s.rxPackets,
+ &s.txBytes, &s.txPackets) == 9) {
+ if (s.idx != lastIdx + 1) {
+ ALOGE("inconsistent idx=%d after lastIdx=%d", s.idx, lastIdx);
+ return -1;
+ }
+ lastIdx = s.idx;
+
+ s.tag = rawTag >> 32;
+ lines.push_back(s);
+ }
+ }
+
+ if (fclose(fp) != 0) {
+ return -1;
+ }
+
+ int size = lines.size();
+
+ ScopedLocalRef<jobjectArray> iface(env, env->NewObjectArray(size, gStringClass, NULL));
+ if (iface.get() == NULL) return -1;
+ ScopedIntArrayRW uid(env, env->NewIntArray(size));
+ if (uid.get() == NULL) return -1;
+ ScopedIntArrayRW set(env, env->NewIntArray(size));
+ if (set.get() == NULL) return -1;
+ ScopedIntArrayRW tag(env, env->NewIntArray(size));
+ if (tag.get() == NULL) return -1;
+ ScopedLongArrayRW rxBytes(env, env->NewLongArray(size));
+ if (rxBytes.get() == NULL) return -1;
+ ScopedLongArrayRW rxPackets(env, env->NewLongArray(size));
+ if (rxPackets.get() == NULL) return -1;
+ ScopedLongArrayRW txBytes(env, env->NewLongArray(size));
+ if (txBytes.get() == NULL) return -1;
+ ScopedLongArrayRW txPackets(env, env->NewLongArray(size));
+ if (txPackets.get() == NULL) return -1;
+ ScopedLongArrayRW operations(env, env->NewLongArray(size));
+ if (operations.get() == NULL) return -1;
+
+ for (int i = 0; i < size; i++) {
+ ScopedLocalRef<jstring> ifaceString(env, env->NewStringUTF(lines[i].iface));
+ env->SetObjectArrayElement(iface.get(), i, ifaceString.get());
+
+ uid[i] = lines[i].uid;
+ set[i] = lines[i].set;
+ tag[i] = lines[i].tag;
+ rxBytes[i] = lines[i].rxBytes;
+ rxPackets[i] = lines[i].rxPackets;
+ txBytes[i] = lines[i].txBytes;
+ txPackets[i] = lines[i].txPackets;
+ }
+
+ env->SetIntField(stats, gNetworkStatsClassInfo.size, size);
+ env->SetObjectField(stats, gNetworkStatsClassInfo.iface, iface.get());
+ env->SetObjectField(stats, gNetworkStatsClassInfo.uid, uid.getJavaArray());
+ env->SetObjectField(stats, gNetworkStatsClassInfo.set, set.getJavaArray());
+ env->SetObjectField(stats, gNetworkStatsClassInfo.tag, tag.getJavaArray());
+ env->SetObjectField(stats, gNetworkStatsClassInfo.rxBytes, rxBytes.getJavaArray());
+ env->SetObjectField(stats, gNetworkStatsClassInfo.rxPackets, rxPackets.getJavaArray());
+ env->SetObjectField(stats, gNetworkStatsClassInfo.txBytes, txBytes.getJavaArray());
+ env->SetObjectField(stats, gNetworkStatsClassInfo.txPackets, txPackets.getJavaArray());
+ env->SetObjectField(stats, gNetworkStatsClassInfo.operations, operations.getJavaArray());
+
+ return 0;
+}
+
+static jclass findClass(JNIEnv* env, const char* name) {
+ ScopedLocalRef<jclass> localClass(env, env->FindClass(name));
+ jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get()));
+ if (result == NULL) {
+ ALOGE("failed to find class '%s'", name);
+ abort();
+ }
+ return result;
+}
+
+static JNINativeMethod gMethods[] = {
+ { "nativeReadNetworkStatsDetail",
+ "(Landroid/net/NetworkStats;Ljava/lang/String;I)I",
+ (void*) readNetworkStatsDetail }
+};
+
+int register_com_android_internal_net_NetworkStatsFactory(JNIEnv* env) {
+ int err = AndroidRuntime::registerNativeMethods(env,
+ "com/android/internal/net/NetworkStatsFactory", gMethods,
+ NELEM(gMethods));
+
+ gStringClass = findClass(env, "java/lang/String");
+
+ jclass clazz = env->FindClass("android/net/NetworkStats");
+ gNetworkStatsClassInfo.size = env->GetFieldID(clazz, "size", "I");
+ gNetworkStatsClassInfo.iface = env->GetFieldID(clazz, "iface", "[Ljava/lang/String;");
+ gNetworkStatsClassInfo.uid = env->GetFieldID(clazz, "uid", "[I");
+ gNetworkStatsClassInfo.set = env->GetFieldID(clazz, "set", "[I");
+ gNetworkStatsClassInfo.tag = env->GetFieldID(clazz, "tag", "[I");
+ gNetworkStatsClassInfo.rxBytes = env->GetFieldID(clazz, "rxBytes", "[J");
+ gNetworkStatsClassInfo.rxPackets = env->GetFieldID(clazz, "rxPackets", "[J");
+ gNetworkStatsClassInfo.txBytes = env->GetFieldID(clazz, "txBytes", "[J");
+ gNetworkStatsClassInfo.txPackets = env->GetFieldID(clazz, "txPackets", "[J");
+ gNetworkStatsClassInfo.operations = env->GetFieldID(clazz, "operations", "[J");
+
+ return err;
+}
+
+}
diff --git a/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java b/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java
new file mode 100644
index 0000000..2174be5
--- /dev/null
+++ b/core/tests/benchmarks/src/com/android/internal/net/NetworkStatsFactoryBenchmark.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.net;
+
+import android.net.NetworkStats;
+import android.os.SystemClock;
+
+import com.google.caliper.SimpleBenchmark;
+
+import java.io.File;
+
+public class NetworkStatsFactoryBenchmark extends SimpleBenchmark {
+ private File mStats;
+
+ // TODO: consider staging stats file with different number of rows
+
+ @Override
+ protected void setUp() {
+ mStats = new File("/proc/net/xt_qtaguid/stats");
+ }
+
+ @Override
+ protected void tearDown() {
+ mStats = null;
+ }
+
+ public void timeReadNetworkStatsDetailJava(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ NetworkStatsFactory.javaReadNetworkStatsDetail(mStats, NetworkStats.UID_ALL);
+ }
+ }
+
+ public void timeReadNetworkStatsDetailNative(int reps) {
+ for (int i = 0; i < reps; i++) {
+ final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
+ NetworkStatsFactory.nativeReadNetworkStatsDetail(
+ stats, mStats.getAbsolutePath(), NetworkStats.UID_ALL);
+ }
+ }
+}