blob: 23b6db6ac05e44b01f27074bd2af4fd02f322183 [file] [log] [blame]
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import sys
import time
from optparse import OptionParser
import memreport
import grapher
from subprocess import Popen, PIPE
def logger(iterations, wait, source="/proc", chrome_only=False):
""" Logs smaps behavior of chrome and non-chrome processes.
Required arguments:
iterations -- number of memory data snapshots to take
wait -- seconds between each data capture
chrome -- bool decides if data from all or only chrome processes is acquired
Keyword arguments:
source -- the directory used for parsing (default: /proc)
chrome_only -- If True, does not account for non-Chrome process
Returns:
snapshots_list -- list with ProcSnapshot objects
"""
count = 0
snapshots_list = []
while count < iterations:
snapshot = memreport.ProcSnapshot(source, chrome_only)
#TODO: Decide what information we want to present instead of saving
# all the data available
snapshots_list.append(snapshot)
count += 1
if count < iterations:
time.sleep(wait)
return snapshots_list
def userArgs():
"""
Function uses the OptionParser object to allow for voluntary user input from
the command line.
"""
parser = OptionParser()
parser.add_option("-p", "--proc", type="string", metavar="SOURCE DIR",
dest="source", help="source directory [default: /proc]",
default="/proc")
parser.add_option("-i", "--iterations", type='int', default=1,
help="number of snapshot iterations")
parser.add_option("-t", "--wait_time", type='int', default=0,
help="time in seconds between each snapshot",
dest="interval")
parser.add_option("-c", "--chrome_only", action="store_true", default=False,
help="show only chrome processes")
parser.add_option("-g", "--graph", default="timelog",
help="choose graph type (timelog, snapshot, rss)")
parser.add_option("-m", "--memory", default="Pss",
help="choose memory type to display")
#TODO: add "save file to destination" option + generate it on the fly
return parser.parse_args()
def setTimelogDataTable(snapshots_list, mem_type, show_gpu=False, uptime=0):
'''
Given a list of ProcSnapshots objects, return a list formatted properly for
a line graph
'''
chrome, sys_pids_list = getUniquePidsList(snapshots_list)
# Doing only for chrome right now. Figure out how to use this for
# non-Chrome as well
data_table = []
data_table.append(["Time"])
chrome_keys = sorted(chrome.keys(), key=int)
for pid in chrome_keys:
data_table[0].append(pid + ': ' + chrome[pid])
if show_gpu:
data_table[0].append("Total Graphics Memory")
data_table[0].append("Current Graphics Memory in GTT")
i = 0
for snapshot in snapshots_list:
i += 1
data_table.append([int(snapshot.timestamp - snapshots_list[0].timestamp +
uptime)])
# Iterate through all the pids in the first table
for pid in chrome_keys:
try:
data_table[i].append(round(snapshot.chrome_smaps.pid[pid][mem_type] /
1024.0, 1))
except KeyError:
data_table[i].append(0)
if show_gpu:
data_table[i].append(round(snapshot.gpu_mem.total / 1024.0, 1))
data_table[i].append(round(snapshot.gpu_mem.current / 1024.0, 1))
return data_table
def getUniquePidsList(snapshots_list):
'''
Return a sorted list with all the unique PIDs in a list of ProcSnapshot
objects.
'''
# Create the two sets
#chrome_set = set()
sys_set = set()
chrome = {}
# Iterate through snapshots list
for snapshot in snapshots_list:
# Add chrome PIDs to set
for pid in snapshot.chrome_pids_list:
chrome[pid] = snapshot.chrome_types[pid]
#chrome_set.add(pid)
# Add non-Chrome PIDs to set
for pid in snapshot.sys_pids_list:
sys_set.add(pid)
# Return sorted list of the sets
return chrome, sorted(sys_set, key=int)
def setSnapshotDataTable(snapshot, mem_type, chrome_only):
'''
Given a list of ProcSnapshots objects, return a list formatted properly for
a pie graph
'''
data_table = []
data_table.append(["PID", "Size"])
mem_type = mem_type.capitalize()
sorted_chrome = snapshot.chrome_smaps.sortByMemType(mem_type)
for item in sorted_chrome:
if item[0] != "Total":
data_table.append([item[0], item[1][mem_type]])
if not chrome_only:
sorted_sys = snapshot.sys_smaps.sortByMemType(mem_type)
for item in sorted_sys:
if item[0] != "Total":
data_table.append([item[0], item[1][mem_type]])
return data_table
def setRssDataTable(snapshot, chrome_only):
'''
Given a list of ProcSnapshots objects, return a list formatted properly for
a bar graph
'''
data_table = []
data_table.append(["PID", "Private", "Shared"])
for pid in (snapshot.chrome_pids_list + ["Total"]):
private = snapshot.chrome_smaps.pid[pid]["Private"]
shared = snapshot.chrome_smaps.pid[pid]["Shared"]
data_table.append([pid, private, shared])
if not chrome_only:
for pid in (snapshot.sys_pids_list + ["Total"]):
private = snapshot.sys_smaps.pid[pid]["Private"]
shared = snapshot.sys_smaps.pid[pid]["Shared"]
data_table.append([pid, private, shared])
return data_table
def getSystemParameters():
sys_info = {}
# first, get uname
sys_info["uname"] = Popen("uname -a", shell=True,
stdout=PIPE).stdout.readline()
# now get lsb info
lsb_f = open("/etc/lsb-release", 'r')
lsb = lsb_f.readlines()
lsb_f.close()
for line in lsb:
line = line.split('=')
try:
sys_info[line[0]] = line[1]
except IndexError:
pass
return sys_info
# Main Function
#TODO(thiagog): Add command line support to input all required fields for
# graph creation.
if __name__ == "__main__":
options, args = userArgs()
if options.graph not in ("rss", "snapshot", "timelog"):
sys.exit("ERROR: graph argument invalid")
uptime_file = open("/proc/uptime", 'r')
uptime = float(uptime_file.readline().split()[0])
uptime_file.close()
sys_info = getSystemParameters()
timestamp = time.asctime(time.localtime())
snapshots = logger(options.iterations, options.interval,
options.source, options.chrome_only)
gpu_mem = ("Pss", "Rss")
# For testing right now
mem_type = "Pss"
# Testing on linux machine (no gpu memory information)
show_gpu = False
html_args = {}
if options.graph == "timelog":
graphs = ("Pss", "Rss", "Private_Clean", "Private_Dirty", "Shared_Clean",
"Shared_Dirty", "Private", "Shared", "Referenced", "Anonymous")
for mem_type in graphs:
show_gpu = mem_type in gpu_mem
html_args[mem_type] = setTimelogDataTable(snapshots, mem_type, show_gpu,
uptime)
if options.graph == "snapshot":
snapshot = snapshots[0]
html_args["title"] = mem_type + " [kBs] per PID"
html_args["data"] = setSnapshotDataTable(snapshot, mem_type,
options.chrome_only)
if options.graph == "rss":
snapshot = snapshots[0]
html_args["title"] = "Private and Shared memory usage [kBs] per PID"
html_args["haxis"] = "PIDs"
html_args["data"] = setRssDataTable(snapshot, True)
html_args["version"] = sys_info["uname"]
html_args["release"] = sys_info["CHROMEOS_RELEASE_DESCRIPTION"]
html_args["timestamp"] = timestamp
#print html_args["data"]
print grapher.chooseGraph(options.graph, html_args)