microsoft/perf-optimization
Guide for analyzing performance issues based on profiler and code optimizations, including CPU, latency, and throughput. Use this when asked to investigate performance bottlenecks or optimize application performance.
npx skills add https://github.com/microsoft/code-optimizations-skills --skill perf-optimization
When asked to analyze performance issues based on profiler data, follow these steps:
investigation-notes.md with the confirmed values. If only a resource ID is available, resolve the app ID using resolve-app-id.md.ServiceProfilerSample events associated. Run the script in query-slow-requests.md to find the slowest requests that have profiler trace coverage. This gives you a concrete picture of what's slow and what has profiler data available for deeper analysis. The default lookback is 24 hours — adjust the $lookbackHours parameter to widen or narrow the window as needed.> Why start here: This step surfaces real slow requests with profiler traces attached, so you can prioritize which operations to investigate. Use the results to guide the Code Optimization and hot path analysis steps that follow.
> ⚠️ CLI query pitfalls: The az monitor app-insights query CLI has known issues that cause silent failures. Before running or modifying the query script, read az CLI query pitfalls. Key points: (1) --offset is mandatory — without it the CLI applies a 1-hour server-side filter that overrides KQL ago(), (2) always use --output json — --output table silently drops results for join queries, (3) flatten KQL to a single line to avoid here-string truncation.
> ⚠️ No profiler data? If this query returns zero results, check for profiler activity by counting both ServiceProfilerIndex (session-level) and ServiceProfilerSample (request-level) events:
> - Neither event type exists → before recommending the enable-profiler skill, check local source code for an existing profiler configuration. Search for profiler NuGet packages (Microsoft.ApplicationInsights.Profiler.AspNetCore or Azure.Monitor.OpenTelemetry.Profiler in *.csproj) and registration calls (AddServiceProfiler or AddAzureMonitorProfiler in Program.cs/Startup.cs). If profiler code IS present, run check-connection-string-match.md to verify the app's connection string points to the target resource. A mismatch in source code is a troubleshooting signal — note that connection strings are often overridden at deployment time via environment variables, App Service settings, or CI/CD, so a source-code mismatch does not definitively mean the app is sending data elsewhere. Present the finding to the user and ask them to confirm which scenario applies (see guidance in check-connection-string-match.md). Only recommend the enable-profiler skill if the profiler is genuinely not configured in the source code, or if connection strings match (or the mismatch is confirmed as a non-issue) but no events exist.
> - ServiceProfilerIndex exists but no ServiceProfilerSample → the profiler IS running sessions but captured zero request-level samples (e.g., low traffic, no requests during profiling windows). Skip steps 5 and 6 (Code Optimizations and hot path both require request-level samples), but do NOT recommend enabling the profiler — it is already enabled. Instead, suggest the user generate more traffic and wait for the next profiling cycle.
> - In either case, you may still analyze request telemetry (durations, error rates, operation names) from the requests table to give the user a high-level picture of what's slow, but without ServiceProfilerSample data, method-level analysis is not possible.
How to present the results:
> Why ask the user: The user may have domain context you don't — they may know that a particular endpoint is low-priority, or that a specific operation is already being worked on. Letting them choose avoids wasting time on the wrong target.
> If only one result: If the query returned a single slow request, still present it with context and confirm with the user before proceeding.
> ⚠️ Skip this step if no request-level profiler data exists: Code Optimizations are generated from profiler trace data. If no ServiceProfilerSample events were found (regardless of whether ServiceProfilerIndex sessions exist), skip this step — the API will return empty results. If no ServiceProfilerIndex events exist either, recommend the enable-profiler skill.
> If no recommendations are found (but profiler data does exist): This may happen when the profiler hasn't collected enough data yet. Try these fallback steps:
> 1. Widen the time range — increase $startTime to cover the last 7 or 30 days instead of 24 hours.
> 2. Verify the profiler is active — check that Application Insights Profiler is enabled and has recent profiling sessions. If step 3 returned no results either, the profiler may not be enabled.
> 3. Fall back to manual trace analysis — skip to step 6 and invoke the get-profile-hotpath skill directly. Use the slow request IDs from step 3 (if available) to analyze the most expensive operations without Code Optimization guidance.
get-profile-hotpath skill to retrieve the call tree and hot path for specific traces. This is an expensive operation — only invoke it for operations that warrant deep investigation. See Leveraging Profiler Hot Path Data for details.> Bridging request IDs to trace location IDs: The slow requests from step 3 return request IDs, but the get-profile-hotpath skill requires a ServiceProfilerContent trace location ID. To look up trace location IDs for specific request IDs, query Application Insights customEvents filtering by customDimensions.RequestId:
>
> `kql
> customEvents | where name == 'ServiceProfilerSample' | extend reqId = tostring(customDimensions['RequestId']) | where reqId in ('REQUEST_ID_1', 'REQUEST_ID_2') | project timestamp, tostring(customDimensions['ServiceProfilerContent']), reqId
> `
>
> Run this using the same az monitor app-insights query pattern from step 3 (with --offset and --output json). The ServiceProfilerContent value from the results is the trace location ID needed for the hot path skill.
> Set user expectations: The hot path fetch involves multiple API steps (trigger analysis → poll for completion → fetch root tree → expand child nodes) and typically takes 1–2 minutes for fresh analyses. Inform the user upfront that this will take some time. For previously analyzed traces, results are cached and return in seconds.
The get-profile-hotpath skill provides method-level profiler trace data. Invoke it to get the hot path call tree for a specific profiler trace, then use the results here for deeper analysis.
The get-profile-hotpath skill returns a call tree with timing data. Use it as follows:
Values.Metric).TotalCpuTime, TotalAwaitTime, and TotalBlockedTime from the root tree to determine if the issue is CPU-bound, I/O-bound, or contention-bound.> Note on log queries: If you need to query Application Insights logs (requests, dependencies, performanceCounters tables) for additional context beyond what query-slow-requests.md provides, use the Azure portal, Azure CLI (az monitor app-insights query), or the Azure Monitor REST API directly. This skill focuses on profiler-based analysis via the dataplane API.
1. Check investigation-notes.md for previously identified App Insights resource
2. If found, confirm with user; if not, identify and write to investigation notes
3. Resolve app ID from resource ID if needed
4. Query for slow requests with profiler traces (query-slow-requests.md)
5. If zero profiler events (no ServiceProfilerIndex):
a. Check local source for profiler NuGet packages and registration calls
b. If profiler code found → run check-connection-string-match.md
c. If mismatch in source → present as troubleshooting signal (may be overridden at deploy time), ask user to confirm → stop or continue based on user response
d. If no profiler code, or strings match, or mismatch confirmed as non-issue → recommend enable-profiler skill → stop
If ServiceProfilerIndex exists but no ServiceProfilerSample: profiler is running but no request samples → advise on traffic/triggers → stop
6. Present ranked results with rationales; ask user which request(s) to investigate
7. Run get-code-optimizations.md to fetch Code Optimization recommendations
8. Identify the top operations worth deep-diving (from steps 6–7)
9. Invoke get-profile-hotpath skill with the app ID and trace location ID
10. Receive hot path call tree (e.g., WeatherForecastController.Get → 70% in ToList lambda)
11. Combine hot path + recommendations into prioritized action plan
12. Suggest code changes targeting the hot path bottleneck methods
get-profile-hotpath skill to get method-level bottleneck data — but only after Code Optimization recommendations have confirmed the operation is worth investigating.dependencies table or hot path), and those services have their own App Insights resources, suggest the deep-analysis skill. The user can provide the operation ID to get a unified cross-resource timeline showing where time was spent across all services.For the investigation notes format and read/write protocol, see:
For known az monitor app-insights query CLI issues, see:
For detailed guidance on finding application insights resource, see:
For detailed guidance on analyzing Application Insights Profiler traces, see:
For fetching and interpreting profiler hot path call trees, see:
Take microsoft/perf-optimization from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.