Skip to content

Commit

Permalink
feat(pprof): apply all provider settings
Browse files Browse the repository at this point in the history
Previously reportGlob, binaryGlob, and all TopOptions
were ignored even if set in settings.json.
  • Loading branch information
bevzzz committed Jun 13, 2024
1 parent 8c77271 commit fd348b8
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 14 deletions.
59 changes: 50 additions & 9 deletions provider/pprof/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,55 @@ import type {
MetaParams,
MetaResult,
Provider,
ProviderSettings,
} from '@openctx/provider'
import { parseGolang } from './parser.js'
import { type Node, findReportPath as findPprofSources, getPprof } from './pprof.js'
import { type Node, type TopOptions, findReportPath as findPprofSources, getPprof } from './pprof.js'

interface Settings {
/**
* Glob pattern to match the profile report.
*
* Note, that forward slashes _do not need_ to be escaped in the patterns provided in `settings.json`
*
* @default "**\/*.pprof"
* @example "**\/cmd\/*.pb.gz" (limit to asubdirectory)
*/
reportGlob?: string

/**
* Glob pattern to match the Go binary from which the report was generated.
*
* By default `binaryGlob` not set. The provider will try to locate it by searching
* for an executable file whose name matches that of its parent directory.
* This is what a binary produces by `go build .` would be conventionally named.
*/
binaryGlob?: string

/**
* The provider will not traverse the file tree past the directory containing `rootDirectoryMarkers`,
* when searching for the profile report and the binary.
*
* @default [".git", "go.mod"]
*/
rootDirectoryMarkers?: string[]

/**
* Options to control `pprof -top` output.
*
* @default top: { excludeInline: true, sort: 'cum' }
* @example top: { excludeInline: false, sort: 'flat', nodeCount: 10 }
*/
top?: Pick<TopOptions, 'excludeInline' | 'nodeCount' | 'sort'>
}

/**
* An [OpenCtx](https://openctx.org) provider that annotates every function declaration with
* the CPU time and memory allocations associated with it.
*
* Only Go files are supported.
*/
const pprof: Provider = {
meta(params: MetaParams, settings: ProviderSettings): MetaResult {
const pprof: Provider<Settings> = {
meta(params: MetaParams, settings: Settings): MetaResult {
return {
name: 'pprof',
annotations: {
Expand All @@ -26,7 +64,7 @@ const pprof: Provider = {
}
},

annotations(params: AnnotationsParams, settings: ProviderSettings): AnnotationsResult {
annotations(params: AnnotationsParams, settings: Settings): AnnotationsResult {
// Test files do not need pprof annotations.
if (params.uri.endsWith('_test.go')) {
return []
Expand All @@ -40,8 +78,9 @@ const pprof: Provider = {

const searchDir = dirname(params.uri).replace(/^file:\/{2}/, '')
const sources = findPprofSources(searchDir, {
reportGlob: (settings.reportGlob as string) || '**/*.pb.gz',
rootDirectoryMarkers: settings.rootDirectoryMarkers as string[],
reportGlob: settings.reportGlob || '**/*.pprof',
rootDirectoryMarkers: settings.rootDirectoryMarkers || ['.git', 'go.mod'],
binaryGlob: settings.binaryGlob,
// TODO: pass workspaceRoot once it's made available
// workspaceRoot: workspaceRoot,
})
Expand All @@ -55,7 +94,7 @@ const pprof: Provider = {
return []
}

const top = pprof.top({ package: content.package })
const top = pprof.top({ ...settings.top, package: content.package })
if (top === null) {
return []
}
Expand All @@ -68,7 +107,9 @@ const pprof: Provider = {
}

let item: Item = {
title: `pprof ${top.type}: ${node.cum}${top.unit}, ${node.cumPerc}% (#${i + 1}, cum)`,
title: `pprof ${top.type}: cum ${node.cum}${top.unit}, ${node.cumPerc}% (#${
i + 1
}, sort=${settings.top?.sort || 'cum'})`,
}

const list = pprof.list(node.function)
Expand Down
6 changes: 6 additions & 0 deletions provider/pprof/pprof.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,12 @@ describe('pprof', () => {
opts: { package: 'main', excludeInline: false },
want: `go tool pprof -top -show="main\\." -cum report.pprof`,
},
{
name: 'limit node count',
tool: 'go tool pprof',
opts: { package: 'main', nodeCount: 2 },
want: `go tool pprof -top -show="main\\." -cum -noinlines -nodecount=2 report.pprof`,
},
])('top command ($name)', (tt: TopCmdTest) => {
execSyncMock.mockReturnValueOnce(buffer(''))
const pprof = new Pprof(tt.tool)
Expand Down
19 changes: 14 additions & 5 deletions provider/pprof/pprof.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ export interface Node {
}

export interface ListOutput {
/** Raw output of `pprof -list`. */
raw: string
}

Expand Down Expand Up @@ -229,14 +230,22 @@ export class Pprof {

private topCmd(options: TopOptions): string {
const { report, binary } = this.sources
const opt: TopOptions = {
...options,
sort: options.sort ?? 'cum',
excludeInline: options.excludeInline ?? true,
}

let cmd = this.tool + ` -top -show="${options.package}\\."`
cmd += options.sort ? ` -${options.sort}` : ' -cum'
let cmd = this.tool + ` -top -show="${options.package}\\." -${opt.sort}`

if (options.excludeInline === undefined || options.excludeInline === true) {
if (opt.excludeInline) {
cmd += ' -noinlines'
}

if (options.nodeCount) {
cmd += ` -nodecount=${options.nodeCount}`
}

// Standalone `pprof` is not able to parse a Go binary, so it ignores it altogether.
// Should we omit it from the command in case this.tool === 'pprof' ?
if (binary) {
Expand Down Expand Up @@ -284,9 +293,9 @@ export class Pprof {
}

return {
type: reportType ? reportType[1] || 'cpu' : '',
type: reportType ? reportType[1] ?? 'cpu' : '',
file: binaryName ? binaryName[1] : undefined,
unit: unit || 's',
unit: unit ?? 's',
nodes: nodes,
}
}
Expand Down

0 comments on commit fd348b8

Please sign in to comment.