blob: 15d0e073002d0c9b844cf73196ffb57f76656bf6 [file] [log] [blame]
/*
* Copyright (C) 2010 0xlab - http://0xlab.org/
*
* 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 org.zeroxlab.benchmark;
import java.lang.Runtime;
import java.lang.IllegalThreadStateException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.lang.Math;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.net.Socket;
import java.net.ServerSocket;
import android.app.Activity;
import android.util.Log;
import android.os.Bundle;
import android.os.SystemClock;
import android.os.Handler;
import android.os.Message;
import android.content.Intent;
import android.widget.TextView;
import android.widget.ScrollView;
import android.content.pm.ActivityInfo;
public abstract class NativeTester extends Tester {
private TextView mTextView;
private ScrollView mScrollView;
private Runtime mRuntime;
private Process mProcess;
public final String TAG = "NativeTester";
public final String PING_MSG = "PING";
public final String ENV_VAR = "ZXBENCH_PORT";
public final int CHECK_FREQ = 1000;
public final int IDLE_KILL = 40000; //1000 * 60 * 5 (5mins);
public String mCommand;
public Handler mHandler;
public static final int GUINOTIFIER = 0x1234;
public Map<String, String> mStdOuts = new HashMap<String, String>();
public Map<String, String> mStdErrs = new HashMap<String, String>();
public Map<String, String> mSockets = new HashMap<String, String>();
private BufferedReader stdOutReader;
private BufferedReader stdErrReader;
private BufferedReader sckOutReader;
private ProcessMonitor mProcessMonitor;
private boolean mProcessRunning = false;
private double mIdleTime;
private StringBuilder stdOut = new StringBuilder();
private StringBuilder stdErr = new StringBuilder();
private StringBuilder sckOut = new StringBuilder();
private ServerSocket mServerSocket;
private Socket mClientSocket = null;
private int mBindPort = -1;
private String mCurrentCommand;
public NativeTester() {
mRuntime = Runtime.getRuntime();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gc);
mTextView = (TextView) findViewById(R.id.myTextView1);
mScrollView = (ScrollView) findViewById(R.id.myScrollView);
mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case GUINOTIFIER:
StringBuilder display = new StringBuilder();
display.append("Idle time: " + (int)mIdleTime + "ms");
display.append("\nCommand -->\n");
display.append(mCurrentCommand);
display.append("\nStderr -->\n");
display.append(stdErr.toString());
display.append("\nStdout -->\n");
display.append(stdOut.toString());
mTextView.setText(display.toString());
mScrollView.post(new Runnable() {
public void run() {
mScrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
});
break;
}
}
};
startTester();
}
protected abstract List<String> getCommands();
protected abstract String getPath();
protected abstract boolean saveResult(Intent intent);
@Override
protected int sleepBeforeStart() {
return 0;
};
@Override
protected int sleepBetweenRound(){
return 0;
};
private void reportOutputs() {
Log.i(TAG, stdOut.toString() );
Log.i(TAG, stdErr.toString() );
String [] sckOutLines = sckOut.toString().split("\n");
for(String sckOutLine: sckOutLines)
Log.i(TAG, sckOutLine);
}
public void oneRound() {
new ProcessRunner().start();
}
public int exitValue() throws IllegalThreadStateException {
return mProcess.exitValue();
}
public void killProcess() {
mProcess.destroy();
try {
mClientSocket.close();
mServerSocket.close();
} catch (IOException e) {
Log.i(TAG, "close server socket failed: " + e.toString());
}
mProcessRunning = false;
}
class ProcessRunner extends Thread {
public void run() {
for(String command: getCommands()) {
mCurrentCommand = command;
Log.i(TAG, "------------------------ process " + command + " start ------------------------ ");
try {
mServerSocket = new ServerSocket(0);
mServerSocket.setSoTimeout(IDLE_KILL);
} catch (IOException e) {
Log.e(TAG, "cannot create ServerSocket. " + e.toString());
interruptTester();
}
Log.i(TAG, "server socket created");
mBindPort = mServerSocket.getLocalPort();
String[] envp = {
ENV_VAR + "=" + mBindPort,
};
try {
mProcess = mRuntime.exec(getPath() + command, envp, getFilesDir());
if(mProcess == null)
throw new Exception();
mProcessRunning = true;
} catch (Exception e) {
Log.e(TAG, "Cannot execute command: `" + command + "`. " + e.toString());
mNow = 0;
continue;
}
Log.i(TAG, "command executed");
stdOutReader = new BufferedReader(new InputStreamReader(mProcess.getInputStream()));
stdErrReader = new BufferedReader(new InputStreamReader(mProcess.getErrorStream()));
updateBuffer stdOutThread = new updateBuffer(stdOutReader, stdOut);
updateBuffer stdErrThread = new updateBuffer(stdErrReader, stdErr);
stdOutThread.start();
stdErrThread.start();
try {
Log.i(TAG, "waiting conncetion with timeout " + mServerSocket.getSoTimeout());
mClientSocket = mServerSocket.accept();
} catch (IOException e) {
Log.e(TAG, "cannot acception incoming connection. " + e.toString());
killProcess();
continue;
}
Log.i(TAG, "connection accepted");
try {
sckOutReader = new BufferedReader(new InputStreamReader(mClientSocket.getInputStream()));
} catch (IOException e) {
Log.e(TAG, "cannot create input stream, lost connection? " + e.toString());
killProcess();
continue;
}
Log.i(TAG, "stream created");
updateBuffer socketThread = new updateBuffer(sckOutReader, sckOut);
socketThread.start();
mProcessMonitor = new ProcessMonitor(stdOutThread, stdErrThread, socketThread);
mProcessMonitor.start();
try {
mProcessMonitor.join();
} catch (InterruptedException e) {
Log.e(TAG, "inturrupted before process monitor joins: " + e.toString());
killProcess();
continue;
}
reportOutputs();
if (!mStdOuts.containsKey(command)) {
mStdOuts.put(command, mStdOuts.get(command) + stdOut.toString());
} else {
mStdOuts.put(command, stdOut.toString());
}
if (!mStdErrs.containsKey(command)) {
mStdErrs.put(command, mStdErrs.get(command) + stdErr.toString());
} else {
mStdErrs.put(command, stdErr.toString());
}
if (!mSockets.containsKey(command)) {
mSockets.put(command, sckOut.toString());
} else {
mSockets.put(command, mSockets.get(command) + sckOut.toString());
}
stdOut = new StringBuilder();
stdErr = new StringBuilder();
sckOut = new StringBuilder();
Log.i(TAG, "------------------------ process " + command + " finish ------------------------ ");
}
decreaseCounter();
Log.i(TAG, "counter decreased by 1 to " + mNow);
}
}
class ProcessMonitor extends Thread {
updateBuffer stdOutThread;
updateBuffer stdErrThread;
updateBuffer sckOutThread;
float idleTime;
ProcessMonitor (updateBuffer stdOutThread, updateBuffer stdErrThread, updateBuffer sckOutThread) {
this.stdOutThread = stdOutThread;
this.stdErrThread = stdErrThread;
this.sckOutThread = sckOutThread;
}
public void run() {
int value;
mIdleTime = 0.0;
while (true) {
try {
value = mProcess.exitValue();
} catch (IllegalThreadStateException e) {
mIdleTime = Math.min(Math.min(stdOutThread.idleTime(), stdErrThread.idleTime()), sckOutThread.idleTime()) ;
Message m = new Message();
m.what = GUINOTIFIER;
mHandler.sendMessage(m);
if ( mIdleTime > IDLE_KILL ) {
Log.e(TAG, "Native process idle for over " + IDLE_KILL/1000 + " Seconds, killing.");
killProcess();
Log.w(TAG, "Monitor: ends with no update threads joined");
//TODO: should find a way to end update threads gracefully
return;
}
SystemClock.sleep(CHECK_FREQ);
continue;
}
break;
}
Log.i(TAG, "Process exited with value = " + value);
try {
stdOutThread.join();
} catch (InterruptedException e) {
Log.w(TAG, "stdout update thread cannot join");
}
Log.i(TAG, "stdout update thread joined");
try {
stdErrThread.join();
} catch (InterruptedException e) {
Log.w(TAG, "stderr update thread cannot join");
}
Log.i(TAG, "stderr update thread joined");
try {
sckOutThread.join();
} catch (InterruptedException e) {
Log.w(TAG, "socket update thread cannot join");
}
Log.i(TAG, "socket update thread joined");
Log.i(TAG, "closing server socket");
try {
mClientSocket.close();
mServerSocket.close();
} catch (IOException e) {
Log.i(TAG, "close server socket failed: " + e.toString());
}
Log.w(TAG, "Monitor: All update threads joined");
}
}
class updateBuffer extends Thread {
long mLastRead;
BufferedReader is;
StringBuilder mBuffer;
final int UNREAD = -1;
updateBuffer(BufferedReader is, StringBuilder targetBuffer) {
this.is = is;
mBuffer = targetBuffer;
mLastRead = UNREAD;
}
public void run() {
char[] c = new char[1024];
int count;
try {
while ( (count = is.read(c,0,1024)) != -1) {
mLastRead = SystemClock.uptimeMillis();
mBuffer.append(c, 0, count);
Message m = new Message();
m.what = GUINOTIFIER;
mHandler.sendMessage(m);
if (!mProcessRunning && !is.ready()) {
is.close();
break;
}
}
} catch (IOException e) {
Log.e(TAG, "update buffer failed. " + e.toString());
}
}
public long idleTime() {
return SystemClock.uptimeMillis() - mLastRead;
}
}
}