Home | History | Annotate | Download | only in vm
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /*
     18  * Miscellaneous utility functions.
     19  */
     20 #include "Dalvik.h"
     21 
     22 #include <stdlib.h>
     23 #include <stddef.h>
     24 #include <string.h>
     25 #include <strings.h>
     26 #include <ctype.h>
     27 #include <time.h>
     28 #include <sys/time.h>
     29 #include <fcntl.h>
     30 #include <cutils/ashmem.h>
     31 #include <sys/mman.h>
     32 
     33 /*
     34  * Print a hex dump in this format:
     35  *
     36 01234567: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff  0123456789abcdef\n
     37  *
     38  * If "mode" is kHexDumpLocal, we start at offset zero, and show a full
     39  * 16 bytes on the first line.  If it's kHexDumpMem, we make this look
     40  * like a memory dump, using the actual address, outputting a partial line
     41  * if "vaddr" isn't aligned on a 16-byte boundary.
     42  *
     43  * "priority" and "tag" determine the values passed to the log calls.
     44  *
     45  * Does not use printf() or other string-formatting calls.
     46  */
     47 void dvmPrintHexDumpEx(int priority, const char* tag, const void* vaddr,
     48     size_t length, HexDumpMode mode)
     49 {
     50     static const char gHexDigit[] = "0123456789abcdef";
     51     const unsigned char* addr = (const unsigned char*)vaddr;
     52     char out[77];           /* exact fit */
     53     unsigned int offset;    /* offset to show while printing */
     54     char* hex;
     55     char* asc;
     56     int gap;
     57     //int trickle = 0;
     58 
     59     if (mode == kHexDumpLocal)
     60         offset = 0;
     61     else
     62         offset = (int) addr;
     63 
     64     memset(out, ' ', sizeof(out)-1);
     65     out[8] = ':';
     66     out[sizeof(out)-2] = '\n';
     67     out[sizeof(out)-1] = '\0';
     68 
     69     gap = (int) offset & 0x0f;
     70     while (length) {
     71         unsigned int lineOffset = offset & ~0x0f;
     72         int i, count;
     73 
     74         hex = out;
     75         asc = out + 59;
     76 
     77         for (i = 0; i < 8; i++) {
     78             *hex++ = gHexDigit[lineOffset >> 28];
     79             lineOffset <<= 4;
     80         }
     81         hex++;
     82         hex++;
     83 
     84         count = ((int)length > 16-gap) ? 16-gap : (int)length; /* cap length */
     85         assert(count != 0);
     86         assert(count+gap <= 16);
     87 
     88         if (gap) {
     89             /* only on first line */
     90             hex += gap * 3;
     91             asc += gap;
     92         }
     93 
     94         for (i = gap ; i < count+gap; i++) {
     95             *hex++ = gHexDigit[*addr >> 4];
     96             *hex++ = gHexDigit[*addr & 0x0f];
     97             hex++;
     98             if (*addr >= 0x20 && *addr < 0x7f /*isprint(*addr)*/)
     99                 *asc++ = *addr;
    100             else
    101                 *asc++ = '.';
    102             addr++;
    103         }
    104         for ( ; i < 16; i++) {
    105             /* erase extra stuff; only happens on last line */
    106             *hex++ = ' ';
    107             *hex++ = ' ';
    108             hex++;
    109             *asc++ = ' ';
    110         }
    111 
    112         LOG_PRI(priority, tag, "%s", out);
    113 #if 0 //def HAVE_ANDROID_OS
    114         /*
    115          * We can overrun logcat easily by writing at full speed.  On the
    116          * other hand, we can make Eclipse time out if we're showing
    117          * packet dumps while debugging JDWP.
    118          */
    119         {
    120             if (trickle++ == 8) {
    121                 trickle = 0;
    122                 usleep(20000);
    123             }
    124         }
    125 #endif
    126 
    127         gap = 0;
    128         length -= count;
    129         offset += count;
    130     }
    131 }
    132 
    133 
    134 /*
    135  * Fill out a DebugOutputTarget, suitable for printing to the log.
    136  */
    137 void dvmCreateLogOutputTarget(DebugOutputTarget* target, int priority,
    138     const char* tag)
    139 {
    140     assert(target != NULL);
    141     assert(tag != NULL);
    142 
    143     target->which = kDebugTargetLog;
    144     target->data.log.priority = priority;
    145     target->data.log.tag = tag;
    146 }
    147 
    148 /*
    149  * Fill out a DebugOutputTarget suitable for printing to a file pointer.
    150  */
    151 void dvmCreateFileOutputTarget(DebugOutputTarget* target, FILE* fp)
    152 {
    153     assert(target != NULL);
    154     assert(fp != NULL);
    155 
    156     target->which = kDebugTargetFile;
    157     target->data.file.fp = fp;
    158 }
    159 
    160 /*
    161  * Free "target" and any associated data.
    162  */
    163 void dvmFreeOutputTarget(DebugOutputTarget* target)
    164 {
    165     free(target);
    166 }
    167 
    168 /*
    169  * Print a debug message, to either a file or the log.
    170  */
    171 void dvmPrintDebugMessage(const DebugOutputTarget* target, const char* format,
    172     ...)
    173 {
    174     va_list args;
    175 
    176     va_start(args, format);
    177 
    178     switch (target->which) {
    179     case kDebugTargetLog:
    180         LOG_PRI_VA(target->data.log.priority, target->data.log.tag,
    181             format, args);
    182         break;
    183     case kDebugTargetFile:
    184         vfprintf(target->data.file.fp, format, args);
    185         break;
    186     default:
    187         LOGE("unexpected 'which' %d", target->which);
    188         break;
    189     }
    190 
    191     va_end(args);
    192 }
    193 
    194 
    195 /*
    196  * Return a newly-allocated string in which all occurrences of '.' have
    197  * been changed to '/'.  If we find a '/' in the original string, NULL
    198  * is returned to avoid ambiguity.
    199  */
    200 char* dvmDotToSlash(const char* str)
    201 {
    202     char* newStr = strdup(str);
    203     char* cp = newStr;
    204 
    205     if (newStr == NULL)
    206         return NULL;
    207 
    208     while (*cp != '\0') {
    209         if (*cp == '/') {
    210             assert(false);
    211             return NULL;
    212         }
    213         if (*cp == '.')
    214             *cp = '/';
    215         cp++;
    216     }
    217 
    218     return newStr;
    219 }
    220 
    221 std::string dvmHumanReadableDescriptor(const char* descriptor) {
    222     // Count the number of '['s to get the dimensionality.
    223     const char* c = descriptor;
    224     size_t dim = 0;
    225     while (*c == '[') {
    226         dim++;
    227         c++;
    228     }
    229 
    230     // Reference or primitive?
    231     if (*c == 'L') {
    232         // "[[La/b/C;" -> "a.b.C[][]".
    233         c++; // Skip the 'L'.
    234     } else {
    235         // "[[B" -> "byte[][]".
    236         // To make life easier, we make primitives look like unqualified
    237         // reference types.
    238         switch (*c) {
    239         case 'B': c = "byte;"; break;
    240         case 'C': c = "char;"; break;
    241         case 'D': c = "double;"; break;
    242         case 'F': c = "float;"; break;
    243         case 'I': c = "int;"; break;
    244         case 'J': c = "long;"; break;
    245         case 'S': c = "short;"; break;
    246         case 'Z': c = "boolean;"; break;
    247         default: return descriptor;
    248         }
    249     }
    250 
    251     // At this point, 'c' is a string of the form "fully/qualified/Type;"
    252     // or "primitive;". Rewrite the type with '.' instead of '/':
    253     std::string result;
    254     const char* p = c;
    255     while (*p != ';') {
    256         char ch = *p++;
    257         if (ch == '/') {
    258           ch = '.';
    259         }
    260         result.push_back(ch);
    261     }
    262     // ...and replace the semicolon with 'dim' "[]" pairs:
    263     while (dim--) {
    264         result += "[]";
    265     }
    266     return result;
    267 }
    268 
    269 std::string dvmHumanReadableType(const Object* obj)
    270 {
    271     if (obj == NULL) {
    272         return "null";
    273     }
    274     if (obj->clazz == NULL) {
    275         /* should only be possible right after a plain dvmMalloc() */
    276         return "(raw)";
    277     }
    278     std::string result(dvmHumanReadableDescriptor(obj->clazz->descriptor));
    279     if (dvmIsClassObject(obj)) {
    280         const ClassObject* clazz = reinterpret_cast<const ClassObject*>(obj);
    281         result += "<" + dvmHumanReadableDescriptor(clazz->descriptor) + ">";
    282     }
    283     return result;
    284 }
    285 
    286 std::string dvmHumanReadableField(const Field* field)
    287 {
    288     if (field == NULL) {
    289         return "(null)";
    290     }
    291     std::string result(dvmHumanReadableDescriptor(field->clazz->descriptor));
    292     result += '.';
    293     result += field->name;
    294     return result;
    295 }
    296 
    297 std::string dvmHumanReadableMethod(const Method* method, bool withSignature)
    298 {
    299     if (method == NULL) {
    300         return "(null)";
    301     }
    302     std::string result(dvmHumanReadableDescriptor(method->clazz->descriptor));
    303     result += '.';
    304     result += method->name;
    305     if (withSignature) {
    306         // TODO: the types in this aren't human readable!
    307         char* signature = dexProtoCopyMethodDescriptor(&method->prototype);
    308         result += signature;
    309         free(signature);
    310     }
    311     return result;
    312 }
    313 
    314 /*
    315  * Return a newly-allocated string for the "dot version" of the class
    316  * name for the given type descriptor. That is, The initial "L" and
    317  * final ";" (if any) have been removed and all occurrences of '/'
    318  * have been changed to '.'.
    319  *
    320  * "Dot version" names are used in the class loading machinery.
    321  * See also dvmHumanReadableDescriptor.
    322  */
    323 char* dvmDescriptorToDot(const char* str)
    324 {
    325     size_t at = strlen(str);
    326     char* newStr;
    327 
    328     if ((at >= 2) && (str[0] == 'L') && (str[at - 1] == ';')) {
    329         at -= 2; /* Two fewer chars to copy. */
    330         str++; /* Skip the 'L'. */
    331     }
    332 
    333     newStr = (char*)malloc(at + 1); /* Add one for the '\0'. */
    334     if (newStr == NULL)
    335         return NULL;
    336 
    337     newStr[at] = '\0';
    338 
    339     while (at > 0) {
    340         at--;
    341         newStr[at] = (str[at] == '/') ? '.' : str[at];
    342     }
    343 
    344     return newStr;
    345 }
    346 
    347 /*
    348  * Return a newly-allocated string for the type descriptor
    349  * corresponding to the "dot version" of the given class name. That
    350  * is, non-array names are surrounded by "L" and ";", and all
    351  * occurrences of '.' have been changed to '/'.
    352  *
    353  * "Dot version" names are used in the class loading machinery.
    354  */
    355 char* dvmDotToDescriptor(const char* str)
    356 {
    357     size_t length = strlen(str);
    358     int wrapElSemi = 0;
    359     char* newStr;
    360     char* at;
    361 
    362     if (str[0] != '[') {
    363         length += 2; /* for "L" and ";" */
    364         wrapElSemi = 1;
    365     }
    366 
    367     newStr = at = (char*)malloc(length + 1); /* + 1 for the '\0' */
    368 
    369     if (newStr == NULL) {
    370         return NULL;
    371     }
    372 
    373     if (wrapElSemi) {
    374         *(at++) = 'L';
    375     }
    376 
    377     while (*str) {
    378         char c = *(str++);
    379         if (c == '.') {
    380             c = '/';
    381         }
    382         *(at++) = c;
    383     }
    384 
    385     if (wrapElSemi) {
    386         *(at++) = ';';
    387     }
    388 
    389     *at = '\0';
    390     return newStr;
    391 }
    392 
    393 /*
    394  * Return a newly-allocated string for the internal-form class name for
    395  * the given type descriptor. That is, the initial "L" and final ";" (if
    396  * any) have been removed.
    397  */
    398 char* dvmDescriptorToName(const char* str)
    399 {
    400     if (str[0] == 'L') {
    401         size_t length = strlen(str) - 1;
    402         char* newStr = (char*)malloc(length);
    403 
    404         if (newStr == NULL) {
    405             return NULL;
    406         }
    407 
    408         strlcpy(newStr, str + 1, length);
    409         return newStr;
    410     }
    411 
    412     return strdup(str);
    413 }
    414 
    415 /*
    416  * Return a newly-allocated string for the type descriptor for the given
    417  * internal-form class name. That is, a non-array class name will get
    418  * surrounded by "L" and ";", while array names are left as-is.
    419  */
    420 char* dvmNameToDescriptor(const char* str)
    421 {
    422     if (str[0] != '[') {
    423         size_t length = strlen(str);
    424         char* descriptor = (char*)malloc(length + 3);
    425 
    426         if (descriptor == NULL) {
    427             return NULL;
    428         }
    429 
    430         descriptor[0] = 'L';
    431         strcpy(descriptor + 1, str);
    432         descriptor[length + 1] = ';';
    433         descriptor[length + 2] = '\0';
    434 
    435         return descriptor;
    436     }
    437 
    438     return strdup(str);
    439 }
    440 
    441 /*
    442  * Get a notion of the current time, in nanoseconds.  This is meant for
    443  * computing durations (e.g. "operation X took 52nsec"), so the result
    444  * should not be used to get the current date/time.
    445  */
    446 u8 dvmGetRelativeTimeNsec()
    447 {
    448 #ifdef HAVE_POSIX_CLOCKS
    449     struct timespec now;
    450     clock_gettime(CLOCK_MONOTONIC, &now);
    451     return (u8)now.tv_sec*1000000000LL + now.tv_nsec;
    452 #else
    453     struct timeval now;
    454     gettimeofday(&now, NULL);
    455     return (u8)now.tv_sec*1000000000LL + now.tv_usec * 1000LL;
    456 #endif
    457 }
    458 
    459 /*
    460  * Get the per-thread CPU time, in nanoseconds.
    461  *
    462  * Only useful for time deltas.
    463  */
    464 u8 dvmGetThreadCpuTimeNsec()
    465 {
    466 #ifdef HAVE_POSIX_CLOCKS
    467     struct timespec now;
    468     clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now);
    469     return (u8)now.tv_sec*1000000000LL + now.tv_nsec;
    470 #else
    471     return (u8) -1;
    472 #endif
    473 }
    474 
    475 /*
    476  * Get the per-thread CPU time, in nanoseconds, for the specified thread.
    477  */
    478 u8 dvmGetOtherThreadCpuTimeNsec(pthread_t thread)
    479 {
    480 #if 0 /*def HAVE_POSIX_CLOCKS*/
    481     int clockId;
    482 
    483     if (pthread_getcpuclockid(thread, &clockId) != 0)
    484         return (u8) -1;
    485 
    486     struct timespec now;
    487     clock_gettime(clockId, &now);
    488     return (u8)now.tv_sec*1000000000LL + now.tv_nsec;
    489 #else
    490     return (u8) -1;
    491 #endif
    492 }
    493 
    494 
    495 /*
    496  * Call this repeatedly, with successively higher values for "iteration",
    497  * to sleep for a period of time not to exceed "maxTotalSleep".
    498  *
    499  * For example, when called with iteration==0 we will sleep for a very
    500  * brief time.  On the next call we will sleep for a longer time.  When
    501  * the sum total of all sleeps reaches "maxTotalSleep", this returns false.
    502  *
    503  * The initial start time value for "relStartTime" MUST come from the
    504  * dvmGetRelativeTimeUsec call.  On the device this must come from the
    505  * monotonic clock source, not the wall clock.
    506  *
    507  * This should be used wherever you might be tempted to call sched_yield()
    508  * in a loop.  The problem with sched_yield is that, for a high-priority
    509  * thread, the kernel might not actually transfer control elsewhere.
    510  *
    511  * Returns "false" if we were unable to sleep because our time was up.
    512  */
    513 bool dvmIterativeSleep(int iteration, int maxTotalSleep, u8 relStartTime)
    514 {
    515     const int minSleep = 10000;
    516     u8 curTime;
    517     int curDelay;
    518 
    519     /*
    520      * Get current time, and see if we've already exceeded the limit.
    521      */
    522     curTime = dvmGetRelativeTimeUsec();
    523     if (curTime >= relStartTime + maxTotalSleep) {
    524         LOGVV("exsl: sleep exceeded (start=%llu max=%d now=%llu)",
    525             relStartTime, maxTotalSleep, curTime);
    526         return false;
    527     }
    528 
    529     /*
    530      * Compute current delay.  We're bounded by "maxTotalSleep", so no
    531      * real risk of overflow assuming "usleep" isn't returning early.
    532      * (Besides, 2^30 usec is about 18 minutes by itself.)
    533      *
    534      * For iteration==0 we just call sched_yield(), so the first sleep
    535      * at iteration==1 is actually (minSleep * 2).
    536      */
    537     curDelay = minSleep;
    538     while (iteration-- > 0)
    539         curDelay *= 2;
    540     assert(curDelay > 0);
    541 
    542     if (curTime + curDelay >= relStartTime + maxTotalSleep) {
    543         LOGVV("exsl: reduced delay from %d to %d",
    544             curDelay, (int) ((relStartTime + maxTotalSleep) - curTime));
    545         curDelay = (int) ((relStartTime + maxTotalSleep) - curTime);
    546     }
    547 
    548     if (iteration == 0) {
    549         LOGVV("exsl: yield");
    550         sched_yield();
    551     } else {
    552         LOGVV("exsl: sleep for %d", curDelay);
    553         usleep(curDelay);
    554     }
    555     return true;
    556 }
    557 
    558 
    559 /*
    560  * Set the "close on exec" flag so we don't expose our file descriptors
    561  * to processes launched by us.
    562  */
    563 bool dvmSetCloseOnExec(int fd)
    564 {
    565     int flags;
    566 
    567     /*
    568      * There's presently only one flag defined, so getting the previous
    569      * value of the fd flags is probably unnecessary.
    570      */
    571     flags = fcntl(fd, F_GETFD);
    572     if (flags < 0) {
    573         LOGW("Unable to get fd flags for fd %d", fd);
    574         return false;
    575     }
    576     if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
    577         LOGW("Unable to set close-on-exec for fd %d", fd);
    578         return false;
    579     }
    580     return true;
    581 }
    582 
    583 #if (!HAVE_STRLCPY)
    584 /* Implementation of strlcpy() for platforms that don't already have it. */
    585 size_t strlcpy(char *dst, const char *src, size_t size) {
    586     size_t srcLength = strlen(src);
    587     size_t copyLength = srcLength;
    588 
    589     if (srcLength > (size - 1)) {
    590         copyLength = size - 1;
    591     }
    592 
    593     if (size != 0) {
    594         strncpy(dst, src, copyLength);
    595         dst[copyLength] = '\0';
    596     }
    597 
    598     return srcLength;
    599 }
    600 #endif
    601 
    602 /*
    603  *  Allocates a memory region using ashmem and mmap, initialized to
    604  *  zero.  Actual allocation rounded up to page multiple.  Returns
    605  *  NULL on failure.
    606  */
    607 void *dvmAllocRegion(size_t byteCount, int prot, const char *name) {
    608     void *base;
    609     int fd, ret;
    610 
    611     byteCount = ALIGN_UP_TO_PAGE_SIZE(byteCount);
    612     fd = ashmem_create_region(name, byteCount);
    613     if (fd == -1) {
    614         return NULL;
    615     }
    616     base = mmap(NULL, byteCount, prot, MAP_PRIVATE, fd, 0);
    617     ret = close(fd);
    618     if (base == MAP_FAILED) {
    619         return NULL;
    620     }
    621     if (ret == -1) {
    622         return NULL;
    623     }
    624     return base;
    625 }
    626 
    627 /*
    628  * Get some per-thread stats.
    629  *
    630  * This is currently generated by opening the appropriate "stat" file
    631  * in /proc and reading the pile of stuff that comes out.
    632  */
    633 bool dvmGetThreadStats(ProcStatData* pData, pid_t tid)
    634 {
    635     /*
    636     int pid;
    637     char comm[128];
    638     char state;
    639     int ppid, pgrp, session, tty_nr, tpgid;
    640     unsigned long flags, minflt, cminflt, majflt, cmajflt, utime, stime;
    641     long cutime, cstime, priority, nice, zero, itrealvalue;
    642     unsigned long starttime, vsize;
    643     long rss;
    644     unsigned long rlim, startcode, endcode, startstack, kstkesp, kstkeip;
    645     unsigned long signal, blocked, sigignore, sigcatch, wchan, nswap, cnswap;
    646     int exit_signal, processor;
    647     unsigned long rt_priority, policy;
    648 
    649     scanf("%d %s %c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld "
    650           "%ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu "
    651           "%lu %lu %lu %d %d %lu %lu",
    652         &pid, comm, &state, &ppid, &pgrp, &session, &tty_nr, &tpgid,
    653         &flags, &minflt, &cminflt, &majflt, &cmajflt, &utime, &stime,
    654         &cutime, &cstime, &priority, &nice, &zero, &itrealvalue,
    655         &starttime, &vsize, &rss, &rlim, &startcode, &endcode,
    656         &startstack, &kstkesp, &kstkeip, &signal, &blocked, &sigignore,
    657         &sigcatch, &wchan, &nswap, &cnswap, &exit_signal, &processor,
    658         &rt_priority, &policy);
    659 
    660         (new: delayacct_blkio_ticks %llu (since Linux 2.6.18))
    661     */
    662 
    663     char nameBuf[64];
    664     int i, fd;
    665 
    666     /*
    667      * Open and read the appropriate file.  This is expected to work on
    668      * Linux but will fail on other platforms (e.g. Mac sim).
    669      */
    670     sprintf(nameBuf, "/proc/self/task/%d/stat", (int) tid);
    671     fd = open(nameBuf, O_RDONLY);
    672     if (fd < 0) {
    673         LOGV("Unable to open '%s': %s", nameBuf, strerror(errno));
    674         return false;
    675     }
    676 
    677     char lineBuf[512];      /* > 2x typical */
    678     int cc = read(fd, lineBuf, sizeof(lineBuf)-1);
    679     if (cc <= 0) {
    680         const char* msg = (cc == 0) ? "unexpected EOF" : strerror(errno);
    681         LOGI("Unable to read '%s': %s", nameBuf, msg);
    682         close(fd);
    683         return false;
    684     }
    685     close(fd);
    686     lineBuf[cc] = '\0';
    687 
    688     /*
    689      * Skip whitespace-separated tokens.  For the most part we can assume
    690      * that tokens do not contain spaces, and are separated by exactly one
    691      * space character.  The only exception is the second field ("comm")
    692      * which may contain spaces but is surrounded by parenthesis.
    693      */
    694     char* cp = strchr(lineBuf, ')');
    695     if (cp == NULL)
    696         goto parse_fail;
    697     cp++;
    698     for (i = 2; i < 13; i++) {
    699         cp = strchr(cp+1, ' ');
    700         if (cp == NULL)
    701             goto parse_fail;
    702     }
    703 
    704     /*
    705      * Grab utime/stime.
    706      */
    707     char* endp;
    708     pData->utime = strtoul(cp+1, &endp, 10);
    709     if (endp == cp+1)
    710         LOGI("Warning: strtoul failed on utime ('%.30s...')", cp);
    711 
    712     cp = strchr(cp+1, ' ');
    713     if (cp == NULL)
    714         goto parse_fail;
    715 
    716     pData->stime = strtoul(cp+1, &endp, 10);
    717     if (endp == cp+1)
    718         LOGI("Warning: strtoul failed on stime ('%.30s...')", cp);
    719 
    720     /*
    721      * Skip more stuff we don't care about.
    722      */
    723     for (i = 14; i < 38; i++) {
    724         cp = strchr(cp+1, ' ');
    725         if (cp == NULL)
    726             goto parse_fail;
    727     }
    728 
    729     /*
    730      * Grab processor number.
    731      */
    732     pData->processor = strtol(cp+1, &endp, 10);
    733     if (endp == cp+1)
    734         LOGI("Warning: strtoul failed on processor ('%.30s...')", cp);
    735 
    736     return true;
    737 
    738 parse_fail:
    739     LOGI("stat parse failed (%s)", lineBuf);
    740     return false;
    741 }
    742 
    743 /* documented in header file */
    744 const char* dvmPathToAbsolutePortion(const char* path) {
    745     if (path == NULL) {
    746         return NULL;
    747     }
    748 
    749     if (path[0] == '/') {
    750         /* It's a regular absolute path. Return it. */
    751         return path;
    752     }
    753 
    754     const char* sentinel = strstr(path, "/./");
    755 
    756     if (sentinel != NULL) {
    757         /* It's got the sentinel. Return a pointer to the second slash. */
    758         return sentinel + 2;
    759     }
    760 
    761     return NULL;
    762 }
    763 
    764 // From RE2.
    765 void StringAppendV(std::string* dst, const char* format, va_list ap) {
    766     // First try with a small fixed size buffer
    767     char space[1024];
    768 
    769     // It's possible for methods that use a va_list to invalidate
    770     // the data in it upon use.  The fix is to make a copy
    771     // of the structure before using it and use that copy instead.
    772     va_list backup_ap;
    773     va_copy(backup_ap, ap);
    774     int result = vsnprintf(space, sizeof(space), format, backup_ap);
    775     va_end(backup_ap);
    776 
    777     if ((result >= 0) && ((size_t) result < sizeof(space))) {
    778         // It fit
    779         dst->append(space, result);
    780         return;
    781     }
    782 
    783     // Repeatedly increase buffer size until it fits
    784     int length = sizeof(space);
    785     while (true) {
    786         if (result < 0) {
    787             // Older behavior: just try doubling the buffer size
    788             length *= 2;
    789         } else {
    790             // We need exactly "result+1" characters
    791             length = result+1;
    792         }
    793         char* buf = new char[length];
    794 
    795         // Restore the va_list before we use it again
    796         va_copy(backup_ap, ap);
    797         result = vsnprintf(buf, length, format, backup_ap);
    798         va_end(backup_ap);
    799 
    800         if ((result >= 0) && (result < length)) {
    801             // It fit
    802             dst->append(buf, result);
    803             delete[] buf;
    804             return;
    805         }
    806         delete[] buf;
    807     }
    808 }
    809 
    810 std::string StringPrintf(const char* fmt, ...) {
    811     va_list ap;
    812     va_start(ap, fmt);
    813     std::string result;
    814     StringAppendV(&result, fmt, ap);
    815     va_end(ap);
    816     return result;
    817 }
    818 
    819 void StringAppendF(std::string* dst, const char* format, ...) {
    820     va_list ap;
    821     va_start(ap, format);
    822     StringAppendV(dst, format, ap);
    823     va_end(ap);
    824 }
    825