Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 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 | |
Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 31 | #include "LogBuffer.h" |
| 32 | #include "LogStatistics.h" |
Elliott Hughes | 225e5c6 | 2022-09-16 23:01:59 +0000 | [diff] [blame] | 33 | #include "PruneList.h" |
Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 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 | |
Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 43 | char* android::uidToName(uid_t) { |
| 44 | return nullptr; |
| 45 | } |
| 46 | |
| 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 | |
| 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 | |
| 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 | |
Elliott Hughes | daf6399 | 2021-11-16 18:45:06 -0800 | [diff] [blame] | 96 | android_log_printLogLine(GetLogFormat(), stdout, &entry); |
Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 97 | } |
| 98 | |
| 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 | |
Tom Cherry | 303c715 | 2020-07-31 10:45:46 -0700 | [diff] [blame] | 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 | |
Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 126 | class StdoutWriter : public LogWriter { |
| 127 | public: |
| 128 | StdoutWriter() : LogWriter(0, true) {} |
| 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 | |
Tom Cherry | 303c715 | 2020-07-31 10:45:46 -0700 | [diff] [blame] | 143 | void Shutdown() override { |
| 144 | fprintf(stderr, "LogWriter::Shutdown() called\n"); |
| 145 | exit(1); |
| 146 | } |
| 147 | |
Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 148 | std::string name() const override { return "stdout writer"; } |
| 149 | }; |
| 150 | |
| 151 | class Operation { |
| 152 | public: |
| 153 | virtual ~Operation() {} |
| 154 | |
| 155 | virtual void Begin() {} |
| 156 | virtual void Log(const RecordedLogMessage& meta, const char* msg) = 0; |
| 157 | virtual void End() {} |
| 158 | }; |
| 159 | |
| 160 | class PrintInteresting : public Operation { |
| 161 | public: |
| 162 | PrintInteresting(log_time first_log_timestamp) |
| 163 | : stats_simple_{false, false, first_log_timestamp}, |
Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 164 | stats_serialized_{false, true, first_log_timestamp} {} |
| 165 | |
| 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," |
Elliott Hughes | 225e5c6 | 2022-09-16 23:01:59 +0000 | [diff] [blame] | 175 | "simple_kernel_range," |
| 176 | "serialized_main_lines,serialized_radio_lines,serialized_events_lines,serialized_" |
Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 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 | |
| 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 | |
Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 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) { |
Elliott Hughes | 225e5c6 | 2022-09-16 23:01:59 +0000 | [diff] [blame] | 202 | printf("%" PRIu64 ",%s,%s\n", num_message_, stats_simple_.ReportInteresting().c_str(), |
Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 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 | |
Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 219 | LogStatistics stats_serialized_; |
| 220 | SerializedLogBuffer serialized_log_buffer_{&reader_list_, &tags_, &stats_serialized_}; |
| 221 | }; |
| 222 | |
| 223 | class SingleBufferOperation : public Operation { |
| 224 | public: |
| 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())); |
Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 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 | |
| 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 | |
| 248 | virtual void PreOperation() {} |
| 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: |
| 264 | PrintMemory(log_time first_log_timestamp, const char* buffer) |
| 265 | : SingleBufferOperation(first_log_timestamp, buffer) {} |
| 266 | |
| 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: |
| 280 | PrintLogs(log_time first_log_timestamp, const char* buffer, const char* buffers, |
| 281 | const char* print_point) |
| 282 | : SingleBufferOperation(first_log_timestamp, buffer) { |
Tom Cherry | 303c715 | 2020-07-31 10:45:46 -0700 | [diff] [blame] | 283 | mask_ = BuffersToLogMask(buffers); |
Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 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 | |
| 294 | void Operation() override { |
| 295 | if (print_point_ && num_message_ >= *print_point_) { |
| 296 | End(); |
| 297 | exit(0); |
| 298 | } |
| 299 | } |
| 300 | |
Tom Cherry | 303c715 | 2020-07-31 10:45:46 -0700 | [diff] [blame] | 301 | void End() override { |
Tom Cherry | a33c2c0 | 2020-10-06 10:22:35 -0700 | [diff] [blame] | 302 | auto lock = std::lock_guard{logd_lock}; |
Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 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: |
| 318 | PrintLatency(log_time first_log_timestamp, const char* buffer) |
| 319 | : SingleBufferOperation(first_log_timestamp, buffer) {} |
| 320 | |
| 321 | void PreOperation() override { operation_start_ = std::chrono::steady_clock::now(); } |
| 322 | |
| 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 | |
Tom Cherry | 303c715 | 2020-07-31 10:45:46 -0700 | [diff] [blame] | 329 | void End() override { |
Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 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 | |
Tom Cherry | 303c715 | 2020-07-31 10:45:46 -0700 | [diff] [blame] | 349 | class PrintAllLogs : public SingleBufferOperation { |
| 350 | public: |
| 351 | PrintAllLogs(log_time first_log_timestamp, const char* buffer, const char* buffers) |
| 352 | : SingleBufferOperation(first_log_timestamp, buffer) { |
| 353 | LogMask mask = BuffersToLogMask(buffers); |
Tom Cherry | a33c2c0 | 2020-10-06 10:22:35 -0700 | [diff] [blame] | 354 | auto lock = std::unique_lock{logd_lock}; |
Tom Cherry | 303c715 | 2020-07-31 10:45:46 -0700 | [diff] [blame] | 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, {})); |
Wenhao Wang | b1f6c6c | 2021-11-19 16:42:43 -0800 | [diff] [blame] | 359 | reader_list_.AddAndRunThread(std::move(log_reader)); |
Tom Cherry | 303c715 | 2020-07-31 10:45:46 -0700 | [diff] [blame] | 360 | } |
| 361 | |
| 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 | } |
Tom Cherry | 4509ca5 | 2020-10-08 11:54:48 -0700 | [diff] [blame] | 368 | |
| 369 | void End() override { |
| 370 | // Release the reader thread. |
| 371 | { |
| 372 | auto lock = std::lock_guard{logd_lock}; |
Wenhao Wang | b1f6c6c | 2021-11-19 16:42:43 -0800 | [diff] [blame] | 373 | reader_list_.running_reader_threads().back()->Release(); |
Tom Cherry | 4509ca5 | 2020-10-08 11:54:48 -0700 | [diff] [blame] | 374 | } |
| 375 | |
| 376 | // Wait until it has deleted itself. |
| 377 | while (true) { |
| 378 | usleep(500); |
| 379 | auto lock = std::lock_guard{logd_lock}; |
Wenhao Wang | b1f6c6c | 2021-11-19 16:42:43 -0800 | [diff] [blame] | 380 | if (reader_list_.running_reader_threads().size() == 0) { |
Tom Cherry | 4509ca5 | 2020-10-08 11:54:48 -0700 | [diff] [blame] | 381 | break; |
| 382 | } |
| 383 | } |
| 384 | } |
Tom Cherry | 303c715 | 2020-07-31 10:45:46 -0700 | [diff] [blame] | 385 | }; |
| 386 | |
Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 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)); |
Tom Cherry | 303c715 | 2020-07-31 10:45:46 -0700 | [diff] [blame] | 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)); |
Tom Cherry | b4c1e95 | 2020-07-09 09:51:16 -0700 | [diff] [blame] | 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 | } |