From b8ecdf487b20ff93abeaa8b4f000a37c4c17dd7e Mon Sep 17 00:00:00 2001 From: Reuben Thompson Date: Sun, 17 May 2020 17:31:14 +0100 Subject: [PATCH 1/9] A debugging experiment --- go.mod | 4 +- go.sum | 53 +++ lisp/x/debugger/debugger.go | 469 ++++++++++++++++++++++ lisp/x/debugger/debugger_test.go | 45 +++ lisp/x/debugger/server/rpc.go | 301 ++++++++++++++ lisp/x/debugger/server/server.go | 316 +++++++++++++++ lisp/x/debugger/server/server_debugger.go | 30 ++ 7 files changed, 1216 insertions(+), 2 deletions(-) create mode 100644 lisp/x/debugger/debugger.go create mode 100644 lisp/x/debugger/debugger_test.go create mode 100644 lisp/x/debugger/server/rpc.go create mode 100644 lisp/x/debugger/server/server.go create mode 100644 lisp/x/debugger/server/server_debugger.go diff --git a/go.mod b/go.mod index c7bcef7..041172d 100644 --- a/go.mod +++ b/go.mod @@ -7,15 +7,15 @@ require ( github.com/chzyer/readline v0.0.0-20160726135117-62c6fe619375 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fsnotify/fsnotify v1.4.7 // indirect + github.com/go-delve/delve v1.4.0 github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/magiconair/properties v1.8.0 // indirect github.com/mitchellh/go-homedir v0.0.0-20180523094522-3864e76763d9 github.com/mitchellh/mapstructure v0.0.0-20180511142126-bb74f1db0675 // indirect github.com/pelletier/go-toml v1.1.0 // indirect github.com/prataprc/goparsec v0.0.0-20180208125142-3db61e7995f1 + github.com/sirupsen/logrus v0.0.0-20180523074243-ea8897e79973 github.com/spf13/afero v1.1.0 // indirect github.com/spf13/cast v1.2.0 // indirect github.com/spf13/cobra v0.0.3 diff --git a/go.sum b/go.sum index 8aa4678..754db29 100644 --- a/go.sum +++ b/go.sum @@ -8,11 +8,15 @@ github.com/chzyer/readline v0.0.0-20160726135117-62c6fe619375/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ= +github.com/cpuguy83/go-md2man v1.0.8/go.mod h1:N6JayAiVKtlHSnuTCeuLSQVs75hb8q+dYQLjr7cDsKY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-delve/delve v1.4.0 h1:O+1dw1XBZXqhC6fIPQwGxLlbd2wDRau7NxNhVpw02ag= +github.com/go-delve/delve v1.4.0/go.mod h1:gQM0ReOJLNAvPuKAXfjHngtE93C2yc/ekTbo7YbAHSo= github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4= github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -20,41 +24,70 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno= github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mitchellh/go-homedir v0.0.0-20180523094522-3864e76763d9 h1:Y94YB7jrsihrbGSqRNMwRWJ2/dCxr0hdC2oPRohkx0A= github.com/mitchellh/go-homedir v0.0.0-20180523094522-3864e76763d9/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v0.0.0-20180511142126-bb74f1db0675 h1:/rdJjIiKG5rRdwG5yxHmSE/7ZREjpyC0kL7GxGT/qJw= github.com/mitchellh/mapstructure v0.0.0-20180511142126-bb74f1db0675/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pelletier/go-toml v1.1.0 h1:cmiOvKzEunMsAxyhXSzpL5Q1CRKpVv0KQsnAIcSEVYM= github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= +github.com/pkg/profile v0.0.0-20170413231811-06b906832ed0 h1:wBza4Dlm/NCQF572oSGNZ69flNFxlwIHjtwS6oy3Rvw= +github.com/pkg/profile v0.0.0-20170413231811-06b906832ed0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prataprc/goparsec v0.0.0-20180208125142-3db61e7995f1 h1:19MwL2eVeBf4Dqpu34nZL53jZCXi90K9BiC7YHwAUVw= github.com/prataprc/goparsec v0.0.0-20180208125142-3db61e7995f1/go.mod h1:YbpxZqbf10o5u96/iDpcfDQmbIOTX/iNCH/yBByTfaM= +github.com/russross/blackfriday v0.0.0-20180428102519-11635eb403ff/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sirupsen/logrus v0.0.0-20180523074243-ea8897e79973 h1:3AJZYTzw3gm3TNTt30x0CCKD7GOn2sdd50Hn35fQkGY= +github.com/sirupsen/logrus v0.0.0-20180523074243-ea8897e79973/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/spf13/afero v1.1.0 h1:bopulORc2JeYaxfHLvJa5NzxviA9PoWhpiiJkru7Ji4= github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg= github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= +github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec h1:2ZXvIUGghLpdTVHR1UfvfrzoVlZaE/yOWC5LueIHZig= github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.0.2 h1:Ncr3ZIuJn322w2k1qmzXDnkLAdQMlJqBa9kfAH+irso= github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.starlark.net v0.0.0-20190702223751-32f345186213/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg= +golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4 h1:QlVATYS7JBoZMVaf+cNjb90WD/beKVHnIxFKT4QaHVI= +golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -62,17 +95,23 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd h1:r7DufRZuZbWB7j439YfAzP8RPDa9unLkpwQKUYbIMPI= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= @@ -81,14 +120,28 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/lisp/x/debugger/debugger.go b/lisp/x/debugger/debugger.go new file mode 100644 index 0000000..8572e44 --- /dev/null +++ b/lisp/x/debugger/debugger.go @@ -0,0 +1,469 @@ +package debugger + +import ( + "errors" + "github.com/go-delve/delve/service/rpc2" + "github.com/luthersystems/elps/lisp" + "github.com/luthersystems/elps/lisp/x/debugger/server" + "github.com/sirupsen/logrus" + "math/rand" + "reflect" + "strconv" + "sync" + "time" +) +import "github.com/go-delve/delve/service/api" + +type Debugger struct { + sync.Mutex + enabled bool + breakpoints map[int]*api.Breakpoint + runtime *lisp.Runtime + lastModified time.Time + stopped bool + step chan bool + run chan bool + server *server.RPCServer + currentOp *lisp.LVal + logger *logrus.Logger +} + +func NewDebugger(runtime *lisp.Runtime, address string) lisp.Profiler { + db := &Debugger{ + runtime: runtime, + enabled: true, + stopped: true, + run: make(chan bool), + step: make(chan bool), + lastModified: time.Now(), + breakpoints: make(map[int]*api.Breakpoint), + logger: logrus.New(), + } + srv := server.NewServer(db, address) + srv.Run() + db.server = srv + runtime.Profiler = db + return db +} + +func (d *Debugger) IsEnabled() bool { + return d.enabled +} + +func (d *Debugger) Enable() error { + if d.enabled { + return nil + } + return errors.New("Cannot re-enable debugger from here") +} + +func (d *Debugger) SetFile(filename string) error { + return errors.New("Not used") +} + +func (d *Debugger) Complete() error { + d.Lock() + defer d.Unlock() + d.logger.Infof("COMPLETE") + d.enabled = false + d.server.Stop() + d.run <- true + return nil +} + +func (d *Debugger) Start(function *lisp.LVal) { + d.logger.Infof("Instr %v", function.Type) + switch function.Type { + case lisp.LInt: + return + case lisp.LBytes: + return + case lisp.LString: + return + case lisp.LFloat: + return + case lisp.LSortMap: + return + case lisp.LArray: + return + } + if !d.stopped { + d.Lock() + for _, v := range d.breakpoints { + if v.File == function.Source.File && v.Line == function.Source.Line { + d.logger.Infof("BREAKPOINT") + d.stopped = true + d.Unlock() + d.Start(function) + return + } + } + d.Unlock() + return + } + select { + case <-d.run: + d.logger.Infof("RUN") + d.stopped = false + case <-d.step: + d.logger.Infof("STEP") + } + d.currentOp = function +} + +func (d *Debugger) End(function *lisp.LVal) { + d.logger.Infof("End %v",function) + // no op for now +} + +func (d *Debugger) GetBreakpoint(id int) (*api.Breakpoint, error) { + d.Lock() + defer d.Unlock() + if bp, ok := d.breakpoints[id]; ok { + return bp, nil + } + return nil, errors.New("not found") +} + +func (d *Debugger) GetBreakpointByName(name string) (*api.Breakpoint, error) { + d.Lock() + defer d.Unlock() + for _, v := range d.breakpoints { + if v.Name == name { + return v, nil + } + } + return nil, errors.New("not found") +} + +func (d *Debugger) GetStacktrace(st *rpc2.StacktraceOut) { + d.Lock() + defer d.Unlock() + st.Locations = make([]api.Stackframe, 0) + for _, frame := range d.runtime.Stack.Frames { + st.Locations = append(st.Locations, api.Stackframe{ + Location: api.Location{ + PC: 0, + File: frame.Source.File, + Line: frame.Source.Line, + Function: &api.Function{ + Name_: "", + Value: 0, + Type: 0, + GoType: 0, + Optimized: false, + }, + PCs: nil, + }, + Locals: nil, + Arguments: nil, + FrameOffset: 0, + FramePointerOffset: 0, + Defers: nil, + Bottom: false, + Err: "", + }) + } +} + +func (d *Debugger) GetAllBreakpoints() map[int]*api.Breakpoint { + d.Lock() + defer d.Unlock() + return d.breakpoints +} + +func (d *Debugger) CreateBreakpoint(breakpoint *api.Breakpoint) *api.Breakpoint { + d.Lock() + defer d.Unlock() + d.lastModified = time.Now() + if breakpoint.ID == 0 { + breakpoint.ID = rand.Int() + } + d.breakpoints[breakpoint.ID] = breakpoint + return breakpoint +} + +func (d *Debugger) RemoveBreakpoint(id int) error { + d.Lock() + defer d.Unlock() + d.lastModified = time.Now() + _, err := d.GetBreakpoint(id) + if err != nil { + return err + } + d.breakpoints[id] = nil + return nil +} + +func (d *Debugger) AmendBreakpoint(bp *api.Breakpoint) error { + d.Lock() + defer d.Unlock() + d.lastModified = time.Now() + _, err := d.GetBreakpoint(bp.ID) + if err != nil { + return err + } + d.breakpoints[bp.ID] = bp + return nil +} + +func (d *Debugger) GetThread() *api.Thread { + return &api.Thread{ + ID: 1, + PC: 0, + File: "", + Line: 0, + Function: nil, + GoroutineID: 0, + Breakpoint: nil, + BreakpointInfo: nil, + ReturnValues: nil, + } +} + +func mapLispType(in lisp.LType) string { + switch in { + case lisp.LFloat: + return "float" + case lisp.LInt: + return "int" + case lisp.LString: + return "string" + case lisp.LQSymbol: + return "q_symbol" + case lisp.LSExpr: + return "s_expr" + case lisp.LSortMap: + return "sorted map" + case lisp.LNative: + return "native" + case lisp.LFun: + return "function" + case lisp.LQuote: + return "quote" + case lisp.LArray: + return "array" + case lisp.LError: + return "error" + case lisp.LBytes: + return "[]byte" + case lisp.LInvalid: + return "invalid" + default: + return "unknown" + + } +} + +func mapKind(in *lisp.LVal) reflect.Kind { + switch in.Type { + case lisp.LFloat: + return reflect.Float64 + case lisp.LInt: + return reflect.Int + case lisp.LString: + return reflect.String + case lisp.LQSymbol: + return reflect.Struct + case lisp.LSExpr: + return reflect.Struct + case lisp.LSortMap: + return reflect.Map + case lisp.LNative: + return reflect.ValueOf(in.Native).Kind() + case lisp.LFun: + return reflect.Func + case lisp.LQuote: + return reflect.String + case lisp.LArray: + return reflect.Slice + case lisp.LError: + return reflect.Struct + case lisp.LBytes: + return reflect.Slice + case lisp.LInvalid: + return reflect.Invalid + default: + return reflect.Invalid + } +} + +func (d *Debugger) GetVariables() []api.Variable { + d.Lock() + defer d.Unlock() + out := make([]api.Variable, len(d.runtime.Package.Symbols)) + count := 0 + for k, v := range d.runtime.Package.Symbols { + out[count] = api.Variable{ + Name: k, + Addr: uintptr(count), + OnlyAddr: false, + Type: mapLispType(v.Type), + RealType: mapLispType(v.Type), + Flags: 0, + Kind: mapKind(v), + Value: v.Str, + Len: 0, + Cap: 0, + Children: []api.Variable{}, + Base: 0, + Unreadable: "", + LocationExpr: "", + DeclLine: int64(v.Source.Line), + } + count++ + } + return out +} + +func (d *Debugger) GetFunctionArgs() []api.Variable { + return []api.Variable{} +} + +func (d *Debugger) GetGoRoutine() *api.Goroutine { + d.Lock() + defer d.Unlock() + var loc *api.Location + if d.currentOp != nil { + loc = &api.Location{ + PC: uint64(d.currentOp.Source.Pos), + File: d.currentOp.Source.File, + Line: d.currentOp.Source.Line, + Function: &api.Function{ + Name_: "", + Value: 0, + Type: 0, + GoType: 0, + Optimized: false, + }, + PCs: nil, + } + } else { + loc = &api.Location{ + PC: 0, + } + } + return &api.Goroutine{ + ID: 0, + CurrentLoc: *loc, + UserCurrentLoc: *loc, + GoStatementLoc: *loc, + StartLoc: api.Location{}, + ThreadID: 0, + Unreadable: "", + } +} + +func (d *Debugger) SetVariableInScope(scope api.EvalScope, symbol string, value string) error { + d.Lock() + defer d.Unlock() + d.lastModified = time.Now() + if existing, exists := d.runtime.Package.Symbols[symbol]; exists { + switch existing.Type { + case lisp.LString: + d.runtime.Package.Symbols[symbol].Str = value + return nil + case lisp.LInt: + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + d.runtime.Package.Symbols[symbol].Int = int(v) + return nil + case lisp.LFloat: + v, err := strconv.ParseFloat(value, 64) + if err != nil { + return err + } + d.runtime.Package.Symbols[symbol].Float = v + return nil + } + return errors.New("Cannot set this type") + } + return errors.New("Symbol not in scope") +} + +func (d *Debugger) Sources(filter string) ([]string, error) { + return []string{}, nil +} + +func (d *Debugger) Functions(filter string) ([]string, error) { + d.Lock() + defer d.Unlock() + out := make([]string, len(d.runtime.Package.FunNames)) + count := 0 + for _, v := range d.runtime.Package.FunNames { + out[count] = v + count++ + } + return out, nil +} + +func (d *Debugger) FindLocation(scope api.EvalScope, loc string, lines bool) ([]api.Location, error) { + return nil, errors.New("Not implemented... yet") +} + +func (d *Debugger) ListPackagesBuildInfo(files bool) []api.PackageBuildInfo { + d.Lock() + defer d.Unlock() + out := make([]api.PackageBuildInfo, 0) + for k, v := range d.runtime.Registry.Packages { + out = append(out, api.PackageBuildInfo{ + ImportPath: k, + DirectoryPath: v.Name, + Files: nil, + }) + } + return out +} + +func (d *Debugger) State(blocking bool) (*api.DebuggerState, error) { + d.Lock() + defer d.Unlock() + state := &api.DebuggerState{ + Running: !d.stopped, + CurrentThread: d.GetThread(), + SelectedGoroutine: nil, + Threads: []*api.Thread{d.GetThread()}, + NextInProgress: false, + Exited: !d.enabled, + ExitStatus: 0, + When: "", + Err: nil, + } + return state, nil +} + +func (d *Debugger) Command(a *api.DebuggerCommand) (*api.DebuggerState, error) { + d.logger.Infof("Command: %s", a.Name) + d.lastModified = time.Now() + switch a.Name { + case api.Halt: + d.stopped = true + case api.Continue: + go func(d *Debugger) { + d.run <- true + }(d) + case api.Next: + return nil, errors.New("Not implemented") + case api.Step: + go func(d *Debugger) { + d.step <- true + }(d) + case api.Call: + return nil, errors.New("Not implemented") + case api.ReverseStepInstruction: + return nil, errors.New("Not implemented") + case api.Rewind: + return nil, errors.New("Not implemented") + case api.SwitchGoroutine: + return nil, errors.New("Not implemented") + case api.SwitchThread: + return nil, errors.New("Not implemented") + } + return d.State(false) +} + +func (d *Debugger) LastModified() time.Time { + return d.lastModified +} diff --git a/lisp/x/debugger/debugger_test.go b/lisp/x/debugger/debugger_test.go new file mode 100644 index 0000000..b99e7d0 --- /dev/null +++ b/lisp/x/debugger/debugger_test.go @@ -0,0 +1,45 @@ +package debugger + +import ( + "github.com/luthersystems/elps/lisp" + "github.com/luthersystems/elps/parser" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestNewDebugger(t *testing.T) { + env := lisp.NewEnv(nil) + env.Runtime.Reader = parser.NewReader() + lerr := lisp.InitializeUserEnv(env) + if lisp.GoError(lerr) != nil { + t.Fatal(lisp.GoError(lerr)) + } + // Create a profiler + profiler := NewDebugger(env.Runtime, ":8883") + var testsrc *lisp.LVal + // Some spurious functions to check we get a profile out + testsrc = env.LoadString("test.lisp", ` +(set 'j 4) +(defun print-it + ('x) + (debug-print x) +) +(defun add-it + ('x 'y) + (+ x y) +) +(defun recurse-it + ('x) + (if + (< x j) + (recurse-it (- x 1)) + (add-it x 3) + ) +) +(print-it "Hello") +(print-it (add-it (add-it 3 (recurse-it 5)) 8))`) + lerr = env.Eval(testsrc) + assert.NotEqual(t, lisp.LError, lerr.Type) + profiler.Complete() + t.Fail() +} diff --git a/lisp/x/debugger/server/rpc.go b/lisp/x/debugger/server/rpc.go new file mode 100644 index 0000000..b2a1d4c --- /dev/null +++ b/lisp/x/debugger/server/rpc.go @@ -0,0 +1,301 @@ +package server + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/go-delve/delve/pkg/logflags" + "github.com/sirupsen/logrus" + "io" + "net/rpc" + "net/rpc/jsonrpc" + "reflect" + "runtime" + "sync" + "unicode" + "unicode/utf8" +) + +func (s *RPCServer) Run() error { + go func() { + defer s.listener.Close() + for { + c, err := s.listener.Accept() + if err != nil { + s.log.Errorf("Error starting: %v", err) + select { + case <-s.stopChan: + // We were supposed to exit, do nothing and return + return + default: + panic(err) + } + } + + go s.serveJSONCodec(c) + } + }() + return nil +} + +func suitableMethods(rcvr interface{}, methods map[string]*methodType, log *logrus.Entry) { + typ := reflect.TypeOf(rcvr) + rcvrv := reflect.ValueOf(rcvr) + sname := reflect.Indirect(rcvrv).Type().Name() + if sname == "" { + log.Debugf("rpc.Register: no service name for type %s", typ) + return + } + for m := 0; m < typ.NumMethod(); m++ { + method := typ.Method(m) + mname := method.Name + mtype := method.Type + // method must be exported + if method.PkgPath != "" { + continue + } + // Method needs three ins: (receive, *args, *reply) or (receiver, *args, *RPCCallback) + if mtype.NumIn() != 3 { + log.Warn("method", mname, "has wrong number of ins:", mtype.NumIn()) + continue + } + // First arg need not be a pointer. + argType := mtype.In(1) + if !isExportedOrBuiltinType(argType) { + log.Warn(mname, "argument type not exported:", argType) + continue + } + + replyType := mtype.In(2) + synchronous := replyType.String() != "service.RPCCallback" + + if synchronous { + // Second arg must be a pointer. + if replyType.Kind() != reflect.Ptr { + log.Warn("method", mname, "reply type not a pointer:", replyType) + continue + } + // Reply type must be exported. + if !isExportedOrBuiltinType(replyType) { + log.Warn("method", mname, "reply type not exported:", replyType) + continue + } + + // Method needs one out. + if mtype.NumOut() != 1 { + log.Warn("method", mname, "has wrong number of outs:", mtype.NumOut()) + continue + } + // The return type of the method must be error. + if returnType := mtype.Out(0); returnType != typeOfError { + log.Warn("method", mname, "returns", returnType.String(), "not error") + continue + } + } else { + // Method needs zero outs. + if mtype.NumOut() != 0 { + log.Warn("method", mname, "has wrong number of outs:", mtype.NumOut()) + continue + } + } + methods[sname+"."+mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType, Synchronous: synchronous, Rcvr: rcvrv} + } +} + +type methodType struct { + method reflect.Method + Rcvr reflect.Value + ArgType reflect.Type + ReplyType reflect.Type + Synchronous bool +} + +var typeOfError = reflect.TypeOf((*error)(nil)).Elem() + +func isExportedOrBuiltinType(t reflect.Type) bool { + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + // PkgPath will be non-empty even for an exported type, + // so we need to check the type name as well. + return isExported(t.Name()) || t.PkgPath() == "" +} + +func isExported(name string) bool { + rune, _ := utf8.DecodeRuneInString(name) + return unicode.IsUpper(rune) +} + +var invalidRequest = struct{}{} + +func (s *RPCServer) sendResponse(sending *sync.Mutex, req *rpc.Request, resp *rpc.Response, reply interface{}, codec rpc.ServerCodec, errmsg string) { + resp.ServiceMethod = req.ServiceMethod + if errmsg != "" { + resp.Error = errmsg + reply = invalidRequest + } + resp.Seq = req.Seq + sending.Lock() + defer sending.Unlock() + err := codec.WriteResponse(resp, reply) + if err != nil { + s.log.Error("writing response:", err) + } +} + +type internalError struct { + Err interface{} + Stack []internalErrorFrame +} + +type internalErrorFrame struct { + Pc uintptr + Func string + File string + Line int +} + +func newInternalError(ierr interface{}, skip int) *internalError { + r := &internalError{ierr, nil} + for i := skip; ; i++ { + pc, file, line, ok := runtime.Caller(i) + if !ok { + break + } + fname := "" + fn := runtime.FuncForPC(pc) + if fn != nil { + fname = fn.Name() + } + r.Stack = append(r.Stack, internalErrorFrame{pc, fname, file, line}) + } + return r +} + +func (err *internalError) Error() string { + var out bytes.Buffer + fmt.Fprintf(&out, "Internal debugger error: %v\n", err.Err) + for _, frame := range err.Stack { + fmt.Fprintf(&out, "%s (%#x)\n\t%s:%d\n", frame.Func, frame.Pc, frame.File, frame.Line) + } + return out.String() +} + +func (s *RPCServer) serveJSONCodec(conn io.ReadWriteCloser) { + sending := new(sync.Mutex) + codec := jsonrpc.NewServerCodec(conn) + var req rpc.Request + var resp rpc.Response + for { + req = rpc.Request{} + err := codec.ReadRequestHeader(&req) + if err != nil { + if err != io.EOF { + s.log.Error("rpc:", err) + } + break + } + + mtype, ok := s.methods[req.ServiceMethod] + if !ok { + s.log.Errorf("rpc: can't find method %s", req.ServiceMethod) + s.sendResponse(sending, &req, &rpc.Response{}, nil, codec, fmt.Sprintf("unknown method: %s", req.ServiceMethod)) + continue + } + + var argv, replyv reflect.Value + + // Decode the argument value. + argIsValue := false // if true, need to indirect before calling. + if mtype.ArgType.Kind() == reflect.Ptr { + argv = reflect.New(mtype.ArgType.Elem()) + } else { + argv = reflect.New(mtype.ArgType) + argIsValue = true + } + // argv guaranteed to be a pointer now. + if err = codec.ReadRequestBody(argv.Interface()); err != nil { + return + } + if argIsValue { + argv = argv.Elem() + } + s.log.Infof("Received req for %s with arg %v", mtype.method.Name, argv) + if mtype.Synchronous { + if logflags.RPC() { + argvbytes, _ := json.Marshal(argv.Interface()) + s.log.Debugf("<- %s(%T%s)", req.ServiceMethod, argv.Interface(), argvbytes) + } + replyv = reflect.New(mtype.ReplyType.Elem()) + function := mtype.method.Func + var returnValues []reflect.Value + var errInter interface{} + func() { + defer func() { + if ierr := recover(); ierr != nil { + errInter = newInternalError(ierr, 2) + } + }() + returnValues = function.Call([]reflect.Value{mtype.Rcvr, argv, replyv}) + errInter = returnValues[0].Interface() + }() + + errmsg := "" + if errInter != nil { + errmsg = errInter.(error).Error() + } + resp = rpc.Response{} + if logflags.RPC() { + replyvbytes, _ := json.Marshal(replyv.Interface()) + s.log.Debugf("-> %T%s error: %q", replyv.Interface(), replyvbytes, errmsg) + } + s.sendResponse(sending, &req, &resp, replyv.Interface(), codec, errmsg) + } else { + if logflags.RPC() { + argvbytes, _ := json.Marshal(argv.Interface()) + s.log.Debugf("(async %d) <- %s(%T%s)", req.Seq, req.ServiceMethod, argv.Interface(), argvbytes) + } + function := mtype.method.Func + ctl := &RPCCallback{s, sending, codec, req} + go func() { + defer func() { + if ierr := recover(); ierr != nil { + ctl.Return(nil, newInternalError(ierr, 2)) + } + }() + function.Call([]reflect.Value{mtype.Rcvr, argv, reflect.ValueOf(ctl)}) + }() + } + } + codec.Close() +} + +func (cb *RPCCallback) Return(out interface{}, err error) { + errmsg := "" + if err != nil { + errmsg = err.Error() + } + var resp rpc.Response + if logflags.RPC() { + outbytes, _ := json.Marshal(out) + cb.s.log.Debugf("(async %d) -> %T%s error: %q", cb.req.Seq, out, outbytes, errmsg) + } + cb.s.sendResponse(cb.sending, &cb.req, &resp, out, cb.codec, errmsg) +} + +type RPCCallback struct { + s *RPCServer + sending *sync.Mutex + codec rpc.ServerCodec + req rpc.Request +} + +// Stop stops the JSON-RPC server. +func (s *RPCServer) Stop() error { + if s.stopChan != nil { + close(s.stopChan) + s.stopChan = nil + } + s.listener.Close() + return s.debugger.Complete() +} diff --git a/lisp/x/debugger/server/server.go b/lisp/x/debugger/server/server.go new file mode 100644 index 0000000..862afbe --- /dev/null +++ b/lisp/x/debugger/server/server.go @@ -0,0 +1,316 @@ +package server + +import ( + "errors" + "fmt" + "github.com/go-delve/delve/pkg/logflags" + "github.com/go-delve/delve/service" + "github.com/go-delve/delve/service/api" + "github.com/go-delve/delve/service/rpc2" + "github.com/sirupsen/logrus" + "net" +) + +type RPCServer struct { + // debugger is a debugger service. + debugger ServerDebugger + // listener is used to serve HTTP. + listener net.Listener + // stopChan is used to stop the listener goroutine. + stopChan chan struct{} + log *logrus.Entry + methods map[string]*methodType +} + +func NewServer(debugger ServerDebugger, address string) *RPCServer { + listener, err := net.Listen("tcp", address) + if err != nil { + panic(fmt.Sprintf("Can't bind to %s: %v", address, err)) + } + logger := logflags.RPCLogger() + logflags.Setup(true, "", "") + logflags.WriteAPIListeningMessage(address) + logger.Level = logrus.DebugLevel + rpc := &RPCServer{ + listener: listener, + debugger: debugger, + stopChan: make(chan struct{}), + log: logger, + } + methods := make(map[string]*methodType) + suitableMethods(rpc, methods, rpc.log) + rpc.methods = methods + return rpc +} + +func (s *RPCServer) LastModified(arg rpc2.LastModifiedIn, out *rpc2.LastModifiedOut) error { + out.Time = s.debugger.LastModified() + return nil +} + +// Detach detaches the debugger, optionally killing the process. +func (s *RPCServer) Detach(arg rpc2.DetachIn, out *rpc2.DetachOut) error { + err := s.debugger.Complete() + if arg.Kill { + panic("Quitting on command of debugger") + } + return err +} + +// Restart restarts program. +func (s *RPCServer) Restart(arg rpc2.RestartIn, cb service.RPCCallback) { + panic("Not implemented") +} + +// State returns the current debugger state. +func (s *RPCServer) State(arg rpc2.StateIn, cb service.RPCCallback) { + var out rpc2.StateOut + st, err := s.debugger.State(arg.NonBlocking) + if err != nil { + cb.Return(nil, err) + return + } + out.State = st + cb.Return(out, nil) +} + +// Command interrupts, continues and steps through the program. +func (s *RPCServer) Command(command api.DebuggerCommand, cb service.RPCCallback) { + st, err := s.debugger.Command(&command) + if err != nil { + cb.Return(nil, err) + return + } + var out rpc2.CommandOut + out.State = *st + cb.Return(out, nil) +} + +// GetBreakpoint gets a breakpoint by Name (if Name is not an empty string) or by ID. +func (s *RPCServer) GetBreakpoint(arg rpc2.GetBreakpointIn, out *rpc2.GetBreakpointOut) error { + var bp *api.Breakpoint + var err error + if arg.Name != "" { + bp, err = s.debugger.GetBreakpointByName(arg.Name) + if err == nil { + return fmt.Errorf("no breakpoint with name %s", arg.Name) + } + } else { + bp, err = s.debugger.GetBreakpoint(arg.Id) + if err == nil { + return fmt.Errorf("no breakpoint with id %d", arg.Id) + } + } + out.Breakpoint = *bp + return nil +} + +// Stacktrace returns stacktrace of goroutine Id up to the specified Depth. +// +// If Full is set it will also the variable of all local variables +// and function arguments of all stack frames. +func (s *RPCServer) Stacktrace(arg rpc2.StacktraceIn, out *rpc2.StacktraceOut) error { + s.debugger.GetStacktrace(out) + return nil +} + +// Ancestors returns the stacktraces for the ancestors of a goroutine. +func (s *RPCServer) Ancestors(arg rpc2.AncestorsIn, out *rpc2.AncestorsOut) error { + out.Ancestors = []api.Ancestor{} + return nil +} + +// ListBreakpoints gets all breakpoints. +func (s *RPCServer) ListBreakpoints(arg rpc2.ListBreakpointsIn, out *rpc2.ListBreakpointsOut) error { + breakpoints := s.debugger.GetAllBreakpoints() + for _, v := range breakpoints { + out.Breakpoints = append(out.Breakpoints, v) + } + return nil +} + +func (s *RPCServer) CreateBreakpoint(arg rpc2.CreateBreakpointIn, out *rpc2.CreateBreakpointOut) error { + out.Breakpoint = *s.debugger.CreateBreakpoint(&arg.Breakpoint) + return nil +} + +func (s *RPCServer) ClearBreakpoint(arg rpc2.ClearBreakpointIn, out *rpc2.ClearBreakpointOut) error { + var bp *api.Breakpoint + var err error + if arg.Name != "" { + bp, err = s.debugger.GetBreakpointByName(arg.Name) + if err == nil { + return fmt.Errorf("no breakpoint with name %s", arg.Name) + } + } else { + bp, err = s.debugger.GetBreakpoint(arg.Id) + if err == nil { + return fmt.Errorf("no breakpoint with id %d", arg.Id) + } + } + err = s.debugger.RemoveBreakpoint(bp.ID) + if err != nil { + return err + } + out.Breakpoint = bp + return nil +} + +func (s *RPCServer) AmendBreakpoint(arg rpc2.AmendBreakpointIn, out *rpc2.AmendBreakpointOut) error { + return s.debugger.AmendBreakpoint(&arg.Breakpoint) +} + +func (s *RPCServer) CancelNext(arg rpc2.CancelNextIn, out *rpc2.CancelNextOut) error { + return nil +} + +func (s *RPCServer) ListThreads(arg rpc2.ListThreadsIn, out *rpc2.ListThreadsOut) (err error) { + out.Threads = []*api.Thread{ + s.debugger.GetThread(), + } + return nil +} + +func (s *RPCServer) GetThread(arg rpc2.GetThreadIn, out *rpc2.GetThreadOut) error { + if arg.Id != 0 { + return errors.New("We've only got one thread...") + } + out.Thread = s.debugger.GetThread() + return nil +} + +func (s *RPCServer) ListPackageVars(arg rpc2.ListPackageVarsIn, out *rpc2.ListPackageVarsOut) error { + out.Variables = s.debugger.GetVariables() + return nil +} + +func (s *RPCServer) ListRegisters(arg rpc2.ListRegistersIn, out *rpc2.ListRegistersOut) error { + out.Registers = "" + out.Regs = []api.Register{} + return nil +} + +func (s *RPCServer) ListLocalVars(arg rpc2.ListLocalVarsIn, out *rpc2.ListLocalVarsOut) error { + out.Variables = s.debugger.GetVariables() + return nil +} + +func (s *RPCServer) ListFunctionArgs(arg rpc2.ListFunctionArgsIn, out *rpc2.ListFunctionArgsOut) error { + out.Args = s.debugger.GetFunctionArgs() + return nil +} + +func (s *RPCServer) Eval(arg rpc2.EvalIn, out *rpc2.EvalOut) error { + return nil +} + +func (s *RPCServer) Set(arg rpc2.SetIn, out *rpc2.SetOut) error { + return s.debugger.SetVariableInScope(arg.Scope, arg.Symbol, arg.Value) +} + +func (s *RPCServer) ListSources(arg rpc2.ListSourcesIn, out *rpc2.ListSourcesOut) error { + ss, err := s.debugger.Sources(arg.Filter) + if err != nil { + return err + } + out.Sources = ss + return nil +} + +func (s *RPCServer) ListFunctions(arg rpc2.ListFunctionsIn, out *rpc2.ListFunctionsOut) error { + fns, err := s.debugger.Functions(arg.Filter) + if err != nil { + return err + } + out.Funcs = fns + return nil +} + +func (s *RPCServer) ListTypes(arg rpc2.ListTypesIn, out *rpc2.ListTypesOut) error { + return nil +} + +func (s *RPCServer) ListGoroutines(arg rpc2.ListGoroutinesIn, out *rpc2.ListGoroutinesOut) error { + out.Goroutines = []*api.Goroutine{ + s.debugger.GetGoRoutine(), + } + return nil +} + +func (c *RPCServer) AttachedToExistingProcess(arg rpc2.AttachedToExistingProcessIn, out *rpc2.AttachedToExistingProcessOut) error { + out.Answer = true + return nil +} + +// FindLocation returns concrete location information described by a location expression. +// +// loc ::= : | [:] | // | (+|-) | | *
+// * can be the full path of a file or just a suffix +// * ::= .. | .(*). | . | . | (*). | +// * must be unambiguous +// * // will return a location for each function matched by regex +// * + returns a location for the line that is lines after the current line +// * - returns a location for the line that is lines before the current line +// * returns a location for a line in the current file +// * *
returns the location corresponding to the specified address +// +// NOTE: this function does not actually set breakpoints. +func (c *RPCServer) FindLocation(arg rpc2.FindLocationIn, out *rpc2.FindLocationOut) error { + var err error + out.Locations, err = c.debugger.FindLocation(arg.Scope, arg.Loc, arg.IncludeNonExecutableLines) + return err +} + +func (c *RPCServer) Disassemble(arg rpc2.DisassembleIn, out *rpc2.DisassembleOut) error { + return errors.New("Can't dissasemble Lisp...") +} + +func (s *RPCServer) Recorded(arg rpc2.RecordedIn, out *rpc2.RecordedOut) error { + return nil +} + +func (s *RPCServer) Checkpoint(arg rpc2.CheckpointIn, out *rpc2.CheckpointOut) error { + return errors.New("Not implemented") +} + +func (s *RPCServer) ListCheckpoints(arg rpc2.ListCheckpointsIn, out *rpc2.ListCheckpointsOut) error { + return errors.New("Not implemented") +} + +func (s *RPCServer) ClearCheckpoint(arg rpc2.ClearCheckpointIn, out *rpc2.ClearCheckpointOut) error { + return errors.New("Not implemented") +} + +func (s *RPCServer) IsMulticlient(arg rpc2.IsMulticlientIn, out *rpc2.IsMulticlientOut) error { + out.IsMulticlient = false + return nil +} + +func (s *RPCServer) FunctionReturnLocations(in rpc2.FunctionReturnLocationsIn, out *rpc2.FunctionReturnLocationsOut) error { + return nil +} + +func (s *RPCServer) ListDynamicLibraries(in rpc2.ListDynamicLibrariesIn, out *rpc2.ListDynamicLibrariesOut) error { + return nil +} + +func (s *RPCServer) ListPackagesBuildInfo(in rpc2.ListPackagesBuildInfoIn, out *rpc2.ListPackagesBuildInfoOut) error { + out.List = s.debugger.ListPackagesBuildInfo(in.IncludeFiles) + return nil +} + +type StopRecordingIn struct { +} + +type StopRecordingOut struct { +} + +func (s *RPCServer) StopRecording(arg StopRecordingIn, cb service.RPCCallback) { + var out StopRecordingOut + err := s.debugger.Complete() + if err != nil { + cb.Return(nil, err) + return + } + cb.Return(out, nil) +} diff --git a/lisp/x/debugger/server/server_debugger.go b/lisp/x/debugger/server/server_debugger.go new file mode 100644 index 0000000..75748b5 --- /dev/null +++ b/lisp/x/debugger/server/server_debugger.go @@ -0,0 +1,30 @@ +package server + +import ( + "github.com/go-delve/delve/service/api" + "github.com/go-delve/delve/service/rpc2" + "time" +) + +type ServerDebugger interface { + GetBreakpoint(id int) (*api.Breakpoint, error) + GetBreakpointByName(name string) (*api.Breakpoint, error) + GetStacktrace(st *rpc2.StacktraceOut) + GetAllBreakpoints() map[int]*api.Breakpoint + CreateBreakpoint(breakpoint *api.Breakpoint) *api.Breakpoint + RemoveBreakpoint(id int) error + AmendBreakpoint(bp *api.Breakpoint) error + GetThread() *api.Thread + GetVariables() []api.Variable + GetFunctionArgs() []api.Variable + SetVariableInScope(scope api.EvalScope, symbol string, value string) error + Sources(filter string) ([]string, error) + Functions(filter string) ([]string, error) + FindLocation(scope api.EvalScope, loc string, lines bool) ([]api.Location, error) + ListPackagesBuildInfo(files bool) []api.PackageBuildInfo + State(blocking bool) (*api.DebuggerState, error) + Command(a *api.DebuggerCommand) (*api.DebuggerState, error) + LastModified() time.Time + Complete() error + GetGoRoutine() *api.Goroutine +} \ No newline at end of file From ddb75cca0816fb2a9d266ca2996e3864a80c5a4c Mon Sep 17 00:00:00 2001 From: Reuben Thompson Date: Tue, 26 May 2020 09:41:59 +0100 Subject: [PATCH 2/9] More work on debugger --- lisp/package.go | 2 +- lisp/x/debugger/debugger.go | 362 ++++++++++++++++++---- lisp/x/debugger/debugger_test.go | 9 + lisp/x/debugger/server/rpc.go | 3 +- lisp/x/debugger/server/server_debugger.go | 3 +- 5 files changed, 314 insertions(+), 65 deletions(-) diff --git a/lisp/package.go b/lisp/package.go index 087f06d..b7dc78d 100644 --- a/lisp/package.go +++ b/lisp/package.go @@ -145,4 +145,4 @@ func (pkg *Package) put(k, v *LVal) { pkg.FunNames[v.FID()] = k.Str } pkg.Symbols[k.Str] = v -} +} \ No newline at end of file diff --git a/lisp/x/debugger/debugger.go b/lisp/x/debugger/debugger.go index 8572e44..33f8658 100644 --- a/lisp/x/debugger/debugger.go +++ b/lisp/x/debugger/debugger.go @@ -2,13 +2,17 @@ package debugger import ( "errors" + "fmt" "github.com/go-delve/delve/service/rpc2" "github.com/luthersystems/elps/lisp" "github.com/luthersystems/elps/lisp/x/debugger/server" + "github.com/luthersystems/elps/parser/token" "github.com/sirupsen/logrus" "math/rand" "reflect" + "runtime" "strconv" + "strings" "sync" "time" ) @@ -24,8 +28,15 @@ type Debugger struct { step chan bool run chan bool server *server.RPCServer - currentOp *lisp.LVal + currentOp *op logger *logrus.Logger + pwd string +} + +type op struct { + name string + source *token.Location + args map[string]*lisp.LVal } func NewDebugger(runtime *lisp.Runtime, address string) lisp.Profiler { @@ -46,6 +57,10 @@ func NewDebugger(runtime *lisp.Runtime, address string) lisp.Profiler { return db } +func (d *Debugger) SetLispPath(path string) { + d.pwd = strings.ReplaceAll(path, `\`, `/`) +} + func (d *Debugger) IsEnabled() bool { return d.enabled } @@ -72,7 +87,30 @@ func (d *Debugger) Complete() error { } func (d *Debugger) Start(function *lisp.LVal) { - d.logger.Infof("Instr %v", function.Type) + // do we need to do this on SExpr rather than function? Would that make finding the parameters easier? + fname := "" + if function.FunData() != nil { + fname = function.FunData().FID + } + source := function.Source + if source == nil || source.File == "" { + for f := len(d.runtime.Stack.Frames) - 1; f >= 0; f-- { + frame := d.runtime.Stack.Frames[f] + if frame.Source != nil || frame.Source.File == "" { + continue + } + source = frame.Source + break + } + } + if source == nil { + source = &token.Location{ + File: "Unknown", + Line: 0, + } + } + sourceStr := fmt.Sprintf("@%s:%d", source.File, source.Line) + d.logger.Infof("Instr %v %s params %v %s %d", function.Type, fname, function.Cells, sourceStr, len(d.runtime.Stack.Frames)) switch function.Type { case lisp.LInt: return @@ -87,18 +125,32 @@ func (d *Debugger) Start(function *lisp.LVal) { case lisp.LArray: return } + args := make(map[string]*lisp.LVal) + paramCounter := 0 + fname = d.runtime.Package.FunNames[fname] + for _, v := range function.Cells { + args[d.runtime.Package.Symbols[fname].Cells[paramCounter].Str] = v + } + d.currentOp = &op{ + name: fname , + source: source, + args: args, + } if !d.stopped { d.Lock() - for _, v := range d.breakpoints { - if v.File == function.Source.File && v.Line == function.Source.Line { - d.logger.Infof("BREAKPOINT") - d.stopped = true - d.Unlock() - d.Start(function) - return + if function.Source != nil { + for _, v := range d.breakpoints { + if (v.File == function.Source.File || fmt.Sprintf("%s/%s", d.pwd, function.Source.File) == v.File) && v.Line == function.Source.Line { + d.logger.Infof("BREAKPOINT") + d.stopped = true + d.Unlock() + d.Start(function) + return + } } } d.Unlock() + runtime.Gosched() return } select { @@ -108,12 +160,15 @@ func (d *Debugger) Start(function *lisp.LVal) { case <-d.step: d.logger.Infof("STEP") } - d.currentOp = function + // if we don't do this, ELPS' indomitable stack traces prevent the debugger receiving + // calls if we only have a few processor cores + runtime.Gosched() } func (d *Debugger) End(function *lisp.LVal) { - d.logger.Infof("End %v",function) - // no op for now + d.logger.Infof("End %v ", function.FunData().FID) + // no op for now except that we yield the CPU + runtime.Gosched() } func (d *Debugger) GetBreakpoint(id int) (*api.Breakpoint, error) { @@ -137,29 +192,42 @@ func (d *Debugger) GetBreakpointByName(name string) (*api.Breakpoint, error) { } func (d *Debugger) GetStacktrace(st *rpc2.StacktraceOut) { - d.Lock() - defer d.Unlock() + d.logger.Info("Returning STACK") st.Locations = make([]api.Stackframe, 0) for _, frame := range d.runtime.Stack.Frames { + var source *token.Location = frame.Source + if source == nil { + source = &token.Location{ + File: "Unknown file", + Path: "", + Pos: 0, + Line: 0, + Col: 0, + } + } st.Locations = append(st.Locations, api.Stackframe{ Location: api.Location{ PC: 0, - File: frame.Source.File, - Line: frame.Source.Line, + File: fmt.Sprintf("%s/%s", d.pwd, source.File), + Line: source.Line, Function: &api.Function{ - Name_: "", + Name_: "f", Value: 0, Type: 0, GoType: 0, Optimized: false, }, - PCs: nil, + PCs: []uint64{}, + }, + Locals: []api.Variable{ + + }, + Arguments: []api.Variable{ + }, - Locals: nil, - Arguments: nil, FrameOffset: 0, FramePointerOffset: 0, - Defers: nil, + Defers: []api.Defer{}, Bottom: false, Err: "", }) @@ -167,6 +235,7 @@ func (d *Debugger) GetStacktrace(st *rpc2.StacktraceOut) { } func (d *Debugger) GetAllBreakpoints() map[int]*api.Breakpoint { + d.logger.Info("Returning BREAKPOINTS") d.Lock() defer d.Unlock() return d.breakpoints @@ -208,17 +277,40 @@ func (d *Debugger) AmendBreakpoint(bp *api.Breakpoint) error { } func (d *Debugger) GetThread() *api.Thread { - return &api.Thread{ - ID: 1, - PC: 0, - File: "", - Line: 0, - Function: nil, - GoroutineID: 0, - Breakpoint: nil, - BreakpointInfo: nil, - ReturnValues: nil, + d.logger.Info("Returning THREADS") + var loc *api.Thread + if d.currentOp != nil { + var source *token.Location = d.currentOp.source + if source == nil { + source = &token.Location{ + File: "Unknown file", + Path: "", + Pos: 0, + Line: 0, + Col: 0, + } + } + loc = &api.Thread{ + PC: uint64(source.Pos), + File: fmt.Sprintf("%s/%s", d.pwd, source.File), + Line: source.Line, + Function: &api.Function{ + Name_: d.currentOp.name, + Value: 0, + Type: 0, + GoType: 0, + Optimized: false, + }, + ID: 1, + GoroutineID: 1, + } + } else { + loc = &api.Thread{ + ID: 1, + GoroutineID: 1, + } } + return loc } func mapLispType(in lisp.LType) string { @@ -288,13 +380,101 @@ func mapKind(in *lisp.LVal) reflect.Kind { } } +func mapValue(in *lisp.LVal) string { + switch in.Type { + case lisp.LFloat: + return strconv.FormatFloat(in.Float, 'f', 8, 64) + case lisp.LInt: + return strconv.FormatInt(int64(in.Int), 10) + case lisp.LString: + return in.Str + case lisp.LQSymbol: + return in.Str + case lisp.LSExpr: + return in.Str + case lisp.LSortMap: + return "map" + case lisp.LNative: + return fmt.Sprintf("%v", in.Native) + case lisp.LFun: + return in.FunData().FID + case lisp.LQuote: + return in.Str + case lisp.LArray: + return "array" + case lisp.LError: + return fmt.Sprintf("%v", lisp.GoError(in)) + case lisp.LBytes: + return "array" + case lisp.LInvalid: + return "INVALID" + default: + return "UNKNOWN" + } +} + +func extractChildren(in *lisp.LVal) []api.Variable { + children := make([]api.Variable, 0) + switch in.Type { + case lisp.LSortMap: + for _, v := range in.Cells[:1] { + children = append(children, api.Variable{ + Type: mapLispType(v.Type), + RealType: mapLispType(v.Type), + Value: mapValue(v), + Kind: mapKind(v), + }) + } + case lisp.LArray: + for _, v := range in.Cells[:1] { + children = append(children, api.Variable{ + Type: mapLispType(v.Type), + RealType: mapLispType(v.Type), + Value: mapValue(v), + Kind: mapKind(v), + }) + } + case lisp.LBytes: + for _, v := range in.Bytes() { + children = append(children, api.Variable{ + Type: "byte", + RealType: "byte", + Value: string(v), + Kind: reflect.Uint8, + }) + } + } + return children +} + func (d *Debugger) GetVariables() []api.Variable { + d.logger.Info("Returning VARS") d.Lock() defer d.Unlock() - out := make([]api.Variable, len(d.runtime.Package.Symbols)) + out := make([]api.Variable, 0) count := 0 - for k, v := range d.runtime.Package.Symbols { - out[count] = api.Variable{ + // deliberate copy - prevents us having to stop + symbols := d.runtime.Package.Symbols + for k, v := range symbols { + if v.Type == lisp.LFun && v.FunData().Builtin != nil { + continue + } + var source *token.Location = v.Source + if source == nil { + source = &token.Location{ + File: "Unknown file", + Path: "", + Pos: 0, + Line: 0, + Col: 0, + } + } + children := make([]api.Variable,0) + strVal := mapValue(v) + if strVal == "map" || strVal == "array" { + children = extractChildren(v) + } + out = append(out, api.Variable{ Name: k, Addr: uintptr(count), OnlyAddr: false, @@ -302,35 +482,47 @@ func (d *Debugger) GetVariables() []api.Variable { RealType: mapLispType(v.Type), Flags: 0, Kind: mapKind(v), - Value: v.Str, - Len: 0, - Cap: 0, - Children: []api.Variable{}, + Value: strVal, + Len: int64(len(children)), + Cap: int64(len(children)), + Children: children, Base: 0, Unreadable: "", LocationExpr: "", - DeclLine: int64(v.Source.Line), - } + DeclLine: int64(source.Line), + }) count++ } return out } func (d *Debugger) GetFunctionArgs() []api.Variable { + d.logger.Info("Returning ARGS") return []api.Variable{} } func (d *Debugger) GetGoRoutine() *api.Goroutine { + d.logger.Info("Returning GOROUTINE") d.Lock() defer d.Unlock() var loc *api.Location if d.currentOp != nil { + var source *token.Location = d.currentOp.source + if source == nil { + source = &token.Location{ + File: "Unknown file", + Path: "", + Pos: 0, + Line: 0, + Col: 0, + } + } loc = &api.Location{ - PC: uint64(d.currentOp.Source.Pos), - File: d.currentOp.Source.File, - Line: d.currentOp.Source.Line, + PC: uint64(source.Pos), + File: fmt.Sprintf("%s/%s", d.pwd, source.File), + Line: source.Line, Function: &api.Function{ - Name_: "", + Name_: d.currentOp.name, Value: 0, Type: 0, GoType: 0, @@ -344,17 +536,18 @@ func (d *Debugger) GetGoRoutine() *api.Goroutine { } } return &api.Goroutine{ - ID: 0, + ID: 1, CurrentLoc: *loc, UserCurrentLoc: *loc, GoStatementLoc: *loc, StartLoc: api.Location{}, - ThreadID: 0, + ThreadID: 1, Unreadable: "", } } func (d *Debugger) SetVariableInScope(scope api.EvalScope, symbol string, value string) error { + d.logger.Infof("SETTING variable %s to %s", symbol, value) d.Lock() defer d.Unlock() d.lastModified = time.Now() @@ -384,17 +577,51 @@ func (d *Debugger) SetVariableInScope(scope api.EvalScope, symbol string, value } func (d *Debugger) Sources(filter string) ([]string, error) { - return []string{}, nil + d.logger.Info("Returning SOURCES") + intermediate := make(map[string]bool) + for _, currPkg := range d.runtime.Registry.Packages { + d.appendSourcesToMap(intermediate, currPkg, filter) + } + out := make([]string, 0) + for k, _ := range intermediate { + out = append(out, k) + } + return out, nil +} + +func (d *Debugger) getSourcesForPackage(currPkg *lisp.Package) []string { + intermediate := make(map[string]bool) + d.appendSourcesToMap(intermediate, currPkg, "") + out := make([]string, 0) + for k, _ := range intermediate { + out = append(out, k) + } + return out +} + +func (d *Debugger) appendSourcesToMap(intermediate map[string]bool, currPkg *lisp.Package, filter string) { + for _, sym := range currPkg.Symbols { + if sym.Source == nil { + continue + } + if filter != "" && !strings.HasPrefix(fmt.Sprintf("%s/%s", d.pwd, sym.Source.File), filter) { + continue + } + intermediate[fmt.Sprintf("%s/%s", d.pwd, sym.Source.File)] = true + } } func (d *Debugger) Functions(filter string) ([]string, error) { + d.logger.Info("Returning FUNCTIONS") d.Lock() defer d.Unlock() - out := make([]string, len(d.runtime.Package.FunNames)) - count := 0 + out := make([]string, 0) + seen := make(map[string]bool) for _, v := range d.runtime.Package.FunNames { - out[count] = v - count++ + if seen[v] { + continue + } + out = append(out, v) } return out, nil } @@ -404,6 +631,7 @@ func (d *Debugger) FindLocation(scope api.EvalScope, loc string, lines bool) ([] } func (d *Debugger) ListPackagesBuildInfo(files bool) []api.PackageBuildInfo { + d.logger.Info("Returning BUILD INFO") d.Lock() defer d.Unlock() out := make([]api.PackageBuildInfo, 0) @@ -411,19 +639,18 @@ func (d *Debugger) ListPackagesBuildInfo(files bool) []api.PackageBuildInfo { out = append(out, api.PackageBuildInfo{ ImportPath: k, DirectoryPath: v.Name, - Files: nil, + Files: d.getSourcesForPackage(v), }) } return out } func (d *Debugger) State(blocking bool) (*api.DebuggerState, error) { - d.Lock() - defer d.Unlock() + d.logger.Info("Returning STATE") state := &api.DebuggerState{ Running: !d.stopped, CurrentThread: d.GetThread(), - SelectedGoroutine: nil, + SelectedGoroutine: d.GetGoRoutine(), Threads: []*api.Thread{d.GetThread()}, NextInProgress: false, Exited: !d.enabled, @@ -437,19 +664,26 @@ func (d *Debugger) State(blocking bool) (*api.DebuggerState, error) { func (d *Debugger) Command(a *api.DebuggerCommand) (*api.DebuggerState, error) { d.logger.Infof("Command: %s", a.Name) d.lastModified = time.Now() + started := false switch a.Name { case api.Halt: d.stopped = true case api.Continue: - go func(d *Debugger) { - d.run <- true - }(d) + started = true + if d.stopped { + go func(d *Debugger) { + d.run <- true + }(d) + } case api.Next: return nil, errors.New("Not implemented") case api.Step: - go func(d *Debugger) { - d.step <- true - }(d) + started = true + if d.stopped { + go func(d *Debugger) { + d.step <- true + }(d) + } case api.Call: return nil, errors.New("Not implemented") case api.ReverseStepInstruction: @@ -461,7 +695,11 @@ func (d *Debugger) Command(a *api.DebuggerCommand) (*api.DebuggerState, error) { case api.SwitchThread: return nil, errors.New("Not implemented") } - return d.State(false) + state, err := d.State(false) + if err != nil && started == true { + state.Running = true + } + return state, err } func (d *Debugger) LastModified() time.Time { diff --git a/lisp/x/debugger/debugger_test.go b/lisp/x/debugger/debugger_test.go index b99e7d0..7893f30 100644 --- a/lisp/x/debugger/debugger_test.go +++ b/lisp/x/debugger/debugger_test.go @@ -1,6 +1,7 @@ package debugger import ( + "github.com/go-delve/delve/service/api" "github.com/luthersystems/elps/lisp" "github.com/luthersystems/elps/parser" "github.com/stretchr/testify/assert" @@ -16,6 +17,14 @@ func TestNewDebugger(t *testing.T) { } // Create a profiler profiler := NewDebugger(env.Runtime, ":8883") + profiler.(*Debugger).CreateBreakpoint(&api.Breakpoint{ + ID: 1, + Name: "REUBEN TEST", + Addr: 0, + Addrs: []uint64{}, + File: "test.lisp", + Line: 3, + }) var testsrc *lisp.LVal // Some spurious functions to check we get a profile out testsrc = env.LoadString("test.lisp", ` diff --git a/lisp/x/debugger/server/rpc.go b/lisp/x/debugger/server/rpc.go index b2a1d4c..b2d1ad1 100644 --- a/lisp/x/debugger/server/rpc.go +++ b/lisp/x/debugger/server/rpc.go @@ -28,7 +28,8 @@ func (s *RPCServer) Run() error { // We were supposed to exit, do nothing and return return default: - panic(err) + logrus.Errorf("%s", err.Error()) + return } } diff --git a/lisp/x/debugger/server/server_debugger.go b/lisp/x/debugger/server/server_debugger.go index 75748b5..64f384a 100644 --- a/lisp/x/debugger/server/server_debugger.go +++ b/lisp/x/debugger/server/server_debugger.go @@ -27,4 +27,5 @@ type ServerDebugger interface { LastModified() time.Time Complete() error GetGoRoutine() *api.Goroutine -} \ No newline at end of file +} + From 3169e9709f886ae5afaf565599ab78ea6ac974b0 Mon Sep 17 00:00:00 2001 From: Reuben Thompson Date: Sat, 6 Jun 2020 16:42:42 +0100 Subject: [PATCH 3/9] Add DAP support --- .idea/.gitignore | 8 + .idea/elps.iml | 13 + .idea/encodings.xml | 4 + .idea/misc.xml | 6 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + cmd/run.go | 37 +- go.mod | 1 + go.sum | 2 + lisp/x/debugger/dapserver/dapserver.go | 334 ++++++++++++++ lisp/x/debugger/dapserver/debuggerwrapper.go | 411 ++++++++++++++++++ lisp/x/debugger/debugger.go | 74 +++- lisp/x/debugger/debugger_test.go | 2 +- .../x/debugger/{server => delveserver}/rpc.go | 4 +- .../{server => delveserver}/server.go | 7 +- .../server_debugger.go | 6 +- lisp/x/debugger/events/events.go | 11 + 17 files changed, 912 insertions(+), 22 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/elps.iml create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 lisp/x/debugger/dapserver/dapserver.go create mode 100644 lisp/x/debugger/dapserver/debuggerwrapper.go rename lisp/x/debugger/{server => delveserver}/rpc.go (99%) rename lisp/x/debugger/{server => delveserver}/server.go (98%) rename lisp/x/debugger/{server => delveserver}/server_debugger.go (94%) create mode 100644 lisp/x/debugger/events/events.go diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..4aa91ea --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/elps.iml b/.idea/elps.iml new file mode 100644 index 0000000..80cb5f0 --- /dev/null +++ b/.idea/elps.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..15a15b2 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..ef004d1 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..8c7a7a9 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..9661ac7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/cmd/run.go b/cmd/run.go index 6da145f..2a4cf17 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -4,6 +4,7 @@ package cmd import ( "fmt" + "github.com/luthersystems/elps/lisp/x/debugger" "io/ioutil" "os" @@ -14,8 +15,11 @@ import ( ) var ( - runExpression bool - runPrint bool + runExpression bool + runPrint bool + runDebugger bool + runDebuggerPort int + runDebuggerType string ) // runCmd represents the run command @@ -24,6 +28,11 @@ var runCmd = &cobra.Command{ Short: "Run lisp code", Long: `Run lisp code provided supplied via the command line or a file.`, Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + fmt.Fprintln(os.Stderr, "No file specified") + os.Exit(1) + } + env := lisp.NewEnv(nil) reader := parser.NewReader() env.Runtime.Reader = reader @@ -43,6 +52,21 @@ var runCmd = &cobra.Command{ fmt.Fprintln(os.Stderr, rc) os.Exit(1) } + + var profiler lisp.Profiler + if runDebugger { + var mode debugger.DebugMode + switch debugger.DebugMode(runDebuggerType) { + case debugger.DebugModeDelve: + mode = debugger.DebugModeDelve + case debugger.DebugModeDAP: + mode = debugger.DebugModeDAP + default: + fmt.Fprintln(os.Stderr, "Invalid debugger. Specify delve or dap") + os.Exit(1) + } + profiler = debugger.NewDebugger(env.Runtime, fmt.Sprintf(":%d", runDebuggerPort), mode) + } for i := range args { res := env.LoadFile(args[i]) if res.Type == lisp.LError { @@ -50,6 +74,9 @@ var runCmd = &cobra.Command{ os.Exit(1) } } + if runDebugger { + profiler.Complete() + } }, } @@ -79,4 +106,10 @@ func init() { "Interpret arguments as lisp expressions") runCmd.Flags().BoolVarP(&runPrint, "print", "p", false, "Print expression values to stdout") + runCmd.Flags().BoolVarP(&runDebugger, "debugger", "d", false, + "Enable the debugger") + runCmd.Flags().IntVarP(&runDebuggerPort, "debugger.listen", "l", 8883, + "Port for the debugger") + runCmd.Flags().StringVarP(&runDebuggerType, "debugger.type", "t", "dap", + "Type of debugger (delve or dap)") } diff --git a/go.mod b/go.mod index 041172d..ab78f3e 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-delve/delve v1.4.0 github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 + github.com/google/go-dap v0.2.0 github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce // indirect github.com/magiconair/properties v1.8.0 // indirect github.com/mitchellh/go-homedir v0.0.0-20180523094522-3864e76763d9 diff --git a/go.sum b/go.sum index 754db29..37f16bc 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,8 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-dap v0.2.0 h1:whjIGQRumwbR40qRU7CEKuFLmePUUc2s4Nt9DoXXxWk= +github.com/google/go-dap v0.2.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ= github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno= github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= diff --git a/lisp/x/debugger/dapserver/dapserver.go b/lisp/x/debugger/dapserver/dapserver.go new file mode 100644 index 0000000..6f0f808 --- /dev/null +++ b/lisp/x/debugger/dapserver/dapserver.go @@ -0,0 +1,334 @@ +package dapserver + +// implements VSCode's DAP protocol (https://microsoft.github.io/debug-adapter-protocol/specification) +// to keep Amir happy. +// +// Extension point docs at https://code.visualstudio.com/api/extension-guides/debugger-extension +// More useful info on how to integrate in vscode at https://marketplace.visualstudio.com/items?itemName=andreweinand.mock-debug +// and https://github.com/Microsoft/vscode-debugadapter-node + +import ( + "bufio" + "github.com/google/go-dap" + "github.com/luthersystems/elps/lisp/x/debugger/delveserver" + "github.com/luthersystems/elps/lisp/x/debugger/events" + log "github.com/sirupsen/logrus" + "io" + "net" + "sync" +) + +type Server struct { + endChannel chan bool + Address string + listener net.Listener + connQueue chan net.Conn + connectionPool []*connection + sequence uint32 + sequenceLock sync.Mutex + wg *sync.WaitGroup + debugger *debuggerwrapper +} + +type connection struct { + s *Server + rw *bufio.ReadWriter + queue chan dap.Message + kill chan bool + Connected bool + Event chan events.EventType +} + +func NewServer(debugger delveserver.ServerDebugger, address string, handlers int) (*Server, error) { + pool := make([]*connection, handlers) + for x := 0; x < handlers; x++ { + pool[x] = &connection{} + } + server := &Server{ + Address: address, + connectionPool: pool, + connQueue: make(chan net.Conn, 10), + sequence: 0, + wg: new(sync.WaitGroup), + debugger: &debuggerwrapper{debugger: debugger}, + } + for x := 0; x < handlers; x++ { + pool[x].s = server + } + return server, nil +} + +func (s *Server) Run() error { + listener, err := net.Listen("tcp", s.Address) + if err != nil { + return err + } + for _, h := range s.connectionPool { + go h.start() + s.wg.Add(1) + } + s.listener = listener + go s.listen() + return nil +} + +func (s *Server) Stop() error { + s.endChannel <- true + s.wg.Wait() + return nil +} + +func (s *Server) Event(x events.EventType) { + for _, v := range s.connectionPool { + if v.Connected { + go func(ch chan events.EventType) { ch <- x }(v.Event) + } + } +} + +func (s *Server) listen() { + defer s.listener.Close() + for { + conn, err := s.listener.Accept() + if err != nil { + log.Println("Connection failed:", err) + continue + } + log.Println("Accepted connection from", conn.RemoteAddr()) + s.connQueue <- conn + } +} + +func (s *Server) getSequence() uint32 { + s.sequenceLock.Lock() + defer s.sequenceLock.Unlock() + return s.sequence +} + +func (s *Server) incSequence() uint32 { + s.sequenceLock.Lock() + defer s.sequenceLock.Unlock() + s.sequence += 1 + return s.sequence +} + +func (h *connection) killconnection() { + h.kill <- true +} + +func (h *connection) start() { + for { + select { + case conn := <-h.s.connQueue: + h.rw = bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)) + h.queue = make(chan dap.Message, 5) + h.Connected = true + h.Event = make(chan events.EventType) + go func() { + for { + select { + case <-h.kill: + h.kill <- true + return + case event := <-h.Event: + h.sendEventMessage(event) + } + } + }() + for { + select { + case <-h.kill: + h.kill <- true + return + default: + // no-op + } + err := h.handleRequest() + if err != nil { + if err == io.EOF { + log.Println("No more data to read:", err) + break + } + log.Fatal("Server error: ", err) + } + } + case <-h.s.endChannel: + h.s.endChannel <- true + h.Connected = false + h.s.wg.Done() + return + } + } +} + +func (h *connection) handleRequest() error { + request, err := dap.ReadProtocolMessage(h.rw.Reader) + if err != nil { + return err + } + log.Printf("Received request\n\t%#v\n", request) + h.s.wg.Add(1) + go func() { + h.dispatchRequest(request) + h.s.wg.Done() + }() + return nil +} + +func (h *connection) sendHandler() { + for message := range h.queue { + log.Infof("Message: %s", message) + err := dap.WriteProtocolMessage(h.rw.Writer, message) + if err != nil { + log.Errorf("Error sending message: %s", err.Error()) + } + _ = h.rw.Flush() + } +} + +func (h *connection) dispatchRequest(request dap.Message) { + switch request := request.(type) { + case *dap.InitializeRequest: + h.s.debugger.onInitializeRequest(request, h) + case *dap.LaunchRequest: + h.s.debugger.onLaunchRequest(request, h.queue) + case *dap.AttachRequest: + h.s.debugger.onAttachRequest(request, h.queue) + case *dap.DisconnectRequest: + h.s.debugger.onDisconnectRequest(request, h.queue) + h.killconnection() + case *dap.TerminateRequest: + h.s.debugger.onTerminateRequest(request, h.queue) + case *dap.RestartRequest: + h.s.debugger.onRestartRequest(request, h.queue) + case *dap.SetBreakpointsRequest: + h.s.debugger.onSetBreakpointsRequest(request, h.queue) + case *dap.SetFunctionBreakpointsRequest: + h.s.debugger.onSetFunctionBreakpointsRequest(request, h.queue) + case *dap.SetExceptionBreakpointsRequest: + h.s.debugger.onSetExceptionBreakpointsRequest(request, h.queue) + case *dap.ConfigurationDoneRequest: + h.s.debugger.onConfigurationDoneRequest(request, h.queue) + case *dap.ContinueRequest: + h.s.debugger.onContinueRequest(request, h.queue) + case *dap.NextRequest: + h.s.debugger.onNextRequest(request, h.queue) + case *dap.StepInRequest: + h.s.debugger.onStepInRequest(request, h.queue) + case *dap.StepOutRequest: + h.s.debugger.onStepOutRequest(request, h.queue) + case *dap.StepBackRequest: + h.s.debugger.onStepBackRequest(request, h.queue) + case *dap.ReverseContinueRequest: + h.s.debugger.onReverseContinueRequest(request, h.queue) + case *dap.RestartFrameRequest: + h.s.debugger.onRestartFrameRequest(request, h.queue) + case *dap.GotoRequest: + h.s.debugger.onGotoRequest(request, h.queue) + case *dap.PauseRequest: + h.s.debugger.onPauseRequest(request, h.queue) + case *dap.StackTraceRequest: + h.s.debugger.onStackTraceRequest(request, h.queue) + case *dap.ScopesRequest: + h.s.debugger.onScopesRequest(request, h.queue) + case *dap.VariablesRequest: + h.s.debugger.onVariablesRequest(request, h.queue) + case *dap.SetVariableRequest: + h.s.debugger.onSetVariableRequest(request, h.queue) + case *dap.SetExpressionRequest: + h.s.debugger.onSetExpressionRequest(request, h.queue) + case *dap.SourceRequest: + h.s.debugger.onSourceRequest(request, h.queue) + case *dap.ThreadsRequest: + h.s.debugger.onThreadsRequest(request, h.queue) + case *dap.TerminateThreadsRequest: + h.s.debugger.onTerminateThreadsRequest(request, h.queue) + case *dap.EvaluateRequest: + h.s.debugger.onEvaluateRequest(request, h.queue) + case *dap.StepInTargetsRequest: + h.s.debugger.onStepInTargetsRequest(request, h.queue) + case *dap.GotoTargetsRequest: + h.s.debugger.onGotoTargetsRequest(request, h.queue) + case *dap.CompletionsRequest: + h.s.debugger.onCompletionsRequest(request, h.queue) + case *dap.ExceptionInfoRequest: + h.s.debugger.onExceptionInfoRequest(request, h.queue) + case *dap.LoadedSourcesRequest: + h.s.debugger.onLoadedSourcesRequest(request, h.queue) + case *dap.DataBreakpointInfoRequest: + h.s.debugger.onDataBreakpointInfoRequest(request, h.queue) + case *dap.SetDataBreakpointsRequest: + h.s.debugger.onSetDataBreakpointsRequest(request, h.queue) + case *dap.ReadMemoryRequest: + h.s.debugger.onReadMemoryRequest(request, h.queue) + case *dap.DisassembleRequest: + h.s.debugger.onDisassembleRequest(request, h.queue) + case *dap.CancelRequest: + h.s.debugger.onCancelRequest(request, h.queue) + case *dap.BreakpointLocationsRequest: + h.s.debugger.onBreakpointLocationsRequest(request, h.queue) + default: + log.Fatalf("Unable to process %#v", request) + } +} + +func (h *connection) sendEventMessage(event events.EventType) { + log.Infof("Sending event %s", event) + switch event { + case events.EventTypeContinued: + h.queue <- &dap.ContinuedEvent{ + Event: dap.Event{Event: "continued"}, + Body: dap.ContinuedEventBody{ + AllThreadsContinued: true, + }, + } + case events.EventTypeExited: + h.queue <- &dap.ExitedEvent{ + Event: dap.Event{Event: "continued"}, + Body: dap.ExitedEventBody{ + ExitCode: 0, //TODO this is so not true + }, + } + case events.EventTypeStarted: + // this is a no-op for us here + case events.EventTypeStoppedPaused: + h.queue <- &dap.StoppedEvent{ + Event: dap.Event{Event: "stopped"}, + Body: dap.StoppedEventBody{ + AllThreadsStopped: true, + Reason: "pause", // we need to propagate this by using more reasons + }, + } + case events.EventTypeStoppedBreakpoint: + h.queue <- &dap.StoppedEvent{ + Event: dap.Event{Event: "stopped"}, + Body: dap.StoppedEventBody{ + AllThreadsStopped: true, + Reason: "breakpoint", // we need to propagate this by using more reasons + }, + } + case events.EventTypeStoppedEntry: + h.queue <- &dap.StoppedEvent{ + Event: dap.Event{Event: "stopped"}, + Body: dap.StoppedEventBody{ + AllThreadsStopped: true, + Reason: "entry", // we need to propagate this by using more reasons + }, + } + case events.EventTypeStoppedStep: + h.queue <- &dap.StoppedEvent{ + Event: dap.Event{Event: "stopped"}, + Body: dap.StoppedEventBody{ + AllThreadsStopped: true, + Reason: "step", // we need to propagate this by using more reasons + }, + } + case events.EventTypeTerminated: + h.queue <- &dap.TerminatedEvent{ + Event: dap.Event{Event: "terminated"}, + Body: dap.TerminatedEventBody{ + Restart: false, + }, + } + } +} diff --git a/lisp/x/debugger/dapserver/debuggerwrapper.go b/lisp/x/debugger/dapserver/debuggerwrapper.go new file mode 100644 index 0000000..85048c8 --- /dev/null +++ b/lisp/x/debugger/dapserver/debuggerwrapper.go @@ -0,0 +1,411 @@ +package dapserver +import ( + "github.com/go-delve/delve/service/api" + "github.com/go-delve/delve/service/rpc2" + "github.com/google/go-dap" + "github.com/luthersystems/elps/lisp/x/debugger/delveserver" + "github.com/luthersystems/elps/lisp/x/debugger/events" + "math/rand" +) + + +type debuggerwrapper struct { + debugger delveserver.ServerDebugger +} + +func (d *debuggerwrapper) onInitializeRequest(request *dap.InitializeRequest, handler *connection) { + handler.queue <- &dap.InitializeResponse{ + Response: dap.Response{ + Success: true, + RequestSeq: request.Seq, + }, + Body: dap.Capabilities{ + SupportsConfigurationDoneRequest: true, + SupportsFunctionBreakpoints: true, + SupportsConditionalBreakpoints: false, + SupportsHitConditionalBreakpoints: false, + SupportsEvaluateForHovers: false, + SupportsStepBack: false, + SupportsSetVariable: true, + SupportsRestartFrame: false, + SupportsGotoTargetsRequest: false, + SupportsStepInTargetsRequest: false, + SupportsCompletionsRequest: false, + SupportsModulesRequest: false, + SupportsRestartRequest: false, + SupportsExceptionOptions: false, + SupportsValueFormattingOptions: false, + SupportsExceptionInfoRequest: false, + SupportTerminateDebuggee: true, + SupportsDelayedStackTraceLoading: false, + SupportsLoadedSourcesRequest: true, + SupportsLogPoints: false, + SupportsTerminateThreadsRequest: false, + SupportsSetExpression: true, + SupportsTerminateRequest: true, + SupportsDataBreakpoints: false, + SupportsReadMemoryRequest: false, + SupportsDisassembleRequest: false, + SupportsCancelRequest: false, + SupportsBreakpointLocationsRequest: true, + }, + } + if d.debugger.IsStopped() { + handler.Event <- events.EventTypeStoppedEntry + } +} + +func (d *debuggerwrapper) onLaunchRequest(request *dap.LaunchRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onAttachRequest(request *dap.AttachRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onDisconnectRequest(request *dap.DisconnectRequest , returnchan chan dap.Message) { + if request.Arguments.TerminateDebuggee { + d.debugger.Complete() + } + returnchan <- &dap.DisconnectResponse{} +} + +func (d *debuggerwrapper) onTerminateRequest(request *dap.TerminateRequest , returnchan chan dap.Message) { + d.debugger.Complete() + returnchan <- &dap.TerminateResponse{} +} + +func (d *debuggerwrapper) onRestartRequest(request *dap.RestartRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onSetBreakpointsRequest(request *dap.SetBreakpointsRequest , returnchan chan dap.Message) { + if request.Arguments.SourceModified { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Cannot modify source whilst running", + }, + } + return + } + out:= make([]dap.Breakpoint, 0) + for _, v := range request.Arguments.Breakpoints { + id := rand.Int() + d.debugger.CreateBreakpoint(&api.Breakpoint{ + ID: id, + Name: request.Arguments.Source.Name, + Addr: 0, + Addrs: nil, + File: request.Arguments.Source.Path, + Line: v.Line, + FunctionName: "", + Cond: v.Condition, + Tracepoint: false, + TraceReturn: false, + Goroutine: false, + Stacktrace: 0, + TotalHitCount: 0, + }) + out = append(out, dap.Breakpoint{ + Id: id, + Line: v.Line, + Column: 0, + Source: request.Arguments.Source, + Verified: false, + }) + } + returnchan <- &dap.SetBreakpointsResponse{ + Response: dap.Response{ + Success: true, + RequestSeq: request.GetSeq(), + }, + Body: dap.SetBreakpointsResponseBody{Breakpoints: out}, + } +} + +func (d *debuggerwrapper) onSetFunctionBreakpointsRequest(request *dap.SetFunctionBreakpointsRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onSetExceptionBreakpointsRequest(request *dap.SetExceptionBreakpointsRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onConfigurationDoneRequest(request *dap.ConfigurationDoneRequest , returnchan chan dap.Message) { + returnchan <- &dap.ConfigurationDoneResponse{ + Response: dap.Response{RequestSeq: request.GetSeq(), Success: true}, + } +} + +func (d *debuggerwrapper) onContinueRequest(request *dap.ContinueRequest , returnchan chan dap.Message) { + d.debugger.Continue() + returnchan <- &dap.ContinueResponse{ + Response: dap.Response{Success: true, RequestSeq: request.GetSeq()}, + Body: dap.ContinueResponseBody{ + AllThreadsContinued: true, + }, + } +} + +func (d *debuggerwrapper) onNextRequest(request *dap.NextRequest , returnchan chan dap.Message) { + d.debugger.Step() + returnchan <- &dap.NextResponse{ + Response: dap.Response{Success: true, RequestSeq: request.GetSeq()}, + } +} + +func (d *debuggerwrapper) onStepInRequest(request *dap.StepInRequest , returnchan chan dap.Message) { + d.debugger.Step() + returnchan <- &dap.StepInResponse{ + Response: dap.Response{Success: true, RequestSeq: request.GetSeq()}, + } +} + +func (d *debuggerwrapper) onStepOutRequest(request *dap.StepOutRequest , returnchan chan dap.Message) { + d.debugger.Step() + returnchan <- &dap.StepOutResponse{ + Response: dap.Response{Success: true, RequestSeq: request.GetSeq()}, + } +} + +func (d *debuggerwrapper) onStepBackRequest(request *dap.StepBackRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onReverseContinueRequest(request *dap.ReverseContinueRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onRestartFrameRequest(request *dap.RestartFrameRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onGotoRequest(request *dap.GotoRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onPauseRequest(request *dap.PauseRequest , returnchan chan dap.Message) { + d.debugger.Halt() + returnchan <- &dap.PauseResponse{ + Response: dap.Response{Success: true, RequestSeq: request.GetSeq()}, + } +} + +func (d *debuggerwrapper) onStackTraceRequest(request *dap.StackTraceRequest , returnchan chan dap.Message) { + st := &rpc2.StacktraceOut{} + d.debugger.GetStacktrace(st) + out := make([]dap.StackFrame, 0) + for x:=0; x < len(st.Locations); x++ { + out = append(out, dap.StackFrame{ + Id: x, + Name: st.Locations[x].Function.Name(), + Source: dap.Source{ + Name: st.Locations[x].File, + }, + Line: st.Locations[x].Line, + Column: 0, + }) + } + returnchan <- &dap.StackTraceResponse{ + Response: dap.Response{Success: true, RequestSeq: request.Seq}, + Body: dap.StackTraceResponseBody{ + StackFrames: out, + TotalFrames: len(out), + }, + } +} + +func (d *debuggerwrapper) onScopesRequest(request *dap.ScopesRequest , returnchan chan dap.Message) {} + +func (d *debuggerwrapper) onVariablesRequest(request *dap.VariablesRequest , returnchan chan dap.Message) {} + +func (d *debuggerwrapper) onSetVariableRequest(request *dap.SetVariableRequest , returnchan chan dap.Message) {} + +func (d *debuggerwrapper) onSetExpressionRequest(request *dap.SetExpressionRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onSourceRequest(request *dap.SourceRequest , returnchan chan dap.Message) {} + +func (d *debuggerwrapper) onThreadsRequest(request *dap.ThreadsRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onTerminateThreadsRequest(request *dap.TerminateThreadsRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onEvaluateRequest(request *dap.EvaluateRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onStepInTargetsRequest(request *dap.StepInTargetsRequest , returnchan chan dap.Message) {} + +func (d *debuggerwrapper) onGotoTargetsRequest(request *dap.GotoTargetsRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onCompletionsRequest(request *dap.CompletionsRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onExceptionInfoRequest(request *dap.ExceptionInfoRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onLoadedSourcesRequest(request *dap.LoadedSourcesRequest , returnchan chan dap.Message) { + +} + +func (d *debuggerwrapper) onDataBreakpointInfoRequest(request *dap.DataBreakpointInfoRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onSetDataBreakpointsRequest(request *dap.SetDataBreakpointsRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onReadMemoryRequest(request *dap.ReadMemoryRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onDisassembleRequest(request *dap.DisassembleRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onCancelRequest(request *dap.CancelRequest , returnchan chan dap.Message) { + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } +} + +func (d *debuggerwrapper) onBreakpointLocationsRequest(request *dap.BreakpointLocationsRequest , returnchan chan dap.Message) { + +} + diff --git a/lisp/x/debugger/debugger.go b/lisp/x/debugger/debugger.go index 33f8658..db555e2 100644 --- a/lisp/x/debugger/debugger.go +++ b/lisp/x/debugger/debugger.go @@ -5,7 +5,9 @@ import ( "fmt" "github.com/go-delve/delve/service/rpc2" "github.com/luthersystems/elps/lisp" - "github.com/luthersystems/elps/lisp/x/debugger/server" + "github.com/luthersystems/elps/lisp/x/debugger/dapserver" + "github.com/luthersystems/elps/lisp/x/debugger/delveserver" + "github.com/luthersystems/elps/lisp/x/debugger/events" "github.com/luthersystems/elps/parser/token" "github.com/sirupsen/logrus" "math/rand" @@ -27,7 +29,7 @@ type Debugger struct { stopped bool step chan bool run chan bool - server *server.RPCServer + server DebugServer currentOp *op logger *logrus.Logger pwd string @@ -39,7 +41,18 @@ type op struct { args map[string]*lisp.LVal } -func NewDebugger(runtime *lisp.Runtime, address string) lisp.Profiler { +type DebugServer interface { + Run() error + Stop() error + Event(events.EventType) +} + +type DebugMode string +const DebugModeDelve = DebugMode("delve") +const DebugModeDAP = DebugMode("dap") + + +func NewDebugger(runtime *lisp.Runtime, address string, mode DebugMode) lisp.Profiler { db := &Debugger{ runtime: runtime, enabled: true, @@ -50,13 +63,26 @@ func NewDebugger(runtime *lisp.Runtime, address string) lisp.Profiler { breakpoints: make(map[int]*api.Breakpoint), logger: logrus.New(), } - srv := server.NewServer(db, address) + var srv DebugServer + if mode == DebugModeDelve { + srv = delveserver.NewServer(db, address) + } else if mode == DebugModeDAP { + var err error + srv, err = dapserver.NewServer(db, address, 2) + if err != nil { + panic(err.Error()) + } + } srv.Run() db.server = srv runtime.Profiler = db return db } +func (d *Debugger) IsStopped() bool { + return d.stopped +} + func (d *Debugger) SetLispPath(path string) { d.pwd = strings.ReplaceAll(path, `\`, `/`) } @@ -83,6 +109,7 @@ func (d *Debugger) Complete() error { d.enabled = false d.server.Stop() d.run <- true + d.server.Event(events.EventTypeTerminated) return nil } @@ -145,6 +172,7 @@ func (d *Debugger) Start(function *lisp.LVal) { d.stopped = true d.Unlock() d.Start(function) + d.server.Event(events.EventTypeStoppedBreakpoint) return } } @@ -155,9 +183,12 @@ func (d *Debugger) Start(function *lisp.LVal) { } select { case <-d.run: + d.server.Event(events.EventTypeContinued) d.logger.Infof("RUN") d.stopped = false case <-d.step: + d.server.Event(events.EventTypeContinued) + d.server.Event(events.EventTypeStoppedStep) d.logger.Infof("STEP") } // if we don't do this, ELPS' indomitable stack traces prevent the debugger receiving @@ -661,29 +692,42 @@ func (d *Debugger) State(blocking bool) (*api.DebuggerState, error) { return state, nil } +func (d *Debugger) Continue() { + if d.stopped { + go func(d *Debugger) { + d.run <- true + }(d) + } +} + +func (d *Debugger) Step() { + if d.stopped { + go func(d *Debugger) { + d.step <- true + }(d) + } +} + +func (d *Debugger) Halt() { + d.stopped = true +} + func (d *Debugger) Command(a *api.DebuggerCommand) (*api.DebuggerState, error) { + // TODO this is Delve specific d.logger.Infof("Command: %s", a.Name) d.lastModified = time.Now() started := false switch a.Name { case api.Halt: - d.stopped = true + d.Halt() case api.Continue: started = true - if d.stopped { - go func(d *Debugger) { - d.run <- true - }(d) - } + d.Continue() case api.Next: return nil, errors.New("Not implemented") case api.Step: started = true - if d.stopped { - go func(d *Debugger) { - d.step <- true - }(d) - } + d.Step() case api.Call: return nil, errors.New("Not implemented") case api.ReverseStepInstruction: diff --git a/lisp/x/debugger/debugger_test.go b/lisp/x/debugger/debugger_test.go index 7893f30..f2b57a8 100644 --- a/lisp/x/debugger/debugger_test.go +++ b/lisp/x/debugger/debugger_test.go @@ -16,7 +16,7 @@ func TestNewDebugger(t *testing.T) { t.Fatal(lisp.GoError(lerr)) } // Create a profiler - profiler := NewDebugger(env.Runtime, ":8883") + profiler := NewDebugger(env.Runtime, ":8883", DebugModeDelve) profiler.(*Debugger).CreateBreakpoint(&api.Breakpoint{ ID: 1, Name: "REUBEN TEST", diff --git a/lisp/x/debugger/server/rpc.go b/lisp/x/debugger/delveserver/rpc.go similarity index 99% rename from lisp/x/debugger/server/rpc.go rename to lisp/x/debugger/delveserver/rpc.go index b2d1ad1..4e92577 100644 --- a/lisp/x/debugger/server/rpc.go +++ b/lisp/x/debugger/delveserver/rpc.go @@ -1,4 +1,4 @@ -package server +package delveserver import ( "bytes" @@ -291,7 +291,7 @@ type RPCCallback struct { req rpc.Request } -// Stop stops the JSON-RPC server. +// Stop stops the JSON-RPC delveserver. func (s *RPCServer) Stop() error { if s.stopChan != nil { close(s.stopChan) diff --git a/lisp/x/debugger/server/server.go b/lisp/x/debugger/delveserver/server.go similarity index 98% rename from lisp/x/debugger/server/server.go rename to lisp/x/debugger/delveserver/server.go index 862afbe..da3e5f4 100644 --- a/lisp/x/debugger/server/server.go +++ b/lisp/x/debugger/delveserver/server.go @@ -1,4 +1,4 @@ -package server +package delveserver import ( "errors" @@ -7,6 +7,7 @@ import ( "github.com/go-delve/delve/service" "github.com/go-delve/delve/service/api" "github.com/go-delve/delve/service/rpc2" + "github.com/luthersystems/elps/lisp/x/debugger/events" "github.com/sirupsen/logrus" "net" ) @@ -48,6 +49,10 @@ func (s *RPCServer) LastModified(arg rpc2.LastModifiedIn, out *rpc2.LastModified return nil } +func (s *RPCServer) Event(x events.EventType) { + // no-op +} + // Detach detaches the debugger, optionally killing the process. func (s *RPCServer) Detach(arg rpc2.DetachIn, out *rpc2.DetachOut) error { err := s.debugger.Complete() diff --git a/lisp/x/debugger/server/server_debugger.go b/lisp/x/debugger/delveserver/server_debugger.go similarity index 94% rename from lisp/x/debugger/server/server_debugger.go rename to lisp/x/debugger/delveserver/server_debugger.go index 64f384a..531f655 100644 --- a/lisp/x/debugger/server/server_debugger.go +++ b/lisp/x/debugger/delveserver/server_debugger.go @@ -1,4 +1,4 @@ -package server +package delveserver import ( "github.com/go-delve/delve/service/api" @@ -27,5 +27,9 @@ type ServerDebugger interface { LastModified() time.Time Complete() error GetGoRoutine() *api.Goroutine + Step() + Continue() + Halt() + IsStopped() bool } diff --git a/lisp/x/debugger/events/events.go b/lisp/x/debugger/events/events.go new file mode 100644 index 0000000..ad24ad0 --- /dev/null +++ b/lisp/x/debugger/events/events.go @@ -0,0 +1,11 @@ +package events + +type EventType string +const EventTypeStarted = EventType("started") +const EventTypeContinued = EventType("continued") +const EventTypeExited = EventType("exited") +const EventTypeStoppedBreakpoint = EventType("stopped-breakpoint") +const EventTypeStoppedPaused = EventType("stopped-paused") +const EventTypeStoppedStep = EventType("stopped-step") +const EventTypeStoppedEntry = EventType("stopped-entry") +const EventTypeTerminated = EventType("terminated") From ee54263c6d415276a73ff7e9a3c8e560e6ad0006 Mon Sep 17 00:00:00 2001 From: "reuben.thompson@acresoftware.com" Date: Fri, 17 Jul 2020 17:35:45 +0100 Subject: [PATCH 4/9] More work on debugger --- _examples/wasm/server/main.go | 2 +- lisp/x/debugger/dapserver/dapserver.go | 291 +++++++++------- lisp/x/debugger/dapserver/debuggerwrapper.go | 317 ++++++++++++++---- lisp/x/debugger/debugger.go | 44 ++- .../x/debugger/delveserver/server_debugger.go | 2 + 5 files changed, 479 insertions(+), 177 deletions(-) diff --git a/_examples/wasm/server/main.go b/_examples/wasm/server/main.go index e66e561..a49d877 100644 --- a/_examples/wasm/server/main.go +++ b/_examples/wasm/server/main.go @@ -1,6 +1,6 @@ // Copyright © 2018 The ELPS authors -// A basic HTTP server. +// A basic HTTP delveserver. // By default, it serves the current working directory on port 8080. package main diff --git a/lisp/x/debugger/dapserver/dapserver.go b/lisp/x/debugger/dapserver/dapserver.go index 6f0f808..ccc7d1f 100644 --- a/lisp/x/debugger/dapserver/dapserver.go +++ b/lisp/x/debugger/dapserver/dapserver.go @@ -15,19 +15,21 @@ import ( log "github.com/sirupsen/logrus" "io" "net" + "runtime" "sync" + "time" ) type Server struct { - endChannel chan bool - Address string - listener net.Listener - connQueue chan net.Conn - connectionPool []*connection - sequence uint32 - sequenceLock sync.Mutex - wg *sync.WaitGroup - debugger *debuggerwrapper + endChannel chan bool + Address string + listener net.Listener + connQueue chan net.Conn + connection *connection + sequence int + sequenceLock sync.Mutex + wg *sync.WaitGroup + debugger *debuggerwrapper } type connection struct { @@ -40,38 +42,41 @@ type connection struct { } func NewServer(debugger delveserver.ServerDebugger, address string, handlers int) (*Server, error) { - pool := make([]*connection, handlers) - for x := 0; x < handlers; x++ { - pool[x] = &connection{} - } server := &Server{ - Address: address, - connectionPool: pool, - connQueue: make(chan net.Conn, 10), - sequence: 0, - wg: new(sync.WaitGroup), - debugger: &debuggerwrapper{debugger: debugger}, - } - for x := 0; x < handlers; x++ { - pool[x].s = server + Address: address, + connection: &connection{}, + connQueue: make(chan net.Conn, 10), + sequence: 0, + wg: new(sync.WaitGroup), + debugger: &debuggerwrapper{debugger: debugger}, } + server.connection.s = server return server, nil } func (s *Server) Run() error { + go startMonitor() + log.Infof("Listening on %s", s.Address) listener, err := net.Listen("tcp", s.Address) if err != nil { return err } - for _, h := range s.connectionPool { - go h.start() - s.wg.Add(1) - } + s.listener = listener + s.wg.Add(1) go s.listen() + return nil } +func startMonitor() { + for { + log.Debugf("Monitor heartbeat") + time.Sleep(1 * time.Second) + runtime.Gosched() + } +} + func (s *Server) Stop() error { s.endChannel <- true s.wg.Wait() @@ -79,10 +84,8 @@ func (s *Server) Stop() error { } func (s *Server) Event(x events.EventType) { - for _, v := range s.connectionPool { - if v.Connected { - go func(ch chan events.EventType) { ch <- x }(v.Event) - } + if s.connection.Connected { + go func(ch chan events.EventType) { ch <- x }(s.connection.Event) } } @@ -91,21 +94,22 @@ func (s *Server) listen() { for { conn, err := s.listener.Accept() if err != nil { - log.Println("Connection failed:", err) + log.Errorf("Connection failed:", err) continue } - log.Println("Accepted connection from", conn.RemoteAddr()) - s.connQueue <- conn + log.Errorf("Accepted connection from", conn.RemoteAddr()) + s.connection.start(conn) + return } } -func (s *Server) getSequence() uint32 { +func (s *Server) getSequence() int { s.sequenceLock.Lock() defer s.sequenceLock.Unlock() return s.sequence } -func (s *Server) incSequence() uint32 { +func (s *Server) incSequence() int { s.sequenceLock.Lock() defer s.sequenceLock.Unlock() s.sequence += 1 @@ -116,47 +120,50 @@ func (h *connection) killconnection() { h.kill <- true } -func (h *connection) start() { +func (h *connection) start(conn net.Conn) { + h.rw = bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)) + h.queue = make(chan dap.Message) + h.Connected = true + h.Event = make(chan events.EventType) + go func() { + for { + select { + case <-h.kill: + h.kill <- true + return + case event := <-h.Event: + h.sendEventMessage(event) + } + } + }() + go func() { + for { + select { + case message := <-h.queue: + h.sendMessage(message) + case <-h.kill: + h.kill <- true + return + + } + } + }() for { select { - case conn := <-h.s.connQueue: - h.rw = bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)) - h.queue = make(chan dap.Message, 5) - h.Connected = true - h.Event = make(chan events.EventType) - go func() { - for { - select { - case <-h.kill: - h.kill <- true - return - case event := <-h.Event: - h.sendEventMessage(event) - } - } - }() - for { - select { - case <-h.kill: - h.kill <- true - return - default: - // no-op - } - err := h.handleRequest() - if err != nil { - if err == io.EOF { - log.Println("No more data to read:", err) - break - } - log.Fatal("Server error: ", err) - } - } - case <-h.s.endChannel: - h.s.endChannel <- true - h.Connected = false - h.s.wg.Done() + case <-h.kill: + h.kill <- true return + default: + // no-op + } + err := h.handleRequest() + if err != nil { + if err == io.EOF { + log.Errorf("Connection closed: ", err) + h.s.endChannel <- true + return + } + log.Fatal("Server error: ", err) } } } @@ -166,7 +173,7 @@ func (h *connection) handleRequest() error { if err != nil { return err } - log.Printf("Received request\n\t%#v\n", request) + log.Infof("Received request\n\t%#v\n", request) h.s.wg.Add(1) go func() { h.dispatchRequest(request) @@ -191,44 +198,44 @@ func (h *connection) dispatchRequest(request dap.Message) { case *dap.InitializeRequest: h.s.debugger.onInitializeRequest(request, h) case *dap.LaunchRequest: - h.s.debugger.onLaunchRequest(request, h.queue) + h.s.debugger.onLaunchRequest(request, h.queue, h.s.incSequence()) case *dap.AttachRequest: - h.s.debugger.onAttachRequest(request, h.queue) + h.s.debugger.onAttachRequest(request, h.queue, h.s.incSequence()) case *dap.DisconnectRequest: - h.s.debugger.onDisconnectRequest(request, h.queue) + h.s.debugger.onDisconnectRequest(request, h.queue, h.s.incSequence()) h.killconnection() case *dap.TerminateRequest: - h.s.debugger.onTerminateRequest(request, h.queue) + h.s.debugger.onTerminateRequest(request, h.queue, h.s.incSequence()) case *dap.RestartRequest: - h.s.debugger.onRestartRequest(request, h.queue) + h.s.debugger.onRestartRequest(request, h.queue, h.s.incSequence()) case *dap.SetBreakpointsRequest: - h.s.debugger.onSetBreakpointsRequest(request, h.queue) + h.s.debugger.onSetBreakpointsRequest(request, h.queue, h.s.incSequence()) case *dap.SetFunctionBreakpointsRequest: - h.s.debugger.onSetFunctionBreakpointsRequest(request, h.queue) + h.s.debugger.onSetFunctionBreakpointsRequest(request, h.queue, h.s.incSequence()) case *dap.SetExceptionBreakpointsRequest: - h.s.debugger.onSetExceptionBreakpointsRequest(request, h.queue) + h.s.debugger.onSetExceptionBreakpointsRequest(request, h.queue, h.s.incSequence()) case *dap.ConfigurationDoneRequest: - h.s.debugger.onConfigurationDoneRequest(request, h.queue) + h.s.debugger.onConfigurationDoneRequest(request, h.queue, h.s.incSequence()) case *dap.ContinueRequest: - h.s.debugger.onContinueRequest(request, h.queue) + h.s.debugger.onContinueRequest(request, h.queue, h.s.incSequence()) case *dap.NextRequest: - h.s.debugger.onNextRequest(request, h.queue) + h.s.debugger.onNextRequest(request, h.queue, h.s.incSequence()) case *dap.StepInRequest: - h.s.debugger.onStepInRequest(request, h.queue) + h.s.debugger.onStepInRequest(request, h.queue, h.s.incSequence()) case *dap.StepOutRequest: - h.s.debugger.onStepOutRequest(request, h.queue) + h.s.debugger.onStepOutRequest(request, h.queue, h.s.incSequence()) case *dap.StepBackRequest: - h.s.debugger.onStepBackRequest(request, h.queue) + h.s.debugger.onStepBackRequest(request, h.queue, h.s.incSequence()) case *dap.ReverseContinueRequest: - h.s.debugger.onReverseContinueRequest(request, h.queue) + h.s.debugger.onReverseContinueRequest(request, h.queue, h.s.incSequence()) case *dap.RestartFrameRequest: - h.s.debugger.onRestartFrameRequest(request, h.queue) + h.s.debugger.onRestartFrameRequest(request, h.queue, h.s.incSequence()) case *dap.GotoRequest: - h.s.debugger.onGotoRequest(request, h.queue) + h.s.debugger.onGotoRequest(request, h.queue, h.s.incSequence()) case *dap.PauseRequest: - h.s.debugger.onPauseRequest(request, h.queue) + h.s.debugger.onPauseRequest(request, h.queue, h.s.incSequence()) case *dap.StackTraceRequest: - h.s.debugger.onStackTraceRequest(request, h.queue) + h.s.debugger.onStackTraceRequest(request, h.queue, h.s.incSequence()) case *dap.ScopesRequest: h.s.debugger.onScopesRequest(request, h.queue) case *dap.VariablesRequest: @@ -236,37 +243,37 @@ func (h *connection) dispatchRequest(request dap.Message) { case *dap.SetVariableRequest: h.s.debugger.onSetVariableRequest(request, h.queue) case *dap.SetExpressionRequest: - h.s.debugger.onSetExpressionRequest(request, h.queue) + h.s.debugger.onSetExpressionRequest(request, h.queue, h.s.incSequence()) case *dap.SourceRequest: - h.s.debugger.onSourceRequest(request, h.queue) + h.s.debugger.onSourceRequest(request, h.queue, h.s.incSequence()) case *dap.ThreadsRequest: - h.s.debugger.onThreadsRequest(request, h.queue) + h.s.debugger.onThreadsRequest(request, h.queue, h.s.incSequence()) case *dap.TerminateThreadsRequest: - h.s.debugger.onTerminateThreadsRequest(request, h.queue) + h.s.debugger.onTerminateThreadsRequest(request, h.queue, h.s.incSequence()) case *dap.EvaluateRequest: - h.s.debugger.onEvaluateRequest(request, h.queue) + h.s.debugger.onEvaluateRequest(request, h.queue, h.s.incSequence()) case *dap.StepInTargetsRequest: h.s.debugger.onStepInTargetsRequest(request, h.queue) case *dap.GotoTargetsRequest: - h.s.debugger.onGotoTargetsRequest(request, h.queue) + h.s.debugger.onGotoTargetsRequest(request, h.queue, h.s.incSequence()) case *dap.CompletionsRequest: - h.s.debugger.onCompletionsRequest(request, h.queue) + h.s.debugger.onCompletionsRequest(request, h.queue, h.s.incSequence()) case *dap.ExceptionInfoRequest: - h.s.debugger.onExceptionInfoRequest(request, h.queue) + h.s.debugger.onExceptionInfoRequest(request, h.queue, h.s.incSequence()) case *dap.LoadedSourcesRequest: - h.s.debugger.onLoadedSourcesRequest(request, h.queue) + h.s.debugger.onLoadedSourcesRequest(request, h.queue, h.s.incSequence()) case *dap.DataBreakpointInfoRequest: - h.s.debugger.onDataBreakpointInfoRequest(request, h.queue) + h.s.debugger.onDataBreakpointInfoRequest(request, h.queue, h.s.incSequence()) case *dap.SetDataBreakpointsRequest: - h.s.debugger.onSetDataBreakpointsRequest(request, h.queue) + h.s.debugger.onSetDataBreakpointsRequest(request, h.queue, h.s.incSequence()) case *dap.ReadMemoryRequest: - h.s.debugger.onReadMemoryRequest(request, h.queue) + h.s.debugger.onReadMemoryRequest(request, h.queue, h.s.incSequence()) case *dap.DisassembleRequest: - h.s.debugger.onDisassembleRequest(request, h.queue) + h.s.debugger.onDisassembleRequest(request, h.queue, h.s.incSequence()) case *dap.CancelRequest: - h.s.debugger.onCancelRequest(request, h.queue) + h.s.debugger.onCancelRequest(request, h.queue, h.s.incSequence()) case *dap.BreakpointLocationsRequest: - h.s.debugger.onBreakpointLocationsRequest(request, h.queue) + h.s.debugger.onBreakpointLocationsRequest(request, h.queue, h.s.incSequence()) default: log.Fatalf("Unable to process %#v", request) } @@ -277,14 +284,25 @@ func (h *connection) sendEventMessage(event events.EventType) { switch event { case events.EventTypeContinued: h.queue <- &dap.ContinuedEvent{ - Event: dap.Event{Event: "continued"}, + Event: dap.Event{Event: "continued", + ProtocolMessage: dap.ProtocolMessage{ + Seq: h.s.incSequence(), + Type: "event", + }, + }, Body: dap.ContinuedEventBody{ AllThreadsContinued: true, + ThreadId: 1, }, } case events.EventTypeExited: h.queue <- &dap.ExitedEvent{ - Event: dap.Event{Event: "continued"}, + Event: dap.Event{Event: "continued", + ProtocolMessage: dap.ProtocolMessage{ + Seq: h.s.incSequence(), + Type: "event", + }, + }, Body: dap.ExitedEventBody{ ExitCode: 0, //TODO this is so not true }, @@ -293,42 +311,85 @@ func (h *connection) sendEventMessage(event events.EventType) { // this is a no-op for us here case events.EventTypeStoppedPaused: h.queue <- &dap.StoppedEvent{ - Event: dap.Event{Event: "stopped"}, + Event: dap.Event{ + Event: "stopped", + ProtocolMessage: dap.ProtocolMessage{ + Seq: h.s.incSequence(), + Type: "event", + }, + }, Body: dap.StoppedEventBody{ AllThreadsStopped: true, + ThreadId: 1, Reason: "pause", // we need to propagate this by using more reasons }, } case events.EventTypeStoppedBreakpoint: h.queue <- &dap.StoppedEvent{ - Event: dap.Event{Event: "stopped"}, + Event: dap.Event{Event: "stopped", + ProtocolMessage: dap.ProtocolMessage{ + Seq: h.s.incSequence(), + Type: "event", + }, + }, Body: dap.StoppedEventBody{ AllThreadsStopped: true, + ThreadId: 1, Reason: "breakpoint", // we need to propagate this by using more reasons }, } case events.EventTypeStoppedEntry: h.queue <- &dap.StoppedEvent{ - Event: dap.Event{Event: "stopped"}, + Event: dap.Event{Event: "stopped", + ProtocolMessage: dap.ProtocolMessage{ + Seq: h.s.incSequence(), + Type: "event", + }, + }, Body: dap.StoppedEventBody{ AllThreadsStopped: true, + ThreadId: 1, Reason: "entry", // we need to propagate this by using more reasons }, } case events.EventTypeStoppedStep: h.queue <- &dap.StoppedEvent{ - Event: dap.Event{Event: "stopped"}, + Event: dap.Event{Event: "stopped", + ProtocolMessage: dap.ProtocolMessage{ + Seq: h.s.incSequence(), + Type: "event", + }, + }, Body: dap.StoppedEventBody{ AllThreadsStopped: true, + ThreadId: 1, Reason: "step", // we need to propagate this by using more reasons }, } case events.EventTypeTerminated: h.queue <- &dap.TerminatedEvent{ - Event: dap.Event{Event: "terminated"}, + Event: dap.Event{ + Event: "terminated", + ProtocolMessage: dap.ProtocolMessage{ + Seq: h.s.incSequence(), + Type: "event", + }, + }, Body: dap.TerminatedEventBody{ Restart: false, }, } } } + +func (h *connection) sendMessage(message dap.Message) { + log.Infof("Sending message over wire: %#v", message) + err := dap.WriteProtocolMessage(h.rw, message) + if err != nil { + log.Warnf("Error sending: %s", err.Error()) + } + err = h.rw.Flush() + if err != nil { + log.Warnf("Error flushing: %s", err.Error()) + } +} diff --git a/lisp/x/debugger/dapserver/debuggerwrapper.go b/lisp/x/debugger/dapserver/debuggerwrapper.go index 85048c8..fe063d2 100644 --- a/lisp/x/debugger/dapserver/debuggerwrapper.go +++ b/lisp/x/debugger/dapserver/debuggerwrapper.go @@ -18,6 +18,10 @@ func (d *debuggerwrapper) onInitializeRequest(request *dap.InitializeRequest, ha Response: dap.Response{ Success: true, RequestSeq: request.Seq, + ProtocolMessage: dap.ProtocolMessage{ + Seq: handler.s.incSequence(), + Type: "response", + }, }, Body: dap.Capabilities{ SupportsConfigurationDoneRequest: true, @@ -55,41 +59,73 @@ func (d *debuggerwrapper) onInitializeRequest(request *dap.InitializeRequest, ha } } -func (d *debuggerwrapper) onLaunchRequest(request *dap.LaunchRequest , returnchan chan dap.Message) { - returnchan <- &dap.ErrorResponse{ +func (d *debuggerwrapper) onLaunchRequest(request *dap.LaunchRequest , returnchan chan dap.Message, seq int) { + returnchan <- &dap.LaunchResponse{ Response: dap.Response{ RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: true, + Message: "Not supported", + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, }, } } -func (d *debuggerwrapper) onAttachRequest(request *dap.AttachRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onAttachRequest(request *dap.AttachRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, }, } } -func (d *debuggerwrapper) onDisconnectRequest(request *dap.DisconnectRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onDisconnectRequest(request *dap.DisconnectRequest , returnchan chan dap.Message, seq int) { if request.Arguments.TerminateDebuggee { d.debugger.Complete() } - returnchan <- &dap.DisconnectResponse{} + returnchan <- &dap.DisconnectResponse{ + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + RequestSeq: request.GetSeq(), + Success: true, + Message: "Complete", + }, + } } -func (d *debuggerwrapper) onTerminateRequest(request *dap.TerminateRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onTerminateRequest(request *dap.TerminateRequest , returnchan chan dap.Message, seq int) { d.debugger.Complete() - returnchan <- &dap.TerminateResponse{} + returnchan <- &dap.TerminateResponse{ + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + RequestSeq: request.GetSeq(), + Success: true, + Message: "Complete", + }, + } } -func (d *debuggerwrapper) onRestartRequest(request *dap.RestartRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onRestartRequest(request *dap.RestartRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -97,10 +133,14 @@ func (d *debuggerwrapper) onRestartRequest(request *dap.RestartRequest , returnc } } -func (d *debuggerwrapper) onSetBreakpointsRequest(request *dap.SetBreakpointsRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onSetBreakpointsRequest(request *dap.SetBreakpointsRequest , returnchan chan dap.Message, seq int) { if request.Arguments.SourceModified { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Cannot modify source whilst running", @@ -136,6 +176,10 @@ func (d *debuggerwrapper) onSetBreakpointsRequest(request *dap.SetBreakpointsReq } returnchan <- &dap.SetBreakpointsResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, Success: true, RequestSeq: request.GetSeq(), }, @@ -143,9 +187,13 @@ func (d *debuggerwrapper) onSetBreakpointsRequest(request *dap.SetBreakpointsReq } } -func (d *debuggerwrapper) onSetFunctionBreakpointsRequest(request *dap.SetFunctionBreakpointsRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onSetFunctionBreakpointsRequest(request *dap.SetFunctionBreakpointsRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -153,9 +201,13 @@ func (d *debuggerwrapper) onSetFunctionBreakpointsRequest(request *dap.SetFuncti } } -func (d *debuggerwrapper) onSetExceptionBreakpointsRequest(request *dap.SetExceptionBreakpointsRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onSetExceptionBreakpointsRequest(request *dap.SetExceptionBreakpointsRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -163,46 +215,85 @@ func (d *debuggerwrapper) onSetExceptionBreakpointsRequest(request *dap.SetExcep } } -func (d *debuggerwrapper) onConfigurationDoneRequest(request *dap.ConfigurationDoneRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onConfigurationDoneRequest(request *dap.ConfigurationDoneRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ConfigurationDoneResponse{ - Response: dap.Response{RequestSeq: request.GetSeq(), Success: true}, + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + RequestSeq: request.GetSeq(), + Success: true, + }, } } -func (d *debuggerwrapper) onContinueRequest(request *dap.ContinueRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onContinueRequest(request *dap.ContinueRequest , returnchan chan dap.Message, seq int) { d.debugger.Continue() returnchan <- &dap.ContinueResponse{ - Response: dap.Response{Success: true, RequestSeq: request.GetSeq()}, + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + Success: true, + RequestSeq: request.GetSeq(), + }, Body: dap.ContinueResponseBody{ AllThreadsContinued: true, }, } } -func (d *debuggerwrapper) onNextRequest(request *dap.NextRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onNextRequest(request *dap.NextRequest , returnchan chan dap.Message, seq int) { d.debugger.Step() returnchan <- &dap.NextResponse{ - Response: dap.Response{Success: true, RequestSeq: request.GetSeq()}, + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + Success: true, + RequestSeq: request.GetSeq(), + }, } } -func (d *debuggerwrapper) onStepInRequest(request *dap.StepInRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onStepInRequest(request *dap.StepInRequest , returnchan chan dap.Message, seq int) { d.debugger.Step() returnchan <- &dap.StepInResponse{ - Response: dap.Response{Success: true, RequestSeq: request.GetSeq()}, + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + Success: true, + RequestSeq: request.GetSeq(), + }, } } -func (d *debuggerwrapper) onStepOutRequest(request *dap.StepOutRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onStepOutRequest(request *dap.StepOutRequest , returnchan chan dap.Message, seq int) { d.debugger.Step() returnchan <- &dap.StepOutResponse{ - Response: dap.Response{Success: true, RequestSeq: request.GetSeq()}, + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + Success: true, + RequestSeq: request.GetSeq(), + }, } } -func (d *debuggerwrapper) onStepBackRequest(request *dap.StepBackRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onStepBackRequest(request *dap.StepBackRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -210,9 +301,13 @@ func (d *debuggerwrapper) onStepBackRequest(request *dap.StepBackRequest , retur } } -func (d *debuggerwrapper) onReverseContinueRequest(request *dap.ReverseContinueRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onReverseContinueRequest(request *dap.ReverseContinueRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -220,9 +315,13 @@ func (d *debuggerwrapper) onReverseContinueRequest(request *dap.ReverseContinueR } } -func (d *debuggerwrapper) onRestartFrameRequest(request *dap.RestartFrameRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onRestartFrameRequest(request *dap.RestartFrameRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -230,9 +329,13 @@ func (d *debuggerwrapper) onRestartFrameRequest(request *dap.RestartFrameRequest } } -func (d *debuggerwrapper) onGotoRequest(request *dap.GotoRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onGotoRequest(request *dap.GotoRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -240,30 +343,33 @@ func (d *debuggerwrapper) onGotoRequest(request *dap.GotoRequest , returnchan ch } } -func (d *debuggerwrapper) onPauseRequest(request *dap.PauseRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onPauseRequest(request *dap.PauseRequest , returnchan chan dap.Message, seq int) { d.debugger.Halt() returnchan <- &dap.PauseResponse{ - Response: dap.Response{Success: true, RequestSeq: request.GetSeq()}, + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + Success: true, + RequestSeq: request.GetSeq(), + }, } } -func (d *debuggerwrapper) onStackTraceRequest(request *dap.StackTraceRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onStackTraceRequest(request *dap.StackTraceRequest , returnchan chan dap.Message, seq int) { st := &rpc2.StacktraceOut{} d.debugger.GetStacktrace(st) - out := make([]dap.StackFrame, 0) - for x:=0; x < len(st.Locations); x++ { - out = append(out, dap.StackFrame{ - Id: x, - Name: st.Locations[x].Function.Name(), - Source: dap.Source{ - Name: st.Locations[x].File, - }, - Line: st.Locations[x].Line, - Column: 0, - }) - } + out := d.debugger.GetDapStacktrace() returnchan <- &dap.StackTraceResponse{ - Response: dap.Response{Success: true, RequestSeq: request.Seq}, + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + Success: true, + RequestSeq: request.Seq, + }, Body: dap.StackTraceResponseBody{ StackFrames: out, TotalFrames: len(out), @@ -277,9 +383,13 @@ func (d *debuggerwrapper) onVariablesRequest(request *dap.VariablesRequest , ret func (d *debuggerwrapper) onSetVariableRequest(request *dap.SetVariableRequest , returnchan chan dap.Message) {} -func (d *debuggerwrapper) onSetExpressionRequest(request *dap.SetExpressionRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onSetExpressionRequest(request *dap.SetExpressionRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -287,11 +397,13 @@ func (d *debuggerwrapper) onSetExpressionRequest(request *dap.SetExpressionReque } } -func (d *debuggerwrapper) onSourceRequest(request *dap.SourceRequest , returnchan chan dap.Message) {} - -func (d *debuggerwrapper) onThreadsRequest(request *dap.ThreadsRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onSourceRequest(request *dap.SourceRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -299,9 +411,34 @@ func (d *debuggerwrapper) onThreadsRequest(request *dap.ThreadsRequest , returnc } } -func (d *debuggerwrapper) onTerminateThreadsRequest(request *dap.TerminateThreadsRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onThreadsRequest(request *dap.ThreadsRequest , returnchan chan dap.Message, seq int) { + returnchan <- &dap.ThreadsResponse{ + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + RequestSeq: request.GetSeq(), + Success: true, + }, + Body: dap.ThreadsResponseBody{ + Threads: []dap.Thread{ + { + Id: 1, + Name: "Execution thread", + }, + }, + }, + } +} + +func (d *debuggerwrapper) onTerminateThreadsRequest(request *dap.TerminateThreadsRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -309,9 +446,13 @@ func (d *debuggerwrapper) onTerminateThreadsRequest(request *dap.TerminateThread } } -func (d *debuggerwrapper) onEvaluateRequest(request *dap.EvaluateRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onEvaluateRequest(request *dap.EvaluateRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -321,9 +462,13 @@ func (d *debuggerwrapper) onEvaluateRequest(request *dap.EvaluateRequest , retur func (d *debuggerwrapper) onStepInTargetsRequest(request *dap.StepInTargetsRequest , returnchan chan dap.Message) {} -func (d *debuggerwrapper) onGotoTargetsRequest(request *dap.GotoTargetsRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onGotoTargetsRequest(request *dap.GotoTargetsRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -331,9 +476,13 @@ func (d *debuggerwrapper) onGotoTargetsRequest(request *dap.GotoTargetsRequest , } } -func (d *debuggerwrapper) onCompletionsRequest(request *dap.CompletionsRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onCompletionsRequest(request *dap.CompletionsRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -341,9 +490,13 @@ func (d *debuggerwrapper) onCompletionsRequest(request *dap.CompletionsRequest , } } -func (d *debuggerwrapper) onExceptionInfoRequest(request *dap.ExceptionInfoRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onExceptionInfoRequest(request *dap.ExceptionInfoRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -351,13 +504,17 @@ func (d *debuggerwrapper) onExceptionInfoRequest(request *dap.ExceptionInfoReque } } -func (d *debuggerwrapper) onLoadedSourcesRequest(request *dap.LoadedSourcesRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onLoadedSourcesRequest(request *dap.LoadedSourcesRequest , returnchan chan dap.Message, seq int) { } -func (d *debuggerwrapper) onDataBreakpointInfoRequest(request *dap.DataBreakpointInfoRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onDataBreakpointInfoRequest(request *dap.DataBreakpointInfoRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -365,9 +522,13 @@ func (d *debuggerwrapper) onDataBreakpointInfoRequest(request *dap.DataBreakpoin } } -func (d *debuggerwrapper) onSetDataBreakpointsRequest(request *dap.SetDataBreakpointsRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onSetDataBreakpointsRequest(request *dap.SetDataBreakpointsRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -375,9 +536,13 @@ func (d *debuggerwrapper) onSetDataBreakpointsRequest(request *dap.SetDataBreakp } } -func (d *debuggerwrapper) onReadMemoryRequest(request *dap.ReadMemoryRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onReadMemoryRequest(request *dap.ReadMemoryRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -385,9 +550,13 @@ func (d *debuggerwrapper) onReadMemoryRequest(request *dap.ReadMemoryRequest , r } } -func (d *debuggerwrapper) onDisassembleRequest(request *dap.DisassembleRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onDisassembleRequest(request *dap.DisassembleRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -395,9 +564,13 @@ func (d *debuggerwrapper) onDisassembleRequest(request *dap.DisassembleRequest , } } -func (d *debuggerwrapper) onCancelRequest(request *dap.CancelRequest , returnchan chan dap.Message) { +func (d *debuggerwrapper) onCancelRequest(request *dap.CancelRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -405,7 +578,31 @@ func (d *debuggerwrapper) onCancelRequest(request *dap.CancelRequest , returncha } } -func (d *debuggerwrapper) onBreakpointLocationsRequest(request *dap.BreakpointLocationsRequest , returnchan chan dap.Message) { - +func (d *debuggerwrapper) onBreakpointLocationsRequest(request *dap.BreakpointLocationsRequest , returnchan chan dap.Message, seq int) { + file := request.Arguments.Source.Name + breakpoints := make([]dap.BreakpointLocation, 0) + for _, v := range d.debugger.GetAllBreakpoints() { + if file == v.File { + breakpoints = append(breakpoints, dap.BreakpointLocation{ + Line: v.Line, + Column: 0, + EndLine: 0, + EndColumn: 0, + }) + } + } + returnchan <- &dap.BreakpointLocationsResponse{ + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + RequestSeq: request.GetSeq(), + Success: true, + }, + Body: dap.BreakpointLocationsResponseBody{ + Breakpoints: breakpoints, + }, + } } diff --git a/lisp/x/debugger/debugger.go b/lisp/x/debugger/debugger.go index db555e2..e2a14e9 100644 --- a/lisp/x/debugger/debugger.go +++ b/lisp/x/debugger/debugger.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/go-delve/delve/service/rpc2" + "github.com/google/go-dap" "github.com/luthersystems/elps/lisp" "github.com/luthersystems/elps/lisp/x/debugger/dapserver" "github.com/luthersystems/elps/lisp/x/debugger/delveserver" @@ -222,6 +223,47 @@ func (d *Debugger) GetBreakpointByName(name string) (*api.Breakpoint, error) { return nil, errors.New("not found") } +func (d *Debugger) GetDapStacktrace() []dap.StackFrame { + out := make([]dap.StackFrame, 0) + for _, frame := range d.runtime.Stack.Frames { + var source = frame.Source + if source == nil { + source = &token.Location{ + File: "Unknown file", + Path: "", + Pos: 0, + Line: 0, + Col: 0, + } + } + hint := "normal" + origin := "" + if source.File == "Unknown file" || source.File == "" { + hint = "deemphasize" + origin = "internal module" + } + out = append(out, dap.StackFrame{ + Id: 0, + Name: frame.Name, + Source: dap.Source{ + Name: source.File, + Path: source.Path, + PresentationHint: hint, + Origin: origin, + }, + Line: source.Line, + Column: 0, + ModuleId: frame.Package, + PresentationHint: hint, + }) + } + for i := len(out)/2-1; i >= 0; i-- { + opp := len(out)-1-i + out[i], out[opp] = out[opp], out[i] + } + return out +} + func (d *Debugger) GetStacktrace(st *rpc2.StacktraceOut) { d.logger.Info("Returning STACK") st.Locations = make([]api.Stackframe, 0) @@ -239,7 +281,7 @@ func (d *Debugger) GetStacktrace(st *rpc2.StacktraceOut) { st.Locations = append(st.Locations, api.Stackframe{ Location: api.Location{ PC: 0, - File: fmt.Sprintf("%s/%s", d.pwd, source.File), + File: fmt.Sprintf("%s", source.Path), Line: source.Line, Function: &api.Function{ Name_: "f", diff --git a/lisp/x/debugger/delveserver/server_debugger.go b/lisp/x/debugger/delveserver/server_debugger.go index 531f655..58f40c5 100644 --- a/lisp/x/debugger/delveserver/server_debugger.go +++ b/lisp/x/debugger/delveserver/server_debugger.go @@ -3,6 +3,7 @@ package delveserver import ( "github.com/go-delve/delve/service/api" "github.com/go-delve/delve/service/rpc2" + "github.com/google/go-dap" "time" ) @@ -10,6 +11,7 @@ type ServerDebugger interface { GetBreakpoint(id int) (*api.Breakpoint, error) GetBreakpointByName(name string) (*api.Breakpoint, error) GetStacktrace(st *rpc2.StacktraceOut) + GetDapStacktrace() []dap.StackFrame GetAllBreakpoints() map[int]*api.Breakpoint CreateBreakpoint(breakpoint *api.Breakpoint) *api.Breakpoint RemoveBreakpoint(id int) error From 838b81a7778734836de15b14c512f236153374c3 Mon Sep 17 00:00:00 2001 From: "reuben.thompson@acresoftware.com" Date: Fri, 24 Jul 2020 08:58:39 +0100 Subject: [PATCH 5/9] Make variables mostly work --- cmd/run.go | 2 +- lisp/x/debugger/dapserver/dapserver.go | 27 ++- lisp/x/debugger/dapserver/debuggerwrapper.go | 190 ++++++++++++++++-- lisp/x/debugger/debugger.go | 153 ++++++++++---- lisp/x/debugger/debugger_test.go | 2 +- .../x/debugger/delveserver/server_debugger.go | 8 +- 6 files changed, 308 insertions(+), 74 deletions(-) diff --git a/cmd/run.go b/cmd/run.go index 2a4cf17..90971c4 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -65,7 +65,7 @@ var runCmd = &cobra.Command{ fmt.Fprintln(os.Stderr, "Invalid debugger. Specify delve or dap") os.Exit(1) } - profiler = debugger.NewDebugger(env.Runtime, fmt.Sprintf(":%d", runDebuggerPort), mode) + profiler = debugger.NewDebugger(env, fmt.Sprintf(":%d", runDebuggerPort), mode) } for i := range args { res := env.LoadFile(args[i]) diff --git a/lisp/x/debugger/dapserver/dapserver.go b/lisp/x/debugger/dapserver/dapserver.go index ccc7d1f..2fe68eb 100644 --- a/lisp/x/debugger/dapserver/dapserver.go +++ b/lisp/x/debugger/dapserver/dapserver.go @@ -21,7 +21,7 @@ import ( ) type Server struct { - endChannel chan bool + EndChannel chan bool Address string listener net.Listener connQueue chan net.Conn @@ -49,6 +49,7 @@ func NewServer(debugger delveserver.ServerDebugger, address string, handlers int sequence: 0, wg: new(sync.WaitGroup), debugger: &debuggerwrapper{debugger: debugger}, + EndChannel: make(chan bool), } server.connection.s = server return server, nil @@ -78,7 +79,7 @@ func startMonitor() { } func (s *Server) Stop() error { - s.endChannel <- true + s.EndChannel <- true s.wg.Wait() return nil } @@ -160,7 +161,7 @@ func (h *connection) start(conn net.Conn) { if err != nil { if err == io.EOF { log.Errorf("Connection closed: ", err) - h.s.endChannel <- true + h.s.EndChannel <- true return } log.Fatal("Server error: ", err) @@ -173,9 +174,15 @@ func (h *connection) handleRequest() error { if err != nil { return err } - log.Infof("Received request\n\t%#v\n", request) + log.Debugf("Received request\n\t%#v\n", request) h.s.wg.Add(1) go func() { + defer func() { + if r := recover(); r != nil { + log.Fatalf("PANIC! Quitting - %s", r) + h.kill <- true + } + }() h.dispatchRequest(request) h.s.wg.Done() }() @@ -184,7 +191,7 @@ func (h *connection) handleRequest() error { func (h *connection) sendHandler() { for message := range h.queue { - log.Infof("Message: %s", message) + log.Debugf("Sending message: %s", message) err := dap.WriteProtocolMessage(h.rw.Writer, message) if err != nil { log.Errorf("Error sending message: %s", err.Error()) @@ -237,11 +244,11 @@ func (h *connection) dispatchRequest(request dap.Message) { case *dap.StackTraceRequest: h.s.debugger.onStackTraceRequest(request, h.queue, h.s.incSequence()) case *dap.ScopesRequest: - h.s.debugger.onScopesRequest(request, h.queue) + h.s.debugger.onScopesRequest(request, h.queue, h.s.incSequence()) case *dap.VariablesRequest: - h.s.debugger.onVariablesRequest(request, h.queue) + h.s.debugger.onVariablesRequest(request, h.queue, h.s.incSequence()) case *dap.SetVariableRequest: - h.s.debugger.onSetVariableRequest(request, h.queue) + h.s.debugger.onSetVariableRequest(request, h.queue, h.s.incSequence()) case *dap.SetExpressionRequest: h.s.debugger.onSetExpressionRequest(request, h.queue, h.s.incSequence()) case *dap.SourceRequest: @@ -274,6 +281,8 @@ func (h *connection) dispatchRequest(request dap.Message) { h.s.debugger.onCancelRequest(request, h.queue, h.s.incSequence()) case *dap.BreakpointLocationsRequest: h.s.debugger.onBreakpointLocationsRequest(request, h.queue, h.s.incSequence()) + case *dap.ModulesRequest: + h.s.debugger.onModulesRequest(request, h.queue, h.s.incSequence()) default: log.Fatalf("Unable to process %#v", request) } @@ -383,7 +392,7 @@ func (h *connection) sendEventMessage(event events.EventType) { } func (h *connection) sendMessage(message dap.Message) { - log.Infof("Sending message over wire: %#v", message) + log.Debugf("Sending message over wire: %#v", message) err := dap.WriteProtocolMessage(h.rw, message) if err != nil { log.Warnf("Error sending: %s", err.Error()) diff --git a/lisp/x/debugger/dapserver/debuggerwrapper.go b/lisp/x/debugger/dapserver/debuggerwrapper.go index fe063d2..06ff90e 100644 --- a/lisp/x/debugger/dapserver/debuggerwrapper.go +++ b/lisp/x/debugger/dapserver/debuggerwrapper.go @@ -1,4 +1,5 @@ package dapserver + import ( "github.com/go-delve/delve/service/api" "github.com/go-delve/delve/service/rpc2" @@ -28,21 +29,21 @@ func (d *debuggerwrapper) onInitializeRequest(request *dap.InitializeRequest, ha SupportsFunctionBreakpoints: true, SupportsConditionalBreakpoints: false, SupportsHitConditionalBreakpoints: false, - SupportsEvaluateForHovers: false, + SupportsEvaluateForHovers: true, SupportsStepBack: false, SupportsSetVariable: true, SupportsRestartFrame: false, SupportsGotoTargetsRequest: false, SupportsStepInTargetsRequest: false, SupportsCompletionsRequest: false, - SupportsModulesRequest: false, + SupportsModulesRequest: true, SupportsRestartRequest: false, SupportsExceptionOptions: false, SupportsValueFormattingOptions: false, SupportsExceptionInfoRequest: false, SupportTerminateDebuggee: true, SupportsDelayedStackTraceLoading: false, - SupportsLoadedSourcesRequest: true, + SupportsLoadedSourcesRequest: false, SupportsLogPoints: false, SupportsTerminateThreadsRequest: false, SupportsSetExpression: true, @@ -90,6 +91,7 @@ func (d *debuggerwrapper) onAttachRequest(request *dap.AttachRequest , returncha func (d *debuggerwrapper) onDisconnectRequest(request *dap.DisconnectRequest , returnchan chan dap.Message, seq int) { if request.Arguments.TerminateDebuggee { d.debugger.Complete() + panic("DONE") } returnchan <- &dap.DisconnectResponse{ Response: dap.Response{ @@ -117,6 +119,7 @@ func (d *debuggerwrapper) onTerminateRequest(request *dap.TerminateRequest , ret Message: "Complete", }, } + panic("DONE") } func (d *debuggerwrapper) onRestartRequest(request *dap.RestartRequest , returnchan chan dap.Message, seq int) { @@ -377,11 +380,122 @@ func (d *debuggerwrapper) onStackTraceRequest(request *dap.StackTraceRequest , r } } -func (d *debuggerwrapper) onScopesRequest(request *dap.ScopesRequest , returnchan chan dap.Message) {} +func (d *debuggerwrapper) onScopesRequest(request *dap.ScopesRequest , returnchan chan dap.Message, seq int) { + source := d.debugger.GetDapStacktrace()[0] + returnchan <- &dap.ScopesResponse{ + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + RequestSeq: request.GetSeq(), + Success: true, + }, + Body: dap.ScopesResponseBody{ + Scopes: []dap.Scope{ + { + VariablesReference: 2, + PresentationHint: "locals", + Name: "Scope", + }, + { + Name: "Arguments", + PresentationHint: "locals", + VariablesReference: 1, + Expensive: false, + Source: source.Source, + Line: source.Line, + Column: source.Column, + EndLine: source.EndLine, + EndColumn: source.EndColumn, + }, -func (d *debuggerwrapper) onVariablesRequest(request *dap.VariablesRequest , returnchan chan dap.Message) {} + }, + }, + } +} -func (d *debuggerwrapper) onSetVariableRequest(request *dap.SetVariableRequest , returnchan chan dap.Message) {} +func (d *debuggerwrapper) onVariablesRequest(request *dap.VariablesRequest , returnchan chan dap.Message, seq int) { + variables := make([]dap.Variable, 0) + if request.Arguments.VariablesReference == 1 { + variables := d.debugger.GetArguments() + returnchan <- &dap.VariablesResponse{ + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + RequestSeq: request.GetSeq(), + Success: true, + }, + Body: dap.VariablesResponseBody{ + Variables: variables, + }, + } + return + } + + for _, vari := range d.debugger.GetVariables() { + outVar := dap.Variable{ + Name: vari.Name, + Value: vari.Value, + Type: vari.Type, + PresentationHint: dap.VariablePresentationHint{ + Kind: "data", + Visibility: "public", + Attributes: []string{}, + }, + VariablesReference: 0, // TODO support children + } + variables = append(variables, outVar) + } + returnchan <- &dap.VariablesResponse{ + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + RequestSeq: request.GetSeq(), + Success: true, + }, + Body: dap.VariablesResponseBody{ + Variables: variables, + }, + } +} + +func (d *debuggerwrapper) onSetVariableRequest(request *dap.SetVariableRequest , returnchan chan dap.Message, seq int) { + err := d.debugger.SetVariableInScope(api.EvalScope{}, request.Arguments.Name, request.Arguments.Value) + if err != nil { + returnchan <- &dap.SetVariableResponse{ + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + RequestSeq: request.GetSeq(), + Success: false, + Message: err.Error(), + }, + Body: dap.SetVariableResponseBody{}, + } + return + } + returnchan <- &dap.SetVariableResponse{ + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + RequestSeq: request.GetSeq(), + Success: true, + }, + Body: dap.SetVariableResponseBody{ + Value: request.Arguments.Value, + Type: "LVal", + }, + } +} func (d *debuggerwrapper) onSetExpressionRequest(request *dap.SetExpressionRequest , returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ @@ -447,15 +561,19 @@ func (d *debuggerwrapper) onTerminateThreadsRequest(request *dap.TerminateThread } func (d *debuggerwrapper) onEvaluateRequest(request *dap.EvaluateRequest , returnchan chan dap.Message, seq int) { - returnchan <- &dap.ErrorResponse{ + returnchan <- &dap.EvaluateResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: true, + }, + Body: dap.EvaluateResponseBody{ + Result: d.debugger.Eval(request.Arguments.Expression), + Type: "LVal", + // TODO support children }, } } @@ -505,7 +623,22 @@ func (d *debuggerwrapper) onExceptionInfoRequest(request *dap.ExceptionInfoReque } func (d *debuggerwrapper) onLoadedSourcesRequest(request *dap.LoadedSourcesRequest , returnchan chan dap.Message, seq int) { + returnchan <- &dap.LoadedSourcesResponse{ + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + Body: dap.LoadedSourcesResponseBody{ + Sources: []dap.Source{ + }, + }, + } } func (d *debuggerwrapper) onDataBreakpointInfoRequest(request *dap.DataBreakpointInfoRequest , returnchan chan dap.Message, seq int) { @@ -579,18 +712,6 @@ func (d *debuggerwrapper) onCancelRequest(request *dap.CancelRequest , returncha } func (d *debuggerwrapper) onBreakpointLocationsRequest(request *dap.BreakpointLocationsRequest , returnchan chan dap.Message, seq int) { - file := request.Arguments.Source.Name - breakpoints := make([]dap.BreakpointLocation, 0) - for _, v := range d.debugger.GetAllBreakpoints() { - if file == v.File { - breakpoints = append(breakpoints, dap.BreakpointLocation{ - Line: v.Line, - Column: 0, - EndLine: 0, - EndColumn: 0, - }) - } - } returnchan <- &dap.BreakpointLocationsResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -601,7 +722,32 @@ func (d *debuggerwrapper) onBreakpointLocationsRequest(request *dap.BreakpointLo Success: true, }, Body: dap.BreakpointLocationsResponseBody{ - Breakpoints: breakpoints, + Breakpoints: []dap.BreakpointLocation{ // TODO HANDLE COLUMNS + { + Line: request.Arguments.Line, + Column: 0, + EndLine: 0, + EndColumn: 0, + }, + }, + }, + } +} + +func (d *debuggerwrapper) onModulesRequest(request *dap.ModulesRequest, returnchan chan dap.Message, seq int) { + modules := d.debugger.GetModules() + returnchan <- &dap.ModulesResponse{ + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + RequestSeq: request.GetSeq(), + Success: true, + }, + Body: dap.ModulesResponseBody{ + Modules: modules, + TotalModules: len(modules), }, } } diff --git a/lisp/x/debugger/debugger.go b/lisp/x/debugger/debugger.go index e2a14e9..e3511f0 100644 --- a/lisp/x/debugger/debugger.go +++ b/lisp/x/debugger/debugger.go @@ -25,6 +25,7 @@ type Debugger struct { sync.Mutex enabled bool breakpoints map[int]*api.Breakpoint + env *lisp.LEnv runtime *lisp.Runtime lastModified time.Time stopped bool @@ -33,13 +34,13 @@ type Debugger struct { server DebugServer currentOp *op logger *logrus.Logger - pwd string + pwd string } type op struct { - name string + name string source *token.Location - args map[string]*lisp.LVal + args map[string]*lisp.LVal } type DebugServer interface { @@ -49,13 +50,14 @@ type DebugServer interface { } type DebugMode string + const DebugModeDelve = DebugMode("delve") const DebugModeDAP = DebugMode("dap") - -func NewDebugger(runtime *lisp.Runtime, address string, mode DebugMode) lisp.Profiler { +func NewDebugger(env *lisp.LEnv, address string, mode DebugMode) lisp.Profiler { db := &Debugger{ - runtime: runtime, + runtime: env.Runtime, + env: env, enabled: true, stopped: true, run: make(chan bool), @@ -73,10 +75,16 @@ func NewDebugger(runtime *lisp.Runtime, address string, mode DebugMode) lisp.Pro if err != nil { panic(err.Error()) } + go func(end chan bool) { + die := <- end + if die { + panic("This is the end... the end my friend") + } + }(srv.(*dapserver.Server).EndChannel) } srv.Run() db.server = srv - runtime.Profiler = db + db.runtime.Profiler = db return db } @@ -156,13 +164,18 @@ func (d *Debugger) Start(function *lisp.LVal) { args := make(map[string]*lisp.LVal) paramCounter := 0 fname = d.runtime.Package.FunNames[fname] - for _, v := range function.Cells { - args[d.runtime.Package.Symbols[fname].Cells[paramCounter].Str] = v + for k, v := range function.Cells { + argName := d.runtime.Package.Symbols[fname].Cells[paramCounter].String() + if argName == "" { + argName = strconv.Itoa(k) + } + d.logger.Infof("Arg %s %v is %v", argName, d.runtime.Package.Symbols[fname].Cells[paramCounter], mapValue(v)) + args[argName] = v } d.currentOp = &op{ - name: fname , + name: fname, source: source, - args: args, + args: args, } if !d.stopped { d.Lock() @@ -223,6 +236,28 @@ func (d *Debugger) GetBreakpointByName(name string) (*api.Breakpoint, error) { return nil, errors.New("not found") } +func (d *Debugger) GetModules() []dap.Module { + modules := make([]dap.Module, 0) + for k := range d.runtime.Registry.Packages { + modules = append(modules, dap.Module{ + Id: k, + Name: k, + }) + } + return modules +} + +func (d *Debugger) Eval(text string) string { + d.logger.Infof("Evaluating %s", text) + tEnv := lisp.NewEnv(nil) + tEnv.Runtime.Registry = d.runtime.Registry + tEnv.Runtime.Package = d.runtime.Package + tEnv.Runtime.Reader = d.runtime.Reader + tEnv.Parent = d.env + v := tEnv.LoadString("eval", text) + return mapValue(v) +} + func (d *Debugger) GetDapStacktrace() []dap.StackFrame { out := make([]dap.StackFrame, 0) for _, frame := range d.runtime.Stack.Frames { @@ -243,24 +278,40 @@ func (d *Debugger) GetDapStacktrace() []dap.StackFrame { origin = "internal module" } out = append(out, dap.StackFrame{ - Id: 0, - Name: frame.Name, - Source: dap.Source{ + Id: 0, + Name: frame.Name, + Source: dap.Source{ Name: source.File, Path: source.Path, PresentationHint: hint, Origin: origin, }, - Line: source.Line, - Column: 0, - ModuleId: frame.Package, - PresentationHint: hint, + Line: source.Line, + Column: source.Col, + ModuleId: frame.Package, + PresentationHint: hint, }) } - for i := len(out)/2-1; i >= 0; i-- { - opp := len(out)-1-i + for i := len(out)/2 - 1; i >= 0; i-- { + opp := len(out) - 1 - i out[i], out[opp] = out[opp], out[i] } + if len(out) == 0 { + out = append(out, dap.StackFrame{ + Id: 0, + Name: "Entrypoint", + Source: dap.Source{ + Name: "Native code", + Path: "", + PresentationHint: "deemphasize", + Origin: "internal module", + }, + Line: 0, + Column: 0, + ModuleId: "internal", + PresentationHint: "deemphasize", + }) + } return out } @@ -292,10 +343,10 @@ func (d *Debugger) GetStacktrace(st *rpc2.StacktraceOut) { }, PCs: []uint64{}, }, - Locals: []api.Variable{ + Locals: []api.Variable{ }, - Arguments: []api.Variable{ + Arguments: []api.Variable{ }, FrameOffset: 0, @@ -315,6 +366,7 @@ func (d *Debugger) GetAllBreakpoints() map[int]*api.Breakpoint { } func (d *Debugger) CreateBreakpoint(breakpoint *api.Breakpoint) *api.Breakpoint { + // TODO ADD COLUMNS d.Lock() defer d.Unlock() d.lastModified = time.Now() @@ -374,12 +426,12 @@ func (d *Debugger) GetThread() *api.Thread { GoType: 0, Optimized: false, }, - ID: 1, + ID: 1, GoroutineID: 1, } } else { loc = &api.Thread{ - ID: 1, + ID: 1, GoroutineID: 1, } } @@ -453,6 +505,14 @@ func mapKind(in *lisp.LVal) reflect.Kind { } } +func sexprAsString(in *lisp.LVal) string { + out := "" + for _, v := range in.Cells { + out += mapValue(v) + " " + } + return out +} + func mapValue(in *lisp.LVal) string { switch in.Type { case lisp.LFloat: @@ -463,8 +523,10 @@ func mapValue(in *lisp.LVal) string { return in.Str case lisp.LQSymbol: return in.Str - case lisp.LSExpr: + case lisp.LSymbol: return in.Str + case lisp.LSExpr: + return sexprAsString(in) case lisp.LSortMap: return "map" case lisp.LNative: @@ -482,7 +544,7 @@ func mapValue(in *lisp.LVal) string { case lisp.LInvalid: return "INVALID" default: - return "UNKNOWN" + return in.Type.String() } } @@ -492,28 +554,28 @@ func extractChildren(in *lisp.LVal) []api.Variable { case lisp.LSortMap: for _, v := range in.Cells[:1] { children = append(children, api.Variable{ - Type: mapLispType(v.Type), + Type: mapLispType(v.Type), RealType: mapLispType(v.Type), - Value: mapValue(v), - Kind: mapKind(v), + Value: mapValue(v), + Kind: mapKind(v), }) } case lisp.LArray: for _, v := range in.Cells[:1] { children = append(children, api.Variable{ - Type: mapLispType(v.Type), + Type: mapLispType(v.Type), RealType: mapLispType(v.Type), - Value: mapValue(v), - Kind: mapKind(v), + Value: mapValue(v), + Kind: mapKind(v), }) } case lisp.LBytes: for _, v := range in.Bytes() { children = append(children, api.Variable{ - Type: "byte", + Type: "byte", RealType: "byte", - Value: string(v), - Kind: reflect.Uint8, + Value: string(v), + Kind: reflect.Uint8, }) } } @@ -542,7 +604,7 @@ func (d *Debugger) GetVariables() []api.Variable { Col: 0, } } - children := make([]api.Variable,0) + children := make([]api.Variable, 0) strVal := mapValue(v) if strVal == "map" || strVal == "array" { children = extractChildren(v) @@ -659,7 +721,7 @@ func (d *Debugger) Sources(filter string) ([]string, error) { for k, _ := range intermediate { out = append(out, k) } - return out, nil + return out, nil } func (d *Debugger) getSourcesForPackage(currPkg *lisp.Package) []string { @@ -672,6 +734,21 @@ func (d *Debugger) getSourcesForPackage(currPkg *lisp.Package) []string { return out } +func (d *Debugger) GetArguments() []dap.Variable { + out := make([]dap.Variable, 0) + for k, v := range d.currentOp.args { + out = append(out, dap.Variable{ + Name: k, + Value: mapValue(v), + Type: mapLispType(v.Type), + + }) + d.logger.Infof("Sending arg %s as %s", out[len(out)-1].Name, out[len(out) - 1].Value) + } + d.logger.Infof("Args: %v", out) + return out +} + func (d *Debugger) appendSourcesToMap(intermediate map[string]bool, currPkg *lisp.Package, filter string) { for _, sym := range currPkg.Symbols { if sym.Source == nil { @@ -781,7 +858,7 @@ func (d *Debugger) Command(a *api.DebuggerCommand) (*api.DebuggerState, error) { case api.SwitchThread: return nil, errors.New("Not implemented") } - state, err := d.State(false) + state, err := d.State(false) if err != nil && started == true { state.Running = true } diff --git a/lisp/x/debugger/debugger_test.go b/lisp/x/debugger/debugger_test.go index f2b57a8..75cf8fc 100644 --- a/lisp/x/debugger/debugger_test.go +++ b/lisp/x/debugger/debugger_test.go @@ -16,7 +16,7 @@ func TestNewDebugger(t *testing.T) { t.Fatal(lisp.GoError(lerr)) } // Create a profiler - profiler := NewDebugger(env.Runtime, ":8883", DebugModeDelve) + profiler := NewDebugger(env, ":8883", DebugModeDelve) profiler.(*Debugger).CreateBreakpoint(&api.Breakpoint{ ID: 1, Name: "REUBEN TEST", diff --git a/lisp/x/debugger/delveserver/server_debugger.go b/lisp/x/debugger/delveserver/server_debugger.go index 58f40c5..bea2375 100644 --- a/lisp/x/debugger/delveserver/server_debugger.go +++ b/lisp/x/debugger/delveserver/server_debugger.go @@ -13,7 +13,7 @@ type ServerDebugger interface { GetStacktrace(st *rpc2.StacktraceOut) GetDapStacktrace() []dap.StackFrame GetAllBreakpoints() map[int]*api.Breakpoint - CreateBreakpoint(breakpoint *api.Breakpoint) *api.Breakpoint + CreateBreakpoint(breakpoint *api.Breakpoint) *api.Breakpoint RemoveBreakpoint(id int) error AmendBreakpoint(bp *api.Breakpoint) error GetThread() *api.Thread @@ -21,7 +21,7 @@ type ServerDebugger interface { GetFunctionArgs() []api.Variable SetVariableInScope(scope api.EvalScope, symbol string, value string) error Sources(filter string) ([]string, error) - Functions(filter string) ([]string, error) + Functions(filter string) ([]string, error) FindLocation(scope api.EvalScope, loc string, lines bool) ([]api.Location, error) ListPackagesBuildInfo(files bool) []api.PackageBuildInfo State(blocking bool) (*api.DebuggerState, error) @@ -33,5 +33,7 @@ type ServerDebugger interface { Continue() Halt() IsStopped() bool + GetModules() []dap.Module + Eval(text string) string + GetArguments() []dap.Variable } - From 8f3dc413390fa507d1afd7f746fcde3584340c5e Mon Sep 17 00:00:00 2001 From: "reuben.thompson@acresoftware.com" Date: Fri, 24 Jul 2020 11:41:38 +0100 Subject: [PATCH 6/9] Working stack, vars, args and eval --- cmd/run.go | 6 +- lisp/env.go | 6 +- lisp/package.go | 2 +- lisp/profiler.go | 13 + lisp/runtime.go | 1 + lisp/x/debugger/dapserver/debuggerwrapper.go | 278 +++++++++---------- lisp/x/debugger/debugger.go | 221 ++++++++++----- lisp/x/debugger/debugger_test.go | 12 +- lisp/x/debugger/events/events.go | 1 + lisp/x/profiler/callgrind.go | 2 +- lisp/x/profiler/go_annotator.go | 2 +- lisp/x/profiler/opencensus_annotator.go | 2 +- lisp/x/profiler/shared.go | 2 +- 13 files changed, 330 insertions(+), 218 deletions(-) diff --git a/cmd/run.go b/cmd/run.go index 90971c4..ddf1fb7 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -53,7 +53,7 @@ var runCmd = &cobra.Command{ os.Exit(1) } - var profiler lisp.Profiler + var debugInst lisp.Debugger if runDebugger { var mode debugger.DebugMode switch debugger.DebugMode(runDebuggerType) { @@ -65,7 +65,7 @@ var runCmd = &cobra.Command{ fmt.Fprintln(os.Stderr, "Invalid debugger. Specify delve or dap") os.Exit(1) } - profiler = debugger.NewDebugger(env, fmt.Sprintf(":%d", runDebuggerPort), mode) + debugInst = debugger.NewDebugger(env, fmt.Sprintf(":%d", runDebuggerPort), mode) } for i := range args { res := env.LoadFile(args[i]) @@ -75,7 +75,7 @@ var runCmd = &cobra.Command{ } } if runDebugger { - profiler.Complete() + debugInst.Complete() } }, } diff --git a/lisp/env.go b/lisp/env.go index 0f3af99..9e2261c 100644 --- a/lisp/env.go +++ b/lisp/env.go @@ -767,10 +767,14 @@ func (env *LEnv) EvalSExpr(s *LVal) *LVal { fun := call.Cells[0] // call is not an empty expression -- fun is known LFun args := call args.Cells = args.Cells[1:] - if (env.Runtime.Profiler != nil) { + if env.Runtime.Profiler != nil { env.Runtime.Profiler.Start(fun) defer env.Runtime.Profiler.End(fun) } + if env.Runtime.Debugger != nil { + env.Runtime.Debugger.Start(s, fun) + defer env.Runtime.Debugger.End(fun) + } switch fun.FunType { case LFunNone: return env.FunCall(fun, args) diff --git a/lisp/package.go b/lisp/package.go index b7dc78d..087f06d 100644 --- a/lisp/package.go +++ b/lisp/package.go @@ -145,4 +145,4 @@ func (pkg *Package) put(k, v *LVal) { pkg.FunNames[v.FID()] = k.Str } pkg.Symbols[k.Str] = v -} \ No newline at end of file +} diff --git a/lisp/profiler.go b/lisp/profiler.go index a50cf2e..a80948d 100644 --- a/lisp/profiler.go +++ b/lisp/profiler.go @@ -18,3 +18,16 @@ type Profiler interface { End(function *LVal) } +// Interface for a debugger +type Debugger interface { + // Is the profiler enabled? + IsEnabled() bool + // End the session and output summary lines + Complete() error + // Is done + Done() bool + // Marks the start of a process + Start(expr *LVal, function *LVal) + // Marks the end of a process + End(function *LVal) +} diff --git a/lisp/runtime.go b/lisp/runtime.go index 70dfd6b..0ef0ba1 100644 --- a/lisp/runtime.go +++ b/lisp/runtime.go @@ -20,6 +20,7 @@ type Runtime struct { Reader Reader Library SourceLibrary Profiler Profiler + Debugger Debugger numenv atomicCounter numsym atomicCounter } diff --git a/lisp/x/debugger/dapserver/debuggerwrapper.go b/lisp/x/debugger/dapserver/debuggerwrapper.go index 06ff90e..1513c64 100644 --- a/lisp/x/debugger/dapserver/debuggerwrapper.go +++ b/lisp/x/debugger/dapserver/debuggerwrapper.go @@ -9,7 +9,6 @@ import ( "math/rand" ) - type debuggerwrapper struct { debugger delveserver.ServerDebugger } @@ -17,14 +16,14 @@ type debuggerwrapper struct { func (d *debuggerwrapper) onInitializeRequest(request *dap.InitializeRequest, handler *connection) { handler.queue <- &dap.InitializeResponse{ Response: dap.Response{ - Success: true, + Success: true, RequestSeq: request.Seq, ProtocolMessage: dap.ProtocolMessage{ - Seq: handler.s.incSequence(), + Seq: handler.s.incSequence(), Type: "response", }, }, - Body: dap.Capabilities{ + Body: dap.Capabilities{ SupportsConfigurationDoneRequest: true, SupportsFunctionBreakpoints: true, SupportsConditionalBreakpoints: false, @@ -60,7 +59,7 @@ func (d *debuggerwrapper) onInitializeRequest(request *dap.InitializeRequest, ha } } -func (d *debuggerwrapper) onLaunchRequest(request *dap.LaunchRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onLaunchRequest(request *dap.LaunchRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.LaunchResponse{ Response: dap.Response{ RequestSeq: request.GetSeq(), @@ -74,12 +73,12 @@ func (d *debuggerwrapper) onLaunchRequest(request *dap.LaunchRequest , returncha } } -func (d *debuggerwrapper) onAttachRequest(request *dap.AttachRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onAttachRequest(request *dap.AttachRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", @@ -88,7 +87,7 @@ func (d *debuggerwrapper) onAttachRequest(request *dap.AttachRequest , returncha } } -func (d *debuggerwrapper) onDisconnectRequest(request *dap.DisconnectRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onDisconnectRequest(request *dap.DisconnectRequest, returnchan chan dap.Message, seq int) { if request.Arguments.TerminateDebuggee { d.debugger.Complete() panic("DONE") @@ -100,13 +99,13 @@ func (d *debuggerwrapper) onDisconnectRequest(request *dap.DisconnectRequest , r Type: "response", }, RequestSeq: request.GetSeq(), - Success: true, - Message: "Complete", + Success: true, + Message: "Complete", }, } } -func (d *debuggerwrapper) onTerminateRequest(request *dap.TerminateRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onTerminateRequest(request *dap.TerminateRequest, returnchan chan dap.Message, seq int) { d.debugger.Complete() returnchan <- &dap.TerminateResponse{ Response: dap.Response{ @@ -115,14 +114,14 @@ func (d *debuggerwrapper) onTerminateRequest(request *dap.TerminateRequest , ret Type: "response", }, RequestSeq: request.GetSeq(), - Success: true, - Message: "Complete", + Success: true, + Message: "Complete", }, } panic("DONE") } -func (d *debuggerwrapper) onRestartRequest(request *dap.RestartRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onRestartRequest(request *dap.RestartRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -130,13 +129,13 @@ func (d *debuggerwrapper) onRestartRequest(request *dap.RestartRequest , returnc Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onSetBreakpointsRequest(request *dap.SetBreakpointsRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onSetBreakpointsRequest(request *dap.SetBreakpointsRequest, returnchan chan dap.Message, seq int) { if request.Arguments.SourceModified { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ @@ -145,13 +144,13 @@ func (d *debuggerwrapper) onSetBreakpointsRequest(request *dap.SetBreakpointsReq Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Cannot modify source whilst running", + Success: false, + Message: "Cannot modify source whilst running", }, } return } - out:= make([]dap.Breakpoint, 0) + out := make([]dap.Breakpoint, 0) for _, v := range request.Arguments.Breakpoints { id := rand.Int() d.debugger.CreateBreakpoint(&api.Breakpoint{ @@ -170,10 +169,10 @@ func (d *debuggerwrapper) onSetBreakpointsRequest(request *dap.SetBreakpointsReq TotalHitCount: 0, }) out = append(out, dap.Breakpoint{ - Id: id, - Line: v.Line, - Column: 0, - Source: request.Arguments.Source, + Id: id, + Line: v.Line, + Column: 0, + Source: request.Arguments.Source, Verified: false, }) } @@ -183,14 +182,14 @@ func (d *debuggerwrapper) onSetBreakpointsRequest(request *dap.SetBreakpointsReq Seq: seq, Type: "response", }, - Success: true, + Success: true, RequestSeq: request.GetSeq(), }, - Body: dap.SetBreakpointsResponseBody{Breakpoints: out}, + Body: dap.SetBreakpointsResponseBody{Breakpoints: out}, } } -func (d *debuggerwrapper) onSetFunctionBreakpointsRequest(request *dap.SetFunctionBreakpointsRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onSetFunctionBreakpointsRequest(request *dap.SetFunctionBreakpointsRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -198,13 +197,13 @@ func (d *debuggerwrapper) onSetFunctionBreakpointsRequest(request *dap.SetFuncti Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onSetExceptionBreakpointsRequest(request *dap.SetExceptionBreakpointsRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onSetExceptionBreakpointsRequest(request *dap.SetExceptionBreakpointsRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -212,13 +211,13 @@ func (d *debuggerwrapper) onSetExceptionBreakpointsRequest(request *dap.SetExcep Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onConfigurationDoneRequest(request *dap.ConfigurationDoneRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onConfigurationDoneRequest(request *dap.ConfigurationDoneRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ConfigurationDoneResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -226,12 +225,12 @@ func (d *debuggerwrapper) onConfigurationDoneRequest(request *dap.ConfigurationD Type: "response", }, RequestSeq: request.GetSeq(), - Success: true, + Success: true, }, } } -func (d *debuggerwrapper) onContinueRequest(request *dap.ContinueRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onContinueRequest(request *dap.ContinueRequest, returnchan chan dap.Message, seq int) { d.debugger.Continue() returnchan <- &dap.ContinueResponse{ Response: dap.Response{ @@ -239,16 +238,16 @@ func (d *debuggerwrapper) onContinueRequest(request *dap.ContinueRequest , retur Seq: seq, Type: "response", }, - Success: true, + Success: true, RequestSeq: request.GetSeq(), }, - Body: dap.ContinueResponseBody{ + Body: dap.ContinueResponseBody{ AllThreadsContinued: true, }, } } -func (d *debuggerwrapper) onNextRequest(request *dap.NextRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onNextRequest(request *dap.NextRequest, returnchan chan dap.Message, seq int) { d.debugger.Step() returnchan <- &dap.NextResponse{ Response: dap.Response{ @@ -256,13 +255,13 @@ func (d *debuggerwrapper) onNextRequest(request *dap.NextRequest , returnchan ch Seq: seq, Type: "response", }, - Success: true, + Success: true, RequestSeq: request.GetSeq(), }, } } -func (d *debuggerwrapper) onStepInRequest(request *dap.StepInRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onStepInRequest(request *dap.StepInRequest, returnchan chan dap.Message, seq int) { d.debugger.Step() returnchan <- &dap.StepInResponse{ Response: dap.Response{ @@ -270,13 +269,13 @@ func (d *debuggerwrapper) onStepInRequest(request *dap.StepInRequest , returncha Seq: seq, Type: "response", }, - Success: true, + Success: true, RequestSeq: request.GetSeq(), }, } } -func (d *debuggerwrapper) onStepOutRequest(request *dap.StepOutRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onStepOutRequest(request *dap.StepOutRequest, returnchan chan dap.Message, seq int) { d.debugger.Step() returnchan <- &dap.StepOutResponse{ Response: dap.Response{ @@ -284,13 +283,13 @@ func (d *debuggerwrapper) onStepOutRequest(request *dap.StepOutRequest , returnc Seq: seq, Type: "response", }, - Success: true, + Success: true, RequestSeq: request.GetSeq(), }, } } -func (d *debuggerwrapper) onStepBackRequest(request *dap.StepBackRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onStepBackRequest(request *dap.StepBackRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -298,13 +297,13 @@ func (d *debuggerwrapper) onStepBackRequest(request *dap.StepBackRequest , retur Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onReverseContinueRequest(request *dap.ReverseContinueRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onReverseContinueRequest(request *dap.ReverseContinueRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -312,13 +311,13 @@ func (d *debuggerwrapper) onReverseContinueRequest(request *dap.ReverseContinueR Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onRestartFrameRequest(request *dap.RestartFrameRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onRestartFrameRequest(request *dap.RestartFrameRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -326,13 +325,13 @@ func (d *debuggerwrapper) onRestartFrameRequest(request *dap.RestartFrameRequest Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onGotoRequest(request *dap.GotoRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onGotoRequest(request *dap.GotoRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -340,13 +339,13 @@ func (d *debuggerwrapper) onGotoRequest(request *dap.GotoRequest , returnchan ch Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onPauseRequest(request *dap.PauseRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onPauseRequest(request *dap.PauseRequest, returnchan chan dap.Message, seq int) { d.debugger.Halt() returnchan <- &dap.PauseResponse{ Response: dap.Response{ @@ -354,13 +353,13 @@ func (d *debuggerwrapper) onPauseRequest(request *dap.PauseRequest , returnchan Seq: seq, Type: "response", }, - Success: true, + Success: true, RequestSeq: request.GetSeq(), }, } } -func (d *debuggerwrapper) onStackTraceRequest(request *dap.StackTraceRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onStackTraceRequest(request *dap.StackTraceRequest, returnchan chan dap.Message, seq int) { st := &rpc2.StacktraceOut{} d.debugger.GetStacktrace(st) out := d.debugger.GetDapStacktrace() @@ -370,65 +369,64 @@ func (d *debuggerwrapper) onStackTraceRequest(request *dap.StackTraceRequest , r Seq: seq, Type: "response", }, - Success: true, + Success: true, RequestSeq: request.Seq, }, - Body: dap.StackTraceResponseBody{ + Body: dap.StackTraceResponseBody{ StackFrames: out, TotalFrames: len(out), }, } } -func (d *debuggerwrapper) onScopesRequest(request *dap.ScopesRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onScopesRequest(request *dap.ScopesRequest, returnchan chan dap.Message, seq int) { source := d.debugger.GetDapStacktrace()[0] returnchan <- &dap.ScopesResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ - Seq: seq, + Seq: seq, Type: "response", }, - RequestSeq: request.GetSeq(), - Success: true, + RequestSeq: request.GetSeq(), + Success: true, }, Body: dap.ScopesResponseBody{ Scopes: []dap.Scope{ { VariablesReference: 2, - PresentationHint: "locals", - Name: "Scope", + PresentationHint: "locals", + Name: "Scope", }, { Name: "Arguments", PresentationHint: "locals", VariablesReference: 1, Expensive: false, - Source: source.Source, + Source: source.Source, Line: source.Line, Column: source.Column, EndLine: source.EndLine, EndColumn: source.EndColumn, }, - }, }, } } -func (d *debuggerwrapper) onVariablesRequest(request *dap.VariablesRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onVariablesRequest(request *dap.VariablesRequest, returnchan chan dap.Message, seq int) { variables := make([]dap.Variable, 0) if request.Arguments.VariablesReference == 1 { variables := d.debugger.GetArguments() returnchan <- &dap.VariablesResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ - Seq: seq, + Seq: seq, Type: "response", }, - RequestSeq: request.GetSeq(), - Success: true, + RequestSeq: request.GetSeq(), + Success: true, }, - Body: dap.VariablesResponseBody{ + Body: dap.VariablesResponseBody{ Variables: variables, }, } @@ -437,11 +435,11 @@ func (d *debuggerwrapper) onVariablesRequest(request *dap.VariablesRequest , ret for _, vari := range d.debugger.GetVariables() { outVar := dap.Variable{ - Name: vari.Name, - Value: vari.Value, - Type: vari.Type, - PresentationHint: dap.VariablePresentationHint{ - Kind: "data", + Name: vari.Name, + Value: vari.Value, + Type: vari.Type, + PresentationHint: dap.VariablePresentationHint{ + Kind: "data", Visibility: "public", Attributes: []string{}, }, @@ -452,32 +450,32 @@ func (d *debuggerwrapper) onVariablesRequest(request *dap.VariablesRequest , ret returnchan <- &dap.VariablesResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ - Seq: seq, + Seq: seq, Type: "response", }, - RequestSeq: request.GetSeq(), - Success: true, + RequestSeq: request.GetSeq(), + Success: true, }, - Body: dap.VariablesResponseBody{ + Body: dap.VariablesResponseBody{ Variables: variables, }, } } -func (d *debuggerwrapper) onSetVariableRequest(request *dap.SetVariableRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onSetVariableRequest(request *dap.SetVariableRequest, returnchan chan dap.Message, seq int) { err := d.debugger.SetVariableInScope(api.EvalScope{}, request.Arguments.Name, request.Arguments.Value) if err != nil { returnchan <- &dap.SetVariableResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ - Seq: seq, + Seq: seq, Type: "response", }, - RequestSeq: request.GetSeq(), - Success: false, - Message: err.Error(), + RequestSeq: request.GetSeq(), + Success: false, + Message: err.Error(), }, - Body: dap.SetVariableResponseBody{}, + Body: dap.SetVariableResponseBody{}, } return } @@ -492,12 +490,12 @@ func (d *debuggerwrapper) onSetVariableRequest(request *dap.SetVariableRequest , }, Body: dap.SetVariableResponseBody{ Value: request.Arguments.Value, - Type: "LVal", + Type: "LVal", }, } } -func (d *debuggerwrapper) onSetExpressionRequest(request *dap.SetExpressionRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onSetExpressionRequest(request *dap.SetExpressionRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -505,13 +503,13 @@ func (d *debuggerwrapper) onSetExpressionRequest(request *dap.SetExpressionReque Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onSourceRequest(request *dap.SourceRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onSourceRequest(request *dap.SourceRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -519,13 +517,13 @@ func (d *debuggerwrapper) onSourceRequest(request *dap.SourceRequest , returncha Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onThreadsRequest(request *dap.ThreadsRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onThreadsRequest(request *dap.ThreadsRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ThreadsResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -533,12 +531,12 @@ func (d *debuggerwrapper) onThreadsRequest(request *dap.ThreadsRequest , returnc Type: "response", }, RequestSeq: request.GetSeq(), - Success: true, + Success: true, }, Body: dap.ThreadsResponseBody{ Threads: []dap.Thread{ { - Id: 1, + Id: 1, Name: "Execution thread", }, }, @@ -546,7 +544,7 @@ func (d *debuggerwrapper) onThreadsRequest(request *dap.ThreadsRequest , returnc } } -func (d *debuggerwrapper) onTerminateThreadsRequest(request *dap.TerminateThreadsRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onTerminateThreadsRequest(request *dap.TerminateThreadsRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -554,13 +552,13 @@ func (d *debuggerwrapper) onTerminateThreadsRequest(request *dap.TerminateThread Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onEvaluateRequest(request *dap.EvaluateRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onEvaluateRequest(request *dap.EvaluateRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.EvaluateResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -568,19 +566,20 @@ func (d *debuggerwrapper) onEvaluateRequest(request *dap.EvaluateRequest , retur Type: "response", }, RequestSeq: request.GetSeq(), - Success: true, + Success: true, }, Body: dap.EvaluateResponseBody{ Result: d.debugger.Eval(request.Arguments.Expression), - Type: "LVal", + Type: "LVal", // TODO support children }, } } -func (d *debuggerwrapper) onStepInTargetsRequest(request *dap.StepInTargetsRequest , returnchan chan dap.Message) {} +func (d *debuggerwrapper) onStepInTargetsRequest(request *dap.StepInTargetsRequest, returnchan chan dap.Message) { +} -func (d *debuggerwrapper) onGotoTargetsRequest(request *dap.GotoTargetsRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onGotoTargetsRequest(request *dap.GotoTargetsRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -588,13 +587,13 @@ func (d *debuggerwrapper) onGotoTargetsRequest(request *dap.GotoTargetsRequest , Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onCompletionsRequest(request *dap.CompletionsRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onCompletionsRequest(request *dap.CompletionsRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -602,13 +601,13 @@ func (d *debuggerwrapper) onCompletionsRequest(request *dap.CompletionsRequest , Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onExceptionInfoRequest(request *dap.ExceptionInfoRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onExceptionInfoRequest(request *dap.ExceptionInfoRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -616,13 +615,13 @@ func (d *debuggerwrapper) onExceptionInfoRequest(request *dap.ExceptionInfoReque Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onLoadedSourcesRequest(request *dap.LoadedSourcesRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onLoadedSourcesRequest(request *dap.LoadedSourcesRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.LoadedSourcesResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -630,10 +629,10 @@ func (d *debuggerwrapper) onLoadedSourcesRequest(request *dap.LoadedSourcesReque Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, - Body: dap.LoadedSourcesResponseBody{ + Body: dap.LoadedSourcesResponseBody{ Sources: []dap.Source{ }, @@ -641,7 +640,7 @@ func (d *debuggerwrapper) onLoadedSourcesRequest(request *dap.LoadedSourcesReque } } -func (d *debuggerwrapper) onDataBreakpointInfoRequest(request *dap.DataBreakpointInfoRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onDataBreakpointInfoRequest(request *dap.DataBreakpointInfoRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -649,13 +648,13 @@ func (d *debuggerwrapper) onDataBreakpointInfoRequest(request *dap.DataBreakpoin Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onSetDataBreakpointsRequest(request *dap.SetDataBreakpointsRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onSetDataBreakpointsRequest(request *dap.SetDataBreakpointsRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -663,13 +662,13 @@ func (d *debuggerwrapper) onSetDataBreakpointsRequest(request *dap.SetDataBreakp Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onReadMemoryRequest(request *dap.ReadMemoryRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onReadMemoryRequest(request *dap.ReadMemoryRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -677,13 +676,13 @@ func (d *debuggerwrapper) onReadMemoryRequest(request *dap.ReadMemoryRequest , r Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onDisassembleRequest(request *dap.DisassembleRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onDisassembleRequest(request *dap.DisassembleRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -691,13 +690,13 @@ func (d *debuggerwrapper) onDisassembleRequest(request *dap.DisassembleRequest , Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onCancelRequest(request *dap.CancelRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onCancelRequest(request *dap.CancelRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -705,13 +704,13 @@ func (d *debuggerwrapper) onCancelRequest(request *dap.CancelRequest , returncha Type: "response", }, RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: false, + Message: "Not supported", }, } } -func (d *debuggerwrapper) onBreakpointLocationsRequest(request *dap.BreakpointLocationsRequest , returnchan chan dap.Message, seq int) { +func (d *debuggerwrapper) onBreakpointLocationsRequest(request *dap.BreakpointLocationsRequest, returnchan chan dap.Message, seq int) { returnchan <- &dap.BreakpointLocationsResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -719,7 +718,7 @@ func (d *debuggerwrapper) onBreakpointLocationsRequest(request *dap.BreakpointLo Type: "response", }, RequestSeq: request.GetSeq(), - Success: true, + Success: true, }, Body: dap.BreakpointLocationsResponseBody{ Breakpoints: []dap.BreakpointLocation{ // TODO HANDLE COLUMNS @@ -743,12 +742,11 @@ func (d *debuggerwrapper) onModulesRequest(request *dap.ModulesRequest, returnch Type: "response", }, RequestSeq: request.GetSeq(), - Success: true, + Success: true, }, - Body: dap.ModulesResponseBody{ - Modules: modules, + Body: dap.ModulesResponseBody{ + Modules: modules, TotalModules: len(modules), }, } } - diff --git a/lisp/x/debugger/debugger.go b/lisp/x/debugger/debugger.go index e3511f0..3ec7802 100644 --- a/lisp/x/debugger/debugger.go +++ b/lisp/x/debugger/debugger.go @@ -9,6 +9,7 @@ import ( "github.com/luthersystems/elps/lisp/x/debugger/dapserver" "github.com/luthersystems/elps/lisp/x/debugger/delveserver" "github.com/luthersystems/elps/lisp/x/debugger/events" + "github.com/luthersystems/elps/lisp/x/profiler" "github.com/luthersystems/elps/parser/token" "github.com/sirupsen/logrus" "math/rand" @@ -35,6 +36,12 @@ type Debugger struct { currentOp *op logger *logrus.Logger pwd string + callRefs *callRef + isEvaling bool +} + +func (d *Debugger) Done() bool { + panic("implement me") } type op struct { @@ -43,6 +50,7 @@ type op struct { args map[string]*lisp.LVal } + type DebugServer interface { Run() error Stop() error @@ -54,7 +62,7 @@ type DebugMode string const DebugModeDelve = DebugMode("delve") const DebugModeDAP = DebugMode("dap") -func NewDebugger(env *lisp.LEnv, address string, mode DebugMode) lisp.Profiler { +func NewDebugger(env *lisp.LEnv, address string, mode DebugMode) lisp.Debugger { db := &Debugger{ runtime: env.Runtime, env: env, @@ -76,7 +84,7 @@ func NewDebugger(env *lisp.LEnv, address string, mode DebugMode) lisp.Profiler { panic(err.Error()) } go func(end chan bool) { - die := <- end + die := <-end if die { panic("This is the end... the end my friend") } @@ -84,7 +92,7 @@ func NewDebugger(env *lisp.LEnv, address string, mode DebugMode) lisp.Profiler { } srv.Run() db.server = srv - db.runtime.Profiler = db + db.runtime.Debugger = db return db } @@ -122,13 +130,15 @@ func (d *Debugger) Complete() error { return nil } -func (d *Debugger) Start(function *lisp.LVal) { - // do we need to do this on SExpr rather than function? Would that make finding the parameters easier? +func (d *Debugger) Start(expr *lisp.LVal, function *lisp.LVal) { + if d.isEvaling { + return + } fname := "" if function.FunData() != nil { fname = function.FunData().FID } - source := function.Source + source := expr.Source if source == nil || source.File == "" { for f := len(d.runtime.Stack.Frames) - 1; f >= 0; f-- { frame := d.runtime.Stack.Frames[f] @@ -164,28 +174,58 @@ func (d *Debugger) Start(function *lisp.LVal) { args := make(map[string]*lisp.LVal) paramCounter := 0 fname = d.runtime.Package.FunNames[fname] - for k, v := range function.Cells { - argName := d.runtime.Package.Symbols[fname].Cells[paramCounter].String() - if argName == "" { - argName = strconv.Itoa(k) + if function.Type == lisp.LFun { + argList := function.Cells[0] + restMode := false + for count, argName := range argList.Cells { + if argName.Str == lisp.VarArgSymbol { + restMode = true + continue + } + if restMode { + for pos := count; pos < len(expr.Cells); pos++ { + args[fmt.Sprintf("%s[%d]", argName.Str, pos)] = expr.Cells[pos] + } + break + } + val := &lisp.LVal{ + Type: lisp.LInvalid, + } + if len(expr.Cells) >= count { + val = expr.Cells[count+1] + } + args[argName.Str] = val + } + } else { + for k, v := range function.Cells { + argName := d.runtime.Package.Symbols[fname].Cells[paramCounter].String() + if argName == "" { + argName = strconv.Itoa(k) + } + d.logger.Infof("Arg %s %v is %v", argName, d.runtime.Package.Symbols[fname].Cells[paramCounter], mapValue(v)) + if cell := expr.Cells[k+1]; cell != nil { + args[argName] = cell + } else { + args[argName] = v + } } - d.logger.Infof("Arg %s %v is %v", argName, d.runtime.Package.Symbols[fname].Cells[paramCounter], mapValue(v)) - args[argName] = v } d.currentOp = &op{ name: fname, source: source, args: args, } + d.incrementCallRef(expr, function) if !d.stopped { d.Lock() if function.Source != nil { for _, v := range d.breakpoints { - if (v.File == function.Source.File || fmt.Sprintf("%s/%s", d.pwd, function.Source.File) == v.File) && v.Line == function.Source.Line { + if (v.File == function.Source.File || fmt.Sprintf("%s/%s", d.pwd, function.Source.File) == v.File) && + v.Line == function.Source.Line { d.logger.Infof("BREAKPOINT") d.stopped = true d.Unlock() - d.Start(function) + d.Start(expr, function) d.server.Event(events.EventTypeStoppedBreakpoint) return } @@ -210,8 +250,19 @@ func (d *Debugger) Start(function *lisp.LVal) { runtime.Gosched() } +func (p *Debugger) getFunctionParameters(function *lisp.LVal) (string, string) { + return profiler.GetFunNameFromFID(p.runtime, function.FunData().FID), function.FunData().Package +} + func (d *Debugger) End(function *lisp.LVal) { + if d.isEvaling { + return + } d.logger.Infof("End %v ", function.FunData().FID) + switch function.Type { + case lisp.LFun, lisp.LSymbol, lisp.LSExpr, lisp.LQSymbol: + d.decrementCallRef() + } // no op for now except that we yield the CPU runtime.Gosched() } @@ -248,69 +299,51 @@ func (d *Debugger) GetModules() []dap.Module { } func (d *Debugger) Eval(text string) string { + d.Lock() + d.isEvaling = true + defer func() { + d.isEvaling = false + d.Unlock() + }() d.logger.Infof("Evaluating %s", text) tEnv := lisp.NewEnv(nil) tEnv.Runtime.Registry = d.runtime.Registry tEnv.Runtime.Package = d.runtime.Package tEnv.Runtime.Reader = d.runtime.Reader - tEnv.Parent = d.env + tEnv.Runtime.Debugger = nil v := tEnv.LoadString("eval", text) return mapValue(v) } func (d *Debugger) GetDapStacktrace() []dap.StackFrame { + current := d.callRefs out := make([]dap.StackFrame, 0) - for _, frame := range d.runtime.Stack.Frames { - var source = frame.Source - if source == nil { - source = &token.Location{ - File: "Unknown file", - Path: "", - Pos: 0, - Line: 0, - Col: 0, - } + for { + if current == nil { + break } + d.logger.Infof("Line %s in %s line %d col %d", current.name, current.file, current.line, current.col) hint := "normal" origin := "" - if source.File == "Unknown file" || source.File == "" { + if current.file == "Unknown file" || current.file == "" { hint = "deemphasize" origin = "internal module" } out = append(out, dap.StackFrame{ Id: 0, - Name: frame.Name, + Name: current.name, Source: dap.Source{ - Name: source.File, - Path: source.Path, + Name: current.file, + Path: current.path, PresentationHint: hint, Origin: origin, }, - Line: source.Line, - Column: source.Col, - ModuleId: frame.Package, + Line: current.line, + Column: current.col, + ModuleId: current.packageName, PresentationHint: hint, }) - } - for i := len(out)/2 - 1; i >= 0; i-- { - opp := len(out) - 1 - i - out[i], out[opp] = out[opp], out[i] - } - if len(out) == 0 { - out = append(out, dap.StackFrame{ - Id: 0, - Name: "Entrypoint", - Source: dap.Source{ - Name: "Native code", - Path: "", - PresentationHint: "deemphasize", - Origin: "internal module", - }, - Line: 0, - Column: 0, - ModuleId: "internal", - PresentationHint: "deemphasize", - }) + current = current.prev } return out } @@ -319,7 +352,7 @@ func (d *Debugger) GetStacktrace(st *rpc2.StacktraceOut) { d.logger.Info("Returning STACK") st.Locations = make([]api.Stackframe, 0) for _, frame := range d.runtime.Stack.Frames { - var source *token.Location = frame.Source + var source = frame.Source if source == nil { source = &token.Location{ File: "Unknown file", @@ -405,7 +438,7 @@ func (d *Debugger) GetThread() *api.Thread { d.logger.Info("Returning THREADS") var loc *api.Thread if d.currentOp != nil { - var source *token.Location = d.currentOp.source + var source = d.currentOp.source if source == nil { source = &token.Location{ File: "Unknown file", @@ -594,7 +627,7 @@ func (d *Debugger) GetVariables() []api.Variable { if v.Type == lisp.LFun && v.FunData().Builtin != nil { continue } - var source *token.Location = v.Source + var source = v.Source if source == nil { source = &token.Location{ File: "Unknown file", @@ -642,7 +675,7 @@ func (d *Debugger) GetGoRoutine() *api.Goroutine { defer d.Unlock() var loc *api.Location if d.currentOp != nil { - var source *token.Location = d.currentOp.source + var source = d.currentOp.source if source == nil { source = &token.Location{ File: "Unknown file", @@ -718,7 +751,7 @@ func (d *Debugger) Sources(filter string) ([]string, error) { d.appendSourcesToMap(intermediate, currPkg, filter) } out := make([]string, 0) - for k, _ := range intermediate { + for k := range intermediate { out = append(out, k) } return out, nil @@ -728,7 +761,7 @@ func (d *Debugger) getSourcesForPackage(currPkg *lisp.Package) []string { intermediate := make(map[string]bool) d.appendSourcesToMap(intermediate, currPkg, "") out := make([]string, 0) - for k, _ := range intermediate { + for k := range intermediate { out = append(out, k) } return out @@ -738,12 +771,11 @@ func (d *Debugger) GetArguments() []dap.Variable { out := make([]dap.Variable, 0) for k, v := range d.currentOp.args { out = append(out, dap.Variable{ - Name: k, - Value: mapValue(v), - Type: mapLispType(v.Type), - + Name: k, + Value: mapValue(v), + Type: mapLispType(v.Type), }) - d.logger.Infof("Sending arg %s as %s", out[len(out)-1].Name, out[len(out) - 1].Value) + d.logger.Infof("Sending arg %s as %s", out[len(out)-1].Name, out[len(out)-1].Value) } d.logger.Infof("Args: %v", out) return out @@ -868,3 +900,66 @@ func (d *Debugger) Command(a *api.DebuggerCommand) (*api.DebuggerState, error) { func (d *Debugger) LastModified() time.Time { return d.lastModified } + +// Generates a call ref so the same item can be located again +func (p *Debugger) incrementCallRef(expr, function *lisp.LVal) *callRef { + p.Lock() + defer p.Unlock() + name, module := p.getFunctionParameters(function) + frameRef := new(callRef) + frameRef.name = name + frameRef.children = make([]*callRef, 0) + if function.Source != nil { + frameRef.file = expr.Source.File + frameRef.line = expr.Source.Line + frameRef.col = expr.Source.Col + frameRef.path = expr.Source.Path + frameRef.packageName = module + } + if len(p.runtime.Stack.Frames) > 0 { + p.logger.Infof("Overriding...") + current := p.runtime.Stack.Frames[len(p.runtime.Stack.Frames) - 1] + if frameRef.file == "" { + p.logger.Infof("Overriding... file %s", current.Source.File) + frameRef.file = current.Source.File + } + if frameRef.path == "" { + p.logger.Infof("Overriding... path %s", current.Source.Path) + frameRef.path = current.Source.Path + } + if frameRef.line == 0 { + frameRef.line = current.Source.Line + } + if frameRef.col == 0 { + frameRef.col = current.Source.Col + } + } + current := p.callRefs + if current != nil { + frameRef.prev = current + frameRef.prev.children = append(frameRef.prev.children, frameRef) + } + frameRef.start = time.Now() + p.callRefs = frameRef + return frameRef +} + +// Finds a call ref for the current scope +func (p *Debugger) decrementCallRef() *callRef { + current := p.callRefs + p.callRefs = current.prev + return current +} + +// Represents something that got called +type callRef struct { + start time.Time + prev *callRef + name string + children []*callRef + file string + path string + line int + col int + packageName string +} \ No newline at end of file diff --git a/lisp/x/debugger/debugger_test.go b/lisp/x/debugger/debugger_test.go index 75cf8fc..2c2e911 100644 --- a/lisp/x/debugger/debugger_test.go +++ b/lisp/x/debugger/debugger_test.go @@ -18,12 +18,12 @@ func TestNewDebugger(t *testing.T) { // Create a profiler profiler := NewDebugger(env, ":8883", DebugModeDelve) profiler.(*Debugger).CreateBreakpoint(&api.Breakpoint{ - ID: 1, - Name: "REUBEN TEST", - Addr: 0, - Addrs: []uint64{}, - File: "test.lisp", - Line: 3, + ID: 1, + Name: "REUBEN TEST", + Addr: 0, + Addrs: []uint64{}, + File: "test.lisp", + Line: 3, }) var testsrc *lisp.LVal // Some spurious functions to check we get a profile out diff --git a/lisp/x/debugger/events/events.go b/lisp/x/debugger/events/events.go index ad24ad0..c78c285 100644 --- a/lisp/x/debugger/events/events.go +++ b/lisp/x/debugger/events/events.go @@ -1,6 +1,7 @@ package events type EventType string + const EventTypeStarted = EventType("started") const EventTypeContinued = EventType("continued") const EventTypeExited = EventType("exited") diff --git a/lisp/x/profiler/callgrind.go b/lisp/x/profiler/callgrind.go index 26a615e..41b905f 100644 --- a/lisp/x/profiler/callgrind.go +++ b/lisp/x/profiler/callgrind.go @@ -196,7 +196,7 @@ func (p *callgrindProfiler) getFunctionParameters(function *lisp.LVal) (string, source = function.Source.File line = function.Source.Line } - fName := fmt.Sprintf("%s:%s", function.FunData().Package, getFunNameFromFID(p.runtime, function.FunData().FID)) + fName := fmt.Sprintf("%s:%s", function.FunData().Package, GetFunNameFromFID(p.runtime, function.FunData().FID)) return source, line, fName } diff --git a/lisp/x/profiler/go_annotator.go b/lisp/x/profiler/go_annotator.go index dd594fd..c4a3c6e 100644 --- a/lisp/x/profiler/go_annotator.go +++ b/lisp/x/profiler/go_annotator.go @@ -67,7 +67,7 @@ func (p *pprofAnnotator) Start(function *lisp.LVal) { // and doing so would have a negative effect on users not profiling - it would either be an extra stack entry // if we always ran inside a context, or a whole conditional code path and the added complication that brings // if we did it that way. - fName := fmt.Sprintf("%s:%s", function.FunData().Package, getFunNameFromFID(p.runtime, function.FunData().FID)) + fName := fmt.Sprintf("%s:%s", function.FunData().Package, GetFunNameFromFID(p.runtime, function.FunData().FID)) labels := pprof.Labels("function", fName) p.contexts.Push(p.currentContext) p.currentContext = pprof.WithLabels(p.currentContext, labels) diff --git a/lisp/x/profiler/opencensus_annotator.go b/lisp/x/profiler/opencensus_annotator.go index ca3abb1..297d2f8 100644 --- a/lisp/x/profiler/opencensus_annotator.go +++ b/lisp/x/profiler/opencensus_annotator.go @@ -70,7 +70,7 @@ func (p *ocAnnotator) Start(function *lisp.LVal) { // We don't need to profile these types. We could, but we're not that LISP :D return case lisp.LFun, lisp.LSymbol, lisp.LSExpr: - fName := fmt.Sprintf("%s:%s", function.FunData().Package, getFunNameFromFID(p.runtime, function.FunData().FID)) + fName := fmt.Sprintf("%s:%s", function.FunData().Package, GetFunNameFromFID(p.runtime, function.FunData().FID)) p.contexts.Push(p.currentContext) p.currentContext, p.currentSpan = trace.StartSpan(p.currentContext, fName) default: diff --git a/lisp/x/profiler/shared.go b/lisp/x/profiler/shared.go index c752137..497fc53 100644 --- a/lisp/x/profiler/shared.go +++ b/lisp/x/profiler/shared.go @@ -8,7 +8,7 @@ import ( var builtinRegex = regexp.MustCompile("\\<(?:builtin|special)-[a-z]+ \\`\\`(.*)\\'\\'\\>") // Gets a canonical version of the function name suitable for viewing in KCacheGrind -func getFunNameFromFID(rt *lisp.Runtime, in string) string { +func GetFunNameFromFID(rt *lisp.Runtime, in string) string { // Most of the time we can just look this up in FunNames if name, ok := rt.Package.FunNames[in]; ok { return name From 612a565aedb8db449b203b0e21ec67d842b83a9f Mon Sep 17 00:00:00 2001 From: "reuben.thompson@acresoftware.com" Date: Fri, 24 Jul 2020 12:41:19 +0100 Subject: [PATCH 7/9] Reduce logging a lot; neaten things up --- lisp/x/debugger/dapserver/dapserver.go | 2 +- lisp/x/debugger/debugger.go | 85 +++++++++++++++----------- 2 files changed, 49 insertions(+), 38 deletions(-) diff --git a/lisp/x/debugger/dapserver/dapserver.go b/lisp/x/debugger/dapserver/dapserver.go index 2fe68eb..a193322 100644 --- a/lisp/x/debugger/dapserver/dapserver.go +++ b/lisp/x/debugger/dapserver/dapserver.go @@ -289,7 +289,7 @@ func (h *connection) dispatchRequest(request dap.Message) { } func (h *connection) sendEventMessage(event events.EventType) { - log.Infof("Sending event %s", event) + log.Debugf("Sending event %s", event) switch event { case events.EventTypeContinued: h.queue <- &dap.ContinuedEvent{ diff --git a/lisp/x/debugger/debugger.go b/lisp/x/debugger/debugger.go index 3ec7802..7d1c6fb 100644 --- a/lisp/x/debugger/debugger.go +++ b/lisp/x/debugger/debugger.go @@ -50,7 +50,6 @@ type op struct { args map[string]*lisp.LVal } - type DebugServer interface { Run() error Stop() error @@ -86,7 +85,7 @@ func NewDebugger(env *lisp.LEnv, address string, mode DebugMode) lisp.Debugger { go func(end chan bool) { die := <-end if die { - panic("This is the end... the end my friend") + panic("Remote process terminated debuggee") } }(srv.(*dapserver.Server).EndChannel) } @@ -156,7 +155,7 @@ func (d *Debugger) Start(expr *lisp.LVal, function *lisp.LVal) { } } sourceStr := fmt.Sprintf("@%s:%d", source.File, source.Line) - d.logger.Infof("Instr %v %s params %v %s %d", function.Type, fname, function.Cells, sourceStr, len(d.runtime.Stack.Frames)) + d.logger.Debugf("Instr %v %s params %v %s %d", function.Type, fname, function.Cells, sourceStr, len(d.runtime.Stack.Frames)) switch function.Type { case lisp.LInt: return @@ -202,7 +201,7 @@ func (d *Debugger) Start(expr *lisp.LVal, function *lisp.LVal) { if argName == "" { argName = strconv.Itoa(k) } - d.logger.Infof("Arg %s %v is %v", argName, d.runtime.Package.Symbols[fname].Cells[paramCounter], mapValue(v)) + d.logger.Debugf("Arg %s %v is %v", argName, d.runtime.Package.Symbols[fname].Cells[paramCounter], d.mapValue(v)) if cell := expr.Cells[k+1]; cell != nil { args[argName] = cell } else { @@ -216,6 +215,11 @@ func (d *Debugger) Start(expr *lisp.LVal, function *lisp.LVal) { args: args, } d.incrementCallRef(expr, function) + // don't want to pause on code we can't see... + if source.File == "" { + runtime.Gosched() + return + } if !d.stopped { d.Lock() if function.Source != nil { @@ -258,7 +262,7 @@ func (d *Debugger) End(function *lisp.LVal) { if d.isEvaling { return } - d.logger.Infof("End %v ", function.FunData().FID) + d.logger.Debugf("End %v ", function.FunData().FID) switch function.Type { case lisp.LFun, lisp.LSymbol, lisp.LSExpr, lisp.LQSymbol: d.decrementCallRef() @@ -305,14 +309,14 @@ func (d *Debugger) Eval(text string) string { d.isEvaling = false d.Unlock() }() - d.logger.Infof("Evaluating %s", text) + d.logger.Debugf("Evaluating %s", text) tEnv := lisp.NewEnv(nil) tEnv.Runtime.Registry = d.runtime.Registry tEnv.Runtime.Package = d.runtime.Package tEnv.Runtime.Reader = d.runtime.Reader tEnv.Runtime.Debugger = nil v := tEnv.LoadString("eval", text) - return mapValue(v) + return d.mapValue(v) } func (d *Debugger) GetDapStacktrace() []dap.StackFrame { @@ -322,7 +326,7 @@ func (d *Debugger) GetDapStacktrace() []dap.StackFrame { if current == nil { break } - d.logger.Infof("Line %s in %s line %d col %d", current.name, current.file, current.line, current.col) + d.logger.Debugf("Line %s in %s line %d col %d", current.name, current.file, current.line, current.col) hint := "normal" origin := "" if current.file == "Unknown file" || current.file == "" { @@ -349,7 +353,7 @@ func (d *Debugger) GetDapStacktrace() []dap.StackFrame { } func (d *Debugger) GetStacktrace(st *rpc2.StacktraceOut) { - d.logger.Info("Returning STACK") + d.logger.Debug("Returning STACK") st.Locations = make([]api.Stackframe, 0) for _, frame := range d.runtime.Stack.Frames { var source = frame.Source @@ -392,7 +396,7 @@ func (d *Debugger) GetStacktrace(st *rpc2.StacktraceOut) { } func (d *Debugger) GetAllBreakpoints() map[int]*api.Breakpoint { - d.logger.Info("Returning BREAKPOINTS") + d.logger.Debug("Returning BREAKPOINTS") d.Lock() defer d.Unlock() return d.breakpoints @@ -435,7 +439,7 @@ func (d *Debugger) AmendBreakpoint(bp *api.Breakpoint) error { } func (d *Debugger) GetThread() *api.Thread { - d.logger.Info("Returning THREADS") + d.logger.Debug("Returning THREADS") var loc *api.Thread if d.currentOp != nil { var source = d.currentOp.source @@ -538,15 +542,15 @@ func mapKind(in *lisp.LVal) reflect.Kind { } } -func sexprAsString(in *lisp.LVal) string { +func (d *Debugger) sexprAsString(in *lisp.LVal) string { out := "" for _, v := range in.Cells { - out += mapValue(v) + " " + out += d.mapValue(v) + " " } return out } -func mapValue(in *lisp.LVal) string { +func (d *Debugger) mapValue(in *lisp.LVal) string { switch in.Type { case lisp.LFloat: return strconv.FormatFloat(in.Float, 'f', 8, 64) @@ -559,7 +563,7 @@ func mapValue(in *lisp.LVal) string { case lisp.LSymbol: return in.Str case lisp.LSExpr: - return sexprAsString(in) + return d.sexprAsString(in) case lisp.LSortMap: return "map" case lisp.LNative: @@ -581,7 +585,7 @@ func mapValue(in *lisp.LVal) string { } } -func extractChildren(in *lisp.LVal) []api.Variable { +func (d *Debugger) extractChildren(in *lisp.LVal) []api.Variable { children := make([]api.Variable, 0) switch in.Type { case lisp.LSortMap: @@ -589,7 +593,7 @@ func extractChildren(in *lisp.LVal) []api.Variable { children = append(children, api.Variable{ Type: mapLispType(v.Type), RealType: mapLispType(v.Type), - Value: mapValue(v), + Value: d.mapValue(v), Kind: mapKind(v), }) } @@ -598,7 +602,7 @@ func extractChildren(in *lisp.LVal) []api.Variable { children = append(children, api.Variable{ Type: mapLispType(v.Type), RealType: mapLispType(v.Type), - Value: mapValue(v), + Value: d.mapValue(v), Kind: mapKind(v), }) } @@ -616,7 +620,7 @@ func extractChildren(in *lisp.LVal) []api.Variable { } func (d *Debugger) GetVariables() []api.Variable { - d.logger.Info("Returning VARS") + d.logger.Debug("Returning VARS") d.Lock() defer d.Unlock() out := make([]api.Variable, 0) @@ -638,9 +642,9 @@ func (d *Debugger) GetVariables() []api.Variable { } } children := make([]api.Variable, 0) - strVal := mapValue(v) + strVal := d.mapValue(v) if strVal == "map" || strVal == "array" { - children = extractChildren(v) + children = d.extractChildren(v) } out = append(out, api.Variable{ Name: k, @@ -665,12 +669,12 @@ func (d *Debugger) GetVariables() []api.Variable { } func (d *Debugger) GetFunctionArgs() []api.Variable { - d.logger.Info("Returning ARGS") + d.logger.Debug("Returning ARGS") return []api.Variable{} } func (d *Debugger) GetGoRoutine() *api.Goroutine { - d.logger.Info("Returning GOROUTINE") + d.logger.Debug("Returning GOROUTINE") d.Lock() defer d.Unlock() var loc *api.Location @@ -745,7 +749,7 @@ func (d *Debugger) SetVariableInScope(scope api.EvalScope, symbol string, value } func (d *Debugger) Sources(filter string) ([]string, error) { - d.logger.Info("Returning SOURCES") + d.logger.Debug("Returning SOURCES") intermediate := make(map[string]bool) for _, currPkg := range d.runtime.Registry.Packages { d.appendSourcesToMap(intermediate, currPkg, filter) @@ -770,14 +774,21 @@ func (d *Debugger) getSourcesForPackage(currPkg *lisp.Package) []string { func (d *Debugger) GetArguments() []dap.Variable { out := make([]dap.Variable, 0) for k, v := range d.currentOp.args { + argValue := d.mapValue(v) + if d.currentOp.name != "defun" && d.currentOp.name != "set" && d.currentOp.name != "let" && d.currentOp.name != "let*" && d.currentOp.name != "defmacro" { + if v.Type == lisp.LSymbol { + d.logger.Infof("Remapping %s", argValue) + argValue = d.Eval(argValue) + } + } out = append(out, dap.Variable{ Name: k, - Value: mapValue(v), + Value: argValue, Type: mapLispType(v.Type), }) d.logger.Infof("Sending arg %s as %s", out[len(out)-1].Name, out[len(out)-1].Value) } - d.logger.Infof("Args: %v", out) + d.logger.Debugf("Args: %v", out) return out } @@ -794,7 +805,7 @@ func (d *Debugger) appendSourcesToMap(intermediate map[string]bool, currPkg *lis } func (d *Debugger) Functions(filter string) ([]string, error) { - d.logger.Info("Returning FUNCTIONS") + d.logger.Debug("Returning FUNCTIONS") d.Lock() defer d.Unlock() out := make([]string, 0) @@ -813,7 +824,7 @@ func (d *Debugger) FindLocation(scope api.EvalScope, loc string, lines bool) ([] } func (d *Debugger) ListPackagesBuildInfo(files bool) []api.PackageBuildInfo { - d.logger.Info("Returning BUILD INFO") + d.logger.Debug("Returning BUILD INFO") d.Lock() defer d.Unlock() out := make([]api.PackageBuildInfo, 0) @@ -828,7 +839,7 @@ func (d *Debugger) ListPackagesBuildInfo(files bool) []api.PackageBuildInfo { } func (d *Debugger) State(blocking bool) (*api.DebuggerState, error) { - d.logger.Info("Returning STATE") + d.logger.Debug("Returning STATE") state := &api.DebuggerState{ Running: !d.stopped, CurrentThread: d.GetThread(), @@ -865,7 +876,7 @@ func (d *Debugger) Halt() { func (d *Debugger) Command(a *api.DebuggerCommand) (*api.DebuggerState, error) { // TODO this is Delve specific - d.logger.Infof("Command: %s", a.Name) + d.logger.Debug("Command: %s", a.Name) d.lastModified = time.Now() started := false switch a.Name { @@ -917,14 +928,14 @@ func (p *Debugger) incrementCallRef(expr, function *lisp.LVal) *callRef { frameRef.packageName = module } if len(p.runtime.Stack.Frames) > 0 { - p.logger.Infof("Overriding...") - current := p.runtime.Stack.Frames[len(p.runtime.Stack.Frames) - 1] + p.logger.Debug("Overriding...") + current := p.runtime.Stack.Frames[len(p.runtime.Stack.Frames)-1] if frameRef.file == "" { - p.logger.Infof("Overriding... file %s", current.Source.File) + p.logger.Debugf("Overriding... file %s", current.Source.File) frameRef.file = current.Source.File } if frameRef.path == "" { - p.logger.Infof("Overriding... path %s", current.Source.Path) + p.logger.Debugf("Overriding... path %s", current.Source.Path) frameRef.path = current.Source.Path } if frameRef.line == 0 { @@ -958,8 +969,8 @@ type callRef struct { name string children []*callRef file string - path string + path string line int - col int + col int packageName string -} \ No newline at end of file +} From ef27ef928e594e0eee8942bc1bb4770c508c9c8d Mon Sep 17 00:00:00 2001 From: "reuben.thompson@acresoftware.com" Date: Fri, 24 Jul 2020 19:15:32 +0100 Subject: [PATCH 8/9] Make breakpoints great again --- go.mod | 2 +- lisp/x/debugger/dapserver/dapserver.go | 69 ++++++- lisp/x/debugger/dapserver/debuggerwrapper.go | 184 +++++++++++++++--- .../dapserver/debuggerwrapper_test.go | 65 +++++++ lisp/x/debugger/debugger.go | 13 +- lisp/x/debugger/delveserver/server.go | 4 + lisp/x/debugger/events/events.go | 1 + 7 files changed, 302 insertions(+), 36 deletions(-) create mode 100644 lisp/x/debugger/dapserver/debuggerwrapper_test.go diff --git a/go.mod b/go.mod index ab78f3e..8b1c29b 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 github.com/google/go-dap v0.2.0 github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce // indirect - github.com/magiconair/properties v1.8.0 // indirect + github.com/magiconair/properties v1.8.0 github.com/mitchellh/go-homedir v0.0.0-20180523094522-3864e76763d9 github.com/mitchellh/mapstructure v0.0.0-20180511142126-bb74f1db0675 // indirect github.com/pelletier/go-toml v1.1.0 // indirect diff --git a/lisp/x/debugger/dapserver/dapserver.go b/lisp/x/debugger/dapserver/dapserver.go index a193322..fe1d6ce 100644 --- a/lisp/x/debugger/dapserver/dapserver.go +++ b/lisp/x/debugger/dapserver/dapserver.go @@ -9,12 +9,14 @@ package dapserver import ( "bufio" + "github.com/go-delve/delve/service/api" "github.com/google/go-dap" "github.com/luthersystems/elps/lisp/x/debugger/delveserver" "github.com/luthersystems/elps/lisp/x/debugger/events" log "github.com/sirupsen/logrus" "io" "net" + "path/filepath" "runtime" "sync" "time" @@ -48,7 +50,10 @@ func NewServer(debugger delveserver.ServerDebugger, address string, handlers int connQueue: make(chan net.Conn, 10), sequence: 0, wg: new(sync.WaitGroup), - debugger: &debuggerwrapper{debugger: debugger}, + debugger: &debuggerwrapper{ + debugger: debugger, + files: make(map[string][]string), + }, EndChannel: make(chan bool), } server.connection.s = server @@ -90,15 +95,50 @@ func (s *Server) Event(x events.EventType) { } } +func (s *Server) Breakpoint(bp *api.Breakpoint) { + log.Infof("Hit breakpoint %v", bp) + _, filename := filepath.Split(bp.File) + if s.connection.Connected { + go func(ch chan dap.Message, path string) { + s.connection.queue <- &dap.BreakpointEvent{ + Event: dap.Event{ + Event: "breakpoint", + ProtocolMessage: dap.ProtocolMessage{ + Seq: s.incSequence(), + Type: "event", + }, + }, + Body: dap.BreakpointEventBody{ + Reason: "breakpoint", + Breakpoint: dap.Breakpoint{ + Id: bp.ID, + Verified: true, + Message: "Stopped at breakpoint", + Source: dap.Source{ + Path: bp.File, + Name: filename, + }, + Line: bp.Line, + Column: 1, + EndLine: bp.Line, + EndColumn: 1, + }, + }, + } + }(s.connection.queue, filename) + } + runtime.Gosched() +} + func (s *Server) listen() { defer s.listener.Close() for { conn, err := s.listener.Accept() if err != nil { - log.Errorf("Connection failed:", err) + log.Errorf("Connection failed: %v", err) continue } - log.Errorf("Accepted connection from", conn.RemoteAddr()) + log.Infof("Accepted connection from %v", conn.RemoteAddr()) s.connection.start(conn) return } @@ -160,7 +200,7 @@ func (h *connection) start(conn net.Conn) { err := h.handleRequest() if err != nil { if err == io.EOF { - log.Errorf("Connection closed: ", err) + log.Errorf("Connection closed: %v", err) h.s.EndChannel <- true return } @@ -260,7 +300,7 @@ func (h *connection) dispatchRequest(request dap.Message) { case *dap.EvaluateRequest: h.s.debugger.onEvaluateRequest(request, h.queue, h.s.incSequence()) case *dap.StepInTargetsRequest: - h.s.debugger.onStepInTargetsRequest(request, h.queue) + h.s.debugger.onStepInTargetsRequest(request, h.queue, h.s.incSequence()) case *dap.GotoTargetsRequest: h.s.debugger.onGotoTargetsRequest(request, h.queue, h.s.incSequence()) case *dap.CompletionsRequest: @@ -289,7 +329,7 @@ func (h *connection) dispatchRequest(request dap.Message) { } func (h *connection) sendEventMessage(event events.EventType) { - log.Debugf("Sending event %s", event) + log.Infof("Sending event %s", event) switch event { case events.EventTypeContinued: h.queue <- &dap.ContinuedEvent{ @@ -330,12 +370,13 @@ func (h *connection) sendEventMessage(event events.EventType) { Body: dap.StoppedEventBody{ AllThreadsStopped: true, ThreadId: 1, - Reason: "pause", // we need to propagate this by using more reasons + Reason: "pause", }, } case events.EventTypeStoppedBreakpoint: h.queue <- &dap.StoppedEvent{ - Event: dap.Event{Event: "stopped", + Event: dap.Event{ + Event: "stopped", ProtocolMessage: dap.ProtocolMessage{ Seq: h.s.incSequence(), Type: "event", @@ -344,7 +385,7 @@ func (h *connection) sendEventMessage(event events.EventType) { Body: dap.StoppedEventBody{ AllThreadsStopped: true, ThreadId: 1, - Reason: "breakpoint", // we need to propagate this by using more reasons + Reason: "breakpoint", }, } case events.EventTypeStoppedEntry: @@ -388,6 +429,16 @@ func (h *connection) sendEventMessage(event events.EventType) { Restart: false, }, } + case events.EventTypeInitialized: + h.queue <- &dap.InitializedEvent{ + Event: dap.Event{ + Event: "initialized", + ProtocolMessage: dap.ProtocolMessage{ + Seq: h.s.incSequence(), + Type: "event", + }, + }, + } } } diff --git a/lisp/x/debugger/dapserver/debuggerwrapper.go b/lisp/x/debugger/dapserver/debuggerwrapper.go index 1513c64..a8819a1 100644 --- a/lisp/x/debugger/dapserver/debuggerwrapper.go +++ b/lisp/x/debugger/dapserver/debuggerwrapper.go @@ -1,16 +1,22 @@ package dapserver import ( + "encoding/json" "github.com/go-delve/delve/service/api" "github.com/go-delve/delve/service/rpc2" + "github.com/golang-collections/collections/stack" "github.com/google/go-dap" "github.com/luthersystems/elps/lisp/x/debugger/delveserver" "github.com/luthersystems/elps/lisp/x/debugger/events" + log "github.com/sirupsen/logrus" + "io/ioutil" "math/rand" + "strings" ) type debuggerwrapper struct { debugger delveserver.ServerDebugger + files map[string][]string } func (d *debuggerwrapper) onInitializeRequest(request *dap.InitializeRequest, handler *connection) { @@ -22,10 +28,11 @@ func (d *debuggerwrapper) onInitializeRequest(request *dap.InitializeRequest, ha Seq: handler.s.incSequence(), Type: "response", }, + Command: request.Command, }, Body: dap.Capabilities{ SupportsConfigurationDoneRequest: true, - SupportsFunctionBreakpoints: true, + SupportsFunctionBreakpoints: false, SupportsConditionalBreakpoints: false, SupportsHitConditionalBreakpoints: false, SupportsEvaluateForHovers: true, @@ -54,6 +61,8 @@ func (d *debuggerwrapper) onInitializeRequest(request *dap.InitializeRequest, ha SupportsBreakpointLocationsRequest: true, }, } + + handler.Event <- events.EventTypeInitialized if d.debugger.IsStopped() { handler.Event <- events.EventTypeStoppedEntry } @@ -64,25 +73,25 @@ func (d *debuggerwrapper) onLaunchRequest(request *dap.LaunchRequest, returnchan Response: dap.Response{ RequestSeq: request.GetSeq(), Success: true, - Message: "Not supported", ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, }, } } func (d *debuggerwrapper) onAttachRequest(request *dap.AttachRequest, returnchan chan dap.Message, seq int) { - returnchan <- &dap.ErrorResponse{ + returnchan <- &dap.AttachResponse{ Response: dap.Response{ RequestSeq: request.GetSeq(), - Success: false, - Message: "Not supported", + Success: true, ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, }, } } @@ -98,6 +107,7 @@ func (d *debuggerwrapper) onDisconnectRequest(request *dap.DisconnectRequest, re Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: true, Message: "Complete", @@ -113,6 +123,7 @@ func (d *debuggerwrapper) onTerminateRequest(request *dap.TerminateRequest, retu Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: true, Message: "Complete", @@ -122,12 +133,14 @@ func (d *debuggerwrapper) onTerminateRequest(request *dap.TerminateRequest, retu } func (d *debuggerwrapper) onRestartRequest(request *dap.RestartRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported restartRequest was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -136,6 +149,7 @@ func (d *debuggerwrapper) onRestartRequest(request *dap.RestartRequest, returnch } func (d *debuggerwrapper) onSetBreakpointsRequest(request *dap.SetBreakpointsRequest, returnchan chan dap.Message, seq int) { + log.Infof("BREAKPOINT request %v", request) if request.Arguments.SourceModified { returnchan <- &dap.ErrorResponse{ Response: dap.Response{ @@ -143,6 +157,7 @@ func (d *debuggerwrapper) onSetBreakpointsRequest(request *dap.SetBreakpointsReq Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Cannot modify source whilst running", @@ -150,7 +165,16 @@ func (d *debuggerwrapper) onSetBreakpointsRequest(request *dap.SetBreakpointsReq } return } + log.Infof("Name is %s", request.Arguments.Source.Name) out := make([]dap.Breakpoint, 0) + for id, bp := range d.debugger.GetAllBreakpoints() { + if bp.File == request.Arguments.Source.Path { + err := d.debugger.RemoveBreakpoint(id) + if err != nil { + log.Errorf("Error removing breakpoint: %s", err.Error()) + } + } + } for _, v := range request.Arguments.Breakpoints { id := rand.Int() d.debugger.CreateBreakpoint(&api.Breakpoint{ @@ -168,34 +192,49 @@ func (d *debuggerwrapper) onSetBreakpointsRequest(request *dap.SetBreakpointsReq Stacktrace: 0, TotalHitCount: 0, }) - out = append(out, dap.Breakpoint{ - Id: id, - Line: v.Line, - Column: 0, - Source: request.Arguments.Source, - Verified: false, - }) } - returnchan <- &dap.SetBreakpointsResponse{ + for id, bp := range d.debugger.GetAllBreakpoints() { + if bp.File == request.Arguments.Source.Path { + out = append(out, dap.Breakpoint{ + Id: id, + Verified: true, + Source: request.Arguments.Source, + Line: bp.Line, + Column: 1, + EndLine: bp.Line, + EndColumn: 1, + }) + } else { + log.Infof("%s didn't match %s", bp.File, request.Arguments.Source.Path) + } + } + + bpResponse := &dap.SetBreakpointsResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, Success: true, RequestSeq: request.GetSeq(), }, Body: dap.SetBreakpointsResponseBody{Breakpoints: out}, } + returnchan <- bpResponse + o, _ := json.Marshal(bpResponse) + log.Info(string(o)) } func (d *debuggerwrapper) onSetFunctionBreakpointsRequest(request *dap.SetFunctionBreakpointsRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported setFunctionBreakpoint was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -204,12 +243,14 @@ func (d *debuggerwrapper) onSetFunctionBreakpointsRequest(request *dap.SetFuncti } func (d *debuggerwrapper) onSetExceptionBreakpointsRequest(request *dap.SetExceptionBreakpointsRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported setExceptionBreakpoint was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -224,6 +265,7 @@ func (d *debuggerwrapper) onConfigurationDoneRequest(request *dap.ConfigurationD Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: true, }, @@ -238,6 +280,7 @@ func (d *debuggerwrapper) onContinueRequest(request *dap.ContinueRequest, return Seq: seq, Type: "response", }, + Command: request.Command, Success: true, RequestSeq: request.GetSeq(), }, @@ -255,6 +298,7 @@ func (d *debuggerwrapper) onNextRequest(request *dap.NextRequest, returnchan cha Seq: seq, Type: "response", }, + Command: request.Command, Success: true, RequestSeq: request.GetSeq(), }, @@ -269,6 +313,7 @@ func (d *debuggerwrapper) onStepInRequest(request *dap.StepInRequest, returnchan Seq: seq, Type: "response", }, + Command: request.Command, Success: true, RequestSeq: request.GetSeq(), }, @@ -283,6 +328,7 @@ func (d *debuggerwrapper) onStepOutRequest(request *dap.StepOutRequest, returnch Seq: seq, Type: "response", }, + Command: request.Command, Success: true, RequestSeq: request.GetSeq(), }, @@ -290,12 +336,14 @@ func (d *debuggerwrapper) onStepOutRequest(request *dap.StepOutRequest, returnch } func (d *debuggerwrapper) onStepBackRequest(request *dap.StepBackRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported stepBack was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -304,12 +352,14 @@ func (d *debuggerwrapper) onStepBackRequest(request *dap.StepBackRequest, return } func (d *debuggerwrapper) onReverseContinueRequest(request *dap.ReverseContinueRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported reverseContinue was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -318,12 +368,14 @@ func (d *debuggerwrapper) onReverseContinueRequest(request *dap.ReverseContinueR } func (d *debuggerwrapper) onRestartFrameRequest(request *dap.RestartFrameRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported restartFrame was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -332,12 +384,14 @@ func (d *debuggerwrapper) onRestartFrameRequest(request *dap.RestartFrameRequest } func (d *debuggerwrapper) onGotoRequest(request *dap.GotoRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported goto was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -353,6 +407,7 @@ func (d *debuggerwrapper) onPauseRequest(request *dap.PauseRequest, returnchan c Seq: seq, Type: "response", }, + Command: request.Command, Success: true, RequestSeq: request.GetSeq(), }, @@ -369,6 +424,7 @@ func (d *debuggerwrapper) onStackTraceRequest(request *dap.StackTraceRequest, re Seq: seq, Type: "response", }, + Command: request.Command, Success: true, RequestSeq: request.Seq, }, @@ -387,6 +443,7 @@ func (d *debuggerwrapper) onScopesRequest(request *dap.ScopesRequest, returnchan Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: true, }, @@ -423,6 +480,7 @@ func (d *debuggerwrapper) onVariablesRequest(request *dap.VariablesRequest, retu Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: true, }, @@ -453,6 +511,7 @@ func (d *debuggerwrapper) onVariablesRequest(request *dap.VariablesRequest, retu Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: true, }, @@ -471,6 +530,7 @@ func (d *debuggerwrapper) onSetVariableRequest(request *dap.SetVariableRequest, Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: err.Error(), @@ -485,6 +545,7 @@ func (d *debuggerwrapper) onSetVariableRequest(request *dap.SetVariableRequest, Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: true, }, @@ -496,12 +557,14 @@ func (d *debuggerwrapper) onSetVariableRequest(request *dap.SetVariableRequest, } func (d *debuggerwrapper) onSetExpressionRequest(request *dap.SetExpressionRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported setExpression was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -510,12 +573,14 @@ func (d *debuggerwrapper) onSetExpressionRequest(request *dap.SetExpressionReque } func (d *debuggerwrapper) onSourceRequest(request *dap.SourceRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported source request was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -530,6 +595,7 @@ func (d *debuggerwrapper) onThreadsRequest(request *dap.ThreadsRequest, returnch Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: true, }, @@ -545,12 +611,14 @@ func (d *debuggerwrapper) onThreadsRequest(request *dap.ThreadsRequest, returnch } func (d *debuggerwrapper) onTerminateThreadsRequest(request *dap.TerminateThreadsRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported terminateThreads was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -565,6 +633,7 @@ func (d *debuggerwrapper) onEvaluateRequest(request *dap.EvaluateRequest, return Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: true, }, @@ -576,16 +645,31 @@ func (d *debuggerwrapper) onEvaluateRequest(request *dap.EvaluateRequest, return } } -func (d *debuggerwrapper) onStepInTargetsRequest(request *dap.StepInTargetsRequest, returnchan chan dap.Message) { +func (d *debuggerwrapper) onStepInTargetsRequest(request *dap.StepInTargetsRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported stepInTargets was made by client") + returnchan <- &dap.ErrorResponse{ + Response: dap.Response{ + ProtocolMessage: dap.ProtocolMessage{ + Seq: seq, + Type: "response", + }, + Command: request.Command, + RequestSeq: request.GetSeq(), + Success: false, + Message: "Not supported", + }, + } } func (d *debuggerwrapper) onGotoTargetsRequest(request *dap.GotoTargetsRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported gotoTargets was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -594,12 +678,14 @@ func (d *debuggerwrapper) onGotoTargetsRequest(request *dap.GotoTargetsRequest, } func (d *debuggerwrapper) onCompletionsRequest(request *dap.CompletionsRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported completions was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -608,12 +694,14 @@ func (d *debuggerwrapper) onCompletionsRequest(request *dap.CompletionsRequest, } func (d *debuggerwrapper) onExceptionInfoRequest(request *dap.ExceptionInfoRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported exceptionInfo was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -622,12 +710,14 @@ func (d *debuggerwrapper) onExceptionInfoRequest(request *dap.ExceptionInfoReque } func (d *debuggerwrapper) onLoadedSourcesRequest(request *dap.LoadedSourcesRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported loadedSources was made by client") returnchan <- &dap.LoadedSourcesResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -641,12 +731,14 @@ func (d *debuggerwrapper) onLoadedSourcesRequest(request *dap.LoadedSourcesReque } func (d *debuggerwrapper) onDataBreakpointInfoRequest(request *dap.DataBreakpointInfoRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported dataBreakpointInfo was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -655,12 +747,14 @@ func (d *debuggerwrapper) onDataBreakpointInfoRequest(request *dap.DataBreakpoin } func (d *debuggerwrapper) onSetDataBreakpointsRequest(request *dap.SetDataBreakpointsRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported setDataBreakpoints was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -669,12 +763,14 @@ func (d *debuggerwrapper) onSetDataBreakpointsRequest(request *dap.SetDataBreakp } func (d *debuggerwrapper) onReadMemoryRequest(request *dap.ReadMemoryRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported readMemory was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -683,12 +779,14 @@ func (d *debuggerwrapper) onReadMemoryRequest(request *dap.ReadMemoryRequest, re } func (d *debuggerwrapper) onDisassembleRequest(request *dap.DisassembleRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported disassemble was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: false, Message: "Not supported", @@ -697,6 +795,7 @@ func (d *debuggerwrapper) onDisassembleRequest(request *dap.DisassembleRequest, } func (d *debuggerwrapper) onCancelRequest(request *dap.CancelRequest, returnchan chan dap.Message, seq int) { + log.Error("Invalid request to non-supported cancelRequest was made by client") returnchan <- &dap.ErrorResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ @@ -710,27 +809,65 @@ func (d *debuggerwrapper) onCancelRequest(request *dap.CancelRequest, returnchan } } +func (d *debuggerwrapper) getCodeLine(file string, line int) string { + var data []string + var ok bool + if data, ok = d.files[file]; !ok { + dataBytes, err := ioutil.ReadFile(file) + if err != nil { + panic(err) + } + data = strings.Split(string(dataBytes), "\n") + d.files[file] = data + } + return data[line-1] +} + +func (d *debuggerwrapper) getBreakpointsFromLine(file string, lineNumber int) []dap.BreakpointLocation { + locations := make([]dap.BreakpointLocation, 0) + stk := stack.New() + for { + line := d.getCodeLine(file, lineNumber) + for pos, r := range []rune(line) { + switch r { + case '(', '{': + stk.Push([]int{pos + 1, lineNumber}) + case '}', ')': + start := stk.Pop().([]int) + locations = append(locations, dap.BreakpointLocation{ + Line: start[1], + Column: start[0], + EndLine: lineNumber, + EndColumn: pos + 1, + }) + } + } + if stk.Len() == 0 || lineNumber == len(d.files[file]) { + break + } + lineNumber += 1 + } + return locations +} + func (d *debuggerwrapper) onBreakpointLocationsRequest(request *dap.BreakpointLocationsRequest, returnchan chan dap.Message, seq int) { - returnchan <- &dap.BreakpointLocationsResponse{ + // NB This is the POSSIBLE locations for breakpoints - not the actual locations + locations := d.getBreakpointsFromLine(request.Arguments.Source.Path, request.Arguments.Line) + resp := &dap.BreakpointLocationsResponse{ Response: dap.Response{ ProtocolMessage: dap.ProtocolMessage{ Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: true, }, Body: dap.BreakpointLocationsResponseBody{ - Breakpoints: []dap.BreakpointLocation{ // TODO HANDLE COLUMNS - { - Line: request.Arguments.Line, - Column: 0, - EndLine: 0, - EndColumn: 0, - }, - }, + Breakpoints: locations, }, } + returnchan <- resp } func (d *debuggerwrapper) onModulesRequest(request *dap.ModulesRequest, returnchan chan dap.Message, seq int) { @@ -741,6 +878,7 @@ func (d *debuggerwrapper) onModulesRequest(request *dap.ModulesRequest, returnch Seq: seq, Type: "response", }, + Command: request.Command, RequestSeq: request.GetSeq(), Success: true, }, diff --git a/lisp/x/debugger/dapserver/debuggerwrapper_test.go b/lisp/x/debugger/dapserver/debuggerwrapper_test.go new file mode 100644 index 0000000..8e3f7b2 --- /dev/null +++ b/lisp/x/debugger/dapserver/debuggerwrapper_test.go @@ -0,0 +1,65 @@ +package dapserver + +import ( + "github.com/google/go-dap" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestBreakpoints(t *testing.T) { + d := &debuggerwrapper{ + files: map[string][]string{ + "test.lisp": []string{ + "(line 1)", + "(line (get-line 2))", + "(line (", + " (something something)", + "))", + }, + }, + } + + assert.Equal(t, []dap.BreakpointLocation{ + { + Line: 1, + Column: 1, + EndLine: 1, + EndColumn: 8, + }, + }, d.getBreakpointsFromLine("test.lisp", 1)) + assert.Equal(t, []dap.BreakpointLocation{ + { + Line: 2, + Column: 7, + EndLine: 2, + EndColumn: 18, + }, + { + Line: 2, + Column: 1, + EndLine: 2, + EndColumn: 19, + }, + }, d.getBreakpointsFromLine("test.lisp", 2)) + assert.Equal(t, []dap.BreakpointLocation{ + { + Line: 4, + Column: 3, + EndLine: 4, + EndColumn: 23, + }, + { + Line: 3, + Column: 7, + EndLine: 5, + EndColumn: 1, + }, + { + Line: 3, + Column: 1, + EndLine: 5, + EndColumn: 2, + }, + + }, d.getBreakpointsFromLine("test.lisp", 3)) +} diff --git a/lisp/x/debugger/debugger.go b/lisp/x/debugger/debugger.go index 7d1c6fb..aee9c85 100644 --- a/lisp/x/debugger/debugger.go +++ b/lisp/x/debugger/debugger.go @@ -13,6 +13,7 @@ import ( "github.com/luthersystems/elps/parser/token" "github.com/sirupsen/logrus" "math/rand" + "os" "reflect" "runtime" "strconv" @@ -54,6 +55,7 @@ type DebugServer interface { Run() error Stop() error Event(events.EventType) + Breakpoint(v *api.Breakpoint) } type DebugMode string @@ -222,15 +224,20 @@ func (d *Debugger) Start(expr *lisp.LVal, function *lisp.LVal) { } if !d.stopped { d.Lock() - if function.Source != nil { + if expr.Source != nil { + fstat, _ := os.Stat(expr.Source.Path) for _, v := range d.breakpoints { - if (v.File == function.Source.File || fmt.Sprintf("%s/%s", d.pwd, function.Source.File) == v.File) && - v.Line == function.Source.Line { + vStat, _ := os.Stat(v.File) + if (os.SameFile(vStat, fstat) || fmt.Sprintf("%s%c%s", d.pwd, os.PathSeparator, expr.Source.File) == v.File) && + v.Line == expr.Source.Line { d.logger.Infof("BREAKPOINT") d.stopped = true d.Unlock() d.Start(expr, function) + d.server.Breakpoint(v) d.server.Event(events.EventTypeStoppedBreakpoint) + // or it won't yield... + runtime.Gosched() return } } diff --git a/lisp/x/debugger/delveserver/server.go b/lisp/x/debugger/delveserver/server.go index da3e5f4..8f3208b 100644 --- a/lisp/x/debugger/delveserver/server.go +++ b/lisp/x/debugger/delveserver/server.go @@ -44,6 +44,10 @@ func NewServer(debugger ServerDebugger, address string) *RPCServer { return rpc } +func (s *RPCServer) Breakpoint(v *api.Breakpoint) { + // no-op +} + func (s *RPCServer) LastModified(arg rpc2.LastModifiedIn, out *rpc2.LastModifiedOut) error { out.Time = s.debugger.LastModified() return nil diff --git a/lisp/x/debugger/events/events.go b/lisp/x/debugger/events/events.go index c78c285..6ea486f 100644 --- a/lisp/x/debugger/events/events.go +++ b/lisp/x/debugger/events/events.go @@ -10,3 +10,4 @@ const EventTypeStoppedPaused = EventType("stopped-paused") const EventTypeStoppedStep = EventType("stopped-step") const EventTypeStoppedEntry = EventType("stopped-entry") const EventTypeTerminated = EventType("terminated") +const EventTypeInitialized = EventType("initialized") From 392289cdbb5e29d92f7522bc7b31895537fac563 Mon Sep 17 00:00:00 2001 From: "reuben.thompson@acresoftware.com" Date: Mon, 27 Jul 2020 10:01:59 +0100 Subject: [PATCH 9/9] Fix breakpoints --- lisp/x/debugger/debugger.go | 38 +++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/lisp/x/debugger/debugger.go b/lisp/x/debugger/debugger.go index aee9c85..51da36c 100644 --- a/lisp/x/debugger/debugger.go +++ b/lisp/x/debugger/debugger.go @@ -25,20 +25,21 @@ import "github.com/go-delve/delve/service/api" type Debugger struct { sync.Mutex - enabled bool - breakpoints map[int]*api.Breakpoint - env *lisp.LEnv - runtime *lisp.Runtime - lastModified time.Time - stopped bool - step chan bool - run chan bool - server DebugServer - currentOp *op - logger *logrus.Logger - pwd string - callRefs *callRef - isEvaling bool + enabled bool + breakpoints map[int]*api.Breakpoint + env *lisp.LEnv + runtime *lisp.Runtime + lastModified time.Time + stopped bool + step chan bool + run chan bool + server DebugServer + currentOp *op + logger *logrus.Logger + pwd string + callRefs *callRef + isEvaling bool + reenteringStart bool } func (d *Debugger) Done() bool { @@ -216,7 +217,11 @@ func (d *Debugger) Start(expr *lisp.LVal, function *lisp.LVal) { source: source, args: args, } - d.incrementCallRef(expr, function) + if !d.reenteringStart { + d.incrementCallRef(expr, function) + } else { + d.reenteringStart = false + } // don't want to pause on code we can't see... if source.File == "" { runtime.Gosched() @@ -233,11 +238,12 @@ func (d *Debugger) Start(expr *lisp.LVal, function *lisp.LVal) { d.logger.Infof("BREAKPOINT") d.stopped = true d.Unlock() - d.Start(expr, function) d.server.Breakpoint(v) d.server.Event(events.EventTypeStoppedBreakpoint) // or it won't yield... runtime.Gosched() + d.reenteringStart = true + d.Start(expr, function) return } }