storaged: monitor per-uid IO usage
Add uid_monitor class to query /proc/uid_io/stats periodically. Add a log tag to record any UID that exceeds IO threshold. Test: adb shell storaged -u Bug: 34198239 Change-Id: I53568c30dbefe2f4bdb18054d3dedb30b4133d8b
This commit is contained in:
parent
933d02818b
commit
bcd6e3b9d9
|
@ -2,14 +2,16 @@
|
|||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
LIBSTORAGED_SHARED_LIBRARIES := libbinder libbase libutils libcutils liblog libsysutils libcap
|
||||
LIBSTORAGED_SHARED_LIBRARIES := libbinder libbase libutils libcutils liblog libsysutils libcap libpackagelistparser
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := storaged.cpp \
|
||||
storaged_service.cpp \
|
||||
storaged_utils.cpp \
|
||||
storaged_uid_monitor.cpp \
|
||||
EventLogTags.logtags
|
||||
|
||||
LOCAL_MODULE := libstoraged
|
||||
LOCAL_CFLAGS := -Werror
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include external/googletest/googletest/include
|
||||
|
|
|
@ -37,3 +37,5 @@
|
|||
2732 storaged_disk_stats (type|3),(start_time|2|3),(end_time|2|3),(read_ios|2|1),(read_merges|2|1),(read_sectors|2|1),(read_ticks|2|3),(write_ios|2|1),(write_merges|2|1),(write_sectors|2|1),(write_ticks|2|3),(o_in_flight|2|1),(io_ticks|2|3),(io_in_queue|2|1)
|
||||
|
||||
2733 storaged_emmc_info (mmc_ver|3),(eol|1),(lifetime_a|1),(lifetime_b|1)
|
||||
|
||||
2734 storaged_uid_io_alert (name|3),(read|2),(write|2),(interval|2)
|
|
@ -17,13 +17,17 @@
|
|||
#ifndef _STORAGED_H_
|
||||
#define _STORAGED_H_
|
||||
|
||||
#include <queue>
|
||||
#include <semaphore.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "storaged_uid_monitor.h"
|
||||
|
||||
#define FRIEND_TEST(test_case_name, test_name) \
|
||||
friend class test_case_name##_##test_name##_Test
|
||||
|
||||
|
@ -165,6 +169,8 @@ public:
|
|||
#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
|
||||
#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
|
||||
#define EMMC_ECSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
|
||||
#define UID_IO_STATS_PATH "/proc/uid_io/stats"
|
||||
|
||||
class disk_stats_monitor {
|
||||
private:
|
||||
FRIEND_TEST(storaged_test, disk_stats_monitor);
|
||||
|
@ -260,12 +266,15 @@ public:
|
|||
#define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 )
|
||||
#define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 )
|
||||
#define DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH ( 86400 )
|
||||
#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_ALERT ( 3600 )
|
||||
|
||||
struct storaged_config {
|
||||
int periodic_chores_interval_unit;
|
||||
int periodic_chores_interval_disk_stats_publish;
|
||||
int periodic_chores_interval_emmc_info_publish;
|
||||
int periodic_chores_interval_uid_io;
|
||||
bool proc_taskio_readable; // are /proc/[pid]/{io, comm, cmdline, stat} all readable
|
||||
bool proc_uid_io_available; // whether uid_io is accessible
|
||||
bool emmc_available; // whether eMMC est_csd file is readable
|
||||
bool diskstats_available; // whether diskstats is accessible
|
||||
};
|
||||
|
@ -278,6 +287,7 @@ private:
|
|||
disk_stats_monitor mDsm;
|
||||
emmc_info_t mEmmcInfo;
|
||||
tasks_t mTasks;
|
||||
uid_monitor mUidm;
|
||||
time_t mStarttime;
|
||||
public:
|
||||
storaged_t(void);
|
||||
|
@ -295,6 +305,9 @@ public:
|
|||
void set_emmc_interval(int emmc_info) {
|
||||
mConfig.periodic_chores_interval_emmc_info_publish = emmc_info;
|
||||
}
|
||||
void set_uid_io_interval(int uid_io) {
|
||||
mUidm.set_periodic_chores_interval(uid_io);
|
||||
}
|
||||
std::vector<struct task_info> get_tasks(void) {
|
||||
// There could be a race when get_tasks() and the main thread is updating at the same time
|
||||
// While update_running_tasks() is updating the critical sections at the end of the function
|
||||
|
@ -312,11 +325,16 @@ public:
|
|||
time_t get_starttime(void) {
|
||||
return mStarttime;
|
||||
}
|
||||
|
||||
std::unordered_map<uint32_t, struct uid_info> get_uids(void) {
|
||||
return mUidm.get_uids();
|
||||
}
|
||||
};
|
||||
|
||||
// Eventlog tag
|
||||
// The content must match the definition in EventLogTags.logtags
|
||||
#define EVENTLOGTAG_DISKSTATS ( 2732 )
|
||||
#define EVENTLOGTAG_EMMCINFO ( 2733 )
|
||||
#define EVENTLOGTAG_UID_IO_ALERT ( 2734 )
|
||||
|
||||
#endif /* _STORAGED_H_ */
|
||||
|
|
|
@ -31,9 +31,11 @@ class IStoraged : public IInterface {
|
|||
public:
|
||||
enum {
|
||||
DUMPTASKS = IBinder::FIRST_CALL_TRANSACTION,
|
||||
DUMPUIDS = IBinder::FIRST_CALL_TRANSACTION + 1,
|
||||
};
|
||||
// Request the service to run the test function
|
||||
virtual std::vector<struct task_info> dump_tasks(const char* option) = 0;
|
||||
virtual std::vector<struct uid_info> dump_uids(const char* option) = 0;
|
||||
|
||||
DECLARE_META_INTERFACE(Storaged);
|
||||
};
|
||||
|
@ -43,6 +45,7 @@ class BpStoraged : public BpInterface<IStoraged> {
|
|||
public:
|
||||
BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){};
|
||||
virtual std::vector<struct task_info> dump_tasks(const char* option);
|
||||
virtual std::vector<struct uid_info> dump_uids(const char* option);
|
||||
};
|
||||
|
||||
// Server
|
||||
|
@ -52,6 +55,7 @@ class BnStoraged : public BnInterface<IStoraged> {
|
|||
|
||||
class Storaged : public BnStoraged {
|
||||
virtual std::vector<struct task_info> dump_tasks(const char* option);
|
||||
virtual std::vector<struct uid_info> dump_uids(const char* option);
|
||||
};
|
||||
|
||||
sp<IStoraged> get_storaged_service();
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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 _STORAGED_UID_MONITOR_H_
|
||||
#define _STORAGED_UID_MONITOR_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
enum {
|
||||
UID_FOREGROUND = 0,
|
||||
UID_BACKGROUND = 1,
|
||||
UID_STATS_SIZE = 2
|
||||
};
|
||||
|
||||
struct uid_io_stats {
|
||||
uint64_t rchar; // characters read
|
||||
uint64_t wchar; // characters written
|
||||
uint64_t read_bytes; // bytes read (from storage layer)
|
||||
uint64_t write_bytes; // bytes written (to storage layer)
|
||||
};
|
||||
|
||||
struct uid_info {
|
||||
uint32_t uid; // user id
|
||||
std::string name; // package name
|
||||
struct uid_io_stats io[UID_STATS_SIZE]; // [0]:foreground [1]:background
|
||||
|
||||
};
|
||||
|
||||
class uid_monitor {
|
||||
private:
|
||||
std::unordered_map<uint32_t, struct uid_info> last_uids;
|
||||
void set_last_uids(std::unordered_map<uint32_t, struct uid_info>&& uids, uint64_t ts);
|
||||
int interval; // monitor interval in seconds
|
||||
uint64_t last_report_ts; // timestamp of last report in nsec
|
||||
public:
|
||||
uid_monitor();
|
||||
void set_periodic_chores_interval(int t) { interval = t; }
|
||||
int get_periodic_chores_interval() { return interval; }
|
||||
std::unordered_map<uint32_t, struct uid_info> get_uids();
|
||||
void report();
|
||||
};
|
||||
|
||||
#endif /* _STORAGED_UID_MONITOR_H_ */
|
|
@ -34,12 +34,15 @@ bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info);
|
|||
// Task I/O
|
||||
bool parse_task_info(uint32_t pid, struct task_info* info);
|
||||
void sort_running_tasks_info(std::vector<struct task_info> &tasks);
|
||||
// UID I/O
|
||||
void sort_running_uids_info(std::vector<struct uid_info> &uids);
|
||||
|
||||
// Logging
|
||||
void log_console_running_tasks_info(std::vector<struct task_info> tasks);
|
||||
void log_console_running_uids_info(std::vector<struct uid_info> uids);
|
||||
|
||||
void log_debug_disk_perf(struct disk_perf* perf, const char* type);
|
||||
|
||||
void log_event_disk_stats(struct disk_stats* stats, const char* type);
|
||||
void log_event_emmc_info(struct emmc_info* info_);
|
||||
#endif /* _STORAGED_UTILS_H_ */
|
||||
#endif /* _STORAGED_UTILS_H_ */
|
||||
|
|
|
@ -105,9 +105,11 @@ void* storaged_main(void* s) {
|
|||
static void help_message(void) {
|
||||
printf("usage: storaged [OPTION]\n");
|
||||
printf(" -d --dump Dump task I/O usage to stdout\n");
|
||||
printf(" -u --uid Dump uid I/O usage to stdout\n");
|
||||
printf(" -s --start Start storaged (default)\n");
|
||||
printf(" --emmc=INTERVAL Set publish interval of emmc lifetime information (in days)\n");
|
||||
printf(" --diskstats=INTERVAL Set publish interval of diskstats (in hours)\n");
|
||||
printf(" --uidio=INTERVAL Set publish interval of uid io (in hours)\n");
|
||||
printf(" --unit=INTERVAL Set storaged's refresh interval (in seconds)\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
@ -118,10 +120,12 @@ static void help_message(void) {
|
|||
int main(int argc, char** argv) {
|
||||
int flag_main_service = 0;
|
||||
int flag_dump_task = 0;
|
||||
int flag_dump_uid = 0;
|
||||
int flag_config = 0;
|
||||
int unit_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT;
|
||||
int diskstats_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH;
|
||||
int emmc_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH;
|
||||
int uid_io_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_ALERT;
|
||||
int fd_emmc = -1;
|
||||
int opt;
|
||||
|
||||
|
@ -131,12 +135,14 @@ int main(int argc, char** argv) {
|
|||
{"start", no_argument, 0, 's'},
|
||||
{"kill", no_argument, 0, 'k'},
|
||||
{"dump", no_argument, 0, 'd'},
|
||||
{"uid", no_argument, 0, 'u'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"unit", required_argument, 0, 0 },
|
||||
{"diskstats", required_argument, 0, 0 },
|
||||
{"emmc", required_argument, 0, 0 }
|
||||
{"emmc", required_argument, 0, 0 },
|
||||
{"uidio", required_argument, 0, 0 }
|
||||
};
|
||||
opt = getopt_long(argc, argv, ":skdh0", long_options, &opt_idx);
|
||||
opt = getopt_long(argc, argv, ":skdhu0", long_options, &opt_idx);
|
||||
if (opt == -1) {
|
||||
break;
|
||||
}
|
||||
|
@ -165,7 +171,15 @@ int main(int argc, char** argv) {
|
|||
|
||||
} else if (strcmp(long_options[opt_idx].name, "emmc") == 0) {
|
||||
emmc_interval = atoi(optarg) * DAY_TO_SEC;
|
||||
if (diskstats_interval == 0) {
|
||||
if (emmc_interval == 0) {
|
||||
fprintf(stderr, "Invalid argument. Option %s requires an integer argument greater than 0.\n",
|
||||
long_options[opt_idx].name);
|
||||
help_message();
|
||||
return -1;
|
||||
}
|
||||
} else if (strcmp(long_options[opt_idx].name, "uidio") == 0) {
|
||||
uid_io_interval = atoi(optarg) * HOUR_TO_SEC;
|
||||
if (uid_io_interval == 0) {
|
||||
fprintf(stderr, "Invalid argument. Option %s requires an integer argument greater than 0.\n",
|
||||
long_options[opt_idx].name);
|
||||
help_message();
|
||||
|
@ -187,6 +201,9 @@ int main(int argc, char** argv) {
|
|||
case 'd':
|
||||
flag_dump_task = 1;
|
||||
break;
|
||||
case 'u':
|
||||
flag_dump_uid = 1;
|
||||
break;
|
||||
case 'h':
|
||||
help_message();
|
||||
return 0;
|
||||
|
@ -230,6 +247,7 @@ int main(int argc, char** argv) {
|
|||
storaged.set_unit_interval(unit_interval);
|
||||
storaged.set_diskstats_interval(diskstats_interval);
|
||||
storaged.set_emmc_interval(emmc_interval);
|
||||
storaged.set_uid_io_interval(uid_io_interval);
|
||||
}
|
||||
|
||||
// Start the main thread of storaged
|
||||
|
@ -278,5 +296,24 @@ int main(int argc, char** argv) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (flag_dump_uid) {
|
||||
sp<IStoraged> storaged_service = get_storaged_service();
|
||||
if (storaged_service == NULL) {
|
||||
fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
|
||||
return -1;
|
||||
}
|
||||
std::vector<struct uid_info> res = storaged_service->dump_uids(NULL);
|
||||
|
||||
if (res.size() == 0) {
|
||||
fprintf(stderr, "UID I/O is not readable in this version of kernel.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sort_running_uids_info(res);
|
||||
log_console_running_uids_info(res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -174,9 +174,12 @@ storaged_t::storaged_t(void) {
|
|||
}
|
||||
}
|
||||
|
||||
mConfig.proc_uid_io_available = (access(UID_IO_STATS_PATH, R_OK) == 0);
|
||||
|
||||
mConfig.periodic_chores_interval_unit = DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT;
|
||||
mConfig.periodic_chores_interval_disk_stats_publish = DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH;
|
||||
mConfig.periodic_chores_interval_emmc_info_publish = DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH;
|
||||
mUidm.set_periodic_chores_interval(DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_ALERT);
|
||||
|
||||
mStarttime = time(NULL);
|
||||
}
|
||||
|
@ -202,5 +205,10 @@ void storaged_t::event(void) {
|
|||
mEmmcInfo.publish();
|
||||
}
|
||||
|
||||
if (mConfig.proc_uid_io_available && mTimer &&
|
||||
(mTimer % mUidm.get_periodic_chores_interval()) == 0) {
|
||||
mUidm.report();
|
||||
}
|
||||
|
||||
mTimer += mConfig.periodic_chores_interval_unit;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <binder/IBinder.h>
|
||||
|
@ -41,6 +43,22 @@ std::vector<struct task_info> BpStoraged::dump_tasks(const char* /*option*/) {
|
|||
return res;
|
||||
}
|
||||
|
||||
std::vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
|
||||
|
||||
remote()->transact(DUMPUIDS, data, &reply);
|
||||
|
||||
uint32_t res_size = reply.readInt32();
|
||||
std::vector<struct uid_info> res(res_size);
|
||||
for (auto&& uid : res) {
|
||||
uid.uid = reply.readInt32();
|
||||
uid.name = reply.readCString();
|
||||
reply.read(&uid.io, sizeof(uid.io));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
IMPLEMENT_META_INTERFACE(Storaged, "Storaged");
|
||||
|
||||
status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
|
||||
|
@ -57,14 +75,36 @@ status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply
|
|||
return NO_ERROR;
|
||||
}
|
||||
break;
|
||||
case DUMPUIDS: {
|
||||
std::vector<struct uid_info> res = dump_uids(NULL);
|
||||
reply->writeInt32(res.size());
|
||||
for (auto uid : res) {
|
||||
reply->writeInt32(uid.uid);
|
||||
reply->writeCString(uid.name.c_str());
|
||||
reply->write(&uid.io, sizeof(uid.io));
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return BBinder::onTransact(code, data, reply, flags);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<struct task_info> Storaged::dump_tasks(const char* /* option */) {
|
||||
return storaged.get_tasks();
|
||||
}
|
||||
|
||||
std::vector<struct uid_info> Storaged::dump_uids(const char* /* option */) {
|
||||
std::vector<struct uid_info> uids_v;
|
||||
std::unordered_map<uint32_t, struct uid_info> uids_m = storaged.get_uids();
|
||||
|
||||
for (const auto& it : uids_m) {
|
||||
uids_v.push_back(it.second);
|
||||
}
|
||||
return uids_v;
|
||||
}
|
||||
|
||||
sp<IStoraged> get_storaged_service() {
|
||||
sp<IServiceManager> sm = defaultServiceManager();
|
||||
if (sm == NULL) return NULL;
|
||||
|
@ -75,4 +115,4 @@ sp<IStoraged> get_storaged_service() {
|
|||
sp<IStoraged> storaged = interface_cast<IStoraged>(binder);
|
||||
|
||||
return storaged;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "storaged"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/macros.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <log/log_event_list.h>
|
||||
#include <packagelistparser/packagelistparser.h>
|
||||
|
||||
#include "storaged.h"
|
||||
#include "storaged_uid_monitor.h"
|
||||
|
||||
static const uint64_t io_alert_threshold = 1024 * 1024 * 1024; // 1GB
|
||||
|
||||
using namespace android;
|
||||
using namespace android::base;
|
||||
|
||||
static bool packagelist_parse_cb(pkg_info* info, void* userdata)
|
||||
{
|
||||
std::unordered_map<uint32_t, struct uid_info>* uids =
|
||||
reinterpret_cast<std::unordered_map<uint32_t, struct uid_info>*>(userdata);
|
||||
|
||||
if (uids->find(info->uid) != uids->end()) {
|
||||
(*uids)[info->uid].name = info->name;
|
||||
}
|
||||
|
||||
packagelist_free(info);
|
||||
return true;
|
||||
}
|
||||
|
||||
void uid_monitor::set_last_uids(std::unordered_map<uint32_t, struct uid_info>&& uids,
|
||||
uint64_t ts)
|
||||
{
|
||||
last_uids = uids;
|
||||
last_report_ts = ts;
|
||||
}
|
||||
|
||||
std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uids()
|
||||
{
|
||||
std::unordered_map<uint32_t, struct uid_info> uids;
|
||||
std::string buffer;
|
||||
if (!android::base::ReadFileToString(UID_IO_STATS_PATH, &buffer)) {
|
||||
PLOG_TO(SYSTEM, ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
|
||||
return uids;
|
||||
}
|
||||
|
||||
std::stringstream ss(buffer);
|
||||
struct uid_info u;
|
||||
bool refresh_uid = false;
|
||||
|
||||
while (ss >> u.uid) {
|
||||
ss >> u.io[UID_FOREGROUND].rchar >> u.io[UID_FOREGROUND].wchar
|
||||
>> u.io[UID_FOREGROUND].read_bytes >> u.io[UID_FOREGROUND].write_bytes
|
||||
>> u.io[UID_BACKGROUND].rchar >> u.io[UID_BACKGROUND].wchar
|
||||
>> u.io[UID_BACKGROUND].read_bytes >> u.io[UID_BACKGROUND].write_bytes;
|
||||
|
||||
if (!ss.good()) {
|
||||
ss.clear(std::ios_base::badbit);
|
||||
break;
|
||||
}
|
||||
|
||||
if (last_uids.find(u.uid) == last_uids.end()) {
|
||||
refresh_uid = true;
|
||||
u.name = std::to_string(u.uid);
|
||||
} else {
|
||||
u.name = last_uids[u.uid].name;
|
||||
}
|
||||
uids[u.uid] = u;
|
||||
}
|
||||
|
||||
if (!ss.eof() || ss.bad()) {
|
||||
uids.clear();
|
||||
LOG_TO(SYSTEM, ERROR) << "read UID IO stats failed";
|
||||
}
|
||||
|
||||
if (refresh_uid) {
|
||||
packagelist_parse(packagelist_parse_cb, &uids);
|
||||
}
|
||||
|
||||
return uids;
|
||||
}
|
||||
|
||||
void uid_monitor::report()
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
// Use monotonic to exclude suspend time so that we measure IO bytes/sec
|
||||
// when system is running.
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
|
||||
PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t curr_ts = ts.tv_sec * NS_PER_SEC + ts.tv_nsec;
|
||||
uint64_t ts_delta = curr_ts - last_report_ts;
|
||||
uint64_t adjusted_threshold = io_alert_threshold * ((double)ts_delta / interval / NS_PER_SEC);
|
||||
|
||||
std::unordered_map<uint32_t, struct uid_info> uids = get_uids();
|
||||
if (uids.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& it : uids) {
|
||||
const struct uid_info& uid = it.second;
|
||||
uint64_t bg_read_delta = uid.io[UID_BACKGROUND].read_bytes -
|
||||
last_uids[uid.uid].io[UID_BACKGROUND].read_bytes;
|
||||
uint64_t bg_write_delta = uid.io[UID_BACKGROUND].write_bytes -
|
||||
last_uids[uid.uid].io[UID_BACKGROUND].write_bytes;
|
||||
|
||||
if (bg_read_delta + bg_write_delta >= adjusted_threshold) {
|
||||
android_log_event_list(EVENTLOGTAG_UID_IO_ALERT)
|
||||
<< uid.name << bg_read_delta << bg_write_delta
|
||||
<< uint64_t(ts_delta / NS_PER_SEC) << LOG_ID_EVENTS;
|
||||
}
|
||||
}
|
||||
|
||||
set_last_uids(std::move(uids), curr_ts);
|
||||
}
|
||||
|
||||
uid_monitor::uid_monitor()
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
|
||||
PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
|
||||
return;
|
||||
}
|
||||
last_report_ts = ts.tv_sec * NS_PER_SEC + ts.tv_nsec;
|
||||
}
|
|
@ -435,6 +435,54 @@ void log_console_running_tasks_info(std::vector<struct task_info> tasks) {
|
|||
fflush(stdout);
|
||||
}
|
||||
|
||||
static bool cmp_uid_info(struct uid_info l, struct uid_info r) {
|
||||
// Compare background I/O first.
|
||||
for (int i = UID_STATS_SIZE - 1; i >= 0; i--) {
|
||||
uint64_t l_bytes = l.io[i].read_bytes + l.io[i].write_bytes;
|
||||
uint64_t r_bytes = r.io[i].read_bytes + r.io[i].write_bytes;
|
||||
uint64_t l_chars = l.io[i].rchar + l.io[i].wchar;
|
||||
uint64_t r_chars = r.io[i].rchar + r.io[i].wchar;
|
||||
|
||||
if (l_bytes != r_bytes) {
|
||||
return l_bytes > r_bytes;
|
||||
}
|
||||
if (l_chars != r_chars) {
|
||||
return l_chars > r_chars;
|
||||
}
|
||||
}
|
||||
|
||||
return l.name < r.name;
|
||||
}
|
||||
|
||||
void sort_running_uids_info(std::vector<struct uid_info> &uids) {
|
||||
std::sort(uids.begin(), uids.end(), cmp_uid_info);
|
||||
}
|
||||
|
||||
// Logging functions
|
||||
void log_console_running_uids_info(std::vector<struct uid_info> uids) {
|
||||
// Sample Output:
|
||||
// Application FG Read FG Write FG Read FG Write BG Read BG Write BG Read BG Write
|
||||
// NAME/UID Characters Characters Bytes Bytes Characters Characters Bytes Bytes
|
||||
// ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
|
||||
// com.google.android.gsf.login 0 0 0 0 57195097 5137089 176386048 6512640
|
||||
// com.google.android.googlequicksearchbox 0 0 0 0 4196821 12123468 34295808 13225984
|
||||
// 1037 4572 537 0 0 131352 5145643 34263040 5144576
|
||||
// com.google.android.youtube 2182 70 0 0 63969383 482939 38731776 466944
|
||||
|
||||
// Title
|
||||
printf("Per-UID I/O stats\n");
|
||||
printf(" Application FG Read FG Write FG Read FG Write BG Read BG Write BG Read BG Write\n"
|
||||
" NAME/UID Characters Characters Bytes Bytes Characters Characters Bytes Bytes\n"
|
||||
" ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------\n");
|
||||
|
||||
for (const auto& uid : uids) {
|
||||
printf("%50s%15ju%15ju%15ju%15ju%15ju%15ju%15ju%15ju\n", uid.name.c_str(),
|
||||
uid.io[0].rchar, uid.io[0].wchar, uid.io[0].read_bytes, uid.io[0].write_bytes,
|
||||
uid.io[1].rchar, uid.io[1].wchar, uid.io[1].read_bytes, uid.io[1].write_bytes);
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
|
||||
// skip if the input structure are all zeros
|
||||
|
|
|
@ -40,6 +40,6 @@ LOCAL_MODULE := $(test_module_prefix)unit-tests
|
|||
LOCAL_MODULE_TAGS := $(test_tags)
|
||||
LOCAL_CFLAGS += $(test_c_flags)
|
||||
LOCAL_STATIC_LIBRARIES := libstoraged
|
||||
LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
|
||||
LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libpackagelistparser
|
||||
LOCAL_SRC_FILES := $(test_src_files)
|
||||
include $(BUILD_NATIVE_TEST)
|
||||
|
|
Loading…
Reference in New Issue