• 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 * as m from 'mithril';
16import {inflate} from 'pako';
17import {assertTrue} from '../base/logging';
18import {showModal} from './modal';
19
20function readText(blob: Blob): Promise<string> {
21  return new Promise((resolve, reject) => {
22    const reader = new FileReader();
23    reader.onload = () => {
24      if (typeof reader.result === 'string') {
25        return resolve(reader.result);
26      }
27    };
28    reader.onerror = err => {
29      reject(err);
30    };
31    reader.readAsText(blob);
32  });
33}
34
35export async function isLegacyTrace(file: File): Promise<boolean> {
36  const fileName = file.name.toLowerCase();
37  if (fileName.endsWith('.json') || fileName.endsWith('.json.gz') ||
38      fileName.endsWith('.zip') || fileName.endsWith('.ctrace') ||
39      fileName.endsWith('.html')) {
40    return true;
41  }
42
43  // Sometimes systrace formatted traces end with '.trace'. This is a
44  // little generic to assume all such traces are systrace format though
45  // so we read the beginning of the file and check to see if is has the
46  // systrace header (several comment lines):
47  if (fileName.endsWith('.trace')) {
48    const header = await readText(file.slice(0, 512));
49    const lines = header.split('\n');
50    let commentCount = 0;
51    for (const line of lines) {
52      if (line.startsWith('#')) {
53        commentCount++;
54      }
55    }
56    if (commentCount > 5) {
57      return true;
58    }
59  }
60
61  return false;
62}
63
64export function openFileWithLegacyTraceViewer(file: File) {
65  const reader = new FileReader();
66  reader.onload = () => {
67    if (reader.result instanceof ArrayBuffer) {
68      return openBufferWithLegacyTraceViewer(
69          file.name, reader.result, reader.result.byteLength);
70    } else {
71      const str = reader.result as string;
72      return openBufferWithLegacyTraceViewer(file.name, str, str.length);
73    }
74  };
75  reader.onerror = err => {
76    console.error(err);
77  };
78  if (file.name.endsWith('.gz') || file.name.endsWith('.zip') ||
79      file.name.endsWith('.ctrace')) {
80    reader.readAsArrayBuffer(file);
81  } else {
82    reader.readAsText(file);
83  }
84}
85
86export function openBufferWithLegacyTraceViewer(
87    name: string, data: ArrayBuffer|string, size: number) {
88  if (data instanceof ArrayBuffer) {
89    assertTrue(size <= data.byteLength);
90    if (size !== data.byteLength) {
91      data = data.slice(0, size);
92    }
93
94    // Handle .ctrace files.
95    const enc = new TextDecoder('utf-8');
96    const header = enc.decode(data.slice(0, 7));
97    if (header === 'TRACE:\n') {
98      data = inflate(new Uint8Array(data.slice(7, size)), {to: 'string'});
99    }
100  }
101
102  // The location.pathname mangling is to make this code work also when hosted
103  // in a non-root sub-directory, for the case of CI artifacts.
104  const urlParts = location.pathname.split('/');
105  urlParts[urlParts.length - 1] = 'assets/catapult_trace_viewer.html';
106  const catapultUrl = urlParts.join('/');
107  const newWin = window.open(catapultUrl) as Window;
108  if (newWin) {
109    // Popup succeedeed.
110    newWin.addEventListener('load', (e: Event) => {
111      const doc = (e.target as Document);
112      const ctl = doc.querySelector('x-profiling-view') as TraceViewerAPI;
113      ctl.setActiveTrace(name, data);
114    });
115    return;
116  }
117
118  // Popup blocker detected.
119  showModal({
120    title: 'Open trace in the legacy Catapult Trace Viewer',
121    content: m(
122        'div',
123        m('div', 'You are seeing this interstitial because popups are blocked'),
124        m('div', 'Enable popups to skip this dialog next time.')),
125    buttons: [{
126      text: 'Open legacy UI',
127      primary: true,
128      id: 'open_legacy',
129      action: () => openBufferWithLegacyTraceViewer(name, data, size),
130    }],
131  });
132}
133
134// TraceViewer method that we wire up to trigger the file load.
135interface TraceViewerAPI extends Element {
136  setActiveTrace(name: string, data: ArrayBuffer|string): void;
137}
138