storaged: add parser for dumpsys storaged output

Rank uids by io stats and display their tasks. Can either rank by combining
io stats for same uids or io stats for the last appearing uid.
See python ranker.py -h for details.

Test: python ranker.py -i io.txt -o output.txt -u 20 -cnt

Bug: 63739275
Change-Id: Ieee1d7a9bf190266fc6a055c0922434fcd9099c6
This commit is contained in:
Yang Jin 2017-08-08 20:06:34 -07:00
parent 79931b10ea
commit c61cdfe5c3

181
storaged/tools/ranker.py Normal file
View file

@ -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()