diff --git a/core/src/main/scala/chisel3/internal/Error.scala b/core/src/main/scala/chisel3/internal/Error.scala index b0ec392af3d..adca04e2760 100644 --- a/core/src/main/scala/chisel3/internal/Error.scala +++ b/core/src/main/scala/chisel3/internal/Error.scala @@ -54,32 +54,32 @@ object ExceptionHelpers { packageTrimlist.contains(packageName) } - // Step 1: Remove elements from the top in the package trimlist val trimStackTrace = - ((a: Array[StackTraceElement]) => a.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) + (a: Array[StackTraceElement]) => { + // Step 1: Remove elements from the top in the package trimlist + // Only include ellipsis at top if something is dropped from top + val droppedFromTop = inTrimlist(a.head) + val trimmed = + a.dropWhile(inTrimlist) + // Step 2: Optionally remove elements from the bottom until the anchor + .reverse + .dropWhile(ste => anchor.map(b => !ste.getClassName.startsWith(b)).getOrElse(false)) + // Step 3: Remove elements from the bottom in the package trimlist + .dropWhile(inTrimlist) + // Step 4: Reverse back to the original order + .reverse + .toArray // Step 5: Add ellipsis stack trace elements and "--full-stacktrace" info - .andThen(a => - ellipsis() +: - a :+ + val withEllipses = + Option.when(droppedFromTop)(ellipsis()) ++: + trimmed :+ 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(_)) + // Step 6: Mutate the stack trace in this exception + throwable.setStackTrace(withEllipses) + } val stackTrace = throwable.getStackTrace if (stackTrace.nonEmpty) { diff --git a/src/test/scala/circtTests/stage/ChiselStageSpec.scala b/src/test/scala/circtTests/stage/ChiselStageSpec.scala index e8509552ded..f21712e9fa2 100644 --- a/src/test/scala/circtTests/stage/ChiselStageSpec.scala +++ b/src/test/scala/circtTests/stage/ChiselStageSpec.scala @@ -79,8 +79,12 @@ object ChiselStageSpec { }) } + class UserAssertionModule extends RawModule { + assert(false, "User had an assertion") + } + class UserExceptionModule extends RawModule { - assert(false, "User threw an exception") + throw new Exception("User threw an exception") } class UserExceptionNoStackTrace extends RawModule { @@ -410,7 +414,7 @@ class ChiselStageSpec extends AnyFunSpec with Matchers with chiselTests.Utils { (new ChiselStage) .execute( Array("--target", "chirrtl"), - Seq(ChiselGeneratorAnnotation(() => new ChiselStageSpec.UserExceptionModule)) + Seq(ChiselGeneratorAnnotation(() => new ChiselStageSpec.UserAssertionModule)) ) } @@ -418,14 +422,39 @@ class ChiselStageSpec extends AnyFunSpec with Matchers with chiselTests.Utils { val message = exception.getMessage info("The exception includes the user's message") - message should include("User threw an exception") + message should include("User had an assertion") - val stackTrace = exception.getStackTrace.mkString("\n") - info("The stack trace is trimmed") - (stackTrace should not).include("java") + val stackTrace = exception.getStackTrace + info("The stack trace is trimmed at the top") + stackTrace.head.toString should include("...") + + info("The stack trace is trimmed at the bottom") + stackTrace.last.toString should include("...") info("The stack trace include information about running --full-stacktrace") - stackTrace should include("--full-stacktrace") + stackTrace.last.toString should include("--full-stacktrace") + } + + it("should NOT truncate the top of the stack trace if it points to user code") { + info("The user's Exception was thrown") + val exception = intercept[Exception] { + (new ChiselStage) + .execute( + Array("--target", "chirrtl"), + Seq(ChiselGeneratorAnnotation(() => new ChiselStageSpec.UserExceptionModule)) + ) + } + + val message = exception.getMessage + info("The exception includes the user's message") + message should include("User threw an exception") + + val stackTrace = exception.getStackTrace + info("The stack trace is NOT trimmed at the top") + stackTrace.head.toString shouldNot include("...") + + info("The stack trace is trimmed at the bottom") + stackTrace.last.toString should include("...") } it("""should not truncate a user exception with "--full-stacktrace"""") { @@ -433,7 +462,7 @@ class ChiselStageSpec extends AnyFunSpec with Matchers with chiselTests.Utils { val exception = intercept[java.lang.AssertionError] { (new ChiselStage).execute( Array("--target", "chirrtl", "--full-stacktrace"), - Seq(ChiselGeneratorAnnotation(() => new ChiselStageSpec.UserExceptionModule)) + Seq(ChiselGeneratorAnnotation(() => new ChiselStageSpec.UserAssertionModule)) ) } @@ -441,7 +470,7 @@ class ChiselStageSpec extends AnyFunSpec with Matchers with chiselTests.Utils { val message = exception.getMessage info("The exception includes the user's message") - message should include("User threw an exception") + message should include("User had an assertion") info("The stack trace is not trimmed") exception.getStackTrace.mkString("\n") should include("java") @@ -527,7 +556,7 @@ class ChiselStageSpec extends AnyFunSpec with Matchers with chiselTests.Utils { val lines = stdout.split("\n") // Fuzzy includes aren't ideal but there is ANSI color in these strings that is hard to match lines(0) should include( - "src/test/scala/circtTests/stage/ChiselStageSpec.scala 91:9: Negative shift amounts are illegal (got -1)" + "src/test/scala/circtTests/stage/ChiselStageSpec.scala 95:9: Negative shift amounts are illegal (got -1)" ) lines(1) should include(" 3.U >> -1") lines(2) should include(" ^") @@ -548,7 +577,7 @@ class ChiselStageSpec extends AnyFunSpec with Matchers with chiselTests.Utils { // Fuzzy includes aren't ideal but there is ANSI color in these strings that is hard to match lines.size should equal(2) lines(0) should include( - "src/test/scala/circtTests/stage/ChiselStageSpec.scala 91:9: Negative shift amounts are illegal (got -1)" + "src/test/scala/circtTests/stage/ChiselStageSpec.scala 95:9: Negative shift amounts are illegal (got -1)" ) (lines(1) should not).include("3.U >> -1") } @@ -1187,6 +1216,27 @@ class ChiselStageSpec extends AnyFunSpec with Matchers with chiselTests.Utils { it("should truncate a user exception") { info("The user's java.lang.AssertionError was thrown") val exception = intercept[java.lang.AssertionError] { + ChiselStage.emitCHIRRTL(new ChiselStageSpec.UserAssertionModule) + } + + val message = exception.getMessage + info("The exception includes the user's message") + message should include("User had an assertion") + + val stackTrace = exception.getStackTrace + info("The stack trace is trimmed at the top") + stackTrace.head.toString should include("...") + + info("The stack trace is trimmed at the bottom") + stackTrace.last.toString should include("...") + + info("The stack trace include information about running --full-stacktrace") + stackTrace.last.toString should include("--full-stacktrace") + } + + it("should NOT truncate the top of the stack trace if it points to user code") { + info("The user's Exception was thrown") + val exception = intercept[Exception] { ChiselStage.emitCHIRRTL(new ChiselStageSpec.UserExceptionModule) } @@ -1194,8 +1244,12 @@ class ChiselStageSpec extends AnyFunSpec with Matchers with chiselTests.Utils { info("The exception includes the user's message") message should include("User threw an exception") - info("The stack trace is trimmed") - (exception.getStackTrace.mkString("\n") should not).include("java") + val stackTrace = exception.getStackTrace + info("The stack trace is NOT trimmed at the top") + stackTrace.head.toString shouldNot include("...") + + info("The stack trace is trimmed at the bottom") + stackTrace.last.toString should include("...") } it("should NOT add a stack trace to an exception with no stack trace") {