433 lines
11 KiB
C
433 lines
11 KiB
C
/*
|
|
* Copyright (C) 2010 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.
|
|
*
|
|
*/
|
|
|
|
#include <testUtil.h>
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <math.h>
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
|
|
#include <sys/time.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <cutils/log.h>
|
|
|
|
#define ALEN(a) (sizeof(a) / sizeof(a [0])) // Array length
|
|
typedef unsigned int bool_t;
|
|
#define true (0 == 0)
|
|
#define false (!true)
|
|
|
|
#define MAXSTR 200
|
|
|
|
static const char *logCatTag;
|
|
static const unsigned int uSecsPerSec = 1000000;
|
|
static const unsigned int nSecsPerSec = 1000000000;
|
|
|
|
// struct timespec to double
|
|
double ts2double(const struct timespec *val)
|
|
{
|
|
double rv;
|
|
|
|
rv = val->tv_sec;
|
|
rv += (double) val->tv_nsec / nSecsPerSec;
|
|
|
|
return rv;
|
|
}
|
|
|
|
// struct timeval to double
|
|
double tv2double(const struct timeval *val)
|
|
{
|
|
double rv;
|
|
|
|
rv = val->tv_sec;
|
|
rv += (double) val->tv_usec / uSecsPerSec;
|
|
|
|
return rv;
|
|
}
|
|
|
|
// double to struct timespec
|
|
struct timespec double2ts(double amt)
|
|
{
|
|
struct timespec rv;
|
|
|
|
rv.tv_sec = floor(amt);
|
|
rv.tv_nsec = (amt - rv.tv_sec) * nSecsPerSec;
|
|
// TODO: Handle cases where amt is negative
|
|
while ((unsigned) rv.tv_nsec >= nSecsPerSec) {
|
|
rv.tv_nsec -= nSecsPerSec;
|
|
rv.tv_sec++;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
// double to struct timeval
|
|
struct timeval double2tv(double amt)
|
|
{
|
|
struct timeval rv;
|
|
|
|
rv.tv_sec = floor(amt);
|
|
rv.tv_usec = (amt - rv.tv_sec) * uSecsPerSec;
|
|
// TODO: Handle cases where amt is negative
|
|
while ((unsigned) rv.tv_usec >= uSecsPerSec) {
|
|
rv.tv_usec -= uSecsPerSec;
|
|
rv.tv_sec++;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
// Delta (difference) between two struct timespec.
|
|
// It is expected that the time given by the structure pointed to by
|
|
// second, is later than the time pointed to by first.
|
|
struct timespec tsDelta(const struct timespec *first,
|
|
const struct timespec *second)
|
|
{
|
|
struct timespec rv;
|
|
|
|
assert(first != NULL);
|
|
assert(second != NULL);
|
|
assert(first->tv_nsec >= 0 && first->tv_nsec < nSecsPerSec);
|
|
assert(second->tv_nsec >= 0 && second->tv_nsec < nSecsPerSec);
|
|
rv.tv_sec = second->tv_sec - first->tv_sec;
|
|
if (second->tv_nsec >= first->tv_nsec) {
|
|
rv.tv_nsec = second->tv_nsec - first->tv_nsec;
|
|
} else {
|
|
rv.tv_nsec = (second->tv_nsec + nSecsPerSec) - first->tv_nsec;
|
|
rv.tv_sec--;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
// Delta (difference) between two struct timeval.
|
|
// It is expected that the time given by the structure pointed to by
|
|
// second, is later than the time pointed to by first.
|
|
struct timeval tvDelta(const struct timeval *first,
|
|
const struct timeval *second)
|
|
{
|
|
struct timeval rv;
|
|
|
|
assert(first != NULL);
|
|
assert(second != NULL);
|
|
assert(first->tv_usec >= 0 && first->tv_usec < uSecsPerSec);
|
|
assert(second->tv_usec >= 0 && second->tv_usec < uSecsPerSec);
|
|
rv.tv_sec = second->tv_sec - first->tv_sec;
|
|
if (second->tv_usec >= first->tv_usec) {
|
|
rv.tv_usec = second->tv_usec - first->tv_usec;
|
|
} else {
|
|
rv.tv_usec = (second->tv_usec + uSecsPerSec) - first->tv_usec;
|
|
rv.tv_sec--;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
void testPrint(FILE *stream, const char *fmt, ...)
|
|
{
|
|
char line[MAXSTR];
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
vsnprintf(line, sizeof(line), fmt, args);
|
|
if (stream == stderr) {
|
|
ALOG(LOG_ERROR, logCatTag, "%s", line);
|
|
} else {
|
|
ALOG(LOG_INFO, logCatTag, "%s", line);
|
|
}
|
|
vfprintf(stream, fmt, args);
|
|
fputc('\n', stream);
|
|
}
|
|
|
|
// Set tag used while logging to the logcat error interface
|
|
void testSetLogCatTag(const char *tag)
|
|
{
|
|
logCatTag = tag;
|
|
}
|
|
|
|
// Obtain pointer to current log to logcat error interface tag
|
|
const char * testGetLogCatTag(void)
|
|
{
|
|
return logCatTag;
|
|
}
|
|
|
|
/*
|
|
* Random
|
|
*
|
|
* Returns a pseudo random number in the range [0:2^32-1].
|
|
*
|
|
* Precondition: srand48() called to set the seed of
|
|
* the pseudo random number generator.
|
|
*/
|
|
uint32_t testRand(void)
|
|
{
|
|
uint32_t val;
|
|
|
|
// Use lrand48() to obtain 31 bits worth
|
|
// of randomness.
|
|
val = lrand48();
|
|
|
|
// Make an additional lrand48() call and merge
|
|
// the randomness into the most significant bits.
|
|
val ^= lrand48() << 1;
|
|
|
|
return val;
|
|
}
|
|
|
|
/*
|
|
* Random Modulus
|
|
*
|
|
* Pseudo randomly returns unsigned integer in the range [0, mod).
|
|
*
|
|
* Precondition: srand48() called to set the seed of
|
|
* the pseudo random number generator.
|
|
*/
|
|
uint32_t testRandMod(uint32_t mod)
|
|
{
|
|
// Obtain the random value
|
|
// Use lrand48() when it would produce a sufficient
|
|
// number of random bits, otherwise use testRand().
|
|
const uint32_t lrand48maxVal = ((uint32_t) 1 << 31) - 1;
|
|
uint32_t val = (mod <= lrand48maxVal) ? (uint32_t) lrand48() : testRand();
|
|
|
|
/*
|
|
* The contents of individual bytes tend to be less than random
|
|
* across different seeds. For example, srand48(x) and
|
|
* srand48(x + n * 4) cause lrand48() to return the same sequence of
|
|
* least significant bits. For small mod values this can produce
|
|
* noticably non-random sequnces. For mod values of less than 2
|
|
* bytes, will use the randomness from all the bytes.
|
|
*/
|
|
if (mod <= 0x10000) {
|
|
val = (val & 0xffff) ^ (val >> 16);
|
|
|
|
// If mod less than a byte, can further combine down to
|
|
// a single byte.
|
|
if (mod <= 0x100) {
|
|
val = (val & 0xff) ^ (val >> 8);
|
|
}
|
|
}
|
|
|
|
return val % mod;
|
|
}
|
|
|
|
/*
|
|
* Random Boolean
|
|
*
|
|
* Pseudo randomly returns 0 (false) or 1 (true).
|
|
*
|
|
* Precondition: srand48() called to set the seed of
|
|
* the pseudo random number generator.
|
|
*/
|
|
int testRandBool(void)
|
|
{
|
|
return (testRandMod(2));
|
|
}
|
|
|
|
/*
|
|
* Random Fraction
|
|
*
|
|
* Pseudo randomly return a value in the range [0.0, 1.0).
|
|
*
|
|
* Precondition: srand48() called to set the seed of
|
|
* the pseudo random number generator.
|
|
*/
|
|
double testRandFract(void)
|
|
{
|
|
return drand48();
|
|
}
|
|
|
|
// Delays for the number of seconds specified by amt or a greater amount.
|
|
// The amt variable is of type float and thus non-integer amounts
|
|
// of time can be specified. This function automatically handles cases
|
|
// where nanosleep(2) returns early due to reception of a signal.
|
|
void testDelay(float amt)
|
|
{
|
|
struct timespec start, current, delta;
|
|
struct timespec remaining;
|
|
|
|
// Get the time at which we started
|
|
clock_gettime(CLOCK_MONOTONIC, &start);
|
|
|
|
do {
|
|
// Get current time
|
|
clock_gettime(CLOCK_MONOTONIC, ¤t);
|
|
|
|
// How much time is left
|
|
delta = tsDelta(&start, ¤t);
|
|
if (ts2double(&delta) > amt) { break; }
|
|
|
|
// Request to sleep for the remaining time
|
|
remaining = double2ts(amt - ts2double(&delta));
|
|
(void) nanosleep(&remaining, NULL);
|
|
} while (true);
|
|
}
|
|
|
|
// Delay spins for the number of seconds specified by amt or a greater
|
|
// amount. The amt variable is of type float and thus non-integer amounts
|
|
// of time can be specified. Differs from testDelay() in that
|
|
// testDelaySpin() performs a spin loop, instead of using nanosleep().
|
|
void testDelaySpin(float amt)
|
|
{
|
|
struct timespec start, current, delta;
|
|
|
|
// Get the time at which we started
|
|
clock_gettime(CLOCK_MONOTONIC, &start);
|
|
|
|
do {
|
|
// Get current time
|
|
clock_gettime(CLOCK_MONOTONIC, ¤t);
|
|
|
|
// How much time is left
|
|
delta = tsDelta(&start, ¤t);
|
|
if (ts2double(&delta) > amt) { break; }
|
|
} while (true);
|
|
}
|
|
|
|
/*
|
|
* Hex Dump
|
|
*
|
|
* Displays in hex the contents of the memory starting at the location
|
|
* pointed to by buf, for the number of bytes given by size.
|
|
* Each line of output is indented by a number of spaces that
|
|
* can be set by calling xDumpSetIndent(). It is also possible
|
|
* to offset the displayed address by an amount set by calling
|
|
* xDumpSetOffset.
|
|
*/
|
|
static uint8_t xDumpIndent;
|
|
static uint64_t xDumpOffset;
|
|
void
|
|
testXDump(const void *buf, size_t size)
|
|
{
|
|
const unsigned int bytesPerLine = 16;
|
|
int rv;
|
|
char line[MAXSTR];
|
|
const unsigned char *ptr = buf, *start = buf;
|
|
size_t num = size;
|
|
char *linep = line;
|
|
|
|
while (num) {
|
|
if (((ptr - start) % bytesPerLine) == 0) {
|
|
if (linep != line) {
|
|
testPrintE("%s", line);
|
|
}
|
|
linep = line;
|
|
rv = snprintf(linep, ALEN(line) - (linep - line),
|
|
"%*s%06llx:", xDumpIndent, "",
|
|
(long long) (ptr - start) + xDumpOffset);
|
|
linep += rv;
|
|
}
|
|
|
|
// Check that there is at least room for 4
|
|
// more characters. The 4 characters being
|
|
// a space, 2 hex digits and the terminating
|
|
// '\0'.
|
|
assert((ALEN(line) - 4) >= (linep - line));
|
|
rv = snprintf(linep, ALEN(line) - (linep - line),
|
|
" %02x", *ptr++);
|
|
linep += rv;
|
|
num--;
|
|
}
|
|
if (linep != line) {
|
|
testPrintE("%s", line);
|
|
}
|
|
}
|
|
|
|
// Set an indent of spaces for each line of hex dump output
|
|
void
|
|
testXDumpSetIndent(uint8_t indent)
|
|
{
|
|
xDumpIndent = indent;
|
|
}
|
|
|
|
// Obtain the current hex dump indent amount
|
|
uint8_t
|
|
testXDumpGetIndent(void)
|
|
{
|
|
return xDumpIndent;
|
|
}
|
|
|
|
// Set the hex dump address offset amount
|
|
void
|
|
testXDumpSetOffset(uint64_t offset)
|
|
{
|
|
xDumpOffset = offset;
|
|
}
|
|
|
|
// Get the current hex dump address offset amount
|
|
uint64_t
|
|
testXDumpGetOffset(void)
|
|
{
|
|
return xDumpOffset;
|
|
}
|
|
|
|
/*
|
|
* Execute Command
|
|
*
|
|
* Executes the command pointed to by cmd. Output from the
|
|
* executed command is captured and sent to LogCat Info. Once
|
|
* the command has finished execution, it's exit status is captured
|
|
* and checked for an exit status of zero. Any other exit status
|
|
* causes diagnostic information to be printed and an immediate
|
|
* testcase failure.
|
|
*/
|
|
void testExecCmd(const char *cmd)
|
|
{
|
|
FILE *fp;
|
|
int rv;
|
|
int status;
|
|
char str[MAXSTR];
|
|
|
|
// Display command to be executed
|
|
testPrintI("cmd: %s", cmd);
|
|
|
|
// Execute the command
|
|
fflush(stdout);
|
|
if ((fp = popen(cmd, "r")) == NULL) {
|
|
testPrintE("execCmd popen failed, errno: %i", errno);
|
|
exit(100);
|
|
}
|
|
|
|
// Obtain and display each line of output from the executed command
|
|
while (fgets(str, sizeof(str), fp) != NULL) {
|
|
if ((strlen(str) > 1) && (str[strlen(str) - 1] == '\n')) {
|
|
str[strlen(str) - 1] = '\0';
|
|
}
|
|
testPrintI(" out: %s", str);
|
|
}
|
|
|
|
// Obtain and check return status of executed command.
|
|
// Fail on non-zero exit status
|
|
status = pclose(fp);
|
|
if (!(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) {
|
|
testPrintE("Unexpected command failure");
|
|
testPrintE(" status: %#x", status);
|
|
if (WIFEXITED(status)) {
|
|
testPrintE("WEXITSTATUS: %i", WEXITSTATUS(status));
|
|
}
|
|
if (WIFSIGNALED(status)) {
|
|
testPrintE("WTERMSIG: %i", WTERMSIG(status));
|
|
}
|
|
exit(101);
|
|
}
|
|
}
|