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 } 833