blob: 09e44d98afbac0eb7b0d8f81dd77d4bd18c16fa9 [file] [log] [blame]
Tom Cherryfb150dd2020-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 Cherry0a0115f2020-06-22 08:28:45 -070019#include <sys/prctl.h>
20
Tom Cherryfb150dd2020-05-13 09:28:37 -070021#include <limits>
Tom Cherryfb150dd2020-05-13 09:28:37 -070022
23#include <android-base/logging.h>
24#include <android-base/scopeguard.h>
25
Tom Cherry80228952020-08-05 12:14:45 -070026#include "LogSize.h"
Tom Cherryfb150dd2020-05-13 09:28:37 -070027#include "LogStatistics.h"
28#include "SerializedFlushToState.h"
29
30SerializedLogBuffer::SerializedLogBuffer(LogReaderList* reader_list, LogTags* tags,
31 LogStatistics* stats)
32 : reader_list_(reader_list), tags_(tags), stats_(stats) {
33 Init();
34}
35
Tom Cherryfb150dd2020-05-13 09:28:37 -070036void SerializedLogBuffer::Init() {
37 log_id_for_each(i) {
Tom Cherry80228952020-08-05 12:14:45 -070038 if (!SetSize(i, GetBufferSizeFromProperties(i))) {
39 SetSize(i, kLogBufferMinSize);
Tom Cherryfb150dd2020-05-13 09:28:37 -070040 }
41 }
42
43 // Release any sleeping reader threads to dump their current content.
Tom Cherryc5c9eba2020-10-06 10:22:35 -070044 auto lock = std::lock_guard{logd_lock};
Tom Cherryfb150dd2020-05-13 09:28:37 -070045 for (const auto& reader_thread : reader_list_->reader_threads()) {
Tom Cherryc5c9eba2020-10-06 10:22:35 -070046 reader_thread->TriggerReader();
Tom Cherryfb150dd2020-05-13 09:28:37 -070047 }
48}
49
50bool SerializedLogBuffer::ShouldLog(log_id_t log_id, const char* msg, uint16_t len) {
51 if (log_id == LOG_ID_SECURITY) {
52 return true;
53 }
54
55 int prio = ANDROID_LOG_INFO;
56 const char* tag = nullptr;
57 size_t tag_len = 0;
58 if (IsBinary(log_id)) {
59 int32_t tag_int = MsgToTag(msg, len);
60 tag = tags_->tagToName(tag_int);
61 if (tag) {
62 tag_len = strlen(tag);
63 }
64 } else {
65 prio = *msg;
66 tag = msg + 1;
67 tag_len = strnlen(tag, len - 1);
68 }
69 return __android_log_is_loggable_len(prio, tag, tag_len, ANDROID_LOG_VERBOSE);
70}
71
72int SerializedLogBuffer::Log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
73 const char* msg, uint16_t len) {
74 if (log_id >= LOG_ID_MAX || len == 0) {
75 return -EINVAL;
76 }
77
Tom Cherry8c778e62020-08-12 09:36:15 -070078 if (len > LOGGER_ENTRY_MAX_PAYLOAD) {
79 len = LOGGER_ENTRY_MAX_PAYLOAD;
80 }
81
Tom Cherryfb150dd2020-05-13 09:28:37 -070082 if (!ShouldLog(log_id, msg, len)) {
83 stats_->AddTotal(log_id, len);
84 return -EACCES;
85 }
86
87 auto sequence = sequence_.fetch_add(1, std::memory_order_relaxed);
88
Tom Cherryc85ab852020-10-28 17:04:57 -070089 pending_write_ = true;
90 {
91 auto lock = std::lock_guard{logd_lock};
Tom Cherryfb150dd2020-05-13 09:28:37 -070092
Tom Cherryc85ab852020-10-28 17:04:57 -070093 if (logs_[log_id].empty()) {
94 logs_[log_id].push_back(SerializedLogChunk(max_size_[log_id] / 4));
95 }
96
97 auto total_len = sizeof(SerializedLogEntry) + len;
98 if (!logs_[log_id].back().CanLog(total_len)) {
99 logs_[log_id].back().FinishWriting();
100 logs_[log_id].push_back(SerializedLogChunk(max_size_[log_id] / 4));
101 }
102
103 auto entry = logs_[log_id].back().Log(sequence, realtime, uid, pid, tid, msg, len);
104 stats_->Add(entry->ToLogStatisticsElement(log_id));
105
106 MaybePrune(log_id);
107
108 reader_list_->NotifyNewLog(1 << log_id);
Tom Cherryfb150dd2020-05-13 09:28:37 -0700109 }
Tom Cherryc85ab852020-10-28 17:04:57 -0700110 pending_write_ = false;
111 FutexWake(&pending_write_);
Tom Cherryfb150dd2020-05-13 09:28:37 -0700112
Tom Cherryfb150dd2020-05-13 09:28:37 -0700113 return len;
114}
115
116void SerializedLogBuffer::MaybePrune(log_id_t log_id) {
Tom Cherrya3d6aa62020-06-19 12:21:21 -0700117 size_t total_size = GetSizeUsed(log_id);
118 size_t after_size = total_size;
Tom Cherryfb150dd2020-05-13 09:28:37 -0700119 if (total_size > max_size_[log_id]) {
120 Prune(log_id, total_size - max_size_[log_id], 0);
Tom Cherrya3d6aa62020-06-19 12:21:21 -0700121 after_size = GetSizeUsed(log_id);
Tom Cherryb5687442020-09-28 13:04:15 -0700122 LOG(VERBOSE) << "Pruned Logs from log_id: " << log_id << ", previous size: " << total_size
123 << " after size: " << after_size;
Tom Cherryfb150dd2020-05-13 09:28:37 -0700124 }
Tom Cherrya3d6aa62020-06-19 12:21:21 -0700125
126 stats_->set_overhead(log_id, after_size);
Tom Cherryfb150dd2020-05-13 09:28:37 -0700127}
128
Tom Cherry4a89fb72020-07-09 12:12:48 -0700129void SerializedLogBuffer::RemoveChunkFromStats(log_id_t log_id, SerializedLogChunk& chunk) {
130 chunk.IncReaderRefCount();
131 int read_offset = 0;
132 while (read_offset < chunk.write_offset()) {
133 auto* entry = chunk.log_entry(read_offset);
134 stats_->Subtract(entry->ToLogStatisticsElement(log_id));
135 read_offset += entry->total_len();
Tom Cherry0a0115f2020-06-22 08:28:45 -0700136 }
Tom Cherryb8c967e2020-07-16 20:46:14 -0700137 chunk.DecReaderRefCount();
Tom Cherryfb150dd2020-05-13 09:28:37 -0700138}
139
140void SerializedLogBuffer::NotifyReadersOfPrune(
141 log_id_t log_id, const std::list<SerializedLogChunk>::iterator& chunk) {
142 for (const auto& reader_thread : reader_list_->reader_threads()) {
143 auto& state = reinterpret_cast<SerializedFlushToState&>(reader_thread->flush_to_state());
144 state.Prune(log_id, chunk);
145 }
146}
147
Tom Cherry8c778e62020-08-12 09:36:15 -0700148void SerializedLogBuffer::Prune(log_id_t log_id, size_t bytes_to_free, uid_t uid) {
Tom Cherryfb150dd2020-05-13 09:28:37 -0700149 auto& log_buffer = logs_[log_id];
Tom Cherryfb150dd2020-05-13 09:28:37 -0700150 auto it = log_buffer.begin();
151 while (it != log_buffer.end()) {
Tom Cherry8c778e62020-08-12 09:36:15 -0700152 for (const auto& reader_thread : reader_list_->reader_threads()) {
153 if (!reader_thread->IsWatching(log_id)) {
154 continue;
155 }
156
157 if (reader_thread->deadline().time_since_epoch().count() != 0) {
158 // Always wake up wrapped readers when pruning. 'Wrapped' readers are an
159 // optimization that allows the reader to wait until logs starting at a specified
160 // time stamp are about to be pruned. This is error-prone however, since if that
161 // timestamp is about to be pruned, the reader is not likely to read the messages
162 // fast enough to not back-up logd. Instead, we can achieve an nearly-as-efficient
163 // but not error-prune batching effect by waking the reader whenever any chunk is
164 // about to be pruned.
Tom Cherryc5c9eba2020-10-06 10:22:35 -0700165 reader_thread->TriggerReader();
Tom Cherry8c778e62020-08-12 09:36:15 -0700166 }
167
168 // Some readers may be still reading from this log chunk, log a warning that they are
169 // about to lose logs.
170 // TODO: We should forcefully disconnect the reader instead, such that the reader itself
171 // has an indication that they've lost logs.
172 if (reader_thread->start() <= it->highest_sequence_number()) {
173 LOG(WARNING) << "Skipping entries from slow reader, " << reader_thread->name()
174 << ", from LogBuffer::Prune()";
175 }
Tom Cherryfb150dd2020-05-13 09:28:37 -0700176 }
177
Tom Cherry4a89fb72020-07-09 12:12:48 -0700178 // Increment ahead of time since we're going to erase this iterator from the list.
Tom Cherryfb150dd2020-05-13 09:28:37 -0700179 auto it_to_prune = it++;
180
Tom Cherry8c778e62020-08-12 09:36:15 -0700181 // Readers may have a reference to the chunk to track their last read log_position.
Tom Cherryfb150dd2020-05-13 09:28:37 -0700182 // Notify them to delete the reference.
183 NotifyReadersOfPrune(log_id, it_to_prune);
184
185 if (uid != 0) {
186 // Reorder the log buffer to remove logs from the given UID. If there are no logs left
187 // in the buffer after the removal, delete it.
188 if (it_to_prune->ClearUidLogs(uid, log_id, stats_)) {
189 log_buffer.erase(it_to_prune);
190 }
191 } else {
192 size_t buffer_size = it_to_prune->PruneSize();
Tom Cherry4a89fb72020-07-09 12:12:48 -0700193 RemoveChunkFromStats(log_id, *it_to_prune);
194 log_buffer.erase(it_to_prune);
Tom Cherryfb150dd2020-05-13 09:28:37 -0700195 if (buffer_size >= bytes_to_free) {
Tom Cherry8c778e62020-08-12 09:36:15 -0700196 return;
Tom Cherryfb150dd2020-05-13 09:28:37 -0700197 }
198 bytes_to_free -= buffer_size;
199 }
200 }
Tom Cherryfb150dd2020-05-13 09:28:37 -0700201}
202
203std::unique_ptr<FlushToState> SerializedLogBuffer::CreateFlushToState(uint64_t start,
204 LogMask log_mask) {
Tom Cherryc5c9eba2020-10-06 10:22:35 -0700205 return std::make_unique<SerializedFlushToState>(start, log_mask, logs_);
Tom Cherry154b63a2020-10-05 11:35:59 -0700206}
207
Tom Cherryfb150dd2020-05-13 09:28:37 -0700208bool SerializedLogBuffer::FlushTo(
209 LogWriter* writer, FlushToState& abstract_state,
210 const std::function<FilterResult(log_id_t log_id, pid_t pid, uint64_t sequence,
211 log_time realtime)>& filter) {
Tom Cherryfb150dd2020-05-13 09:28:37 -0700212 auto& state = reinterpret_cast<SerializedFlushToState&>(abstract_state);
Tom Cherryfb150dd2020-05-13 09:28:37 -0700213
Tom Cherryc85ab852020-10-28 17:04:57 -0700214 while (true) {
215 if (pending_write_) {
216 logd_lock.unlock();
217 FutexWait(&pending_write_, true);
218 logd_lock.lock();
219 }
220
221 if (!state.HasUnreadLogs()) {
222 break;
223 }
Tom Cherry87a17342020-09-18 15:32:32 -0700224 LogWithId top = state.PopNextUnreadLog();
Tom Cherryfb150dd2020-05-13 09:28:37 -0700225 auto* entry = top.entry;
226 auto log_id = top.log_id;
227
228 if (entry->sequence() < state.start()) {
229 continue;
230 }
231 state.set_start(entry->sequence());
232
233 if (!writer->privileged() && entry->uid() != writer->uid()) {
234 continue;
235 }
236
237 if (filter) {
238 auto ret = filter(log_id, entry->pid(), entry->sequence(), entry->realtime());
239 if (ret == FilterResult::kSkip) {
240 continue;
241 }
242 if (ret == FilterResult::kStop) {
243 break;
244 }
245 }
246
Tom Cherry8c778e62020-08-12 09:36:15 -0700247 // We copy the log entry such that we can flush it without the lock. We never block pruning
248 // waiting for this Flush() to complete.
249 constexpr size_t kMaxEntrySize = sizeof(*entry) + LOGGER_ENTRY_MAX_PAYLOAD + 1;
250 unsigned char entry_copy[kMaxEntrySize] __attribute__((uninitialized));
251 CHECK_LT(entry->msg_len(), LOGGER_ENTRY_MAX_PAYLOAD + 1);
252 memcpy(entry_copy, entry, sizeof(*entry) + entry->msg_len());
Tom Cherryc5c9eba2020-10-06 10:22:35 -0700253 logd_lock.unlock();
Tom Cherry8c778e62020-08-12 09:36:15 -0700254
255 if (!reinterpret_cast<SerializedLogEntry*>(entry_copy)->Flush(writer, log_id)) {
Tom Cherryc5c9eba2020-10-06 10:22:35 -0700256 logd_lock.lock();
Tom Cherryfb150dd2020-05-13 09:28:37 -0700257 return false;
258 }
Tom Cherry8c778e62020-08-12 09:36:15 -0700259
Tom Cherryc5c9eba2020-10-06 10:22:35 -0700260 logd_lock.lock();
Tom Cherryfb150dd2020-05-13 09:28:37 -0700261 }
262
263 state.set_start(state.start() + 1);
264 return true;
265}
266
267bool SerializedLogBuffer::Clear(log_id_t id, uid_t uid) {
Tom Cherryc5c9eba2020-10-06 10:22:35 -0700268 auto lock = std::lock_guard{logd_lock};
Tom Cherry8c778e62020-08-12 09:36:15 -0700269 Prune(id, ULONG_MAX, uid);
270
271 // Clearing SerializedLogBuffer never waits for readers and therefore is always successful.
272 return true;
Tom Cherryfb150dd2020-05-13 09:28:37 -0700273}
274
Tom Cherry80228952020-08-05 12:14:45 -0700275size_t SerializedLogBuffer::GetSizeUsed(log_id_t id) {
Tom Cherrya3d6aa62020-06-19 12:21:21 -0700276 size_t total_size = 0;
277 for (const auto& chunk : logs_[id]) {
278 total_size += chunk.PruneSize();
279 }
280 return total_size;
281}
282
Tom Cherry80228952020-08-05 12:14:45 -0700283size_t SerializedLogBuffer::GetSize(log_id_t id) {
Tom Cherryc5c9eba2020-10-06 10:22:35 -0700284 auto lock = std::lock_guard{logd_lock};
Tom Cherrya3d6aa62020-06-19 12:21:21 -0700285 return max_size_[id];
Tom Cherryfb150dd2020-05-13 09:28:37 -0700286}
287
288// New SerializedLogChunk objects will be allocated according to the new size, but older one are
289// unchanged. MaybePrune() is called on the log buffer to reduce it to an appropriate size if the
290// new size is lower.
Tom Cherry80228952020-08-05 12:14:45 -0700291bool SerializedLogBuffer::SetSize(log_id_t id, size_t size) {
Tom Cherryfb150dd2020-05-13 09:28:37 -0700292 // Reasonable limits ...
Tom Cherry80228952020-08-05 12:14:45 -0700293 if (!IsValidBufferSize(size)) {
294 return false;
Tom Cherryfb150dd2020-05-13 09:28:37 -0700295 }
296
Tom Cherryc5c9eba2020-10-06 10:22:35 -0700297 auto lock = std::lock_guard{logd_lock};
Tom Cherrya3d6aa62020-06-19 12:21:21 -0700298 max_size_[id] = size;
Tom Cherryfb150dd2020-05-13 09:28:37 -0700299
300 MaybePrune(id);
301
Tom Cherry80228952020-08-05 12:14:45 -0700302 return true;
Tom Cherryfb150dd2020-05-13 09:28:37 -0700303}