Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8e741850 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes from topic "dropbox"

* changes:
  Add a cmd line tool to StatsService to parse log files from Dropbox
  Add a DropboxWriter in statsd.
parents a41d8528 482d272d
Loading
Loading
Loading
Loading
+35 −5
Original line number Diff line number Diff line
@@ -14,6 +14,23 @@

LOCAL_PATH:= $(call my-dir)

# ================
# proto static lib
# ================
include $(CLEAR_VARS)

LOCAL_MODULE := statsd_proto
LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := $(call all-proto-files-under, src)

LOCAL_PROTOC_FLAGS :=
LOCAL_PROTOC_OPTIMIZE_TYPE := lite

include $(BUILD_STATIC_LIBRARY)

STATSD_PROTO_INCLUDES := $(local-generated-sources-dir)/src/$(LOCAL_PATH)

# =========
# statsd
# =========
@@ -27,7 +44,14 @@ LOCAL_SRC_FILES := \
    src/StatsService.cpp \
    src/LogEntryPrinter.cpp \
    src/LogReader.cpp \
    src/main.cpp
    src/main.cpp \
    src/DropboxWriter.cpp \
    src/StatsLogProcessor.cpp \
    src/stats_log.proto \
    src/statsd_config.proto \
    src/stats_constants.proto \
    src/DropboxReader.cpp \


LOCAL_CFLAGS += \
    -Wall \
@@ -47,7 +71,10 @@ else
endif

LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/../../core/java
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src \
	STATSD_PROTO_INCLUDES

LOCAL_STATIC_LIBRARIES := statsd_proto

LOCAL_SHARED_LIBRARIES := \
        libbase \
@@ -56,7 +83,9 @@ LOCAL_SHARED_LIBRARIES := \
        libincident \
        liblog \
        libselinux \
        libutils
        libutils \
        libservices \
        libandroidfw \

LOCAL_MODULE_CLASS := EXECUTABLES

@@ -82,7 +111,8 @@ LOCAL_CFLAGS += \
    -Wno-unused-function \
    -Wno-unused-parameter

LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src \
	STATSD_PROTO_INCLUDES

LOCAL_SRC_FILES := \
    ../../core/java/android/os/IStatsManager.aidl \
@@ -93,6 +123,7 @@ LOCAL_SRC_FILES := \

LOCAL_STATIC_LIBRARIES := \
    libgmock \
    statsd_proto

LOCAL_SHARED_LIBRARIES := \
    libbase \
@@ -103,4 +134,3 @@ LOCAL_SHARED_LIBRARIES := \
    libutils

include $(BUILD_NATIVE_TEST)
+120 −0
Original line number Diff line number Diff line
/*
 * 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");
    }
}
+45 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

#ifndef DROPBOX_READER_H
#define DROPBOX_READER_H

#include <frameworks/base/cmds/statsd/src/stats_log.pb.h>

#include <stdint.h>
#include <stdio.h>

using android::base::unique_fd;
using android::os::statsd::StatsLogList;
using android::status_t;
using std::string;

class DropboxReader {
public:
    // msec is the start timestamp.
    static status_t readStatsLogs(FILE* out, const string& tag, long msec);

private:
    static bool parseFromFile(const unique_fd& fd, StatsLogList& list);
    static bool parseFromGzipFile(const unique_fd& fd, StatsLogList& list);
    static void printLog(FILE* out, const StatsLogList& list);
    enum {
      kCompressStored = 0,    // no compression
      kCompressDeflated = 8,  // standard deflate
    };
};

#endif //DROPBOX_READER_H
 No newline at end of file
+61 −0
Original line number Diff line number Diff line
/*
 * 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 <cutils/log.h>

#include "DropboxWriter.h"

using android::os::DropBoxManager;
using android::binder::Status;
using android::sp;
using android::String16;
using std::vector;

DropboxWriter::DropboxWriter(const string& tag)
    : mTag(tag), mLogList(), mBufferSize(0) {
}

void DropboxWriter::addEntry(const StatsLogEntry& entry) {
    flushIfNecessary(entry);
    StatsLogEntry* newEntry = mLogList.add_stats_log_entry();
    newEntry->CopyFrom(entry);
    mBufferSize += entry.ByteSize();
}

void DropboxWriter::flushIfNecessary(const StatsLogEntry& entry) {
    // The serialized size of the StatsLogList is approximately the sum of the serialized size of
    // every StatsLogEntry inside it.
    if (entry.ByteSize() + mBufferSize > kMaxSerializedBytes) {
        flush();
    }
}

void DropboxWriter::flush() {
    // now we get an exact byte size of the output
    const int numBytes = mLogList.ByteSize();
    vector<uint8_t> buffer(numBytes);
    sp<DropBoxManager> dropbox = new DropBoxManager();
    mLogList.SerializeToArray(&buffer[0], numBytes);
    Status status = dropbox->addData(String16(mTag.c_str()), &buffer[0],
            numBytes, 0 /* no flag */);
    if (!status.isOk()) {
        ALOGE("failed to write to dropbox");
        //TODO: What to do if flush fails??
    }
    mLogList.Clear();
    mBufferSize = 0;
}
+66 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

#ifndef DROPBOX_WRITER_H
#define DROPBOX_WRITER_H

#include <frameworks/base/cmds/statsd/src/stats_log.pb.h>

using std::string;
using android::os::statsd::StatsLogEntry;
using android::os::statsd::StatsLogList;

class DropboxWriter {
public:
    /* tag will be part of the file name, and used as the key to build the file index inside
       DropBoxManagerService.
     */
    DropboxWriter(const string& tag);

    void addEntry(const StatsLogEntry& entry);

    /* Request a flush to dropbox. */
    void flush();

private:
    /* Max *serialized* size of the logs kept in memory before flushing to dropbox.
       Proto lite does not implement the SpaceUsed() function which gives the in memory byte size.
       So we cap memory usage by limiting the serialized size. Note that protobuf's in memory size
       is higher than its serialized size. DropboxManager will compress the file when the data is
       larger than 4KB. So the final file size is less than this number.
     */
    static const size_t kMaxSerializedBytes = 16 * 1024;

    const string mTag;

    /* StatsLogList is a wrapper for storing a list of StatsLogEntry */
    StatsLogList mLogList;

    /* Current *serialized* size of the logs kept in memory.
       To save computation, we will not calculate the size of the StatsLogList every time when a new
       entry is added, which would recursively call ByteSize() on every log entry. Instead, we keep
       the sum of all individual stats log entry sizes. The size of a proto is approximately the sum
       of the size of all member protos.
     */
    size_t mBufferSize = 0;

    /* Check if the buffer size exceeds the max buffer size when the new entry is added, and flush
       the logs to dropbox if true. */
    void flushIfNecessary(const StatsLogEntry& entry);

};

#endif //DROPBOX_WRITER_H
Loading