Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/files/node/watcher/watcherStats.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { INonRecursiveWatchRequest, IRecursiveWatchRequest, isRecursiveWatchRequest, IUniversalWatchRequest, requestFilterToString } from '../../common/watcher.js';
7
import { INodeJSWatcherInstance, NodeJSWatcher } from './nodejs/nodejsWatcher.js';
8
import { ParcelWatcher, ParcelWatcherInstance } from './parcel/parcelWatcher.js';
9
10
export function computeStats(
11
requests: IUniversalWatchRequest[],
12
failedRecursiveRequests: number,
13
recursiveWatcher: ParcelWatcher,
14
nonRecursiveWatcher: NodeJSWatcher
15
): string {
16
const lines: string[] = [];
17
18
const allRecursiveRequests = sortByPathPrefix(requests.filter(request => isRecursiveWatchRequest(request)));
19
const nonSuspendedRecursiveRequests = allRecursiveRequests.filter(request => recursiveWatcher.isSuspended(request) === false);
20
const suspendedPollingRecursiveRequests = allRecursiveRequests.filter(request => recursiveWatcher.isSuspended(request) === 'polling');
21
const suspendedNonPollingRecursiveRequests = allRecursiveRequests.filter(request => recursiveWatcher.isSuspended(request) === true);
22
23
const recursiveRequestsStatus = computeRequestStatus(allRecursiveRequests, recursiveWatcher);
24
const recursiveWatcherStatus = computeRecursiveWatchStatus(recursiveWatcher);
25
26
const allNonRecursiveRequests = sortByPathPrefix(requests.filter(request => !isRecursiveWatchRequest(request)));
27
const nonSuspendedNonRecursiveRequests = allNonRecursiveRequests.filter(request => nonRecursiveWatcher.isSuspended(request) === false);
28
const suspendedPollingNonRecursiveRequests = allNonRecursiveRequests.filter(request => nonRecursiveWatcher.isSuspended(request) === 'polling');
29
const suspendedNonPollingNonRecursiveRequests = allNonRecursiveRequests.filter(request => nonRecursiveWatcher.isSuspended(request) === true);
30
31
const nonRecursiveRequestsStatus = computeRequestStatus(allNonRecursiveRequests, nonRecursiveWatcher);
32
const nonRecursiveWatcherStatus = computeNonRecursiveWatchStatus(nonRecursiveWatcher);
33
34
lines.push('[Summary]');
35
lines.push(`- Recursive Requests: total: ${allRecursiveRequests.length}, suspended: ${recursiveRequestsStatus.suspended}, polling: ${recursiveRequestsStatus.polling}, failed: ${failedRecursiveRequests}`);
36
lines.push(`- Non-Recursive Requests: total: ${allNonRecursiveRequests.length}, suspended: ${nonRecursiveRequestsStatus.suspended}, polling: ${nonRecursiveRequestsStatus.polling}`);
37
lines.push(`- Recursive Watchers: total: ${Array.from(recursiveWatcher.watchers).length}, active: ${recursiveWatcherStatus.active}, failed: ${recursiveWatcherStatus.failed}, stopped: ${recursiveWatcherStatus.stopped}`);
38
lines.push(`- Non-Recursive Watchers: total: ${Array.from(nonRecursiveWatcher.watchers).length}, active: ${nonRecursiveWatcherStatus.active}, failed: ${nonRecursiveWatcherStatus.failed}, reusing: ${nonRecursiveWatcherStatus.reusing}`);
39
lines.push(`- I/O Handles Impact: total: ${recursiveRequestsStatus.polling + nonRecursiveRequestsStatus.polling + recursiveWatcherStatus.active + nonRecursiveWatcherStatus.active}`);
40
41
lines.push(`\n[Recursive Requests (${allRecursiveRequests.length}, suspended: ${recursiveRequestsStatus.suspended}, polling: ${recursiveRequestsStatus.polling})]:`);
42
const recursiveRequestLines: string[] = [];
43
for (const request of [nonSuspendedRecursiveRequests, suspendedPollingRecursiveRequests, suspendedNonPollingRecursiveRequests].flat()) {
44
fillRequestStats(recursiveRequestLines, request, recursiveWatcher);
45
}
46
lines.push(...alignTextColumns(recursiveRequestLines));
47
48
const recursiveWatcheLines: string[] = [];
49
fillRecursiveWatcherStats(recursiveWatcheLines, recursiveWatcher);
50
lines.push(...alignTextColumns(recursiveWatcheLines));
51
52
lines.push(`\n[Non-Recursive Requests (${allNonRecursiveRequests.length}, suspended: ${nonRecursiveRequestsStatus.suspended}, polling: ${nonRecursiveRequestsStatus.polling})]:`);
53
const nonRecursiveRequestLines: string[] = [];
54
for (const request of [nonSuspendedNonRecursiveRequests, suspendedPollingNonRecursiveRequests, suspendedNonPollingNonRecursiveRequests].flat()) {
55
fillRequestStats(nonRecursiveRequestLines, request, nonRecursiveWatcher);
56
}
57
lines.push(...alignTextColumns(nonRecursiveRequestLines));
58
59
const nonRecursiveWatcheLines: string[] = [];
60
fillNonRecursiveWatcherStats(nonRecursiveWatcheLines, nonRecursiveWatcher);
61
lines.push(...alignTextColumns(nonRecursiveWatcheLines));
62
63
return `\n\n[File Watcher] request stats:\n\n${lines.join('\n')}\n\n`;
64
}
65
66
function alignTextColumns(lines: string[]) {
67
let maxLength = 0;
68
for (const line of lines) {
69
maxLength = Math.max(maxLength, line.split('\t')[0].length);
70
}
71
72
for (let i = 0; i < lines.length; i++) {
73
const line = lines[i];
74
const parts = line.split('\t');
75
if (parts.length === 2) {
76
const padding = ' '.repeat(maxLength - parts[0].length);
77
lines[i] = `${parts[0]}${padding}\t${parts[1]}`;
78
}
79
}
80
81
return lines;
82
}
83
84
function computeRequestStatus(requests: IUniversalWatchRequest[], watcher: ParcelWatcher | NodeJSWatcher): { suspended: number; polling: number } {
85
let polling = 0;
86
let suspended = 0;
87
88
for (const request of requests) {
89
const isSuspended = watcher.isSuspended(request);
90
if (isSuspended === false) {
91
continue;
92
}
93
94
suspended++;
95
96
if (isSuspended === 'polling') {
97
polling++;
98
}
99
}
100
101
return { suspended, polling };
102
}
103
104
function computeRecursiveWatchStatus(recursiveWatcher: ParcelWatcher): { active: number; failed: number; stopped: number } {
105
let active = 0;
106
let failed = 0;
107
let stopped = 0;
108
109
for (const watcher of recursiveWatcher.watchers) {
110
if (!watcher.failed && !watcher.stopped) {
111
active++;
112
}
113
if (watcher.failed) {
114
failed++;
115
}
116
if (watcher.stopped) {
117
stopped++;
118
}
119
}
120
121
return { active, failed, stopped };
122
}
123
124
function computeNonRecursiveWatchStatus(nonRecursiveWatcher: NodeJSWatcher): { active: number; failed: number; reusing: number } {
125
let active = 0;
126
let failed = 0;
127
let reusing = 0;
128
129
for (const watcher of nonRecursiveWatcher.watchers) {
130
if (!watcher.instance.failed && !watcher.instance.isReusingRecursiveWatcher) {
131
active++;
132
}
133
if (watcher.instance.failed) {
134
failed++;
135
}
136
if (watcher.instance.isReusingRecursiveWatcher) {
137
reusing++;
138
}
139
}
140
141
return { active, failed, reusing };
142
}
143
144
function sortByPathPrefix(requests: IRecursiveWatchRequest[]): IRecursiveWatchRequest[];
145
function sortByPathPrefix(requests: INonRecursiveWatchRequest[]): INonRecursiveWatchRequest[];
146
function sortByPathPrefix(requests: IUniversalWatchRequest[]): IUniversalWatchRequest[];
147
function sortByPathPrefix(requests: INodeJSWatcherInstance[]): INodeJSWatcherInstance[];
148
function sortByPathPrefix(requests: ParcelWatcherInstance[]): ParcelWatcherInstance[];
149
function sortByPathPrefix(requests: IUniversalWatchRequest[] | INodeJSWatcherInstance[] | ParcelWatcherInstance[]): IUniversalWatchRequest[] | INodeJSWatcherInstance[] | ParcelWatcherInstance[] {
150
requests.sort((r1, r2) => {
151
const p1 = isUniversalWatchRequest(r1) ? r1.path : r1.request.path;
152
const p2 = isUniversalWatchRequest(r2) ? r2.path : r2.request.path;
153
154
const minLength = Math.min(p1.length, p2.length);
155
for (let i = 0; i < minLength; i++) {
156
if (p1[i] !== p2[i]) {
157
return (p1[i] < p2[i]) ? -1 : 1;
158
}
159
}
160
161
return p1.length - p2.length;
162
});
163
164
return requests;
165
}
166
167
function isUniversalWatchRequest(obj: unknown): obj is IUniversalWatchRequest {
168
const candidate = obj as IUniversalWatchRequest | undefined;
169
170
return typeof candidate?.path === 'string';
171
}
172
173
function fillRequestStats(lines: string[], request: IUniversalWatchRequest, watcher: ParcelWatcher | NodeJSWatcher): void {
174
const decorations = [];
175
const suspended = watcher.isSuspended(request);
176
if (suspended !== false) {
177
if (suspended === 'polling') {
178
decorations.push('[SUSPENDED <polling>]');
179
} else {
180
decorations.push('[SUSPENDED <non-polling>]');
181
}
182
}
183
184
lines.push(` ${request.path}\t${decorations.length > 0 ? decorations.join(' ') + ' ' : ''}(${requestDetailsToString(request)})`);
185
}
186
187
function requestDetailsToString(request: IUniversalWatchRequest): string {
188
return `excludes: ${request.excludes.length > 0 ? request.excludes : '<none>'}, includes: ${request.includes && request.includes.length > 0 ? JSON.stringify(request.includes) : '<all>'}, filter: ${requestFilterToString(request.filter)}, correlationId: ${typeof request.correlationId === 'number' ? request.correlationId : '<none>'}`;
189
}
190
191
function fillRecursiveWatcherStats(lines: string[], recursiveWatcher: ParcelWatcher): void {
192
const watchers = sortByPathPrefix(Array.from(recursiveWatcher.watchers));
193
194
const { active, failed, stopped } = computeRecursiveWatchStatus(recursiveWatcher);
195
lines.push(`\n[Recursive Watchers (${watchers.length}, active: ${active}, failed: ${failed}, stopped: ${stopped})]:`);
196
197
for (const watcher of watchers) {
198
const decorations = [];
199
if (watcher.failed) {
200
decorations.push('[FAILED]');
201
}
202
if (watcher.stopped) {
203
decorations.push('[STOPPED]');
204
}
205
if (watcher.subscriptionsCount > 0) {
206
decorations.push(`[SUBSCRIBED:${watcher.subscriptionsCount}]`);
207
}
208
if (watcher.restarts > 0) {
209
decorations.push(`[RESTARTED:${watcher.restarts}]`);
210
}
211
lines.push(` ${watcher.request.path}\t${decorations.length > 0 ? decorations.join(' ') + ' ' : ''}(${requestDetailsToString(watcher.request)})`);
212
}
213
}
214
215
function fillNonRecursiveWatcherStats(lines: string[], nonRecursiveWatcher: NodeJSWatcher): void {
216
const allWatchers = sortByPathPrefix(Array.from(nonRecursiveWatcher.watchers));
217
const activeWatchers = allWatchers.filter(watcher => !watcher.instance.failed && !watcher.instance.isReusingRecursiveWatcher);
218
const failedWatchers = allWatchers.filter(watcher => watcher.instance.failed);
219
const reusingWatchers = allWatchers.filter(watcher => watcher.instance.isReusingRecursiveWatcher);
220
221
const { active, failed, reusing } = computeNonRecursiveWatchStatus(nonRecursiveWatcher);
222
lines.push(`\n[Non-Recursive Watchers (${allWatchers.length}, active: ${active}, failed: ${failed}, reusing: ${reusing})]:`);
223
224
for (const watcher of [activeWatchers, failedWatchers, reusingWatchers].flat()) {
225
const decorations = [];
226
if (watcher.instance.failed) {
227
decorations.push('[FAILED]');
228
}
229
if (watcher.instance.isReusingRecursiveWatcher) {
230
decorations.push('[REUSING]');
231
}
232
lines.push(` ${watcher.request.path}\t${decorations.length > 0 ? decorations.join(' ') + ' ' : ''}(${requestDetailsToString(watcher.request)})`);
233
}
234
}
235
236