Skip to content

Commit

Permalink
Dont wrap elaboration annotations (bp #1374) (#1378)
Browse files Browse the repository at this point in the history
* Wrap elaboration in ChiselException

Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>

squash! Wrap elaboration in ChiselException

(cherry picked from commit d67c410)

* Report trimmed stack trace of Builder cause

Changes the behavior of ChiselException stack trace trimming to use
either the first exception that includes a method from the Builder or
the outer exception.

Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
(cherry picked from commit 7397472)

* Test nested ChiselException in ChiselMain

Adds two tests:

  1. Test that an internal requirement failure (a bare exception) inside
     a Builder is properly reported/trimmed by ChsielStage/ChiselMain
  2. Test that the full stack trace includes the ChiselException

Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
(cherry picked from commit 18520bf)

* Code style improvement

Co-authored-by: Jack Koenig <koenig@sifive.com>
Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
(cherry picked from commit 30a0a5f)

* Safer generation of ChiselException.builderName

Change ChiselException.builderName to compute the name of Chisel's
internal Builder as opposed to hard-coding this with a string.

Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
(cherry picked from commit 969a56d)

* Add Scaladoc to ChiselException

Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
(cherry picked from commit 3171cdf)

Co-authored-by: Schuyler Eldridge <schuyler.eldridge@ibm.com>
  • Loading branch information
mergify[bot] and seldridge authored Mar 19, 2020
1 parent 063a23a commit e14dc6d
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 17 deletions.
61 changes: 52 additions & 9 deletions chiselFrontend/src/main/scala/chisel3/internal/Error.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,77 @@

package chisel3.internal

import scala.annotation.tailrec
import scala.collection.mutable.{ArrayBuffer, LinkedHashMap}

class ChiselException(message: String, cause: Throwable = null) extends Exception(message, cause) {

val blacklistPackages = Set("chisel3", "scala", "java", "sun", "sbt")
val builderName = "chisel3.internal.Builder"
/** Package names whose stack trace elements should be trimmed when generating a trimmed stack trace */
val blacklistPackages: Set[String] = Set("chisel3", "scala", "java", "sun", "sbt")

/** trims the top of the stack of elements belonging to [[blacklistPackages]]
* then trims the bottom elements until it reaches [[builderName]]
* then continues trimming elements belonging to [[blacklistPackages]]
/** The object name of Chisel's internal `Builder`. Everything stack trace element after this will be trimmed. */
val builderName: String = chisel3.internal.Builder.getClass.getName

/** Examine a [[Throwable]], recursively searching it's causes, for the first [[Throwable]] that contains a stack
* trace including a specific class name.
* @param throwable the root exception
* @param className a class name string to search for
* @return [[Some]] exception if the class name was found, [[None]] otherwise
*/
@tailrec
private def findCause(throwable: Throwable, className: String): Option[Throwable] =
throwable.getStackTrace().collectFirst {
case ste if ste.getClassName().startsWith(className) => throwable
} match {
case a: Some[_] => a
case None => throwable.getCause() match {
case null => None
case cause: Throwable => findCause(cause, className)
}
}

/** Examine this [[ChiselException]] and it's causes for the first [[Throwable]] that contains a stack trace including
* a stack trace element whose declaring class is the [[builderName]]. If no such element exists, return this
* [[ChiselException]].
*/
def trimmedStackTrace: Array[StackTraceElement] = {
private lazy val likelyCause: Throwable = findCause(this, builderName).getOrElse(this)

/** For an exception, return a stack trace trimmed to user code only
*
* This does the following actions:
*
* 1. Trims the top of the stack trace while elements match [[blacklistPackages]]
* 2. Trims the bottom of the stack trace until an element matches [[builderName]]
* 3. Trims from the [[builderName]] all [[blacklistPackages]]
*
* @param throwable the exception whose stack trace should be trimmed
* @return an array of stack trace elements
*/
private def trimmedStackTrace(throwable: Throwable): Array[StackTraceElement] = {
def isBlacklisted(ste: StackTraceElement) = {
val packageName = ste.getClassName().takeWhile(_ != '.')
blacklistPackages.contains(packageName)
}

val trimmedLeft = getStackTrace().view.dropWhile(isBlacklisted)
val trimmedLeft = throwable.getStackTrace().view.dropWhile(isBlacklisted)
val trimmedReverse = trimmedLeft.reverse
.dropWhile(ste => !ste.getClassName.startsWith(builderName))
.dropWhile(isBlacklisted)
trimmedReverse.reverse.toArray
}

/** trims the top of the stack of elements belonging to [[blacklistPackages]]
* then trims the bottom elements until it reaches [[builderName]]
* then continues trimming elements belonging to [[blacklistPackages]]
*/
@deprecated("This method will be removed in 3.4", "3.3")
def trimmedStackTrace: Array[StackTraceElement] = trimmedStackTrace(this)

def chiselStackTrace: String = {
val trimmed = trimmedStackTrace
val trimmed = trimmedStackTrace(likelyCause)

val sw = new java.io.StringWriter
sw.write(toString + "\n")
sw.write(likelyCause.toString + "\n")
sw.write("\t...\n")
trimmed.foreach(ste => sw.write(s"\tat $ste\n"))
sw.write("\t... (Stack trace trimmed to user code only, rerun with --full-stacktrace if you wish to see the full stack trace)\n") // scalastyle:ignore line.size.limit
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/chisel3/stage/ChiselAnnotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ case class ChiselGeneratorAnnotation(gen: () => RawModule) extends NoTargetAnnot
} catch {
case e @ (_: OptionsException | _: ChiselException) => throw e
case e: Throwable =>
throw new OptionsException(s"Exception thrown when elaborating ChiselGeneratorAnnotation", e)
throw new ChiselException(s"Exception thrown when elaborating ChiselGeneratorAnnotation", e)
}

}
Expand Down
5 changes: 2 additions & 3 deletions src/test/scala/chiselTests/ChiselSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import org.scalatest.prop._
import org.scalacheck._
import chisel3._
import chisel3.testers._
import firrtl.options.OptionsException
import firrtl.{AnnotationSeq, CommonOptions, ExecutionOptionsManager, FirrtlExecutionFailure, FirrtlExecutionSuccess, HasFirrtlOptions}
import firrtl.util.BackendCompilationUtilities
import java.io.ByteArrayOutputStream
Expand Down Expand Up @@ -100,7 +99,7 @@ class ChiselTestUtilitiesSpec extends ChiselFlatSpec {
import org.scalatest.exceptions.TestFailedException
// Who tests the testers?
"assertKnownWidth" should "error when the expected width is wrong" in {
val caught = intercept[OptionsException] {
val caught = intercept[ChiselException] {
assertKnownWidth(7) {
Wire(UInt(8.W))
}
Expand All @@ -123,7 +122,7 @@ class ChiselTestUtilitiesSpec extends ChiselFlatSpec {
}

"assertInferredWidth" should "error if the width is known" in {
val caught = intercept[OptionsException] {
val caught = intercept[ChiselException] {
assertInferredWidth(8) {
Wire(UInt(8.W))
}
Expand Down
16 changes: 12 additions & 4 deletions src/test/scala/chiselTests/OneHotMuxSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package chiselTests

import chisel3._
import chisel3.experimental.FixedPoint
import chisel3.internal.ChiselException
import chisel3.testers.BasicTester
import chisel3.util.{Mux1H, UIntToOH}
import org.scalatest._
Expand Down Expand Up @@ -31,23 +32,31 @@ class OneHotMuxSpec extends FreeSpec with Matchers with ChiselRunners {
assertTesterPasses(new ParameterizedAggregateOneHotTester)
}
"simple one hot mux with all aggregates containing inferred width fixed values should NOT work" in {
intercept[ChiselException] {
intercept [ChiselException] {
assertTesterPasses(new InferredWidthAggregateOneHotTester)
}
}
"simple one hot mux with all fixed width bundles but with different bundles should Not work" in {
intercept[IllegalArgumentException] {
try {
assertTesterPasses(new DifferentBundleOneHotTester)
} catch {
case a: ChiselException => a.getCause match {
case _: IllegalArgumentException =>
}
}
}
"UIntToOH with output width greater than 2^(input width)" in {
assertTesterPasses(new UIntToOHTester)
}
"UIntToOH should not accept width of zero (until zero-width wires are fixed" in {
intercept[java.lang.IllegalArgumentException] {
try {
assertTesterPasses(new BasicTester {
val out = UIntToOH(0.U, 0)
})
} catch {
case a: ChiselException => a.getCause match {
case _: IllegalArgumentException =>
}
}
}

Expand Down Expand Up @@ -305,4 +314,3 @@ class UIntToOHTester extends BasicTester {

stop()
}

18 changes: 18 additions & 0 deletions src/test/scala/chiselTests/stage/ChiselMainSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ object ChiselMainSpec {
out := in
}

/** A module that fails a requirement */
class FailingRequirementModule extends RawModule {
require(false)
}

}

class ChiselMainSpec extends FeatureSpec with GivenWhenThen with Matchers with chiselTests.Utils {
Expand Down Expand Up @@ -114,4 +119,17 @@ class ChiselMainSpec extends FeatureSpec with GivenWhenThen with Matchers with c
).foreach(runStageExpectFiles)
}

feature("Report properly trimmed stack traces") {
Seq(
ChiselMainTest(args = Array("-X", "low"),
generator = Some(classOf[FailingRequirementModule]),
stdout = Some("requirement failed"),
result = 1),
ChiselMainTest(args = Array("-X", "low", "--full-stacktrace"),
generator = Some(classOf[FailingRequirementModule]),
stdout = Some("chisel3.internal.ChiselException"),
result = 1)
).foreach(runStageExpectFiles)
}

}

0 comments on commit e14dc6d

Please sign in to comment.