Skip to content

Commit

Permalink
DBG: Load pretty-printers and stdlib via RsDebugProcessConfigurator
Browse files Browse the repository at this point in the history
  • Loading branch information
artemmukhin committed Aug 2, 2019
1 parent 64c8f60 commit 5048f5b
Show file tree
Hide file tree
Showing 11 changed files with 258 additions and 154 deletions.
@@ -0,0 +1,23 @@
/*
* Use of this source code is governed by the MIT license that can be
* found in the LICENSE file.
*/

package org.rust.debugger.runconfig

import com.intellij.execution.filters.TextConsoleBuilder
import com.intellij.xdebugger.XDebugSession
import com.jetbrains.cidr.execution.RunParameters
import com.jetbrains.cidr.execution.debugger.CidrLocalDebugProcess
import org.rust.cargo.runconfig.CargoRunStateBase

class RsLocalDebugProcess(
parameters: RunParameters,
debugSession: XDebugSession,
consoleBuilder: TextConsoleBuilder
) : CidrLocalDebugProcess(parameters, debugSession, consoleBuilder) {

fun setupDebugSession(state: CargoRunStateBase) {
RsDebugProcessConfigurationHelper(this, state.cargoProject).configure()
}
}
@@ -0,0 +1,2 @@
<idea-plugin>
</idea-plugin>
@@ -0,0 +1,32 @@
/*
* Use of this source code is governed by the MIT license that can be
* found in the LICENSE file.
*/

package org.rust.debugger.runconfig

import com.jetbrains.cidr.execution.debugger.CidrDebugProcess
import com.jetbrains.cidr.execution.debugger.CidrDebugProcessConfigurator
import org.rust.cargo.project.model.cargoProjects

class RsDebugProcessConfigurator : CidrDebugProcessConfigurator {
override fun configure(process: CidrDebugProcess) {
val cargoProject = when {
process is RsLocalDebugProcess -> {
// In case of Rust project, select the corresponding Cargo project
process.runParameters.cargoProject
}
process.project.cargoProjects.hasAtLeastOneValidProject -> {
// In case of cross-language project (e.g. C project with some Rust code inside),
// we actually don't know which Cargo project will be used during execution.
// So any of the available Rust projects can be selected
process.project.cargoProjects.allProjects.firstOrNull()
}
else -> {
// Otherwise, don't configure the debug process for Rust
return
}
}
RsDebugProcessConfigurationHelper(process, cargoProject, isCrossLanguage = true).configure()
}
}
@@ -0,0 +1,20 @@
/*
* Use of this source code is governed by the MIT license that can be
* found in the LICENSE file.
*/

package org.rust.debugger.runconfig

import com.intellij.execution.filters.TextConsoleBuilder
import com.intellij.xdebugger.XDebugSession
import com.jetbrains.cidr.execution.debugger.CidrLocalDebugProcess
import org.rust.cargo.runconfig.CargoRunStateBase

class RsLocalDebugProcess(
val runParameters: RsDebugRunParameters,
debugSession: XDebugSession,
consoleBuilder: TextConsoleBuilder
) : CidrLocalDebugProcess(runParameters, debugSession, consoleBuilder) {

fun setupDebugSession(state: CargoRunStateBase) {}
}
@@ -0,0 +1,5 @@
<idea-plugin>
<extensions defaultExtensionNs="cidr.debugger">
<debugProcessConfigurator implementation="org.rust.debugger.runconfig.RsDebugProcessConfigurator"/>
</extensions>
</idea-plugin>
@@ -0,0 +1,167 @@
/*
* Use of this source code is governed by the MIT license that can be
* found in the LICENSE file.
*/

package org.rust.debugger.runconfig

import com.intellij.notification.NotificationType
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import com.jetbrains.cidr.execution.debugger.CidrDebugProcess
import com.jetbrains.cidr.execution.debugger.backend.DebuggerCommandException
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriver
import com.jetbrains.cidr.execution.debugger.backend.gdb.GDBDriver
import com.jetbrains.cidr.execution.debugger.backend.lldb.LLDBDriver
import org.rust.cargo.project.model.CargoProject
import org.rust.cargo.project.settings.toolchain
import org.rust.cargo.runconfig.command.workingDirectory
import org.rust.debugger.*
import org.rust.debugger.settings.RsDebuggerSettings
import org.rust.ide.notifications.showBalloon
import java.nio.file.InvalidPathException

class RsDebugProcessConfigurationHelper(
private val process: CidrDebugProcess,
cargoProject: CargoProject?,
private val isCrossLanguage: Boolean = false
) {
private val settings = RsDebuggerSettings.getInstance()
private val project = process.project
private val toolchain = process.project.toolchain
private val threadId = process.currentThreadId
private val frameIndex = process.currentFrameIndex

private val commitHash = cargoProject?.rustcInfo?.version?.commitHash

private val sysroot: String? by lazy {
cargoProject?.workingDirectory?.let { toolchain?.getSysroot(it) }
}

fun configure() {
process.postCommand { driver ->
try {
driver.loadRustcSources()
driver.loadPrettyPrinters()
} catch (e: DebuggerCommandException) {
process.printlnToConsole(e.message)
LOG.warn(e)
} catch (e: InvalidPathException) {
LOG.warn(e)
}
}
}

private fun DebuggerDriver.loadRustcSources() {
if (commitHash == null) return

val sysroot = checkSysroot(sysroot, "Cannot load rustc sources") ?: return
val sourceMapCommand = when (this) {
is LLDBDriver -> "settings set target.source-map"
is GDBDriver -> "set substitute-path"
else -> return
}
val rustcHash = "/rustc/$commitHash/".systemDependentAndEscaped()
val rustcSources = "$sysroot/lib/rustlib/src/rust/".systemDependentAndEscaped()
val fullCommand = """$sourceMapCommand "$rustcHash" "$rustcSources" """
executeConsoleCommand(threadId, frameIndex, fullCommand)
}

private fun DebuggerDriver.loadPrettyPrinters() {
when (this) {
is LLDBDriver -> loadPrettyPrinters()
is GDBDriver -> loadPrettyPrinters()
}
}

private fun LLDBDriver.loadPrettyPrinters() {
when (settings.lldbRenderers) {
LLDBRenderers.COMPILER -> {
val sysroot = checkSysroot(sysroot, "Cannot load rustc renderers") ?: return
val path = "$sysroot/lib/rustlib/etc/lldb_rust_formatters.py".systemDependentAndEscaped()
executeConsoleCommand(threadId, frameIndex, """command script import "$path" """)
executeConsoleCommand(threadId, frameIndex, """type summary add --no-value --python-function lldb_rust_formatters.print_val -x ".*" --category Rust""")
executeConsoleCommand(threadId, frameIndex, """type category enable Rust""")
}

LLDBRenderers.BUNDLED -> {
val path = PP_PATH.systemDependentAndEscaped()
executeConsoleCommand(threadId, frameIndex, """command script import "$path/$LLDB_LOOKUP.py" """)

// In case of cross-language projects, lldb pretty-printers should be enabled
// only for specific std types (but should not be enabled for arbitrary struct/enums),
// because `type synthetic add ... -x ".*"` overrides C++ STL pretty-printers
val enabledTypes = if (isCrossLanguage) RUST_STD_TYPES else listOf(".*")
for (type in enabledTypes) {
executeConsoleCommand(threadId, frameIndex, """type synthetic add -l $LLDB_LOOKUP.synthetic_lookup -x "$type" --category Rust""")
executeConsoleCommand(threadId, frameIndex, """type summary add -F $LLDB_LOOKUP.summary_lookup -e -x -h "$type" --category Rust""")
}

executeConsoleCommand(threadId, frameIndex, """type category enable Rust""")
}

LLDBRenderers.NONE -> {
}
}
}

private fun GDBDriver.loadPrettyPrinters() {
when (settings.gdbRenderers) {
GDBRenderers.COMPILER -> {
val sysroot = checkSysroot(sysroot, "Cannot load rustc renderers") ?: return
val path = "$sysroot/lib/rustlib/etc".systemDependentAndEscaped()
// Avoid multiline Python scripts due to https://youtrack.jetbrains.com/issue/CPP-9090
val command = """python """ +
"""sys.path.insert(0, "$path"); """ +
"""import gdb_rust_pretty_printing; """ +
"""gdb_rust_pretty_printing.register_printers(gdb); """
executeConsoleCommand(threadId, frameIndex, command)
}

GDBRenderers.BUNDLED -> {
val path = PP_PATH.systemDependentAndEscaped()
val command = """python """ +
"""sys.path.insert(0, "$path"); """ +
"""import $GDB_LOOKUP; """ +
"""$GDB_LOOKUP.register_printers(gdb); """
executeConsoleCommand(threadId, frameIndex, command)
}

GDBRenderers.NONE -> {
}
}
}

private fun checkSysroot(sysroot: String?, message: String): String? {
if (sysroot == null) {
project.showBalloon(message, NotificationType.WARNING)
}
return sysroot
}

private fun String.systemDependentAndEscaped(): String =
StringUtil.escapeStringCharacters(FileUtil.toSystemDependentName(this))

companion object {
private val LOG: Logger = Logger.getInstance(RsDebugProcessConfigurationHelper::class.java)

private val RUST_STD_TYPES: List<String> = listOf(
"^(alloc::(\\w+::)+)String$",
"^&str$",
"^(std::ffi::(\\w+::)+)OsString$",
"^(alloc::(\\w+::)+)Vec<.+>$",
"^(alloc::(\\w+::)+)VecDeque<.+>$",
"^(alloc::(\\w+::)+)BTreeSet<.+>$",
"^(alloc::(\\w+::)+)BTreeMap<.+>$",
"^(std::collections::(\\w+::)+)HashMap<.+>$",
"^(std::collections::(\\w+::)+)HashSet<.+>$",
"^(alloc::(\\w+::)+)Rc<.+>$",
"^(alloc::(\\w+::)+)Arc<.+>$",
"^(core::(\\w+::)+)Cell<.+>$",
"^(core::(\\w+::)+)Ref<.+>$",
"^(core::(\\w+::)+)RefMut<.+>$",
"^(core::(\\w+::)+)RefCell<.+>$"
)
}
}
Expand Up @@ -13,10 +13,12 @@ import com.jetbrains.cidr.execution.RunParameters
import com.jetbrains.cidr.execution.TrivialInstaller
import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration
import com.jetbrains.cidr.execution.debugger.backend.LLDBDriverConfiguration
import org.rust.cargo.project.model.CargoProject

class RsDebugRunParameters(
val project: Project,
val cmd: GeneralCommandLine
val cmd: GeneralCommandLine,
val cargoProject: CargoProject?
) : RunParameters() {

override fun getInstaller(): Installer = TrivialInstaller(cmd)
Expand Down
Expand Up @@ -24,7 +24,6 @@ import com.jetbrains.cidr.toolchains.OSType
import org.jetbrains.concurrency.AsyncPromise
import org.rust.cargo.runconfig.CargoRunStateBase
import org.rust.cargo.runconfig.RsAsyncRunner
import org.rust.debugger.settings.RsDebuggerSettings

const val ERROR_MESSAGE_TITLE: String = "Debugging is not possible"

Expand All @@ -37,18 +36,13 @@ class RsDebugRunner : RsAsyncRunner(DefaultDebugExecutor.EXECUTOR_ID, ERROR_MESS
environment: ExecutionEnvironment,
runCommand: GeneralCommandLine
): RunContentDescriptor? {
val runParameters = RsDebugRunParameters(environment.project, runCommand)
val runParameters = RsDebugRunParameters(environment.project, runCommand, state.cargoProject)
return XDebuggerManager.getInstance(environment.project)
.startSession(environment, object : XDebugProcessConfiguratorStarter() {
override fun start(session: XDebugSession): XDebugProcess =
RsLocalDebugProcess(runParameters, session, state.consoleBuilder, state::computeSysroot).apply {
RsLocalDebugProcess(runParameters, session, state.consoleBuilder).apply {
ProcessTerminatedListener.attach(processHandler, environment.project)
val settings = RsDebuggerSettings.getInstance()
loadPrettyPrinters(settings.lldbRenderers, settings.gdbRenderers)
val commitHash = state.cargoProject?.rustcInfo?.version?.commitHash
if (commitHash != null) {
loadRustcSources(commitHash)
}
setupDebugSession(state)
start()
}

Expand Down

0 comments on commit 5048f5b

Please sign in to comment.