@perfbase/mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -0
- package/dist/decoder/decompress.d.ts +25 -0
- package/dist/decoder/decompress.d.ts.map +1 -0
- package/dist/decoder/decompress.js +79 -0
- package/dist/decoder/decompress.js.map +1 -0
- package/dist/decoder/trie-walker.d.ts +34 -0
- package/dist/decoder/trie-walker.d.ts.map +1 -0
- package/dist/decoder/trie-walker.js +159 -0
- package/dist/decoder/trie-walker.js.map +1 -0
- package/dist/decoder/types.d.ts +188 -0
- package/dist/decoder/types.d.ts.map +1 -0
- package/dist/decoder/types.js +6 -0
- package/dist/decoder/types.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +309 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/profile-metadata.d.ts +14 -0
- package/dist/resources/profile-metadata.d.ts.map +1 -0
- package/dist/resources/profile-metadata.js +41 -0
- package/dist/resources/profile-metadata.js.map +1 -0
- package/dist/tools/cpu-intensive.d.ts +37 -0
- package/dist/tools/cpu-intensive.d.ts.map +1 -0
- package/dist/tools/cpu-intensive.js +64 -0
- package/dist/tools/cpu-intensive.js.map +1 -0
- package/dist/tools/database-queries.d.ts +33 -0
- package/dist/tools/database-queries.d.ts.map +1 -0
- package/dist/tools/database-queries.js +133 -0
- package/dist/tools/database-queries.js.map +1 -0
- package/dist/tools/memory-hogs.d.ts +38 -0
- package/dist/tools/memory-hogs.d.ts.map +1 -0
- package/dist/tools/memory-hogs.js +65 -0
- package/dist/tools/memory-hogs.js.map +1 -0
- package/dist/tools/most-called.d.ts +36 -0
- package/dist/tools/most-called.d.ts.map +1 -0
- package/dist/tools/most-called.js +59 -0
- package/dist/tools/most-called.js.map +1 -0
- package/dist/tools/n-plus-one.d.ts +29 -0
- package/dist/tools/n-plus-one.d.ts.map +1 -0
- package/dist/tools/n-plus-one.js +137 -0
- package/dist/tools/n-plus-one.js.map +1 -0
- package/dist/tools/slowest-functions.d.ts +36 -0
- package/dist/tools/slowest-functions.d.ts.map +1 -0
- package/dist/tools/slowest-functions.js +59 -0
- package/dist/tools/slowest-functions.js.map +1 -0
- package/dist/tools/summary.d.ts +42 -0
- package/dist/tools/summary.d.ts.map +1 -0
- package/dist/tools/summary.js +118 -0
- package/dist/tools/summary.js.map +1 -0
- package/dist/utils/formatting.d.ts +48 -0
- package/dist/utils/formatting.d.ts.map +1 -0
- package/dist/utils/formatting.js +109 -0
- package/dist/utils/formatting.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: get_database_queries
|
|
3
|
+
* Extracts and analyzes database operations from the profile
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { loadProfile } from '../decoder/decompress.js';
|
|
7
|
+
import { walkTrie, sortByField } from '../decoder/trie-walker.js';
|
|
8
|
+
import { formatTime, formatNumber } from '../utils/formatting.js';
|
|
9
|
+
export const databaseQueriesSchema = z.object({
|
|
10
|
+
filePath: z.string().describe('Path to the PerfBase profile file'),
|
|
11
|
+
span: z.string().optional().describe('Filter to a specific span name'),
|
|
12
|
+
limit: z.number().int().positive().default(50).describe('Maximum number of results'),
|
|
13
|
+
});
|
|
14
|
+
// Patterns to identify database functions
|
|
15
|
+
const DB_PATTERNS = [
|
|
16
|
+
// PDO
|
|
17
|
+
/^PDO::query$/i,
|
|
18
|
+
/^PDO::exec$/i,
|
|
19
|
+
/^PDO::prepare$/i,
|
|
20
|
+
/^PDOStatement::execute$/i,
|
|
21
|
+
/^PDOStatement::fetch/i,
|
|
22
|
+
// mysqli
|
|
23
|
+
/^mysqli::query$/i,
|
|
24
|
+
/^mysqli::prepare$/i,
|
|
25
|
+
/^mysqli_query$/i,
|
|
26
|
+
/^mysqli_stmt::execute$/i,
|
|
27
|
+
// PostgreSQL
|
|
28
|
+
/^pg_query/i,
|
|
29
|
+
/^pg_execute/i,
|
|
30
|
+
/^pg_prepare/i,
|
|
31
|
+
// MongoDB
|
|
32
|
+
/^MongoDB\\/i,
|
|
33
|
+
// Redis
|
|
34
|
+
/^Redis::/i,
|
|
35
|
+
/^Predis\\/i,
|
|
36
|
+
// Memcached
|
|
37
|
+
/^Memcached::/i,
|
|
38
|
+
/^Memcache::/i,
|
|
39
|
+
// Elasticsearch
|
|
40
|
+
/^Elasticsearch\\/i,
|
|
41
|
+
// Generic patterns
|
|
42
|
+
/->query\(/,
|
|
43
|
+
/->execute\(/,
|
|
44
|
+
/->find\(/,
|
|
45
|
+
/->findOne\(/,
|
|
46
|
+
/->insert\(/,
|
|
47
|
+
/->update\(/,
|
|
48
|
+
/->delete\(/,
|
|
49
|
+
/->select\(/,
|
|
50
|
+
];
|
|
51
|
+
function isDatabaseFunction(name) {
|
|
52
|
+
return DB_PATTERNS.some((pattern) => pattern.test(name));
|
|
53
|
+
}
|
|
54
|
+
export async function getDatabaseQueries(input) {
|
|
55
|
+
const data = await loadProfile(input.filePath);
|
|
56
|
+
const allFunctions = walkTrie(data, input.span);
|
|
57
|
+
// Filter to database functions
|
|
58
|
+
const dbFunctions = allFunctions.filter((f) => isDatabaseFunction(f.name));
|
|
59
|
+
// Calculate totals
|
|
60
|
+
const totalTime = dbFunctions.reduce((sum, f) => sum + f.totalWallTime, 0);
|
|
61
|
+
const totalCalls = dbFunctions.reduce((sum, f) => sum + f.callCount, 0);
|
|
62
|
+
// Group by type
|
|
63
|
+
const byType = {};
|
|
64
|
+
for (const f of dbFunctions) {
|
|
65
|
+
const type = categorizeDbFunction(f.name);
|
|
66
|
+
if (!byType[type]) {
|
|
67
|
+
byType[type] = { count: 0, totalTime: 0 };
|
|
68
|
+
}
|
|
69
|
+
byType[type].count += f.callCount;
|
|
70
|
+
byType[type].totalTime += f.totalWallTime;
|
|
71
|
+
}
|
|
72
|
+
// Sort by total time
|
|
73
|
+
const sorted = sortByField(dbFunctions, 'totalWallTime', input.limit);
|
|
74
|
+
return {
|
|
75
|
+
queries: sorted.map((f) => ({
|
|
76
|
+
query: f.name,
|
|
77
|
+
callCount: f.callCount,
|
|
78
|
+
totalTime: f.totalWallTime,
|
|
79
|
+
avgTime: f.avgWallTime,
|
|
80
|
+
callPath: f.callPath,
|
|
81
|
+
})),
|
|
82
|
+
totalQueries: totalCalls,
|
|
83
|
+
totalTime: formatTime(totalTime),
|
|
84
|
+
avgTimePerQuery: totalCalls > 0 ? formatTime(totalTime / totalCalls) : '0µs',
|
|
85
|
+
byType: Object.fromEntries(Object.entries(byType).map(([type, data]) => [
|
|
86
|
+
type,
|
|
87
|
+
{ count: data.count, totalTime: formatTime(data.totalTime) },
|
|
88
|
+
])),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function categorizeDbFunction(name) {
|
|
92
|
+
if (/pdo/i.test(name))
|
|
93
|
+
return 'PDO';
|
|
94
|
+
if (/mysqli/i.test(name))
|
|
95
|
+
return 'MySQLi';
|
|
96
|
+
if (/pg_/i.test(name))
|
|
97
|
+
return 'PostgreSQL';
|
|
98
|
+
if (/mongodb/i.test(name))
|
|
99
|
+
return 'MongoDB';
|
|
100
|
+
if (/redis/i.test(name) || /predis/i.test(name))
|
|
101
|
+
return 'Redis';
|
|
102
|
+
if (/memcache/i.test(name))
|
|
103
|
+
return 'Memcached';
|
|
104
|
+
if (/elasticsearch/i.test(name))
|
|
105
|
+
return 'Elasticsearch';
|
|
106
|
+
return 'Other';
|
|
107
|
+
}
|
|
108
|
+
export function formatDatabaseQueriesOutput(result) {
|
|
109
|
+
const lines = [
|
|
110
|
+
`# Database Queries Analysis`,
|
|
111
|
+
``,
|
|
112
|
+
`**Total Queries**: ${formatNumber(result.totalQueries)}`,
|
|
113
|
+
`**Total Time**: ${result.totalTime}`,
|
|
114
|
+
`**Avg Time/Query**: ${result.avgTimePerQuery}`,
|
|
115
|
+
``,
|
|
116
|
+
`## By Database Type`,
|
|
117
|
+
``,
|
|
118
|
+
];
|
|
119
|
+
for (const [type, data] of Object.entries(result.byType)) {
|
|
120
|
+
lines.push(`- **${type}**: ${formatNumber(data.count)} queries, ${data.totalTime}`);
|
|
121
|
+
}
|
|
122
|
+
lines.push(``, `## Slowest Queries`, ``);
|
|
123
|
+
for (const q of result.queries.slice(0, 20)) {
|
|
124
|
+
lines.push(`### ${q.query}`);
|
|
125
|
+
lines.push(`- **Calls**: ${formatNumber(q.callCount)}`);
|
|
126
|
+
lines.push(`- **Total Time**: ${formatTime(q.totalTime)}`);
|
|
127
|
+
lines.push(`- **Avg Time**: ${formatTime(q.avgTime)}`);
|
|
128
|
+
lines.push(`- **Path**: ${q.callPath}`);
|
|
129
|
+
lines.push(``);
|
|
130
|
+
}
|
|
131
|
+
return lines.join('\n');
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=database-queries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database-queries.js","sourceRoot":"","sources":["../../src/tools/database-queries.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: get_memory_hogs
|
|
3
|
+
* Returns functions sorted by memory allocation
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
export declare const memoryHogsSchema: z.ZodObject<{
|
|
7
|
+
filePath: z.ZodString;
|
|
8
|
+
span: z.ZodOptional<z.ZodString>;
|
|
9
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
filePath: string;
|
|
12
|
+
limit: number;
|
|
13
|
+
span?: string | undefined;
|
|
14
|
+
}, {
|
|
15
|
+
filePath: string;
|
|
16
|
+
span?: string | undefined;
|
|
17
|
+
limit?: number | undefined;
|
|
18
|
+
}>;
|
|
19
|
+
export type MemoryHogsInput = z.infer<typeof memoryHogsSchema>;
|
|
20
|
+
export interface MemoryHogsResult {
|
|
21
|
+
functions: Array<{
|
|
22
|
+
rank: number;
|
|
23
|
+
name: string;
|
|
24
|
+
callPath: string;
|
|
25
|
+
location?: string;
|
|
26
|
+
callCount: number;
|
|
27
|
+
memoryAllocated: string;
|
|
28
|
+
memoryPeak: string;
|
|
29
|
+
memoryNet: string;
|
|
30
|
+
avgMemoryPerCall: string;
|
|
31
|
+
percentOfTotal: string;
|
|
32
|
+
}>;
|
|
33
|
+
totalMemoryAllocated: string;
|
|
34
|
+
totalFunctions: number;
|
|
35
|
+
}
|
|
36
|
+
export declare function getMemoryHogs(input: MemoryHogsInput): Promise<MemoryHogsResult>;
|
|
37
|
+
export declare function formatMemoryHogsOutput(result: MemoryHogsResult): string;
|
|
38
|
+
//# sourceMappingURL=memory-hogs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-hogs.d.ts","sourceRoot":"","sources":["../../src/tools/memory-hogs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;EAI3B,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE/D,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,KAAK,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,eAAe,EAAE,MAAM,CAAC;QACxB,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;QAClB,gBAAgB,EAAE,MAAM,CAAC;QACzB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC,CAAC;IACH,oBAAoB,EAAE,MAAM,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,gBAAgB,CAAC,CAgC3B;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAwBvE"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: get_memory_hogs
|
|
3
|
+
* Returns functions sorted by memory allocation
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { loadProfile } from '../decoder/decompress.js';
|
|
7
|
+
import { walkTrie, sortByField } from '../decoder/trie-walker.js';
|
|
8
|
+
import { formatBytes, formatNumber, formatLocation } from '../utils/formatting.js';
|
|
9
|
+
export const memoryHogsSchema = z.object({
|
|
10
|
+
filePath: z.string().describe('Path to the PerfBase profile file'),
|
|
11
|
+
span: z.string().optional().describe('Filter to a specific span name'),
|
|
12
|
+
limit: z.number().int().positive().default(20).describe('Maximum number of results'),
|
|
13
|
+
});
|
|
14
|
+
export async function getMemoryHogs(input) {
|
|
15
|
+
const data = await loadProfile(input.filePath);
|
|
16
|
+
const allFunctions = walkTrie(data, input.span);
|
|
17
|
+
// Calculate total memory allocated
|
|
18
|
+
const totalMemory = allFunctions.reduce((sum, f) => sum + f.memoryAllocated, 0);
|
|
19
|
+
// Sort by memory allocated
|
|
20
|
+
const sorted = sortByField(allFunctions, 'memoryAllocated', input.limit);
|
|
21
|
+
return {
|
|
22
|
+
functions: sorted.map((f, i) => ({
|
|
23
|
+
rank: i + 1,
|
|
24
|
+
name: f.name,
|
|
25
|
+
callPath: f.callPath,
|
|
26
|
+
location: f.file ? formatLocation(f.file, f.line) : undefined,
|
|
27
|
+
callCount: f.callCount,
|
|
28
|
+
memoryAllocated: formatBytes(f.memoryAllocated),
|
|
29
|
+
memoryPeak: formatBytes(f.memoryPeak),
|
|
30
|
+
memoryNet: formatBytes(f.memoryNet),
|
|
31
|
+
avgMemoryPerCall: f.callCount > 0
|
|
32
|
+
? formatBytes(f.memoryAllocated / f.callCount)
|
|
33
|
+
: '0B',
|
|
34
|
+
percentOfTotal: totalMemory > 0
|
|
35
|
+
? `${((f.memoryAllocated / totalMemory) * 100).toFixed(2)}%`
|
|
36
|
+
: '0%',
|
|
37
|
+
})),
|
|
38
|
+
totalMemoryAllocated: formatBytes(totalMemory),
|
|
39
|
+
totalFunctions: allFunctions.length,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function formatMemoryHogsOutput(result) {
|
|
43
|
+
const lines = [
|
|
44
|
+
`# Memory-Heavy Functions`,
|
|
45
|
+
``,
|
|
46
|
+
`Total memory allocated: ${result.totalMemoryAllocated}`,
|
|
47
|
+
`Total functions analyzed: ${formatNumber(result.totalFunctions)}`,
|
|
48
|
+
``,
|
|
49
|
+
];
|
|
50
|
+
for (const f of result.functions) {
|
|
51
|
+
lines.push(`## ${f.rank}. ${f.name}`);
|
|
52
|
+
lines.push(`- **Call Path**: ${f.callPath}`);
|
|
53
|
+
if (f.location) {
|
|
54
|
+
lines.push(`- **Location**: ${f.location}`);
|
|
55
|
+
}
|
|
56
|
+
lines.push(`- **Calls**: ${formatNumber(f.callCount)}`);
|
|
57
|
+
lines.push(`- **Total Allocated**: ${f.memoryAllocated} (${f.percentOfTotal})`);
|
|
58
|
+
lines.push(`- **Peak**: ${f.memoryPeak}`);
|
|
59
|
+
lines.push(`- **Net Change**: ${f.memoryNet}`);
|
|
60
|
+
lines.push(`- **Avg/Call**: ${f.avgMemoryPerCall}`);
|
|
61
|
+
lines.push(``);
|
|
62
|
+
}
|
|
63
|
+
return lines.join('\n');
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=memory-hogs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-hogs.js","sourceRoot":"","sources":["../../src/tools/memory-hogs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: get_most_called_functions
|
|
3
|
+
* Returns functions sorted by call count
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
export declare const mostCalledSchema: z.ZodObject<{
|
|
7
|
+
filePath: z.ZodString;
|
|
8
|
+
span: z.ZodOptional<z.ZodString>;
|
|
9
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
filePath: string;
|
|
12
|
+
limit: number;
|
|
13
|
+
span?: string | undefined;
|
|
14
|
+
}, {
|
|
15
|
+
filePath: string;
|
|
16
|
+
span?: string | undefined;
|
|
17
|
+
limit?: number | undefined;
|
|
18
|
+
}>;
|
|
19
|
+
export type MostCalledInput = z.infer<typeof mostCalledSchema>;
|
|
20
|
+
export interface MostCalledResult {
|
|
21
|
+
functions: Array<{
|
|
22
|
+
rank: number;
|
|
23
|
+
name: string;
|
|
24
|
+
callPath: string;
|
|
25
|
+
location?: string;
|
|
26
|
+
callCount: number;
|
|
27
|
+
totalWallTime: string;
|
|
28
|
+
avgWallTime: string;
|
|
29
|
+
percentOfCalls: string;
|
|
30
|
+
}>;
|
|
31
|
+
totalCalls: number;
|
|
32
|
+
totalFunctions: number;
|
|
33
|
+
}
|
|
34
|
+
export declare function getMostCalledFunctions(input: MostCalledInput): Promise<MostCalledResult>;
|
|
35
|
+
export declare function formatMostCalledOutput(result: MostCalledResult): string;
|
|
36
|
+
//# sourceMappingURL=most-called.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"most-called.d.ts","sourceRoot":"","sources":["../../src/tools/most-called.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;EAI3B,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE/D,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,KAAK,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC,CAAC;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,gBAAgB,CAAC,CA2B3B;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAsBvE"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: get_most_called_functions
|
|
3
|
+
* Returns functions sorted by call count
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { loadProfile } from '../decoder/decompress.js';
|
|
7
|
+
import { walkTrie, sortByField } from '../decoder/trie-walker.js';
|
|
8
|
+
import { formatTime, formatNumber, formatLocation } from '../utils/formatting.js';
|
|
9
|
+
export const mostCalledSchema = z.object({
|
|
10
|
+
filePath: z.string().describe('Path to the PerfBase profile file'),
|
|
11
|
+
span: z.string().optional().describe('Filter to a specific span name'),
|
|
12
|
+
limit: z.number().int().positive().default(20).describe('Maximum number of results'),
|
|
13
|
+
});
|
|
14
|
+
export async function getMostCalledFunctions(input) {
|
|
15
|
+
const data = await loadProfile(input.filePath);
|
|
16
|
+
const allFunctions = walkTrie(data, input.span);
|
|
17
|
+
// Calculate total calls
|
|
18
|
+
const totalCalls = allFunctions.reduce((sum, f) => sum + f.callCount, 0);
|
|
19
|
+
// Sort by call count
|
|
20
|
+
const sorted = sortByField(allFunctions, 'callCount', input.limit);
|
|
21
|
+
return {
|
|
22
|
+
functions: sorted.map((f, i) => ({
|
|
23
|
+
rank: i + 1,
|
|
24
|
+
name: f.name,
|
|
25
|
+
callPath: f.callPath,
|
|
26
|
+
location: f.file ? formatLocation(f.file, f.line) : undefined,
|
|
27
|
+
callCount: f.callCount,
|
|
28
|
+
totalWallTime: formatTime(f.totalWallTime),
|
|
29
|
+
avgWallTime: formatTime(f.avgWallTime),
|
|
30
|
+
percentOfCalls: totalCalls > 0
|
|
31
|
+
? `${((f.callCount / totalCalls) * 100).toFixed(2)}%`
|
|
32
|
+
: '0%',
|
|
33
|
+
})),
|
|
34
|
+
totalCalls,
|
|
35
|
+
totalFunctions: allFunctions.length,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function formatMostCalledOutput(result) {
|
|
39
|
+
const lines = [
|
|
40
|
+
`# Most Called Functions`,
|
|
41
|
+
``,
|
|
42
|
+
`Total calls: ${formatNumber(result.totalCalls)}`,
|
|
43
|
+
`Total functions analyzed: ${formatNumber(result.totalFunctions)}`,
|
|
44
|
+
``,
|
|
45
|
+
];
|
|
46
|
+
for (const f of result.functions) {
|
|
47
|
+
lines.push(`## ${f.rank}. ${f.name}`);
|
|
48
|
+
lines.push(`- **Call Path**: ${f.callPath}`);
|
|
49
|
+
if (f.location) {
|
|
50
|
+
lines.push(`- **Location**: ${f.location}`);
|
|
51
|
+
}
|
|
52
|
+
lines.push(`- **Calls**: ${formatNumber(f.callCount)} (${f.percentOfCalls})`);
|
|
53
|
+
lines.push(`- **Total Time**: ${f.totalWallTime}`);
|
|
54
|
+
lines.push(`- **Avg Time**: ${f.avgWallTime}`);
|
|
55
|
+
lines.push(``);
|
|
56
|
+
}
|
|
57
|
+
return lines.join('\n');
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=most-called.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"most-called.js","sourceRoot":"","sources":["../../src/tools/most-called.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: detect_n_plus_one
|
|
3
|
+
* Detects N+1 query patterns in the profile
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import type { NPlusOnePattern } from '../decoder/types.js';
|
|
7
|
+
export declare const nPlusOneSchema: z.ZodObject<{
|
|
8
|
+
filePath: z.ZodString;
|
|
9
|
+
span: z.ZodOptional<z.ZodString>;
|
|
10
|
+
minRepetitions: z.ZodDefault<z.ZodNumber>;
|
|
11
|
+
}, "strip", z.ZodTypeAny, {
|
|
12
|
+
filePath: string;
|
|
13
|
+
minRepetitions: number;
|
|
14
|
+
span?: string | undefined;
|
|
15
|
+
}, {
|
|
16
|
+
filePath: string;
|
|
17
|
+
span?: string | undefined;
|
|
18
|
+
minRepetitions?: number | undefined;
|
|
19
|
+
}>;
|
|
20
|
+
export type NPlusOneInput = z.infer<typeof nPlusOneSchema>;
|
|
21
|
+
export interface NPlusOneResult {
|
|
22
|
+
patterns: NPlusOnePattern[];
|
|
23
|
+
totalPatterns: number;
|
|
24
|
+
estimatedWastedTime: string;
|
|
25
|
+
recommendations: string[];
|
|
26
|
+
}
|
|
27
|
+
export declare function detectNPlusOne(input: NPlusOneInput): Promise<NPlusOneResult>;
|
|
28
|
+
export declare function formatNPlusOneOutput(result: NPlusOneResult): string;
|
|
29
|
+
//# sourceMappingURL=n-plus-one.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"n-plus-one.d.ts","sourceRoot":"","sources":["../../src/tools/n-plus-one.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEzE,eAAO,MAAM,cAAc;;;;;;;;;;;;EASzB,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAsB3D,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,wBAAsB,cAAc,CAClC,KAAK,EAAE,aAAa,GACnB,OAAO,CAAC,cAAc,CAAC,CA6DzB;AAuCD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CA+BnE"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: detect_n_plus_one
|
|
3
|
+
* Detects N+1 query patterns in the profile
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { loadProfile } from '../decoder/decompress.js';
|
|
7
|
+
import { walkTrie } from '../decoder/trie-walker.js';
|
|
8
|
+
import { formatTime, formatNumber } from '../utils/formatting.js';
|
|
9
|
+
export const nPlusOneSchema = z.object({
|
|
10
|
+
filePath: z.string().describe('Path to the PerfBase profile file'),
|
|
11
|
+
span: z.string().optional().describe('Filter to a specific span name'),
|
|
12
|
+
minRepetitions: z
|
|
13
|
+
.number()
|
|
14
|
+
.int()
|
|
15
|
+
.positive()
|
|
16
|
+
.default(5)
|
|
17
|
+
.describe('Minimum number of similar queries to flag as N+1'),
|
|
18
|
+
});
|
|
19
|
+
// Database operation patterns
|
|
20
|
+
const DB_OPERATION_PATTERNS = [
|
|
21
|
+
/^PDO::query$/i,
|
|
22
|
+
/^PDO::exec$/i,
|
|
23
|
+
/^PDOStatement::execute$/i,
|
|
24
|
+
/^mysqli::query$/i,
|
|
25
|
+
/^mysqli_query$/i,
|
|
26
|
+
/^mysqli_stmt::execute$/i,
|
|
27
|
+
/^pg_query/i,
|
|
28
|
+
/^pg_execute/i,
|
|
29
|
+
/->query\(/,
|
|
30
|
+
/->execute\(/,
|
|
31
|
+
/->find\(/,
|
|
32
|
+
/->findOne\(/,
|
|
33
|
+
];
|
|
34
|
+
function isQueryOperation(name) {
|
|
35
|
+
return DB_OPERATION_PATTERNS.some((pattern) => pattern.test(name));
|
|
36
|
+
}
|
|
37
|
+
export async function detectNPlusOne(input) {
|
|
38
|
+
const data = await loadProfile(input.filePath);
|
|
39
|
+
const allFunctions = walkTrie(data, input.span);
|
|
40
|
+
// Find database query functions
|
|
41
|
+
const queryFunctions = allFunctions.filter((f) => isQueryOperation(f.name));
|
|
42
|
+
// Group by similar call paths (parent context)
|
|
43
|
+
const pathGroups = new Map();
|
|
44
|
+
for (const f of queryFunctions) {
|
|
45
|
+
// Extract parent path (everything before the query function)
|
|
46
|
+
const parentPath = extractParentPath(f.callPath);
|
|
47
|
+
const key = `${f.name}|${parentPath}`;
|
|
48
|
+
if (!pathGroups.has(key)) {
|
|
49
|
+
pathGroups.set(key, []);
|
|
50
|
+
}
|
|
51
|
+
pathGroups.get(key).push(f);
|
|
52
|
+
}
|
|
53
|
+
// Identify N+1 patterns
|
|
54
|
+
const patterns = [];
|
|
55
|
+
let totalWastedTime = 0;
|
|
56
|
+
for (const [key, functions] of pathGroups) {
|
|
57
|
+
// Sum up all calls from this pattern
|
|
58
|
+
const totalCalls = functions.reduce((sum, f) => sum + f.callCount, 0);
|
|
59
|
+
if (totalCalls >= input.minRepetitions) {
|
|
60
|
+
const totalTime = functions.reduce((sum, f) => sum + f.totalWallTime, 0);
|
|
61
|
+
const [queryPattern] = key.split('|');
|
|
62
|
+
patterns.push({
|
|
63
|
+
queryPattern,
|
|
64
|
+
count: totalCalls,
|
|
65
|
+
totalTime,
|
|
66
|
+
callPaths: [...new Set(functions.map((f) => f.callPath))].slice(0, 5),
|
|
67
|
+
});
|
|
68
|
+
// Estimate wasted time (assuming 1 query would be enough)
|
|
69
|
+
// The "wasted" time is the time spent on N-1 extra queries
|
|
70
|
+
if (totalCalls > 1) {
|
|
71
|
+
const avgTime = totalTime / totalCalls;
|
|
72
|
+
totalWastedTime += avgTime * (totalCalls - 1);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Sort by count descending
|
|
77
|
+
patterns.sort((a, b) => b.count - a.count);
|
|
78
|
+
// Generate recommendations
|
|
79
|
+
const recommendations = generateRecommendations(patterns);
|
|
80
|
+
return {
|
|
81
|
+
patterns: patterns.slice(0, 20),
|
|
82
|
+
totalPatterns: patterns.length,
|
|
83
|
+
estimatedWastedTime: formatTime(totalWastedTime),
|
|
84
|
+
recommendations,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function extractParentPath(callPath) {
|
|
88
|
+
const parts = callPath.split(' > ');
|
|
89
|
+
// Return all but the last element (the query function itself)
|
|
90
|
+
return parts.slice(0, -1).join(' > ');
|
|
91
|
+
}
|
|
92
|
+
function generateRecommendations(patterns) {
|
|
93
|
+
const recommendations = [];
|
|
94
|
+
if (patterns.length === 0) {
|
|
95
|
+
recommendations.push('No N+1 patterns detected in this profile.');
|
|
96
|
+
return recommendations;
|
|
97
|
+
}
|
|
98
|
+
const topPattern = patterns[0];
|
|
99
|
+
recommendations.push(`Found ${patterns.length} potential N+1 query pattern(s).`);
|
|
100
|
+
if (topPattern.count >= 100) {
|
|
101
|
+
recommendations.push(`Critical: "${topPattern.queryPattern}" is called ${topPattern.count} times. Consider using eager loading or batch queries.`);
|
|
102
|
+
}
|
|
103
|
+
else if (topPattern.count >= 20) {
|
|
104
|
+
recommendations.push(`Warning: "${topPattern.queryPattern}" is called ${topPattern.count} times. Consider optimizing with JOINs or preloading.`);
|
|
105
|
+
}
|
|
106
|
+
recommendations.push('Common solutions: Use eager loading (e.g., Eloquent with(), Doctrine JOIN FETCH), batch queries, or query caching.');
|
|
107
|
+
return recommendations;
|
|
108
|
+
}
|
|
109
|
+
export function formatNPlusOneOutput(result) {
|
|
110
|
+
const lines = [
|
|
111
|
+
`# N+1 Query Detection`,
|
|
112
|
+
``,
|
|
113
|
+
`**Patterns Found**: ${result.totalPatterns}`,
|
|
114
|
+
`**Estimated Wasted Time**: ${result.estimatedWastedTime}`,
|
|
115
|
+
``,
|
|
116
|
+
`## Recommendations`,
|
|
117
|
+
``,
|
|
118
|
+
];
|
|
119
|
+
for (const rec of result.recommendations) {
|
|
120
|
+
lines.push(`- ${rec}`);
|
|
121
|
+
}
|
|
122
|
+
if (result.patterns.length > 0) {
|
|
123
|
+
lines.push(``, `## Detected Patterns`, ``);
|
|
124
|
+
for (const pattern of result.patterns) {
|
|
125
|
+
lines.push(`### ${pattern.queryPattern}`);
|
|
126
|
+
lines.push(`- **Repeated**: ${formatNumber(pattern.count)} times`);
|
|
127
|
+
lines.push(`- **Total Time**: ${formatTime(pattern.totalTime)}`);
|
|
128
|
+
lines.push(`- **Example Paths**:`);
|
|
129
|
+
for (const path of pattern.callPaths) {
|
|
130
|
+
lines.push(` - ${path}`);
|
|
131
|
+
}
|
|
132
|
+
lines.push(``);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return lines.join('\n');
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=n-plus-one.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"n-plus-one.js","sourceRoot":"","sources":["../../src/tools/n-plus-one.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: get_slowest_functions
|
|
3
|
+
* Returns functions sorted by total wall time
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
export declare const slowestFunctionsSchema: z.ZodObject<{
|
|
7
|
+
filePath: z.ZodString;
|
|
8
|
+
span: z.ZodOptional<z.ZodString>;
|
|
9
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
filePath: string;
|
|
12
|
+
limit: number;
|
|
13
|
+
span?: string | undefined;
|
|
14
|
+
}, {
|
|
15
|
+
filePath: string;
|
|
16
|
+
span?: string | undefined;
|
|
17
|
+
limit?: number | undefined;
|
|
18
|
+
}>;
|
|
19
|
+
export type SlowestFunctionsInput = z.infer<typeof slowestFunctionsSchema>;
|
|
20
|
+
export interface SlowestFunctionsResult {
|
|
21
|
+
functions: Array<{
|
|
22
|
+
rank: number;
|
|
23
|
+
name: string;
|
|
24
|
+
callPath: string;
|
|
25
|
+
location?: string;
|
|
26
|
+
callCount: number;
|
|
27
|
+
totalWallTime: string;
|
|
28
|
+
avgWallTime: string;
|
|
29
|
+
percentOfTotal: string;
|
|
30
|
+
}>;
|
|
31
|
+
totalWallTime: string;
|
|
32
|
+
totalFunctions: number;
|
|
33
|
+
}
|
|
34
|
+
export declare function getSlowestFunctions(input: SlowestFunctionsInput): Promise<SlowestFunctionsResult>;
|
|
35
|
+
export declare function formatSlowestFunctionsOutput(result: SlowestFunctionsResult): string;
|
|
36
|
+
//# sourceMappingURL=slowest-functions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slowest-functions.d.ts","sourceRoot":"","sources":["../../src/tools/slowest-functions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,sBAAsB;;;;;;;;;;;;EAIjC,CAAC;AAEH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE3E,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,KAAK,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC,CAAC;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,sBAAsB,CAAC,CA2BjC;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,CAsBnF"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: get_slowest_functions
|
|
3
|
+
* Returns functions sorted by total wall time
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { loadProfile } from '../decoder/decompress.js';
|
|
7
|
+
import { walkTrie, sortByField } from '../decoder/trie-walker.js';
|
|
8
|
+
import { formatTime, formatNumber, formatLocation } from '../utils/formatting.js';
|
|
9
|
+
export const slowestFunctionsSchema = z.object({
|
|
10
|
+
filePath: z.string().describe('Path to the PerfBase profile file'),
|
|
11
|
+
span: z.string().optional().describe('Filter to a specific span name'),
|
|
12
|
+
limit: z.number().int().positive().default(20).describe('Maximum number of results'),
|
|
13
|
+
});
|
|
14
|
+
export async function getSlowestFunctions(input) {
|
|
15
|
+
const data = await loadProfile(input.filePath);
|
|
16
|
+
const allFunctions = walkTrie(data, input.span);
|
|
17
|
+
// Calculate total wall time
|
|
18
|
+
const totalTime = allFunctions.reduce((sum, f) => sum + f.totalWallTime, 0);
|
|
19
|
+
// Sort by total wall time
|
|
20
|
+
const sorted = sortByField(allFunctions, 'totalWallTime', input.limit);
|
|
21
|
+
return {
|
|
22
|
+
functions: sorted.map((f, i) => ({
|
|
23
|
+
rank: i + 1,
|
|
24
|
+
name: f.name,
|
|
25
|
+
callPath: f.callPath,
|
|
26
|
+
location: f.file ? formatLocation(f.file, f.line) : undefined,
|
|
27
|
+
callCount: f.callCount,
|
|
28
|
+
totalWallTime: formatTime(f.totalWallTime),
|
|
29
|
+
avgWallTime: formatTime(f.avgWallTime),
|
|
30
|
+
percentOfTotal: totalTime > 0
|
|
31
|
+
? `${((f.totalWallTime / totalTime) * 100).toFixed(2)}%`
|
|
32
|
+
: '0%',
|
|
33
|
+
})),
|
|
34
|
+
totalWallTime: formatTime(totalTime),
|
|
35
|
+
totalFunctions: allFunctions.length,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function formatSlowestFunctionsOutput(result) {
|
|
39
|
+
const lines = [
|
|
40
|
+
`# Slowest Functions (by wall time)`,
|
|
41
|
+
``,
|
|
42
|
+
`Total wall time: ${result.totalWallTime}`,
|
|
43
|
+
`Total functions analyzed: ${formatNumber(result.totalFunctions)}`,
|
|
44
|
+
``,
|
|
45
|
+
];
|
|
46
|
+
for (const f of result.functions) {
|
|
47
|
+
lines.push(`## ${f.rank}. ${f.name}`);
|
|
48
|
+
lines.push(`- **Call Path**: ${f.callPath}`);
|
|
49
|
+
if (f.location) {
|
|
50
|
+
lines.push(`- **Location**: ${f.location}`);
|
|
51
|
+
}
|
|
52
|
+
lines.push(`- **Calls**: ${formatNumber(f.callCount)}`);
|
|
53
|
+
lines.push(`- **Total Time**: ${f.totalWallTime} (${f.percentOfTotal})`);
|
|
54
|
+
lines.push(`- **Avg Time**: ${f.avgWallTime}`);
|
|
55
|
+
lines.push(``);
|
|
56
|
+
}
|
|
57
|
+
return lines.join('\n');
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=slowest-functions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slowest-functions.js","sourceRoot":"","sources":["../../src/tools/slowest-functions.ts"],"names":[],"mappings":"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"}
|