tp: write back converted timestamps for systrace lines (#6200)

SystraceTraceParser and JsonTraceTokenizer (for embedded
systemTraceEvents) converted line timestamps to trace time but only
used the result as the sorter key: SystraceLineParser populates tables
from line.ts, so events were written with the unconverted value. This
was masked whenever the conversion was the identity, but inside an
archive where a proto trace provides a MONOTONIC<->BOOTTIME snapshot
the converted and raw values differ.

Write the converted timestamp back into the line before pushing it to
the sorter.
diff --git a/src/trace_processor/importers/json/json_trace_tokenizer.cc b/src/trace_processor/importers/json/json_trace_tokenizer.cc
index 1fa29cd..013661c 100644
--- a/src/trace_processor/importers/json/json_trace_tokenizer.cc
+++ b/src/trace_processor/importers/json/json_trace_tokenizer.cc
@@ -984,6 +984,9 @@
     auto trace_ts =
         context_->clock_tracker->ToTraceTime(trace_file_clock_, line.ts);
     if (trace_ts) {
+      // SystraceLineParser populates tables from line.ts, so the conversion
+      // must be written back, not just used as the sorting key.
+      line.ts = *trace_ts;
       systrace_stream_->Push(*trace_ts, std::move(line));
     }
   }
diff --git a/src/trace_processor/importers/systrace/systrace_trace_parser.cc b/src/trace_processor/importers/systrace/systrace_trace_parser.cc
index 2b9c2e8..c71978a 100644
--- a/src/trace_processor/importers/systrace/systrace_trace_parser.cc
+++ b/src/trace_processor/importers/systrace/systrace_trace_parser.cc
@@ -191,6 +191,10 @@
               ClockId::Machine(protos::pbzero::BUILTIN_CLOCK_MONOTONIC),
               line.ts);
           if (trace_ts) {
+            // The converted timestamp is both the sorting key and the value
+            // the parser stage reads: SystraceLineParser populates tables
+            // from line.ts, so the conversion must be written back.
+            line.ts = *trace_ts;
             stream_->Push(*trace_ts, std::move(line));
           }
         } else {
diff --git a/test/trace_processor/diff_tests/parser/zip/tests.py b/test/trace_processor/diff_tests/parser/zip/tests.py
index f84efc1..b79a329 100644
--- a/test/trace_processor/diff_tests/parser/zip/tests.py
+++ b/test/trace_processor/diff_tests/parser/zip/tests.py
@@ -152,6 +152,38 @@
         "a.systrace","systrace",2
         '''))
 
+  # Systrace timestamps must be written back after clock conversion: inside a
+  # zip with a proto trace providing a MONOTONIC<->BOOTTIME snapshot the
+  # conversion is not the identity, and the value written to tables must be
+  # the converted one (which is also the sorting key). The 1.0s MONOTONIC
+  # slice lands at BOOTTIME 1_000_000_000 + 500_000_000.
+  def test_zip_systrace_converted_timestamps(self):
+    return DiffTestBlueprint(
+        trace=ZipTrace({
+            'sys.systrace':
+                '''# tracer: nop
+#
+  app-100 (  100) [001] ...1  1.000000: tracing_mark_write: B|100|sys_slice
+  app-100 (  100) [001] ...1  1.500000: tracing_mark_write: E|100
+''',
+            'spine.pb':
+                TextProto('''
+              packet {
+                clock_snapshot {
+                  clocks { clock_id: 6 timestamp: 1000000000 }
+                  clocks { clock_id: 3 timestamp: 500000000 }
+                }
+              }
+            '''),
+        }),
+        query='''
+          SELECT name, ts, dur FROM slice WHERE name = 'sys_slice';
+        ''',
+        out=Csv('''
+        "name","ts","dur"
+        "sys_slice",1500000000,500000000
+        '''))
+
   # A tar archive with an external file (DataPath) as a member: the raw bytes
   # of the checked-in trace are included verbatim.
   def test_tar_blueprint_external_member(self):