Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Unified Diff: client/common_lib/utils.py

Issue 4823005: Merge remote branch 'cros/upstream' into tempbranch (Closed) Base URL: http://git.chromium.org/git/autotest.git@master
Patch Set: patch Created 10 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « client/common_lib/global_config_unittest.py ('k') | client/samples/all_tests » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: client/common_lib/utils.py
diff --git a/client/common_lib/utils.py b/client/common_lib/utils.py
index 8a34ef17a02856ded4fcd3be9dd341718f8eb5d3..47f3cb4c2e11d32a7331f6d12fbc448a6ee09b7f 100644
--- a/client/common_lib/utils.py
+++ b/client/common_lib/utils.py
@@ -4,6 +4,7 @@
import os, pickle, random, re, resource, select, shutil, signal, StringIO
import socket, struct, subprocess, sys, time, textwrap, urlparse
import warnings, smtplib, logging, urllib2
+from threading import Thread, Event
try:
import hashlib
except ImportError:
@@ -190,6 +191,30 @@ def read_file(filename):
f.close()
+def get_field(data, param, linestart="", sep=" "):
+ """
+ Parse data from string.
+ @param data: Data to parse.
+ example:
+ data:
+ cpu 324 345 34 5 345
+ cpu0 34 11 34 34 33
+ ^^^^
+ start of line
+ params 0 1 2 3 4
+ @param param: Position of parameter after linestart marker.
+ @param linestart: String to which start line with parameters.
+ @param sep: Separator between parameters regular expression.
+ """
+ search = re.compile(r"(?<=^%s)\s*(.*)" % linestart, re.MULTILINE)
+ find = search.search(data)
+ if find != None:
+ return re.split("%s" % sep, find.group(1))[param]
+ else:
+ print "There is no line which starts with %s in data." % linestart
+ return None
+
+
def write_one_line(filename, line):
open_write_close(filename, line.rstrip('\n') + '\n')
@@ -211,9 +236,14 @@ def matrix_to_string(matrix, header=None):
in each column, and determining the format string dynamically.
@param matrix: Matrix representation (list with n rows of m elements).
- @param header: Optional tuple with header elements to be displayed.
+ @param header: Optional tuple or list with header elements to be displayed.
"""
+ if type(header) is list:
+ header = tuple(header)
lengths = []
+ if header:
+ for column in header:
+ lengths.append(len(column))
for row in matrix:
for column in row:
i = row.index(column)
@@ -294,6 +324,156 @@ def write_keyval(path, dictionary, type_tag=None):
keyval.close()
+class FileFieldMonitor(object):
+ """
+ Monitors the information from the file and reports it's values.
+
+ It gather the information at start and stop of the measurement or
+ continuously during the measurement.
+ """
+ class Monitor(Thread):
+ """
+ Internal monitor class to ensure continuous monitor of monitored file.
+ """
+ def __init__(self, master):
+ """
+ @param master: Master class which control Monitor
+ """
+ Thread.__init__(self)
+ self.master = master
+
+ def run(self):
+ """
+ Start monitor in thread mode
+ """
+ while not self.master.end_event.isSet():
+ self.master._get_value(self.master.logging)
+ time.sleep(self.master.time_step)
+
+
+ def __init__(self, status_file, data_to_read, mode_diff, continuously=False,
+ contlogging=False, separator=" +", time_step=0.1):
+ """
+ Initialize variables.
+ @param status_file: File contain status.
+ @param mode_diff: If True make a difference of value, else average.
+ @param data_to_read: List of tuples with data position.
+ format: [(start_of_line,position in params)]
+ example:
+ data:
+ cpu 324 345 34 5 345
+ cpu0 34 11 34 34 33
+ ^^^^
+ start of line
+ params 0 1 2 3 4
+ @param mode_diff: True to subtract old value from new value,
+ False make average of the values.
+ @parma continuously: Start the monitoring thread using the time_step
+ as the measurement period.
+ @param contlogging: Log data in continuous run.
+ @param separator: Regular expression of separator.
+ @param time_step: Time period of the monitoring value.
+ """
+ self.end_event = Event()
+ self.start_time = 0
+ self.end_time = 0
+ self.test_time = 0
+
+ self.status_file = status_file
+ self.separator = separator
+ self.data_to_read = data_to_read
+ self.num_of_params = len(self.data_to_read)
+ self.mode_diff = mode_diff
+ self.continuously = continuously
+ self.time_step = time_step
+
+ self.value = [0 for i in range(self.num_of_params)]
+ self.old_value = [0 for i in range(self.num_of_params)]
+ self.log = []
+ self.logging = contlogging
+
+ self.started = False
+ self.num_of_get_value = 0
+ self.monitor = None
+
+
+ def _get_value(self, logging=True):
+ """
+ Return current values.
+ @param logging: If true log value in memory. There can be problem
+ with long run.
+ """
+ data = read_file(self.status_file)
+ value = []
+ for i in range(self.num_of_params):
+ value.append(int(get_field(data,
+ self.data_to_read[i][1],
+ self.data_to_read[i][0],
+ self.separator)))
+
+ if logging:
+ self.log.append(value)
+ if not self.mode_diff:
+ value = map(lambda x, y: x + y, value, self.old_value)
+
+ self.old_value = value
+ self.num_of_get_value += 1
+ return value
+
+
+ def start(self):
+ """
+ Start value monitor.
+ """
+ if self.started:
+ self.stop()
+ self.old_value = [0 for i in range(self.num_of_params)]
+ self.num_of_get_value = 0
+ self.log = []
+ self.end_event.clear()
+ self.start_time = time.time()
+ self._get_value()
+ self.started = True
+ if (self.continuously):
+ self.monitor = FileFieldMonitor.Monitor(self)
+ self.monitor.start()
+
+
+ def stop(self):
+ """
+ Stop value monitor.
+ """
+ if self.started:
+ self.started = False
+ self.end_time = time.time()
+ self.test_time = self.end_time - self.start_time
+ self.value = self._get_value()
+ if (self.continuously):
+ self.end_event.set()
+ self.monitor.join()
+ if (self.mode_diff):
+ self.value = map(lambda x, y: x - y, self.log[-1], self.log[0])
+ else:
+ self.value = map(lambda x: x / self.num_of_get_value,
+ self.value)
+
+
+ def get_status(self):
+ """
+ @return: Status of monitored process average value,
+ time of test and array of monitored values and time step of
+ continuous run.
+ """
+ if self.started:
+ self.stop()
+ if self.mode_diff:
+ for i in range(len(self.log) - 1):
+ self.log[i] = (map(lambda x, y: x - y,
+ self.log[i + 1], self.log[i]))
+ self.log.pop()
+ return (self.value, self.test_time, self.log, self.time_step)
+
+
def is_url(path):
"""Return true if path looks like a URL"""
# for now, just handle http and ftp
@@ -801,6 +981,226 @@ def get_cpu_percentage(function, *args, **dargs):
return cpu_percent, to_return
+class SystemLoad(object):
+ """
+ Get system and/or process values and return average value of load.
+ """
+ def __init__(self, pids, advanced=False, time_step=0.1, cpu_cont=False,
+ use_log=False):
+ """
+ @param pids: List of pids to be monitored. If pid = 0 whole system will
+ be monitored. pid == 0 means whole system.
+ @param advanced: monitor add value for system irq count and softirq
+ for process minor and maior page fault
+ @param time_step: Time step for continuous monitoring.
+ @param cpu_cont: If True monitor CPU load continuously.
+ @param use_log: If true every monitoring is logged for dump.
+ """
+ self.pids = []
+ self.stats = {}
+ for pid in pids:
+ if pid == 0:
+ cpu = FileFieldMonitor("/proc/stat",
+ [("cpu", 0), # User Time
+ ("cpu", 2), # System Time
+ ("intr", 0), # IRQ Count
+ ("softirq", 0)], # Soft IRQ Count
+ True,
+ cpu_cont,
+ use_log,
+ " +",
+ time_step)
+ mem = FileFieldMonitor("/proc/meminfo",
+ [("MemTotal:", 0), # Mem Total
+ ("MemFree:", 0), # Mem Free
+ ("Buffers:", 0), # Buffers
+ ("Cached:", 0)], # Cached
+ False,
+ True,
+ use_log,
+ " +",
+ time_step)
+ self.stats[pid] = ["TOTAL", cpu, mem]
+ self.pids.append(pid)
+ else:
+ name = ""
+ if (type(pid) is int):
+ self.pids.append(pid)
+ name = get_process_name(pid)
+ else:
+ self.pids.append(pid[0])
+ name = pid[1]
+
+ cpu = FileFieldMonitor("/proc/%d/stat" %
+ self.pids[-1],
+ [("", 13), # User Time
+ ("", 14), # System Time
+ ("", 9), # Minority Page Fault
+ ("", 11)], # Majority Page Fault
+ True,
+ cpu_cont,
+ use_log,
+ " +",
+ time_step)
+ mem = FileFieldMonitor("/proc/%d/status" %
+ self.pids[-1],
+ [("VmSize:", 0), # Virtual Memory Size
+ ("VmRSS:", 0), # Resident Set Size
+ ("VmPeak:", 0), # Peak VM Size
+ ("VmSwap:", 0)], # VM in Swap
+ False,
+ True,
+ use_log,
+ " +",
+ time_step)
+ self.stats[self.pids[-1]] = [name, cpu, mem]
+
+ self.advanced = advanced
+
+
+ def __str__(self):
+ """
+ Define format how to print
+ """
+ out = ""
+ for pid in self.pids:
+ for stat in self.stats[pid][1:]:
+ out += str(stat.get_status()) + "\n"
+ return out
+
+
+ def start(self, pids=[]):
+ """
+ Start monitoring of the process system usage.
+ @param pids: List of PIDs you intend to control. Use pids=[] to control
+ all defined PIDs.
+ """
+ if pids == []:
+ pids = self.pids
+
+ for pid in pids:
+ for stat in self.stats[pid][1:]:
+ stat.start()
+
+
+ def stop(self, pids=[]):
+ """
+ Stop monitoring of the process system usage.
+ @param pids: List of PIDs you intend to control. Use pids=[] to control
+ all defined PIDs.
+ """
+ if pids == []:
+ pids = self.pids
+
+ for pid in pids:
+ for stat in self.stats[pid][1:]:
+ stat.stop()
+
+
+ def dump(self, pids=[]):
+ """
+ Get the status of monitoring.
+ @param pids: List of PIDs you intend to control. Use pids=[] to control
+ all defined PIDs.
+ @return:
+ tuple([cpu load], [memory load]):
+ ([(PID1, (PID1_cpu_meas)), (PID2, (PID2_cpu_meas)), ...],
+ [(PID1, (PID1_mem_meas)), (PID2, (PID2_mem_meas)), ...])
+
+ PID1_cpu_meas:
+ average_values[], test_time, cont_meas_values[[]], time_step
+ PID1_mem_meas:
+ average_values[], test_time, cont_meas_values[[]], time_step
+ where average_values[] are the measured values (mem_free,swap,...)
+ which are described in SystemLoad.__init__()-FileFieldMonitor.
+ cont_meas_values[[]] is a list of average_values in the sampling
+ times.
+ """
+ if pids == []:
+ pids = self.pids
+
+ cpus = []
+ memory = []
+ for pid in pids:
+ stat = (pid, self.stats[pid][1].get_status())
+ cpus.append(stat)
+ for pid in pids:
+ stat = (pid, self.stats[pid][2].get_status())
+ memory.append(stat)
+
+ return (cpus, memory)
+
+
+ def get_cpu_status_string(self, pids=[]):
+ """
+ Convert status to string array.
+ @param pids: List of PIDs you intend to control. Use pids=[] to control
+ all defined PIDs.
+ @return: String format to table.
+ """
+ if pids == []:
+ pids = self.pids
+
+ headers = ["NAME",
+ ("%7s") % "PID",
+ ("%5s") % "USER",
+ ("%5s") % "SYS",
+ ("%5s") % "SUM"]
+ if self.advanced:
+ headers.extend(["MINFLT/IRQC",
+ "MAJFLT/SOFTIRQ"])
+ headers.append(("%11s") % "TIME")
+ textstatus = []
+ for pid in pids:
+ stat = self.stats[pid][1].get_status()
+ time = stat[1]
+ stat = stat[0]
+ textstatus.append(["%s" % self.stats[pid][0],
+ "%7s" % pid,
+ "%4.0f%%" % (stat[0] / time),
+ "%4.0f%%" % (stat[1] / time),
+ "%4.0f%%" % ((stat[0] + stat[1]) / time),
+ "%10.3fs" % time])
+ if self.advanced:
+ textstatus[-1].insert(-1, "%11d" % stat[2])
+ textstatus[-1].insert(-1, "%14d" % stat[3])
+
+ return matrix_to_string(textstatus, tuple(headers))
+
+
+ def get_mem_status_string(self, pids=[]):
+ """
+ Convert status to string array.
+ @param pids: List of PIDs you intend to control. Use pids=[] to control
+ all defined PIDs.
+ @return: String format to table.
+ """
+ if pids == []:
+ pids = self.pids
+
+ headers = ["NAME",
+ ("%7s") % "PID",
+ ("%8s") % "TOTAL/VMSIZE",
+ ("%8s") % "FREE/VMRSS",
+ ("%8s") % "BUFFERS/VMPEAK",
+ ("%8s") % "CACHED/VMSWAP",
+ ("%11s") % "TIME"]
+ textstatus = []
+ for pid in pids:
+ stat = self.stats[pid][2].get_status()
+ time = stat[1]
+ stat = stat[0]
+ textstatus.append(["%s" % self.stats[pid][0],
+ "%7s" % pid,
+ "%10dMB" % (stat[0] / 1024),
+ "%8dMB" % (stat[1] / 1024),
+ "%12dMB" % (stat[2] / 1024),
+ "%11dMB" % (stat[3] / 1024),
+ "%10.3fs" % time])
+
+ return matrix_to_string(textstatus, tuple(headers))
+
+
def get_arch(run_function=run):
"""
Get the hardware architecture of the machine.
@@ -1100,6 +1500,14 @@ def get_pid_from_file(program_name):
return pid
+def get_process_name(pid):
+ """
+ Get process name from PID.
+ @param pid: PID of process.
+ """
+ return get_field(read_file("/proc/%d/stat" % pid), 1)[1:-1]
+
+
def program_is_alive(program_name):
"""
Checks if the process is alive and not in Zombie state.
« no previous file with comments | « client/common_lib/global_config_unittest.py ('k') | client/samples/all_tests » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698