Skip to content

Commit

Permalink
Merge pull request #12 from VirtusLab/jmh
Browse files Browse the repository at this point in the history
Add JMH tests for pretty stacktraces
  • Loading branch information
BarkingBad authored Sep 6, 2021
2 parents a45a0ce + 64f830d commit 73e3762
Show file tree
Hide file tree
Showing 12 changed files with 87 additions and 55 deletions.
10 changes: 5 additions & 5 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
enablePlugins(JmhPlugin)

inThisBuild(List(
sonatypeProfileName := "org.virtuslab",
organization := "org.virtuslab",
Expand All @@ -19,20 +21,18 @@ inThisBuild(List(
)
))

Global / excludeLintKeys += ThisBuild / organization

val scala3Version = "3.1.0-RC1-bin-20210820-68044a6-NIGHTLY"
val Examples = config("examples") extend Compile

lazy val root = project
.in(file("."))
.configs(Examples)
.enablePlugins(JmhPlugin)
.settings(
organization := "org.virtuslab",
name := "pretty-stacktraces",

scalaVersion := scala3Version,
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test",
libraryDependencies += "org.scala-lang" %% "scala3-tasty-inspector" % scalaVersion.value,

inConfig(Examples)(Defaults.compileSettings),
Examples / sourceDirectory := baseDirectory.value / "src" / "examples",
)
1 change: 1 addition & 0 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
addSbtPlugin("com.geirsson" % "sbt-ci-release" % "1.5.7")
addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3")
Binary file not shown.
34 changes: 0 additions & 34 deletions src/examples/scala/com/virtuslab/stacktraces/tests/Main.scala

This file was deleted.

46 changes: 46 additions & 0 deletions src/jmh/scala/com/virtuslab/stacktraces/benchmarks/Main.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.virtuslab.stacktraces
package benchmarks

import org.virtuslab.stacktraces.core.Stacktraces
import org.virtuslab.stacktraces.printer.PrettyExceptionPrinter

// Must not be in default package
import java.util.concurrent.TimeUnit

import org.openjdk.jmh.annotations.{Benchmark, BenchmarkMode, Mode, OutputTimeUnit, State, Scope}


trait A:
def doSth(n: Int) = doSthNested(n)
def doSthNested(n: Int) = doSthInlined(n)
extension[T] (t: T) def let[R](l: (T) => R): R = l(t)
inline def doSthInlined(n: Int): Int =
if math.random < n/10.0 then n.let {
n => n.let { n => throw RuntimeException("error") }
}
n

class B extends A

extension (b: B)
def !(n: Int): Int = b.doSth(n)

@OutputTimeUnit(TimeUnit.MILLISECONDS)
@BenchmarkMode(Array(Mode.AverageTime))
@State(Scope.Thread)
class Benchmarks:

@Benchmark
@BenchmarkMode(Array(Mode.AverageTime))
@OutputTimeUnit(TimeUnit.MILLISECONDS)
def method: Unit =
try
val x = (0 to 10).flatMap {
n => (0 to n).map {
n => B() ! n.toInt
}
}
catch
case e: Exception =>
val prettyStackTrace = convertToPrettyStackTraceWithStdlib(e)
// PrettyExceptionPrinter.printStacktrace(prettyStackTrace)
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package org.virtuslab.stacktraces
package parser

import org.virtuslab.stacktraces.parser.StacktraceParser
import org.virtuslab.stacktraces.core.Stacktraces
Expand Down Expand Up @@ -36,7 +37,7 @@ object StacktraceParserTest:
errorOrStacktrace match
case Left(msg) => throw AssertionError(msg)
case Right(stacktrace) =>
val prettyStackTrace = Stacktraces.convertToPrettyStackTrace(stacktrace)
val prettyStackTrace = convertToPrettyStackTraceWithStdlib(stacktrace)
/*
* This stacktrace is subject to Tasty files generated for BasicTest.scala, if you see some weird output, probably BasicTest.scala diverged.
* Nonetheless, we can count test as passing correctly if it hits Right branch.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package org.virtuslab.stacktraces
package tests

import org.virtuslab.stacktraces.core.Stacktraces
import org.virtuslab.stacktraces.printer.PrettyExceptionPrinter
import org.virtuslab.stacktraces.util.TestExecutor


trait A:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.virtuslab.stacktraces.util
package org.virtuslab.stacktraces

import org.virtuslab.stacktraces.core.Stacktraces
import org.virtuslab.stacktraces.printer.PrettyExceptionPrinter
Expand All @@ -10,5 +10,8 @@ object TestExecutor:
test()
catch
case e: Exception =>
val prettyStackTrace = Stacktraces.convertToPrettyStackTrace(e)
val prettyStackTrace = convertToPrettyStackTraceWithStdlib(e)
PrettyExceptionPrinter.printStacktrace(prettyStackTrace)

def convertToPrettyStackTraceWithStdlib(e: Exception) =
Stacktraces.convertToPrettyStackTrace(e, Seq("scala-library_3-3.1.0-RC1-bin-SNAPSHOT.jar"))
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package org.virtuslab.stacktraces.core
import org.virtuslab.stacktraces.model.ClasspathWrapper
import org.virtuslab.stacktraces.model.TastyWrapper
import org.virtuslab.stacktraces.model.PrettyException
import org.virtuslab.stacktraces.model.PrettyStackTraceElement
import org.virtuslab.stacktraces.model.ElementType
import org.virtuslab.stacktraces.io.TastyFilesLocator

Expand All @@ -19,8 +18,8 @@ import java.nio.file.Paths

object Stacktraces:

def convertToPrettyStackTrace(e: Throwable): PrettyException =
val tastyFilesLocator = TastyFilesLocator(Thread.currentThread().getContextClassLoader)
def convertToPrettyStackTrace(e: Throwable, additionalClasspath: Seq[String] = Nil): PrettyException =
val tastyFilesLocator = TastyFilesLocator(Thread.currentThread().getContextClassLoader, additionalClasspath)
val st = filterInternalStackFrames(e.getStackTrace)
val ctp = tastyFilesLocator.classNameToPath(st.map(_.getClassName))
val tastyFiles = tastyFilesLocator.tastyFilesFromStackTrace(ctp)
Expand All @@ -31,4 +30,4 @@ object Stacktraces:
st.sliding(2).toList.flatMap {
case Array(fs, sc) =>
if sc.getMethodName.contains("$adapted") then Nil else List(fs)
}
} :+ st.last
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ import org.virtuslab.stacktraces.model.TastyWrapper
object StacktracesInspector:
def inspectStackTrace(st: List[StackTraceElement], tastyFiles: List[TastyWrapper], ctp: Map[String, String]): List[PrettyStackTraceElement] =
val stacktracesInspector = StacktracesInspector(st, ctp)
val (tastys, jars) = tastyFiles.map(_.file.toPath.toString).partition(_.endsWith(".tasty"))
TastyInspector.inspectAllTastyFiles(tastys, jars, Nil)(stacktracesInspector)
val tastys = tastyFiles.map(_.file.toPath.toString)
val dependencies = tastyFiles.flatMap(_.jar).map(_.toPath.toString)
TastyInspector.inspectAllTastyFiles(tastys, Nil, dependencies)(stacktracesInspector)
stacktracesInspector.prettyStackTraceElements.toList


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,36 @@ import java.nio.file.Files
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import java.net.JarURLConnection
import java.net.URLClassLoader
import java.net.URL

class TastyFilesLocator(classLoader: ClassLoader):
class TastyFilesLocator(classLoader: ClassLoader, additionalClasspath: Seq[String]):

val newClassloader = URLClassLoader(additionalClasspath.map(File(_).toURI.toURL).toArray, classLoader)

def tastyFilesFromStackTrace(classNameToPath: Map[String, String]): List[TastyWrapper] =
classNameToPath.values.toList.distinct.flatMap { clPath =>
val url = classLoader.getResource(clPath + ".tasty")
val url = newClassloader.getResource(clPath + ".tasty")
if url != null then
url.getProtocol match

// Get temporary copy of the tasty file from the URL
val tastyFile: File =
val inputStream = url.openStream
val tf: Path = Files.createTempFile("pretty-stacktraces", ".tasty")
tf.toFile.deleteOnExit()
Files.copy(inputStream, tf, StandardCopyOption.REPLACE_EXISTING)
tf.toFile

// Get path of the external jar (if any)
val extraJar = url.getProtocol match
case "jar" =>
val outerUrl = url.openConnection.asInstanceOf[JarURLConnection].getJarFileURL
Some(TastyWrapper(Paths.get(outerUrl.toURI).toFile, null))
Some(Paths.get(outerUrl.toURI).toFile)
case "file" =>
Some(TastyWrapper(Paths.get(url.toURI).toFile, null))
None
case _ =>
None
Some(TastyWrapper(tastyFile, extraJar))
else
None
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/com/virtuslab/stacktraces/model/model.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.virtuslab.stacktraces.model
import java.io.File

case class ClasspathWrapper(file: File, jarName: Option[String])
case class TastyWrapper(file: File, jarName: Option[String])
case class TastyWrapper(file: File, jar: Option[File])
case class PrettyException(original: Throwable, prettyStackTrace: List[PrettyStackTraceElement])

enum ElementType(val name: String):
Expand Down

0 comments on commit 73e3762

Please sign in to comment.