1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 /* 18 * Preparation and completion of hprof data generation. The output is 19 * written into two files and then combined. This is necessary because 20 * we generate some of the data (strings and classes) while we dump the 21 * heap, and some analysis tools require that the class and string data 22 * appear first. 23 */ 24 25 #include "Hprof.h" 26 #include "alloc/HeapInternal.h" 27 #include "alloc/Visit.h" 28 29 #include <string.h> 30 #include <unistd.h> 31 #include <fcntl.h> 32 #include <errno.h> 33 #include <sys/time.h> 34 #include <time.h> 35 36 #define kHeadSuffix "-hptemp" 37 38 hprof_context_t* hprofStartup(const char *outputFileName, int fd, 39 bool directToDdms) 40 { 41 hprofStartup_String(); 42 hprofStartup_Class(); 43 44 hprof_context_t *ctx = (hprof_context_t *)calloc(1, sizeof(*ctx)); 45 if (ctx == NULL) { 46 ALOGE("hprof: can't allocate context."); 47 return NULL; 48 } 49 50 /* pass in name or descriptor of the output file */ 51 hprofContextInit(ctx, strdup(outputFileName), fd, false, directToDdms); 52 53 assert(ctx->memFp != NULL); 54 55 return ctx; 56 } 57 58 /* 59 * Finish up the hprof dump. Returns true on success. 60 */ 61 bool hprofShutdown(hprof_context_t *tailCtx) 62 { 63 /* flush the "tail" portion of the output */ 64 hprofFlushCurrentRecord(tailCtx); 65 66 /* 67 * Create a new context struct for the start of the file. We 68 * heap-allocate it so we can share the "free" function. 69 */ 70 hprof_context_t *headCtx = (hprof_context_t *)calloc(1, sizeof(*headCtx)); 71 if (headCtx == NULL) { 72 ALOGE("hprof: can't allocate context."); 73 hprofFreeContext(tailCtx); 74 return false; 75 } 76 hprofContextInit(headCtx, strdup(tailCtx->fileName), tailCtx->fd, true, 77 tailCtx->directToDdms); 78 79 ALOGI("hprof: dumping heap strings to \"%s\".", tailCtx->fileName); 80 hprofDumpStrings(headCtx); 81 hprofDumpClasses(headCtx); 82 83 /* Write a dummy stack trace record so the analysis 84 * tools don't freak out. 85 */ 86 hprofStartNewRecord(headCtx, HPROF_TAG_STACK_TRACE, HPROF_TIME); 87 hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_STACK_TRACE); 88 hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_THREAD); 89 hprofAddU4ToRecord(&headCtx->curRec, 0); // no frames 90 91 hprofFlushCurrentRecord(headCtx); 92 93 hprofShutdown_Class(); 94 hprofShutdown_String(); 95 96 /* flush to ensure memstream pointer and size are updated */ 97 fflush(headCtx->memFp); 98 fflush(tailCtx->memFp); 99 100 if (tailCtx->directToDdms) { 101 /* send the data off to DDMS */ 102 struct iovec iov[2]; 103 iov[0].iov_base = headCtx->fileDataPtr; 104 iov[0].iov_len = headCtx->fileDataSize; 105 iov[1].iov_base = tailCtx->fileDataPtr; 106 iov[1].iov_len = tailCtx->fileDataSize; 107 dvmDbgDdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2); 108 } else { 109 /* 110 * Open the output file, and copy the head and tail to it. 111 */ 112 assert(headCtx->fd == tailCtx->fd); 113 114 int outFd; 115 if (headCtx->fd >= 0) { 116 outFd = dup(headCtx->fd); 117 if (outFd < 0) { 118 ALOGE("dup(%d) failed: %s", headCtx->fd, strerror(errno)); 119 /* continue to fail-handler below */ 120 } 121 } else { 122 outFd = open(tailCtx->fileName, O_WRONLY|O_CREAT|O_TRUNC, 0644); 123 if (outFd < 0) { 124 ALOGE("can't open %s: %s", headCtx->fileName, strerror(errno)); 125 /* continue to fail-handler below */ 126 } 127 } 128 if (outFd < 0) { 129 hprofFreeContext(headCtx); 130 hprofFreeContext(tailCtx); 131 return false; 132 } 133 134 int result; 135 result = sysWriteFully(outFd, headCtx->fileDataPtr, 136 headCtx->fileDataSize, "hprof-head"); 137 result |= sysWriteFully(outFd, tailCtx->fileDataPtr, 138 tailCtx->fileDataSize, "hprof-tail"); 139 close(outFd); 140 if (result != 0) { 141 hprofFreeContext(headCtx); 142 hprofFreeContext(tailCtx); 143 return false; 144 } 145 } 146 147 /* throw out a log message for the benefit of "runhat" */ 148 ALOGI("hprof: heap dump completed (%dKB)", 149 (headCtx->fileDataSize + tailCtx->fileDataSize + 1023) / 1024); 150 151 hprofFreeContext(headCtx); 152 hprofFreeContext(tailCtx); 153 154 return true; 155 } 156 157 /* 158 * Free any heap-allocated items in "ctx", and then free "ctx" itself. 159 */ 160 void hprofFreeContext(hprof_context_t *ctx) 161 { 162 assert(ctx != NULL); 163 164 /* we don't own ctx->fd, do not close */ 165 166 if (ctx->memFp != NULL) 167 fclose(ctx->memFp); 168 free(ctx->curRec.body); 169 free(ctx->fileName); 170 free(ctx->fileDataPtr); 171 free(ctx); 172 } 173 174 /* 175 * Visitor invoked on every root reference. 176 */ 177 static void hprofRootVisitor(void *addr, u4 threadId, RootType type, void *arg) 178 { 179 static const hprof_heap_tag_t xlate[] = { 180 HPROF_ROOT_UNKNOWN, 181 HPROF_ROOT_JNI_GLOBAL, 182 HPROF_ROOT_JNI_LOCAL, 183 HPROF_ROOT_JAVA_FRAME, 184 HPROF_ROOT_NATIVE_STACK, 185 HPROF_ROOT_STICKY_CLASS, 186 HPROF_ROOT_THREAD_BLOCK, 187 HPROF_ROOT_MONITOR_USED, 188 HPROF_ROOT_THREAD_OBJECT, 189 HPROF_ROOT_INTERNED_STRING, 190 HPROF_ROOT_FINALIZING, 191 HPROF_ROOT_DEBUGGER, 192 HPROF_ROOT_REFERENCE_CLEANUP, 193 HPROF_ROOT_VM_INTERNAL, 194 HPROF_ROOT_JNI_MONITOR, 195 }; 196 hprof_context_t *ctx; 197 Object *obj; 198 199 assert(addr != NULL); 200 assert(arg != NULL); 201 assert(type < NELEM(xlate)); 202 obj = *(Object **)addr; 203 if (obj == NULL) { 204 return; 205 } 206 ctx = (hprof_context_t *)arg; 207 ctx->gcScanState = xlate[type]; 208 ctx->gcThreadSerialNumber = threadId; 209 hprofMarkRootObject(ctx, obj, 0); 210 ctx->gcScanState = 0; 211 ctx->gcThreadSerialNumber = 0; 212 } 213 214 /* 215 * Visitor invoked on every heap object. 216 */ 217 static void hprofBitmapCallback(Object *obj, void *arg) 218 { 219 assert(obj != NULL); 220 assert(arg != NULL); 221 hprof_context_t *ctx = (hprof_context_t *)arg; 222 hprofDumpHeapObject(ctx, obj); 223 } 224 225 /* 226 * Walk the roots and heap writing heap information to the specified 227 * file. 228 * 229 * If "fd" is >= 0, the output will be written to that file descriptor. 230 * Otherwise, "fileName" is used to create an output file. 231 * 232 * If "directToDdms" is set, the other arguments are ignored, and data is 233 * sent directly to DDMS. 234 * 235 * Returns 0 on success, or an error code on failure. 236 */ 237 int hprofDumpHeap(const char* fileName, int fd, bool directToDdms) 238 { 239 hprof_context_t *ctx; 240 int success; 241 242 assert(fileName != NULL); 243 dvmLockHeap(); 244 dvmSuspendAllThreads(SUSPEND_FOR_HPROF); 245 ctx = hprofStartup(fileName, fd, directToDdms); 246 if (ctx == NULL) { 247 return -1; 248 } 249 // first record 250 hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME); 251 dvmVisitRoots(hprofRootVisitor, ctx); 252 dvmHeapBitmapWalk(dvmHeapSourceGetLiveBits(), hprofBitmapCallback, ctx); 253 hprofFinishHeapDump(ctx); 254 //TODO: write a HEAP_SUMMARY record 255 success = hprofShutdown(ctx) ? 0 : -1; 256 dvmResumeAllThreads(SUSPEND_FOR_HPROF); 257 dvmUnlockHeap(); 258 return success; 259 } 260