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 */
dvmPrintHexDumpEx(int priority,const char * tag,const void * vaddr,size_t length,HexDumpMode mode)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 */
dvmCreateLogOutputTarget(DebugOutputTarget * target,int priority,const char * tag)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 */
dvmCreateFileOutputTarget(DebugOutputTarget * target,FILE * fp)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 */
dvmFreeOutputTarget(DebugOutputTarget * target)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 */
dvmPrintDebugMessage(const DebugOutputTarget * target,const char * format,...)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 */
dvmDotToSlash(const char * str)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
dvmHumanReadableDescriptor(const char * descriptor)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
dvmHumanReadableType(const Object * obj)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
dvmHumanReadableField(const Field * field)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
dvmHumanReadableMethod(const Method * method,bool withSignature)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 */
dvmDescriptorToDot(const char * str)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 */
dvmDotToDescriptor(const char * str)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 */
dvmDescriptorToName(const char * str)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 */
dvmNameToDescriptor(const char * str)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 */
dvmGetRelativeTimeNsec()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 */
dvmGetThreadCpuTimeNsec()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 */
dvmGetOtherThreadCpuTimeNsec(pthread_t thread)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 */
dvmIterativeSleep(int iteration,int maxTotalSleep,u8 relStartTime)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 */
dvmSetCloseOnExec(int fd)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. */
strlcpy(char * dst,const char * src,size_t size)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 */
dvmAllocRegion(size_t byteCount,int prot,const char * name)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 return NULL;
628 }
629 return base;
630 }
631
632 /*
633 * Get some per-thread stats.
634 *
635 * This is currently generated by opening the appropriate "stat" file
636 * in /proc and reading the pile of stuff that comes out.
637 */
dvmGetThreadStats(ProcStatData * pData,pid_t tid)638 bool dvmGetThreadStats(ProcStatData* pData, pid_t tid)
639 {
640 /*
641 int pid;
642 char comm[128];
643 char state;
644 int ppid, pgrp, session, tty_nr, tpgid;
645 unsigned long flags, minflt, cminflt, majflt, cmajflt, utime, stime;
646 long cutime, cstime, priority, nice, zero, itrealvalue;
647 unsigned long starttime, vsize;
648 long rss;
649 unsigned long rlim, startcode, endcode, startstack, kstkesp, kstkeip;
650 unsigned long signal, blocked, sigignore, sigcatch, wchan, nswap, cnswap;
651 int exit_signal, processor;
652 unsigned long rt_priority, policy;
653
654 scanf("%d %s %c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld "
655 "%ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu "
656 "%lu %lu %lu %d %d %lu %lu",
657 &pid, comm, &state, &ppid, &pgrp, &session, &tty_nr, &tpgid,
658 &flags, &minflt, &cminflt, &majflt, &cmajflt, &utime, &stime,
659 &cutime, &cstime, &priority, &nice, &zero, &itrealvalue,
660 &starttime, &vsize, &rss, &rlim, &startcode, &endcode,
661 &startstack, &kstkesp, &kstkeip, &signal, &blocked, &sigignore,
662 &sigcatch, &wchan, &nswap, &cnswap, &exit_signal, &processor,
663 &rt_priority, &policy);
664
665 (new: delayacct_blkio_ticks %llu (since Linux 2.6.18))
666 */
667
668 char nameBuf[64];
669 int i, fd;
670
671 /*
672 * Open and read the appropriate file. This is expected to work on
673 * Linux but will fail on other platforms (e.g. Mac sim).
674 */
675 sprintf(nameBuf, "/proc/self/task/%d/stat", (int) tid);
676 fd = open(nameBuf, O_RDONLY);
677 if (fd < 0) {
678 ALOGV("Unable to open '%s': %s", nameBuf, strerror(errno));
679 return false;
680 }
681
682 char lineBuf[512]; /* > 2x typical */
683 int cc = read(fd, lineBuf, sizeof(lineBuf)-1);
684 if (cc <= 0) {
685 const char* msg = (cc == 0) ? "unexpected EOF" : strerror(errno);
686 ALOGI("Unable to read '%s': %s", nameBuf, msg);
687 close(fd);
688 return false;
689 }
690 close(fd);
691 lineBuf[cc] = '\0';
692
693 /*
694 * Skip whitespace-separated tokens. For the most part we can assume
695 * that tokens do not contain spaces, and are separated by exactly one
696 * space character. The only exception is the second field ("comm")
697 * which may contain spaces but is surrounded by parenthesis.
698 */
699 char* cp = strchr(lineBuf, ')');
700 if (cp == NULL)
701 goto parse_fail;
702 cp += 2;
703 pData->state = *cp++;
704
705 for (i = 3; i < 13; i++) {
706 cp = strchr(cp+1, ' ');
707 if (cp == NULL)
708 goto parse_fail;
709 }
710
711 /*
712 * Grab utime/stime.
713 */
714 char* endp;
715 pData->utime = strtoul(cp+1, &endp, 10);
716 if (endp == cp+1)
717 ALOGI("Warning: strtoul failed on utime ('%.30s...')", cp);
718
719 cp = strchr(cp+1, ' ');
720 if (cp == NULL)
721 goto parse_fail;
722
723 pData->stime = strtoul(cp+1, &endp, 10);
724 if (endp == cp+1)
725 ALOGI("Warning: strtoul failed on stime ('%.30s...')", cp);
726
727 /*
728 * Skip more stuff we don't care about.
729 */
730 for (i = 14; i < 38; i++) {
731 cp = strchr(cp+1, ' ');
732 if (cp == NULL)
733 goto parse_fail;
734 }
735
736 /*
737 * Grab processor number.
738 */
739 pData->processor = strtol(cp+1, &endp, 10);
740 if (endp == cp+1)
741 ALOGI("Warning: strtoul failed on processor ('%.30s...')", cp);
742
743 return true;
744
745 parse_fail:
746 ALOGI("stat parse failed (%s)", lineBuf);
747 return false;
748 }
749
750 /* documented in header file */
dvmPathToAbsolutePortion(const char * path)751 const char* dvmPathToAbsolutePortion(const char* path) {
752 if (path == NULL) {
753 return NULL;
754 }
755
756 if (path[0] == '/') {
757 /* It's a regular absolute path. Return it. */
758 return path;
759 }
760
761 const char* sentinel = strstr(path, "/./");
762
763 if (sentinel != NULL) {
764 /* It's got the sentinel. Return a pointer to the second slash. */
765 return sentinel + 2;
766 }
767
768 return NULL;
769 }
770
771 // From RE2.
StringAppendV(std::string * dst,const char * format,va_list ap)772 void StringAppendV(std::string* dst, const char* format, va_list ap) {
773 // First try with a small fixed size buffer
774 char space[1024];
775
776 // It's possible for methods that use a va_list to invalidate
777 // the data in it upon use. The fix is to make a copy
778 // of the structure before using it and use that copy instead.
779 va_list backup_ap;
780 va_copy(backup_ap, ap);
781 int result = vsnprintf(space, sizeof(space), format, backup_ap);
782 va_end(backup_ap);
783
784 if ((result >= 0) && ((size_t) result < sizeof(space))) {
785 // It fit
786 dst->append(space, result);
787 return;
788 }
789
790 // Repeatedly increase buffer size until it fits
791 int length = sizeof(space);
792 while (true) {
793 if (result < 0) {
794 // Older behavior: just try doubling the buffer size
795 length *= 2;
796 } else {
797 // We need exactly "result+1" characters
798 length = result+1;
799 }
800 char* buf = new char[length];
801
802 // Restore the va_list before we use it again
803 va_copy(backup_ap, ap);
804 result = vsnprintf(buf, length, format, backup_ap);
805 va_end(backup_ap);
806
807 if ((result >= 0) && (result < length)) {
808 // It fit
809 dst->append(buf, result);
810 delete[] buf;
811 return;
812 }
813 delete[] buf;
814 }
815 }
816
StringPrintf(const char * fmt,...)817 std::string StringPrintf(const char* fmt, ...) {
818 va_list ap;
819 va_start(ap, fmt);
820 std::string result;
821 StringAppendV(&result, fmt, ap);
822 va_end(ap);
823 return result;
824 }
825
StringAppendF(std::string * dst,const char * format,...)826 void StringAppendF(std::string* dst, const char* format, ...) {
827 va_list ap;
828 va_start(ap, format);
829 StringAppendV(dst, format, ap);
830 va_end(ap);
831 }
832