internal/trace: fix int overflow in timestamps

Fixes #15102

Change-Id: I7fdb6464afd0b7af9b6652051416f0fddd34dc9a
Reviewed-on: https://go-review.googlesource.com/21730
Reviewed-by: Austin Clements <austin@google.com>
diff --git a/src/internal/trace/parser.go b/src/internal/trace/parser.go
index 82ddb8b..3099b0f 100644
--- a/src/internal/trace/parser.go
+++ b/src/internal/trace/parser.go
@@ -372,8 +372,10 @@
 		return
 	}
 	minTs := events[0].Ts
+	// Use floating point to avoid integer overflows.
+	freq := 1e9 / float64(ticksPerSec)
 	for _, ev := range events {
-		ev.Ts = (ev.Ts - minTs) * 1e9 / ticksPerSec
+		ev.Ts = int64(float64(ev.Ts-minTs) * freq)
 		// Move timers and syscalls to separate fake Ps.
 		if timerGoid != 0 && ev.G == timerGoid && ev.Type == EvGoUnblock {
 			ev.P = TimerP
diff --git a/src/internal/trace/parser_test.go b/src/internal/trace/parser_test.go
index db8d2a3..337d5a8 100644
--- a/src/internal/trace/parser_test.go
+++ b/src/internal/trace/parser_test.go
@@ -85,3 +85,55 @@
 		}
 	}
 }
+
+func TestTimestampOverflow(t *testing.T) {
+	// Test that parser correctly handles large timestamps (long tracing).
+	w := newWriter()
+	w.emit(EvBatch, 0, 0, 0)
+	w.emit(EvFrequency, 1e9, 0)
+	for ts := uint64(1); ts < 1e16; ts *= 2 {
+		w.emit(EvGoCreate, 1, ts, ts, 1, 0)
+	}
+	if _, err := Parse(w, ""); err != nil {
+		t.Fatalf("failed to parse: %v", err)
+	}
+}
+
+type writer struct {
+	bytes.Buffer
+}
+
+func newWriter() *writer {
+	w := new(writer)
+	w.Write([]byte("go 1.7 trace\x00\x00\x00\x00"))
+	return w
+}
+
+func (w *writer) emit(typ byte, args ...uint64) {
+	nargs := byte(len(args)) - 2
+	if nargs > 3 {
+		nargs = 3
+	}
+	buf := []byte{typ | nargs<<6}
+	if nargs == 3 {
+		buf = append(buf, 0)
+	}
+	for _, a := range args {
+		buf = appendVarint(buf, a)
+	}
+	if nargs == 3 {
+		buf[1] = byte(len(buf) - 2)
+	}
+	n, err := w.Write(buf)
+	if n != len(buf) || err != nil {
+		panic("failed to write")
+	}
+}
+
+func appendVarint(buf []byte, v uint64) []byte {
+	for ; v >= 0x80; v >>= 7 {
+		buf = append(buf, 0x80|byte(v))
+	}
+	buf = append(buf, byte(v))
+	return buf
+}