@memlab/core 1.1.14 → 1.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -257,10 +257,9 @@ class HeapNode {
257
257
  get location() {
258
258
  const heapSnapshot = this.heapSnapshot;
259
259
  const locationIdx = heapSnapshot._nodeIdx2LocationIdx[this.idx];
260
- if (locationIdx == null) {
261
- return null;
262
- }
263
- return new HeapLocation_1.default(heapSnapshot, locationIdx);
260
+ return locationIdx == null
261
+ ? null
262
+ : new HeapLocation_1.default(heapSnapshot, locationIdx);
264
263
  }
265
264
  // search reference by edge name and edge type
266
265
  getReference(edgeName, edgeType) {
@@ -206,8 +206,8 @@ class HeapSnapshot {
206
206
  const locationFieldsCount = this._locationFieldsCount;
207
207
  let locationIdx = 0;
208
208
  while (locationIdx < this._locationCount) {
209
- const id = locations[locationIdx * locationFieldsCount + this._locationObjectIndexOffset];
210
- this._nodeIdx2LocationIdx[id] = locationIdx;
209
+ const nodeIndex = locations[locationIdx * locationFieldsCount + this._locationObjectIndexOffset];
210
+ this._nodeIdx2LocationIdx[nodeIndex] = locationIdx;
211
211
  ++locationIdx;
212
212
  }
213
213
  }
@@ -7,8 +7,22 @@
7
7
  * @format
8
8
  * @oncall web_perf_infra
9
9
  */
10
+ import type { LeakTrace } from '../lib/Types';
10
11
  declare const _default: {
11
- isSimilarTrace: (t1: import("..").LeakTrace, t2: import("..").LeakTrace) => boolean;
12
+ isSimilarTrace: (t1: LeakTrace, t2: LeakTrace) => boolean;
12
13
  };
13
14
  export default _default;
15
+ /**
16
+ * const elements = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
17
+ * randomChunks(elements, 3) -> [[4, 8, 3], [9, 5, 1], [2, 6, 7, 10]]
18
+ * @internal
19
+ */
20
+ export declare const randomChunks: <T>(items: T[], n: number) => T[][];
21
+ /**
22
+ * chunks(elements, 3) -> [[1, 2, 3], [4, 5, 6], [7, 8, 9, 10]]
23
+ * @internal
24
+ */
25
+ export declare const chunks: <T>(items: T[], n: number) => T[][];
26
+ /** @internal*/
27
+ export declare const lastNodeFromTrace: (trace: LeakTrace) => import("../lib/Types").LeakTraceElement;
14
28
  //# sourceMappingURL=ClusterUtils.d.ts.map
@@ -12,6 +12,48 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.lastNodeFromTrace = exports.chunks = exports.randomChunks = void 0;
15
16
  const ClusterUtilsHelper_1 = __importDefault(require("./ClusterUtilsHelper"));
16
17
  const ClusteringHeuristics_1 = __importDefault(require("./ClusteringHeuristics"));
17
18
  exports.default = ClusterUtilsHelper_1.default.initialize(ClusteringHeuristics_1.default);
19
+ /**
20
+ * const elements = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
21
+ * randomChunks(elements, 3) -> [[4, 8, 3], [9, 5, 1], [2, 6, 7, 10]]
22
+ * @internal
23
+ */
24
+ const randomChunks = (items, n) => {
25
+ const array = [...items];
26
+ const size = Math.floor(array.length / n);
27
+ const chunks = [];
28
+ for (let i = 0; i < n - 1; i++) {
29
+ const chunk = [];
30
+ for (let j = 0; j < size; j++) {
31
+ const idx = Math.floor(Math.random() * array.length);
32
+ chunk[j] = array[idx];
33
+ array.splice(idx, 1);
34
+ }
35
+ chunks.push(chunk);
36
+ }
37
+ chunks.push(array);
38
+ return chunks;
39
+ };
40
+ exports.randomChunks = randomChunks;
41
+ /**
42
+ * chunks(elements, 3) -> [[1, 2, 3], [4, 5, 6], [7, 8, 9, 10]]
43
+ * @internal
44
+ */
45
+ const chunks = (items, n) => {
46
+ const array = [...items];
47
+ const size = Math.floor(array.length / n);
48
+ const chunks = [];
49
+ for (let i = 0; i < n - 1; i++) {
50
+ const chunk = array.splice(0, size);
51
+ chunks.push(chunk);
52
+ }
53
+ chunks.push(array);
54
+ return chunks;
55
+ };
56
+ exports.chunks = chunks;
57
+ /** @internal*/
58
+ const lastNodeFromTrace = (trace) => trace[trace.length - 1];
59
+ exports.lastNodeFromTrace = lastNodeFromTrace;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall web_perf_infra
9
+ */
10
+ import type { LeakTrace } from '../lib/Types';
11
+ declare type ClusterOption = {
12
+ numberOfIteration?: number;
13
+ numberOfTraceToRetainInCluster?: number;
14
+ };
15
+ export default class MultiIterationSeqClustering {
16
+ private traceSimilarity;
17
+ constructor();
18
+ cluster(newLeakTraces: LeakTrace[], options?: ClusterOption): LeakTrace[][];
19
+ }
20
+ export {};
21
+ //# sourceMappingURL=MultiIterationSeqClustering.d.ts.map
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ *
8
+ * @format
9
+ * @oncall web_perf_infra
10
+ */
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const ClusterUtils_1 = require("./ClusterUtils");
16
+ const TraceSimilarityStrategy_1 = __importDefault(require("./strategies/TraceSimilarityStrategy"));
17
+ const MLTraceSimilarityStrategy_1 = __importDefault(require("./strategies/MLTraceSimilarityStrategy"));
18
+ const Config_1 = __importDefault(require("../lib/Config"));
19
+ const Utils_1 = __importDefault(require("../lib/Utils"));
20
+ const Console_1 = __importDefault(require("../lib/Console"));
21
+ class MultiIterationSeqClustering {
22
+ constructor() {
23
+ this.traceSimilarity = Config_1.default.isMLClustering
24
+ ? new MLTraceSimilarityStrategy_1.default()
25
+ : new TraceSimilarityStrategy_1.default();
26
+ }
27
+ cluster(newLeakTraces, options = {}) {
28
+ const maxNumOfSampleTraceInCluster = positiveIntOrDefaultValue(options.numberOfTraceToRetainInCluster, Infinity);
29
+ const numIteration = positiveIntOrDefaultValue(options.numberOfIteration, 1);
30
+ if (Config_1.default.verbose) {
31
+ Console_1.default.lowLevel(`maxNumOfSampleTraceInCluster: ${maxNumOfSampleTraceInCluster}`);
32
+ Console_1.default.lowLevel(`numIteration: ${numIteration}`);
33
+ }
34
+ // build trace and id mapping
35
+ const traceId2RepTraceId = new Map();
36
+ const traceId2Trace = new Map();
37
+ for (const trace of newLeakTraces) {
38
+ traceId2Trace.set(traceId(trace), trace);
39
+ }
40
+ // split all traces into several batches
41
+ const splitFn = Config_1.default.seqClusteringIsRandomChunks ? ClusterUtils_1.randomChunks : ClusterUtils_1.chunks;
42
+ const traceGroups = splitFn(newLeakTraces, numIteration);
43
+ let clusteredTraceSamples = [];
44
+ for (let iter = 0; iter < numIteration; ++iter) {
45
+ Console_1.default.overwrite(`Iteration: ${iter + 1}`);
46
+ // mix the current traces to cluster with all clustered trace samples
47
+ const curTraceGroup = traceGroups[iter].concat(clusteredTraceSamples);
48
+ // cluster the trace group
49
+ const { allClusters: clusters } = this.traceSimilarity.diffTraces(curTraceGroup, []);
50
+ // assign trace id to representative trace id
51
+ updateTraceId2RepTraceIdMap(clusters, traceId2RepTraceId);
52
+ // sample each group
53
+ for (let i = 0; i < clusters.length; ++i) {
54
+ clusters[i] = clusters[i].slice(0, maxNumOfSampleTraceInCluster);
55
+ }
56
+ // update samples
57
+ clusteredTraceSamples = clusters.reduce((acc, cluster) => acc.concat(cluster), []);
58
+ }
59
+ // rebuild full clusters based on the mappings
60
+ const repTraceId2Cluster = new Map();
61
+ for (const id of traceId2RepTraceId.keys()) {
62
+ const repTraceId = traceId2RepTraceId.get(id);
63
+ if (!repTraceId2Cluster.has(repTraceId)) {
64
+ repTraceId2Cluster.set(repTraceId, []);
65
+ }
66
+ const cluster = repTraceId2Cluster.get(repTraceId);
67
+ if (cluster.length === 0) {
68
+ const repTrace = traceId2Trace.get(repTraceId);
69
+ cluster.push(repTrace);
70
+ }
71
+ if (id !== repTraceId) {
72
+ const trace = traceId2Trace.get(id);
73
+ cluster.push(trace);
74
+ }
75
+ }
76
+ return Array.from(repTraceId2Cluster.values());
77
+ }
78
+ }
79
+ exports.default = MultiIterationSeqClustering;
80
+ function traceId(trace) {
81
+ const lastNode = (0, ClusterUtils_1.lastNodeFromTrace)(trace);
82
+ if (lastNode.id == null) {
83
+ throw Utils_1.default.haltOrThrow('last node id missing');
84
+ }
85
+ return lastNode.id;
86
+ }
87
+ function updateTraceId2RepTraceIdMap(clusters, traceId2RepTraceId) {
88
+ for (const cluster of clusters) {
89
+ const repTrace = cluster[0];
90
+ for (const trace of cluster) {
91
+ traceId2RepTraceId.set(traceId(trace), traceId(repTrace));
92
+ }
93
+ }
94
+ // update trace id to representative trace id closure
95
+ for (const id of traceId2RepTraceId.keys()) {
96
+ const queue = [];
97
+ let cur = id;
98
+ let repTraceId = traceId2RepTraceId.get(cur);
99
+ while (repTraceId !== cur) {
100
+ queue.push(cur);
101
+ cur = repTraceId;
102
+ repTraceId = traceId2RepTraceId.get(cur);
103
+ }
104
+ for (const idInQueue of queue) {
105
+ traceId2RepTraceId.set(idInQueue, repTraceId);
106
+ }
107
+ }
108
+ return traceId2RepTraceId;
109
+ }
110
+ function positiveIntOrDefaultValue(v, d) {
111
+ return typeof v !== 'number' || v <= 0 ? d : v;
112
+ }
@@ -21,6 +21,7 @@ const TraceElement_1 = require("./TraceElement");
21
21
  const TraceSimilarityStrategy_1 = __importDefault(require("./strategies/TraceSimilarityStrategy"));
22
22
  const TraceAsClusterStrategy_1 = __importDefault(require("./strategies/TraceAsClusterStrategy"));
23
23
  const MLTraceSimilarityStrategy_1 = __importDefault(require("./strategies/MLTraceSimilarityStrategy"));
24
+ const ClusterUtils_1 = require("./ClusterUtils");
24
25
  // sync up with html/intern/js/webspeed/memlab/lib/LeakCluster.js
25
26
  class NormalizedTrace {
26
27
  constructor(p = null, snapshot = null) {
@@ -166,10 +167,9 @@ class NormalizedTrace {
166
167
  return NormalizedTrace.clusteredLeakTracesToRecord(allClusters);
167
168
  }
168
169
  static clusteredLeakTracesToRecord(allClusters) {
169
- const lastNodeFromTrace = (trace) => trace[trace.length - 1];
170
170
  const labaledLeakTraces = allClusters.reduce((acc, bucket) => {
171
- const lastNodeFromFirstTrace = lastNodeFromTrace(bucket[0]);
172
- bucket.map(lastNodeFromTrace).forEach(lastNodeInTrace => {
171
+ const lastNodeFromFirstTrace = (0, ClusterUtils_1.lastNodeFromTrace)(bucket[0]);
172
+ bucket.map(ClusterUtils_1.lastNodeFromTrace).forEach(lastNodeInTrace => {
173
173
  if (lastNodeInTrace.id == null || lastNodeFromFirstTrace.id == null) {
174
174
  throw new Error('node id not found in last node of the leak trace');
175
175
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memlab/core",
3
- "version": "1.1.14",
3
+ "version": "1.1.17",
4
4
  "license": "MIT",
5
5
  "description": "memlab core libraries",
6
6
  "author": "Liang Gong <lgong@fb.com>",
@@ -59,7 +59,7 @@
59
59
  "scripts": {
60
60
  "build-pkg": "tsc",
61
61
  "test-pkg": "jest .",
62
- "publish-patch": "npm version patch --force && npm publish",
62
+ "publish-patch": "npm publish",
63
63
  "clean-pkg": "rm -rf ./dist && rm -rf ./node_modules && rm -f ./tsconfig.tsbuildinfo"
64
64
  },
65
65
  "bugs": {