aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Egnor <egnor@google.com>2010-04-29 17:23:22 -0700
committerDan Egnor <egnor@google.com>2010-04-29 17:23:22 -0700
commitbbb8304e20da95f79b76df1a5b905503c8988457 (patch)
treef56eb16a7b8732b5aecf358e52d6c2c72323d369
parentc58e5db92595ceb241125a84c4aee02b33c521ff (diff)
downloadexperimental-bbb8304e20da95f79b76df1a5b905503c8988457.tar.gz
More RpcPerformance benchmarks; record non-yaffs disks in procstatlog.
Change-Id: I09a67b8e08aeed02ea03f5d9f590af6a583802d2
-rw-r--r--RpcPerformance/AndroidManifest.xml2
-rw-r--r--RpcPerformance/res/layout/main.xml46
-rw-r--r--RpcPerformance/src/com/android/rpc_performance/IService.aidl3
-rw-r--r--RpcPerformance/src/com/android/rpc_performance/MiscService.java19
-rw-r--r--RpcPerformance/src/com/android/rpc_performance/ProviderPerfActivity.java116
-rw-r--r--procstatlog/procstatlog.c37
-rwxr-xr-xprocstatlog/procstatreport.py98
7 files changed, 263 insertions, 58 deletions
diff --git a/RpcPerformance/AndroidManifest.xml b/RpcPerformance/AndroidManifest.xml
index 0378a5a..5a81912 100644
--- a/RpcPerformance/AndroidManifest.xml
+++ b/RpcPerformance/AndroidManifest.xml
@@ -4,6 +4,8 @@
android:versionCode="1"
android:versionName="1.0">
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_SDCARD" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="7" />
diff --git a/RpcPerformance/res/layout/main.xml b/RpcPerformance/res/layout/main.xml
index 90e33d8..e4f8530 100644
--- a/RpcPerformance/res/layout/main.xml
+++ b/RpcPerformance/res/layout/main.xml
@@ -25,6 +25,20 @@
</TableRow>
<TableRow>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Profile filename:"
+ />
+
+ <EditText android:id="@+id/trace_edit"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ />
+ </TableRow>
+
+ <TableRow>
<LinearLayout android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:orientation="vertical">
@@ -256,6 +270,38 @@
android:text="cp.Call() no exist" />
</LinearLayout>
</TableRow>
+
+ <TableRow>
+ <LinearLayout android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/obtain_text"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Obtain parcel:" />
+ <Button
+ android:id="@+id/obtain_button"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Parcel.obtain()" />
+ </LinearLayout>
+
+ <LinearLayout android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/recycle_text"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Recycle parcel:" />
+ <Button
+ android:id="@+id/recycle_button"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Parcel.recycle()" />
+ </LinearLayout>
+ </TableRow>
</TableLayout>
</ScrollView>
diff --git a/RpcPerformance/src/com/android/rpc_performance/IService.aidl b/RpcPerformance/src/com/android/rpc_performance/IService.aidl
index f3f979c..9b50fd5 100644
--- a/RpcPerformance/src/com/android/rpc_performance/IService.aidl
+++ b/RpcPerformance/src/com/android/rpc_performance/IService.aidl
@@ -19,4 +19,7 @@ package com.android.rpc_performance;
interface IService {
String pingString(String v);
void pingVoid();
+
+ void startTracing(String name);
+ void stopTracing();
}
diff --git a/RpcPerformance/src/com/android/rpc_performance/MiscService.java b/RpcPerformance/src/com/android/rpc_performance/MiscService.java
index 3438ed8..003bd73 100644
--- a/RpcPerformance/src/com/android/rpc_performance/MiscService.java
+++ b/RpcPerformance/src/com/android/rpc_performance/MiscService.java
@@ -19,6 +19,7 @@ package com.android.rpc_performance;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
+import android.os.Debug;
import android.os.IBinder;
import android.util.Log;
import android.net.LocalServerSocket;
@@ -93,10 +94,16 @@ public class MiscService extends Service {
}
private final IService.Stub mBinder = new IService.Stub() {
- public String pingString(String v) {
- return "some string";
- }
- public void pingVoid() {
- }
- };
+ public String pingString(String v) {
+ return v;
+ }
+ public void pingVoid() {
+ }
+ public void startTracing(String name) {
+ Debug.startMethodTracing(name);
+ }
+ public void stopTracing() {
+ Debug.stopMethodTracing();
+ }
+ };
}
diff --git a/RpcPerformance/src/com/android/rpc_performance/ProviderPerfActivity.java b/RpcPerformance/src/com/android/rpc_performance/ProviderPerfActivity.java
index a47c6b7..dbb479c 100644
--- a/RpcPerformance/src/com/android/rpc_performance/ProviderPerfActivity.java
+++ b/RpcPerformance/src/com/android/rpc_performance/ProviderPerfActivity.java
@@ -39,8 +39,10 @@ import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Parcel;
import android.os.RemoteException;
import android.os.SystemClock;
import android.telephony.TelephonyManager;
@@ -88,6 +90,7 @@ public class ProviderPerfActivity extends Activity {
private ContentResolver cr;
private int mIterations = 100;
+ private String mTraceName = null;
/** Called when the activity is first created. */
@Override
@@ -154,13 +157,13 @@ public class ProviderPerfActivity extends Activity {
setButtonAction(R.id.service_button, new Runnable() {
public void run() {
- final float avgTime = serviceLoop(0);
+ final float avgTime = serviceLoop(null);
endAsyncOp(R.id.service_button, R.id.service_text, avgTime);
}});
setButtonAction(R.id.service2_button, new Runnable() {
public void run() {
- final float avgTime = serviceLoop(1);
+ final float avgTime = serviceLoop("xyzzy");
endAsyncOp(R.id.service2_button, R.id.service2_text, avgTime);
}});
@@ -181,6 +184,18 @@ public class ProviderPerfActivity extends Activity {
final float avgTime = callLoop("XXXXXXXX"); // non-existent
endAsyncOp(R.id.call2_button, R.id.call2_text, avgTime);
}});
+
+ setButtonAction(R.id.obtain_button, new Runnable() {
+ public void run() {
+ final float avgTime = parcelLoop(true);
+ endAsyncOp(R.id.obtain_button, R.id.obtain_text, avgTime);
+ }});
+
+ setButtonAction(R.id.recycle_button, new Runnable() {
+ public void run() {
+ final float avgTime = parcelLoop(false);
+ endAsyncOp(R.id.recycle_button, R.id.recycle_text, avgTime);
+ }});
}
@Override public void onResume() {
@@ -199,35 +214,39 @@ public class ProviderPerfActivity extends Activity {
private void setButtonAction(int button_id, final Runnable r) {
final Button button = (Button) findViewById(button_id);
- if (button == null) {
- Log.w(TAG, "Bogus button ID: " + button_id);
- return;
- }
button.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- TextView tv = (TextView) findViewById(R.id.iterations_edit);
- if (tv != null) {
- try {
- mIterations = Integer.parseInt(tv.getText().toString());
- } catch (NumberFormatException e) {
- Log.w(TAG, "Invalid iteration count", e);
- if (tv != null) tv.setText(Integer.toString(mIterations));
- }
- }
-
- button.setEnabled(false);
- new Thread(r).start();
+ public void onClick(View v) {
+ button.requestFocus();
+ button.setEnabled(false);
+
+ TextView tvIter = (TextView) findViewById(R.id.iterations_edit);
+ try {
+ mIterations = Integer.parseInt(tvIter.getText().toString());
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Invalid iteration count", e);
+ if (tvIter != null) tvIter.setText(Integer.toString(mIterations));
+ }
+
+ TextView tvTrace = (TextView) findViewById(R.id.trace_edit);
+ String name = tvTrace.getText().toString();
+ if (name != null && name.length() > 0) {
+ mTraceName = name;
+ Debug.startMethodTracing(name);
}
- });
+
+ new Thread(r).start();
+ }
+ });
}
private void endAsyncOp(final int button_id, final int text_id, final float avgTime) {
mHandler.post(new Runnable() {
- public void run() {
- Button button = (Button) findViewById(button_id);
- button.setEnabled(true);
- setTextTime(text_id, avgTime);
- }});
+ public void run() {
+ Debug.stopMethodTracing();
+ findViewById(button_id).setEnabled(true);
+ setTextTime(text_id, avgTime);
+ }
+ });
}
private void setTextTime(int id, float avgTime) {
@@ -397,33 +416,33 @@ public class ProviderPerfActivity extends Activity {
}
// Returns average cross-process dummy query time in milliseconds.
- private float serviceLoop(int amtEncoding) {
+ private float serviceLoop(String value) {
if (mServiceStub == null) {
Log.v(TAG, "No service stub.");
return -999;
}
+
String dummy = null;
try {
+ if (mTraceName != null) mServiceStub.startTracing(mTraceName + ".service");
+
long sumNanos = 0;
- int count = 0;
for (int i = 0; i < mIterations; i++) {
long lastTime = System.nanoTime();
- if (amtEncoding == 0) {
+ if (value == null) {
mServiceStub.pingVoid();
} else {
- dummy = mServiceStub.pingString(dummy);
+ value = mServiceStub.pingString(value);
}
- long curTime = System.nanoTime();
- long duration = curTime - lastTime;
- count++;
- sumNanos += duration;
+ sumNanos += System.nanoTime() - lastTime;
}
- float averageMillis = (float) sumNanos / (float) (count != 0 ? count : 1) / 1000000.0f;
- Log.v(TAG, "service loop: total: " + count + "; avg_ms=" + averageMillis);
- return averageMillis;
+
+ if (mTraceName != null) mServiceStub.stopTracing();
+
+ return (float) sumNanos / Math.max(1.0f, (float) mIterations) / 1000000.0f;
} catch (RemoteException e) {
- Log.e(TAG, "error in service loop: " + e);
- return -999.0f;
+ Log.e(TAG, "Binder call failed", e);
+ return -999;
}
}
@@ -467,6 +486,27 @@ public class ProviderPerfActivity extends Activity {
}
}
+ // Returns average operation time in milliseconds.
+ // obtain: true = measure obtain(), false = measure recycle()
+ private float parcelLoop(boolean obtain) {
+ long sumNanos = 0;
+ for (int i = 0; i < mIterations; i++) {
+ if (obtain) {
+ long lastTime = System.nanoTime();
+ Parcel p = Parcel.obtain();
+ sumNanos += System.nanoTime() - lastTime;
+ p.recycle();
+ } else {
+ Parcel p = Parcel.obtain();
+ long lastTime = System.nanoTime();
+ p.recycle();
+ sumNanos += System.nanoTime() - lastTime;
+ }
+ }
+
+ return (float) sumNanos / Math.max(1.0f, (float) mIterations) / 1000000.0f;
+ }
+
// Returns average milliseconds.
private static final int MODE_READ = 0;
private static final int MODE_WRITE = 1;
diff --git a/procstatlog/procstatlog.c b/procstatlog/procstatlog.c
index 443efce..c4c39e3 100644
--- a/procstatlog/procstatlog.c
+++ b/procstatlog/procstatlog.c
@@ -44,10 +44,15 @@
//
// Files read:
//
-// /proc/*/stat - for all running processes
-// /proc/*/wchan - for all running processes
-// /proc/stat - one per line, e.g. "/proc/stat:intr"
-// /proc/yaffs - one per line, e.g. "/proc/yaffs:userdata:nBlockErasures"
+// /proc/*/stat - for all running/selected processes
+// /proc/*/wchan - for all running/selected processes
+// /proc/binder/stats - per line: "/proc/binder/stats:BC_REPLY"
+// /proc/diskstats - per device: "/proc/diskstats:mmcblk0"
+// /proc/net/dev - per interface: "/proc/net/dev:rmnet0"
+// /proc/stat - per line: "/proc/stat:intr"
+// /proc/yaffs - per device/line: "/proc/yaffs:userdata:nBlockErasures"
+// /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state
+// - per line: "/sys/.../time_in_state:245000"
struct data {
char *name; // filename, plus ":var" for many-valued files
@@ -89,11 +94,14 @@ static void read_data(struct data *data, const char *filename) {
}
// Read a name/value file and write data entries for each line.
-// The delimiter is used to split each line into name and value.
-// If terminator is non-NULL, processing stops after it appears.
// Returns the number of entries written (always <= stats_count).
+//
+// delimiter: used to split each line into name and value
+// terminator: if non-NULL, processing stops after this string
+// skip_words: skip this many words at the start of each line
static int read_lines(
- const char *filename, char delimiter, const char *terminator,
+ const char *filename,
+ char delimiter, const char *terminator, int skip_words,
struct data *stats, int stats_count) {
char buf[8192];
int fd = open(filename, O_RDONLY);
@@ -121,7 +129,13 @@ static int read_lines(
line = strtok(NULL, "\n")) {
// Line format: <sp>name<delim><sp>value
+ int i;
while (isspace(*line)) ++line;
+ for (i = 0; i < skip_words; ++i) {
+ while (isgraph(*line)) ++line;
+ while (isspace(*line)) ++line;
+ }
+
char *name_end = strchr(line, delimiter);
if (name_end == NULL) continue;
@@ -280,12 +294,13 @@ static struct data *read_stats(char *names[], int name_count) {
struct data *end = stats + stats_count;
next += read_proc_yaffs(next, stats + stats_count - next);
- next += read_lines("/proc/net/dev", ':', NULL, next, end - next);
- next += read_lines("/proc/stat", ' ', NULL, next, end - next);
- next += read_lines("/proc/binder/stats", ':', "\nproc ", next, end - next);
+ next += read_lines("/proc/net/dev", ':', NULL, 0, next, end - next);
+ next += read_lines("/proc/stat", ' ', NULL, 0, next, end - next);
+ next += read_lines("/proc/binder/stats", ':', "\nproc ", 0, next, end - next);
+ next += read_lines("/proc/diskstats", ' ', NULL, 2, next, end - next);
next += read_lines(
"/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state",
- ' ', NULL, next, end - next);
+ ' ', NULL, 0, next, end - next);
assert(next < stats + stats_count);
next->name = NULL;
diff --git a/procstatlog/procstatreport.py b/procstatlog/procstatreport.py
index a5af829..d38feac 100755
--- a/procstatlog/procstatreport.py
+++ b/procstatlog/procstatreport.py
@@ -112,10 +112,19 @@ pages: <nobr>%(nPageReads)d read</nobr>,
blocks: <nobr>%(nBlockErasures)d erased</nobr>
"""
+DISK_LABEL = """
+<span style="font-size: 150%%">disk: %(device)s</span><br>
+sectors: <nobr>%(reads)d read</nobr>, <nobr>%(writes)d written</nobr>
+"""
+
+DISK_TIME_LABEL = """
+msec: <nobr>%(msec)d waiting</nobr>
+"""
+
NET_LABEL = """
<span style="font-size: 150%%">net: %(interface)s</span><br>
bytes: <nobr>%(tx)d tx</nobr>,
-<nobr>%(rx)d rx</nobr><br>
+<nobr>%(rx)d rx</nobr>
"""
PAGE_END = """
@@ -478,11 +487,94 @@ def WriteOutput(history, log_filename, filename):
})
#
+ # Output non-YAFFS statistics
+ #
+
+ disk_reads = {}
+ disk_writes = {}
+ disk_msec = {}
+ total_io = max_io = max_msec = 0
+
+ last_state = {}
+ for when, state in sorted_history:
+ for key in state:
+ if not key.startswith("/proc/diskstats:"): continue
+
+ last = last_state.get(key, "").split()
+ next = state.get(key, "").split()
+ if not (last and next): continue
+
+ reads = int(next[2]) - int(last[2])
+ writes = int(next[6]) - int(last[6])
+ msec = int(next[10]) - int(last[10])
+ total_io += reads + writes
+ max_io = max(max_io, reads, writes)
+ max_msec = max(max_msec, msec)
+
+ diskstats, device = key.split(":", 1)
+ disk_reads.setdefault(device, {})[when] = reads
+ disk_writes.setdefault(device, {})[when] = writes
+ disk_msec.setdefault(device, {})[when] = msec
+
+ last_state = state
+
+ io_cutoff = total_io / 100
+ for num, device in enumerate(sorted(disk_reads.keys())):
+ if [d for d in disk_reads.keys()
+ if d.startswith(device) and d != device]: continue
+
+ reads, writes = disk_reads[device], disk_writes[device]
+ total_reads, total_writes = sum(reads.values()), sum(writes.values())
+ if total_reads + total_writes <= io_cutoff: continue
+
+ WriteChartData(
+ ["reads", "writes"], [reads, writes],
+ os.path.join(files_dir, "disk%d.csv" % num))
+
+ out.append(CHART % {
+ "id": cgi.escape("disk%d" % num),
+ "id_js": json.write("disk%d" % num),
+ "label_html": DISK_LABEL % {
+ "device": cgi.escape(device),
+ "reads": total_reads,
+ "writes": total_writes,
+ },
+ "filename_js": json.write("%s/disk%d.csv" % (files_url, num)),
+ "options_js": json.write({
+ "colors": ["gray", "teal"],
+ "dateWindow": date_window,
+ "fillGraph": True,
+ "height": 75,
+ "valueRange": [0, max_io * 11 / 10],
+ }),
+ })
+
+ msec = disk_msec[device]
+
+ WriteChartData(
+ ["msec"], [msec],
+ os.path.join(files_dir, "disk%d_time.csv" % num))
+
+ out.append(CHART % {
+ "id": cgi.escape("disk%d_time" % num),
+ "id_js": json.write("disk%d_time" % num),
+ "label_html": DISK_TIME_LABEL % {"msec": sum(msec.values())},
+ "filename_js": json.write("%s/disk%d_time.csv" % (files_url, num)),
+ "options_js": json.write({
+ "colors": ["blue"],
+ "dateWindow": date_window,
+ "fillGraph": True,
+ "height": 50,
+ "valueRange": [0, max_msec * 11 / 10],
+ }),
+ })
+
+ #
# Output per-process CPU and page faults collected earlier
#
- cpu_cutoff = (total_sys + total_user) / 1000
- faults_cutoff = sum(total_faults.values()) / 200
+ cpu_cutoff = (total_sys + total_user) / 200
+ faults_cutoff = sum(total_faults.values()) / 100
for start, pid in sorted([(s, p) for p, s in process_start.iteritems()]):
sys = sum([n for n, d in process_sys.get(pid, {}).values()])
sys_user = sum([n for n, d in process_sys_user.get(pid, {}).values()])