Merge "Track PSS in loadtest."
This commit is contained in:
@@ -39,5 +39,6 @@
|
||||
</activity>
|
||||
<receiver android:name=".LoadtestActivity$PusherAlarmReceiver" />
|
||||
<receiver android:name=".LoadtestActivity$StopperAlarmReceiver"/>
|
||||
<receiver android:name=".PerfData$PerfAlarmReceiver"/>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -166,12 +166,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/display_output"
|
||||
android:textSize="30dp"/>
|
||||
<Button
|
||||
android:id="@+id/display_perf"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/display_perf"
|
||||
android:textSize="30dp"/>
|
||||
|
||||
<Space
|
||||
android:layout_width="1dp"
|
||||
@@ -179,6 +173,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/report_text"
|
||||
android:gravity="center"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
<string name="bucket_label">bucket size (mins): </string>
|
||||
<string name="burst_label">burst: </string>
|
||||
<string name="display_output">Show metrics data</string>
|
||||
<string name="display_perf">Show perf data</string>
|
||||
<string name="placebo">placebo</string>
|
||||
<string name="period_label">logging period (secs): </string>
|
||||
<string name="replication_label">metric replication: </string>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.statsd.loadtest;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import java.text.ParseException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class BatteryDataRecorder extends PerfDataRecorder {
|
||||
private static final String TAG = "loadtest.BatteryDataRecorder";
|
||||
private static final String DUMP_FILENAME = TAG + "_dump.tmp";
|
||||
|
||||
public BatteryDataRecorder(boolean placebo, int replication, long bucketMins, long periodSecs,
|
||||
int burst) {
|
||||
super(placebo, replication, bucketMins, periodSecs, burst);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startRecording(Context context) {
|
||||
// Reset batterystats.
|
||||
runDumpsysStats(context, DUMP_FILENAME, "batterystats", "--reset");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAlarm(Context context) {
|
||||
// Nothing to do as for battery, the whole data is in the final dumpsys call.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopRecording(Context context) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// Don't use --checkin.
|
||||
runDumpsysStats(context, DUMP_FILENAME, "batterystats");
|
||||
readDumpData(context, DUMP_FILENAME, new BatteryStatsParser(), sb);
|
||||
writeData(context, "battery_", "time,battery_level", sb);
|
||||
}
|
||||
}
|
||||
@@ -21,13 +21,13 @@ import java.text.ParseException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class BatteryStatsParser {
|
||||
public class BatteryStatsParser implements PerfParser {
|
||||
|
||||
private static final Pattern LINE_PATTERN =
|
||||
Pattern.compile("\\s*\\+*(\\S*)\\s\\(\\d+\\)\\s(\\d\\d\\d)\\s.*");
|
||||
private static final Pattern TIME_PATTERN =
|
||||
Pattern.compile("(\\d+)?(h)?(\\d+)?(m)?(\\d+)?(s)?(\\d+)?(ms)?");
|
||||
private static final String TAG = "BatteryStatsParser";
|
||||
private static final String TAG = "loadtest.BatteryStatsParser";
|
||||
|
||||
private boolean mHistoryStarted;
|
||||
private boolean mHistoryEnded;
|
||||
@@ -35,6 +35,7 @@ public class BatteryStatsParser {
|
||||
public BatteryStatsParser() {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String parseLine(String line) {
|
||||
if (mHistoryEnded) {
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.android.statsd.loadtest;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.Log;
|
||||
import android.util.StatsLog;
|
||||
|
||||
import com.android.internal.os.StatsdConfigProto.Bucket;
|
||||
import com.android.internal.os.StatsdConfigProto.Predicate;
|
||||
@@ -45,7 +44,7 @@ import java.util.List;
|
||||
public class ConfigFactory {
|
||||
public static final String CONFIG_NAME = "LOADTEST";
|
||||
|
||||
private static final String TAG = "ConfigFactory";
|
||||
private static final String TAG = "loadtest.ConfigFactory";
|
||||
|
||||
private final StatsdConfig mTemplate;
|
||||
|
||||
|
||||
@@ -65,8 +65,9 @@ import android.widget.Toast;
|
||||
public class LoadtestActivity extends Activity {
|
||||
|
||||
private static final String TAG = "StatsdLoadtest";
|
||||
private static final String TYPE = "type";
|
||||
private static final String ALARM = "push_alarm";
|
||||
public static final String TYPE = "type";
|
||||
private static final String PUSH_ALARM = "push_alarm";
|
||||
public static final String PERF_ALARM = "perf_alarm";
|
||||
private static final String START = "start";
|
||||
private static final String STOP = "stop";
|
||||
|
||||
@@ -74,7 +75,7 @@ public class LoadtestActivity extends Activity {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Intent activityIntent = new Intent(context, LoadtestActivity.class);
|
||||
activityIntent.putExtra(TYPE, ALARM);
|
||||
activityIntent.putExtra(TYPE, PUSH_ALARM);
|
||||
context.startActivity(activityIntent);
|
||||
}
|
||||
}
|
||||
@@ -105,6 +106,9 @@ public class LoadtestActivity extends Activity {
|
||||
private TextView mReportText;
|
||||
private CheckBox mPlaceboCheckBox;
|
||||
|
||||
/** When the load test started. */
|
||||
private long mStartedTimeMillis;
|
||||
|
||||
/** For measuring perf data. */
|
||||
private PerfData mPerfData;
|
||||
|
||||
@@ -180,7 +184,7 @@ public class LoadtestActivity extends Activity {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mStarted) {
|
||||
stopLoadtest(true);
|
||||
stopLoadtest();
|
||||
} else {
|
||||
startLoadtest();
|
||||
}
|
||||
@@ -194,20 +198,11 @@ public class LoadtestActivity extends Activity {
|
||||
}
|
||||
});
|
||||
|
||||
findViewById(R.id.display_perf).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mPerfData.publishData(LoadtestActivity.this, mPlacebo, mReplication, mBucketMins,
|
||||
mPeriodSecs, mBurst);
|
||||
}
|
||||
});
|
||||
|
||||
mAlarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
|
||||
mStatsManager = (StatsManager) getSystemService("stats");
|
||||
mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
mFactory = new ConfigFactory(this);
|
||||
mPerfData = new PerfData();
|
||||
stopLoadtest(false);
|
||||
stopLoadtest();
|
||||
mReportText.setText("");
|
||||
}
|
||||
|
||||
@@ -218,14 +213,17 @@ public class LoadtestActivity extends Activity {
|
||||
return;
|
||||
}
|
||||
switch (type) {
|
||||
case ALARM:
|
||||
onAlarm(intent);
|
||||
break;
|
||||
case PERF_ALARM:
|
||||
onPerfAlarm();
|
||||
break;
|
||||
case PUSH_ALARM:
|
||||
onAlarm();
|
||||
break;
|
||||
case START:
|
||||
startLoadtest();
|
||||
break;
|
||||
case STOP:
|
||||
stopLoadtest(true);
|
||||
case STOP:
|
||||
stopLoadtest();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown type: " + type);
|
||||
@@ -235,12 +233,23 @@ public class LoadtestActivity extends Activity {
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.d(TAG, "Destroying");
|
||||
stopLoadtest(false);
|
||||
mPerfData.onDestroy();
|
||||
stopLoadtest();
|
||||
clearConfigs();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void onAlarm(Intent intent) {
|
||||
private void onPerfAlarm() {
|
||||
if (mPerfData != null) {
|
||||
mPerfData.onAlarm(this);
|
||||
}
|
||||
// Piggy-back on that alarm to show the elapsed time.
|
||||
long elapsedTimeMins = (long) Math.floor(
|
||||
(SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
|
||||
mReportText.setText("Loadtest in progress. Elapsed time = " + elapsedTimeMins + " min(s)");
|
||||
}
|
||||
|
||||
private void onAlarm() {
|
||||
Log.d(TAG, "ON ALARM");
|
||||
|
||||
// Set the next task.
|
||||
@@ -259,7 +268,7 @@ public class LoadtestActivity extends Activity {
|
||||
/** Schedules the next cycle of pushing atoms into logd. */
|
||||
private void scheduleNext() {
|
||||
Intent intent = new Intent(this, PusherAlarmReceiver.class);
|
||||
intent.putExtra(TYPE, ALARM);
|
||||
intent.putExtra(TYPE, PUSH_ALARM);
|
||||
mPushPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
|
||||
long nextTime = SystemClock.elapsedRealtime() + mPeriodSecs * 1000;
|
||||
mAlarmMgr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, mPushPendingIntent);
|
||||
@@ -271,7 +280,7 @@ public class LoadtestActivity extends Activity {
|
||||
}
|
||||
|
||||
// Clean up the state.
|
||||
stopLoadtest(false);
|
||||
stopLoadtest();
|
||||
|
||||
// Prepare to push a sequence of atoms to logd.
|
||||
mPusher = new SequencePusher(mBurst, mPlacebo);
|
||||
@@ -291,15 +300,17 @@ public class LoadtestActivity extends Activity {
|
||||
// Log atoms.
|
||||
scheduleNext();
|
||||
|
||||
// Reset battery data.
|
||||
mPerfData.resetData(this);
|
||||
// Start tracking performance.
|
||||
mPerfData = new PerfData(this, mPlacebo, mReplication, mBucketMins, mPeriodSecs, mBurst);
|
||||
mPerfData.startRecording(this);
|
||||
|
||||
mReportText.setText("");
|
||||
mReportText.setText("Loadtest in progress.");
|
||||
mStartedTimeMillis = SystemClock.elapsedRealtime();
|
||||
|
||||
updateStarted(true);
|
||||
}
|
||||
|
||||
private synchronized void stopLoadtest(boolean publishPerfData) {
|
||||
private synchronized void stopLoadtest() {
|
||||
if (mPushPendingIntent != null) {
|
||||
Log.d(TAG, "Canceling pre-existing push alarm");
|
||||
mAlarmMgr.cancel(mPushPendingIntent);
|
||||
@@ -314,12 +325,17 @@ public class LoadtestActivity extends Activity {
|
||||
mWakeLock.release();
|
||||
mWakeLock = null;
|
||||
}
|
||||
fetchAndDisplayData();
|
||||
if (mPerfData != null) {
|
||||
mPerfData.stopRecording(this);
|
||||
mPerfData.onDestroy();
|
||||
mPerfData = null;
|
||||
}
|
||||
|
||||
long elapsedTimeMins = (long) Math.floor(
|
||||
(SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
|
||||
mReportText.setText("Loadtest ended. Elapsed time = " + elapsedTimeMins + " min(s)");
|
||||
clearConfigs();
|
||||
updateStarted(false);
|
||||
if (publishPerfData) {
|
||||
mPerfData.publishData(this, mPlacebo, mReplication, mBucketMins, mPeriodSecs, mBurst);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void updateStarted(boolean started) {
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.statsd.loadtest;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** Parses PSS info from dumpsys meminfo */
|
||||
public class MemInfoParser implements PerfParser {
|
||||
|
||||
private static final Pattern LINE_PATTERN =
|
||||
Pattern.compile("\\s*(\\d*,*\\d*)K:\\s(\\S*)\\s\\.*");
|
||||
private static final String PSS_BY_PROCESS = "Total PSS by process:";
|
||||
private static final String TAG = "loadtest.MemInfoParser";
|
||||
|
||||
private boolean mPssStarted;
|
||||
private boolean mPssEnded;
|
||||
private final long mStartTimeMillis;
|
||||
|
||||
public MemInfoParser(long startTimeMillis) {
|
||||
mStartTimeMillis = startTimeMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String parseLine(String line) {
|
||||
if (mPssEnded) {
|
||||
return null;
|
||||
}
|
||||
if (!mPssStarted) {
|
||||
if (line.contains(PSS_BY_PROCESS)) {
|
||||
mPssStarted = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (line.isEmpty()) {
|
||||
mPssEnded = true;
|
||||
return null;
|
||||
}
|
||||
Matcher lineMatcher = LINE_PATTERN.matcher(line);
|
||||
if (lineMatcher.find() && lineMatcher.group(1) != null && lineMatcher.group(2) != null) {
|
||||
if (lineMatcher.group(2).equals("statsd")) {
|
||||
long timeDeltaMillis = SystemClock.elapsedRealtime() - mStartTimeMillis;
|
||||
return timeDeltaMillis + "," + convertToPss(lineMatcher.group(1));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String convertToPss(String input) {
|
||||
return input.replace(",", "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.statsd.loadtest;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
public class MemoryDataRecorder extends PerfDataRecorder {
|
||||
private static final String TAG = "loadtest.MemoryDataDataRecorder";
|
||||
private static final String DUMP_FILENAME = TAG + "_dump.tmp";
|
||||
|
||||
private long mStartTimeMillis;
|
||||
private StringBuilder mSb;
|
||||
|
||||
public MemoryDataRecorder(boolean placebo, int replication, long bucketMins, long periodSecs,
|
||||
int burst) {
|
||||
super(placebo, replication, bucketMins, periodSecs, burst);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startRecording(Context context) {
|
||||
mStartTimeMillis = SystemClock.elapsedRealtime();
|
||||
mSb = new StringBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAlarm(Context context) {
|
||||
Log.d(TAG, "GOT ALARM IN MEM");
|
||||
runDumpsysStats(context, DUMP_FILENAME, "meminfo");
|
||||
readDumpData(context, DUMP_FILENAME, new MemInfoParser(mStartTimeMillis), mSb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopRecording(Context context) {
|
||||
writeData(context, "meminfo_", "time,pss", mSb);
|
||||
}
|
||||
}
|
||||
@@ -15,29 +15,14 @@
|
||||
*/
|
||||
package com.android.statsd.loadtest;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.util.StatsLog;
|
||||
import android.util.StatsManager;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.os.IStatsManager;
|
||||
import android.os.ServiceManager;
|
||||
import android.view.View.OnFocusChangeListener;
|
||||
|
||||
public abstract class NumericalWatcher implements TextWatcher {
|
||||
|
||||
private static final String TAG = "NumericalWatcher";
|
||||
private static final String TAG = "loadtest.NumericalWatcher";
|
||||
|
||||
private final TextView mTextView;
|
||||
private final int mMin;
|
||||
|
||||
@@ -16,147 +16,86 @@
|
||||
package com.android.statsd.loadtest;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.os.Debug;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/** Prints some information about the device via Dumpsys in order to evaluate health metrics. */
|
||||
public class PerfData {
|
||||
public class PerfData extends PerfDataRecorder {
|
||||
|
||||
private static final String TAG = "PerfData";
|
||||
private static final String DUMP_FILENAME = TAG + "_dump.tmp";
|
||||
private static final String TAG = "loadtest.PerfData";
|
||||
|
||||
public void resetData(Context context) {
|
||||
runDumpsysStats(context, "batterystats", "--reset");
|
||||
/** Polling period for performance snapshots like memory. */
|
||||
private static final long POLLING_PERIOD_MILLIS = 1 * 60 * 1000;
|
||||
|
||||
public final static class PerfAlarmReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Intent activityIntent = new Intent(context, LoadtestActivity.class);
|
||||
activityIntent.putExtra(LoadtestActivity.TYPE, LoadtestActivity.PERF_ALARM);
|
||||
context.startActivity(activityIntent);
|
||||
}
|
||||
}
|
||||
|
||||
public void publishData(Context context, boolean placebo, int replication, long bucketMins,
|
||||
long periodSecs, int burst) {
|
||||
publishBatteryData(context, placebo, replication, bucketMins, periodSecs, burst);
|
||||
private AlarmManager mAlarmMgr;
|
||||
|
||||
/** Used to periodically poll some dumpsys data. */
|
||||
private PendingIntent mPendingIntent;
|
||||
|
||||
private final Set<PerfDataRecorder> mRecorders;
|
||||
|
||||
public PerfData(Context context, boolean placebo, int replication, long bucketMins,
|
||||
long periodSecs, int burst) {
|
||||
super(placebo, replication, bucketMins, periodSecs, burst);
|
||||
mRecorders = new HashSet();
|
||||
mRecorders.add(new BatteryDataRecorder(placebo, replication, bucketMins, periodSecs, burst));
|
||||
mRecorders.add(new MemoryDataRecorder(placebo, replication, bucketMins, periodSecs, burst));
|
||||
mAlarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
}
|
||||
|
||||
private void publishBatteryData(Context context, boolean placebo, int replication,
|
||||
long bucketMins, long periodSecs, int burst) {
|
||||
// Don't use --checkin.
|
||||
runDumpsysStats(context, "batterystats");
|
||||
writeBatteryData(context, placebo, replication, bucketMins, periodSecs, burst);
|
||||
}
|
||||
|
||||
private void runDumpsysStats(Context context, String cmd, String... args) {
|
||||
boolean success = false;
|
||||
// Call dumpsys Dump statistics to a file.
|
||||
FileOutputStream fo = null;
|
||||
try {
|
||||
fo = context.openFileOutput(DUMP_FILENAME, Context.MODE_PRIVATE);
|
||||
if (!Debug.dumpService(cmd, fo.getFD(), args)) {
|
||||
Log.w(TAG, "Dumpsys failed.");
|
||||
}
|
||||
success = true;
|
||||
} catch (IOException | SecurityException | NullPointerException e) {
|
||||
// SecurityException may occur when trying to dump multi-user info.
|
||||
// NPE can occur during dumpService (root cause unknown).
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
closeQuietly(fo);
|
||||
public void onDestroy() {
|
||||
if (mPendingIntent != null) {
|
||||
mAlarmMgr.cancel(mPendingIntent);
|
||||
mPendingIntent = null;
|
||||
}
|
||||
}
|
||||
|
||||
private String readDumpFile(Context context) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
FileInputStream fi = null;
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
fi = context.openFileInput(DUMP_FILENAME);
|
||||
br = new BufferedReader(new InputStreamReader(fi));
|
||||
String line = br.readLine();
|
||||
while (line != null) {
|
||||
sb.append(line);
|
||||
sb.append(System.lineSeparator());
|
||||
line = br.readLine();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
closeQuietly(br);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
@Override
|
||||
public void startRecording(Context context) {
|
||||
Intent intent = new Intent(context, PerfAlarmReceiver.class);
|
||||
intent.putExtra(LoadtestActivity.TYPE, LoadtestActivity.PERF_ALARM);
|
||||
mPendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
|
||||
mAlarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, -1 /* now */,
|
||||
POLLING_PERIOD_MILLIS, mPendingIntent);
|
||||
|
||||
private static void closeQuietly(@Nullable Closeable c) {
|
||||
if (c != null) {
|
||||
try {
|
||||
c.close();
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
for (PerfDataRecorder recorder : mRecorders) {
|
||||
recorder.startRecording(context);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeBatteryData(Context context, boolean placebo, int replication, long bucketMins,
|
||||
long periodSecs, int burst) {
|
||||
BatteryStatsParser parser = new BatteryStatsParser();
|
||||
FileInputStream fi = null;
|
||||
BufferedReader br = null;
|
||||
String suffix = new SimpleDateFormat("YYYY_MM_dd_HH_mm_ss").format(new Date());
|
||||
File batteryDataFile = new File(getStorageDir(), "battery_" + suffix + ".csv");
|
||||
Log.d(TAG, "Writing battery data to " + batteryDataFile.getAbsolutePath());
|
||||
|
||||
FileWriter writer = null;
|
||||
try {
|
||||
fi = context.openFileInput(DUMP_FILENAME);
|
||||
writer = new FileWriter(batteryDataFile);
|
||||
writer.append("time,battery_level"
|
||||
+ getColumnName(placebo, replication, bucketMins, periodSecs, burst) + "\n");
|
||||
br = new BufferedReader(new InputStreamReader(fi));
|
||||
String line = br.readLine();
|
||||
while (line != null) {
|
||||
String recordLine = parser.parseLine(line);
|
||||
if (recordLine != null) {
|
||||
writer.append(recordLine);
|
||||
}
|
||||
line = br.readLine();
|
||||
}
|
||||
writer.flush();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
closeQuietly(writer);
|
||||
closeQuietly(br);
|
||||
@Override
|
||||
public void onAlarm(Context context) {
|
||||
for (PerfDataRecorder recorder : mRecorders) {
|
||||
recorder.onAlarm(context);
|
||||
}
|
||||
}
|
||||
|
||||
private File getStorageDir() {
|
||||
File file = new File(Environment.getExternalStoragePublicDirectory(
|
||||
Environment.DIRECTORY_DOCUMENTS), "loadtest");
|
||||
if (!file.mkdirs()) {
|
||||
Log.e(TAG, "Directory not created");
|
||||
@Override
|
||||
public void stopRecording(Context context) {
|
||||
if (mPendingIntent != null) {
|
||||
mAlarmMgr.cancel(mPendingIntent);
|
||||
mPendingIntent = null;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
private String getColumnName(boolean placebo, int replication, long bucketMins, long periodSecs,
|
||||
int burst) {
|
||||
if (placebo) {
|
||||
return "_placebo_p=" + periodSecs;
|
||||
for (PerfDataRecorder recorder : mRecorders) {
|
||||
recorder.stopRecording(context);
|
||||
}
|
||||
return "_r=" + replication + "_bkt=" + bucketMins + "_p=" + periodSecs + "_bst=" + burst;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.statsd.loadtest;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.os.Debug;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public abstract class PerfDataRecorder {
|
||||
private static final String TAG = "loadtest.PerfDataRecorder";
|
||||
|
||||
protected final String mFileSuffix;
|
||||
protected final String mColumnSuffix;
|
||||
|
||||
protected PerfDataRecorder(boolean placebo, int replication, long bucketMins, long periodSecs,
|
||||
int burst) {
|
||||
mFileSuffix = new SimpleDateFormat("YYYY_MM_dd_HH_mm_ss").format(new Date());
|
||||
mColumnSuffix = getColumnSuffix(placebo, replication, bucketMins, periodSecs, burst);
|
||||
}
|
||||
|
||||
/** Starts recording performance data. */
|
||||
public abstract void startRecording(Context context);
|
||||
|
||||
/** Called periodically. For the recorder to sample data, if needed. */
|
||||
public abstract void onAlarm(Context context);
|
||||
|
||||
/** Stops recording performance data, and writes it to disk. */
|
||||
public abstract void stopRecording(Context context);
|
||||
|
||||
/** Runs the dumpsys command. */
|
||||
protected void runDumpsysStats(Context context, String dumpFilename, String cmd,
|
||||
String... args) {
|
||||
boolean success = false;
|
||||
// Call dumpsys Dump statistics to a file.
|
||||
FileOutputStream fo = null;
|
||||
try {
|
||||
fo = context.openFileOutput(dumpFilename, Context.MODE_PRIVATE);
|
||||
if (!Debug.dumpService(cmd, fo.getFD(), args)) {
|
||||
Log.w(TAG, "Dumpsys failed.");
|
||||
}
|
||||
success = true;
|
||||
} catch (IOException | SecurityException | NullPointerException e) {
|
||||
// SecurityException may occur when trying to dump multi-user info.
|
||||
// NPE can occur during dumpService (root cause unknown).
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
closeQuietly(fo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a text file and parses each line, one by one. The result of the parsing is stored
|
||||
* in the passed {@link StringBuffer}.
|
||||
*/
|
||||
protected void readDumpData(Context context, String dumpFilename, PerfParser parser,
|
||||
StringBuilder sb) {
|
||||
FileInputStream fi = null;
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
fi = context.openFileInput(dumpFilename);
|
||||
br = new BufferedReader(new InputStreamReader(fi));
|
||||
String line = br.readLine();
|
||||
while (line != null) {
|
||||
String recordLine = parser.parseLine(line);
|
||||
if (recordLine != null) {
|
||||
sb.append(recordLine).append('\n');
|
||||
}
|
||||
line = br.readLine();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
closeQuietly(br);
|
||||
}
|
||||
}
|
||||
|
||||
/** Writes CSV data to a file. */
|
||||
protected void writeData(Context context, String filePrefix, String columnPrefix,
|
||||
StringBuilder sb) {
|
||||
File dataFile = new File(getStorageDir(), filePrefix + mFileSuffix + ".csv");
|
||||
|
||||
FileWriter writer = null;
|
||||
try {
|
||||
writer = new FileWriter(dataFile);
|
||||
writer.append(columnPrefix + mColumnSuffix + "\n");
|
||||
writer.append(sb.toString());
|
||||
writer.flush();
|
||||
Log.d(TAG, "Finished writing data at " + dataFile.getAbsolutePath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
closeQuietly(writer);
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the suffix to use in the column name for perf data. */
|
||||
private String getColumnSuffix(boolean placebo, int replication, long bucketMins,
|
||||
long periodSecs, int burst) {
|
||||
if (placebo) {
|
||||
return "_placebo_p=" + periodSecs;
|
||||
}
|
||||
return "_r=" + replication + "_bkt=" + bucketMins + "_p=" + periodSecs + "_bst=" + burst;
|
||||
}
|
||||
|
||||
|
||||
private File getStorageDir() {
|
||||
File file = new File(Environment.getExternalStoragePublicDirectory(
|
||||
Environment.DIRECTORY_DOCUMENTS), "loadtest");
|
||||
if (!file.mkdirs()) {
|
||||
Log.e(TAG, "Directory not created");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
private void closeQuietly(@Nullable Closeable c) {
|
||||
if (c != null) {
|
||||
try {
|
||||
c.close();
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.statsd.loadtest;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
|
||||
public interface PerfParser {
|
||||
|
||||
/**
|
||||
* Parses one line of the dumpsys output, and returns a string to write to the data file,
|
||||
* or null if no string should be written.
|
||||
*/
|
||||
@Nullable String parseLine(String line);
|
||||
}
|
||||
Reference in New Issue
Block a user