blob: 991006e34710f3ba6cd1f5fbdd059572354da349 [file] [log] [blame]
Florian Mayerb64d6b12018-08-30 10:46:30 -07001/*
2 * Copyright (C) 2018 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 <stdlib.h>
Florian Mayerb85a9382018-09-27 13:59:01 +010018#include <array>
Florian Mayerb64d6b12018-08-30 10:46:30 -070019#include <memory>
Florian Mayerb85a9382018-09-27 13:59:01 +010020#include <vector>
Florian Mayerb64d6b12018-08-30 10:46:30 -070021
22#include "src/ipc/unix_socket.h"
Florian Mayerb85a9382018-09-27 13:59:01 +010023#include "src/profiling/memory/bounded_queue.h"
Florian Mayerb64d6b12018-08-30 10:46:30 -070024#include "src/profiling/memory/socket_listener.h"
25
26#include "perfetto/base/unix_task_runner.h"
27
28namespace perfetto {
29namespace {
30
Florian Mayerb85a9382018-09-27 13:59:01 +010031constexpr size_t kUnwinderQueueSize = 1000;
32constexpr size_t kBookkeepingQueueSize = 1000;
33constexpr size_t kUnwinderThreads = 5;
34
35// We create kUnwinderThreads unwinding threads and one bookeeping thread.
36// The bookkeeping thread is singleton in order to avoid expensive and
37// complicated synchronisation in the bookkeeping.
38//
39// We wire up the system by creating BoundedQueues between the threads. The main
40// thread runs the TaskRunner driving the SocketListener. The unwinding thread
41// takes the data received by the SocketListener and if it is a malloc does
42// stack unwinding, and if it is a free just forwards the content of the record
43// to the bookkeeping thread.
44//
45// +--------------+
46// |SocketListener|
47// +------+-------+
48// |
49// +--UnwindingRecord -+
50// | |
51// +--------v-------+ +-------v--------+
52// |Unwinding Thread| |Unwinding Thread|
53// +--------+-------+ +-------+--------+
54// | |
55// +-BookkeepingRecord +
56// |
57// +--------v---------+
58// |Bookkeeping Thread|
59// +------------------+
Florian Mayerb64d6b12018-08-30 10:46:30 -070060int HeapprofdMain(int argc, char** argv) {
Florian Mayerb85a9382018-09-27 13:59:01 +010061 GlobalCallstackTrie callsites;
Florian Mayerb64d6b12018-08-30 10:46:30 -070062 std::unique_ptr<ipc::UnixSocket> sock;
63
Florian Mayerb85a9382018-09-27 13:59:01 +010064 BoundedQueue<BookkeepingRecord> callsites_queue(kBookkeepingQueueSize);
65 std::thread bookkeeping_thread(
66 [&callsites_queue] { BookkeepingMainLoop(&callsites_queue); });
67
68 std::array<BoundedQueue<UnwindingRecord>, kUnwinderThreads> unwinder_queues;
69 for (size_t i = 0; i < kUnwinderThreads; ++i)
70 unwinder_queues[i].SetSize(kUnwinderQueueSize);
71 std::vector<std::thread> unwinding_threads;
72 unwinding_threads.reserve(kUnwinderThreads);
73 for (size_t i = 0; i < kUnwinderThreads; ++i) {
74 unwinding_threads.emplace_back([&unwinder_queues, &callsites_queue, i] {
75 UnwindingMainLoop(&unwinder_queues[i], &callsites_queue);
76 });
77 }
78
79 auto on_record_received = [&unwinder_queues](UnwindingRecord r) {
80 unwinder_queues[static_cast<size_t>(r.pid) % kUnwinderThreads].Add(
81 std::move(r));
82 };
83 SocketListener listener(std::move(on_record_received), &callsites);
84
Florian Mayerb64d6b12018-08-30 10:46:30 -070085 base::UnixTaskRunner read_task_runner;
86 if (argc == 2) {
87 // Allow to be able to manually specify the socket to listen on
88 // for testing and sideloading purposes.
89 sock = ipc::UnixSocket::Listen(argv[1], &listener, &read_task_runner);
90 } else if (argc == 1) {
91 // When running as a service launched by init on Android, the socket
92 // is created by init and passed to the application using an environment
93 // variable.
94 const char* sock_fd = getenv("ANDROID_SOCKET_heapprofd");
95 if (sock_fd == nullptr)
96 PERFETTO_FATAL(
97 "No argument given and environment variable ANDROID_SOCKET_heapprof "
98 "is unset.");
99 char* end;
100 int raw_fd = static_cast<int>(strtol(sock_fd, &end, 10));
101 if (*end != '\0')
102 PERFETTO_FATAL(
103 "Invalid ANDROID_SOCKET_heapprofd. Expected decimal integer.");
104 sock = ipc::UnixSocket::Listen(base::ScopedFile(raw_fd), &listener,
105 &read_task_runner);
106 } else {
107 PERFETTO_FATAL("Invalid number of arguments. %s [SOCKET]", argv[0]);
108 }
109
110 if (sock->last_error() != 0)
111 PERFETTO_FATAL("Failed to initialize socket: %s",
112 strerror(sock->last_error()));
113
114 read_task_runner.Run();
115 return 0;
116}
117} // namespace
118} // namespace perfetto
119
120int main(int argc, char** argv) {
121 return perfetto::HeapprofdMain(argc, argv);
122}