Skip to content

Commit 54dd73f

Browse files
committed
adding performance info for methods
1 parent caab6d9 commit 54dd73f

File tree

6 files changed

+199
-8
lines changed

6 files changed

+199
-8
lines changed

src/extension.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { EventManager } from './services/EventManager';
2323
import { Scheduler } from './services/Scheduler';
2424
import { DocumentInfoCache } from './services/DocumentInfoCache';
2525
import { SpanLinkResolver } from './services/spanLinkResolver';
26+
import { PerformanceDecorator } from './views/codeAnalytics/decorators/performanceDecorator';
2627

2728
export async function activate(context: vscode.ExtensionContext)
2829
{
@@ -61,12 +62,13 @@ export async function activate(context: vscode.ExtensionContext)
6162

6263
context.subscriptions.push(codeLensProvider);
6364
//context.subscriptions.push(new ContextView(analyticsProvider, context.extensionUri));
64-
context.subscriptions.push(new MethodCallErrorTooltip(documentInfoProvider, codeInspector));
65+
context.subscriptions.push(new MethodCallErrorTooltip(documentInfoProvider, codeInspector,workspaceState));
6566
context.subscriptions.push(sourceControl);
6667
context.subscriptions.push(documentInfoProvider);
6768
context.subscriptions.push(new CodeAnalyticsView(analyticsProvider, documentInfoProvider,
6869
context.extensionUri, editorHelper, workspaceState, codeLensProvider, envStatusbar, environmentManager,spanLinkResolver, documentInfoCache));
6970
context.subscriptions.push(new ErrorsLineDecorator(documentInfoProvider));
71+
context.subscriptions.push(new PerformanceDecorator(documentInfoProvider));
7072
context.subscriptions.push(new HotspotMarkerDecorator(documentInfoProvider));
7173
context.subscriptions.push(new VsCodeDebugInstrumentation(analyticsProvider));
7274

src/services/analyticsProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ export class AnalyticsProvider {
361361
}
362362

363363
public async getCodeObjectsErrors(codeObjectIds: string[]): Promise<CodeObjectErrorResponse[]> {
364-
let params: [string, any][] = [["environment", this.state.environment]];
364+
let params: [string, any][ ] = [["environment", this.state.environment]];
365365
codeObjectIds.forEach(o => params.push(["codeObjectId", o]));
366366

367367
const response = await this.send<CodeObjectErrorResponse[]>(

src/services/codeInspector.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as vscode from 'vscode';
22

3-
import { DocumentInfoProvider, MethodInfo } from './documentInfoProvider';
3+
import { DocumentInfo, DocumentInfoProvider, MethodInfo } from './documentInfoProvider';
44
import { SymbolProvider, SymbolTree } from './languages/symbolProvider';
55
import { Token, TokenType } from './languages/tokens';
66

@@ -32,6 +32,19 @@ export class CodeInspector {
3232
return methodInfo;
3333
}
3434

35+
public async getDocumentInfo(
36+
usageDocument: vscode.TextDocument,
37+
usagePosition: vscode.Position,
38+
documentInfoProvider: DocumentInfoProvider,
39+
): Promise<DocumentInfo | undefined> {
40+
const definition = await this.getDefinition(usageDocument, usagePosition);
41+
if (!definition)
42+
return;
43+
44+
const docInfo = await documentInfoProvider.getDocumentInfo(definition.document);
45+
return docInfo;
46+
}
47+
3548
public async getDefinitionWithTokens(
3649
usageDocument: vscode.TextDocument,
3750
usagePosition: vscode.Position,

src/services/methodCallErrorTooltip.ts

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import * as vscode from 'vscode';
22
import { ErrorsViewTab } from '../views/codeAnalytics/errorsViewTab';
3-
import { DocumentInfoProvider, MethodInfo } from './documentInfoProvider';
3+
import { DocumentInfo, DocumentInfoProvider, MethodInfo } from './documentInfoProvider';
44
import { TokenType } from './languages/tokens';
55
import { CodeInspector } from './codeInspector';
6+
import { WorkspaceState } from '../state';
7+
import { SpanDurationsInsight } from '../views/codeAnalytics/InsightListView/SpanInsight';
68

79
export class MethodCallErrorTooltip implements vscode.Disposable
810
{
@@ -14,10 +16,11 @@ export class MethodCallErrorTooltip implements vscode.Disposable
1416
constructor(
1517
documentInfoProvider: DocumentInfoProvider,
1618
codeInspector: CodeInspector,
19+
workspaceState: WorkspaceState
1720
) {
1821
this._disposables.push(vscode.languages.registerHoverProvider(
1922
documentInfoProvider.symbolProvider.languageExtractors.map(x => x.documentFilter),
20-
new MethodCallErrorHoverProvider(documentInfoProvider, codeInspector))
23+
new MethodCallErrorHoverProvider(documentInfoProvider, codeInspector,workspaceState))
2124
);
2225
this._disposables.push(vscode.commands.registerCommand(MethodCallErrorTooltip.Commands.ShowErrorView, async (args) => {
2326
await vscode.commands.executeCommand(ErrorsViewTab.Commands.ShowErrorView, args.codeObjectId, args.codeObjectDisplayName, args.errorFlowId);
@@ -36,6 +39,7 @@ class MethodCallErrorHoverProvider implements vscode.HoverProvider
3639
constructor(
3740
private _documentInfoProvider: DocumentInfoProvider,
3841
private _codeInspector: CodeInspector,
42+
private _workspaceState: WorkspaceState
3943
) {
4044
}
4145

@@ -46,15 +50,24 @@ class MethodCallErrorHoverProvider implements vscode.HoverProvider
4650
return;
4751

4852
let methodInfo: MethodInfo | undefined = sourceDocInfo?.methods.firstOrDefault((m) => m.nameRange?.contains(position) ?? false);
53+
let remoteDoc: DocumentInfo | undefined = undefined;
4954
if(!methodInfo){
5055
if(!sourceDocInfo.tokens.any(t => (t.type == TokenType.function || t.type == TokenType.method) && t.range.contains(position)))
5156
return;
5257
methodInfo = await this._codeInspector.getExecuteDefinitionMethodInfo(document, position, this._documentInfoProvider);
5358
if(!methodInfo) {
5459
return;
5560
}
61+
remoteDoc = await this._codeInspector.getDocumentInfo(document, position, this._documentInfoProvider);
62+
if(!methodInfo) {
63+
return;
64+
}
65+
66+
}
67+
if (!remoteDoc){
68+
return;
5669
}
57-
const markdown = await this.getMethodMarkdown(methodInfo);
70+
const markdown = await this.getMethodMarkdown(methodInfo,remoteDoc);
5871
if(markdown)
5972
{
6073
return new vscode.Hover(markdown);
@@ -79,13 +92,48 @@ class MethodCallErrorHoverProvider implements vscode.HoverProvider
7992
//return new vscode.Hover(markdown);
8093
}
8194

82-
private async getMethodMarkdown(methodInfo: MethodInfo): Promise<vscode.MarkdownString | undefined>
95+
private async getMethodMarkdown(methodInfo: MethodInfo, remoteDoc: DocumentInfo): Promise<vscode.MarkdownString | undefined>
8396
{
97+
let markdown = new vscode.MarkdownString('', true);
98+
99+
100+
const insights = remoteDoc.insights.forMethod(methodInfo, this._workspaceState.environment);
101+
const importantInsights = insights.filter(x=>x.importance<4);
102+
if (importantInsights.length>0){
103+
markdown.appendMarkdown(` <span style="color:#FF0000;"><i>${importantInsights.map(x=>x.name).join(' ')}</i></span>`);
104+
markdown.appendText('\n');
105+
106+
}
107+
108+
const perfStats = insights.filter(x=>x.name=="Performance Stats");
109+
for (var stat of perfStats){
110+
111+
const durationInsight : SpanDurationsInsight = stat as SpanDurationsInsight;
112+
113+
const p50 =durationInsight.percentiles.filter(x=>x.percentile==0.5)
114+
.firstOrDefault();
115+
const p95 =durationInsight.percentiles.filter(x=>x.percentile==0.95).firstOrDefault();
116+
const spanName = durationInsight.span.displayName;
117+
118+
if (p50 || p95){
119+
markdown.appendText(`${spanName} Duration: `)
120+
if (p50!=null){
121+
markdown.appendText(`${p50.currentDuration.value} ${p50.currentDuration.unit} (Median) `);
122+
}
123+
124+
if (p95!=null){
125+
markdown.appendText(`${p95.currentDuration.value} ${p95.currentDuration.unit} (P95) `);
126+
}
127+
markdown.appendText('\n');
128+
}
129+
130+
131+
132+
}
84133
const errors = await this._documentInfoProvider.analyticsProvider.getCodeObjectsErrors(methodInfo.idsWithType);
85134
if(!errors?.length)
86135
return;
87136

88-
let markdown = new vscode.MarkdownString('', true);
89137
markdown.appendText('Throws:\n');
90138
for(let error of errors)
91139
{
@@ -96,6 +144,8 @@ class MethodCallErrorHoverProvider implements vscode.HoverProvider
96144
markdown.appendMarkdown(` \u00B7 [$(link-external)](command:${command}?${args} "Show in side panel") `);
97145
markdown.appendText('\n');
98146
}
147+
148+
99149
markdown.supportHtml = true;
100150
markdown.isTrusted = true;
101151
return markdown;

src/views/codeAnalytics/codeAnalyticsView.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { EnvironmentManager } from '../../services/EnvironmentManager';
3636
import { Action } from "./InsightListView/Actions/Action";
3737
import { SpanLinkResolver } from "../../services/spanLinkResolver";
3838
import { DocumentInfoCache, ScanningStatus } from "../../services/DocumentInfoCache";
39+
import { PerformanceDecorator } from "./decorators/performanceDecorator";
3940
//import { DigmaFileDecorator } from "../../decorators/fileDecorator";
4041

4142

@@ -444,6 +445,8 @@ class CodeAnalyticsViewProvider implements vscode.WebviewViewProvider,vscode.Dis
444445
this._overlay.hide();
445446
}
446447
this._currentCodeObject = codeObject;
448+
vscode.commands.executeCommand(PerformanceDecorator.Commands.Show);
449+
447450

448451
}
449452

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import * as vscode from 'vscode';
2+
import { DocumentInfoProvider, LineInfo } from '../../../services/documentInfoProvider';
3+
import { WorkspaceState } from '../../../state';
4+
5+
export class PerformanceDecorator implements vscode.Disposable
6+
{
7+
public static Commands = class {
8+
public static readonly Show = `digma.performanceDecorator.show`;
9+
public static readonly Hide = `digma.performanceDecorator.hide`;
10+
};
11+
12+
private _iconDecorationType: vscode.TextEditorDecorationType;
13+
private _textDecorationType: vscode.TextEditorDecorationType;
14+
private _disposables: vscode.Disposable[] = [];
15+
16+
constructor(private _documentInfoProvider: DocumentInfoProvider)
17+
{
18+
this._iconDecorationType = vscode.window.createTextEditorDecorationType({
19+
after:{
20+
contentText: "\uea86",
21+
color: 'var(--vscode-editorCodeLens-foreground)',
22+
margin: '0 0 0 2em',
23+
textDecoration: "none; font-family: codicon; position: absolute; "
24+
}
25+
});
26+
this._textDecorationType = vscode.window.createTextEditorDecorationType({
27+
after:{
28+
margin: '0 0 0 3em',
29+
color: 'var(--vscode-editorCodeLens-foreground)',
30+
}
31+
});
32+
this._disposables.push(this._textDecorationType);
33+
this._disposables.push(vscode.commands.registerCommand(PerformanceDecorator.Commands.Show, this.onShow.bind(this)));
34+
this._disposables.push(vscode.commands.registerCommand(PerformanceDecorator.Commands.Hide, this.onHide.bind(this)));
35+
}
36+
37+
private async onShow(codeObjectId?: string)
38+
{
39+
if(!codeObjectId)
40+
return;
41+
42+
const editor = vscode.window.activeTextEditor;
43+
if(!editor)
44+
return;
45+
46+
const docInfo = await this._documentInfoProvider.getDocumentInfo(editor.document);
47+
if(!docInfo)
48+
return;
49+
50+
const method = docInfo.methods.firstOrDefault(m => m.symbol.id == codeObjectId);
51+
if(!method)
52+
return;
53+
54+
const lines = docInfo.lines.getAllByCurrentEnv().filter(l => method.range.contains(l.range.start));
55+
if(!lines)
56+
return;
57+
58+
const textDecorationOptions: vscode.DecorationOptions[] = lines
59+
.map(lineInfo => {
60+
return <vscode.DecorationOptions>{
61+
hoverMessage: this.getTooltip(lineInfo),
62+
range: new vscode.Range(lineInfo.range.end, lineInfo.range.end),
63+
renderOptions: {
64+
after:{
65+
contentText: [...new Set( lineInfo.exceptions.map(e => e.type))].join('\xB7')
66+
}
67+
}
68+
}
69+
});
70+
const iconDecorationOptions = lines
71+
.map(lineInfo => {
72+
return <vscode.DecorationOptions>{
73+
range: new vscode.Range(lineInfo.range.end, lineInfo.range.end),
74+
}
75+
});
76+
77+
editor.setDecorations(this._iconDecorationType, iconDecorationOptions);
78+
editor.setDecorations(this._textDecorationType, textDecorationOptions);
79+
}
80+
81+
private async onHide()
82+
{
83+
const editor = vscode.window.activeTextEditor;
84+
if(!editor)
85+
return;
86+
87+
editor.setDecorations(this._iconDecorationType, []);
88+
editor.setDecorations(this._textDecorationType, []);
89+
}
90+
91+
private getTooltip(lineInfo: LineInfo): vscode.MarkdownString
92+
{
93+
let markdown = new vscode.MarkdownString('', true);
94+
markdown.appendText('Throws:\n');
95+
let typesShown : string[] = [];
96+
for(let exception of lineInfo.exceptions)
97+
{
98+
if (typesShown.includes(exception.type)){
99+
continue;
100+
}
101+
typesShown.push(exception.type);
102+
103+
markdown.appendMarkdown(`- \`${exception.type}\``);
104+
105+
if (!exception.handled){
106+
markdown.appendMarkdown(` \u00B7 <span style="color:#f14c4c;"><i>Unhandled</i></span>`);
107+
}
108+
if (exception.unexpected){
109+
markdown.appendMarkdown(` \u00B7 <span style="color:#cca700"><i>Unexpected</i></span>`);
110+
}
111+
112+
markdown.appendMarkdown(`\\`);
113+
markdown.appendMarkdown(`\n${exception.message}\n`);
114+
}
115+
markdown.isTrusted = true;
116+
return markdown;
117+
}
118+
119+
public dispose() {
120+
for(let dis of this._disposables)
121+
dis.dispose();
122+
}
123+
}

0 commit comments

Comments
 (0)