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 <fcntl.h>
29 #include <errno.h>
30 #include <sys/time.h>
31 #include <time.h>
32
33
34 #define kHeadSuffix "-hptemp"
35
36 hprof_context_t *
hprofStartup(const char * outputFileName,int fd,bool directToDdms)37 hprofStartup(const char *outputFileName, int fd, bool directToDdms)
38 {
39 hprofStartup_String();
40 hprofStartup_Class();
41 #if WITH_HPROF_STACK
42 hprofStartup_StackFrame();
43 hprofStartup_Stack();
44 #endif
45
46 hprof_context_t *ctx = malloc(sizeof(*ctx));
47 if (ctx == NULL) {
48 LOGE("hprof: can't allocate context.\n");
49 return NULL;
50 }
51
52 /* pass in name or descriptor of the output file */
53 hprofContextInit(ctx, strdup(outputFileName), fd, false, directToDdms);
54
55 assert(ctx->memFp != NULL);
56
57 return ctx;
58 }
59
60 /*
61 * Finish up the hprof dump. Returns true on success.
62 */
63 bool
hprofShutdown(hprof_context_t * tailCtx)64 hprofShutdown(hprof_context_t *tailCtx)
65 {
66 /* flush the "tail" portion of the output */
67 hprofFlushCurrentRecord(tailCtx);
68
69 /*
70 * Create a new context struct for the start of the file. We
71 * heap-allocate it so we can share the "free" function.
72 */
73 hprof_context_t *headCtx = malloc(sizeof(*headCtx));
74 if (headCtx == NULL) {
75 LOGE("hprof: can't allocate context.\n");
76 hprofFreeContext(tailCtx);
77 return false;
78 }
79 hprofContextInit(headCtx, strdup(tailCtx->fileName), tailCtx->fd, true,
80 tailCtx->directToDdms);
81
82 LOGI("hprof: dumping heap strings to \"%s\".\n", tailCtx->fileName);
83 hprofDumpStrings(headCtx);
84 hprofDumpClasses(headCtx);
85
86 /* Write a dummy stack trace record so the analysis
87 * tools don't freak out.
88 */
89 hprofStartNewRecord(headCtx, HPROF_TAG_STACK_TRACE, HPROF_TIME);
90 hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_STACK_TRACE);
91 hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_THREAD);
92 hprofAddU4ToRecord(&headCtx->curRec, 0); // no frames
93
94 #if WITH_HPROF_STACK
95 hprofDumpStackFrames(headCtx);
96 hprofDumpStacks(headCtx);
97 #endif
98
99 hprofFlushCurrentRecord(headCtx);
100
101 hprofShutdown_Class();
102 hprofShutdown_String();
103 #if WITH_HPROF_STACK
104 hprofShutdown_Stack();
105 hprofShutdown_StackFrame();
106 #endif
107
108 /* flush to ensure memstream pointer and size are updated */
109 fflush(headCtx->memFp);
110 fflush(tailCtx->memFp);
111
112 if (tailCtx->directToDdms) {
113 /* send the data off to DDMS */
114 struct iovec iov[2];
115 iov[0].iov_base = headCtx->fileDataPtr;
116 iov[0].iov_len = headCtx->fileDataSize;
117 iov[1].iov_base = tailCtx->fileDataPtr;
118 iov[1].iov_len = tailCtx->fileDataSize;
119 dvmDbgDdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
120 } else {
121 /*
122 * Open the output file, and copy the head and tail to it.
123 */
124 assert(headCtx->fd == tailCtx->fd);
125
126 int outFd;
127 if (headCtx->fd >= 0) {
128 outFd = dup(headCtx->fd);
129 if (outFd < 0) {
130 LOGE("dup(%d) failed: %s\n", headCtx->fd, strerror(errno));
131 /* continue to fail-handler below */
132 }
133 } else {
134 outFd = open(tailCtx->fileName, O_WRONLY|O_CREAT, 0644);
135 if (outFd < 0) {
136 LOGE("can't open %s: %s\n", headCtx->fileName, strerror(errno));
137 /* continue to fail-handler below */
138 }
139 }
140 if (outFd < 0) {
141 hprofFreeContext(headCtx);
142 hprofFreeContext(tailCtx);
143 return false;
144 }
145
146 int result;
147 result = sysWriteFully(outFd, headCtx->fileDataPtr,
148 headCtx->fileDataSize, "hprof-head");
149 result |= sysWriteFully(outFd, tailCtx->fileDataPtr,
150 tailCtx->fileDataSize, "hprof-tail");
151 close(outFd);
152 if (result != 0) {
153 hprofFreeContext(headCtx);
154 hprofFreeContext(tailCtx);
155 return false;
156 }
157 }
158
159 /* throw out a log message for the benefit of "runhat" */
160 LOGI("hprof: heap dump completed (%dKB)\n",
161 (headCtx->fileDataSize + tailCtx->fileDataSize + 1023) / 1024);
162
163 hprofFreeContext(headCtx);
164 hprofFreeContext(tailCtx);
165
166 return true;
167 }
168
169 /*
170 * Free any heap-allocated items in "ctx", and then free "ctx" itself.
171 */
172 void
hprofFreeContext(hprof_context_t * ctx)173 hprofFreeContext(hprof_context_t *ctx)
174 {
175 assert(ctx != NULL);
176
177 /* we don't own ctx->fd, do not close */
178
179 if (ctx->memFp != NULL)
180 fclose(ctx->memFp);
181 free(ctx->curRec.body);
182 free(ctx->fileName);
183 free(ctx->fileDataPtr);
184 free(ctx);
185 }
186