Hprof.cpp revision 60fc806b679a3655c228b4093058c59941a49cfe
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 38hprof_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 *)malloc(sizeof(*ctx)); 45 if (ctx == NULL) { 46 LOGE("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 */ 61bool 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 *)malloc(sizeof(*headCtx)); 71 if (headCtx == NULL) { 72 LOGE("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 LOGI("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 LOGE("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 LOGE("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 LOGI("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 */ 160void 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 */ 177static 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 */ 217static void hprofBitmapCallback(void *ptr, void *arg) 218{ 219 Object *obj; 220 hprof_context_t *ctx; 221 222 assert(ptr != NULL); 223 assert(arg != NULL); 224 obj = (Object *)ptr; 225 ctx = (hprof_context_t *)arg; 226 hprofDumpHeapObject(ctx, obj); 227} 228 229/* 230 * Walk the roots and heap writing heap information to the specified 231 * file. 232 * 233 * If "fd" is >= 0, the output will be written to that file descriptor. 234 * Otherwise, "fileName" is used to create an output file. 235 * 236 * If "directToDdms" is set, the other arguments are ignored, and data is 237 * sent directly to DDMS. 238 * 239 * Returns 0 on success, or an error code on failure. 240 */ 241int hprofDumpHeap(const char* fileName, int fd, bool directToDdms) 242{ 243 hprof_context_t *ctx; 244 int success; 245 246 assert(fileName != NULL); 247 dvmLockHeap(); 248 dvmSuspendAllThreads(SUSPEND_FOR_HPROF); 249 ctx = hprofStartup(fileName, fd, directToDdms); 250 if (ctx == NULL) { 251 return -1; 252 } 253 dvmVisitRoots(hprofRootVisitor, ctx); 254 dvmHeapBitmapWalk(dvmHeapSourceGetLiveBits(), hprofBitmapCallback, ctx); 255 hprofFinishHeapDump(ctx); 256//TODO: write a HEAP_SUMMARY record 257 success = hprofShutdown(ctx) ? 0 : -1; 258 dvmResumeAllThreads(SUSPEND_FOR_HPROF); 259 dvmUnlockHeap(); 260 return success; 261} 262