Recently I have had to debug our server with jdb.
I could not use remote debug or sdb(it require sources on target machine) for security reason.
The most difficult task was to set correct breakpoint( `stop at class_id:line’ ). It was a real challenge for me, to find out anonymous classes and its source lines with javap :)))
So I have done simple utility which allow to do something like:
>jdb -attach jdbconn
>stop in ru.bitec.app.gtk.ExampleDebug$.main
>resume
>eval java.lang.Class.forName("ru.bitec.Jdb")
>print ru.bitec.Jdb.parseClasses("Example")
print ru.bitec.Jdb.parseClasses("Example")
ru.bitec.Jdb.parseClasses("Example") = "
ExampleDebug.scala
0007-0008:ru.bitec.app.gtk.ExampleDebug
0010-0011:ru.bitec.app.gtk.ExampleDebug$$anon$1
ru.bitec.app.gtk.ExampleDebug
0013-0015:ru.bitec.app.gtk.ExampleDebug$$anon$1
0023-0023:ru.bitec.app.gtk.ExampleDebug$
ru.bitec.app.gtk.ExampleDebug
"
>stop at ru.bitec.app.gtk.ExampleDebug$$anon$1:14
I think it will be greate if such helper exists in a standard scala package.
I used asm to do it:
def parseClasses(name:String):mutable.HashMap[String,
mutable.HashMap[Int,mutable.Set[String]]] = {
val classLoader = this.getClass.getClassLoader
val stringBuilder = StringBuilder.newBuilder
val s = new Reflections(ConfigurationBuilder.build(
classLoader,
ClasspathHelper.forPackage("some.root"),new ResourcesScanner()
)).getResources(Pattern.compile(s".*$name.*.class")).asScala.toSet
val map = mutable.HashMap.empty[String,
mutable.HashMap[Int,mutable.Set[String]]]
for(c <-s; s <- managed(classLoader.getResourceAsStream(c))){
val reader = new ClassReader(s)
reader.accept(
new ClassVisitor(Opcodes.ASM7) {
var source:String = _
override def visitSource(source: String, debug: String): Unit = {
this.source = source
map.getOrElseUpdate(source,mutable.HashMap.empty)
super.visitSource(source, debug)
}
override def visitMethod(access: Int, name: String, descriptor: String, signature: String, exceptions: Array[String]): MethodVisitor = {
super.visitMethod(access,name,descriptor,signature,exceptions)
new MethodVisitor(Opcodes.ASM7) {
override def visitLineNumber(line: Int, start: Label): Unit = {
val lineMap = map(source)
val classSet = lineMap.getOrElseUpdate(line, mutable.Set.empty[String])
classSet.add(c)
super.visitLineNumber(line, start)
}
}
}
}, 0)
}
map
}