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 <inttypes.h>
18
19 #include <chrono>
20 #include <map>
21
22 #include <android-base/file.h>
23 #include <android-base/mapped_file.h>
24 #include <android-base/parseint.h>
25 #include <android-base/strings.h>
26 #include <android-base/unique_fd.h>
27 #include <android/log.h>
28 #include <log/log_time.h>
29 #include <log/logprint.h>
30
31 #include "LogBuffer.h"
32 #include "LogStatistics.h"
33 #include "PruneList.h"
34 #include "RecordedLogMessage.h"
35 #include "SerializedLogBuffer.h"
36 #include "SimpleLogBuffer.h"
37
38 using android::base::MappedFile;
39 using android::base::ParseInt;
40 using android::base::ParseUint;
41 using android::base::Split;
42
uidToName(uid_t)43 char* android::uidToName(uid_t) {
44 return nullptr;
45 }
46
GetPrivateDirty()47 static size_t GetPrivateDirty() {
48 // Allocate once and hope that we don't need to reallocate >40000, to prevent heap fragmentation
49 static std::string smaps(40000, '\0');
50 android::base::ReadFileToString("/proc/self/smaps", &smaps);
51
52 size_t result = 0;
53 size_t base = 0;
54 size_t found;
55 while (true) {
56 found = smaps.find("Private_Dirty:", base);
57 if (found == smaps.npos) break;
58
59 found += sizeof("Private_Dirty:");
60
61 result += atoi(&smaps[found]);
62
63 base = found + 1;
64 }
65
66 return result;
67 }
68
GetLogFormat()69 static AndroidLogFormat* GetLogFormat() {
70 static AndroidLogFormat* format = [] {
71 auto* format = android_log_format_new();
72 android_log_setPrintFormat(format, android_log_formatFromString("threadtime"));
73 android_log_setPrintFormat(format, android_log_formatFromString("uid"));
74 return format;
75 }();
76 return format;
77 }
78
PrintMessage(struct log_msg * buf)79 static void PrintMessage(struct log_msg* buf) {
80 bool is_binary =
81 buf->id() == LOG_ID_EVENTS || buf->id() == LOG_ID_STATS || buf->id() == LOG_ID_SECURITY;
82
83 AndroidLogEntry entry;
84 int err;
85 if (is_binary) {
86 char binaryMsgBuf[1024];
87 err = android_log_processBinaryLogBuffer(&buf->entry, &entry, nullptr, binaryMsgBuf,
88 sizeof(binaryMsgBuf));
89 } else {
90 err = android_log_processLogBuffer(&buf->entry, &entry);
91 }
92 if (err < 0) {
93 fprintf(stderr, "Error parsing log message\n");
94 }
95
96 android_log_printLogLine(GetLogFormat(), stdout, &entry);
97 }
98
GetFirstTimeStamp(const MappedFile & recorded_messages)99 static log_time GetFirstTimeStamp(const MappedFile& recorded_messages) {
100 if (sizeof(RecordedLogMessage) >= recorded_messages.size()) {
101 fprintf(stderr, "At least one log message must be present in the input\n");
102 exit(1);
103 }
104
105 auto* meta = reinterpret_cast<RecordedLogMessage*>(recorded_messages.data());
106 return meta->realtime;
107 }
108
BuffersToLogMask(const char * buffers)109 static LogMask BuffersToLogMask(const char* buffers) {
110 if (buffers == nullptr || !strcmp(buffers, "all")) {
111 return kLogMaskAll;
112 }
113 auto string_ids = Split(buffers, ",");
114 LogMask log_mask = 0;
115 for (const auto& string_id : string_ids) {
116 int buffer_id;
117 if (!ParseInt(string_id, &buffer_id, 0, 7)) {
118 fprintf(stderr, "Could not parse buffer_id '%s'\n", string_id.c_str());
119 exit(1);
120 }
121 log_mask |= 1 << buffer_id;
122 }
123 return log_mask;
124 }
125
126 class StdoutWriter : public LogWriter {
127 public:
StdoutWriter()128 StdoutWriter() : LogWriter(0, true) {}
Write(const logger_entry & entry,const char * message)129 bool Write(const logger_entry& entry, const char* message) override {
130 struct log_msg log_msg;
131 log_msg.entry = entry;
132 if (log_msg.entry.len > LOGGER_ENTRY_MAX_PAYLOAD) {
133 fprintf(stderr, "payload too large %" PRIu16, log_msg.entry.len);
134 exit(1);
135 }
136 memcpy(log_msg.msg(), message, log_msg.entry.len);
137
138 PrintMessage(&log_msg);
139
140 return true;
141 }
142
Shutdown()143 void Shutdown() override {
144 fprintf(stderr, "LogWriter::Shutdown() called\n");
145 exit(1);
146 }
147
name() const148 std::string name() const override { return "stdout writer"; }
149 };
150
151 class Operation {
152 public:
~Operation()153 virtual ~Operation() {}
154
Begin()155 virtual void Begin() {}
156 virtual void Log(const RecordedLogMessage& meta, const char* msg) = 0;
End()157 virtual void End() {}
158 };
159
160 class PrintInteresting : public Operation {
161 public:
PrintInteresting(log_time first_log_timestamp)162 PrintInteresting(log_time first_log_timestamp)
163 : stats_simple_{false, false, first_log_timestamp},
164 stats_serialized_{false, true, first_log_timestamp} {}
165
Begin()166 void Begin() override {
167 printf("message_count,simple_main_lines,simple_radio_lines,simple_events_lines,simple_"
168 "system_lines,simple_crash_lines,simple_stats_lines,simple_security_lines,simple_"
169 "kernel_lines,simple_main_size,simple_radio_size,simple_events_size,simple_system_"
170 "size,simple_crash_size,simple_stats_size,simple_security_size,simple_kernel_size,"
171 "simple_main_overhead,simple_radio_overhead,simple_events_overhead,simple_system_"
172 "overhead,simple_crash_overhead,simple_stats_overhead,simple_security_overhead,"
173 "simple_kernel_overhead,simple_main_range,simple_radio_range,simple_events_range,"
174 "simple_system_range,simple_crash_range,simple_stats_range,simple_security_range,"
175 "simple_kernel_range,"
176 "serialized_main_lines,serialized_radio_lines,serialized_events_lines,serialized_"
177 "system_lines,serialized_crash_lines,serialized_stats_lines,serialized_security_"
178 "lines,serialized_"
179 "kernel_lines,serialized_main_size,serialized_radio_size,serialized_events_size,"
180 "serialized_system_"
181 "size,serialized_crash_size,serialized_stats_size,serialized_security_size,"
182 "serialized_kernel_size,"
183 "serialized_main_overhead,serialized_radio_overhead,serialized_events_overhead,"
184 "serialized_system_"
185 "overhead,serialized_crash_overhead,serialized_stats_overhead,serialized_security_"
186 "overhead,"
187 "serialized_kernel_overhead,serialized_main_range,serialized_radio_range,serialized_"
188 "events_range,"
189 "serialized_system_range,serialized_crash_range,serialized_stats_range,serialized_"
190 "security_range,"
191 "serialized_kernel_range\n");
192 }
193
Log(const RecordedLogMessage & meta,const char * msg)194 void Log(const RecordedLogMessage& meta, const char* msg) override {
195 simple_log_buffer_.Log(static_cast<log_id_t>(meta.log_id), meta.realtime, meta.uid,
196 meta.pid, meta.tid, msg, meta.msg_len);
197
198 serialized_log_buffer_.Log(static_cast<log_id_t>(meta.log_id), meta.realtime, meta.uid,
199 meta.pid, meta.tid, msg, meta.msg_len);
200
201 if (num_message_ % 10000 == 0) {
202 printf("%" PRIu64 ",%s,%s\n", num_message_, stats_simple_.ReportInteresting().c_str(),
203 stats_serialized_.ReportInteresting().c_str());
204 }
205
206 num_message_++;
207 }
208
209 private:
210 uint64_t num_message_ = 1;
211
212 LogReaderList reader_list_;
213 LogTags tags_;
214 PruneList prune_list_;
215
216 LogStatistics stats_simple_;
217 SimpleLogBuffer simple_log_buffer_{&reader_list_, &tags_, &stats_simple_};
218
219 LogStatistics stats_serialized_;
220 SerializedLogBuffer serialized_log_buffer_{&reader_list_, &tags_, &stats_serialized_};
221 };
222
223 class SingleBufferOperation : public Operation {
224 public:
SingleBufferOperation(log_time first_log_timestamp,const char * buffer)225 SingleBufferOperation(log_time first_log_timestamp, const char* buffer) {
226 if (!strcmp(buffer, "simple")) {
227 stats_.reset(new LogStatistics{false, false, first_log_timestamp});
228 log_buffer_.reset(new SimpleLogBuffer(&reader_list_, &tags_, stats_.get()));
229 } else if (!strcmp(buffer, "serialized")) {
230 stats_.reset(new LogStatistics{false, true, first_log_timestamp});
231 log_buffer_.reset(new SerializedLogBuffer(&reader_list_, &tags_, stats_.get()));
232 } else {
233 fprintf(stderr, "invalid log buffer type '%s'\n", buffer);
234 abort();
235 }
236 }
237
Log(const RecordedLogMessage & meta,const char * msg)238 void Log(const RecordedLogMessage& meta, const char* msg) override {
239 PreOperation();
240 log_buffer_->Log(static_cast<log_id_t>(meta.log_id), meta.realtime, meta.uid, meta.pid,
241 meta.tid, msg, meta.msg_len);
242
243 Operation();
244
245 num_message_++;
246 }
247
PreOperation()248 virtual void PreOperation() {}
Operation()249 virtual void Operation() {}
250
251 protected:
252 uint64_t num_message_ = 1;
253
254 LogReaderList reader_list_;
255 LogTags tags_;
256 PruneList prune_list_;
257
258 std::unique_ptr<LogStatistics> stats_;
259 std::unique_ptr<LogBuffer> log_buffer_;
260 };
261
262 class PrintMemory : public SingleBufferOperation {
263 public:
PrintMemory(log_time first_log_timestamp,const char * buffer)264 PrintMemory(log_time first_log_timestamp, const char* buffer)
265 : SingleBufferOperation(first_log_timestamp, buffer) {}
266
Operation()267 void Operation() override {
268 if (num_message_ % 100000 == 0) {
269 printf("%" PRIu64 ",%s\n", num_message_,
270 std::to_string(GetPrivateDirty() - baseline_memory_).c_str());
271 }
272 }
273
274 private:
275 size_t baseline_memory_ = GetPrivateDirty();
276 };
277
278 class PrintLogs : public SingleBufferOperation {
279 public:
PrintLogs(log_time first_log_timestamp,const char * buffer,const char * buffers,const char * print_point)280 PrintLogs(log_time first_log_timestamp, const char* buffer, const char* buffers,
281 const char* print_point)
282 : SingleBufferOperation(first_log_timestamp, buffer) {
283 mask_ = BuffersToLogMask(buffers);
284 if (print_point != nullptr) {
285 uint64_t result = 0;
286 if (!ParseUint(print_point, &result)) {
287 fprintf(stderr, "Could not parse print point '%s'\n", print_point);
288 exit(1);
289 }
290 print_point_ = result;
291 }
292 }
293
Operation()294 void Operation() override {
295 if (print_point_ && num_message_ >= *print_point_) {
296 End();
297 exit(0);
298 }
299 }
300
End()301 void End() override {
302 auto lock = std::lock_guard{logd_lock};
303 std::unique_ptr<LogWriter> test_writer(new StdoutWriter());
304 std::unique_ptr<FlushToState> flush_to_state = log_buffer_->CreateFlushToState(1, mask_);
305 log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr);
306
307 auto stats_string = stats_->Format(0, 0, mask_);
308 printf("%s\n", stats_string.c_str());
309 }
310
311 private:
312 LogMask mask_ = kLogMaskAll;
313 std::optional<uint64_t> print_point_;
314 };
315
316 class PrintLatency : public SingleBufferOperation {
317 public:
PrintLatency(log_time first_log_timestamp,const char * buffer)318 PrintLatency(log_time first_log_timestamp, const char* buffer)
319 : SingleBufferOperation(first_log_timestamp, buffer) {}
320
PreOperation()321 void PreOperation() override { operation_start_ = std::chrono::steady_clock::now(); }
322
Operation()323 void Operation() override {
324 auto end = std::chrono::steady_clock::now();
325 auto duration = (end - operation_start_).count();
326 durations_.emplace_back(duration);
327 }
328
End()329 void End() override {
330 std::sort(durations_.begin(), durations_.end());
331 auto q1 = durations_.size() / 4;
332 auto q2 = durations_.size() / 2;
333 auto q3 = 3 * durations_.size() / 4;
334
335 auto p95 = 95 * durations_.size() / 100;
336 auto p99 = 99 * durations_.size() / 100;
337 auto p9999 = 9999 * durations_.size() / 10000;
338
339 printf("q1: %lld q2: %lld q3: %lld p95: %lld p99: %lld p99.99: %lld max: %lld\n",
340 durations_[q1], durations_[q2], durations_[q3], durations_[p95], durations_[p99],
341 durations_[p9999], durations_.back());
342 }
343
344 private:
345 std::chrono::steady_clock::time_point operation_start_;
346 std::vector<long long> durations_;
347 };
348
349 class PrintAllLogs : public SingleBufferOperation {
350 public:
PrintAllLogs(log_time first_log_timestamp,const char * buffer,const char * buffers)351 PrintAllLogs(log_time first_log_timestamp, const char* buffer, const char* buffers)
352 : SingleBufferOperation(first_log_timestamp, buffer) {
353 LogMask mask = BuffersToLogMask(buffers);
354 auto lock = std::unique_lock{logd_lock};
355 std::unique_ptr<LogWriter> stdout_writer(new StdoutWriter());
356 std::unique_ptr<LogReaderThread> log_reader(
357 new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(stdout_writer),
358 false, 0, mask, 0, {}, 1, {}));
359 reader_list_.AddAndRunThread(std::move(log_reader));
360 }
361
Operation()362 void Operation() override {
363 // If the rate of reading logs is slower than the rate of incoming logs, then the reader
364 // thread is disconnected to not overflow log buffers, therefore we artificially slow down
365 // the incoming log rate.
366 usleep(100);
367 }
368
End()369 void End() override {
370 // Release the reader thread.
371 {
372 auto lock = std::lock_guard{logd_lock};
373 reader_list_.running_reader_threads().back()->Release();
374 }
375
376 // Wait until it has deleted itself.
377 while (true) {
378 usleep(500);
379 auto lock = std::lock_guard{logd_lock};
380 if (reader_list_.running_reader_threads().size() == 0) {
381 break;
382 }
383 }
384 }
385 };
386
main(int argc,char ** argv)387 int main(int argc, char** argv) {
388 if (argc < 3) {
389 fprintf(stderr, "Usage: %s FILE OPERATION [BUFFER] [OPTIONS]\n", argv[0]);
390 return 1;
391 }
392
393 if (strcmp(argv[2], "interesting") != 0 && argc < 4) {
394 fprintf(stderr, "Operations other than 'interesting' require a BUFFER argument\n");
395 return 1;
396 }
397
398 int recorded_messages_fd = open(argv[1], O_RDONLY);
399 if (recorded_messages_fd == -1) {
400 fprintf(stderr, "Couldn't open input file\n");
401 return 1;
402 }
403 struct stat fd_stat;
404 if (fstat(recorded_messages_fd, &fd_stat) != 0) {
405 fprintf(stderr, "Couldn't fstat input file\n");
406 return 1;
407 }
408 auto recorded_messages = MappedFile::FromFd(recorded_messages_fd, 0,
409 static_cast<size_t>(fd_stat.st_size), PROT_READ);
410 if (recorded_messages == nullptr) {
411 fprintf(stderr, "Couldn't mmap input file\n");
412 return 1;
413 }
414
415 // LogStatistics typically uses 'now()' to initialize its log range state, but this doesn't work
416 // when replaying older logs, so we instead give it the timestamp from the first log.
417 log_time first_log_timestamp = GetFirstTimeStamp(*recorded_messages);
418
419 std::unique_ptr<Operation> operation;
420 if (!strcmp(argv[2], "interesting")) {
421 operation.reset(new PrintInteresting(first_log_timestamp));
422 } else if (!strcmp(argv[2], "memory_usage")) {
423 operation.reset(new PrintMemory(first_log_timestamp, argv[3]));
424 } else if (!strcmp(argv[2], "latency")) {
425 operation.reset(new PrintLatency(first_log_timestamp, argv[3]));
426 } else if (!strcmp(argv[2], "print_logs")) {
427 operation.reset(new PrintLogs(first_log_timestamp, argv[3], argc > 4 ? argv[4] : nullptr,
428 argc > 5 ? argv[5] : nullptr));
429 } else if (!strcmp(argv[2], "print_all_logs")) {
430 operation.reset(
431 new PrintAllLogs(first_log_timestamp, argv[3], argc > 4 ? argv[4] : nullptr));
432 } else if (!strcmp(argv[2], "nothing")) {
433 operation.reset(new SingleBufferOperation(first_log_timestamp, argv[3]));
434 } else {
435 fprintf(stderr, "unknown operation '%s'\n", argv[2]);
436 return 1;
437 }
438
439 // LogBuffer::Log() won't log without this on host.
440 __android_log_set_minimum_priority(ANDROID_LOG_VERBOSE);
441 // But we still want to suppress messages <= error to not interrupt the rest of the output.
442 __android_log_set_logger([](const struct __android_log_message* log_message) {
443 if (log_message->priority < ANDROID_LOG_ERROR) {
444 return;
445 }
446 __android_log_stderr_logger(log_message);
447 });
448
449 operation->Begin();
450
451 uint64_t read_position = 0;
452 while (read_position + sizeof(RecordedLogMessage) < recorded_messages->size()) {
453 auto* meta =
454 reinterpret_cast<RecordedLogMessage*>(recorded_messages->data() + read_position);
455 if (read_position + sizeof(RecordedLogMessage) + meta->msg_len >=
456 recorded_messages->size()) {
457 break;
458 }
459 char* msg = recorded_messages->data() + read_position + sizeof(RecordedLogMessage);
460 read_position += sizeof(RecordedLogMessage) + meta->msg_len;
461
462 operation->Log(*meta, msg);
463 }
464
465 operation->End();
466
467 return 0;
468 }
469