Bug 1193707 - temporary record network statistics from the operating system as counters and only submit to treeherder for linux64 tresize. r=parkouss
authorJoel Maher <jmaher@mozilla.com>
Sat, 22 Aug 2015 06:09:50 -0400
changeset 1001 762f27d9a07b
parent 1000 3625fcaa75ea
child 1002 57baf0c8ef5d
push id690
push userjmaher@mozilla.com
push dateSat, 22 Aug 2015 10:09:57 +0000
reviewersparkouss
bugs1193707
Bug 1193707 - temporary record network statistics from the operating system as counters and only submit to treeherder for linux64 tresize. r=parkouss
talos/cmanager_linux.py
talos/output.py
talos/test.py
--- a/talos/cmanager_linux.py
+++ b/talos/cmanager_linux.py
@@ -1,15 +1,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import os
 import re
 import subprocess
+import psutil
+
 from cmanager import CounterManager
 from mozprocess import pid as mozpid
 
 
 def xrestop(binary='xrestop'):
     """
     python front-end to running xrestop:
     http://www.freedesktop.org/wiki/Software/xrestop
@@ -137,28 +139,55 @@ def GetXRes(pids):
             except ValueError:
                 print "Invalid data, not a float"
                 raise
         else:
             raise Exception("Could not find PID=%s in xrestop output" % pid)
     return XRes
 
 
+def GetNetworkCounters(counterName):
+    args = {
+       "Network_lo_packets_rx": ('lo', 'packets_recv'),
+       "Network_lo_packets_tx": ('lo', 'packets_sent'),
+       "Network_lo_bytes_rx": ('lo', 'bytes_recv'),
+       "Network_lo_bytes_tx": ('lo', 'bytes_sent'),
+       "Network_eth0_packets_rx": ('eth0', 'packets_recv'),
+       "Network_eth0_packets_tx": ('eth0', 'packets_sent'),
+       "Network_eth0_bytes_rx": ('eth0', 'bytes_recv'),
+       "Network_eth0_bytes_tx": ('eth0', 'bytes_sent')
+    }
+    nic, field = args[counterName]
+    counters = psutil.net_io_counters(pernic=True)
+    data = counters[nic]
+    fields = ['bytes_sent', 'bytes_recv', 'packets_sent', 'packets_recv', 'errin', 'errout', 'dropin', 'dropout']
+    idx = fields.index(field)
+    return data[idx]
+
+
 class LinuxCounterManager(CounterManager):
     """This class manages the monitoring of a process with any number of
        counters.
 
        A counter can be any function that takes an argument of one pid and
        returns a piece of data about that process.
        Some examples are: CalcCPUTime, GetResidentSize, and GetPrivateBytes
     """
 
     counterDict = {"Private Bytes": GetPrivateBytes,
                    "RSS": GetResidentSize,
-                   "XRes": GetXRes}
+                   "XRes": GetXRes,
+                   "Network_lo_packets_rx": GetNetworkCounters,
+                   "Network_lo_packets_tx": GetNetworkCounters,
+                   "Network_lo_bytes_rx": GetNetworkCounters,
+                   "Network_lo_bytes_tx": GetNetworkCounters,
+                   "Network_eth0_packets_rx": GetNetworkCounters,
+                   "Network_eth0_packets_tx": GetNetworkCounters,
+                   "Network_eth0_bytes_rx": GetNetworkCounters,
+                   "Network_eth0_bytes_tx": GetNetworkCounters}
 
     def __init__(self, process, counters=None,
                  childProcess="plugin-container"):
         """Args:
              counters: A list of counters to monitor. Any counters whose name
              does not match a key in 'counterDict' will be ignored.
         """
 
@@ -170,17 +199,20 @@ class LinuxCounterManager(CounterManager
 
         self._loadCounters()
         self.registerCounters(counters)
 
     def getCounterValue(self, counterName):
         """Returns the last value of the counter 'counterName'"""
         try:
             self.updatePidList()
-            return self.registeredCounters[counterName][0](self.pidList)
+            if counterName.startswith('Network'):
+                return self.registeredCounters[counterName][0](counterName)
+            else:
+                return self.registeredCounters[counterName][0](self.pidList)
         except:
             return None
 
     def updatePidList(self):
         """Updates the list of PIDs we're interested in"""
         try:
             self.pidList = [self.primaryPid]
             childPids = mozpid.get_pids(self.childProcess)
--- a/talos/output.py
+++ b/talos/output.py
@@ -178,16 +178,21 @@ class GraphserverOutput(Output):
                                                              **info_dict))
                 utils.stamped_msg("Generating results file: %s" % test.name(),
                                   "Stopped")
 
             # counter results
             for cd in test.all_counter_results:
                 for counter_type, values in cd.items():
                     # get the counter name
+
+                    # We don't upload network counters to graph server
+                    if counter_type.startswith('Network'):
+                        continue
+
                     counterName = '%s_%s' % (test.name(),
                                              self.shortName(counter_type))
                     if not values:
                         # failed to collect any data for this counter
                         utils.stamped_msg(
                             "No results collected for: " + counterName,
                             "Error"
                         )
@@ -492,17 +497,16 @@ class PerfherderOutput(Output):
 
             # serialize test results
             results = {}
             tsresult = None
             summary = {"suite": 0, "subtests": {}}
             if not test.using_xperf:
                 vals = []
 
-                # TODO: counters!!!! we don't have any, but they suffer the same
                 for result in test.results:
                     # XXX this will not work for manifests which list
                     # the same page name twice. It also ignores cycles
                     for page, val in result.raw_values():
                         if page == 'NULL':
                             results.setdefault(test.name(), []).extend(val)
                             if tsresult is None:
                                 tsresult = r = TalosResults.Results()
@@ -532,46 +536,63 @@ class PerfherderOutput(Output):
                 suite_summary = self.construct_results(vals,
                                                        testname=test.name())
                 summary['suite'] = suite_summary
                 test_result['summary'] = summary
 
                 for result, values in results.items():
                     test_result['results'][result] = values
 
+            # coalesce counters on --cycles, do filtering as needed
+            tcounters = {}
             # counters results_aux data
             for cd in test.all_counter_results:
                 for name, vals in cd.items():
                     # We want to add the xperf data as talos_counters
                     # exclude counters whose values are tuples (bad for
                     # graphserver)
                     if len(vals) > 0 and isinstance(vals[0], list):
                         continue
 
                     # mainthread IO is a list of filenames and accesses, we do
                     # not report this as a counter
                     if 'mainthreadio' in name:
                         continue
 
-                    if test.using_xperf:
-                        test_result['talos_counters'][name] = {"mean": vals[0]}
-                    else:
-                        # calculate mean and max value
-                        varray = []
-                        counter_mean = 0
-                        counter_max = 0
-                        if len(vals) > 0:
-                            for v in vals:
-                                varray.append(float(v))
-                            counter_mean = "%.2f" % filter.mean(varray)
-                            counter_max = "%.2f" % max(varray)
-                        test_result['talos_counters'][name] = {
-                            "mean": counter_mean,
-                            "max": counter_max
-                        }
+                    if name not in tcounters:
+                        tcounters[name] = []
+                    tcounters[name].extend(vals)
+
+            # counters results_aux data
+            for name in tcounters:
+                vals = tcounters[name]
+
+                if test.using_xperf:
+                    test_result['talos_counters'][name] = {"mean": vals[0]}
+                else:
+                    # calculate mean and max value
+                    varray = []
+                    counter_mean = 0
+                    counter_max = 0
+                    counter_range = 0
+                    if len(vals) > 0:
+                        for v in vals:
+                            varray.append(float(v))
+                        counter_mean = "%.2f" % filter.mean(varray)
+                        counter_max = "%.2f" % max(varray)
+                        counter_range = "%.2f" % (max(varray) - min(varray))
+
+                    # HACK: for network stats to debug stuff we want total traffic, not mean
+                    if name.startswith('Network'):
+                        counter_mean = counter_range
+
+                    test_result['talos_counters'][name] = {
+                        "mean": counter_mean,
+                        "max": counter_max
+                    }
 
             if browser_config['develop'] and not browser_config['sourcestamp']:
                 browser_config['sourcestamp'] = ''
 
             test_result['test_build'] = {
                 'version': browser_config['browser_version'],
                 'revision': browser_config['sourcestamp'],
                 'id': browser_config['buildid'],
--- a/talos/test.py
+++ b/talos/test.py
@@ -102,16 +102,17 @@ class TsBase(Test):
         'xperf_user_providers',
         'xperf_stackwalk',
         'tpmozafterpaint',
         'test_name_extension',
         'extensions',
         'filters',
         'setup',
         'cleanup',
+        'linux_counters',
         'reinstall',     # A list of files from the profile directory that
                          # should be copied to the temporary profile prior to
                          # running each cycle, to avoid one cycle overwriting
                          # the data used by the next another cycle (may be used
                          # e.g. for sessionstore.js to ensure that all cycles
                          # use the exact same sessionstore.js, rather than a
                          # more recent copy).
     ]
@@ -197,16 +198,18 @@ class tresize(TsBase):
     """
     extensions = '${talos}/startup_test/tresize/addon'
     cycles = 20
     url = 'startup_test/tresize/addon/content/tresize-test.html'
     timeout = 150
     sps_profile_interval = 2
     sps_profile_entries = 1000000
     tpmozafterpaint = True
+    linux_counters = ['Network_lo_packets_rx', 'Network_lo_packets_tx', 'Network_eth0_packets_rx', 'Network_eth0_packets_tx',
+                      'Network_lo_bytes_rx', 'Network_lo_bytes_tx', 'Network_eth0_bytes_rx', 'Network_eth0_bytes_tx']
     filters = filter.ignore_first.prepare(5) + filter.median.prepare()
 
 
 # Media Test
 @register_test()
 class media_tests(TsBase):
     """
     Media Performance Tests