binary hprof を text に convert

hprof file には ascii と binary フォーマットがあるが、これらが
相互変換できると便利だなと思いつつ、差し迫ったことがなかったので
ほっておいたが、今ちょっと必要に迫られている。

OpenGrok で heapDumper.cpp あたりを見るとフォーマットが書かれている。
jhat を使っていて気づいてはいたが、binary format の方が含むデータの
種類は多い。例えば、文字列。でも、ascii format も最近のものにはあるの
かも。

コンバーターは書きかけ。やはり可変長のレコードの処理が面倒だ。


//
// hprofscan.groovy

HPROF_UTF8 = 0x01
HPROF_LOAD_CLASS = 0x02
HPROF_UNLOAD_CLASS = 0x03
HPROF_FRAME = 0x04
HPROF_TRACE = 0x05
HPROF_ALLOC_SITES = 0x06
HPROF_HEAP_SUMMARY = 0x07
HPROF_START_THREAD = 0x0a
HPROF_END_THREAD = 0x0b
HPROF_HEAP_DUMP = 0x0c
HPROF_CPU_SAMPLES = 0x0d
HPROF_CONTROL_SETTINGS = 0x0e
HPROF_LOCKSTATS_WAIT_TIME = 0x10
HPROF_LOCKSTATS_HOLD_TIME = 0x11


HPROF_GC_ROOT_UNKNOWN = 0xff
HPROF_GC_ROOT_JNI_GLOBAL = 0x01
HPROF_GC_ROOT_JNI_LOCAL = 0x02
HPROF_GC_ROOT_JAVA_FRAME = 0x03
HPROF_GC_ROOT_NATIVE_STACK = 0x04
HPROF_GC_ROOT_STICKY_CLASS = 0x05
HPROF_GC_ROOT_THREAD_BLOCK = 0x06
HPROF_GC_ROOT_MONITOR_USED = 0x07
HPROF_GC_ROOT_THREAD_OBJ = 0x08

HPROF_GC_CLASS_DUMP = 0x20
HPROF_GC_INSTANCE_DUMP = 0x21
HPROF_GC_OBJ_ARRAY_DUMP = 0x22
HPROF_GC_PRIM_ARRAY_DUMP = 0x23


verbose = false

hprof_files = []
for ( a in args ) {
if (a == "-v") {
verbose = true
} else {
hprof_files << a
}
// println a
}

if (verbose) {
println "verbose requested."
}

for (hfn in hprof_files) {
File file = new File(hfn)

if ( !file.exists() ) {
println "file ${hfn} does not exist."
continue
}

try {
fis = new DataInputStream(new FileInputStream(file))
magic_str_bytes = new byte[19]
fis.read(magic_str_bytes)
magic = new String(magic_str_bytes, "ASCII")
println magic
pointer_size = fis.readInt()
println "pointer size ${pointer_size}"
tms = fis.readLong()
ts = new Date(tms)
println "ts ${ts}"

while (true) {
tag = fis.readByte()
eltms = fis.readInt()
remaining = fis.readInt()
data = new byte[remaining]
fis.read(data)

if (tag == HPROF_HEAP_DUMP) {
println "${tag} ${eltms} ${remaining}"
idx = 0
while (idx < data.length) {
srt = data[idx]
switch (srt) {
case HPROF_GC_ROOT_UNKNOWN:
println "HPROF_GC_ROOT_UNKNOWN"
idx = idx + pointer_size
break
case HPROF_GC_ROOT_THREAD_OBJ:
println "HPROF_GC_ROOT_THREAD_OBJ"
idx = idx + pointer_size + 4*2
break
case HPROF_GC_ROOT_JNI_GLOBAL:
println "HPROF_GC_ROOT_JNI_GLOBAL"
idx = idx + pointer_size*2
break
case HPROF_GC_ROOT_JNI_LOCAL:
println "HPROF_GC_ROOT_JNI_LOCAL"
idx = idx + pointer_size + 4*2
break
case HPROF_GC_ROOT_JAVA_FRAME:
println "HPROF_GC_ROOT_JAVA_FRAME"
idx = idx + pointer_size + 4*2
break
case HPROF_GC_ROOT_NATIVE_STACK:
println "HPROF_GC_ROOT_NATIVE_STACK"
idx = idx + pointer_size + 4
break
case HPROF_GC_ROOT_STICKY_CLASS:
println "HPROF_GC_ROOT_STICKY_CLASS"
idx = idx + pointer_size
break
case HPROF_GC_ROOT_THREAD_BLOCK:
println "HPROF_GC_ROOT_THREAD_BLOCK"
idx = idx + pointer_size + 4
break
case HPROF_GC_ROOT_MONITOR_USED:
println "HPROF_GC_ROOT_MONITOR_USED"
idx = idx + pointer_size
break
case HPROF_GC_CLASS_DUMP:
println "HPROF_GC_CLASS_DUMP"

:
break
default:
println "ERROR: unknown heap dump sub record type ${srt}"
}
}
}
}

} catch (FileNotFoundException fnfe) {

} catch (EOFException eofe) {

} catch (IOException ioe) {
ioe.printStackTrace()
} finally {
fis.close()
}

/*
file.withReader { reader ->
// println "process file"
magic_str = new byte[19]
reader.read(magic_str)
magic = new String(magic_str, "ASCII")
println magic
}