blob: 52967f2edff46c4127ba360677aad64fac4e2c84 [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.
*/
#define LOG_TAG "TestFrameworkCommon"
#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 <unistd.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 <inc/testframework.h>
#include "TestFramework.h"
#include "TestFrameworkService.h"
#include "TestFrameworkHash.h"
#define _STR(x) #x
#define STR(x) _STR(x)
TestFrameworkCommon::TestFrameworkCommon() {
TF_LOGD("TestFrameworkCommon created");
mTfInitDone = false;
mTfTracerFd = -1;
mTfMarkerFd = -1;
mLogType = TF_DISABLE;
mDebugfsFound = 0;
mFilterTable = NULL;
mElapsedTime = 0;
mLastRecordedTime = ns2ms(systemTime());
mEventType = TF_EVENT | TF_EVENT_START | TF_EVENT_STOP |
TF_EVENT_JAVA | TF_EVENT_JAVA_START | TF_EVENT_JAVA_STOP;
mClosedInterval = 0;
mOpenInterval = 0;
mIsService = 1;
mProbeFreq = TF_EXCHANGE_INTERVAL;
mFilters[0] = '\0';
mFiltersLen = 0;
mTurnOnByApi = false;
TfInit();
}
TestFrameworkCommon::~TestFrameworkCommon() {
if (mFilterTable) {
delete mFilterTable;
mFilterTable = NULL;
}
}
int TestFrameworkCommon::TfWrite(const char *str) {
int ret = 0;
if (mTfMarkerFd < 0) {
return -1;
}
if (str) {
ret = ::write(mTfMarkerFd, str, strlen(str));
}
return ret;
}
int TestFrameworkCommon::TfWrite(int evType, const char *evArg) {
struct iovec iov[2];
char evid[TF_EVENT_ID_SIZE_MAX];
int num, ret = 0;
if (mTfMarkerFd < 0) {
return -1;
}
switch(evType)
{
case TF_EVENT_START:
strlcpy(evid, "uspace_start", sizeof(evid));
break;
case TF_EVENT_STOP:
strlcpy(evid, "uspace_stop", sizeof(evid));
break;
case TF_EVENT:
strlcpy(evid, "uspace_single", sizeof(evid));
break;
case TF_EVENT_JAVA_START:
strlcpy(evid, "uspace_start", sizeof(evid));
break;
case TF_EVENT_JAVA_STOP:
strlcpy(evid, "uspace_stop", sizeof(evid));
break;
case TF_EVENT_JAVA:
strlcpy(evid, "uspace_single", sizeof(evid));
break;
default:
evid[0] = '\0';
}
num = 1;
//iov[0].iov_base = (void *) evArg;
//iov[0].iov_len = strlen(evArg)+1;
//iov[1].iov_base = (void *) evid;
//iov[1].iov_len = strlen(evid)+1;
if (evArg) {
ret = ::write(mTfMarkerFd, evArg, strlen(evArg));
//ret = writev(mTfMarkerFd, iov, num);
}
return ret;
}
int TestFrameworkCommon::TfInit() {
if (NULL == mFilterTable) {
mFilterTable = new TfHashTable(256, (HashFreeFunc) free,
(HashCompareFunc) strcmp,
(HashCompute) TfHashTable::ComputeUtf8Hash);
}
mIsService = !(0 == TfGetPropertyService());
mProbeFreq = TfGetPropertyProbeFreq();
return 0;
}
int TestFrameworkCommon::TfTracersInit() {
char *debugfs;
char path[MAX_PATH];
if (mTfInitDone) {
return 0;
}
//open tracing/tracing_on
if (mTfTracerFd < 0) {
TfOpenTracer();
}
//open tracing/trace_marker
if (mTfMarkerFd < 0) {
TfOpenMarker();
}
if (mTfTracerFd >= 0 && mTfMarkerFd >= 0) {
mTfInitDone = true;
}
return 0;
}
void TestFrameworkCommon::TfEnable() {
if (mTfTracerFd >= 0) {
lseek(mTfTracerFd, 0, SEEK_SET);
write(mTfTracerFd, "1", 1);
TF_LOGD("TfEnable");
}
else {
TF_LOGE("tf_turnon: could not open trace file");
}
}
void TestFrameworkCommon::TfDisable() {
if (mTfTracerFd >= 0) {
lseek(mTfTracerFd, 0, SEEK_SET);
write(mTfTracerFd, "0", 1);
TF_LOGD("TfDisable");
}
else {
TF_LOGE("tf_turnoff: could not open trace file");
}
}
void TestFrameworkCommon::TfUpdate(int logtype, int updatetype) {
if (updatetype != SYNC || !mTurnOnByApi) {
mLogType = logtype;
if (logtype < 0) {
mLogType = TfGetPropertyLogType();
}
}
int pf = TfGetPropertyProbeFreq();
if ((1 != pf) || (0 != mProbeFreq)) {
mProbeFreq = pf;
}
if (updatetype != SYNC && mTurnOnByApi) {
//start, stop commands can close api turned on logging
mTurnOnByApi = false;
}
if (mLogType != TF_DISABLE) {
mEventType = TfGetPropertyEventType();
TfGetPropertyTraceGatesInterval(mOpenInterval, mClosedInterval);
}
if (((mLogType == TF_ALL) || (mLogType == TF_TESTFRAMEWORK)) &&
!TfIsTracerEnabled()) {
TfEnable();
}
else if (((mLogType != TF_ALL) && (mLogType != TF_TESTFRAMEWORK)) &&
TfIsTracerEnabled()) {
TfDisable();
}
return;
}
int TestFrameworkCommon::TfGetPropertyService() {
char property[PROPERTY_VALUE_MAX];
int service = 0;
if (property_get("debug.tf.service", property, "0") > 0) {
service = atoi(property);
}
return service;
}
int TestFrameworkCommon::TfGetPropertyProbeFreq() {
char property[PROPERTY_VALUE_MAX];
int period = 0;
if (property_get("debug.tf.probeperiod", property, "1000") > 0) {
period = atoi(property);
}
return period;
}
//usage:
//setprop debug.testframework.enable logcat - logs to logcat only
//setprop debug.testframework.enable tf - logs to testframework
//setprop debug.testframework.enable testframework - logs to tf only
//setprop debug.testframework.enable all - logs to logcat and tf
//setprop debug.testframework.enable * - logs to logcat and tf
int TestFrameworkCommon::TfGetPropertyLogType() {
int status = TF_DISABLE;
char property[PROPERTY_VALUE_MAX];
if (property_get("debug.tf.enable", property, "0") > 0) {
switch(property[0]) {
case 'L':
case 'l':
status = TF_LOGCAT;
break;
case 'T':
case 't':
case '1':
status = TF_TESTFRAMEWORK;
break;
case '*':
case 'a':
case 'A':
status = TF_ALL;
break;
case 'd':
case 'D':
case '0':
default:
status = TF_DISABLE;
break;
}
}
return status;
}
int TestFrameworkCommon::TfGetPropertyFilters() {
char property[PROPERTY_VALUE_MAX];
char key[PROPERTY_VALUE_MAX];
char *tptr = NULL, *sptr = NULL, *eptr = NULL;
int len = 0;
if (property_get("debug.tf.filters", property, "*:*") > 0) {
sptr = property;
len = strlen(property);
eptr = sptr + len;
if (mFiltersLen != len || strcmp(mFilters, property)) {
strlcpy(mFilters, property, sizeof(mFilters));
mFiltersLen = len;
//clear table
if (mFilterTable) {
mFilterTable->TfHashTableClear();
}
do {
tptr = strchr(sptr, '%');
if (!tptr && sptr < eptr) {
tptr = eptr;
}
if(tptr) {
strncpy(key, sptr, tptr-sptr);
key[tptr-sptr] = '\0';
//don't bother about duplicate entries, hashtable
//will take care of them
TfAddFilterToTable(key);
sptr = tptr+1;
}
} while(tptr && sptr < eptr);
}
}
return 0;
}
int TestFrameworkCommon::TfGetPropertyEventType() {
int status = TF_EVENT | TF_EVENT_STOP;
char property[PROPERTY_VALUE_MAX];
if (property_get("debug.tf.eventtype", property, "255") > 0) {
status = atoi(property);
}
return status;
}
void TestFrameworkCommon::TfGetPropertyTraceGatesInterval(int &openInterval,
int &closeInterval) {
int interval = 1;
char property[PROPERTY_VALUE_MAX];
char *tptr = NULL;
if (property_get("debug.tf.interval", property, "0 0") > 0) {
tptr = strchr(property, ' ');
if (tptr) {
mClosedInterval = atoi(tptr+1);
*tptr = '\0';
mOpenInterval = atoi(property);
}
}
return;
}
bool TestFrameworkCommon::TfIsTracerEnabled() {
bool ret = false;
int n = 0;
char buf[1];
buf[0] = 0;
if (mTfTracerFd) {
lseek(mTfTracerFd, 0, SEEK_SET);
n = read(mTfTracerFd, buf, 1);
if ((n > 0) && (buf[0] == '1')) {
ret = true;
}
}
return ret;
}
bool TestFrameworkCommon::TfIsValid() {
return mTfInitDone;
}
bool TestFrameworkCommon::TfIsServiceRunning() {
return mIsService;
}
int TestFrameworkCommon::TfGetLoggingType() {
return mLogType;
}
const char *TestFrameworkCommon::TfFindDebugfs(void) {
char type[100];
FILE *fp;
if (mDebugfsFound)
return mDebugfs;
if ((fp = fopen("/proc/mounts","r")) == NULL)
return NULL;
while (fscanf(fp, "%*s %"
STR(MAX_PATH)
"s %99s %*s %*d %*d\n",
mDebugfs, type) == 2) {
if (strcmp(type, "debugfs") == 0)
break;
}
fclose(fp);
if (strcmp(type, "debugfs") != 0)
return NULL;
mDebugfsFound = 1;
return mDebugfs;
}
int TestFrameworkCommon::TfMountDebugfs(void) {
int ret = 0;
ret = ::mount("nodev", "/sys/kernel/debug", "debugfs", MS_MGC_VAL, "");
return ret;
}
int TestFrameworkCommon::TfOpenMarker() {
char *debugfs;
char path[MAX_PATH];
//TfMountDebugfs();
debugfs = (char *) TfFindDebugfs();
if (debugfs) {
strlcpy(path, debugfs, sizeof(path));
strlcat(path, "/tracing/trace_marker", sizeof(path));
errno = 0;
mTfMarkerFd = open(path, O_WRONLY);
TF_LOGD("tf_marker opened=%d", mTfMarkerFd);
}
return mTfMarkerFd;
}
int TestFrameworkCommon::TfOpenTracer() {
char *debugfs;
char path[MAX_PATH];
//TfMountDebugfs();
debugfs = (char *) TfFindDebugfs();
if (debugfs) {
strlcpy(path, debugfs, sizeof(path));
strlcat(path, "/tracing/tracing_on", sizeof(path));
errno = 0;
mTfTracerFd = open(path, O_RDWR);
TF_LOGD("tf_tracer opened=%d", mTfTracerFd);
}
return mTfTracerFd;
}
void TestFrameworkCommon::TfAddFilterToTable(const char *key) {
if (mFilterTable) {
mFilterTable->TfHashTableLookup((void *)key, strlen(key)+1, true);
}
}
bool TestFrameworkCommon::TfSearchFilterInTable(const char *eventgrp,
const char *eventid) {
char key[128];
const char* ent = NULL;
if (mFilterTable) {
snprintf(key, 128, "%s:%s", eventgrp, eventid);
ent = (const char *) mFilterTable->TfHashTableLookup(key);
if (ent == NULL) {
snprintf(key, 128, "%s:*", eventgrp);
ent = (const char *) mFilterTable->TfHashTableLookup(key);
if (ent == NULL) {
strlcpy(key, "*:*", 128);
ent = (const char *) mFilterTable->TfHashTableLookup(key);
if (ent == NULL) {
strlcpy(key, "*.*", 128);
ent = (const char *) mFilterTable->TfHashTableLookup(key);
}
}
}
}
return (ent != NULL);
}
bool TestFrameworkCommon::IsTraceGatesOpen() {
static bool bClosed = false;
int time_now = ns2ms(systemTime());
mElapsedTime += (time_now - mLastRecordedTime);
mLastRecordedTime = time_now;
bool timeout = true;
if(bClosed) {
timeout = (!mClosedInterval || (mElapsedTime >= mClosedInterval));
if (timeout) {
bClosed = false;
mElapsedTime = 0;
}
}
else {
bClosed = (mOpenInterval && (mElapsedTime >= mOpenInterval));
if (bClosed) {
mElapsedTime = 0;
}
}
return timeout;
}