blob: d00f6f968a19f555f73eb8439b39042552f52b3b [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
Jesse Wilson0b075f12011-11-09 10:57:41 -050047int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg) {
48 while (count != 0) {
49 ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count));
50 if (actual < 0) {
51 int err = errno;
52 LOG(ERROR) << StringPrintf("%s: write failed: %s", logMsg, strerror(err));
53 return err;
54 } else if (actual != (ssize_t) count) {
55 LOG(DEBUG) << StringPrintf("%s: partial write (will retry): (%d of %zd)",
56 logMsg, (int) actual, count);
57 buf = (const void*) (((const uint8_t*) buf) + actual);
Jesse Wilsonc4824e62011-11-01 14:39:04 -040058 }
Jesse Wilson0b075f12011-11-09 10:57:41 -050059 count -= actual;
60 }
61 return 0;
Jesse Wilsonc4824e62011-11-01 14:39:04 -040062}
63
64/*
65 * Finish up the hprof dump. Returns true on success.
66 */
Jesse Wilson0b075f12011-11-09 10:57:41 -050067bool Hprof::Finish() {
68 // flush the "tail" portion of the output
69 StartNewRecord(HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
70 FlushCurrentRecord();
Jesse Wilsonc4824e62011-11-01 14:39:04 -040071
Jesse Wilson0b075f12011-11-09 10:57:41 -050072 // create a new Hprof for the start of the file (as opposed to this, which is the tail)
73 Hprof headCtx(file_name_, fd_, true, direct_to_ddms_);
74 headCtx.classes_ = classes_;
75 headCtx.strings_ = strings_;
Jesse Wilsonc4824e62011-11-01 14:39:04 -040076
Jesse Wilson0b075f12011-11-09 10:57:41 -050077 LOG(INFO) << StringPrintf("hprof: dumping heap strings to \"%s\".", file_name_);
78 headCtx.DumpStrings();
79 headCtx.DumpClasses();
Jesse Wilsonc4824e62011-11-01 14:39:04 -040080
Jesse Wilson0b075f12011-11-09 10:57:41 -050081 // write a dummy stack trace record so the analysis tools don't freak out
82 headCtx.StartNewRecord(HPROF_TAG_STACK_TRACE, HPROF_TIME);
83 headCtx.current_record_.AddU4(HPROF_NULL_STACK_TRACE);
84 headCtx.current_record_.AddU4(HPROF_NULL_THREAD);
85 headCtx.current_record_.AddU4(0); // no frames
Jesse Wilsonc4824e62011-11-01 14:39:04 -040086
Jesse Wilson0b075f12011-11-09 10:57:41 -050087 headCtx.FlushCurrentRecord();
Jesse Wilsonc4824e62011-11-01 14:39:04 -040088
Jesse Wilson0b075f12011-11-09 10:57:41 -050089 // flush to ensure memstream pointer and size are updated
90 fflush(headCtx.mem_fp_);
91 fflush(mem_fp_);
Jesse Wilsonc4824e62011-11-01 14:39:04 -040092
Jesse Wilson0b075f12011-11-09 10:57:41 -050093 if (direct_to_ddms_) {
94 // send the data off to DDMS
95 struct iovec iov[2];
96 iov[0].iov_base = headCtx.file_data_ptr_;
97 iov[0].iov_len = headCtx.file_data_size_;
98 iov[1].iov_base = file_data_ptr_;
99 iov[1].iov_len = file_data_size_;
100 Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
101 } else {
102 // open the output file, and copy the head and tail to it.
103 CHECK_EQ(headCtx.fd_, fd_);
104
105 int outFd;
106 if (headCtx.fd_ >= 0) {
107 outFd = dup(headCtx.fd_);
108 if (outFd < 0) {
109 LOG(ERROR) << StringPrintf("dup(%d) failed: %s", headCtx.fd_, strerror(errno));
110 // continue to fail-handler below
111 }
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400112 } else {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500113 outFd = open(file_name_, O_WRONLY|O_CREAT|O_TRUNC, 0644);
114 if (outFd < 0) {
115 LOG(ERROR) << StringPrintf("can't open %s: %s", headCtx.file_name_, strerror(errno));
116 // continue to fail-handler below
117 }
118 }
119 if (outFd < 0) {
120 return false;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400121 }
122
Jesse Wilson0b075f12011-11-09 10:57:41 -0500123 int result = sysWriteFully(outFd, headCtx.file_data_ptr_,
124 headCtx.file_data_size_, "hprof-head");
125 result |= sysWriteFully(outFd, file_data_ptr_, file_data_size_, "hprof-tail");
126 close(outFd);
127 if (result != 0) {
128 return false;
129 }
130 }
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400131
Jesse Wilson0b075f12011-11-09 10:57:41 -0500132 // throw out a log message for the benefit of "runhat"
133 LOG(INFO) << StringPrintf("hprof: heap dump completed (%dKB)",
134 (headCtx.file_data_size_ + file_data_size_ + 1023) / 1024);
135
136 return true;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400137}
138
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500139Hprof::~Hprof() {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500140 // we don't own ctx->fd_, do not close
141 if (mem_fp_ != NULL) {
142 fclose(mem_fp_);
143 }
144 free(current_record_.body_);
145 free(file_name_);
146 free(file_data_ptr_);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400147}
148
149/*
150 * Visitor invoked on every root reference.
151 */
152void HprofRootVisitor(const Object* obj, void* arg) {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500153 CHECK(arg != NULL);
154 Hprof* hprof = (Hprof*)arg;
155 hprof->VisitRoot(obj);
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500156}
157
158void Hprof::VisitRoot(const Object* obj) {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500159 uint32_t threadId = 0; // TODO
160 /*RootType */ size_t type = 0; // TODO
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400161
Jesse Wilson0b075f12011-11-09 10:57:41 -0500162 static const HprofHeapTag xlate[] = {
163 HPROF_ROOT_UNKNOWN,
164 HPROF_ROOT_JNI_GLOBAL,
165 HPROF_ROOT_JNI_LOCAL,
166 HPROF_ROOT_JAVA_FRAME,
167 HPROF_ROOT_NATIVE_STACK,
168 HPROF_ROOT_STICKY_CLASS,
169 HPROF_ROOT_THREAD_BLOCK,
170 HPROF_ROOT_MONITOR_USED,
171 HPROF_ROOT_THREAD_OBJECT,
172 HPROF_ROOT_INTERNED_STRING,
173 HPROF_ROOT_FINALIZING,
174 HPROF_ROOT_DEBUGGER,
175 HPROF_ROOT_REFERENCE_CLEANUP,
176 HPROF_ROOT_VM_INTERNAL,
177 HPROF_ROOT_JNI_MONITOR,
178 };
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400179
Jesse Wilson0b075f12011-11-09 10:57:41 -0500180 CHECK_LT(type, sizeof(xlate) / sizeof(HprofHeapTag));
181 if (obj == NULL) {
182 return;
183 }
184 gc_scan_state_ = xlate[type];
185 gc_thread_serial_number_ = threadId;
186 MarkRootObject(obj, 0);
187 gc_scan_state_ = 0;
188 gc_thread_serial_number_ = 0;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400189}
190
191/*
192 * Visitor invoked on every heap object.
193 */
Jesse Wilson0b075f12011-11-09 10:57:41 -0500194static void HprofBitmapCallback(Object *obj, void *arg) {
195 CHECK(obj != NULL);
196 CHECK(arg != NULL);
197 Hprof *hprof = (Hprof*)arg;
198 hprof->DumpHeapObject(obj);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400199}
200
201/*
202 * Walk the roots and heap writing heap information to the specified
203 * file.
204 *
205 * If "fd" is >= 0, the output will be written to that file descriptor.
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500206 * Otherwise, "file_name_" is used to create an output file.
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400207 *
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500208 * If "direct_to_ddms_" is set, the other arguments are ignored, and data is
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400209 * sent directly to DDMS.
210 *
211 * Returns 0 on success, or an error code on failure.
212 */
Jesse Wilson0b075f12011-11-09 10:57:41 -0500213int DumpHeap(const char* fileName, int fd, bool directToDdms) {
214 CHECK(fileName != NULL);
215 ScopedHeapLock lock;
216 ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400217
Jesse Wilson0b075f12011-11-09 10:57:41 -0500218 ThreadList* thread_list = Runtime::Current()->GetThreadList();
219 thread_list->SuspendAll();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400220
Jesse Wilson0b075f12011-11-09 10:57:41 -0500221 Hprof hprof(fileName, fd, false, directToDdms);
222 Runtime::Current()->VisitRoots(HprofRootVisitor, &hprof);
223 Heap::GetLiveBits()->Walk(HprofBitmapCallback, &hprof);
224 // TODO: write a HEAP_SUMMARY record
225 int success = hprof.Finish() ? 0 : -1;
226 thread_list->ResumeAll();
227 return success;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400228}
229
230} // namespace hprof
231
232} // namespace art