OSDN Git Service

Invert sense of a test.
[android-x86/dalvik.git] / vm / Misc.cpp
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         ALOGE("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     /*
516      * Minimum sleep is one millisecond, it is important to keep this value
517      * low to ensure short GC pauses since dvmSuspendAllThreads() uses this
518      * function.
519      */
520     const int minSleep = 1000;
521     u8 curTime;
522     int curDelay;
523
524     /*
525      * Get current time, and see if we've already exceeded the limit.
526      */
527     curTime = dvmGetRelativeTimeUsec();
528     if (curTime >= relStartTime + maxTotalSleep) {
529         LOGVV("exsl: sleep exceeded (start=%llu max=%d now=%llu)",
530             relStartTime, maxTotalSleep, curTime);
531         return false;
532     }
533
534     /*
535      * Compute current delay.  We're bounded by "maxTotalSleep", so no
536      * real risk of overflow assuming "usleep" isn't returning early.
537      * (Besides, 2^30 usec is about 18 minutes by itself.)
538      *
539      * For iteration==0 we just call sched_yield(), so the first sleep
540      * at iteration==1 is actually (minSleep * 2).
541      */
542     curDelay = minSleep;
543     while (iteration-- > 0)
544         curDelay *= 2;
545     assert(curDelay > 0);
546
547     if (curTime + curDelay >= relStartTime + maxTotalSleep) {
548         LOGVV("exsl: reduced delay from %d to %d",
549             curDelay, (int) ((relStartTime + maxTotalSleep) - curTime));
550         curDelay = (int) ((relStartTime + maxTotalSleep) - curTime);
551     }
552
553     if (iteration == 0) {
554         LOGVV("exsl: yield");
555         sched_yield();
556     } else {
557         LOGVV("exsl: sleep for %d", curDelay);
558         usleep(curDelay);
559     }
560     return true;
561 }
562
563
564 /*
565  * Set the "close on exec" flag so we don't expose our file descriptors
566  * to processes launched by us.
567  */
568 bool dvmSetCloseOnExec(int fd)
569 {
570     int flags;
571
572     /*
573      * There's presently only one flag defined, so getting the previous
574      * value of the fd flags is probably unnecessary.
575      */
576     flags = fcntl(fd, F_GETFD);
577     if (flags < 0) {
578         ALOGW("Unable to get fd flags for fd %d", fd);
579         return false;
580     }
581     if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
582         ALOGW("Unable to set close-on-exec for fd %d", fd);
583         return false;
584     }
585     return true;
586 }
587
588 #if (!HAVE_STRLCPY)
589 /* Implementation of strlcpy() for platforms that don't already have it. */
590 size_t strlcpy(char *dst, const char *src, size_t size) {
591     size_t srcLength = strlen(src);
592     size_t copyLength = srcLength;
593
594     if (srcLength > (size - 1)) {
595         copyLength = size - 1;
596     }
597
598     if (size != 0) {
599         strncpy(dst, src, copyLength);
600         dst[copyLength] = '\0';
601     }
602
603     return srcLength;
604 }
605 #endif
606
607 /*
608  *  Allocates a memory region using ashmem and mmap, initialized to
609  *  zero.  Actual allocation rounded up to page multiple.  Returns
610  *  NULL on failure.
611  */
612 void *dvmAllocRegion(size_t byteCount, int prot, const char *name) {
613     void *base;
614     int fd, ret;
615
616     byteCount = ALIGN_UP_TO_PAGE_SIZE(byteCount);
617     fd = ashmem_create_region(name, byteCount);
618     if (fd == -1) {
619         return NULL;
620     }
621     base = mmap(NULL, byteCount, prot, MAP_PRIVATE, fd, 0);
622     ret = close(fd);
623     if (base == MAP_FAILED) {
624         return NULL;
625     }
626     if (ret == -1) {
627         munmap(base, byteCount);
628         return NULL;
629     }
630     return base;
631 }
632
633 /*
634  * Get some per-thread stats.
635  *
636  * This is currently generated by opening the appropriate "stat" file
637  * in /proc and reading the pile of stuff that comes out.
638  */
639 bool dvmGetThreadStats(ProcStatData* pData, pid_t tid)
640 {
641     /*
642     int pid;
643     char comm[128];
644     char state;
645     int ppid, pgrp, session, tty_nr, tpgid;
646     unsigned long flags, minflt, cminflt, majflt, cmajflt, utime, stime;
647     long cutime, cstime, priority, nice, zero, itrealvalue;
648     unsigned long starttime, vsize;
649     long rss;
650     unsigned long rlim, startcode, endcode, startstack, kstkesp, kstkeip;
651     unsigned long signal, blocked, sigignore, sigcatch, wchan, nswap, cnswap;
652     int exit_signal, processor;
653     unsigned long rt_priority, policy;
654
655     scanf("%d %s %c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld "
656           "%ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu "
657           "%lu %lu %lu %d %d %lu %lu",
658         &pid, comm, &state, &ppid, &pgrp, &session, &tty_nr, &tpgid,
659         &flags, &minflt, &cminflt, &majflt, &cmajflt, &utime, &stime,
660         &cutime, &cstime, &priority, &nice, &zero, &itrealvalue,
661         &starttime, &vsize, &rss, &rlim, &startcode, &endcode,
662         &startstack, &kstkesp, &kstkeip, &signal, &blocked, &sigignore,
663         &sigcatch, &wchan, &nswap, &cnswap, &exit_signal, &processor,
664         &rt_priority, &policy);
665
666         (new: delayacct_blkio_ticks %llu (since Linux 2.6.18))
667     */
668
669     char nameBuf[64];
670     int i, fd;
671
672     /*
673      * Open and read the appropriate file.  This is expected to work on
674      * Linux but will fail on other platforms (e.g. Mac sim).
675      */
676     sprintf(nameBuf, "/proc/self/task/%d/stat", (int) tid);
677     fd = open(nameBuf, O_RDONLY);
678     if (fd < 0) {
679         ALOGV("Unable to open '%s': %s", nameBuf, strerror(errno));
680         return false;
681     }
682
683     char lineBuf[512];      /* > 2x typical */
684     int cc = read(fd, lineBuf, sizeof(lineBuf)-1);
685     if (cc <= 0) {
686         const char* msg = (cc == 0) ? "unexpected EOF" : strerror(errno);
687         ALOGI("Unable to read '%s': %s", nameBuf, msg);
688         close(fd);
689         return false;
690     }
691     close(fd);
692     lineBuf[cc] = '\0';
693
694     /*
695      * Skip whitespace-separated tokens.  For the most part we can assume
696      * that tokens do not contain spaces, and are separated by exactly one
697      * space character.  The only exception is the second field ("comm")
698      * which may contain spaces but is surrounded by parenthesis.
699      */
700     char* cp = strchr(lineBuf, ')');
701     if (cp == NULL)
702         goto parse_fail;
703     cp += 2;
704     pData->state = *cp++;
705
706     for (i = 3; i < 13; i++) {
707         cp = strchr(cp+1, ' ');
708         if (cp == NULL)
709             goto parse_fail;
710     }
711
712     /*
713      * Grab utime/stime.
714      */
715     char* endp;
716     pData->utime = strtoul(cp+1, &endp, 10);
717     if (endp == cp+1)
718         ALOGI("Warning: strtoul failed on utime ('%.30s...')", cp);
719
720     cp = strchr(cp+1, ' ');
721     if (cp == NULL)
722         goto parse_fail;
723
724     pData->stime = strtoul(cp+1, &endp, 10);
725     if (endp == cp+1)
726         ALOGI("Warning: strtoul failed on stime ('%.30s...')", cp);
727
728     /*
729      * Skip more stuff we don't care about.
730      */
731     for (i = 14; i < 38; i++) {
732         cp = strchr(cp+1, ' ');
733         if (cp == NULL)
734             goto parse_fail;
735     }
736
737     /*
738      * Grab processor number.
739      */
740     pData->processor = strtol(cp+1, &endp, 10);
741     if (endp == cp+1)
742         ALOGI("Warning: strtoul failed on processor ('%.30s...')", cp);
743
744     return true;
745
746 parse_fail:
747     ALOGI("stat parse failed (%s)", lineBuf);
748     return false;
749 }
750
751 /* documented in header file */
752 const char* dvmPathToAbsolutePortion(const char* path) {
753     if (path == NULL) {
754         return NULL;
755     }
756
757     if (path[0] == '/') {
758         /* It's a regular absolute path. Return it. */
759         return path;
760     }
761
762     const char* sentinel = strstr(path, "/./");
763
764     if (sentinel != NULL) {
765         /* It's got the sentinel. Return a pointer to the second slash. */
766         return sentinel + 2;
767     }
768
769     return NULL;
770 }
771
772 // From RE2.
773 void StringAppendV(std::string* dst, const char* format, va_list ap) {
774     // First try with a small fixed size buffer
775     char space[1024];
776
777     // It's possible for methods that use a va_list to invalidate
778     // the data in it upon use.  The fix is to make a copy
779     // of the structure before using it and use that copy instead.
780     va_list backup_ap;
781     va_copy(backup_ap, ap);
782     int result = vsnprintf(space, sizeof(space), format, backup_ap);
783     va_end(backup_ap);
784
785     if ((result >= 0) && ((size_t) result < sizeof(space))) {
786         // It fit
787         dst->append(space, result);
788         return;
789     }
790
791     // Repeatedly increase buffer size until it fits
792     int length = sizeof(space);
793     while (true) {
794         if (result < 0) {
795             // Older behavior: just try doubling the buffer size
796             length *= 2;
797         } else {
798             // We need exactly "result+1" characters
799             length = result+1;
800         }
801         char* buf = new char[length];
802
803         // Restore the va_list before we use it again
804         va_copy(backup_ap, ap);
805         result = vsnprintf(buf, length, format, backup_ap);
806         va_end(backup_ap);
807
808         if ((result >= 0) && (result < length)) {
809             // It fit
810             dst->append(buf, result);
811             delete[] buf;
812             return;
813         }
814         delete[] buf;
815     }
816 }
817
818 std::string StringPrintf(const char* fmt, ...) {
819     va_list ap;
820     va_start(ap, fmt);
821     std::string result;
822     StringAppendV(&result, fmt, ap);
823     va_end(ap);
824     return result;
825 }
826
827 void StringAppendF(std::string* dst, const char* format, ...) {
828     va_list ap;
829     va_start(ap, format);
830     StringAppendV(dst, format, ap);
831     va_end(ap);
832 }