1 /*
2 * Copyright (C) 2019 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 #define LOG_TAG "perfetto_hprof"
18
19 #include "perfetto_hprof.h"
20
21 #include <android-base/logging.h>
22 #include <fcntl.h>
23 #include <inttypes.h>
24 #include <sched.h>
25 #include <signal.h>
26 #include <sys/socket.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <sys/un.h>
30 #include <sys/wait.h>
31 #include <thread>
32 #include <time.h>
33
34 #include <type_traits>
35
36 #include "gc/heap-visit-objects-inl.h"
37 #include "gc/heap.h"
38 #include "gc/scoped_gc_critical_section.h"
39 #include "mirror/object-refvisitor-inl.h"
40 #include "nativehelper/scoped_local_ref.h"
41 #include "perfetto/profiling/normalize.h"
42 #include "perfetto/profiling/parse_smaps.h"
43 #include "perfetto/trace/interned_data/interned_data.pbzero.h"
44 #include "perfetto/trace/profiling/heap_graph.pbzero.h"
45 #include "perfetto/trace/profiling/profile_common.pbzero.h"
46 #include "perfetto/trace/profiling/smaps.pbzero.h"
47 #include "perfetto/config/profiling/java_hprof_config.pbzero.h"
48 #include "perfetto/protozero/packed_repeated_fields.h"
49 #include "perfetto/tracing.h"
50 #include "runtime-inl.h"
51 #include "runtime_callbacks.h"
52 #include "scoped_thread_state_change-inl.h"
53 #include "thread_list.h"
54 #include "well_known_classes.h"
55 #include "dex/descriptors_names.h"
56
57 // There are three threads involved in this:
58 // * listener thread: this is idle in the background when this plugin gets loaded, and waits
59 // for data on on g_signal_pipe_fds.
60 // * signal thread: an arbitrary thread that handles the signal and writes data to
61 // g_signal_pipe_fds.
62 // * perfetto producer thread: once the signal is received, the app forks. In the newly forked
63 // child, the Perfetto Client API spawns a thread to communicate with traced.
64
65 namespace perfetto_hprof {
66
67 constexpr int kJavaHeapprofdSignal = __SIGRTMIN + 6;
68 constexpr time_t kWatchdogTimeoutSec = 120;
69 // This needs to be lower than the maximum acceptable chunk size, because this
70 // is checked *before* writing another submessage. We conservatively assume
71 // submessages can be up to 100k here for a 500k chunk size.
72 // DropBox has a 500k chunk limit, and each chunk needs to parse as a proto.
73 constexpr uint32_t kPacketSizeThreshold = 400000;
74 constexpr char kByte[1] = {'x'};
GetStateMutex()75 static art::Mutex& GetStateMutex() {
76 static art::Mutex state_mutex("perfetto_hprof_state_mutex", art::LockLevel::kGenericBottomLock);
77 return state_mutex;
78 }
79
GetStateCV()80 static art::ConditionVariable& GetStateCV() {
81 static art::ConditionVariable state_cv("perfetto_hprof_state_cv", GetStateMutex());
82 return state_cv;
83 }
84
85 static State g_state = State::kUninitialized;
86
87 // Pipe to signal from the signal handler into a worker thread that handles the
88 // dump requests.
89 int g_signal_pipe_fds[2];
90 static struct sigaction g_orig_act = {};
91
92 template <typename T>
FindOrAppend(std::map<T,uint64_t> * m,const T & s)93 uint64_t FindOrAppend(std::map<T, uint64_t>* m, const T& s) {
94 auto it = m->find(s);
95 if (it == m->end()) {
96 std::tie(it, std::ignore) = m->emplace(s, m->size());
97 }
98 return it->second;
99 }
100
ArmWatchdogOrDie()101 void ArmWatchdogOrDie() {
102 timer_t timerid{};
103 struct sigevent sev {};
104 sev.sigev_notify = SIGEV_SIGNAL;
105 sev.sigev_signo = SIGKILL;
106
107 if (timer_create(CLOCK_MONOTONIC, &sev, &timerid) == -1) {
108 // This only gets called in the child, so we can fatal without impacting
109 // the app.
110 PLOG(FATAL) << "failed to create watchdog timer";
111 }
112
113 struct itimerspec its {};
114 its.it_value.tv_sec = kWatchdogTimeoutSec;
115
116 if (timer_settime(timerid, 0, &its, nullptr) == -1) {
117 // This only gets called in the child, so we can fatal without impacting
118 // the app.
119 PLOG(FATAL) << "failed to arm watchdog timer";
120 }
121 }
122
StartsWith(const std::string & str,const std::string & prefix)123 bool StartsWith(const std::string& str, const std::string& prefix) {
124 return str.compare(0, prefix.length(), prefix) == 0;
125 }
126
127 // Sample entries that match one of the following
128 // start with /system/
129 // start with /vendor/
130 // start with /data/app/
131 // contains "extracted in memory from Y", where Y matches any of the above
ShouldSampleSmapsEntry(const perfetto::profiling::SmapsEntry & e)132 bool ShouldSampleSmapsEntry(const perfetto::profiling::SmapsEntry& e) {
133 if (StartsWith(e.pathname, "/system/") || StartsWith(e.pathname, "/vendor/") ||
134 StartsWith(e.pathname, "/data/app/")) {
135 return true;
136 }
137 if (StartsWith(e.pathname, "[anon:")) {
138 if (e.pathname.find("extracted in memory from /system/") != std::string::npos) {
139 return true;
140 }
141 if (e.pathname.find("extracted in memory from /vendor/") != std::string::npos) {
142 return true;
143 }
144 if (e.pathname.find("extracted in memory from /data/app/") != std::string::npos) {
145 return true;
146 }
147 }
148 return false;
149 }
150
CanConnectToSocket(const char * name)151 bool CanConnectToSocket(const char* name) {
152 struct sockaddr_un addr = {};
153 addr.sun_family = AF_UNIX;
154 strncpy(addr.sun_path, name, sizeof(addr.sun_path) - 1);
155 int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
156 if (fd == -1) {
157 PLOG(ERROR) << "failed to create socket";
158 return false;
159 }
160 bool connected = connect(fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) == 0;
161 close(fd);
162 return connected;
163 }
164
165 constexpr size_t kMaxCmdlineSize = 512;
166
167 class JavaHprofDataSource : public perfetto::DataSource<JavaHprofDataSource> {
168 public:
169 constexpr static perfetto::BufferExhaustedPolicy kBufferExhaustedPolicy =
170 perfetto::BufferExhaustedPolicy::kStall;
OnSetup(const SetupArgs & args)171 void OnSetup(const SetupArgs& args) override {
172 // This is on the heap as it triggers -Wframe-larger-than.
173 std::unique_ptr<perfetto::protos::pbzero::JavaHprofConfig::Decoder> cfg(
174 new perfetto::protos::pbzero::JavaHprofConfig::Decoder(
175 args.config->java_hprof_config_raw()));
176
177 if (args.config->enable_extra_guardrails() && !CanConnectToSocket("/dev/socket/heapprofd")) {
178 LOG(ERROR) << "rejecting extra guardrails";
179 enabled_ = false;
180 return;
181 }
182
183 dump_smaps_ = cfg->dump_smaps();
184
185 uint64_t self_pid = static_cast<uint64_t>(getpid());
186 for (auto pid_it = cfg->pid(); pid_it; ++pid_it) {
187 if (*pid_it == self_pid) {
188 enabled_ = true;
189 return;
190 }
191 }
192
193 if (cfg->has_process_cmdline()) {
194 int fd = open("/proc/self/cmdline", O_RDONLY | O_CLOEXEC);
195 if (fd == -1) {
196 PLOG(ERROR) << "failed to open /proc/self/cmdline";
197 return;
198 }
199 char cmdline[kMaxCmdlineSize];
200 ssize_t rd = read(fd, cmdline, sizeof(cmdline) - 1);
201 if (rd == -1) {
202 PLOG(ERROR) << "failed to read /proc/self/cmdline";
203 }
204 close(fd);
205 if (rd == -1) {
206 return;
207 }
208 cmdline[rd] = '\0';
209 char* cmdline_ptr = cmdline;
210 ssize_t sz = perfetto::profiling::NormalizeCmdLine(&cmdline_ptr, static_cast<size_t>(rd + 1));
211 if (sz == -1) {
212 PLOG(ERROR) << "failed to normalize cmdline";
213 }
214 for (auto it = cfg->process_cmdline(); it; ++it) {
215 std::string other = (*it).ToStdString();
216 // Append \0 to make this a C string.
217 other.resize(other.size() + 1);
218 char* other_ptr = &(other[0]);
219 ssize_t other_sz = perfetto::profiling::NormalizeCmdLine(&other_ptr, other.size());
220 if (other_sz == -1) {
221 PLOG(ERROR) << "failed to normalize other cmdline";
222 continue;
223 }
224 if (sz == other_sz && strncmp(cmdline_ptr, other_ptr, static_cast<size_t>(sz)) == 0) {
225 enabled_ = true;
226 return;
227 }
228 }
229 }
230 }
231
dump_smaps()232 bool dump_smaps() { return dump_smaps_; }
enabled()233 bool enabled() { return enabled_; }
234
OnStart(const StartArgs &)235 void OnStart(const StartArgs&) override {
236 if (!enabled()) {
237 return;
238 }
239 art::MutexLock lk(art_thread(), GetStateMutex());
240 if (g_state == State::kWaitForStart) {
241 g_state = State::kStart;
242 GetStateCV().Broadcast(art_thread());
243 }
244 }
245
OnStop(const StopArgs &)246 void OnStop(const StopArgs&) override {}
247
art_thread()248 static art::Thread* art_thread() {
249 // TODO(fmayer): Attach the Perfetto producer thread to ART and give it a name. This is
250 // not trivial, we cannot just attach the first time this method is called, because
251 // AttachCurrentThread deadlocks with the ConditionVariable::Wait in WaitForDataSource.
252 //
253 // We should attach the thread as soon as the Client API spawns it, but that needs more
254 // complicated plumbing.
255 return nullptr;
256 }
257
258 private:
259 bool enabled_ = false;
260 bool dump_smaps_ = false;
261 static art::Thread* self_;
262 };
263
264 art::Thread* JavaHprofDataSource::self_ = nullptr;
265
266
WaitForDataSource(art::Thread * self)267 void WaitForDataSource(art::Thread* self) {
268 perfetto::TracingInitArgs args;
269 args.backends = perfetto::BackendType::kSystemBackend;
270 perfetto::Tracing::Initialize(args);
271
272 perfetto::DataSourceDescriptor dsd;
273 dsd.set_name("android.java_hprof");
274 JavaHprofDataSource::Register(dsd);
275
276 LOG(INFO) << "waiting for data source";
277
278 art::MutexLock lk(self, GetStateMutex());
279 while (g_state != State::kStart) {
280 GetStateCV().Wait(self);
281 }
282 }
283
284 class Writer {
285 public:
Writer(pid_t parent_pid,JavaHprofDataSource::TraceContext * ctx,uint64_t timestamp)286 Writer(pid_t parent_pid, JavaHprofDataSource::TraceContext* ctx, uint64_t timestamp)
287 : parent_pid_(parent_pid), ctx_(ctx), timestamp_(timestamp),
288 last_written_(ctx_->written()) {}
289
290 // Return whether the next call to GetHeapGraph will create a new TracePacket.
will_create_new_packet()291 bool will_create_new_packet() {
292 return !heap_graph_ || ctx_->written() - last_written_ > kPacketSizeThreshold;
293 }
294
GetHeapGraph()295 perfetto::protos::pbzero::HeapGraph* GetHeapGraph() {
296 if (will_create_new_packet()) {
297 CreateNewHeapGraph();
298 }
299 return heap_graph_;
300 }
301
CreateNewHeapGraph()302 void CreateNewHeapGraph() {
303 if (heap_graph_) {
304 heap_graph_->set_continued(true);
305 }
306 Finalize();
307
308 uint64_t written = ctx_->written();
309
310 trace_packet_ = ctx_->NewTracePacket();
311 trace_packet_->set_timestamp(timestamp_);
312 heap_graph_ = trace_packet_->set_heap_graph();
313 heap_graph_->set_pid(parent_pid_);
314 heap_graph_->set_index(index_++);
315
316 last_written_ = written;
317 }
318
Finalize()319 void Finalize() {
320 if (trace_packet_) {
321 trace_packet_->Finalize();
322 }
323 heap_graph_ = nullptr;
324 }
325
~Writer()326 ~Writer() { Finalize(); }
327
328 private:
329 const pid_t parent_pid_;
330 JavaHprofDataSource::TraceContext* const ctx_;
331 const uint64_t timestamp_;
332
333 uint64_t last_written_ = 0;
334
335 perfetto::DataSource<JavaHprofDataSource>::TraceContext::TracePacketHandle
336 trace_packet_;
337 perfetto::protos::pbzero::HeapGraph* heap_graph_ = nullptr;
338
339 uint64_t index_ = 0;
340 };
341
342 class ReferredObjectsFinder {
343 public:
ReferredObjectsFinder(std::vector<std::pair<std::string,art::mirror::Object * >> * referred_objects)344 explicit ReferredObjectsFinder(
345 std::vector<std::pair<std::string, art::mirror::Object*>>* referred_objects)
346 : referred_objects_(referred_objects) {}
347
348 // For art::mirror::Object::VisitReferences.
operator ()(art::ObjPtr<art::mirror::Object> obj,art::MemberOffset offset,bool is_static) const349 void operator()(art::ObjPtr<art::mirror::Object> obj, art::MemberOffset offset,
350 bool is_static) const
351 REQUIRES_SHARED(art::Locks::mutator_lock_) {
352 art::mirror::Object* ref = obj->GetFieldObject<art::mirror::Object>(offset);
353 art::ArtField* field;
354 if (is_static) {
355 field = art::ArtField::FindStaticFieldWithOffset(obj->AsClass(), offset.Uint32Value());
356 } else {
357 field = art::ArtField::FindInstanceFieldWithOffset(obj->GetClass(), offset.Uint32Value());
358 }
359 std::string field_name = "";
360 if (field != nullptr) {
361 field_name = field->PrettyField(/*with_type=*/true);
362 }
363 referred_objects_->emplace_back(std::move(field_name), ref);
364 }
365
VisitRootIfNonNull(art::mirror::CompressedReference<art::mirror::Object> * root ATTRIBUTE_UNUSED) const366 void VisitRootIfNonNull(art::mirror::CompressedReference<art::mirror::Object>* root
367 ATTRIBUTE_UNUSED) const {}
VisitRoot(art::mirror::CompressedReference<art::mirror::Object> * root ATTRIBUTE_UNUSED) const368 void VisitRoot(art::mirror::CompressedReference<art::mirror::Object>* root
369 ATTRIBUTE_UNUSED) const {}
370
371 private:
372 // We can use a raw Object* pointer here, because there are no concurrent GC threads after the
373 // fork.
374 std::vector<std::pair<std::string, art::mirror::Object*>>* referred_objects_;
375 };
376
377 class RootFinder : public art::SingleRootVisitor {
378 public:
RootFinder(std::map<art::RootType,std::vector<art::mirror::Object * >> * root_objects)379 explicit RootFinder(
380 std::map<art::RootType, std::vector<art::mirror::Object*>>* root_objects)
381 : root_objects_(root_objects) {}
382
VisitRoot(art::mirror::Object * root,const art::RootInfo & info)383 void VisitRoot(art::mirror::Object* root, const art::RootInfo& info) override {
384 (*root_objects_)[info.GetType()].emplace_back(root);
385 }
386
387 private:
388 // We can use a raw Object* pointer here, because there are no concurrent GC threads after the
389 // fork.
390 std::map<art::RootType, std::vector<art::mirror::Object*>>* root_objects_;
391 };
392
ToProtoType(art::RootType art_type)393 perfetto::protos::pbzero::HeapGraphRoot::Type ToProtoType(art::RootType art_type) {
394 switch (art_type) {
395 case art::kRootUnknown:
396 return perfetto::protos::pbzero::HeapGraphRoot::ROOT_UNKNOWN;
397 case art::kRootJNIGlobal:
398 return perfetto::protos::pbzero::HeapGraphRoot::ROOT_JNI_GLOBAL;
399 case art::kRootJNILocal:
400 return perfetto::protos::pbzero::HeapGraphRoot::ROOT_JNI_LOCAL;
401 case art::kRootJavaFrame:
402 return perfetto::protos::pbzero::HeapGraphRoot::ROOT_JAVA_FRAME;
403 case art::kRootNativeStack:
404 return perfetto::protos::pbzero::HeapGraphRoot::ROOT_NATIVE_STACK;
405 case art::kRootStickyClass:
406 return perfetto::protos::pbzero::HeapGraphRoot::ROOT_STICKY_CLASS;
407 case art::kRootThreadBlock:
408 return perfetto::protos::pbzero::HeapGraphRoot::ROOT_THREAD_BLOCK;
409 case art::kRootMonitorUsed:
410 return perfetto::protos::pbzero::HeapGraphRoot::ROOT_MONITOR_USED;
411 case art::kRootThreadObject:
412 return perfetto::protos::pbzero::HeapGraphRoot::ROOT_THREAD_OBJECT;
413 case art::kRootInternedString:
414 return perfetto::protos::pbzero::HeapGraphRoot::ROOT_INTERNED_STRING;
415 case art::kRootFinalizing:
416 return perfetto::protos::pbzero::HeapGraphRoot::ROOT_FINALIZING;
417 case art::kRootDebugger:
418 return perfetto::protos::pbzero::HeapGraphRoot::ROOT_DEBUGGER;
419 case art::kRootReferenceCleanup:
420 return perfetto::protos::pbzero::HeapGraphRoot::ROOT_REFERENCE_CLEANUP;
421 case art::kRootVMInternal:
422 return perfetto::protos::pbzero::HeapGraphRoot::ROOT_VM_INTERNAL;
423 case art::kRootJNIMonitor:
424 return perfetto::protos::pbzero::HeapGraphRoot::ROOT_JNI_MONITOR;
425 }
426 }
427
PrettyType(art::mirror::Class * klass)428 std::string PrettyType(art::mirror::Class* klass) NO_THREAD_SAFETY_ANALYSIS {
429 if (klass == nullptr) {
430 return "(raw)";
431 }
432 std::string temp;
433 std::string result(art::PrettyDescriptor(klass->GetDescriptor(&temp)));
434 return result;
435 }
436
DumpSmaps(JavaHprofDataSource::TraceContext * ctx)437 void DumpSmaps(JavaHprofDataSource::TraceContext* ctx) {
438 FILE* smaps = fopen("/proc/self/smaps", "r");
439 if (smaps != nullptr) {
440 auto trace_packet = ctx->NewTracePacket();
441 auto* smaps_packet = trace_packet->set_smaps_packet();
442 smaps_packet->set_pid(getpid());
443 perfetto::profiling::ParseSmaps(smaps,
444 [&smaps_packet](const perfetto::profiling::SmapsEntry& e) {
445 if (ShouldSampleSmapsEntry(e)) {
446 auto* smaps_entry = smaps_packet->add_entries();
447 smaps_entry->set_path(e.pathname);
448 smaps_entry->set_size_kb(e.size_kb);
449 smaps_entry->set_private_dirty_kb(e.private_dirty_kb);
450 smaps_entry->set_swap_kb(e.swap_kb);
451 }
452 });
453 fclose(smaps);
454 } else {
455 PLOG(ERROR) << "failed to open smaps";
456 }
457 }
458
GetObjectId(const art::mirror::Object * obj)459 uint64_t GetObjectId(const art::mirror::Object* obj) {
460 return reinterpret_cast<uint64_t>(obj) / std::alignment_of<art::mirror::Object>::value;
461 }
462
DumpPerfetto(art::Thread * self)463 void DumpPerfetto(art::Thread* self) {
464 pid_t parent_pid = getpid();
465 LOG(INFO) << "preparing to dump heap for " << parent_pid;
466
467 // Need to take a heap dump while GC isn't running. See the comment in
468 // Heap::VisitObjects(). Also we need the critical section to avoid visiting
469 // the same object twice. See b/34967844.
470 //
471 // We need to do this before the fork, because otherwise it can deadlock
472 // waiting for the GC, as all other threads get terminated by the clone, but
473 // their locks are not released.
474 art::gc::ScopedGCCriticalSection gcs(self, art::gc::kGcCauseHprof,
475 art::gc::kCollectorTypeHprof);
476
477 art::ScopedSuspendAll ssa(__FUNCTION__, /* long_suspend=*/ true);
478
479 pid_t pid = fork();
480 if (pid == -1) {
481 // Fork error.
482 PLOG(ERROR) << "fork";
483 return;
484 }
485 if (pid != 0) {
486 // Parent
487 int stat_loc;
488 for (;;) {
489 if (waitpid(pid, &stat_loc, 0) != -1 || errno != EINTR) {
490 break;
491 }
492 }
493 return;
494 }
495
496 // The following code is only executed by the child of the original process.
497 //
498 // Daemon creates a new process that is the grand-child of the original process, and exits.
499 if (daemon(0, 0) == -1) {
500 PLOG(FATAL) << "daemon";
501 }
502
503 // The following code is only executed by the grand-child of the original process.
504
505 // Make sure that this is the first thing we do after forking, so if anything
506 // below hangs, the fork will go away from the watchdog.
507 ArmWatchdogOrDie();
508
509 struct timespec ts = {};
510 if (clock_gettime(CLOCK_BOOTTIME, &ts) != 0) {
511 LOG(FATAL) << "Failed to get boottime.";
512 }
513 uint64_t timestamp = ts.tv_sec * 1000000000LL + ts.tv_nsec;
514
515 WaitForDataSource(self);
516
517 JavaHprofDataSource::Trace(
518 [parent_pid, timestamp](JavaHprofDataSource::TraceContext ctx)
519 NO_THREAD_SAFETY_ANALYSIS {
520 bool dump_smaps;
521 {
522 auto ds = ctx.GetDataSourceLocked();
523 if (!ds || !ds->enabled()) {
524 LOG(INFO) << "skipping irrelevant data source.";
525 return;
526 }
527 dump_smaps = ds->dump_smaps();
528 }
529 LOG(INFO) << "dumping heap for " << parent_pid;
530 if (dump_smaps) {
531 DumpSmaps(&ctx);
532 }
533 Writer writer(parent_pid, &ctx, timestamp);
534 // Make sure that intern ID 0 (default proto value for a uint64_t) always maps to ""
535 // (default proto value for a string).
536 std::map<std::string, uint64_t> interned_fields{{"", 0}};
537 std::map<std::string, uint64_t> interned_locations{{"", 0}};
538 std::map<uintptr_t, uint64_t> interned_classes{{0, 0}};
539
540 std::map<art::RootType, std::vector<art::mirror::Object*>> root_objects;
541 RootFinder rcf(&root_objects);
542 art::Runtime::Current()->VisitRoots(&rcf);
543 std::unique_ptr<protozero::PackedVarInt> object_ids(
544 new protozero::PackedVarInt);
545 for (const auto& p : root_objects) {
546 const art::RootType root_type = p.first;
547 const std::vector<art::mirror::Object*>& children = p.second;
548 perfetto::protos::pbzero::HeapGraphRoot* root_proto =
549 writer.GetHeapGraph()->add_roots();
550 root_proto->set_root_type(ToProtoType(root_type));
551 for (art::mirror::Object* obj : children) {
552 if (writer.will_create_new_packet()) {
553 root_proto->set_object_ids(*object_ids);
554 object_ids->Reset();
555 root_proto = writer.GetHeapGraph()->add_roots();
556 root_proto->set_root_type(ToProtoType(root_type));
557 }
558 object_ids->Append(GetObjectId(obj));
559 }
560 root_proto->set_object_ids(*object_ids);
561 object_ids->Reset();
562 }
563
564 std::unique_ptr<protozero::PackedVarInt> reference_field_ids(
565 new protozero::PackedVarInt);
566 std::unique_ptr<protozero::PackedVarInt> reference_object_ids(
567 new protozero::PackedVarInt);
568
569 art::Runtime::Current()->GetHeap()->VisitObjectsPaused(
570 [&writer, &interned_fields, &interned_locations,
571 &reference_field_ids, &reference_object_ids, &interned_classes](
572 art::mirror::Object* obj) REQUIRES_SHARED(art::Locks::mutator_lock_) {
573 if (obj->IsClass()) {
574 art::mirror::Class* klass = obj->AsClass().Ptr();
575 perfetto::protos::pbzero::HeapGraphType* type_proto =
576 writer.GetHeapGraph()->add_types();
577 type_proto->set_id(FindOrAppend(&interned_classes,
578 reinterpret_cast<uintptr_t>(klass)));
579 type_proto->set_class_name(PrettyType(klass));
580 type_proto->set_location_id(FindOrAppend(&interned_locations,
581 klass->GetLocation()));
582 }
583
584 art::mirror::Class* klass = obj->GetClass();
585 uintptr_t class_ptr = reinterpret_cast<uintptr_t>(klass);
586 // We need to synethesize a new type for Class<Foo>, which does not exist
587 // in the runtime. Otherwise, all the static members of all classes would be
588 // attributed to java.lang.Class.
589 if (klass->IsClassClass()) {
590 CHECK(obj->IsClass());
591 perfetto::protos::pbzero::HeapGraphType* type_proto =
592 writer.GetHeapGraph()->add_types();
593 // All pointers are at least multiples of two, so this way we can make sure
594 // we are not colliding with a real class.
595 class_ptr = reinterpret_cast<uintptr_t>(obj) | 1;
596 auto class_id = FindOrAppend(&interned_classes, class_ptr);
597 type_proto->set_id(class_id);
598 type_proto->set_class_name(obj->PrettyTypeOf());
599 type_proto->set_location_id(FindOrAppend(&interned_locations,
600 obj->AsClass()->GetLocation()));
601 }
602
603 auto class_id = FindOrAppend(&interned_classes, class_ptr);
604
605 perfetto::protos::pbzero::HeapGraphObject* object_proto =
606 writer.GetHeapGraph()->add_objects();
607 object_proto->set_id(GetObjectId(obj));
608 object_proto->set_type_id(class_id);
609 object_proto->set_self_size(obj->SizeOf());
610
611 std::vector<std::pair<std::string, art::mirror::Object*>>
612 referred_objects;
613 ReferredObjectsFinder objf(&referred_objects);
614 obj->VisitReferences(objf, art::VoidFunctor());
615 for (const auto& p : referred_objects) {
616 reference_field_ids->Append(FindOrAppend(&interned_fields, p.first));
617 reference_object_ids->Append(GetObjectId(p.second));
618 }
619 object_proto->set_reference_field_id(*reference_field_ids);
620 object_proto->set_reference_object_id(*reference_object_ids);
621 reference_field_ids->Reset();
622 reference_object_ids->Reset();
623 });
624
625 for (const auto& p : interned_fields) {
626 const std::string& str = p.first;
627 uint64_t id = p.second;
628
629 perfetto::protos::pbzero::InternedString* field_proto =
630 writer.GetHeapGraph()->add_field_names();
631 field_proto->set_iid(id);
632 field_proto->set_str(
633 reinterpret_cast<const uint8_t*>(str.c_str()), str.size());
634 }
635 for (const auto& p : interned_locations) {
636 const std::string& str = p.first;
637 uint64_t id = p.second;
638
639 perfetto::protos::pbzero::InternedString* location_proto =
640 writer.GetHeapGraph()->add_location_names();
641 location_proto->set_iid(id);
642 location_proto->set_str(reinterpret_cast<const uint8_t*>(str.c_str()),
643 str.size());
644 }
645
646 writer.Finalize();
647
648 ctx.Flush([] {
649 {
650 art::MutexLock lk(JavaHprofDataSource::art_thread(), GetStateMutex());
651 g_state = State::kEnd;
652 GetStateCV().Broadcast(JavaHprofDataSource::art_thread());
653 }
654 });
655 });
656
657 art::MutexLock lk(self, GetStateMutex());
658 while (g_state != State::kEnd) {
659 GetStateCV().Wait(self);
660 }
661 LOG(INFO) << "finished dumping heap for " << parent_pid;
662 // Prevent the atexit handlers to run. We do not want to call cleanup
663 // functions the parent process has registered.
664 _exit(0);
665 }
666
667 // The plugin initialization function.
ArtPlugin_Initialize()668 extern "C" bool ArtPlugin_Initialize() {
669 if (art::Runtime::Current() == nullptr) {
670 return false;
671 }
672 art::Thread* self = art::Thread::Current();
673 {
674 art::MutexLock lk(self, GetStateMutex());
675 if (g_state != State::kUninitialized) {
676 LOG(ERROR) << "perfetto_hprof already initialized. state: " << g_state;
677 return false;
678 }
679 g_state = State::kWaitForListener;
680 }
681
682 if (pipe2(g_signal_pipe_fds, O_CLOEXEC) == -1) {
683 PLOG(ERROR) << "Failed to pipe";
684 return false;
685 }
686
687 struct sigaction act = {};
688 act.sa_flags = SA_SIGINFO | SA_RESTART;
689 act.sa_sigaction = [](int, siginfo_t*, void*) {
690 if (write(g_signal_pipe_fds[1], kByte, sizeof(kByte)) == -1) {
691 PLOG(ERROR) << "Failed to trigger heap dump";
692 }
693 };
694
695 // TODO(fmayer): We can probably use the SignalCatcher thread here to not
696 // have an idle thread.
697 if (sigaction(kJavaHeapprofdSignal, &act, &g_orig_act) != 0) {
698 close(g_signal_pipe_fds[0]);
699 close(g_signal_pipe_fds[1]);
700 PLOG(ERROR) << "Failed to sigaction";
701 return false;
702 }
703
704 std::thread th([] {
705 art::Runtime* runtime = art::Runtime::Current();
706 if (!runtime) {
707 LOG(FATAL_WITHOUT_ABORT) << "no runtime in perfetto_hprof_listener";
708 return;
709 }
710 if (!runtime->AttachCurrentThread("perfetto_hprof_listener", /*as_daemon=*/ true,
711 runtime->GetSystemThreadGroup(), /*create_peer=*/ false)) {
712 LOG(ERROR) << "failed to attach thread.";
713 {
714 art::MutexLock lk(nullptr, GetStateMutex());
715 g_state = State::kUninitialized;
716 GetStateCV().Broadcast(nullptr);
717 }
718
719 return;
720 }
721 art::Thread* self = art::Thread::Current();
722 if (!self) {
723 LOG(FATAL_WITHOUT_ABORT) << "no thread in perfetto_hprof_listener";
724 return;
725 }
726 {
727 art::MutexLock lk(self, GetStateMutex());
728 if (g_state == State::kWaitForListener) {
729 g_state = State::kWaitForStart;
730 GetStateCV().Broadcast(self);
731 }
732 }
733 char buf[1];
734 for (;;) {
735 int res;
736 do {
737 res = read(g_signal_pipe_fds[0], buf, sizeof(buf));
738 } while (res == -1 && errno == EINTR);
739
740 if (res <= 0) {
741 if (res == -1) {
742 PLOG(ERROR) << "failed to read";
743 }
744 close(g_signal_pipe_fds[0]);
745 return;
746 }
747
748 perfetto_hprof::DumpPerfetto(self);
749 }
750 });
751 th.detach();
752
753 return true;
754 }
755
ArtPlugin_Deinitialize()756 extern "C" bool ArtPlugin_Deinitialize() {
757 if (sigaction(kJavaHeapprofdSignal, &g_orig_act, nullptr) != 0) {
758 PLOG(ERROR) << "failed to reset signal handler";
759 // We cannot close the pipe if the signal handler wasn't unregistered,
760 // to avoid receiving SIGPIPE.
761 return false;
762 }
763 close(g_signal_pipe_fds[1]);
764
765 art::Thread* self = art::Thread::Current();
766 art::MutexLock lk(self, GetStateMutex());
767 // Wait until after the thread was registered to the runtime. This is so
768 // we do not attempt to register it with the runtime after it had been torn
769 // down (ArtPlugin_Deinitialize gets called in the Runtime dtor).
770 while (g_state == State::kWaitForListener) {
771 GetStateCV().Wait(art::Thread::Current());
772 }
773 g_state = State::kUninitialized;
774 GetStateCV().Broadcast(self);
775 return true;
776 }
777
778 } // namespace perfetto_hprof
779
780 namespace perfetto {
781
782 PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(perfetto_hprof::JavaHprofDataSource);
783
784 }
785