1/* 2 * Copyright (C) 2017 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 "logd/LogReader.h" 18 19#include "guardrail/StatsdStats.h" 20 21#include <time.h> 22#include <unistd.h> 23#include <utils/Errors.h> 24 25using namespace android; 26using namespace std; 27 28namespace android { 29namespace os { 30namespace statsd { 31 32#define SNOOZE_INITIAL_MS 100 33#define SNOOZE_MAX_MS (10 * 60 * 1000) // Ten minutes 34 35LogReader::LogReader(const sp<LogListener>& listener) : mListener(listener) { 36} 37 38LogReader::~LogReader() { 39} 40 41void LogReader::Run() { 42 int nextSnoozeMs = SNOOZE_INITIAL_MS; 43 44 // In an ideal world, this outer loop will only ever run one iteration, but it 45 // exists to handle crashes in logd. The inner loop inside connect_and_read() 46 // reads from logd forever, but if that read fails, we fall out to the outer 47 // loop, do the backoff (resetting the backoff timeout if we successfully read 48 // something), and then try again. 49 while (true) { 50 // Connect and read 51 int lineCount = connect_and_read(); 52 53 // Figure out how long to sleep. 54 if (lineCount > 0) { 55 // If we managed to read at least one line, reset the backoff 56 nextSnoozeMs = SNOOZE_INITIAL_MS; 57 } else { 58 // Otherwise, expontial backoff 59 nextSnoozeMs *= 1.5f; 60 if (nextSnoozeMs > 10 * 60 * 1000) { 61 // Don't wait for toooo long. 62 nextSnoozeMs = SNOOZE_MAX_MS; 63 } 64 } 65 66 // Sleep 67 timespec ts; 68 timespec rem; 69 ts.tv_sec = nextSnoozeMs / 1000; 70 ts.tv_nsec = (nextSnoozeMs % 1000) * 1000000L; 71 while (nanosleep(&ts, &rem) == -1) { 72 if (errno == EINTR) { 73 ts = rem; 74 } 75 // other errors are basically impossible 76 } 77 } 78} 79 80int LogReader::connect_and_read() { 81 int lineCount = 0; 82 status_t err; 83 logger_list* loggers; 84 logger* eventLogger; 85 86 // Prepare the logging context 87 loggers = android_logger_list_alloc(ANDROID_LOG_RDONLY, 88 /* don't stop after N lines */ 0, 89 /* no pid restriction */ 0); 90 91 // Open the buffer(s) 92 eventLogger = android_logger_open(loggers, LOG_ID_STATS); 93 94 // Read forever 95 if (eventLogger) { 96 log_msg msg; 97 while (true) { 98 // Read a message 99 err = android_logger_list_read(loggers, &msg); 100 // err = 0 - no content, unexpected connection drop or EOF. 101 // err = +ive number - size of retrieved data from logger 102 // err = -ive number, OS supplied error _except_ for -EAGAIN 103 if (err <= 0) { 104 StatsdStats::getInstance().noteLoggerError(err); 105 fprintf(stderr, "logcat read failure: %s\n", strerror(err)); 106 break; 107 } 108 109 // Record that we read one (used above to know how to snooze). 110 lineCount++; 111 112 // Wrap it in a LogEvent object 113 LogEvent event(msg); 114 115 // Call the listener 116 mListener->OnLogEvent(&event, 117 lineCount == 1 /* indicate whether it's a new connection */); 118 } 119 } 120 121 // Free the logger list and close the individual loggers 122 android_logger_list_free(loggers); 123 124 return lineCount; 125} 126 127} // namespace statsd 128} // namespace os 129} // namespace android 130