Merge pull request #130 from Leo-Yan/fix_energy_probe_signal_2

energy_probe: send signal SIGINT to caiman
diff --git a/devlib/instrument/monsoon.py b/devlib/instrument/monsoon.py
index 9d544fd..e373d68 100644
--- a/devlib/instrument/monsoon.py
+++ b/devlib/instrument/monsoon.py
@@ -14,8 +14,8 @@
 https://android.googlesource.com/platform/cts/+/master/tools/utils/monsoon.py
 
 Download this script and put it in your $PATH (or pass it as the monsoon_bin
-parameter to MonsoonInstrument). `pip install gflags pyserial` to install the
-dependencies.
+parameter to MonsoonInstrument). `pip install python-gflags pyserial` to install
+the dependencies.
 """
 
 class MonsoonInstrument(Instrument):
diff --git a/devlib/module/cpufreq.py b/devlib/module/cpufreq.py
index d72b8fd..01fb79b 100644
--- a/devlib/module/cpufreq.py
+++ b/devlib/module/cpufreq.py
@@ -421,3 +421,14 @@
         sysfile = '/sys/devices/system/cpu/{}/cpufreq/affected_cpus'.format(cpu)
 
         return [int(c) for c in self.target.read_value(sysfile).split()]
+
+    def iter_domains(self):
+        """
+        Iterate over the frequency domains in the system
+        """
+        cpus = set(range(self.target.number_of_cpus))
+        while cpus:
+            cpu = iter(cpus).next()
+            domain = self.target.cpufreq.get_domain_cpus(cpu)
+            yield domain
+            cpus = cpus.difference(domain)
diff --git a/devlib/target.py b/devlib/target.py
index 9d7f282..35473b4 100644
--- a/devlib/target.py
+++ b/devlib/target.py
@@ -4,6 +4,7 @@
 import logging
 import posixpath
 import subprocess
+import tarfile
 import tempfile
 import threading
 from collections import namedtuple
@@ -281,11 +282,34 @@
     def pull(self, source, dest, timeout=None):
         return self.conn.pull(source, dest, timeout=timeout)
 
-    # execution
+    def get_directory(self, source_dir, dest):
+        """ Pull a directory from the device, after compressing dir """
+        # Create all file names
+        tar_file_name = source_dir.lstrip(self.path.sep).replace(self.path.sep, '.')
+        # Host location of dir
+        outdir = os.path.join(dest, tar_file_name)
+        # Host location of archive
+        tar_file_name  = '{}.tar'.format(tar_file_name)
+        tempfile = os.path.join(dest, tar_file_name)
 
-    def _execute_util(self, command, timeout=None, check_exit_code=True, as_root=False):
-        command = '{} {}'.format(self.shutils, command)
-        return self.conn.execute(command, timeout, check_exit_code, as_root)
+        # Does the folder exist?
+        self.execute('ls -la {}'.format(source_dir))
+        # Try compressing the folder
+        try:
+            self.execute('{} tar -cvf {} {}'.format(self.busybox, tar_file_name,
+                                                     source_dir))
+        except TargetError:
+            self.logger.debug('Failed to run tar command on target! ' \
+                              'Not pulling directory {}'.format(source_dir))
+        # Pull the file
+        os.mkdir(outdir)
+        self.pull(tar_file_name, tempfile )
+        # Decompress
+        f = tarfile.open(tempfile, 'r')
+        f.extractall(outdir)
+        os.remove(tempfile)
+
+    # execution
 
     def execute(self, command, timeout=None, check_exit_code=True, as_root=False):
         return self.conn.execute(command, timeout, check_exit_code, as_root)
@@ -556,8 +580,16 @@
         else:
             raise ValueError('Unknown compression format: {}'.format(ext))
 
+    def sleep(self, duration):
+        timeout = duration + 10
+        self.execute('sleep {}'.format(duration), timeout=timeout)
+
     # internal methods
 
+    def _execute_util(self, command, timeout=None, check_exit_code=True, as_root=False):
+        command = '{} {}'.format(self.shutils, command)
+        return self.conn.execute(command, timeout, check_exit_code, as_root)
+
     def _extract_archive(self, path, cmd, dest=None):
         cmd = '{} ' + cmd  # busybox
         if dest:
@@ -1067,6 +1099,13 @@
         if not self.is_screen_on():
             self.execute('input keyevent 26')
 
+    def ensure_screen_is_off(self):
+        if self.is_screen_on():
+            self.execute('input keyevent 26')
+
+    def homescreen(self):
+        self.execute('am start -a android.intent.action.MAIN -c android.intent.category.HOME')
+
     def _resolve_paths(self):
         if self.working_directory is None:
             self.working_directory = '/data/local/tmp/devlib-target'
diff --git a/devlib/trace/ftrace.py b/devlib/trace/ftrace.py
index 7affed5..d4e37e1 100644
--- a/devlib/trace/ftrace.py
+++ b/devlib/trace/ftrace.py
@@ -60,6 +60,7 @@
                  autoview=False,
                  no_install=False,
                  strict=False,
+                 report_on_target=False,
                  ):
         super(FtraceCollector, self).__init__(target)
         self.events = events if events is not None else DEFAULT_EVENTS
@@ -70,7 +71,10 @@
         self.automark = automark
         self.autoreport = autoreport
         self.autoview = autoview
-        self.target_output_file = os.path.join(self.target.working_directory, OUTPUT_TRACE_FILE)
+        self.report_on_target = report_on_target
+        self.target_output_file = target.path.join(self.target.working_directory, OUTPUT_TRACE_FILE)
+        text_file_name = target.path.splitext(OUTPUT_TRACE_FILE)[0] + '.txt'
+        self.target_text_file = target.path.join(self.target.working_directory, text_file_name)
         self.target_binary = None
         self.host_binary = None
         self.start_time = None
@@ -93,7 +97,7 @@
 
         if not self.target.is_rooted:
             raise TargetError('trace-cmd instrument cannot be used on an unrooted device.')
-        if self.autoreport and self.host_binary is None:
+        if self.autoreport and not self.report_on_target and self.host_binary is None:
             raise HostError('trace-cmd binary must be installed on the host if autoreport=True.')
         if self.autoview and self.kernelshark is None:
             raise HostError('kernelshark binary must be installed on the host if autoview=True.')
@@ -202,8 +206,9 @@
 
     def get_trace(self, outfile):
         if os.path.isdir(outfile):
-            outfile = os.path.join(outfile, os.path.dirname(self.target_output_file))
-        self.target.execute('{} extract -o {}'.format(self.target_binary, self.target_output_file),
+            outfile = os.path.join(outfile, os.path.basename(self.target_output_file))
+        self.target.execute('{0} extract -o {1}; chmod 666 {1}'.format(self.target_binary,
+                                                                       self.target_output_file),
                             timeout=TIMEOUT, as_root=True)
 
         # The size of trace.dat will depend on how long trace-cmd was running.
@@ -216,7 +221,12 @@
         else:
             if self.autoreport:
                 textfile = os.path.splitext(outfile)[0] + '.txt'
-                self.report(outfile, textfile)
+                if self.report_on_target:
+                    self.generate_report_on_target()
+                    self.target.pull(self.target_text_file,
+                                     textfile, timeout=pull_timeout)
+                else:
+                    self.report(outfile, textfile)
             if self.autoview:
                 self.view(outfile)
 
@@ -286,6 +296,12 @@
         except OSError:
             raise HostError('Could not find trace-cmd. Please make sure it is installed and is in PATH.')
 
+    def generate_report_on_target(self):
+        command = '{} report {} > {}'.format(self.target_binary,
+                                             self.target_output_file,
+                                             self.target_text_file)
+        self.target.execute(command, timeout=TIMEOUT)
+
     def view(self, binfile):
         check_output('{} {}'.format(self.kernelshark, binfile), shell=True)
 
diff --git a/devlib/utils/android.py b/devlib/utils/android.py
index 5832553..bd49ea4 100644
--- a/devlib/utils/android.py
+++ b/devlib/utils/android.py
@@ -189,7 +189,7 @@
             self.ls_command = 'ls -1'
         else:
             self.ls_command = 'ls'
-        logger.info("ls command is set to {}".format(self.ls_command))
+        logger.debug("ls command is set to {}".format(self.ls_command))
 
     def __init__(self, device=None, timeout=None, platform=None):
         self.timeout = timeout if timeout is not None else self.default_timeout
diff --git a/devlib/utils/ssh.py b/devlib/utils/ssh.py
index b82680e..8704008 100644
--- a/devlib/utils/ssh.py
+++ b/devlib/utils/ssh.py
@@ -439,26 +439,40 @@
         # First check if the connection is set up to interact with gem5
         self._check_ready()
 
-        filename = os.path.basename(source)
+        result = self._gem5_shell("ls {}".format(source))
+        files = result.split()
 
-        logger.debug("pull_file {} {}".format(source, filename))
-        # We don't check the exit code here because it is non-zero if the source
-        # and destination are the same. The ls below will cause an error if the
-        # file was not where we expected it to be.
-        if os.path.dirname(source) != os.getcwd():
-            self._gem5_shell("cat '{}' > '{}'".format(source, filename))
-        self._gem5_shell("sync")
-        self._gem5_shell("ls -la {}".format(filename))
-        logger.debug('Finished the copy in the simulator')
-        self._gem5_util("writefile {}".format(filename))
+        for filename in files:
+            dest_file = os.path.basename(filename)
+            logger.debug("pull_file {} {}".format(filename, dest_file))
+            # writefile needs the file to be copied to be in the current
+            # working directory so if needed, copy to the working directory
+            # We don't check the exit code here because it is non-zero if the
+            # source and destination are the same. The ls below will cause an
+            # error if the file was not where we expected it to be.
+            if os.path.isabs(source):
+                if os.path.dirname(source) != self.execute('pwd',
+                                              check_exit_code=False):
+                    self._gem5_shell("cat '{}' > '{}'".format(filename,
+                                                              dest_file))
+            self._gem5_shell("sync")
+            self._gem5_shell("ls -la {}".format(dest_file))
+            logger.debug('Finished the copy in the simulator')
+            self._gem5_util("writefile {}".format(dest_file))
 
-        if 'cpu' not in filename:
-            while not os.path.exists(os.path.join(self.gem5_out_dir, filename)):
-                time.sleep(1)
+            if 'cpu' not in filename:
+                while not os.path.exists(os.path.join(self.gem5_out_dir,
+                                                      dest_file)):
+                    time.sleep(1)
 
-        # Perform the local move
-        shutil.move(os.path.join(self.gem5_out_dir, filename), dest)
-        logger.debug("Pull complete.")
+            # Perform the local move
+            if os.path.exists(os.path.join(dest, dest_file)):
+                logger.warning(
+                            'Destination file {} already exists!'\
+                            .format(dest_file))
+            else:
+                shutil.move(os.path.join(self.gem5_out_dir, dest_file), dest)
+            logger.debug("Pull complete.")
 
     def execute(self, command, timeout=1000, check_exit_code=True,
                 as_root=False, strip_colors=True):
@@ -468,7 +482,9 @@
         # First check if the connection is set up to interact with gem5
         self._check_ready()
 
-        output = self._gem5_shell(command, as_root=as_root)
+        output = self._gem5_shell(command,
+                                  check_exit_code=check_exit_code,
+                                  as_root=as_root)
         if strip_colors:
             output = strip_bash_colors(output)
         return output
diff --git a/setup.py b/setup.py
index 47a4475..e7753d4 100644
--- a/setup.py
+++ b/setup.py
@@ -74,6 +74,7 @@
     extras_require={
         'daq': ['daqpower'],
         'doc': ['sphinx'],
+        'monsoon': ['python-gflags'],
     },
     # https://pypi.python.org/pypi?%3Aaction=list_classifiers
     classifiers=[