blob: 922fc5f530f9b1c8e1414db24999ab6c9fdf24d5 [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"
Ian Rogers6d4d9fc2011-11-30 16:24:48 -080026
Elliott Hughes622a6982012-06-08 17:58:54 -070027#include <cutils/open_memstream.h>
28#include <errno.h>
29#include <fcntl.h>
30#include <stdio.h>
31#include <string.h>
32#include <sys/time.h>
33#include <sys/uio.h>
34#include <time.h>
35#include <time.h>
36#include <unistd.h>
37
38#include <set>
39
Mathieu Chartierc7853442015-03-27 14:35:38 -070040#include "art_field-inl.h"
Elliott Hughes07ed66b2012-12-12 18:34:25 -080041#include "base/logging.h"
Elliott Hughese222ee02012-12-13 14:41:43 -080042#include "base/stringprintf.h"
Vladimir Marko80afd022015-05-19 18:08:00 +010043#include "base/time_utils.h"
Elliott Hughes76160052012-12-12 16:31:20 -080044#include "base/unix_file/fd_file.h"
Ian Rogers6d4d9fc2011-11-30 16:24:48 -080045#include "class_linker.h"
Ian Rogers62d6c772013-02-27 08:32:07 -080046#include "common_throws.h"
Jesse Wilsonc4824e62011-11-01 14:39:04 -040047#include "debugger.h"
Ian Rogers4f6ad8a2013-03-18 15:27:28 -070048#include "dex_file-inl.h"
Mathieu Chartiere34fa1d2015-01-14 14:55:47 -080049#include "gc_root.h"
Ian Rogers1d54e732013-05-02 21:10:01 -070050#include "gc/accounting/heap_bitmap.h"
51#include "gc/heap.h"
52#include "gc/space/space.h"
Elliott Hughes622a6982012-06-08 17:58:54 -070053#include "globals.h"
Mathieu Chartierad466ad2015-01-08 16:28:08 -080054#include "jdwp/jdwp.h"
55#include "jdwp/jdwp_priv.h"
Ian Rogers2dd0e2c2013-01-24 12:42:14 -080056#include "mirror/class.h"
57#include "mirror/class-inl.h"
Ian Rogers2dd0e2c2013-01-24 12:42:14 -080058#include "mirror/object-inl.h"
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -070059#include "os.h"
Elliott Hughes622a6982012-06-08 17:58:54 -070060#include "safe_map.h"
Ian Rogers00f7d0e2012-07-19 15:28:27 -070061#include "scoped_thread_state_change.h"
Elliott Hughes622a6982012-06-08 17:58:54 -070062#include "thread_list.h"
Jesse Wilsonc4824e62011-11-01 14:39:04 -040063
64namespace art {
65
66namespace hprof {
67
Mathieu Chartierad466ad2015-01-08 16:28:08 -080068static constexpr bool kDirectStream = true;
Jesse Wilson0c54ac12011-11-09 15:14:05 -050069
Andreas Gampe3a913092015-01-10 00:26:17 -080070static constexpr uint32_t kHprofTime = 0;
71static constexpr uint32_t kHprofNullStackTrace = 0;
72static constexpr uint32_t kHprofNullThread = 0;
Elliott Hughes622a6982012-06-08 17:58:54 -070073
Andreas Gampe3a913092015-01-10 00:26:17 -080074static constexpr size_t kMaxObjectsPerSegment = 128;
75static constexpr size_t kMaxBytesPerSegment = 4096;
Elliott Hughes622a6982012-06-08 17:58:54 -070076
Andreas Gampe3a913092015-01-10 00:26:17 -080077// The static field-name for the synthetic object generated to account for class static overhead.
78static constexpr const char* kStaticOverheadName = "$staticOverhead";
Elliott Hughes622a6982012-06-08 17:58:54 -070079
80enum HprofTag {
81 HPROF_TAG_STRING = 0x01,
82 HPROF_TAG_LOAD_CLASS = 0x02,
83 HPROF_TAG_UNLOAD_CLASS = 0x03,
84 HPROF_TAG_STACK_FRAME = 0x04,
85 HPROF_TAG_STACK_TRACE = 0x05,
86 HPROF_TAG_ALLOC_SITES = 0x06,
87 HPROF_TAG_HEAP_SUMMARY = 0x07,
88 HPROF_TAG_START_THREAD = 0x0A,
89 HPROF_TAG_END_THREAD = 0x0B,
90 HPROF_TAG_HEAP_DUMP = 0x0C,
91 HPROF_TAG_HEAP_DUMP_SEGMENT = 0x1C,
92 HPROF_TAG_HEAP_DUMP_END = 0x2C,
93 HPROF_TAG_CPU_SAMPLES = 0x0D,
94 HPROF_TAG_CONTROL_SETTINGS = 0x0E,
95};
96
97// Values for the first byte of HEAP_DUMP and HEAP_DUMP_SEGMENT records:
98enum HprofHeapTag {
99 // Traditional.
100 HPROF_ROOT_UNKNOWN = 0xFF,
101 HPROF_ROOT_JNI_GLOBAL = 0x01,
102 HPROF_ROOT_JNI_LOCAL = 0x02,
103 HPROF_ROOT_JAVA_FRAME = 0x03,
104 HPROF_ROOT_NATIVE_STACK = 0x04,
105 HPROF_ROOT_STICKY_CLASS = 0x05,
106 HPROF_ROOT_THREAD_BLOCK = 0x06,
107 HPROF_ROOT_MONITOR_USED = 0x07,
108 HPROF_ROOT_THREAD_OBJECT = 0x08,
109 HPROF_CLASS_DUMP = 0x20,
110 HPROF_INSTANCE_DUMP = 0x21,
111 HPROF_OBJECT_ARRAY_DUMP = 0x22,
112 HPROF_PRIMITIVE_ARRAY_DUMP = 0x23,
113
114 // Android.
115 HPROF_HEAP_DUMP_INFO = 0xfe,
116 HPROF_ROOT_INTERNED_STRING = 0x89,
117 HPROF_ROOT_FINALIZING = 0x8a, // Obsolete.
118 HPROF_ROOT_DEBUGGER = 0x8b,
119 HPROF_ROOT_REFERENCE_CLEANUP = 0x8c, // Obsolete.
120 HPROF_ROOT_VM_INTERNAL = 0x8d,
121 HPROF_ROOT_JNI_MONITOR = 0x8e,
122 HPROF_UNREACHABLE = 0x90, // Obsolete.
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700123 HPROF_PRIMITIVE_ARRAY_NODATA_DUMP = 0xc3, // Obsolete.
Elliott Hughes622a6982012-06-08 17:58:54 -0700124};
125
126enum HprofHeapId {
127 HPROF_HEAP_DEFAULT = 0,
128 HPROF_HEAP_ZYGOTE = 'Z',
Mathieu Chartierae1ad002014-07-18 17:58:22 -0700129 HPROF_HEAP_APP = 'A',
130 HPROF_HEAP_IMAGE = 'I',
Elliott Hughes622a6982012-06-08 17:58:54 -0700131};
132
133enum HprofBasicType {
134 hprof_basic_object = 2,
135 hprof_basic_boolean = 4,
136 hprof_basic_char = 5,
137 hprof_basic_float = 6,
138 hprof_basic_double = 7,
139 hprof_basic_byte = 8,
140 hprof_basic_short = 9,
141 hprof_basic_int = 10,
142 hprof_basic_long = 11,
143};
144
Ian Rogersef7d42f2014-01-06 12:55:46 -0800145typedef uint32_t HprofStringId;
146typedef uint32_t HprofClassObjectId;
Elliott Hughes622a6982012-06-08 17:58:54 -0700147
Andreas Gampe3a913092015-01-10 00:26:17 -0800148class EndianOutput {
Elliott Hughes622a6982012-06-08 17:58:54 -0700149 public:
Andreas Gampe3a913092015-01-10 00:26:17 -0800150 EndianOutput() : length_(0), sum_length_(0), max_length_(0), started_(false) {}
151 virtual ~EndianOutput() {}
152
153 void StartNewRecord(uint8_t tag, uint32_t time) {
154 if (length_ > 0) {
155 EndRecord();
156 }
157 DCHECK_EQ(length_, 0U);
158 AddU1(tag);
159 AddU4(time);
160 AddU4(0xdeaddead); // Length, replaced on flush.
161 started_ = true;
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700162 }
163
Andreas Gampe3a913092015-01-10 00:26:17 -0800164 void EndRecord() {
165 // Replace length in header.
166 if (started_) {
167 UpdateU4(sizeof(uint8_t) + sizeof(uint32_t),
168 length_ - sizeof(uint8_t) - 2 * sizeof(uint32_t));
169 }
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700170
Andreas Gampe3a913092015-01-10 00:26:17 -0800171 HandleEndRecord();
172
173 sum_length_ += length_;
174 max_length_ = std::max(max_length_, length_);
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700175 length_ = 0;
Andreas Gampe3a913092015-01-10 00:26:17 -0800176 started_ = false;
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700177 }
178
Andreas Gampe3a913092015-01-10 00:26:17 -0800179 void AddU1(uint8_t value) {
180 AddU1List(&value, 1);
181 }
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800182 void AddU2(uint16_t value) {
183 AddU2List(&value, 1);
Elliott Hughes622a6982012-06-08 17:58:54 -0700184 }
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800185 void AddU4(uint32_t value) {
186 AddU4List(&value, 1);
Elliott Hughes622a6982012-06-08 17:58:54 -0700187 }
188
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800189 void AddU8(uint64_t value) {
190 AddU8List(&value, 1);
Elliott Hughes622a6982012-06-08 17:58:54 -0700191 }
192
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800193 void AddObjectId(const mirror::Object* value) {
194 AddU4(PointerToLowMemUInt32(value));
Ian Rogersef7d42f2014-01-06 12:55:46 -0800195 }
196
197 // The ID for the synthetic object generated to account for class static overhead.
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800198 void AddClassStaticsId(const mirror::Class* value) {
199 AddU4(1 | PointerToLowMemUInt32(value));
Ian Rogersef7d42f2014-01-06 12:55:46 -0800200 }
201
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800202 void AddJniGlobalRefId(jobject value) {
203 AddU4(PointerToLowMemUInt32(value));
Ian Rogersef7d42f2014-01-06 12:55:46 -0800204 }
205
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800206 void AddClassId(HprofClassObjectId value) {
207 AddU4(value);
Ian Rogersef7d42f2014-01-06 12:55:46 -0800208 }
209
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800210 void AddStringId(HprofStringId value) {
211 AddU4(value);
Elliott Hughes622a6982012-06-08 17:58:54 -0700212 }
213
Andreas Gampe3a913092015-01-10 00:26:17 -0800214 void AddU1List(const uint8_t* values, size_t count) {
215 HandleU1List(values, count);
216 length_ += count;
217 }
218 void AddU2List(const uint16_t* values, size_t count) {
219 HandleU2List(values, count);
220 length_ += count * sizeof(uint16_t);
221 }
222 void AddU4List(const uint32_t* values, size_t count) {
223 HandleU4List(values, count);
224 length_ += count * sizeof(uint32_t);
225 }
Andreas Gampeca714582015-04-03 19:41:34 -0700226 virtual void UpdateU4(size_t offset, uint32_t new_value ATTRIBUTE_UNUSED) {
Andreas Gampe3a913092015-01-10 00:26:17 -0800227 DCHECK_LE(offset, length_ - 4);
228 }
229 void AddU8List(const uint64_t* values, size_t count) {
230 HandleU8List(values, count);
231 length_ += count * sizeof(uint64_t);
232 }
Elliott Hughes622a6982012-06-08 17:58:54 -0700233
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800234 void AddIdList(mirror::ObjectArray<mirror::Object>* values)
Andreas Gampe3a913092015-01-10 00:26:17 -0800235 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800236 const int32_t length = values->GetLength();
Ian Rogersef7d42f2014-01-06 12:55:46 -0800237 for (int32_t i = 0; i < length; ++i) {
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800238 AddObjectId(values->GetWithoutChecks(i));
Ian Rogersef7d42f2014-01-06 12:55:46 -0800239 }
Elliott Hughes622a6982012-06-08 17:58:54 -0700240 }
241
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800242 void AddUtf8String(const char* str) {
Elliott Hughes622a6982012-06-08 17:58:54 -0700243 // The terminating NUL character is NOT written.
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800244 AddU1List((const uint8_t*)str, strlen(str));
Elliott Hughes622a6982012-06-08 17:58:54 -0700245 }
246
Andreas Gampe3a913092015-01-10 00:26:17 -0800247 size_t Length() const {
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700248 return length_;
249 }
Elliott Hughes622a6982012-06-08 17:58:54 -0700250
Andreas Gampe3a913092015-01-10 00:26:17 -0800251 size_t SumLength() const {
252 return sum_length_;
Elliott Hughes622a6982012-06-08 17:58:54 -0700253 }
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700254
Andreas Gampe3a913092015-01-10 00:26:17 -0800255 size_t MaxLength() const {
256 return max_length_;
257 }
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700258
Andreas Gampe3a913092015-01-10 00:26:17 -0800259 protected:
260 virtual void HandleU1List(const uint8_t* values ATTRIBUTE_UNUSED,
261 size_t count ATTRIBUTE_UNUSED) {
262 }
263 virtual void HandleU2List(const uint16_t* values ATTRIBUTE_UNUSED,
264 size_t count ATTRIBUTE_UNUSED) {
265 }
266 virtual void HandleU4List(const uint32_t* values ATTRIBUTE_UNUSED,
267 size_t count ATTRIBUTE_UNUSED) {
268 }
269 virtual void HandleU8List(const uint64_t* values ATTRIBUTE_UNUSED,
270 size_t count ATTRIBUTE_UNUSED) {
271 }
272 virtual void HandleEndRecord() {
273 }
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700274
Andreas Gampe3a913092015-01-10 00:26:17 -0800275 size_t length_; // Current record size.
276 size_t sum_length_; // Size of all data.
277 size_t max_length_; // Maximum seen length.
278 bool started_; // Was StartRecord called?
Elliott Hughes622a6982012-06-08 17:58:54 -0700279};
280
Andreas Gampe3a913092015-01-10 00:26:17 -0800281// This keeps things buffered until flushed.
282class EndianOutputBuffered : public EndianOutput {
283 public:
284 explicit EndianOutputBuffered(size_t reserve_size) {
285 buffer_.reserve(reserve_size);
286 }
287 virtual ~EndianOutputBuffered() {}
288
289 void UpdateU4(size_t offset, uint32_t new_value) OVERRIDE {
290 DCHECK_LE(offset, length_ - 4);
291 buffer_[offset + 0] = static_cast<uint8_t>((new_value >> 24) & 0xFF);
292 buffer_[offset + 1] = static_cast<uint8_t>((new_value >> 16) & 0xFF);
293 buffer_[offset + 2] = static_cast<uint8_t>((new_value >> 8) & 0xFF);
294 buffer_[offset + 3] = static_cast<uint8_t>((new_value >> 0) & 0xFF);
295 }
296
297 protected:
298 void HandleU1List(const uint8_t* values, size_t count) OVERRIDE {
299 DCHECK_EQ(length_, buffer_.size());
300 buffer_.insert(buffer_.end(), values, values + count);
301 }
302
303 void HandleU2List(const uint16_t* values, size_t count) OVERRIDE {
304 DCHECK_EQ(length_, buffer_.size());
305 for (size_t i = 0; i < count; ++i) {
306 uint16_t value = *values;
307 buffer_.push_back(static_cast<uint8_t>((value >> 8) & 0xFF));
308 buffer_.push_back(static_cast<uint8_t>((value >> 0) & 0xFF));
309 values++;
310 }
311 }
312
313 void HandleU4List(const uint32_t* values, size_t count) OVERRIDE {
314 DCHECK_EQ(length_, buffer_.size());
315 for (size_t i = 0; i < count; ++i) {
316 uint32_t value = *values;
317 buffer_.push_back(static_cast<uint8_t>((value >> 24) & 0xFF));
318 buffer_.push_back(static_cast<uint8_t>((value >> 16) & 0xFF));
319 buffer_.push_back(static_cast<uint8_t>((value >> 8) & 0xFF));
320 buffer_.push_back(static_cast<uint8_t>((value >> 0) & 0xFF));
321 values++;
322 }
323 }
324
325 void HandleU8List(const uint64_t* values, size_t count) OVERRIDE {
326 DCHECK_EQ(length_, buffer_.size());
327 for (size_t i = 0; i < count; ++i) {
328 uint64_t value = *values;
329 buffer_.push_back(static_cast<uint8_t>((value >> 56) & 0xFF));
330 buffer_.push_back(static_cast<uint8_t>((value >> 48) & 0xFF));
331 buffer_.push_back(static_cast<uint8_t>((value >> 40) & 0xFF));
332 buffer_.push_back(static_cast<uint8_t>((value >> 32) & 0xFF));
333 buffer_.push_back(static_cast<uint8_t>((value >> 24) & 0xFF));
334 buffer_.push_back(static_cast<uint8_t>((value >> 16) & 0xFF));
335 buffer_.push_back(static_cast<uint8_t>((value >> 8) & 0xFF));
336 buffer_.push_back(static_cast<uint8_t>((value >> 0) & 0xFF));
337 values++;
338 }
339 }
340
341 void HandleEndRecord() OVERRIDE {
342 DCHECK_EQ(buffer_.size(), length_);
343 if (kIsDebugBuild && started_) {
344 uint32_t stored_length =
345 static_cast<uint32_t>(buffer_[5]) << 24 |
346 static_cast<uint32_t>(buffer_[6]) << 16 |
347 static_cast<uint32_t>(buffer_[7]) << 8 |
348 static_cast<uint32_t>(buffer_[8]);
349 DCHECK_EQ(stored_length, length_ - sizeof(uint8_t) - 2 * sizeof(uint32_t));
350 }
351 HandleFlush(buffer_.data(), length_);
352 buffer_.clear();
353 }
354
355 virtual void HandleFlush(const uint8_t* buffer ATTRIBUTE_UNUSED, size_t length ATTRIBUTE_UNUSED) {
356 }
357
358 std::vector<uint8_t> buffer_;
359};
360
361class FileEndianOutput FINAL : public EndianOutputBuffered {
362 public:
363 FileEndianOutput(File* fp, size_t reserved_size)
364 : EndianOutputBuffered(reserved_size), fp_(fp), errors_(false) {
365 DCHECK(fp != nullptr);
366 }
367 ~FileEndianOutput() {
368 }
369
370 bool Errors() {
371 return errors_;
372 }
373
374 protected:
375 void HandleFlush(const uint8_t* buffer, size_t length) OVERRIDE {
376 if (!errors_) {
377 errors_ = !fp_->WriteFully(buffer, length);
378 }
379 }
380
381 private:
382 File* fp_;
383 bool errors_;
384};
385
386class NetStateEndianOutput FINAL : public EndianOutputBuffered {
387 public:
388 NetStateEndianOutput(JDWP::JdwpNetStateBase* net_state, size_t reserved_size)
389 : EndianOutputBuffered(reserved_size), net_state_(net_state) {
390 DCHECK(net_state != nullptr);
391 }
392 ~NetStateEndianOutput() {}
393
394 protected:
395 void HandleFlush(const uint8_t* buffer, size_t length) OVERRIDE {
396 std::vector<iovec> iov;
397 iov.push_back(iovec());
398 iov[0].iov_base = const_cast<void*>(reinterpret_cast<const void*>(buffer));
399 iov[0].iov_len = length;
400 net_state_->WriteBufferedPacketLocked(iov);
401 }
402
403 private:
404 JDWP::JdwpNetStateBase* net_state_;
405};
406
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700407#define __ output_->
Andreas Gampe3a913092015-01-10 00:26:17 -0800408
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700409class Hprof : public SingleRootVisitor {
Elliott Hughes622a6982012-06-08 17:58:54 -0700410 public:
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700411 Hprof(const char* output_filename, int fd, bool direct_to_ddms)
412 : filename_(output_filename),
413 fd_(fd),
414 direct_to_ddms_(direct_to_ddms),
415 start_ns_(NanoTime()),
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700416 current_heap_(HPROF_HEAP_DEFAULT),
417 objects_in_segment_(0),
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700418 next_string_id_(0x400000) {
419 LOG(INFO) << "hprof: heap dump \"" << filename_ << "\" starting...";
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500420 }
421
Andreas Gampe3a913092015-01-10 00:26:17 -0800422 void Dump()
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800423 EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800424 LOCKS_EXCLUDED(Locks::heap_bitmap_lock_) {
Andreas Gampe3a913092015-01-10 00:26:17 -0800425 // First pass to measure the size of the dump.
426 size_t overall_size;
427 size_t max_length;
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800428 {
Andreas Gampe3a913092015-01-10 00:26:17 -0800429 EndianOutput count_output;
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700430 output_ = &count_output;
431 ProcessHeap(false);
Andreas Gampe3a913092015-01-10 00:26:17 -0800432 overall_size = count_output.SumLength();
433 max_length = count_output.MaxLength();
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700434 output_ = nullptr;
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800435 }
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700436
Andreas Gampe3a913092015-01-10 00:26:17 -0800437 bool okay;
438 if (direct_to_ddms_) {
439 if (kDirectStream) {
440 okay = DumpToDdmsDirect(overall_size, max_length, CHUNK_TYPE("HPDS"));
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700441 } else {
Andreas Gampe3a913092015-01-10 00:26:17 -0800442 okay = DumpToDdmsBuffered(overall_size, max_length);
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700443 }
Andreas Gampe3a913092015-01-10 00:26:17 -0800444 } else {
445 okay = DumpToFile(overall_size, max_length);
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700446 }
447
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700448 if (okay) {
449 uint64_t duration = NanoTime() - start_ns_;
Ian Rogers50b35e22012-10-04 10:09:15 -0700450 LOG(INFO) << "hprof: heap dump completed ("
Andreas Gampe3a913092015-01-10 00:26:17 -0800451 << PrettySize(RoundUp(overall_size, 1024))
Ian Rogers50b35e22012-10-04 10:09:15 -0700452 << ") in " << PrettyDuration(duration);
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700453 }
454 }
455
456 private:
Mathieu Chartier83c8ee02014-01-28 14:50:23 -0800457 static void VisitObjectCallback(mirror::Object* obj, void* arg)
Ian Rogersb726dcb2012-09-05 08:57:23 -0700458 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800459 DCHECK(obj != nullptr);
460 DCHECK(arg != nullptr);
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700461 reinterpret_cast<Hprof*>(arg)->DumpHeapObject(obj);
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700462 }
463
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700464 void DumpHeapObject(mirror::Object* obj)
Mathieu Chartier83c8ee02014-01-28 14:50:23 -0800465 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700466
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700467 void DumpHeapClass(mirror::Class* klass)
Andreas Gampe3a913092015-01-10 00:26:17 -0800468 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700469
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700470 void DumpHeapArray(mirror::Array* obj, mirror::Class* klass)
Andreas Gampe3a913092015-01-10 00:26:17 -0800471 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
472
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700473 void DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass)
Andreas Gampe3a913092015-01-10 00:26:17 -0800474 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
475
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700476 void ProcessHeap(bool header_first)
Hiroshi Yamauchi0c8c3032015-01-16 16:54:35 -0800477 EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
Andreas Gampe3a913092015-01-10 00:26:17 -0800478 // Reset current heap and object count.
479 current_heap_ = HPROF_HEAP_DEFAULT;
480 objects_in_segment_ = 0;
481
482 if (header_first) {
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700483 ProcessHeader();
484 ProcessBody();
Andreas Gampe3a913092015-01-10 00:26:17 -0800485 } else {
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700486 ProcessBody();
487 ProcessHeader();
Andreas Gampe3a913092015-01-10 00:26:17 -0800488 }
489 }
490
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700491 void ProcessBody() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
492 Runtime* const runtime = Runtime::Current();
Andreas Gampe3a913092015-01-10 00:26:17 -0800493 // Walk the roots and the heap.
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700494 output_->StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, kHprofTime);
Andreas Gampe3a913092015-01-10 00:26:17 -0800495
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700496 runtime->VisitRoots(this);
497 runtime->VisitImageRoots(this);
498 runtime->GetHeap()->VisitObjectsPaused(VisitObjectCallback, this);
Andreas Gampe3a913092015-01-10 00:26:17 -0800499
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700500 output_->StartNewRecord(HPROF_TAG_HEAP_DUMP_END, kHprofTime);
501 output_->EndRecord();
Andreas Gampe3a913092015-01-10 00:26:17 -0800502 }
503
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700504 void ProcessHeader() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
Andreas Gampe3a913092015-01-10 00:26:17 -0800505 // Write the header.
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700506 WriteFixedHeader();
Andreas Gampe3a913092015-01-10 00:26:17 -0800507 // Write the string and class tables, and any stack traces, to the header.
508 // (jhat requires that these appear before any of the data in the body that refers to them.)
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700509 WriteStringTable();
510 WriteClassTable();
511 WriteStackTraces();
512 output_->EndRecord();
Andreas Gampe3a913092015-01-10 00:26:17 -0800513 }
514
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700515 void WriteClassTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700516 uint32_t nextSerialNumber = 1;
517
Ian Rogersef7d42f2014-01-06 12:55:46 -0800518 for (mirror::Class* c : classes_) {
519 CHECK(c != nullptr);
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700520 output_->StartNewRecord(HPROF_TAG_LOAD_CLASS, kHprofTime);
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700521 // LOAD CLASS format:
522 // U4: class serial number (always > 0)
523 // ID: class object ID. We use the address of the class object structure as its ID.
524 // U4: stack trace serial number
525 // ID: class name string ID
Andreas Gampe3a913092015-01-10 00:26:17 -0800526 __ AddU4(nextSerialNumber++);
527 __ AddObjectId(c);
528 __ AddU4(kHprofNullStackTrace);
529 __ AddStringId(LookupClassNameId(c));
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700530 }
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700531 }
532
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700533 void WriteStringTable() {
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800534 for (const std::pair<std::string, HprofStringId>& p : strings_) {
Ian Rogersef7d42f2014-01-06 12:55:46 -0800535 const std::string& string = p.first;
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800536 const size_t id = p.second;
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700537
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700538 output_->StartNewRecord(HPROF_TAG_STRING, kHprofTime);
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700539
540 // STRING format:
541 // ID: ID for this string
Mathieu Chartier2cebb242015-04-21 16:50:40 -0700542 // U1*: UTF8 characters for string (NOT null terminated)
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700543 // (the record format encodes the length)
Andreas Gampe3a913092015-01-10 00:26:17 -0800544 __ AddU4(id);
545 __ AddUtf8String(string.c_str());
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700546 }
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700547 }
548
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700549 void StartNewHeapDumpSegment() {
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700550 // This flushes the old segment and starts a new one.
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700551 output_->StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, kHprofTime);
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700552 objects_in_segment_ = 0;
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700553 // Starting a new HEAP_DUMP resets the heap to default.
554 current_heap_ = HPROF_HEAP_DEFAULT;
555 }
556
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700557 void CheckHeapSegmentConstraints() {
558 if (objects_in_segment_ >= kMaxObjectsPerSegment || output_->Length() >= kMaxBytesPerSegment) {
559 StartNewHeapDumpSegment();
Andreas Gampe3a913092015-01-10 00:26:17 -0800560 }
561 }
562
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700563 void VisitRoot(mirror::Object* obj, const RootInfo& root_info)
564 OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
Andreas Gampe3a913092015-01-10 00:26:17 -0800565 void MarkRootObject(const mirror::Object* obj, jobject jni_obj, HprofHeapTag heap_tag,
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700566 uint32_t thread_serial);
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700567
Ian Rogersef7d42f2014-01-06 12:55:46 -0800568 HprofClassObjectId LookupClassId(mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800569 if (c != nullptr) {
Ian Rogersef7d42f2014-01-06 12:55:46 -0800570 auto result = classes_.insert(c);
571 const mirror::Class* present = *result.first;
572 CHECK_EQ(present, c);
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800573 // Make sure that we've assigned a string ID for this class' name
574 LookupClassNameId(c);
Ian Rogersef7d42f2014-01-06 12:55:46 -0800575 }
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800576 return PointerToLowMemUInt32(c);
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700577 }
578
Ian Rogersef7d42f2014-01-06 12:55:46 -0800579 HprofStringId LookupStringId(mirror::String* string) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700580 return LookupStringId(string->ToModifiedUtf8());
581 }
582
583 HprofStringId LookupStringId(const char* string) {
584 return LookupStringId(std::string(string));
585 }
586
587 HprofStringId LookupStringId(const std::string& string) {
Ian Rogersef7d42f2014-01-06 12:55:46 -0800588 auto it = strings_.find(string);
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700589 if (it != strings_.end()) {
590 return it->second;
591 }
592 HprofStringId id = next_string_id_++;
593 strings_.Put(string, id);
594 return id;
595 }
596
Ian Rogersef7d42f2014-01-06 12:55:46 -0800597 HprofStringId LookupClassNameId(mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700598 return LookupStringId(PrettyDescriptor(c));
599 }
600
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700601 void WriteFixedHeader() {
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500602 // Write the file header.
603 // U1: NUL-terminated magic string.
Andreas Gampe3a913092015-01-10 00:26:17 -0800604 const char magic[] = "JAVA PROFILE 1.0.3";
605 __ AddU1List(reinterpret_cast<const uint8_t*>(magic), sizeof(magic));
606
Calin Juravle32805172014-07-04 16:24:03 +0100607 // U4: size of identifiers. We're using addresses as IDs and our heap references are stored
608 // as uint32_t.
609 // Note of warning: hprof-conv hard-codes the size of identifiers to 4.
Andreas Gampe575e78c2014-11-03 23:41:03 -0800610 static_assert(sizeof(mirror::HeapReference<mirror::Object>) == sizeof(uint32_t),
611 "Unexpected HeapReference size");
Andreas Gampe3a913092015-01-10 00:26:17 -0800612 __ AddU4(sizeof(uint32_t));
613
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500614 // The current time, in milliseconds since 0:00 GMT, 1/1/70.
Elliott Hughes7b9d9962012-04-20 18:48:18 -0700615 timeval now;
Andreas Gampe3a913092015-01-10 00:26:17 -0800616 const uint64_t nowMs = (gettimeofday(&now, nullptr) < 0) ? 0 :
Mathieu Chartierad466ad2015-01-08 16:28:08 -0800617 (uint64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
Andreas Gampe3a913092015-01-10 00:26:17 -0800618 // TODO: It seems it would be correct to use U8.
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500619 // U4: high word of the 64-bit time.
Andreas Gampe3a913092015-01-10 00:26:17 -0800620 __ AddU4(static_cast<uint32_t>(nowMs >> 32));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500621 // U4: low word of the 64-bit time.
Andreas Gampe3a913092015-01-10 00:26:17 -0800622 __ AddU4(static_cast<uint32_t>(nowMs & 0xFFFFFFFF));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500623 }
624
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700625 void WriteStackTraces() {
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700626 // Write a dummy stack trace record so the analysis tools don't freak out.
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700627 output_->StartNewRecord(HPROF_TAG_STACK_TRACE, kHprofTime);
Andreas Gampe3a913092015-01-10 00:26:17 -0800628 __ AddU4(kHprofNullStackTrace);
629 __ AddU4(kHprofNullThread);
630 __ AddU4(0); // no frames
631 }
632
633 bool DumpToDdmsBuffered(size_t overall_size ATTRIBUTE_UNUSED, size_t max_length ATTRIBUTE_UNUSED)
Hiroshi Yamauchi0c8c3032015-01-16 16:54:35 -0800634 EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
Andreas Gampe3a913092015-01-10 00:26:17 -0800635 LOG(FATAL) << "Unimplemented";
636 UNREACHABLE();
637 // // Send the data off to DDMS.
638 // iovec iov[2];
639 // iov[0].iov_base = header_data_ptr_;
640 // iov[0].iov_len = header_data_size_;
641 // iov[1].iov_base = body_data_ptr_;
642 // iov[1].iov_len = body_data_size_;
643 // Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
644 }
645
646 bool DumpToFile(size_t overall_size, size_t max_length)
Hiroshi Yamauchi0c8c3032015-01-16 16:54:35 -0800647 EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
Andreas Gampe3a913092015-01-10 00:26:17 -0800648 // Where exactly are we writing to?
649 int out_fd;
650 if (fd_ >= 0) {
651 out_fd = dup(fd_);
652 if (out_fd < 0) {
653 ThrowRuntimeException("Couldn't dump heap; dup(%d) failed: %s", fd_, strerror(errno));
654 return false;
655 }
656 } else {
657 out_fd = open(filename_.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644);
658 if (out_fd < 0) {
659 ThrowRuntimeException("Couldn't dump heap; open(\"%s\") failed: %s", filename_.c_str(),
660 strerror(errno));
661 return false;
662 }
663 }
664
665 std::unique_ptr<File> file(new File(out_fd, filename_, true));
666 bool okay;
667 {
668 FileEndianOutput file_output(file.get(), max_length);
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700669 output_ = &file_output;
670 ProcessHeap(true);
Andreas Gampe3a913092015-01-10 00:26:17 -0800671 okay = !file_output.Errors();
672
673 if (okay) {
674 // Check for expected size.
675 CHECK_EQ(file_output.SumLength(), overall_size);
676 }
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700677 output_ = nullptr;
Andreas Gampe3a913092015-01-10 00:26:17 -0800678 }
679
680 if (okay) {
681 okay = file->FlushCloseOrErase() == 0;
682 } else {
683 file->Erase();
684 }
685 if (!okay) {
686 std::string msg(StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s",
687 filename_.c_str(), strerror(errno)));
688 ThrowRuntimeException("%s", msg.c_str());
689 LOG(ERROR) << msg;
690 }
691
692 return okay;
693 }
694
695 bool DumpToDdmsDirect(size_t overall_size, size_t max_length, uint32_t chunk_type)
Hiroshi Yamauchi0c8c3032015-01-16 16:54:35 -0800696 EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) {
Andreas Gampe3a913092015-01-10 00:26:17 -0800697 CHECK(direct_to_ddms_);
698 JDWP::JdwpState* state = Dbg::GetJdwpState();
699 CHECK(state != nullptr);
700 JDWP::JdwpNetStateBase* net_state = state->netState;
701 CHECK(net_state != nullptr);
702
703 // Hold the socket lock for the whole time since we want this to be atomic.
704 MutexLock mu(Thread::Current(), *net_state->GetSocketLock());
705
706 // Prepare the Ddms chunk.
707 constexpr size_t kChunkHeaderSize = kJDWPHeaderLen + 8;
708 uint8_t chunk_header[kChunkHeaderSize] = { 0 };
709 state->SetupChunkHeader(chunk_type, overall_size, kChunkHeaderSize, chunk_header);
710
711 // Prepare the output and send the chunk header.
712 NetStateEndianOutput net_output(net_state, max_length);
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700713 output_ = &net_output;
Andreas Gampe3a913092015-01-10 00:26:17 -0800714 net_output.AddU1List(chunk_header, kChunkHeaderSize);
715
716 // Write the dump.
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700717 ProcessHeap(true);
Andreas Gampe3a913092015-01-10 00:26:17 -0800718
719 // Check for expected size.
720 CHECK_EQ(net_output.SumLength(), overall_size + kChunkHeaderSize);
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700721 output_ = nullptr;
Andreas Gampe3a913092015-01-10 00:26:17 -0800722
723 return true;
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700724 }
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500725
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700726 // If direct_to_ddms_ is set, "filename_" and "fd" will be ignored.
727 // Otherwise, "filename_" must be valid, though if "fd" >= 0 it will
728 // only be used for debug messages.
729 std::string filename_;
730 int fd_;
731 bool direct_to_ddms_;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500732
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700733 uint64_t start_ns_;
734
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700735 EndianOutput* output_;
736
Brian Carlstrom7934ac22013-07-26 10:54:15 -0700737 HprofHeapId current_heap_; // Which heap we're currently dumping.
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700738 size_t objects_in_segment_;
739
Ian Rogersef7d42f2014-01-06 12:55:46 -0800740 std::set<mirror::Class*> classes_;
741 HprofStringId next_string_id_;
742 SafeMap<std::string, HprofStringId> strings_;
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700743
744 DISALLOW_COPY_AND_ASSIGN(Hprof);
745};
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500746
Andreas Gampe3a913092015-01-10 00:26:17 -0800747static HprofBasicType SignatureToBasicTypeAndSize(const char* sig, size_t* size_out) {
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500748 char c = sig[0];
749 HprofBasicType ret;
750 size_t size;
751
752 switch (c) {
Andreas Gampe3a913092015-01-10 00:26:17 -0800753 case '[':
754 case 'L':
755 ret = hprof_basic_object;
756 size = 4;
757 break;
758 case 'Z':
759 ret = hprof_basic_boolean;
760 size = 1;
761 break;
762 case 'C':
763 ret = hprof_basic_char;
764 size = 2;
765 break;
766 case 'F':
767 ret = hprof_basic_float;
768 size = 4;
769 break;
770 case 'D':
771 ret = hprof_basic_double;
772 size = 8;
773 break;
774 case 'B':
775 ret = hprof_basic_byte;
776 size = 1;
777 break;
778 case 'S':
779 ret = hprof_basic_short;
780 size = 2;
781 break;
782 case 'I':
783 ret = hprof_basic_int;
784 size = 4;
785 break;
786 case 'J':
787 ret = hprof_basic_long;
788 size = 8;
789 break;
790 default:
791 LOG(FATAL) << "UNREACHABLE";
792 UNREACHABLE();
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500793 }
794
Andreas Gampe3a913092015-01-10 00:26:17 -0800795 if (size_out != nullptr) {
796 *size_out = size;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500797 }
798
799 return ret;
800}
801
802// Always called when marking objects, but only does
803// something when ctx->gc_scan_state_ is non-zero, which is usually
804// only true when marking the root set or unreachable
805// objects. Used to add rootset references to obj.
Andreas Gampe3a913092015-01-10 00:26:17 -0800806void Hprof::MarkRootObject(const mirror::Object* obj, jobject jni_obj, HprofHeapTag heap_tag,
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700807 uint32_t thread_serial) {
Andreas Gampe3a913092015-01-10 00:26:17 -0800808 if (heap_tag == 0) {
809 return;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500810 }
811
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700812 CheckHeapSegmentConstraints();
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500813
Andreas Gampe3a913092015-01-10 00:26:17 -0800814 switch (heap_tag) {
815 // ID: object ID
816 case HPROF_ROOT_UNKNOWN:
817 case HPROF_ROOT_STICKY_CLASS:
818 case HPROF_ROOT_MONITOR_USED:
819 case HPROF_ROOT_INTERNED_STRING:
820 case HPROF_ROOT_DEBUGGER:
821 case HPROF_ROOT_VM_INTERNAL:
822 __ AddU1(heap_tag);
823 __ AddObjectId(obj);
824 break;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500825
Andreas Gampe3a913092015-01-10 00:26:17 -0800826 // ID: object ID
827 // ID: JNI global ref ID
828 case HPROF_ROOT_JNI_GLOBAL:
829 __ AddU1(heap_tag);
830 __ AddObjectId(obj);
831 __ AddJniGlobalRefId(jni_obj);
832 break;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500833
Andreas Gampe3a913092015-01-10 00:26:17 -0800834 // ID: object ID
835 // U4: thread serial number
836 // U4: frame number in stack trace (-1 for empty)
837 case HPROF_ROOT_JNI_LOCAL:
838 case HPROF_ROOT_JNI_MONITOR:
839 case HPROF_ROOT_JAVA_FRAME:
840 __ AddU1(heap_tag);
841 __ AddObjectId(obj);
842 __ AddU4(thread_serial);
843 __ AddU4((uint32_t)-1);
844 break;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500845
Andreas Gampe3a913092015-01-10 00:26:17 -0800846 // ID: object ID
847 // U4: thread serial number
848 case HPROF_ROOT_NATIVE_STACK:
849 case HPROF_ROOT_THREAD_BLOCK:
850 __ AddU1(heap_tag);
851 __ AddObjectId(obj);
852 __ AddU4(thread_serial);
853 break;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500854
Andreas Gampe3a913092015-01-10 00:26:17 -0800855 // ID: thread object ID
856 // U4: thread serial number
857 // U4: stack trace serial number
858 case HPROF_ROOT_THREAD_OBJECT:
859 __ AddU1(heap_tag);
860 __ AddObjectId(obj);
861 __ AddU4(thread_serial);
862 __ AddU4((uint32_t)-1); // xxx
863 break;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500864
Andreas Gampe3a913092015-01-10 00:26:17 -0800865 case HPROF_CLASS_DUMP:
866 case HPROF_INSTANCE_DUMP:
867 case HPROF_OBJECT_ARRAY_DUMP:
868 case HPROF_PRIMITIVE_ARRAY_DUMP:
869 case HPROF_HEAP_DUMP_INFO:
870 case HPROF_PRIMITIVE_ARRAY_NODATA_DUMP:
871 // Ignored.
872 break;
Elliott Hughes73e66f72012-05-09 09:34:45 -0700873
Andreas Gampe3a913092015-01-10 00:26:17 -0800874 case HPROF_ROOT_FINALIZING:
875 case HPROF_ROOT_REFERENCE_CLEANUP:
876 case HPROF_UNREACHABLE:
877 LOG(FATAL) << "obsolete tag " << static_cast<int>(heap_tag);
878 break;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500879 }
880
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700881 ++objects_in_segment_;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500882}
883
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800884static int StackTraceSerialNumber(const mirror::Object* /*obj*/) {
Andreas Gampe3a913092015-01-10 00:26:17 -0800885 return kHprofNullStackTrace;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500886}
887
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700888void Hprof::DumpHeapObject(mirror::Object* obj) {
Andreas Gampe3a913092015-01-10 00:26:17 -0800889 // Ignore classes that are retired.
890 if (obj->IsClass() && obj->AsClass()->IsRetired()) {
891 return;
892 }
893
Mathieu Chartierae1ad002014-07-18 17:58:22 -0700894 gc::space::ContinuousSpace* space =
895 Runtime::Current()->GetHeap()->FindContinuousSpaceFromObject(obj, true);
896 HprofHeapId heap_type = HPROF_HEAP_APP;
897 if (space != nullptr) {
898 if (space->IsZygoteSpace()) {
899 heap_type = HPROF_HEAP_ZYGOTE;
900 } else if (space->IsImageSpace()) {
901 heap_type = HPROF_HEAP_IMAGE;
902 }
903 }
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700904 CheckHeapSegmentConstraints();
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500905
Mathieu Chartierae1ad002014-07-18 17:58:22 -0700906 if (heap_type != current_heap_) {
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500907 HprofStringId nameId;
908
909 // This object is in a different heap than the current one.
910 // Emit a HEAP_DUMP_INFO tag to change heaps.
Andreas Gampe3a913092015-01-10 00:26:17 -0800911 __ AddU1(HPROF_HEAP_DUMP_INFO);
912 __ AddU4(static_cast<uint32_t>(heap_type)); // uint32_t: heap type
Mathieu Chartierae1ad002014-07-18 17:58:22 -0700913 switch (heap_type) {
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500914 case HPROF_HEAP_APP:
915 nameId = LookupStringId("app");
916 break;
917 case HPROF_HEAP_ZYGOTE:
918 nameId = LookupStringId("zygote");
919 break;
Mathieu Chartierae1ad002014-07-18 17:58:22 -0700920 case HPROF_HEAP_IMAGE:
921 nameId = LookupStringId("image");
922 break;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500923 default:
924 // Internal error
925 LOG(ERROR) << "Unexpected desiredHeap";
926 nameId = LookupStringId("<ILLEGAL>");
927 break;
928 }
Andreas Gampe3a913092015-01-10 00:26:17 -0800929 __ AddStringId(nameId);
Mathieu Chartierae1ad002014-07-18 17:58:22 -0700930 current_heap_ = heap_type;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500931 }
932
Ian Rogers2dd0e2c2013-01-24 12:42:14 -0800933 mirror::Class* c = obj->GetClass();
Andreas Gampe3a913092015-01-10 00:26:17 -0800934 if (c == nullptr) {
Mathieu Chartier2cebb242015-04-21 16:50:40 -0700935 // This object will bother HprofReader, because it has a null
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500936 // class, so just don't dump it. It could be
937 // gDvm.unlinkedJavaLangClass or it could be an object just
938 // allocated which hasn't been initialized yet.
939 } else {
940 if (obj->IsClass()) {
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700941 DumpHeapClass(obj->AsClass());
Elliott Hughese84278b2012-03-22 10:06:53 -0700942 } else if (c->IsArrayClass()) {
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700943 DumpHeapArray(obj->AsArray(), c);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500944 } else {
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700945 DumpHeapInstanceObject(obj, c);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500946 }
947 }
948
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -0700949 ++objects_in_segment_;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500950}
951
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -0700952void Hprof::DumpHeapClass(mirror::Class* klass) {
Sebastien Hertzca068b22015-04-07 10:28:53 +0200953 if (!klass->IsLoaded() && !klass->IsErroneous()) {
954 // Class is allocated but not yet loaded: we cannot access its fields or super class.
955 return;
956 }
Andreas Gampe3a913092015-01-10 00:26:17 -0800957 size_t sFieldCount = klass->NumStaticFields();
958 if (sFieldCount != 0) {
959 int byteLength = sFieldCount * sizeof(JValue); // TODO bogus; fields are packed
960 // Create a byte array to reflect the allocation of the
961 // StaticField array at the end of this class.
962 __ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
963 __ AddClassStaticsId(klass);
964 __ AddU4(StackTraceSerialNumber(klass));
965 __ AddU4(byteLength);
966 __ AddU1(hprof_basic_byte);
967 for (int i = 0; i < byteLength; ++i) {
968 __ AddU1(0);
969 }
970 }
971
972 __ AddU1(HPROF_CLASS_DUMP);
973 __ AddClassId(LookupClassId(klass));
974 __ AddU4(StackTraceSerialNumber(klass));
975 __ AddClassId(LookupClassId(klass->GetSuperClass()));
976 __ AddObjectId(klass->GetClassLoader());
977 __ AddObjectId(nullptr); // no signer
978 __ AddObjectId(nullptr); // no prot domain
979 __ AddObjectId(nullptr); // reserved
980 __ AddObjectId(nullptr); // reserved
981 if (klass->IsClassClass()) {
982 // ClassObjects have their static fields appended, so aren't all the same size.
983 // But they're at least this size.
984 __ AddU4(sizeof(mirror::Class)); // instance size
Jeff Hao0ce43532015-05-12 18:58:32 -0700985 } else if (klass->IsStringClass()) {
986 // Strings are variable length with character data at the end like arrays.
987 // This outputs the size of an empty string.
988 __ AddU4(sizeof(mirror::String));
989 } else if (klass->IsArrayClass() || klass->IsPrimitive()) {
Andreas Gampe3a913092015-01-10 00:26:17 -0800990 __ AddU4(0);
991 } else {
992 __ AddU4(klass->GetObjectSize()); // instance size
993 }
994
995 __ AddU2(0); // empty const pool
996
997 // Static fields
998 if (sFieldCount == 0) {
999 __ AddU2((uint16_t)0);
1000 } else {
1001 __ AddU2((uint16_t)(sFieldCount+1));
1002 __ AddStringId(LookupStringId(kStaticOverheadName));
1003 __ AddU1(hprof_basic_object);
1004 __ AddClassStaticsId(klass);
1005
1006 for (size_t i = 0; i < sFieldCount; ++i) {
Mathieu Chartierc7853442015-03-27 14:35:38 -07001007 ArtField* f = klass->GetStaticField(i);
Andreas Gampe3a913092015-01-10 00:26:17 -08001008
1009 size_t size;
1010 HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
1011 __ AddStringId(LookupStringId(f->GetName()));
1012 __ AddU1(t);
Mathieu Chartier15f345c2015-03-06 12:45:44 -08001013 switch (t) {
1014 case hprof_basic_byte:
Mathieu Chartierff38c042015-03-06 11:33:36 -08001015 __ AddU1(f->GetByte(klass));
Andreas Gampe3a913092015-01-10 00:26:17 -08001016 break;
Mathieu Chartier15f345c2015-03-06 12:45:44 -08001017 case hprof_basic_boolean:
1018 __ AddU1(f->GetBoolean(klass));
1019 break;
1020 case hprof_basic_char:
Mathieu Chartierff38c042015-03-06 11:33:36 -08001021 __ AddU2(f->GetChar(klass));
Andreas Gampe3a913092015-01-10 00:26:17 -08001022 break;
Mathieu Chartier15f345c2015-03-06 12:45:44 -08001023 case hprof_basic_short:
1024 __ AddU2(f->GetShort(klass));
1025 break;
1026 case hprof_basic_float:
1027 case hprof_basic_int:
1028 case hprof_basic_object:
Andreas Gampe3a913092015-01-10 00:26:17 -08001029 __ AddU4(f->Get32(klass));
1030 break;
Mathieu Chartier15f345c2015-03-06 12:45:44 -08001031 case hprof_basic_double:
1032 case hprof_basic_long:
Andreas Gampe3a913092015-01-10 00:26:17 -08001033 __ AddU8(f->Get64(klass));
1034 break;
1035 default:
1036 LOG(FATAL) << "Unexpected size " << size;
1037 UNREACHABLE();
1038 }
1039 }
1040 }
1041
1042 // Instance fields for this class (no superclass fields)
Mathieu Chartier7c1f53e2015-06-03 10:51:13 -07001043 int iFieldCount = klass->NumInstanceFields();
Jeff Hao848f70a2014-01-15 13:49:50 -08001044 if (klass->IsStringClass()) {
1045 __ AddU2((uint16_t)iFieldCount + 1);
1046 } else {
1047 __ AddU2((uint16_t)iFieldCount);
1048 }
Andreas Gampe3a913092015-01-10 00:26:17 -08001049 for (int i = 0; i < iFieldCount; ++i) {
Mathieu Chartierc7853442015-03-27 14:35:38 -07001050 ArtField* f = klass->GetInstanceField(i);
Andreas Gampe3a913092015-01-10 00:26:17 -08001051 __ AddStringId(LookupStringId(f->GetName()));
1052 HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), nullptr);
1053 __ AddU1(t);
1054 }
Jeff Hao848f70a2014-01-15 13:49:50 -08001055 // Add native value character array for strings.
1056 if (klass->IsStringClass()) {
1057 __ AddStringId(LookupStringId("value"));
1058 __ AddU1(hprof_basic_object);
1059 }
Andreas Gampe3a913092015-01-10 00:26:17 -08001060}
1061
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -07001062void Hprof::DumpHeapArray(mirror::Array* obj, mirror::Class* klass) {
Andreas Gampe3a913092015-01-10 00:26:17 -08001063 uint32_t length = obj->GetLength();
1064
1065 if (obj->IsObjectArray()) {
1066 // obj is an object array.
1067 __ AddU1(HPROF_OBJECT_ARRAY_DUMP);
1068
1069 __ AddObjectId(obj);
1070 __ AddU4(StackTraceSerialNumber(obj));
1071 __ AddU4(length);
1072 __ AddClassId(LookupClassId(klass));
1073
Mathieu Chartier2cebb242015-04-21 16:50:40 -07001074 // Dump the elements, which are always objects or null.
Andreas Gampe3a913092015-01-10 00:26:17 -08001075 __ AddIdList(obj->AsObjectArray<mirror::Object>());
1076 } else {
1077 size_t size;
1078 HprofBasicType t = SignatureToBasicTypeAndSize(
1079 Primitive::Descriptor(klass->GetComponentType()->GetPrimitiveType()), &size);
1080
1081 // obj is a primitive array.
1082 __ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
1083
1084 __ AddObjectId(obj);
1085 __ AddU4(StackTraceSerialNumber(obj));
1086 __ AddU4(length);
1087 __ AddU1(t);
1088
1089 // Dump the raw, packed element values.
1090 if (size == 1) {
1091 __ AddU1List(reinterpret_cast<const uint8_t*>(obj->GetRawData(sizeof(uint8_t), 0)), length);
1092 } else if (size == 2) {
1093 __ AddU2List(reinterpret_cast<const uint16_t*>(obj->GetRawData(sizeof(uint16_t), 0)), length);
1094 } else if (size == 4) {
1095 __ AddU4List(reinterpret_cast<const uint32_t*>(obj->GetRawData(sizeof(uint32_t), 0)), length);
1096 } else if (size == 8) {
1097 __ AddU8List(reinterpret_cast<const uint64_t*>(obj->GetRawData(sizeof(uint64_t), 0)), length);
1098 }
1099 }
1100}
1101
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -07001102void Hprof::DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass) {
Andreas Gampe3a913092015-01-10 00:26:17 -08001103 // obj is an instance object.
1104 __ AddU1(HPROF_INSTANCE_DUMP);
1105 __ AddObjectId(obj);
1106 __ AddU4(StackTraceSerialNumber(obj));
1107 __ AddClassId(LookupClassId(klass));
1108
1109 // Reserve some space for the length of the instance data, which we won't
1110 // know until we're done writing it.
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -07001111 size_t size_patch_offset = output_->Length();
Andreas Gampe3a913092015-01-10 00:26:17 -08001112 __ AddU4(0x77777777);
1113
1114 // Write the instance data; fields for this class, followed by super class fields,
1115 // and so on. Don't write the klass or monitor fields of Object.class.
Jeff Hao848f70a2014-01-15 13:49:50 -08001116 mirror::Class* orig_klass = klass;
Mathieu Chartier7c1f53e2015-06-03 10:51:13 -07001117 do {
Andreas Gampe3a913092015-01-10 00:26:17 -08001118 int ifieldCount = klass->NumInstanceFields();
1119 for (int i = 0; i < ifieldCount; ++i) {
Mathieu Chartierc7853442015-03-27 14:35:38 -07001120 ArtField* f = klass->GetInstanceField(i);
Andreas Gampe3a913092015-01-10 00:26:17 -08001121 size_t size;
Mathieu Chartier15f345c2015-03-06 12:45:44 -08001122 auto t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
1123 switch (t) {
1124 case hprof_basic_byte:
Mathieu Chartierff38c042015-03-06 11:33:36 -08001125 __ AddU1(f->GetByte(obj));
Mathieu Chartier15f345c2015-03-06 12:45:44 -08001126 break;
1127 case hprof_basic_boolean:
1128 __ AddU1(f->GetBoolean(obj));
1129 break;
1130 case hprof_basic_char:
Mathieu Chartierff38c042015-03-06 11:33:36 -08001131 __ AddU2(f->GetChar(obj));
Mathieu Chartier15f345c2015-03-06 12:45:44 -08001132 break;
1133 case hprof_basic_short:
1134 __ AddU2(f->GetShort(obj));
1135 break;
1136 case hprof_basic_float:
1137 case hprof_basic_int:
1138 case hprof_basic_object:
Andreas Gampe3a913092015-01-10 00:26:17 -08001139 __ AddU4(f->Get32(obj));
Mathieu Chartier15f345c2015-03-06 12:45:44 -08001140 break;
1141 case hprof_basic_double:
1142 case hprof_basic_long:
Andreas Gampe3a913092015-01-10 00:26:17 -08001143 __ AddU8(f->Get64(obj));
Mathieu Chartier15f345c2015-03-06 12:45:44 -08001144 break;
Andreas Gampe3a913092015-01-10 00:26:17 -08001145 }
1146 }
1147
1148 klass = klass->GetSuperClass();
Mathieu Chartier7c1f53e2015-06-03 10:51:13 -07001149 } while (klass != nullptr);
Andreas Gampe3a913092015-01-10 00:26:17 -08001150
Jeff Hao848f70a2014-01-15 13:49:50 -08001151 // Output native value character array for strings.
1152 if (orig_klass->IsStringClass()) {
1153 mirror::String* s = obj->AsString();
Jeff Hao90bcbd52015-05-14 17:19:15 -07001154 mirror::Object* value;
1155 if (s->GetLength() == 0) {
1156 // If string is empty, use an object-aligned address within the string for the value.
1157 value = reinterpret_cast<mirror::Object*>(reinterpret_cast<uintptr_t>(s) + kObjectAlignment);
1158 } else {
1159 value = reinterpret_cast<mirror::Object*>(s->GetValue());
1160 }
1161 __ AddObjectId(value);
Jeff Hao848f70a2014-01-15 13:49:50 -08001162
1163 // Patch the instance field length.
1164 __ UpdateU4(size_patch_offset, output_->Length() - (size_patch_offset + 4));
1165
1166 __ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
Jeff Hao90bcbd52015-05-14 17:19:15 -07001167 __ AddObjectId(value);
Jeff Hao848f70a2014-01-15 13:49:50 -08001168 __ AddU4(StackTraceSerialNumber(obj));
1169 __ AddU4(s->GetLength());
1170 __ AddU1(hprof_basic_char);
1171 __ AddU2List(s->GetValue(), s->GetLength());
1172 } else {
1173 // Patch the instance field length.
1174 __ UpdateU4(size_patch_offset, output_->Length() - (size_patch_offset + 4));
1175 }
Andreas Gampe3a913092015-01-10 00:26:17 -08001176}
1177
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -07001178void Hprof::VisitRoot(mirror::Object* obj, const RootInfo& info) {
Jesse Wilson0b075f12011-11-09 10:57:41 -05001179 static const HprofHeapTag xlate[] = {
1180 HPROF_ROOT_UNKNOWN,
1181 HPROF_ROOT_JNI_GLOBAL,
1182 HPROF_ROOT_JNI_LOCAL,
1183 HPROF_ROOT_JAVA_FRAME,
1184 HPROF_ROOT_NATIVE_STACK,
1185 HPROF_ROOT_STICKY_CLASS,
1186 HPROF_ROOT_THREAD_BLOCK,
1187 HPROF_ROOT_MONITOR_USED,
1188 HPROF_ROOT_THREAD_OBJECT,
1189 HPROF_ROOT_INTERNED_STRING,
1190 HPROF_ROOT_FINALIZING,
1191 HPROF_ROOT_DEBUGGER,
1192 HPROF_ROOT_REFERENCE_CLEANUP,
1193 HPROF_ROOT_VM_INTERNAL,
1194 HPROF_ROOT_JNI_MONITOR,
1195 };
Mathieu Chartiere34fa1d2015-01-14 14:55:47 -08001196 CHECK_LT(info.GetType(), sizeof(xlate) / sizeof(HprofHeapTag));
Andreas Gampe3a913092015-01-10 00:26:17 -08001197 if (obj == nullptr) {
Jesse Wilson0b075f12011-11-09 10:57:41 -05001198 return;
1199 }
Mathieu Chartierbb87e0f2015-04-03 11:21:55 -07001200 MarkRootObject(obj, 0, xlate[info.GetType()], info.GetThreadId());
Jesse Wilsonc4824e62011-11-01 14:39:04 -04001201}
1202
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -07001203// If "direct_to_ddms" is true, the other arguments are ignored, and data is
1204// sent directly to DDMS.
1205// If "fd" is >= 0, the output will be written to that file descriptor.
1206// Otherwise, "filename" is used to create an output file.
1207void DumpHeap(const char* filename, int fd, bool direct_to_ddms) {
Andreas Gampe3a913092015-01-10 00:26:17 -08001208 CHECK(filename != nullptr);
Jesse Wilsonc4824e62011-11-01 14:39:04 -04001209
Hiroshi Yamauchi2cd334a2015-01-09 14:03:35 -08001210 Thread* self = Thread::Current();
1211 gc::Heap* heap = Runtime::Current()->GetHeap();
1212 if (heap->IsGcConcurrentAndMoving()) {
1213 // Need to take a heap dump while GC isn't running. See the
1214 // comment in Heap::VisitObjects().
1215 heap->IncrementDisableMovingGC(self);
1216 }
Mathieu Chartierbf44d422015-06-02 11:42:18 -07001217 Runtime::Current()->GetThreadList()->SuspendAll(__FUNCTION__, true /* long suspend */);
Elliott Hughesdcfdd2b2012-07-09 18:27:46 -07001218 Hprof hprof(filename, fd, direct_to_ddms);
1219 hprof.Dump();
Ian Rogers00f7d0e2012-07-19 15:28:27 -07001220 Runtime::Current()->GetThreadList()->ResumeAll();
Hiroshi Yamauchi2cd334a2015-01-09 14:03:35 -08001221 if (heap->IsGcConcurrentAndMoving()) {
1222 heap->DecrementDisableMovingGC(self);
1223 }
Jesse Wilsonc4824e62011-11-01 14:39:04 -04001224}
1225
Mathieu Chartierad466ad2015-01-08 16:28:08 -08001226} // namespace hprof
Jesse Wilsonc4824e62011-11-01 14:39:04 -04001227} // namespace art