debug.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. /*
  2. * ECAL
  3. *
  4. * Copyright 2020 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the MIT
  7. * License, If a copy of the MIT License was not distributed with this
  8. * file, You can obtain one at https://opensource.org/licenses/MIT.
  9. */
  10. /*
  11. Package interpreter contains the ECAL interpreter.
  12. */
  13. package interpreter
  14. import (
  15. "fmt"
  16. "runtime"
  17. "strings"
  18. "sync"
  19. "time"
  20. "devt.de/krotik/common/datautil"
  21. "devt.de/krotik/common/errorutil"
  22. "devt.de/krotik/ecal/engine/pool"
  23. "devt.de/krotik/ecal/parser"
  24. "devt.de/krotik/ecal/scope"
  25. "devt.de/krotik/ecal/util"
  26. )
  27. /*
  28. ecalDebugger is the inbuild default debugger.
  29. */
  30. type ecalDebugger struct {
  31. breakPoints map[string]bool // Break points (active or not)
  32. interrogationStates map[uint64]*interrogationState // Collection of threads which are interrogated
  33. callStacks map[uint64][]*parser.ASTNode // Call stack locations of threads
  34. callStackVsSnapshots map[uint64][]map[string]interface{} // Call stack variable scope snapshots of threads
  35. callStackGlobalVsSnapshots map[uint64][]map[string]interface{} // Call stack global variable scope snapshots of threads
  36. sources map[string]bool // All known sources
  37. breakOnStart bool // Flag to stop at the start of the next execution
  38. breakOnError bool // Flag to stop if an error occurs
  39. globalScope parser.Scope // Global variable scope which can be used to transfer data
  40. lock *sync.RWMutex // Lock for this debugger
  41. lastVisit int64 // Last time the debugger had a state visit
  42. mutexeOwners map[string]uint64 // A map of current mutex owners
  43. mutexLog *datautil.RingBuffer // A log of taken mutexes
  44. threadpool *pool.ThreadPool // Reference to the thread pool of the processor
  45. }
  46. /*
  47. interrogationState contains state information of a thread interrogation.
  48. */
  49. type interrogationState struct {
  50. cond *sync.Cond // Condition on which the thread is waiting when suspended
  51. running bool // Flag if the thread is running or waiting
  52. cmd interrogationCmd // Next interrogation command for the thread
  53. stepOutStack []*parser.ASTNode // Target stack when doing a step out
  54. node *parser.ASTNode // Node on which the thread was last stopped
  55. vs parser.Scope // Variable scope of the thread when it was last stopped
  56. err error // Error which was returned by a function call
  57. }
  58. /*
  59. interrogationCmd represents a command for a thread interrogation.
  60. */
  61. type interrogationCmd int
  62. /*
  63. Interrogation commands
  64. */
  65. const (
  66. Stop interrogationCmd = iota // Stop the execution (default)
  67. StepIn // Step into the next function
  68. StepOut // Step out of the current function
  69. StepOver // Step over the next function
  70. Resume // Resume execution - do not break again on the same line
  71. Kill // Resume execution - and kill the thread on the next state change
  72. )
  73. /*
  74. newInterrogationState creates a new interrogation state.
  75. */
  76. func newInterrogationState(node *parser.ASTNode, vs parser.Scope) *interrogationState {
  77. return &interrogationState{
  78. sync.NewCond(&sync.Mutex{}),
  79. false,
  80. Stop,
  81. nil,
  82. node,
  83. vs,
  84. nil,
  85. }
  86. }
  87. /*
  88. NewECALDebugger returns a new debugger object.
  89. */
  90. func NewECALDebugger(globalVS parser.Scope) util.ECALDebugger {
  91. return &ecalDebugger{
  92. breakPoints: make(map[string]bool),
  93. interrogationStates: make(map[uint64]*interrogationState),
  94. callStacks: make(map[uint64][]*parser.ASTNode),
  95. callStackVsSnapshots: make(map[uint64][]map[string]interface{}),
  96. callStackGlobalVsSnapshots: make(map[uint64][]map[string]interface{}),
  97. sources: make(map[string]bool),
  98. breakOnStart: false,
  99. breakOnError: true,
  100. globalScope: globalVS,
  101. lock: &sync.RWMutex{},
  102. lastVisit: 0,
  103. mutexeOwners: nil,
  104. mutexLog: nil,
  105. threadpool: nil,
  106. }
  107. }
  108. /*
  109. HandleInput handles a given debug instruction from a console.
  110. */
  111. func (ed *ecalDebugger) HandleInput(input string) (interface{}, error) {
  112. var res interface{}
  113. var err error
  114. args := strings.Fields(input)
  115. if len(args) > 0 {
  116. if cmd, ok := DebugCommandsMap[args[0]]; ok {
  117. if len(args) > 1 {
  118. res, err = cmd.Run(ed, args[1:])
  119. } else {
  120. res, err = cmd.Run(ed, nil)
  121. }
  122. } else {
  123. err = fmt.Errorf("Unknown command: %v", args[0])
  124. }
  125. }
  126. return res, err
  127. }
  128. /*
  129. StopThreads will continue all suspended threads and set them to be killed.
  130. Returns true if a waiting thread was resumed. Can wait for threads to end
  131. by ensuring that for at least d time no state change occurred.
  132. */
  133. func (ed *ecalDebugger) StopThreads(d time.Duration) bool {
  134. var ret = false
  135. for _, is := range ed.interrogationStates {
  136. if is.running == false {
  137. ret = true
  138. is.cmd = Kill
  139. is.running = true
  140. is.cond.L.Lock()
  141. is.cond.Broadcast()
  142. is.cond.L.Unlock()
  143. }
  144. }
  145. if ret && d > 0 {
  146. var lastVisit int64 = -1
  147. for lastVisit != ed.lastVisit {
  148. lastVisit = ed.lastVisit
  149. time.Sleep(d)
  150. }
  151. }
  152. return ret
  153. }
  154. /*
  155. BreakOnStart breaks on the start of the next execution.
  156. */
  157. func (ed *ecalDebugger) BreakOnStart(flag bool) {
  158. ed.lock.Lock()
  159. defer ed.lock.Unlock()
  160. ed.breakOnStart = flag
  161. }
  162. /*
  163. BreakOnError breaks if an error occurs.
  164. */
  165. func (ed *ecalDebugger) BreakOnError(flag bool) {
  166. ed.lock.Lock()
  167. defer ed.lock.Unlock()
  168. ed.breakOnError = flag
  169. }
  170. /*
  171. SetLockingState sets locking status information.
  172. */
  173. func (ed *ecalDebugger) SetLockingState(mutexeOwners map[string]uint64, mutexLog *datautil.RingBuffer) {
  174. if ed.mutexeOwners == nil {
  175. ed.mutexeOwners = mutexeOwners
  176. ed.mutexLog = mutexLog
  177. }
  178. }
  179. /*
  180. SetThreadPool sets the reference to the current used thread pool.
  181. */
  182. func (ed *ecalDebugger) SetThreadPool(tp *pool.ThreadPool) {
  183. if ed.threadpool == nil {
  184. ed.threadpool = tp
  185. }
  186. }
  187. /*
  188. VisitState is called for every state during the execution of a program.
  189. */
  190. func (ed *ecalDebugger) VisitState(node *parser.ASTNode, vs parser.Scope, tid uint64) util.TraceableRuntimeError {
  191. ed.lock.RLock()
  192. _, ok := ed.callStacks[tid]
  193. ed.lastVisit = time.Now().UnixNano()
  194. ed.lock.RUnlock()
  195. if !ok {
  196. // Make the debugger aware of running threads
  197. ed.lock.Lock()
  198. ed.callStacks[tid] = make([]*parser.ASTNode, 0, 10)
  199. ed.callStackVsSnapshots[tid] = make([]map[string]interface{}, 0, 10)
  200. ed.callStackGlobalVsSnapshots[tid] = make([]map[string]interface{}, 0, 10)
  201. ed.lock.Unlock()
  202. }
  203. if node.Token != nil { // Statements are excluded here
  204. targetIdentifier := fmt.Sprintf("%v:%v", node.Token.Lsource, node.Token.Lline)
  205. ed.lock.RLock()
  206. is, ok := ed.interrogationStates[tid]
  207. _, sourceKnown := ed.sources[node.Token.Lsource]
  208. ed.lock.RUnlock()
  209. if !sourceKnown {
  210. ed.RecordSource(node.Token.Lsource)
  211. }
  212. if ok {
  213. // The thread is being interrogated
  214. switch is.cmd {
  215. case Resume, Kill:
  216. if is.node.Token.Lline != node.Token.Lline {
  217. // Remove the resume command once we are on a different line
  218. ed.lock.Lock()
  219. delete(ed.interrogationStates, tid)
  220. ed.lock.Unlock()
  221. if is.cmd == Kill {
  222. runtime.Goexit()
  223. }
  224. return ed.VisitState(node, vs, tid)
  225. }
  226. case Stop, StepIn, StepOver:
  227. if is.node.Token.Lline != node.Token.Lline || is.cmd == Stop {
  228. is.node = node
  229. is.vs = vs
  230. is.running = false
  231. is.cond.L.Lock()
  232. is.cond.Wait()
  233. is.cond.L.Unlock()
  234. }
  235. }
  236. } else if active, ok := ed.breakPoints[targetIdentifier]; (ok && active) || ed.breakOnStart {
  237. // A globally defined breakpoint has been hit - note the position
  238. // in the thread specific map and wait
  239. is := newInterrogationState(node, vs)
  240. ed.lock.Lock()
  241. ed.breakOnStart = false
  242. ed.interrogationStates[tid] = is
  243. ed.lock.Unlock()
  244. is.cond.L.Lock()
  245. is.cond.Wait()
  246. is.cond.L.Unlock()
  247. }
  248. }
  249. return nil
  250. }
  251. /*
  252. VisitStepInState is called before entering a function call.
  253. */
  254. func (ed *ecalDebugger) VisitStepInState(node *parser.ASTNode, vs parser.Scope, tid uint64) util.TraceableRuntimeError {
  255. ed.lock.Lock()
  256. defer ed.lock.Unlock()
  257. var err util.TraceableRuntimeError
  258. threadCallStack := ed.callStacks[tid]
  259. threadCallStackVs := ed.callStackVsSnapshots[tid]
  260. threadCallStackGlobalVs := ed.callStackGlobalVsSnapshots[tid]
  261. is, ok := ed.interrogationStates[tid]
  262. if ok {
  263. if is.cmd == Stop {
  264. // Special case a parameter of a function was resolved by another
  265. // function call - the debugger should stop before entering
  266. ed.lock.Unlock()
  267. err = ed.VisitState(node, vs, tid)
  268. ed.lock.Lock()
  269. }
  270. if err == nil {
  271. // The thread is being interrogated
  272. switch is.cmd {
  273. case StepIn:
  274. is.cmd = Stop
  275. case StepOver:
  276. is.cmd = StepOut
  277. is.stepOutStack = threadCallStack
  278. }
  279. }
  280. }
  281. ed.callStacks[tid] = append(threadCallStack, node)
  282. ed.callStackVsSnapshots[tid] = append(threadCallStackVs, ed.buildVsSnapshot(vs))
  283. ed.callStackGlobalVsSnapshots[tid] = append(threadCallStackGlobalVs, ed.buildGlobalVsSnapshot(vs))
  284. return err
  285. }
  286. /*
  287. VisitStepOutState is called after returning from a function call.
  288. */
  289. func (ed *ecalDebugger) VisitStepOutState(node *parser.ASTNode, vs parser.Scope, tid uint64, soErr error) util.TraceableRuntimeError {
  290. ed.lock.Lock()
  291. defer ed.lock.Unlock()
  292. threadCallStack := ed.callStacks[tid]
  293. threadCallStackVs := ed.callStackVsSnapshots[tid]
  294. threadCallStackGlobalVs := ed.callStackGlobalVsSnapshots[tid]
  295. lastIndex := len(threadCallStack) - 1
  296. ok, cerr := threadCallStack[lastIndex].Equals(node, false) // Sanity check step in node must be the same as step out node
  297. errorutil.AssertTrue(ok,
  298. fmt.Sprintf("Unexpected callstack when stepping out - callstack: %v - funccall: %v - comparison error: %v",
  299. threadCallStack, node, cerr))
  300. ed.callStacks[tid] = threadCallStack[:lastIndex] // Remove the last item
  301. ed.callStackVsSnapshots[tid] = threadCallStackVs[:lastIndex]
  302. ed.callStackGlobalVsSnapshots[tid] = threadCallStackGlobalVs[:lastIndex]
  303. is, ok := ed.interrogationStates[tid]
  304. if ed.breakOnError && soErr != nil {
  305. if !ok {
  306. is = newInterrogationState(node, vs)
  307. ed.breakOnStart = false
  308. ed.interrogationStates[tid] = is
  309. } else {
  310. is.node = node
  311. is.vs = vs
  312. is.running = false
  313. }
  314. if is.err == nil {
  315. // Only stop if the error is being set
  316. is.err = soErr
  317. ed.lock.Unlock()
  318. is.cond.L.Lock()
  319. is.cond.Wait()
  320. is.cond.L.Unlock()
  321. ed.lock.Lock()
  322. }
  323. } else if ok {
  324. is.err = soErr
  325. // The thread is being interrogated
  326. switch is.cmd {
  327. case StepOver, StepOut:
  328. if len(ed.callStacks[tid]) == len(is.stepOutStack) {
  329. is.cmd = Stop
  330. }
  331. }
  332. }
  333. return nil
  334. }
  335. /*
  336. RecordSource records a code source.
  337. */
  338. func (ed *ecalDebugger) RecordSource(source string) {
  339. ed.lock.Lock()
  340. defer ed.lock.Unlock()
  341. ed.sources[source] = true
  342. }
  343. /*
  344. RecordThreadFinished lets the debugger know that a thread has finished.
  345. */
  346. func (ed *ecalDebugger) RecordThreadFinished(tid uint64) {
  347. ed.lock.Lock()
  348. defer ed.lock.Unlock()
  349. if is, ok := ed.interrogationStates[tid]; !ok || !is.running {
  350. delete(ed.interrogationStates, tid)
  351. delete(ed.callStacks, tid)
  352. delete(ed.callStackVsSnapshots, tid)
  353. delete(ed.callStackGlobalVsSnapshots, tid)
  354. }
  355. }
  356. /*
  357. SetBreakPoint sets a break point.
  358. */
  359. func (ed *ecalDebugger) SetBreakPoint(source string, line int) {
  360. ed.lock.Lock()
  361. defer ed.lock.Unlock()
  362. ed.breakPoints[fmt.Sprintf("%v:%v", source, line)] = true
  363. }
  364. /*
  365. DisableBreakPoint disables a break point but keeps the code reference.
  366. */
  367. func (ed *ecalDebugger) DisableBreakPoint(source string, line int) {
  368. ed.lock.Lock()
  369. defer ed.lock.Unlock()
  370. ed.breakPoints[fmt.Sprintf("%v:%v", source, line)] = false
  371. }
  372. /*
  373. RemoveBreakPoint removes a break point.
  374. */
  375. func (ed *ecalDebugger) RemoveBreakPoint(source string, line int) {
  376. ed.lock.Lock()
  377. defer ed.lock.Unlock()
  378. if line > 0 {
  379. delete(ed.breakPoints, fmt.Sprintf("%v:%v", source, line))
  380. } else {
  381. for k := range ed.breakPoints {
  382. if ksource := strings.Split(k, ":")[0]; ksource == source {
  383. delete(ed.breakPoints, k)
  384. }
  385. }
  386. }
  387. }
  388. /*
  389. ExtractValue copies a value from a suspended thread into the
  390. global variable scope.
  391. */
  392. func (ed *ecalDebugger) ExtractValue(threadID uint64, varName string, destVarName string) error {
  393. if ed.globalScope == nil {
  394. return fmt.Errorf("Cannot access global scope")
  395. }
  396. err := fmt.Errorf("Cannot find suspended thread %v", threadID)
  397. ed.lock.Lock()
  398. defer ed.lock.Unlock()
  399. is, ok := ed.interrogationStates[threadID]
  400. if ok && !is.running {
  401. var val interface{}
  402. var ok bool
  403. if val, ok, err = is.vs.GetValue(varName); ok {
  404. err = ed.globalScope.SetValue(destVarName, val)
  405. } else if err == nil {
  406. err = fmt.Errorf("No such value %v", varName)
  407. }
  408. }
  409. return err
  410. }
  411. /*
  412. InjectValue copies a value from an expression (using the global variable scope) into
  413. a suspended thread.
  414. */
  415. func (ed *ecalDebugger) InjectValue(threadID uint64, varName string, expression string) error {
  416. if ed.globalScope == nil {
  417. return fmt.Errorf("Cannot access global scope")
  418. }
  419. err := fmt.Errorf("Cannot find suspended thread %v", threadID)
  420. ed.lock.Lock()
  421. defer ed.lock.Unlock()
  422. is, ok := ed.interrogationStates[threadID]
  423. if ok && !is.running {
  424. var ast *parser.ASTNode
  425. var val interface{}
  426. // Eval expression
  427. ast, err = parser.ParseWithRuntime("InjectValueExpression", expression,
  428. NewECALRuntimeProvider("InjectValueExpression2", nil, nil))
  429. if err == nil {
  430. if err = ast.Runtime.Validate(); err == nil {
  431. ivs := scope.NewScopeWithParent("InjectValueExpressionScope", ed.globalScope)
  432. val, err = ast.Runtime.Eval(ivs, make(map[string]interface{}), 999)
  433. if err == nil {
  434. err = is.vs.SetValue(varName, val)
  435. }
  436. }
  437. }
  438. }
  439. return err
  440. }
  441. /*
  442. Continue will continue a suspended thread.
  443. */
  444. func (ed *ecalDebugger) Continue(threadID uint64, contType util.ContType) {
  445. ed.lock.RLock()
  446. defer ed.lock.RUnlock()
  447. if is, ok := ed.interrogationStates[threadID]; ok && !is.running {
  448. switch contType {
  449. case util.Resume:
  450. is.cmd = Resume
  451. case util.StepIn:
  452. is.cmd = StepIn
  453. case util.StepOver:
  454. is.cmd = StepOver
  455. case util.StepOut:
  456. is.cmd = StepOut
  457. stack := ed.callStacks[threadID]
  458. is.stepOutStack = stack[:len(stack)-1]
  459. }
  460. is.running = true
  461. is.cond.L.Lock()
  462. is.cond.Broadcast()
  463. is.cond.L.Unlock()
  464. }
  465. }
  466. /*
  467. Status returns the current status of the debugger.
  468. */
  469. func (ed *ecalDebugger) Status() interface{} {
  470. ed.lock.RLock()
  471. defer ed.lock.RUnlock()
  472. var sources []string
  473. threadStates := make(map[string]map[string]interface{})
  474. res := map[string]interface{}{
  475. "breakpoints": ed.breakPoints,
  476. "breakonstart": ed.breakOnStart,
  477. "threads": threadStates,
  478. }
  479. for k := range ed.sources {
  480. sources = append(sources, k)
  481. }
  482. res["sources"] = sources
  483. for k, v := range ed.callStacks {
  484. s := map[string]interface{}{
  485. "callStack": ed.prettyPrintCallStack(v),
  486. }
  487. if is, ok := ed.interrogationStates[k]; ok {
  488. s["threadRunning"] = is.running
  489. s["error"] = is.err
  490. }
  491. threadStates[fmt.Sprint(k)] = s
  492. }
  493. return res
  494. }
  495. /*
  496. LockState returns the current locking state.
  497. */
  498. func (ed *ecalDebugger) LockState() interface{} {
  499. return map[string]interface{}{
  500. "log": ed.mutexLog.StringSlice(),
  501. "owners": ed.mutexeOwners,
  502. "threads": ed.threadpool.State(),
  503. }
  504. }
  505. /*
  506. Describe describes a thread currently observed by the debugger.
  507. */
  508. func (ed *ecalDebugger) Describe(threadID uint64) interface{} {
  509. ed.lock.RLock()
  510. defer ed.lock.RUnlock()
  511. var res map[string]interface{}
  512. threadCallStack, ok1 := ed.callStacks[threadID]
  513. if is, ok2 := ed.interrogationStates[threadID]; ok1 && ok2 {
  514. callStackNode := make([]map[string]interface{}, 0)
  515. for _, sn := range threadCallStack {
  516. callStackNode = append(callStackNode, sn.ToJSONObject())
  517. }
  518. res = map[string]interface{}{
  519. "threadRunning": is.running,
  520. "error": is.err,
  521. "callStack": ed.prettyPrintCallStack(threadCallStack),
  522. "callStackNode": callStackNode,
  523. "callStackVsSnapshot": ed.callStackVsSnapshots[threadID],
  524. "callStackVsSnapshotGlobal": ed.callStackGlobalVsSnapshots[threadID],
  525. }
  526. if !is.running {
  527. codeString, _ := parser.PrettyPrint(is.node)
  528. res["code"] = codeString
  529. res["node"] = is.node.ToJSONObject()
  530. res["vs"] = ed.buildVsSnapshot(is.vs)
  531. res["vsGlobal"] = ed.buildGlobalVsSnapshot(is.vs)
  532. }
  533. }
  534. return res
  535. }
  536. func (ed *ecalDebugger) buildVsSnapshot(vs parser.Scope) map[string]interface{} {
  537. vsValues := make(map[string]interface{})
  538. // Collect all parent scopes except the global scope
  539. parent := vs.Parent()
  540. for parent != nil &&
  541. parent.Name() != scope.GlobalScope {
  542. vsValues = datautil.MergeMaps(vsValues, parent.ToJSONObject())
  543. parent = parent.Parent()
  544. }
  545. return ed.MergeMaps(vsValues, vs.ToJSONObject())
  546. }
  547. func (ed *ecalDebugger) buildGlobalVsSnapshot(vs parser.Scope) map[string]interface{} {
  548. vsValues := make(map[string]interface{})
  549. globalVs := vs
  550. for globalVs != nil &&
  551. globalVs.Name() != scope.GlobalScope {
  552. globalVs = globalVs.Parent()
  553. }
  554. if globalVs != nil && globalVs.Name() == scope.GlobalScope {
  555. vsValues = globalVs.ToJSONObject()
  556. }
  557. return vsValues
  558. }
  559. /*
  560. MergeMaps merges all given maps into a new map. Contents are shallow copies
  561. and conflicts are resolved as first-one-wins.
  562. */
  563. func (ed *ecalDebugger) MergeMaps(maps ...map[string]interface{}) map[string]interface{} {
  564. ret := make(map[string]interface{})
  565. for _, m := range maps {
  566. for k, v := range m {
  567. if _, ok := ret[k]; !ok {
  568. ret[k] = v
  569. }
  570. }
  571. }
  572. return ret
  573. }
  574. /*
  575. Describe describes a thread currently observed by the debugger.
  576. */
  577. func (ed *ecalDebugger) prettyPrintCallStack(threadCallStack []*parser.ASTNode) []string {
  578. cs := []string{}
  579. for _, s := range threadCallStack {
  580. pp, _ := parser.PrettyPrint(s)
  581. cs = append(cs, fmt.Sprintf("%v (%v:%v)",
  582. pp, s.Token.Lsource, s.Token.Lline))
  583. }
  584. return cs
  585. }