1 <script type="text/ecmascript"> 2 function init() { 3 var x = document.getElementsByTagName("svg") 4 for (i = 0; i < x.length; i=i+1) { 5 createZoomHistoryStack(x[i]); 6 } 7 } 8 9 // Create a stack add the root svg element in it. 10 function createZoomHistoryStack(svgElement) { 11 stack = []; 12 svgElement.zoomStack = stack; 13 stack.push(svgElement.getElementById(svgElement.attributes["rootid"].value)) 14 } 15 16 function dumpStack(svgElement) { 17 // Disable (enable for debugging) 18 return 19 stack = svgElement.zoomStack; 20 for (i=0 ; i < stack.length; i++) { 21 title = stack[i].getElementsByTagName("title")[0]; 22 console.log("[" +i+ "]-" + title.textContent) 23 } 24 } 25 26 function adjust_node_text_size(x) { 27 title = x.getElementsByTagName("title")[0]; 28 text = x.getElementsByTagName("text")[0]; 29 rect = x.getElementsByTagName("rect")[0]; 30 31 width = parseFloat(rect.attributes["width"].value); 32 33 // Don't even bother trying to find a best fit. The area is too small. 34 if (width < 25) { 35 text.textContent = ""; 36 return; 37 } 38 // Remove dso and #samples which are here only for mouseover purposes. 39 methodName = title.textContent.substring(0, title.textContent.indexOf("|")); 40 41 var numCharacters; 42 for (numCharacters=methodName.length; numCharacters>4; numCharacters--) { 43 // Avoid reflow by using hard-coded estimate instead of text.getSubStringLength(0, numCharacters) 44 // if (text.getSubStringLength(0, numCharacters) <= width) { 45 if (numCharacters * 7.5 <= width) { 46 break ; 47 } 48 } 49 50 if (numCharacters == methodName.length) { 51 text.textContent = methodName; 52 return 53 } 54 55 text.textContent = methodName.substring(0, numCharacters-2) + ".."; 56 } 57 58 function adjust_text_size(svgElement) { 59 var x = svgElement.getElementsByTagName("g"); 60 var i; 61 for (i=0 ; i < x.length ; i=i+1) { 62 adjust_node_text_size(x[i]) 63 } 64 } 65 66 function zoom(e) { 67 svgElement = e.ownerSVGElement 68 zoomStack = svgElement.zoomStack; 69 zoomStack.push(e); 70 displayFromElement(e) 71 select(e); 72 dumpStack(e.ownerSVGElement); 73 74 // Show zoom out button. 75 svgElement.getElementById("zoom_rect").style.display = "block"; 76 svgElement.getElementById("zoom_text").style.display = "block"; 77 } 78 79 function displayFromElement(e) { 80 var clicked_rect = e.getElementsByTagName("rect")[0]; 81 var clicked_origin_x = clicked_rect.attributes["ox"].value; 82 var clicked_origin_y = clicked_rect.attributes["oy"].value; 83 var clicked_origin_width = clicked_rect.attributes["owidth"].value; 84 85 86 var svgBox = e.ownerSVGElement.getBoundingClientRect(); 87 var svgBoxHeight = svgBox.height 88 var svgBoxWidth = svgBox.width 89 var scaleFactor = svgBoxWidth/clicked_origin_width; 90 91 var callsites = e.ownerSVGElement.getElementsByTagName("g"); 92 var i; 93 for (i = 0; i < callsites.length; i=i+1) { 94 text = callsites[i].getElementsByTagName("text")[0]; 95 rect = callsites[i].getElementsByTagName("rect")[0]; 96 97 rect_o_x = rect.attributes["ox"].value 98 rect_o_y = parseFloat(rect.attributes["oy"].value) 99 100 // Avoid multiple forced reflow by hiding nodes. 101 if (rect_o_y > clicked_origin_y) { 102 rect.style.display = "none" 103 text.style.display = "none" 104 continue; 105 } else { 106 rect.style.display = "block" 107 text.style.display = "block" 108 } 109 110 rect.attributes["x"].value = newrec_x = (rect_o_x - clicked_origin_x) * scaleFactor ; 111 rect.attributes["y"].value = newrec_y = rect_o_y + (svgBoxHeight - clicked_origin_y - 17 -2); 112 113 text.attributes["y"].value = newrec_y + 12; 114 text.attributes["x"].value = newrec_x + 4; 115 116 rect.attributes["width"].value = rect.attributes["owidth"].value * scaleFactor; 117 } 118 119 adjust_text_size(e.ownerSVGElement); 120 121 } 122 123 function unzoom(e) { 124 125 var svgOwner = e.ownerSVGElement; 126 stack = svgOwner.zoomStack; 127 128 // Unhighlight whatever was selected. 129 if (selected != null) 130 selected.classList.remove("s") 131 132 133 // Stack management: Never remove the last element which is the flamegraph root. 134 if (stack.length > 1) { 135 previouslySelected = stack.pop(); 136 select(previouslySelected); 137 } 138 nextElement = stack[stack.length-1] // stack.peek() 139 140 // Hide zoom out button. 141 if (stack.length==1) { 142 svgOwner.getElementById("zoom_rect").style.display = "none"; 143 svgOwner.getElementById("zoom_text").style.display = "none"; 144 } 145 146 displayFromElement(nextElement); 147 dumpStack(svgOwner); 148 } 149 150 function search(e) { 151 var term = prompt("Search for:", ""); 152 153 var svgOwner = e.ownerSVGElement 154 var callsites = e.ownerSVGElement.getElementsByTagName("g"); 155 156 if (term == null || term == "") { 157 for (i = 0; i < callsites.length; i=i+1) { 158 rect = callsites[i].getElementsByTagName("rect")[0]; 159 rect.attributes["fill"].value = rect.attributes["ofill"].value; 160 } 161 return; 162 } 163 164 for (i = 0; i < callsites.length; i=i+1) { 165 title = callsites[i].getElementsByTagName("title")[0]; 166 rect = callsites[i].getElementsByTagName("rect")[0]; 167 if (title.textContent.indexOf(term) != -1) { 168 rect.attributes["fill"].value = "rgb(230,100,230)"; 169 } else { 170 rect.attributes["fill"].value = rect.attributes["ofill"].value; 171 } 172 } 173 } 174 175 var selected; 176 document.onkeydown = function handle_keyboard_input(e) { 177 if (selected == null) 178 return; 179 180 title = selected.getElementsByTagName("title")[0]; 181 nav = selected.attributes["nav"].value.split(",") 182 navigation_index = -1 183 switch (e.keyCode) { 184 //case 38: // ARROW UP 185 case 87 : navigation_index = 0;break; //W 186 187 //case 32 : // ARROW LEFT 188 case 65 : navigation_index = 1;break; //A 189 190 // case 43: // ARROW DOWN 191 case 68 : navigation_index = 3;break; // S 192 193 // case 39: // ARROW RIGHT 194 case 83 : navigation_index = 2;break; // D 195 196 case 32 : zoom(selected); return false; break; // SPACE 197 198 case 8: // BACKSPACE 199 unzoom(selected); return false; 200 default: return true; 201 } 202 203 if (nav[navigation_index] == "0") 204 return false; 205 206 target_element = selected.ownerSVGElement.getElementById(nav[navigation_index]); 207 select(target_element) 208 return false; 209 } 210 211 function select(e) { 212 if (selected != null) 213 selected.classList.remove("s") 214 selected = e; 215 selected.classList.add("s") 216 217 // Update info bar 218 titleElement = selected.getElementsByTagName("title")[0]; 219 text = titleElement.textContent; 220 221 // Parse title 222 method_and_info = text.split(" | "); 223 methodName = method_and_info[0]; 224 info = method_and_info[1] 225 226 // Parse info 227 // '/system/lib64/libhwbinder.so (4 samples: 0.28%)' 228 var regexp = /(.*) \(.* ([0-9**\.[0-9]*%)\)/g; 229 match = regexp.exec(info); 230 if (match.length > 2) { 231 percentage = match[2] 232 // Write percentage 233 percentageTextElement = selected.ownerSVGElement.getElementById("percent_text") 234 percentageTextElement.textContent = percentage 235 //console.log("'" + percentage + "'") 236 } 237 238 // Set fields 239 barTextElement = selected.ownerSVGElement.getElementById("info_text") 240 barTextElement.textContent = methodName 241 } 242 243 244 </script>