feat: add shared tool helpers
This commit is contained in:
parent
8d41653be4
commit
c56b803f8c
1 changed files with 79 additions and 0 deletions
79
internal/tools/shared.go
Normal file
79
internal/tools/shared.go
Normal 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, " ")
|
||||
}
|
||||
Loading…
Reference in a new issue