Merge "Add a new unwind method on error." am: 9b91324cb0 am: fb8ea2626b am: e288e330c8
am: c3e8c82d84
Change-Id: I8771b4a6cc14c609aeddea6bd2ce3adc38b50a98
diff --git a/CleanSpec.mk b/CleanSpec.mk
index d5b7554..e2e9577 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -70,3 +70,4 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/storageproxyd)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/tipc-test)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/trusty_keymaster_tipc)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/root)
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index fc88217..85a593f 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -34,23 +34,35 @@
#include "fs_mgr_priv.h"
#include "cryptfs.h"
-static int format_ext4(char *fs_blkdev, char *fs_mnt_point, bool crypt_footer)
+static int get_dev_sz(char *fs_blkdev, uint64_t *dev_sz)
{
- uint64_t dev_sz;
- int fd, rc = 0;
+ int fd;
- if ((fd = open(fs_blkdev, O_WRONLY)) < 0) {
+ if ((fd = open(fs_blkdev, O_RDONLY)) < 0) {
PERROR << "Cannot open block device";
return -1;
}
- if ((ioctl(fd, BLKGETSIZE64, &dev_sz)) == -1) {
+ if ((ioctl(fd, BLKGETSIZE64, dev_sz)) == -1) {
PERROR << "Cannot get block device size";
close(fd);
return -1;
}
close(fd);
+ return 0;
+}
+
+static int format_ext4(char *fs_blkdev, char *fs_mnt_point, bool crypt_footer)
+{
+ uint64_t dev_sz;
+ int rc = 0;
+ int status;
+
+ rc = get_dev_sz(fs_blkdev, &dev_sz);
+ if (rc) {
+ return rc;
+ }
/* Format the partition using the calculated length */
if (crypt_footer) {
@@ -85,9 +97,25 @@
return rc;
}
-static int format_f2fs(char *fs_blkdev)
+static int format_f2fs(char *fs_blkdev, uint64_t dev_sz, bool crypt_footer)
{
- const char* const args[] = {"/system/bin/make_f2fs", "-f", "-O encrypt", fs_blkdev, nullptr};
+ int status;
+
+ if (!dev_sz) {
+ int rc = get_dev_sz(fs_blkdev, &dev_sz);
+ if (rc) {
+ return rc;
+ }
+ }
+
+ /* Format the partition using the calculated length */
+ if (crypt_footer) {
+ dev_sz -= CRYPT_FOOTER_OFFSET;
+ }
+
+ std::string size_str = std::to_string(dev_sz / 4096);
+ const char* const args[] = {
+ "/system/bin/make_f2fs", "-f", "-O", "encrypt", fs_blkdev, size_str.c_str(), nullptr};
return android_fork_execvp_ext(arraysize(args), const_cast<char**>(args), NULL, true,
LOG_KLOG, true, nullptr, nullptr, 0);
@@ -101,7 +129,7 @@
<< " as '" << fstab->fs_type << "'";
if (!strncmp(fstab->fs_type, "f2fs", 4)) {
- rc = format_f2fs(fstab->blk_device);
+ rc = format_f2fs(fstab->blk_device, fstab->length, crypt_footer);
} else if (!strncmp(fstab->fs_type, "ext4", 4)) {
rc = format_ext4(fstab->blk_device, fstab->mount_point, crypt_footer);
} else {
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 7c22199..1d3e5ea 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -80,7 +80,6 @@
{ 00775, AID_ROOT, AID_ROOT, 0, "data/preloads" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
{ 00755, AID_ROOT, AID_SYSTEM, 0, "mnt" },
- { 00755, AID_ROOT, AID_ROOT, 0, "root" },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin" },
{ 00777, AID_ROOT, AID_ROOT, 0, "sdcard" },
{ 00751, AID_ROOT, AID_SDCARD_R, 0, "storage" },
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index d01708d..3813e6e 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -95,6 +95,8 @@
size_t len);
int __android_log_bswrite(int32_t tag, const char* payload);
+int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len);
+
#define android_bWriteLog(tag, payload, len) \
__android_log_bwrite(tag, payload, len)
#define android_btWriteLog(tag, type, payload, len) \
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
index 7bfa277..c44f5a2 100644
--- a/liblog/include/log/log_id.h
+++ b/liblog/include/log/log_id.h
@@ -31,8 +31,9 @@
LOG_ID_EVENTS = 2,
LOG_ID_SYSTEM = 3,
LOG_ID_CRASH = 4,
- LOG_ID_SECURITY = 5,
- LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */
+ LOG_ID_STATS = 5,
+ LOG_ID_SECURITY = 6,
+ LOG_ID_KERNEL = 7, /* place last, third-parties can not use it */
LOG_ID_MAX
} log_id_t;
diff --git a/liblog/log_event_list.c b/liblog/log_event_list.c
index 59ea5ef..a59cb87 100644
--- a/liblog/log_event_list.c
+++ b/liblog/log_event_list.c
@@ -301,7 +301,7 @@
const char* msg;
ssize_t len;
- if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY)) {
+ if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY) && (id != LOG_ID_STATS)) {
return -EINVAL;
}
@@ -326,7 +326,9 @@
}
return (id == LOG_ID_EVENTS)
? __android_log_bwrite(context->tag, msg, len)
- : __android_log_security_bwrite(context->tag, msg, len);
+ : ((id == LOG_ID_STATS)
+ ? __android_log_stats_bwrite(context->tag, msg, len)
+ : __android_log_security_bwrite(context->tag, msg, len));
}
LIBLOG_ABI_PRIVATE int android_log_write_list_buffer(android_log_context ctx,
diff --git a/liblog/logger_name.c b/liblog/logger_name.c
index a5a83e0..979b82d 100644
--- a/liblog/logger_name.c
+++ b/liblog/logger_name.c
@@ -28,6 +28,7 @@
[LOG_ID_EVENTS] = "events",
[LOG_ID_SYSTEM] = "system",
[LOG_ID_CRASH] = "crash",
+ [LOG_ID_STATS] = "stats",
[LOG_ID_SECURITY] = "security",
[LOG_ID_KERNEL] = "kernel",
/* clang-format on */
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index 84feb20..589ce84 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.c
@@ -546,6 +546,19 @@
return write_to_log(LOG_ID_EVENTS, vec, 2);
}
+LIBLOG_ABI_PUBLIC int __android_log_stats_bwrite(int32_t tag,
+ const void* payload,
+ size_t len) {
+ struct iovec vec[2];
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = (void*)payload;
+ vec[1].iov_len = len;
+
+ return write_to_log(LOG_ID_STATS, vec, 2);
+}
+
LIBLOG_ABI_PUBLIC int __android_log_security_bwrite(int32_t tag,
const void* payload,
size_t len) {
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 3d56472..5ef220c 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -1126,8 +1126,9 @@
}
if (found) continue;
- bool binary =
- !strcmp(name, "events") || !strcmp(name, "security");
+ bool binary = !strcmp(name, "events") ||
+ !strcmp(name, "security") ||
+ !strcmp(name, "stats");
log_device_t* d = new log_device_t(name, binary);
if (dev) {
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index cfcbaa5..86572f1 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -374,7 +374,7 @@
reinterpret_cast<android_log_event_string_t*>(buffer);
event->header.tag = htole32(AUDITD_LOG_TAG);
event->type = EVENT_TYPE_STRING;
- event->length = htole32(message_len);
+ event->length = htole32(str_len);
memcpy(event->data, str, str_len - bug_metadata.length());
memcpy(event->data + str_len - bug_metadata.length(),
bug_metadata.c_str(), bug_metadata.length());
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index d2df68e..0bd4008 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -101,6 +101,14 @@
return false;
}
+ if (header->id == LOG_ID_STATS) {
+ // Only accept logging from *ManagerService in system server
+ // Will add more later as we see fit.
+ if (cred->uid != AID_SYSTEM && cred->gid != AID_SYSTEM) {
+ return false;
+ }
+ }
+
// Check credential validity, acquire corrected details if not supplied.
if (cred->pid == 0) {
cred->pid = logbuf ? logbuf->tidToPid(header->tid)
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 3377716..5c1ffb3 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -142,7 +142,7 @@
#
# create some directories (some are mount points) and symlinks
LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
- sbin dev proc sys system data oem acct config storage mnt root $(BOARD_ROOT_EXTRA_FOLDERS)); \
+ sbin dev proc sys system data oem acct config storage mnt $(BOARD_ROOT_EXTRA_FOLDERS)); \
ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
ln -sf /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
diff --git a/storaged/Android.bp b/storaged/Android.bp
new file mode 100644
index 0000000..25b433c
--- /dev/null
+++ b/storaged/Android.bp
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+cc_defaults {
+ name: "storaged_defaults",
+
+ shared_libs: [
+ "libbase",
+ "libbatteryservice",
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ "libsysutils",
+ "libutils",
+ "libz",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ "-Wno-unused-parameter"
+ ],
+}
+
+cc_library_static {
+ name: "libstoraged",
+
+ defaults: ["storaged_defaults"],
+
+ srcs: [
+ "storaged.cpp",
+ "storaged_info.cpp",
+ "storaged_service.cpp",
+ "storaged_utils.cpp",
+ "storaged_uid_monitor.cpp",
+ "storaged.proto",
+ ],
+
+ logtags: ["EventLogTags.logtags"],
+
+ proto: {
+ type: "lite",
+ export_proto_headers: true,
+ },
+
+ export_include_dirs: ["include"],
+}
+
+cc_binary {
+ name: "storaged",
+
+ defaults: ["storaged_defaults"],
+
+ init_rc: ["storaged.rc"],
+
+ srcs: ["main.cpp"],
+
+ static_libs: ["libstoraged"],
+}
+
+/*
+ * Run with:
+ * adb shell /data/nativetest/storaged-unit-tests/storaged-unit-tests
+ */
+cc_test {
+ name: "storaged-unit-tests",
+
+ defaults: ["storaged_defaults"],
+
+ srcs: ["tests/storaged_test.cpp"],
+
+ static_libs: ["libstoraged"],
+}
\ No newline at end of file
diff --git a/storaged/Android.mk b/storaged/Android.mk
deleted file mode 100644
index a1abe0f..0000000
--- a/storaged/Android.mk
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2016 The Android Open Source Project
-
-LOCAL_PATH := $(call my-dir)
-
-LIBSTORAGED_SHARED_LIBRARIES := \
- libbinder \
- libbase \
- libutils \
- libcutils \
- liblog \
- libsysutils \
- libbatteryservice \
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- storaged.cpp \
- storaged_info.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
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SHARED_LIBRARIES := $(LIBSTORAGED_SHARED_LIBRARIES)
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := storaged
-LOCAL_INIT_RC := storaged.rc
-LOCAL_SRC_FILES := main.cpp
-# libstoraged is an internal static library, only main.cpp and storaged_test.cpp should be using it
-LOCAL_STATIC_LIBRARIES := libstoraged
-LOCAL_SHARED_LIBRARIES := $(LIBSTORAGED_SHARED_LIBRARIES)
-LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
-LOCAL_C_INCLUDES := external/googletest/googletest/include
-
-include $(BUILD_EXECUTABLE)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index fa68406..bd99361 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -97,22 +97,6 @@
uint32_t queue; // I/Os in queue
};
-#define CMD_MAX_LEN ( 64 )
-struct task_info {
- uint32_t pid; // task id
- uint64_t rchar; // characters read
- uint64_t wchar; // characters written
- uint64_t syscr; // read syscalls
- uint64_t syscw; // write syscalls
- uint64_t read_bytes; // bytes read (from storage layer)
- uint64_t write_bytes; // bytes written (to storage layer)
- uint64_t cancelled_write_bytes; // cancelled write byte by truncate
-
- uint64_t starttime; // start time of task
-
- char cmd[CMD_MAX_LEN]; // filename of the executable
-};
-
class lock_t {
sem_t* mSem;
public:
diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h
index 901a872..86d3c28 100644
--- a/storaged/include/storaged_uid_monitor.h
+++ b/storaged/include/storaged_uid_monitor.h
@@ -41,7 +41,7 @@
IO_TYPES = 2
};
-struct uid_io_stats {
+struct io_stats {
uint64_t rchar; // characters read
uint64_t wchar; // characters written
uint64_t read_bytes; // bytes read (from storage layer)
@@ -49,14 +49,30 @@
uint64_t fsync; // number of fsync syscalls
};
+struct task_info {
+ std::string comm;
+ pid_t pid;
+ struct io_stats io[UID_STATS];
+ bool parse_task_io_stats(std::string&& s);
+};
+
struct uid_info {
uint32_t uid; // user id
std::string name; // package name
- struct uid_io_stats io[UID_STATS]; // [0]:foreground [1]:background
+ struct io_stats io[UID_STATS]; // [0]:foreground [1]:background
+ std::unordered_map<uint32_t, task_info> tasks; // mapped from pid
+ bool parse_uid_io_stats(std::string&& s);
+};
+
+struct io_usage {
+ uint64_t bytes[IO_TYPES][UID_STATS][CHARGER_STATS];
+ bool is_zero() const;
};
struct uid_io_usage {
- uint64_t bytes[IO_TYPES][UID_STATS][CHARGER_STATS];
+ struct io_usage uid_ios;
+ // mapped from task comm to task io usage
+ std::map<std::string, struct io_usage> task_ios;
};
struct uid_record {
@@ -76,13 +92,15 @@
// current io usage for next report, app name -> uid_io_usage
std::unordered_map<std::string, struct uid_io_usage> curr_io_stats;
// io usage records, end timestamp -> {start timestamp, vector of records}
- std::map<uint64_t, struct uid_records> records;
+ std::map<uint64_t, struct uid_records> io_history;
// charger ON/OFF
charger_stat_t charger_stat;
// protects curr_io_stats, last_uid_io_stats, records and charger_stat
sem_t um_lock;
// start time for IO records
uint64_t start_ts;
+ // protobuf file for io_history
+ static const std::string io_history_proto_file;
// reads from /proc/uid_io/stats
std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats_locked();
@@ -90,6 +108,10 @@
void add_records_locked(uint64_t curr_ts);
// updates curr_io_stats and set last_uid_io_stats
void update_curr_io_stats_locked();
+ // restores io_history from protobuf file
+ void load_io_history_from_proto();
+ // converts io_history to protobuf and writes to a file
+ void flush_io_history_to_proto();
public:
uid_monitor();
diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h
index 2161c40..1435707 100644
--- a/storaged/include/storaged_utils.h
+++ b/storaged/include/storaged_utils.h
@@ -35,7 +35,7 @@
void sort_running_uids_info(std::vector<struct uid_info> &uids);
// Logging
-void log_console_running_uids_info(std::vector<struct uid_info> uids);
+void log_console_running_uids_info(const std::vector<struct uid_info>& uids, bool flag_dump_task);
void log_debug_disk_perf(struct disk_perf* perf, const char* type);
diff --git a/storaged/main.cpp b/storaged/main.cpp
index 6b82904..49bd916 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -63,13 +63,15 @@
static void help_message(void) {
printf("usage: storaged [OPTION]\n");
printf(" -u --uid Dump uid I/O usage to stdout\n");
+ printf(" -t --task Dump task I/O usage to stdout\n");
printf(" -s --start Start storaged (default)\n");
fflush(stdout);
}
int main(int argc, char** argv) {
- int flag_main_service = 0;
- int flag_dump_uid = 0;
+ bool flag_main_service = false;
+ bool flag_dump_uid = false;
+ bool flag_dump_task = false;
int opt;
for (;;) {
@@ -78,19 +80,23 @@
{"start", no_argument, 0, 's'},
{"kill", no_argument, 0, 'k'},
{"uid", no_argument, 0, 'u'},
+ {"task", no_argument, 0, 't'},
{"help", no_argument, 0, 'h'}
};
- opt = getopt_long(argc, argv, ":skdhu0", long_options, &opt_idx);
+ opt = getopt_long(argc, argv, ":skdhu0t", long_options, &opt_idx);
if (opt == -1) {
break;
}
switch (opt) {
case 's':
- flag_main_service = 1;
+ flag_main_service = true;
break;
case 'u':
- flag_dump_uid = 1;
+ flag_dump_uid = true;
+ break;
+ case 't':
+ flag_dump_task = true;
break;
case 'h':
help_message();
@@ -104,10 +110,10 @@
}
if (argc == 1) {
- flag_main_service = 1;
+ flag_main_service = true;
}
- if (flag_main_service && flag_dump_uid) {
+ if (flag_main_service && (flag_dump_uid || flag_dump_task)) {
fprintf(stderr, "Invalid arguments. Option \"start\" and \"dump\" cannot be used together.\n");
help_message();
return -1;
@@ -130,7 +136,7 @@
return 0;
}
- if (flag_dump_uid) {
+ if (flag_dump_uid || flag_dump_task) {
sp<IStoraged> storaged_service = get_storaged_service();
if (storaged_service == NULL) {
fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
@@ -144,7 +150,7 @@
}
sort_running_uids_info(res);
- log_console_running_uids_info(res);
+ log_console_running_uids_info(res, flag_dump_task);
return 0;
}
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 06afea6..49592eb 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -243,7 +243,7 @@
if (mConfig.proc_uid_io_available && mTimer &&
(mTimer % mConfig.periodic_chores_interval_uid_io) == 0) {
- mUidm.report();
+ mUidm.report();
}
mTimer += mConfig.periodic_chores_interval_unit;
diff --git a/storaged/storaged.proto b/storaged/storaged.proto
new file mode 100644
index 0000000..5222846
--- /dev/null
+++ b/storaged/storaged.proto
@@ -0,0 +1,42 @@
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+package storaged_proto;
+option java_package = "com.android.storaged.proto";
+option java_outer_classname = "Storaged";
+
+message IOUsage {
+ optional uint64 rd_fg_chg_on = 1;
+ optional uint64 rd_fg_chg_off = 2;
+ optional uint64 rd_bg_chg_on = 3;
+ optional uint64 rd_bg_chg_off = 4;
+ optional uint64 wr_fg_chg_on = 5;
+ optional uint64 wr_fg_chg_off = 6;
+ optional uint64 wr_bg_chg_on = 7;
+ optional uint64 wr_bg_chg_off = 8;
+}
+
+message TaskIOUsage {
+ optional string task_name = 1;
+ optional IOUsage ios = 2;
+}
+
+message UidRecord {
+ optional string uid_name = 1;
+ optional IOUsage uid_io = 2;
+ repeated TaskIOUsage task_io = 3;
+}
+
+message UidIORecords {
+ optional uint64 start_ts = 1;
+ repeated UidRecord entries = 2;
+}
+
+message UidIOItem {
+ optional uint64 end_ts = 1;
+ optional UidIORecords records = 2;
+}
+
+message UidIOHistoryProto {
+ optional uint32 crc = 1;
+ repeated UidIOItem items = 2;
+}
diff --git a/storaged/storaged.rc b/storaged/storaged.rc
index a24c7fb..bd4022b 100644
--- a/storaged/storaged.rc
+++ b/storaged/storaged.rc
@@ -1,7 +1,11 @@
+on post-fs-data
+ mkdir /data/misc/storaged 0700 root root
+ restorecon /data/misc/storaged
+
service storaged /system/bin/storaged
class main
priority 10
file /d/mmc0/mmc0:0001/ext_csd r
writepid /dev/cpuset/system-background/tasks
user root
- group package_info
\ No newline at end of file
+ group package_info
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
index b1d3bfd..4364c6a 100644
--- a/storaged/storaged_service.cpp
+++ b/storaged/storaged_service.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <inttypes.h>
#include <stdint.h>
#include <vector>
@@ -47,6 +48,15 @@
uid.uid = reply.readInt32();
uid.name = reply.readCString();
reply.read(&uid.io, sizeof(uid.io));
+
+ uint32_t tasks_size = reply.readInt32();
+ for (uint32_t i = 0; i < tasks_size; i++) {
+ struct task_info task;
+ task.pid = reply.readInt32();
+ task.comm = reply.readCString();
+ reply.read(&task.io, sizeof(task.io));
+ uid.tasks[task.pid] = task;
+ }
}
return res;
}
@@ -59,10 +69,17 @@
return BAD_TYPE;
std::vector<struct uid_info> res = dump_uids(NULL);
reply->writeInt32(res.size());
- for (auto uid : res) {
+ for (const auto& uid : res) {
reply->writeInt32(uid.uid);
reply->writeCString(uid.name.c_str());
reply->write(&uid.io, sizeof(uid.io));
+
+ reply->writeInt32(uid.tasks.size());
+ for (const auto& task_it : uid.tasks) {
+ reply->writeInt32(task_it.first);
+ reply->writeCString(task_it.second.comm.c_str());
+ reply->write(&task_it.second.io, sizeof(task_it.second.io));
+ }
}
return NO_ERROR;
}
@@ -96,6 +113,7 @@
int time_window = 0;
uint64_t threshold = 0;
bool force_report = false;
+ bool debug = false;
for (size_t i = 0; i < args.size(); i++) {
const auto& arg = args[i];
if (arg == String16("--hours")) {
@@ -123,6 +141,10 @@
force_report = true;
continue;
}
+ if (arg == String16("--debug")) {
+ debug = true;
+ continue;
+ }
}
uint64_t last_ts = 0;
@@ -130,22 +152,41 @@
storaged->get_uid_records(hours, threshold, force_report);
for (const auto& it : records) {
if (last_ts != it.second.start_ts) {
- dprintf(fd, "%llu", (unsigned long long)it.second.start_ts);
+ dprintf(fd, "%" PRIu64, it.second.start_ts);
}
- dprintf(fd, ",%llu\n", (unsigned long long)it.first);
+ dprintf(fd, ",%" PRIu64 "\n", it.first);
last_ts = it.first;
for (const auto& record : it.second.entries) {
- dprintf(fd, "%s %ju %ju %ju %ju %ju %ju %ju %ju\n",
+ const struct io_usage& uid_usage = record.ios.uid_ios;
+ dprintf(fd, "%s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+ " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
record.name.c_str(),
- record.ios.bytes[READ][FOREGROUND][CHARGER_OFF],
- record.ios.bytes[WRITE][FOREGROUND][CHARGER_OFF],
- record.ios.bytes[READ][BACKGROUND][CHARGER_OFF],
- record.ios.bytes[WRITE][BACKGROUND][CHARGER_OFF],
- record.ios.bytes[READ][FOREGROUND][CHARGER_ON],
- record.ios.bytes[WRITE][FOREGROUND][CHARGER_ON],
- record.ios.bytes[READ][BACKGROUND][CHARGER_ON],
- record.ios.bytes[WRITE][BACKGROUND][CHARGER_ON]);
+ uid_usage.bytes[READ][FOREGROUND][CHARGER_OFF],
+ uid_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF],
+ uid_usage.bytes[READ][BACKGROUND][CHARGER_OFF],
+ uid_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF],
+ uid_usage.bytes[READ][FOREGROUND][CHARGER_ON],
+ uid_usage.bytes[WRITE][FOREGROUND][CHARGER_ON],
+ uid_usage.bytes[READ][BACKGROUND][CHARGER_ON],
+ uid_usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);
+ if (debug) {
+ for (const auto& task_it : record.ios.task_ios) {
+ const struct io_usage& task_usage = task_it.second;
+ const std::string& comm = task_it.first;
+ dprintf(fd, "-> %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+ " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+ comm.c_str(),
+ task_usage.bytes[READ][FOREGROUND][CHARGER_OFF],
+ task_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF],
+ task_usage.bytes[READ][BACKGROUND][CHARGER_OFF],
+ task_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF],
+ task_usage.bytes[READ][FOREGROUND][CHARGER_ON],
+ task_usage.bytes[WRITE][FOREGROUND][CHARGER_ON],
+ task_usage.bytes[READ][BACKGROUND][CHARGER_ON],
+ task_usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);
+ }
+ }
}
}
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
index dd8bdd6..1c98477 100644
--- a/storaged/storaged_uid_monitor.cpp
+++ b/storaged/storaged_uid_monitor.cpp
@@ -17,8 +17,11 @@
#define LOG_TAG "storaged"
#include <stdint.h>
+#include <stdio.h>
#include <time.h>
+#include <zlib.h>
+#include <fstream>
#include <string>
#include <unordered_map>
@@ -34,12 +37,19 @@
#include "storaged.h"
#include "storaged_uid_monitor.h"
+#include "system/core/storaged/storaged.pb.h"
using namespace android;
using namespace android::base;
using namespace android::content::pm;
+using namespace google::protobuf::io;
+using namespace storaged_proto;
static bool refresh_uid_names;
+static const uint32_t crc_init = 0x5108A4ED; /* STORAGED */
+
+const std::string uid_monitor::io_history_proto_file =
+ "/data/misc/storaged/io_history.proto";
std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats()
{
@@ -47,6 +57,66 @@
return get_uid_io_stats_locked();
};
+/* return true on parse success and false on failure */
+bool uid_info::parse_uid_io_stats(std::string&& s)
+{
+ std::vector<std::string> fields = Split(s, " ");
+ if (fields.size() < 11 ||
+ !ParseUint(fields[0], &uid) ||
+ !ParseUint(fields[1], &io[FOREGROUND].rchar) ||
+ !ParseUint(fields[2], &io[FOREGROUND].wchar) ||
+ !ParseUint(fields[3], &io[FOREGROUND].read_bytes) ||
+ !ParseUint(fields[4], &io[FOREGROUND].write_bytes) ||
+ !ParseUint(fields[5], &io[BACKGROUND].rchar) ||
+ !ParseUint(fields[6], &io[BACKGROUND].wchar) ||
+ !ParseUint(fields[7], &io[BACKGROUND].read_bytes) ||
+ !ParseUint(fields[8], &io[BACKGROUND].write_bytes) ||
+ !ParseUint(fields[9], &io[FOREGROUND].fsync) ||
+ !ParseUint(fields[10], &io[BACKGROUND].fsync)) {
+ LOG_TO(SYSTEM, WARNING) << "Invalid I/O stats: \""
+ << s << "\"";
+ return false;
+ }
+ return true;
+}
+
+/* return true on parse success and false on failure */
+bool task_info::parse_task_io_stats(std::string&& s)
+{
+ std::vector<std::string> fields = Split(s, ",");
+ if (fields.size() < 13 ||
+ !ParseInt(fields[2], &pid) ||
+ !ParseUint(fields[3], &io[FOREGROUND].rchar) ||
+ !ParseUint(fields[4], &io[FOREGROUND].wchar) ||
+ !ParseUint(fields[5], &io[FOREGROUND].read_bytes) ||
+ !ParseUint(fields[6], &io[FOREGROUND].write_bytes) ||
+ !ParseUint(fields[7], &io[BACKGROUND].rchar) ||
+ !ParseUint(fields[8], &io[BACKGROUND].wchar) ||
+ !ParseUint(fields[9], &io[BACKGROUND].read_bytes) ||
+ !ParseUint(fields[10], &io[BACKGROUND].write_bytes) ||
+ !ParseUint(fields[11], &io[FOREGROUND].fsync) ||
+ !ParseUint(fields[12], &io[BACKGROUND].fsync)) {
+ LOG_TO(SYSTEM, WARNING) << "Invalid I/O stats: \""
+ << s << "\"";
+ return false;
+ }
+ comm = fields[1];
+ return true;
+}
+
+bool io_usage::is_zero() const
+{
+ for (int i = 0; i < IO_TYPES; i++) {
+ for (int j = 0; j < UID_STATS; j++) {
+ for (int k = 0; k < CHARGER_STATS; k++) {
+ if (bytes[i][j][k])
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
static void get_uid_names(const vector<int>& uids, const vector<std::string*>& uid_names)
{
sp<IServiceManager> sm = defaultServiceManager();
@@ -88,7 +158,7 @@
return uid_io_stats;
}
- std::vector<std::string> io_stats = Split(buffer, "\n");
+ std::vector<std::string> io_stats = Split(std::move(buffer), "\n");
struct uid_info u;
vector<int> uids;
vector<std::string*> uid_names;
@@ -97,32 +167,24 @@
if (io_stats[i].empty()) {
continue;
}
- std::vector<std::string> fields = Split(io_stats[i], " ");
- if (fields.size() < 11 ||
- !ParseUint(fields[0], &u.uid) ||
- !ParseUint(fields[1], &u.io[FOREGROUND].rchar) ||
- !ParseUint(fields[2], &u.io[FOREGROUND].wchar) ||
- !ParseUint(fields[3], &u.io[FOREGROUND].read_bytes) ||
- !ParseUint(fields[4], &u.io[FOREGROUND].write_bytes) ||
- !ParseUint(fields[5], &u.io[BACKGROUND].rchar) ||
- !ParseUint(fields[6], &u.io[BACKGROUND].wchar) ||
- !ParseUint(fields[7], &u.io[BACKGROUND].read_bytes) ||
- !ParseUint(fields[8], &u.io[BACKGROUND].write_bytes) ||
- !ParseUint(fields[9], &u.io[FOREGROUND].fsync) ||
- !ParseUint(fields[10], &u.io[BACKGROUND].fsync)) {
- LOG_TO(SYSTEM, WARNING) << "Invalid I/O stats: \""
- << io_stats[i] << "\"";
- continue;
- }
- uid_io_stats[u.uid] = u;
- uid_io_stats[u.uid].name = std::to_string(u.uid);
- uids.push_back(u.uid);
- uid_names.push_back(&uid_io_stats[u.uid].name);
- if (last_uid_io_stats.find(u.uid) == last_uid_io_stats.end()) {
- refresh_uid_names = true;
+ if (io_stats[i].compare(0, 4, "task")) {
+ if (!u.parse_uid_io_stats(std::move(io_stats[i])))
+ continue;
+ uid_io_stats[u.uid] = u;
+ uid_io_stats[u.uid].name = std::to_string(u.uid);
+ uids.push_back(u.uid);
+ uid_names.push_back(&uid_io_stats[u.uid].name);
+ if (last_uid_io_stats.find(u.uid) == last_uid_io_stats.end()) {
+ refresh_uid_names = true;
+ } else {
+ uid_io_stats[u.uid].name = last_uid_io_stats[u.uid].name;
+ }
} else {
- uid_io_stats[u.uid].name = last_uid_io_stats[u.uid].name;
+ struct task_info t;
+ if (!t.parse_task_io_stats(std::move(io_stats[i])))
+ continue;
+ uid_io_stats[u.uid].tasks[t.pid] = t;
}
}
@@ -135,32 +197,34 @@
static const int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
-static inline int records_size(
- const std::map<uint64_t, struct uid_records>& curr_records)
+static inline size_t history_size(
+ const std::map<uint64_t, struct uid_records>& history)
{
- int count = 0;
- for (auto const& it : curr_records) {
+ size_t count = 0;
+ for (auto const& it : history) {
count += it.second.entries.size();
}
return count;
}
-static struct uid_io_usage zero_io_usage;
-
void uid_monitor::add_records_locked(uint64_t curr_ts)
{
// remove records more than 5 days old
if (curr_ts > 5 * DAY_TO_SEC) {
- auto it = records.lower_bound(curr_ts - 5 * DAY_TO_SEC);
- records.erase(records.begin(), it);
+ auto it = io_history.lower_bound(curr_ts - 5 * DAY_TO_SEC);
+ io_history.erase(io_history.begin(), it);
}
struct uid_records new_records;
for (const auto& p : curr_io_stats) {
struct uid_record record = {};
record.name = p.first;
- record.ios = p.second;
- if (memcmp(&record.ios, &zero_io_usage, sizeof(struct uid_io_usage))) {
+ if (!p.second.uid_ios.is_zero()) {
+ record.ios.uid_ios = p.second.uid_ios;
+ for (const auto& p_task : p.second.task_ios) {
+ if (!p_task.second.is_zero())
+ record.ios.task_ios[p_task.first] = p_task.second;
+ }
new_records.entries.push_back(record);
}
}
@@ -173,15 +237,15 @@
return;
// make some room for new records
- int overflow = records_size(records) +
+ ssize_t overflow = history_size(io_history) +
new_records.entries.size() - MAX_UID_RECORDS_SIZE;
- while (overflow > 0 && records.size() > 0) {
- auto del_it = records.begin();
+ while (overflow > 0 && io_history.size() > 0) {
+ auto del_it = io_history.begin();
overflow -= del_it->second.entries.size();
- records.erase(records.begin());
+ io_history.erase(io_history.begin());
}
- records[curr_ts] = new_records;
+ io_history[curr_ts] = new_records;
}
std::map<uint64_t, struct uid_records> uid_monitor::dump(
@@ -200,19 +264,20 @@
first_ts = time(NULL) - hours * HOUR_TO_SEC;
}
- for (auto it = records.lower_bound(first_ts); it != records.end(); ++it) {
+ for (auto it = io_history.lower_bound(first_ts); it != io_history.end(); ++it) {
const std::vector<struct uid_record>& recs = it->second.entries;
struct uid_records filtered;
for (const auto& rec : recs) {
- if (rec.ios.bytes[READ][FOREGROUND][CHARGER_ON] +
- rec.ios.bytes[READ][FOREGROUND][CHARGER_OFF] +
- rec.ios.bytes[READ][BACKGROUND][CHARGER_ON] +
- rec.ios.bytes[READ][BACKGROUND][CHARGER_OFF] +
- rec.ios.bytes[WRITE][FOREGROUND][CHARGER_ON] +
- rec.ios.bytes[WRITE][FOREGROUND][CHARGER_OFF] +
- rec.ios.bytes[WRITE][BACKGROUND][CHARGER_ON] +
- rec.ios.bytes[WRITE][BACKGROUND][CHARGER_OFF] > threshold) {
+ const io_usage& uid_usage = rec.ios.uid_ios;
+ if (uid_usage.bytes[READ][FOREGROUND][CHARGER_ON] +
+ uid_usage.bytes[READ][FOREGROUND][CHARGER_OFF] +
+ uid_usage.bytes[READ][BACKGROUND][CHARGER_ON] +
+ uid_usage.bytes[READ][BACKGROUND][CHARGER_OFF] +
+ uid_usage.bytes[WRITE][FOREGROUND][CHARGER_ON] +
+ uid_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF] +
+ uid_usage.bytes[WRITE][BACKGROUND][CHARGER_ON] +
+ uid_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF] > threshold) {
filtered.entries.push_back(rec);
}
}
@@ -253,14 +318,38 @@
int64_t bg_wr_delta = uid.io[BACKGROUND].write_bytes -
last_uid_io_stats[uid.uid].io[BACKGROUND].write_bytes;
- usage.bytes[READ][FOREGROUND][charger_stat] +=
+ usage.uid_ios.bytes[READ][FOREGROUND][charger_stat] +=
(fg_rd_delta < 0) ? 0 : fg_rd_delta;
- usage.bytes[READ][BACKGROUND][charger_stat] +=
+ usage.uid_ios.bytes[READ][BACKGROUND][charger_stat] +=
(bg_rd_delta < 0) ? 0 : bg_rd_delta;
- usage.bytes[WRITE][FOREGROUND][charger_stat] +=
+ usage.uid_ios.bytes[WRITE][FOREGROUND][charger_stat] +=
(fg_wr_delta < 0) ? 0 : fg_wr_delta;
- usage.bytes[WRITE][BACKGROUND][charger_stat] +=
+ usage.uid_ios.bytes[WRITE][BACKGROUND][charger_stat] +=
(bg_wr_delta < 0) ? 0 : bg_wr_delta;
+
+ for (const auto& task_it : uid.tasks) {
+ const struct task_info& task = task_it.second;
+ const pid_t pid = task_it.first;
+ const std::string& comm = task_it.second.comm;
+ int64_t task_fg_rd_delta = task.io[FOREGROUND].read_bytes -
+ last_uid_io_stats[uid.uid].tasks[pid].io[FOREGROUND].read_bytes;
+ int64_t task_bg_rd_delta = task.io[BACKGROUND].read_bytes -
+ last_uid_io_stats[uid.uid].tasks[pid].io[BACKGROUND].read_bytes;
+ int64_t task_fg_wr_delta = task.io[FOREGROUND].write_bytes -
+ last_uid_io_stats[uid.uid].tasks[pid].io[FOREGROUND].write_bytes;
+ int64_t task_bg_wr_delta = task.io[BACKGROUND].write_bytes -
+ last_uid_io_stats[uid.uid].tasks[pid].io[BACKGROUND].write_bytes;
+
+ struct io_usage& task_usage = usage.task_ios[comm];
+ task_usage.bytes[READ][FOREGROUND][charger_stat] +=
+ (task_fg_rd_delta < 0) ? 0 : task_fg_rd_delta;
+ task_usage.bytes[READ][BACKGROUND][charger_stat] +=
+ (task_bg_rd_delta < 0) ? 0 : task_bg_rd_delta;
+ task_usage.bytes[WRITE][FOREGROUND][charger_stat] +=
+ (task_fg_wr_delta < 0) ? 0 : task_fg_wr_delta;
+ task_usage.bytes[WRITE][BACKGROUND][charger_stat] +=
+ (task_bg_wr_delta < 0) ? 0 : task_bg_wr_delta;
+ }
}
last_uid_io_stats = uid_io_stats;
@@ -272,6 +361,128 @@
update_curr_io_stats_locked();
add_records_locked(time(NULL));
+
+ flush_io_history_to_proto();
+}
+
+static void set_io_usage_proto(IOUsage* usage_proto, const struct io_usage& usage)
+{
+ usage_proto->set_rd_fg_chg_on(usage.bytes[READ][FOREGROUND][CHARGER_ON]);
+ usage_proto->set_rd_fg_chg_off(usage.bytes[READ][FOREGROUND][CHARGER_OFF]);
+ usage_proto->set_rd_bg_chg_on(usage.bytes[READ][BACKGROUND][CHARGER_ON]);
+ usage_proto->set_rd_bg_chg_off(usage.bytes[READ][BACKGROUND][CHARGER_OFF]);
+ usage_proto->set_wr_fg_chg_on(usage.bytes[WRITE][FOREGROUND][CHARGER_ON]);
+ usage_proto->set_wr_fg_chg_off(usage.bytes[WRITE][FOREGROUND][CHARGER_OFF]);
+ usage_proto->set_wr_bg_chg_on(usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);
+ usage_proto->set_wr_bg_chg_off(usage.bytes[WRITE][BACKGROUND][CHARGER_OFF]);
+}
+
+static void get_io_usage_proto(struct io_usage* usage, const IOUsage& io_proto)
+{
+ usage->bytes[READ][FOREGROUND][CHARGER_ON] = io_proto.rd_fg_chg_on();
+ usage->bytes[READ][FOREGROUND][CHARGER_OFF] = io_proto.rd_fg_chg_off();
+ usage->bytes[READ][BACKGROUND][CHARGER_ON] = io_proto.rd_bg_chg_on();
+ usage->bytes[READ][BACKGROUND][CHARGER_OFF] = io_proto.rd_bg_chg_off();
+ usage->bytes[WRITE][FOREGROUND][CHARGER_ON] = io_proto.wr_fg_chg_on();
+ usage->bytes[WRITE][FOREGROUND][CHARGER_OFF] = io_proto.wr_fg_chg_off();
+ usage->bytes[WRITE][BACKGROUND][CHARGER_ON] = io_proto.wr_bg_chg_on();
+ usage->bytes[WRITE][BACKGROUND][CHARGER_OFF] = io_proto.wr_bg_chg_off();
+}
+
+void uid_monitor::flush_io_history_to_proto()
+{
+ UidIOHistoryProto out_proto;
+
+ for (const auto& item : io_history) {
+ const uint64_t& end_ts = item.first;
+ const struct uid_records& recs = item.second;
+
+ UidIOItem* item_proto = out_proto.add_items();
+ item_proto->set_end_ts(end_ts);
+
+ UidIORecords* recs_proto = item_proto->mutable_records();
+ recs_proto->set_start_ts(recs.start_ts);
+
+ for (const auto& entry : recs.entries) {
+ UidRecord* rec_proto = recs_proto->add_entries();
+ rec_proto->set_uid_name(entry.name);
+
+ IOUsage* uid_io_proto = rec_proto->mutable_uid_io();
+ const struct io_usage& uio_ios = entry.ios.uid_ios;
+ set_io_usage_proto(uid_io_proto, uio_ios);
+
+ for (const auto& task_io : entry.ios.task_ios) {
+ const std::string& task_name = task_io.first;
+ const struct io_usage& task_ios = task_io.second;
+
+ TaskIOUsage* task_io_proto = rec_proto->add_task_io();
+ task_io_proto->set_task_name(task_name);
+ set_io_usage_proto(task_io_proto->mutable_ios(), task_ios);
+ }
+ }
+ }
+
+ out_proto.set_crc(crc_init);
+ std::string out_proto_str = out_proto.SerializeAsString();
+ out_proto.set_crc(crc32(crc_init,
+ reinterpret_cast<const Bytef*>(out_proto_str.c_str()),
+ out_proto_str.size()));
+
+ std::string tmp_file = io_history_proto_file + "_tmp";
+ std::ofstream out(tmp_file,
+ std::ofstream::out | std::ofstream::binary | std::ofstream::trunc);
+ out << out_proto.SerializeAsString();
+ out.close();
+
+ /* Atomically replace existing proto file to reduce chance of data loss. */
+ rename(tmp_file.c_str(), io_history_proto_file.c_str());
+}
+
+void uid_monitor::load_io_history_from_proto()
+{
+ std::ifstream in(io_history_proto_file,
+ std::ofstream::in | std::ofstream::binary);
+
+ if (!in.good()) {
+ PLOG_TO(SYSTEM, INFO) << "Open " << io_history_proto_file << " failed";
+ return;
+ }
+
+ stringstream ss;
+ ss << in.rdbuf();
+ UidIOHistoryProto in_proto;
+ in_proto.ParseFromString(ss.str());
+
+ uint32_t crc = in_proto.crc();
+ in_proto.set_crc(crc_init);
+ std::string io_proto_str = in_proto.SerializeAsString();
+ uint32_t computed_crc = crc32(crc_init,
+ reinterpret_cast<const Bytef*>(io_proto_str.c_str()),
+ io_proto_str.size());
+
+ if (crc != computed_crc) {
+ LOG_TO(SYSTEM, WARNING) << "CRC mismatch in " << io_history_proto_file;
+ return;
+ }
+
+ for (const auto& item_proto : in_proto.items()) {
+ const UidIORecords& records_proto = item_proto.records();
+ struct uid_records* recs = &io_history[item_proto.end_ts()];
+
+ recs->start_ts = records_proto.start_ts();
+ for (const auto& rec_proto : records_proto.entries()) {
+ struct uid_record record;
+ record.name = rec_proto.uid_name();
+ get_io_usage_proto(&record.ios.uid_ios, rec_proto.uid_io());
+
+ for (const auto& task_io_proto : rec_proto.task_io()) {
+ get_io_usage_proto(
+ &record.ios.task_ios[task_io_proto.task_name()],
+ task_io_proto.ios());
+ }
+ recs->entries.push_back(record);
+ }
+ }
}
void uid_monitor::set_charger_state(charger_stat_t stat)
@@ -289,6 +500,9 @@
void uid_monitor::init(charger_stat_t stat)
{
charger_stat = stat;
+
+ load_io_history_from_proto();
+
start_ts = time(NULL);
last_uid_io_stats = get_uid_io_stats();
}
diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp
index 74b7436..59cf251 100644
--- a/storaged/storaged_utils.cpp
+++ b/storaged/storaged_utils.cpp
@@ -18,6 +18,7 @@
#include <dirent.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <linux/time.h>
#include <stdint.h>
#include <stdio.h>
@@ -182,15 +183,28 @@
}
// Logging functions
-void log_console_running_uids_info(std::vector<struct uid_info> uids) {
+void log_console_running_uids_info(const std::vector<struct uid_info>& uids, bool flag_dump_task) {
printf("name/uid fg_rchar fg_wchar fg_rbytes fg_wbytes "
"bg_rchar bg_wchar bg_rbytes bg_wbytes fg_fsync bg_fsync\n");
for (const auto& uid : uids) {
- printf("%s %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju\n", uid.name.c_str(),
+ printf("%s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+ " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\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,
uid.io[0].fsync, uid.io[1].fsync);
+ if (flag_dump_task) {
+ for (const auto& task_it : uid.tasks) {
+ const struct task_info& task = task_it.second;
+ printf("-> %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+ " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+ task.comm.c_str(),
+ task.io[0].rchar, task.io[0].wchar, task.io[0].read_bytes, task.io[0].write_bytes,
+ task.io[1].rchar, task.io[1].wchar, task.io[1].read_bytes, task.io[1].write_bytes,
+ task.io[0].fsync, task.io[1].fsync);
+ }
+ }
}
fflush(stdout);
}
diff --git a/storaged/tests/Android.mk b/storaged/tests/Android.mk
deleted file mode 100644
index 26d04b1..0000000
--- a/storaged/tests/Android.mk
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# Copyright (C) 2014 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-test_module_prefix := storaged-
-test_tags := tests
-
-# -----------------------------------------------------------------------------
-# Unit tests.
-# -----------------------------------------------------------------------------
-
-test_c_flags := \
- -fstack-protector-all \
- -g \
- -Wall -Wextra \
- -Werror \
- -fno-builtin \
-
-test_src_files := \
- storaged_test.cpp \
-
-# Build tests for the logger. Run with:
-# adb shell /data/nativetest/storaged-unit-tests/storaged-unit-tests
-include $(CLEAR_VARS)
-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 libpackagelistparser
-LOCAL_SRC_FILES := $(test_src_files)
-include $(BUILD_NATIVE_TEST)
diff --git a/storaged/tools/ranker.py b/storaged/tools/ranker.py
new file mode 100644
index 0000000..d8096b7
--- /dev/null
+++ b/storaged/tools/ranker.py
@@ -0,0 +1,181 @@
+# Copyright 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.
+
+"""Parser and ranker for dumpsys storaged output.
+
+This module parses output from dumpsys storaged by ranking uids based on
+their io usage measured in 8 different stats. It must be provided the input
+file through command line argument -i/--input.
+
+For more details, see:
+ $ python ranker.py -h
+
+Example:
+ $ python ranker.py -i io.txt -o output.txt -u 20 -cnt
+"""
+
+import argparse
+import sys
+
+IO_NAMES = ["[READ][FOREGROUND][CHARGER_OFF]",
+ "[WRITE][FOREGROUND][CHARGER_OFF]",
+ "[READ][BACKGROUND][CHARGER_OFF]",
+ "[WRITE][BACKGROUND][CHARGER_OFF]",
+ "[READ][FOREGROUND][CHARGER_ON]",
+ "[WRITE][FOREGROUND][CHARGER_ON]",
+ "[READ][BACKGROUND][CHARGER_ON]",
+ "[WRITE][BACKGROUND][CHARGER_ON]"]
+
+
+def get_args():
+ """Get arguments from command line.
+
+ The only required argument is input file.
+
+ Returns:
+ Args containing cmdline arguments
+ """
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-i", "--input", dest="input", required="true",
+ help="input io FILE, must provide", metavar="FILE")
+ parser.add_argument("-o", "--output", dest="output", default="stdout",
+ help="output FILE, default to stdout", metavar="FILE")
+ parser.add_argument("-u", "--uidcnt", dest="uidcnt", type=int, default=10,
+ help="set number of uids to display for each rank, "
+ "default 10")
+ parser.add_argument("-c", "--combine", dest="combine", default=False,
+ action="store_true", help="add io stats for same uids, "
+ "default to take io stats of last appearing uids")
+ parser.add_argument("-n", "--native", dest="native", default=False,
+ action="store_true", help="only include native apps in "
+ "ranking, default to include all apps")
+ parser.add_argument("-t", "--task", dest="task", default=False,
+ action="store_true", help="display task io under uids, "
+ "default to not display tasks")
+ return parser.parse_args()
+
+
+def is_number(word):
+ try:
+ int(word)
+ return True
+ except ValueError:
+ return False
+
+
+def combine_or_filter(args):
+ """Parser for io input.
+
+ Either args.combine io stats for the same uids
+ or take the io stats for the last uid and ignore
+ the same uids before it.
+
+ If task is required, store task ios along with uid
+ for later display.
+
+ Returns:
+ The structure for the return value uids is as follows:
+ uids: {uid -> [UID_STATS, TASK_STATS(optional)]}
+ UID_STATS: [io1, io2, ..., io8]
+ TASK_STATS: {task_name -> [io1, io2, ..., io8]}
+ """
+ fin = open(args.input, "r")
+ uids = {}
+ cur_uid = 0
+ task_enabled = args.task
+ for line in fin:
+ words = line.split()
+ if words[0] == "->":
+ # task io
+ if not task_enabled:
+ continue
+ # get task command line
+ i = len(words) - 8
+ task = " ".join(words[1:i])
+ if task in uids[cur_uid][1]:
+ task_io = uids[cur_uid][1][task]
+ for j in range(8):
+ task_io[j] += long(words[i+j])
+ else:
+ task_io = []
+ for j in range(8):
+ task_io.append(long(words[i+j]))
+ uids[cur_uid][1][task] = task_io
+
+ elif len(words) > 8:
+ if not is_number(words[0]) and args.native:
+ # uid not requested, ignore its tasks as well
+ task_enabled = False
+ continue
+ task_enabled = args.task
+ i = len(words) - 8
+ uid = " ".join(words[:i])
+ if uid in uids and args.combine:
+ uid_io = uids[uid][0]
+ for j in range(8):
+ uid_io[j] += long(words[i+j])
+ uids[uid][0] = uid_io
+ else:
+ uid_io = [long(words[i+j]) for j in range(8)]
+ uids[uid] = [uid_io]
+ if task_enabled:
+ uids[uid].append({})
+ cur_uid = uid
+
+ return uids
+
+
+def rank_uids(uids):
+ """Sort uids based on eight different io stats.
+
+ Returns:
+ uid_rank is a 2d list of tuples:
+ The first dimension represent the 8 different io stats.
+ The second dimension is a sorted list of tuples by tup[0],
+ each tuple is a uid's perticular stat at the first dimension and the uid.
+ """
+ uid_rank = [[(uids[uid][0][i], uid) for uid in uids] for i in range(8)]
+ for i in range(8):
+ uid_rank[i].sort(key=lambda tup: tup[0], reverse=True)
+ return uid_rank
+
+
+def display_uids(uid_rank, uids, args):
+ """Display ranked uid io, along with task io if specified."""
+ fout = sys.stdout
+ if args.output != "stdout":
+ fout = open(args.output, "w")
+
+ for i in range(8):
+ fout.write("RANKING BY " + IO_NAMES[i] + "\n")
+ for j in range(min(args.uidcnt, len(uid_rank[0]))):
+ uid = uid_rank[i][j][1]
+ uid_stat = " ".join([str(uid_io) for uid_io in uids[uid][0]])
+ fout.write(uid + " " + uid_stat + "\n")
+ if args.task:
+ for task in uids[uid][1]:
+ task_stat = " ".join([str(task_io) for task_io in uids[uid][1][task]])
+ fout.write("-> " + task + " " + task_stat + "\n")
+ fout.write("\n")
+
+
+def main():
+ args = get_args()
+ uids = combine_or_filter(args)
+ uid_rank = rank_uids(uids)
+ display_uids(uid_rank, uids, args)
+
+if __name__ == "__main__":
+ main()