aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pkg/report/akaros.go2
-rw-r--r--pkg/report/freebsd.go20
-rw-r--r--pkg/report/fuchsia.go2
-rw-r--r--pkg/report/linux.go38
-rw-r--r--pkg/report/linux_test.go56
-rw-r--r--pkg/report/netbsd.go4
-rw-r--r--pkg/report/report.go19
-rw-r--r--pkg/report/report_test.go26
-rw-r--r--pkg/report/windows.go2
-rw-r--r--pkg/repro/repro.go9
-rw-r--r--tools/syz-symbolize/symbolize.go9
-rw-r--r--vm/vm.go11
12 files changed, 110 insertions, 88 deletions
diff --git a/pkg/report/akaros.go b/pkg/report/akaros.go
index bddc4c9de..605047fae 100644
--- a/pkg/report/akaros.go
+++ b/pkg/report/akaros.go
@@ -31,7 +31,7 @@ func (ctx *akaros) ContainsCrash(output []byte) bool {
panic("not implemented")
}
-func (ctx *akaros) Parse(output []byte) (desc string, text []byte, start int, end int, corrupted bool) {
+func (ctx *akaros) Parse(output []byte) *Report {
panic("not implemented")
}
diff --git a/pkg/report/freebsd.go b/pkg/report/freebsd.go
index 976917623..70ffd2249 100644
--- a/pkg/report/freebsd.go
+++ b/pkg/report/freebsd.go
@@ -33,7 +33,8 @@ func (ctx *freebsd) ContainsCrash(output []byte) bool {
return containsCrash(output, freebsdOopses, ctx.ignores)
}
-func (ctx *freebsd) Parse(output []byte) (desc string, text []byte, start int, end int, corrupted bool) {
+func (ctx *freebsd) Parse(output []byte) *Report {
+ rep := new(Report)
var oops *oops
for pos := 0; pos < len(output); {
next := bytes.IndexByte(output[pos:], '\n')
@@ -49,10 +50,10 @@ func (ctx *freebsd) Parse(output []byte) (desc string, text []byte, start int, e
}
if oops == nil {
oops = oops1
- start = pos
- desc = string(output[pos+match : next])
+ rep.Start = pos
+ rep.Desc = string(output[pos+match : next])
}
- end = next
+ rep.End = next
}
// Console output is indistinguishable from fuzzer output,
// so we just collect everything after the oops.
@@ -61,17 +62,16 @@ func (ctx *freebsd) Parse(output []byte) (desc string, text []byte, start int, e
if lineEnd != 0 && output[lineEnd-1] == '\r' {
lineEnd--
}
- text = append(text, output[pos:lineEnd]...)
- text = append(text, '\n')
+ rep.Text = append(rep.Text, output[pos:lineEnd]...)
+ rep.Text = append(rep.Text, '\n')
}
pos = next + 1
}
if oops == nil {
- return
+ return nil
}
- desc = extractDescription(output[start:], oops)
- corrupted = false
- return
+ rep.Desc = extractDescription(output[rep.Start:], oops)
+ return rep
}
func (ctx *freebsd) Symbolize(text []byte) ([]byte, error) {
diff --git a/pkg/report/fuchsia.go b/pkg/report/fuchsia.go
index c68291e9e..0d529a1d5 100644
--- a/pkg/report/fuchsia.go
+++ b/pkg/report/fuchsia.go
@@ -31,7 +31,7 @@ func (ctx *fuchsia) ContainsCrash(output []byte) bool {
panic("not implemented")
}
-func (ctx *fuchsia) Parse(output []byte) (desc string, text []byte, start int, end int, corrupted bool) {
+func (ctx *fuchsia) Parse(output []byte) *Report {
panic("not implemented")
}
diff --git a/pkg/report/linux.go b/pkg/report/linux.go
index 9d8a90eea..8e4ed57b5 100644
--- a/pkg/report/linux.go
+++ b/pkg/report/linux.go
@@ -75,7 +75,8 @@ func (ctx *linux) ContainsCrash(output []byte) bool {
return containsCrash(output, linuxOopses, ctx.ignores)
}
-func (ctx *linux) Parse(output []byte) (desc string, text []byte, start int, end int, corrupted bool) {
+func (ctx *linux) Parse(output []byte) *Report {
+ rep := new(Report)
var oops *oops
var textPrefix [][]byte
textLines := 0
@@ -94,10 +95,10 @@ func (ctx *linux) Parse(output []byte) (desc string, text []byte, start int, end
}
if oops == nil {
oops = oops1
- start = pos
- desc = string(output[pos+match : next])
+ rep.Start = pos
+ rep.Desc = string(output[pos+match : next])
}
- end = next
+ rep.End = next
}
if ctx.consoleOutputRe.Match(output[pos:next]) &&
(!ctx.questionableRe.Match(output[pos:next]) ||
@@ -116,8 +117,8 @@ func (ctx *linux) Parse(output []byte) (desc string, text []byte, start int, end
// Prepend 5 lines preceding start of the report,
// they can contain additional info related to the report.
for _, prefix := range textPrefix {
- text = append(text, prefix...)
- text = append(text, '\n')
+ rep.Text = append(rep.Text, prefix...)
+ rep.Text = append(rep.Text, '\n')
}
textPrefix = nil
textLines++
@@ -139,34 +140,33 @@ func (ctx *linux) Parse(output []byte) (desc string, text []byte, start int, end
skipLine = true
}
if !skipLine {
- text = append(text, ln...)
- text = append(text, '\n')
+ rep.Text = append(rep.Text, ln...)
+ rep.Text = append(rep.Text, '\n')
}
}
}
pos = next + 1
}
if oops == nil {
- corrupted = isCorrupted("", string(text))
- return
+ return nil
}
- desc = extractDescription(output[start:], oops)
+ rep.Desc = extractDescription(output[rep.Start:], oops)
// Executor PIDs are not interesting.
- desc = executorRe.ReplaceAllLiteralString(desc, "syz-executor")
+ rep.Desc = executorRe.ReplaceAllLiteralString(rep.Desc, "syz-executor")
// Replace that everything looks like an address with "ADDR",
// addresses in descriptions can't be good regardless of the oops regexps.
- desc = addrRe.ReplaceAllLiteralString(desc, "ADDR")
+ rep.Desc = addrRe.ReplaceAllLiteralString(rep.Desc, "ADDR")
// Replace that everything looks like a decimal number with "NUM".
- desc = decNumRe.ReplaceAllLiteralString(desc, "NUM")
+ rep.Desc = decNumRe.ReplaceAllLiteralString(rep.Desc, "NUM")
// Replace that everything looks like a file line number with "LINE".
- desc = lineNumRe.ReplaceAllLiteralString(desc, ":LINE")
+ rep.Desc = lineNumRe.ReplaceAllLiteralString(rep.Desc, ":LINE")
// Replace all raw references to runctions (e.g. "ip6_fragment+0x1052/0x2d80")
// with just function name ("ip6_fragment"). Offsets and sizes are not stable.
- desc = funcRe.ReplaceAllString(desc, "$1")
+ rep.Desc = funcRe.ReplaceAllString(rep.Desc, "$1")
// CPU numbers are not interesting.
- desc = cpuRe.ReplaceAllLiteralString(desc, "CPU")
- corrupted = isCorrupted(desc, string(text))
- return
+ rep.Desc = cpuRe.ReplaceAllLiteralString(rep.Desc, "CPU")
+ rep.Corrupted = isCorrupted(rep.Desc, string(rep.Text))
+ return rep
}
func (ctx *linux) Symbolize(text []byte) ([]byte, error) {
diff --git a/pkg/report/linux_test.go b/pkg/report/linux_test.go
index c4880c9b5..e957ad40e 100644
--- a/pkg/report/linux_test.go
+++ b/pkg/report/linux_test.go
@@ -794,19 +794,19 @@ r0 = ioctl$KVM_CREATE_VM(0xffffffffffffffff, 0xae01, 0x0)
}, {
`
[ 72.159680] BUG UNIX (Not tainted): kasan: bad access detected
-`, ``, true,
+`, ``, false,
}, {
`
[901320.960000] INFO: lockdep is turned off.
-`, ``, true,
+`, ``, false,
}, {
`
[ 72.159680] INFO: Stall ended before state dump start
-`, ``, true,
+`, ``, false,
}, {
`
[ 72.159680] WARNING: /etc/ssh/moduli does not exist, using fixed modulus
-`, ``, true,
+`, ``, false,
}, {
`
[ 1579.244514] BUG: KASAN: slab-out-of-bounds in ip6_fragment+0x1052/0x2d80 at addr ffff88004ec29b58
@@ -836,7 +836,7 @@ r0 = ioctl$KVM_CREATE_VM(0xffffffffffffffff, 0xae01, 0x0)
[ 92.396607] APIC base relocation is unsupported by KVM
[ 95.445015] INFO: NMI handler (perf_event_nmi_handler) took too long to run: 1.356 msecs
[ 95.445015] perf: interrupt took too long (3985 > 3976), lowering kernel.perf_event_max_sample_rate to 50000
-`, ``, true,
+`, ``, false,
}, {
`
[ 92.396607] general protection fault: 0000 [#1] [ 387.811073] audit: type=1326 audit(1486238739.637:135): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=10020 comm="syz-executor1" exe="/root/syz-executor1" sig=31 arch=c000003e syscall=202 compat=0 ip=0x44fad9 code=0x0
@@ -908,13 +908,13 @@ r0 = ioctl$KVM_CREATE_VM(0xffffffffffffffff, 0xae01, 0x0)
[ 37.991733] [4:SdpManagerServi: 3874] KEK_PACK[3874] __add_kek :: item ffffffc822340400
[ 38.018742] [4: system_server: 3344] logger: !@Boot_DEBUG: start networkManagement
[ 38.039013] [2: kworker/2:1: 1608] Trustonic TEE: c01|TL_TZ_KEYSTORE: Starting
-`, ``, true,
+`, ``, false,
}, {
`
[ 16.761978] [syscamera][msm_companion_pll_init::526][BIN_INFO::0x0008]
[ 16.762666] [syscamera][msm_companion_pll_init::544][WAFER_INFO::0xcf80]
[ 16.763144] [syscamera][msm_companion_pll_init::594][BIN_INFO::0x0008][WAFER_INFO::0xcf80][voltage 0.775]
-`, ``, true,
+`, ``, false,
}, {
`
[ 72.159680] BUG: workqueue lockup - pool cpus=0 node=0 flags=0x0 nice=0 stuck for 32s!
@@ -957,7 +957,7 @@ r0 = ioctl$KVM_CREATE_VM(0xffffffffffffffff, 0xae01, 0x0)
[ 128.793239] (ftrace buffer empty)
[ 128.793242] Kernel Offset: disabled
[ 129.380444] Rebooting in 86400 seconds..
-`, ``, true,
+`, `kernel panic: Fatal exception`, true,
}, {
`
[ 238.092073] page:ffffea000712e200 count:1 mapcount:0 mapping:ffff8801c4b88c00 index:0x0 compound_mapcount: 0
@@ -981,7 +981,7 @@ r0 = ioctl$KVM_CREATE_VM(0xffffffffffffffff, 0xae01, 0x0)
[ 238.187128] ? __internal_add_timer+0x275/0x2d0
[ 238.191766] kasan_end_report+0x50/0x50
[ 238.195711] kasan_report+0x144/0x340
-`, ``, true,
+`, `kernel panic: panic_on_warn set ...`, true,
}, {
`
[ 308.130685] ======================================================
@@ -1001,7 +1001,7 @@ r0 = ioctl$KVM_CREATE_VM(0xffffffffffffffff, 0xae01, 0x0)
[ 1722.511384] PGD 5a25067
[ 1722.511384] P4D 5a25067
[ 1722.511384] PUD 0
-`, ``, true,
+`, `BUG: unable to handle kernel `, true,
}, {
`
[ 1722.511384] kasan: CONFIG_KASAN_INLINE enabled
@@ -1021,7 +1021,7 @@ r0 = ioctl$KVM_CREATE_VM(0xffffffffffffffff, 0xae01, 0x0)
[ 153.634416] PGD a0ab067 PUD 21ffff067 PMD 80000000b3c001e3
[ 153.640483] Oops: 0011 [#1] SMP KASAN
[ 153.644615] Modules linked in:
-`, ``, true,
+`, `BUG: unable to handle kernel [ 153.NUM] deprecated getsockopt IP_VLAN used by syz-executor!`, true,
}, {
`
[ 46.415093] syz2: link speed 10 Mbps
@@ -1039,7 +1039,7 @@ r0 = ioctl$KVM_CREATE_VM(0xffffffffffffffff, 0xae01, 0x0)
`
[ 59.534220] ==================================================================
[ 59.541645] BUG: KASAN: slab-out-of-bounds in gup_huge_pmd+0x739/0x770 at addr ffff8800b46111c0
-`, ``, true,
+`, `KASAN: slab-out-of-bounds in gup_huge_pmd at addr ADDR`, true,
}, {
`
[ 42.361487] ==================================================================
@@ -1165,7 +1165,7 @@ r0 = ioctl$KVM_CREATE_VM(0xffffffffffffffff, 0xae01, 0x0)
[ 55.502578] Freed:
[ 55.504709] PID = 4655
[ 55.507369] Memory state around the buggy address:
-`, ``, true,
+`, `KASAN: use-after-free Read in consume_skb`, true,
}, {
`
[ 322.909624] FAULT_FLAG_ALLOW_RETRY missing 30
@@ -1179,7 +1179,7 @@ r0 = ioctl$KVM_CREATE_VM(0xffffffffffffffff, 0xae01, 0x0)
[ 322.914882] [<ffffffff81d91389>] dump_stack+0xc1/0x128
** 93 printk messages dropped ** [ 322.962139] BUG: KASAN: slab-out-of-bounds in do_raw_write_lock+0x1a3/0x1d0 at addr ffff8801c464b568
** 1987 printk messages dropped ** [ 322.975979] ffff8801c464b400: fb fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc
-`, ``, true,
+`, `KASAN: slab-out-of-bounds in do_raw_write_lock at addr ADDR`, true,
}, {
`
[ 208.131930] ==================================================================
@@ -1253,29 +1253,29 @@ func TestLinuxIgnores(t *testing.T) {
if !reporter.ContainsCrash([]byte(log)) {
t.Fatalf("no crash")
}
- if desc, _, _, _, _ := reporter.Parse([]byte(log)); desc != "BUG: bug1" {
- t.Fatalf("want `BUG: bug1`, found `%v`", desc)
+ if rep := reporter.Parse([]byte(log)); rep.Desc != "BUG: bug1" {
+ t.Fatalf("want `BUG: bug1`, found `%v`", rep.Desc)
}
if !reporter1.ContainsCrash([]byte(log)) {
t.Fatalf("no crash")
}
- if desc, _, _, _, _ := reporter1.Parse([]byte(log)); desc != "BUG: bug1" {
- t.Fatalf("want `BUG: bug1`, found `%v`", desc)
+ if rep := reporter1.Parse([]byte(log)); rep.Desc != "BUG: bug1" {
+ t.Fatalf("want `BUG: bug1`, found `%v`", rep.Desc)
}
if !reporter2.ContainsCrash([]byte(log)) {
t.Fatalf("no crash")
}
- if desc, _, _, _, _ := reporter2.Parse([]byte(log)); desc != "BUG: bug2" {
- t.Fatalf("want `BUG: bug2`, found `%v`", desc)
+ if rep := reporter2.Parse([]byte(log)); rep.Desc != "BUG: bug2" {
+ t.Fatalf("want `BUG: bug2`, found `%v`", rep.Desc)
}
if reporter3.ContainsCrash([]byte(log)) {
t.Fatalf("found crash, should be ignored")
}
- if desc, _, _, _, _ := reporter3.Parse([]byte(log)); desc != "" {
- t.Fatalf("found `%v`, should be ignored", desc)
+ if rep := reporter3.Parse([]byte(log)); rep != nil {
+ t.Fatalf("found `%v`, should be ignored", rep.Desc)
}
}
@@ -1328,11 +1328,11 @@ Read of size 4 by task syz-executor2/5764
t.Fatal(err)
}
for log, text0 := range tests {
- if desc, text, _, _, _ := reporter.Parse([]byte(log)); string(text) != text0 {
+ if rep := reporter.Parse([]byte(log)); string(rep.Text) != text0 {
t.Logf("log:\n%s", log)
t.Logf("want text:\n%s", text0)
- t.Logf("got text:\n%s", text)
- t.Fatalf("bad text, desc: '%v'", desc)
+ t.Logf("got text:\n%s", rep.Text)
+ t.Fatalf("bad text, desc: '%v'", rep.Desc)
}
}
}
@@ -1496,10 +1496,10 @@ func TestLinuxParseReport(t *testing.T) {
}
for i, test := range parseReportTests {
t.Run(fmt.Sprint(i), func(t *testing.T) {
- _, text, _, _, _ := reporter.Parse([]byte(test.in))
- if test.out != string(text) {
+ rep := reporter.Parse([]byte(test.in))
+ if test.out != string(rep.Text) {
t.Logf("expect:\n%v", test.out)
- t.Logf("got:\n%v", string(text))
+ t.Logf("got:\n%v", string(rep.Text))
t.Fail()
}
})
diff --git a/pkg/report/netbsd.go b/pkg/report/netbsd.go
index 4a43046c7..f5aab3faf 100644
--- a/pkg/report/netbsd.go
+++ b/pkg/report/netbsd.go
@@ -32,8 +32,8 @@ func (ctx *netbsd) ContainsCrash(output []byte) bool {
return false
}
-func (ctx *netbsd) Parse(output []byte) (desc string, text []byte, start int, end int, corrupted bool) {
- return "", nil, 0, 0, false
+func (ctx *netbsd) Parse(output []byte) *Report {
+ return nil
}
func (ctx *netbsd) Symbolize(text []byte) ([]byte, error) {
diff --git a/pkg/report/report.go b/pkg/report/report.go
index 2738e4070..efc1623bd 100644
--- a/pkg/report/report.go
+++ b/pkg/report/report.go
@@ -19,11 +19,8 @@ type Reporter interface {
ContainsCrash(output []byte) bool
// Parse extracts information about oops from console output.
- // Desc contains a representative description of the first oops (empty if no oops found),
- // text contains whole oops text,
- // start and end denote region of output with oops message(s),
- // corrupted indicates whether the report is truncated of corrupted in some other way.
- Parse(output []byte) (desc string, text []byte, start int, end int, corrupted bool)
+ // Returns nil if no oops found.
+ Parse(output []byte) *Report
Symbolize(text []byte) ([]byte, error)
@@ -32,6 +29,18 @@ type Reporter interface {
GetMaintainers(file string) ([]string, error)
}
+type Report struct {
+ // Desc contains a representative description of the first oops.
+ Desc string
+ // Text contains whole oops text.
+ Text []byte
+ // Start and End denote region of output with oops message(s).
+ Start int
+ End int
+ // Corrupted indicates whether the report is truncated of corrupted in some other way.
+ Corrupted bool
+}
+
// NewReporter creates reporter for the specified OS:
// kernelSrc: path to kernel sources directory
// kernelObj: path to kernel build directory (can be empty for in-tree build)
diff --git a/pkg/report/report_test.go b/pkg/report/report_test.go
index b4aa9a215..6802d0772 100644
--- a/pkg/report/report_test.go
+++ b/pkg/report/report_test.go
@@ -60,17 +60,7 @@ func testParse(t *testing.T, os string, tests []ParseTest) {
tests = append(tests, test)
}
for _, test := range tests {
- desc, _, _, _, corrupted := reporter.Parse([]byte(test.Log))
- if corrupted && !test.Corrupted {
- t.Fatalf("incorrectly marked report as corrupted: '%v'\n%v", desc, test.Log)
- }
- if !corrupted && test.Corrupted {
- t.Fatalf("failed to mark report as corrupted: '%v'\n%v", desc, test.Log)
- }
- if corrupted && test.Desc == "" {
- // Allow ignoring crash description for corrupted reports
- continue
- }
+ rep := reporter.Parse([]byte(test.Log))
containsCrash := reporter.ContainsCrash([]byte(test.Log))
expectCrash := (test.Desc != "")
if expectCrash && !containsCrash {
@@ -79,6 +69,14 @@ func testParse(t *testing.T, os string, tests []ParseTest) {
if !expectCrash && containsCrash {
t.Fatalf("ContainsCrash found unexpected crash:\n%v", test.Log)
}
+ if rep != nil && rep.Desc == "" {
+ t.Fatalf("found crash, but title is empty '%v' in:\n%v", test.Desc, test.Log)
+ }
+ desc, corrupted := "", false
+ if rep != nil {
+ desc = rep.Desc
+ corrupted = rep.Corrupted
+ }
if desc == "" && test.Desc != "" {
t.Fatalf("did not find crash message '%v' in:\n%v", test.Desc, test.Log)
}
@@ -88,5 +86,11 @@ func testParse(t *testing.T, os string, tests []ParseTest) {
if desc != test.Desc {
t.Fatalf("extracted bad crash message:\n%+q\nwant:\n%+q", desc, test.Desc)
}
+ if corrupted && !test.Corrupted {
+ t.Fatalf("incorrectly marked report as corrupted: '%v'\n%v", desc, test.Log)
+ }
+ if !corrupted && test.Corrupted {
+ t.Fatalf("failed to mark report as corrupted: '%v'\n%v", desc, test.Log)
+ }
}
}
diff --git a/pkg/report/windows.go b/pkg/report/windows.go
index cb62cf181..17e1f1bc7 100644
--- a/pkg/report/windows.go
+++ b/pkg/report/windows.go
@@ -31,7 +31,7 @@ func (ctx *windows) ContainsCrash(output []byte) bool {
panic("not implemented")
}
-func (ctx *windows) Parse(output []byte) (desc string, text []byte, start int, end int, corrupted bool) {
+func (ctx *windows) Parse(output []byte) *Report {
panic("not implemented")
}
diff --git a/pkg/repro/repro.go b/pkg/repro/repro.go
index 699f0d7c3..b6b6a908e 100644
--- a/pkg/repro/repro.go
+++ b/pkg/repro/repro.go
@@ -74,10 +74,11 @@ func Run(crashLog []byte, cfg *mgrconfig.Config, reporter report.Reporter, vmPoo
if len(entries) == 0 {
return nil, fmt.Errorf("crash log does not contain any programs")
}
- crashDesc, _, crashStart, _, _ := reporter.Parse(crashLog)
- if crashDesc == "" {
- crashStart = len(crashLog) // assuming VM hanged
- crashDesc = "hang"
+ crashStart := len(crashLog) // assuming VM hanged
+ crashDesc := "hang"
+ if rep := reporter.Parse(crashLog); rep != nil {
+ crashStart = rep.Start
+ crashDesc = rep.Desc
}
ctx := &context{
diff --git a/tools/syz-symbolize/symbolize.go b/tools/syz-symbolize/symbolize.go
index 8383c02d1..e925159d2 100644
--- a/tools/syz-symbolize/symbolize.go
+++ b/tools/syz-symbolize/symbolize.go
@@ -38,14 +38,19 @@ func main() {
os.Exit(1)
}
if *flagReport {
- desc, text, _, _, _ := reporter.Parse(text)
+ rep := reporter.Parse(text)
+ if rep == nil {
+ fmt.Fprintf(os.Stderr, "no crash found\n")
+ os.Exit(1)
+ }
+ text = rep.Text
text, err = reporter.Symbolize(text)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to symbolize: %v\n", err)
os.Exit(1)
}
guiltyFile := reporter.ExtractGuiltyFile(text)
- fmt.Printf("%v\n\n", desc)
+ fmt.Printf("%v\n\n", rep.Desc)
os.Stdout.Write(text)
fmt.Printf("\n")
fmt.Printf("guilty file: %v\n", guiltyFile)
diff --git a/vm/vm.go b/vm/vm.go
index a0be9cbdc..b786dc131 100644
--- a/vm/vm.go
+++ b/vm/vm.go
@@ -131,16 +131,19 @@ func MonitorExecution(outc <-chan []byte, errc <-chan error, needOutput bool,
if !reporter.ContainsCrash(output[matchPos:]) {
return defaultError, nil, output, defaultError != "", false
}
- desc, text, start, end, _ := reporter.Parse(output[matchPos:])
- start = start + matchPos - beforeContext
+ rep := reporter.Parse(output[matchPos:])
+ if rep == nil {
+ panic(fmt.Sprintf("reporter.ContainsCrash/Parse disagree:\n%s", output[matchPos:]))
+ }
+ start := rep.Start + matchPos - beforeContext
if start < 0 {
start = 0
}
- end = end + matchPos + afterContext
+ end := rep.End + matchPos + afterContext
if end > len(output) {
end = len(output)
}
- return desc, text, output[start:end], true, false
+ return rep.Desc, rep.Text, output[start:end], true, false
}
lastExecuteTime := time.Now()