package main import ( "encoding/json" "fmt" "strings" ) // TestTraceSpecs provides test trace specifications for unit testing the BCC-style tracing // These are used to validate the tracing functionality without requiring remote API calls var TestTraceSpecs = map[string]TraceSpec{ // Basic system call tracing for testing "test_sys_open": { ProbeType: "p", Target: "__x64_sys_openat", Format: "opening file: %s", Arguments: []string{"arg2@user"}, // filename Duration: 5, // Short duration for testing }, "test_sys_read": { ProbeType: "p", Target: "__x64_sys_read", Format: "read %d bytes from fd %d", Arguments: []string{"arg3", "arg1"}, // count, fd Filter: "arg3 > 100", // Only reads >100 bytes for testing Duration: 5, }, "test_sys_write": { ProbeType: "p", Target: "__x64_sys_write", Format: "write %d bytes to fd %d", Arguments: []string{"arg3", "arg1"}, // count, fd Duration: 5, }, "test_process_creation": { ProbeType: "p", Target: "__x64_sys_execve", Format: "exec: %s", Arguments: []string{"arg1@user"}, // filename Duration: 5, }, // Test with different probe types "test_kretprobe": { ProbeType: "r", Target: "__x64_sys_openat", Format: "open returned: %d", Arguments: []string{"retval"}, Duration: 5, }, "test_with_filter": { ProbeType: "p", Target: "__x64_sys_write", Format: "stdout write: %d bytes", Arguments: []string{"arg3"}, Filter: "arg1 == 1", // Only stdout writes Duration: 5, }, } // GetTestSpec returns a pre-defined test trace specification func GetTestSpec(name string) (TraceSpec, bool) { spec, exists := TestTraceSpecs[name] return spec, exists } // ListTestSpecs returns all available test trace specifications func ListTestSpecs() map[string]string { descriptions := map[string]string{ "test_sys_open": "Test file open operations", "test_sys_read": "Test read operations (>100 bytes)", "test_sys_write": "Test write operations", "test_process_creation": "Test process execution", "test_kretprobe": "Test kretprobe on file open", "test_with_filter": "Test filtered writes to stdout", } return descriptions } // TraceSpecBuilder helps build custom trace specifications type TraceSpecBuilder struct { spec TraceSpec } // NewTraceSpecBuilder creates a new trace specification builder func NewTraceSpecBuilder() *TraceSpecBuilder { return &TraceSpecBuilder{ spec: TraceSpec{ ProbeType: "p", // Default to kprobe Duration: 30, // Default 30 seconds }, } } // Kprobe sets up a kernel probe func (b *TraceSpecBuilder) Kprobe(function string) *TraceSpecBuilder { b.spec.ProbeType = "p" b.spec.Target = function return b } // Kretprobe sets up a kernel return probe func (b *TraceSpecBuilder) Kretprobe(function string) *TraceSpecBuilder { b.spec.ProbeType = "r" b.spec.Target = function return b } // Tracepoint sets up a tracepoint func (b *TraceSpecBuilder) Tracepoint(category, name string) *TraceSpecBuilder { b.spec.ProbeType = "t" b.spec.Target = fmt.Sprintf("%s:%s", category, name) return b } // Uprobe sets up a userspace probe func (b *TraceSpecBuilder) Uprobe(library, function string) *TraceSpecBuilder { b.spec.ProbeType = "u" b.spec.Library = library b.spec.Target = function return b } // Format sets the output format string func (b *TraceSpecBuilder) Format(format string, args ...string) *TraceSpecBuilder { b.spec.Format = format b.spec.Arguments = args return b } // Filter adds a filter condition func (b *TraceSpecBuilder) Filter(condition string) *TraceSpecBuilder { b.spec.Filter = condition return b } // Duration sets the trace duration in seconds func (b *TraceSpecBuilder) Duration(seconds int) *TraceSpecBuilder { b.spec.Duration = seconds return b } // PID filters by process ID func (b *TraceSpecBuilder) PID(pid int) *TraceSpecBuilder { b.spec.PID = pid return b } // UID filters by user ID func (b *TraceSpecBuilder) UID(uid int) *TraceSpecBuilder { b.spec.UID = uid return b } // ProcessName filters by process name func (b *TraceSpecBuilder) ProcessName(name string) *TraceSpecBuilder { b.spec.ProcessName = name return b } // Build returns the constructed trace specification func (b *TraceSpecBuilder) Build() TraceSpec { return b.spec } // TraceSpecParser parses trace specifications from various formats type TraceSpecParser struct{} // NewTraceSpecParser creates a new parser func NewTraceSpecParser() *TraceSpecParser { return &TraceSpecParser{} } // ParseFromBCCStyle parses BCC trace.py style specifications // Examples: // // "sys_open" -> trace sys_open syscall // "p::do_sys_open" -> kprobe on do_sys_open // "r::do_sys_open" -> kretprobe on do_sys_open // "t:syscalls:sys_enter_open" -> tracepoint // "sys_read (arg3 > 1024)" -> with filter // "sys_read \"read %d bytes\", arg3" -> with format func (p *TraceSpecParser) ParseFromBCCStyle(spec string) (TraceSpec, error) { result := TraceSpec{ ProbeType: "p", Duration: 30, } // Split by quotes to separate format string parts := strings.Split(spec, "\"") var probeSpec string if len(parts) >= 1 { probeSpec = strings.TrimSpace(parts[0]) } var formatPart string if len(parts) >= 2 { formatPart = parts[1] } var argsPart string if len(parts) >= 3 { argsPart = strings.TrimSpace(parts[2]) if strings.HasPrefix(argsPart, ",") { argsPart = strings.TrimSpace(argsPart[1:]) } } // Parse probe specification if err := p.parseProbeSpec(probeSpec, &result); err != nil { return result, err } // Parse format string if formatPart != "" { result.Format = formatPart } // Parse arguments if argsPart != "" { result.Arguments = p.parseArguments(argsPart) } return result, nil } // parseProbeSpec parses the probe specification part func (p *TraceSpecParser) parseProbeSpec(spec string, result *TraceSpec) error { // Handle filter conditions in parentheses if idx := strings.Index(spec, "("); idx != -1 { filterEnd := strings.LastIndex(spec, ")") if filterEnd > idx { result.Filter = strings.TrimSpace(spec[idx+1 : filterEnd]) spec = strings.TrimSpace(spec[:idx]) } } // Parse probe type and target if strings.Contains(spec, ":") { parts := strings.SplitN(spec, ":", 3) if len(parts) >= 1 && parts[0] != "" { switch parts[0] { case "p": result.ProbeType = "p" case "r": result.ProbeType = "r" case "t": result.ProbeType = "t" case "u": result.ProbeType = "u" default: return fmt.Errorf("unsupported probe type: %s", parts[0]) } } if len(parts) >= 2 { result.Library = parts[1] } if len(parts) >= 3 { result.Target = parts[2] } else if len(parts) == 2 { result.Target = parts[1] result.Library = "" } } else { // Simple function name result.Target = spec // Auto-detect syscall format if strings.HasPrefix(spec, "sys_") && !strings.HasPrefix(spec, "__x64_sys_") { result.Target = "__x64_sys_" + spec[4:] } } return nil } // parseArguments parses the arguments part func (p *TraceSpecParser) parseArguments(args string) []string { var result []string // Split by comma and clean up parts := strings.Split(args, ",") for _, part := range parts { arg := strings.TrimSpace(part) if arg != "" { result = append(result, arg) } } return result } // ParseFromJSON parses trace specification from JSON func (p *TraceSpecParser) ParseFromJSON(jsonData []byte) (TraceSpec, error) { var spec TraceSpec err := json.Unmarshal(jsonData, &spec) return spec, err } // GetCommonSpec returns a pre-defined test trace specification (renamed for backward compatibility) func GetCommonSpec(name string) (TraceSpec, bool) { // Map old names to new test names for compatibility testName := name if strings.HasPrefix(name, "trace_") { testName = strings.Replace(name, "trace_", "test_", 1) } spec, exists := TestTraceSpecs[testName] return spec, exists } // ListCommonSpecs returns all available test trace specifications (renamed for backward compatibility) func ListCommonSpecs() map[string]string { return ListTestSpecs() } // ValidateTraceSpec validates a trace specification func ValidateTraceSpec(spec TraceSpec) error { if spec.Target == "" { return fmt.Errorf("target function/syscall is required") } if spec.Duration <= 0 { return fmt.Errorf("duration must be positive") } if spec.Duration > 600 { // 10 minutes max return fmt.Errorf("duration too long (max 600 seconds)") } switch spec.ProbeType { case "p", "r", "t", "u": // Valid probe types case "": // Default to kprobe default: return fmt.Errorf("unsupported probe type: %s", spec.ProbeType) } if spec.ProbeType == "u" && spec.Library == "" { return fmt.Errorf("library required for userspace probes") } if spec.ProbeType == "t" && !strings.Contains(spec.Target, ":") { return fmt.Errorf("tracepoint requires format 'category:name'") } return nil } // SuggestSyscallTargets suggests syscall targets based on the issue description func SuggestSyscallTargets(issueDescription string) []string { description := strings.ToLower(issueDescription) var suggestions []string // File I/O issues if strings.Contains(description, "file") || strings.Contains(description, "disk") || strings.Contains(description, "io") { suggestions = append(suggestions, "trace_sys_open", "trace_sys_read", "trace_sys_write", "trace_sys_unlink") } // Network issues if strings.Contains(description, "network") || strings.Contains(description, "socket") || strings.Contains(description, "connection") { suggestions = append(suggestions, "trace_sys_connect", "trace_sys_socket", "trace_sys_bind", "trace_sys_accept") } // Process issues if strings.Contains(description, "process") || strings.Contains(description, "crash") || strings.Contains(description, "exec") { suggestions = append(suggestions, "trace_sys_execve", "trace_sys_clone", "trace_sys_exit", "trace_sys_kill") } // Memory issues if strings.Contains(description, "memory") || strings.Contains(description, "malloc") || strings.Contains(description, "leak") { suggestions = append(suggestions, "trace_sys_mmap", "trace_sys_brk") } // Performance issues - trace common syscalls if strings.Contains(description, "slow") || strings.Contains(description, "performance") || strings.Contains(description, "hang") { suggestions = append(suggestions, "trace_sys_read", "trace_sys_write", "trace_sys_connect", "trace_sys_mmap") } // If no specific suggestions, provide general monitoring if len(suggestions) == 0 { suggestions = append(suggestions, "trace_sys_execve", "trace_sys_open", "trace_sys_connect") } return suggestions }