[K/N] Hide throwable constructor frames from stack traces.

Merge-request: KT-MR-8242
Merged-by: Alexey Glushko <aleksei.glushko@jetbrains.com>
This commit is contained in:
Aleksei.Glushko
2023-01-13 14:14:40 +00:00
committed by Space Team
parent f20e5daa92
commit 20e80401b2
16 changed files with 217 additions and 87 deletions
@@ -734,7 +734,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
private val scope by lazy { private val scope by lazy {
if (!context.shouldContainLocationDebugInfo() || declaration == null) if (!context.shouldContainLocationDebugInfo() || declaration == null)
return@lazy null return@lazy null
declaration.scope() ?: llvmFunction.scope(0, debugInfo.subroutineType(codegen.llvmTargetData, listOf(context.irBuiltIns.intType))) declaration.scope() ?: llvmFunction.scope(0, debugInfo.subroutineType(codegen.llvmTargetData, listOf(context.irBuiltIns.intType)), false)
} }
private val fileScope = (fileScope() as? FileScope) private val fileScope = (fileScope() as? FileScope)
@@ -2189,11 +2189,13 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
else -> codegen.llvmFunctionOrNull(this)?.llvmValue else -> codegen.llvmFunctionOrNull(this)?.llvmValue
} }
return with(debugInfo) { return with(debugInfo) {
val f = this@scope
val nodebug = f is IrConstructor && f.parentAsClass.isSubclassOf(context.irBuiltIns.throwableClass.owner)
if (functionLlvmValue != null) { if (functionLlvmValue != null) {
subprograms.getOrPut(functionLlvmValue) { subprograms.getOrPut(functionLlvmValue) {
memScoped { memScoped {
val subroutineType = subroutineType(codegen.llvmTargetData) val subroutineType = subroutineType(codegen.llvmTargetData)
diFunctionScope(name.asString(), functionLlvmValue.name!!, startLine, subroutineType).also { diFunctionScope(name.asString(), functionLlvmValue.name!!, startLine, subroutineType, nodebug).also {
if (!this@scope.isInline) if (!this@scope.isInline)
DIFunctionAddSubprogram(functionLlvmValue, it) DIFunctionAddSubprogram(functionLlvmValue, it)
} }
@@ -2203,7 +2205,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
inlinedSubprograms.getOrPut(this@scope) { inlinedSubprograms.getOrPut(this@scope) {
memScoped { memScoped {
val subroutineType = subroutineType(codegen.llvmTargetData) val subroutineType = subroutineType(codegen.llvmTargetData)
diFunctionScope(name.asString(), "<inlined-out:$name>", startLine, subroutineType) diFunctionScope(name.asString(), "<inlined-out:$name>", startLine, subroutineType, nodebug)
} }
} as DIScopeOpaqueRef } as DIScopeOpaqueRef
} }
@@ -2212,19 +2214,19 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
private fun LLVMValueRef.scope(startLine:Int, subroutineType: DISubroutineTypeRef): DIScopeOpaqueRef? { private fun LLVMValueRef.scope(startLine:Int, subroutineType: DISubroutineTypeRef, nodebug: Boolean): DIScopeOpaqueRef? {
return debugInfo.subprograms.getOrPut(this) { return debugInfo.subprograms.getOrPut(this) {
diFunctionScope(name!!, name!!, startLine, subroutineType).also { diFunctionScope(name!!, name!!, startLine, subroutineType, nodebug).also {
DIFunctionAddSubprogram(this@scope, it) DIFunctionAddSubprogram(this@scope, it)
} }
} as DIScopeOpaqueRef } as DIScopeOpaqueRef
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
private fun diFunctionScope(name: String, linkageName: String, startLine: Int, subroutineType: DISubroutineTypeRef) = DICreateFunction( private fun diFunctionScope(name: String, linkageName: String, startLine: Int, subroutineType: DISubroutineTypeRef, nodebug: Boolean) = DICreateFunction(
builder = debugInfo.builder, builder = debugInfo.builder,
scope = debugInfo.compilationUnit, scope = debugInfo.compilationUnit,
name = name, name = (if (nodebug) "<NODEBUG>" else "") + name,
linkageName = linkageName, linkageName = linkageName,
file = file().file(), file = file().file(),
lineNo = startLine, lineNo = startLine,
+18 -14
View File
@@ -2735,15 +2735,10 @@ task extend_exception(type: KonanLocalTest) {
source = "runtime/exceptions/extend0.kt" source = "runtime/exceptions/extend0.kt"
} }
standaloneTest("check_stacktrace_format") {
disabled = !supportsCoreSymbolication(project) || project.globalTestArgs.contains('-opt')
flags = ['-g']
source = "runtime/exceptions/check_stacktrace_format.kt"
}
standaloneTest("check_stacktrace_format_coresymbolication") { standaloneTest("check_stacktrace_format_coresymbolication") {
disabled = !supportsCoreSymbolication(project) || project.globalTestArgs.contains('-opt') disabled = !supportsCoreSymbolication(project) || project.globalTestArgs.contains('-opt')
flags = ['-g', '-Xbinary=sourceInfoType=coresymbolication'] flags = ['-g', '-Xbinary=sourceInfoType=coresymbolication']
arguments = ['coresymbolication']
source = "runtime/exceptions/check_stacktrace_format.kt" source = "runtime/exceptions/check_stacktrace_format.kt"
} }
@@ -2751,9 +2746,7 @@ standaloneTest("stack_trace_inline") {
disabled = !supportsCoreSymbolication(project) || project.globalTestArgs.contains('-opt') disabled = !supportsCoreSymbolication(project) || project.globalTestArgs.contains('-opt')
flags = ['-g', '-Xg-generate-debug-trampoline=enable', '-Xbinary=sourceInfoType=coresymbolication'] flags = ['-g', '-Xg-generate-debug-trampoline=enable', '-Xbinary=sourceInfoType=coresymbolication']
source = "runtime/exceptions/stack_trace_inline.kt" source = "runtime/exceptions/stack_trace_inline.kt"
// test on simulator is writing Invalid connection: com.apple.coresymbolicationd by unknown reason arguments = ['coresymbolication']
// so we need to use custom checker as workaround
outputChecker = { str -> str.split("\n").contains("0") }
} }
standaloneTest("kt-49240-stack-trace-completeness") { standaloneTest("kt-49240-stack-trace-completeness") {
@@ -2771,30 +2764,41 @@ standaloneTest("stack_trace_out_of_bounds") {
standaloneTest("kt-37572") { standaloneTest("kt-37572") {
disabled = !supportsCoreSymbolication(project) || project.globalTestArgs.contains('-opt') disabled = !supportsCoreSymbolication(project) || project.globalTestArgs.contains('-opt')
flags = ['-g', '-Xbinary=sourceInfoType=coresymbolication'] flags = ['-g', '-Xbinary=sourceInfoType=coresymbolication']
arguments = ['coresymbolication']
source = "runtime/exceptions/kt-37572.kt" source = "runtime/exceptions/kt-37572.kt"
// test on simulator is writing Invalid connection: com.apple.coresymbolicationd by unknown reason
// so we need to use custom checker as workaround
outputChecker = { str -> str.split("\n").contains("0") }
} }
standaloneTest("check_stacktrace_format_libbacktrace") { standaloneTest("check_stacktrace_format_libbacktrace") {
disabled = !supportsLibBacktrace(project)|| project.globalTestArgs.contains('-opt') disabled = !supportsLibBacktrace(project)|| project.globalTestArgs.contains('-opt')
flags = ['-g', '-Xbinary=sourceInfoType=libbacktrace'] flags = ['-g', '-Xbinary=sourceInfoType=libbacktrace']
arguments = ['libbacktrace']
source = "runtime/exceptions/check_stacktrace_format.kt" source = "runtime/exceptions/check_stacktrace_format.kt"
} }
standaloneTest("stack_trace_inline_libbacktrace") { standaloneTest("stack_trace_inline_libbacktrace") {
disabled = !supportsLibBacktrace(project) || project.globalTestArgs.contains('-opt') disabled = !supportsLibBacktrace(project) || project.globalTestArgs.contains('-opt')
flags = ['-g', '-Xbinary=sourceInfoType=libbacktrace'] flags = ['-g', '-Xbinary=sourceInfoType=libbacktrace']
arguments = ['libbacktrace']
source = "runtime/exceptions/stack_trace_inline.kt" source = "runtime/exceptions/stack_trace_inline.kt"
outputChecker = { str -> str.split("\n").contains("1") }
} }
standaloneTest("kt-37572-libbacktrace") { standaloneTest("kt-37572-libbacktrace") {
disabled = !supportsLibBacktrace(project) || project.globalTestArgs.contains('-opt') disabled = !supportsLibBacktrace(project) || project.globalTestArgs.contains('-opt')
flags = ['-g', '-Xbinary=sourceInfoType=libbacktrace'] flags = ['-g', '-Xbinary=sourceInfoType=libbacktrace']
arguments = ['libbacktrace']
source = "runtime/exceptions/kt-37572.kt" source = "runtime/exceptions/kt-37572.kt"
outputChecker = { str -> str.split("\n").contains("2") } }
standaloneTest("except_constr_w_default") {
disabled = !supportsLibBacktrace(project) || project.globalTestArgs.contains('-opt')
flags = ['-g', '-Xbinary=sourceInfoType=libbacktrace']
source = "runtime/exceptions/except_constr_w_default.kt"
}
standaloneTest("throw_from_except_constr") {
disabled = !supportsLibBacktrace(project) || project.globalTestArgs.contains('-opt')
flags = ['-g', '-Xbinary=sourceInfoType=libbacktrace']
source = "runtime/exceptions/throw_from_except_constr.kt"
} }
standaloneTest("custom_hook") { standaloneTest("custom_hook") {
@@ -3,15 +3,13 @@ import StacktraceByLibbacktrace
func testStackTrace() throws { func testStackTrace() throws {
let trace = StacktraceByLibbacktraceKt.getStackTrace() let trace = StacktraceByLibbacktraceKt.getStackTrace()
try assertTrue(trace[0].contains("Throwable.kt")) try assertTrue(trace[0].contains("stacktraceByLibbacktrace.kt:7"))
try assertTrue(trace[1].contains("Exceptions.kt")) try assertTrue(trace[0].contains("[inlined]"))
try assertTrue(trace[2].contains("stacktraceByLibbacktrace.kt:7")) try assertTrue(trace[1].contains("stacktraceByLibbacktrace.kt:11"))
try assertTrue(trace[2].contains("[inlined]")) try assertTrue(trace[2].contains("stacktraceByLibbacktrace.kt:16"))
try assertTrue(trace[3].contains("stacktraceByLibbacktrace.kt:11")) try assertTrue(trace[3].contains("<compiler-generated>"))
try assertTrue(trace[4].contains("stacktraceByLibbacktrace.kt:16")) try assertTrue(trace[4].contains("stacktraceByLibbacktrace.swift:5"))
try assertTrue(trace[5].contains("<compiler-generated>")) try assertTrue(trace[5].contains("main.swift:126"))
try assertTrue(trace[6].contains("stacktraceByLibbacktrace.swift:5"))
try assertTrue(trace[7].contains("main.swift:126"))
} }
class StacktraceByLibbacktraceTests : TestProvider { class StacktraceByLibbacktraceTests : TestProvider {
@@ -29,9 +29,16 @@ fun functionB() {
functionA() functionA()
} }
const val depth = 5 var depth = 3
fun main(args : Array<String>) { fun main(args : Array<String>) {
val sourceInfoType = args.first()
val exceptionalFrames = when (sourceInfoType) {
"libbacktrace" -> 0
"coresymbolication" -> 2
else -> throw AssertionError("Unknown source info type " + sourceInfoType)
}
depth += exceptionalFrames
try { try {
functionB() functionB()
} catch (e: Throwable) { } catch (e: Throwable) {
@@ -0,0 +1,38 @@
import kotlin.text.Regex
import kotlin.test.*
class CustomException(msg: String = "Exceptional message") : Exception(msg) {
}
fun exception() {
throw CustomException()
}
fun main() {
try {
exception()
}
catch (e:Exception) {
val stackTrace = e.getStackTrace().filter { "kfun:" in it }
println("Kotlin part of call stack is:")
for (entry in stackTrace)
println(entry)
println("Verifying...")
val goldValues = arrayOf(
"kfun:#exception(){}",
"kfun:#main(){}",
)
assertEquals(goldValues.size, stackTrace.size)
goldValues.zip(stackTrace).forEach { checkFrame(it.first, it.second) }
println("Passed")
}
}
internal val regex = Regex("(kfun.+) \\+ (\\d+)")
internal fun checkFrame(goldFunName: String, actualLine: String) {
val findResult = regex.find(actualLine)
val (funName, offset) = findResult?.destructured ?: throw Error("Cannot find '$goldFunName + <int>' in $actualLine")
assertEquals(goldFunName, funName)
assertTrue(offset.toInt() > 0)
}
@@ -1,17 +1,28 @@
import kotlin.text.Regex import kotlin.text.Regex
import kotlin.test.* import kotlin.test.*
var inlinesCount = 0 var expectedInlinesCount = 0
var expectedExceptionContrFrames = 0
fun main() { fun main(args: Array<String>) {
val sourceInfoType = args.first()
val (e, i) = when (sourceInfoType) {
"libbacktrace" -> Pair(0, 2)
"coresymbolication" -> Pair(2, 0)
else -> throw AssertionError("Unknown source info type " + sourceInfoType)
}
expectedExceptionContrFrames = e
expectedInlinesCount = i
var actualInlinesCount = 0
try { try {
foo() foo()
} catch (tw:Throwable) { } catch (tw:Throwable) {
val stackTrace = tw.getStackTrace(); val stackTrace = tw.getStackTrace();
inlinesCount = stackTrace.count { it.contains("[inlined]")} actualInlinesCount = stackTrace.count { it.contains("[inlined]")}
stackTrace.take(6).forEach(::checkFrame) stackTrace.take(expectedExceptionContrFrames + 4).forEach(::checkFrame)
} }
println(inlinesCount) assertEquals(expectedInlinesCount, actualInlinesCount)
} }
fun foo() { fun foo() {
@@ -32,16 +43,15 @@ internal val regex = Regex("^(\\d+)\\ +.*/(.*):(\\d+):.*$")
internal fun checkFrame(value:String) { internal fun checkFrame(value:String) {
val goldValues = arrayOf<Pair<String, Int>?>( val goldValues = arrayOf<Pair<String, Int>?>(
null, *arrayOfNulls(expectedExceptionContrFrames),
null, "kt-37572.kt" to 40,
"kt-37572.kt" to 29, "kt-37572.kt" to 31,
"kt-37572.kt" to 20, *(if (expectedInlinesCount != 0) arrayOf(
*(if (inlinesCount != 0) arrayOf( "kt-37572.kt" to 36,
"kt-37572.kt" to 25, "kt-37572.kt" to 29,
"kt-37572.kt" to 18,
) else emptyArray()), ) else emptyArray()),
"kt-37572.kt" to 8, "kt-37572.kt" to 19,
"kt-37572.kt" to 6) "kt-37572.kt" to 7)
val (pos, file, line) = regex.find(value)!!.destructured val (pos, file, line) = regex.find(value)!!.destructured
goldValues[pos.toInt()]?.let { goldValues[pos.toInt()]?.let {
@@ -1,33 +1,41 @@
import kotlin.text.Regex import kotlin.text.Regex
import kotlin.test.* import kotlin.test.*
var inlinesCount = 0 var expectedInlinesCount = 0
var expectedExceptionContrFrames = 0
fun exception() { fun exception() {
error("FAIL!") error("FAIL!")
} }
fun main() { fun main(args: Array<String>) {
val sourceInfoType = args.first()
val (e, i) = when (sourceInfoType) {
"libbacktrace" -> Pair(0, 1)
"coresymbolication" -> Pair(4, 0)
else -> throw AssertionError("Unknown source info type " + sourceInfoType)
}
expectedExceptionContrFrames = e
expectedInlinesCount = i
var actualInlinesCount = 0
try { try {
exception() exception()
} }
catch (e:Exception) { catch (e:Exception) {
val stackTrace = e.getStackTrace() val stackTrace = e.getStackTrace()
inlinesCount = stackTrace.count { it.contains("[inlined]")} actualInlinesCount = stackTrace.count { it.contains("[inlined]")}
stackTrace.take(6).forEach(::checkFrame) stackTrace.take(expectedExceptionContrFrames + 2).forEach(::checkFrame)
} }
println(inlinesCount) assertEquals(expectedInlinesCount, actualInlinesCount)
} }
internal val regex = Regex("^(\\d+)\\ +.*/(.*):(\\d+):.*$") internal val regex = Regex("^(\\d+)\\ +.*/(.*):(\\d+):.*$")
internal fun checkFrame(value:String) { internal fun checkFrame(value:String) {
val goldValues = arrayOf<Pair<String, Int>?>( val goldValues = arrayOf<Pair<String, Int>?>(
null, *arrayOfNulls(expectedExceptionContrFrames),
null, *arrayOfNulls(expectedInlinesCount),
null, "stack_trace_inline.kt" to 8,
null, "stack_trace_inline.kt" to 23)
*(if (inlinesCount != 0) arrayOf(null) else emptyArray()),
"stack_trace_inline.kt" to 7,
"stack_trace_inline.kt" to 12)
val (pos, file, line) = regex.find(value)!!.destructured val (pos, file, line) = regex.find(value)!!.destructured
goldValues[pos.toInt()]?.let { goldValues[pos.toInt()]?.let {
assertEquals(it.first, file) assertEquals(it.first, file)
@@ -0,0 +1,46 @@
import kotlin.text.Regex
import kotlin.test.*
class CustomException(msg: String) : Exception(msg) {
init {
constrException()
}
fun constrException() {
throw Exception("Exception during exception's construction")
}
}
fun exception() {
throw CustomException("Exceptional message")
}
fun main() {
try {
exception()
}
catch (e:Exception) {
val stackTrace = e.getStackTrace().filter { "kfun:" in it }
println("Kotlin part of call stack is:")
for (entry in stackTrace)
println(entry)
println("Verifying...")
val goldValues = arrayOf(
"kfun:CustomException#constrException(){}",
"kfun:#exception(){}",
"kfun:#main(){}",
)
assertEquals(goldValues.size, stackTrace.size)
goldValues.zip(stackTrace).forEach { checkFrame(it.first, it.second) }
println("Passed")
}
}
internal val regex = Regex("(kfun.+) \\+ (\\d+)")
internal fun checkFrame(goldFunName: String, actualLine: String) {
val findResult = regex.find(actualLine)
val (funName, offset) = findResult?.destructured ?: throw Error("Cannot find '$goldFunName + <int>' in $actualLine")
assertEquals(goldFunName, funName)
assertTrue(offset.toInt() > 0)
}
@@ -86,7 +86,7 @@ unwind (struct _Unwind_Context *context, void *vdata)
--pc; --pc;
if (!bdata->can_alloc) if (!bdata->can_alloc)
bdata->ret = bdata->callback (bdata->data, pc, NULL, 0, 0, NULL); bdata->ret = bdata->callback (bdata->data, pc, NULL, 0, 0, NULL, 0);
else else
bdata->ret = backtrace_pcinfo (bdata->state, pc, bdata->callback, bdata->ret = backtrace_pcinfo (bdata->state, pc, bdata->callback,
bdata->error_callback, bdata->data); bdata->error_callback, bdata->data);
@@ -376,6 +376,8 @@ enum dwarf_unit_type {
DW_UT_hi_user = 0xff DW_UT_hi_user = 0xff
}; };
const char * const KN_nodebug_prefix = "<NODEBUG>";
#if !defined(HAVE_DECL_STRNLEN) || !HAVE_DECL_STRNLEN #if !defined(HAVE_DECL_STRNLEN) || !HAVE_DECL_STRNLEN
/* If strnlen is not declared, provide our own version. */ /* If strnlen is not declared, provide our own version. */
@@ -601,6 +603,8 @@ struct function
/* If this is an inlined function, the column of the call /* If this is an inlined function, the column of the call
site. */ site. */
int caller_column; int caller_column;
/* Whether the funciton should be hidden from user-level debug output (e.g. from Kotlin stack traces). */
int is_nodebug;
/* Map PC ranges to inlined functions. */ /* Map PC ranges to inlined functions. */
struct function_addrs *function_addrs; struct function_addrs *function_addrs;
size_t function_addrs_count; size_t function_addrs_count;
@@ -3675,15 +3679,22 @@ read_function_entry (struct backtrace_state *state, struct dwarf_data *ddata,
case DW_AT_name: case DW_AT_name:
/* Third name preference: don't override. */ /* Third name preference: don't override. */
if (function->name != NULL) {
const char *n = NULL;
if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64,
ddata->is_bigendian,
u->str_offsets_base, &val,
error_callback, data, &n))
return 0;
if (n != NULL) {
if (function->name == NULL)
function->name = n;
/* nodebug prefix may not be mangled in other name variants */
if (strncmp(n, KN_nodebug_prefix, strlen(KN_nodebug_prefix)) == 0)
function->is_nodebug = 1;
}
break; break;
if (!resolve_string (&ddata->dwarf_sections, u->is_dwarf64, }
ddata->is_bigendian,
u->str_offsets_base, &val,
error_callback, data, &function->name))
return 0;
break;
case DW_AT_linkage_name: case DW_AT_linkage_name:
case DW_AT_MIPS_linkage_name: case DW_AT_MIPS_linkage_name:
/* First name preference: override all. */ /* First name preference: override all. */
@@ -3954,7 +3965,7 @@ report_inlined_functions (uintptr_t pc, struct function *function,
return ret; return ret;
/* Report this inlined call. */ /* Report this inlined call. */
ret = callback (data, pc, *filename, *lineno, *column, inlined->name); ret = callback (data, pc, *filename, *lineno, *column, inlined->name, inlined->is_nodebug);
if (ret != 0) if (ret != 0)
return ret; return ret;
@@ -4131,7 +4142,7 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
if (new_data) if (new_data)
return dwarf_lookup_pc (state, ddata, pc, callback, error_callback, return dwarf_lookup_pc (state, ddata, pc, callback, error_callback,
data, found); data, found);
return callback (data, pc, NULL, 0, 0, NULL); return callback (data, pc, NULL, 0, 0, NULL, 0);
} }
/* Search for PC within this unit. */ /* Search for PC within this unit. */
@@ -4178,13 +4189,13 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
entry->u->abs_filename = filename; entry->u->abs_filename = filename;
} }
return callback (data, pc, entry->u->abs_filename, 0, 0, NULL); return callback (data, pc, entry->u->abs_filename, 0, 0, NULL, 0);
} }
/* Search for function name within this unit. */ /* Search for function name within this unit. */
if (entry->u->function_addrs_count == 0) if (entry->u->function_addrs_count == 0)
return callback (data, pc, ln->filename, ln->lineno, ln->column, NULL); return callback (data, pc, ln->filename, ln->lineno, ln->column, NULL, 0);
p = ((struct function_addrs *) p = ((struct function_addrs *)
bsearch (&pc, entry->u->function_addrs, bsearch (&pc, entry->u->function_addrs,
@@ -4192,7 +4203,7 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
sizeof (struct function_addrs), sizeof (struct function_addrs),
function_addrs_search)); function_addrs_search));
if (p == NULL) if (p == NULL)
return callback (data, pc, ln->filename, ln->lineno, ln->column, NULL); return callback (data, pc, ln->filename, ln->lineno, ln->column, NULL, 0);
/* Here pc >= p->low && pc < (p + 1)->low. The function_addrs are /* Here pc >= p->low && pc < (p + 1)->low. The function_addrs are
sorted by low, so if pc > p->low we are at the end of a range of sorted by low, so if pc > p->low we are at the end of a range of
@@ -4216,7 +4227,7 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
--p; --p;
} }
if (fmatch == NULL) if (fmatch == NULL)
return callback (data, pc, ln->filename, ln->lineno, ln->column, NULL); return callback (data, pc, ln->filename, ln->lineno, ln->column, NULL, 0);
function = fmatch->function; function = fmatch->function;
@@ -4229,7 +4240,7 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
if (ret != 0) if (ret != 0)
return ret; return ret;
return callback (data, pc, filename, lineno, column, function->name); return callback (data, pc, filename, lineno, column, function->name, function->is_nodebug);
} }
@@ -4279,7 +4290,7 @@ dwarf_fileline (struct backtrace_state *state, uintptr_t pc,
/* FIXME: See if any libraries have been dlopen'ed. */ /* FIXME: See if any libraries have been dlopen'ed. */
return callback (data, pc, NULL, 0, 0, NULL); return callback (data, pc, NULL, 0, 0, NULL, 0);
} }
/* Initialize our data structures from the DWARF debug info for a /* Initialize our data structures from the DWARF debug info for a
@@ -330,7 +330,7 @@ backtrace_syminfo_to_full_callback (void *data, uintptr_t pc,
{ {
struct backtrace_call_full *bdata = (struct backtrace_call_full *) data; struct backtrace_call_full *bdata = (struct backtrace_call_full *) data;
bdata->ret = bdata->full_callback (bdata->full_data, pc, NULL, 0, 0, symname); bdata->ret = bdata->full_callback (bdata->full_data, pc, NULL, 0, 0, symname, 0);
} }
/* An error callback that corresponds to /* An error callback that corresponds to
@@ -99,7 +99,7 @@ extern struct backtrace_state *backtrace_create_state (
typedef int (*backtrace_full_callback) (void *data, uintptr_t pc, typedef int (*backtrace_full_callback) (void *data, uintptr_t pc,
const char *filename, int lineno, int column, const char *filename, int lineno, int column,
const char *function); const char *function, int is_nodebug);
/* Get a full stack backtrace. SKIP is the number of frames to skip; /* Get a full stack backtrace. SKIP is the number of frames to skip;
passing 0 will start the trace with the function calling passing 0 will start the trace with the function calling
@@ -51,15 +51,16 @@ struct print_data
static int static int
print_callback (void *data, uintptr_t pc, const char *filename, int lineno, int column, print_callback (void *data, uintptr_t pc, const char *filename, int lineno, int column,
const char *function) const char *function, int is_nodebug)
{ {
struct print_data *pdata = (struct print_data *) data; if (!is_nodebug) {
struct print_data *pdata = (struct print_data *) data;
fprintf (pdata->f, "0x%lx %s\n\t%s:%d:%d\n", fprintf (pdata->f, "0x%lx %s\n\t%s:%d:%d\n",
(unsigned long) pc, (unsigned long) pc,
function == NULL ? "???" : function, function == NULL ? "???" : function,
filename == NULL ? "???" : filename, filename == NULL ? "???" : filename,
lineno, column); lineno, column);
}
return 0; return 0;
} }
@@ -24,6 +24,7 @@ class SourceInfo {
public: public:
int lineNumber = -1; int lineNumber = -1;
int column = -1; int column = -1;
bool nodebug = false;
std::string& getFileName() { return fileName; } std::string& getFileName() { return fileName; }
void setFilename(const char *newFileName) { fileName = newFileName ?: ""; } void setFilename(const char *newFileName) { fileName = newFileName ?: ""; }
}; };
@@ -294,10 +294,13 @@ std_support::vector<std_support::string> kotlin::GetStackTraceStrings(std_suppor
int frames_or_overflow = getSourceInfo(address, buffer, std::size(buffer)); int frames_or_overflow = getSourceInfo(address, buffer, std::size(buffer));
int frames = std::min<int>(frames_or_overflow, std::size(buffer)); int frames = std::min<int>(frames_or_overflow, std::size(buffer));
bool isSomethingPrinted = false; bool isSomethingPrinted = false;
bool isSomethingHidden = false;
char line[1024]; char line[1024];
for (int frame = 0; frame < frames; frame++) { for (int frame = 0; frame < frames; frame++) {
auto &sourceInfo = buffer[frame]; auto &sourceInfo = buffer[frame];
if (!sourceInfo.getFileName().empty()) { if (sourceInfo.nodebug) {
isSomethingHidden = true;
} else if (!sourceInfo.getFileName().empty()) {
bool is_last = frame == frames - 1; bool is_last = frame == frames - 1;
if (is_last && frames_or_overflow != frames) { if (is_last && frames_or_overflow != frames) {
snprintf_with_addr(line, sizeof(line) - 1, strings.size(), address, false, "[some inlined frames skipped]"); snprintf_with_addr(line, sizeof(line) - 1, strings.size(), address, false, "[some inlined frames skipped]");
@@ -322,7 +325,7 @@ std_support::vector<std_support::string> kotlin::GetStackTraceStrings(std_suppor
strings.push_back(line); strings.push_back(line);
} }
} }
if (!isSomethingPrinted) { if (!isSomethingPrinted && !isSomethingHidden) {
snprintf_with_addr(line, sizeof(line) - 1, strings.size(), address, false, "%s", ""); snprintf_with_addr(line, sizeof(line) - 1, strings.size(), address, false, "%s", "");
strings.push_back(line); strings.push_back(line);
} }
@@ -23,7 +23,7 @@ extern "C" int Kotlin_getSourceInfo_libbacktrace(void* addr, SourceInfo *result,
callback_arg.result_ptr = 0; callback_arg.result_ptr = 0;
callback_arg.result_size = result_size; callback_arg.result_size = result_size;
callback_arg.total_count = 0; callback_arg.total_count = 0;
auto process_line = [](void *data, uintptr_t pc, const char *filename, int lineno, int column, const char *function) -> int { auto process_line = [](void *data, uintptr_t pc, const char *filename, int lineno, int column, const char *function, int is_nodebug) -> int {
auto &callback_arg = *static_cast<callback_arg_t*>(data); auto &callback_arg = *static_cast<callback_arg_t*>(data);
// Non-inlined frame would be last one, it's better to have it, then intermediate ones // Non-inlined frame would be last one, it's better to have it, then intermediate ones
if (callback_arg.result_ptr == callback_arg.result_size) { if (callback_arg.result_ptr == callback_arg.result_size) {
@@ -33,6 +33,7 @@ extern "C" int Kotlin_getSourceInfo_libbacktrace(void* addr, SourceInfo *result,
info.setFilename(filename); info.setFilename(filename);
info.lineNumber = lineno; info.lineNumber = lineno;
info.column = column; info.column = column;
info.nodebug = is_nodebug;
callback_arg.result_ptr++; callback_arg.result_ptr++;
callback_arg.total_count++; callback_arg.total_count++;
// Let's stop at least at some point // Let's stop at least at some point