Improve symbolicator, generate better debug info (#2488)

This commit is contained in:
Nikolay Igotti
2018-12-21 18:20:53 +03:00
committed by GitHub
parent f78e80ceda
commit 4d6fb2926d
4 changed files with 55 additions and 26 deletions
@@ -89,7 +89,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
} }
fun generateLocationInfo(locationInfo: LocationInfo): DILocationRef? { fun generateLocationInfo(locationInfo: LocationInfo): DILocationRef? {
return LLVMCreateLocation(LLVMGetModuleContext(context.llvmModule), locationInfo.line, locationInfo.line, locationInfo.scope) return LLVMCreateLocation(LLVMGetModuleContext(context.llvmModule), locationInfo.line, locationInfo.column, locationInfo.scope)
} }
val objCDataGenerator = when (context.config.target.family) { val objCDataGenerator = when (context.config.target.family) {
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.konan.CurrentKonanModuleOrigin
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.SourceManager import org.jetbrains.kotlin.ir.SourceManager
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrPropertyDelegateDescriptor import org.jetbrains.kotlin.ir.descriptors.IrPropertyDelegateDescriptor
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
@@ -728,8 +729,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
if (body == null) return if (body == null) return
generateFunction(codegen, declaration, generateFunction(codegen, declaration,
declaration.location(declaration.startLine(), declaration.startColumn()), declaration.location(start = true),
declaration.location(declaration.endLine(), declaration.endColumn())) { declaration.location(start = false)) {
using(FunctionScope(declaration, it)) { using(FunctionScope(declaration, it)) {
val parameterScope = ParameterScope(declaration, functionGenerationContext) val parameterScope = ParameterScope(declaration, functionGenerationContext)
using(parameterScope) { using(parameterScope) {
@@ -755,11 +756,11 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
"${declaration.descriptor.containingDeclaration}::${ir2string(declaration)}") "${declaration.descriptor.containingDeclaration}::${ir2string(declaration)}")
} }
private fun IrFunction.location(line: Int, column:Int) = private fun IrFunction.location(start: Boolean) =
if (context.shouldContainDebugInfo()) LocationInfo( if (context.shouldContainDebugInfo() && startOffset != UNDEFINED_OFFSET) LocationInfo(
scope = scope()!!, scope = scope()!!,
line = line, line = if (start) startLine() else endLine(),
column = column) column = if (start) startColumn() else endColumn())
else null else null
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
@@ -920,8 +921,9 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
* Creates new [ContinuationBlock] that receives the value of given Kotlin type * Creates new [ContinuationBlock] that receives the value of given Kotlin type
* and generates [code] starting from its beginning. * and generates [code] starting from its beginning.
*/ */
private fun continuationBlock(type: IrType, private fun continuationBlock(
locationInfo: LocationInfo?, code: (ContinuationBlock) -> Unit = {}): ContinuationBlock { type: IrType, locationInfo: LocationInfo?, code: (ContinuationBlock) -> Unit = {}): ContinuationBlock {
val entry = functionGenerationContext.basicBlock("continuation_block", locationInfo) val entry = functionGenerationContext.basicBlock("continuation_block", locationInfo)
functionGenerationContext.appendingTo(entry) { functionGenerationContext.appendingTo(entry) {
@@ -1697,7 +1699,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
private open inner class FileScope(val file:IrFile) : InnerScopeImpl() { private open inner class FileScope(val file: IrFile) : InnerScopeImpl() {
override fun fileScope(): CodeContext? = this override fun fileScope(): CodeContext? = this
override fun location(line: Int, column: Int) = scope()?.let {LocationInfo(it, line, column) } override fun location(line: Int, column: Int) = scope()?.let {LocationInfo(it, line, column) }
@@ -1856,17 +1858,19 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
private fun file() = (currentCodeContext.fileScope() as FileScope).file private fun file() = (currentCodeContext.fileScope() as FileScope).file
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
private fun updateBuilderDebugLocation(element: IrElement):DILocationRef? { private fun updateBuilderDebugLocation(element: IrElement): DILocationRef? {
if (!context.shouldContainDebugInfo() || currentCodeContext.functionScope() == null) return null if (!context.shouldContainDebugInfo() || currentCodeContext.functionScope() == null) return null
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return element.startLocation?.let{functionGenerationContext.debugLocation(it)} return element.startLocation?.let{ functionGenerationContext.debugLocation(it) }
} }
private val IrElement.startLocation: LocationInfo? private val IrElement.startLocation: LocationInfo?
get() = currentCodeContext.location(startLine(), startColumn()) get() = if (startOffset == UNDEFINED_OFFSET) null
else currentCodeContext.location(startLine(), startColumn())
private val IrElement.endLocation: LocationInfo? private val IrElement.endLocation: LocationInfo?
get() = currentCodeContext.location(endLine(), endColumn()) get() = if (startOffset == UNDEFINED_OFFSET) null
else currentCodeContext.location(endLine(), endColumn())
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
private fun IrElement.startLine() = file().fileEntry.line(this.startOffset) private fun IrElement.startLine() = file().fileEntry.line(this.startOffset)
@@ -2497,6 +2501,10 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
} }
} }
internal data class LocationInfo(val scope:DIScopeOpaqueRef, internal data class LocationInfo(val scope: DIScopeOpaqueRef,
val line:Int, val line: Int,
val column:Int) val column: Int) {
init {
assert(line != 0)
}
}
@@ -60,6 +60,7 @@ const char* CSSourceInfoGetPath(CSSourceInfoRef info);
const char* CSSourceInfoGetFilename(CSSourceInfoRef info); const char* CSSourceInfoGetFilename(CSSourceInfoRef info);
uint32_t CSSourceInfoGetLineNumber(CSSourceInfoRef info); uint32_t CSSourceInfoGetLineNumber(CSSourceInfoRef info);
uint32_t CSSourceInfoGetColumn(CSSourceInfoRef info);
CSTypeRef CSRetain(CSTypeRef); CSTypeRef CSRetain(CSTypeRef);
@@ -21,6 +21,32 @@ fun archToCpuName(arch: String): cpu_type_t = when (arch) {
else -> TODO("unsupported $arch") else -> TODO("unsupported $arch")
} }
fun sourceInfoString(owner: CValue<CSSymbolOwnerRef>, address: ULong): String? {
var sourceInfo: CValue<CSTypeRef>? = CSSymbolOwnerGetSourceInfoWithAddress(owner, address)
if (CSIsNull(sourceInfo!!)) return null
var lineNumber = CSSourceInfoGetLineNumber(sourceInfo)
var maybe = false
if (lineNumber == 0u) {
// Workaround compiler bug.
sourceInfo = null
for (maybeAddress in address - 10u .. address + 10u) {
val maybeSourceInfo = CSSymbolOwnerGetSourceInfoWithAddress(owner, maybeAddress)
if (CSIsNull(maybeSourceInfo)) continue
var maybeLineNumber = CSSourceInfoGetLineNumber(maybeSourceInfo)
if (maybeLineNumber != 0u) {
lineNumber = maybeLineNumber
sourceInfo = maybeSourceInfo
maybe = true
break
}
}
}
if (sourceInfo == null) return null
val filePath = CSSourceInfoGetPath(sourceInfo)?.toKString() ?: return null
val columnNumber = CSSourceInfoGetColumn(sourceInfo)
return "$filePath:${if (maybe) "~" else ""}$lineNumber:$columnNumber"
}
fun analyzeTrace(program: String, arch: String, input: Sequence<String>) { fun analyzeTrace(program: String, arch: String, input: Sequence<String>) {
val symbolicator = CSSymbolicatorCreateWithPathAndArchitecture(program, archToCpuName(arch)) val symbolicator = CSSymbolicatorCreateWithPathAndArchitecture(program, archToCpuName(arch))
if (CSIsNull(symbolicator)) throw Error("Cannot create \"$arch\" symbolicator for $program") if (CSIsNull(symbolicator)) throw Error("Cannot create \"$arch\" symbolicator for $program")
@@ -39,15 +65,9 @@ fun analyzeTrace(program: String, arch: String, input: Sequence<String>) {
if (!CSIsNull(symbol)) { if (!CSIsNull(symbol)) {
CSSymbolGetRange(symbol).useContents { CSSymbolGetRange(symbol).useContents {
val address = this.location + offsetInFunction val address = this.location + offsetInFunction
val sourceInfo = CSSymbolOwnerGetSourceInfoWithAddress(owner, address) val sourceInfo = sourceInfoString(owner, address)
if (!CSIsNull(sourceInfo)) { if (sourceInfo != null) {
val filePath = CSSourceInfoGetPath(sourceInfo)?.toKString() result = matcher.replaceFirst(line, "$atPart$functionName $sourceInfo")
// or val fileName = CSSourceInfoGetFilename(sourceInfo)?.toKString()
val lineNumber = CSSourceInfoGetLineNumber(sourceInfo)
val lineNumberString = if (lineNumber != 0u) lineNumber.toString() else "<unknown>"
if (filePath != null)
result = matcher.replaceFirst(line,
"$atPart$functionName $filePath:$lineNumberString")
} }
} }
} }