blob: 20fc4269d7539b97192d2b7efbf0281ae557c642 [file] [log] [blame]
Jesse Wilsonc4824e62011-11-01 14:39:04 -04001/*
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 "heap.h"
27#include "debugger.h"
28#include "stringprintf.h"
29#include "thread_list.h"
30#include "logging.h"
31
32#include <sys/uio.h>
33#include <string.h>
34#include <unistd.h>
35#include <fcntl.h>
36#include <errno.h>
37#include <sys/time.h>
38#include <time.h>
39
40namespace art {
41
42namespace hprof {
43
44#define kHeadSuffix "-hptemp"
45
46hprof_context_t* hprofStartup(const char *outputFileName, int fd,
47 bool directToDdms)
48{
49 hprofStartup_String();
50 hprofStartup_Class();
51
52 hprof_context_t *ctx = (hprof_context_t *)malloc(sizeof(*ctx));
53 if (ctx == NULL) {
54 LOG(ERROR) << "hprof: can't allocate context.";
55 return NULL;
56 }
57
58 /* pass in name or descriptor of the output file */
59 hprofContextInit(ctx, strdup(outputFileName), fd, false, directToDdms);
60
61 CHECK(ctx->memFp != NULL);
62
63 return ctx;
64}
65
66// TODO: use File::WriteFully
67int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg)
68{
69 while (count != 0) {
70 ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count));
71 if (actual < 0) {
72 int err = errno;
73 LOG(ERROR) << StringPrintf("%s: write failed: %s", logMsg, strerror(err));
74 return err;
75 } else if (actual != (ssize_t) count) {
76 LOG(DEBUG) << StringPrintf("%s: partial write (will retry): (%d of %zd)",
77 logMsg, (int) actual, count);
78 buf = (const void*) (((const uint8_t*) buf) + actual);
79 }
80 count -= actual;
81 }
82
83 return 0;
84}
85
86/*
87 * Finish up the hprof dump. Returns true on success.
88 */
89bool hprofShutdown(hprof_context_t *tailCtx)
90{
91 /* flush the "tail" portion of the output */
92 hprofFlushCurrentRecord(tailCtx);
93
94 /*
95 * Create a new context struct for the start of the file. We
96 * heap-allocate it so we can share the "free" function.
97 */
98 hprof_context_t *headCtx = (hprof_context_t *)malloc(sizeof(*headCtx));
99 if (headCtx == NULL) {
100 LOG(ERROR) << "hprof: can't allocate context.";
101 hprofFreeContext(tailCtx);
102 return false;
103 }
104 hprofContextInit(headCtx, strdup(tailCtx->fileName), tailCtx->fd, true,
105 tailCtx->directToDdms);
106
107 LOG(INFO) << StringPrintf("hprof: dumping heap strings to \"%s\".", tailCtx->fileName);
108 hprofDumpStrings(headCtx);
109 hprofDumpClasses(headCtx);
110
111 /* Write a dummy stack trace record so the analysis
112 * tools don't freak out.
113 */
114 hprofStartNewRecord(headCtx, HPROF_TAG_STACK_TRACE, HPROF_TIME);
115 hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_STACK_TRACE);
116 hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_THREAD);
117 hprofAddU4ToRecord(&headCtx->curRec, 0); // no frames
118
119 hprofFlushCurrentRecord(headCtx);
120
121 hprofShutdown_Class();
122 hprofShutdown_String();
123
124 /* flush to ensure memstream pointer and size are updated */
125 fflush(headCtx->memFp);
126 fflush(tailCtx->memFp);
127
128 if (tailCtx->directToDdms) {
129 /* send the data off to DDMS */
130 struct iovec iov[2];
131 iov[0].iov_base = headCtx->fileDataPtr;
132 iov[0].iov_len = headCtx->fileDataSize;
133 iov[1].iov_base = tailCtx->fileDataPtr;
134 iov[1].iov_len = tailCtx->fileDataSize;
135 Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
136 } else {
137 /*
138 * Open the output file, and copy the head and tail to it.
139 */
140 CHECK(headCtx->fd == tailCtx->fd);
141
142 int outFd;
143 if (headCtx->fd >= 0) {
144 outFd = dup(headCtx->fd);
145 if (outFd < 0) {
146 LOG(ERROR) << StringPrintf("dup(%d) failed: %s", headCtx->fd, strerror(errno));
147 /* continue to fail-handler below */
148 }
149 } else {
150 outFd = open(tailCtx->fileName, O_WRONLY|O_CREAT|O_TRUNC, 0644);
151 if (outFd < 0) {
152 LOG(ERROR) << StringPrintf("can't open %s: %s", headCtx->fileName, strerror(errno));
153 /* continue to fail-handler below */
154 }
155 }
156 if (outFd < 0) {
157 hprofFreeContext(headCtx);
158 hprofFreeContext(tailCtx);
159 return false;
160 }
161
162 int result;
163 result = sysWriteFully(outFd, headCtx->fileDataPtr,
164 headCtx->fileDataSize, "hprof-head");
165 result |= sysWriteFully(outFd, tailCtx->fileDataPtr,
166 tailCtx->fileDataSize, "hprof-tail");
167 close(outFd);
168 if (result != 0) {
169 hprofFreeContext(headCtx);
170 hprofFreeContext(tailCtx);
171 return false;
172 }
173 }
174
175 /* throw out a log message for the benefit of "runhat" */
176 LOG(INFO) << StringPrintf("hprof: heap dump completed (%dKB)",
177 (headCtx->fileDataSize + tailCtx->fileDataSize + 1023) / 1024);
178
179 hprofFreeContext(headCtx);
180 hprofFreeContext(tailCtx);
181
182 return true;
183}
184
185/*
186 * Free any heap-allocated items in "ctx", and then free "ctx" itself.
187 */
188void hprofFreeContext(hprof_context_t *ctx)
189{
190 CHECK(ctx != NULL);
191
192 /* we don't own ctx->fd, do not close */
193
194 if (ctx->memFp != NULL)
195 fclose(ctx->memFp);
196 free(ctx->curRec.body);
197 free(ctx->fileName);
198 free(ctx->fileDataPtr);
199 free(ctx);
200}
201
202/*
203 * Visitor invoked on every root reference.
204 */
205void HprofRootVisitor(const Object* obj, void* arg) {
206 uint32_t threadId = 0; // TODO
207 /*RootType */ size_t type = 0; // TODO
208
209 static const hprof_heap_tag_t xlate[] = {
210 HPROF_ROOT_UNKNOWN,
211 HPROF_ROOT_JNI_GLOBAL,
212 HPROF_ROOT_JNI_LOCAL,
213 HPROF_ROOT_JAVA_FRAME,
214 HPROF_ROOT_NATIVE_STACK,
215 HPROF_ROOT_STICKY_CLASS,
216 HPROF_ROOT_THREAD_BLOCK,
217 HPROF_ROOT_MONITOR_USED,
218 HPROF_ROOT_THREAD_OBJECT,
219 HPROF_ROOT_INTERNED_STRING,
220 HPROF_ROOT_FINALIZING,
221 HPROF_ROOT_DEBUGGER,
222 HPROF_ROOT_REFERENCE_CLEANUP,
223 HPROF_ROOT_VM_INTERNAL,
224 HPROF_ROOT_JNI_MONITOR,
225 };
226 hprof_context_t *ctx;
227
228 CHECK(arg != NULL);
229 CHECK(type < (sizeof(xlate) / sizeof(hprof_heap_tag_t)));
230 if (obj == NULL) {
231 return;
232 }
233 ctx = (hprof_context_t *)arg;
234 ctx->gcScanState = xlate[type];
235 ctx->gcThreadSerialNumber = threadId;
236 hprofMarkRootObject(ctx, obj, 0);
237 ctx->gcScanState = 0;
238 ctx->gcThreadSerialNumber = 0;
239}
240
241/*
242 * Visitor invoked on every heap object.
243 */
244
245static void HprofBitmapCallback(Object *obj, void *arg)
246{
247 CHECK(obj != NULL);
248 CHECK(arg != NULL);
249 hprof_context_t *ctx = (hprof_context_t *)arg;
Elliott Hughesfbd84562011-11-07 18:56:13 -0800250 DumpHeapObject(ctx, obj);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400251}
252
253/*
254 * Walk the roots and heap writing heap information to the specified
255 * file.
256 *
257 * If "fd" is >= 0, the output will be written to that file descriptor.
258 * Otherwise, "fileName" is used to create an output file.
259 *
260 * If "directToDdms" is set, the other arguments are ignored, and data is
261 * sent directly to DDMS.
262 *
263 * Returns 0 on success, or an error code on failure.
264 */
Elliott Hughesfbd84562011-11-07 18:56:13 -0800265int DumpHeap(const char* fileName, int fd, bool directToDdms)
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400266{
267 hprof_context_t *ctx;
268 int success;
269
270 CHECK(fileName != NULL);
271 ScopedHeapLock lock;
Jesse Wilson1121e0b2011-11-07 15:37:42 -0500272 ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400273
274 ThreadList* thread_list = Runtime::Current()->GetThreadList();
275 thread_list->SuspendAll();
276
277 ctx = hprofStartup(fileName, fd, directToDdms);
278 if (ctx == NULL) {
279 return -1;
280 }
281 Runtime::Current()->VisitRoots(HprofRootVisitor, ctx);
282 Heap::GetLiveBits()->Walk(HprofBitmapCallback, ctx);
283 hprofFinishHeapDump(ctx);
284//TODO: write a HEAP_SUMMARY record
285 success = hprofShutdown(ctx) ? 0 : -1;
286 thread_list->ResumeAll();
287 return success;
288}
289
290} // namespace hprof
291
292} // namespace art