Merge "storaged: monitor per-uid IO usage"
diff --git a/storaged/Android.mk b/storaged/Android.mk
index db97040..0e8b574 100644
--- a/storaged/Android.mk
+++ b/storaged/Android.mk
@@ -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
diff --git a/storaged/EventLogTags.logtags b/storaged/EventLogTags.logtags
index ee92dd1..71fda25 100644
--- a/storaged/EventLogTags.logtags
+++ b/storaged/EventLogTags.logtags
@@ -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)
\ No newline at end of file
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index 521cc9e..7fa9958 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -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 @@
#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 @@
#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 @@
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 @@
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 @@
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_ */
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
index 64a9c81..220038c 100644
--- a/storaged/include/storaged_service.h
+++ b/storaged/include/storaged_service.h
@@ -31,9 +31,11 @@
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 @@
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 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();
diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h
new file mode 100644
index 0000000..eceb7fd
--- /dev/null
+++ b/storaged/include/storaged_uid_monitor.h
@@ -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_ */
diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h
index 83538c2..bb14708 100644
--- a/storaged/include/storaged_utils.h
+++ b/storaged/include/storaged_utils.h
@@ -34,12 +34,15 @@
// 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_ */
\ No newline at end of file
+#endif /* _STORAGED_UTILS_H_ */
diff --git a/storaged/main.cpp b/storaged/main.cpp
index 0cb0f5f..9151574 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -105,9 +105,11 @@
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 @@
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 @@
{"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 @@
} 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 @@
case 'd':
flag_dump_task = 1;
break;
+ case 'u':
+ flag_dump_uid = 1;
+ break;
case 'h':
help_message();
return 0;
@@ -230,6 +247,7 @@
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 @@
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;
}
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index e4d3e68..0c53f44 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -174,9 +174,12 @@
}
}
+ 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 @@
mEmmcInfo.publish();
}
+ if (mConfig.proc_uid_io_available && mTimer &&
+ (mTimer % mUidm.get_periodic_chores_interval()) == 0) {
+ mUidm.report();
+ }
+
mTimer += mConfig.periodic_chores_interval_unit;
}
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
index aa38ceb..2a81aef 100644
--- a/storaged/storaged_service.cpp
+++ b/storaged/storaged_service.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <stdint.h>
+
#include <vector>
#include <binder/IBinder.h>
@@ -41,6 +43,22 @@
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 @@
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> storaged = interface_cast<IStoraged>(binder);
return storaged;
-}
\ No newline at end of file
+}
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
new file mode 100644
index 0000000..4105dae
--- /dev/null
+++ b/storaged/storaged_uid_monitor.cpp
@@ -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;
+}
diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp
index c845ac4..6e4ddb6 100644
--- a/storaged/storaged_utils.cpp
+++ b/storaged/storaged_utils.cpp
@@ -435,6 +435,54 @@
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
diff --git a/storaged/tests/Android.mk b/storaged/tests/Android.mk
index 4a0e45c..26d04b1 100644
--- a/storaged/tests/Android.mk
+++ b/storaged/tests/Android.mk
@@ -40,6 +40,6 @@
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)