• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "debugger.h"
18 
19 #include <sys/uio.h>
20 
21 #include <functional>
22 #include <memory>
23 #include <set>
24 #include <vector>
25 
26 #include "android-base/macros.h"
27 #include "android-base/stringprintf.h"
28 
29 #include "arch/context.h"
30 #include "art_field-inl.h"
31 #include "art_method-inl.h"
32 #include "base/endian_utils.h"
33 #include "base/enums.h"
34 #include "base/logging.h"
35 #include "base/memory_tool.h"
36 #include "base/safe_map.h"
37 #include "base/strlcpy.h"
38 #include "base/time_utils.h"
39 #include "class_linker-inl.h"
40 #include "class_linker.h"
41 #include "dex/descriptors_names.h"
42 #include "dex/dex_file-inl.h"
43 #include "dex/dex_file_annotations.h"
44 #include "dex/dex_file_types.h"
45 #include "dex/dex_instruction.h"
46 #include "dex/utf.h"
47 #include "entrypoints/runtime_asm_entrypoints.h"
48 #include "gc/accounting/card_table-inl.h"
49 #include "gc/allocation_record.h"
50 #include "gc/gc_cause.h"
51 #include "gc/scoped_gc_critical_section.h"
52 #include "gc/space/bump_pointer_space-walk-inl.h"
53 #include "gc/space/large_object_space.h"
54 #include "gc/space/space-inl.h"
55 #include "handle_scope-inl.h"
56 #include "instrumentation.h"
57 #include "jni/jni_internal.h"
58 #include "jvalue-inl.h"
59 #include "mirror/array-alloc-inl.h"
60 #include "mirror/class-alloc-inl.h"
61 #include "mirror/class-inl.h"
62 #include "mirror/class.h"
63 #include "mirror/class_loader.h"
64 #include "mirror/object-inl.h"
65 #include "mirror/object_array-inl.h"
66 #include "mirror/string-alloc-inl.h"
67 #include "mirror/string-inl.h"
68 #include "mirror/throwable.h"
69 #include "nativehelper/scoped_local_ref.h"
70 #include "nativehelper/scoped_primitive_array.h"
71 #include "oat_file.h"
72 #include "obj_ptr-inl.h"
73 #include "reflection.h"
74 #include "reflective_handle.h"
75 #include "reflective_handle_scope-inl.h"
76 #include "runtime-inl.h"
77 #include "runtime_callbacks.h"
78 #include "scoped_thread_state_change-inl.h"
79 #include "scoped_thread_state_change.h"
80 #include "stack.h"
81 #include "thread.h"
82 #include "thread_list.h"
83 #include "thread_pool.h"
84 #include "well_known_classes.h"
85 
86 namespace art {
87 
88 using android::base::StringPrintf;
89 
90 // Limit alloc_record_count to the 2BE value (64k-1) that is the limit of the current protocol.
CappedAllocRecordCount(size_t alloc_record_count)91 static uint16_t CappedAllocRecordCount(size_t alloc_record_count) {
92   const size_t cap = 0xffff;
93   if (alloc_record_count > cap) {
94     return cap;
95   }
96   return alloc_record_count;
97 }
98 
99 // JDWP is allowed unless the Zygote forbids it.
100 static bool gJdwpAllowed = true;
101 
102 static bool gDdmThreadNotification = false;
103 
104 // DDMS GC-related settings.
105 static Dbg::HpifWhen gDdmHpifWhen = Dbg::HPIF_WHEN_NEVER;
106 static Dbg::HpsgWhen gDdmHpsgWhen = Dbg::HPSG_WHEN_NEVER;
107 static Dbg::HpsgWhat gDdmHpsgWhat;
108 static Dbg::HpsgWhen gDdmNhsgWhen = Dbg::HPSG_WHEN_NEVER;
109 static Dbg::HpsgWhat gDdmNhsgWhat;
110 
111 Dbg::DbgThreadLifecycleCallback Dbg::thread_lifecycle_callback_;
112 
GcDidFinish()113 void Dbg::GcDidFinish() {
114   if (gDdmHpifWhen != HPIF_WHEN_NEVER) {
115     ScopedObjectAccess soa(Thread::Current());
116     VLOG(jdwp) << "Sending heap info to DDM";
117     DdmSendHeapInfo(gDdmHpifWhen);
118   }
119   if (gDdmHpsgWhen != HPSG_WHEN_NEVER) {
120     ScopedObjectAccess soa(Thread::Current());
121     VLOG(jdwp) << "Dumping heap to DDM";
122     DdmSendHeapSegments(false);
123   }
124   if (gDdmNhsgWhen != HPSG_WHEN_NEVER) {
125     ScopedObjectAccess soa(Thread::Current());
126     VLOG(jdwp) << "Dumping native heap to DDM";
127     DdmSendHeapSegments(true);
128   }
129 }
130 
SetJdwpAllowed(bool allowed)131 void Dbg::SetJdwpAllowed(bool allowed) {
132   gJdwpAllowed = allowed;
133 }
134 
IsJdwpAllowed()135 bool Dbg::IsJdwpAllowed() {
136   return gJdwpAllowed;
137 }
138 
139 // Do we need to deoptimize the stack to handle an exception?
IsForcedInterpreterNeededForExceptionImpl(Thread * thread)140 bool Dbg::IsForcedInterpreterNeededForExceptionImpl(Thread* thread) {
141   // Deoptimization is required if at least one method in the stack needs it. However we
142   // skip frames that will be unwound (thus not executed).
143   bool needs_deoptimization = false;
144   StackVisitor::WalkStack(
145       [&](art::StackVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_) {
146         // The visitor is meant to be used when handling exception from compiled code only.
147         CHECK(!visitor->IsShadowFrame()) << "We only expect to visit compiled frame: "
148                                          << ArtMethod::PrettyMethod(visitor->GetMethod());
149         ArtMethod* method = visitor->GetMethod();
150         if (method == nullptr) {
151           // We reach an upcall and don't need to deoptimize this part of the stack (ManagedFragment)
152           // so we can stop the visit.
153           DCHECK(!needs_deoptimization);
154           return false;
155         }
156         if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) {
157           // We found a compiled frame in the stack but instrumentation is set to interpret
158           // everything: we need to deoptimize.
159           needs_deoptimization = true;
160           return false;
161         }
162         if (Runtime::Current()->GetInstrumentation()->IsDeoptimized(method)) {
163           // We found a deoptimized method in the stack.
164           needs_deoptimization = true;
165           return false;
166         }
167         ShadowFrame* frame = visitor->GetThread()->FindDebuggerShadowFrame(visitor->GetFrameId());
168         if (frame != nullptr) {
169           // The debugger allocated a ShadowFrame to update a variable in the stack: we need to
170           // deoptimize the stack to execute (and deallocate) this frame.
171           needs_deoptimization = true;
172           return false;
173         }
174         return true;
175       },
176       thread,
177       /* context= */ nullptr,
178       art::StackVisitor::StackWalkKind::kIncludeInlinedFrames,
179       /* check_suspended */ true,
180       /* include_transitions */ true);
181   return needs_deoptimization;
182 }
183 
184 
DdmHandleChunk(JNIEnv * env,uint32_t type,const ArrayRef<const jbyte> & data,uint32_t * out_type,std::vector<uint8_t> * out_data)185 bool Dbg::DdmHandleChunk(JNIEnv* env,
186                          uint32_t type,
187                          const ArrayRef<const jbyte>& data,
188                          /*out*/uint32_t* out_type,
189                          /*out*/std::vector<uint8_t>* out_data) {
190   ScopedObjectAccess soa(env);
191   StackHandleScope<1u> hs(soa.Self());
192   Handle<mirror::ByteArray> data_array =
193       hs.NewHandle(mirror::ByteArray::Alloc(soa.Self(), data.size()));
194   if (data_array == nullptr) {
195     LOG(WARNING) << "byte[] allocation failed: " << data.size();
196     env->ExceptionClear();
197     return false;
198   }
199   memcpy(data_array->GetData(), data.data(), data.size());
200   // Call "private static Chunk dispatch(int type, byte[] data, int offset, int length)".
201   ArtMethod* dispatch = WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
202   ObjPtr<mirror::Object> chunk = dispatch->InvokeStatic<'L', 'I', 'L', 'I', 'I'>(
203       soa.Self(), type, data_array.Get(), 0, static_cast<jint>(data.size()));
204   if (soa.Self()->IsExceptionPending()) {
205     LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type) << std::endl
206               << soa.Self()->GetException()->Dump();
207     soa.Self()->ClearException();
208     return false;
209   }
210 
211   if (chunk == nullptr) {
212     return false;
213   }
214 
215   /*
216    * Pull the pieces out of the chunk.  We copy the results into a
217    * newly-allocated buffer that the caller can free.  We don't want to
218    * continue using the Chunk object because nothing has a reference to it.
219    *
220    * We could avoid this by returning type/data/offset/length and having
221    * the caller be aware of the object lifetime issues, but that
222    * integrates the JDWP code more tightly into the rest of the runtime, and doesn't work
223    * if we have responses for multiple chunks.
224    *
225    * So we're pretty much stuck with copying data around multiple times.
226    */
227   ObjPtr<mirror::ByteArray> reply_data = ObjPtr<mirror::ByteArray>::DownCast(
228       WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data->GetObject(chunk));
229   jint offset = WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset->GetInt(chunk);
230   jint length = WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length->GetInt(chunk);
231   *out_type = WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_type->GetInt(chunk);
232 
233   VLOG(jdwp) << StringPrintf("DDM reply: type=0x%08x data=%p offset=%d length=%d",
234                              type,
235                              reply_data.Ptr(),
236                              offset,
237                              length);
238 
239   if (reply_data == nullptr) {
240     LOG(INFO) << "Null reply data";
241     return false;
242   }
243 
244   jint reply_length = reply_data->GetLength();
245   if (offset < 0 || offset > reply_length || length < 0 || length > reply_length - offset) {
246     LOG(INFO) << "Invalid reply data range: offset=" << offset << ", length=" << length
247               << " reply_length=" << reply_length;
248     return false;
249   }
250 
251   out_data->resize(length);
252   memcpy(out_data->data(), reply_data->GetData() + offset, length);
253 
254   return true;
255 }
256 
DdmBroadcast(bool connect)257 void Dbg::DdmBroadcast(bool connect) {
258   VLOG(jdwp) << "Broadcasting DDM " << (connect ? "connect" : "disconnect") << "...";
259 
260   Thread* self = Thread::Current();
261   if (self->GetState() != ThreadState::kRunnable) {
262     LOG(ERROR) << "DDM broadcast in thread state " << self->GetState();
263     /* try anyway? */
264   }
265 
266   // TODO: Can we really get here while not `Runnable`? If not, we do not need the `soa`.
267   ScopedObjectAccessUnchecked soa(self);
268   JNIEnv* env = self->GetJniEnv();
269   jint event = connect ? 1 /*DdmServer.CONNECTED*/ : 2 /*DdmServer.DISCONNECTED*/;
270   ArtMethod* broadcast = WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
271   broadcast->InvokeStatic<'V', 'I'>(self, event);
272   if (self->IsExceptionPending()) {
273     LOG(ERROR) << "DdmServer.broadcast " << event << " failed";
274     env->ExceptionDescribe();
275     env->ExceptionClear();
276   }
277 }
278 
DdmConnected()279 void Dbg::DdmConnected() {
280   Dbg::DdmBroadcast(true);
281 }
282 
DdmDisconnected()283 void Dbg::DdmDisconnected() {
284   Dbg::DdmBroadcast(false);
285   gDdmThreadNotification = false;
286 }
287 
288 
289 /*
290  * Send a notification when a thread starts, stops, or changes its name.
291  *
292  * Because we broadcast the full set of threads when the notifications are
293  * first enabled, it's possible for "thread" to be actively executing.
294  */
DdmSendThreadNotification(Thread * t,uint32_t type)295 void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) {
296   Locks::mutator_lock_->AssertNotExclusiveHeld(Thread::Current());
297   if (!gDdmThreadNotification) {
298     return;
299   }
300 
301   RuntimeCallbacks* cb = Runtime::Current()->GetRuntimeCallbacks();
302   if (type == CHUNK_TYPE("THDE")) {
303     uint8_t buf[4];
304     Set4BE(&buf[0], t->GetThreadId());
305     cb->DdmPublishChunk(CHUNK_TYPE("THDE"), ArrayRef<const uint8_t>(buf));
306   } else {
307     CHECK(type == CHUNK_TYPE("THCR") || type == CHUNK_TYPE("THNM")) << type;
308     StackHandleScope<1> hs(Thread::Current());
309     Handle<mirror::String> name(hs.NewHandle(t->GetThreadName()));
310     size_t char_count = (name != nullptr) ? name->GetLength() : 0;
311     const jchar* chars = (name != nullptr) ? name->GetValue() : nullptr;
312     bool is_compressed = (name != nullptr) ? name->IsCompressed() : false;
313 
314     std::vector<uint8_t> bytes;
315     Append4BE(bytes, t->GetThreadId());
316     if (is_compressed) {
317       const uint8_t* chars_compressed = name->GetValueCompressed();
318       AppendUtf16CompressedBE(bytes, chars_compressed, char_count);
319     } else {
320       AppendUtf16BE(bytes, chars, char_count);
321     }
322     CHECK_EQ(bytes.size(), char_count*2 + sizeof(uint32_t)*2);
323     cb->DdmPublishChunk(type, ArrayRef<const uint8_t>(bytes));
324   }
325 }
326 
DdmSetThreadNotification(bool enable)327 void Dbg::DdmSetThreadNotification(bool enable) {
328   // Enable/disable thread notifications.
329   gDdmThreadNotification = enable;
330   if (enable) {
331     // Use a Checkpoint to cause every currently running thread to send their own notification when
332     // able. We then wait for every thread thread active at the time to post the creation
333     // notification. Threads created later will send this themselves.
334     Thread* self = Thread::Current();
335     ScopedObjectAccess soa(self);
336     Barrier finish_barrier(0);
337     FunctionClosure fc([&](Thread* thread) REQUIRES_SHARED(Locks::mutator_lock_) {
338       Thread* cls_self = Thread::Current();
339       Locks::mutator_lock_->AssertSharedHeld(cls_self);
340       Dbg::DdmSendThreadNotification(thread, CHUNK_TYPE("THCR"));
341       finish_barrier.Pass(cls_self);
342     });
343     size_t checkpoints = Runtime::Current()->GetThreadList()->RunCheckpoint(&fc);
344     ScopedThreadSuspension sts(self, ThreadState::kWaitingForCheckPointsToRun);
345     finish_barrier.Increment(self, checkpoints);
346   }
347 }
348 
PostThreadStartOrStop(Thread * t,uint32_t type)349 void Dbg::PostThreadStartOrStop(Thread* t, uint32_t type) {
350   Dbg::DdmSendThreadNotification(t, type);
351 }
352 
PostThreadStart(Thread * t)353 void Dbg::PostThreadStart(Thread* t) {
354   Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THCR"));
355 }
356 
PostThreadDeath(Thread * t)357 void Dbg::PostThreadDeath(Thread* t) {
358   Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THDE"));
359 }
360 
DdmHandleHpifChunk(HpifWhen when)361 int Dbg::DdmHandleHpifChunk(HpifWhen when) {
362   if (when == HPIF_WHEN_NOW) {
363     DdmSendHeapInfo(when);
364     return 1;
365   }
366 
367   if (when != HPIF_WHEN_NEVER && when != HPIF_WHEN_NEXT_GC && when != HPIF_WHEN_EVERY_GC) {
368     LOG(ERROR) << "invalid HpifWhen value: " << static_cast<int>(when);
369     return 0;
370   }
371 
372   gDdmHpifWhen = when;
373   return 1;
374 }
375 
DdmHandleHpsgNhsgChunk(Dbg::HpsgWhen when,Dbg::HpsgWhat what,bool native)376 bool Dbg::DdmHandleHpsgNhsgChunk(Dbg::HpsgWhen when, Dbg::HpsgWhat what, bool native) {
377   if (when != HPSG_WHEN_NEVER && when != HPSG_WHEN_EVERY_GC) {
378     LOG(ERROR) << "invalid HpsgWhen value: " << static_cast<int>(when);
379     return false;
380   }
381 
382   if (what != HPSG_WHAT_MERGED_OBJECTS && what != HPSG_WHAT_DISTINCT_OBJECTS) {
383     LOG(ERROR) << "invalid HpsgWhat value: " << static_cast<int>(what);
384     return false;
385   }
386 
387   if (native) {
388     gDdmNhsgWhen = when;
389     gDdmNhsgWhat = what;
390   } else {
391     gDdmHpsgWhen = when;
392     gDdmHpsgWhat = what;
393   }
394   return true;
395 }
396 
DdmSendHeapInfo(HpifWhen reason)397 void Dbg::DdmSendHeapInfo(HpifWhen reason) {
398   // If there's a one-shot 'when', reset it.
399   if (reason == gDdmHpifWhen) {
400     if (gDdmHpifWhen == HPIF_WHEN_NEXT_GC) {
401       gDdmHpifWhen = HPIF_WHEN_NEVER;
402     }
403   }
404 
405   /*
406    * Chunk HPIF (client --> server)
407    *
408    * Heap Info. General information about the heap,
409    * suitable for a summary display.
410    *
411    *   [u4]: number of heaps
412    *
413    *   For each heap:
414    *     [u4]: heap ID
415    *     [u8]: timestamp in ms since Unix epoch
416    *     [u1]: capture reason (same as 'when' value from server)
417    *     [u4]: max heap size in bytes (-Xmx)
418    *     [u4]: current heap size in bytes
419    *     [u4]: current number of bytes allocated
420    *     [u4]: current number of objects allocated
421    */
422   uint8_t heap_count = 1;
423   gc::Heap* heap = Runtime::Current()->GetHeap();
424   std::vector<uint8_t> bytes;
425   Append4BE(bytes, heap_count);
426   Append4BE(bytes, 1);  // Heap id (bogus; we only have one heap).
427   Append8BE(bytes, MilliTime());
428   Append1BE(bytes, reason);
429   Append4BE(bytes, heap->GetMaxMemory());  // Max allowed heap size in bytes.
430   Append4BE(bytes, heap->GetTotalMemory());  // Current heap size in bytes.
431   Append4BE(bytes, heap->GetBytesAllocated());
432   Append4BE(bytes, heap->GetObjectsAllocated());
433   CHECK_EQ(bytes.size(), 4U + (heap_count * (4 + 8 + 1 + 4 + 4 + 4 + 4)));
434   Runtime::Current()->GetRuntimeCallbacks()->DdmPublishChunk(CHUNK_TYPE("HPIF"),
435                                                              ArrayRef<const uint8_t>(bytes));
436 }
437 
438 enum HpsgSolidity {
439   SOLIDITY_FREE = 0,
440   SOLIDITY_HARD = 1,
441   SOLIDITY_SOFT = 2,
442   SOLIDITY_WEAK = 3,
443   SOLIDITY_PHANTOM = 4,
444   SOLIDITY_FINALIZABLE = 5,
445   SOLIDITY_SWEEP = 6,
446 };
447 
448 enum HpsgKind {
449   KIND_OBJECT = 0,
450   KIND_CLASS_OBJECT = 1,
451   KIND_ARRAY_1 = 2,
452   KIND_ARRAY_2 = 3,
453   KIND_ARRAY_4 = 4,
454   KIND_ARRAY_8 = 5,
455   KIND_UNKNOWN = 6,
456   KIND_NATIVE = 7,
457 };
458 
459 #define HPSG_PARTIAL (1<<7)
460 #define HPSG_STATE(solidity, kind) ((uint8_t)((((kind) & 0x7) << 3) | ((solidity) & 0x7)))
461 
462 class HeapChunkContext {
463  public:
464   // Maximum chunk size.  Obtain this from the formula:
465   // (((maximum_heap_size / ALLOCATION_UNIT_SIZE) + 255) / 256) * 2
HeapChunkContext(bool merge,bool native)466   HeapChunkContext(bool merge, bool native)
467       : buf_(16384 - 16),
468         type_(0),
469         chunk_overhead_(0) {
470     Reset();
471     if (native) {
472       type_ = CHUNK_TYPE("NHSG");
473     } else {
474       type_ = merge ? CHUNK_TYPE("HPSG") : CHUNK_TYPE("HPSO");
475     }
476   }
477 
~HeapChunkContext()478   ~HeapChunkContext() {
479     if (p_ > &buf_[0]) {
480       Flush();
481     }
482   }
483 
SetChunkOverhead(size_t chunk_overhead)484   void SetChunkOverhead(size_t chunk_overhead) {
485     chunk_overhead_ = chunk_overhead;
486   }
487 
ResetStartOfNextChunk()488   void ResetStartOfNextChunk() {
489     startOfNextMemoryChunk_ = nullptr;
490   }
491 
EnsureHeader(const void * chunk_ptr)492   void EnsureHeader(const void* chunk_ptr) {
493     if (!needHeader_) {
494       return;
495     }
496 
497     // Start a new HPSx chunk.
498     Write4BE(&p_, 1);  // Heap id (bogus; we only have one heap).
499     Write1BE(&p_, 8);  // Size of allocation unit, in bytes.
500 
501     Write4BE(&p_, reinterpret_cast<uintptr_t>(chunk_ptr));  // virtual address of segment start.
502     Write4BE(&p_, 0);  // offset of this piece (relative to the virtual address).
503     // [u4]: length of piece, in allocation units
504     // We won't know this until we're done, so save the offset and stuff in a fake value.
505     pieceLenField_ = p_;
506     Write4BE(&p_, 0x55555555);
507     needHeader_ = false;
508   }
509 
Flush()510   void Flush() REQUIRES_SHARED(Locks::mutator_lock_) {
511     if (pieceLenField_ == nullptr) {
512       // Flush immediately post Reset (maybe back-to-back Flush). Ignore.
513       CHECK(needHeader_);
514       return;
515     }
516     // Patch the "length of piece" field.
517     CHECK_LE(&buf_[0], pieceLenField_);
518     CHECK_LE(pieceLenField_, p_);
519     Set4BE(pieceLenField_, totalAllocationUnits_);
520 
521     ArrayRef<const uint8_t> out(&buf_[0], p_ - &buf_[0]);
522     Runtime::Current()->GetRuntimeCallbacks()->DdmPublishChunk(type_, out);
523     Reset();
524   }
525 
HeapChunkJavaCallback(void * start,void * end,size_t used_bytes,void * arg)526   static void HeapChunkJavaCallback(void* start, void* end, size_t used_bytes, void* arg)
527       REQUIRES_SHARED(Locks::heap_bitmap_lock_,
528                             Locks::mutator_lock_) {
529     reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkJavaCallback(start, end, used_bytes);
530   }
531 
HeapChunkNativeCallback(void * start,void * end,size_t used_bytes,void * arg)532   static void HeapChunkNativeCallback(void* start, void* end, size_t used_bytes, void* arg)
533       REQUIRES_SHARED(Locks::mutator_lock_) {
534     reinterpret_cast<HeapChunkContext*>(arg)->HeapChunkNativeCallback(start, end, used_bytes);
535   }
536 
537  private:
538   enum { ALLOCATION_UNIT_SIZE = 8 };
539 
Reset()540   void Reset() {
541     p_ = &buf_[0];
542     ResetStartOfNextChunk();
543     totalAllocationUnits_ = 0;
544     needHeader_ = true;
545     pieceLenField_ = nullptr;
546   }
547 
IsNative() const548   bool IsNative() const {
549     return type_ == CHUNK_TYPE("NHSG");
550   }
551 
552   // Returns true if the object is not an empty chunk.
ProcessRecord(void * start,size_t used_bytes)553   bool ProcessRecord(void* start, size_t used_bytes) REQUIRES_SHARED(Locks::mutator_lock_) {
554     // Note: heap call backs cannot manipulate the heap upon which they are crawling, care is taken
555     // in the following code not to allocate memory, by ensuring buf_ is of the correct size
556     if (used_bytes == 0) {
557       if (start == nullptr) {
558         // Reset for start of new heap.
559         startOfNextMemoryChunk_ = nullptr;
560         Flush();
561       }
562       // Only process in use memory so that free region information
563       // also includes dlmalloc book keeping.
564       return false;
565     }
566     if (startOfNextMemoryChunk_ != nullptr) {
567       // Transmit any pending free memory. Native free memory of over kMaxFreeLen could be because
568       // of the use of mmaps, so don't report. If not free memory then start a new segment.
569       bool flush = true;
570       if (start > startOfNextMemoryChunk_) {
571         const size_t kMaxFreeLen = 2 * kPageSize;
572         void* free_start = startOfNextMemoryChunk_;
573         void* free_end = start;
574         const size_t free_len =
575             reinterpret_cast<uintptr_t>(free_end) - reinterpret_cast<uintptr_t>(free_start);
576         if (!IsNative() || free_len < kMaxFreeLen) {
577           AppendChunk(HPSG_STATE(SOLIDITY_FREE, 0), free_start, free_len, IsNative());
578           flush = false;
579         }
580       }
581       if (flush) {
582         startOfNextMemoryChunk_ = nullptr;
583         Flush();
584       }
585     }
586     return true;
587   }
588 
HeapChunkNativeCallback(void * start,void *,size_t used_bytes)589   void HeapChunkNativeCallback(void* start, void* /*end*/, size_t used_bytes)
590       REQUIRES_SHARED(Locks::mutator_lock_) {
591     if (ProcessRecord(start, used_bytes)) {
592       uint8_t state = ExamineNativeObject(start);
593       AppendChunk(state, start, used_bytes + chunk_overhead_, /*is_native=*/ true);
594       startOfNextMemoryChunk_ = reinterpret_cast<char*>(start) + used_bytes + chunk_overhead_;
595     }
596   }
597 
HeapChunkJavaCallback(void * start,void *,size_t used_bytes)598   void HeapChunkJavaCallback(void* start, void* /*end*/, size_t used_bytes)
599       REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
600     if (ProcessRecord(start, used_bytes)) {
601       // Determine the type of this chunk.
602       // OLD-TODO: if context.merge, see if this chunk is different from the last chunk.
603       // If it's the same, we should combine them.
604       uint8_t state = ExamineJavaObject(reinterpret_cast<mirror::Object*>(start));
605       AppendChunk(state, start, used_bytes + chunk_overhead_, /*is_native=*/ false);
606       startOfNextMemoryChunk_ = reinterpret_cast<char*>(start) + used_bytes + chunk_overhead_;
607     }
608   }
609 
AppendChunk(uint8_t state,void * ptr,size_t length,bool is_native)610   void AppendChunk(uint8_t state, void* ptr, size_t length, bool is_native)
611       REQUIRES_SHARED(Locks::mutator_lock_) {
612     // Make sure there's enough room left in the buffer.
613     // We need to use two bytes for every fractional 256 allocation units used by the chunk plus
614     // 17 bytes for any header.
615     const size_t needed = ((RoundUp(length / ALLOCATION_UNIT_SIZE, 256) / 256) * 2) + 17;
616     size_t byte_left = &buf_.back() - p_;
617     if (byte_left < needed) {
618       if (is_native) {
619       // Cannot trigger memory allocation while walking native heap.
620         return;
621       }
622       Flush();
623     }
624 
625     byte_left = &buf_.back() - p_;
626     if (byte_left < needed) {
627       LOG(WARNING) << "Chunk is too big to transmit (chunk_len=" << length << ", "
628           << needed << " bytes)";
629       return;
630     }
631     EnsureHeader(ptr);
632     // Write out the chunk description.
633     length /= ALLOCATION_UNIT_SIZE;   // Convert to allocation units.
634     totalAllocationUnits_ += length;
635     while (length > 256) {
636       *p_++ = state | HPSG_PARTIAL;
637       *p_++ = 255;     // length - 1
638       length -= 256;
639     }
640     *p_++ = state;
641     *p_++ = length - 1;
642   }
643 
ExamineNativeObject(const void * p)644   uint8_t ExamineNativeObject(const void* p) REQUIRES_SHARED(Locks::mutator_lock_) {
645     return p == nullptr ? HPSG_STATE(SOLIDITY_FREE, 0) : HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
646   }
647 
ExamineJavaObject(ObjPtr<mirror::Object> o)648   uint8_t ExamineJavaObject(ObjPtr<mirror::Object> o)
649       REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
650     if (o == nullptr) {
651       return HPSG_STATE(SOLIDITY_FREE, 0);
652     }
653     // It's an allocated chunk. Figure out what it is.
654     gc::Heap* heap = Runtime::Current()->GetHeap();
655     if (!heap->IsLiveObjectLocked(o)) {
656       LOG(ERROR) << "Invalid object in managed heap: " << o;
657       return HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
658     }
659     ObjPtr<mirror::Class> c = o->GetClass();
660     if (c == nullptr) {
661       // The object was probably just created but hasn't been initialized yet.
662       return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
663     }
664     if (!heap->IsValidObjectAddress(c.Ptr())) {
665       LOG(ERROR) << "Invalid class for managed heap object: " << o << " " << c;
666       return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
667     }
668     if (c->GetClass() == nullptr) {
669       LOG(ERROR) << "Null class of class " << c << " for object " << o;
670       return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
671     }
672     if (c->IsClassClass()) {
673       return HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
674     }
675     if (c->IsArrayClass()) {
676       switch (c->GetComponentSize()) {
677       case 1: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
678       case 2: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
679       case 4: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
680       case 8: return HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
681       }
682     }
683     return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
684   }
685 
686   std::vector<uint8_t> buf_;
687   uint8_t* p_;
688   uint8_t* pieceLenField_;
689   void* startOfNextMemoryChunk_;
690   size_t totalAllocationUnits_;
691   uint32_t type_;
692   bool needHeader_;
693   size_t chunk_overhead_;
694 
695   DISALLOW_COPY_AND_ASSIGN(HeapChunkContext);
696 };
697 
698 
DdmSendHeapSegments(bool native)699 void Dbg::DdmSendHeapSegments(bool native) {
700   Dbg::HpsgWhen when = native ? gDdmNhsgWhen : gDdmHpsgWhen;
701   Dbg::HpsgWhat what = native ? gDdmNhsgWhat : gDdmHpsgWhat;
702   if (when == HPSG_WHEN_NEVER) {
703     return;
704   }
705   RuntimeCallbacks* cb = Runtime::Current()->GetRuntimeCallbacks();
706   // Figure out what kind of chunks we'll be sending.
707   CHECK(what == HPSG_WHAT_MERGED_OBJECTS || what == HPSG_WHAT_DISTINCT_OBJECTS)
708       << static_cast<int>(what);
709 
710   // First, send a heap start chunk.
711   uint8_t heap_id[4];
712   Set4BE(&heap_id[0], 1);  // Heap id (bogus; we only have one heap).
713   cb->DdmPublishChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"),
714                       ArrayRef<const uint8_t>(heap_id));
715   Thread* self = Thread::Current();
716   Locks::mutator_lock_->AssertSharedHeld(self);
717 
718   // Send a series of heap segment chunks.
719   HeapChunkContext context(what == HPSG_WHAT_MERGED_OBJECTS, native);
720   auto bump_pointer_space_visitor = [&](mirror::Object* obj)
721       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
722     const size_t size = RoundUp(obj->SizeOf(), kObjectAlignment);
723     HeapChunkContext::HeapChunkJavaCallback(
724         obj, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(obj) + size), size, &context);
725   };
726   if (native) {
727     UNIMPLEMENTED(WARNING) << "Native heap inspection is not supported";
728   } else {
729     gc::Heap* heap = Runtime::Current()->GetHeap();
730     for (const auto& space : heap->GetContinuousSpaces()) {
731       if (space->IsDlMallocSpace()) {
732         ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
733         // dlmalloc's chunk header is 2 * sizeof(size_t), but if the previous chunk is in use for an
734         // allocation then the first sizeof(size_t) may belong to it.
735         context.SetChunkOverhead(sizeof(size_t));
736         space->AsDlMallocSpace()->Walk(HeapChunkContext::HeapChunkJavaCallback, &context);
737       } else if (space->IsRosAllocSpace()) {
738         context.SetChunkOverhead(0);
739         // Need to acquire the mutator lock before the heap bitmap lock with exclusive access since
740         // RosAlloc's internal logic doesn't know to release and reacquire the heap bitmap lock.
741         ScopedThreadSuspension sts(self, ThreadState::kSuspended);
742         ScopedSuspendAll ssa(__FUNCTION__);
743         ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
744         space->AsRosAllocSpace()->Walk(HeapChunkContext::HeapChunkJavaCallback, &context);
745       } else if (space->IsBumpPointerSpace()) {
746         ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
747         context.SetChunkOverhead(0);
748         space->AsBumpPointerSpace()->Walk(bump_pointer_space_visitor);
749         HeapChunkContext::HeapChunkJavaCallback(nullptr, nullptr, 0, &context);
750       } else if (space->IsRegionSpace()) {
751         heap->IncrementDisableMovingGC(self);
752         {
753           ScopedThreadSuspension sts(self, ThreadState::kSuspended);
754           ScopedSuspendAll ssa(__FUNCTION__);
755           ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
756           context.SetChunkOverhead(0);
757           space->AsRegionSpace()->Walk(bump_pointer_space_visitor);
758           HeapChunkContext::HeapChunkJavaCallback(nullptr, nullptr, 0, &context);
759         }
760         heap->DecrementDisableMovingGC(self);
761       } else {
762         UNIMPLEMENTED(WARNING) << "Not counting objects in space " << *space;
763       }
764       context.ResetStartOfNextChunk();
765     }
766     ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
767     // Walk the large objects, these are not in the AllocSpace.
768     context.SetChunkOverhead(0);
769     heap->GetLargeObjectsSpace()->Walk(HeapChunkContext::HeapChunkJavaCallback, &context);
770   }
771 
772   // Finally, send a heap end chunk.
773   cb->DdmPublishChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"),
774                       ArrayRef<const uint8_t>(heap_id));
775 }
776 
SetAllocTrackingEnabled(bool enable)777 void Dbg::SetAllocTrackingEnabled(bool enable) {
778   gc::AllocRecordObjectMap::SetAllocTrackingEnabled(enable);
779 }
780 
781 class StringTable {
782  private:
783   struct Entry {
Entryart::StringTable::Entry784     explicit Entry(const char* data_in)
785         : data(data_in), hash(ComputeModifiedUtf8Hash(data_in)), index(0) {
786     }
787     Entry(const Entry& entry) = default;
788     Entry(Entry&& entry) = default;
789 
790     // Pointer to the actual string data.
791     const char* data;
792 
793     // The hash of the data.
794     const uint32_t hash;
795 
796     // The index. This will be filled in on Finish and is not part of the ordering, so mark it
797     // mutable.
798     mutable uint32_t index;
799 
operator ==art::StringTable::Entry800     bool operator==(const Entry& other) const {
801       return strcmp(data, other.data) == 0;
802     }
803   };
804   struct EntryHash {
operator ()art::StringTable::EntryHash805     size_t operator()(const Entry& entry) const {
806       return entry.hash;
807     }
808   };
809 
810  public:
StringTable()811   StringTable() : finished_(false) {
812   }
813 
Add(const char * str,bool copy_string)814   void Add(const char* str, bool copy_string) {
815     DCHECK(!finished_);
816     if (UNLIKELY(copy_string)) {
817       // Check whether it's already there.
818       Entry entry(str);
819       if (table_.find(entry) != table_.end()) {
820         return;
821       }
822 
823       // Make a copy.
824       size_t str_len = strlen(str);
825       char* copy = new char[str_len + 1];
826       strlcpy(copy, str, str_len + 1);
827       string_backup_.emplace_back(copy);
828       str = copy;
829     }
830     Entry entry(str);
831     table_.insert(entry);
832   }
833 
834   // Update all entries and give them an index. Note that this is likely not the insertion order,
835   // as the set will with high likelihood reorder elements. Thus, Add must not be called after
836   // Finish, and Finish must be called before IndexOf. In that case, WriteTo will walk in
837   // the same order as Finish, and indices will agree. The order invariant, as well as indices,
838   // are enforced through debug checks.
Finish()839   void Finish() {
840     DCHECK(!finished_);
841     finished_ = true;
842     uint32_t index = 0;
843     for (auto& entry : table_) {
844       entry.index = index;
845       ++index;
846     }
847   }
848 
IndexOf(const char * s) const849   size_t IndexOf(const char* s) const {
850     DCHECK(finished_);
851     Entry entry(s);
852     auto it = table_.find(entry);
853     if (it == table_.end()) {
854       LOG(FATAL) << "IndexOf(\"" << s << "\") failed";
855     }
856     return it->index;
857   }
858 
Size() const859   size_t Size() const {
860     return table_.size();
861   }
862 
WriteTo(std::vector<uint8_t> & bytes) const863   void WriteTo(std::vector<uint8_t>& bytes) const {
864     DCHECK(finished_);
865     uint32_t cur_index = 0;
866     for (const auto& entry : table_) {
867       DCHECK_EQ(cur_index++, entry.index);
868 
869       size_t s_len = CountModifiedUtf8Chars(entry.data);
870       std::unique_ptr<uint16_t[]> s_utf16(new uint16_t[s_len]);
871       ConvertModifiedUtf8ToUtf16(s_utf16.get(), entry.data);
872       AppendUtf16BE(bytes, s_utf16.get(), s_len);
873     }
874   }
875 
876  private:
877   std::unordered_set<Entry, EntryHash> table_;
878   std::vector<std::unique_ptr<char[]>> string_backup_;
879 
880   bool finished_;
881 
882   DISALLOW_COPY_AND_ASSIGN(StringTable);
883 };
884 
885 
GetMethodSourceFile(ArtMethod * method)886 static const char* GetMethodSourceFile(ArtMethod* method)
887     REQUIRES_SHARED(Locks::mutator_lock_) {
888   DCHECK(method != nullptr);
889   const char* source_file = method->GetDeclaringClassSourceFile();
890   return (source_file != nullptr) ? source_file : "";
891 }
892 
893 /*
894  * The data we send to DDMS contains everything we have recorded.
895  *
896  * Message header (all values big-endian):
897  * (1b) message header len (to allow future expansion); includes itself
898  * (1b) entry header len
899  * (1b) stack frame len
900  * (2b) number of entries
901  * (4b) offset to string table from start of message
902  * (2b) number of class name strings
903  * (2b) number of method name strings
904  * (2b) number of source file name strings
905  * For each entry:
906  *   (4b) total allocation size
907  *   (2b) thread id
908  *   (2b) allocated object's class name index
909  *   (1b) stack depth
910  *   For each stack frame:
911  *     (2b) method's class name
912  *     (2b) method name
913  *     (2b) method source file
914  *     (2b) line number, clipped to 32767; -2 if native; -1 if no source
915  * (xb) class name strings
916  * (xb) method name strings
917  * (xb) source file strings
918  *
919  * As with other DDM traffic, strings are sent as a 4-byte length
920  * followed by UTF-16 data.
921  *
922  * We send up 16-bit unsigned indexes into string tables.  In theory there
923  * can be (kMaxAllocRecordStackDepth * alloc_record_max_) unique strings in
924  * each table, but in practice there should be far fewer.
925  *
926  * The chief reason for using a string table here is to keep the size of
927  * the DDMS message to a minimum.  This is partly to make the protocol
928  * efficient, but also because we have to form the whole thing up all at
929  * once in a memory buffer.
930  *
931  * We use separate string tables for class names, method names, and source
932  * files to keep the indexes small.  There will generally be no overlap
933  * between the contents of these tables.
934  */
GetRecentAllocations()935 jbyteArray Dbg::GetRecentAllocations() {
936   if ((false)) {
937     DumpRecentAllocations();
938   }
939 
940   Thread* self = Thread::Current();
941   std::vector<uint8_t> bytes;
942   {
943     MutexLock mu(self, *Locks::alloc_tracker_lock_);
944     gc::AllocRecordObjectMap* records = Runtime::Current()->GetHeap()->GetAllocationRecords();
945     // In case this method is called when allocation tracker is not enabled,
946     // we should still send some data back.
947     gc::AllocRecordObjectMap fallback_record_map;
948     if (records == nullptr) {
949       CHECK(!Runtime::Current()->GetHeap()->IsAllocTrackingEnabled());
950       records = &fallback_record_map;
951     }
952     // We don't need to wait on the condition variable records->new_record_condition_, because this
953     // function only reads the class objects, which are already marked so it doesn't change their
954     // reachability.
955 
956     //
957     // Part 1: generate string tables.
958     //
959     StringTable class_names;
960     StringTable method_names;
961     StringTable filenames;
962 
963     VLOG(jdwp) << "Collecting StringTables.";
964 
965     const uint16_t capped_count = CappedAllocRecordCount(records->GetRecentAllocationSize());
966     uint16_t count = capped_count;
967     size_t alloc_byte_count = 0;
968     for (auto it = records->RBegin(), end = records->REnd();
969          count > 0 && it != end; count--, it++) {
970       const gc::AllocRecord* record = &it->second;
971       std::string temp;
972       const char* class_descr = record->GetClassDescriptor(&temp);
973       class_names.Add(class_descr, !temp.empty());
974 
975       // Size + tid + class name index + stack depth.
976       alloc_byte_count += 4u + 2u + 2u + 1u;
977 
978       for (size_t i = 0, depth = record->GetDepth(); i < depth; i++) {
979         ArtMethod* m = record->StackElement(i).GetMethod();
980         class_names.Add(m->GetDeclaringClassDescriptor(), false);
981         method_names.Add(m->GetName(), false);
982         filenames.Add(GetMethodSourceFile(m), false);
983       }
984 
985       // Depth * (class index + method name index + file name index + line number).
986       alloc_byte_count += record->GetDepth() * (2u + 2u + 2u + 2u);
987     }
988 
989     class_names.Finish();
990     method_names.Finish();
991     filenames.Finish();
992     VLOG(jdwp) << "Done collecting StringTables:" << std::endl
993                << "  ClassNames: " << class_names.Size() << std::endl
994                << "  MethodNames: " << method_names.Size() << std::endl
995                << "  Filenames: " << filenames.Size();
996 
997     LOG(INFO) << "recent allocation records: " << capped_count;
998     LOG(INFO) << "allocation records all objects: " << records->Size();
999 
1000     //
1001     // Part 2: Generate the output and store it in the buffer.
1002     //
1003 
1004     // (1b) message header len (to allow future expansion); includes itself
1005     // (1b) entry header len
1006     // (1b) stack frame len
1007     const int kMessageHeaderLen = 15;
1008     const int kEntryHeaderLen = 9;
1009     const int kStackFrameLen = 8;
1010     Append1BE(bytes, kMessageHeaderLen);
1011     Append1BE(bytes, kEntryHeaderLen);
1012     Append1BE(bytes, kStackFrameLen);
1013 
1014     // (2b) number of entries
1015     // (4b) offset to string table from start of message
1016     // (2b) number of class name strings
1017     // (2b) number of method name strings
1018     // (2b) number of source file name strings
1019     Append2BE(bytes, capped_count);
1020     size_t string_table_offset = bytes.size();
1021     Append4BE(bytes, 0);  // We'll patch this later...
1022     Append2BE(bytes, class_names.Size());
1023     Append2BE(bytes, method_names.Size());
1024     Append2BE(bytes, filenames.Size());
1025 
1026     VLOG(jdwp) << "Dumping allocations with stacks";
1027 
1028     // Enlarge the vector for the allocation data.
1029     size_t reserve_size = bytes.size() + alloc_byte_count;
1030     bytes.reserve(reserve_size);
1031 
1032     std::string temp;
1033     count = capped_count;
1034     // The last "count" number of allocation records in "records" are the most recent "count" number
1035     // of allocations. Reverse iterate to get them. The most recent allocation is sent first.
1036     for (auto it = records->RBegin(), end = records->REnd();
1037          count > 0 && it != end; count--, it++) {
1038       // For each entry:
1039       // (4b) total allocation size
1040       // (2b) thread id
1041       // (2b) allocated object's class name index
1042       // (1b) stack depth
1043       const gc::AllocRecord* record = &it->second;
1044       size_t stack_depth = record->GetDepth();
1045       size_t allocated_object_class_name_index =
1046           class_names.IndexOf(record->GetClassDescriptor(&temp));
1047       Append4BE(bytes, record->ByteCount());
1048       Append2BE(bytes, static_cast<uint16_t>(record->GetTid()));
1049       Append2BE(bytes, allocated_object_class_name_index);
1050       Append1BE(bytes, stack_depth);
1051 
1052       for (size_t stack_frame = 0; stack_frame < stack_depth; ++stack_frame) {
1053         // For each stack frame:
1054         // (2b) method's class name
1055         // (2b) method name
1056         // (2b) method source file
1057         // (2b) line number, clipped to 32767; -2 if native; -1 if no source
1058         ArtMethod* m = record->StackElement(stack_frame).GetMethod();
1059         size_t class_name_index = class_names.IndexOf(m->GetDeclaringClassDescriptor());
1060         size_t method_name_index = method_names.IndexOf(m->GetName());
1061         size_t file_name_index = filenames.IndexOf(GetMethodSourceFile(m));
1062         Append2BE(bytes, class_name_index);
1063         Append2BE(bytes, method_name_index);
1064         Append2BE(bytes, file_name_index);
1065         Append2BE(bytes, record->StackElement(stack_frame).ComputeLineNumber());
1066       }
1067     }
1068 
1069     CHECK_EQ(bytes.size(), reserve_size);
1070     VLOG(jdwp) << "Dumping tables.";
1071 
1072     // (xb) class name strings
1073     // (xb) method name strings
1074     // (xb) source file strings
1075     Set4BE(&bytes[string_table_offset], bytes.size());
1076     class_names.WriteTo(bytes);
1077     method_names.WriteTo(bytes);
1078     filenames.WriteTo(bytes);
1079 
1080     VLOG(jdwp) << "GetRecentAllocations: data created. " << bytes.size();
1081   }
1082   JNIEnv* env = self->GetJniEnv();
1083   jbyteArray result = env->NewByteArray(bytes.size());
1084   if (result != nullptr) {
1085     env->SetByteArrayRegion(result, 0, bytes.size(), reinterpret_cast<const jbyte*>(&bytes[0]));
1086   }
1087   return result;
1088 }
1089 
ThreadStart(Thread * self)1090 void Dbg::DbgThreadLifecycleCallback::ThreadStart(Thread* self) {
1091   Dbg::PostThreadStart(self);
1092 }
1093 
ThreadDeath(Thread * self)1094 void Dbg::DbgThreadLifecycleCallback::ThreadDeath(Thread* self) {
1095   Dbg::PostThreadDeath(self);
1096 }
1097 
1098 }  // namespace art
1099