mbeckerle commented on code in PR #666:
URL: https://github.com/apache/daffodil-vscode/pull/666#discussion_r1232330524
##########
server/core/src/main/scala/org.apache.daffodil.debugger.dap/DAPodil.scala:
##########
@@ -205,8 +212,8 @@ class DAPodil(
debugee(request) match {
case Left(errors) =>
state.set(DAPodil.State.FailedToLaunch(request, errors, None)) *>
- Logger[IO].warn(show"error parsing launch args:
${errors.mkString_(", ")}") *> session
- .sendResponse(request.respondFailure(Some(show"error parsing
launch args: ${errors.mkString_(", ")}")))
+ Logger[IO].error(show"error parsing launch args:
${errors.mkString_(", ")}") *>
Review Comment:
You may want the separator to contain a newline so that giant long lines
(which each error message already is by itself) don't run together to further
compound the super long line problem.
##########
server/core/src/main/scala/org.apache.daffodil.debugger.dap/DAPodil.scala:
##########
@@ -49,8 +49,11 @@ trait DAPSession[Req, Res, Ev] {
def sendResponse(response: Res): IO[Unit]
def sendEvent(event: Ev): IO[Unit]
+ def terminate(message: String): IO[Unit]
Review Comment:
There are so many reasons the server could terminate. Is this rich enough?
Passing just a string back?
##########
server/core/src/main/scala/org.apache.daffodil.debugger.dap/DAPodil.scala:
##########
@@ -63,6 +66,10 @@ object DAPSession {
def sendEvent(event: DebugEvent): IO[Unit] =
Logger[IO].info(show"<E $event") *>
IO.blocking(server.sendEvent(event))
+
+ def terminate(message: String): IO[Unit] =
Review Comment:
Can you provide scaladoc here about exactly the purpose of the terminate
call?
Ex: Is this to be called for normal termination or abnormal termination?
Maybe you should have an abend(msg) or abort(message) or abort(throwable) which
are reserved for abnormal termination. Having a separate call makes the code
more self-documenting, and lets the message strings ignore having to describe
abnormal situations.
##########
server/core/src/main/scala/org.apache.daffodil.debugger.dap/DAPodil.scala:
##########
@@ -382,10 +387,10 @@ class DAPodil(
.variables(DAPodil.VariablesReference(args.variablesReference))
.fold(
Logger[IO]
- .warn(
+ .error(
show"couldn't find variablesReference
${args.variablesReference} in stack ${data}"
) *> // TODO: handle better
- session.sendResponse(request.respondFailure())
+ session.terminate(s"couldn't find variablesReference
${args.variablesReference} in stack ${data}")
Review Comment:
Should this actually ever happen, assuming the system is working properly?
Session.terminate seems like a pretty heavy handed response to an inquiry about
a variable that doesn't exist, but it is warranted if the client already
obtained a list of all the variables earlier and should not be asking about
non-existing variables.
I'm having trouble distinguishing the normal back and forth of server
request/response which has to talk about "errors" meaning DFDL processing
situations, and distinghishing that from defensive coding where the server is
just trying to gracefully exit, providing context. The latter really wants to
be abstracted down to single-line checks, otherwise it is too much code clutter
and people won't want to put it in.
##########
server/core/src/main/scala/org.apache.daffodil.debugger.dap/DAPodil.scala:
##########
@@ -49,8 +49,11 @@ trait DAPSession[Req, Res, Ev] {
def sendResponse(response: Res): IO[Unit]
def sendEvent(event: Ev): IO[Unit]
+ def terminate(message: String): IO[Unit]
}
+case class ErrorEvent(message: String) extends
Events.DebugEvent("daffodil.error")
Review Comment:
Scaladoc please: what's an ErrorEvent for? You should distinguish which of
these is this for?
- An unexpected bug in our code (some throwable got thrown)
- A code invariant check failure (which is also a bug in our code, just one
we're checking for)
- Data doesn't match schema - A parse error which propagates to top level,
causing the parse to fail.
- Data doesn't match schema - A parse error which is causing backtracking
(these need to be visible as people are debugging even though they are often
suppressed subsequently)
- Infoset doesn't match schema (an unparse error - these are always fatal)
- Schema-definition error (some of these are detected at runtime, most at
schema compilation time - the runtime ones are always fatal to a parse/unparse.
The compile time ones, well the execution hasn't started yet. )
Daffodil error classes let you distinguish these. I expect the GUI needs to
preserve that distinction and cause different GUI behaviors, so this shouldn't
just be in the human crafted part of the text of message strings, but a
structured prefix of it that can be reliably parsed. (Ex: the error class name
starts the message being sent back to the client, or something like that).
##########
src/adapter/daffodilEvent.ts:
##########
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as vscode from 'vscode'
+import * as fs from 'fs'
+
+import * as daf from '../daffodil'
+import { ensureFile, tmpFile } from '../utils'
+
+export function handleDebugEvent(e: vscode.DebugSessionCustomEvent) {
+ switch (e.event) {
+ case daf.infosetEvent:
+ let update: daf.InfosetEvent = e.body
+ let path = ensureFile(tmpFile(e.session.id))
+ fs.copyFileSync(path, `${path}.prev`)
+ fs.writeFileSync(path, update.content)
+ break
+ case daf.errorEvent:
+ let error: daf.ErrorEvent = e.body
Review Comment:
Ok, so for now we're just treating all errors as fatal, and getting the
output out to the user somehow.
But is "debugger crashed" really the only situation here? A parse error
propagating to top level isn't a crash of anything. It's normal for a parse to
end that way on bad data.
I think you need to distinguish parse errors (and someday unparse errors) as
well as schema definition errors (which can be detected at runtime) from other
"real" crashes.
For today, as part of getting 1.3.0 released, I'd be happy if the errors at
least were distinguished by error types and not just "debugger crashed" when
they are often "normal" ends to a parse that has simply detected that the data
doesn't match the schema.
##########
server/core/src/main/scala/org.apache.daffodil.debugger.dap/DAPodil.scala:
##########
@@ -358,11 +363,11 @@ class DAPodil(
_ <- data.stack
.findFrame(DAPodil.Frame.Id(args.frameId))
.fold(
- Logger[IO].warn(
+ Logger[IO].error(
Review Comment:
Can you introduce a one-liner method call to replace all these 5 lines of
error-checking and handling?
This looks like an abend to me. You are logging the error. So it appears
this is never supposed to happen, i.e., one is technically always supposed to
find scopes for the frames.
##########
server/core/src/main/scala/org.apache.daffodil.debugger.dap/DAPodil.scala:
##########
@@ -399,7 +404,7 @@ class DAPodil(
for {
variable <- launched.debugee.eval(args)
_ <- variable match {
- case None => session.sendResponse(request.respondFailure())
+ case None => session.terminate("eval failed")
Review Comment:
I guess I need to better understand what session.terminate(...) really
means.
To me, I think nothing should terminate the session ever, except some sort
of bug in our code on one side or the other.
For example, if a parse ends in a parse error, I would expect the UI to
continue to support the user interacting with the server to poke around in the
state more, examine variables, understand what is on the stack, etc. They can't
continue to step the parse any more, but why should the server session
terminate?
Has everything in the state already been brought across to the client so
that the server really truly can't be relevant any longer?
Is there code-comments or other doc about what are the conditions where the
session is supposed to stay intact, and what are the conditions where it is
supposed to terminate normally?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]