From 716813a470055085c1bbea1d6712a4f3df5f266f Mon Sep 17 00:00:00 2001 From: Luke Cotter <4013877+lukecotter@users.noreply.github.com> Date: Wed, 3 Jan 2024 10:47:46 +0000 Subject: [PATCH 1/9] ci: fix --skip-duplicate being ignored on ovsx stage --- .github/workflows/cd-prerelease.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cd-prerelease.yml b/.github/workflows/cd-prerelease.yml index 723951e5..31c142f0 100644 --- a/.github/workflows/cd-prerelease.yml +++ b/.github/workflows/cd-prerelease.yml @@ -95,11 +95,11 @@ jobs: echo "Publish to vsce vsix name: $pkgPath" - vsce publish --packagePath ${pkgPath} --no-dependencies --pre-release -p ${{ secrets.VSCE_TOKEN }} --skip-duplicate + vsce publish --packagePath ${pkgPath} --no-dependencies --pre-release --skip-duplicate -p ${{ secrets.VSCE_TOKEN }} echo " Publish to ovsx" - ovsx publish ${pkgPath} --no-dependencies --pre-release -p ${{ secrets.OVSX_TOKEN }} --skip-duplicate + ovsx publish ${pkgPath} --no-dependencies --pre-release --skip-duplicate -p ${{ secrets.OVSX_TOKEN }} - name: Update pre-release tag run: | echo "Updating pre release tag" From 624fc545374f27ffd4d728d0368573be2cc1d930 Mon Sep 17 00:00:00 2001 From: Luke Cotter <4013877+lukecotter@users.noreply.github.com> Date: Wed, 3 Jan 2024 15:40:41 +0000 Subject: [PATCH 2/9] perf: improve aggregateTotals by removing recursion --- log-viewer/modules/parsers/ApexLogParser.ts | 57 +++++++++++++-------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/log-viewer/modules/parsers/ApexLogParser.ts b/log-viewer/modules/parsers/ApexLogParser.ts index caeac8ed..37252c4c 100644 --- a/log-viewer/modules/parsers/ApexLogParser.ts +++ b/log-viewer/modules/parsers/ApexLogParser.ts @@ -287,34 +287,51 @@ export default class ApexLogParser { } } + private flattenByDepth(nodes: LogLine[]) { + const result = new Map(); + result.set(0, nodes); + + let currentDepth = 1; + + let currentNodes = nodes; + let len = currentNodes.length; + while (len) { + while (len--) { + const node = currentNodes[len]; + if (node?.children) { + let children = result.get(currentDepth); + if (!children) { + children = []; + result.set(currentDepth, children); + } + + Array.prototype.push.apply(children, node.children); + } + } + currentNodes = result.get(currentDepth++) || []; + len = currentNodes.length; + } + + return result; + } + private aggregateTotals(nodes: LogLine[]) { const len = nodes.length; if (!len) { return; } - // This method purposely collects the children in bulk to avoid as much recursion as possible. This increases performance to be just over ~3 times faster or ~70% faster. - - // collect all children for the supplied nodes. - const children: LogLine[] = []; - let i = len; - while (i--) { - const parent = nodes[i]; - if (parent?.children.length) { - parent.children.forEach((child) => { - children.push(child); - }); - } - } - - if (children.length) { - this.aggregateTotals(children); + // This method purposely processes the children at the lowest depth first in bulk to avoid as much recursion as possible. This increases performance to be just over ~3 times faster or ~70% faster. - // sum the children in bulk - i = len; + // collect all children for the supplied nodes by depth. + const nodesByDepth = this.flattenByDepth(nodes); + let depth = nodesByDepth.size; + while (depth--) { + const nds = nodesByDepth.get(depth) || []; + let i = nds.length; while (i--) { - const parent = nodes[i]; - if (parent?.children.length) { + const parent = nds[i]; + if (parent?.children) { parent.children.forEach((child) => { parent.dmlCount.total += child.dmlCount.total; parent.soqlCount.total += child.soqlCount.total; From 160bffa03ea2008298c73e19f994dfc24b83bd8c Mon Sep 17 00:00:00 2001 From: Luke Cotter <4013877+lukecotter@users.noreply.github.com> Date: Wed, 3 Jan 2024 15:41:04 +0000 Subject: [PATCH 3/9] perf: replace for each with reduce --- log-viewer/modules/parsers/ApexLogParser.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/log-viewer/modules/parsers/ApexLogParser.ts b/log-viewer/modules/parsers/ApexLogParser.ts index 37252c4c..f9760bc5 100644 --- a/log-viewer/modules/parsers/ApexLogParser.ts +++ b/log-viewer/modules/parsers/ApexLogParser.ts @@ -722,12 +722,10 @@ export class TimedNode extends LogLine { recalculateDurations() { if (this.exitStamp) { + const childDuration = this.children.reduce((accumulator, currentValue) => { + return accumulator + currentValue.duration.total; + }, 0); this.duration.total = this.exitStamp - this.timestamp; - - let childDuration = 0; - this.children.forEach((child) => { - childDuration += child.duration.total; - }); this.duration.self = this.duration.total - childDuration; } } From 87e4ba06e161421709b927233667ce7fd3f7c257 Mon Sep 17 00:00:00 2001 From: Luke Cotter <4013877+lukecotter@users.noreply.github.com> Date: Thu, 4 Jan 2024 16:24:16 +0000 Subject: [PATCH 4/9] perf: added fast path for splitting lines when EOL is \n --- log-viewer/modules/parsers/ApexLogParser.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/log-viewer/modules/parsers/ApexLogParser.ts b/log-viewer/modules/parsers/ApexLogParser.ts index f9760bc5..aca5e68b 100644 --- a/log-viewer/modules/parsers/ApexLogParser.ts +++ b/log-viewer/modules/parsers/ApexLogParser.ts @@ -107,11 +107,9 @@ export default class ApexLogParser { return null; } - // Matches CRLF (\r\n) + LF (\n) - // the ? matches the previous token 0 or 1 times. private parseLog(log: string): LogLine[] { const start = log.match(/^.*EXECUTION_STARTED.*$/m)?.index || 0; - const rawLines = log.slice(start).split(/\r?\n/); + const rawLines = this.splitByNextLine(log.slice(start)); // reset global variables to be captured during parsing this.logIssues = []; @@ -138,6 +136,19 @@ export default class ApexLogParser { return logLines; } + // Matches CRLF (\r\n) + LF (\n) + // the ? matches the previous token 0 or 1 times. + private splitByNextLine(text: string) { + const hascrlfEOL = text.indexOf('\r\n') > -1; + let regex; + if (!hascrlfEOL) { + regex = /\n/; + } else { + regex = /\r?\n/; + } + return text.split(regex); + } + private toLogTree(logLines: LogLine[]) { const lineIter = new LineIterator(logLines), rootMethod = new ApexLog(), From 29f4b88fbb3d0ff472e060d13a5eef59d3020775 Mon Sep 17 00:00:00 2001 From: Luke Cotter <4013877+lukecotter@users.noreply.github.com> Date: Thu, 4 Jan 2024 16:25:37 +0000 Subject: [PATCH 5/9] perf: make parseLineNumber + parseTimestamp functions of the LogLine Should reduce gc a little --- .../modules/__tests__/ApexLogParser.test.ts | 43 +++---- log-viewer/modules/parsers/ApexLogParser.ts | 121 +++++++++--------- 2 files changed, 82 insertions(+), 82 deletions(-) diff --git a/log-viewer/modules/__tests__/ApexLogParser.test.ts b/log-viewer/modules/__tests__/ApexLogParser.test.ts index ed453f8c..3e096e8a 100644 --- a/log-viewer/modules/__tests__/ApexLogParser.test.ts +++ b/log-viewer/modules/__tests__/ApexLogParser.test.ts @@ -12,10 +12,8 @@ import { TimedNode, lineTypeMap, parse, - parseLineNumber, parseObjectNamespace, parseRows, - parseTimestamp, parseVfNamespace, } from '../parsers/ApexLogParser.js'; @@ -43,18 +41,6 @@ describe('parseVfNamespace tests', () => { }); }); -describe('parseTimestamp tests', () => { - it("Should parse the timestamp from it's section", () => { - expect(parseTimestamp('22:00:05.0 (59752074)')).toEqual(59752074); - }); -}); - -describe('parseLineNumber tests', () => { - it("Should parse the line-number from it's section", () => { - expect(parseLineNumber('[37]')).toEqual(37); - }); -}); - describe('parseRows tests', () => { it("Should parse the row-count from it's section", () => { expect(parseRows('Rows:12')).toEqual(12); @@ -93,20 +79,32 @@ describe('Pseudo EXIT events', () => { expect(log1.duration).toEqual({ self: 0, total: 3 }); const approval1 = log1.children[0] as Method; - expect(approval1.duration).toEqual({ self: 1, total: 1 }); - expect(approval1.type).toEqual('WF_APPROVAL_SUBMIT'); + expect(approval1).toMatchObject({ + type: 'WF_APPROVAL_SUBMIT', + timestamp: 1, + duration: { self: 1, total: 1 }, + }); const processFound1 = log1.children[1] as Method; - expect(processFound1.duration).toEqual({ self: 1, total: 1 }); - expect(processFound1.type).toEqual('WF_PROCESS_FOUND'); + expect(processFound1).toMatchObject({ + type: 'WF_PROCESS_FOUND', + timestamp: 2, + duration: { self: 1, total: 1 }, + }); const approval2 = log1.children[2] as Method; - expect(approval2.duration).toEqual({ self: 1, total: 1 }); - expect(approval2.type).toEqual('WF_APPROVAL_SUBMIT'); + expect(approval2).toMatchObject({ + type: 'WF_APPROVAL_SUBMIT', + timestamp: 3, + duration: { self: 1, total: 1 }, + }); const processFound2 = log1.children[3] as Method; - expect(processFound2.duration).toEqual({ self: 0, total: 0 }); // no lines after the last WF_PROCESS_FOUND to use as an exit - expect(processFound2.type).toEqual('WF_PROCESS_FOUND'); + expect(processFound2).toMatchObject({ + type: 'WF_PROCESS_FOUND', + timestamp: 4, + duration: { self: 0, total: 0 }, // no lines after the last WF_PROCESS_FOUND to use as an exit + }); }); it('Pseudo EXIT With Entry after last event', () => { @@ -693,6 +691,7 @@ describe('Recalculate durations tests', () => { node.exitStamp = 3; node.recalculateDurations(); + expect(node.timestamp).toEqual(1); expect(node.duration).toEqual({ self: 2, total: 2 }); }); diff --git a/log-viewer/modules/parsers/ApexLogParser.ts b/log-viewer/modules/parsers/ApexLogParser.ts index aca5e68b..1e916749 100644 --- a/log-viewer/modules/parsers/ApexLogParser.ts +++ b/log-viewer/modules/parsers/ApexLogParser.ts @@ -681,9 +681,10 @@ export abstract class LogLine { constructor(parts: string[] | null) { if (parts) { - this.type = parts[1] as LogEventType; + const [timeData, type] = parts; + this.type = type as LogEventType; this.text = this.type; - this.timestamp = parseTimestamp(parts[0] || ''); + this.timestamp = this.parseTimestamp(timeData as string); } } @@ -692,6 +693,30 @@ export abstract class LogLine { /** Called when the Log event after this one is created in the line parser*/ onAfter?(parser: ApexLogParser, next?: LogLine): void; + + private parseTimestamp(text: string): number { + const start = text.indexOf('('); + if (start !== -1) { + return Number(text.slice(start + 1, -1)); + } + throw new Error(`Unable to parse timestamp: '${text}'`); + } + + protected parseLineNumber(text: string | null | undefined): string | number { + switch (true) { + case text === '[EXTERNAL]': + return 'EXTERNAL'; + case !!text: { + const lineNumberStr = text.slice(1, -1); + if (lineNumberStr) { + return Number(lineNumberStr); + } + throw new Error(`Unable to parse line number: '${text}'`); + } + default: + return 0; + } + } } class BasicLogLine extends LogLine {} @@ -862,30 +887,6 @@ export function parseVfNamespace(text: string): string { return text.substring(secondSlash + 1, sep); } -export function parseTimestamp(text: string): number { - const start = text.indexOf('('); - if (start !== -1) { - return Number(text.slice(start + 1, -1)); - } - throw new Error(`Unable to parse timestamp: '${text}'`); -} - -export function parseLineNumber(text: string | null | undefined): string | number { - switch (true) { - case text === '[EXTERNAL]': - return 'EXTERNAL'; - case !!text: { - const lineNumberStr = text.slice(1, -1); - if (lineNumberStr) { - return Number(lineNumberStr); - } - throw new Error(`Unable to parse line number: '${text}'`); - } - default: - return 0; - } -} - export function parseRows(text: string | null | undefined): number { if (!text) { return 0; @@ -949,7 +950,7 @@ class ConstructorEntryLine extends Method { constructor(parts: string[]) { super(parts, ['CONSTRUCTOR_EXIT'], 'Method', 'method'); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); const args = parts[4]; this.text = parts[5] + (args ? args.substring(args.lastIndexOf('(')) : ''); @@ -961,7 +962,7 @@ class ConstructorExitLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); } } @@ -969,7 +970,7 @@ class EmailQueueLine extends LogLine { acceptsText = true; constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); } } @@ -978,7 +979,7 @@ export class MethodEntryLine extends Method { constructor(parts: string[]) { super(parts, ['METHOD_EXIT'], 'Method', 'method'); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = parts[4] || this.type || ''; if (this.text === 'System.Type.forName(String, String)') { this.cpuType = 'loading'; // assume we are not charged for class loading (or at least not lengthy remote-loading / compiling) @@ -991,7 +992,7 @@ class MethodExitLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); } } @@ -1000,7 +1001,7 @@ class SystemConstructorEntryLine extends Method { constructor(parts: string[]) { super(parts, ['SYSTEM_CONSTRUCTOR_EXIT'], 'System Method', 'method'); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = parts[3] || ''; } } @@ -1010,13 +1011,13 @@ class SystemConstructorExitLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); } } class SystemMethodEntryLine extends Method { constructor(parts: string[]) { super(parts, ['SYSTEM_METHOD_EXIT'], 'System Method', 'method'); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = parts[3] || ''; } } @@ -1026,7 +1027,7 @@ class SystemMethodExitLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); } } @@ -1108,7 +1109,7 @@ class VFApexCallStartLine extends Method { constructor(parts: string[]) { super(parts, ['VF_APEX_CALL_END'], 'Method', 'method'); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); const classText = parts[5] || parts[3] || ''; let methodtext = parts[4] || ''; @@ -1202,7 +1203,7 @@ class DMLBeginLine extends Method { constructor(parts: string[]) { super(parts, ['DML_END'], 'DML', 'free'); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = 'DML ' + parts[3] + ' ' + parts[4]; const rowCountString = parts[5]; this.rowCount.total = this.rowCount.self = rowCountString ? parseRows(rowCountString) : 0; @@ -1214,14 +1215,14 @@ class DMLEndLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); } } class IdeasQueryExecuteLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); } } @@ -1234,7 +1235,7 @@ class SOQLExecuteBeginLine extends Method { constructor(parts: string[]) { super(parts, ['SOQL_EXECUTE_END'], 'SOQL', 'free'); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); const [, , , aggregations, soqlString] = parts; @@ -1256,7 +1257,7 @@ class SOQLExecuteEndLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.rowCount.total = this.rowCount.self = parseRows(parts[3] || ''); } } @@ -1271,7 +1272,7 @@ class SOQLExecuteExplainLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); const queryPlanDetails = parts[3] || ''; this.text = queryPlanDetails; @@ -1310,7 +1311,7 @@ class SOQLExecuteExplainLine extends LogLine { class SOSLExecuteBeginLine extends Method { constructor(parts: string[]) { super(parts, ['SOSL_EXECUTE_END'], 'SOQL', 'free'); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = `SOSL: ${parts[3]}`; } @@ -1324,7 +1325,7 @@ class SOSLExecuteEndLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.rowCount.total = this.rowCount.self = parseRows(parts[3] || ''); } } @@ -1332,7 +1333,7 @@ class SOSLExecuteEndLine extends LogLine { class HeapAllocateLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = parts[3] || ''; } } @@ -1340,21 +1341,21 @@ class HeapAllocateLine extends LogLine { class HeapDeallocateLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); } } class StatementExecuteLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); } } class VariableScopeBeginLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = parts.slice(3).join(' | '); } } @@ -1362,14 +1363,14 @@ class VariableScopeBeginLine extends LogLine { class VariableAssignmentLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = parts.slice(3).join(' | '); } } class UserInfoLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = parts[3] + ' ' + parts[4]; } } @@ -1379,7 +1380,7 @@ class UserDebugLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = parts.slice(3).join(' | '); } } @@ -1407,7 +1408,7 @@ class CumulativeProfilingBeginLine extends Method { class LimitUsageLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = parts[3] + ' ' + parts[4] + ' out of ' + parts[5]; } } @@ -1488,7 +1489,7 @@ class PushTraceFlagsLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = parts[4] + ', line:' + this.lineNumber + ' - ' + parts[5]; } } @@ -1498,7 +1499,7 @@ class PopTraceFlagsLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = parts[4] + ', line:' + this.lineNumber + ' - ' + parts[5]; } } @@ -1506,7 +1507,7 @@ class PopTraceFlagsLine extends LogLine { class QueryMoreBeginLine extends Method { constructor(parts: string[]) { super(parts, ['QUERY_MORE_END'], 'SOQL', 'custom'); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = `line: ${this.lineNumber}`; } } @@ -1516,14 +1517,14 @@ class QueryMoreEndLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = `line: ${this.lineNumber}`; } } class QueryMoreIterationsLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = `line: ${this.lineNumber}, iterations:${parts[3]}`; } } @@ -1531,7 +1532,7 @@ class QueryMoreIterationsLine extends LogLine { class SavepointRollbackLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = `${parts[3]}, line: ${this.lineNumber}`; } } @@ -1539,7 +1540,7 @@ class SavepointRollbackLine extends LogLine { class SavePointSetLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = `${parts[3]}, line: ${this.lineNumber}`; } } @@ -2270,7 +2271,7 @@ class ExceptionThrownLine extends LogLine { constructor(parts: string[]) { super(parts); - this.lineNumber = parseLineNumber(parts[2]); + this.lineNumber = this.parseLineNumber(parts[2]); this.text = parts[3] || ''; } From fedfd9e4ec5785fd5e8a22e2e7fa123d36307603 Mon Sep 17 00:00:00 2001 From: Luke Cotter <4013877+lukecotter@users.noreply.github.com> Date: Thu, 4 Jan 2024 16:40:42 +0000 Subject: [PATCH 6/9] perf: add fast path to get log class type for most common types 'METHOD_ENTRY' => MethodEntryLine; 'METHOD_EXIT' => MethodExitLine; 'CONSTRUCTOR_ENTRY' => ConstructorEntryLine; 'CONSTRUCTOR_EXIT' => ConstructorExitLine; --- log-viewer/modules/parsers/ApexLogParser.ts | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/log-viewer/modules/parsers/ApexLogParser.ts b/log-viewer/modules/parsers/ApexLogParser.ts index 1e916749..1650eeda 100644 --- a/log-viewer/modules/parsers/ApexLogParser.ts +++ b/log-viewer/modules/parsers/ApexLogParser.ts @@ -2434,6 +2434,29 @@ class MatchEngineBegin extends Method { } function getLogEventClass(eventName: LogEventType): LogLineConstructor | null | undefined { + // Fast path for the most commonly occuring types + switch (eventName) { + case 'METHOD_ENTRY': + return MethodEntryLine; + break; + + case 'METHOD_EXIT': + return MethodExitLine; + break; + + case 'CONSTRUCTOR_ENTRY': + return ConstructorEntryLine; + break; + + case 'CONSTRUCTOR_EXIT': + return ConstructorExitLine; + break; + + default: + break; + } + + // Handle all other types const logType = lineTypeMap.get(eventName); if (logType) { return logType; From a0c09f1e6cfee6a1f15642b3d9c8fddc38539281 Mon Sep 17 00:00:00 2001 From: Luke Cotter <4013877+lukecotter@users.noreply.github.com> Date: Fri, 5 Jan 2024 10:46:13 +0000 Subject: [PATCH 7/9] perf: only add parent node with children to depth map This means that we have less to loop later on when aggregating values. --- log-viewer/modules/parsers/ApexLogParser.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/log-viewer/modules/parsers/ApexLogParser.ts b/log-viewer/modules/parsers/ApexLogParser.ts index 1650eeda..36620d24 100644 --- a/log-viewer/modules/parsers/ApexLogParser.ts +++ b/log-viewer/modules/parsers/ApexLogParser.ts @@ -307,16 +307,16 @@ export default class ApexLogParser { let currentNodes = nodes; let len = currentNodes.length; while (len) { + result.set(currentDepth, []); while (len--) { const node = currentNodes[len]; if (node?.children) { - let children = result.get(currentDepth); - if (!children) { - children = []; - result.set(currentDepth, children); - } - - Array.prototype.push.apply(children, node.children); + const children = result.get(currentDepth)!; + node.children.forEach((c) => { + if (c.children.length) { + children.push(c); + } + }); } } currentNodes = result.get(currentDepth++) || []; From 6e9590415e81dbbdcced7fa7ef33d878fd1ebeec Mon Sep 17 00:00:00 2001 From: Luke Cotter <4013877+lukecotter@users.noreply.github.com> Date: Fri, 5 Jan 2024 10:46:53 +0000 Subject: [PATCH 8/9] perf: move the child duration aggregation to the aggregateTotals function --- log-viewer/modules/parsers/ApexLogParser.ts | 22 ++++++++------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/log-viewer/modules/parsers/ApexLogParser.ts b/log-viewer/modules/parsers/ApexLogParser.ts index 36620d24..d9e6043d 100644 --- a/log-viewer/modules/parsers/ApexLogParser.ts +++ b/log-viewer/modules/parsers/ApexLogParser.ts @@ -165,7 +165,6 @@ export default class ApexLogParser { rootMethod.setTimes(); this.insertPackageWrappers(rootMethod); - this.setNamespaces(rootMethod); this.aggregateTotals([rootMethod]); return rootMethod; @@ -342,14 +341,13 @@ export default class ApexLogParser { let i = nds.length; while (i--) { const parent = nds[i]; - if (parent?.children) { - parent.children.forEach((child) => { - parent.dmlCount.total += child.dmlCount.total; - parent.soqlCount.total += child.soqlCount.total; - parent.totalThrownCount += child.totalThrownCount; - parent.rowCount.total += child.rowCount.total; - }); - } + parent?.children.forEach((child) => { + parent.dmlCount.total += child.dmlCount.total; + parent.soqlCount.total += child.soqlCount.total; + parent.totalThrownCount += child.totalThrownCount; + parent.rowCount.total += child.rowCount.total; + parent.duration.self -= child.duration.total; + }); } } } @@ -758,11 +756,7 @@ export class TimedNode extends LogLine { recalculateDurations() { if (this.exitStamp) { - const childDuration = this.children.reduce((accumulator, currentValue) => { - return accumulator + currentValue.duration.total; - }, 0); - this.duration.total = this.exitStamp - this.timestamp; - this.duration.self = this.duration.total - childDuration; + this.duration.total = this.duration.self = this.exitStamp - this.timestamp; } } } From d2949e3f4607e4aa153459254331dd0273953698 Mon Sep 17 00:00:00 2001 From: Luke Cotter <4013877+lukecotter@users.noreply.github.com> Date: Wed, 10 Jan 2024 16:19:35 +0000 Subject: [PATCH 9/9] test: fix duration rollup test now that has moved --- log-viewer/modules/__tests__/ApexLogParser.test.ts | 13 ------------- log-viewer/modules/parsers/ApexLogParser.ts | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/log-viewer/modules/__tests__/ApexLogParser.test.ts b/log-viewer/modules/__tests__/ApexLogParser.test.ts index 3e096e8a..01cfb055 100644 --- a/log-viewer/modules/__tests__/ApexLogParser.test.ts +++ b/log-viewer/modules/__tests__/ApexLogParser.test.ts @@ -694,19 +694,6 @@ describe('Recalculate durations tests', () => { expect(node.timestamp).toEqual(1); expect(node.duration).toEqual({ self: 2, total: 2 }); }); - - it('Children are subtracted from net duration', () => { - const node = new Method(['14:32:07.563 (0)', 'DUMMY'], [], 'Method', ''), - child1 = new Method(['14:32:07.563 (10)', 'DUMMY'], [], 'Method', ''), - child2 = new Method(['14:32:07.563 (70)', 'DUMMY'], [], 'Method', ''); - node.exitStamp = 100; - child1.duration.total = 50; - child2.duration.total = 25; - node.addChild(child1); - node.addChild(child2); - node.recalculateDurations(); - expect(node.duration).toEqual({ self: 25, total: 100 }); - }); }); describe('Line Type Tests', () => { diff --git a/log-viewer/modules/parsers/ApexLogParser.ts b/log-viewer/modules/parsers/ApexLogParser.ts index d9e6043d..d1bad419 100644 --- a/log-viewer/modules/parsers/ApexLogParser.ts +++ b/log-viewer/modules/parsers/ApexLogParser.ts @@ -166,7 +166,7 @@ export default class ApexLogParser { this.insertPackageWrappers(rootMethod); this.setNamespaces(rootMethod); - this.aggregateTotals([rootMethod]); + this.aggregateTotals(rootMethod.children); return rootMethod; }