1 /*
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>
31 #include <inttypes.h>
32 #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 #include <memory_trace/MemoryTrace.h>
43
44 #include "Config.h"
45 #include "DebugData.h"
46 #include "Nanotime.h"
47 #include "RecordData.h"
48 #include "debug_disable.h"
49 #include "debug_log.h"
50
51 struct ThreadData {
ThreadDataThreadData52 ThreadData(RecordData* record_data) : record_data(record_data) {}
53
54 RecordData* record_data = nullptr;
55 size_t count = 0;
56 };
57
ThreadKeyDelete(void * data)58 static void ThreadKeyDelete(void* data) {
59 ThreadData* thread_data = reinterpret_cast<ThreadData*>(data);
60
61 thread_data->count++;
62
63 // This should be the last time we are called.
64 if (thread_data->count == 4) {
65 ScopedDisableDebugCalls disable;
66
67 thread_data->record_data->AddEntryOnly(memory_trace::Entry{
68 .tid = gettid(), .type = memory_trace::THREAD_DONE, .end_ns = Nanotime()});
69 delete thread_data;
70 } else {
71 pthread_setspecific(thread_data->record_data->key(), data);
72 }
73 }
74
75 RecordData* RecordData::record_obj_ = nullptr;
76
WriteData(int,siginfo_t *,void *)77 void RecordData::WriteData(int, siginfo_t*, void*) {
78 // Dump from here, the function must not allocate so this is safe.
79 record_obj_->WriteEntries();
80 }
81
WriteEntriesOnExit()82 void RecordData::WriteEntriesOnExit() {
83 if (record_obj_ == nullptr) return;
84
85 // Append the current pid to the file name to avoid multiple processes
86 // writing to the same file.
87 std::string file(record_obj_->file());
88 file += "." + std::to_string(getpid());
89 record_obj_->WriteEntries(file);
90 }
91
WriteEntries()92 void RecordData::WriteEntries() {
93 WriteEntries(file_);
94 }
95
WriteEntries(const std::string & file)96 void RecordData::WriteEntries(const std::string& file) {
97 std::lock_guard<std::mutex> entries_lock(entries_lock_);
98 if (cur_index_ == 0) {
99 info_log("No alloc entries to write.");
100 return;
101 }
102
103 int dump_fd = open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, 0755);
104 if (dump_fd == -1) {
105 error_log("Cannot create record alloc file %s: %s", file.c_str(), strerror(errno));
106 return;
107 }
108
109 for (size_t i = 0; i < cur_index_; i++) {
110 if (!memory_trace::WriteEntryToFd(dump_fd, entries_[i])) {
111 error_log("Failed to write record alloc information: %s", strerror(errno));
112 break;
113 }
114 }
115 close(dump_fd);
116
117 // Mark the entries dumped.
118 cur_index_ = 0U;
119 }
120
RecordData()121 RecordData::RecordData() {
122 pthread_key_create(&key_, ThreadKeyDelete);
123 }
124
Initialize(const Config & config)125 bool RecordData::Initialize(const Config& config) {
126 record_obj_ = this;
127 struct sigaction64 dump_act = {};
128 dump_act.sa_sigaction = RecordData::WriteData;
129 dump_act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
130 if (sigaction64(config.record_allocs_signal(), &dump_act, nullptr) != 0) {
131 error_log("Unable to set up record dump signal function: %s", strerror(errno));
132 return false;
133 }
134 pthread_setspecific(key_, nullptr);
135
136 if (config.options() & VERBOSE) {
137 info_log("%s: Run: 'kill -%d %d' to dump the allocation records.", getprogname(),
138 config.record_allocs_signal(), getpid());
139 }
140
141 entries_.resize(config.record_allocs_num_entries());
142 cur_index_ = 0U;
143 file_ = config.record_allocs_file();
144
145 return true;
146 }
147
~RecordData()148 RecordData::~RecordData() {
149 pthread_key_delete(key_);
150 }
151
AddEntryOnly(const memory_trace::Entry & entry)152 void RecordData::AddEntryOnly(const memory_trace::Entry& entry) {
153 std::lock_guard<std::mutex> entries_lock(entries_lock_);
154 if (cur_index_ == entries_.size()) {
155 // Maxed out, throw the entry away.
156 return;
157 }
158
159 entries_[cur_index_++] = entry;
160 if (cur_index_ == entries_.size()) {
161 info_log("Maximum number of records added, all new operations will be dropped.");
162 }
163 }
164
AddEntry(const memory_trace::Entry & entry)165 void RecordData::AddEntry(const memory_trace::Entry& entry) {
166 void* data = pthread_getspecific(key_);
167 if (data == nullptr) {
168 ThreadData* thread_data = new ThreadData(this);
169 pthread_setspecific(key_, thread_data);
170 }
171
172 AddEntryOnly(entry);
173 }
174