util/rendering: add gfxinfo_get_last_dump

Add gfxinfo_get_last_dump utility function to get the last gfxinfo dump
from a (potentially large) file containing a concatenation of such dumps
(as in the raw output of the GfxinfoFrames instrument).
diff --git a/devlib/utils/rendering.py b/devlib/utils/rendering.py
index 3b7b6c4..9ab1e00 100644
--- a/devlib/utils/rendering.py
+++ b/devlib/utils/rendering.py
@@ -203,3 +203,43 @@
         if not found:
             logger.warning('Could not find frames data in gfxinfo output')
             return
+
+
+def _file_reverse_iter(fh, buf_size=1024):
+    fh.seek(0, os.SEEK_END)
+    offset = 0
+    file_size = remaining_size = fh.tell()
+    while remaining_size > 0:
+        offset = min(file_size, offset + buf_size)
+        fh.seek(file_size - offset)
+        buf = fh.read(min(remaining_size, buf_size))
+        remaining_size -= buf_size
+        yield buf
+
+
+def gfxinfo_get_last_dump(filepath):
+    """
+    Return the last gfxinfo dump from the frame collector's raw output.
+
+    """
+    record = ''
+    with open(filepath, 'r') as fh:
+        fh_iter = _file_reverse_iter(fh)
+        try:
+            while True:
+                buf = fh_iter.next()
+                ix = buf.find('** Graphics')
+                if ix >= 0:
+                    return buf[ix:] + record
+
+                ix = buf.find(' **\n')
+                if ix >= 0:
+                    buf =  fh_iter.next() + buf
+                    ix = buf.find('** Graphics')
+                    if ix < 0:
+                        msg = '"{}" appears to be corrupted'
+                        raise RuntimeError(msg.format(filepath))
+                    return buf[ix:] + record
+                record = buf + record
+        except StopIteration:
+            pass