| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 8f11b16 commit 64c02eb
91 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,24 +8,27 @@ const { | |||
| 8 | 8 | } = require('perf_hooks'); | |
| 9 | 9 | ||
| 10 | 10 | const bench = common.createBenchmark(main, { | |
| 11 | - n: [1e5] | ||
| 11 | + n: [1e5], | ||
| 12 | + observe: ['all', 'measure'], | ||
| 12 | 13 | }); | |
| 13 | 14 | ||
| 14 | 15 | function test() { | |
| 15 | 16 | performance.mark('a'); | |
| 16 | - setImmediate(() => { | ||
| 17 | - performance.mark('b'); | ||
| 18 | - performance.measure('a to b', 'a', 'b'); | ||
| 19 | - }); | ||
| 17 | + performance.mark('b'); | ||
| 18 | + performance.measure('a to b', 'a', 'b'); | ||
| 20 | 19 | } | |
| 21 | 20 | ||
| 22 | - function main({ n }) { | ||
| 21 | + function main({ n, observe }) { | ||
| 22 | + const entryTypes = observe === 'all' ? | ||
| 23 | + [ 'mark', 'measure' ] : | ||
| 24 | + [ observe ]; | ||
| 23 | 25 | const obs = new PerformanceObserver(() => { | |
| 24 | 26 | bench.end(n); | |
| 25 | 27 | }); | |
| 26 | - obs.observe({ entryTypes: ['measure'], buffered: true }); | ||
| 28 | + obs.observe({ entryTypes, buffered: true }); | ||
| 27 | 29 | ||
| 28 | 30 | bench.start(); | |
| 29 | - for (let i = 0; i < n; i++) | ||
| 31 | + performance.mark('start'); | ||
| 32 | + for (let i = 0; i < 1e5; i++) | ||
| 30 | 33 | test(); | |
| 31 | 34 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1264,8 +1264,6 @@ E('ERR_INVALID_PACKAGE_TARGET', | |||
| 1264 | 1264 | pkgPath}package.json${base ? ` imported from ${base}` : ''}${relError ? | |
| 1265 | 1265 | '; targets must start with "./"' : ''}`; | |
| 1266 | 1266 | }, Error); | |
| 1267 | - E('ERR_INVALID_PERFORMANCE_MARK', | ||
| 1268 | - 'The "%s" performance mark has not been set', Error); | ||
| 1269 | 1267 | E('ERR_INVALID_PROTOCOL', | |
| 1270 | 1268 | 'Protocol "%s" not supported. Expected "%s"', | |
| 1271 | 1269 | TypeError); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,10 +4,13 @@ const { | |||
| 4 | 4 | ArrayFrom, | |
| 5 | 5 | ArrayIsArray, | |
| 6 | 6 | ArrayPrototypeFilter, | |
| 7 | + ArrayPrototypeFlatMap, | ||
| 7 | 8 | ArrayPrototypeIncludes, | |
| 8 | 9 | ArrayPrototypePush, | |
| 10 | + ArrayPrototypePushApply, | ||
| 9 | 11 | ArrayPrototypeSlice, | |
| 10 | 12 | ArrayPrototypeSort, | |
| 13 | + Error, | ||
| 11 | 14 | ObjectDefineProperties, | |
| 12 | 15 | ObjectFreeze, | |
| 13 | 16 | ObjectKeys, | |
@@ -31,6 +34,7 @@ const { | |||
| 31 | 34 | const { | |
| 32 | 35 | InternalPerformanceEntry, | |
| 33 | 36 | isPerformanceEntry, | |
| 37 | + kBufferNext, | ||
| 34 | 38 | } = require('internal/perf/performance_entry'); | |
| 35 | 39 | ||
| 36 | 40 | const { | |
@@ -83,6 +87,16 @@ const kSupportedEntryTypes = ObjectFreeze([ | |||
| 83 | 87 | 'measure', | |
| 84 | 88 | ]); | |
| 85 | 89 | ||
| 90 | + // Performance timeline entry Buffers | ||
| 91 | + const markEntryBuffer = createBuffer(); | ||
| 92 | + const measureEntryBuffer = createBuffer(); | ||
| 93 | + const kMaxPerformanceEntryBuffers = 1e6; | ||
| 94 | + const kClearPerformanceEntryBuffers = ObjectFreeze({ | ||
| 95 | + 'mark': 'performance.clearMarks', | ||
| 96 | + 'measure': 'performance.clearMeasures', | ||
| 97 | + }); | ||
| 98 | + const kWarnedEntryTypes = new SafeMap(); | ||
| 99 | + | ||
| 86 | 100 | const kObservers = new SafeSet(); | |
| 87 | 101 | const kPending = new SafeSet(); | |
| 88 | 102 | let isPending = false; | |
@@ -190,6 +204,7 @@ class PerformanceObserver { | |||
| 190 | 204 | const { | |
| 191 | 205 | entryTypes, | |
| 192 | 206 | type, | |
| 207 | + buffered, | ||
| 193 | 208 | } = { ...options }; | |
| 194 | 209 | if (entryTypes === undefined && type === undefined) | |
| 195 | 210 | throw new ERR_MISSING_ARGS('options.entryTypes', 'options.type'); | |
@@ -229,6 +244,13 @@ class PerformanceObserver { | |||
| 229 | 244 | return; | |
| 230 | 245 | this[kEntryTypes].add(type); | |
| 231 | 246 | maybeIncrementObserverCount(type); | |
| 247 | + if (buffered) { | ||
| 248 | + const entries = filterBufferMapByNameAndType(undefined, type); | ||
| 249 | + ArrayPrototypePushApply(this[kBuffer], entries); | ||
| 250 | + kPending.add(this); | ||
| 251 | + if (kPending.size) | ||
| 252 | + queuePending(); | ||
| 253 | + } | ||
| 232 | 254 | } | |
| 233 | 255 | ||
| 234 | 256 | if (this[kEntryTypes].size) | |
@@ -291,6 +313,99 @@ function enqueue(entry) { | |||
| 291 | 313 | for (const obs of kObservers) { | |
| 292 | 314 | obs[kMaybeBuffer](entry); | |
| 293 | 315 | } | |
| 316 | + | ||
| 317 | + const entryType = entry.entryType; | ||
| 318 | + let buffer; | ||
| 319 | + if (entryType === 'mark') { | ||
| 320 | + buffer = markEntryBuffer; | ||
| 321 | + } else if (entryType === 'measure') { | ||
| 322 | + buffer = measureEntryBuffer; | ||
| 323 | + } else { | ||
| 324 | + return; | ||
| 325 | + } | ||
| 326 | + | ||
| 327 | + const count = buffer.count + 1; | ||
| 328 | + buffer.count = count; | ||
| 329 | + if (count === 1) { | ||
| 330 | + buffer.head = entry; | ||
| 331 | + buffer.tail = entry; | ||
| 332 | + return; | ||
| 333 | + } | ||
| 334 | + buffer.tail[kBufferNext] = entry; | ||
| 335 | + buffer.tail = entry; | ||
| 336 | + | ||
| 337 | + if (count > kMaxPerformanceEntryBuffers && | ||
| 338 | + !kWarnedEntryTypes.has(entryType)) { | ||
| 339 | + kWarnedEntryTypes.set(entryType, true); | ||
| 340 | + // No error code for this since it is a Warning | ||
| 341 | + // eslint-disable-next-line no-restricted-syntax | ||
| 342 | + const w = new Error('Possible perf_hooks memory leak detected. ' + | ||
| 343 | + `${count} ${entryType} entries added to the global ` + | ||
| 344 | + 'performance entry buffer. Use ' + | ||
| 345 | + `${kClearPerformanceEntryBuffers[entryType]} to ` + | ||
| 346 | + 'clear the buffer.'); | ||
| 347 | + w.name = 'MaxPerformanceEntryBufferExceededWarning'; | ||
| 348 | + w.entryType = entryType; | ||
| 349 | + w.count = count; | ||
| 350 | + process.emitWarning(w); | ||
| 351 | + } | ||
| 352 | + } | ||
| 353 | + | ||
| 354 | + function clearEntriesFromBuffer(type, name) { | ||
| 355 | + let buffer; | ||
| 356 | + if (type === 'mark') { | ||
| 357 | + buffer = markEntryBuffer; | ||
| 358 | + } else if (type === 'measure') { | ||
| 359 | + buffer = measureEntryBuffer; | ||
| 360 | + } else { | ||
| 361 | + return; | ||
| 362 | + } | ||
| 363 | + if (name === undefined) { | ||
| 364 | + resetBuffer(buffer); | ||
| 365 | + return; | ||
| 366 | + } | ||
| 367 | + | ||
| 368 | + let head = null; | ||
| 369 | + let tail = null; | ||
| 370 | + for (let entry = buffer.head; entry !== null; entry = entry[kBufferNext]) { | ||
| 371 | + if (entry.name !== name) { | ||
| 372 | + head = head ?? entry; | ||
| 373 | + tail = entry; | ||
| 374 | + continue; | ||
| 375 | + } | ||
| 376 | + if (tail === null) { | ||
| 377 | + continue; | ||
| 378 | + } | ||
| 379 | + tail[kBufferNext] = entry[kBufferNext]; | ||
| 380 | + } | ||
| 381 | + buffer.head = head; | ||
| 382 | + buffer.tail = tail; | ||
| 383 | + } | ||
| 384 | + | ||
| 385 | + function filterBufferMapByNameAndType(name, type) { | ||
| 386 | + let bufferList; | ||
| 387 | + if (type === 'mark') { | ||
| 388 | + bufferList = [markEntryBuffer]; | ||
| 389 | + } else if (type === 'measure') { | ||
| 390 | + bufferList = [measureEntryBuffer]; | ||
| 391 | + } else if (type !== undefined) { | ||
| 392 | + // Unrecognized type; | ||
| 393 | + return []; | ||
| 394 | + } else { | ||
| 395 | + bufferList = [markEntryBuffer, measureEntryBuffer]; | ||
| 396 | + } | ||
| 397 | + return ArrayPrototypeFlatMap(bufferList, | ||
| 398 | + (buffer) => filterBufferByName(buffer, name)); | ||
| 399 | + } | ||
| 400 | + | ||
| 401 | + function filterBufferByName(buffer, name) { | ||
| 402 | + const arr = []; | ||
| 403 | + for (let entry = buffer.head; entry !== null; entry = entry[kBufferNext]) { | ||
| 404 | + if (name === undefined || entry.name === name) { | ||
| 405 | + ArrayPrototypePush(arr, entry); | ||
| 406 | + } | ||
| 407 | + } | ||
| 408 | + return arr; | ||
| 294 | 409 | } | |
| 295 | 410 | ||
| 296 | 411 | function observerCallback(name, type, startTime, duration, details) { | |
@@ -338,8 +453,24 @@ function hasObserver(type) { | |||
| 338 | 453 | return observerCounts[observerType] > 0; | |
| 339 | 454 | } | |
| 340 | 455 | ||
| 456 | + function createBuffer() { | ||
| 457 | + return { | ||
| 458 | + head: null, | ||
| 459 | + tail: null, | ||
| 460 | + count: 0, | ||
| 461 | + }; | ||
| 462 | + } | ||
| 463 | + | ||
| 464 | + function resetBuffer(buffer) { | ||
| 465 | + buffer.head = null; | ||
| 466 | + buffer.tail = null; | ||
| 467 | + buffer.count = 0; | ||
| 468 | + } | ||
| 469 | + | ||
| 341 | 470 | module.exports = { | |
| 342 | 471 | PerformanceObserver, | |
| 343 | 472 | enqueue, | |
| 344 | 473 | hasObserver, | |
| 474 | + clearEntriesFromBuffer, | ||
| 475 | + filterBufferMapByNameAndType, | ||
| 345 | 476 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -16,8 +16,12 @@ const { now } = require('internal/perf/utils'); | |||
| 16 | 16 | const { | |
| 17 | 17 | mark, | |
| 18 | 18 | measure, | |
| 19 | - clearMarks, | ||
| 19 | + clearMarkTimings, | ||
| 20 | 20 | } = require('internal/perf/usertiming'); | |
| 21 | + const { | ||
| 22 | + clearEntriesFromBuffer, | ||
| 23 | + filterBufferMapByNameAndType, | ||
| 24 | + } = require('internal/perf/observe'); | ||
| 21 | 25 | ||
| 22 | 26 | const eventLoopUtilization = require('internal/perf/event_loop_utilization'); | |
| 23 | 27 | const nodeTiming = require('internal/perf/nodetiming'); | |
@@ -48,7 +52,6 @@ class Performance extends EventTarget { | |||
| 48 | 52 | timeOrigin: this.timeOrigin, | |
| 49 | 53 | }, opts)}`; | |
| 50 | 54 | } | |
| 51 | - | ||
| 52 | 55 | } | |
| 53 | 56 | ||
| 54 | 57 | function toJSON() { | |
@@ -59,6 +62,39 @@ function toJSON() { | |||
| 59 | 62 | }; | |
| 60 | 63 | } | |
| 61 | 64 | ||
| 65 | + function clearMarks(name) { | ||
| 66 | + if (name !== undefined) { | ||
| 67 | + name = `${name}`; | ||
| 68 | + } | ||
| 69 | + clearMarkTimings(name); | ||
| 70 | + clearEntriesFromBuffer('mark', name); | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + function clearMeasures(name) { | ||
| 74 | + if (name !== undefined) { | ||
| 75 | + name = `${name}`; | ||
| 76 | + } | ||
| 77 | + clearEntriesFromBuffer('measure', name); | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + function getEntries() { | ||
| 81 | + return filterBufferMapByNameAndType(); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + function getEntriesByName(name) { | ||
| 85 | + if (name !== undefined) { | ||
| 86 | + name = `${name}`; | ||
| 87 | + } | ||
| 88 | + return filterBufferMapByNameAndType(name, undefined); | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + function getEntriesByType(type) { | ||
| 92 | + if (type !== undefined) { | ||
| 93 | + type = `${type}`; | ||
| 94 | + } | ||
| 95 | + return filterBufferMapByNameAndType(undefined, type); | ||
| 96 | + } | ||
| 97 | + | ||
| 62 | 98 | class InternalPerformance extends EventTarget {} | |
| 63 | 99 | InternalPerformance.prototype.constructor = Performance.prototype.constructor; | |
| 64 | 100 | ObjectSetPrototypeOf(InternalPerformance.prototype, Performance.prototype); | |
@@ -69,11 +105,31 @@ ObjectDefineProperties(Performance.prototype, { | |||
| 69 | 105 | enumerable: false, | |
| 70 | 106 | value: clearMarks, | |
| 71 | 107 | }, | |
| 108 | + clearMeasures: { | ||
| 109 | + configurable: true, | ||
| 110 | + enumerable: false, | ||
| 111 | + value: clearMeasures, | ||
| 112 | + }, | ||
| 72 | 113 | eventLoopUtilization: { | |
| 73 | 114 | configurable: true, | |
| 74 | 115 | enumerable: false, | |
| 75 | 116 | value: eventLoopUtilization, | |
| 76 | 117 | }, | |
| 118 | + getEntries: { | ||
| 119 | + configurable: true, | ||
| 120 | + enumerable: false, | ||
| 121 | + value: getEntries, | ||
| 122 | + }, | ||
| 123 | + getEntriesByName: { | ||
| 124 | + configurable: true, | ||
| 125 | + enumerable: false, | ||
| 126 | + value: getEntriesByName, | ||
| 127 | + }, | ||
| 128 | + getEntriesByType: { | ||
| 129 | + configurable: true, | ||
| 130 | + enumerable: false, | ||
| 131 | + value: getEntriesByType, | ||
| 132 | + }, | ||
| 77 | 133 | mark: { | |
| 78 | 134 | configurable: true, | |
| 79 | 135 | enumerable: false, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -17,6 +17,7 @@ const kType = Symbol('kType'); | |||
| 17 | 17 | const kStart = Symbol('kStart'); | |
| 18 | 18 | const kDuration = Symbol('kDuration'); | |
| 19 | 19 | const kDetail = Symbol('kDetail'); | |
| 20 | + const kBufferNext = Symbol('kBufferNext'); | ||
| 20 | 21 | ||
| 21 | 22 | function isPerformanceEntry(obj) { | |
| 22 | 23 | return obj?.[kName] !== undefined; | |
@@ -67,6 +68,7 @@ class InternalPerformanceEntry { | |||
| 67 | 68 | this[kStart] = start; | |
| 68 | 69 | this[kDuration] = duration; | |
| 69 | 70 | this[kDetail] = detail; | |
| 71 | + this[kBufferNext] = null; | ||
| 70 | 72 | } | |
| 71 | 73 | } | |
| 72 | 74 | ||
@@ -79,4 +81,5 @@ module.exports = { | |||
| 79 | 81 | InternalPerformanceEntry, | |
| 80 | 82 | PerformanceEntry, | |
| 81 | 83 | isPerformanceEntry, | |
| 84 | + kBufferNext, | ||
| 82 | 85 | }; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments