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

Commit 9fc9edf9 authored by Joe Onorato's avatar Joe Onorato
Browse files

Clean up how we handle configurations, and other assorted cleanup

- Add a ConfigManager class that tracks the configurations
  that have been passed to us.  Configurations are now
  tracked by tuples of (uid,tag), where the tag is an
  app-defined string, in case a single uid has multiple
  configurations.
- Move all of the initialization into StatsService.
- Get rid of the ability to have multiple LogListeners. Raw
  events are now pushed directly into StatsService, which
  can distribute them to the interested parties (and will
  eventually be able to do the proper locking).
- Add Log.h, which sets our LOG_TAG correctly.
- Move some of the related files that I expect will grow some
  into their own subdirectories.

Test: statsd_test
Test: adb shell cmd stats config ...
Test: adb shell dumpsys stats
Change-Id: I79487603003d8a842d5bd319741f1ecbf72063d1
parent aeff5b88
Loading
Loading
Loading
Loading
+18 −14
Original line number Diff line number Diff line
@@ -14,16 +14,23 @@

LOCAL_PATH:= $(call my-dir)


statsd_common_src := \
    ../../core/java/android/os/IStatsCompanionService.aidl \
    ../../core/java/android/os/IStatsManager.aidl \
    src/stats_log.proto \
    src/statsd_config.proto \
    src/stats_events_copy.proto \
    src/anomaly/AnomalyMonitor.cpp \
    src/condition/CombinationConditionTracker.cpp \
    src/condition/condition_util.cpp \
    src/condition/SimpleConditionTracker.cpp \
    src/config/ConfigKey.cpp \
    src/config/ConfigListener.cpp \
    src/config/ConfigManager.cpp \
    src/external/KernelWakelockPuller.cpp \
    src/external/StatsPullerManager.cpp \
    src/logd/LogListener.cpp \
    src/logd/LogReader.cpp \
    src/matchers/CombinationLogMatchingTracker.cpp \
    src/matchers/matcher_util.cpp \
    src/matchers/SimpleLogMatchingTracker.cpp \
@@ -31,17 +38,12 @@ statsd_common_src := \
    src/metrics/CountMetricProducer.cpp \
    src/metrics/MetricsManager.cpp \
    src/metrics/metrics_manager_util.cpp \
    src/AnomalyMonitor.cpp \
    src/DropboxReader.cpp \
    src/DropboxWriter.cpp \
    src/KernelWakelockPuller.cpp \
    src/LogEntryPrinter.cpp \
    src/LogReader.cpp \
    src/packages/UidMap.cpp \
    src/storage/DropboxReader.cpp \
    src/storage/DropboxWriter.cpp \
    src/StatsLogProcessor.cpp \
    src/StatsPullerManager.cpp \
    src/StatsService.cpp \
    src/stats_util.cpp \
    src/UidMap.cpp
    src/stats_util.cpp

statsd_common_c_includes := \
    $(LOCAL_PATH)/src
@@ -125,13 +127,15 @@ LOCAL_CFLAGS += \

LOCAL_SRC_FILES := \
    $(statsd_common_src) \
    tests/AnomalyMonitor_test.cpp \
    tests/ConditionTracker_test.cpp \
    tests/ConfigManager_test.cpp \
    tests/indexed_priority_queue_test.cpp \
    tests/LogEntryMatcher_test.cpp \
    tests/LogReader_test.cpp \
    tests/MetricsManager_test.cpp \
    tests/UidMap_test.cpp \
    tests/LogEntryMatcher_test.cpp \
    tests/AnomalyMonitor_test.cpp \
    tests/ConditionTracker_test.cpp
    tests/UidMap_test.cpp


LOCAL_STATIC_LIBRARIES := \
    libgmock

cmds/statsd/src/Log.h

0 → 100644
+30 −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.
 */

/*
 * This file must be included at the top of the file. Other header files
 * occasionally include log.h, and if LOG_TAG isn't set when that happens
 * we'll get a preprocesser error when we try to define it here.
 */

#pragma once

#define LOG_TAG "statsd"

#include <log/log.h>

#define VLOG(...) \
    if (DEBUG) ALOGD(__VA_ARGS__);
+21 −14
Original line number Diff line number Diff line
@@ -14,12 +14,14 @@
 * limitations under the License.
 */

#include <StatsLogProcessor.h>
#include "Log.h"

#include "StatsLogProcessor.h"
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "metrics/CountMetricProducer.h"
#include "stats_util.h"

#include <cutils/log.h>
#include <frameworks/base/cmds/statsd/src/stats_log.pb.h>
#include <log/log_event_list.h>
#include <metrics/CountMetricProducer.h>
#include <utils/Errors.h>

using namespace android;
@@ -32,11 +34,7 @@ namespace os {
namespace statsd {

StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap)
        : m_dropbox_writer("all-logs"), m_UidMap(uidMap)
{
    // hardcoded config
    // this should be called from StatsService when it receives a statsd_config
    UpdateConfig(0, buildFakeConfig());
    : m_dropbox_writer("all-logs"), mUidMap(uidMap) {
}

StatsLogProcessor::~StatsLogProcessor() {
@@ -54,17 +52,18 @@ void StatsLogProcessor::OnLogEvent(const log_msg& msg) {
    }
}

void StatsLogProcessor::UpdateConfig(const int config_source, const StatsdConfig& config) {
    auto it = mMetricsManagers.find(config_source);
void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
    auto it = mMetricsManagers.find(key);
    if (it != mMetricsManagers.end()) {
        it->second->finish();
    }

    ALOGD("Updated configuration for source %i", config_source);
    ALOGD("Updated configuration for key %s", key.ToString().c_str());

    unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(config);
    if (newMetricsManager->isConfigValid()) {
        mMetricsManagers.insert({config_source, std::move(newMetricsManager)});
        mMetricsManagers[key] = std::move(newMetricsManager);
        // Why doesn't this work? mMetricsManagers.insert({key, std::move(newMetricsManager)});
        ALOGD("StatsdConfig valid");
    } else {
        // If there is any error in the config, don't use it.
@@ -72,6 +71,14 @@ void StatsLogProcessor::UpdateConfig(const int config_source, const StatsdConfig
    }
}

void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
    auto it = mMetricsManagers.find(key);
    if (it != mMetricsManagers.end()) {
        it->second->finish();
        mMetricsManagers.erase(it);
    }
}

}  // namespace statsd
}  // namespace os
}  // namespace android
+11 −9
Original line number Diff line number Diff line
@@ -16,12 +16,13 @@
#ifndef STATS_LOG_PROCESSOR_H
#define STATS_LOG_PROCESSOR_H

#include "DropboxWriter.h"
#include "LogReader.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "config/ConfigListener.h"
#include "logd/LogReader.h"
#include "metrics/MetricsManager.h"
#include "stats_util.h"
#include "UidMap.h"
#include "packages/UidMap.h"
#include "storage/DropboxWriter.h"

#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"

#include <log/logprint.h>
#include <stdio.h>
@@ -31,22 +32,23 @@ namespace android {
namespace os {
namespace statsd {

class StatsLogProcessor : public LogListener {
class StatsLogProcessor : public ConfigListener {
public:
    StatsLogProcessor(const sp<UidMap> &uidMap);
    virtual ~StatsLogProcessor();

    virtual void OnLogEvent(const log_msg& msg);

    void UpdateConfig(const int config_source, const StatsdConfig& config);
    void OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config);
    void OnConfigRemoved(const ConfigKey& key);

private:
    // TODO: use EventMetrics to log the events.
    DropboxWriter m_dropbox_writer;

    std::unordered_map<int, std::unique_ptr<MetricsManager>> mMetricsManagers;
    std::unordered_map<ConfigKey, std::unique_ptr<MetricsManager>> mMetricsManagers;

    sp<UidMap> m_UidMap; // Reference to the UidMap to lookup app name and version for each uid.
    sp<UidMap> mUidMap;  // Reference to the UidMap to lookup app name and version for each uid.
};

}  // namespace statsd
+191 −67
Original line number Diff line number Diff line
@@ -14,16 +14,15 @@
 * limitations under the License.
 */

#define LOG_TAG "statsd"
#define DEBUG true
#include "Log.h"

#include "StatsService.h"
#include "DropboxReader.h"
#include "storage/DropboxReader.h"

#include <android-base/file.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <cutils/log.h>
#include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
#include <private/android_filesystem_config.h>
#include <utils/Looper.h>
@@ -31,6 +30,7 @@

#include <stdio.h>
#include <stdlib.h>
#include <sys/system_properties.h>
#include <unistd.h>

using namespace android;
@@ -39,24 +39,65 @@ namespace android {
namespace os {
namespace statsd {

// ======================================================================
/**
 * Watches for the death of the stats companion (system process).
 */
class CompanionDeathRecipient : public IBinder::DeathRecipient {
public:
    CompanionDeathRecipient(const sp<AnomalyMonitor>& anomalyMonitor);
    virtual void binderDied(const wp<IBinder>& who);

private:
    const sp<AnomalyMonitor> mAnomalyMonitor;
};

CompanionDeathRecipient::CompanionDeathRecipient(const sp<AnomalyMonitor>& anomalyMonitor)
    : mAnomalyMonitor(anomalyMonitor) {
}

void CompanionDeathRecipient::binderDied(const wp<IBinder>& who) {
    ALOGW("statscompanion service died");
    mAnomalyMonitor->setStatsCompanionService(nullptr);
}

// ======================================================================
StatsService::StatsService(const sp<Looper>& handlerLooper)
    :   mAnomalyMonitor(new AnomalyMonitor(2)),m_UidMap(new UidMap()), mStatsPullerManager()
    // TODO: Change AnomalyMonitor initialization based on the config
    : mStatsPullerManager(),

      mAnomalyMonitor(new AnomalyMonitor(2))  // TODO: Put this comment somewhere better
{
    ALOGD("stats service constructed");
    mUidMap = new UidMap();
    mConfigManager = new ConfigManager();
    mProcessor = new StatsLogProcessor(mUidMap);

    mConfigManager->AddListener(mProcessor);

    init_system_properties();
}

StatsService::~StatsService() {
}

status_t StatsService::setProcessor(const sp<StatsLogProcessor>& main_processor) {
    m_processor = main_processor;
    ALOGD("stats service set to processor %p", m_processor.get());
    return NO_ERROR;
void StatsService::init_system_properties() {
    mEngBuild = false;
    const prop_info* buildType = __system_property_find("ro.build.type");
    if (buildType != NULL) {
        __system_property_read_callback(buildType, init_build_type_callback, this);
    }
}

void StatsService::init_build_type_callback(void* cookie, const char* /*name*/, const char* value,
                                            uint32_t serial) {
    if (0 == strcmp("eng", value)) {
        reinterpret_cast<StatsService*>(cookie)->mEngBuild = true;
    }
}

// Implement our own because the default binder implementation isn't
// properly handling SHELL_COMMAND_TRANSACTION
/**
 * Implement our own because the default binder implementation isn't
 * properly handling SHELL_COMMAND_TRANSACTION.
 */
status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
                                  uint32_t flags) {
    status_t err;
@@ -105,56 +146,159 @@ status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* rep
    }
}

/**
 * Write debugging data about statsd.
 */
status_t StatsService::dump(int fd, const Vector<String16>& args) {
    FILE* out = fdopen(fd, "w");
    if (out == NULL) {
        return NO_MEMORY;  // the fd is already open
    }

    fprintf(out, "StatsService::dump:");
    ALOGD("StatsService::dump:");
    const int N = args.size();
    for (int i = 0; i < N; i++) {
        fprintf(out, " %s", String8(args[i]).string());
        ALOGD("   %s", String8(args[i]).string());
    }
    fprintf(out, "\n");
    // TODO: Proto format for incident reports
    dump_impl(out);

    fclose(out);
    return NO_ERROR;
}

status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
    if (args.size() > 0) {
        if (!args[0].compare(String8("print-stats-log")) && args.size() > 1) {
            return doPrintStatsLog(out, args);
/**
 * Write debugging data about statsd in text format.
 */
void StatsService::dump_impl(FILE* out) {
    mConfigManager->Dump(out);
}

/**
 * Implementation of the adb shell cmd stats command.
 */
status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
    // TODO: Permission check

    const int argCount = args.size();
    if (argCount >= 1) {
        // adb shell cmd stats config ...
        if (!args[0].compare(String8("config"))) {
            return doLoadConfig(in);
            return cmd_config(in, out, err, args);
        }

        // adb shell cmd stats print-stats-log
        if (!args[0].compare(String8("print-stats-log")) && args.size() > 1) {
            return cmd_print_stats_log(out, args);
        }

        // adb shell cmd stats print-stats-log
        if (!args[0].compare(String8("print-uid-map"))) {
            return doPrintUidMap(out);
            return cmd_print_uid_map(out);
        }
    }

    printCmdHelp(out);
    print_cmd_help(out);
    return NO_ERROR;
}

status_t StatsService::doLoadConfig(FILE* in) {
    string content;
    if (!android::base::ReadFdToString(fileno(in), &content)) {
void StatsService::print_cmd_help(FILE* out) {
    fprintf(out,
            "usage: adb shell cmd stats print-stats-log [tag_required] "
            "[timestamp_nsec_optional]\n");
    fprintf(out, "\n");
    fprintf(out, "\n");
    fprintf(out, "usage: adb shell cmd stats print-uid-map \n");
    fprintf(out, "\n");
    fprintf(out, "  Prints the UID, app name, version mapping.\n");
    fprintf(out, "\n");
    fprintf(out, "\n");
    fprintf(out, "usage: adb shell cmd stats config remove [UID] NAME\n");
    fprintf(out, "usage: adb shell cmd stats config update [UID] NAME\n");
    fprintf(out, "\n");
    fprintf(out, "  Adds, updates or removes a configuration. The proto should be in\n");
    fprintf(out, "  wire-encoded protobuf format and passed via stdin.\n");
    fprintf(out, "\n");
    fprintf(out, "  UID           The uid to use. It is only possible to pass the UID\n");
    fprintf(out, "                parameter on eng builds.  If UID is omitted the calling\n");
    fprintf(out, "                uid is used.\n");
    fprintf(out, "  NAME          The per-uid name to use\n");
}

status_t StatsService::cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
    const int argCount = args.size();
    if (argCount >= 2) {
        if (args[1] == "update" || args[1] == "remove") {
            bool good = false;
            int uid = -1;
            string name;

            if (argCount == 3) {
                // Automatically pick the UID
                uid = IPCThreadState::self()->getCallingUid();
                // TODO: What if this isn't a binder call? Should we fail?
                name.assign(args[2].c_str(), args[2].size());
                good = true;
            } else if (argCount == 4) {
                // If it's a userdebug or eng build, then the shell user can
                // impersonate other uids.
                if (mEngBuild) {
                    const char* s = args[2].c_str();
                    if (*s != '\0') {
                        char* end = NULL;
                        uid = strtol(s, &end, 0);
                        if (*end == '\0') {
                            name.assign(args[3].c_str(), args[3].size());
                            good = true;
                        }
                    }
                } else {
                    fprintf(err, "The config can only be set for other UIDs on eng builds.\n");
                }
            }

            if (!good) {
                // If arg parsing failed, print the help text and return an error.
                print_cmd_help(out);
                return UNKNOWN_ERROR;
            }

            if (args[1] == "update") {
                // Read stream into buffer.
                string buffer;
                if (!android::base::ReadFdToString(fileno(in), &buffer)) {
                    fprintf(err, "Error reading stream for StatsConfig.\n");
                    return UNKNOWN_ERROR;
                }

                // Parse buffer.
                StatsdConfig config;
    if (config.ParseFromString(content)) {
        ALOGD("Config parsed from command line: %s", config.SerializeAsString().c_str());
        m_processor->UpdateConfig(0, config);
        return NO_ERROR;
                if (!config.ParseFromString(buffer)) {
                    fprintf(err, "Error parsing proto stream for StatsConfig.\n");
                    return UNKNOWN_ERROR;
                }

                // Add / update the config.
                mConfigManager->UpdateConfig(ConfigKey(uid, name), config);
            } else {
        ALOGD("Config failed to be parsed");
                // Remove the config.
                mConfigManager->RemoveConfig(ConfigKey(uid, name));
            }

            return NO_ERROR;
        }
    }
    print_cmd_help(out);
    return UNKNOWN_ERROR;
}

status_t StatsService::cmd_print_stats_log(FILE* out, const Vector<String8>& args) {
    long msec = 0;

    if (args.size() > 2) {
        msec = strtol(args[2].string(), NULL, 10);
    }
    return DropboxReader::readStatsLogs(out, args[1].string(), msec);
}

status_t StatsService::cmd_print_uid_map(FILE* out) {
    mUidMap->printUidMap(out);
    return NO_ERROR;
}

Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version,
@@ -166,7 +310,7 @@ Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<i
                                         "Only system uid can call informAllUidData");
    }

    m_UidMap->updateMap(uid, version, app);
    mUidMap->updateMap(uid, version, app);
    if (DEBUG) ALOGD("StatsService::informAllUidData succeeded");

    return Status::ok();
@@ -179,7 +323,7 @@ Status StatsService::informOnePackage(const String16& app, int32_t uid, int32_t
        return Status::fromExceptionCode(Status::EX_SECURITY,
                                         "Only system uid can call informOnePackage");
    }
    m_UidMap->updateApp(app, uid, version);
    mUidMap->updateApp(app, uid, version);
    return Status::ok();
}

@@ -190,7 +334,7 @@ Status StatsService::informOnePackageRemoved(const String16& app, int32_t uid) {
        return Status::fromExceptionCode(Status::EX_SECURITY,
                                         "Only system uid can call informOnePackageRemoved");
    }
    m_UidMap->removeApp(app, uid);
    mUidMap->removeApp(app, uid);
    return Status::ok();
}

@@ -282,38 +426,18 @@ Status StatsService::statsCompanionReady() {
                "statscompanion unavailable despite it contacting statsd!");
    }
    if (DEBUG) ALOGD("StatsService::statsCompanionReady linking to statsCompanion.");
    IInterface::asBinder(statsCompanion)->linkToDeath(new StatsdDeathRecipient(mAnomalyMonitor));
    IInterface::asBinder(statsCompanion)->linkToDeath(new CompanionDeathRecipient(mAnomalyMonitor));
    mAnomalyMonitor->setStatsCompanionService(statsCompanion);

    return Status::ok();
}

void StatsdDeathRecipient::binderDied(const wp<IBinder>& who) {
    ALOGW("statscompanion service died");
    mAnmlyMntr->setStatsCompanionService(nullptr);
}

status_t StatsService::doPrintStatsLog(FILE* out, const Vector<String8>& args) {
    long msec = 0;

    if (args.size() > 2) {
        msec = strtol(args[2].string(), NULL, 10);
    }
    return DropboxReader::readStatsLogs(out, args[1].string(), msec);
void StatsService::Startup() {
    mConfigManager->Startup();
}

status_t StatsService::doPrintUidMap(FILE* out) {
    m_UidMap->printUidMap(out);
    return NO_ERROR;
}

void StatsService::printCmdHelp(FILE* out) {
    fprintf(out, "Usage:\n");
    fprintf(out, "\t print-stats-log [tag_required] [timestamp_nsec_optional]\n");
    fprintf(out, "\t print-uid-map Prints the UID, app name, version mapping.\n");
    fprintf(out,
            "\t config\t Loads a new config from command-line (must be proto in wire-encoded "
            "format).\n");
void StatsService::OnLogEvent(const log_msg& msg) {
    mProcessor->OnLogEvent(msg);
}

}  // namespace statsd
Loading