diff options
| author | Schuyler Eldridge | 2021-02-10 22:19:09 -0500 |
|---|---|---|
| committer | GitHub | 2021-02-11 03:19:09 +0000 |
| commit | 2b5466c7773c8cd7a08c48aa00d9365cbb205fd2 (patch) | |
| tree | 0bdc21b84f51ab9fb3e1a8e37a708ec2a3b186ef /core/src/main/scala/chisel3 | |
| parent | f41e762830c5af1a92de9d8ee26e2b0de52b76ad (diff) | |
Fix stack trace trimming across Driver/ChiselStage (#1771)
* Handle MemTypeBinding in Analog
Signed-off-by: Schuyler Eldridge <schuyler.eldridge@sifive.com>
* Fix stack trace trimming across ChiselStage
Fix bug in stack trace trimming behavior. Now, the following is what
happens:
1. The Builder, if catching accumulated errors, will now throw a
ChiselException with a Scala-trimmed Stack trace. Previously, this
would throw the full excpetion.
2. The Elaborate phase handles stack trace trimming. By default, any
Throwable thrown during elaboration will have its stack
trace *mutably* trimmed and is rethrown. A logger.error is printed
stating that there was an error during elaboration and how the user
can turn on the full stack trace. If the --full-stacktrace option
is on, then the Throwable is not caught and only the first
logger.error (saying that elaboration failed) will be printed.
3. ChiselStage (the class), ChiselStage$ (the object), and ChiselMain
all inherit the behavior of (2).
Mutable stack trace trimming behavior is moved into an implicit
class (previously this was defined on ChiselException only) so this
can be applied to any Throwable.
No StageErrors are now thrown anymore. However, StageErrors may still
be caught by ChiselMain (since it is a StageMain).
Testing is added for ChiselMain, ChiselStage, and ChiselStage$ to test
all this behavior.
Signed-off-by: Schuyler Eldridge <schuyler.eldridge@sifive.com>
Diffstat (limited to 'core/src/main/scala/chisel3')
| -rw-r--r-- | core/src/main/scala/chisel3/experimental/Analog.scala | 2 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/internal/Error.scala | 77 |
2 files changed, 75 insertions, 4 deletions
diff --git a/core/src/main/scala/chisel3/experimental/Analog.scala b/core/src/main/scala/chisel3/experimental/Analog.scala index 2ce2c86d..6cca81f5 100644 --- a/core/src/main/scala/chisel3/experimental/Analog.scala +++ b/core/src/main/scala/chisel3/experimental/Analog.scala @@ -56,6 +56,7 @@ final class Analog private (private[chisel3] val width: Width) extends Element { case ChildBinding(parent) => parent.topBinding // See https://github.com/freechipsproject/chisel3/pull/946 case SampleElementBinding(parent) => parent.topBinding + case a: MemTypeBinding[_] => a } targetTopBinding match { @@ -83,4 +84,3 @@ final class Analog private (private[chisel3] val width: Width) extends Element { object Analog { def apply(width: Width): Analog = new Analog(width) } - diff --git a/core/src/main/scala/chisel3/internal/Error.scala b/core/src/main/scala/chisel3/internal/Error.scala index 60bb6e2b..134f4c87 100644 --- a/core/src/main/scala/chisel3/internal/Error.scala +++ b/core/src/main/scala/chisel3/internal/Error.scala @@ -5,12 +5,82 @@ package chisel3.internal import scala.annotation.tailrec import scala.collection.mutable.{ArrayBuffer, LinkedHashMap} -class ChiselException(message: String, cause: Throwable = null) extends Exception(message, cause) { +object ExceptionHelpers { + + /** Root packages that are not typically relevant to Chisel user code. */ + final val packageTrimlist: Set[String] = Set("chisel3", "scala", "java", "jdk", "sun", "sbt") + + /** The object name of Chisel's internal `Builder`. */ + final val builderName: String = chisel3.internal.Builder.getClass.getName + + /** Return a stack trace element that looks like `... (someMessage)`. + * @param message an optional message to include + */ + def ellipsis(message: Option[String] = None): StackTraceElement = + new StackTraceElement("..", " ", message.getOrElse(""), -1) + + /** Utility methods that can be added to exceptions. + */ + implicit class ThrowableHelpers(throwable: Throwable) { + + /** For an exception, mutably trim a stack trace to user code only. + * + * This does the following actions to the stack trace: + * + * 1. From the top, remove elements while the (root) package matches the packageTrimlist + * 2. Optionally, from the bottom, remove elements until the class matches an anchor + * 3. From the anchor (or the bottom), remove elements while the (root) package matches the packageTrimlist + * + * @param packageTrimlist packages that should be removed from the stack trace + * @param anchor an optional class name at which user execution might begin, e.g., a main object + * @return nothing as this mutates the exception directly + */ + def trimStackTraceToUserCode( + packageTrimlist: Set[String] = packageTrimlist, + anchor: Option[String] = Some(builderName) + ): Unit = { + def inTrimlist(ste: StackTraceElement) = { + val packageName = ste.getClassName().takeWhile(_ != '.') + packageTrimlist.contains(packageName) + } + + // Step 1: Remove elements from the top in the package trimlist + ((a: Array[StackTraceElement]) => a.view.dropWhile(inTrimlist)) + // Step 2: Optionally remove elements from the bottom until the anchor + .andThen(_.reverse) + .andThen( a => + anchor match { + case Some(b) => a.dropWhile(ste => !ste.getClassName.startsWith(b)) + case None => a + } + ) + // Step 3: Remove elements from the bottom in the package trimlist + .andThen(_.dropWhile(inTrimlist)) + // Step 4: Reverse back to the original order + .andThen(_.reverse.toArray) + // Step 5: Add ellipsis stack trace elements and "--full-stacktrace" info + .andThen(a => + ellipsis() +: + a :+ + ellipsis() :+ + ellipsis(Some("Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace"))) + // Step 5: Mutate the stack trace in this exception + .andThen(throwable.setStackTrace(_)) + .apply(throwable.getStackTrace) + } + + } + +} + +class ChiselException(message: String, cause: Throwable = null) extends Exception(message, cause, true, true) { /** Package names whose stack trace elements should be trimmed when generating a trimmed stack trace */ + @deprecated("Use ExceptionHelpers.packageTrimlist. This will be removed in Chisel 3.6", "3.5") val blacklistPackages: Set[String] = Set("chisel3", "scala", "java", "sun", "sbt") /** The object name of Chisel's internal `Builder`. Everything stack trace element after this will be trimmed. */ + @deprecated("Use ExceptionHelpers.builderName. This will be removed in Chisel 3.6", "3.5") val builderName: String = chisel3.internal.Builder.getClass.getName /** Examine a [[Throwable]], to extract all its causes. Innermost cause is first. @@ -27,7 +97,7 @@ class ChiselException(message: String, cause: Throwable = null) extends Exceptio /** Returns true if an exception contains */ private def containsBuilder(throwable: Throwable): Boolean = throwable.getStackTrace().collectFirst { - case ste if ste.getClassName().startsWith(builderName) => throwable + case ste if ste.getClassName().startsWith(ExceptionHelpers.builderName) => throwable }.isDefined /** Examine this [[ChiselException]] and it's causes for the first [[Throwable]] that contains a stack trace including @@ -142,7 +212,8 @@ private[chisel3] class ErrorLog { } if (!allErrors.isEmpty) { - throwException("Fatal errors during hardware elaboration") + throw new ChiselException("Fatal errors during hardware elaboration. Look above for error list.") + with scala.util.control.NoStackTrace } else { // No fatal errors, clear accumulated warnings since they've been reported errors.clear() |
