• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2018 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import {assertTrue} from '../base/logging';
16import {ColumnType} from './query_result';
17
18// TODO(hjd): Combine with timeToCode.
19export function timeToString(sec: number) {
20  const units = ['s', 'ms', 'us', 'ns'];
21  const sign = Math.sign(sec);
22  let n = Math.abs(sec);
23  let u = 0;
24  while (n < 1 && n !== 0 && u < units.length - 1) {
25    n *= 1000;
26    u++;
27  }
28  return `${sign < 0 ? '-' : ''}${Math.round(n * 10) / 10} ${units[u]}`;
29}
30
31export function tpTimeToString(time: TPTime) {
32  return timeToString(tpTimeToSeconds(time));
33}
34
35export function fromNs(ns: number) {
36  return ns / 1e9;
37}
38
39export function toNsFloor(seconds: number) {
40  return Math.floor(seconds * 1e9);
41}
42
43export function toNsCeil(seconds: number) {
44  return Math.ceil(seconds * 1e9);
45}
46
47export function toNs(seconds: number) {
48  return Math.round(seconds * 1e9);
49}
50
51// 1000000023ns -> "1.000 000 023"
52export function formatTimestamp(sec: number) {
53  const parts = sec.toFixed(9).split('.');
54  parts[1] = parts[1].replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
55  return parts.join('.');
56}
57
58export function formatTPTime(time: TPTime) {
59  return formatTimestamp(tpTimeToSeconds(time));
60}
61
62// TODO(hjd): Rename to formatTimestampWithUnits
63// 1000000023ns -> "1s 23ns"
64export function timeToCode(sec: number): string {
65  let result = '';
66  let ns = Math.round(sec * 1e9);
67  if (ns < 1) return '0s';
68  const unitAndValue = [
69    ['m', 60000000000],
70    ['s', 1000000000],
71    ['ms', 1000000],
72    ['us', 1000],
73    ['ns', 1],
74  ];
75  unitAndValue.forEach((pair) => {
76    const unit = pair[0] as string;
77    const val = pair[1] as number;
78    if (ns >= val) {
79      const i = Math.floor(ns / val);
80      ns -= i * val;
81      result += i.toLocaleString() + unit + ' ';
82    }
83  });
84  return result.slice(0, -1);
85}
86
87export function tpTimeToCode(time: TPTime) {
88  return timeToCode(tpTimeToSeconds(time));
89}
90
91export function currentDateHourAndMinute(): string {
92  const date = new Date();
93  return `${date.toISOString().substr(0, 10)}-${date.getHours()}-${
94      date.getMinutes()}`;
95}
96
97// Aliased "Trace Processor" time and duration types.
98// Note(stevegolton): While it might be nice to type brand these in the future,
99// for now we're going to keep things simple. We do a lot of maths with these
100// timestamps and type branding requires a lot of jumping through hoops to
101// coerse the type back to the correct format.
102export type TPTime = bigint;
103export type TPDuration = bigint;
104
105export function tpTimeFromNanos(nanos: number): TPTime {
106  return BigInt(Math.floor(nanos));
107}
108
109export function tpTimeFromSeconds(seconds: number): TPTime {
110  return BigInt(Math.floor(seconds * 1e9));
111}
112
113export function tpTimeToNanos(time: TPTime): number {
114  return Number(time);
115}
116
117export function tpTimeToMillis(time: TPTime): number {
118  return Number(time) / 1e6;
119}
120
121export function tpTimeToSeconds(time: TPTime): number {
122  return Number(time) / 1e9;
123}
124
125// Create a TPTime from an arbitrary SQL value.
126// Throws if the value cannot be reasonably converted to a bigint.
127// Assumes value is in nanoseconds.
128export function tpTimeFromSql(value: ColumnType): TPTime {
129  if (typeof value === 'bigint') {
130    return value;
131  } else if (typeof value === 'number') {
132    return tpTimeFromNanos(value);
133  } else if (value === null) {
134    return 0n;
135  } else {
136    throw Error(`Refusing to create Timestamp from unrelated type ${value}`);
137  }
138}
139
140export function tpDurationToSeconds(dur: TPDuration): number {
141  return tpTimeToSeconds(dur);
142}
143
144export function tpDurationToNanos(dur: TPDuration): number {
145  return tpTimeToSeconds(dur);
146}
147
148export function tpDurationFromNanos(nanos: number): TPDuration {
149  return tpTimeFromNanos(nanos);
150}
151
152export function tpDurationFromSql(nanos: ColumnType): TPDuration {
153  return tpTimeFromSql(nanos);
154}
155
156export interface Span<Unit, Duration = Unit> {
157  get start(): Unit;
158  get end(): Unit;
159  get duration(): Duration;
160  get midpoint(): Unit;
161  contains(span: Unit|Span<Unit, Duration>): boolean;
162  intersects(x: Span<Unit>): boolean;
163  equals(span: Span<Unit, Duration>): boolean;
164  add(offset: Duration): Span<Unit, Duration>;
165  pad(padding: Duration): Span<Unit, Duration>;
166}
167
168export class TPTimeSpan implements Span<TPTime, TPDuration> {
169  readonly start: TPTime;
170  readonly end: TPTime;
171
172  constructor(start: TPTime, end: TPTime) {
173    assertTrue(
174        start <= end,
175        `Span start [${start}] cannot be greater than end [${end}]`);
176    this.start = start;
177    this.end = end;
178  }
179
180  get duration(): TPDuration {
181    return this.end - this.start;
182  }
183
184  get midpoint(): TPTime {
185    return (this.start + this.end) / 2n;
186  }
187
188  contains(x: TPTime|Span<TPTime, TPDuration>): boolean {
189    if (typeof x === 'bigint') {
190      return this.start <= x && x < this.end;
191    } else {
192      return this.start <= x.start && x.end <= this.end;
193    }
194  }
195
196  intersects(x: Span<TPTime, TPDuration>): boolean {
197    return !(x.end <= this.start || x.start >= this.end);
198  }
199
200  equals(span: Span<TPTime, TPDuration>): boolean {
201    return this.start === span.start && this.end === span.end;
202  }
203
204  add(x: TPTime): Span<TPTime, TPDuration> {
205    return new TPTimeSpan(this.start + x, this.end + x);
206  }
207
208  pad(padding: TPDuration): Span<TPTime, TPDuration> {
209    return new TPTimeSpan(this.start - padding, this.end + padding);
210  }
211}
212