Add a cmd line tool to StatsService to parse log files from Dropbox
Test: adb shell cmd stats all-logs
Change-Id: I7803c9c021a971619f60fbf6bdfabd33d2f476ef
diff --git a/cmds/statsd/src/DropboxReader.cpp b/cmds/statsd/src/DropboxReader.cpp
new file mode 100644
index 0000000..187f4ad
--- /dev/null
+++ b/cmds/statsd/src/DropboxReader.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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 <android/os/DropBoxManager.h>
+#include <android-base/file.h>
+#include <cutils/log.h>
+#include <androidfw/ZipUtils.h>
+#include <stdio.h>
+
+#include "DropboxReader.h"
+
+using android::sp;
+using android::String16;
+using android::binder::Status;
+using android::base::unique_fd;
+using android::os::DropBoxManager;
+using android::os::statsd::StatsLogEntry;
+using android::ZipUtils;
+using std::vector;
+
+status_t DropboxReader::readStatsLogs(FILE* out, const string& tag, long msec) {
+ sp<DropBoxManager> dropbox = new DropBoxManager();
+ StatsLogList logList;
+
+ long timestamp = msec;
+ // instead of while(true), put a hard limit 1000. Dropbox won't have more than 1000 files.
+ for(int i = 0; i < 1000; i++ ) {
+ DropBoxManager::Entry entry;
+ Status status = dropbox->getNextEntry(String16(tag.c_str()),
+ timestamp, &entry);
+ if (!status.isOk()) {
+ ALOGD("No more entries, or failed to read. We can't tell unfortunately.");
+ return android::OK;
+ }
+
+ const unique_fd& fd = entry.getFd();
+
+ // use this timestamp for next query.
+ timestamp = entry.getTimestamp();
+
+ if (entry.getFlags() & DropBoxManager::IS_GZIPPED) {
+ if (!parseFromGzipFile(fd, logList)) {
+ // Failed to parse from the file. Continue to fetch the next entry.
+ continue;
+ }
+ } else {
+ if (!parseFromFile(fd, logList)) {
+ // Failed to parse from the file. Continue to fetch the next entry.
+ continue;
+ }
+ }
+
+ printLog(out, logList);
+ }
+ return android::OK;
+}
+
+bool DropboxReader::parseFromGzipFile(const unique_fd& fd, StatsLogList& list) {
+ FILE *file = fdopen(fd, "r");
+ bool result = false;
+ bool scanResult;
+ int method;
+ long compressedLen;
+ long uncompressedLen;
+ unsigned long crc32;
+ scanResult = ZipUtils::examineGzip(file, &method, &uncompressedLen,
+ &compressedLen, &crc32);
+ if (scanResult && method == kCompressDeflated) {
+ vector<uint8_t> buf(uncompressedLen);
+ if (ZipUtils::inflateToBuffer(file, &buf[0], uncompressedLen, compressedLen)) {
+ if (list.ParseFromArray(&buf[0], uncompressedLen)) {
+ result = true;
+ }
+ }
+ } else {
+ ALOGE("This isn't a valid deflated gzip file");
+ }
+ fclose(file);
+ return result;
+}
+
+// parse a non zipped file.
+bool DropboxReader::parseFromFile(const unique_fd& fd, StatsLogList& list) {
+ string content;
+ if (!android::base::ReadFdToString(fd, &content)) {
+ ALOGE("Failed to read file");
+ return false;
+ }
+ if (!list.ParseFromString(content)) {
+ ALOGE("failed to parse log entry from data");
+ return false;
+ }
+ return true;
+}
+
+void DropboxReader::printLog(FILE* out, const StatsLogList& list) {
+ for (int i = 0; i < list.stats_log_entry_size(); i++) {
+ const StatsLogEntry entry = list.stats_log_entry(i);
+ // TODO: print pretty
+ fprintf(out, "time_msec=%lld, type=%d, aggregate_type=%d, uid=%d, pid=%d ",
+ entry.start_report_millis(), entry.type(), entry.aggregate_type(),
+ entry.uid(), entry.pid());
+ for (int j = 0; j < entry.pairs_size(); j++) {
+ fprintf(out, "msg=%s ", entry.pairs(j).value_str().c_str());
+ }
+ fprintf(out, "\n");
+ }
+}