/*
* 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <time.h>
#include <log/log.h>
#define ALEN(a) (sizeof(a) / sizeof((a)[0])) // Array length
#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 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);
}
}