Skip to content

Commit e31e2e9

Browse files
mjames-cjakebailey
andauthored
add -pprofDir profiling to LSP (#614)
Co-authored-by: Jake Bailey <[email protected]>
1 parent 769595a commit e31e2e9

File tree

5 files changed

+86
-57
lines changed

5 files changed

+86
-57
lines changed

_extension/package.json

+4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
],
3131
"default": "verbose",
3232
"description": "Trace TypeScript Go server communication."
33+
},
34+
"typescript-go.pprofDir": {
35+
"type": "string",
36+
"description": "Directory to write pprof profiles to."
3337
}
3438
}
3539
}

_extension/src/extension.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,21 @@ export function activate(context: vscode.ExtensionContext) {
2626

2727
output.appendLine(`Resolved to ${exe}`);
2828

29+
const config = vscode.workspace.getConfiguration("typescript-go");
30+
31+
// Get pprofDir
32+
const pprofDir = config.get<string>("pprofDir");
33+
const pprofArgs = pprofDir ? ["-pprofDir", pprofDir] : [];
34+
2935
const serverOptions: ServerOptions = {
3036
run: {
3137
command: exe,
32-
args: ["lsp"],
38+
args: ["lsp", ...pprofArgs],
3339
transport: TransportKind.stdio,
3440
},
3541
debug: {
3642
command: exe,
37-
args: ["lsp"],
43+
args: ["lsp", ...pprofArgs],
3844
transport: TransportKind.stdio,
3945
},
4046
};

cmd/tsgo/lsp.go

+8
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ import (
1010
"github.com/microsoft/typescript-go/internal/bundled"
1111
"github.com/microsoft/typescript-go/internal/core"
1212
"github.com/microsoft/typescript-go/internal/lsp"
13+
"github.com/microsoft/typescript-go/internal/pprof"
1314
"github.com/microsoft/typescript-go/internal/vfs/osvfs"
1415
)
1516

1617
func runLSP(args []string) int {
1718
flag := flag.NewFlagSet("lsp", flag.ContinueOnError)
1819
stdio := flag.Bool("stdio", false, "use stdio for communication")
20+
pprofDir := flag.String("pprofDir", "", "Generate pprof CPU/memory profiles to the given directory.")
1921
pipe := flag.String("pipe", "", "use named pipe for communication")
2022
_ = pipe
2123
socket := flag.String("socket", "", "use socket for communication")
@@ -29,6 +31,12 @@ func runLSP(args []string) int {
2931
return 1
3032
}
3133

34+
if *pprofDir != "" {
35+
fmt.Fprintf(os.Stderr, "pprof profiles will be written to: %v\n", *pprofDir)
36+
profileSession := pprof.BeginProfiling(*pprofDir, os.Stderr)
37+
defer profileSession.Stop()
38+
}
39+
3240
fs := bundled.WrapFS(osvfs.FS())
3341
defaultLibraryPath := bundled.LibPath()
3442

cmd/tsgo/main.go

+3-55
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ import (
66
"flag"
77
"fmt"
88
"os"
9-
"path/filepath"
109
"runtime"
11-
"runtime/pprof"
1210
"slices"
1311
"strconv"
1412
"strings"
@@ -21,6 +19,7 @@ import (
2119
"github.com/microsoft/typescript-go/internal/core"
2220
"github.com/microsoft/typescript-go/internal/diagnosticwriter"
2321
"github.com/microsoft/typescript-go/internal/execute"
22+
"github.com/microsoft/typescript-go/internal/pprof"
2423
"github.com/microsoft/typescript-go/internal/scanner"
2524
"github.com/microsoft/typescript-go/internal/tspath"
2625
"github.com/microsoft/typescript-go/internal/vfs/osvfs"
@@ -135,8 +134,8 @@ func main() {
135134
opts := parseArgs()
136135

137136
if opts.devel.pprofDir != "" {
138-
profileSession := beginProfiling(opts.devel.pprofDir)
139-
defer profileSession.stop()
137+
profileSession := pprof.BeginProfiling(opts.devel.pprofDir, os.Stdout)
138+
defer profileSession.Stop()
140139
}
141140

142141
startTime := time.Now()
@@ -335,54 +334,3 @@ func printDiagnostics(diagnostics []*ast.Diagnostic, host ts.CompilerHost, compi
335334
}
336335
}
337336
}
338-
339-
type profileSession struct {
340-
cpuFilePath string
341-
memFilePath string
342-
cpuFile *os.File
343-
memFile *os.File
344-
}
345-
346-
func beginProfiling(profileDir string) *profileSession {
347-
if err := os.MkdirAll(profileDir, 0o755); err != nil {
348-
panic(err)
349-
}
350-
351-
pid := os.Getpid()
352-
353-
cpuProfilePath := filepath.Join(profileDir, fmt.Sprintf("%d-cpuprofile.pb.gz", pid))
354-
memProfilePath := filepath.Join(profileDir, fmt.Sprintf("%d-memprofile.pb.gz", pid))
355-
cpuFile, err := os.Create(cpuProfilePath)
356-
if err != nil {
357-
panic(err)
358-
}
359-
memFile, err := os.Create(memProfilePath)
360-
if err != nil {
361-
panic(err)
362-
}
363-
364-
if err := pprof.StartCPUProfile(cpuFile); err != nil {
365-
panic(err)
366-
}
367-
368-
return &profileSession{
369-
cpuFilePath: cpuProfilePath,
370-
memFilePath: memProfilePath,
371-
cpuFile: cpuFile,
372-
memFile: memFile,
373-
}
374-
}
375-
376-
func (p *profileSession) stop() {
377-
pprof.StopCPUProfile()
378-
err := pprof.Lookup("allocs").WriteTo(p.memFile, 0)
379-
if err != nil {
380-
panic(err)
381-
}
382-
383-
p.cpuFile.Close()
384-
p.memFile.Close()
385-
386-
fmt.Printf("CPU profile: %v\n", p.cpuFilePath)
387-
fmt.Printf("Memory profile: %v\n", p.memFilePath)
388-
}

internal/pprof/pprof.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package pprof
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"os"
7+
"path/filepath"
8+
"runtime/pprof"
9+
)
10+
11+
type profileSession struct {
12+
cpuFilePath string
13+
memFilePath string
14+
cpuFile *os.File
15+
memFile *os.File
16+
logWriter io.Writer
17+
}
18+
19+
// BeginProfiling starts CPU and memory profiling, writing the profiles to the specified directory.
20+
func BeginProfiling(profileDir string, logWriter io.Writer) *profileSession {
21+
if err := os.MkdirAll(profileDir, 0o755); err != nil {
22+
panic(err)
23+
}
24+
25+
pid := os.Getpid()
26+
27+
cpuProfilePath := filepath.Join(profileDir, fmt.Sprintf("%d-cpuprofile.pb.gz", pid))
28+
memProfilePath := filepath.Join(profileDir, fmt.Sprintf("%d-memprofile.pb.gz", pid))
29+
cpuFile, err := os.Create(cpuProfilePath)
30+
if err != nil {
31+
panic(err)
32+
}
33+
memFile, err := os.Create(memProfilePath)
34+
if err != nil {
35+
panic(err)
36+
}
37+
38+
if err := pprof.StartCPUProfile(cpuFile); err != nil {
39+
panic(err)
40+
}
41+
42+
return &profileSession{
43+
cpuFilePath: cpuProfilePath,
44+
memFilePath: memProfilePath,
45+
cpuFile: cpuFile,
46+
memFile: memFile,
47+
logWriter: logWriter,
48+
}
49+
}
50+
51+
func (p *profileSession) Stop() {
52+
pprof.StopCPUProfile()
53+
err := pprof.Lookup("allocs").WriteTo(p.memFile, 0)
54+
if err != nil {
55+
panic(err)
56+
}
57+
58+
p.cpuFile.Close()
59+
p.memFile.Close()
60+
61+
fmt.Fprintf(p.logWriter, "CPU profile: %v\n", p.cpuFilePath)
62+
fmt.Fprintf(p.logWriter, "Memory profile: %v\n", p.memFilePath)
63+
}

0 commit comments

Comments
 (0)