| OLD | NEW |
| 1 // Copyright 2017 the V8 project authors. All rights reserved. | 1 // Copyright 2017 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 "use strict" | 5 "use strict" |
| 6 | 6 |
| 7 let codeKinds = [ | 7 let codeKinds = [ |
| 8 "UNKNOWN", | 8 "UNKNOWN", |
| 9 "CPPCOMP", | 9 "CPPCOMP", |
| 10 "CPPGC", | 10 "CPPGC", |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 72 return kind; | 72 return kind; |
| 73 } | 73 } |
| 74 | 74 |
| 75 function createNodeFromStackEntry(code) { | 75 function createNodeFromStackEntry(code) { |
| 76 let name = code ? code.name : "UNKNOWN"; | 76 let name = code ? code.name : "UNKNOWN"; |
| 77 | 77 |
| 78 return { name, type : resolveCodeKind(code), | 78 return { name, type : resolveCodeKind(code), |
| 79 children : [], ownTicks : 0, ticks : 0 }; | 79 children : [], ownTicks : 0, ticks : 0 }; |
| 80 } | 80 } |
| 81 | 81 |
| 82 function childIdFromCode(codeId, code) { |
| 83 // For JavaScript function, pretend there is one instance of optimized |
| 84 // function and one instance of unoptimized function per SFI. |
| 85 // Otherwise, just compute the id from code id. |
| 86 let type = resolveCodeKind(code); |
| 87 if (type === "JSOPT") { |
| 88 return code.func * 4 + 1; |
| 89 } else if (type === "JSUNOPT") { |
| 90 return code.func * 4 + 2; |
| 91 } else { |
| 92 return codeId * 4; |
| 93 } |
| 94 } |
| 95 |
| 96 // We store list of ticks and positions within the ticks stack by |
| 97 // storing flattened triplets of { tickIndex, depth, count }. |
| 98 // Triplet { 123, 2, 3 } encodes positions in ticks 123, 124, 125, |
| 99 // all of them at depth 2. The flattened array is used to encode |
| 100 // position within the call-tree. |
| 101 |
| 102 // The following function helps to encode such triplets. |
| 103 function addFrameToFrameList(paths, pathIndex, depth) { |
| 104 // Try to combine with the previous code run. |
| 105 if (paths.length > 0 && |
| 106 paths[paths.length - 3] + 1 === pathIndex && |
| 107 paths[paths.length - 2] === depth) { |
| 108 paths[paths.length - 1]++; |
| 109 } else { |
| 110 paths.push(pathIndex, depth, 1); |
| 111 } |
| 112 } |
| 113 |
| 114 // This expands a tree node (direct children only). |
| 115 function expandTreeNode(file, node, filter) { |
| 116 let { frameList, ascending } = node.delayedExpansion; |
| 117 |
| 118 let step = ascending ? 2 : -2; |
| 119 |
| 120 for (let i = 0; i < frameList.length; i+= 3) { |
| 121 let stackIndex = frameList[i]; |
| 122 let depth = frameList[i + 1]; |
| 123 let count = frameList[i + 2]; |
| 124 for (let j = 0; j < count; j++) { |
| 125 let tick = file.ticks[stackIndex + j]; |
| 126 let stack = tick.s; |
| 127 |
| 128 // Get to the next frame that has not been filtered out. |
| 129 let k = depth + step; |
| 130 let codeId = -1; |
| 131 let code = null; |
| 132 while (k >= 0 && k < stack.length) { |
| 133 codeId = stack[k]; |
| 134 code = codeId >= 0 ? file.code[codeId] : undefined; |
| 135 if (filter) { |
| 136 let type = code ? code.type : undefined; |
| 137 let kind = code ? code.kind : undefined; |
| 138 if (filter(type, kind)) break; |
| 139 } |
| 140 k += step; |
| 141 } |
| 142 if (k < 0 || k >= stack.length) { |
| 143 // We reached the end without finding the next step. |
| 144 // If we are doing top-down call tree, update own ticks. |
| 145 if (!ascending) { |
| 146 node.ownTicks++; |
| 147 } |
| 148 } else { |
| 149 // We found a child node. |
| 150 let childId = childIdFromCode(codeId, code); |
| 151 let child = node.children[childId]; |
| 152 if (!child) { |
| 153 child = createNodeFromStackEntry(code); |
| 154 child.delayedExpansion = { frameList : [], ascending }; |
| 155 node.children[childId] = child; |
| 156 } |
| 157 child.ticks++; |
| 158 addFrameToFrameList(child.delayedExpansion.frameList, stackIndex, k); |
| 159 } |
| 160 } |
| 161 } |
| 162 node.delayedExpansion = null; |
| 163 } |
| 164 |
| 82 function addStackToTree(file, stack, tree, filter, ascending, start) { | 165 function addStackToTree(file, stack, tree, filter, ascending, start) { |
| 83 if (start === undefined) { | 166 if (start === undefined) { |
| 84 start = ascending ? 0 : stack.length - 2; | 167 start = ascending ? 0 : stack.length - 2; |
| 85 } | 168 } |
| 86 tree.ticks++; | 169 tree.ticks++; |
| 87 for (let i = start; | 170 for (let i = start; |
| 88 ascending ? (i < stack.length) : (i >= 0); | 171 ascending ? (i < stack.length) : (i >= 0); |
| 89 i += ascending ? 2 : -2) { | 172 i += ascending ? 2 : -2) { |
| 90 let codeId = stack[i]; | 173 let codeId = stack[i]; |
| 91 let code = codeId >= 0 ? file.code[codeId] : undefined; | 174 let code = codeId >= 0 ? file.code[codeId] : undefined; |
| 92 if (filter) { | 175 if (filter) { |
| 93 let type = code ? code.type : undefined; | 176 let type = code ? code.type : undefined; |
| 94 let kind = code ? code.kind : undefined; | 177 let kind = code ? code.kind : undefined; |
| 95 if (!filter(type, kind)) continue; | 178 if (!filter(type, kind)) continue; |
| 96 } | 179 } |
| 97 | 180 |
| 98 // For JavaScript function, pretend there is one instance of optimized | 181 // For JavaScript function, pretend there is one instance of optimized |
| 99 // function and one instance of unoptimized function per SFI. | 182 // function and one instance of unoptimized function per SFI. |
| 100 let type = resolveCodeKind(code); | 183 let childId = childIdFromCode(codeId, code); |
| 101 let childId; | |
| 102 if (type === "JSOPT") { | |
| 103 childId = code.func * 4 + 1; | |
| 104 } else if (type === "JSUNOPT") { | |
| 105 childId = code.func * 4 + 2; | |
| 106 } else { | |
| 107 childId = codeId * 4; | |
| 108 } | |
| 109 let child = tree.children[childId]; | 184 let child = tree.children[childId]; |
| 110 if (!child) { | 185 if (!child) { |
| 111 child = createNodeFromStackEntry(code); | 186 child = createNodeFromStackEntry(code); |
| 112 tree.children[childId] = child; | 187 tree.children[childId] = child; |
| 113 } | 188 } |
| 114 child.ticks++; | 189 child.ticks++; |
| 115 tree = child; | 190 tree = child; |
| 116 } | 191 } |
| 117 tree.ownTicks++; | 192 tree.ownTicks++; |
| 118 } | 193 } |
| 119 | 194 |
| 120 function createEmptyNode(name) { | 195 function createEmptyNode(name) { |
| 121 return { | 196 return { |
| 122 name : name, | 197 name : name, |
| 123 type : "CAT", | 198 type : "CAT", |
| 124 children : [], | 199 children : [], |
| 125 ownTicks : 0, | 200 ownTicks : 0, |
| 126 ticks : 0 | 201 ticks : 0 |
| 127 }; | 202 }; |
| 128 } | 203 } |
| 129 | 204 |
| 130 class PlainCallTreeProcessor { | 205 class PlainCallTreeProcessor { |
| 131 constructor(filter, isBottomUp) { | 206 constructor(filter, isBottomUp) { |
| 132 this.filter = filter; | 207 this.filter = filter; |
| 133 this.tree = createEmptyNode("root"); | 208 this.tree = createEmptyNode("root"); |
| 134 this.isBottomUp = isBottomUp; | 209 this.isBottomUp = isBottomUp; |
| 135 } | 210 } |
| 136 | 211 |
| 137 addStack(file, timestamp, vmState, stack) { | 212 addStack(file, tickIndex) { |
| 213 let stack = file.ticks[tickIndex].s; |
| 138 addStackToTree(file, stack, this.tree, this.filter, this.isBottomUp); | 214 addStackToTree(file, stack, this.tree, this.filter, this.isBottomUp); |
| 139 } | 215 } |
| 140 } | 216 } |
| 141 | 217 |
| 218 function buildCategoryTreeAndLookup() { |
| 219 let root = createEmptyNode("root"); |
| 220 let categories = {}; |
| 221 function addCategory(name, types) { |
| 222 let n = createEmptyNode(name); |
| 223 for (let i = 0; i < types.length; i++) { |
| 224 categories[types[i]] = n; |
| 225 } |
| 226 root.children.push(n); |
| 227 } |
| 228 addCategory("JS Optimized", [ "JSOPT" ]); |
| 229 addCategory("JS Unoptimized", [ "JSUNOPT", "BC" ]); |
| 230 addCategory("IC", [ "IC" ]); |
| 231 addCategory("RegExp", [ "REGEXP" ]); |
| 232 addCategory("Other generated", [ "STUB", "BUILTIN" ]); |
| 233 addCategory("C++", [ "CPP", "LIB" ]); |
| 234 addCategory("C++/GC", [ "CPPGC" ]); |
| 235 addCategory("C++/Compiler", [ "CPPCOMP" ]); |
| 236 addCategory("C++/External", [ "CPPEXT" ]); |
| 237 addCategory("Unknown", [ "UNKNOWN" ]); |
| 238 |
| 239 return { categories, root }; |
| 240 } |
| 241 |
| 142 class CategorizedCallTreeProcessor { | 242 class CategorizedCallTreeProcessor { |
| 143 constructor(filter, isBottomUp) { | 243 constructor(filter, isBottomUp) { |
| 144 this.filter = filter; | 244 this.filter = filter; |
| 145 let root = createEmptyNode("root"); | 245 let { categories, root } = buildCategoryTreeAndLookup(); |
| 146 let categories = {}; | |
| 147 function addCategory(name, types) { | |
| 148 let n = createEmptyNode(name); | |
| 149 for (let i = 0; i < types.length; i++) { | |
| 150 categories[types[i]] = n; | |
| 151 } | |
| 152 root.children.push(n); | |
| 153 } | |
| 154 addCategory("JS Optimized", [ "JSOPT" ]); | |
| 155 addCategory("JS Unoptimized", [ "JSUNOPT", "BC" ]); | |
| 156 addCategory("IC", [ "IC" ]); | |
| 157 addCategory("RegExp", [ "REGEXP" ]); | |
| 158 addCategory("Other generated", [ "STUB", "BUILTIN" ]); | |
| 159 addCategory("C++", [ "CPP", "LIB" ]); | |
| 160 addCategory("C++/GC", [ "CPPGC" ]); | |
| 161 addCategory("C++/Compiler", [ "CPPCOMP" ]); | |
| 162 addCategory("C++/External", [ "CPPEXT" ]); | |
| 163 addCategory("Unknown", [ "UNKNOWN" ]); | |
| 164 | 246 |
| 165 this.tree = root; | 247 this.tree = root; |
| 166 this.categories = categories; | 248 this.categories = categories; |
| 167 this.isBottomUp = isBottomUp; | 249 this.isBottomUp = isBottomUp; |
| 168 } | 250 } |
| 169 | 251 |
| 170 addStack(file, timestamp, vmState, stack) { | 252 addStack(file, tickIndex) { |
| 253 let stack = file.ticks[tickIndex].s; |
| 254 let vmState = file.ticks[tickIndex].vm; |
| 171 if (stack.length === 0) return; | 255 if (stack.length === 0) return; |
| 172 let codeId = stack[0]; | 256 let codeId = stack[0]; |
| 173 let code = codeId >= 0 ? file.code[codeId] : undefined; | 257 let code = codeId >= 0 ? file.code[codeId] : undefined; |
| 174 let kind = resolveCodeKindAndVmState(code, vmState); | 258 let kind = resolveCodeKindAndVmState(code, vmState); |
| 175 let node = this.categories[kind]; | 259 let node = this.categories[kind]; |
| 176 | 260 |
| 177 this.tree.ticks++; | 261 this.tree.ticks++; |
| 178 | 262 |
| 179 console.assert(node); | 263 console.assert(node); |
| 180 | 264 |
| 181 addStackToTree(file, stack, node, this.filter, this.isBottomUp); | 265 addStackToTree(file, stack, node, this.filter, this.isBottomUp); |
| 182 } | 266 } |
| 183 } | 267 } |
| 184 | 268 |
| 185 class FunctionListTree { | 269 class FunctionListTree { |
| 186 constructor(filter) { | 270 constructor(filter, withCategories) { |
| 187 this.tree = { name : "root", children : [], ownTicks : 0, ticks : 0 }; | 271 if (withCategories) { |
| 272 let { categories, root } = buildCategoryTreeAndLookup(); |
| 273 this.tree = root; |
| 274 this.categories = categories; |
| 275 } else { |
| 276 this.tree = { name : "root", children : [], ownTicks : 0, ticks : 0 }; |
| 277 this.categories = null; |
| 278 } |
| 279 |
| 188 this.codeVisited = []; | 280 this.codeVisited = []; |
| 189 this.filter = filter; | 281 this.filter = filter; |
| 190 } | 282 } |
| 191 | 283 |
| 192 addStack(file, timestamp, vmState, stack) { | 284 addStack(file, tickIndex) { |
| 285 let stack = file.ticks[tickIndex].s; |
| 286 let vmState = file.ticks[tickIndex].vm; |
| 287 |
| 193 this.tree.ticks++; | 288 this.tree.ticks++; |
| 194 let child = null; | 289 let child = null; |
| 290 let tree = null; |
| 195 for (let i = stack.length - 2; i >= 0; i -= 2) { | 291 for (let i = stack.length - 2; i >= 0; i -= 2) { |
| 196 let codeId = stack[i]; | 292 let codeId = stack[i]; |
| 197 if (codeId < 0 || this.codeVisited[codeId]) continue; | 293 if (codeId < 0 || this.codeVisited[codeId]) continue; |
| 198 | 294 |
| 199 let code = codeId >= 0 ? file.code[codeId] : undefined; | 295 let code = codeId >= 0 ? file.code[codeId] : undefined; |
| 200 if (this.filter) { | 296 if (this.filter) { |
| 201 let type = code ? code.type : undefined; | 297 let type = code ? code.type : undefined; |
| 202 let kind = code ? code.kind : undefined; | 298 let kind = code ? code.kind : undefined; |
| 203 if (!this.filter(type, kind)) continue; | 299 if (!this.filter(type, kind)) continue; |
| 204 } | 300 } |
| 205 child = this.tree.children[codeId]; | 301 let childId = childIdFromCode(codeId, code); |
| 302 if (this.categories) { |
| 303 let kind = resolveCodeKindAndVmState(code, vmState); |
| 304 tree = this.categories[kind]; |
| 305 } else { |
| 306 tree = this.tree; |
| 307 } |
| 308 child = tree.children[childId]; |
| 206 if (!child) { | 309 if (!child) { |
| 207 child = createNodeFromStackEntry(code); | 310 child = createNodeFromStackEntry(code); |
| 208 this.tree.children[codeId] = child; | 311 child.children[0] = createEmptyNode("Top-down tree"); |
| 312 child.children[0].delayedExpansion = |
| 313 { frameList : [], ascending : false }; |
| 314 child.children[1] = createEmptyNode("Bottom-up tree"); |
| 315 child.children[1].delayedExpansion = |
| 316 { frameList : [], ascending : true }; |
| 317 tree.children[childId] = child; |
| 209 } | 318 } |
| 210 child.ticks++; | 319 child.ticks++; |
| 320 child.children[0].ticks++; |
| 321 addFrameToFrameList( |
| 322 child.children[0].delayedExpansion.frameList, tickIndex, i); |
| 323 child.children[1].ticks++; |
| 324 addFrameToFrameList( |
| 325 child.children[1].delayedExpansion.frameList, tickIndex, i); |
| 211 this.codeVisited[codeId] = true; | 326 this.codeVisited[codeId] = true; |
| 212 } | 327 } |
| 213 if (child) { | 328 if (child) { |
| 214 child.ownTicks++; | 329 child.ownTicks++; |
| 330 console.assert(tree !== null); |
| 331 tree.ticks++; |
| 332 console.assert(tree.type === "CAT"); |
| 215 } | 333 } |
| 216 | 334 |
| 217 for (let i = 0; i < stack.length; i += 2) { | 335 for (let i = 0; i < stack.length; i += 2) { |
| 218 let codeId = stack[i]; | 336 let codeId = stack[i]; |
| 219 if (codeId >= 0) this.codeVisited[codeId] = false; | 337 if (codeId >= 0) this.codeVisited[codeId] = false; |
| 220 } | 338 } |
| 221 } | 339 } |
| 222 } | 340 } |
| 223 | 341 |
| 224 | 342 |
| 225 class CategorySampler { | 343 class CategorySampler { |
| 226 constructor(file, bucketCount) { | 344 constructor(file, bucketCount) { |
| 227 this.bucketCount = bucketCount; | 345 this.bucketCount = bucketCount; |
| 228 | 346 |
| 229 this.firstTime = file.ticks[0].tm; | 347 this.firstTime = file.ticks[0].tm; |
| 230 let lastTime = file.ticks[file.ticks.length - 1].tm; | 348 let lastTime = file.ticks[file.ticks.length - 1].tm; |
| 231 this.step = (lastTime - this.firstTime) / bucketCount; | 349 this.step = (lastTime - this.firstTime) / bucketCount; |
| 232 | 350 |
| 233 this.buckets = []; | 351 this.buckets = []; |
| 234 let bucket = {}; | 352 let bucket = {}; |
| 235 for (let i = 0; i < codeKinds.length; i++) { | 353 for (let i = 0; i < codeKinds.length; i++) { |
| 236 bucket[codeKinds[i]] = 0; | 354 bucket[codeKinds[i]] = 0; |
| 237 } | 355 } |
| 238 for (let i = 0; i < bucketCount; i++) { | 356 for (let i = 0; i < bucketCount; i++) { |
| 239 this.buckets.push(Object.assign({ total : 0 }, bucket)); | 357 this.buckets.push(Object.assign({ total : 0 }, bucket)); |
| 240 } | 358 } |
| 241 } | 359 } |
| 242 | 360 |
| 243 addStack(file, timestamp, vmState, stack) { | 361 addStack(file, tickIndex) { |
| 362 let { tm : timestamp, vm : vmState, s : stack } = file.ticks[tickIndex]; |
| 363 |
| 244 let i = Math.floor((timestamp - this.firstTime) / this.step); | 364 let i = Math.floor((timestamp - this.firstTime) / this.step); |
| 245 if (i == this.buckets.length) i--; | 365 if (i == this.buckets.length) i--; |
| 246 console.assert(i >= 0 && i < this.buckets.length); | 366 console.assert(i >= 0 && i < this.buckets.length); |
| 247 | 367 |
| 248 let bucket = this.buckets[i]; | 368 let bucket = this.buckets[i]; |
| 249 bucket.total++; | 369 bucket.total++; |
| 250 | 370 |
| 251 let codeId = (stack.length > 0) ? stack[0] : -1; | 371 let codeId = (stack.length > 0) ? stack[0] : -1; |
| 252 let code = codeId >= 0 ? file.code[codeId] : undefined; | 372 let code = codeId >= 0 ? file.code[codeId] : undefined; |
| 253 let kind = resolveCodeKindAndVmState(code, vmState); | 373 let kind = resolveCodeKindAndVmState(code, vmState); |
| 254 bucket[kind]++; | 374 bucket[kind]++; |
| 255 } | 375 } |
| 256 } | 376 } |
| 257 | 377 |
| 258 // Generates a tree out of a ticks sequence. | 378 // Generates a tree out of a ticks sequence. |
| 259 // {file} is the JSON files with the ticks and code objects. | 379 // {file} is the JSON files with the ticks and code objects. |
| 260 // {startTime}, {endTime} is the interval. | 380 // {startTime}, {endTime} is the interval. |
| 261 // {tree} is the processor of stacks. | 381 // {tree} is the processor of stacks. |
| 262 function generateTree( | 382 function generateTree( |
| 263 file, startTime, endTime, tree) { | 383 file, startTime, endTime, tree) { |
| 264 let ticks = file.ticks; | 384 let ticks = file.ticks; |
| 265 let i = 0; | 385 let i = 0; |
| 266 while (i < ticks.length && ticks[i].tm < startTime) { | 386 while (i < ticks.length && ticks[i].tm < startTime) { |
| 267 i++; | 387 i++; |
| 268 } | 388 } |
| 269 | 389 |
| 270 let tickCount = 0; | 390 let tickCount = 0; |
| 271 while (i < ticks.length && ticks[i].tm < endTime) { | 391 while (i < ticks.length && ticks[i].tm < endTime) { |
| 272 tree.addStack(file, ticks[i].tm, ticks[i].vm, ticks[i].s); | 392 tree.addStack(file, i); |
| 273 i++; | 393 i++; |
| 274 tickCount++; | 394 tickCount++; |
| 275 } | 395 } |
| 276 | 396 |
| 277 return tickCount; | 397 return tickCount; |
| 278 } | 398 } |
| OLD | NEW |