this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "heap-profiler.h"
3
4#include <cerrno>
5
6#include "file.h"
7#include "handles.h"
8#include "os.h"
9#include "runtime.h"
10#include "thread.h"
11
12namespace py {
13
14const char HeapProfiler::kBytearrayClassName[] = "byte[]";
15const char HeapProfiler::kDoubleArrayClassName[] = "double[]";
16const char HeapProfiler::kInvalid[] = "<INVALID>";
17const char HeapProfiler::kOverflow[] = "<OVERFLOW>";
18const char HeapProfiler::kJavaLangClass[] = "java.lang.Class";
19const char HeapProfiler::kJavaLangClassLoader[] = "java.lang.ClassLoader";
20const char HeapProfiler::kJavaLangObject[] = "java.lang.Object";
21const char HeapProfiler::kJavaLangString[] = "java.lang.String";
22const char HeapProfiler::kLongArrayClassName[] = "long[]";
23const char HeapProfiler::kObjectArrayClassName[] = "java.lang.Object[]";
24
25HeapProfiler::HeapProfiler(Thread* thread, HeapProfilerWriteCallback callback,
26 void* stream)
27 : thread_(thread), output_stream_(stream), write_callback_(callback) {}
28
29void HeapProfiler::write(const void* data, word size) {
30 (*write_callback_)(data, size, output_stream_);
31}
32
33// LOAD CLASS - 0x02
34//
35// Format:
36// u4 - class serial number (always > 0)
37// ID - class object ID
38// u4 - stack trace serial number
39// ID - class name string ID
40void HeapProfiler::writeFakeLoadClass(FakeClass fake_class,
41 const char* class_name) {
42 Record record(kLoadClass, this);
43 // class serial number (always > 0)
44 record.write32(1);
45 // class object ID
46 record.writeObjectId(static_cast<uword>(fake_class));
47 // stack trace serial number
48 record.write32(0);
49 // TODO(T61807224): Dump type names discriminated by layout ID
50 // class name string ID
51 record.writeObjectId(cStringId(class_name));
52}
53
54// CLASS DUMP - 0x20
55//
56// Format:
57// u4 - class serial number (always > 0)
58// ID - class object ID
59// u4 - stack trace serial number
60// ID - class name string ID
61void HeapProfiler::writeFakeClassDump(FakeClass fake_class,
62 const char* class_name,
63 FakeClass fake_super_class) {
64 writeFakeLoadClass(fake_class, class_name);
65 CHECK(!class_dump_table_.add(static_cast<uword>(fake_class)),
66 "cannot dump object twice");
67 SubRecord sub(Subtag::kClassDump, current_record_);
68 // class object ID
69 sub.writeObjectId(static_cast<uword>(fake_class));
70 // stack trace serial number
71 sub.write32(0);
72 // super class object ID
73 sub.writeObjectId(static_cast<uword>(fake_super_class));
74 // class loader object ID
75 sub.writeObjectId(0);
76 // signers object ID
77 sub.writeObjectId(0);
78 // protection domain object ID
79 sub.writeObjectId(0);
80 // reserved
81 sub.writeObjectId(0);
82 // reserved
83 sub.writeObjectId(0);
84 // instance size (in bytes)
85 sub.write32(0);
86 // size of constant pool and number of records that follow
87 sub.write16(0);
88 // Number of static fields
89 sub.write16(0);
90 // Number of instance fields (not include super class's)
91 sub.write16(0);
92}
93
94// STACK TRACE - 0x05
95//
96// u4 - stack trace serial number
97// u4 - thread serial number
98// u4 - number of frames
99// [ID]* - series of stack frame ID's
100void HeapProfiler::writeFakeStackTrace() {
101 Record record(kStackTrace, this);
102 // stack trace serial number
103 record.write32(0);
104 // thread serial number
105 // TODO(T70833159): Support multiple threads in heap dumper.
106 record.write32(0);
107 // number of frames
108 record.write32(0);
109}
110
111void HeapProfiler::writeHeader() {
112 const char magic[] = "JAVA PROFILE 1.0.2";
113 write(magic, sizeof(magic));
114 write32(kPointerSize);
115 double seconds_double = OS::currentTime();
116 uint64_t seconds = static_cast<uint64_t>(seconds_double);
117 seconds += (seconds_double - seconds);
118 uint64_t milliseconds = seconds * kMillisecondsPerSecond;
119 uint32_t hi =
120 static_cast<uint32_t>((milliseconds >> 32) & 0x00000000FFFFFFFF);
121 write32(hi);
122 uint32_t lo = static_cast<uint32_t>(milliseconds & 0x00000000FFFFFFFF);
123 write32(lo);
124}
125
126// LOAD CLASS - 0x02
127//
128// Format:
129// u4 - class serial number (always > 0)
130// ID - class object ID
131// u4 - stack trace serial number
132// ID - class name string ID
133void HeapProfiler::writeLoadClass(RawLayout layout) {
134 CHECK(!load_class_table_.add(layout.raw()), "cannot dump object twice");
135 Record record(kLoadClass, this);
136 // class serial number (always > 0)
137 record.write32(1);
138 // class object ID
139 record.writeObjectId(objectId(layout));
140 // stack trace serial number
141 record.write32(0);
142 // class name string ID
143 HandleScope scope(thread_);
144 Type type(&scope, thread_->runtime()->concreteTypeAt(layout.id()));
145 Str name(&scope, type.name());
146 record.writeObjectId(stringId(*name));
147}
148
149// CLASS DUMP - 0x20
150//
151// Format:
152// ID - class object ID
153// u4 - stack trace serial number
154// ID - super class object ID
155// ID - class loader object ID
156// ID - signers object ID
157// ID - protection domain object ID
158// ID - reserved
159// ID - reserved
160// u4 - instance size (in bytes)
161// u2 - size of constant pool and number of records that follow
162// u2 - constant pool index
163// u1 - type of entry: (See Basic Type)
164// value - value of entry (u1, u2, u4, or u8 based on type of entry)
165// u2 - Number of static fields:
166// ID - static field name string ID
167// u1 - type of field: (See Basic Type)
168// value - value of entry (u1, u2, u4, or u8 based on type of field)
169// u2 - Number of instance fields (not including super class's)
170// ID - field name string ID
171// u1 - type of field: (See Basic Type)
172void HeapProfiler::writeClassDump(RawLayout layout) {
173 CHECK(!class_dump_table_.add(layout.raw()), "cannot dump object twice");
174 SubRecord sub(kClassDump, current_record_);
175 // class object ID
176 sub.writeObjectId(classId(layout));
177 // stack trace serial number
178 sub.write32(0);
179 // super class object ID
180 if (layout.id() == LayoutId::kObject) {
181 // Superclass == 0 => object is java.lang.Object
182 sub.writeObjectId(0);
183 } else {
184 // Since there is not much of a concept of inheritance in the Layout
185 // system, pretend all Layouts' super is "object". This allows much easier
186 // dumping of attributes.
187 // TODO(emacs): Figure out how to dump class hierarchies
188 RawLayout super_layout =
189 Layout::cast(thread_->runtime()->layoutAt(LayoutId::kObject));
190 sub.writeObjectId(classId(super_layout));
191 }
192 // class loader object ID
193 sub.writeObjectId(0);
194 // signers object ID
195 sub.writeObjectId(0);
196 // protection domain object ID
197 sub.writeObjectId(0);
198 // reserved
199 sub.writeObjectId(0);
200 // reserved
201 sub.writeObjectId(0);
202
203 // instance size (in bytes)
204 sub.write32(layout.instanceSize());
205 // size of constant pool and number of records that follow
206 // Constant pool is variable-length and empty here
207 sub.write16(0);
208 // number of static fields
209 // Static fields are variable-length and empty here
210 sub.write16(0);
211 Runtime* runtime = thread_->runtime();
212 if (layout.id() == LayoutId::kComplex) {
213 // Two instance fields: "real", "imag"
214 sub.write16(2);
215 sub.writeObjectId(stringId(Str::cast(runtime->symbols()->at(ID(real)))));
216 sub.write8(BasicType::kDouble);
217 sub.writeObjectId(stringId(Str::cast(runtime->symbols()->at(ID(imag)))));
218 sub.write8(BasicType::kDouble);
219 return;
220 }
221 if (layout.id() == LayoutId::kFloat) {
222 // One instance field: "value"
223 sub.write16(1);
224 sub.writeObjectId(stringId(Str::cast(runtime->symbols()->at(ID(value)))));
225 sub.write8(BasicType::kDouble);
226 return;
227 }
228 // number of instance fields (not include super class's)
229 RawTuple in_object = Tuple::cast(layout.inObjectAttributes());
230 word num_in_object = in_object.length();
231 bool has_tuple_overflow = layout.hasTupleOverflow();
232 word num_overflow = has_tuple_overflow ? 1 : 0;
233 word num_attributes = num_in_object + num_overflow;
234 sub.write16(num_attributes);
235 // instance fields
236 for (word i = 0; i < num_in_object; i++) {
237 // allocated on the layout for an attribute
238 RawObject name = Tuple::cast(in_object.at(i)).at(0);
239 if (name == SmallInt::fromWord(0)) {
240 sub.writeObjectId(cStringId(kInvalid));
241 } else {
242 sub.writeObjectId(stringId(Str::cast(name)));
243 }
244 sub.write8(BasicType::kObject);
245 }
246 // TODO(emacs): Remove this special case once tuple overflow fits neatly into
247 // the allocated in-object attributes
248 if (has_tuple_overflow) {
249 sub.writeObjectId(cStringId(kOverflow));
250 sub.write8(BasicType::kObject);
251 }
252}
253
254void HeapProfiler::writeInstanceDump(RawInstance obj) {
255 CHECK(!heap_object_table_.add(obj.raw()), "cannot dump object twice");
256 SubRecord sub(kInstanceDump, current_record_);
257 RawLayout layout = Layout::cast(Thread::current()->runtime()->layoutOf(obj));
258 RawTuple in_object = Tuple::cast(layout.inObjectAttributes());
259 word num_in_object = in_object.length();
260 bool has_tuple_overflow = layout.hasTupleOverflow();
261 word num_overflow = has_tuple_overflow ? 1 : 0;
262 word num_attributes = num_in_object + num_overflow;
263 sub.beginInstanceDump(obj, /*stack_trace=*/0, num_attributes * kPointerSize,
264 classId(layout));
265 // write in-object attributes
266 for (word i = 0; i < num_in_object; i++) {
267 RawTuple elt = Tuple::cast(in_object.at(i));
268 AttributeInfo info(elt.at(1));
269 sub.writeObjectId(
270 objectId(Instance::cast(obj).instanceVariableAt(info.offset())));
271 }
272 // write tuple overflow (dict overflow is in-object)
273 if (has_tuple_overflow) {
274 sub.writeObjectId(objectId(
275 Instance::cast(obj).instanceVariableAt(layout.overflowOffset())));
276 }
277}
278
279void HeapProfiler::writeImmediate(RawObject obj) {
280 DCHECK(!obj.isHeapObject(), "obj must be an immediate");
281 SubRecord sub(kInstanceDump, current_record_);
282 RawLayout layout = Layout::cast(Thread::current()->runtime()->layoutOf(obj));
283 sub.beginInstanceDump(obj, /*stack_trace=*/0, /*num_bytes=*/0,
284 classId(layout));
285}
286
287class ImmediateVisitor : public WordVisitor {
288 public:
289 ImmediateVisitor(HeapProfiler* profiler) : profiler_(profiler) {}
290 void visit(uword element) { profiler_->writeImmediate(RawObject{element}); }
291
292 private:
293 HeapProfiler* profiler_;
294};
295
296void HeapProfiler::writeImmediates() {
297 ImmediateVisitor visitor(this);
298 immediate_table_.visitElements(&visitor);
299}
300
301// OBJECT ARRAY DUMP - 0x22
302//
303// Format:
304// ID - array object ID
305// u4 - stack trace serial number
306// u4 - number of elements
307// ID - array class object id
308// [ID]* - elements
309void HeapProfiler::writeObjectArray(RawTuple tuple) {
310 SubRecord sub(kObjectArrayDump, current_record_);
311 // array object id
312 sub.writeObjectId(objectId(tuple));
313 // stack trace serial number
314 sub.write32(0);
315 // number of elements
316 word length = tuple.length();
317 CHECK(length < kMaxUint32, "length %ld too big for Java length field",
318 length);
319 sub.write32(static_cast<uint32_t>(length));
320 // array class object id
321 sub.writeObjectId(static_cast<uword>(FakeClass::kObjectArray));
322 // elements
323 for (word i = 0; i < length; i++) {
324 sub.writeObjectId(objectId(tuple.at(i)));
325 }
326}
327
328void HeapProfiler::writeBytes(RawBytes bytes) {
329 CHECK(!heap_object_table_.add(bytes.raw()), "cannot dump object twice");
330 SubRecord sub(kPrimitiveArrayDump, current_record_);
331 sub.beginPrimitiveArrayDump(objectId(bytes), /*stack_trace=*/0,
332 bytes.length(), BasicType::kByte);
333 for (word i = 0; i < bytes.length(); i++) {
334 sub.write8(bytes.byteAt(i));
335 }
336}
337
338void HeapProfiler::writeLargeStr(RawLargeStr str) {
339 CHECK(!heap_object_table_.add(str.raw()), "cannot dump object twice");
340 SubRecord sub(kPrimitiveArrayDump, current_record_);
341 word length = str.length();
342 sub.beginPrimitiveArrayDump(objectId(str), /*stack_trace=*/0, length,
343 BasicType::kByte);
344 for (word i = 0; i < length; i++) {
345 sub.write8(str.byteAt(i));
346 }
347}
348
349void HeapProfiler::writeComplex(RawComplex obj) {
350 CHECK(!heap_object_table_.add(obj.raw()), "cannot dump object twice");
351 SubRecord sub(kInstanceDump, current_record_);
352 RawLayout layout = Layout::cast(Thread::current()->runtime()->layoutOf(obj));
353 sub.beginInstanceDump(obj, /*stack_trace=*/0, 2 * kDoubleSize,
354 classId(layout));
355 sub.write64(bit_cast<uint64_t>(obj.real()));
356 sub.write64(bit_cast<uint64_t>(obj.imag()));
357}
358
359void HeapProfiler::writeEllipsis(RawEllipsis obj) {
360 CHECK(!heap_object_table_.add(obj.raw()), "cannot dump object twice");
361 SubRecord sub(kInstanceDump, current_record_);
362 RawLayout layout =
363 Layout::cast(thread_->runtime()->layoutAt(LayoutId::kEllipsis));
364 sub.beginInstanceDump(obj, /*stack_trace=*/0, layout.instanceSize(),
365 classId(layout));
366 for (word i = 0; i < layout.instanceSize(); i += kPointerSize) {
367 sub.writeObjectId(objectId(Unbound::object()));
368 }
369}
370
371void HeapProfiler::writeFloat(RawFloat obj) {
372 CHECK(!heap_object_table_.add(obj.raw()), "cannot dump object twice");
373 SubRecord sub(kInstanceDump, current_record_);
374 RawLayout layout = Layout::cast(Thread::current()->runtime()->layoutOf(obj));
375 sub.beginInstanceDump(obj, /*stack_trace=*/0, kDoubleSize, classId(layout));
376 sub.write64(bit_cast<uint64_t>(obj.value()));
377}
378
379void HeapProfiler::writeLargeInt(RawLargeInt obj) {
380 CHECK(!heap_object_table_.add(obj.raw()), "cannot dump object twice");
381 SubRecord sub(kPrimitiveArrayDump, current_record_);
382 sub.beginPrimitiveArrayDump(objectId(obj), /*stack_trace=*/0, obj.numDigits(),
383 BasicType::kLong);
384 for (word i = 0; i < obj.numDigits(); i++) {
385 sub.write64(obj.digitAt(i));
386 }
387}
388
389// HEAP DUMP SEGMENT - 0x1C
390void HeapProfiler::setRecord(Record* current_record) {
391 DCHECK(current_record != nullptr, "record should be non-null");
392 DCHECK(current_record_ == nullptr, "current record already exists");
393 current_record_ = current_record;
394}
395
396void HeapProfiler::clearRecord() {
397 DCHECK(current_record_ != nullptr, "current record does not exist");
398 current_record_ = nullptr;
399}
400
401// HEAP DUMP END - 0x2C
402void HeapProfiler::writeHeapDumpEnd() { Record record(kHeapDumpEnd, this); }
403
404uword HeapProfiler::objectId(RawObject obj) {
405 uword id = obj.raw();
406 if (obj.isHeader()) {
407 std::fprintf(stderr,
408 "objectId called on header @ 0x%lx, indicating misalignment\n",
409 id);
410 }
411 if (!obj.isError() && !obj.isHeapObject()) {
412 immediate_table_.add(id);
413 }
414 return id;
415}
416
417uword HeapProfiler::classId(RawLayout layout) {
418 uword id = layout.raw();
419 if (!layout_table_.add(id)) {
420 writeLoadClass(layout);
421 }
422 return id;
423}
424
425uword HeapProfiler::cStringId(const char* c_str) {
426 uword id = reinterpret_cast<uword>(c_str);
427 if (!string_table_.add(id)) {
428 writeCStringInUTF8(c_str);
429 }
430 return id;
431}
432
433uword HeapProfiler::stringId(RawStr str) {
434 uword id = objectId(str);
435 if (!string_table_.add(id)) {
436 writeStringInUTF8(str);
437 }
438 return id;
439}
440
441// ROOT UNKNOWN - 0xFF
442//
443// Describes a root of unknown provenance.
444//
445// Format:
446// ID - object ID
447void HeapProfiler::writeRuntimeRoot(RawObject obj) {
448 SubRecord sub(kRootUnknown, current_record_);
449 // object ID
450 sub.writeObjectId(objectId(obj));
451}
452
453// ROOT STICKY CLASS - 0x05
454//
455// Describes a built-in Layout.
456//
457// Format:
458// ID - object ID
459void HeapProfiler::writeStickyClassRoot(RawObject obj) {
460 SubRecord sub(kRootStickyClass, current_record_);
461 // object ID
462 sub.writeObjectId(objectId(obj));
463}
464
465// ROOT JAVA FRAME - 0x03
466//
467// Describes a value found in a Frame in the Python stack.
468//
469// Format:
470// ID - object ID
471// u4 - thread serial number
472// u4 - frame number in stack trace (-1 for empty)
473void HeapProfiler::writeStackRoot(RawObject obj) {
474 SubRecord sub(kRootJavaFrame, current_record_);
475 // object ID
476 sub.writeObjectId(objectId(obj));
477 // thread serial number
478 // TODO(T70833159): Support multiple threads in heap dumper.
479 sub.write32(0);
480 // frame number in stack trace
481 sub.write32(-1);
482}
483
484// ROOT THREAD OBJECT - 0x08
485//
486// Describes a Thread object.
487//
488// Format:
489// ID - object ID
490// u4 - thread serial number
491// u4 - stack trace serial number
492void HeapProfiler::writeThreadRoot(Thread* thread) {
493 SubRecord sub(kRootThreadObject, current_record_);
494 // object ID
495 sub.writeObjectId(reinterpret_cast<uword>(thread));
496 // thread serial number
497 // TODO(T70833159): Support multiple threads in heap dumper.
498 sub.write32(0);
499 // stack trace serial number
500 sub.write32(0);
501}
502
503// ROOT JNI GLOBAL - 0x01
504//
505// Describes an object wrapped in an ApiHandle.
506//
507// Format:
508// ID - object ID
509// ID - ApiHandle address
510void HeapProfiler::writeApiHandleRoot(void* handle, RawObject obj) {
511 SubRecord sub(kRootJniGlobal, current_record_);
512 // object ID
513 sub.writeObjectId(objectId(obj));
514 // ApiHandle address
515 sub.writeObjectId(reinterpret_cast<uword>(handle));
516}
517
518// ROOT UNKNOWN - 0xFF
519//
520// Describes an object of unknown provenance (typically Runtime or Thread root).
521//
522// Format:
523// ID - object ID
524void HeapProfiler::writeUnknownRoot(RawObject obj) {
525 SubRecord sub(kRootUnknown, current_record_);
526 // object ID
527 sub.writeObjectId(objectId(obj));
528}
529
530// ROOT NATIVE STACK - 0x04
531//
532// Describes an object inside a native frame (in a Handle).
533//
534// Format:
535// ID - object ID
536// u4 - thread serial number
537void HeapProfiler::writeHandleRoot(RawObject obj) {
538 SubRecord sub(kRootNativeStack, current_record_);
539 // object ID
540 sub.writeObjectId(objectId(obj));
541 // thread serial number
542 // TODO(T70833159): Support multiple threads in heap dumper.
543 sub.write32(0);
544}
545
546// STRING IN UTF8 - 0x01
547//
548// Format:
549// ID - ID for this string
550// [u1]* - UTF8 characters for string (NOT NULL terminated)
551void HeapProfiler::writeStringInUTF8(RawStr str) {
552 Record record(kStringInUtf8, this);
553 record.writeObjectId(str.raw());
554 for (word i = 0, length = str.length(); i < length; i++) {
555 record.write8(str.byteAt(i));
556 }
557}
558
559void HeapProfiler::writeCStringInUTF8(const char* c_str) {
560 Record record(kStringInUtf8, this);
561 record.writeObjectId(reinterpret_cast<uword>(c_str));
562 for (; *c_str != '\0'; ++c_str) {
563 record.write8(*c_str);
564 }
565}
566
567HeapProfiler::Buffer::Buffer() : data_(Vector<uint8_t>()) {}
568
569void HeapProfiler::Buffer::write(const uint8_t* data, word size) {
570 for (word i = 0; i < size; i++) {
571 data_.push_back(data[i]);
572 }
573}
574
575void HeapProfiler::write8(uint8_t value) { write(&value, sizeof(value)); }
576
577void HeapProfiler::write16(uint16_t value) {
578 for (word i = 1; i >= 0; i--) {
579 write8((value >> (i * kBitsPerByte)) & 0xff);
580 }
581}
582
583void HeapProfiler::write32(uint32_t value) {
584 for (word i = 3; i >= 0; i--) {
585 write8((value >> (i * kBitsPerByte)) & 0xff);
586 }
587}
588
589void HeapProfiler::write64(uint64_t value) {
590 for (word i = 7; i >= 0; i--) {
591 write8((value >> (i * kBitsPerByte)) & 0xff);
592 }
593}
594
595HeapProfiler::Record::Record(Tag tag, HeapProfiler* profiler)
596 : tag_(tag), profiler_(profiler) {}
597
598// Record
599//
600// Format:
601// u1 - TAG: denoting the type of the record
602// u4 - TIME: number of microseconds since the time stamp in the header
603// u4 - LENGTH: number of bytes that follow this u4 field and belong
604// to this record
605// [u1]* - BODY: as many bytes as specified in the above u4 field
606HeapProfiler::Record::~Record() {
607 if (profiler_) {
608 profiler_->write8(tag());
609 profiler_->write32(time());
610 profiler_->write32(length());
611 if (length() > 0) {
612 profiler_->write(body(), length());
613 }
614 }
615}
616
617void HeapProfiler::Record::write(const byte* value, word size) {
618 body_.write(value, size);
619}
620
621void HeapProfiler::Record::write8(uint8_t value) {
622 body_.write(&value, sizeof(value));
623}
624
625void HeapProfiler::Record::write16(uint16_t value) {
626 for (word i = 1; i >= 0; i--) {
627 write8((value >> (i * kBitsPerByte)) & 0xff);
628 }
629}
630
631void HeapProfiler::Record::write32(uint32_t value) {
632 for (word i = 3; i >= 0; i--) {
633 write8((value >> (i * kBitsPerByte)) & 0xff);
634 }
635}
636
637void HeapProfiler::Record::write64(uint64_t value) {
638 for (word i = 7; i >= 0; i--) {
639 write8((value >> (i * kBitsPerByte)) & 0xff);
640 }
641}
642
643void HeapProfiler::Record::writeObjectId(uword value) { write64(value); }
644
645HeapProfiler::SubRecord::SubRecord(Subtag sub_tag, Record* record)
646 : record_(record) {
647 DCHECK(record_ != nullptr, "heap dump segment does not exist");
648 record_->write8(sub_tag);
649}
650
651void HeapProfiler::SubRecord::beginInstanceDump(RawObject obj,
652 uword stack_trace,
653 uword num_bytes,
654 uword layout_id) {
655 // TODO(emacs): This is a hack that works around MAT expecting ClassLoader at
656 // 0. Once we have modified MAT to dump ClassLoader at a different location
657 // than 0, we should just dump SmallInt 0 normally.
658 word id = obj.raw() == 0 ? 73 : obj.raw();
659 writeObjectId(id);
660 write32(stack_trace);
661 writeObjectId(layout_id);
662 write32(num_bytes);
663}
664
665void HeapProfiler::SubRecord::beginPrimitiveArrayDump(uword object_id,
666 uword stack_trace,
667 uword length,
668 BasicType type) {
669 writeObjectId(object_id);
670 write32(stack_trace);
671 CHECK(length < kMaxUint32, "length %ld too big for Java length field",
672 length);
673 write32(static_cast<uint32_t>(length));
674 write8(type);
675}
676
677void HeapProfiler::SubRecord::write(const uint8_t* value, intptr_t size) {
678 record_->write(value, size);
679}
680
681void HeapProfiler::SubRecord::write8(uint8_t value) { record_->write8(value); }
682
683void HeapProfiler::SubRecord::write16(uint16_t value) {
684 record_->write16(value);
685}
686
687void HeapProfiler::SubRecord::write32(uint32_t value) {
688 record_->write32(value);
689}
690
691void HeapProfiler::SubRecord::write64(uint64_t value) {
692 record_->write64(value);
693}
694
695void HeapProfiler::SubRecord::writeObjectId(uword value) {
696 record_->writeObjectId(value);
697}
698
699static void writeToFileStream(const void* data, word length, void* stream) {
700 DCHECK(data != nullptr, "data must not be null");
701 DCHECK(length > 0, "length must be positive");
702 word fd = static_cast<int>(reinterpret_cast<word>(stream));
703 int result = File::write(fd, data, length);
704 CHECK(result == length, "could not write the whole chunk to disk");
705}
706
707class HeapProfilerHandleVisitor : public HandleVisitor {
708 public:
709 HeapProfilerHandleVisitor(HeapProfiler* profiler) : profiler_(profiler) {}
710 void visitHandle(void* handle, RawObject obj) {
711 return profiler_->writeApiHandleRoot(handle, obj);
712 }
713
714 protected:
715 HeapProfiler* profiler_;
716
717 DISALLOW_COPY_AND_ASSIGN(HeapProfilerHandleVisitor);
718};
719
720class HeapProfilerRootVisitor : public PointerVisitor {
721 public:
722 HeapProfilerRootVisitor(HeapProfiler* profiler) : profiler_(profiler) {}
723 void visitPointer(RawObject* pointer, PointerKind kind) {
724 // TODO(emacs): This is a hack that works around MAT expecting ClassLoader
725 // at 0. Once we have modified MAT to dump ClassLoader at a different
726 // location than 0, we should just dump SmallInt 0 normally.
727 RawObject obj = RawObject{pointer->raw() == 0 ? 73 : pointer->raw()};
728 switch (kind) {
729 case PointerKind::kRuntime:
730 case PointerKind::kThread:
731 case PointerKind::kUnknown:
732 return profiler_->writeUnknownRoot(obj);
733 case PointerKind::kHandle:
734 return profiler_->writeHandleRoot(obj);
735 case PointerKind::kStack:
736 return profiler_->writeStackRoot(obj);
737 case PointerKind::kApiHandle:
738 // Should only see handles in `HeapProfilerHandleVisitor`.
739 UNREACHABLE("should not be used");
740 case PointerKind::kLayout:
741 return profiler_->writeStickyClassRoot(obj);
742 }
743 }
744
745 protected:
746 HeapProfiler* profiler_;
747
748 DISALLOW_COPY_AND_ASSIGN(HeapProfilerRootVisitor);
749};
750
751class HeapProfilerObjectVisitor : public HeapObjectVisitor {
752 public:
753 HeapProfilerObjectVisitor(HeapProfiler* profiler) : profiler_(profiler) {}
754 void visitHeapObject(RawHeapObject obj) {
755 switch (obj.layoutId()) {
756 case LayoutId::kLayout:
757 return profiler_->writeClassDump(Layout::cast(obj));
758 case LayoutId::kLargeInt:
759 return profiler_->writeLargeInt(LargeInt::cast(obj));
760 case LayoutId::kLargeBytes:
761 case LayoutId::kMutableBytes:
762 return profiler_->writeBytes(Bytes::cast(obj));
763 case LayoutId::kFloat:
764 return profiler_->writeFloat(Float::cast(obj));
765 case LayoutId::kComplex:
766 return profiler_->writeComplex(Complex::cast(obj));
767 case LayoutId::kTuple:
768 case LayoutId::kMutableTuple:
769 return profiler_->writeObjectArray(Tuple::cast(obj));
770 case LayoutId::kLargeStr:
771 return profiler_->writeLargeStr(LargeStr::cast(obj));
772 case LayoutId::kEllipsis:
773 return profiler_->writeEllipsis(Ellipsis::cast(obj));
774 default:
775 CHECK(obj.isInstance(), "obj should be instance, but is %ld",
776 obj.layoutId());
777 return profiler_->writeInstanceDump(Instance::cast(obj));
778 }
779 }
780
781 protected:
782 HeapProfiler* profiler_;
783
784 DISALLOW_COPY_AND_ASSIGN(HeapProfilerObjectVisitor);
785};
786
787RawObject heapDump(Thread* thread, const char* filename) {
788 int fd = File::open(
789 filename,
790 File::kBinaryFlag | File::kCreate | File::kTruncate | File::kWriteOnly,
791 0644);
792 if (fd < 0) {
793 int saved_errno = errno;
794 return thread->raiseOSErrorFromErrno(saved_errno);
795 }
796
797 HeapProfiler profiler(thread, writeToFileStream, reinterpret_cast<void*>(fd));
798 profiler.writeHeader();
799 profiler.writeFakeStackTrace();
800
801 {
802 HeapProfiler::Record record(HeapProfiler::kHeapDumpSegment, &profiler);
803 profiler.setRecord(&record);
804 profiler.writeThreadRoot(thread);
805 // java.lang.Class
806 profiler.writeFakeClassDump(HeapProfiler::FakeClass::kJavaLangClass,
807 HeapProfiler::kJavaLangClass,
808 HeapProfiler::FakeClass::kJavaLangObject);
809 // java.lang.ClassLoader
810 profiler.writeFakeClassDump(HeapProfiler::FakeClass::kJavaLangClassLoader,
811 HeapProfiler::kJavaLangClassLoader,
812 HeapProfiler::FakeClass::kJavaLangObject);
813 // java.lang.Object
814 profiler.writeFakeClassDump(HeapProfiler::FakeClass::kJavaLangObject,
815 HeapProfiler::kJavaLangObject,
816 static_cast<HeapProfiler::FakeClass>(0x0));
817 // java.lang.String
818 profiler.writeFakeClassDump(HeapProfiler::FakeClass::kJavaLangString,
819 HeapProfiler::kJavaLangString,
820 HeapProfiler::FakeClass::kJavaLangObject);
821 // byte[]
822 profiler.writeFakeClassDump(HeapProfiler::FakeClass::kBytearray,
823 HeapProfiler::kBytearrayClassName,
824 HeapProfiler::FakeClass::kJavaLangObject);
825 // double[]
826 profiler.writeFakeClassDump(HeapProfiler::FakeClass::kDoubleArray,
827 HeapProfiler::kDoubleArrayClassName,
828 HeapProfiler::FakeClass::kJavaLangObject);
829 // long[]
830 profiler.writeFakeClassDump(HeapProfiler::FakeClass::kLongArray,
831 HeapProfiler::kLongArrayClassName,
832 HeapProfiler::FakeClass::kJavaLangObject);
833 // java.lang.Object[]
834 profiler.writeFakeClassDump(HeapProfiler::FakeClass::kObjectArray,
835 HeapProfiler::kObjectArrayClassName,
836 HeapProfiler::FakeClass::kJavaLangObject);
837
838 Runtime* runtime = thread->runtime();
839
840 HeapProfilerRootVisitor root_visitor(&profiler);
841 runtime->visitRootsWithoutApiHandles(&root_visitor);
842 HeapProfilerHandleVisitor handle_visitor(&profiler);
843 visitApiHandles(runtime, &handle_visitor);
844
845 HeapProfilerObjectVisitor object_visitor(&profiler);
846 runtime->heap()->visitAllObjects(&object_visitor);
847
848 profiler.writeImmediates();
849 profiler.clearRecord();
850 }
851 profiler.writeHeapDumpEnd();
852 int result = File::close(fd);
853 CHECK(result == 0, "could not close file '%s'", filename);
854 return NoneType::object();
855}
856
857} // namespace py