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 |