| /* |
| * 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) { |
| LOG(LOG_ERROR, logCatTag, "%s", line); |
| } else { |
| LOG(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); |
| } |
| } |