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