/* * 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 <string.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); } }