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#include "Hprof.h" 25 26#include <string.h> 27#include <unistd.h> 28#include <errno.h> 29#include <sys/time.h> 30#include <time.h> 31 32 33#define kHeadSuffix "-hptemp" 34 35hprof_context_t * 36hprofStartup(const char *outputFileName, bool directToDdms) 37{ 38 FILE* fp = NULL; 39 40 if (!directToDdms) { 41 int len = strlen(outputFileName); 42 char fileName[len + sizeof(kHeadSuffix)]; 43 44 /* Construct the temp file name. This wasn't handed to us by the 45 * application, so we need to be careful about stomping on it. 46 */ 47 sprintf(fileName, "%s" kHeadSuffix, outputFileName); 48 if (access(fileName, F_OK) == 0) { 49 LOGE("hprof: temp file %s exists, bailing\n", fileName); 50 return NULL; 51 } 52 53 fp = fopen(fileName, "w+"); 54 if (fp == NULL) { 55 LOGE("hprof: can't open %s: %s.\n", fileName, strerror(errno)); 56 return NULL; 57 } 58 if (unlink(fileName) != 0) { 59 LOGW("hprof: WARNING: unable to remove temp file %s\n", fileName); 60 /* keep going */ 61 } 62 LOGI("hprof: dumping VM heap to \"%s\".\n", fileName); 63 } 64 65 hprofStartup_String(); 66 hprofStartup_Class(); 67#if WITH_HPROF_STACK 68 hprofStartup_StackFrame(); 69 hprofStartup_Stack(); 70#endif 71 72 hprof_context_t *ctx = malloc(sizeof(*ctx)); 73 if (ctx == NULL) { 74 LOGE("hprof: can't allocate context.\n"); 75 if (fp != NULL) 76 fclose(fp); 77 return NULL; 78 } 79 80 /* pass in "fp" for the temp file, and the name of the output file */ 81 hprofContextInit(ctx, strdup(outputFileName), fp, false, directToDdms); 82 83 assert(ctx->fp != NULL); 84 85 return ctx; 86} 87 88/* 89 * Copy the entire contents of "srcFp" to "dstFp". 90 * 91 * Returns "true" on success. 92 */ 93static bool 94copyFileToFile(FILE *dstFp, FILE *srcFp) 95{ 96 char buf[65536]; 97 size_t dataRead, dataWritten; 98 99 while (true) { 100 dataRead = fread(buf, 1, sizeof(buf), srcFp); 101 if (dataRead > 0) { 102 dataWritten = fwrite(buf, 1, dataRead, dstFp); 103 if (dataWritten != dataRead) { 104 LOGE("hprof: failed writing data (%d of %d): %s\n", 105 dataWritten, dataRead, strerror(errno)); 106 return false; 107 } 108 } else { 109 if (feof(srcFp)) 110 return true; 111 LOGE("hprof: failed reading data (res=%d): %s\n", 112 dataRead, strerror(errno)); 113 return false; 114 } 115 } 116} 117 118/* 119 * Finish up the hprof dump. Returns true on success. 120 */ 121bool 122hprofShutdown(hprof_context_t *tailCtx) 123{ 124 FILE *fp = NULL; 125 126 /* flush output to the temp file, then prepare the output file */ 127 hprofFlushCurrentRecord(tailCtx); 128 129 LOGI("hprof: dumping heap strings to \"%s\".\n", tailCtx->fileName); 130 if (!tailCtx->directToDdms) { 131 fp = fopen(tailCtx->fileName, "w"); 132 if (fp == NULL) { 133 LOGE("can't open %s: %s\n", tailCtx->fileName, strerror(errno)); 134 hprofFreeContext(tailCtx); 135 return false; 136 } 137 } 138 139 /* 140 * Create a new context struct for the start of the file. We 141 * heap-allocate it so we can share the "free" function. 142 */ 143 hprof_context_t *headCtx = malloc(sizeof(*headCtx)); 144 if (headCtx == NULL) { 145 LOGE("hprof: can't allocate context.\n"); 146 if (fp != NULL) 147 fclose(fp); 148 hprofFreeContext(tailCtx); 149 return NULL; 150 } 151 hprofContextInit(headCtx, strdup(tailCtx->fileName), fp, true, 152 tailCtx->directToDdms); 153 154 hprofDumpStrings(headCtx); 155 hprofDumpClasses(headCtx); 156 157 /* Write a dummy stack trace record so the analysis 158 * tools don't freak out. 159 */ 160 hprofStartNewRecord(headCtx, HPROF_TAG_STACK_TRACE, HPROF_TIME); 161 hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_STACK_TRACE); 162 hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_THREAD); 163 hprofAddU4ToRecord(&headCtx->curRec, 0); // no frames 164 165#if WITH_HPROF_STACK 166 hprofDumpStackFrames(headCtx); 167 hprofDumpStacks(headCtx); 168#endif 169 170 hprofFlushCurrentRecord(headCtx); 171 172 hprofShutdown_Class(); 173 hprofShutdown_String(); 174#if WITH_HPROF_STACK 175 hprofShutdown_Stack(); 176 hprofShutdown_StackFrame(); 177#endif 178 179 if (tailCtx->directToDdms) { 180 /* flush to ensure memstream pointer and size are updated */ 181 fflush(headCtx->fp); 182 fflush(tailCtx->fp); 183 184 /* send the data off to DDMS */ 185 struct iovec iov[2]; 186 iov[0].iov_base = headCtx->fileDataPtr; 187 iov[0].iov_len = headCtx->fileDataSize; 188 iov[1].iov_base = tailCtx->fileDataPtr; 189 iov[1].iov_len = tailCtx->fileDataSize; 190 dvmDbgDdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2); 191 } else { 192 /* 193 * Append the contents of the temp file to the output file. The temp 194 * file was removed immediately after being opened, so it will vanish 195 * when we close it. 196 */ 197 rewind(tailCtx->fp); 198 if (!copyFileToFile(headCtx->fp, tailCtx->fp)) { 199 LOGW("hprof: file copy failed, hprof data may be incomplete\n"); 200 /* finish up anyway */ 201 } 202 } 203 204 hprofFreeContext(headCtx); 205 hprofFreeContext(tailCtx); 206 207 /* throw out a log message for the benefit of "runhat" */ 208 LOGI("hprof: heap dump completed, temp file removed\n"); 209 return true; 210} 211 212/* 213 * Free any heap-allocated items in "ctx", and then free "ctx" itself. 214 */ 215void 216hprofFreeContext(hprof_context_t *ctx) 217{ 218 assert(ctx != NULL); 219 220 if (ctx->fp != NULL) 221 fclose(ctx->fp); 222 free(ctx->curRec.body); 223 free(ctx->fileName); 224 free(ctx->fileDataPtr); 225 free(ctx); 226} 227 228