Store machine description info in cache and json reports.

This CL updates the cache mechanism to store the machine spec
info (the checksum string) in the cache, as well as storing the
machine information in the json reports (to go into the dashboard
archive).  It also updates the json reports to always include the
full set of result fields for a test, and it verifies that the
old cache directory exists before trying to remove it.

BUG=None
TEST=Tested in my directory, with --cache_only=True, --rerun=True and
--json_report=True

Change-Id: Ib12bae4ca10141de0b972e3a7aeee83c7b59049b
Reviewed-on: https://chrome-internal-review.googlesource.com/229765
Commit-Ready: Caroline Tice <cmtice@google.com>
Tested-by: Caroline Tice <cmtice@google.com>
Reviewed-by: Han Shen <shenhan@google.com>
diff --git a/crosperf/benchmark_run.py b/crosperf/benchmark_run.py
index f8bc379..9be4238 100644
--- a/crosperf/benchmark_run.py
+++ b/crosperf/benchmark_run.py
@@ -107,7 +107,7 @@
         retval = 1
         err = "No cache hit."
         self.result = Result.CreateFromRun(self._logger, self.log_level,
-                                           self.label,
+                                           self.label, self.machine,
                                            output, err, retval,
                                            self.benchmark.show_all_results,
                                            self.benchmark.test_name,
@@ -225,6 +225,7 @@
     return Result.CreateFromRun(self._logger,
                                 self.log_level,
                                 self.label,
+                                self.machine,
                                 out,
                                 err,
                                 retval,
diff --git a/crosperf/experiment_runner.py b/crosperf/experiment_runner.py
index 73e9fe3..ec74fdb 100644
--- a/crosperf/experiment_runner.py
+++ b/crosperf/experiment_runner.py
@@ -139,8 +139,9 @@
                   br.label, br.share_cache, br.benchmark.suite,
                   br.benchmark.show_all_results, br.benchmark.run_local)
       cache_dir = cache._GetCacheDirForWrite()
-      self.l.LogOutput("Removing cache dir: %s" % cache_dir)
-      shutil.rmtree(cache_dir)
+      if os.path.exists(cache_dir):
+        self.l.LogOutput("Removing cache dir: %s" % cache_dir)
+        shutil.rmtree(cache_dir)
 
   def _Run(self, experiment):
     try:
diff --git a/crosperf/results_cache.py b/crosperf/results_cache.py
index 5d0f96e..b42ec78 100644
--- a/crosperf/results_cache.py
+++ b/crosperf/results_cache.py
@@ -36,7 +36,7 @@
   perf.report, etc. The key generation is handled by the ResultsCache class.
   """
 
-  def __init__(self, logger, label, log_level, cmd_exec=None):
+  def __init__(self, logger, label, log_level, machine, cmd_exec=None):
     self._chromeos_root = label.chromeos_root
     self._logger = logger
     self._ce = cmd_exec or command_executer.GetCommandExecuter(self._logger,
@@ -45,6 +45,7 @@
     self.label = label
     self.results_dir = None
     self.log_level = log_level
+    self.machine = machine
     self.perf_data_files = []
     self.perf_report_files = []
 
@@ -331,6 +332,7 @@
       pickle.dump(self.retval, f)
 
     with open(os.path.join(temp_dir, CACHE_KEYS_FILE), "w") as f:
+      f.write("%s\n" % self.machine.checksum_string)
       for k in key_list:
         f.write(k)
         f.write("\n")
@@ -366,22 +368,22 @@
                       (temp_dir, cache_dir))
 
   @classmethod
-  def CreateFromRun(cls, logger, log_level, label, out, err, retval, show_all,
-                    test, suite="telemetry_Crosperf"):
+  def CreateFromRun(cls, logger, log_level, label, machine, out, err, retval,
+                    show_all, test, suite="telemetry_Crosperf"):
     if suite == "telemetry":
-      result = TelemetryResult(logger, label, log_level)
+      result = TelemetryResult(logger, label, log_level, machine)
     else:
-      result = cls(logger, label, log_level)
+      result = cls(logger, label, log_level, machine)
     result._PopulateFromRun(out, err, retval, show_all, test, suite)
     return result
 
   @classmethod
-  def CreateFromCacheHit(cls, logger, log_level, label, cache_dir,
+  def CreateFromCacheHit(cls, logger, log_level, label, machine, cache_dir,
                          show_all, test, suite="telemetry_Crosperf"):
     if suite == "telemetry":
-      result = TelemetryResult(logger, label)
+      result = TelemetryResult(logger, label, log_level, machine)
     else:
-      result = cls(logger, label, log_level)
+      result = cls(logger, label, log_level, machine)
     try:
       result._PopulateFromCacheDir(cache_dir, show_all, test, suite)
 
@@ -393,8 +395,9 @@
 
 class TelemetryResult(Result):
 
-  def __init__(self, logger, label, log_level, cmd_exec=None):
-    super(TelemetryResult, self).__init__(logger, label, log_level, cmd_exec)
+  def __init__(self, logger, label, log_level, machine, cmd_exec=None):
+    super(TelemetryResult, self).__init__(logger, label, log_level, machine,
+                                          cmd_exec)
 
   def _PopulateFromRun(self, out, err, retval, show_all, test, suite):
     self.out = out
@@ -600,6 +603,7 @@
     result = Result.CreateFromCacheHit(self._logger,
                                        self.log_level,
                                        self.label,
+                                       self.machine,
                                        cache_dir,
                                        self.show_all,
                                        self.test_name,
diff --git a/crosperf/results_organizer.py b/crosperf/results_organizer.py
index 0460b6e..5395d8e 100644
--- a/crosperf/results_organizer.py
+++ b/crosperf/results_organizer.py
@@ -29,7 +29,8 @@
     ]}.
   """
 
-  def __init__(self, benchmark_runs, labels, benchmarks=None):
+  def __init__(self, benchmark_runs, labels, benchmarks=None,
+               json_report=False):
     self.result = {}
     self.labels = []
     self.prog = re.compile(r"(\w+)\{(\d+)\}")
@@ -40,7 +41,10 @@
       self.labels.append(label.name)
     for benchmark_run in benchmark_runs:
       benchmark_name = benchmark_run.benchmark.name
-      show_all_results = benchmark_run.benchmark.show_all_results
+      if json_report:
+        show_all_results = True
+      else:
+        show_all_results = benchmark_run.benchmark.show_all_results
       if benchmark_name not in self.result:
         self.result[benchmark_name] = []
         while len(self.result[benchmark_name]) < len(labels):
@@ -66,6 +70,10 @@
           continue
         result_value = benchmark_run.result.keyvals[test_key]
         cur_dict[test_key] = result_value
+      if json_report and benchmark_run.machine:
+        cur_dict['machine'] = benchmark_run.machine.name
+        cur_dict['machine_checksum'] = benchmark_run.machine.checksum
+        cur_dict['machine_string'] = benchmark_run.machine.checksum_string
     self._DuplicatePass()
 
   def _GetSummaryResults (self, test_name):
diff --git a/crosperf/results_report.py b/crosperf/results_report.py
index f1b7181..4c7bd08 100644
--- a/crosperf/results_report.py
+++ b/crosperf/results_report.py
@@ -568,7 +568,8 @@
     super(JSONResultsReport, self).__init__(experiment)
     self.ro = ResultOrganizer(experiment.benchmark_runs,
                               experiment.labels,
-                              experiment.benchmarks)
+                              experiment.benchmarks,
+                              json_report=True)
     self.date = date
     self.time = time
     self.defaults = TelemetryDefaults()
@@ -592,11 +593,18 @@
           json_results['date'] = self.date
           json_results['time'] = self.time
           json_results['board'] = board
+          json_results['label'] = label
+          common_checksum = ''
+          common_string = ''
           for l in self.experiment.labels:
             if l.name == label:
               ver, img = ParseChromeosImage(l.chromeos_image)
               json_results['chromeos_image'] = img
               json_results['chromeos_version'] = ver
+              common_checksum = \
+                self.experiment.machine_manager.machine_checksum[l.name]
+              common_string = \
+                self.experiment.machine_manager.machine_checksum_string[l.name]
               break
           json_results['test_name'] = test
           if not iter_results or iter_results['retval'] != 0:
@@ -622,7 +630,14 @@
                 if type(v) == list:
                   v = v[0]
                 if v != 'PASS':
-                  detail_results[k] = float(v)
+                  if k.find('machine') == -1:
+                    detail_results[k] = float(v)
+                  else:
+                    json_results[k] = v
+            if 'machine_checksum' not in json_results.keys():
+              json_results['machine_checksum'] = common_checksum
+            if 'machine_string' not in json_results.keys():
+              json_results['machine_string'] = common_string
             json_results['detailed_results'] = detail_results
           final_results.append(json_results)