Commit 5d333f12 authored by Kunshan Wang's avatar Kunshan Wang

WIP: write heap/global to file

parent 1d41a37e
package uvm.refimpl.bootimg
import uvm._
import uvm.types._
import uvm.ssavariables._
import java.io._
import java.io.OutputStream
import java.nio.charset.StandardCharsets
import java.nio.file._
import uvm.refimpl._
import uvm.refimpl.mem.TypeSizes.Word
import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.{ Set, Queue }
import uvm.refimpl.itpr.BoxIRef
import scala.collection.immutable.TreeMap
import uvm.refimpl.mem.TypeSizes
import uvm.refimpl.mem.Space
import uvm.refimpl.mem.HeaderUtils
import uvm.refimpl.mem.scanning.RefFieldHandler
import uvm.refimpl.itpr.InterpreterThread
import uvm.refimpl.itpr.BoxStack
import uvm.refimpl.itpr.HasObjRef
import uvm.refimpl.itpr.InterpreterStack
import org.slf4j.LoggerFactory
import com.typesafe.scalalogging.Logger
import uvm.refimpl.mem.scanning.MemoryDataScanner
import uvm._
import uvm.refimpl._
import uvm.refimpl.mem.HeaderUtils
import uvm.refimpl.mem.Space
import uvm.refimpl.mem.TypeSizes
import uvm.refimpl.nat.NativeSupport
import uvm.refimpl.nat.PlatformConstants.Word
import uvm.types._
import uvm.utils.WithUtils.tryWithResource
class BootImageBuilder(implicit microVM: MicroVM) {
def makeBootImage(whiteList: Seq[Int], outputFile: String): Unit = {
implicit val tc = new TransitiveClosureBuilder(whiteList)
implicit val tcb = new TransitiveClosureBuilder(whiteList)
tcb.doTransitiveClosure()
val biw = new BootImageWriter(tcb, outputFile)
biw.writeFile()
}
}
object BootImageWriter {
val logger = Logger(LoggerFactory.getLogger(getClass.getName))
val FILEEXT_DATA = ".data"
val FILEEXT_ALLOC = ".alloc"
val FILEEXT_RELOC = ".reloc"
val IDNAMEMAP_FILE = "idnamemap"
val AS_REF = "R"
val AS_IREF = "I"
val AS_FUNCREF = "F"
val IN_GLOBAL = "G"
val IN_HEAP = "H"
val TO_FUNC = "F"
/**
* Allocation record. For global and heap.
*
* @param num An integer to identify the unit in a .data file. For global, it is the ID of the global cell; for heap
* object, it is a sequential number.
* @param fileOffset The offset from the beginning of the .data file.
* @param ty The type of the allocation unit.
* @param varLen The length of the variable part if it is a hybrid; otherwise 0.
*/
case class UnitAllocRecord(num: Long, fileOffset: Long, ty: Type, varLen: Long)
/**
* Relocation record. One for each field of ref<T>, iref<T>, weakref<T> or funcref<sig> types.
*
* @param num The source allocation unit number.
* @param fieldOffset The offset of the field from the beginning of the allocation unit.
* @param targetKind Whether the target is in the global memory, heap, or is a function.
* @param targetNum To identify the target. For memory, it is the beginning of the allocation unit; for function, it
* is the ID of the function.
* @param targetOffset If the source is an iref<T>, it is the offset from the beginning of the target allocation unit;
* otherwise 0.
*/
case class FieldRelocRecord(num: Long, fieldOffset: Long, fieldKind: String, targetKind: String, targetNum: Long, targetOffset: Long)
class FileGroup(baseName: String, dir: Path) {
val dataFile = dir.resolve(baseName + FILEEXT_DATA)
val allocFile = dir.resolve(baseName + FILEEXT_ALLOC)
val relocFile = dir.resolve(baseName + FILEEXT_RELOC)
val dataOS = new AlignedOutputStream(new BufferedOutputStream(Files.newOutputStream(dataFile)))
val allocRecs = new ArrayBuffer[UnitAllocRecord]
val relocRecs = new ArrayBuffer[FieldRelocRecord]
}
}
class BootImageWriter(tcb: TransitiveClosureBuilder, outputFile: String)(implicit microVM: MicroVM) {
import BootImageWriter._
val tempDir = Files.createTempDirectory("mu-bootimg")
logger.info("Writing bootimg files to temp dir: %s".format(tempDir.toAbsolutePath().toString()))
val globalGroup = new FileGroup("global", tempDir)
val heapGroup = new FileGroup("heap", tempDir)
private val globalMemory: Space = microVM.memoryManager.globalMemory
private val smallObjectSpace: Space = microVM.memoryManager.heap.space
private val largeObjectSpace: Space = microVM.memoryManager.heap.los
private implicit val memorySupport = microVM.memoryManager.memorySupport
def writeFile(): Unit = {
for (addr <- tcb.allocs.set) {
if (globalMemory.isInSpace(addr)) {
writeAllocUnit(addr, globalGroup, isGlobal = true)
} else {
writeAllocUnit(addr, heapGroup, isGlobal = false)
}
}
globalGroup.dataOS.close()
heapGroup.dataOS.close()
writeAllocRecs(globalGroup)
writeAllocRecs(heapGroup)
writeIDNameMap()
}
private def writeAllocUnit(addr: Word, group: FileGroup, isGlobal: Boolean): Unit = {
val tag = HeaderUtils.getTag(addr)
val ty = HeaderUtils.getType(microVM, tag)
val (size, align, varLen) = ty match {
case t: TypeHybrid => {
val len = HeaderUtils.getVarLength(addr)
val sz = TypeSizes.hybridSizeOf(t, len)
val al = TypeSizes.hybridAlignOf(t, len)
(sz, al, len)
}
case t => {
val sz = TypeSizes.sizeOf(t)
val al = TypeSizes.alignOf(t)
(sz, al, 0L)
}
}
val num = if (isGlobal) ty.id else group.allocRecs.length
group.dataOS.alignUp(align)
val pos = group.dataOS.position
group.dataOS.copy(addr, size)
tc.doTransitiveClosure()
val rec = UnitAllocRecord(num, pos, ty, varLen)
group.allocRecs += rec
}
private def writeAllocRecs(group: FileGroup): Unit = {
tryWithResource(Files.newBufferedWriter(group.allocFile, StandardCharsets.UTF_8)) { writer =>
for (ar <- group.allocRecs) {
writer.write("%d,%d,%d,%d,%s\n".format(ar.num, ar.fileOffset, ar.ty.id, ar.varLen, ar.ty.name))
}
}
}
private def writeIDNameMap(): Unit = {
}
}
class AlignedOutputStream(os: OutputStream) extends Closeable {
var position: Long = 0L
private val BUFFER_SZ = 16384
private val buffer = new Array[Byte](BUFFER_SZ)
def alignUp(alignment: Long): Unit = {
val newPos = TypeSizes.alignUp(position, alignment)
position until newPos foreach { _ => os.write(0) }
position = newPos
}
def copy(addr: Long, size: Long): Unit = {
var nextAddr = addr
var remain = size
while (remain > BUFFER_SZ) {
transfer(nextAddr, BUFFER_SZ)
nextAddr += BUFFER_SZ
remain -= BUFFER_SZ
}
if (remain > 0) {
transfer(nextAddr, remain.toInt)
}
}
private def transfer(addr: Long, size: Int): Unit = {
NativeSupport.theMemory.get(addr, buffer, 0, size)
os.write(buffer, 0, size)
}
def close(): Unit = {
os.close()
}
}
\ No newline at end of file
package uvm.utils
/**
* Emulate Java 1.7 try-with-resource in Scala. Unlike Java, res cannot be null.
*/
object WithUtils {
def tryWithResource[T <: AutoCloseable, U](res: T)(f: T => U): U = {
var maybePrimaryExc: Option[Throwable] = None
try {
f(res)
} catch {
case t: Throwable =>
maybePrimaryExc = Some(t)
throw t
} finally {
maybePrimaryExc match {
case None => res.close()
case Some(primaryExc) => try {
res.close()
} catch {
case secondaryExc: Throwable =>
primaryExc.addSuppressed(secondaryExc)
}
}
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment