summaryrefslogtreecommitdiff
path: root/cmds/statsd/src/logd/LogReader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cmds/statsd/src/logd/LogReader.cpp')
-rw-r--r--cmds/statsd/src/logd/LogReader.cpp128
1 files changed, 128 insertions, 0 deletions
diff --git a/cmds/statsd/src/logd/LogReader.cpp b/cmds/statsd/src/logd/LogReader.cpp
new file mode 100644
index 000000000000..0fe896bb10c3
--- /dev/null
+++ b/cmds/statsd/src/logd/LogReader.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "logd/LogReader.h"
+
+#include "guardrail/StatsdStats.h"
+
+#include <time.h>
+#include <unistd.h>
+#include <utils/Errors.h>
+
+using namespace android;
+using namespace std;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#define SNOOZE_INITIAL_MS 100
+#define SNOOZE_MAX_MS (10 * 60 * 1000) // Ten minutes
+
+LogReader::LogReader(const sp<LogListener>& listener) : mListener(listener) {
+}
+
+LogReader::~LogReader() {
+}
+
+void LogReader::Run() {
+ int nextSnoozeMs = SNOOZE_INITIAL_MS;
+
+ // In an ideal world, this outer loop will only ever run one iteration, but it
+ // exists to handle crashes in logd. The inner loop inside connect_and_read()
+ // reads from logd forever, but if that read fails, we fall out to the outer
+ // loop, do the backoff (resetting the backoff timeout if we successfully read
+ // something), and then try again.
+ while (true) {
+ // Connect and read
+ int lineCount = connect_and_read();
+
+ // Figure out how long to sleep.
+ if (lineCount > 0) {
+ // If we managed to read at least one line, reset the backoff
+ nextSnoozeMs = SNOOZE_INITIAL_MS;
+ } else {
+ // Otherwise, expontial backoff
+ nextSnoozeMs *= 1.5f;
+ if (nextSnoozeMs > 10 * 60 * 1000) {
+ // Don't wait for toooo long.
+ nextSnoozeMs = SNOOZE_MAX_MS;
+ }
+ }
+
+ // Sleep
+ timespec ts;
+ timespec rem;
+ ts.tv_sec = nextSnoozeMs / 1000;
+ ts.tv_nsec = (nextSnoozeMs % 1000) * 1000000L;
+ while (nanosleep(&ts, &rem) == -1) {
+ if (errno == EINTR) {
+ ts = rem;
+ }
+ // other errors are basically impossible
+ }
+ }
+}
+
+int LogReader::connect_and_read() {
+ int lineCount = 0;
+ status_t err;
+ logger_list* loggers;
+ logger* eventLogger;
+
+ // Prepare the logging context
+ loggers = android_logger_list_alloc(ANDROID_LOG_RDONLY,
+ /* don't stop after N lines */ 0,
+ /* no pid restriction */ 0);
+
+ // Open the buffer(s)
+ eventLogger = android_logger_open(loggers, LOG_ID_STATS);
+
+ // Read forever
+ if (eventLogger) {
+ log_msg msg;
+ while (true) {
+ // Read a message
+ err = android_logger_list_read(loggers, &msg);
+ // err = 0 - no content, unexpected connection drop or EOF.
+ // err = +ive number - size of retrieved data from logger
+ // err = -ive number, OS supplied error _except_ for -EAGAIN
+ if (err <= 0) {
+ StatsdStats::getInstance().noteLoggerError(err);
+ fprintf(stderr, "logcat read failure: %s\n", strerror(err));
+ break;
+ }
+
+ // Record that we read one (used above to know how to snooze).
+ lineCount++;
+
+ // Wrap it in a LogEvent object
+ LogEvent event(msg);
+
+ // Call the listener
+ mListener->OnLogEvent(&event);
+ }
+ }
+
+ // Free the logger list and close the individual loggers
+ android_logger_list_free(loggers);
+
+ return lineCount;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android