Extend the server-side profiler code to handle reboots as well. Sadly,
this is a bit of a kludge, but I can't see any better solution without
redesigning the client-side profiler interface first.

I handle a reboot by pulling back any existing profiler data right
after a reboot and then re-starting the profiler. This generally works
because most of the profilers are idempotent; either they write into
different files in each run, or they just continuously append logs
to a log file of some kind and so concatenating all their output
is good enough.

In general there's unfortunately no reliable way to signal the
profilers before a reboot occurs, we can only deal with it after the
fact, so we don't have the option of adding some sort of
save-and-restore methods to the client-side interface that could be
used to deal with this.

Right now we simply don't have any real use cases anyway where we want
to run a profiler on top of a reboot test where the profiler requires
special handling after a reboot. Until we actually have an example of
that to work with, trying to implement support for that situation
may just be overdesigning things anyway.

Risk: Medium
Visibility: Server-side profilers will be restarted after a reboot
during a test.

Signed-off-by: John Admanski <jadmanski@google.com>



git-svn-id: http://test.kernel.org/svn/autotest/trunk@2526 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/client/common_lib/utils.py b/client/common_lib/utils.py
index b7695ba..f85213f 100644
--- a/client/common_lib/utils.py
+++ b/client/common_lib/utils.py
@@ -670,6 +670,43 @@
     return arch
 
 
+def merge_trees(src, dest):
+    """
+    Merges a source directory tree at 'src' into a destination tree at
+    'dest'. If a path is a file in both trees than the file in the source
+    tree is APPENDED to the one in the destination tree. If a path is
+    a directory in both trees then the directories are recursively merged
+    with this function. In any other case, the function will skip the
+    paths that cannot be merged (instead of failing).
+    """
+    if not os.path.exists(src):
+        return # exists only in dest
+    elif not os.path.exists(dest):
+        if os.path.isfile(src):
+            shutil.copy2(src, dest) # file only in src
+        else:
+            shutil.copytree(src, dest, symlinks=True) # dir only in src
+        return
+    elif os.path.isfile(src) and os.path.isfile(dest):
+        # src & dest are files in both trees, append src to dest
+        destfile = open(dest, "a")
+        try:
+            srcfile = open(src)
+            try:
+                destfile.write(srcfile.read())
+            finally:
+                srcfile.close()
+        finally:
+            destfile.close()
+    elif os.path.isdir(src) and os.path.isdir(dest):
+        # src & dest are directories in both trees, so recursively merge
+        for name in os.listdir(src):
+            merge_trees(os.path.join(src, name), os.path.join(dest, name))
+    else:
+        # src & dest both exist, but are incompatible
+        return
+
+
 class CmdResult(object):
     """
     Command execution result.