blob: 79e051b001b434e389b0a23cb0f9020b1ea5744c [file] [log] [blame]
Christopher Ferris7bd01782016-04-20 12:30:58 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <errno.h>
30#include <fcntl.h>
Chia-hung Duanf7e8b172022-11-01 21:37:56 +000031#include <inttypes.h>
Christopher Ferris7bd01782016-04-20 12:30:58 -070032#include <pthread.h>
33#include <stdatomic.h>
34#include <stdint.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <sys/types.h>
38
39#include <mutex>
40
41#include <android-base/stringprintf.h>
42
43#include "Config.h"
Christopher Ferris7bd01782016-04-20 12:30:58 -070044#include "DebugData.h"
45#include "RecordData.h"
Christopher Ferris4da25032018-03-07 13:38:48 -080046#include "debug_disable.h"
47#include "debug_log.h"
Christopher Ferris7bd01782016-04-20 12:30:58 -070048
49RecordEntry::RecordEntry() : tid_(gettid()) {
50}
51
Christopher Ferris06993a62022-09-07 18:19:45 -070052bool ThreadCompleteEntry::Write(int fd) const {
53 return dprintf(fd, "%d: thread_done 0x0\n", tid_) > 0;
Christopher Ferris7bd01782016-04-20 12:30:58 -070054}
55
Chia-hung Duanf7e8b172022-11-01 21:37:56 +000056AllocEntry::AllocEntry(void* pointer, uint64_t start_ns, uint64_t end_ns)
57 : pointer_(pointer), start_ns_(start_ns), end_ns_(end_ns) {}
Christopher Ferris7bd01782016-04-20 12:30:58 -070058
Chia-hung Duanf7e8b172022-11-01 21:37:56 +000059MallocEntry::MallocEntry(void* pointer, size_t size, uint64_t start_ns, uint64_t end_ns)
60 : AllocEntry(pointer, start_ns, end_ns), size_(size) {}
Christopher Ferris7bd01782016-04-20 12:30:58 -070061
Christopher Ferris06993a62022-09-07 18:19:45 -070062bool MallocEntry::Write(int fd) const {
Chia-hung Duanf7e8b172022-11-01 21:37:56 +000063 return dprintf(fd, "%d: malloc %p %zu %" PRIu64 " %" PRIu64 "\n", tid_, pointer_, size_,
64 start_ns_, end_ns_) > 0;
Christopher Ferris7bd01782016-04-20 12:30:58 -070065}
66
Chia-hung Duanf7e8b172022-11-01 21:37:56 +000067FreeEntry::FreeEntry(void* pointer, uint64_t start_ns, uint64_t end_ns)
68 : AllocEntry(pointer, start_ns, end_ns) {}
Christopher Ferris7bd01782016-04-20 12:30:58 -070069
Christopher Ferris06993a62022-09-07 18:19:45 -070070bool FreeEntry::Write(int fd) const {
Chia-hung Duanf7e8b172022-11-01 21:37:56 +000071 return dprintf(fd, "%d: free %p %" PRIu64 " %" PRIu64 "\n", tid_, pointer_, start_ns_, end_ns_) >
72 0;
Christopher Ferris7bd01782016-04-20 12:30:58 -070073}
74
Chia-hung Duanf7e8b172022-11-01 21:37:56 +000075CallocEntry::CallocEntry(void* pointer, size_t nmemb, size_t size, uint64_t start_ns,
76 uint64_t end_ns)
77 : MallocEntry(pointer, size, start_ns, end_ns), nmemb_(nmemb) {}
Christopher Ferris7bd01782016-04-20 12:30:58 -070078
Christopher Ferris06993a62022-09-07 18:19:45 -070079bool CallocEntry::Write(int fd) const {
Chia-hung Duanf7e8b172022-11-01 21:37:56 +000080 return dprintf(fd, "%d: calloc %p %zu %zu %" PRIu64 " %" PRIu64 "\n", tid_, pointer_, nmemb_,
81 size_, start_ns_, end_ns_) > 0;
Christopher Ferris7bd01782016-04-20 12:30:58 -070082}
83
Chia-hung Duanf7e8b172022-11-01 21:37:56 +000084ReallocEntry::ReallocEntry(void* pointer, size_t size, void* old_pointer, uint64_t start_ns,
85 uint64_t end_ns)
86 : MallocEntry(pointer, size, start_ns, end_ns), old_pointer_(old_pointer) {}
Christopher Ferris7bd01782016-04-20 12:30:58 -070087
Christopher Ferris06993a62022-09-07 18:19:45 -070088bool ReallocEntry::Write(int fd) const {
Chia-hung Duanf7e8b172022-11-01 21:37:56 +000089 return dprintf(fd, "%d: realloc %p %p %zu %" PRIu64 " %" PRIu64 "\n", tid_, pointer_,
90 old_pointer_, size_, start_ns_, end_ns_) > 0;
Christopher Ferris7bd01782016-04-20 12:30:58 -070091}
92
Christopher Ferriscae21a92018-02-05 18:14:55 -080093// aligned_alloc, posix_memalign, memalign, pvalloc, valloc all recorded with this class.
Chia-hung Duanf7e8b172022-11-01 21:37:56 +000094MemalignEntry::MemalignEntry(void* pointer, size_t size, size_t alignment, uint64_t start_ns,
95 uint64_t end_ns)
96 : MallocEntry(pointer, size, start_ns, end_ns), alignment_(alignment) {}
Christopher Ferris7bd01782016-04-20 12:30:58 -070097
Christopher Ferris06993a62022-09-07 18:19:45 -070098bool MemalignEntry::Write(int fd) const {
Chia-hung Duanf7e8b172022-11-01 21:37:56 +000099 return dprintf(fd, "%d: memalign %p %zu %zu %" PRIu64 " %" PRIu64 "\n", tid_, pointer_,
100 alignment_, size_, start_ns_, end_ns_) > 0;
Christopher Ferris7bd01782016-04-20 12:30:58 -0700101}
102
103struct ThreadData {
Christopher Ferris4da25032018-03-07 13:38:48 -0800104 ThreadData(RecordData* record_data, ThreadCompleteEntry* entry)
105 : record_data(record_data), entry(entry) {}
Christopher Ferris7bd01782016-04-20 12:30:58 -0700106 RecordData* record_data;
107 ThreadCompleteEntry* entry;
108 size_t count = 0;
109};
110
111static void ThreadKeyDelete(void* data) {
112 ThreadData* thread_data = reinterpret_cast<ThreadData*>(data);
113
114 thread_data->count++;
115
116 // This should be the last time we are called.
117 if (thread_data->count == 4) {
118 ScopedDisableDebugCalls disable;
119
120 thread_data->record_data->AddEntryOnly(thread_data->entry);
121 delete thread_data;
122 } else {
123 pthread_setspecific(thread_data->record_data->key(), data);
124 }
125}
126
Christopher Ferris06993a62022-09-07 18:19:45 -0700127RecordData* RecordData::record_obj_ = nullptr;
128
129void RecordData::WriteData(int, siginfo_t*, void*) {
130 // Dump from here, the function must not allocate so this is safe.
131 record_obj_->WriteEntries();
Christopher Ferris7bd01782016-04-20 12:30:58 -0700132}
133
Christopher Ferris9b88d0a2024-06-13 13:22:02 -0700134void RecordData::WriteEntriesOnExit() {
135 if (record_obj_ == nullptr) return;
136
137 // Append the current pid to the file name to avoid multiple processes
138 // writing to the same file.
139 std::string file(record_obj_->file());
140 file += "." + std::to_string(getpid());
141 record_obj_->WriteEntries(file);
142}
143
Christopher Ferris06993a62022-09-07 18:19:45 -0700144void RecordData::WriteEntries() {
Christopher Ferris9b88d0a2024-06-13 13:22:02 -0700145 WriteEntries(file_);
146}
147
148void RecordData::WriteEntries(const std::string& file) {
Christopher Ferris06993a62022-09-07 18:19:45 -0700149 std::lock_guard<std::mutex> entries_lock(entries_lock_);
150 if (cur_index_ == 0) {
151 info_log("No alloc entries to write.");
Christopher Ferris7bd01782016-04-20 12:30:58 -0700152 return;
153 }
154
Christopher Ferris9b88d0a2024-06-13 13:22:02 -0700155 int dump_fd = open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, 0755);
Christopher Ferris06993a62022-09-07 18:19:45 -0700156 if (dump_fd == -1) {
Christopher Ferris9b88d0a2024-06-13 13:22:02 -0700157 error_log("Cannot create record alloc file %s: %s", file.c_str(), strerror(errno));
Christopher Ferris06993a62022-09-07 18:19:45 -0700158 return;
Christopher Ferris7bd01782016-04-20 12:30:58 -0700159 }
160
Christopher Ferris06993a62022-09-07 18:19:45 -0700161 for (size_t i = 0; i < cur_index_; i++) {
162 if (!entries_[i]->Write(dump_fd)) {
163 error_log("Failed to write record alloc information: %s", strerror(errno));
164 break;
165 }
166 }
167 close(dump_fd);
168
169 // Mark the entries dumped.
170 cur_index_ = 0U;
Christopher Ferris7bd01782016-04-20 12:30:58 -0700171}
172
173RecordData::RecordData() {
174 pthread_key_create(&key_, ThreadKeyDelete);
175}
176
177bool RecordData::Initialize(const Config& config) {
Christopher Ferris06993a62022-09-07 18:19:45 -0700178 record_obj_ = this;
Elliott Hughes3e235912018-02-01 14:21:51 -0800179 struct sigaction64 dump_act = {};
Christopher Ferris06993a62022-09-07 18:19:45 -0700180 dump_act.sa_sigaction = RecordData::WriteData;
Christopher Ferris7bd01782016-04-20 12:30:58 -0700181 dump_act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
Elliott Hughes3e235912018-02-01 14:21:51 -0800182 if (sigaction64(config.record_allocs_signal(), &dump_act, nullptr) != 0) {
Christopher Ferris7bd01782016-04-20 12:30:58 -0700183 error_log("Unable to set up record dump signal function: %s", strerror(errno));
184 return false;
185 }
186 pthread_setspecific(key_, nullptr);
187
Christopher Ferrisc328e442019-04-01 19:31:26 -0700188 if (config.options() & VERBOSE) {
189 info_log("%s: Run: 'kill -%d %d' to dump the allocation records.", getprogname(),
190 config.record_allocs_signal(), getpid());
191 }
Christopher Ferris7bd01782016-04-20 12:30:58 -0700192
Christopher Ferris06993a62022-09-07 18:19:45 -0700193 entries_.resize(config.record_allocs_num_entries());
194 cur_index_ = 0U;
Christopher Ferris9b88d0a2024-06-13 13:22:02 -0700195 file_ = config.record_allocs_file();
Christopher Ferris7bd01782016-04-20 12:30:58 -0700196
197 return true;
198}
199
200RecordData::~RecordData() {
Christopher Ferris7bd01782016-04-20 12:30:58 -0700201 pthread_key_delete(key_);
202}
203
204void RecordData::AddEntryOnly(const RecordEntry* entry) {
Christopher Ferris06993a62022-09-07 18:19:45 -0700205 std::lock_guard<std::mutex> entries_lock(entries_lock_);
206 if (cur_index_ == entries_.size()) {
207 // Maxed out, throw the entry away.
208 return;
209 }
210
211 entries_[cur_index_++].reset(entry);
212 if (cur_index_ == entries_.size()) {
213 info_log("Maximum number of records added, all new operations will be dropped.");
Christopher Ferris7bd01782016-04-20 12:30:58 -0700214 }
215}
216
217void RecordData::AddEntry(const RecordEntry* entry) {
218 void* data = pthread_getspecific(key_);
219 if (data == nullptr) {
220 ThreadData* thread_data = new ThreadData(this, new ThreadCompleteEntry());
221 pthread_setspecific(key_, thread_data);
222 }
223
224 AddEntryOnly(entry);
Christopher Ferris7bd01782016-04-20 12:30:58 -0700225}