1 /*
2  * Copyright (C) 2020 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 "SerializedLogBuffer.h"
18 
19 #include <sys/prctl.h>
20 
21 #include <limits>
22 
23 #include <android-base/logging.h>
24 #include <android-base/scopeguard.h>
25 
26 #include "LogStatistics.h"
27 #include "SerializedFlushToState.h"
28 
SerializedLogBuffer(LogReaderList * reader_list,LogTags * tags,LogStatistics * stats)29 SerializedLogBuffer::SerializedLogBuffer(LogReaderList* reader_list, LogTags* tags,
30                                          LogStatistics* stats)
31     : reader_list_(reader_list), tags_(tags), stats_(stats) {
32     Init();
33 }
34 
Init()35 void SerializedLogBuffer::Init() {
36     log_id_for_each(i) {
37         if (SetSize(i, __android_logger_get_buffer_size(i))) {
38             SetSize(i, LOG_BUFFER_MIN_SIZE);
39         }
40     }
41 
42     // Release any sleeping reader threads to dump their current content.
43     auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
44     for (const auto& reader_thread : reader_list_->reader_threads()) {
45         reader_thread->triggerReader_Locked();
46     }
47 }
48 
ShouldLog(log_id_t log_id,const char * msg,uint16_t len)49 bool SerializedLogBuffer::ShouldLog(log_id_t log_id, const char* msg, uint16_t len) {
50     if (log_id == LOG_ID_SECURITY) {
51         return true;
52     }
53 
54     int prio = ANDROID_LOG_INFO;
55     const char* tag = nullptr;
56     size_t tag_len = 0;
57     if (IsBinary(log_id)) {
58         int32_t tag_int = MsgToTag(msg, len);
59         tag = tags_->tagToName(tag_int);
60         if (tag) {
61             tag_len = strlen(tag);
62         }
63     } else {
64         prio = *msg;
65         tag = msg + 1;
66         tag_len = strnlen(tag, len - 1);
67     }
68     return __android_log_is_loggable_len(prio, tag, tag_len, ANDROID_LOG_VERBOSE);
69 }
70 
Log(log_id_t log_id,log_time realtime,uid_t uid,pid_t pid,pid_t tid,const char * msg,uint16_t len)71 int SerializedLogBuffer::Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
72                              const char* msg, uint16_t len) {
73     if (log_id >= LOG_ID_MAX || len == 0) {
74         return -EINVAL;
75     }
76 
77     if (!ShouldLog(log_id, msg, len)) {
78         stats_->AddTotal(log_id, len);
79         return -EACCES;
80     }
81 
82     auto sequence = sequence_.fetch_add(1, std::memory_order_relaxed);
83 
84     auto lock = std::lock_guard{lock_};
85 
86     if (logs_[log_id].empty()) {
87         logs_[log_id].push_back(SerializedLogChunk(max_size_[log_id] / 4));
88     }
89 
90     auto total_len = sizeof(SerializedLogEntry) + len;
91     if (!logs_[log_id].back().CanLog(total_len)) {
92         logs_[log_id].back().FinishWriting();
93         logs_[log_id].push_back(SerializedLogChunk(max_size_[log_id] / 4));
94     }
95 
96     auto entry = logs_[log_id].back().Log(sequence, realtime, uid, pid, tid, msg, len);
97     stats_->Add(entry->ToLogStatisticsElement(log_id));
98 
99     MaybePrune(log_id);
100 
101     reader_list_->NotifyNewLog(1 << log_id);
102     return len;
103 }
104 
MaybePrune(log_id_t log_id)105 void SerializedLogBuffer::MaybePrune(log_id_t log_id) {
106     size_t total_size = GetSizeUsed(log_id);
107     size_t after_size = total_size;
108     if (total_size > max_size_[log_id]) {
109         Prune(log_id, total_size - max_size_[log_id], 0);
110         after_size = GetSizeUsed(log_id);
111         LOG(INFO) << "Pruned Logs from log_id: " << log_id << ", previous size: " << total_size
112                   << " after size: " << after_size;
113     }
114 
115     stats_->set_overhead(log_id, after_size);
116 }
117 
RemoveChunkFromStats(log_id_t log_id,SerializedLogChunk & chunk)118 void SerializedLogBuffer::RemoveChunkFromStats(log_id_t log_id, SerializedLogChunk& chunk) {
119     chunk.IncReaderRefCount();
120     int read_offset = 0;
121     while (read_offset < chunk.write_offset()) {
122         auto* entry = chunk.log_entry(read_offset);
123         stats_->Subtract(entry->ToLogStatisticsElement(log_id));
124         read_offset += entry->total_len();
125     }
126     chunk.DecReaderRefCount();
127 }
128 
NotifyReadersOfPrune(log_id_t log_id,const std::list<SerializedLogChunk>::iterator & chunk)129 void SerializedLogBuffer::NotifyReadersOfPrune(
130         log_id_t log_id, const std::list<SerializedLogChunk>::iterator& chunk) {
131     for (const auto& reader_thread : reader_list_->reader_threads()) {
132         auto& state = reinterpret_cast<SerializedFlushToState&>(reader_thread->flush_to_state());
133         state.Prune(log_id, chunk);
134     }
135 }
136 
Prune(log_id_t log_id,size_t bytes_to_free,uid_t uid)137 bool SerializedLogBuffer::Prune(log_id_t log_id, size_t bytes_to_free, uid_t uid) {
138     // Don't prune logs that are newer than the point at which any reader threads are reading from.
139     LogReaderThread* oldest = nullptr;
140     auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
141     for (const auto& reader_thread : reader_list_->reader_threads()) {
142         if (!reader_thread->IsWatching(log_id)) {
143             continue;
144         }
145         if (!oldest || oldest->start() > reader_thread->start() ||
146             (oldest->start() == reader_thread->start() &&
147              reader_thread->deadline().time_since_epoch().count() != 0)) {
148             oldest = reader_thread.get();
149         }
150     }
151 
152     auto& log_buffer = logs_[log_id];
153     auto it = log_buffer.begin();
154     while (it != log_buffer.end()) {
155         if (oldest != nullptr && it->highest_sequence_number() >= oldest->start()) {
156             break;
157         }
158 
159         // Increment ahead of time since we're going to erase this iterator from the list.
160         auto it_to_prune = it++;
161 
162         // The sequence number check ensures that all readers have read all logs in this chunk, but
163         // they may still hold a reference to the chunk to track their last read log_position.
164         // Notify them to delete the reference.
165         NotifyReadersOfPrune(log_id, it_to_prune);
166 
167         if (uid != 0) {
168             // Reorder the log buffer to remove logs from the given UID.  If there are no logs left
169             // in the buffer after the removal, delete it.
170             if (it_to_prune->ClearUidLogs(uid, log_id, stats_)) {
171                 log_buffer.erase(it_to_prune);
172             }
173         } else {
174             size_t buffer_size = it_to_prune->PruneSize();
175             RemoveChunkFromStats(log_id, *it_to_prune);
176             log_buffer.erase(it_to_prune);
177             if (buffer_size >= bytes_to_free) {
178                 return true;
179             }
180             bytes_to_free -= buffer_size;
181         }
182     }
183 
184     // If we've deleted all buffers without bytes_to_free hitting 0, then we're called by Clear()
185     // and should return true.
186     if (it == log_buffer.end()) {
187         return true;
188     }
189 
190     // Otherwise we are stuck due to a reader, so mitigate it.
191     CHECK(oldest != nullptr);
192     KickReader(oldest, log_id, bytes_to_free);
193     return false;
194 }
195 
196 // If the selected reader is blocking our pruning progress, decide on
197 // what kind of mitigation is necessary to unblock the situation.
KickReader(LogReaderThread * reader,log_id_t id,size_t bytes_to_free)198 void SerializedLogBuffer::KickReader(LogReaderThread* reader, log_id_t id, size_t bytes_to_free) {
199     if (bytes_to_free >= max_size_[id]) {  // +100%
200         // A misbehaving or slow reader is dropped if we hit too much memory pressure.
201         LOG(WARNING) << "Kicking blocked reader, " << reader->name()
202                      << ", from LogBuffer::kickMe()";
203         reader->release_Locked();
204     } else if (reader->deadline().time_since_epoch().count() != 0) {
205         // Allow a blocked WRAP deadline reader to trigger and start reporting the log data.
206         reader->triggerReader_Locked();
207     } else {
208         // Tell slow reader to skip entries to catch up.
209         unsigned long prune_rows = bytes_to_free / 300;
210         LOG(WARNING) << "Skipping " << prune_rows << " entries from slow reader, " << reader->name()
211                      << ", from LogBuffer::kickMe()";
212         reader->triggerSkip_Locked(id, prune_rows);
213     }
214 }
215 
CreateFlushToState(uint64_t start,LogMask log_mask)216 std::unique_ptr<FlushToState> SerializedLogBuffer::CreateFlushToState(uint64_t start,
217                                                                       LogMask log_mask) {
218     return std::make_unique<SerializedFlushToState>(start, log_mask);
219 }
220 
FlushTo(LogWriter * writer,FlushToState & abstract_state,const std::function<FilterResult (log_id_t log_id,pid_t pid,uint64_t sequence,log_time realtime)> & filter)221 bool SerializedLogBuffer::FlushTo(
222         LogWriter* writer, FlushToState& abstract_state,
223         const std::function<FilterResult(log_id_t log_id, pid_t pid, uint64_t sequence,
224                                          log_time realtime)>& filter) {
225     auto lock = std::unique_lock{lock_};
226 
227     auto& state = reinterpret_cast<SerializedFlushToState&>(abstract_state);
228     state.InitializeLogs(logs_);
229 
230     while (state.HasUnreadLogs()) {
231         MinHeapElement top = state.PopNextUnreadLog();
232         auto* entry = top.entry;
233         auto log_id = top.log_id;
234 
235         if (entry->sequence() < state.start()) {
236             continue;
237         }
238         state.set_start(entry->sequence());
239 
240         if (!writer->privileged() && entry->uid() != writer->uid()) {
241             continue;
242         }
243 
244         if (filter) {
245             auto ret = filter(log_id, entry->pid(), entry->sequence(), entry->realtime());
246             if (ret == FilterResult::kSkip) {
247                 continue;
248             }
249             if (ret == FilterResult::kStop) {
250                 break;
251             }
252         }
253 
254         lock.unlock();
255         // We never prune logs equal to or newer than any LogReaderThreads' `start` value, so the
256         // `entry` pointer is safe here without the lock
257         if (!entry->Flush(writer, log_id)) {
258             return false;
259         }
260         lock.lock();
261     }
262 
263     state.set_start(state.start() + 1);
264     return true;
265 }
266 
Clear(log_id_t id,uid_t uid)267 bool SerializedLogBuffer::Clear(log_id_t id, uid_t uid) {
268     // Try three times to clear, then disconnect the readers and try one final time.
269     for (int retry = 0; retry < 3; ++retry) {
270         {
271             auto lock = std::lock_guard{lock_};
272             bool prune_success = Prune(id, ULONG_MAX, uid);
273             if (prune_success) {
274                 return true;
275             }
276         }
277         sleep(1);
278     }
279     // Check if it is still busy after the sleep, we try to prune one entry, not another clear run,
280     // so we are looking for the quick side effect of the return value to tell us if we have a
281     // _blocked_ reader.
282     bool busy = false;
283     {
284         auto lock = std::lock_guard{lock_};
285         busy = !Prune(id, 1, uid);
286     }
287     // It is still busy, disconnect all readers.
288     if (busy) {
289         auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
290         for (const auto& reader_thread : reader_list_->reader_threads()) {
291             if (reader_thread->IsWatching(id)) {
292                 LOG(WARNING) << "Kicking blocked reader, " << reader_thread->name()
293                              << ", from LogBuffer::clear()";
294                 reader_thread->release_Locked();
295             }
296         }
297     }
298     auto lock = std::lock_guard{lock_};
299     return Prune(id, ULONG_MAX, uid);
300 }
301 
GetSizeUsed(log_id_t id)302 unsigned long SerializedLogBuffer::GetSizeUsed(log_id_t id) {
303     size_t total_size = 0;
304     for (const auto& chunk : logs_[id]) {
305         total_size += chunk.PruneSize();
306     }
307     return total_size;
308 }
309 
GetSize(log_id_t id)310 unsigned long SerializedLogBuffer::GetSize(log_id_t id) {
311     auto lock = std::lock_guard{lock_};
312     return max_size_[id];
313 }
314 
315 // New SerializedLogChunk objects will be allocated according to the new size, but older one are
316 // unchanged.  MaybePrune() is called on the log buffer to reduce it to an appropriate size if the
317 // new size is lower.
SetSize(log_id_t id,unsigned long size)318 int SerializedLogBuffer::SetSize(log_id_t id, unsigned long size) {
319     // Reasonable limits ...
320     if (!__android_logger_valid_buffer_size(size)) {
321         return -1;
322     }
323 
324     auto lock = std::lock_guard{lock_};
325     max_size_[id] = size;
326 
327     MaybePrune(id);
328 
329     return 0;
330 }
331