Skip to content

Commit ece8349

Browse files
authored
Interprocess debugging doc reviewed; minor fixes for RD doc (#1465)
1 parent b0b0ebc commit ece8349

File tree

2 files changed

+90
-76
lines changed

2 files changed

+90
-76
lines changed

docs/RD for UnitTestBot.md

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ A `Lifetime` instance has these useful methods:
5757
instance is alive (i.e. will not be terminated) during the whole time of _lambda_ execution.
5858
- `createdNested` creates the _child_ `LifetimeDefinition` instance: it can be terminated if the _parent_
5959
instance is terminated as well; or it can be terminated separately, while the parent instance stays alive.
60-
- `usingNested` is the same as the `createNested` method, but behaves like the `Closeable.use` pattern.
60+
- `usingNested` is the same as the `createNested` method but behaves like the `Closeable.use` pattern.
6161

6262
See also:
6363
- `Lifetime.Eternal` is a global `Lifetime` instance that is never terminated.
@@ -74,8 +74,8 @@ executing all the callbacks because some other thread executes them.
7474

7575
## Rd entities
7676

77-
Rd is a light-weight reactive one-to-one RPC protocol, which is cross-language as well as cross-platform. It can
78-
work on the same or different machines via Internet.
77+
Rd is a lightweight reactive one-to-one RPC protocol, which is cross-language as well as cross-platform. It can
78+
work on the same or different machines via the Internet.
7979

8080
These are some of Rd entities:
8181
- `Protocol` encapsulates the logic of all Rd communications. All the entities should be bound to `Protocol` before
@@ -89,7 +89,7 @@ These are some of Rd entities:
8989
messages from the other process, but also the ones you `fire`.
9090

9191
- `RdProperty` is a stateful property. You can get the current value and advise the callback — an advised
92-
callback is executed on a current value and on every change.
92+
callback is executed on a current value and every change.
9393
- `RdCall` is the remote procedure call.
9494

9595
There are `RdSet`, `RdMap`, and other entities.
@@ -112,11 +112,11 @@ Examples:
112112

113113
First, you need to define a `Root` object: only one instance of each `Root` can be assigned to `Protocol`.
114114

115-
There is a `Root` extension — `Ext(YourRoot)` — where you can define your own types and model entities. You can assign
115+
There is a `Root` extension — `Ext(YourRoot)` — where you can define custom types and model entities. You can assign
116116
multiple `Root` extensions to the `Protocol`. To generate the auxiliary structures, define them as direct fields.
117117

118118
DSL:
119-
- `structdef` is a structure with fields that cannot be bound to `Protocol`, but can be serialized. This structure
119+
- `structdef` is a structure with fields that cannot be bound to `Protocol` but can be serialized. This structure
120120
can be `openstruct`, i.e. open for inheritance, and `basestruct`, i.e. abstract. Only `field` can be a member.
121121
- `classdef` is a class that can be bound to a model. It can have `property`, `signal`, `call`, etc.
122122
as members. It is possible to inherit: the class can be `openclass`, `baseclass`.
@@ -141,7 +141,7 @@ Useful properties in DSL entities:
141141

142142
[Example](https://github.com/korifey/rd_example/blob/main/build.gradle)
143143

144-
`RdGenExtension` configurates `Rdgen`. The properties are:
144+
`RdGenExtension` configures `Rdgen`. The properties are:
145145
- `sources` — the folders with DSL `.kt` files. If there are no `sources`, scan classpath for the inheritors of `Root`
146146
and `Ext`.
147147
- `hashfile` — a folder to store the `.rdgen` hash file for incremental generation.
@@ -153,8 +153,8 @@ Configure model generation with the `RdGenExtension.generator` method:
153153
- `namespace` — which namespace should be used in the generated source. In Kotlin, it configures the generated package
154154
name.
155155
- `directory` — where to put the generated files.
156-
- `transform` — can be `symmetric`, `asis` and `reversed`. It allows to configure different model interfaces for
157-
various client-server scenarios. _Note:_ in 99% of cases you should use `symmetric`. If you really need another option, consult with someone.
156+
- `transform` — can be `symmetric`, `asis`, and `reversed`. It allows configuring of different model interfaces for
157+
various client-server scenarios. _Note:_ in 99% of cases you should use `symmetric`. If you need another option, consult with someone.
158158
- `language` — can be `kotlin`, `cpp` or `csharp`.
159159

160160
## UnitTestBot project
@@ -173,7 +173,7 @@ The _IDE process_ starts the _Engine process_. The _IDE process_ keeps the `UtSe
173173
### Engine process
174174

175175
`TestCaseGenerator` and `UtBotSymbolicEngine` run here, in the _Engine process_. The process classpath contains all
176-
the plugin JAR-files (it uses the plugin classpath).
176+
the plugin JAR files (it uses the plugin classpath).
177177

178178
The _Engine process_ _**must**_ run on the JDK that is used in the project under analysis. Otherwise, there will be
179179
numerous problems with code analysis, `soot`, _Reflection_, and the divergence of the generated code Java API will occur.
@@ -208,28 +208,28 @@ Sometimes the _Instrumented process_ may unexpectedly die due to concrete execut
208208
```
209209
2. There are useful classes in `utbot-rd` to work with Rd and processes:
210210
- `LifetimedProcess` binds a `Lifetime` instance to a process. If the process dies, the `Lifetime` instance
211-
terminates and vice versa. You can terminate the `Lifetime` instance manually — this will destroy the process.
212-
- `ProcessWithRdServer` starts the Rd server and waits for connection.
211+
terminates, and vice versa. You can terminate the `Lifetime` instance manually — this will destroy the process.
212+
- `ProcessWithRdServer` starts the Rd server and waits for the connection.
213213
- `ClientProtocolBuilder` — you can use it in a client process to correctly connect to `ProcessWithRdServer`.
214214
3. How `ProcessWithRdServer` communication works:
215215
- Choose a free port.
216-
- Create a client process, pass the port as an argument.
216+
- Create a client process and pass the port as an argument.
217217
- Both processes create protocols, bind the model and setup callbacks.
218218
- A server process cannot send messages until the _child_ creates a protocol (otherwise, messages are lost), so
219-
the client process have to signal that it is ready.
219+
the client process has to signal that it is ready.
220220
- The client process creates a special file in the `temp` directory, which is observed by a _parent_ process.
221221
- When the parent process spots the file, it deletes this file and sends a special message to the client process
222222
confirming communication success.
223223
- Only when the answer of the client process reaches the server, the processes are ready.
224224
4. How to write custom RPC commands:
225-
- Add new `call` in a model, for example, in `EngineProcessModel`.
225+
- Add a new `call` in a model, for example, in `EngineProcessModel`.
226226
- Re-generate models: there are special Gradle tasks for this in the `utbot-rd/build.gradle` file.
227227
- Add a callback for the new `call` in the corresponding start files, for example, in `EngineProcessMain.kt`.
228228
- **Important**: do not add [`Rdgen`](https://mvnrepository.com/artifact/com.jetbrains.rd/rd-gen) as
229-
an implementation dependency — it breaks some JAR-files as it contains `kotlin-compiler-embeddable`.
229+
an implementation dependency — it breaks some JAR files as it contains `kotlin-compiler-embeddable`.
230230
5. Logging & debugging:
231231
- [Interprocess logging](./contributing/InterProcessLogging.md)
232232
- [Interprocess debugging](./contributing/InterProcessDebugging.md)
233-
6. Custom protocol marshalling types: do not spend time on it until `UtModels` get simpler, e.g. compatible with
233+
6. Custom protocol marshaling types: do not spend time on it until `UtModels` get simpler, e.g. compatible with
234234
`kotlinx.serialization`.
235235

docs/contributing/InterProcessDebugging.md

Lines changed: 73 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,107 +2,121 @@
22

33
### Background
44

5-
We have split the UnitTestBot machinery into three processes. See [doc about processes](../RD%20for%20UnitTestBot.md).
6-
This approach has improved UnitTestBot capabilities, e.g. provided support for various JVMs and scenarios, but also complicated the debugging flow.
5+
We have split the UnitTestBot machinery into three processes. See the [document on UnitTestBot multiprocess
6+
architecture](../RD%20for%20UnitTestBot.md).
7+
This approach has improved UnitTestBot capabilities, e.g., provided support for various JVMs and scenarios but also
8+
complicated the debugging flow.
79

810
These are UnitTestBot processes (according to the execution order):
911

10-
* IDE process
11-
* Engine process
12-
* Instrumented process
12+
* _IDE process_
13+
* _Engine process_
14+
* _Instrumented process_
1315

14-
Usually, main problems happen in the Engine process, but it is not the process we run first.
15-
The most straightforward way to debug the Engine process is the following.
16+
Usually, the main problems happen in the _Engine process_, but it is not the process we run first.
17+
See how to debug UnitTestBot processes effectively.
1618

17-
### Enable Debugging
19+
### Enable debugging
1820

19-
IDE debugging is pretty straightforward - start `runIde` task in `utbot-intellij` project from IDEA with debug.
21+
Debugging the _IDE process_ is pretty straightforward: start the debugger session (**Shift+F9**) for the `runIde`
22+
Gradle task in `utbot-intellij` project from your IntelliJ IDEA.
23+
24+
To debug the _Engine process_ and the _Instrumented process_, you need to enable the debugging options:
25+
1. Open [`UtSettings.kt`](../../utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt).
26+
2. There are two similar options: `runEngineProcessWithDebug` and `runInstrumentedProcessWithDebug` — enable the
27+
relevant one(s). There are two ways to do this:
28+
* You can create the `~/.utbot/settings.properties` file and write the following:
2029

21-
For engine and instrumented processes you need to enable some options:
22-
1. Open [`UtSettings.kt`](../../utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt)
23-
2. There are 2 similar options: `runEngineProcessWithDebug` and `runInstrumentedProcessWithDebug`.
24-
3. Enable for processes you need to debug. It can be done in 2 ways:
25-
* Can create `~/.utbot/settings.properties` file and write following:
2630
```
2731
runEngineProcessWithDebug=true
2832
runInstrumentedProcessWithDebug=true
2933
```
30-
After you will need to restart IDEA you want to debug.
31-
* ***Discouraged***: change in source file, but this will involve moderate project recompilation.
32-
4. Additionally, you can set additional options for JDWP agent if debug is enabled:
33-
* `engineProcessDebugPort` and `instrumentedProcessDebugPort` - port for debugging.
34-
Default values - 5005 for Engine and 5006 for Instrumented processes.
35-
* `suspendEngineProcessExecutionInDebugMode` and `suspendInstrumentedProcessExecutionInDebugMode` - whether JDWP agent should
36-
suspend process until debugger is connected.
37-
38-
More formally, if debug is enabled following switch is added to engine process JVM at start by default:
39-
34+
Then restart the IntelliJ IDEA instance you want to debug.
35+
36+
* **Discouraged**: you can change the options in the source file, but this will involve moderate project
37+
recompilation.
38+
4. You can set additional options for the Java Debug Wire Protocol (JDWP) agent if debugging is enabled:
39+
* `engineProcessDebugPort` and `instrumentedProcessDebugPort` are the ports for debugging.
40+
41+
Default values:
42+
- 5005 for the _Engine process_
43+
- 5006 for the _Instrumented process_
44+
45+
* `suspendEngineProcessExecutionInDebugMode` and `suspendInstrumentedProcessExecutionInDebugMode` define whether
46+
the JDWP agent should suspend the process until the debugger is connected.
47+
48+
More formally, if debugging is enabled, the following switch is added to the _Engine process_ JVM at the start by
49+
default:
4050
```
41-
-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=5005"
51+
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=5005"
4252
```
43-
These options regulate values for parts `suspend` and `address`, for example with following in `~/.utbot/settings.properties`:
53+
54+
These options set `suspend` and `address` values. For example, with the following options in `~/.utbot/settings.properties`:
4455
```
4556
runEngineProcessWithDebug=true
4657
engineProcessDebugPort=12345
4758
suspendEngineProcessExecutionInDebugMode=false
4859
```
49-
result switch will be:
60+
the resulting switch will be:
5061
```
51-
-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,quiet=y,address=12345"
62+
"-agentlib:jdwp=transport=dt_socket,server=n,suspend=n,quiet=y,address=12345"
5263
```
53-
See `org.utbot.intellij.plugin.process.EngineProcess.Companion.getDebugArgument`
54-
5. For information about logs - see [this](InterProcessLogging.md).
64+
See `org.utbot.intellij.plugin.process.EngineProcess.Companion.debugArgument` for switch implementation.
65+
5. For information about logs, refer to the [Interprocess logging](InterProcessLogging.md) guide.
5566

5667
### Run configurations for debugging the Engine process
5768

58-
There are 3 basic run configurations:
59-
1. `Run IDE` - run plugin in IDEA
60-
2. `Utility configuration/Listen for Instrumented Process` - listen on 5006 port if instrumented process is available for debug
61-
3. `Utility configuration/Listen for Engine Process` - listen on 5005 port if engine process is available for debug
69+
There are three basic run configurations:
70+
1. `Run IDE` configuration allows running the plugin in IntelliJ IDEA.
71+
2. `Utility Configurations/Listen for Instrumented Process` configuration allows listening to port 5006 to check if
72+
the _Instrumented process_ is available for debugging.
73+
3. `Utility Configurations/Listen for Engine Process` configuration allows listening to port 5005 to check if the _Engine process_ is available for debugging.
6274

63-
On top of them, there are 3 compound run configurations for debugging:
64-
1. `Debug Engine Process` and `Debug Instrumented Process` - combo for debug IDE and selected process
65-
3. `Debug All` - debug all 3 processes.
75+
On top of them, there are three compound run configurations for debugging:
76+
1. `Debug Engine Process` and `Debug Instrumented Process` — a combination for debugging the _IDE process_ and
77+
the selected process.
78+
3. `Debug All` — a combination for debugging all three processes.
6679

67-
For debug configurations to work you need to provide required properties in `~/.utbot/settings.properties`.
68-
If you either change port and/or suspend mode - do review utility configuration to change default values as well.
80+
To make debug configurations work properly, you need to set the required properties in `~/.utbot/settings.properties`. If you change the _port number_ and/or the _suspend mode_, do change these default values in the corresponding Utility Configuration.
6981

7082
### How to debug
7183

72-
Let's see through example of how to debug IDE to engine process communication.
84+
Let's walk through an example illustrating how to debug the "_IDE process__Engine process_" communication.
7385

74-
1. In your current IntelliJ IDEA with source, use breakpoints to define where the program needs to be stopped. For example, set the breakpoints at `EngineProcess.generate`
75-
and somewhere in `watchdog.wrapActiveCall(generate)`.
76-
2. Select `Debug Engine Process` configuration, add required parameters to `~/.utbot/settings.properties` and start debug.
77-
3. Generate tests with UnitTestBot in the debug IDE instance.
78-
4. The debug IDE instance will stop generation (if you have not changed the debug parameters). If you take no action, test generation will be cancelled by timeout.
79-
5. When the Engine process started (build processes have finished, and the progress bar says: _"Generate tests: read
80-
classes"_), there will be
81-
6. Wait for the program to be suspended upon reaching the first breakpoint in Engine proces.
82-
7. If symbolic execution is not turned on - часть магии может нахуй не случиться
86+
1. In your current IntelliJ IDEA with source code, use breakpoints to define where the program needs to be stopped. For example, set the breakpoints at `EngineProcess.generate` and somewhere in `watchdog.wrapActiveCall(generate)`.
87+
2. Select the `Debug Engine Process` configuration, add the required parameters to `~/.utbot/settings.properties` and
88+
start the debugger session.
89+
3. Generate tests with UnitTestBot in the debug IDE instance. Make sure symbolic execution is turned on, otherwise some processes do not even start.
90+
4. The debug IDE instance will stop generation (if you have not changed the debug parameters). If you take no action, test generation will be canceled by timeout.
91+
5. When the _Engine process_ has started (build processes have finished, and the progress bar says: _"Generate
92+
tests: read classes"_), there will be another debug window — "Listen for Engine Process", — which automatically
93+
connects and starts debugging.
94+
6. Wait for the program to be suspended upon reaching the first breakpoint in the _Engine process_.
8395

8496
### Interprocess call mapping
8597

86-
Now you are standing on a breakpoint in the IDE process, for example, the process stopped on:
98+
Now you are standing on a breakpoint in the _IDE process_, for example, the process stopped on:
8799

88-
`EngineProcess.generate()`
100+
EngineProcess.generate()
89101

90-
If you would go along execution, it reaches the next line (you are still in the IDE process):
102+
If you go along the execution, it reaches the next line (you are still in the _IDE process_):
91103

92-
`engineModel.generate.startBlocking(params)`
104+
engineModel.generate.startBlocking(params)
93105

94-
It seems that the test generation itself should occur in the Engine process and there should be an entry point in the Engine process.
95-
How can we find it?
106+
It seems that test generation itself should occur in the _Engine process_ and there should be an entry point in the _Engine process_.
107+
How can we find it?
96108

97-
Standing on the breakpoint `engineModel.generate.startBlocking(params)`, you may right-click in IDE on `EngineProcessModel.generate` and **Go to Declaration or
98-
Usage**. This would navigate to the definition of `RdCall` (which is responsible for cross-process communication) in file `EngineProcesModel.Generated.kt`.
109+
Standing on the breakpoint at `engineModel.generate.startBlocking(params)`, right-click on
110+
`EngineProcessModel.generate` and **Go to** > **Declaration or Usages**. This navigates to the `RdCall` definition (which is
111+
responsible for cross-process communication) in the `EngineProcesModel.Generated.kt` file.
99112

100113
Now **Find Usages** for `EngineProcessModel.generate` and see the point where `RdCall` is passed to the next method:
101114

102115
watchdog.wrapActiveCall(generate)
103116

104-
This is the point where `RdCall` is called in the Engine process.
117+
This is the point where `RdCall` is called in the _Engine process_.
105118

106-
Actually you could have skipped the previous step and used **Find Usages** right away, but it is useful to know where `RdCall` is defined.
119+
You could have skipped the previous step and used **Find Usages** right away, but it is useful to know
120+
where `RdCall` is defined.
107121

108122
If you are interested in the trailing lambda of `watchdog.wrapActiveCall(generate)`, set the breakpoint here.

0 commit comments

Comments
 (0)