scan scans one line, updates goroutines and move to the next state. Returns true if the line was processed and thus should not be printed out. TODO(maruel): Handle corrupted stack cases: - missed stack barrier - found next stack barrier at 0x123; expected - runtime: unexpected return pc for FUNC_N
(line []byte)
| 470 | // - found next stack barrier at 0x123; expected |
| 471 | // - runtime: unexpected return pc for FUNC_NAME called from 0x123 |
| 472 | func (s *scanningState) scan(line []byte) (bool, error) { |
| 473 | /* This is very useful to debug issues in the state machine. |
| 474 | defer func() { |
| 475 | log.Printf("scan(%q) -> %s", line, s.state) |
| 476 | }() |
| 477 | //*/ |
| 478 | var cur *Goroutine |
| 479 | if len(s.Goroutines) != 0 { |
| 480 | cur = s.Goroutines[len(s.Goroutines)-1] |
| 481 | } |
| 482 | trimmed := line |
| 483 | if bytes.HasSuffix(line, crlf) { |
| 484 | trimmed = line[:len(line)-2] |
| 485 | } else if bytes.HasSuffix(line, lf) { |
| 486 | trimmed = line[:len(line)-1] |
| 487 | } else { |
| 488 | // It's the end of the stream and it's not terminating with EOL character. |
| 489 | if s.state == looking || s.state == done { |
| 490 | return false, nil |
| 491 | } |
| 492 | // Let it flow. It's possible the last line was trimmed and we still want |
| 493 | // to parse it. |
| 494 | } |
| 495 | |
| 496 | if len(trimmed) != 0 && len(s.prefix) != 0 { |
| 497 | // This can only be the case if s.state != looking | done or the line is |
| 498 | // empty. |
| 499 | if !bytes.HasPrefix(trimmed, s.prefix) { |
| 500 | prefix := s.prefix |
| 501 | s.state = done |
| 502 | s.prefix = nil |
| 503 | return false, fmt.Errorf("inconsistent indentation: %q, expected %q", trimmed, prefix) |
| 504 | } |
| 505 | trimmed = trimmed[len(s.prefix):] |
| 506 | } |
| 507 | |
| 508 | switch s.state { |
| 509 | case done: |
| 510 | return false, nil |
| 511 | |
| 512 | case looking: |
| 513 | // We could look for '^panic:' but this is more risky, there can be a lot |
| 514 | // of junk between this and the stack dump. |
| 515 | fallthrough |
| 516 | |
| 517 | case betweenRoutine: |
| 518 | // Look for a goroutine header. |
| 519 | if match := reRoutineHeader.FindSubmatch(trimmed); match != nil { |
| 520 | if id, ok := atou(match[2]); ok { |
| 521 | // See runtime/traceback.go. |
| 522 | // "<state>, \d+ minutes, locked to thread" |
| 523 | items := bytes.Split(match[3], commaSpace) |
| 524 | sleep := 0 |
| 525 | locked := false |
| 526 | for i := 1; i < len(items); i++ { |
| 527 | if bytes.Equal(items[i], lockedToThread) { |
| 528 | locked = true |
| 529 | continue |
no test coverage detected