Skip to content

Commit 3a2062f

Browse files
committed
GH-570 Custom inclusive/exclusive filter for CPU Sampler
1 parent ab57b64 commit 3a2062f

File tree

2 files changed

+90
-14
lines changed

2 files changed

+90
-14
lines changed

integrations/vscode/src/parameters.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,14 @@ export async function resolveSamplingFilter(samplingFilter?: string, workspaceFo
211211
return undefined;
212212
}
213213
}
214-
default: // include all classes
215-
return `${CPU_SAMPLER_FILTER_INCLUSIVE}=`;
214+
default:
215+
if (samplingFilter?.startsWith(CPU_SAMPLER_FILTER_EXCLUSIVE + ':')) { // exclude custom classes
216+
const filter = samplingFilter.substring(CPU_SAMPLER_FILTER_EXCLUSIVE.length + 1);
217+
return `${CPU_SAMPLER_FILTER_EXCLUSIVE}=${encode(filter)}`;
218+
} else { // include custom or all classes
219+
const filter = samplingFilter?.startsWith(CPU_SAMPLER_FILTER_INCLUSIVE + ':') ? samplingFilter.substring(CPU_SAMPLER_FILTER_INCLUSIVE.length + 1) : '';
220+
return `${CPU_SAMPLER_FILTER_INCLUSIVE}=${encode(filter)}`;
221+
}
216222
}
217223
}
218224

@@ -266,11 +272,13 @@ export function vmArgDisplayName(displayName: string, includePid: boolean = true
266272

267273

268274
export function encode(text: string | undefined): string {
269-
if (!text) return 'undefined';
270-
text = text.replace(/\'/g, '%27');
271-
text = text.replace(/\"/g, '%22');
272-
text = text.replace(/\s/g, '%20');
273-
text = text.replace( /,/g, '%2C');
275+
if (text === undefined) return 'undefined';
276+
if (text.length) {
277+
text = text.replace(/\'/g, '%27');
278+
text = text.replace(/\"/g, '%22');
279+
text = text.replace(/\s/g, '%20');
280+
text = text.replace( /,/g, '%2C');
281+
}
274282
return text;
275283
}
276284

integrations/vscode/src/presets.ts

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,19 +97,27 @@ export abstract class Presets {
9797
return this.presets[this.selectedPreset][2];
9898
}
9999

100-
protected setSelected(preset: number): boolean {
101-
if (this.selectedPreset !== preset) {
100+
protected getSelectedPreset(): number {
101+
return this.selectedPreset;
102+
}
103+
104+
protected async setSelected(preset: number, forceSelected: boolean = false): Promise<boolean | undefined> {
105+
if (forceSelected || this.selectedPreset !== preset) {
102106
this.selectedPreset = preset;
103-
if (this.storage && this.persistenceKey) {
104-
this.storage.update(this.persistenceKey, this.selectedPreset);
105-
}
107+
await this.savePersistent(this.persistenceKey, this.selectedPreset);
106108
this.notifyChanged();
107109
return true;
108110
} else {
109111
return false;
110112
}
111113
}
112114

115+
protected async savePersistent(key: string | undefined, value: any): Promise<void> {
116+
if (this.storage && key) {
117+
await this.storage.update(key, value);
118+
}
119+
}
120+
113121
private readonly listeners: OnChanged[] = [];
114122

115123
onChanged(listener: OnChanged) {
@@ -158,28 +166,88 @@ export class WhenStartedPresets extends Presets {
158166
export class CpuSamplerFilterPresets extends Presets {
159167

160168
private static PERSISTENCE_KEY = 'visualvm.presets.CpuSamplerFilter';
169+
private static PERSISTENCE_KEY_CPU_SAMPLER_FILTER_CUSTOM_INCLUSIVE = 'visualvm.presets.CpuSamplerFilterCustomInclusive';
170+
private static PERSISTENCE_KEY_CPU_SAMPLER_FILTER_CUSTOM_EXCLUSIVE = 'visualvm.presets.CpuSamplerFilterCustomExclusive';
161171
private static NAME = 'CPU Sampling Filter';
162172
private static SELECT_PROMPT = 'Select CPU sampling filter';
163173
private static PRESETS = [
164174
[ 'Include All Classes', 'Collects data from all classes', '' ],
165175
[ 'Exclude JDK Classes', 'Excludes data from JDK classes (java.*, com.sun.*, org.graalvm.*, etc.)', parameters.CPU_SAMPLER_FILTER_EXCLUSIVE ],
166-
[ 'Include Only Project Classes', 'Collects data only from project classes', parameters.CPU_SAMPLER_FILTER_INCLUSIVE ]
176+
[ 'Include Only Project Classes', 'Collects data only from project classes', parameters.CPU_SAMPLER_FILTER_INCLUSIVE ],
177+
[ 'Include Only Defined Classes', 'Collects data only from user defined classes', parameters.CPU_SAMPLER_FILTER_INCLUSIVE ],
178+
[ 'Exclude Defined Classes', 'Excludes data from user defined classes', parameters.CPU_SAMPLER_FILTER_EXCLUSIVE ]
167179
];
168180
private static INITIAL_PRESET = 0;
169181
private static SINGLE_ROW_CHOICES = false;
170182

171183
static PERSISTENT = new CpuSamplerFilterPresets();
172184

185+
private customInclusiveFilter: string = '*';
186+
private customExclusiveFilter: string = '*';
187+
173188
constructor() {
174189
super(CpuSamplerFilterPresets.NAME, CpuSamplerFilterPresets.PRESETS, CpuSamplerFilterPresets.INITIAL_PRESET, CpuSamplerFilterPresets.SELECT_PROMPT, CpuSamplerFilterPresets.SINGLE_ROW_CHOICES);
175190
}
176191

177192
initializePersistent(context: vscode.ExtensionContext) {
178193
this.doInitializePersistent(context.workspaceState, CpuSamplerFilterPresets.PERSISTENCE_KEY);
194+
this.customInclusiveFilter = context.workspaceState.get(CpuSamplerFilterPresets.PERSISTENCE_KEY_CPU_SAMPLER_FILTER_CUSTOM_INCLUSIVE, '*');
195+
this.customExclusiveFilter = context.workspaceState.get(CpuSamplerFilterPresets.PERSISTENCE_KEY_CPU_SAMPLER_FILTER_CUSTOM_EXCLUSIVE, '*');
196+
}
197+
198+
protected async setSelected(preset: number): Promise<boolean | undefined> {
199+
if (preset >= 3) {
200+
function validateFilter(filter: string): string | undefined {
201+
if (!filter.length) {
202+
return 'Filter cannot be empty';
203+
}
204+
// TODO: validate properly
205+
return undefined;
206+
}
207+
const newValue = await vscode.window.showInputBox({
208+
title: CpuSamplerFilterPresets.PRESETS[preset][0],
209+
value: preset === 3 ? this.customInclusiveFilter : this.customExclusiveFilter,
210+
placeHolder: 'Define CPU sampling filter',
211+
prompt: 'Format: org.pkg.**, org.pkg.*, org.pkg.Class',
212+
validateInput: filter => validateFilter(filter)
213+
});
214+
if (newValue) {
215+
if (preset === 3) {
216+
this.customInclusiveFilter = newValue.trim();
217+
await this.savePersistent(CpuSamplerFilterPresets.PERSISTENCE_KEY_CPU_SAMPLER_FILTER_CUSTOM_INCLUSIVE, this.customInclusiveFilter);
218+
} else if (preset === 4) {
219+
this.customExclusiveFilter = newValue.trim();
220+
await this.savePersistent(CpuSamplerFilterPresets.PERSISTENCE_KEY_CPU_SAMPLER_FILTER_CUSTOM_EXCLUSIVE, this.customExclusiveFilter);
221+
}
222+
return super.setSelected(preset, true);
223+
} else {
224+
return undefined;
225+
}
226+
} else {
227+
return super.setSelected(preset);
228+
}
179229
}
180230

181231
getSelectedString(): string {
182-
return super.getSelectedString().replace(/ jdk /g, ' JDK ');
232+
switch (this.getSelectedPreset()) {
233+
case 3:
234+
return `include ${this.customInclusiveFilter}`;
235+
case 4:
236+
return `exclude ${this.customExclusiveFilter}`;
237+
default:
238+
return super.getSelectedString().replace(/ jdk /g, ' JDK ');
239+
}
240+
}
241+
242+
getSelectedValue(): string {
243+
switch (this.getSelectedPreset()) {
244+
case 3:
245+
return `${parameters.CPU_SAMPLER_FILTER_INCLUSIVE}:${this.customInclusiveFilter}`;
246+
case 4:
247+
return `${parameters.CPU_SAMPLER_FILTER_EXCLUSIVE}:${this.customExclusiveFilter}`;
248+
default:
249+
return super.getSelectedValue();
250+
}
183251
}
184252

185253
}

0 commit comments

Comments
 (0)