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