blob: 4f96e21662a7124455e1f2cc62159b83fc6ef73c [file] [log] [blame]
/*
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <utils/Errors.h>
#include <utils/threads.h>
#include <utils/CallStack.h>
#include <utils/Log.h>
#include <cutils/properties.h>
#include <cutils/atomic.h>
#include <binder/IServiceManager.h>
#include <utils/String16.h>
#include <binder/Parcel.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include "TestFrameworkService.h"
#include "TestFramework.h"
#include "TFSShell.h"
#define TFS_VERSION "TF v2.0"
#define PROMPT "\n>>>"
#define MAX_FILTERLEN 64
#define TFS_MIN(x, y) ((x) < (y))?(x):(y)
TFSShell::TFSShell() {
mBuf = (char *) malloc(sizeof(char) * MAX_BUFLEN);
mPath = (char *) malloc(sizeof(char) * MAX_PATHLEN);
mCmds = (char *) malloc(sizeof(char) * MAX_PATHLEN);
if(mPath) {
mPath[0] = '/';
mPath[1] = '\0';
}
mService = NULL;
mQuit = false;
}
TFSShell::~TFSShell() {
if(mBuf) {
free(mBuf);
mBuf = NULL;
}
if(mPath) {
free(mPath);
mPath = NULL;
}
if(mCmds) {
free(mCmds);
mCmds = NULL;
}
}
int TFSShell::MainLoop(ITestFrameworkService *service, char *script) {
int ret = -1;
int arglen = 0;
FILE *fp = stdin;
if(!mBuf) {
return -1;
}
mService = service;
if(script) {
fp = fopen(script, "rt");
if(NULL == fp) {
TFSTtyPrint("Script does not exist");
return -1;
}
}
while(!mQuit && !feof(fp)) {
TFSTtyPrint(PROMPT);
ret = -1;
fgets(mBuf, MAX_BUFLEN, fp);
arglen = strlen(mBuf);
if(arglen > 0 && mBuf[arglen-1] == '\n') {
mBuf[arglen-1] = '\0';
}
ret = RunCmd(mBuf, arglen);
}
return 0;
}
int TFSShell::RunCmd(char *buf, int arglen) {
int ret = -1;
char *arg = NULL;
FILE *fp = NULL;
int n = 0, fd = -1, fd2 = -1;
int len = 0;
int total = 0;
arg = TFSGetCmdArg(buf, arglen);
switch(buf[0]) {
case '#':
//comment, ignore
ret = 0;
break;
case 'h':
case 'H':
//help
if(arglen != 4 || strncmp(buf, "help", 4)) {
break;
}
TFSCmdHint("help");
ret = 0;
break;
case 'v':
case 'V':
TFSTtyPrint(TFS_VERSION);
ret = 0;
break;
case 'e':
case 'E':
//exit
if(arglen == 4 && !strncmp(buf, "exit", 4)) {
property_set("debug.tf.exit", "1");
mQuit = true;
ret = 0;
}
else if(arglen == 9 && !strncmp(buf, "eventtype", 9)) {
arg = TFSGetCmdArg(arg+arglen, arglen);
//eventtype <type>
if(arg) {
arg[arglen] = '\0';
property_set("debug.tf.eventtype", arg);
}
else {
property_get("debug.tf.eventtype", mBuf, "0");
TFSTtyPrint(mBuf, strlen(mBuf));
}
ret = 0;
}
break;
case 'k':
if(arglen != 8 || strncmp(buf, "kfilters", 4)) {
break;
}
//kfilters [available|list|set <filters>]
//filters := !|+<subsystem>:<event>
arg = TFSGetCmdArg(arg+arglen, arglen);
if(NULL == arg) {
//list
fd = open(NODE_STR(set_event), O_RDONLY);
while ((n = read(fd, mBuf, MAX_BUFLEN)) > 0) {
mBuf[n-1] = '\0';
TFSTtyPrint(mBuf, n);
}
close(fd);
}
else if(arg[0] == 'l') {
//list
fd = open(NODE_STR(set_event), O_RDONLY);
while ((n = read(fd, mBuf, MAX_BUFLEN)) > 0) {
mBuf[n-1] = '\0';
TFSTtyPrint(mBuf, n);
}
close(fd);
}
else if(arg[0] == 's') {
//set <filters>
arg = TFSGetCmdArg(arg+arglen, arglen);
if(arg) {
char filter[MAX_FILTERLEN];
if(arg[0] == '+') {
arg = TFSGetCmdArg(arg+1, arglen);
fd = open(NODE_STR(set_event), O_WRONLY);
}
else {
fd = open(NODE_STR(set_event), O_WRONLY|O_TRUNC);
}
if(arg) {
arg[arglen] = '\0';
}
arg = TFSGetFilterArg(arg, arglen);
while(arg) {
strlcpy(filter, arg, TFS_MIN(arglen+1, MAX_FILTERLEN));
n = write(fd, filter, arglen);
arg = TFSGetFilterArg(arg+arglen, arglen);
}
close(fd);
}
else {
//empty filters
fd = open(NODE_STR(set_event), O_WRONLY|O_TRUNC);
close(fd);
}
}
else if(arg[0] == 'a') {
//available events
fd = open(NODE_STR(available_events), O_RDONLY);
while ((n = read(fd, mBuf, MAX_BUFLEN)) > 0) {
mBuf[n-1] = '\0';
TFSTtyPrint(mBuf, n);
}
close(fd);
}
else {
TFSCmdHint("kfilters");
}
ret = 0;
break;
case 'f':
if(arglen != 7 || strncmp(buf, "filters", 7)) {
break;
}
//filters [available|list|set <filters>]
arg = TFSGetCmdArg(arg+arglen, arglen);
if(NULL == arg) {
property_get("debug.tf.filters", mBuf, "");
TFSTtyPrint(mBuf, strlen(mBuf));
}
else if(arg[0] == 's' || arg[0] == 's') {
//ufilters [available|list|set <filters>]
//filters := !|+<subsystem>:<event>
bool bRemove = false;
arg = (char *) TFSGetCmdArg(arg+arglen, arglen);
if(arg) {
char filters[MAX_FILTERLEN];
char * str = NULL;
filters[0] = '\0';
if(arg[0] == '+') {
arg = (char *)TFSGetCmdArg(arg+1, arglen);
if(arg) {
arg[arglen] = '\0';
}
property_get("debug.tf.filters", filters, NULL);
arg = TFSGetFilterArg(arg, arglen);
while(arg) {
bRemove = false;
arg[arglen] = '\0';
if (arg[0] == '!') {
arg++;
arglen--;
bRemove = true;
}
str = strstr(filters, arg);
if (str) {
if (bRemove) {
if ((filters < str) && (*(str-1) == '%'))
strlcpy(str-1, str+arglen, MAX_FILTERLEN - (str - filters));
else if(filters+strlen(filters) > str+arglen && *(str+arglen) == '%') {
strlcpy(str, str+arglen+1, MAX_FILTERLEN - (str - filters));
}
else
strlcpy(str, str+arglen, MAX_FILTERLEN);
}
}
else if (!bRemove) {
if (filters[0] != '\0')
strlcat(filters, "%", MAX_FILTERLEN);
strlcat(filters, arg, MAX_FILTERLEN);
}
arg = TFSGetFilterArg(arg+arglen+1, arglen);
}
}
else {
arg[arglen] = '\0';
filters[0] = '\0';
arg = TFSGetFilterArg(arg, arglen);
while(arg) {
arg[arglen] = '\0';
if (arg[0] == '!') {
arg++;
arglen--;
}
else {
str = strstr(filters, arg);
if (!str) {
if (filters[0] != '\0')
strlcat(filters, "%", MAX_FILTERLEN);
strlcat(filters, arg, MAX_FILTERLEN);
}
}
arg = TFSGetFilterArg(arg+arglen+1, arglen);
}
}
property_set("debug.tf.filters", filters);
}
else {
property_set("debug.tf.filters", "$.$");
}
}
else if(arg[0] == 'l') {
property_get("debug.tf.filters", mBuf, "");
TFSTtyPrint(mBuf, strlen(mBuf));
}
else if(arg[0] == 'a') {
//not supported yet
property_get("debug.tf.filters", mBuf, "");
TFSTtyPrint(mBuf, strlen(mBuf));
}
else {
TFSCmdHint("filters");
}
ret = 0;
break;
case 's':
if(!strncmp(buf, "start", 5)) {
//start
arg = TFSGetCmdArg(arg+arglen, arglen);
if(arg && (arg[0] == 'l' || arg[0] == 'L'))
property_set("debug.tf.enable", "logcat");
else if(arg && (arg[0] == '+'))
property_set("debug.tf.enable", "1");
else if(!arg) {
fd = open(NODE_STR(trace), O_WRONLY|O_TRUNC);
close(fd);
property_set("debug.tf.enable", "1");
}
else
TFSCmdHint("start");
if(mService) {
mService->TFSUpdate(START);
}
ret = 0;
}
else if(!strncmp(buf, "stop", 4)) {
//stop
property_set("debug.tf.enable", "0");
if(mService) {
mService->TFSUpdate(STOP);
}
ret = 0;
}
else if(!strncmp(buf, "size", 4)) {
//size
arg = TFSGetCmdArg(arg+arglen, arglen);
if(arg) {
arg[arglen] = '\0';
fd = open(NODE_STR(buffer_size_kb), O_WRONLY|O_TRUNC);
n = write(fd, arg, arglen);
close(fd);
}
else {
fd = open(NODE_STR(buffer_size_kb), O_RDONLY);
n = read(fd, mBuf, MAX_BUFLEN);
mBuf[n-1] = '\0';
TFSTtyPrint(mBuf, n);
close(fd);
}
ret = 0;
}
if(!strncmp(buf, "sleep", 5)) {
//sleep
unsigned long time = 0;
int sec = 0;
arg = TFSGetCmdArg(arg+arglen, arglen);
if(arg) {
time = strtoul(arg, NULL, 10);
sec = time/1000;
time = time - sec * 1000;
TFSTtyPrint("sleeping...\n");
if(sec) {
sleep(sec);
}
if(time) {
usleep(time*1000);
}
TFSTtyPrint("done sleeping\n");
}
else
TFSCmdHint("sleep");
ret = 0;
}
break;
case 'd':
if(arglen != 4 || strncmp(buf, "dump", 4)) {
break;
}
total = 0;
//dump [path]
arg = TFSGetCmdArg(arg+arglen, arglen);
if(arg && !strncmp("clear", arg, 5) && (arglen == 5)) {
fd = open(NODE_STR(trace), O_WRONLY|O_TRUNC);
close(fd);
ret = 0;
break;
}
if(NULL != arg) {
arg[arglen] = '\0';
if(mPath) {
chdir(mPath);
}
fd2 = open(arg, O_WRONLY|O_CREAT|O_TRUNC,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
}
fd = open(NODE_STR(trace), O_RDONLY);
while ((n = read(fd, mBuf, MAX_BUFLEN)) > 0) {
if(NULL == arg) {
TFSTtyPrint(mBuf, n);
}
else {
if(fd2 >= 0) {
write(fd2, mBuf, n);
total += n;
}
else {
TFSTtyPrint("could not dump into file");
break;
}
}
}
if(fd2 >= 0) {
snprintf(mBuf, MAX_BUFLEN, "written %d bytes...\n", total);
TFSTtyPrint(mBuf);
}
close(fd);
close(fd2);
fd2 = -1;
ret = 0;
break;
case 'c':
if(!strncmp(buf, "cd", 2) && arglen==2 && mPath && mCmds) {
arg = TFSGetCmdArg(arg+arglen, arglen);
if(arg) {
arg[arglen] = '\0';
snprintf(mCmds, MAX_PATHLEN, "cd %s;cd %s;pwd", mPath, arg);
fp = popen(mCmds, "r");
if(fp) {
while ( fgets( mPath, MAX_PATHLEN, fp));
pclose(fp);
}
int l = strlen(mPath);
mPath[l-1] = '\0';
}
ret = 0;
}
break;
case 'p':
if(!strncmp(buf, "pwd", 3) && arglen==3 && mPath) {
TFSTtyPrint(mPath);
ret = 0;
}
if(arglen == 11 && !strncmp(buf, "probeperiod", 11)) {
arg = TFSGetCmdArg(arg+arglen, arglen);
if(arg) {
property_set("debug.tf.probeperiod", arg);
}
else {
property_get("debug.tf.probeperiod", mBuf, "");
TFSTtyPrint(mBuf, strlen(mBuf));
}
ret = 0;
}
break;
case '\n':
ret = 0;
break;
default:
break;
}
if(ret < 0 && mPath && mCmds) {
snprintf(mCmds, MAX_PATHLEN, "cd %s;%s", mPath, buf);
fp = popen(mCmds, "r");
if(fp) {
while ( fgets( buf, MAX_BUFLEN, fp)) {
TFSTtyPrint(buf);
}
pclose(fp);
}
}
return ret;
}
int TFSShell::TFSTtyPrint(const char *buf, int len) {
int ret = 0;
if(buf) {
if(!len) {
len = strlen(buf);
}
ret = write(STDOUT_FILENO, buf, len);
}
return ret;
}
char *TFSShell::TFSGetCmdArg(char *buf, int &len)
{
char *start = buf;
int n = 0;
char *end = NULL;
len = 0;
if (!buf) {
return NULL;
}
n = strlen(buf);
end = start + n;
while (start && (start < end)) {
if (*start == ' ' || *start == '\t' || ( *start == '\r' ||
*start == '\n' || *start == '\"'))
++start;
else
break;
}
buf = start;
while (start && (start < end)) {
if (*start == ' ' || *start == '\t' || ( *start == '\r' ||
*start == '\n' || *start == '\"')) {
break;
}
++len;
++start;
}
if(!len && start >= end) {
len = 0;
return NULL;
}
return buf;
}
char *TFSShell::TFSGetFilterArg(char *buf, int &len)
{
char *start = buf;
int n = 0;
char *end = NULL;
len = 0;
if (!buf) {
return NULL;
}
n = strlen(buf);
end = start + n;
if(*start == '%') {
start++;
}
buf = start;
while (start && (start < end))
{
if (*start == '%') {
break;
}
++len;
++start;
}
if(!len && start >= end) {
len = 0;
return NULL;
}
return buf;
}
void TFSShell::TFSCmdHelp() {
TFSCmdHint("filters");
TFSCmdHint("kfilters");
TFSCmdHint("start");
TFSCmdHint("stop");
TFSCmdHint("sleep");
TFSCmdHint("dump");
TFSCmdHint("eventtype");
TFSCmdHint("version");
TFSCmdHint("exit");
}
void TFSShell::TFSCmdHint(const char *buf) {
switch(buf[0]) {
case 'h':
case 'H':
TFSCmdHelp();
break;
case 'd':
TFSTtyPrint("dump [clear|<logfile>] : dumps log\n"
" 'clear' - clears log\n"
" '<logfile>' - dumps trace log to this file\n\n");
break;
case 'v':
TFSTtyPrint("version : prints version number\n\n");
break;
case 'e':
if(buf[1] == 'v') {
TFSTtyPrint("eventtype [<type>] : displays (in decimal) current eventtypes allowed\n"
" <type> event type in decimal\n"
" TF_EVENT_START = 0x01\n"
" TF_EVENT_STOP = 0x02\n"
" TF_EVENT = 0x04\n"
" TF_EVENT_JAVA_START = 0x08\n"
" TF_EVENT_JAVA_STOP = 0x10\n"
" TF_EVENT_JAVA = 0x20\n\n");
}
else if(buf[1] == 'x') {
TFSTtyPrint("exit] : exit\n\n");
}
break;
case 's':
if(buf[2] == 'a') {
TFSTtyPrint("start [+|logcat] : starts tracing and clears previous log\n"
" '+' - previous log not cleared,\n"
" appendeds to previous log\n"
" 'logcat' - output redirected to logcat\n\n");
}
else if(buf[2] == 'o') {
TFSTtyPrint("stop : stops tracing\n\n");
}
else if(buf[1] == 'i') {
TFSTtyPrint("size [<size-in-kb>] : displays current buffer size\n"
" <size-in-kb> sets to this size\n\n");
}
else if(buf[1] == 'l') {
TFSTtyPrint("sleep [<time-in-ms>] : sleeps for <time-in-ms>\n");
}
break;
case 'f':
TFSTtyPrint("filters [set [<uspacefilters>]|available|list] : userspace filters\n"
" 'set' - clears filters \n"
" 'set <uspacefilters>' - sets filters \n"
" <uspacefilters> := [+]<filters> \n"
" <filters> := <filter>{%<filter>} \n"
" <filter> := [!]<subsystem>:<event>\n"
" ex: filters set SF:*%STC:* all SF, STC events set\n"
" filters set +Display:* - all Display events\n"
" added to previous filters\n"
" filters set +ui:*%!STC:* - all STC events removed,\n"
" all ui events added\n"
" 'available' - all available events\n"
" 'list' - lists all set events\n\n");
break;
case 'k':
TFSTtyPrint("kfilters [set [<kspacefilters>]|available|list] : kernel filters\n"
" 'set' - clears filters \n"
" 'set <kspacefilters>' - sets filters \n"
" <kspacefilters> := [+]<filters> \n"
" <filters> := <filter>{%<filter>} \n"
" <filter> := [!]<subsystem>:<event>\n"
" ex: filters set power:*%irq:* sets all power, irq events\n"
" filters set +sched:* - all scheduler events\n"
" added to previous filters\n"
" filters set +kgsl:*%!power:* - all power events removed,\n"
" all kgsl events added\n"
" 'available' - all available events\n"
" 'list' - lists all set events\n\n");
break;
}
}