| <!-- |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| --> |
| |
| {{> www/common-header.tmpl }} |
| |
| </div> |
| |
| <script src="{{ __common__.host-url }}/www/d3.v5.min.js"></script> |
| <script src="{{ __common__.host-url }}/www/c3/c3.v7.min.js"></script> |
| <script src="{{ __common__.host-url }}/www/scripts/compression_util.js" type="module"></script> |
| <link href="{{ __common__.host-url }}/www/c3/c3.v7.min.css" rel="stylesheet"> |
| |
| <div class="container"> |
| |
| <style id="page_export-css"> |
| #fragment_diagram, #phases_header, #timeticks_footer { |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", |
| Arial, "Noto Sans", sans-serif; |
| vertical-align: middle; |
| } |
| </style> |
| |
| <style> |
| g.plan_node:hover { |
| transform-box: fill-box; |
| transform: translateY(-50%) scaleY(1.50); |
| } |
| |
| g.plan_node:active { |
| transform-box: fill-box; |
| transform: translateY(-100%) scaleY(2); |
| } |
| |
| #host_utilization_wrapper, #fragment_metrics_wrapper { |
| display: none; |
| user-select: none; |
| } |
| |
| #host_utilization_resize_bar, #fragment_metrics_resize_bar { |
| display: inline-block; |
| cursor: row-resize; |
| user-select: none; |
| height: 6px; |
| } |
| |
| #hor_zoomout { |
| background-image: url("{{ __common__.host-url }}/www/icons/horizontal_zoomout.png"); |
| } |
| |
| #hor_zoomin { |
| background-image: url("{{ __common__.host-url }}/www/icons/horizontal_zoomin.png"); |
| } |
| |
| #hor_zoomout, #hor_zoomin{ |
| background-size: cover; |
| height: 1.75em; |
| width: 1.75em; |
| cursor: pointer; |
| } |
| </style> |
| |
| {{> www/query_detail_tabs.tmpl }} |
| |
| |
| {{?plan_metadata_unavailable}} |
| <h3>Plan not yet available. Page will update when query planning completes.</h3> |
| {{/plan_metadata_unavailable}} |
| |
| {{^plan_metadata_unavailable}} |
| <div style="display:flex; justify-content:space-between;"> |
| <h3>Timeline</h3> |
| <span id="controls"> |
| <button id="hor_zoomin" class="btn btn-primary"></button> |
| <button id="hor_zoomout" class="btn btn-primary" disabled></button> |
| </span> |
| <label> |
| <h4 style="display:inline;"> Download : </h4> |
| <input type="button" class="btn btn-primary" data-toggle="modal" value="HTML" |
| data-target="#export_modal" role="button"/> |
| </label> |
| </div> |
| <label> |
| <input type="checkbox" id="plan_order"/> |
| Print tree in plan order (if unchecked, print in fragment order) |
| </label> |
| |
| </div> |
| |
| <div id="export_modal" style="transition-duration: 0.15s;" class="modal fade" |
| role="dialog" data-keyboard="true" tabindex="-1"> |
| <div class="modal-dialog modal-dialog-centered"> |
| <div class="modal-content"> |
| <div class="modal-header"> |
| <h5> Download Timeline </h5> |
| <input class="btn btn-primary" type="button" value="X" data-dismiss="modal"/> |
| </div> |
| <div class="modal-body"> |
| <h6 class="d-inline"> Filename: </h6> |
| <input id="export_filename" class="form-control-sm" type="text" |
| value="{{query_id}}_timeline"/> |
| <select id="export_format" class="form-control-sm btn btn-primary"> |
| <option selected>.html</option> |
| </select> |
| </div> |
| <div class="modal-footer"> |
| <a id="export_link" class="btn btn-primary" data-dismiss="modal" href="#" |
| role="button"> Download </a> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <span id="scrollable_screen" style="display: inline-block; width: 100vw; overflow-x: scroll;"> |
| <div id="timing_diagram" style="border:1px solid #c3c3c3;"> |
| <div style="border:1px solid #c3c3c3;"> |
| <svg id="phases_header"></svg> |
| </div> |
| <div style="border:1px solid #c3c3c3; overflow-y:scroll; overflow-x:hidden;"> |
| <svg id="fragment_diagram"></svg> |
| </div> |
| <div style="border:1px solid #c3c3c3;"> |
| <svg id="timeticks_footer"></svg> |
| </div> |
| </div> |
| <div id="host_utilization_wrapper"> |
| <img id="host_utilization_resize_bar" src="{{ __common__.host-url }}/www/icons/drag_handle.png" draggable="false"/> |
| <input id="host_utilization_close_btn" class="btn btn-primary btn-sm" type="button" |
| value="X" data-dismiss="modal"/> |
| <div id="host_utilization_diagram"> |
| <!--Utilization metrics is not available. Please make sure to set query option |
| RESOURCE_TRACE_RATIO=true.--> |
| </div> |
| </div> |
| <div id="fragment_metrics_wrapper"> |
| <img id="fragment_metrics_resize_bar" src="{{ __common__.host-url }}/www/icons/drag_handle.png" draggable="false"/> |
| <input id="fragment_metrics_close_btn" class="btn btn-primary btn-sm" type="button" |
| value="X" data-dismiss="modal"/> |
| <div id="fragment_metrics_diagram"> |
| </div> |
| </div> |
| </span> |
| |
| {{/plan_metadata_unavailable}} |
| |
| {{> www/common-footer.tmpl }} |
| |
| <script type="module"> |
| $("#plan-timing-tab").addClass("active"); |
| |
| import {renderTimingDiagram, collectFragmentEventsFromProfile, ntics, set_ntics} from |
| "{{ __common__.host-url }}/www/scripts/query_timeline/fragment_diagram.js"; |
| import {collectUtilizationFromProfile, toogleUtilizationVisibility, |
| destroyUtilizationChart} |
| from "{{ __common__.host-url }}/www/scripts/query_timeline/host_utilization_diagram.js"; |
| import {collectFragmentMetricsFromProfile, closeFragmentMetricsChart} from |
| "{{ __common__.host-url }}/www/scripts/query_timeline/fragment_metrics_diagram.js"; |
| import {profile, set_profile, maxts, set_maxts, diagram_width, set_diagram_width, |
| border_stroke_width, resizeHorizontalAll} |
| from "{{ __common__.host-url }}/www/scripts/query_timeline/global_members.js"; |
| import {inflateParseJSON} from "{{ __common__.host-url }}/www/scripts/compression_util.js"; |
| |
| var chart_export_style; |
| var last_maxts; |
| |
| var dbOpenReq = indexedDB.open("imported_queries"); |
| var db; |
| |
| var supported_tabs = ["Query", "Timeline", "Text plan", "Profile"]; |
| |
| function refreshView() { |
| collectFragmentEventsFromProfile(); |
| collectUtilizationFromProfile(); |
| collectFragmentMetricsFromProfile(); |
| if (last_maxts !== maxts) { |
| renderTimingDiagram(); |
| last_maxts = maxts; |
| } |
| toogleUtilizationVisibility(); |
| } |
| |
| |
| if (window.location.search.includes("imported")) { |
| var alertMessage = document.getElementsByClassName("alert alert-danger")[0]; |
| if (alertMessage) { |
| alertMessage.remove(); |
| } |
| var nav_links = document.getElementsByClassName("nav nav-tabs")[0]; |
| nav_links = nav_links.getElementsByClassName("nav-link"); |
| for (var i = 0; i < nav_links.length;) { |
| if (supported_tabs.includes(nav_links[i].textContent)) { |
| nav_links[i].href = `${nav_links[i].href}&imported=true`; |
| i++; |
| } else { |
| nav_links[i].parentElement.remove(); |
| } |
| } |
| |
| dbOpenReq.onsuccess = (e) => { |
| db = e.target.result; |
| db.onerror = (e) => { |
| console.log("IndexedDB error"); |
| console.log(e); |
| } |
| var profileStore = db.transaction("profiles", "readonly").objectStore("profiles"); |
| profileStore.get(getQueryID()).onsuccess = (e) => { |
| set_profile(inflateParseJSON(e.target.result.profile).contents); |
| refreshView(); |
| }; |
| }; |
| } else { |
| window.onload = function refreshProfile() { |
| var req = new XMLHttpRequest(); |
| req.onload = function() { |
| if (req.status === 200) { |
| set_profile(JSON.parse(req.responseText)["profile_json"]); |
| refreshView(); |
| if (profile.child_profiles[0].info_strings.find(({key}) => |
| key === "End Time").value === "") { |
| setTimeout(refreshProfile, 1000); |
| } |
| } |
| } |
| req.open("GET", make_url("/query_timeline?query_id={{query_id}}&json"), true); |
| req.send(); |
| } |
| } |
| |
| scrollable_screen.addEventListener('mousemove', function(e) { |
| var scroll_margin = window.innerWidth / 10; |
| var scroll_amount = window.innerWidth / 3; |
| if (e.clientX <= scroll_margin) { |
| scrollable_screen.scrollBy({left : - scroll_amount, behavior : "smooth"}); |
| } else if (e.clientX >= window.innerWidth - scroll_margin) { |
| scrollable_screen.scrollBy({left : + scroll_amount, behavior : "smooth"}); |
| } |
| }); |
| |
| window.addEventListener('resize', function(event) { |
| var window_diagram_width = window.innerWidth - border_stroke_width; |
| if (diagram_width <= window_diagram_width) { |
| set_diagram_width(window_diagram_width); |
| hor_zoomout.disabled = true; |
| } else { |
| hor_zoomout.disabled = false; |
| } |
| resizeHorizontalAll(); |
| }, true); |
| |
| hor_zoomin.addEventListener('click', function(event) { |
| set_diagram_width(diagram_width + window.innerWidth * 0.2) |
| set_ntics(ntics + 2); |
| resizeHorizontalAll(); |
| hor_zoomout.disabled = false; |
| }); |
| |
| hor_zoomout.addEventListener('click', function(event) { |
| var prev_diagram_width = diagram_width; |
| var next_width = diagram_width - window.innerWidth * 0.2; |
| var window_diagram_width = window.innerWidth - border_stroke_width; |
| if (next_width <= window_diagram_width) { |
| next_width = window_diagram_width; |
| hor_zoomout.disabled = true; |
| } |
| set_diagram_width(next_width); |
| var next_ntics = ntics + (diagram_width - prev_diagram_width) * 10 / window.innerWidth; |
| next_ntics = Math.round(next_ntics); |
| set_ntics(next_ntics); |
| resizeHorizontalAll(); |
| }); |
| |
| // Attaches a SVG blob of the complete timeline to the associated link |
| export_link.addEventListener('click', function (event) { |
| if (export_format.value === ".html") { |
| var export_style = document.getElementById("page_export-css"); |
| |
| // Deep clone 'parentNode's as wrappers to SVG components |
| var fragment_diagram_wrapper = fragment_diagram.parentNode.cloneNode(true); |
| |
| // Set dimensions for fragment diagram's wrapper |
| fragment_diagram_wrapper.style.height = fragment_diagram.style.height; |
| |
| var html_blob = new Blob(['<!DOCTYPE html><body>', |
| '<h1 style=\"font-family:monospace;\">Query {{query_id}}</h1>', |
| `<style>${export_style.innerHTML} ${chart_export_style}</style>`, |
| phases_header.parentNode.outerHTML, fragment_diagram_wrapper.outerHTML, |
| timeticks_footer.parentElement.outerHTML, host_utilization_diagram.outerHTML, |
| '</body></html>'], {type: 'text/html;charset=utf-8'}); |
| export_link.href = URL.createObjectURL(html_blob); |
| } |
| export_link.download = `${export_filename.value}${export_format.value}`; |
| export_link.click(); |
| }); |
| |
| fetch(make_url("/www/c3/c3.v7.min.css")) |
| .then(res => res.text()) |
| .then(style => chart_export_style = style); |
| |
| export_filename.value = export_filename.value.replace(/\W/g,'_'); |
| </script> |