aboutsummaryrefslogtreecommitdiff
path: root/src/profiling/memory/main.cc
blob: f0bb47b39ab728414ef83545f6b221dcf86431c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <unistd.h>
#include <array>
#include <memory>
#include <vector>

#include <signal.h>

#include "perfetto/ext/base/event_fd.h"
#include "perfetto/ext/base/getopt.h"
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/unix_socket.h"
#include "perfetto/ext/base/watchdog.h"
#include "perfetto/ext/tracing/ipc/default_socket.h"
#include "src/profiling/memory/heapprofd_producer.h"
#include "src/profiling/memory/java_hprof_producer.h"
#include "src/profiling/memory/wire_protocol.h"

#include "perfetto/ext/base/unix_task_runner.h"

// TODO(rsavitski): the task runner watchdog spawns a thread (normally for
// tracking cpu/mem usage) that we don't strictly need.

namespace perfetto {
namespace profiling {
namespace {

int StartCentralHeapprofd();

int GetListeningSocket() {
  const char* sock_fd = getenv(kHeapprofdSocketEnvVar);
  if (sock_fd == nullptr)
    PERFETTO_FATAL("Did not inherit socket from init.");
  char* end;
  int raw_fd = static_cast<int>(strtol(sock_fd, &end, 10));
  if (*end != '\0')
    PERFETTO_FATAL("Invalid %s. Expected decimal integer.",
                   kHeapprofdSocketEnvVar);
  return raw_fd;
}

base::EventFd* g_dump_evt = nullptr;

int HeapprofdMain(int argc, char** argv) {
  bool cleanup_crash = false;

  enum { kCleanupCrash = 256, kTargetPid, kTargetCmd, kInheritFd };
  static option long_options[] = {
      {"cleanup-after-crash", no_argument, nullptr, kCleanupCrash},
      {nullptr, 0, nullptr, 0}};
  int c;
  while ((c = getopt_long(argc, argv, "", long_options, nullptr)) != -1) {
    switch (c) {
      case kCleanupCrash:
        cleanup_crash = true;
        break;
    }
  }

  if (cleanup_crash) {
    PERFETTO_LOG(
        "Recovering from crash: unsetting heapprofd system properties. "
        "Expect SELinux denials for unrelated properties.");
    SystemProperties::ResetHeapprofdProperties();
    PERFETTO_LOG(
        "Finished unsetting heapprofd system properties. "
        "SELinux denials about properties are unexpected after "
        "this point.");
    return 0;
  }

  // start as a central daemon.
  return StartCentralHeapprofd();
}

int StartCentralHeapprofd() {
  // We set this up before launching any threads, so we do not have to use a
  // std::atomic for g_dump_evt.
  g_dump_evt = new base::EventFd();

  base::UnixTaskRunner task_runner;
  base::Watchdog::GetInstance()->Start();  // crash on exceedingly long tasks
  HeapprofdProducer producer(HeapprofdMode::kCentral, &task_runner,
                             /* exit_when_done= */ false);

  int listening_raw_socket = GetListeningSocket();
  auto listening_socket = base::UnixSocket::Listen(
      base::ScopedFile(listening_raw_socket), &producer.socket_delegate(),
      &task_runner, base::SockFamily::kUnix, base::SockType::kStream);

  struct sigaction action = {};
  action.sa_handler = [](int) { g_dump_evt->Notify(); };
  // Allow to trigger a full dump by sending SIGUSR1 to heapprofd.
  // This will allow manually deciding when to dump on userdebug.
  PERFETTO_CHECK(sigaction(SIGUSR1, &action, nullptr) == 0);
  task_runner.AddFileDescriptorWatch(g_dump_evt->fd(), [&producer] {
    g_dump_evt->Clear();
    producer.DumpAll();
  });
  producer.ConnectWithRetries(GetProducerSocket());
  // TODO(fmayer): Create one producer that manages both heapprofd and Java
  // producers, so we do not have two connections to traced.
  JavaHprofProducer java_producer(&task_runner);
  java_producer.ConnectWithRetries(GetProducerSocket());
  task_runner.Run();
  return 0;
}

}  // namespace
}  // namespace profiling
}  // namespace perfetto

int main(int argc, char** argv) {
  return perfetto::profiling::HeapprofdMain(argc, argv);
}