Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 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 | |
| 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 | |
| 40 | namespace art { |
| 41 | |
| 42 | namespace hprof { |
| 43 | |
| 44 | #define kHeadSuffix "-hptemp" |
| 45 | |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 46 | // TODO: use File::WriteFully |
| 47 | int 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 Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 69 | bool Hprof::Finish() |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 70 | { |
| 71 | /* flush the "tail" portion of the output */ |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 72 | StartNewRecord(HPROF_TAG_HEAP_DUMP_END, HPROF_TIME); |
| 73 | FlushCurrentRecord(); |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 74 | |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 75 | // 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 Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 79 | |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 80 | LOG(INFO) << StringPrintf("hprof: dumping heap strings to \"%s\".", file_name_); |
| 81 | headCtx.DumpStrings(); |
| 82 | headCtx.DumpClasses(); |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 83 | |
| 84 | /* Write a dummy stack trace record so the analysis |
| 85 | * tools don't freak out. |
| 86 | */ |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 87 | 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 Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 91 | |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 92 | headCtx.FlushCurrentRecord(); |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 93 | |
| 94 | /* flush to ensure memstream pointer and size are updated */ |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 95 | fflush(headCtx.mem_fp_); |
| 96 | fflush(mem_fp_); |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 97 | |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 98 | if (direct_to_ddms_) { |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 99 | /* send the data off to DDMS */ |
| 100 | struct iovec iov[2]; |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 101 | 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 Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 105 | 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 Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 110 | CHECK_EQ(headCtx.fd_, fd_); |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 111 | |
| 112 | int outFd; |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 113 | if (headCtx.fd_ >= 0) { |
| 114 | outFd = dup(headCtx.fd_); |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 115 | if (outFd < 0) { |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 116 | LOG(ERROR) << StringPrintf("dup(%d) failed: %s", headCtx.fd_, strerror(errno)); |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 117 | /* continue to fail-handler below */ |
| 118 | } |
| 119 | } else { |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 120 | outFd = open(file_name_, O_WRONLY|O_CREAT|O_TRUNC, 0644); |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 121 | if (outFd < 0) { |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 122 | LOG(ERROR) << StringPrintf("can't open %s: %s", headCtx.file_name_, strerror(errno)); |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 123 | /* continue to fail-handler below */ |
| 124 | } |
| 125 | } |
| 126 | if (outFd < 0) { |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 127 | return false; |
| 128 | } |
| 129 | |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 130 | 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 Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 133 | close(outFd); |
| 134 | if (result != 0) { |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 135 | 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 Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 141 | (headCtx.file_data_size_ + file_data_size_ + 1023) / 1024); |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 142 | |
| 143 | return true; |
| 144 | } |
| 145 | |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 146 | Hprof::~Hprof() { |
| 147 | /* we don't own ctx->fd_, do not close */ |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 148 | |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 149 | if (mem_fp_ != NULL) { |
| 150 | fclose(mem_fp_); |
| 151 | } |
| 152 | free(current_record_.body_); |
| 153 | free(file_name_); |
| 154 | free(file_data_ptr_); |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 155 | } |
| 156 | |
| 157 | /* |
| 158 | * Visitor invoked on every root reference. |
| 159 | */ |
| 160 | void HprofRootVisitor(const Object* obj, void* arg) { |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 161 | CHECK(arg != NULL); |
| 162 | Hprof* hprof = (Hprof*)arg; |
| 163 | hprof->VisitRoot(obj); |
| 164 | } |
| 165 | |
| 166 | void Hprof::VisitRoot(const Object* obj) { |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 167 | uint32_t threadId = 0; // TODO |
| 168 | /*RootType */ size_t type = 0; // TODO |
| 169 | |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 170 | static const HprofHeapTag xlate[] = { |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 171 | 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 Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 187 | |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 188 | CHECK_LT(type, sizeof(xlate) / sizeof(HprofHeapTag)); |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 189 | if (obj == NULL) { |
| 190 | return; |
| 191 | } |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 192 | 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 Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 197 | } |
| 198 | |
| 199 | /* |
| 200 | * Visitor invoked on every heap object. |
| 201 | */ |
| 202 | |
| 203 | static void HprofBitmapCallback(Object *obj, void *arg) |
| 204 | { |
| 205 | CHECK(obj != NULL); |
| 206 | CHECK(arg != NULL); |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 207 | Hprof *hprof = (Hprof*)arg; |
| 208 | hprof->DumpHeapObject(obj); |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 209 | } |
| 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 Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 216 | * Otherwise, "file_name_" is used to create an output file. |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 217 | * |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 218 | * If "direct_to_ddms_" is set, the other arguments are ignored, and data is |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 219 | * sent directly to DDMS. |
| 220 | * |
| 221 | * Returns 0 on success, or an error code on failure. |
| 222 | */ |
Elliott Hughes | fbd8456 | 2011-11-07 18:56:13 -0800 | [diff] [blame] | 223 | int DumpHeap(const char* fileName, int fd, bool directToDdms) |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 224 | { |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 225 | CHECK(fileName != NULL); |
| 226 | ScopedHeapLock lock; |
Jesse Wilson | 1121e0b | 2011-11-07 15:37:42 -0500 | [diff] [blame] | 227 | ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable); |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 228 | |
| 229 | ThreadList* thread_list = Runtime::Current()->GetThreadList(); |
| 230 | thread_list->SuspendAll(); |
| 231 | |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 232 | Hprof hprof(fileName, fd, false, directToDdms); |
| 233 | Runtime::Current()->VisitRoots(HprofRootVisitor, &hprof); |
| 234 | Heap::GetLiveBits()->Walk(HprofBitmapCallback, &hprof); |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 235 | //TODO: write a HEAP_SUMMARY record |
Jesse Wilson | 3aa66fd | 2011-11-08 20:53:40 -0500 | [diff] [blame^] | 236 | int success = hprof.Finish() ? 0 : -1; |
Jesse Wilson | c4824e6 | 2011-11-01 14:39:04 -0400 | [diff] [blame] | 237 | thread_list->ResumeAll(); |
| 238 | return success; |
| 239 | } |
| 240 | |
| 241 | } // namespace hprof |
| 242 | |
| 243 | } // namespace art |