Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mechanism to gather stacktraces if job is deadlock/slagging #32

Open
wants to merge 4 commits into
base: 2.13.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ mainClass in assembly := Some("org.perftester.ProfileMain")
// scalafmt configuration
scalafmtOnCompile in ThisBuild := true // all projects
scalafmtTestOnCompile in ThisBuild := true // all projects

TaskKey[Unit]("exitSBT") := ???
9 changes: 9 additions & 0 deletions corpus/compiler-bridge/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
scalaSource.in(Compile) := baseDirectory.value / "src"
scalaVersion := "2.13.0-M4"
libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value

scalacOptions in Compile ++= Seq("-encoding", "UTF-8", "-target:jvm-1.8", "-feature", "-unchecked", "-Xlog-reflective-calls", "-Xlint", "-deprecation")
javacOptions in compile ++= Seq("-encoding", "UTF-8", "-source", "1.8", "-target", "1.8", "-Xlint:unchecked", "-XDignore.symbol.file", "-Xlint:deprecation")

Global / serverConnectionType := ConnectionType.Tcp
Global / serverAuthentication := Set(ServerAuthentication.Token)
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions corpus/compiler-bridge/project/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sbt.version=1.1.1
5 changes: 5 additions & 0 deletions corpus/compiler-bridge/src/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Manifest-Version: 1.0
Specification-Vendor: org.scala-sbt
Specification-Title: Compiler Bridge
Specification-Version: 1.1.1

89 changes: 89 additions & 0 deletions corpus/compiler-bridge/src/xsbt/API.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Zinc - The incremental compiler for Scala.
* Copyright 2011 - 2017, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* This software is released under the terms written in LICENSE.
*/

package xsbt

import scala.tools.nsc.Phase
import scala.tools.nsc.symtab.Flags
import xsbti.api._

object API {
val name = "xsbt-api"
}

final class API(val global: CallbackGlobal) extends Compat with GlobalHelpers {
import global._

def newPhase(prev: Phase) = new ApiPhase(prev)
class ApiPhase(prev: Phase) extends GlobalPhase(prev) {
override def description = "Extracts the public API from source files."
def name = API.name
override def run(): Unit = {
val start = System.currentTimeMillis
super.run()
callback.apiPhaseCompleted()
val stop = System.currentTimeMillis
debuglog("API phase took : " + ((stop - start) / 1000.0) + " s")
}

def apply(unit: global.CompilationUnit): Unit = processUnit(unit)

private def processUnit(unit: CompilationUnit) = if (!unit.isJava) processScalaUnit(unit)

private def processScalaUnit(unit: CompilationUnit): Unit = {
val sourceFile = unit.source.file.file
debuglog("Traversing " + sourceFile)
callback.startSource(sourceFile)
val extractApi = new ExtractAPI[global.type](global, sourceFile)
val traverser = new TopLevelHandler(extractApi)
traverser.apply(unit.body)

val extractUsedNames = new ExtractUsedNames[global.type](global)
extractUsedNames.extractAndReport(unit)

val classApis = traverser.allNonLocalClasses
val mainClasses = traverser.mainClasses

classApis.foreach(callback.api(sourceFile, _))
mainClasses.foreach(callback.mainClass(sourceFile, _))
}
}

private final class TopLevelHandler(extractApi: ExtractAPI[global.type])
extends TopLevelTraverser {
def allNonLocalClasses: Set[ClassLike] = {
extractApi.allExtractedNonLocalClasses
}

def mainClasses: Set[String] = extractApi.mainClasses

def `class`(c: Symbol): Unit = {
extractApi.extractAllClassesOf(c.owner, c)
}
}

private abstract class TopLevelTraverser extends Traverser {
def `class`(s: Symbol): Unit
override def traverse(tree: Tree): Unit = {
tree match {
case (_: ClassDef | _: ModuleDef) if isTopLevel(tree.symbol) => `class`(tree.symbol)
case _: PackageDef =>
super.traverse(tree)
case _ =>
}
}
def isTopLevel(sym: Symbol): Boolean = {
!ignoredSymbol(sym) &&
sym.isStatic &&
!sym.isImplClass &&
!sym.hasFlag(Flags.SYNTHETIC) &&
!sym.hasFlag(Flags.JAVA) &&
!sym.isNestedClass
}
}

}
61 changes: 61 additions & 0 deletions corpus/compiler-bridge/src/xsbt/Analyzer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Zinc - The incremental compiler for Scala.
* Copyright 2011 - 2017, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* This software is released under the terms written in LICENSE.
*/

package xsbt

import scala.tools.nsc.Phase

object Analyzer {
def name = "xsbt-analyzer"
}
final class Analyzer(val global: CallbackGlobal) extends LocateClassFile {
import global._

def newPhase(prev: Phase): Phase = new AnalyzerPhase(prev)
private class AnalyzerPhase(prev: Phase) extends GlobalPhase(prev) {
override def description =
"Finds concrete instances of provided superclasses, and application entry points."
def name = Analyzer.name
def apply(unit: CompilationUnit): Unit = {
if (!unit.isJava) {
val sourceFile = unit.source.file.file
// build list of generated classes
for (iclass <- unit.icode) {
val sym = iclass.symbol
def addGenerated(separatorRequired: Boolean): Unit = {
for (classFile <- outputDirs map (fileForClass(_, sym, separatorRequired)) find (_.exists)) {
assert(sym.isClass, s"${sym.fullName} is not a class")
// we would like to use Symbol.isLocalClass but that relies on Symbol.owner which
// is lost at this point due to lambdalift
// the LocalNonLocalClass.isLocal can return None, which means, we're asking about
// the class it has not seen before. How's that possible given we're performing a lookup
// for every declared class in Dependency phase? We can have new classes introduced after
// Dependency phase has ran. For example, the implementation classes for traits.
val isLocalClass = localToNonLocalClass.isLocal(sym).getOrElse(true)
if (!isLocalClass) {
val srcClassName = classNameAsString(sym)
val binaryClassName = flatclassName(sym, '.', separatorRequired)
callback.generatedNonLocalClass(sourceFile,
classFile,
binaryClassName,
srcClassName)
} else {
callback.generatedLocalClass(sourceFile, classFile)
}
}
}
if (sym.isModuleClass && !sym.isImplClass) {
if (isTopLevelModule(sym) && sym.companionClass == NoSymbol)
addGenerated(false)
addGenerated(true)
} else
addGenerated(false)
}
}
}
}
}
169 changes: 169 additions & 0 deletions corpus/compiler-bridge/src/xsbt/CallbackGlobal.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* Zinc - The incremental compiler for Scala.
* Copyright 2011 - 2017, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* This software is released under the terms written in LICENSE.
*/

package xsbt

import xsbti.{ AnalysisCallback, Severity }
import xsbti.compile._

import scala.tools.nsc._
import io.AbstractFile
import java.io.File

/** Defines the interface of the incremental compiler hiding implementation details. */
sealed abstract class CallbackGlobal(settings: Settings,
reporter: reporters.Reporter,
output: Output)
extends Global(settings, reporter) {

def callback: AnalysisCallback
def findClass(name: String): Option[(AbstractFile, Boolean)]

lazy val outputDirs: Iterable[File] = {
output match {
case single: SingleOutput => List(single.getOutputDirectory)
// Use Stream instead of List because Analyzer maps intensively over the directories
case multi: MultipleOutput => multi.getOutputGroups.toStream map (_.getOutputDirectory)
}
}

/**
* Defines the sbt phase in which the dependency analysis is performed.
* The reason why this is exposed in the callback global is because it's used
* in [[xsbt.LocalToNonLocalClass]] to make sure the we don't resolve local
* classes before we reach this phase.
*/
private[xsbt] val sbtDependency: SubComponent

/**
* A map from local classes to non-local class that contains it.
*
* This map is used by both Dependency and Analyzer phase so it has to be
* exposed here. The Analyzer phase uses the cached lookups performed by
* the Dependency phase. By the time Analyzer phase is run (close to backend
* phases), original owner chains are lost so Analyzer phase relies on
* information saved before.
*
* The LocalToNonLocalClass duplicates the tracking that Scala compiler does
* internally for backed purposes (generation of EnclosingClass attributes) but
* that internal mapping doesn't have a stable interface we could rely on.
*/
private[xsbt] val localToNonLocalClass = new LocalToNonLocalClass[this.type](this)
}

/** Defines the implementation of Zinc with all its corresponding phases. */
sealed class ZincCompiler(settings: Settings, dreporter: DelegatingReporter, output: Output)
extends CallbackGlobal(settings, dreporter, output)
with ZincGlobalCompat {

final class ZincRun(compileProgress: CompileProgress) extends Run {
override def informUnitStarting(phase: Phase, unit: CompilationUnit): Unit =
compileProgress.startUnit(phase.name, unit.source.path)
override def progress(current: Int, total: Int): Unit =
if (!compileProgress.advance(current, total)) cancel else ()
}

object dummy // temporary fix for #4426

/** Phase that analyzes the generated class files and maps them to sources. */
object sbtAnalyzer extends {
val global: ZincCompiler.this.type = ZincCompiler.this
val phaseName = Analyzer.name
val runsAfter = List("jvm")
override val runsBefore = List("terminal")
val runsRightAfter = None
} with SubComponent {
val analyzer = new Analyzer(global)
def newPhase(prev: Phase) = analyzer.newPhase(prev)
def name = phaseName
}

/** Phase that extracts dependency information */
object sbtDependency extends {
val global: ZincCompiler.this.type = ZincCompiler.this
val phaseName = Dependency.name
val runsAfter = List(API.name)
override val runsBefore = List("refchecks")
// Keep API and dependency close to each other -- we may want to merge them in the future.
override val runsRightAfter = Some(API.name)
} with SubComponent {
val dependency = new Dependency(global)
def newPhase(prev: Phase) = dependency.newPhase(prev)
def name = phaseName
}

/**
* Phase that walks the trees and constructs a representation of the public API.
*
* @note It extracts the API information after picklers to see the same symbol information
* irrespective of whether we typecheck from source or unpickle previously compiled classes.
*/
object apiExtractor extends {
val global: ZincCompiler.this.type = ZincCompiler.this
val phaseName = API.name
val runsAfter = List("typer")
override val runsBefore = List("erasure")
// TODO: Consider migrating to "uncurry" for `runsBefore`.
// TODO: Consider removing the system property to modify which phase is used for API extraction.
val runsRightAfter = Option(System.getProperty("sbt.api.phase")) orElse Some("pickler")
} with SubComponent {
val api = new API(global)
def newPhase(prev: Phase) = api.newPhase(prev)
def name = phaseName
}

override lazy val phaseDescriptors = {
phasesSet += sbtAnalyzer
if (callback.enabled()) {
phasesSet += sbtDependency
phasesSet += apiExtractor
}
this.computePhaseDescriptors
}

/** Returns the class file location of a fully qualified name and whether it's on the classpath. */
def findClass(fqn: String): Option[(AbstractFile, Boolean)] = {
def getOutputClass(name: String): Option[AbstractFile] = {
// This could be improved if a hint where to look is given.
val className = name.replace('.', '/') + ".class"
outputDirs.map(new File(_, className)).find((_.exists)).map((AbstractFile.getFile(_)))
}

def findOnClassPath(name: String): Option[AbstractFile] =
classPath.findClass(name).flatMap(_.binary.asInstanceOf[Option[AbstractFile]])

getOutputClass(fqn).map(f => (f, true)).orElse(findOnClassPath(fqn).map(f => (f, false)))
}

private[this] var callback0: AnalysisCallback = null

/** Returns the active analysis callback, set by [[set]] and cleared by [[clear]]. */
def callback: AnalysisCallback = callback0

final def set(callback: AnalysisCallback, dreporter: DelegatingReporter): Unit = {
this.callback0 = callback
reporter = dreporter
}

final def clear(): Unit = {
callback0 = null
superDropRun()
reporter = null
}

// Scala 2.10.x and later
private[xsbt] def logUnreportedWarnings(seq: Seq[(String, List[(Position, String)])]): Unit = {
for ((what, warnings) <- seq; (pos, msg) <- warnings)
yield callback.problem(what, DelegatingReporter.convert(pos), msg, Severity.Warn, false)
()
}
}

import scala.tools.nsc.interactive.RangePositions
final class ZincCompilerRangePos(settings: Settings, dreporter: DelegatingReporter, output: Output)
extends ZincCompiler(settings, dreporter, output)
with RangePositions
Loading