Path: blob/main/src/vs/platform/files/node/watcher/watcherStats.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { INonRecursiveWatchRequest, IRecursiveWatchRequest, isRecursiveWatchRequest, IUniversalWatchRequest, requestFilterToString } from '../../common/watcher.js';6import { INodeJSWatcherInstance, NodeJSWatcher } from './nodejs/nodejsWatcher.js';7import { ParcelWatcher, ParcelWatcherInstance } from './parcel/parcelWatcher.js';89export function computeStats(10requests: IUniversalWatchRequest[],11failedRecursiveRequests: number,12recursiveWatcher: ParcelWatcher,13nonRecursiveWatcher: NodeJSWatcher14): string {15const lines: string[] = [];1617const allRecursiveRequests = sortByPathPrefix(requests.filter(request => isRecursiveWatchRequest(request)));18const nonSuspendedRecursiveRequests = allRecursiveRequests.filter(request => recursiveWatcher.isSuspended(request) === false);19const suspendedPollingRecursiveRequests = allRecursiveRequests.filter(request => recursiveWatcher.isSuspended(request) === 'polling');20const suspendedNonPollingRecursiveRequests = allRecursiveRequests.filter(request => recursiveWatcher.isSuspended(request) === true);2122const recursiveRequestsStatus = computeRequestStatus(allRecursiveRequests, recursiveWatcher);23const recursiveWatcherStatus = computeRecursiveWatchStatus(recursiveWatcher);2425const allNonRecursiveRequests = sortByPathPrefix(requests.filter(request => !isRecursiveWatchRequest(request)));26const nonSuspendedNonRecursiveRequests = allNonRecursiveRequests.filter(request => nonRecursiveWatcher.isSuspended(request) === false);27const suspendedPollingNonRecursiveRequests = allNonRecursiveRequests.filter(request => nonRecursiveWatcher.isSuspended(request) === 'polling');28const suspendedNonPollingNonRecursiveRequests = allNonRecursiveRequests.filter(request => nonRecursiveWatcher.isSuspended(request) === true);2930const nonRecursiveRequestsStatus = computeRequestStatus(allNonRecursiveRequests, nonRecursiveWatcher);31const nonRecursiveWatcherStatus = computeNonRecursiveWatchStatus(nonRecursiveWatcher);3233lines.push('[Summary]');34lines.push(`- Recursive Requests: total: ${allRecursiveRequests.length}, suspended: ${recursiveRequestsStatus.suspended}, polling: ${recursiveRequestsStatus.polling}, failed: ${failedRecursiveRequests}`);35lines.push(`- Non-Recursive Requests: total: ${allNonRecursiveRequests.length}, suspended: ${nonRecursiveRequestsStatus.suspended}, polling: ${nonRecursiveRequestsStatus.polling}`);36lines.push(`- Recursive Watchers: total: ${Array.from(recursiveWatcher.watchers).length}, active: ${recursiveWatcherStatus.active}, failed: ${recursiveWatcherStatus.failed}, stopped: ${recursiveWatcherStatus.stopped}`);37lines.push(`- Non-Recursive Watchers: total: ${Array.from(nonRecursiveWatcher.watchers).length}, active: ${nonRecursiveWatcherStatus.active}, failed: ${nonRecursiveWatcherStatus.failed}, reusing: ${nonRecursiveWatcherStatus.reusing}`);38lines.push(`- I/O Handles Impact: total: ${recursiveRequestsStatus.polling + nonRecursiveRequestsStatus.polling + recursiveWatcherStatus.active + nonRecursiveWatcherStatus.active}`);3940lines.push(`\n[Recursive Requests (${allRecursiveRequests.length}, suspended: ${recursiveRequestsStatus.suspended}, polling: ${recursiveRequestsStatus.polling})]:`);41const recursiveRequestLines: string[] = [];42for (const request of [nonSuspendedRecursiveRequests, suspendedPollingRecursiveRequests, suspendedNonPollingRecursiveRequests].flat()) {43fillRequestStats(recursiveRequestLines, request, recursiveWatcher);44}45lines.push(...alignTextColumns(recursiveRequestLines));4647const recursiveWatcheLines: string[] = [];48fillRecursiveWatcherStats(recursiveWatcheLines, recursiveWatcher);49lines.push(...alignTextColumns(recursiveWatcheLines));5051lines.push(`\n[Non-Recursive Requests (${allNonRecursiveRequests.length}, suspended: ${nonRecursiveRequestsStatus.suspended}, polling: ${nonRecursiveRequestsStatus.polling})]:`);52const nonRecursiveRequestLines: string[] = [];53for (const request of [nonSuspendedNonRecursiveRequests, suspendedPollingNonRecursiveRequests, suspendedNonPollingNonRecursiveRequests].flat()) {54fillRequestStats(nonRecursiveRequestLines, request, nonRecursiveWatcher);55}56lines.push(...alignTextColumns(nonRecursiveRequestLines));5758const nonRecursiveWatcheLines: string[] = [];59fillNonRecursiveWatcherStats(nonRecursiveWatcheLines, nonRecursiveWatcher);60lines.push(...alignTextColumns(nonRecursiveWatcheLines));6162return `\n\n[File Watcher] request stats:\n\n${lines.join('\n')}\n\n`;63}6465function alignTextColumns(lines: string[]) {66let maxLength = 0;67for (const line of lines) {68maxLength = Math.max(maxLength, line.split('\t')[0].length);69}7071for (let i = 0; i < lines.length; i++) {72const line = lines[i];73const parts = line.split('\t');74if (parts.length === 2) {75const padding = ' '.repeat(maxLength - parts[0].length);76lines[i] = `${parts[0]}${padding}\t${parts[1]}`;77}78}7980return lines;81}8283function computeRequestStatus(requests: IUniversalWatchRequest[], watcher: ParcelWatcher | NodeJSWatcher): { suspended: number; polling: number } {84let polling = 0;85let suspended = 0;8687for (const request of requests) {88const isSuspended = watcher.isSuspended(request);89if (isSuspended === false) {90continue;91}9293suspended++;9495if (isSuspended === 'polling') {96polling++;97}98}99100return { suspended, polling };101}102103function computeRecursiveWatchStatus(recursiveWatcher: ParcelWatcher): { active: number; failed: number; stopped: number } {104let active = 0;105let failed = 0;106let stopped = 0;107108for (const watcher of recursiveWatcher.watchers) {109if (!watcher.failed && !watcher.stopped) {110active++;111}112if (watcher.failed) {113failed++;114}115if (watcher.stopped) {116stopped++;117}118}119120return { active, failed, stopped };121}122123function computeNonRecursiveWatchStatus(nonRecursiveWatcher: NodeJSWatcher): { active: number; failed: number; reusing: number } {124let active = 0;125let failed = 0;126let reusing = 0;127128for (const watcher of nonRecursiveWatcher.watchers) {129if (!watcher.instance.failed && !watcher.instance.isReusingRecursiveWatcher) {130active++;131}132if (watcher.instance.failed) {133failed++;134}135if (watcher.instance.isReusingRecursiveWatcher) {136reusing++;137}138}139140return { active, failed, reusing };141}142143function sortByPathPrefix(requests: IRecursiveWatchRequest[]): IRecursiveWatchRequest[];144function sortByPathPrefix(requests: INonRecursiveWatchRequest[]): INonRecursiveWatchRequest[];145function sortByPathPrefix(requests: IUniversalWatchRequest[]): IUniversalWatchRequest[];146function sortByPathPrefix(requests: INodeJSWatcherInstance[]): INodeJSWatcherInstance[];147function sortByPathPrefix(requests: ParcelWatcherInstance[]): ParcelWatcherInstance[];148function sortByPathPrefix(requests: IUniversalWatchRequest[] | INodeJSWatcherInstance[] | ParcelWatcherInstance[]): IUniversalWatchRequest[] | INodeJSWatcherInstance[] | ParcelWatcherInstance[] {149requests.sort((r1, r2) => {150const p1 = isUniversalWatchRequest(r1) ? r1.path : r1.request.path;151const p2 = isUniversalWatchRequest(r2) ? r2.path : r2.request.path;152153const minLength = Math.min(p1.length, p2.length);154for (let i = 0; i < minLength; i++) {155if (p1[i] !== p2[i]) {156return (p1[i] < p2[i]) ? -1 : 1;157}158}159160return p1.length - p2.length;161});162163return requests;164}165166function isUniversalWatchRequest(obj: unknown): obj is IUniversalWatchRequest {167const candidate = obj as IUniversalWatchRequest | undefined;168169return typeof candidate?.path === 'string';170}171172function fillRequestStats(lines: string[], request: IUniversalWatchRequest, watcher: ParcelWatcher | NodeJSWatcher): void {173const decorations = [];174const suspended = watcher.isSuspended(request);175if (suspended !== false) {176if (suspended === 'polling') {177decorations.push('[SUSPENDED <polling>]');178} else {179decorations.push('[SUSPENDED <non-polling>]');180}181}182183lines.push(` ${request.path}\t${decorations.length > 0 ? decorations.join(' ') + ' ' : ''}(${requestDetailsToString(request)})`);184}185186function requestDetailsToString(request: IUniversalWatchRequest): string {187return `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>'}`;188}189190function fillRecursiveWatcherStats(lines: string[], recursiveWatcher: ParcelWatcher): void {191const watchers = sortByPathPrefix(Array.from(recursiveWatcher.watchers));192193const { active, failed, stopped } = computeRecursiveWatchStatus(recursiveWatcher);194lines.push(`\n[Recursive Watchers (${watchers.length}, active: ${active}, failed: ${failed}, stopped: ${stopped})]:`);195196for (const watcher of watchers) {197const decorations = [];198if (watcher.failed) {199decorations.push('[FAILED]');200}201if (watcher.stopped) {202decorations.push('[STOPPED]');203}204if (watcher.subscriptionsCount > 0) {205decorations.push(`[SUBSCRIBED:${watcher.subscriptionsCount}]`);206}207if (watcher.restarts > 0) {208decorations.push(`[RESTARTED:${watcher.restarts}]`);209}210lines.push(` ${watcher.request.path}\t${decorations.length > 0 ? decorations.join(' ') + ' ' : ''}(${requestDetailsToString(watcher.request)})`);211}212}213214function fillNonRecursiveWatcherStats(lines: string[], nonRecursiveWatcher: NodeJSWatcher): void {215const allWatchers = sortByPathPrefix(Array.from(nonRecursiveWatcher.watchers));216const activeWatchers = allWatchers.filter(watcher => !watcher.instance.failed && !watcher.instance.isReusingRecursiveWatcher);217const failedWatchers = allWatchers.filter(watcher => watcher.instance.failed);218const reusingWatchers = allWatchers.filter(watcher => watcher.instance.isReusingRecursiveWatcher);219220const { active, failed, reusing } = computeNonRecursiveWatchStatus(nonRecursiveWatcher);221lines.push(`\n[Non-Recursive Watchers (${allWatchers.length}, active: ${active}, failed: ${failed}, reusing: ${reusing})]:`);222223for (const watcher of [activeWatchers, failedWatchers, reusingWatchers].flat()) {224const decorations = [];225if (watcher.instance.failed) {226decorations.push('[FAILED]');227}228if (watcher.instance.isReusingRecursiveWatcher) {229decorations.push('[REUSING]');230}231lines.push(` ${watcher.request.path}\t${decorations.length > 0 ? decorations.join(' ') + ' ' : ''}(${requestDetailsToString(watcher.request)})`);232}233}234235236