| Elliott Hughes | 2faa5f1 | 2012-01-30 14:42:07 -0800 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2011 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 |  */ | 
| Elliott Hughes | 11e4507 | 2011-08-16 17:40:46 -0700 | [diff] [blame] | 16 |  | 
| Elliott Hughes | 42ee142 | 2011-09-06 12:33:32 -0700 | [diff] [blame] | 17 | #include "utils.h" | 
 | 18 |  | 
| Christopher Ferris | 943af7d | 2014-01-16 12:41:46 -0800 | [diff] [blame] | 19 | #include <inttypes.h> | 
| Elliott Hughes | 92b3b56 | 2011-09-08 16:32:26 -0700 | [diff] [blame] | 20 | #include <pthread.h> | 
| Brian Carlstrom | a9f1978 | 2011-10-13 00:14:47 -0700 | [diff] [blame] | 21 | #include <sys/stat.h> | 
| Elliott Hughes | 42ee142 | 2011-09-06 12:33:32 -0700 | [diff] [blame] | 22 | #include <sys/syscall.h> | 
 | 23 | #include <sys/types.h> | 
| Brian Carlstrom | 4cf5e57 | 2014-02-25 11:47:48 -0800 | [diff] [blame] | 24 | #include <sys/wait.h> | 
| Elliott Hughes | 42ee142 | 2011-09-06 12:33:32 -0700 | [diff] [blame] | 25 | #include <unistd.h> | 
| Ian Rogers | 700a402 | 2014-05-19 16:49:03 -0700 | [diff] [blame] | 26 | #include <memory> | 
| Elliott Hughes | 42ee142 | 2011-09-06 12:33:32 -0700 | [diff] [blame] | 27 |  | 
| Brian Carlstrom | 6449c62 | 2014-02-10 23:48:36 -0800 | [diff] [blame] | 28 | #include "base/stl_util.h" | 
| Elliott Hughes | 7616005 | 2012-12-12 16:31:20 -0800 | [diff] [blame] | 29 | #include "base/unix_file/fd_file.h" | 
| Ian Rogers | 4f6ad8a | 2013-03-18 15:27:28 -0700 | [diff] [blame] | 30 | #include "dex_file-inl.h" | 
| Ian Rogers | 22d5e73 | 2014-07-15 22:23:51 -0700 | [diff] [blame] | 31 | #include "field_helper.h" | 
| Brian Carlstrom | ea46f95 | 2013-07-30 01:26:50 -0700 | [diff] [blame] | 32 | #include "mirror/art_field-inl.h" | 
 | 33 | #include "mirror/art_method-inl.h" | 
| Ian Rogers | 4f6ad8a | 2013-03-18 15:27:28 -0700 | [diff] [blame] | 34 | #include "mirror/class-inl.h" | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 35 | #include "mirror/class_loader.h" | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 36 | #include "mirror/object-inl.h" | 
 | 37 | #include "mirror/object_array-inl.h" | 
 | 38 | #include "mirror/string.h" | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame] | 39 | #include "os.h" | 
| Kenny Root | 067d20f | 2014-03-05 14:57:21 -0800 | [diff] [blame] | 40 | #include "scoped_thread_state_change.h" | 
| Ian Rogers | a672490 | 2013-09-23 09:23:37 -0700 | [diff] [blame] | 41 | #include "utf-inl.h" | 
| Elliott Hughes | 11e4507 | 2011-08-16 17:40:46 -0700 | [diff] [blame] | 42 |  | 
| Elliott Hughes | ad6c9c3 | 2012-01-19 17:39:12 -0800 | [diff] [blame] | 43 | #if !defined(HAVE_POSIX_CLOCKS) | 
 | 44 | #include <sys/time.h> | 
 | 45 | #endif | 
 | 46 |  | 
| Elliott Hughes | dcc2474 | 2011-09-07 14:02:44 -0700 | [diff] [blame] | 47 | #if defined(HAVE_PRCTL) | 
 | 48 | #include <sys/prctl.h> | 
 | 49 | #endif | 
 | 50 |  | 
| Elliott Hughes | 4ae722a | 2012-03-13 11:08:51 -0700 | [diff] [blame] | 51 | #if defined(__APPLE__) | 
| Brian Carlstrom | 7934ac2 | 2013-07-26 10:54:15 -0700 | [diff] [blame] | 52 | #include "AvailabilityMacros.h"  // For MAC_OS_X_VERSION_MAX_ALLOWED | 
| Elliott Hughes | f149843 | 2012-03-28 19:34:27 -0700 | [diff] [blame] | 53 | #include <sys/syscall.h> | 
| Elliott Hughes | 4ae722a | 2012-03-13 11:08:51 -0700 | [diff] [blame] | 54 | #endif | 
 | 55 |  | 
| Christopher Ferris | 7b5f0cf | 2013-11-01 15:18:45 -0700 | [diff] [blame] | 56 | #include <backtrace/Backtrace.h>  // For DumpNativeStack. | 
| Elliott Hughes | 46e251b | 2012-05-22 15:10:45 -0700 | [diff] [blame] | 57 |  | 
| Elliott Hughes | 058a6de | 2012-05-24 19:13:02 -0700 | [diff] [blame] | 58 | #if defined(__linux__) | 
| Elliott Hughes | e1aee69 | 2012-01-17 16:40:10 -0800 | [diff] [blame] | 59 | #include <linux/unistd.h> | 
| Elliott Hughes | e1aee69 | 2012-01-17 16:40:10 -0800 | [diff] [blame] | 60 | #endif | 
 | 61 |  | 
| Elliott Hughes | 11e4507 | 2011-08-16 17:40:46 -0700 | [diff] [blame] | 62 | namespace art { | 
 | 63 |  | 
| Elliott Hughes | 11d1b0c | 2012-01-23 16:57:47 -0800 | [diff] [blame] | 64 | pid_t GetTid() { | 
| Brian Carlstrom | f3a2641 | 2012-08-24 11:06:02 -0700 | [diff] [blame] | 65 | #if defined(__APPLE__) | 
 | 66 |   uint64_t owner; | 
 | 67 |   CHECK_PTHREAD_CALL(pthread_threadid_np, (NULL, &owner), __FUNCTION__);  // Requires Mac OS 10.6 | 
 | 68 |   return owner; | 
| Elliott Hughes | 323aa86 | 2014-08-20 15:00:04 -0700 | [diff] [blame] | 69 | #elif defined(__BIONIC__) | 
 | 70 |   return gettid(); | 
| Elliott Hughes | 11d1b0c | 2012-01-23 16:57:47 -0800 | [diff] [blame] | 71 | #else | 
| Elliott Hughes | 11d1b0c | 2012-01-23 16:57:47 -0800 | [diff] [blame] | 72 |   return syscall(__NR_gettid); | 
 | 73 | #endif | 
 | 74 | } | 
 | 75 |  | 
| Elliott Hughes | 289be85 | 2012-06-12 13:57:20 -0700 | [diff] [blame] | 76 | std::string GetThreadName(pid_t tid) { | 
 | 77 |   std::string result; | 
 | 78 |   if (ReadFileToString(StringPrintf("/proc/self/task/%d/comm", tid), &result)) { | 
| Brian Carlstrom | 7934ac2 | 2013-07-26 10:54:15 -0700 | [diff] [blame] | 79 |     result.resize(result.size() - 1);  // Lose the trailing '\n'. | 
| Elliott Hughes | 289be85 | 2012-06-12 13:57:20 -0700 | [diff] [blame] | 80 |   } else { | 
 | 81 |     result = "<unknown>"; | 
 | 82 |   } | 
 | 83 |   return result; | 
 | 84 | } | 
 | 85 |  | 
| Elliott Hughes | 6d3fc56 | 2014-08-27 11:47:01 -0700 | [diff] [blame] | 86 | void GetThreadStack(pthread_t thread, void** stack_base, size_t* stack_size, size_t* guard_size) { | 
| Elliott Hughes | e188419 | 2012-04-23 12:38:15 -0700 | [diff] [blame] | 87 | #if defined(__APPLE__) | 
| Brian Carlstrom | 2921201 | 2013-09-12 22:18:30 -0700 | [diff] [blame] | 88 |   *stack_size = pthread_get_stacksize_np(thread); | 
| Ian Rogers | 120f1c7 | 2012-09-28 17:17:10 -0700 | [diff] [blame] | 89 |   void* stack_addr = pthread_get_stackaddr_np(thread); | 
| Elliott Hughes | e188419 | 2012-04-23 12:38:15 -0700 | [diff] [blame] | 90 |  | 
 | 91 |   // Check whether stack_addr is the base or end of the stack. | 
 | 92 |   // (On Mac OS 10.7, it's the end.) | 
 | 93 |   int stack_variable; | 
 | 94 |   if (stack_addr > &stack_variable) { | 
| Brian Carlstrom | 2921201 | 2013-09-12 22:18:30 -0700 | [diff] [blame] | 95 |     *stack_base = reinterpret_cast<byte*>(stack_addr) - *stack_size; | 
| Elliott Hughes | e188419 | 2012-04-23 12:38:15 -0700 | [diff] [blame] | 96 |   } else { | 
| Brian Carlstrom | 2921201 | 2013-09-12 22:18:30 -0700 | [diff] [blame] | 97 |     *stack_base = stack_addr; | 
| Elliott Hughes | e188419 | 2012-04-23 12:38:15 -0700 | [diff] [blame] | 98 |   } | 
| Elliott Hughes | 6d3fc56 | 2014-08-27 11:47:01 -0700 | [diff] [blame] | 99 |  | 
 | 100 |   // This is wrong, but there doesn't seem to be a way to get the actual value on the Mac. | 
 | 101 |   pthread_attr_t attributes; | 
 | 102 |   CHECK_PTHREAD_CALL(pthread_attr_init, (&attributes), __FUNCTION__); | 
 | 103 |   CHECK_PTHREAD_CALL(pthread_attr_getguardsize, (&attributes, guard_size), __FUNCTION__); | 
 | 104 |   CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), __FUNCTION__); | 
| Elliott Hughes | e188419 | 2012-04-23 12:38:15 -0700 | [diff] [blame] | 105 | #else | 
 | 106 |   pthread_attr_t attributes; | 
| Ian Rogers | 120f1c7 | 2012-09-28 17:17:10 -0700 | [diff] [blame] | 107 |   CHECK_PTHREAD_CALL(pthread_getattr_np, (thread, &attributes), __FUNCTION__); | 
| Brian Carlstrom | 2921201 | 2013-09-12 22:18:30 -0700 | [diff] [blame] | 108 |   CHECK_PTHREAD_CALL(pthread_attr_getstack, (&attributes, stack_base, stack_size), __FUNCTION__); | 
| Elliott Hughes | 6d3fc56 | 2014-08-27 11:47:01 -0700 | [diff] [blame] | 109 |   CHECK_PTHREAD_CALL(pthread_attr_getguardsize, (&attributes, guard_size), __FUNCTION__); | 
| Elliott Hughes | e188419 | 2012-04-23 12:38:15 -0700 | [diff] [blame] | 110 |   CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), __FUNCTION__); | 
| Elliott Hughes | 839cc30 | 2014-08-28 10:24:44 -0700 | [diff] [blame] | 111 |  | 
 | 112 | #if defined(__GLIBC__) | 
 | 113 |   // If we're the main thread, check whether we were run with an unlimited stack. In that case, | 
 | 114 |   // glibc will have reported a 2GB stack for our 32-bit process, and our stack overflow detection | 
 | 115 |   // will be broken because we'll die long before we get close to 2GB. | 
 | 116 |   bool is_main_thread = (::art::GetTid() == getpid()); | 
 | 117 |   if (is_main_thread) { | 
 | 118 |     rlimit stack_limit; | 
 | 119 |     if (getrlimit(RLIMIT_STACK, &stack_limit) == -1) { | 
 | 120 |       PLOG(FATAL) << "getrlimit(RLIMIT_STACK) failed"; | 
 | 121 |     } | 
 | 122 |     if (stack_limit.rlim_cur == RLIM_INFINITY) { | 
 | 123 |       size_t old_stack_size = *stack_size; | 
 | 124 |  | 
 | 125 |       // Use the kernel default limit as our size, and adjust the base to match. | 
 | 126 |       *stack_size = 8 * MB; | 
 | 127 |       *stack_base = reinterpret_cast<uint8_t*>(*stack_base) + (old_stack_size - *stack_size); | 
 | 128 |  | 
 | 129 |       VLOG(threads) << "Limiting unlimited stack (reported as " << PrettySize(old_stack_size) << ")" | 
 | 130 |                     << " to " << PrettySize(*stack_size) | 
 | 131 |                     << " with base " << *stack_base; | 
 | 132 |     } | 
 | 133 |   } | 
 | 134 | #endif | 
 | 135 |  | 
| Elliott Hughes | e188419 | 2012-04-23 12:38:15 -0700 | [diff] [blame] | 136 | #endif | 
 | 137 | } | 
 | 138 |  | 
| Elliott Hughes | d92bec4 | 2011-09-02 17:04:36 -0700 | [diff] [blame] | 139 | bool ReadFileToString(const std::string& file_name, std::string* result) { | 
| Ian Rogers | 700a402 | 2014-05-19 16:49:03 -0700 | [diff] [blame] | 140 |   std::unique_ptr<File> file(new File); | 
| Elliott Hughes | 7616005 | 2012-12-12 16:31:20 -0800 | [diff] [blame] | 141 |   if (!file->Open(file_name, O_RDONLY)) { | 
| Elliott Hughes | d92bec4 | 2011-09-02 17:04:36 -0700 | [diff] [blame] | 142 |     return false; | 
 | 143 |   } | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame] | 144 |  | 
| Elliott Hughes | 3b6baaa | 2011-10-14 19:13:56 -0700 | [diff] [blame] | 145 |   std::vector<char> buf(8 * KB); | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame] | 146 |   while (true) { | 
| Elliott Hughes | 7616005 | 2012-12-12 16:31:20 -0800 | [diff] [blame] | 147 |     int64_t n = TEMP_FAILURE_RETRY(read(file->Fd(), &buf[0], buf.size())); | 
| Elliott Hughes | d92bec4 | 2011-09-02 17:04:36 -0700 | [diff] [blame] | 148 |     if (n == -1) { | 
 | 149 |       return false; | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame] | 150 |     } | 
| Elliott Hughes | d92bec4 | 2011-09-02 17:04:36 -0700 | [diff] [blame] | 151 |     if (n == 0) { | 
 | 152 |       return true; | 
 | 153 |     } | 
| Elliott Hughes | 3b6baaa | 2011-10-14 19:13:56 -0700 | [diff] [blame] | 154 |     result->append(&buf[0], n); | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame] | 155 |   } | 
| buzbee | c143c55 | 2011-08-20 17:38:58 -0700 | [diff] [blame] | 156 | } | 
 | 157 |  | 
| Elliott Hughes | e27955c | 2011-08-26 15:21:24 -0700 | [diff] [blame] | 158 | std::string GetIsoDate() { | 
 | 159 |   time_t now = time(NULL); | 
| Elliott Hughes | 7b9d996 | 2012-04-20 18:48:18 -0700 | [diff] [blame] | 160 |   tm tmbuf; | 
 | 161 |   tm* ptm = localtime_r(&now, &tmbuf); | 
| Elliott Hughes | e27955c | 2011-08-26 15:21:24 -0700 | [diff] [blame] | 162 |   return StringPrintf("%04d-%02d-%02d %02d:%02d:%02d", | 
 | 163 |       ptm->tm_year + 1900, ptm->tm_mon+1, ptm->tm_mday, | 
 | 164 |       ptm->tm_hour, ptm->tm_min, ptm->tm_sec); | 
 | 165 | } | 
 | 166 |  | 
| Elliott Hughes | 7162ad9 | 2011-10-27 14:08:42 -0700 | [diff] [blame] | 167 | uint64_t MilliTime() { | 
| Elliott Hughes | ad6c9c3 | 2012-01-19 17:39:12 -0800 | [diff] [blame] | 168 | #if defined(HAVE_POSIX_CLOCKS) | 
| Elliott Hughes | 7b9d996 | 2012-04-20 18:48:18 -0700 | [diff] [blame] | 169 |   timespec now; | 
| Elliott Hughes | 7162ad9 | 2011-10-27 14:08:42 -0700 | [diff] [blame] | 170 |   clock_gettime(CLOCK_MONOTONIC, &now); | 
| Ian Rogers | 0f67847 | 2014-03-10 16:18:37 -0700 | [diff] [blame] | 171 |   return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000) + now.tv_nsec / UINT64_C(1000000); | 
| Elliott Hughes | ad6c9c3 | 2012-01-19 17:39:12 -0800 | [diff] [blame] | 172 | #else | 
| Elliott Hughes | 7b9d996 | 2012-04-20 18:48:18 -0700 | [diff] [blame] | 173 |   timeval now; | 
| Elliott Hughes | ad6c9c3 | 2012-01-19 17:39:12 -0800 | [diff] [blame] | 174 |   gettimeofday(&now, NULL); | 
| Ian Rogers | 0f67847 | 2014-03-10 16:18:37 -0700 | [diff] [blame] | 175 |   return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000) + now.tv_usec / UINT64_C(1000); | 
| Elliott Hughes | ad6c9c3 | 2012-01-19 17:39:12 -0800 | [diff] [blame] | 176 | #endif | 
| Elliott Hughes | 7162ad9 | 2011-10-27 14:08:42 -0700 | [diff] [blame] | 177 | } | 
 | 178 |  | 
| jeffhao | a9ef3fd | 2011-12-13 18:33:43 -0800 | [diff] [blame] | 179 | uint64_t MicroTime() { | 
| Elliott Hughes | ad6c9c3 | 2012-01-19 17:39:12 -0800 | [diff] [blame] | 180 | #if defined(HAVE_POSIX_CLOCKS) | 
| Elliott Hughes | 7b9d996 | 2012-04-20 18:48:18 -0700 | [diff] [blame] | 181 |   timespec now; | 
| jeffhao | a9ef3fd | 2011-12-13 18:33:43 -0800 | [diff] [blame] | 182 |   clock_gettime(CLOCK_MONOTONIC, &now); | 
| Ian Rogers | 0f67847 | 2014-03-10 16:18:37 -0700 | [diff] [blame] | 183 |   return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000) + now.tv_nsec / UINT64_C(1000); | 
| Elliott Hughes | ad6c9c3 | 2012-01-19 17:39:12 -0800 | [diff] [blame] | 184 | #else | 
| Elliott Hughes | 7b9d996 | 2012-04-20 18:48:18 -0700 | [diff] [blame] | 185 |   timeval now; | 
| Elliott Hughes | ad6c9c3 | 2012-01-19 17:39:12 -0800 | [diff] [blame] | 186 |   gettimeofday(&now, NULL); | 
| Ian Rogers | 0f67847 | 2014-03-10 16:18:37 -0700 | [diff] [blame] | 187 |   return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000) + now.tv_usec; | 
| Elliott Hughes | ad6c9c3 | 2012-01-19 17:39:12 -0800 | [diff] [blame] | 188 | #endif | 
| jeffhao | a9ef3fd | 2011-12-13 18:33:43 -0800 | [diff] [blame] | 189 | } | 
 | 190 |  | 
| Elliott Hughes | 83df2ac | 2011-10-11 16:37:54 -0700 | [diff] [blame] | 191 | uint64_t NanoTime() { | 
| Elliott Hughes | ad6c9c3 | 2012-01-19 17:39:12 -0800 | [diff] [blame] | 192 | #if defined(HAVE_POSIX_CLOCKS) | 
| Elliott Hughes | 7b9d996 | 2012-04-20 18:48:18 -0700 | [diff] [blame] | 193 |   timespec now; | 
| Elliott Hughes | 83df2ac | 2011-10-11 16:37:54 -0700 | [diff] [blame] | 194 |   clock_gettime(CLOCK_MONOTONIC, &now); | 
| Ian Rogers | 0f67847 | 2014-03-10 16:18:37 -0700 | [diff] [blame] | 195 |   return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000000) + now.tv_nsec; | 
| Elliott Hughes | ad6c9c3 | 2012-01-19 17:39:12 -0800 | [diff] [blame] | 196 | #else | 
| Elliott Hughes | 7b9d996 | 2012-04-20 18:48:18 -0700 | [diff] [blame] | 197 |   timeval now; | 
| Elliott Hughes | ad6c9c3 | 2012-01-19 17:39:12 -0800 | [diff] [blame] | 198 |   gettimeofday(&now, NULL); | 
| Ian Rogers | 0f67847 | 2014-03-10 16:18:37 -0700 | [diff] [blame] | 199 |   return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000000) + now.tv_usec * UINT64_C(1000); | 
| Elliott Hughes | ad6c9c3 | 2012-01-19 17:39:12 -0800 | [diff] [blame] | 200 | #endif | 
| Elliott Hughes | 83df2ac | 2011-10-11 16:37:54 -0700 | [diff] [blame] | 201 | } | 
 | 202 |  | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 203 | uint64_t ThreadCpuNanoTime() { | 
 | 204 | #if defined(HAVE_POSIX_CLOCKS) | 
| Elliott Hughes | 7b9d996 | 2012-04-20 18:48:18 -0700 | [diff] [blame] | 205 |   timespec now; | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 206 |   clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now); | 
| Ian Rogers | 0f67847 | 2014-03-10 16:18:37 -0700 | [diff] [blame] | 207 |   return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000000) + now.tv_nsec; | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 208 | #else | 
 | 209 |   UNIMPLEMENTED(WARNING); | 
 | 210 |   return -1; | 
 | 211 | #endif | 
 | 212 | } | 
 | 213 |  | 
| Ian Rogers | 56edc43 | 2013-01-18 16:51:51 -0800 | [diff] [blame] | 214 | void NanoSleep(uint64_t ns) { | 
 | 215 |   timespec tm; | 
 | 216 |   tm.tv_sec = 0; | 
 | 217 |   tm.tv_nsec = ns; | 
 | 218 |   nanosleep(&tm, NULL); | 
 | 219 | } | 
 | 220 |  | 
| Brian Carlstrom | bcc2926 | 2012-11-02 11:36:03 -0700 | [diff] [blame] | 221 | void InitTimeSpec(bool absolute, int clock, int64_t ms, int32_t ns, timespec* ts) { | 
 | 222 |   int64_t endSec; | 
 | 223 |  | 
 | 224 |   if (absolute) { | 
 | 225 | #if !defined(__APPLE__) | 
 | 226 |     clock_gettime(clock, ts); | 
 | 227 | #else | 
 | 228 |     UNUSED(clock); | 
 | 229 |     timeval tv; | 
 | 230 |     gettimeofday(&tv, NULL); | 
 | 231 |     ts->tv_sec = tv.tv_sec; | 
 | 232 |     ts->tv_nsec = tv.tv_usec * 1000; | 
 | 233 | #endif | 
 | 234 |   } else { | 
 | 235 |     ts->tv_sec = 0; | 
 | 236 |     ts->tv_nsec = 0; | 
 | 237 |   } | 
 | 238 |   endSec = ts->tv_sec + ms / 1000; | 
 | 239 |   if (UNLIKELY(endSec >= 0x7fffffff)) { | 
 | 240 |     std::ostringstream ss; | 
 | 241 |     LOG(INFO) << "Note: end time exceeds epoch: " << ss.str(); | 
 | 242 |     endSec = 0x7ffffffe; | 
 | 243 |   } | 
 | 244 |   ts->tv_sec = endSec; | 
 | 245 |   ts->tv_nsec = (ts->tv_nsec + (ms % 1000) * 1000000) + ns; | 
 | 246 |  | 
 | 247 |   // Catch rollover. | 
 | 248 |   if (ts->tv_nsec >= 1000000000L) { | 
 | 249 |     ts->tv_sec++; | 
 | 250 |     ts->tv_nsec -= 1000000000L; | 
 | 251 |   } | 
 | 252 | } | 
 | 253 |  | 
| Ian Rogers | ef7d42f | 2014-01-06 12:55:46 -0800 | [diff] [blame] | 254 | std::string PrettyDescriptor(mirror::String* java_descriptor) { | 
| Brian Carlstrom | e24fa61 | 2011-09-29 00:53:55 -0700 | [diff] [blame] | 255 |   if (java_descriptor == NULL) { | 
 | 256 |     return "null"; | 
 | 257 |   } | 
| Ian Rogers | 1ff3c98 | 2014-08-12 02:30:58 -0700 | [diff] [blame] | 258 |   return PrettyDescriptor(java_descriptor->ToModifiedUtf8().c_str()); | 
| Elliott Hughes | 6c8867d | 2011-10-03 16:34:05 -0700 | [diff] [blame] | 259 | } | 
| Elliott Hughes | 5174fe6 | 2011-08-23 15:12:35 -0700 | [diff] [blame] | 260 |  | 
| Ian Rogers | ef7d42f | 2014-01-06 12:55:46 -0800 | [diff] [blame] | 261 | std::string PrettyDescriptor(mirror::Class* klass) { | 
| Ian Rogers | 6d4d9fc | 2011-11-30 16:24:48 -0800 | [diff] [blame] | 262 |   if (klass == NULL) { | 
 | 263 |     return "null"; | 
 | 264 |   } | 
| Ian Rogers | 1ff3c98 | 2014-08-12 02:30:58 -0700 | [diff] [blame] | 265 |   std::string temp; | 
 | 266 |   return PrettyDescriptor(klass->GetDescriptor(&temp)); | 
| Ian Rogers | 6d4d9fc | 2011-11-30 16:24:48 -0800 | [diff] [blame] | 267 | } | 
 | 268 |  | 
| Ian Rogers | 1ff3c98 | 2014-08-12 02:30:58 -0700 | [diff] [blame] | 269 | std::string PrettyDescriptor(const char* descriptor) { | 
| Elliott Hughes | 11e4507 | 2011-08-16 17:40:46 -0700 | [diff] [blame] | 270 |   // Count the number of '['s to get the dimensionality. | 
| Ian Rogers | 1ff3c98 | 2014-08-12 02:30:58 -0700 | [diff] [blame] | 271 |   const char* c = descriptor; | 
| Elliott Hughes | 11e4507 | 2011-08-16 17:40:46 -0700 | [diff] [blame] | 272 |   size_t dim = 0; | 
 | 273 |   while (*c == '[') { | 
 | 274 |     dim++; | 
 | 275 |     c++; | 
 | 276 |   } | 
 | 277 |  | 
 | 278 |   // Reference or primitive? | 
 | 279 |   if (*c == 'L') { | 
 | 280 |     // "[[La/b/C;" -> "a.b.C[][]". | 
| Brian Carlstrom | 7934ac2 | 2013-07-26 10:54:15 -0700 | [diff] [blame] | 281 |     c++;  // Skip the 'L'. | 
| Elliott Hughes | 11e4507 | 2011-08-16 17:40:46 -0700 | [diff] [blame] | 282 |   } else { | 
 | 283 |     // "[[B" -> "byte[][]". | 
 | 284 |     // To make life easier, we make primitives look like unqualified | 
 | 285 |     // reference types. | 
 | 286 |     switch (*c) { | 
 | 287 |     case 'B': c = "byte;"; break; | 
 | 288 |     case 'C': c = "char;"; break; | 
 | 289 |     case 'D': c = "double;"; break; | 
 | 290 |     case 'F': c = "float;"; break; | 
 | 291 |     case 'I': c = "int;"; break; | 
 | 292 |     case 'J': c = "long;"; break; | 
 | 293 |     case 'S': c = "short;"; break; | 
 | 294 |     case 'Z': c = "boolean;"; break; | 
| Brian Carlstrom | 7934ac2 | 2013-07-26 10:54:15 -0700 | [diff] [blame] | 295 |     case 'V': c = "void;"; break;  // Used when decoding return types. | 
| Elliott Hughes | 5174fe6 | 2011-08-23 15:12:35 -0700 | [diff] [blame] | 296 |     default: return descriptor; | 
| Elliott Hughes | 11e4507 | 2011-08-16 17:40:46 -0700 | [diff] [blame] | 297 |     } | 
 | 298 |   } | 
 | 299 |  | 
 | 300 |   // At this point, 'c' is a string of the form "fully/qualified/Type;" | 
 | 301 |   // or "primitive;". Rewrite the type with '.' instead of '/': | 
 | 302 |   std::string result; | 
 | 303 |   const char* p = c; | 
 | 304 |   while (*p != ';') { | 
 | 305 |     char ch = *p++; | 
 | 306 |     if (ch == '/') { | 
 | 307 |       ch = '.'; | 
 | 308 |     } | 
 | 309 |     result.push_back(ch); | 
 | 310 |   } | 
 | 311 |   // ...and replace the semicolon with 'dim' "[]" pairs: | 
| Ian Rogers | 1ff3c98 | 2014-08-12 02:30:58 -0700 | [diff] [blame] | 312 |   for (size_t i = 0; i < dim; ++i) { | 
| Elliott Hughes | 11e4507 | 2011-08-16 17:40:46 -0700 | [diff] [blame] | 313 |     result += "[]"; | 
 | 314 |   } | 
 | 315 |   return result; | 
 | 316 | } | 
 | 317 |  | 
| Ian Rogers | 68d8b42 | 2014-07-17 11:09:10 -0700 | [diff] [blame] | 318 | std::string PrettyDescriptor(Primitive::Type type) { | 
| Ian Rogers | 1ff3c98 | 2014-08-12 02:30:58 -0700 | [diff] [blame] | 319 |   return PrettyDescriptor(Primitive::Descriptor(type)); | 
| Ian Rogers | 68d8b42 | 2014-07-17 11:09:10 -0700 | [diff] [blame] | 320 | } | 
 | 321 |  | 
| Ian Rogers | ef7d42f | 2014-01-06 12:55:46 -0800 | [diff] [blame] | 322 | std::string PrettyField(mirror::ArtField* f, bool with_type) { | 
| Elliott Hughes | a250199 | 2011-08-26 19:39:54 -0700 | [diff] [blame] | 323 |   if (f == NULL) { | 
 | 324 |     return "null"; | 
 | 325 |   } | 
| Elliott Hughes | 54e7df1 | 2011-09-16 11:47:04 -0700 | [diff] [blame] | 326 |   std::string result; | 
 | 327 |   if (with_type) { | 
| Mathieu Chartier | 61c5ebc | 2014-06-05 17:42:53 -0700 | [diff] [blame] | 328 |     result += PrettyDescriptor(f->GetTypeDescriptor()); | 
| Elliott Hughes | 54e7df1 | 2011-09-16 11:47:04 -0700 | [diff] [blame] | 329 |     result += ' '; | 
 | 330 |   } | 
| Mathieu Chartier | 61c5ebc | 2014-06-05 17:42:53 -0700 | [diff] [blame] | 331 |   StackHandleScope<1> hs(Thread::Current()); | 
 | 332 |   result += PrettyDescriptor(FieldHelper(hs.NewHandle(f)).GetDeclaringClassDescriptor()); | 
| Elliott Hughes | a250199 | 2011-08-26 19:39:54 -0700 | [diff] [blame] | 333 |   result += '.'; | 
| Mathieu Chartier | 61c5ebc | 2014-06-05 17:42:53 -0700 | [diff] [blame] | 334 |   result += f->GetName(); | 
| Elliott Hughes | a250199 | 2011-08-26 19:39:54 -0700 | [diff] [blame] | 335 |   return result; | 
 | 336 | } | 
 | 337 |  | 
| Brian Carlstrom | 6f29d0e | 2012-05-11 15:50:29 -0700 | [diff] [blame] | 338 | std::string PrettyField(uint32_t field_idx, const DexFile& dex_file, bool with_type) { | 
| Elliott Hughes | 60641a7 | 2013-02-27 14:36:16 -0800 | [diff] [blame] | 339 |   if (field_idx >= dex_file.NumFieldIds()) { | 
 | 340 |     return StringPrintf("<<invalid-field-idx-%d>>", field_idx); | 
 | 341 |   } | 
| Brian Carlstrom | 6f29d0e | 2012-05-11 15:50:29 -0700 | [diff] [blame] | 342 |   const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); | 
 | 343 |   std::string result; | 
 | 344 |   if (with_type) { | 
 | 345 |     result += dex_file.GetFieldTypeDescriptor(field_id); | 
 | 346 |     result += ' '; | 
 | 347 |   } | 
 | 348 |   result += PrettyDescriptor(dex_file.GetFieldDeclaringClassDescriptor(field_id)); | 
 | 349 |   result += '.'; | 
 | 350 |   result += dex_file.GetFieldName(field_id); | 
 | 351 |   return result; | 
 | 352 | } | 
 | 353 |  | 
| Mathieu Chartier | 18c24b6 | 2012-09-10 08:54:25 -0700 | [diff] [blame] | 354 | std::string PrettyType(uint32_t type_idx, const DexFile& dex_file) { | 
| Elliott Hughes | 60641a7 | 2013-02-27 14:36:16 -0800 | [diff] [blame] | 355 |   if (type_idx >= dex_file.NumTypeIds()) { | 
 | 356 |     return StringPrintf("<<invalid-type-idx-%d>>", type_idx); | 
 | 357 |   } | 
| Mathieu Chartier | 18c24b6 | 2012-09-10 08:54:25 -0700 | [diff] [blame] | 358 |   const DexFile::TypeId& type_id = dex_file.GetTypeId(type_idx); | 
| Mathieu Chartier | 4c70d77 | 2012-09-10 14:08:32 -0700 | [diff] [blame] | 359 |   return PrettyDescriptor(dex_file.GetTypeDescriptor(type_id)); | 
| Mathieu Chartier | 18c24b6 | 2012-09-10 08:54:25 -0700 | [diff] [blame] | 360 | } | 
 | 361 |  | 
| Elliott Hughes | 9058f2b | 2012-03-22 18:06:48 -0700 | [diff] [blame] | 362 | std::string PrettyArguments(const char* signature) { | 
 | 363 |   std::string result; | 
 | 364 |   result += '('; | 
 | 365 |   CHECK_EQ(*signature, '('); | 
| Brian Carlstrom | 7934ac2 | 2013-07-26 10:54:15 -0700 | [diff] [blame] | 366 |   ++signature;  // Skip the '('. | 
| Elliott Hughes | 9058f2b | 2012-03-22 18:06:48 -0700 | [diff] [blame] | 367 |   while (*signature != ')') { | 
 | 368 |     size_t argument_length = 0; | 
 | 369 |     while (signature[argument_length] == '[') { | 
 | 370 |       ++argument_length; | 
 | 371 |     } | 
 | 372 |     if (signature[argument_length] == 'L') { | 
 | 373 |       argument_length = (strchr(signature, ';') - signature + 1); | 
 | 374 |     } else { | 
 | 375 |       ++argument_length; | 
 | 376 |     } | 
| Ian Rogers | 1ff3c98 | 2014-08-12 02:30:58 -0700 | [diff] [blame] | 377 |     { | 
 | 378 |       std::string argument_descriptor(signature, argument_length); | 
 | 379 |       result += PrettyDescriptor(argument_descriptor.c_str()); | 
 | 380 |     } | 
| Elliott Hughes | 9058f2b | 2012-03-22 18:06:48 -0700 | [diff] [blame] | 381 |     if (signature[argument_length] != ')') { | 
 | 382 |       result += ", "; | 
 | 383 |     } | 
 | 384 |     signature += argument_length; | 
 | 385 |   } | 
 | 386 |   CHECK_EQ(*signature, ')'); | 
| Brian Carlstrom | 7934ac2 | 2013-07-26 10:54:15 -0700 | [diff] [blame] | 387 |   ++signature;  // Skip the ')'. | 
| Elliott Hughes | 9058f2b | 2012-03-22 18:06:48 -0700 | [diff] [blame] | 388 |   result += ')'; | 
 | 389 |   return result; | 
 | 390 | } | 
 | 391 |  | 
 | 392 | std::string PrettyReturnType(const char* signature) { | 
 | 393 |   const char* return_type = strchr(signature, ')'); | 
 | 394 |   CHECK(return_type != NULL); | 
| Brian Carlstrom | 7934ac2 | 2013-07-26 10:54:15 -0700 | [diff] [blame] | 395 |   ++return_type;  // Skip ')'. | 
| Elliott Hughes | 9058f2b | 2012-03-22 18:06:48 -0700 | [diff] [blame] | 396 |   return PrettyDescriptor(return_type); | 
 | 397 | } | 
 | 398 |  | 
| Ian Rogers | ef7d42f | 2014-01-06 12:55:46 -0800 | [diff] [blame] | 399 | std::string PrettyMethod(mirror::ArtMethod* m, bool with_signature) { | 
| Ian Rogers | 16ce092 | 2014-01-10 14:59:36 -0800 | [diff] [blame] | 400 |   if (m == nullptr) { | 
| Elliott Hughes | a0b8feb | 2011-08-20 09:50:55 -0700 | [diff] [blame] | 401 |     return "null"; | 
 | 402 |   } | 
| Mathieu Chartier | bfd9a43 | 2014-05-21 17:43:44 -0700 | [diff] [blame] | 403 |   std::string result(PrettyDescriptor(m->GetDeclaringClassDescriptor())); | 
| Elliott Hughes | a0b8feb | 2011-08-20 09:50:55 -0700 | [diff] [blame] | 404 |   result += '.'; | 
| Mathieu Chartier | bfd9a43 | 2014-05-21 17:43:44 -0700 | [diff] [blame] | 405 |   result += m->GetName(); | 
| Ian Rogers | 16ce092 | 2014-01-10 14:59:36 -0800 | [diff] [blame] | 406 |   if (UNLIKELY(m->IsFastNative())) { | 
 | 407 |     result += "!"; | 
 | 408 |   } | 
| Elliott Hughes | a0b8feb | 2011-08-20 09:50:55 -0700 | [diff] [blame] | 409 |   if (with_signature) { | 
| Mathieu Chartier | bfd9a43 | 2014-05-21 17:43:44 -0700 | [diff] [blame] | 410 |     const Signature signature = m->GetSignature(); | 
| Ian Rogers | d91d6d6 | 2013-09-25 20:26:14 -0700 | [diff] [blame] | 411 |     std::string sig_as_string(signature.ToString()); | 
 | 412 |     if (signature == Signature::NoSignature()) { | 
 | 413 |       return result + sig_as_string; | 
| Elliott Hughes | f8c1193 | 2012-03-23 19:53:59 -0700 | [diff] [blame] | 414 |     } | 
| Ian Rogers | d91d6d6 | 2013-09-25 20:26:14 -0700 | [diff] [blame] | 415 |     result = PrettyReturnType(sig_as_string.c_str()) + " " + result + | 
 | 416 |         PrettyArguments(sig_as_string.c_str()); | 
| Elliott Hughes | a0b8feb | 2011-08-20 09:50:55 -0700 | [diff] [blame] | 417 |   } | 
 | 418 |   return result; | 
 | 419 | } | 
 | 420 |  | 
| Ian Rogers | 0571d35 | 2011-11-03 19:51:38 -0700 | [diff] [blame] | 421 | std::string PrettyMethod(uint32_t method_idx, const DexFile& dex_file, bool with_signature) { | 
| Elliott Hughes | 60641a7 | 2013-02-27 14:36:16 -0800 | [diff] [blame] | 422 |   if (method_idx >= dex_file.NumMethodIds()) { | 
 | 423 |     return StringPrintf("<<invalid-method-idx-%d>>", method_idx); | 
 | 424 |   } | 
| Ian Rogers | 0571d35 | 2011-11-03 19:51:38 -0700 | [diff] [blame] | 425 |   const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); | 
 | 426 |   std::string result(PrettyDescriptor(dex_file.GetMethodDeclaringClassDescriptor(method_id))); | 
 | 427 |   result += '.'; | 
 | 428 |   result += dex_file.GetMethodName(method_id); | 
 | 429 |   if (with_signature) { | 
| Ian Rogers | d91d6d6 | 2013-09-25 20:26:14 -0700 | [diff] [blame] | 430 |     const Signature signature = dex_file.GetMethodSignature(method_id); | 
 | 431 |     std::string sig_as_string(signature.ToString()); | 
 | 432 |     if (signature == Signature::NoSignature()) { | 
 | 433 |       return result + sig_as_string; | 
| Elliott Hughes | f8c1193 | 2012-03-23 19:53:59 -0700 | [diff] [blame] | 434 |     } | 
| Ian Rogers | d91d6d6 | 2013-09-25 20:26:14 -0700 | [diff] [blame] | 435 |     result = PrettyReturnType(sig_as_string.c_str()) + " " + result + | 
 | 436 |         PrettyArguments(sig_as_string.c_str()); | 
| Ian Rogers | 0571d35 | 2011-11-03 19:51:38 -0700 | [diff] [blame] | 437 |   } | 
 | 438 |   return result; | 
 | 439 | } | 
 | 440 |  | 
| Ian Rogers | ef7d42f | 2014-01-06 12:55:46 -0800 | [diff] [blame] | 441 | std::string PrettyTypeOf(mirror::Object* obj) { | 
| Elliott Hughes | 11e4507 | 2011-08-16 17:40:46 -0700 | [diff] [blame] | 442 |   if (obj == NULL) { | 
 | 443 |     return "null"; | 
 | 444 |   } | 
 | 445 |   if (obj->GetClass() == NULL) { | 
 | 446 |     return "(raw)"; | 
 | 447 |   } | 
| Ian Rogers | 1ff3c98 | 2014-08-12 02:30:58 -0700 | [diff] [blame] | 448 |   std::string temp; | 
 | 449 |   std::string result(PrettyDescriptor(obj->GetClass()->GetDescriptor(&temp))); | 
| Elliott Hughes | 11e4507 | 2011-08-16 17:40:46 -0700 | [diff] [blame] | 450 |   if (obj->IsClass()) { | 
| Ian Rogers | 1ff3c98 | 2014-08-12 02:30:58 -0700 | [diff] [blame] | 451 |     result += "<" + PrettyDescriptor(obj->AsClass()->GetDescriptor(&temp)) + ">"; | 
| Elliott Hughes | 11e4507 | 2011-08-16 17:40:46 -0700 | [diff] [blame] | 452 |   } | 
 | 453 |   return result; | 
 | 454 | } | 
 | 455 |  | 
| Ian Rogers | ef7d42f | 2014-01-06 12:55:46 -0800 | [diff] [blame] | 456 | std::string PrettyClass(mirror::Class* c) { | 
| Elliott Hughes | 54e7df1 | 2011-09-16 11:47:04 -0700 | [diff] [blame] | 457 |   if (c == NULL) { | 
 | 458 |     return "null"; | 
 | 459 |   } | 
 | 460 |   std::string result; | 
 | 461 |   result += "java.lang.Class<"; | 
| Ian Rogers | 6d4d9fc | 2011-11-30 16:24:48 -0800 | [diff] [blame] | 462 |   result += PrettyDescriptor(c); | 
| Elliott Hughes | 54e7df1 | 2011-09-16 11:47:04 -0700 | [diff] [blame] | 463 |   result += ">"; | 
 | 464 |   return result; | 
 | 465 | } | 
 | 466 |  | 
| Ian Rogers | ef7d42f | 2014-01-06 12:55:46 -0800 | [diff] [blame] | 467 | std::string PrettyClassAndClassLoader(mirror::Class* c) { | 
| Ian Rogers | d81871c | 2011-10-03 13:57:23 -0700 | [diff] [blame] | 468 |   if (c == NULL) { | 
 | 469 |     return "null"; | 
 | 470 |   } | 
 | 471 |   std::string result; | 
 | 472 |   result += "java.lang.Class<"; | 
| Ian Rogers | 6d4d9fc | 2011-11-30 16:24:48 -0800 | [diff] [blame] | 473 |   result += PrettyDescriptor(c); | 
| Ian Rogers | d81871c | 2011-10-03 13:57:23 -0700 | [diff] [blame] | 474 |   result += ","; | 
 | 475 |   result += PrettyTypeOf(c->GetClassLoader()); | 
 | 476 |   // TODO: add an identifying hash value for the loader | 
 | 477 |   result += ">"; | 
 | 478 |   return result; | 
 | 479 | } | 
 | 480 |  | 
| Mathieu Chartier | e6da9af | 2013-12-16 11:54:42 -0800 | [diff] [blame] | 481 | std::string PrettySize(int64_t byte_count) { | 
| Elliott Hughes | c967f78 | 2012-04-16 10:23:15 -0700 | [diff] [blame] | 482 |   // The byte thresholds at which we display amounts.  A byte count is displayed | 
 | 483 |   // in unit U when kUnitThresholds[U] <= bytes < kUnitThresholds[U+1]. | 
| Ian Rogers | ef7d42f | 2014-01-06 12:55:46 -0800 | [diff] [blame] | 484 |   static const int64_t kUnitThresholds[] = { | 
| Elliott Hughes | c967f78 | 2012-04-16 10:23:15 -0700 | [diff] [blame] | 485 |     0,              // B up to... | 
 | 486 |     3*1024,         // KB up to... | 
 | 487 |     2*1024*1024,    // MB up to... | 
 | 488 |     1024*1024*1024  // GB from here. | 
 | 489 |   }; | 
| Mathieu Chartier | e6da9af | 2013-12-16 11:54:42 -0800 | [diff] [blame] | 490 |   static const int64_t kBytesPerUnit[] = { 1, KB, MB, GB }; | 
| Elliott Hughes | c967f78 | 2012-04-16 10:23:15 -0700 | [diff] [blame] | 491 |   static const char* const kUnitStrings[] = { "B", "KB", "MB", "GB" }; | 
| Mathieu Chartier | e6da9af | 2013-12-16 11:54:42 -0800 | [diff] [blame] | 492 |   const char* negative_str = ""; | 
 | 493 |   if (byte_count < 0) { | 
 | 494 |     negative_str = "-"; | 
 | 495 |     byte_count = -byte_count; | 
 | 496 |   } | 
| Elliott Hughes | c967f78 | 2012-04-16 10:23:15 -0700 | [diff] [blame] | 497 |   int i = arraysize(kUnitThresholds); | 
 | 498 |   while (--i > 0) { | 
 | 499 |     if (byte_count >= kUnitThresholds[i]) { | 
 | 500 |       break; | 
 | 501 |     } | 
| Ian Rogers | 3bb17a6 | 2012-01-27 23:56:44 -0800 | [diff] [blame] | 502 |   } | 
| Brian Carlstrom | 474cc79 | 2014-03-07 14:18:15 -0800 | [diff] [blame] | 503 |   return StringPrintf("%s%" PRId64 "%s", | 
 | 504 |                       negative_str, byte_count / kBytesPerUnit[i], kUnitStrings[i]); | 
| Ian Rogers | 3bb17a6 | 2012-01-27 23:56:44 -0800 | [diff] [blame] | 505 | } | 
 | 506 |  | 
| Mathieu Chartier | f5997b4 | 2014-06-20 10:37:54 -0700 | [diff] [blame] | 507 | std::string PrettyDuration(uint64_t nano_duration, size_t max_fraction_digits) { | 
| Ian Rogers | 3bb17a6 | 2012-01-27 23:56:44 -0800 | [diff] [blame] | 508 |   if (nano_duration == 0) { | 
 | 509 |     return "0"; | 
 | 510 |   } else { | 
| Mathieu Chartier | f5997b4 | 2014-06-20 10:37:54 -0700 | [diff] [blame] | 511 |     return FormatDuration(nano_duration, GetAppropriateTimeUnit(nano_duration), | 
 | 512 |                           max_fraction_digits); | 
| Mathieu Chartier | 0325e62 | 2012-09-05 14:22:51 -0700 | [diff] [blame] | 513 |   } | 
 | 514 | } | 
 | 515 |  | 
 | 516 | TimeUnit GetAppropriateTimeUnit(uint64_t nano_duration) { | 
 | 517 |   const uint64_t one_sec = 1000 * 1000 * 1000; | 
 | 518 |   const uint64_t one_ms  = 1000 * 1000; | 
 | 519 |   const uint64_t one_us  = 1000; | 
 | 520 |   if (nano_duration >= one_sec) { | 
 | 521 |     return kTimeUnitSecond; | 
 | 522 |   } else if (nano_duration >= one_ms) { | 
 | 523 |     return kTimeUnitMillisecond; | 
 | 524 |   } else if (nano_duration >= one_us) { | 
 | 525 |     return kTimeUnitMicrosecond; | 
 | 526 |   } else { | 
 | 527 |     return kTimeUnitNanosecond; | 
 | 528 |   } | 
 | 529 | } | 
 | 530 |  | 
 | 531 | uint64_t GetNsToTimeUnitDivisor(TimeUnit time_unit) { | 
 | 532 |   const uint64_t one_sec = 1000 * 1000 * 1000; | 
 | 533 |   const uint64_t one_ms  = 1000 * 1000; | 
 | 534 |   const uint64_t one_us  = 1000; | 
 | 535 |  | 
 | 536 |   switch (time_unit) { | 
 | 537 |     case kTimeUnitSecond: | 
 | 538 |       return one_sec; | 
 | 539 |     case kTimeUnitMillisecond: | 
 | 540 |       return one_ms; | 
 | 541 |     case kTimeUnitMicrosecond: | 
 | 542 |       return one_us; | 
 | 543 |     case kTimeUnitNanosecond: | 
 | 544 |       return 1; | 
 | 545 |   } | 
 | 546 |   return 0; | 
 | 547 | } | 
 | 548 |  | 
| Mathieu Chartier | f5997b4 | 2014-06-20 10:37:54 -0700 | [diff] [blame] | 549 | std::string FormatDuration(uint64_t nano_duration, TimeUnit time_unit, | 
 | 550 |                            size_t max_fraction_digits) { | 
 | 551 |   const char* unit = nullptr; | 
| Mathieu Chartier | 0325e62 | 2012-09-05 14:22:51 -0700 | [diff] [blame] | 552 |   uint64_t divisor = GetNsToTimeUnitDivisor(time_unit); | 
| Mathieu Chartier | 0325e62 | 2012-09-05 14:22:51 -0700 | [diff] [blame] | 553 |   switch (time_unit) { | 
 | 554 |     case kTimeUnitSecond: | 
| Ian Rogers | 3bb17a6 | 2012-01-27 23:56:44 -0800 | [diff] [blame] | 555 |       unit = "s"; | 
| Mathieu Chartier | 0325e62 | 2012-09-05 14:22:51 -0700 | [diff] [blame] | 556 |       break; | 
 | 557 |     case kTimeUnitMillisecond: | 
| Ian Rogers | 3bb17a6 | 2012-01-27 23:56:44 -0800 | [diff] [blame] | 558 |       unit = "ms"; | 
| Mathieu Chartier | 0325e62 | 2012-09-05 14:22:51 -0700 | [diff] [blame] | 559 |       break; | 
 | 560 |     case kTimeUnitMicrosecond: | 
| Ian Rogers | 3bb17a6 | 2012-01-27 23:56:44 -0800 | [diff] [blame] | 561 |       unit = "us"; | 
| Mathieu Chartier | 0325e62 | 2012-09-05 14:22:51 -0700 | [diff] [blame] | 562 |       break; | 
 | 563 |     case kTimeUnitNanosecond: | 
| Ian Rogers | 3bb17a6 | 2012-01-27 23:56:44 -0800 | [diff] [blame] | 564 |       unit = "ns"; | 
| Mathieu Chartier | 0325e62 | 2012-09-05 14:22:51 -0700 | [diff] [blame] | 565 |       break; | 
 | 566 |   } | 
| Mathieu Chartier | f5997b4 | 2014-06-20 10:37:54 -0700 | [diff] [blame] | 567 |   const uint64_t whole_part = nano_duration / divisor; | 
| Mathieu Chartier | 0325e62 | 2012-09-05 14:22:51 -0700 | [diff] [blame] | 568 |   uint64_t fractional_part = nano_duration % divisor; | 
 | 569 |   if (fractional_part == 0) { | 
| Ian Rogers | ef7d42f | 2014-01-06 12:55:46 -0800 | [diff] [blame] | 570 |     return StringPrintf("%" PRIu64 "%s", whole_part, unit); | 
| Mathieu Chartier | 0325e62 | 2012-09-05 14:22:51 -0700 | [diff] [blame] | 571 |   } else { | 
| Mathieu Chartier | f5997b4 | 2014-06-20 10:37:54 -0700 | [diff] [blame] | 572 |     static constexpr size_t kMaxDigits = 30; | 
| Andreas Gampe | 829b4ba | 2014-06-26 13:49:36 -0700 | [diff] [blame] | 573 |     size_t avail_digits = kMaxDigits; | 
| Mathieu Chartier | f5997b4 | 2014-06-20 10:37:54 -0700 | [diff] [blame] | 574 |     char fraction_buffer[kMaxDigits]; | 
 | 575 |     char* ptr = fraction_buffer; | 
 | 576 |     uint64_t multiplier = 10; | 
 | 577 |     // This infinite loops if fractional part is 0. | 
| Andreas Gampe | 829b4ba | 2014-06-26 13:49:36 -0700 | [diff] [blame] | 578 |     while (avail_digits > 1 && fractional_part * multiplier < divisor) { | 
| Mathieu Chartier | f5997b4 | 2014-06-20 10:37:54 -0700 | [diff] [blame] | 579 |       multiplier *= 10; | 
 | 580 |       *ptr++ = '0'; | 
| Andreas Gampe | 829b4ba | 2014-06-26 13:49:36 -0700 | [diff] [blame] | 581 |       avail_digits--; | 
| Ian Rogers | 3bb17a6 | 2012-01-27 23:56:44 -0800 | [diff] [blame] | 582 |     } | 
| Andreas Gampe | 829b4ba | 2014-06-26 13:49:36 -0700 | [diff] [blame] | 583 |     snprintf(ptr, avail_digits, "%" PRIu64, fractional_part); | 
| Mathieu Chartier | f5997b4 | 2014-06-20 10:37:54 -0700 | [diff] [blame] | 584 |     fraction_buffer[std::min(kMaxDigits - 1, max_fraction_digits)] = '\0'; | 
 | 585 |     return StringPrintf("%" PRIu64 ".%s%s", whole_part, fraction_buffer, unit); | 
| Ian Rogers | 3bb17a6 | 2012-01-27 23:56:44 -0800 | [diff] [blame] | 586 |   } | 
 | 587 | } | 
 | 588 |  | 
| Ian Rogers | 576ca0c | 2014-06-06 15:58:22 -0700 | [diff] [blame] | 589 | std::string PrintableChar(uint16_t ch) { | 
 | 590 |   std::string result; | 
 | 591 |   result += '\''; | 
 | 592 |   if (NeedsEscaping(ch)) { | 
 | 593 |     StringAppendF(&result, "\\u%04x", ch); | 
 | 594 |   } else { | 
 | 595 |     result += ch; | 
 | 596 |   } | 
 | 597 |   result += '\''; | 
 | 598 |   return result; | 
 | 599 | } | 
 | 600 |  | 
| Ian Rogers | 68b5685 | 2014-08-29 20:19:11 -0700 | [diff] [blame] | 601 | std::string PrintableString(const char* utf) { | 
| Elliott Hughes | 82914b6 | 2012-04-09 15:56:29 -0700 | [diff] [blame] | 602 |   std::string result; | 
 | 603 |   result += '"'; | 
| Ian Rogers | 68b5685 | 2014-08-29 20:19:11 -0700 | [diff] [blame] | 604 |   const char* p = utf; | 
| Elliott Hughes | 82914b6 | 2012-04-09 15:56:29 -0700 | [diff] [blame] | 605 |   size_t char_count = CountModifiedUtf8Chars(p); | 
 | 606 |   for (size_t i = 0; i < char_count; ++i) { | 
 | 607 |     uint16_t ch = GetUtf16FromUtf8(&p); | 
 | 608 |     if (ch == '\\') { | 
 | 609 |       result += "\\\\"; | 
 | 610 |     } else if (ch == '\n') { | 
 | 611 |       result += "\\n"; | 
 | 612 |     } else if (ch == '\r') { | 
 | 613 |       result += "\\r"; | 
 | 614 |     } else if (ch == '\t') { | 
 | 615 |       result += "\\t"; | 
 | 616 |     } else if (NeedsEscaping(ch)) { | 
 | 617 |       StringAppendF(&result, "\\u%04x", ch); | 
 | 618 |     } else { | 
 | 619 |       result += ch; | 
 | 620 |     } | 
 | 621 |   } | 
 | 622 |   result += '"'; | 
 | 623 |   return result; | 
 | 624 | } | 
 | 625 |  | 
| Elliott Hughes | d8c00d0 | 2012-01-30 14:08:31 -0800 | [diff] [blame] | 626 | // See http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/design.html#wp615 for the full rules. | 
| Elliott Hughes | 79082e3 | 2011-08-25 12:07:32 -0700 | [diff] [blame] | 627 | std::string MangleForJni(const std::string& s) { | 
 | 628 |   std::string result; | 
 | 629 |   size_t char_count = CountModifiedUtf8Chars(s.c_str()); | 
 | 630 |   const char* cp = &s[0]; | 
 | 631 |   for (size_t i = 0; i < char_count; ++i) { | 
 | 632 |     uint16_t ch = GetUtf16FromUtf8(&cp); | 
| Elliott Hughes | d8c00d0 | 2012-01-30 14:08:31 -0800 | [diff] [blame] | 633 |     if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) { | 
 | 634 |       result.push_back(ch); | 
 | 635 |     } else if (ch == '.' || ch == '/') { | 
 | 636 |       result += "_"; | 
 | 637 |     } else if (ch == '_') { | 
 | 638 |       result += "_1"; | 
 | 639 |     } else if (ch == ';') { | 
 | 640 |       result += "_2"; | 
 | 641 |     } else if (ch == '[') { | 
 | 642 |       result += "_3"; | 
| Elliott Hughes | 79082e3 | 2011-08-25 12:07:32 -0700 | [diff] [blame] | 643 |     } else { | 
| Elliott Hughes | d8c00d0 | 2012-01-30 14:08:31 -0800 | [diff] [blame] | 644 |       StringAppendF(&result, "_0%04x", ch); | 
| Elliott Hughes | 79082e3 | 2011-08-25 12:07:32 -0700 | [diff] [blame] | 645 |     } | 
 | 646 |   } | 
 | 647 |   return result; | 
 | 648 | } | 
 | 649 |  | 
| Brian Carlstrom | f91c8c3 | 2011-09-21 17:30:34 -0700 | [diff] [blame] | 650 | std::string DotToDescriptor(const char* class_name) { | 
 | 651 |   std::string descriptor(class_name); | 
 | 652 |   std::replace(descriptor.begin(), descriptor.end(), '.', '/'); | 
 | 653 |   if (descriptor.length() > 0 && descriptor[0] != '[') { | 
 | 654 |     descriptor = "L" + descriptor + ";"; | 
 | 655 |   } | 
 | 656 |   return descriptor; | 
 | 657 | } | 
 | 658 |  | 
| Elliott Hughes | f1a5adc | 2012-02-10 18:09:35 -0800 | [diff] [blame] | 659 | std::string DescriptorToDot(const char* descriptor) { | 
| Elliott Hughes | 2435a57 | 2012-02-17 16:07:41 -0800 | [diff] [blame] | 660 |   size_t length = strlen(descriptor); | 
| Ian Rogers | 1ff3c98 | 2014-08-12 02:30:58 -0700 | [diff] [blame] | 661 |   if (length > 1) { | 
 | 662 |     if (descriptor[0] == 'L' && descriptor[length - 1] == ';') { | 
 | 663 |       // Descriptors have the leading 'L' and trailing ';' stripped. | 
 | 664 |       std::string result(descriptor + 1, length - 2); | 
 | 665 |       std::replace(result.begin(), result.end(), '/', '.'); | 
 | 666 |       return result; | 
 | 667 |     } else { | 
 | 668 |       // For arrays the 'L' and ';' remain intact. | 
 | 669 |       std::string result(descriptor); | 
 | 670 |       std::replace(result.begin(), result.end(), '/', '.'); | 
 | 671 |       return result; | 
 | 672 |     } | 
| Elliott Hughes | 2435a57 | 2012-02-17 16:07:41 -0800 | [diff] [blame] | 673 |   } | 
| Ian Rogers | 1ff3c98 | 2014-08-12 02:30:58 -0700 | [diff] [blame] | 674 |   // Do nothing for non-class/array descriptors. | 
| Elliott Hughes | 2435a57 | 2012-02-17 16:07:41 -0800 | [diff] [blame] | 675 |   return descriptor; | 
| Elliott Hughes | 91bf6cd | 2012-02-14 17:27:48 -0800 | [diff] [blame] | 676 | } | 
 | 677 |  | 
 | 678 | std::string DescriptorToName(const char* descriptor) { | 
| Elliott Hughes | f1a5adc | 2012-02-10 18:09:35 -0800 | [diff] [blame] | 679 |   size_t length = strlen(descriptor); | 
| Elliott Hughes | 2435a57 | 2012-02-17 16:07:41 -0800 | [diff] [blame] | 680 |   if (descriptor[0] == 'L' && descriptor[length - 1] == ';') { | 
 | 681 |     std::string result(descriptor + 1, length - 2); | 
 | 682 |     return result; | 
 | 683 |   } | 
 | 684 |   return descriptor; | 
| Brian Carlstrom | aded5f7 | 2011-10-07 17:15:04 -0700 | [diff] [blame] | 685 | } | 
 | 686 |  | 
| Ian Rogers | ef7d42f | 2014-01-06 12:55:46 -0800 | [diff] [blame] | 687 | std::string JniShortName(mirror::ArtMethod* m) { | 
| Mathieu Chartier | bfd9a43 | 2014-05-21 17:43:44 -0700 | [diff] [blame] | 688 |   std::string class_name(m->GetDeclaringClassDescriptor()); | 
| Elliott Hughes | 79082e3 | 2011-08-25 12:07:32 -0700 | [diff] [blame] | 689 |   // Remove the leading 'L' and trailing ';'... | 
| Elliott Hughes | f5a7a47 | 2011-10-07 14:31:02 -0700 | [diff] [blame] | 690 |   CHECK_EQ(class_name[0], 'L') << class_name; | 
 | 691 |   CHECK_EQ(class_name[class_name.size() - 1], ';') << class_name; | 
| Elliott Hughes | 79082e3 | 2011-08-25 12:07:32 -0700 | [diff] [blame] | 692 |   class_name.erase(0, 1); | 
 | 693 |   class_name.erase(class_name.size() - 1, 1); | 
 | 694 |  | 
| Mathieu Chartier | bfd9a43 | 2014-05-21 17:43:44 -0700 | [diff] [blame] | 695 |   std::string method_name(m->GetName()); | 
| Elliott Hughes | 79082e3 | 2011-08-25 12:07:32 -0700 | [diff] [blame] | 696 |  | 
 | 697 |   std::string short_name; | 
 | 698 |   short_name += "Java_"; | 
 | 699 |   short_name += MangleForJni(class_name); | 
 | 700 |   short_name += "_"; | 
 | 701 |   short_name += MangleForJni(method_name); | 
 | 702 |   return short_name; | 
 | 703 | } | 
 | 704 |  | 
| Ian Rogers | ef7d42f | 2014-01-06 12:55:46 -0800 | [diff] [blame] | 705 | std::string JniLongName(mirror::ArtMethod* m) { | 
| Elliott Hughes | 79082e3 | 2011-08-25 12:07:32 -0700 | [diff] [blame] | 706 |   std::string long_name; | 
 | 707 |   long_name += JniShortName(m); | 
 | 708 |   long_name += "__"; | 
 | 709 |  | 
| Mathieu Chartier | bfd9a43 | 2014-05-21 17:43:44 -0700 | [diff] [blame] | 710 |   std::string signature(m->GetSignature().ToString()); | 
| Elliott Hughes | 79082e3 | 2011-08-25 12:07:32 -0700 | [diff] [blame] | 711 |   signature.erase(0, 1); | 
 | 712 |   signature.erase(signature.begin() + signature.find(')'), signature.end()); | 
 | 713 |  | 
 | 714 |   long_name += MangleForJni(signature); | 
 | 715 |  | 
 | 716 |   return long_name; | 
 | 717 | } | 
 | 718 |  | 
| jeffhao | 10037c8 | 2012-01-23 15:06:23 -0800 | [diff] [blame] | 719 | // Helper for IsValidPartOfMemberNameUtf8(), a bit vector indicating valid low ascii. | 
| Elliott Hughes | 64bf5a3 | 2011-09-20 14:43:12 -0700 | [diff] [blame] | 720 | uint32_t DEX_MEMBER_VALID_LOW_ASCII[4] = { | 
| Brian Carlstrom | 7934ac2 | 2013-07-26 10:54:15 -0700 | [diff] [blame] | 721 |   0x00000000,  // 00..1f low control characters; nothing valid | 
 | 722 |   0x03ff2010,  // 20..3f digits and symbols; valid: '0'..'9', '$', '-' | 
 | 723 |   0x87fffffe,  // 40..5f uppercase etc.; valid: 'A'..'Z', '_' | 
 | 724 |   0x07fffffe   // 60..7f lowercase etc.; valid: 'a'..'z' | 
| Elliott Hughes | 64bf5a3 | 2011-09-20 14:43:12 -0700 | [diff] [blame] | 725 | }; | 
 | 726 |  | 
| jeffhao | 10037c8 | 2012-01-23 15:06:23 -0800 | [diff] [blame] | 727 | // Helper for IsValidPartOfMemberNameUtf8(); do not call directly. | 
 | 728 | bool IsValidPartOfMemberNameUtf8Slow(const char** pUtf8Ptr) { | 
| Elliott Hughes | 64bf5a3 | 2011-09-20 14:43:12 -0700 | [diff] [blame] | 729 |   /* | 
 | 730 |    * It's a multibyte encoded character. Decode it and analyze. We | 
 | 731 |    * accept anything that isn't (a) an improperly encoded low value, | 
 | 732 |    * (b) an improper surrogate pair, (c) an encoded '\0', (d) a high | 
 | 733 |    * control character, or (e) a high space, layout, or special | 
 | 734 |    * character (U+00a0, U+2000..U+200f, U+2028..U+202f, | 
 | 735 |    * U+fff0..U+ffff). This is all specified in the dex format | 
 | 736 |    * document. | 
 | 737 |    */ | 
 | 738 |  | 
 | 739 |   uint16_t utf16 = GetUtf16FromUtf8(pUtf8Ptr); | 
 | 740 |  | 
 | 741 |   // Perform follow-up tests based on the high 8 bits. | 
 | 742 |   switch (utf16 >> 8) { | 
 | 743 |   case 0x00: | 
 | 744 |     // It's only valid if it's above the ISO-8859-1 high space (0xa0). | 
 | 745 |     return (utf16 > 0x00a0); | 
 | 746 |   case 0xd8: | 
 | 747 |   case 0xd9: | 
 | 748 |   case 0xda: | 
 | 749 |   case 0xdb: | 
 | 750 |     // It's a leading surrogate. Check to see that a trailing | 
 | 751 |     // surrogate follows. | 
 | 752 |     utf16 = GetUtf16FromUtf8(pUtf8Ptr); | 
 | 753 |     return (utf16 >= 0xdc00) && (utf16 <= 0xdfff); | 
 | 754 |   case 0xdc: | 
 | 755 |   case 0xdd: | 
 | 756 |   case 0xde: | 
 | 757 |   case 0xdf: | 
 | 758 |     // It's a trailing surrogate, which is not valid at this point. | 
 | 759 |     return false; | 
 | 760 |   case 0x20: | 
 | 761 |   case 0xff: | 
 | 762 |     // It's in the range that has spaces, controls, and specials. | 
 | 763 |     switch (utf16 & 0xfff8) { | 
 | 764 |     case 0x2000: | 
 | 765 |     case 0x2008: | 
 | 766 |     case 0x2028: | 
 | 767 |     case 0xfff0: | 
 | 768 |     case 0xfff8: | 
 | 769 |       return false; | 
 | 770 |     } | 
 | 771 |     break; | 
 | 772 |   } | 
 | 773 |   return true; | 
 | 774 | } | 
 | 775 |  | 
 | 776 | /* Return whether the pointed-at modified-UTF-8 encoded character is | 
 | 777 |  * valid as part of a member name, updating the pointer to point past | 
 | 778 |  * the consumed character. This will consume two encoded UTF-16 code | 
 | 779 |  * points if the character is encoded as a surrogate pair. Also, if | 
 | 780 |  * this function returns false, then the given pointer may only have | 
 | 781 |  * been partially advanced. | 
 | 782 |  */ | 
| Ian Rogers | 8d31bbd | 2013-10-13 10:44:14 -0700 | [diff] [blame] | 783 | static bool IsValidPartOfMemberNameUtf8(const char** pUtf8Ptr) { | 
| Elliott Hughes | 64bf5a3 | 2011-09-20 14:43:12 -0700 | [diff] [blame] | 784 |   uint8_t c = (uint8_t) **pUtf8Ptr; | 
| Ian Rogers | 8d31bbd | 2013-10-13 10:44:14 -0700 | [diff] [blame] | 785 |   if (LIKELY(c <= 0x7f)) { | 
| Elliott Hughes | 64bf5a3 | 2011-09-20 14:43:12 -0700 | [diff] [blame] | 786 |     // It's low-ascii, so check the table. | 
 | 787 |     uint32_t wordIdx = c >> 5; | 
 | 788 |     uint32_t bitIdx = c & 0x1f; | 
 | 789 |     (*pUtf8Ptr)++; | 
 | 790 |     return (DEX_MEMBER_VALID_LOW_ASCII[wordIdx] & (1 << bitIdx)) != 0; | 
 | 791 |   } | 
 | 792 |  | 
 | 793 |   // It's a multibyte encoded character. Call a non-inline function | 
 | 794 |   // for the heavy lifting. | 
| jeffhao | 10037c8 | 2012-01-23 15:06:23 -0800 | [diff] [blame] | 795 |   return IsValidPartOfMemberNameUtf8Slow(pUtf8Ptr); | 
 | 796 | } | 
 | 797 |  | 
 | 798 | bool IsValidMemberName(const char* s) { | 
 | 799 |   bool angle_name = false; | 
 | 800 |  | 
| Elliott Hughes | b25c3f6 | 2012-03-26 16:35:06 -0700 | [diff] [blame] | 801 |   switch (*s) { | 
| jeffhao | 10037c8 | 2012-01-23 15:06:23 -0800 | [diff] [blame] | 802 |     case '\0': | 
 | 803 |       // The empty string is not a valid name. | 
 | 804 |       return false; | 
 | 805 |     case '<': | 
 | 806 |       angle_name = true; | 
 | 807 |       s++; | 
 | 808 |       break; | 
 | 809 |   } | 
 | 810 |  | 
 | 811 |   while (true) { | 
 | 812 |     switch (*s) { | 
 | 813 |       case '\0': | 
 | 814 |         return !angle_name; | 
 | 815 |       case '>': | 
 | 816 |         return angle_name && s[1] == '\0'; | 
 | 817 |     } | 
 | 818 |  | 
 | 819 |     if (!IsValidPartOfMemberNameUtf8(&s)) { | 
 | 820 |       return false; | 
 | 821 |     } | 
 | 822 |   } | 
| Elliott Hughes | 64bf5a3 | 2011-09-20 14:43:12 -0700 | [diff] [blame] | 823 | } | 
 | 824 |  | 
| Elliott Hughes | 906e685 | 2011-10-28 14:52:10 -0700 | [diff] [blame] | 825 | enum ClassNameType { kName, kDescriptor }; | 
| Ian Rogers | 7b078e8 | 2014-09-10 14:44:24 -0700 | [diff] [blame^] | 826 | template<ClassNameType kType, char kSeparator> | 
 | 827 | static bool IsValidClassName(const char* s) { | 
| Elliott Hughes | 64bf5a3 | 2011-09-20 14:43:12 -0700 | [diff] [blame] | 828 |   int arrayCount = 0; | 
 | 829 |   while (*s == '[') { | 
 | 830 |     arrayCount++; | 
 | 831 |     s++; | 
 | 832 |   } | 
 | 833 |  | 
 | 834 |   if (arrayCount > 255) { | 
 | 835 |     // Arrays may have no more than 255 dimensions. | 
 | 836 |     return false; | 
 | 837 |   } | 
 | 838 |  | 
| Ian Rogers | 7b078e8 | 2014-09-10 14:44:24 -0700 | [diff] [blame^] | 839 |   ClassNameType type = kType; | 
 | 840 |   if (type != kDescriptor && arrayCount != 0) { | 
| Elliott Hughes | 64bf5a3 | 2011-09-20 14:43:12 -0700 | [diff] [blame] | 841 |     /* | 
 | 842 |      * If we're looking at an array of some sort, then it doesn't | 
 | 843 |      * matter if what is being asked for is a class name; the | 
 | 844 |      * format looks the same as a type descriptor in that case, so | 
 | 845 |      * treat it as such. | 
 | 846 |      */ | 
| Elliott Hughes | 906e685 | 2011-10-28 14:52:10 -0700 | [diff] [blame] | 847 |     type = kDescriptor; | 
| Elliott Hughes | 64bf5a3 | 2011-09-20 14:43:12 -0700 | [diff] [blame] | 848 |   } | 
 | 849 |  | 
| Elliott Hughes | 906e685 | 2011-10-28 14:52:10 -0700 | [diff] [blame] | 850 |   if (type == kDescriptor) { | 
| Elliott Hughes | 64bf5a3 | 2011-09-20 14:43:12 -0700 | [diff] [blame] | 851 |     /* | 
 | 852 |      * We are looking for a descriptor. Either validate it as a | 
 | 853 |      * single-character primitive type, or continue on to check the | 
 | 854 |      * embedded class name (bracketed by "L" and ";"). | 
 | 855 |      */ | 
 | 856 |     switch (*(s++)) { | 
 | 857 |     case 'B': | 
 | 858 |     case 'C': | 
 | 859 |     case 'D': | 
 | 860 |     case 'F': | 
 | 861 |     case 'I': | 
 | 862 |     case 'J': | 
 | 863 |     case 'S': | 
 | 864 |     case 'Z': | 
 | 865 |       // These are all single-character descriptors for primitive types. | 
 | 866 |       return (*s == '\0'); | 
 | 867 |     case 'V': | 
 | 868 |       // Non-array void is valid, but you can't have an array of void. | 
 | 869 |       return (arrayCount == 0) && (*s == '\0'); | 
 | 870 |     case 'L': | 
 | 871 |       // Class name: Break out and continue below. | 
 | 872 |       break; | 
 | 873 |     default: | 
 | 874 |       // Oddball descriptor character. | 
 | 875 |       return false; | 
 | 876 |     } | 
 | 877 |   } | 
 | 878 |  | 
 | 879 |   /* | 
 | 880 |    * We just consumed the 'L' that introduces a class name as part | 
 | 881 |    * of a type descriptor, or we are looking for an unadorned class | 
 | 882 |    * name. | 
 | 883 |    */ | 
 | 884 |  | 
| Brian Carlstrom | 7934ac2 | 2013-07-26 10:54:15 -0700 | [diff] [blame] | 885 |   bool sepOrFirst = true;  // first character or just encountered a separator. | 
| Elliott Hughes | 64bf5a3 | 2011-09-20 14:43:12 -0700 | [diff] [blame] | 886 |   for (;;) { | 
 | 887 |     uint8_t c = (uint8_t) *s; | 
 | 888 |     switch (c) { | 
 | 889 |     case '\0': | 
 | 890 |       /* | 
 | 891 |        * Premature end for a type descriptor, but valid for | 
 | 892 |        * a class name as long as we haven't encountered an | 
 | 893 |        * empty component (including the degenerate case of | 
 | 894 |        * the empty string ""). | 
 | 895 |        */ | 
| Elliott Hughes | 906e685 | 2011-10-28 14:52:10 -0700 | [diff] [blame] | 896 |       return (type == kName) && !sepOrFirst; | 
| Elliott Hughes | 64bf5a3 | 2011-09-20 14:43:12 -0700 | [diff] [blame] | 897 |     case ';': | 
 | 898 |       /* | 
 | 899 |        * Invalid character for a class name, but the | 
 | 900 |        * legitimate end of a type descriptor. In the latter | 
 | 901 |        * case, make sure that this is the end of the string | 
 | 902 |        * and that it doesn't end with an empty component | 
 | 903 |        * (including the degenerate case of "L;"). | 
 | 904 |        */ | 
| Elliott Hughes | 906e685 | 2011-10-28 14:52:10 -0700 | [diff] [blame] | 905 |       return (type == kDescriptor) && !sepOrFirst && (s[1] == '\0'); | 
| Elliott Hughes | 64bf5a3 | 2011-09-20 14:43:12 -0700 | [diff] [blame] | 906 |     case '/': | 
 | 907 |     case '.': | 
| Ian Rogers | 7b078e8 | 2014-09-10 14:44:24 -0700 | [diff] [blame^] | 908 |       if (c != kSeparator) { | 
| Elliott Hughes | 64bf5a3 | 2011-09-20 14:43:12 -0700 | [diff] [blame] | 909 |         // The wrong separator character. | 
 | 910 |         return false; | 
 | 911 |       } | 
 | 912 |       if (sepOrFirst) { | 
 | 913 |         // Separator at start or two separators in a row. | 
 | 914 |         return false; | 
 | 915 |       } | 
 | 916 |       sepOrFirst = true; | 
 | 917 |       s++; | 
 | 918 |       break; | 
 | 919 |     default: | 
| jeffhao | 10037c8 | 2012-01-23 15:06:23 -0800 | [diff] [blame] | 920 |       if (!IsValidPartOfMemberNameUtf8(&s)) { | 
| Elliott Hughes | 64bf5a3 | 2011-09-20 14:43:12 -0700 | [diff] [blame] | 921 |         return false; | 
 | 922 |       } | 
 | 923 |       sepOrFirst = false; | 
 | 924 |       break; | 
 | 925 |     } | 
 | 926 |   } | 
 | 927 | } | 
 | 928 |  | 
| Elliott Hughes | 906e685 | 2011-10-28 14:52:10 -0700 | [diff] [blame] | 929 | bool IsValidBinaryClassName(const char* s) { | 
| Ian Rogers | 7b078e8 | 2014-09-10 14:44:24 -0700 | [diff] [blame^] | 930 |   return IsValidClassName<kName, '.'>(s); | 
| Elliott Hughes | 906e685 | 2011-10-28 14:52:10 -0700 | [diff] [blame] | 931 | } | 
 | 932 |  | 
 | 933 | bool IsValidJniClassName(const char* s) { | 
| Ian Rogers | 7b078e8 | 2014-09-10 14:44:24 -0700 | [diff] [blame^] | 934 |   return IsValidClassName<kName, '/'>(s); | 
| Elliott Hughes | 906e685 | 2011-10-28 14:52:10 -0700 | [diff] [blame] | 935 | } | 
 | 936 |  | 
 | 937 | bool IsValidDescriptor(const char* s) { | 
| Ian Rogers | 7b078e8 | 2014-09-10 14:44:24 -0700 | [diff] [blame^] | 938 |   return IsValidClassName<kDescriptor, '/'>(s); | 
| Elliott Hughes | 906e685 | 2011-10-28 14:52:10 -0700 | [diff] [blame] | 939 | } | 
 | 940 |  | 
| Elliott Hughes | 48436bb | 2012-02-07 15:23:28 -0800 | [diff] [blame] | 941 | void Split(const std::string& s, char separator, std::vector<std::string>& result) { | 
| Elliott Hughes | 3402380 | 2011-08-30 12:06:17 -0700 | [diff] [blame] | 942 |   const char* p = s.data(); | 
 | 943 |   const char* end = p + s.size(); | 
 | 944 |   while (p != end) { | 
| Elliott Hughes | 48436bb | 2012-02-07 15:23:28 -0800 | [diff] [blame] | 945 |     if (*p == separator) { | 
| Elliott Hughes | 3402380 | 2011-08-30 12:06:17 -0700 | [diff] [blame] | 946 |       ++p; | 
 | 947 |     } else { | 
 | 948 |       const char* start = p; | 
| Elliott Hughes | 48436bb | 2012-02-07 15:23:28 -0800 | [diff] [blame] | 949 |       while (++p != end && *p != separator) { | 
 | 950 |         // Skip to the next occurrence of the separator. | 
| Elliott Hughes | 3402380 | 2011-08-30 12:06:17 -0700 | [diff] [blame] | 951 |       } | 
 | 952 |       result.push_back(std::string(start, p - start)); | 
 | 953 |     } | 
 | 954 |   } | 
 | 955 | } | 
 | 956 |  | 
| Dave Allison | 7020278 | 2013-10-22 17:52:19 -0700 | [diff] [blame] | 957 | std::string Trim(std::string s) { | 
 | 958 |   std::string result; | 
 | 959 |   unsigned int start_index = 0; | 
 | 960 |   unsigned int end_index = s.size() - 1; | 
 | 961 |  | 
 | 962 |   // Skip initial whitespace. | 
 | 963 |   while (start_index < s.size()) { | 
 | 964 |     if (!isspace(s[start_index])) { | 
 | 965 |       break; | 
 | 966 |     } | 
 | 967 |     start_index++; | 
 | 968 |   } | 
 | 969 |  | 
 | 970 |   // Skip terminating whitespace. | 
 | 971 |   while (end_index >= start_index) { | 
 | 972 |     if (!isspace(s[end_index])) { | 
 | 973 |       break; | 
 | 974 |     } | 
 | 975 |     end_index--; | 
 | 976 |   } | 
 | 977 |  | 
 | 978 |   // All spaces, no beef. | 
 | 979 |   if (end_index < start_index) { | 
 | 980 |     return ""; | 
 | 981 |   } | 
 | 982 |   // Start_index is the first non-space, end_index is the last one. | 
 | 983 |   return s.substr(start_index, end_index - start_index + 1); | 
 | 984 | } | 
 | 985 |  | 
| Elliott Hughes | 48436bb | 2012-02-07 15:23:28 -0800 | [diff] [blame] | 986 | template <typename StringT> | 
 | 987 | std::string Join(std::vector<StringT>& strings, char separator) { | 
 | 988 |   if (strings.empty()) { | 
 | 989 |     return ""; | 
 | 990 |   } | 
 | 991 |  | 
 | 992 |   std::string result(strings[0]); | 
 | 993 |   for (size_t i = 1; i < strings.size(); ++i) { | 
 | 994 |     result += separator; | 
 | 995 |     result += strings[i]; | 
 | 996 |   } | 
 | 997 |   return result; | 
 | 998 | } | 
 | 999 |  | 
 | 1000 | // Explicit instantiations. | 
 | 1001 | template std::string Join<std::string>(std::vector<std::string>& strings, char separator); | 
 | 1002 | template std::string Join<const char*>(std::vector<const char*>& strings, char separator); | 
 | 1003 | template std::string Join<char*>(std::vector<char*>& strings, char separator); | 
 | 1004 |  | 
| Elliott Hughes | f1a5adc | 2012-02-10 18:09:35 -0800 | [diff] [blame] | 1005 | bool StartsWith(const std::string& s, const char* prefix) { | 
 | 1006 |   return s.compare(0, strlen(prefix), prefix) == 0; | 
 | 1007 | } | 
 | 1008 |  | 
| Brian Carlstrom | 7a967b3 | 2012-03-28 15:23:10 -0700 | [diff] [blame] | 1009 | bool EndsWith(const std::string& s, const char* suffix) { | 
 | 1010 |   size_t suffix_length = strlen(suffix); | 
 | 1011 |   size_t string_length = s.size(); | 
 | 1012 |   if (suffix_length > string_length) { | 
 | 1013 |     return false; | 
 | 1014 |   } | 
 | 1015 |   size_t offset = string_length - suffix_length; | 
 | 1016 |   return s.compare(offset, suffix_length, suffix) == 0; | 
 | 1017 | } | 
 | 1018 |  | 
| Elliott Hughes | 22869a9 | 2012-03-27 14:08:24 -0700 | [diff] [blame] | 1019 | void SetThreadName(const char* thread_name) { | 
| Elliott Hughes | dcc2474 | 2011-09-07 14:02:44 -0700 | [diff] [blame] | 1020 |   int hasAt = 0; | 
 | 1021 |   int hasDot = 0; | 
| Elliott Hughes | 22869a9 | 2012-03-27 14:08:24 -0700 | [diff] [blame] | 1022 |   const char* s = thread_name; | 
| Elliott Hughes | dcc2474 | 2011-09-07 14:02:44 -0700 | [diff] [blame] | 1023 |   while (*s) { | 
 | 1024 |     if (*s == '.') { | 
 | 1025 |       hasDot = 1; | 
 | 1026 |     } else if (*s == '@') { | 
 | 1027 |       hasAt = 1; | 
 | 1028 |     } | 
 | 1029 |     s++; | 
 | 1030 |   } | 
| Elliott Hughes | 22869a9 | 2012-03-27 14:08:24 -0700 | [diff] [blame] | 1031 |   int len = s - thread_name; | 
| Elliott Hughes | dcc2474 | 2011-09-07 14:02:44 -0700 | [diff] [blame] | 1032 |   if (len < 15 || hasAt || !hasDot) { | 
| Elliott Hughes | 22869a9 | 2012-03-27 14:08:24 -0700 | [diff] [blame] | 1033 |     s = thread_name; | 
| Elliott Hughes | dcc2474 | 2011-09-07 14:02:44 -0700 | [diff] [blame] | 1034 |   } else { | 
| Elliott Hughes | 22869a9 | 2012-03-27 14:08:24 -0700 | [diff] [blame] | 1035 |     s = thread_name + len - 15; | 
| Elliott Hughes | dcc2474 | 2011-09-07 14:02:44 -0700 | [diff] [blame] | 1036 |   } | 
| Elliott Hughes | 49e36ec | 2014-08-20 20:18:18 -0700 | [diff] [blame] | 1037 | #if defined(__BIONIC__) | 
| Elliott Hughes | 7c6a61e | 2012-03-12 18:01:41 -0700 | [diff] [blame] | 1038 |   // pthread_setname_np fails rather than truncating long strings. | 
| Elliott Hughes | dcc2474 | 2011-09-07 14:02:44 -0700 | [diff] [blame] | 1039 |   char buf[16];       // MAX_TASK_COMM_LEN=16 is hard-coded into bionic | 
 | 1040 |   strncpy(buf, s, sizeof(buf)-1); | 
 | 1041 |   buf[sizeof(buf)-1] = '\0'; | 
 | 1042 |   errno = pthread_setname_np(pthread_self(), buf); | 
 | 1043 |   if (errno != 0) { | 
 | 1044 |     PLOG(WARNING) << "Unable to set the name of current thread to '" << buf << "'"; | 
 | 1045 |   } | 
| Elliott Hughes | 4ae722a | 2012-03-13 11:08:51 -0700 | [diff] [blame] | 1046 | #elif defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 | 
| Elliott Hughes | 22869a9 | 2012-03-27 14:08:24 -0700 | [diff] [blame] | 1047 |   pthread_setname_np(thread_name); | 
| Elliott Hughes | dcc2474 | 2011-09-07 14:02:44 -0700 | [diff] [blame] | 1048 | #elif defined(HAVE_PRCTL) | 
| Elliott Hughes | 398f64b | 2012-03-26 18:05:48 -0700 | [diff] [blame] | 1049 |   prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0);  // NOLINT (unsigned long) | 
| Elliott Hughes | dcc2474 | 2011-09-07 14:02:44 -0700 | [diff] [blame] | 1050 | #else | 
| Elliott Hughes | 22869a9 | 2012-03-27 14:08:24 -0700 | [diff] [blame] | 1051 |   UNIMPLEMENTED(WARNING) << thread_name; | 
| Elliott Hughes | dcc2474 | 2011-09-07 14:02:44 -0700 | [diff] [blame] | 1052 | #endif | 
 | 1053 | } | 
 | 1054 |  | 
| Brian Carlstrom | 2921201 | 2013-09-12 22:18:30 -0700 | [diff] [blame] | 1055 | void GetTaskStats(pid_t tid, char* state, int* utime, int* stime, int* task_cpu) { | 
 | 1056 |   *utime = *stime = *task_cpu = 0; | 
| Elliott Hughes | bfe487b | 2011-10-26 15:48:55 -0700 | [diff] [blame] | 1057 |   std::string stats; | 
| Elliott Hughes | 8a31b50 | 2012-04-30 19:36:11 -0700 | [diff] [blame] | 1058 |   if (!ReadFileToString(StringPrintf("/proc/self/task/%d/stat", tid), &stats)) { | 
| Elliott Hughes | bfe487b | 2011-10-26 15:48:55 -0700 | [diff] [blame] | 1059 |     return; | 
 | 1060 |   } | 
 | 1061 |   // Skip the command, which may contain spaces. | 
 | 1062 |   stats = stats.substr(stats.find(')') + 2); | 
 | 1063 |   // Extract the three fields we care about. | 
 | 1064 |   std::vector<std::string> fields; | 
 | 1065 |   Split(stats, ' ', fields); | 
| Brian Carlstrom | 2921201 | 2013-09-12 22:18:30 -0700 | [diff] [blame] | 1066 |   *state = fields[0][0]; | 
 | 1067 |   *utime = strtoull(fields[11].c_str(), NULL, 10); | 
 | 1068 |   *stime = strtoull(fields[12].c_str(), NULL, 10); | 
 | 1069 |   *task_cpu = strtoull(fields[36].c_str(), NULL, 10); | 
| Elliott Hughes | bfe487b | 2011-10-26 15:48:55 -0700 | [diff] [blame] | 1070 | } | 
 | 1071 |  | 
| Elliott Hughes | 1bac54f | 2012-03-16 12:48:31 -0700 | [diff] [blame] | 1072 | std::string GetSchedulerGroupName(pid_t tid) { | 
 | 1073 |   // /proc/<pid>/cgroup looks like this: | 
 | 1074 |   // 2:devices:/ | 
 | 1075 |   // 1:cpuacct,cpu:/ | 
 | 1076 |   // We want the third field from the line whose second field contains the "cpu" token. | 
 | 1077 |   std::string cgroup_file; | 
 | 1078 |   if (!ReadFileToString(StringPrintf("/proc/self/task/%d/cgroup", tid), &cgroup_file)) { | 
 | 1079 |     return ""; | 
 | 1080 |   } | 
 | 1081 |   std::vector<std::string> cgroup_lines; | 
 | 1082 |   Split(cgroup_file, '\n', cgroup_lines); | 
 | 1083 |   for (size_t i = 0; i < cgroup_lines.size(); ++i) { | 
 | 1084 |     std::vector<std::string> cgroup_fields; | 
 | 1085 |     Split(cgroup_lines[i], ':', cgroup_fields); | 
 | 1086 |     std::vector<std::string> cgroups; | 
 | 1087 |     Split(cgroup_fields[1], ',', cgroups); | 
 | 1088 |     for (size_t i = 0; i < cgroups.size(); ++i) { | 
 | 1089 |       if (cgroups[i] == "cpu") { | 
| Brian Carlstrom | 7934ac2 | 2013-07-26 10:54:15 -0700 | [diff] [blame] | 1090 |         return cgroup_fields[2].substr(1);  // Skip the leading slash. | 
| Elliott Hughes | 1bac54f | 2012-03-16 12:48:31 -0700 | [diff] [blame] | 1091 |       } | 
 | 1092 |     } | 
 | 1093 |   } | 
 | 1094 |   return ""; | 
 | 1095 | } | 
 | 1096 |  | 
| Christopher Ferris | a2cee18 | 2014-04-16 19:13:59 -0700 | [diff] [blame] | 1097 | void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix, | 
| Kenny Root | 067d20f | 2014-03-05 14:57:21 -0800 | [diff] [blame] | 1098 |     mirror::ArtMethod* current_method) { | 
| Ian Rogers | c5f1773 | 2014-06-05 20:48:42 -0700 | [diff] [blame] | 1099 | #ifdef __linux__ | 
| Ian Rogers | 700a402 | 2014-05-19 16:49:03 -0700 | [diff] [blame] | 1100 |   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid)); | 
| Christopher Ferris | 7b5f0cf | 2013-11-01 15:18:45 -0700 | [diff] [blame] | 1101 |   if (!backtrace->Unwind(0)) { | 
 | 1102 |     os << prefix << "(backtrace::Unwind failed for thread " << tid << ")\n"; | 
| Elliott Hughes | 46e251b | 2012-05-22 15:10:45 -0700 | [diff] [blame] | 1103 |     return; | 
| Christopher Ferris | 7b5f0cf | 2013-11-01 15:18:45 -0700 | [diff] [blame] | 1104 |   } else if (backtrace->NumFrames() == 0) { | 
| Elliott Hughes | 225f5a1 | 2012-06-11 11:23:48 -0700 | [diff] [blame] | 1105 |     os << prefix << "(no native stack frames for thread " << tid << ")\n"; | 
| Elliott Hughes | 46e251b | 2012-05-22 15:10:45 -0700 | [diff] [blame] | 1106 |     return; | 
 | 1107 |   } | 
 | 1108 |  | 
| Christopher Ferris | 943af7d | 2014-01-16 12:41:46 -0800 | [diff] [blame] | 1109 |   for (Backtrace::const_iterator it = backtrace->begin(); | 
 | 1110 |        it != backtrace->end(); ++it) { | 
| Elliott Hughes | 46e251b | 2012-05-22 15:10:45 -0700 | [diff] [blame] | 1111 |     // We produce output like this: | 
| Christopher Ferris | a2cee18 | 2014-04-16 19:13:59 -0700 | [diff] [blame] | 1112 |     // ]    #00 pc 000075bb8  /system/lib/libc.so (unwind_backtrace_thread+536) | 
 | 1113 |     // In order for parsing tools to continue to function, the stack dump | 
 | 1114 |     // format must at least adhere to this format: | 
 | 1115 |     //  #XX pc <RELATIVE_ADDR>  <FULL_PATH_TO_SHARED_LIBRARY> ... | 
 | 1116 |     // The parsers require a single space before and after pc, and two spaces | 
 | 1117 |     // after the <RELATIVE_ADDR>. There can be any prefix data before the | 
 | 1118 |     // #XX. <RELATIVE_ADDR> has to be a hex number but with no 0x prefix. | 
 | 1119 |     os << prefix << StringPrintf("#%02zu pc ", it->num); | 
 | 1120 |     if (!it->map) { | 
 | 1121 |       os << StringPrintf("%08" PRIxPTR "  ???", it->pc); | 
| Christopher Ferris | 7b5f0cf | 2013-11-01 15:18:45 -0700 | [diff] [blame] | 1122 |     } else { | 
| Christopher Ferris | a2cee18 | 2014-04-16 19:13:59 -0700 | [diff] [blame] | 1123 |       os << StringPrintf("%08" PRIxPTR "  ", it->pc - it->map->start) | 
 | 1124 |          << it->map->name << " ("; | 
 | 1125 |       if (!it->func_name.empty()) { | 
 | 1126 |         os << it->func_name; | 
 | 1127 |         if (it->func_offset != 0) { | 
 | 1128 |           os << "+" << it->func_offset; | 
 | 1129 |         } | 
| Hiroshi Yamauchi | 7895d55 | 2014-08-28 14:55:56 -0700 | [diff] [blame] | 1130 |       } else if (current_method != nullptr && | 
 | 1131 |                  Locks::mutator_lock_->IsSharedHeld(Thread::Current()) && | 
 | 1132 |                  current_method->IsWithinQuickCode(it->pc)) { | 
| Brian Carlstrom | 474cc79 | 2014-03-07 14:18:15 -0800 | [diff] [blame] | 1133 |         const void* start_of_code = current_method->GetEntryPointFromQuickCompiledCode(); | 
 | 1134 |         os << JniLongName(current_method) << "+" | 
 | 1135 |            << (it->pc - reinterpret_cast<uintptr_t>(start_of_code)); | 
| Kenny Root | 067d20f | 2014-03-05 14:57:21 -0800 | [diff] [blame] | 1136 |       } else { | 
 | 1137 |         os << "???"; | 
 | 1138 |       } | 
| Christopher Ferris | a2cee18 | 2014-04-16 19:13:59 -0700 | [diff] [blame] | 1139 |       os << ")"; | 
| Elliott Hughes | 46e251b | 2012-05-22 15:10:45 -0700 | [diff] [blame] | 1140 |     } | 
| Christopher Ferris | a2cee18 | 2014-04-16 19:13:59 -0700 | [diff] [blame] | 1141 |     os << "\n"; | 
| Elliott Hughes | 46e251b | 2012-05-22 15:10:45 -0700 | [diff] [blame] | 1142 |   } | 
| Ian Rogers | c5f1773 | 2014-06-05 20:48:42 -0700 | [diff] [blame] | 1143 | #endif | 
| Elliott Hughes | 46e251b | 2012-05-22 15:10:45 -0700 | [diff] [blame] | 1144 | } | 
 | 1145 |  | 
| Elliott Hughes | 058a6de | 2012-05-24 19:13:02 -0700 | [diff] [blame] | 1146 | #if defined(__APPLE__) | 
 | 1147 |  | 
 | 1148 | // TODO: is there any way to get the kernel stack on Mac OS? | 
 | 1149 | void DumpKernelStack(std::ostream&, pid_t, const char*, bool) {} | 
 | 1150 |  | 
 | 1151 | #else | 
 | 1152 |  | 
| Elliott Hughes | 46e251b | 2012-05-22 15:10:45 -0700 | [diff] [blame] | 1153 | void DumpKernelStack(std::ostream& os, pid_t tid, const char* prefix, bool include_count) { | 
| Elliott Hughes | 12a9502 | 2012-05-24 21:41:38 -0700 | [diff] [blame] | 1154 |   if (tid == GetTid()) { | 
 | 1155 |     // There's no point showing that we're reading our stack out of /proc! | 
 | 1156 |     return; | 
 | 1157 |   } | 
 | 1158 |  | 
| Elliott Hughes | 46e251b | 2012-05-22 15:10:45 -0700 | [diff] [blame] | 1159 |   std::string kernel_stack_filename(StringPrintf("/proc/self/task/%d/stack", tid)); | 
 | 1160 |   std::string kernel_stack; | 
 | 1161 |   if (!ReadFileToString(kernel_stack_filename, &kernel_stack)) { | 
| Elliott Hughes | 058a6de | 2012-05-24 19:13:02 -0700 | [diff] [blame] | 1162 |     os << prefix << "(couldn't read " << kernel_stack_filename << ")\n"; | 
| jeffhao | c4c3ee2 | 2012-05-25 16:16:32 -0700 | [diff] [blame] | 1163 |     return; | 
| Elliott Hughes | 46e251b | 2012-05-22 15:10:45 -0700 | [diff] [blame] | 1164 |   } | 
 | 1165 |  | 
 | 1166 |   std::vector<std::string> kernel_stack_frames; | 
 | 1167 |   Split(kernel_stack, '\n', kernel_stack_frames); | 
 | 1168 |   // We skip the last stack frame because it's always equivalent to "[<ffffffff>] 0xffffffff", | 
 | 1169 |   // which looking at the source appears to be the kernel's way of saying "that's all, folks!". | 
 | 1170 |   kernel_stack_frames.pop_back(); | 
 | 1171 |   for (size_t i = 0; i < kernel_stack_frames.size(); ++i) { | 
| Brian Carlstrom | 474cc79 | 2014-03-07 14:18:15 -0800 | [diff] [blame] | 1172 |     // Turn "[<ffffffff8109156d>] futex_wait_queue_me+0xcd/0x110" | 
 | 1173 |     // into "futex_wait_queue_me+0xcd/0x110". | 
| Elliott Hughes | 46e251b | 2012-05-22 15:10:45 -0700 | [diff] [blame] | 1174 |     const char* text = kernel_stack_frames[i].c_str(); | 
 | 1175 |     const char* close_bracket = strchr(text, ']'); | 
 | 1176 |     if (close_bracket != NULL) { | 
 | 1177 |       text = close_bracket + 2; | 
 | 1178 |     } | 
 | 1179 |     os << prefix; | 
 | 1180 |     if (include_count) { | 
 | 1181 |       os << StringPrintf("#%02zd ", i); | 
 | 1182 |     } | 
 | 1183 |     os << text << "\n"; | 
 | 1184 |   } | 
 | 1185 | } | 
 | 1186 |  | 
 | 1187 | #endif | 
 | 1188 |  | 
| Brian Carlstrom | a56fcd6 | 2012-02-04 21:23:01 -0800 | [diff] [blame] | 1189 | const char* GetAndroidRoot() { | 
 | 1190 |   const char* android_root = getenv("ANDROID_ROOT"); | 
 | 1191 |   if (android_root == NULL) { | 
 | 1192 |     if (OS::DirectoryExists("/system")) { | 
 | 1193 |       android_root = "/system"; | 
| Brian Carlstrom | a9f1978 | 2011-10-13 00:14:47 -0700 | [diff] [blame] | 1194 |     } else { | 
| Brian Carlstrom | a56fcd6 | 2012-02-04 21:23:01 -0800 | [diff] [blame] | 1195 |       LOG(FATAL) << "ANDROID_ROOT not set and /system does not exist"; | 
 | 1196 |       return ""; | 
| Brian Carlstrom | a9f1978 | 2011-10-13 00:14:47 -0700 | [diff] [blame] | 1197 |     } | 
 | 1198 |   } | 
| Brian Carlstrom | a56fcd6 | 2012-02-04 21:23:01 -0800 | [diff] [blame] | 1199 |   if (!OS::DirectoryExists(android_root)) { | 
 | 1200 |     LOG(FATAL) << "Failed to find ANDROID_ROOT directory " << android_root; | 
| Brian Carlstrom | a9f1978 | 2011-10-13 00:14:47 -0700 | [diff] [blame] | 1201 |     return ""; | 
 | 1202 |   } | 
| Brian Carlstrom | a56fcd6 | 2012-02-04 21:23:01 -0800 | [diff] [blame] | 1203 |   return android_root; | 
 | 1204 | } | 
| Brian Carlstrom | a9f1978 | 2011-10-13 00:14:47 -0700 | [diff] [blame] | 1205 |  | 
| Brian Carlstrom | a56fcd6 | 2012-02-04 21:23:01 -0800 | [diff] [blame] | 1206 | const char* GetAndroidData() { | 
| Alex Light | a59dd80 | 2014-07-02 16:28:08 -0700 | [diff] [blame] | 1207 |   std::string error_msg; | 
 | 1208 |   const char* dir = GetAndroidDataSafe(&error_msg); | 
 | 1209 |   if (dir != nullptr) { | 
 | 1210 |     return dir; | 
 | 1211 |   } else { | 
 | 1212 |     LOG(FATAL) << error_msg; | 
 | 1213 |     return ""; | 
 | 1214 |   } | 
 | 1215 | } | 
 | 1216 |  | 
 | 1217 | const char* GetAndroidDataSafe(std::string* error_msg) { | 
| Brian Carlstrom | a56fcd6 | 2012-02-04 21:23:01 -0800 | [diff] [blame] | 1218 |   const char* android_data = getenv("ANDROID_DATA"); | 
 | 1219 |   if (android_data == NULL) { | 
 | 1220 |     if (OS::DirectoryExists("/data")) { | 
 | 1221 |       android_data = "/data"; | 
 | 1222 |     } else { | 
| Alex Light | a59dd80 | 2014-07-02 16:28:08 -0700 | [diff] [blame] | 1223 |       *error_msg = "ANDROID_DATA not set and /data does not exist"; | 
 | 1224 |       return nullptr; | 
| Brian Carlstrom | a56fcd6 | 2012-02-04 21:23:01 -0800 | [diff] [blame] | 1225 |     } | 
 | 1226 |   } | 
 | 1227 |   if (!OS::DirectoryExists(android_data)) { | 
| Alex Light | a59dd80 | 2014-07-02 16:28:08 -0700 | [diff] [blame] | 1228 |     *error_msg = StringPrintf("Failed to find ANDROID_DATA directory %s", android_data); | 
 | 1229 |     return nullptr; | 
| Brian Carlstrom | a56fcd6 | 2012-02-04 21:23:01 -0800 | [diff] [blame] | 1230 |   } | 
 | 1231 |   return android_data; | 
 | 1232 | } | 
 | 1233 |  | 
| Alex Light | a59dd80 | 2014-07-02 16:28:08 -0700 | [diff] [blame] | 1234 | void GetDalvikCache(const char* subdir, const bool create_if_absent, std::string* dalvik_cache, | 
 | 1235 |                     bool* have_android_data, bool* dalvik_cache_exists) { | 
 | 1236 |   CHECK(subdir != nullptr); | 
 | 1237 |   std::string error_msg; | 
 | 1238 |   const char* android_data = GetAndroidDataSafe(&error_msg); | 
 | 1239 |   if (android_data == nullptr) { | 
 | 1240 |     *have_android_data = false; | 
 | 1241 |     *dalvik_cache_exists = false; | 
 | 1242 |     return; | 
 | 1243 |   } else { | 
 | 1244 |     *have_android_data = true; | 
 | 1245 |   } | 
 | 1246 |   const std::string dalvik_cache_root(StringPrintf("%s/dalvik-cache/", android_data)); | 
 | 1247 |   *dalvik_cache = dalvik_cache_root + subdir; | 
 | 1248 |   *dalvik_cache_exists = OS::DirectoryExists(dalvik_cache->c_str()); | 
 | 1249 |   if (create_if_absent && !*dalvik_cache_exists && strcmp(android_data, "/data") != 0) { | 
 | 1250 |     // Don't create the system's /data/dalvik-cache/... because it needs special permissions. | 
 | 1251 |     *dalvik_cache_exists = ((mkdir(dalvik_cache_root.c_str(), 0700) == 0 || errno == EEXIST) && | 
 | 1252 |                             (mkdir(dalvik_cache->c_str(), 0700) == 0 || errno == EEXIST)); | 
 | 1253 |   } | 
 | 1254 | } | 
 | 1255 |  | 
| Narayan Kamath | 11d9f06 | 2014-04-23 20:24:57 +0100 | [diff] [blame] | 1256 | std::string GetDalvikCacheOrDie(const char* subdir, const bool create_if_absent) { | 
 | 1257 |   CHECK(subdir != nullptr); | 
| Brian Carlstrom | 41ccffd | 2014-05-06 10:37:30 -0700 | [diff] [blame] | 1258 |   const char* android_data = GetAndroidData(); | 
 | 1259 |   const std::string dalvik_cache_root(StringPrintf("%s/dalvik-cache/", android_data)); | 
| Narayan Kamath | 11d9f06 | 2014-04-23 20:24:57 +0100 | [diff] [blame] | 1260 |   const std::string dalvik_cache = dalvik_cache_root + subdir; | 
 | 1261 |   if (create_if_absent && !OS::DirectoryExists(dalvik_cache.c_str())) { | 
| Brian Carlstrom | 41ccffd | 2014-05-06 10:37:30 -0700 | [diff] [blame] | 1262 |     // Don't create the system's /data/dalvik-cache/... because it needs special permissions. | 
 | 1263 |     if (strcmp(android_data, "/data") != 0) { | 
| Narayan Kamath | 11d9f06 | 2014-04-23 20:24:57 +0100 | [diff] [blame] | 1264 |       int result = mkdir(dalvik_cache_root.c_str(), 0700); | 
| Narayan Kamath | ef204fa | 2014-04-30 17:25:23 +0100 | [diff] [blame] | 1265 |       if (result != 0 && errno != EEXIST) { | 
| Narayan Kamath | 11d9f06 | 2014-04-23 20:24:57 +0100 | [diff] [blame] | 1266 |         PLOG(FATAL) << "Failed to create dalvik-cache directory " << dalvik_cache_root; | 
 | 1267 |         return ""; | 
 | 1268 |       } | 
 | 1269 |       result = mkdir(dalvik_cache.c_str(), 0700); | 
 | 1270 |       if (result != 0) { | 
 | 1271 |         PLOG(FATAL) << "Failed to create dalvik-cache directory " << dalvik_cache; | 
| Brian Carlstrom | a9f1978 | 2011-10-13 00:14:47 -0700 | [diff] [blame] | 1272 |         return ""; | 
 | 1273 |       } | 
 | 1274 |     } else { | 
| Brian Carlstrom | 7675e16 | 2013-06-10 16:18:04 -0700 | [diff] [blame] | 1275 |       LOG(FATAL) << "Failed to find dalvik-cache directory " << dalvik_cache; | 
| Brian Carlstrom | a9f1978 | 2011-10-13 00:14:47 -0700 | [diff] [blame] | 1276 |       return ""; | 
 | 1277 |     } | 
 | 1278 |   } | 
| Brian Carlstrom | 7675e16 | 2013-06-10 16:18:04 -0700 | [diff] [blame] | 1279 |   return dalvik_cache; | 
| Brian Carlstrom | a9f1978 | 2011-10-13 00:14:47 -0700 | [diff] [blame] | 1280 | } | 
 | 1281 |  | 
| Alex Light | a59dd80 | 2014-07-02 16:28:08 -0700 | [diff] [blame] | 1282 | bool GetDalvikCacheFilename(const char* location, const char* cache_location, | 
 | 1283 |                             std::string* filename, std::string* error_msg) { | 
| Ian Rogers | e606010 | 2013-05-16 12:01:04 -0700 | [diff] [blame] | 1284 |   if (location[0] != '/') { | 
| Alex Light | a59dd80 | 2014-07-02 16:28:08 -0700 | [diff] [blame] | 1285 |     *error_msg = StringPrintf("Expected path in location to be absolute: %s", location); | 
 | 1286 |     return false; | 
| Ian Rogers | e606010 | 2013-05-16 12:01:04 -0700 | [diff] [blame] | 1287 |   } | 
| Ian Rogers | 8d31bbd | 2013-10-13 10:44:14 -0700 | [diff] [blame] | 1288 |   std::string cache_file(&location[1]);  // skip leading slash | 
| Alex Light | 6e183f2 | 2014-07-18 14:57:04 -0700 | [diff] [blame] | 1289 |   if (!EndsWith(location, ".dex") && !EndsWith(location, ".art") && !EndsWith(location, ".oat")) { | 
| Brian Carlstrom | 30e2ea4 | 2013-06-19 23:25:37 -0700 | [diff] [blame] | 1290 |     cache_file += "/"; | 
 | 1291 |     cache_file += DexFile::kClassesDex; | 
 | 1292 |   } | 
| Brian Carlstrom | b7bbba4 | 2011-10-13 14:58:47 -0700 | [diff] [blame] | 1293 |   std::replace(cache_file.begin(), cache_file.end(), '/', '@'); | 
| Alex Light | a59dd80 | 2014-07-02 16:28:08 -0700 | [diff] [blame] | 1294 |   *filename = StringPrintf("%s/%s", cache_location, cache_file.c_str()); | 
 | 1295 |   return true; | 
 | 1296 | } | 
 | 1297 |  | 
 | 1298 | std::string GetDalvikCacheFilenameOrDie(const char* location, const char* cache_location) { | 
 | 1299 |   std::string ret; | 
 | 1300 |   std::string error_msg; | 
 | 1301 |   if (!GetDalvikCacheFilename(location, cache_location, &ret, &error_msg)) { | 
 | 1302 |     LOG(FATAL) << error_msg; | 
 | 1303 |   } | 
 | 1304 |   return ret; | 
| Brian Carlstrom | b7bbba4 | 2011-10-13 14:58:47 -0700 | [diff] [blame] | 1305 | } | 
 | 1306 |  | 
| Brian Carlstrom | 2afe494 | 2014-05-19 10:25:33 -0700 | [diff] [blame] | 1307 | static void InsertIsaDirectory(const InstructionSet isa, std::string* filename) { | 
| Brian Carlstrom | 0e12bdc | 2014-05-14 17:44:28 -0700 | [diff] [blame] | 1308 |   // in = /foo/bar/baz | 
 | 1309 |   // out = /foo/bar/<isa>/baz | 
 | 1310 |   size_t pos = filename->rfind('/'); | 
 | 1311 |   CHECK_NE(pos, std::string::npos) << *filename << " " << isa; | 
 | 1312 |   filename->insert(pos, "/", 1); | 
 | 1313 |   filename->insert(pos + 1, GetInstructionSetString(isa)); | 
 | 1314 | } | 
 | 1315 |  | 
 | 1316 | std::string GetSystemImageFilename(const char* location, const InstructionSet isa) { | 
 | 1317 |   // location = /system/framework/boot.art | 
 | 1318 |   // filename = /system/framework/<isa>/boot.art | 
 | 1319 |   std::string filename(location); | 
| Brian Carlstrom | 2afe494 | 2014-05-19 10:25:33 -0700 | [diff] [blame] | 1320 |   InsertIsaDirectory(isa, &filename); | 
| Brian Carlstrom | 0e12bdc | 2014-05-14 17:44:28 -0700 | [diff] [blame] | 1321 |   return filename; | 
 | 1322 | } | 
 | 1323 |  | 
 | 1324 | std::string DexFilenameToOdexFilename(const std::string& location, const InstructionSet isa) { | 
 | 1325 |   // location = /foo/bar/baz.jar | 
 | 1326 |   // odex_location = /foo/bar/<isa>/baz.odex | 
| Andreas Gampe | 833a485 | 2014-05-21 18:46:59 -0700 | [diff] [blame] | 1327 |  | 
| Brian Carlstrom | 0e12bdc | 2014-05-14 17:44:28 -0700 | [diff] [blame] | 1328 |   CHECK_GE(location.size(), 4U) << location;  // must be at least .123 | 
 | 1329 |   std::string odex_location(location); | 
| Brian Carlstrom | 2afe494 | 2014-05-19 10:25:33 -0700 | [diff] [blame] | 1330 |   InsertIsaDirectory(isa, &odex_location); | 
| Brian Carlstrom | 0e12bdc | 2014-05-14 17:44:28 -0700 | [diff] [blame] | 1331 |   size_t dot_index = odex_location.size() - 3 - 1;  // 3=dex or zip or apk | 
 | 1332 |   CHECK_EQ('.', odex_location[dot_index]) << location; | 
 | 1333 |   odex_location.resize(dot_index + 1); | 
 | 1334 |   CHECK_EQ('.', odex_location[odex_location.size()-1]) << location << " " << odex_location; | 
 | 1335 |   odex_location += "odex"; | 
 | 1336 |   return odex_location; | 
 | 1337 | } | 
 | 1338 |  | 
| Brian Carlstrom | 7c3d13a | 2013-09-04 17:15:11 -0700 | [diff] [blame] | 1339 | bool IsZipMagic(uint32_t magic) { | 
 | 1340 |   return (('P' == ((magic >> 0) & 0xff)) && | 
 | 1341 |           ('K' == ((magic >> 8) & 0xff))); | 
| jeffhao | 262bf46 | 2011-10-20 18:36:32 -0700 | [diff] [blame] | 1342 | } | 
 | 1343 |  | 
| Brian Carlstrom | 7c3d13a | 2013-09-04 17:15:11 -0700 | [diff] [blame] | 1344 | bool IsDexMagic(uint32_t magic) { | 
 | 1345 |   return DexFile::IsMagicValid(reinterpret_cast<const byte*>(&magic)); | 
| Brian Carlstrom | 7a967b3 | 2012-03-28 15:23:10 -0700 | [diff] [blame] | 1346 | } | 
 | 1347 |  | 
| Brian Carlstrom | 7c3d13a | 2013-09-04 17:15:11 -0700 | [diff] [blame] | 1348 | bool IsOatMagic(uint32_t magic) { | 
 | 1349 |   return (memcmp(reinterpret_cast<const byte*>(magic), | 
 | 1350 |                  OatHeader::kOatMagic, | 
 | 1351 |                  sizeof(OatHeader::kOatMagic)) == 0); | 
| jeffhao | 262bf46 | 2011-10-20 18:36:32 -0700 | [diff] [blame] | 1352 | } | 
 | 1353 |  | 
| Brian Carlstrom | 6449c62 | 2014-02-10 23:48:36 -0800 | [diff] [blame] | 1354 | bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) { | 
 | 1355 |   const std::string command_line(Join(arg_vector, ' ')); | 
 | 1356 |  | 
 | 1357 |   CHECK_GE(arg_vector.size(), 1U) << command_line; | 
 | 1358 |  | 
 | 1359 |   // Convert the args to char pointers. | 
 | 1360 |   const char* program = arg_vector[0].c_str(); | 
 | 1361 |   std::vector<char*> args; | 
| Brian Carlstrom | 35d8b8e | 2014-02-25 10:51:11 -0800 | [diff] [blame] | 1362 |   for (size_t i = 0; i < arg_vector.size(); ++i) { | 
 | 1363 |     const std::string& arg = arg_vector[i]; | 
 | 1364 |     char* arg_str = const_cast<char*>(arg.c_str()); | 
 | 1365 |     CHECK(arg_str != nullptr) << i; | 
 | 1366 |     args.push_back(arg_str); | 
| Brian Carlstrom | 6449c62 | 2014-02-10 23:48:36 -0800 | [diff] [blame] | 1367 |   } | 
 | 1368 |   args.push_back(NULL); | 
 | 1369 |  | 
 | 1370 |   // fork and exec | 
 | 1371 |   pid_t pid = fork(); | 
 | 1372 |   if (pid == 0) { | 
 | 1373 |     // no allocation allowed between fork and exec | 
 | 1374 |  | 
 | 1375 |     // change process groups, so we don't get reaped by ProcessManager | 
 | 1376 |     setpgid(0, 0); | 
 | 1377 |  | 
 | 1378 |     execv(program, &args[0]); | 
 | 1379 |  | 
| Brian Carlstrom | 13db9aa | 2014-02-27 12:44:32 -0800 | [diff] [blame] | 1380 |     PLOG(ERROR) << "Failed to execv(" << command_line << ")"; | 
 | 1381 |     exit(1); | 
| Brian Carlstrom | 6449c62 | 2014-02-10 23:48:36 -0800 | [diff] [blame] | 1382 |   } else { | 
 | 1383 |     if (pid == -1) { | 
 | 1384 |       *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s", | 
 | 1385 |                                 command_line.c_str(), strerror(errno)); | 
 | 1386 |       return false; | 
 | 1387 |     } | 
 | 1388 |  | 
 | 1389 |     // wait for subprocess to finish | 
 | 1390 |     int status; | 
 | 1391 |     pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); | 
 | 1392 |     if (got_pid != pid) { | 
 | 1393 |       *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: " | 
 | 1394 |                                 "wanted %d, got %d: %s", | 
 | 1395 |                                 command_line.c_str(), pid, got_pid, strerror(errno)); | 
 | 1396 |       return false; | 
 | 1397 |     } | 
 | 1398 |     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { | 
 | 1399 |       *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status", | 
 | 1400 |                                 command_line.c_str()); | 
 | 1401 |       return false; | 
 | 1402 |     } | 
 | 1403 |   } | 
 | 1404 |   return true; | 
 | 1405 | } | 
 | 1406 |  | 
| Tong Shen | 547cdfd | 2014-08-05 01:54:19 -0700 | [diff] [blame] | 1407 | void EncodeUnsignedLeb128(uint32_t data, std::vector<uint8_t>* dst) { | 
| Yevgeny Rouban | e3ea838 | 2014-08-08 16:29:38 +0700 | [diff] [blame] | 1408 |   Leb128Encoder(dst).PushBackUnsigned(data); | 
| Tong Shen | 547cdfd | 2014-08-05 01:54:19 -0700 | [diff] [blame] | 1409 | } | 
 | 1410 |  | 
 | 1411 | void EncodeSignedLeb128(int32_t data, std::vector<uint8_t>* dst) { | 
| Yevgeny Rouban | e3ea838 | 2014-08-08 16:29:38 +0700 | [diff] [blame] | 1412 |   Leb128Encoder(dst).PushBackSigned(data); | 
| Tong Shen | 547cdfd | 2014-08-05 01:54:19 -0700 | [diff] [blame] | 1413 | } | 
 | 1414 |  | 
 | 1415 | void PushWord(std::vector<uint8_t>* buf, int data) { | 
 | 1416 |   buf->push_back(data & 0xff); | 
 | 1417 |   buf->push_back((data >> 8) & 0xff); | 
 | 1418 |   buf->push_back((data >> 16) & 0xff); | 
 | 1419 |   buf->push_back((data >> 24) & 0xff); | 
 | 1420 | } | 
 | 1421 |  | 
| Elliott Hughes | 42ee142 | 2011-09-06 12:33:32 -0700 | [diff] [blame] | 1422 | }  // namespace art |