Index: /trunk/src/VBox/ValidationKit/testmanager/core/testresults.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/core/testresults.py	(revision 61455)
+++ /trunk/src/VBox/ValidationKit/testmanager/core/testresults.py	(revision 61456)
@@ -1027,6 +1027,20 @@
                           'FROM   TestResults\n'
                           'WHERE  idTestSet = %s\n'
+                          ') UNION (\n'
+                          'SELECT TestResultFiles.tsCreated\n'
+                          'FROM   TestResultFiles\n'
+                          '  JOIN TestResults ON TestResultFiles.idTestResult = TestResults.idTestResult\n'
+                          'WHERE  idTestSet = %s\n'
+                          ') UNION (\n'
+                          'SELECT tsCreated\n'
+                          'FROM   TestResultValues\n'
+                          'WHERE  idTestSet = %s\n'
+                          ') UNION (\n'
+                          'SELECT TestResultMsgs.tsCreated\n'
+                          'FROM   TestResultMsgs\n'
+                          '  JOIN TestResults ON TestResultMsgs.idTestResult = TestResults.idTestResult\n'
+                          'WHERE  idTestSet = %s\n'
                           ') ORDER by 1'
-                          , ( idTestSet, idTestSet, ));
+                          , ( idTestSet, idTestSet, idTestSet, idTestSet, idTestSet, ));
         return [aoRow[0] for aoRow in self._oDb.fetchAll()];
 
Index: /trunk/src/VBox/ValidationKit/testmanager/webui/wuilogviewer.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/webui/wuilogviewer.py	(revision 61455)
+++ /trunk/src/VBox/ValidationKit/testmanager/webui/wuilogviewer.py	(revision 61456)
@@ -39,10 +39,11 @@
     """Log viewer."""
 
-    def __init__(self, oTestSet, oLogFile, cbChunk, iChunk, oDisp = None, fnDPrint = None):
+    def __init__(self, oTestSet, oLogFile, cbChunk, iChunk, aoTimestamps, oDisp = None, fnDPrint = None):
         WuiContentBase.__init__(self, oDisp = oDisp, fnDPrint = fnDPrint);
-        self._oTestSet  = oTestSet;
-        self._oLogFile  = oLogFile;
-        self._cbChunk   = cbChunk;
-        self._iChunk    = iChunk;
+        self._oTestSet      = oTestSet;
+        self._oLogFile      = oLogFile;
+        self._cbChunk       = cbChunk;
+        self._iChunk        = iChunk;
+        self._aoTimestamps  = aoTimestamps;
 
     def _generateNavigation(self, cbFile):
@@ -125,6 +126,27 @@
                '</div>\n';
 
-    def _displayLog(self, oFile, offFile, cbFile):
+    def _displayLog(self, oFile, offFile, cbFile, aoTimestamps):
         """Displays the current section of the log file."""
+        from testmanager.core import db;
+
+        def prepCurTs():
+            """ Formats the current timestamp. """
+            if iCurTs < len(aoTimestamps):
+                oTsZulu = db.dbTimestampToZuluDatetime(aoTimestamps[iCurTs]);
+                return (oTsZulu.strftime('%H:%M:%S.%f'), oTsZulu.strftime('%H_%M_%S_%f'));
+            return '~~|~~|~~|~~~~~~'; # ASCII chars with high values. Limit hits.
+
+        def isCurLineAtOrAfterCurTs():
+            """ Checks if the current line starts with a timestamp that is after the current one. """
+            if    len(sLine) >= 15 \
+              and sLine[2]  == ':' \
+              and sLine[5]  == ':' \
+              and sLine[8]  == '.' \
+              and sLine[14] in '0123456789':
+                if sLine[:15] >=  sCurTs and iCurTs < len(aoTimestamps):
+                    return True;
+            return False;
+
+        # Figure the end offset.
         offEnd = offFile + self._cbChunk;
         if offEnd > cbFile:
@@ -136,6 +158,8 @@
         # numbers while we're at it.
         #
-        offCur  = 0;
-        iLine   = 0;
+        iCurTs           = 0;
+        (sCurTs, sCurId) = prepCurTs();
+        offCur           = 0;
+        iLine            = 0;
         while True:
             sLine   = oFile.readline().decode('utf-8', 'replace');
@@ -145,12 +169,26 @@
             if offCur >= offFile or len(sLine) == 0:
                 break;
+            while isCurLineAtOrAfterCurTs():
+                iCurTs += 1;
+                (sCurTs, sCurId) = prepCurTs();
 
         #
         # Got to where we wanted, format the chunk.
         #
-        asLines = [];
+        asLines = ['\n<div class="tmlog">\n<pre>\n', ];
         while True:
+            # The timestamp IDs.
+            sPrevTs = '';
+            while isCurLineAtOrAfterCurTs():
+                if sPrevTs != sCurTs:
+                    asLines.append('<a id="%s"></a>' % (sCurId,));
+                iCurTs += 1;
+                (sCurTs, sCurId) = prepCurTs();
+
+            # The line.
             asLines.append('<a id="L%d" href="#L%d">%05d</a><a id="O%d"></a>%s\n' \
                            % (iLine, iLine, iLine, offLine, webutils.escapeElem(sLine.rstrip())));
+
+            # next
             if offCur >= offEnd:
                 break;
@@ -161,5 +199,6 @@
             if len(sLine) == 0:
                 break;
-        return '\n<div class="tmlog">\n<pre>\n' + ''.join(asLines) + '<pre/></div>\n';
+        asLines.append('<pre/></div>\n');
+        return ''.join(asLines);
 
 
@@ -195,5 +234,5 @@
         offFile   = self._iChunk * self._cbChunk;
         if offFile < cbFile:
-            sHtml += self._displayLog(oFile, offFile, cbFile);
+            sHtml += self._displayLog(oFile, offFile, cbFile, self._aoTimestamps);
             sHtml += sNaviHtml;
         else:
Index: /trunk/src/VBox/ValidationKit/testmanager/webui/wuimain.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/webui/wuimain.py	(revision 61455)
+++ /trunk/src/VBox/ValidationKit/testmanager/webui/wuimain.py	(revision 61456)
@@ -1017,11 +1017,14 @@
         oTestSet = TestSetData().initFromDbWithId(self._oDb, idTestSet);
         if idLogFile == 0:
-            oTestFile = TestResultFileDataEx().initFakeMainLog(oTestSet);
+            oTestFile    = TestResultFileDataEx().initFakeMainLog(oTestSet);
+            aoTimestamps = TestResultLogic(self._oDb).fetchTimestampsForLogViewer(idTestSet);
         else:
-            oTestFile = TestSetLogic(self._oDb).getFile(idTestSet, idLogFile);
+            oTestFile    = TestSetLogic(self._oDb).getFile(idTestSet, idLogFile);
+            aoTimestamps = [];
         if oTestFile.sMime not in [ 'text/plain',]:
             raise WuiException('The log view does not display files of type: %s' % (oTestFile.sMime,));
 
-        oContent = WuiLogViewer(oTestSet, oTestFile, cbChunk, iChunk, oDisp = self, fnDPrint = self._oSrvGlue.dprint);
+        oContent = WuiLogViewer(oTestSet, oTestFile, cbChunk, iChunk, aoTimestamps,
+                                oDisp = self, fnDPrint = self._oSrvGlue.dprint);
         (self._sPageTitle, self._sPageBody) = oContent.show();
         return True;
Index: /trunk/src/VBox/ValidationKit/testmanager/webui/wuitestresult.py
===================================================================
--- /trunk/src/VBox/ValidationKit/testmanager/webui/wuitestresult.py	(revision 61455)
+++ /trunk/src/VBox/ValidationKit/testmanager/webui/wuitestresult.py	(revision 61456)
@@ -135,4 +135,22 @@
 
 
+    def _formatEventTimestampHtml(self, tsEvent, tsLog, idEvent, oTestSet):
+        """ Formats an event timestamp with a main log link. """
+        tsEvent = db.dbTimestampToZuluDatetime(tsEvent);
+        #sFormattedTimestamp = u'%04u\u2011%02u\u2011%02u\u00a0%02u:%02u:%02uZ' \
+        #                    % ( tsEvent.year, tsEvent.month, tsEvent.day,
+        #                        tsEvent.hour, tsEvent.minute, tsEvent.second,);
+        sFormattedTimestamp = u'%02u:%02u:%02uZ' \
+                            % ( tsEvent.hour, tsEvent.minute, tsEvent.second,);
+        sTitle              = u'#%u - %04u\u2011%02u\u2011%02u\u00a0%02u:%02u:%02u.%06uZ' \
+                            % ( idEvent, tsEvent.year, tsEvent.month, tsEvent.day,
+                                tsEvent.hour, tsEvent.minute, tsEvent.second, tsEvent.microsecond, );
+        tsLog = db.dbTimestampToZuluDatetime(tsLog);
+        sFragment = u'%02u_%02u_%02u_%06u' % ( tsLog.hour, tsLog.minute, tsLog.second, tsLog.microsecond);
+        return WuiTmLink(sFormattedTimestamp, '',
+                         { WuiMain.ksParamAction:             WuiMain.ksActionViewLog,
+                           WuiMain.ksParamLogSetId:           oTestSet.idTestSet,  },
+                         sFragmentId = sFragment, sTitle = sTitle, fBracketed = False, ).toHtml();
+
     def _recursivelyGenerateEvents(self, oTestResult, sParentName, sLineage, iRow,
                                    iFailure, oTestSet, iDepth):     # pylint: disable=R0914
@@ -191,5 +209,6 @@
                      ' </tr>\n' \
                    % ( 'tmodd' if iRow & 1 else 'tmeven', iDepth, oTestResult.enmStatus, oTestResult.idTestResult,
-                       oTestResult.idTestResult, webutils.escapeElem(self.formatTsShort(tsEvent)),
+                       oTestResult.idTestResult,
+                       self._formatEventTimestampHtml(tsEvent, oTestResult.tsCreated, oTestResult.idTestResult, oTestSet),
                        sElapsedGraph,
                        webutils.escapeElem(self.formatIntervalShort(oTestResult.tsElapsed)) if oTestResult.tsElapsed is not None
@@ -212,5 +231,6 @@
                      ' </tr>\n' \
                    % ( 'tmodd' if iRow & 1 else 'tmeven', iDepth,
-                       webutils.escapeElem(self.formatTsShort(oTestResult.tsCreated)), ## @todo more timeline stuff later.
+                       self._formatEventTimestampHtml(oTestResult.tsCreated, oTestResult.tsCreated,
+                                                      oTestResult.idTestResult, oTestSet),
                        sDisplayName,
                        'running' if oTestResult.tsElapsed is None else '', );
@@ -235,5 +255,5 @@
                          ' </tr>\n' \
                        % ( 'tmodd' if iRow & 1 else 'tmeven', iDepth,
-                           webutils.escapeElem(self.formatTsShort(oMsg.tsCreated)),
+                           self._formatEventTimestampHtml(oMsg.tsCreated, oMsg.tsCreated, oMsg.idTestResultMsg, oTestSet),
                            webutils.escapeElem(oMsg.enmLevel),
                            webutils.escapeElem(oMsg.sMsg), );
@@ -252,5 +272,5 @@
                          ' </tr>\n' \
                        % ( 'tmodd' if iRow & 1 else 'tmeven', iDepth,
-                           webutils.escapeElem(self.formatTsShort(oValue.tsCreated)),
+                           self._formatEventTimestampHtml(oValue.tsCreated, oValue.tsCreated, oValue.idTestResultValue, oTestSet),
                            webutils.escapeElem(oValue.sName),
                            utils.formatNumber(oValue.lValue).replace(' ', '&nbsp;'),
@@ -301,5 +321,5 @@
                          ' </tr>\n' \
                        % ( 'tmodd' if iRow & 1 else 'tmeven', iDepth,
-                           webutils.escapeElem(self.formatTsShort(oFile.tsCreated)),
+                           self._formatEventTimestampHtml(oFile.tsCreated, oFile.tsCreated, oFile.idTestResultFile, oTestSet),
                            '\n'.join(oLink.toHtml() for oLink in aoLinks),);
                 iRow += 1;
@@ -307,4 +327,5 @@
             # Done?
             if oTestResult.tsElapsed is not None:
+                tsEvent = oTestResult.tsCreated + oTestResult.tsElapsed;
                 sHtml += ' <tr class="%s tmtbl-events-final tmtbl-events-lvl%s tmstatusrow-%s" id="E%d">\n' \
                          '  <td>%s</td>\n' \
@@ -316,5 +337,5 @@
                          ' </tr>\n' \
                        % ( 'tmodd' if iRow & 1 else 'tmeven', iDepth, oTestResult.enmStatus, oTestResult.idTestResult,
-                           webutils.escapeElem(self.formatTsShort(oTestResult.tsCreated + oTestResult.tsElapsed)),
+                           self._formatEventTimestampHtml(tsEvent, tsEvent, oTestResult.idTestResult, oTestSet),
                            sElapsedGraph,
                            webutils.escapeElem(self.formatIntervalShort(oTestResult.tsElapsed)),
