blob: 078ac761b37775da9625bd6ba24f0750dcd1b52a [file] [log] [blame]
Florian Mayer07710c52019-09-16 15:53:38 +00001/*
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/stat.h>
27#include <sys/types.h>
28#include <thread>
29
30#include "gc/heap-visit-objects-inl.h"
31#include "gc/heap.h"
32#include "gc/scoped_gc_critical_section.h"
33#include "mirror/object-refvisitor-inl.h"
34#include "nativehelper/scoped_local_ref.h"
35#include "perfetto/trace/interned_data/interned_data.pbzero.h"
36#include "perfetto/trace/profiling/heap_graph.pbzero.h"
37#include "perfetto/trace/profiling/profile_common.pbzero.h"
38#include "perfetto/tracing.h"
39#include "runtime-inl.h"
40#include "runtime_callbacks.h"
41#include "scoped_thread_state_change-inl.h"
42#include "thread_list.h"
43#include "well_known_classes.h"
44
45// There are three threads involved in this:
46// * listener thread: this is idle in the background when this plugin gets loaded, and waits
47// for data on on g_signal_pipe_fds.
48// * signal thread: an arbitrary thread that handles the signal and writes data to
49// g_signal_pipe_fds.
50// * perfetto producer thread: once the signal is received, the app forks. In the newly forked
51// child, the Perfetto Client API spawns a thread to communicate with traced.
52
53namespace perfetto_hprof {
54
55constexpr int kJavaHeapprofdSignal = __SIGRTMIN + 6;
56constexpr time_t kWatchdogTimeoutSec = 120;
57constexpr size_t kObjectsPerPacket = 100;
58constexpr char kByte[1] = {'x'};
59static art::Mutex& GetStateMutex() {
60 static art::Mutex state_mutex("perfetto_hprof_state_mutex", art::LockLevel::kGenericBottomLock);
61 return state_mutex;
62}
63
64static art::ConditionVariable& GetStateCV() {
65 static art::ConditionVariable state_cv("perfetto_hprof_state_cv", GetStateMutex());
66 return state_cv;
67}
68
69static State g_state = State::kUninitialized;
70
71// Pipe to signal from the signal handler into a worker thread that handles the
72// dump requests.
73int g_signal_pipe_fds[2];
74static struct sigaction g_orig_act = {};
75
76uint64_t FindOrAppend(std::map<std::string, uint64_t>* m,
77 const std::string& s) {
78 auto it = m->find(s);
79 if (it == m->end()) {
80 std::tie(it, std::ignore) = m->emplace(s, m->size());
81 }
82 return it->second;
83}
84
85void ArmWatchdogOrDie() {
86 timer_t timerid{};
87 struct sigevent sev {};
88 sev.sigev_notify = SIGEV_SIGNAL;
89 sev.sigev_signo = SIGKILL;
90
91 if (timer_create(CLOCK_MONOTONIC, &sev, &timerid) == -1) {
92 // This only gets called in the child, so we can fatal without impacting
93 // the app.
94 PLOG(FATAL) << "failed to create watchdog timer";
95 }
96
97 struct itimerspec its {};
98 its.it_value.tv_sec = kWatchdogTimeoutSec;
99
100 if (timer_settime(timerid, 0, &its, nullptr) == -1) {
101 // This only gets called in the child, so we can fatal without impacting
102 // the app.
103 PLOG(FATAL) << "failed to arm watchdog timer";
104 }
105}
106
107class JavaHprofDataSource : public perfetto::DataSource<JavaHprofDataSource> {
108 public:
109 // TODO(fmayer): Change Client API and reject configs that do not target
110 // this process.
111 void OnSetup(const SetupArgs&) override {}
112
113 void OnStart(const StartArgs&) override {
114 art::MutexLock lk(art_thread(), GetStateMutex());
115 if (g_state == State::kWaitForStart) {
116 g_state = State::kStart;
117 GetStateCV().Broadcast(art_thread());
118 }
119 }
120
121 void OnStop(const StopArgs&) override {}
122
123 static art::Thread* art_thread() {
124 // TODO(fmayer): Attach the Perfetto producer thread to ART and give it a name. This is
125 // not trivial, we cannot just attach the first time this method is called, because
126 // AttachCurrentThread deadlocks with the ConditionVariable::Wait in WaitForDataSource.
127 //
128 // We should attach the thread as soon as the Client API spawns it, but that needs more
129 // complicated plumbing.
130 return nullptr;
131 }
132
133 private:
134 static art::Thread* self_;
135};
136
137art::Thread* JavaHprofDataSource::self_ = nullptr;
138
139
140void WaitForDataSource(art::Thread* self) {
141 perfetto::TracingInitArgs args;
142 args.backends = perfetto::BackendType::kSystemBackend;
143 perfetto::Tracing::Initialize(args);
144
145 perfetto::DataSourceDescriptor dsd;
146 dsd.set_name("android.java_hprof");
147 JavaHprofDataSource::Register(dsd);
148
149 LOG(INFO) << "waiting for data source";
150
151 art::MutexLock lk(self, GetStateMutex());
152 while (g_state != State::kStart) {
153 GetStateCV().Wait(self);
154 }
155}
156
157class Writer {
158 public:
159 Writer(pid_t parent_pid, JavaHprofDataSource::TraceContext* ctx)
160 : parent_pid_(parent_pid), ctx_(ctx) {}
161
162 perfetto::protos::pbzero::HeapGraph* GetHeapGraph() {
163 if (!heap_graph_ || ++objects_written_ % kObjectsPerPacket == 0) {
164 if (heap_graph_) {
165 heap_graph_->set_continued(true);
166 }
167 Finalize();
168
169 trace_packet_ = ctx_->NewTracePacket();
170 heap_graph_ = trace_packet_->set_heap_graph();
171 heap_graph_->set_pid(parent_pid_);
172 heap_graph_->set_index(index_++);
173 }
174 return heap_graph_;
175 }
176
177 void Finalize() {
178 if (trace_packet_) {
179 trace_packet_->Finalize();
180 }
181 heap_graph_ = nullptr;
182 }
183
184 ~Writer() { Finalize(); }
185
186 private:
187 const pid_t parent_pid_;
188 JavaHprofDataSource::TraceContext* const ctx_;
189
190 perfetto::DataSource<JavaHprofDataSource>::TraceContext::TracePacketHandle
191 trace_packet_;
192 perfetto::protos::pbzero::HeapGraph* heap_graph_ = nullptr;
193
194 uint64_t index_ = 0;
195 size_t objects_written_ = 0;
196};
197
198class ReferredObjectsFinder {
199 public:
200 explicit ReferredObjectsFinder(
201 std::vector<std::pair<std::string, art::mirror::Object*>>* referred_objects)
202 REQUIRES_SHARED(art::Locks::mutator_lock_)
203 : referred_objects_(referred_objects) {}
204
205 // For art::mirror::Object::VisitReferences.
206 void operator()(art::ObjPtr<art::mirror::Object> obj, art::MemberOffset offset,
207 bool is_static) const
208 REQUIRES_SHARED(art::Locks::mutator_lock_) {
209 art::mirror::Object* ref = obj->GetFieldObject<art::mirror::Object>(offset);
210 art::ArtField* field;
211 if (is_static) {
212 field = art::ArtField::FindStaticFieldWithOffset(obj->AsClass(), offset.Uint32Value());
213 } else {
214 field = art::ArtField::FindInstanceFieldWithOffset(obj->GetClass(), offset.Uint32Value());
215 }
216 std::string field_name = "";
217 if (field != nullptr) {
218 field_name = field->PrettyField(/*with_type=*/false);
219 }
220 referred_objects_->emplace_back(std::move(field_name), ref);
221 }
222
223 void VisitRootIfNonNull(art::mirror::CompressedReference<art::mirror::Object>* root
224 ATTRIBUTE_UNUSED) const {}
225 void VisitRoot(art::mirror::CompressedReference<art::mirror::Object>* root
226 ATTRIBUTE_UNUSED) const {}
227
228 private:
229 // We can use a raw Object* pointer here, because there are no concurrent GC threads after the
230 // fork.
231 std::vector<std::pair<std::string, art::mirror::Object*>>* referred_objects_;
232};
233
234void DumpPerfetto(art::Thread* self) {
235 pid_t parent_pid = getpid();
236 LOG(INFO) << "preparing to dump heap for " << parent_pid;
237
238 // Need to take a heap dump while GC isn't running. See the comment in
239 // Heap::VisitObjects(). Also we need the critical section to avoid visiting
240 // the same object twice. See b/34967844.
241 //
242 // We need to do this before the fork, because otherwise it can deadlock
243 // waiting for the GC, as all other threads get terminated by the clone, but
244 // their locks are not released.
245 art::gc::ScopedGCCriticalSection gcs(self, art::gc::kGcCauseHprof,
246 art::gc::kCollectorTypeHprof);
247
248 art::ScopedSuspendAll ssa(__FUNCTION__, /* long_suspend=*/ true);
249
250 pid_t pid = fork();
251 if (pid != 0) {
252 return;
253 }
254
255 // Make sure that this is the first thing we do after forking, so if anything
256 // below hangs, the fork will go away from the watchdog.
257 ArmWatchdogOrDie();
258
259 WaitForDataSource(self);
260
261 JavaHprofDataSource::Trace(
262 [parent_pid](JavaHprofDataSource::TraceContext ctx)
263 NO_THREAD_SAFETY_ANALYSIS {
264 LOG(INFO) << "dumping heap for " << parent_pid;
265 Writer writer(parent_pid, &ctx);
266 // Make sure that intern ID 0 (default proto value for a uint64_t) always maps to ""
267 // (default proto value for a string).
268 std::map<std::string, uint64_t> interned_fields{{"", 0}};
269 std::map<std::string, uint64_t> interned_types{{"", 0}};
270
271 art::Runtime::Current()->GetHeap()->VisitObjectsPaused(
272 [&writer, &interned_types, &interned_fields](
273 art::mirror::Object* obj) REQUIRES_SHARED(art::Locks::mutator_lock_) {
274 perfetto::protos::pbzero::HeapGraphObject* object_proto =
275 writer.GetHeapGraph()->add_objects();
276 object_proto->set_id(reinterpret_cast<uintptr_t>(obj));
277 object_proto->set_type_id(
278 FindOrAppend(&interned_types, obj->PrettyTypeOf()));
279 object_proto->set_self_size(obj->SizeOf());
280
281 std::vector<std::pair<std::string, art::mirror::Object*>>
282 referred_objects;
283 ReferredObjectsFinder objf(&referred_objects);
284 obj->VisitReferences(objf, art::VoidFunctor());
285 for (const auto& p : referred_objects) {
286 object_proto->add_reference_field_id(
287 FindOrAppend(&interned_fields, p.first));
288 object_proto->add_reference_object_id(
289 reinterpret_cast<uintptr_t>(p.second));
290 }
291 });
292
293 for (const auto& p : interned_fields) {
294 const std::string& str = p.first;
295 uint64_t id = p.second;
296
297 perfetto::protos::pbzero::InternedString* field_proto =
298 writer.GetHeapGraph()->add_field_names();
299 field_proto->set_iid(id);
300 field_proto->set_str(
301 reinterpret_cast<const uint8_t*>(str.c_str()), str.size());
302 }
303 for (const auto& p : interned_types) {
304 const std::string& str = p.first;
305 uint64_t id = p.second;
306
307 perfetto::protos::pbzero::InternedString* type_proto =
308 writer.GetHeapGraph()->add_type_names();
309 type_proto->set_iid(id);
310 type_proto->set_str(reinterpret_cast<const uint8_t*>(str.c_str()),
311 str.size());
312 }
313
314 writer.Finalize();
315
316 ctx.Flush([] {
317 {
318 art::MutexLock lk(JavaHprofDataSource::art_thread(), GetStateMutex());
319 g_state = State::kEnd;
320 GetStateCV().Broadcast(JavaHprofDataSource::art_thread());
321 }
322 });
323 });
324
325 art::MutexLock lk(self, GetStateMutex());
326 while (g_state != State::kEnd) {
327 GetStateCV().Wait(self);
328 }
329 LOG(INFO) << "finished dumping heap for " << parent_pid;
330 // Prevent the atexit handlers to run. We do not want to call cleanup
331 // functions the parent process has registered.
332 _exit(0);
333}
334
335// The plugin initialization function.
336extern "C" bool ArtPlugin_Initialize() {
337 if (art::Runtime::Current() == nullptr) {
338 return false;
339 }
340 art::Thread* self = art::Thread::Current();
341 {
342 art::MutexLock lk(self, GetStateMutex());
343 if (g_state != State::kUninitialized) {
344 LOG(ERROR) << "perfetto_hprof already initialized. state: " << g_state;
345 return false;
346 }
347 g_state = State::kWaitForListener;
348 }
349
350 if (pipe(g_signal_pipe_fds) == -1) {
351 PLOG(ERROR) << "Failed to pipe";
352 return false;
353 }
354
355 struct sigaction act = {};
356 act.sa_sigaction = [](int, siginfo_t*, void*) {
357 if (write(g_signal_pipe_fds[1], kByte, sizeof(kByte)) == -1) {
358 PLOG(ERROR) << "Failed to trigger heap dump";
359 }
360 };
361
362 // TODO(fmayer): We can probably use the SignalCatcher thread here to not
363 // have an idle thread.
364 if (sigaction(kJavaHeapprofdSignal, &act, &g_orig_act) != 0) {
365 close(g_signal_pipe_fds[0]);
366 close(g_signal_pipe_fds[1]);
367 PLOG(ERROR) << "Failed to sigaction";
368 return false;
369 }
370
371 std::thread th([] {
372 art::Runtime* runtime = art::Runtime::Current();
373 if (!runtime) {
374 LOG(FATAL_WITHOUT_ABORT) << "no runtime in hprof_listener";
375 return;
376 }
377 if (!runtime->AttachCurrentThread("hprof_listener", /*as_daemon=*/ true,
378 runtime->GetSystemThreadGroup(), /*create_peer=*/ false)) {
379 LOG(ERROR) << "failed to attach thread.";
380 return;
381 }
382 art::Thread* self = art::Thread::Current();
383 if (!self) {
384 LOG(FATAL_WITHOUT_ABORT) << "no thread in hprof_listener";
385 return;
386 }
387 {
388 art::MutexLock lk(self, GetStateMutex());
389 if (g_state == State::kWaitForListener) {
390 g_state = State::kWaitForStart;
391 GetStateCV().Broadcast(self);
392 }
393 }
394 char buf[1];
395 for (;;) {
396 int res;
397 do {
398 res = read(g_signal_pipe_fds[0], buf, sizeof(buf));
399 } while (res == -1 && errno == EINTR);
400
401 if (res <= 0) {
402 if (res == -1) {
403 PLOG(ERROR) << "failed to read";
404 }
405 close(g_signal_pipe_fds[0]);
406 return;
407 }
408
409 perfetto_hprof::DumpPerfetto(self);
410 }
411 });
412 th.detach();
413
414 art::MutexLock lk(art::Thread::Current(), GetStateMutex());
415 while (g_state == State::kWaitForListener) {
416 GetStateCV().Wait(art::Thread::Current());
417 }
418 return true;
419}
420
421extern "C" bool ArtPlugin_Deinitialize() {
422 if (sigaction(kJavaHeapprofdSignal, &g_orig_act, nullptr) != 0) {
423 PLOG(ERROR) << "failed to reset signal handler";
424 // We cannot close the pipe if the signal handler wasn't unregistered,
425 // to avoid receiving SIGPIPE.
426 return false;
427 }
428 close(g_signal_pipe_fds[1]);
429
430 art::Thread* self = art::Thread::Current();
431 art::MutexLock lk(self, GetStateMutex());
432 if (g_state != State::kWaitForListener) {
433 g_state = State::kUninitialized;
434 GetStateCV().Broadcast(self);
435 }
436 return true;
437}
438
439} // namespace perfetto_hprof
440
441namespace perfetto {
442
443PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(perfetto_hprof::JavaHprofDataSource);
444
445}