gcp-profiler-analyse-performance
Analyze Google Cloud Profiler data to identify performance bottlenecks and optimize application efficiency in cloud environments.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/services/profiler/tools.ts:183-332 (registration)MCP server.tool registration for the 'gcp-profiler-analyse-performance' tool, including title 'Analyse Profile Performance', description, inputSchema with Zod validation for profileType, target, pageSize, and the inline async handler function."gcp-profiler-analyse-performance", { title: "Analyse Profile Performance", description: "Analyse profiles to identify performance patterns, bottlenecks, and optimisation opportunities", inputSchema: { profileType: z .enum([ ProfileType.CPU, ProfileType.WALL, ProfileType.HEAP, ProfileType.THREADS, ProfileType.CONTENTION, ProfileType.PEAK_HEAP, ProfileType.HEAP_ALLOC, ]) .optional() .describe("Focus analysis on specific profile type"), target: z .string() .optional() .describe("Focus analysis on specific deployment target"), pageSize: z .number() .min(1) .max(1000) .default(100) .describe( "Number of profiles to analyse (more profiles = better insights)", ), }, }, async ({ profileType, target, pageSize }) => { try { const projectId = await getProjectId(); // Initialize Google Auth client (same pattern as error reporting) const auth = await initGoogleAuth(true); if (!auth) { throw new GcpMcpError( "Google Cloud authentication not available. Please configure authentication to access profiler data.", "UNAUTHENTICATED", 401, ); } const client = await auth.getClient(); const token = await client.getAccessToken(); // Parse parameters const actualPageSize = pageSize || 100; // Build query parameters for maximum data collection const params = new URLSearchParams({ pageSize: actualPageSize.toString(), }); // Make REST API call to list profiles const apiUrl = `https://cloudprofiler.googleapis.com/v2/projects/${projectId}/profiles?${params}`; const response = await fetch(apiUrl, { method: "GET", headers: { Authorization: `Bearer ${token.token}`, Accept: "application/json", }, }); if (!response.ok) { const errorText = await response.text(); throw new GcpMcpError( `Failed to fetch profiles for analysis: ${errorText}`, "FAILED_PRECONDITION", response.status, ); } const data: ListProfilesResponse = await response.json(); let profiles = data.profiles || []; // Apply filtering if specified if (profileType) { profiles = profiles.filter((p) => p.profileType === profileType); } if (target) { profiles = profiles.filter((p) => p.deployment?.target?.toLowerCase().includes(target.toLowerCase()), ); } if (!profiles || profiles.length === 0) { let filterText = "No profiles found for analysis"; if (profileType) filterText += ` with profile type: ${profileType}`; if (target) filterText += ` and target: ${target}`; return { content: [ { type: "text", text: `# Profile Performance Analysis\n\nProject: ${projectId}\n\n${filterText}.`, }, ], }; } // Generate comprehensive analysis let content = `# Profile Performance Analysis\n\nProject: ${projectId}\n`; if (profileType) content += `Focus: ${getProfileTypeDescription(profileType)}\n`; if (target) content += `Target: ${target}\n`; content += `Analysed: ${profiles.length} profiles\n\n`; // Get detailed analysis const analysis = analyseProfilePatterns(profiles); content += analysis; // Add performance insights specific to the analysis content += `\n## Performance Insights\n\n`; // Analyse profile collection patterns const timeDistribution = analyseProfileTimeDistribution(profiles); content += timeDistribution; // Analyse deployment patterns const deploymentAnalysis = analyseDeploymentPatterns(profiles); content += deploymentAnalysis; // Add actionable recommendations content += `\n## Actionable Recommendations\n\n`; content += getActionableRecommendations(profiles, profileType); return { content: [ { type: "text", text: content, }, ], }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; throw new GcpMcpError( `Failed to analyse profile performance: ${errorMessage}`, "INTERNAL_ERROR", 500, ); } }, );
- src/services/profiler/tools.ts:215-331 (handler)The core handler function that: authenticates with Google Cloud, fetches profiles from the Cloud Profiler API using REST, filters by profileType and target, performs analysis using helper functions like analyseProfilePatterns, analyseProfileTimeDistribution, analyseDeploymentPatterns, generates performance insights and actionable recommendations, formats markdown output.async ({ profileType, target, pageSize }) => { try { const projectId = await getProjectId(); // Initialize Google Auth client (same pattern as error reporting) const auth = await initGoogleAuth(true); if (!auth) { throw new GcpMcpError( "Google Cloud authentication not available. Please configure authentication to access profiler data.", "UNAUTHENTICATED", 401, ); } const client = await auth.getClient(); const token = await client.getAccessToken(); // Parse parameters const actualPageSize = pageSize || 100; // Build query parameters for maximum data collection const params = new URLSearchParams({ pageSize: actualPageSize.toString(), }); // Make REST API call to list profiles const apiUrl = `https://cloudprofiler.googleapis.com/v2/projects/${projectId}/profiles?${params}`; const response = await fetch(apiUrl, { method: "GET", headers: { Authorization: `Bearer ${token.token}`, Accept: "application/json", }, }); if (!response.ok) { const errorText = await response.text(); throw new GcpMcpError( `Failed to fetch profiles for analysis: ${errorText}`, "FAILED_PRECONDITION", response.status, ); } const data: ListProfilesResponse = await response.json(); let profiles = data.profiles || []; // Apply filtering if specified if (profileType) { profiles = profiles.filter((p) => p.profileType === profileType); } if (target) { profiles = profiles.filter((p) => p.deployment?.target?.toLowerCase().includes(target.toLowerCase()), ); } if (!profiles || profiles.length === 0) { let filterText = "No profiles found for analysis"; if (profileType) filterText += ` with profile type: ${profileType}`; if (target) filterText += ` and target: ${target}`; return { content: [ { type: "text", text: `# Profile Performance Analysis\n\nProject: ${projectId}\n\n${filterText}.`, }, ], }; } // Generate comprehensive analysis let content = `# Profile Performance Analysis\n\nProject: ${projectId}\n`; if (profileType) content += `Focus: ${getProfileTypeDescription(profileType)}\n`; if (target) content += `Target: ${target}\n`; content += `Analysed: ${profiles.length} profiles\n\n`; // Get detailed analysis const analysis = analyseProfilePatterns(profiles); content += analysis; // Add performance insights specific to the analysis content += `\n## Performance Insights\n\n`; // Analyse profile collection patterns const timeDistribution = analyseProfileTimeDistribution(profiles); content += timeDistribution; // Analyse deployment patterns const deploymentAnalysis = analyseDeploymentPatterns(profiles); content += deploymentAnalysis; // Add actionable recommendations content += `\n## Actionable Recommendations\n\n`; content += getActionableRecommendations(profiles, profileType); return { content: [ { type: "text", text: content, }, ], }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; throw new GcpMcpError( `Failed to analyse profile performance: ${errorMessage}`, "INTERNAL_ERROR", 500, ); } },
- Input schema using Zod: profileType (optional ProfileType enum), target (optional string for deployment filter), pageSize (optional number 1-1000 default 100).inputSchema: { profileType: z .enum([ ProfileType.CPU, ProfileType.WALL, ProfileType.HEAP, ProfileType.THREADS, ProfileType.CONTENTION, ProfileType.PEAK_HEAP, ProfileType.HEAP_ALLOC, ]) .optional() .describe("Focus analysis on specific profile type"), target: z .string() .optional() .describe("Focus analysis on specific deployment target"), pageSize: z .number() .min(1) .max(1000) .default(100) .describe( "Number of profiles to analyse (more profiles = better insights)", ), },
- analyseProfilePatterns helper: analyses list of profiles for summary stats, type distribution, recent activity, type-specific insights, and generates performance recommendations.export function analyseProfilePatterns(profiles: Profile[]): string { if (!profiles || profiles.length === 0) { return "No profiles found in the specified criteria."; } let analysis = `# Profile Analysis and Performance Insights\n\n`; // Profile summary statistics const totalProfiles = profiles.length; const profileTypes = [...new Set(profiles.map((p) => p.profileType))]; const targets = [ ...new Set(profiles.map((p) => p.deployment?.target).filter(Boolean)), ]; analysis += `## Summary\n\n`; analysis += `- **Total Profiles:** ${totalProfiles}\n`; analysis += `- **Profile Types:** ${profileTypes.length} (${profileTypes.join(", ")})\n`; analysis += `- **Targets:** ${targets.length} (${targets.join(", ")})\n\n`; // Profile type distribution const typeDistribution = profiles.reduce( (acc, profile) => { acc[profile.profileType] = (acc[profile.profileType] || 0) + 1; return acc; }, {} as Record<string, number>, ); analysis += `## Profile Type Distribution\n\n`; Object.entries(typeDistribution) .sort(([, a], [, b]) => b - a) .forEach(([type, count]) => { const percentage = Math.round((count / totalProfiles) * 100); analysis += `- **${getProfileTypeDescription(type)}:** ${count} profiles (${percentage}%)\n`; }); analysis += `\n`; // Recent activity analysis const recentProfiles = profiles .filter((p) => p.startTime) .sort( (a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime(), ) .slice(0, 5); if (recentProfiles.length > 0) { analysis += `## Recent Profile Activity\n\n`; recentProfiles.forEach((profile, index) => { const timeAgo = getTimeAgo(profile.startTime); analysis += `${index + 1}. **${profile.deployment?.target || "Unknown Target"}** - ${getProfileTypeDescription(profile.profileType)} (${timeAgo})\n`; }); analysis += `\n`; } // Performance analysis by profile type analysis += `## Performance Analysis by Profile Type\n\n`; profileTypes.forEach((type) => { const typeProfiles = profiles.filter((p) => p.profileType === type); analysis += `### ${getProfileTypeDescription(type)}\n\n`; analysis += getProfileTypeAnalysis(type, typeProfiles); analysis += `\n`; }); // Recommendations analysis += `## Recommendations\n\n`; analysis += getPerformanceRecommendations(profiles, typeDistribution); return analysis; }
- analyseProfileTimeDistribution helper: groups profiles by time buckets (last 24h, week, older), provides timeline analysis.function analyseProfileTimeDistribution(profiles: Profile[]): string { const profilesByTime = profiles .filter((p) => p.startTime) .sort( (a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime(), ); if (profilesByTime.length === 0) { return "No time-stamped profiles available for temporal analysis.\n\n"; } let analysis = "### Profile Collection Timeline\n\n"; // Group by time buckets (last 24 hours, last week, older) const now = new Date().getTime(); const oneDayAgo = now - 24 * 60 * 60 * 1000; const oneWeekAgo = now - 7 * 24 * 60 * 60 * 1000; const recent = profilesByTime.filter( (p) => new Date(p.startTime).getTime() > oneDayAgo, ); const thisWeek = profilesByTime.filter((p) => { const time = new Date(p.startTime).getTime(); return time <= oneDayAgo && time > oneWeekAgo; }); const older = profilesByTime.filter( (p) => new Date(p.startTime).getTime() <= oneWeekAgo, ); analysis += `- **Last 24 hours:** ${recent.length} profiles\n`; analysis += `- **Last week:** ${thisWeek.length} profiles\n`; analysis += `- **Older:** ${older.length} profiles\n\n`; if (recent.length > 0) { const oldestRecent = new Date(recent[0].startTime).toLocaleString(); const newestRecent = new Date( recent[recent.length - 1].startTime, ).toLocaleString(); analysis += `Recent activity spans from ${oldestRecent} to ${newestRecent}\n\n`; } return analysis; }