| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 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 |  | 
| Andreas Gampe | 277ccbd | 2014-11-03 21:36:10 -0800 | [diff] [blame] | 17 | #include "dalvik_system_VMDebug.h" | 
 | 18 |  | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 19 | #include <string.h> | 
 | 20 | #include <unistd.h> | 
 | 21 |  | 
| Hiroshi Yamauchi | a1c9f01 | 2015-04-02 10:18:12 -0700 | [diff] [blame] | 22 | #include <sstream> | 
 | 23 |  | 
 | 24 | #include "base/histogram-inl.h" | 
| Vladimir Marko | 80afd02 | 2015-05-19 18:08:00 +0100 | [diff] [blame] | 25 | #include "base/time_utils.h" | 
| Elliott Hughes | eac7667 | 2012-05-24 21:56:51 -0700 | [diff] [blame] | 26 | #include "class_linker.h" | 
| Ian Rogers | 62d6c77 | 2013-02-27 08:32:07 -0800 | [diff] [blame] | 27 | #include "common_throws.h" | 
| Elliott Hughes | eac7667 | 2012-05-24 21:56:51 -0700 | [diff] [blame] | 28 | #include "debugger.h" | 
| Mathieu Chartier | 7410f29 | 2013-11-24 13:17:35 -0800 | [diff] [blame] | 29 | #include "gc/space/bump_pointer_space.h" | 
| Hiroshi Yamauchi | 09b07a9 | 2013-07-15 13:17:06 -0700 | [diff] [blame] | 30 | #include "gc/space/dlmalloc_space.h" | 
 | 31 | #include "gc/space/large_object_space.h" | 
 | 32 | #include "gc/space/space-inl.h" | 
| Mathieu Chartier | 1f3b535 | 2014-02-03 14:00:42 -0800 | [diff] [blame] | 33 | #include "gc/space/zygote_space.h" | 
| Elliott Hughes | eac7667 | 2012-05-24 21:56:51 -0700 | [diff] [blame] | 34 | #include "hprof/hprof.h" | 
 | 35 | #include "jni_internal.h" | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 36 | #include "mirror/class.h" | 
| Ian Rogers | dd157d7 | 2014-05-15 14:47:50 -0700 | [diff] [blame] | 37 | #include "ScopedLocalRef.h" | 
| Elliott Hughes | eac7667 | 2012-05-24 21:56:51 -0700 | [diff] [blame] | 38 | #include "ScopedUtfChars.h" | 
| Ian Rogers | 53b8b09 | 2014-03-13 23:45:53 -0700 | [diff] [blame] | 39 | #include "scoped_fast_native_object_access.h" | 
| Elliott Hughes | eac7667 | 2012-05-24 21:56:51 -0700 | [diff] [blame] | 40 | #include "trace.h" | 
| Ian Rogers | dd157d7 | 2014-05-15 14:47:50 -0700 | [diff] [blame] | 41 | #include "well_known_classes.h" | 
| Elliott Hughes | eac7667 | 2012-05-24 21:56:51 -0700 | [diff] [blame] | 42 |  | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 43 | namespace art { | 
 | 44 |  | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 45 | static jobjectArray VMDebug_getVmFeatureList(JNIEnv* env, jclass) { | 
| Ian Rogers | dd157d7 | 2014-05-15 14:47:50 -0700 | [diff] [blame] | 46 |   static const char* features[] = { | 
 | 47 |     "method-trace-profiling", | 
 | 48 |     "method-trace-profiling-streaming", | 
 | 49 |     "method-sample-profiling", | 
 | 50 |     "hprof-heap-dump", | 
 | 51 |     "hprof-heap-dump-streaming", | 
 | 52 |   }; | 
 | 53 |   jobjectArray result = env->NewObjectArray(arraysize(features), | 
 | 54 |                                             WellKnownClasses::java_lang_String, | 
 | 55 |                                             nullptr); | 
 | 56 |   if (result != nullptr) { | 
 | 57 |     for (size_t i = 0; i < arraysize(features); ++i) { | 
 | 58 |       ScopedLocalRef<jstring> jfeature(env, env->NewStringUTF(features[i])); | 
 | 59 |       if (jfeature.get() == nullptr) { | 
 | 60 |         return nullptr; | 
 | 61 |       } | 
 | 62 |       env->SetObjectArrayElement(result, i, jfeature.get()); | 
 | 63 |     } | 
 | 64 |   } | 
 | 65 |   return result; | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 66 | } | 
 | 67 |  | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 68 | static void VMDebug_startAllocCounting(JNIEnv*, jclass) { | 
| Mathieu Chartier | 9ef78b5 | 2014-09-25 17:03:12 -0700 | [diff] [blame] | 69 |   Runtime::Current()->SetStatsEnabled(true); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 70 | } | 
 | 71 |  | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 72 | static void VMDebug_stopAllocCounting(JNIEnv*, jclass) { | 
| Mathieu Chartier | 9ef78b5 | 2014-09-25 17:03:12 -0700 | [diff] [blame] | 73 |   Runtime::Current()->SetStatsEnabled(false); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 74 | } | 
 | 75 |  | 
| Elliott Hughes | 1bac54f | 2012-03-16 12:48:31 -0700 | [diff] [blame] | 76 | static jint VMDebug_getAllocCount(JNIEnv*, jclass, jint kind) { | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 77 |   return Runtime::Current()->GetStat(kind); | 
 | 78 | } | 
 | 79 |  | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 80 | static void VMDebug_resetAllocCount(JNIEnv*, jclass, jint kinds) { | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 81 |   Runtime::Current()->ResetStats(kinds); | 
 | 82 | } | 
 | 83 |  | 
| Jeff Hao | 23009dc | 2013-08-22 15:36:42 -0700 | [diff] [blame] | 84 | static void VMDebug_startMethodTracingDdmsImpl(JNIEnv*, jclass, jint bufferSize, jint flags, | 
 | 85 |                                                jboolean samplingEnabled, jint intervalUs) { | 
| Andreas Gampe | 7e7e0f4 | 2015-03-29 15:26:23 -0700 | [diff] [blame] | 86 |   Trace::Start("[DDMS]", -1, bufferSize, flags, Trace::TraceOutputMode::kDDMS, | 
 | 87 |                samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, | 
 | 88 |                intervalUs); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 89 | } | 
 | 90 |  | 
| Ian Rogers | 00f7d0e | 2012-07-19 15:28:27 -0700 | [diff] [blame] | 91 | static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceFilename, | 
| Jeff Hao | 4044bda | 2014-01-06 15:50:45 -0800 | [diff] [blame] | 92 |                                          jobject javaFd, jint bufferSize, jint flags, | 
 | 93 |                                          jboolean samplingEnabled, jint intervalUs) { | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 94 |   int originalFd = jniGetFDFromFileDescriptor(env, javaFd); | 
 | 95 |   if (originalFd < 0) { | 
 | 96 |     return; | 
 | 97 |   } | 
 | 98 |  | 
 | 99 |   int fd = dup(originalFd); | 
 | 100 |   if (fd < 0) { | 
| Ian Rogers | 00f7d0e | 2012-07-19 15:28:27 -0700 | [diff] [blame] | 101 |     ScopedObjectAccess soa(env); | 
| Nicolas Geoffray | 0aa50ce | 2015-03-10 11:03:29 +0000 | [diff] [blame] | 102 |     soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", | 
| Ian Rogers | 62d6c77 | 2013-02-27 08:32:07 -0800 | [diff] [blame] | 103 |                                    "dup(%d) failed: %s", originalFd, strerror(errno)); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 104 |     return; | 
 | 105 |   } | 
 | 106 |  | 
 | 107 |   ScopedUtfChars traceFilename(env, javaTraceFilename); | 
| Mathieu Chartier | 2cebb24 | 2015-04-21 16:50:40 -0700 | [diff] [blame] | 108 |   if (traceFilename.c_str() == nullptr) { | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 109 |     return; | 
 | 110 |   } | 
| Andreas Gampe | 7e7e0f4 | 2015-03-29 15:26:23 -0700 | [diff] [blame] | 111 |   Trace::Start(traceFilename.c_str(), fd, bufferSize, flags, Trace::TraceOutputMode::kFile, | 
 | 112 |                samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, | 
 | 113 |                intervalUs); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 114 | } | 
 | 115 |  | 
| Ian Rogers | 00f7d0e | 2012-07-19 15:28:27 -0700 | [diff] [blame] | 116 | static void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring javaTraceFilename, | 
| Jeff Hao | 4044bda | 2014-01-06 15:50:45 -0800 | [diff] [blame] | 117 |                                                jint bufferSize, jint flags, | 
 | 118 |                                                jboolean samplingEnabled, jint intervalUs) { | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 119 |   ScopedUtfChars traceFilename(env, javaTraceFilename); | 
| Mathieu Chartier | 2cebb24 | 2015-04-21 16:50:40 -0700 | [diff] [blame] | 120 |   if (traceFilename.c_str() == nullptr) { | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 121 |     return; | 
 | 122 |   } | 
| Andreas Gampe | 7e7e0f4 | 2015-03-29 15:26:23 -0700 | [diff] [blame] | 123 |   Trace::Start(traceFilename.c_str(), -1, bufferSize, flags, Trace::TraceOutputMode::kFile, | 
 | 124 |                samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, | 
 | 125 |                intervalUs); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 126 | } | 
 | 127 |  | 
| Jeff Hao | 64caa7d | 2013-08-29 11:18:01 -0700 | [diff] [blame] | 128 | static jint VMDebug_getMethodTracingMode(JNIEnv*, jclass) { | 
 | 129 |   return Trace::GetMethodTracingMode(); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 130 | } | 
 | 131 |  | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 132 | static void VMDebug_stopMethodTracing(JNIEnv*, jclass) { | 
| jeffhao | e343b76 | 2011-12-05 16:36:44 -0800 | [diff] [blame] | 133 |   Trace::Stop(); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 134 | } | 
 | 135 |  | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 136 | static void VMDebug_startEmulatorTracing(JNIEnv*, jclass) { | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 137 |   UNIMPLEMENTED(WARNING); | 
| Brian Carlstrom | 7934ac2 | 2013-07-26 10:54:15 -0700 | [diff] [blame] | 138 |   // dvmEmulatorTraceStart(); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 139 | } | 
 | 140 |  | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 141 | static void VMDebug_stopEmulatorTracing(JNIEnv*, jclass) { | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 142 |   UNIMPLEMENTED(WARNING); | 
| Brian Carlstrom | 7934ac2 | 2013-07-26 10:54:15 -0700 | [diff] [blame] | 143 |   // dvmEmulatorTraceStop(); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 144 | } | 
 | 145 |  | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 146 | static jboolean VMDebug_isDebuggerConnected(JNIEnv*, jclass) { | 
| Elliott Hughes | c0f0933 | 2012-03-26 13:27:06 -0700 | [diff] [blame] | 147 |   return Dbg::IsDebuggerActive(); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 148 | } | 
 | 149 |  | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 150 | static jboolean VMDebug_isDebuggingEnabled(JNIEnv*, jclass) { | 
| Elliott Hughes | c0f0933 | 2012-03-26 13:27:06 -0700 | [diff] [blame] | 151 |   return Dbg::IsJdwpConfigured(); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 152 | } | 
 | 153 |  | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 154 | static jlong VMDebug_lastDebuggerActivity(JNIEnv*, jclass) { | 
| Elliott Hughes | 872d4ec | 2011-10-21 17:07:15 -0700 | [diff] [blame] | 155 |   return Dbg::LastDebuggerActivity(); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 156 | } | 
 | 157 |  | 
| Ian Rogers | 62d6c77 | 2013-02-27 08:32:07 -0800 | [diff] [blame] | 158 | static void ThrowUnsupportedOperationException(JNIEnv* env) { | 
| Ian Rogers | 00f7d0e | 2012-07-19 15:28:27 -0700 | [diff] [blame] | 159 |   ScopedObjectAccess soa(env); | 
| Mathieu Chartier | 2cebb24 | 2015-04-21 16:50:40 -0700 | [diff] [blame] | 160 |   soa.Self()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", nullptr); | 
| Ian Rogers | 62d6c77 | 2013-02-27 08:32:07 -0800 | [diff] [blame] | 161 | } | 
 | 162 |  | 
 | 163 | static void VMDebug_startInstructionCounting(JNIEnv* env, jclass) { | 
 | 164 |   ThrowUnsupportedOperationException(env); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 165 | } | 
 | 166 |  | 
| Ian Rogers | 00f7d0e | 2012-07-19 15:28:27 -0700 | [diff] [blame] | 167 | static void VMDebug_stopInstructionCounting(JNIEnv* env, jclass) { | 
| Ian Rogers | 62d6c77 | 2013-02-27 08:32:07 -0800 | [diff] [blame] | 168 |   ThrowUnsupportedOperationException(env); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 169 | } | 
 | 170 |  | 
| Ian Rogers | 00f7d0e | 2012-07-19 15:28:27 -0700 | [diff] [blame] | 171 | static void VMDebug_getInstructionCount(JNIEnv* env, jclass, jintArray /*javaCounts*/) { | 
| Ian Rogers | 62d6c77 | 2013-02-27 08:32:07 -0800 | [diff] [blame] | 172 |   ThrowUnsupportedOperationException(env); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 173 | } | 
 | 174 |  | 
| Ian Rogers | 00f7d0e | 2012-07-19 15:28:27 -0700 | [diff] [blame] | 175 | static void VMDebug_resetInstructionCount(JNIEnv* env, jclass) { | 
| Ian Rogers | 62d6c77 | 2013-02-27 08:32:07 -0800 | [diff] [blame] | 176 |   ThrowUnsupportedOperationException(env); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 177 | } | 
 | 178 |  | 
| Ian Rogers | 00f7d0e | 2012-07-19 15:28:27 -0700 | [diff] [blame] | 179 | static void VMDebug_printLoadedClasses(JNIEnv* env, jclass, jint flags) { | 
| Ian Rogers | 53b8b09 | 2014-03-13 23:45:53 -0700 | [diff] [blame] | 180 |   ScopedFastNativeObjectAccess soa(env); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 181 |   return Runtime::Current()->GetClassLinker()->DumpAllClasses(flags); | 
 | 182 | } | 
 | 183 |  | 
| Ian Rogers | 7dfb28c | 2013-08-22 08:18:36 -0700 | [diff] [blame] | 184 | static jint VMDebug_getLoadedClassCount(JNIEnv* env, jclass) { | 
| Ian Rogers | 53b8b09 | 2014-03-13 23:45:53 -0700 | [diff] [blame] | 185 |   ScopedFastNativeObjectAccess soa(env); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 186 |   return Runtime::Current()->GetClassLinker()->NumLoadedClasses(); | 
 | 187 | } | 
 | 188 |  | 
 | 189 | /* | 
 | 190 |  * Returns the thread-specific CPU-time clock value for the current thread, | 
 | 191 |  * or -1 if the feature isn't supported. | 
 | 192 |  */ | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 193 | static jlong VMDebug_threadCpuTimeNanos(JNIEnv*, jclass) { | 
 | 194 |   return ThreadCpuNanoTime(); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 195 | } | 
 | 196 |  | 
 | 197 | /* | 
 | 198 |  * static void dumpHprofData(String fileName, FileDescriptor fd) | 
 | 199 |  * | 
 | 200 |  * Cause "hprof" data to be dumped.  We can throw an IOException if an | 
 | 201 |  * error occurs during file handling. | 
 | 202 |  */ | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 203 | static void VMDebug_dumpHprofData(JNIEnv* env, jclass, jstring javaFilename, jobject javaFd) { | 
| Mathieu Chartier | 2cebb24 | 2015-04-21 16:50:40 -0700 | [diff] [blame] | 204 |   // Only one of these may be null. | 
 | 205 |   if (javaFilename == nullptr && javaFd == nullptr) { | 
| Ian Rogers | 00f7d0e | 2012-07-19 15:28:27 -0700 | [diff] [blame] | 206 |     ScopedObjectAccess soa(env); | 
| Nicolas Geoffray | 0aa50ce | 2015-03-10 11:03:29 +0000 | [diff] [blame] | 207 |     ThrowNullPointerException("fileName == null && fd == null"); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 208 |     return; | 
 | 209 |   } | 
 | 210 |  | 
 | 211 |   std::string filename; | 
| Mathieu Chartier | 2cebb24 | 2015-04-21 16:50:40 -0700 | [diff] [blame] | 212 |   if (javaFilename != nullptr) { | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 213 |     ScopedUtfChars chars(env, javaFilename); | 
 | 214 |     if (env->ExceptionCheck()) { | 
 | 215 |       return; | 
 | 216 |     } | 
 | 217 |     filename = chars.c_str(); | 
 | 218 |   } else { | 
 | 219 |     filename = "[fd]"; | 
 | 220 |   } | 
 | 221 |  | 
 | 222 |   int fd = -1; | 
| Mathieu Chartier | 2cebb24 | 2015-04-21 16:50:40 -0700 | [diff] [blame] | 223 |   if (javaFd != nullptr) { | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 224 |     fd = jniGetFDFromFileDescriptor(env, javaFd); | 
 | 225 |     if (fd < 0) { | 
| Ian Rogers | 00f7d0e | 2012-07-19 15:28:27 -0700 | [diff] [blame] | 226 |       ScopedObjectAccess soa(env); | 
| Ian Rogers | 62d6c77 | 2013-02-27 08:32:07 -0800 | [diff] [blame] | 227 |       ThrowRuntimeException("Invalid file descriptor"); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 228 |       return; | 
 | 229 |     } | 
 | 230 |   } | 
 | 231 |  | 
| Elliott Hughes | 622a698 | 2012-06-08 17:58:54 -0700 | [diff] [blame] | 232 |   hprof::DumpHeap(filename.c_str(), fd, false); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 233 | } | 
 | 234 |  | 
| Elliott Hughes | eac7667 | 2012-05-24 21:56:51 -0700 | [diff] [blame] | 235 | static void VMDebug_dumpHprofDataDdms(JNIEnv*, jclass) { | 
| Elliott Hughes | 622a698 | 2012-06-08 17:58:54 -0700 | [diff] [blame] | 236 |   hprof::DumpHeap("[DDMS]", -1, true); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 237 | } | 
 | 238 |  | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 239 | static void VMDebug_dumpReferenceTables(JNIEnv* env, jclass) { | 
| Ian Rogers | 00f7d0e | 2012-07-19 15:28:27 -0700 | [diff] [blame] | 240 |   ScopedObjectAccess soa(env); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 241 |   LOG(INFO) << "--- reference table dump ---"; | 
 | 242 |  | 
| Ian Rogers | 00f7d0e | 2012-07-19 15:28:27 -0700 | [diff] [blame] | 243 |   soa.Env()->DumpReferenceTables(LOG(INFO)); | 
 | 244 |   soa.Vm()->DumpReferenceTables(LOG(INFO)); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 245 |  | 
 | 246 |   LOG(INFO) << "---"; | 
 | 247 | } | 
 | 248 |  | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 249 | static void VMDebug_crash(JNIEnv*, jclass) { | 
| Elliott Hughes | 81ff318 | 2012-03-23 20:35:56 -0700 | [diff] [blame] | 250 |   LOG(FATAL) << "Crashing runtime on request"; | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 251 | } | 
 | 252 |  | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 253 | static void VMDebug_infopoint(JNIEnv*, jclass, jint id) { | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 254 |   LOG(INFO) << "VMDebug infopoint " << id << " hit"; | 
 | 255 | } | 
 | 256 |  | 
| Ian Rogers | 00f7d0e | 2012-07-19 15:28:27 -0700 | [diff] [blame] | 257 | static jlong VMDebug_countInstancesOfClass(JNIEnv* env, jclass, jclass javaClass, | 
 | 258 |                                            jboolean countAssignable) { | 
 | 259 |   ScopedObjectAccess soa(env); | 
| Mathieu Chartier | f182085 | 2015-07-10 13:19:51 -0700 | [diff] [blame] | 260 |   gc::Heap* const heap = Runtime::Current()->GetHeap(); | 
 | 261 |   // Caller's responsibility to do GC if desired. | 
| Ian Rogers | 2dd0e2c | 2013-01-24 12:42:14 -0800 | [diff] [blame] | 262 |   mirror::Class* c = soa.Decode<mirror::Class*>(javaClass); | 
| Mathieu Chartier | 412c7fc | 2014-02-07 12:18:39 -0800 | [diff] [blame] | 263 |   if (c == nullptr) { | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 264 |     return 0; | 
 | 265 |   } | 
| Mathieu Chartier | f182085 | 2015-07-10 13:19:51 -0700 | [diff] [blame] | 266 |   std::vector<mirror::Class*> classes {c}; | 
| Elliott Hughes | ec0f83d | 2013-01-15 16:54:08 -0800 | [diff] [blame] | 267 |   uint64_t count = 0; | 
| Mathieu Chartier | 412c7fc | 2014-02-07 12:18:39 -0800 | [diff] [blame] | 268 |   heap->CountInstances(classes, countAssignable, &count); | 
| Elliott Hughes | ec0f83d | 2013-01-15 16:54:08 -0800 | [diff] [blame] | 269 |   return count; | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 270 | } | 
 | 271 |  | 
| Mathieu Chartier | f182085 | 2015-07-10 13:19:51 -0700 | [diff] [blame] | 272 | static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env, jclass, jobjectArray javaClasses, | 
 | 273 |                                                   jboolean countAssignable) { | 
 | 274 |   ScopedObjectAccess soa(env); | 
 | 275 |   gc::Heap* const heap = Runtime::Current()->GetHeap(); | 
 | 276 |   // Caller's responsibility to do GC if desired. | 
 | 277 |   auto* decoded_classes = soa.Decode<mirror::ObjectArray<mirror::Class>*>(javaClasses); | 
 | 278 |   if (decoded_classes == nullptr) { | 
 | 279 |     return nullptr; | 
 | 280 |   } | 
 | 281 |   std::vector<mirror::Class*> classes; | 
 | 282 |   for (size_t i = 0, count = decoded_classes->GetLength(); i < count; ++i) { | 
 | 283 |     classes.push_back(decoded_classes->Get(i)); | 
 | 284 |   } | 
 | 285 |   std::vector<uint64_t> counts(classes.size(), 0u); | 
 | 286 |   // Heap::CountInstances can handle null and will put 0 for these classes. | 
 | 287 |   heap->CountInstances(classes, countAssignable, &counts[0]); | 
 | 288 |   auto* long_counts = mirror::LongArray::Alloc(soa.Self(), counts.size()); | 
 | 289 |   if (long_counts == nullptr) { | 
 | 290 |     soa.Self()->AssertPendingOOMException(); | 
 | 291 |     return nullptr; | 
 | 292 |   } | 
 | 293 |   for (size_t i = 0; i < counts.size(); ++i) { | 
 | 294 |     long_counts->Set(i, counts[i]); | 
 | 295 |   } | 
 | 296 |   return soa.AddLocalReference<jlongArray>(long_counts); | 
 | 297 | } | 
 | 298 |  | 
| Hiroshi Yamauchi | 09b07a9 | 2013-07-15 13:17:06 -0700 | [diff] [blame] | 299 | // We export the VM internal per-heap-space size/alloc/free metrics | 
 | 300 | // for the zygote space, alloc space (application heap), and the large | 
 | 301 | // object space for dumpsys meminfo. The other memory region data such | 
 | 302 | // as PSS, private/shared dirty/shared data are available via | 
 | 303 | // /proc/<pid>/smaps. | 
 | 304 | static void VMDebug_getHeapSpaceStats(JNIEnv* env, jclass, jlongArray data) { | 
| Ian Rogers | 6b99dd1 | 2013-07-25 12:11:32 -0700 | [diff] [blame] | 305 |   jlong* arr = reinterpret_cast<jlong*>(env->GetPrimitiveArrayCritical(data, 0)); | 
| Mathieu Chartier | 7410f29 | 2013-11-24 13:17:35 -0800 | [diff] [blame] | 306 |   if (arr == nullptr || env->GetArrayLength(data) < 9) { | 
| Hiroshi Yamauchi | 09b07a9 | 2013-07-15 13:17:06 -0700 | [diff] [blame] | 307 |     return; | 
 | 308 |   } | 
 | 309 |  | 
 | 310 |   size_t allocSize = 0; | 
 | 311 |   size_t allocUsed = 0; | 
 | 312 |   size_t zygoteSize = 0; | 
 | 313 |   size_t zygoteUsed = 0; | 
 | 314 |   size_t largeObjectsSize = 0; | 
 | 315 |   size_t largeObjectsUsed = 0; | 
| Hiroshi Yamauchi | 09b07a9 | 2013-07-15 13:17:06 -0700 | [diff] [blame] | 316 |   gc::Heap* heap = Runtime::Current()->GetHeap(); | 
| Mathieu Chartier | a9d82fe | 2016-01-25 20:06:11 -0800 | [diff] [blame] | 317 |   { | 
 | 318 |     ScopedObjectAccess soa(env); | 
 | 319 |     for (gc::space::ContinuousSpace* space : heap->GetContinuousSpaces()) { | 
 | 320 |       if (space->IsImageSpace()) { | 
 | 321 |         // Currently don't include the image space. | 
 | 322 |       } else if (space->IsZygoteSpace()) { | 
 | 323 |         gc::space::ZygoteSpace* zygote_space = space->AsZygoteSpace(); | 
 | 324 |         zygoteSize += zygote_space->Size(); | 
 | 325 |         zygoteUsed += zygote_space->GetBytesAllocated(); | 
 | 326 |       } else if (space->IsMallocSpace()) { | 
 | 327 |         // This is a malloc space. | 
 | 328 |         gc::space::MallocSpace* malloc_space = space->AsMallocSpace(); | 
 | 329 |         allocSize += malloc_space->GetFootprint(); | 
 | 330 |         allocUsed += malloc_space->GetBytesAllocated(); | 
 | 331 |       } else if (space->IsBumpPointerSpace()) { | 
 | 332 |         gc::space::BumpPointerSpace* bump_pointer_space = space->AsBumpPointerSpace(); | 
 | 333 |         allocSize += bump_pointer_space->Size(); | 
 | 334 |         allocUsed += bump_pointer_space->GetBytesAllocated(); | 
 | 335 |       } | 
 | 336 |     } | 
 | 337 |     for (gc::space::DiscontinuousSpace* space : heap->GetDiscontinuousSpaces()) { | 
 | 338 |       if (space->IsLargeObjectSpace()) { | 
 | 339 |         largeObjectsSize += space->AsLargeObjectSpace()->GetBytesAllocated(); | 
 | 340 |         largeObjectsUsed += largeObjectsSize; | 
 | 341 |       } | 
| Hiroshi Yamauchi | 09b07a9 | 2013-07-15 13:17:06 -0700 | [diff] [blame] | 342 |     } | 
 | 343 |   } | 
| Hiroshi Yamauchi | 09b07a9 | 2013-07-15 13:17:06 -0700 | [diff] [blame] | 344 |   size_t allocFree = allocSize - allocUsed; | 
 | 345 |   size_t zygoteFree = zygoteSize - zygoteUsed; | 
 | 346 |   size_t largeObjectsFree = largeObjectsSize - largeObjectsUsed; | 
 | 347 |  | 
 | 348 |   int j = 0; | 
 | 349 |   arr[j++] = allocSize; | 
 | 350 |   arr[j++] = allocUsed; | 
 | 351 |   arr[j++] = allocFree; | 
 | 352 |   arr[j++] = zygoteSize; | 
 | 353 |   arr[j++] = zygoteUsed; | 
 | 354 |   arr[j++] = zygoteFree; | 
 | 355 |   arr[j++] = largeObjectsSize; | 
 | 356 |   arr[j++] = largeObjectsUsed; | 
 | 357 |   arr[j++] = largeObjectsFree; | 
 | 358 |   env->ReleasePrimitiveArrayCritical(data, arr, 0); | 
 | 359 | } | 
 | 360 |  | 
| Hiroshi Yamauchi | a1c9f01 | 2015-04-02 10:18:12 -0700 | [diff] [blame] | 361 | // The runtime stat names for VMDebug.getRuntimeStat(). | 
 | 362 | enum class VMDebugRuntimeStatId { | 
 | 363 |   kArtGcGcCount = 0, | 
 | 364 |   kArtGcGcTime, | 
 | 365 |   kArtGcBytesAllocated, | 
 | 366 |   kArtGcBytesFreed, | 
 | 367 |   kArtGcBlockingGcCount, | 
 | 368 |   kArtGcBlockingGcTime, | 
 | 369 |   kArtGcGcCountRateHistogram, | 
 | 370 |   kArtGcBlockingGcCountRateHistogram, | 
 | 371 |   kNumRuntimeStats, | 
 | 372 | }; | 
 | 373 |  | 
 | 374 | static jobject VMDebug_getRuntimeStatInternal(JNIEnv* env, jclass, jint statId) { | 
 | 375 |   gc::Heap* heap = Runtime::Current()->GetHeap(); | 
 | 376 |   switch (static_cast<VMDebugRuntimeStatId>(statId)) { | 
 | 377 |     case VMDebugRuntimeStatId::kArtGcGcCount: { | 
 | 378 |       std::string output = std::to_string(heap->GetGcCount()); | 
 | 379 |       return env->NewStringUTF(output.c_str()); | 
 | 380 |     } | 
 | 381 |     case VMDebugRuntimeStatId::kArtGcGcTime: { | 
 | 382 |       std::string output = std::to_string(NsToMs(heap->GetGcTime())); | 
 | 383 |       return env->NewStringUTF(output.c_str()); | 
 | 384 |     } | 
 | 385 |     case VMDebugRuntimeStatId::kArtGcBytesAllocated: { | 
 | 386 |       std::string output = std::to_string(heap->GetBytesAllocatedEver()); | 
 | 387 |       return env->NewStringUTF(output.c_str()); | 
 | 388 |     } | 
 | 389 |     case VMDebugRuntimeStatId::kArtGcBytesFreed: { | 
 | 390 |       std::string output = std::to_string(heap->GetBytesFreedEver()); | 
 | 391 |       return env->NewStringUTF(output.c_str()); | 
 | 392 |     } | 
 | 393 |     case VMDebugRuntimeStatId::kArtGcBlockingGcCount: { | 
 | 394 |       std::string output = std::to_string(heap->GetBlockingGcCount()); | 
 | 395 |       return env->NewStringUTF(output.c_str()); | 
 | 396 |     } | 
 | 397 |     case VMDebugRuntimeStatId::kArtGcBlockingGcTime: { | 
 | 398 |       std::string output = std::to_string(NsToMs(heap->GetBlockingGcTime())); | 
 | 399 |       return env->NewStringUTF(output.c_str()); | 
 | 400 |     } | 
 | 401 |     case VMDebugRuntimeStatId::kArtGcGcCountRateHistogram: { | 
 | 402 |       std::ostringstream output; | 
 | 403 |       heap->DumpGcCountRateHistogram(output); | 
 | 404 |       return env->NewStringUTF(output.str().c_str()); | 
 | 405 |     } | 
 | 406 |     case VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram: { | 
 | 407 |       std::ostringstream output; | 
 | 408 |       heap->DumpBlockingGcCountRateHistogram(output); | 
 | 409 |       return env->NewStringUTF(output.str().c_str()); | 
 | 410 |     } | 
 | 411 |     default: | 
 | 412 |       return nullptr; | 
 | 413 |   } | 
 | 414 | } | 
 | 415 |  | 
 | 416 | static bool SetRuntimeStatValue(JNIEnv* env, jobjectArray result, VMDebugRuntimeStatId id, | 
 | 417 |                                 std::string value) { | 
 | 418 |   ScopedLocalRef<jstring> jvalue(env, env->NewStringUTF(value.c_str())); | 
 | 419 |   if (jvalue.get() == nullptr) { | 
 | 420 |     return false; | 
 | 421 |   } | 
 | 422 |   env->SetObjectArrayElement(result, static_cast<jint>(id), jvalue.get()); | 
 | 423 |   return true; | 
 | 424 | } | 
 | 425 |  | 
 | 426 | static jobjectArray VMDebug_getRuntimeStatsInternal(JNIEnv* env, jclass) { | 
 | 427 |   jobjectArray result = env->NewObjectArray( | 
 | 428 |       static_cast<jint>(VMDebugRuntimeStatId::kNumRuntimeStats), | 
 | 429 |       WellKnownClasses::java_lang_String, | 
 | 430 |       nullptr); | 
 | 431 |   if (result == nullptr) { | 
 | 432 |     return nullptr; | 
 | 433 |   } | 
 | 434 |   gc::Heap* heap = Runtime::Current()->GetHeap(); | 
 | 435 |   if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcCount, | 
 | 436 |                            std::to_string(heap->GetGcCount()))) { | 
 | 437 |     return nullptr; | 
 | 438 |   } | 
 | 439 |   if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcTime, | 
 | 440 |                            std::to_string(NsToMs(heap->GetGcTime())))) { | 
 | 441 |     return nullptr; | 
 | 442 |   } | 
 | 443 |   if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBytesAllocated, | 
 | 444 |                            std::to_string(heap->GetBytesAllocatedEver()))) { | 
 | 445 |     return nullptr; | 
 | 446 |   } | 
 | 447 |   if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBytesFreed, | 
 | 448 |                            std::to_string(heap->GetBytesFreedEver()))) { | 
 | 449 |     return nullptr; | 
 | 450 |   } | 
 | 451 |   if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcCount, | 
 | 452 |                            std::to_string(heap->GetBlockingGcCount()))) { | 
 | 453 |     return nullptr; | 
 | 454 |   } | 
 | 455 |   if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcTime, | 
 | 456 |                            std::to_string(NsToMs(heap->GetBlockingGcTime())))) { | 
 | 457 |     return nullptr; | 
 | 458 |   } | 
 | 459 |   { | 
 | 460 |     std::ostringstream output; | 
 | 461 |     heap->DumpGcCountRateHistogram(output); | 
 | 462 |     if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcCountRateHistogram, | 
 | 463 |                              output.str())) { | 
 | 464 |       return nullptr; | 
 | 465 |     } | 
 | 466 |   } | 
 | 467 |   { | 
 | 468 |     std::ostringstream output; | 
 | 469 |     heap->DumpBlockingGcCountRateHistogram(output); | 
 | 470 |     if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram, | 
 | 471 |                              output.str())) { | 
 | 472 |       return nullptr; | 
 | 473 |     } | 
 | 474 |   } | 
 | 475 |   return result; | 
 | 476 | } | 
 | 477 |  | 
| Elliott Hughes | 0512f02 | 2012-03-15 22:10:52 -0700 | [diff] [blame] | 478 | static JNINativeMethod gMethods[] = { | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 479 |   NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"), | 
| Mathieu Chartier | f182085 | 2015-07-10 13:19:51 -0700 | [diff] [blame] | 480 |   NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"), | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 481 |   NATIVE_METHOD(VMDebug, crash, "()V"), | 
 | 482 |   NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;Ljava/io/FileDescriptor;)V"), | 
 | 483 |   NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"), | 
 | 484 |   NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"), | 
 | 485 |   NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"), | 
| Hiroshi Yamauchi | 09b07a9 | 2013-07-15 13:17:06 -0700 | [diff] [blame] | 486 |   NATIVE_METHOD(VMDebug, getHeapSpaceStats, "([J)V"), | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 487 |   NATIVE_METHOD(VMDebug, getInstructionCount, "([I)V"), | 
| Ian Rogers | 53b8b09 | 2014-03-13 23:45:53 -0700 | [diff] [blame] | 488 |   NATIVE_METHOD(VMDebug, getLoadedClassCount, "!()I"), | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 489 |   NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"), | 
 | 490 |   NATIVE_METHOD(VMDebug, infopoint, "(I)V"), | 
| Ian Rogers | 53b8b09 | 2014-03-13 23:45:53 -0700 | [diff] [blame] | 491 |   NATIVE_METHOD(VMDebug, isDebuggerConnected, "!()Z"), | 
 | 492 |   NATIVE_METHOD(VMDebug, isDebuggingEnabled, "!()Z"), | 
| Jeff Hao | 64caa7d | 2013-08-29 11:18:01 -0700 | [diff] [blame] | 493 |   NATIVE_METHOD(VMDebug, getMethodTracingMode, "()I"), | 
| Ian Rogers | 53b8b09 | 2014-03-13 23:45:53 -0700 | [diff] [blame] | 494 |   NATIVE_METHOD(VMDebug, lastDebuggerActivity, "!()J"), | 
 | 495 |   NATIVE_METHOD(VMDebug, printLoadedClasses, "!(I)V"), | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 496 |   NATIVE_METHOD(VMDebug, resetAllocCount, "(I)V"), | 
 | 497 |   NATIVE_METHOD(VMDebug, resetInstructionCount, "()V"), | 
 | 498 |   NATIVE_METHOD(VMDebug, startAllocCounting, "()V"), | 
 | 499 |   NATIVE_METHOD(VMDebug, startEmulatorTracing, "()V"), | 
 | 500 |   NATIVE_METHOD(VMDebug, startInstructionCounting, "()V"), | 
| Jeff Hao | 23009dc | 2013-08-22 15:36:42 -0700 | [diff] [blame] | 501 |   NATIVE_METHOD(VMDebug, startMethodTracingDdmsImpl, "(IIZI)V"), | 
| Jeff Hao | 4044bda | 2014-01-06 15:50:45 -0800 | [diff] [blame] | 502 |   NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;Ljava/io/FileDescriptor;IIZI)V"), | 
 | 503 |   NATIVE_METHOD(VMDebug, startMethodTracingFilename, "(Ljava/lang/String;IIZI)V"), | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 504 |   NATIVE_METHOD(VMDebug, stopAllocCounting, "()V"), | 
 | 505 |   NATIVE_METHOD(VMDebug, stopEmulatorTracing, "()V"), | 
 | 506 |   NATIVE_METHOD(VMDebug, stopInstructionCounting, "()V"), | 
 | 507 |   NATIVE_METHOD(VMDebug, stopMethodTracing, "()V"), | 
| Ian Rogers | 53b8b09 | 2014-03-13 23:45:53 -0700 | [diff] [blame] | 508 |   NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "!()J"), | 
| Hiroshi Yamauchi | a1c9f01 | 2015-04-02 10:18:12 -0700 | [diff] [blame] | 509 |   NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"), | 
 | 510 |   NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;") | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 511 | }; | 
 | 512 |  | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 513 | void register_dalvik_system_VMDebug(JNIEnv* env) { | 
| Elliott Hughes | eac7667 | 2012-05-24 21:56:51 -0700 | [diff] [blame] | 514 |   REGISTER_NATIVE_METHODS("dalvik/system/VMDebug"); | 
| Elliott Hughes | 9d5ccec | 2011-09-19 13:19:50 -0700 | [diff] [blame] | 515 | } | 
 | 516 |  | 
 | 517 | }  // namespace art |