• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
43 #include "Config.h"
44 #include "DebugData.h"
45 #include "RecordData.h"
46 #include "debug_disable.h"
47 #include "debug_log.h"
48 
RecordEntry()49 RecordEntry::RecordEntry() : tid_(gettid()) {
50 }
51 
Write(int fd) const52 bool ThreadCompleteEntry::Write(int fd) const {
53   return dprintf(fd, "%d: thread_done 0x0\n", tid_) > 0;
54 }
55 
AllocEntry(void * pointer,uint64_t start_ns,uint64_t end_ns)56 AllocEntry::AllocEntry(void* pointer, uint64_t start_ns, uint64_t end_ns)
57     : pointer_(pointer), start_ns_(start_ns), end_ns_(end_ns) {}
58 
MallocEntry(void * pointer,size_t size,uint64_t start_ns,uint64_t end_ns)59 MallocEntry::MallocEntry(void* pointer, size_t size, uint64_t start_ns, uint64_t end_ns)
60     : AllocEntry(pointer, start_ns, end_ns), size_(size) {}
61 
Write(int fd) const62 bool MallocEntry::Write(int fd) const {
63   return dprintf(fd, "%d: malloc %p %zu %" PRIu64 " %" PRIu64 "\n", tid_, pointer_, size_,
64                  start_ns_, end_ns_) > 0;
65 }
66 
FreeEntry(void * pointer,uint64_t start_ns,uint64_t end_ns)67 FreeEntry::FreeEntry(void* pointer, uint64_t start_ns, uint64_t end_ns)
68     : AllocEntry(pointer, start_ns, end_ns) {}
69 
Write(int fd) const70 bool FreeEntry::Write(int fd) const {
71   return dprintf(fd, "%d: free %p %" PRIu64 " %" PRIu64 "\n", tid_, pointer_, start_ns_, end_ns_) >
72          0;
73 }
74 
CallocEntry(void * pointer,size_t nmemb,size_t size,uint64_t start_ns,uint64_t end_ns)75 CallocEntry::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) {}
78 
Write(int fd) const79 bool CallocEntry::Write(int fd) const {
80   return dprintf(fd, "%d: calloc %p %zu %zu %" PRIu64 " %" PRIu64 "\n", tid_, pointer_, nmemb_,
81                  size_, start_ns_, end_ns_) > 0;
82 }
83 
ReallocEntry(void * pointer,size_t size,void * old_pointer,uint64_t start_ns,uint64_t end_ns)84 ReallocEntry::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) {}
87 
Write(int fd) const88 bool ReallocEntry::Write(int fd) const {
89   return dprintf(fd, "%d: realloc %p %p %zu %" PRIu64 " %" PRIu64 "\n", tid_, pointer_,
90                  old_pointer_, size_, start_ns_, end_ns_) > 0;
91 }
92 
93 // aligned_alloc, posix_memalign, memalign, pvalloc, valloc all recorded with this class.
MemalignEntry(void * pointer,size_t size,size_t alignment,uint64_t start_ns,uint64_t end_ns)94 MemalignEntry::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) {}
97 
Write(int fd) const98 bool MemalignEntry::Write(int fd) const {
99   return dprintf(fd, "%d: memalign %p %zu %zu %" PRIu64 " %" PRIu64 "\n", tid_, pointer_,
100                  alignment_, size_, start_ns_, end_ns_) > 0;
101 }
102 
103 struct ThreadData {
ThreadDataThreadData104   ThreadData(RecordData* record_data, ThreadCompleteEntry* entry)
105       : record_data(record_data), entry(entry) {}
106   RecordData* record_data;
107   ThreadCompleteEntry* entry;
108   size_t count = 0;
109 };
110 
ThreadKeyDelete(void * data)111 static 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 
127 RecordData* RecordData::record_obj_ = nullptr;
128 
WriteData(int,siginfo_t *,void *)129 void RecordData::WriteData(int, siginfo_t*, void*) {
130   // Dump from here, the function must not allocate so this is safe.
131   record_obj_->WriteEntries();
132 }
133 
WriteEntries()134 void RecordData::WriteEntries() {
135   std::lock_guard<std::mutex> entries_lock(entries_lock_);
136   if (cur_index_ == 0) {
137     info_log("No alloc entries to write.");
138     return;
139   }
140 
141   int dump_fd =
142       open(dump_file_.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, 0755);
143   if (dump_fd == -1) {
144     error_log("Cannot create record alloc file %s: %s", dump_file_.c_str(), strerror(errno));
145     return;
146   }
147 
148   for (size_t i = 0; i < cur_index_; i++) {
149     if (!entries_[i]->Write(dump_fd)) {
150       error_log("Failed to write record alloc information: %s", strerror(errno));
151       break;
152     }
153   }
154   close(dump_fd);
155 
156   // Mark the entries dumped.
157   cur_index_ = 0U;
158 }
159 
RecordData()160 RecordData::RecordData() {
161   pthread_key_create(&key_, ThreadKeyDelete);
162 }
163 
Initialize(const Config & config)164 bool RecordData::Initialize(const Config& config) {
165   record_obj_ = this;
166   struct sigaction64 dump_act = {};
167   dump_act.sa_sigaction = RecordData::WriteData;
168   dump_act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
169   if (sigaction64(config.record_allocs_signal(), &dump_act, nullptr) != 0) {
170     error_log("Unable to set up record dump signal function: %s", strerror(errno));
171     return false;
172   }
173   pthread_setspecific(key_, nullptr);
174 
175   if (config.options() & VERBOSE) {
176     info_log("%s: Run: 'kill -%d %d' to dump the allocation records.", getprogname(),
177              config.record_allocs_signal(), getpid());
178   }
179 
180   entries_.resize(config.record_allocs_num_entries());
181   cur_index_ = 0U;
182   dump_file_ = config.record_allocs_file();
183 
184   return true;
185 }
186 
~RecordData()187 RecordData::~RecordData() {
188   pthread_key_delete(key_);
189 }
190 
AddEntryOnly(const RecordEntry * entry)191 void RecordData::AddEntryOnly(const RecordEntry* entry) {
192   std::lock_guard<std::mutex> entries_lock(entries_lock_);
193   if (cur_index_ == entries_.size()) {
194     // Maxed out, throw the entry away.
195     return;
196   }
197 
198   entries_[cur_index_++].reset(entry);
199   if (cur_index_ == entries_.size()) {
200     info_log("Maximum number of records added, all new operations will be dropped.");
201   }
202 }
203 
AddEntry(const RecordEntry * entry)204 void RecordData::AddEntry(const RecordEntry* entry) {
205   void* data = pthread_getspecific(key_);
206   if (data == nullptr) {
207     ThreadData* thread_data = new ThreadData(this, new ThreadCompleteEntry());
208     pthread_setspecific(key_, thread_data);
209   }
210 
211   AddEntryOnly(entry);
212 }
213