Skip to content

Commit

Permalink
feat: initial work on profiling (microsoft#392)
Browse files Browse the repository at this point in the history
This PR implements basic .cpuprofile capturing for debug sessions.

- Users can start profiling through a context menu on the debug session,
or by running a command from the palette. If there's more than one
session, running a command will open a quickpick to choose the session
to profile.
- They will then be asked what kind of profile they want to record. For
Node.js this will be .cpuprofiles and .heapprofiles. For browsers, it
will allow the newer trace profiles.
- While profiling, the debugger is disabled, like the chrome devtools
does. This removes performance overhead and the ability to hit
breakpoints, which mess with the profile...
- Profiling state is shown in the status bar, and clicking on the entry
will stop profiling and ask the user where they want to save the profile
file. There's also a 'stop profiling' command.

This should give us a good foundation for further types and profiling
mechanics in the future.

I'm not totally happy with the entry and exitpoints as-is. You have to
know it's there to be able to start profiling, and then it's kind of
hard to figure out how to stop profiling. The chrome devtools shows an
overlay while profiling, which prevents the user try to e.g. set
breakpoints or use the REPL, implicitly communicating that debugging is
not possible in this state. I'm not sure what the best solution for us
is.

cc @roblourens @ididorn for thoughts. Will also bring this/whatever we
discuss up in next week's UX sync.
  • Loading branch information
connor4312 authored Mar 13, 2020
1 parent 564e3e1 commit 8b680f1
Show file tree
Hide file tree
Showing 26 changed files with 1,425 additions and 191 deletions.
31 changes: 29 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,9 @@
"onCommand:extension.NAMESPACE(chrome-debug).removeAllCustomBreakpoints",
"onCommand:extension.NAMESPACE(chrome-debug).removeCustomBreakpoint",
"onDebugResolve:NAMESPACE(msedge)",
"onDebugResolve:NAMESPACE(extensionHost)"
"onDebugResolve:NAMESPACE(extensionHost)",
"onCommand:extension.NAMESPACE(node-debug).startProfile",
"onCommand:extension.NAMESPACE(node-debug).stopProfile"
],
"extensionKind": [
"workspace"
Expand Down Expand Up @@ -231,6 +233,16 @@
"command": "extension.NAMESPACE(node-debug).createDebuggerTerminal",
"title": "%debug.terminal.label%",
"category": "Debug"
},
{
"command": "extension.NAMESPACE(node-debug).startProfile",
"title": "%profile.start%",
"category": "Debug"
},
{
"command": "extension.NAMESPACE(node-debug).stopProfile",
"title": "%profile.stop%",
"category": "Debug"
}
],
"menus": {
Expand All @@ -239,6 +251,16 @@
"command": "extension.NAMESPACE(node-debug).prettyPrint",
"title": "%pretty.print.script%",
"when": "inDebugMode && debugType == NAMESPACE(chrome) && editorLangId == javascript"
},
{
"command": "extension.NAMESPACE(node-debug).startProfile",
"title": "%profile.start%",
"when": "inDebugMode && debugType == NAMESPACE(chrome)"
},
{
"command": "extension.NAMESPACE(node-debug).stopProfile",
"title": "%profile.stop%",
"when": "inDebugMode && debugType == NAMESPACE(chrome)"
}
],
"debug/callstack/context": [
Expand All @@ -250,7 +272,12 @@
{
"command": "extension.NAMESPACE(node-debug).toggleSkippingFile",
"group": "navigation",
"when": "inDebugMode && debugType == NAMESPACE(chrome) && callStackItemType == 'stackFrame'"
"when": "inDebugMode && debugType == NAMESPACE(chrome) && callStackItemType == 'session'"
},
{
"command": "extension.NAMESPACE(node-debug).startProfile",
"icon": "watch",
"group": "navigation"
}
],
"view/title": [
Expand Down
110 changes: 110 additions & 0 deletions scripts/dap-custom.json
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,116 @@
"required": ["event", "body"]
}
]
},

"StartProfileRequest": {
"allOf": [
{ "$ref": "#/definitions/Request" },
{
"type": "object",
"description": "Starts taking a profile of the target.",
"properties": {
"command": {
"type": "string",
"enum": ["startProfile"]
},
"arguments": {
"$ref": "#/definitions/StartProfileArguments"
}
},
"required": ["command", "arguments"]
}
]
},
"StartProfileArguments": {
"type": "object",
"description": "Arguments for 'StartProfile' request.",
"properties": {
"file": {
"type": "string",
"description": "Location where the profile should be saved."
},
"type": {
"type": "string",
"description": "Type of profile that should be taken"
},
"params": {
"type": "object",
"description": "Additional arguments for the type of profiler"
}
},
"required": ["file", "type"]
},
"StartProfileResponse": {
"allOf": [
{ "$ref": "#/definitions/Response" },
{
"type": "object",
"description": "Response to 'StartProfile' request."
}
]
},

"StopProfileRequest": {
"allOf": [
{ "$ref": "#/definitions/Request" },
{
"type": "object",
"description": "Stops a running profile.",
"properties": {
"command": {
"type": "string",
"enum": ["stopProfile"]
},
"arguments": {
"$ref": "#/definitions/StopProfileArguments"
}
},
"required": ["command", "arguments"]
}
]
},
"StopProfileArguments": {
"type": "object",
"description": "Arguments for 'StopProfile' request.",
"properties": {}
},
"StopProfileResponse": {
"allOf": [
{ "$ref": "#/definitions/Response" },
{
"type": "object",
"description": "Response to 'StopProfile' request."
}
]
},

"ProfilerStateUpdateEvent": {
"allOf": [
{ "$ref": "#/definitions/Event" },
{
"type": "object",
"description": "Fired when a profiling state changes.",
"properties": {
"event": {
"type": "string",
"enum": ["profilerStateUpdate"]
},
"body": {
"type": "object",
"description": "Body for 'ProfilerStateUpdateEvent' event.",
"required": ["label"],
"properties": {
"label": {
"type": "string",
"description": "Description of the current state"
}
}
}
},
"required": ["event", "body"]
}
]
}
}
}
Loading

0 comments on commit 8b680f1

Please sign in to comment.