feat: add shared tool helpers

This commit is contained in:
thibaud-leclere 2026-05-12 09:46:15 +02:00
parent 8d41653be4
commit c56b803f8c

79
internal/tools/shared.go Normal file
View file

@ -0,0 +1,79 @@
package tools
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"forge.lclr.dev/AI/xdebug-mcp/internal/cache"
"forge.lclr.dev/AI/xdebug-mcp/internal/cachegrind"
)
// loadProfile returns a parsed Profile from cache or by parsing the file.
func loadProfile(filePath string, c *cache.Cache) (*cachegrind.Profile, error) {
abs, err := filepath.Abs(filePath)
if err != nil {
return nil, fmt.Errorf("resolve path %s: %w", filePath, err)
}
info, err := os.Stat(abs)
if err != nil {
return nil, fmt.Errorf("file not found: %s", abs)
}
if p, ok := c.Get(abs, info.ModTime()); ok {
return p, nil
}
p, err := cachegrind.ParseFile(abs)
if err != nil {
return nil, err
}
c.Set(abs, p, info.ModTime())
return p, nil
}
// findFunctions returns functions matching name (exact first, then contains).
// Returns a non-empty errMsg if nothing is found.
func findFunctions(p *cachegrind.Profile, name string) ([]*cachegrind.Function, string) {
if fns, ok := p.ByName[name]; ok {
return fns, ""
}
var matches []*cachegrind.Function
for _, fn := range p.Functions {
if strings.Contains(fn.Name, name) {
matches = append(matches, fn)
}
}
if len(matches) == 0 {
return nil, fmt.Sprintf("function %q not found in profile (no exact or contains match)", name)
}
return matches, ""
}
// sortedByTime returns a copy of fns sorted by Costs[0] descending.
func sortedByTime(fns []*cachegrind.Function) []*cachegrind.Function {
sorted := make([]*cachegrind.Function, len(fns))
copy(sorted, fns)
sort.Slice(sorted, func(i, j int) bool {
ci, cj := int64(0), int64(0)
if len(sorted[i].Costs) > 0 {
ci = sorted[i].Costs[0]
}
if len(sorted[j].Costs) > 0 {
cj = sorted[j].Costs[0]
}
return ci > cj
})
return sorted
}
// formatCosts formats a cost slice as "Event=value Event=value".
func formatCosts(costs []int64, events []string) string {
parts := make([]string, 0, len(events))
for i, ev := range events {
if i < len(costs) {
parts = append(parts, fmt.Sprintf("%s=%d", ev, costs[i]))
}
}
return strings.Join(parts, " ")
}