summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/catapult/tracing/tracing/value/histogram_importer.html
blob: 49f1ecc7e87f68bb5376856d22d00fff109f4330 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
<!DOCTYPE html>
<!--
Copyright 2017 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="/tracing/base/raf.html">
<link rel="import" href="/tracing/base/timing.html">
<link rel="import" href="/tracing/value/histogram_set.html">

<script>
'use strict';
tr.exportTo('tr.v', function() {
  class HistogramImporter {
    /**
     * @param {!Element} loadingEl
     */
    constructor(loadingEl) {
      this.loadingEl_ = loadingEl;
      this.histograms_ = undefined;
      this.string_ = '';
      this.dataOffset_ = 0;
      this.view_ = undefined;
      this.fmpMark_ = tr.b.Timing.mark('HistogramImporter', 'fmp');

      this.loadingEl_.textContent = 'Parsing HTML...';
      // The json comment appears after this script tag in results.html, so the
      // browser will parse them into DOM now.
    }

    /**
     * @param {string} message
     * @return {Promise} resolves when |message| is displayed.
     */
    async update_(message) {
      this.loadingEl_.textContent = message;
      // Use rAF callbacks only if the document is visible. If the document is
      // hidden, then the user-agent can stop triggering the rAF callbacks. So
      // avoid rAF callbacks when hidden.
      if (window.document.visibilityState === 'visible') {
        await tr.b.animationFrame();
      }
    }

    /**
     * The string contains a list of histograms of the following form:
     *  JSON\n
     *  JSON\n
     *  ...
     * The |view| should have 'display:none' so that it doesn't obnoxiously
     * display "zero Histograms" while they are being imported.
     *
     * @param {!String[]} strings
     * @param {!Element} view A histogram-set-view.
     * @return {Promise} resolves when |view| is displayed.
     */
    async importHistograms(strings, view) {
      this.histograms_ = new tr.v.HistogramSet();
      this.view_ = view;
      tr.b.Timing.instant('HistogramImporter', 'string', this.string_.length);
      const loadMark = tr.b.Timing.mark('HistogramImporter', 'loadHistograms');
      for (const string of strings) {
        this.string_ = string;
        this.dataOffset_ = 0;
        if (this.string_.length == 0) continue;
        await this.update_('Loading Histogram 0');
        if (!this.findDataStart_()) continue
        await this.loadSomeHistograms_();
      }
      loadMark.end();
      tr.b.Timing.instant('HistogramImporter', 'nsPerJson',
          parseInt(1e3 * loadMark.durationMs / this.histograms_.length));

      await this.update_('Displaying Histogram table...');
      await this.displayHistograms_();
    }

    findDataStart_() {
      // Find the initial data start.
      this.dataOffset_ = this.string_.indexOf('\n', this.dataOffset_);
      if (this.dataOffset_ < 0) return false;
      // Skip over newline character.
      this.dataOffset_++;
      return true;
    }

    async loadSomeHistograms_() {
      // Don't spend so long on this chunk of Histograms that the user gets
      // frustrated, but also don't call requestAnimationFrame faster than every
      // 16ms, so that the browser doesn't have to wait for the next vsync.
      // Powerful computers can load several hundred Histograms in 32ms.
      // Also don't call performance.now() more often than necessary.
      const startTime = performance.now();
      do {
        for (let i = 0; i < 100; i++) {
          const endIndex = this.string_.indexOf('\n', this.dataOffset_);
          if (endIndex < 0) return;
          const json = this.string_.substring(this.dataOffset_, endIndex);
          const dict = JSON.parse(json);
          if (dict instanceof Array) {
            // TODO(benjhayden): progress callback
            this.histograms_.importDicts(dict);
          } else {
            this.histograms_.importLegacyDict(dict);
          }
          // Continue after last found newline character.
          this.dataOffset_ = endIndex + 1;
        }
      } while (performance.now() - startTime < 50);

      await this.update_(`Loading Histogram ${this.histograms_.length}`);
      await this.loadSomeHistograms_();
    }

    async displayHistograms_() {
      this.view_.addEventListener('display-ready', async() => {
        this.loadingEl_.style.display = 'none';
        this.view_.style.display = 'block';
        await tr.b.animationFrame();
        this.fmpMark_.end();
      });

      await this.view_.build(this.histograms_, {
        progress: message => this.update_(message),
        helpHref: 'https://github.com/catapult-project/catapult/blob/master/docs/metrics-results-ui.md',
        feedbackHref: 'https://docs.google.com/a/google.com/forms/d/e/1FAIpQLSfXvMvm_z2F9-khFaKyH_LHVZ6caPPkxI27BZqMnEt4XjyJ3g/viewform',
      });
    }
  }

  return {
    HistogramImporter,
  };
});
</script>