blob: e2853079c2c90a8a18dc6ee3f6cad25d879f867d [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
Jesse Wilsonc4824e62011-11-01 14:39:04 -040046// TODO: use File::WriteFully
47int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg)
48{
49 while (count != 0) {
50 ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count));
51 if (actual < 0) {
52 int err = errno;
53 LOG(ERROR) << StringPrintf("%s: write failed: %s", logMsg, strerror(err));
54 return err;
55 } else if (actual != (ssize_t) count) {
56 LOG(DEBUG) << StringPrintf("%s: partial write (will retry): (%d of %zd)",
57 logMsg, (int) actual, count);
58 buf = (const void*) (((const uint8_t*) buf) + actual);
59 }
60 count -= actual;
61 }
62
63 return 0;
64}
65
66/*
67 * Finish up the hprof dump. Returns true on success.
68 */
Jesse Wilson3aa66fd2011-11-08 20:53:40 -050069bool Hprof::Finish()
Jesse Wilsonc4824e62011-11-01 14:39:04 -040070{
71 /* flush the "tail" portion of the output */
Jesse Wilson3aa66fd2011-11-08 20:53:40 -050072 StartNewRecord(HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
73 FlushCurrentRecord();
Jesse Wilsonc4824e62011-11-01 14:39:04 -040074
Jesse Wilson3aa66fd2011-11-08 20:53:40 -050075 // Create a new Hprof for the start of the file (as opposed to this, which is the tail).
76 Hprof headCtx(file_name_, fd_, true, direct_to_ddms_);
77 headCtx.classes_ = classes_;
78 headCtx.strings_ = strings_;
Jesse Wilsonc4824e62011-11-01 14:39:04 -040079
Jesse Wilson3aa66fd2011-11-08 20:53:40 -050080 LOG(INFO) << StringPrintf("hprof: dumping heap strings to \"%s\".", file_name_);
81 headCtx.DumpStrings();
82 headCtx.DumpClasses();
Jesse Wilsonc4824e62011-11-01 14:39:04 -040083
84 /* Write a dummy stack trace record so the analysis
85 * tools don't freak out.
86 */
Jesse Wilson3aa66fd2011-11-08 20:53:40 -050087 headCtx.StartNewRecord(HPROF_TAG_STACK_TRACE, HPROF_TIME);
88 headCtx.current_record_.AddU4(HPROF_NULL_STACK_TRACE);
89 headCtx.current_record_.AddU4(HPROF_NULL_THREAD);
90 headCtx.current_record_.AddU4(0); // no frames
Jesse Wilsonc4824e62011-11-01 14:39:04 -040091
Jesse Wilson3aa66fd2011-11-08 20:53:40 -050092 headCtx.FlushCurrentRecord();
Jesse Wilsonc4824e62011-11-01 14:39:04 -040093
94 /* flush to ensure memstream pointer and size are updated */
Jesse Wilson3aa66fd2011-11-08 20:53:40 -050095 fflush(headCtx.mem_fp_);
96 fflush(mem_fp_);
Jesse Wilsonc4824e62011-11-01 14:39:04 -040097
Jesse Wilson3aa66fd2011-11-08 20:53:40 -050098 if (direct_to_ddms_) {
Jesse Wilsonc4824e62011-11-01 14:39:04 -040099 /* send the data off to DDMS */
100 struct iovec iov[2];
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500101 iov[0].iov_base = headCtx.file_data_ptr_;
102 iov[0].iov_len = headCtx.file_data_size_;
103 iov[1].iov_base = file_data_ptr_;
104 iov[1].iov_len = file_data_size_;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400105 Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
106 } else {
107 /*
108 * Open the output file, and copy the head and tail to it.
109 */
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500110 CHECK_EQ(headCtx.fd_, fd_);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400111
112 int outFd;
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500113 if (headCtx.fd_ >= 0) {
114 outFd = dup(headCtx.fd_);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400115 if (outFd < 0) {
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500116 LOG(ERROR) << StringPrintf("dup(%d) failed: %s", headCtx.fd_, strerror(errno));
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400117 /* continue to fail-handler below */
118 }
119 } else {
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500120 outFd = open(file_name_, O_WRONLY|O_CREAT|O_TRUNC, 0644);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400121 if (outFd < 0) {
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500122 LOG(ERROR) << StringPrintf("can't open %s: %s", headCtx.file_name_, strerror(errno));
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400123 /* continue to fail-handler below */
124 }
125 }
126 if (outFd < 0) {
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400127 return false;
128 }
129
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500130 int result = sysWriteFully(outFd, headCtx.file_data_ptr_,
131 headCtx.file_data_size_, "hprof-head");
132 result |= sysWriteFully(outFd, file_data_ptr_, file_data_size_, "hprof-tail");
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400133 close(outFd);
134 if (result != 0) {
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400135 return false;
136 }
137 }
138
139 /* throw out a log message for the benefit of "runhat" */
140 LOG(INFO) << StringPrintf("hprof: heap dump completed (%dKB)",
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500141 (headCtx.file_data_size_ + file_data_size_ + 1023) / 1024);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400142
143 return true;
144}
145
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500146Hprof::~Hprof() {
147 /* we don't own ctx->fd_, do not close */
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400148
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500149 if (mem_fp_ != NULL) {
150 fclose(mem_fp_);
151 }
152 free(current_record_.body_);
153 free(file_name_);
154 free(file_data_ptr_);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400155}
156
157/*
158 * Visitor invoked on every root reference.
159 */
160void HprofRootVisitor(const Object* obj, void* arg) {
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500161 CHECK(arg != NULL);
162 Hprof* hprof = (Hprof*)arg;
163 hprof->VisitRoot(obj);
164}
165
166void Hprof::VisitRoot(const Object* obj) {
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400167 uint32_t threadId = 0; // TODO
168 /*RootType */ size_t type = 0; // TODO
169
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500170 static const HprofHeapTag xlate[] = {
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400171 HPROF_ROOT_UNKNOWN,
172 HPROF_ROOT_JNI_GLOBAL,
173 HPROF_ROOT_JNI_LOCAL,
174 HPROF_ROOT_JAVA_FRAME,
175 HPROF_ROOT_NATIVE_STACK,
176 HPROF_ROOT_STICKY_CLASS,
177 HPROF_ROOT_THREAD_BLOCK,
178 HPROF_ROOT_MONITOR_USED,
179 HPROF_ROOT_THREAD_OBJECT,
180 HPROF_ROOT_INTERNED_STRING,
181 HPROF_ROOT_FINALIZING,
182 HPROF_ROOT_DEBUGGER,
183 HPROF_ROOT_REFERENCE_CLEANUP,
184 HPROF_ROOT_VM_INTERNAL,
185 HPROF_ROOT_JNI_MONITOR,
186 };
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400187
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500188 CHECK_LT(type, sizeof(xlate) / sizeof(HprofHeapTag));
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400189 if (obj == NULL) {
190 return;
191 }
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500192 gc_scan_state_ = xlate[type];
193 gc_thread_serial_number_ = threadId;
194 MarkRootObject(obj, 0);
195 gc_scan_state_ = 0;
196 gc_thread_serial_number_ = 0;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400197}
198
199/*
200 * Visitor invoked on every heap object.
201 */
202
203static void HprofBitmapCallback(Object *obj, void *arg)
204{
205 CHECK(obj != NULL);
206 CHECK(arg != NULL);
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500207 Hprof *hprof = (Hprof*)arg;
208 hprof->DumpHeapObject(obj);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400209}
210
211/*
212 * Walk the roots and heap writing heap information to the specified
213 * file.
214 *
215 * If "fd" is >= 0, the output will be written to that file descriptor.
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500216 * Otherwise, "file_name_" is used to create an output file.
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400217 *
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500218 * If "direct_to_ddms_" is set, the other arguments are ignored, and data is
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400219 * sent directly to DDMS.
220 *
221 * Returns 0 on success, or an error code on failure.
222 */
Elliott Hughesfbd84562011-11-07 18:56:13 -0800223int DumpHeap(const char* fileName, int fd, bool directToDdms)
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400224{
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400225 CHECK(fileName != NULL);
226 ScopedHeapLock lock;
Jesse Wilson1121e0b2011-11-07 15:37:42 -0500227 ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400228
229 ThreadList* thread_list = Runtime::Current()->GetThreadList();
230 thread_list->SuspendAll();
231
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500232 Hprof hprof(fileName, fd, false, directToDdms);
233 Runtime::Current()->VisitRoots(HprofRootVisitor, &hprof);
234 Heap::GetLiveBits()->Walk(HprofBitmapCallback, &hprof);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400235//TODO: write a HEAP_SUMMARY record
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500236 int success = hprof.Finish() ? 0 : -1;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400237 thread_list->ResumeAll();
238 return success;
239}
240
241} // namespace hprof
242
243} // namespace art