func main() { flag.Parse() data, err := ioutil.ReadFile(*flagProg) if err != nil { fmt.Fprintf(os.Stderr, "failed to read prog file: %v\n", err) os.Exit(1) } p, err := prog.Deserialize(data) if err != nil { fmt.Fprintf(os.Stderr, "failed to deserialize the program: %v\n", err) os.Exit(1) } var flags uint64 if *flagThreaded { flags |= ipc.FlagThreaded } if *flagDebug { flags |= ipc.FlagDebug } if *flagStrace { flags |= ipc.FlagStrace } if *flagCover != "" { flags |= ipc.FlagCover } if *flagDedup { flags |= ipc.FlagDedupCover } env, err := ipc.MakeEnv(*flagExecutor, *flagTimeout, flags) if err != nil { fmt.Fprintf(os.Stderr, "failed to create execution environment: %v\n", err) os.Exit(1) } defer env.Close() output, strace, cov, failed, hanged, err := env.Exec(p) fmt.Printf("result: failed=%v hanged=%v err=%v\n\n%s", failed, hanged, err, output) if *flagStrace { fmt.Printf("strace output:\n%s", strace) } // Coverage is dumped in sanitizer format. // github.com/google/sanitizers/tools/sancov command can be used to dump PCs, // then they can be piped via addr2line to symbolize. for i, c := range cov { fmt.Printf("call #%v: coverage %v\n", i, len(c)) if len(c) == 0 { continue } buf := new(bytes.Buffer) binary.Write(buf, binary.LittleEndian, uint64(0xC0BFFFFFFFFFFF64)) for _, pc := range c { binary.Write(buf, binary.LittleEndian, cover.RestorePC(pc)) } err := ioutil.WriteFile(fmt.Sprintf("%v.%v", *flagCover, i), buf.Bytes(), 0660) if err != nil { fmt.Fprintf(os.Stderr, "failed to write coverage file: %v\n", err) os.Exit(1) } } }
func symbolize(vmlinux string, cov []uint32) error { cmd := exec.Command("addr2line", "-a", "-i", "-e", vmlinux) stdin, err := cmd.StdinPipe() if err != nil { return err } defer stdin.Close() stdout, err := cmd.StdoutPipe() if err != nil { return err } defer stdout.Close() if err := cmd.Start(); err != nil { return err } defer cmd.Wait() go func() { for _, pc := range cov { fmt.Fprintf(stdin, "0x%x\n", cover.RestorePC(pc)-1) } stdin.Close() }() s := bufio.NewScanner(stdout) var pc uint32 for s.Scan() { ln := s.Text() if len(ln) > 3 && ln[0] == '0' && ln[1] == 'x' { v, err := strconv.ParseUint(ln, 0, 64) if err != nil { return fmt.Errorf("failed to parse pc in addr2line output: %v", err) } pc = uint32(v) + 1 continue } colon := strings.IndexByte(ln, ':') if colon == -1 { continue } file := ln[:colon] line, err := strconv.Atoi(ln[colon+1:]) if err != nil || pc == 0 || file == "" || file == "??" || line <= 0 { continue } pcLines[pc] = append(pcLines[pc], LineInfo{file, line}) } if err := s.Err(); err != nil { return err } return nil }
func symbolize(vmlinux string, cov []uint32) ([]LineInfo, string, error) { cmd := exec.Command("addr2line", "-a", "-i", "-e", vmlinux) stdin, err := cmd.StdinPipe() if err != nil { return nil, "", err } defer stdin.Close() stdout, err := cmd.StdoutPipe() if err != nil { return nil, "", err } defer stdout.Close() if err := cmd.Start(); err != nil { return nil, "", err } defer cmd.Wait() go func() { for _, pc := range cov { fmt.Fprintf(stdin, "0x%x\n", cover.RestorePC(pc)-1) } stdin.Close() }() var info []LineInfo prefix := "" s := bufio.NewScanner(stdout) var pc uint32 for s.Scan() { ln := s.Text() if len(ln) > 3 && ln[0] == '0' && ln[1] == 'x' { v, err := strconv.ParseUint(ln, 0, 64) if err != nil { return nil, "", fmt.Errorf("failed to parse pc in addr2line output: %v", err) } pc = uint32(v) + 1 continue } colon := strings.IndexByte(ln, ':') if colon == -1 { continue } file := ln[:colon] line, err := strconv.Atoi(ln[colon+1:]) if err != nil || pc == 0 || file == "" || file == "??" || line <= 0 { continue } info = append(info, LineInfo{file, line}) if prefix == "" { prefix = file } else { i := 0 for ; i < len(prefix) && i < len(file); i++ { if prefix[i] != file[i] { break } } prefix = prefix[:i] } } if err := s.Err(); err != nil { return nil, "", err } return info, prefix, nil }
func main() { flag.Parse() if len(flag.Args()) == 0 { fmt.Fprintf(os.Stderr, "usage: execprog [flags] file-with-programs+\n") flag.PrintDefaults() os.Exit(1) } var progs []*prog.Prog for _, fn := range flag.Args() { data, err := ioutil.ReadFile(fn) if err != nil { Fatalf("failed to read log file: %v", err) } entries := prog.ParseLog(data) for _, ent := range entries { progs = append(progs, ent.P) } } Logf(0, "parsed %v programs", len(progs)) if len(progs) == 0 { return } flags, timeout, err := ipc.DefaultFlags() if err != nil { Fatalf("%v", err) } if *flagCoverFile != "" { flags |= ipc.FlagCover flags &= ^ipc.FlagDedupCover } var wg sync.WaitGroup wg.Add(*flagProcs) var posMu, logMu sync.Mutex gate := ipc.NewGate(2**flagProcs, nil) var pos int var lastPrint time.Time var shutdown uint32 for p := 0; p < *flagProcs; p++ { pid := p go func() { defer wg.Done() env, err := ipc.MakeEnv(*flagExecutor, timeout, flags, pid) if err != nil { Fatalf("failed to create ipc env: %v", err) } defer env.Close() for { if !func() bool { // Limit concurrency window. ticket := gate.Enter() defer gate.Leave(ticket) posMu.Lock() idx := pos pos++ if idx%len(progs) == 0 && time.Since(lastPrint) > 5*time.Second { Logf(0, "executed programs: %v", idx) lastPrint = time.Now() } posMu.Unlock() if *flagRepeat > 0 && idx >= len(progs)**flagRepeat { return false } p := progs[idx%len(progs)] switch *flagOutput { case "stdout": data := p.Serialize() logMu.Lock() Logf(0, "executing program %v:\n%s", pid, data) logMu.Unlock() } output, cov, _, failed, hanged, err := env.Exec(p) if atomic.LoadUint32(&shutdown) != 0 { return false } if failed { fmt.Printf("BUG: executor-detected bug:\n%s", output) } if flags&ipc.FlagDebug != 0 || err != nil { fmt.Printf("result: failed=%v hanged=%v err=%v\n\n%s", failed, hanged, err, output) } if *flagCoverFile != "" { // Coverage is dumped in sanitizer format. // github.com/google/sanitizers/tools/sancov command can be used to dump PCs, // then they can be piped via addr2line to symbolize. for i, c := range cov { fmt.Printf("call #%v: coverage %v\n", i, len(c)) if len(c) == 0 { continue } buf := new(bytes.Buffer) binary.Write(buf, binary.LittleEndian, uint64(0xC0BFFFFFFFFFFF64)) for _, pc := range c { binary.Write(buf, binary.LittleEndian, cover.RestorePC(pc, 0xffffffff)) } err := ioutil.WriteFile(fmt.Sprintf("%v.%v", *flagCoverFile, i), buf.Bytes(), 0660) if err != nil { Fatalf("failed to write coverage file: %v", err) } } } return true }() { return } } }() } go func() { c := make(chan os.Signal, 2) signal.Notify(c, syscall.SIGINT) <-c Logf(0, "shutting down...") atomic.StoreUint32(&shutdown, 1) <-c Fatalf("terminating") }() wg.Wait() }
func generateCoverHtml(w io.Writer, vmlinux string, cov []uint32) error { if len(cov) == 0 { return fmt.Errorf("No coverage data available") } base, err := getVmOffset(vmlinux) if err != nil { return err } pcs := make([]uint64, len(cov)) for i, pc := range cov { pcs[i] = cover.RestorePC(pc, base) - 1 } allPcs, err := allPcsInFuncs(vmlinux, pcs) if err != nil { return err } frames, prefix, err := symbolize(vmlinux, pcs) if err != nil { return err } if len(frames) == 0 { return fmt.Errorf("'%s' does not have debug info (set CONFIG_DEBUG_INFO=y)", vmlinux) } allFrames, prefix, err := symbolize(vmlinux, allPcs) if err != nil { return err } var d templateData for f, covered := range fileSet(frames, allFrames) { lines, err := parseFile(f) if err != nil { return err } coverage := 0 var buf bytes.Buffer for i, ln := range lines { if len(covered) > 0 && covered[0].line == i+1 { if covered[0].covered { buf.Write([]byte("<span id='covered'>")) buf.Write(ln) buf.Write([]byte("</span> /*covered*/\n")) coverage++ } else { buf.Write([]byte("<span id='uncovered'>")) buf.Write(ln) buf.Write([]byte("</span>\n")) } covered = covered[1:] } else { buf.Write(ln) buf.Write([]byte{'\n'}) } } if len(f) > len(prefix) { f = f[len(prefix):] } d.Files = append(d.Files, &templateFile{ Name: f, Body: template.HTML(buf.String()), Coverage: coverage, }) } sort.Sort(templateFileArray(d.Files)) if err := coverTemplate.Execute(w, d); err != nil { return err } return nil }
func main() { flag.Parse() if len(flag.Args()) == 0 { fmt.Fprintf(os.Stderr, "usage: execprog [flags] file-with-programs+\n") flag.PrintDefaults() os.Exit(1) } var progs []*prog.Prog for _, fn := range flag.Args() { data, err := ioutil.ReadFile(fn) if err != nil { log.Fatalf("failed to read log file: %v", err) } entries := prog.ParseLog(data) for _, ent := range entries { progs = append(progs, ent.P) } } log.Printf("parsed %v programs", len(progs)) if len(progs) == 0 { return } flags, timeout := ipc.DefaultFlags() if *flagCoverFile != "" { flags |= ipc.FlagCover flags &= ^ipc.FlagDedupCover } var wg sync.WaitGroup wg.Add(*flagProcs) var posMu sync.Mutex var pos int var lastPrint time.Time for p := 0; p < *flagProcs; p++ { go func() { env, err := ipc.MakeEnv(*flagExecutor, timeout, flags) if err != nil { log.Fatalf("failed to create ipc env: %v", err) } for { posMu.Lock() idx := pos pos++ if idx%len(progs) == 0 && time.Since(lastPrint) > 5*time.Second { log.Printf("executed %v programs\n", idx) lastPrint = time.Now() } posMu.Unlock() if *flagRepeat > 0 && idx >= len(progs)**flagRepeat { env.Close() wg.Done() return } p := progs[idx%len(progs)] output, cov, _, failed, hanged, err := env.Exec(p) if failed { fmt.Printf("BUG: executor-detected bug:\n%s", output) } if flags&ipc.FlagDebug != 0 || err != nil { fmt.Printf("result: failed=%v hanged=%v err=%v\n\n%s", failed, hanged, err, output) } if *flagCoverFile != "" { // Coverage is dumped in sanitizer format. // github.com/google/sanitizers/tools/sancov command can be used to dump PCs, // then they can be piped via addr2line to symbolize. for i, c := range cov { fmt.Printf("call #%v: coverage %v\n", i, len(c)) if len(c) == 0 { continue } buf := new(bytes.Buffer) binary.Write(buf, binary.LittleEndian, uint64(0xC0BFFFFFFFFFFF64)) for _, pc := range c { binary.Write(buf, binary.LittleEndian, cover.RestorePC(pc)) } err := ioutil.WriteFile(fmt.Sprintf("%v.%v", *flagCoverFile, i), buf.Bytes(), 0660) if err != nil { log.Fatalf("failed to write coverage file: %v", err) } } } } }() } wg.Wait() }
func main() { flag.Parse() if len(flag.Args()) == 0 { fmt.Fprintf(os.Stderr, "usage: execprog [flags] file-with-programs*\n") flag.PrintDefaults() os.Exit(1) } var progs []*prog.Prog for _, fn := range flag.Args() { progs = append(progs, parseFile(fn)...) } log.Printf("parsed %v programs", len(progs)) if len(progs) == 0 { return } var flags uint64 if *flagThreaded { flags |= ipc.FlagThreaded } if *flagCollide { flags |= ipc.FlagCollide } if *flagDebug { flags |= ipc.FlagDebug } if *flagStrace { flags |= ipc.FlagStrace } if *flagCover || *flagCoverFile != "" { flags |= ipc.FlagCover } if *flagDedup { flags |= ipc.FlagDedupCover } if *flagNobody { flags |= ipc.FlagDropPrivs } var wg sync.WaitGroup wg.Add(*flagProcs) var pos uint32 for p := 0; p < *flagProcs; p++ { go func() { env, err := ipc.MakeEnv(*flagExecutor, *flagTimeout, flags) if err != nil { log.Fatalf("failed to create ipc env: %v", err) } for { idx := int(atomic.AddUint32(&pos, 1) - 1) if idx%len(progs) == 0 { log.Printf("executed %v programs\n", idx) } if !*flagLoop && idx >= len(progs) { env.Close() wg.Done() return } p := progs[idx%len(progs)] output, strace, cov, failed, hanged, err := env.Exec(p) if *flagDebug || err != nil { fmt.Printf("result: failed=%v hanged=%v err=%v\n\n%s", failed, hanged, err, output) } if *flagStrace { fmt.Printf("strace output:\n%s", strace) } if *flagCoverFile != "" { // Coverage is dumped in sanitizer format. // github.com/google/sanitizers/tools/sancov command can be used to dump PCs, // then they can be piped via addr2line to symbolize. for i, c := range cov { fmt.Printf("call #%v: coverage %v\n", i, len(c)) if len(c) == 0 { continue } buf := new(bytes.Buffer) binary.Write(buf, binary.LittleEndian, uint64(0xC0BFFFFFFFFFFF64)) for _, pc := range c { binary.Write(buf, binary.LittleEndian, cover.RestorePC(pc)) } err := ioutil.WriteFile(fmt.Sprintf("%v.%v", *flagCoverFile, i), buf.Bytes(), 0660) if err != nil { log.Fatalf("failed to write coverage file: %v", err) } } } } }() } wg.Wait() }