|
2 | 2 |
|
3 | 3 | ### Background
|
4 | 4 |
|
5 |
| -We have split the UnitTestBot machinery into three processes. This approach has improved UnitTestBot capabilities, e.g. |
6 |
| -provided support for various JVMs and scenarios, but also complicated the debugging flow. |
| 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. |
7 | 7 |
|
8 | 8 | These are UnitTestBot processes (according to the execution order):
|
9 | 9 |
|
10 | 10 | * IDE process
|
11 | 11 | * Engine process
|
12 |
| -* Concrete execution process |
| 12 | +* Instrumented process |
13 | 13 |
|
14 | 14 | Usually, main problems happen in the Engine process, but it is not the process we run first.
|
15 | 15 | The most straightforward way to debug the Engine process is the following.
|
16 | 16 |
|
17 |
| -### Enable debugging for the Engine process |
| 17 | +### Enable Debugging |
18 | 18 |
|
19 |
| -1. Open `org/utbot/framework/UtSettings.kt`. |
20 |
| -2. Set `runIdeaProcessWithDebug` property to _true_. This enables `EngineProcess.debugArgument`. |
21 |
| - * Alternatively you can create `~/.utbot/settings.properties` file and write following: |
| 19 | +IDE debugging is pretty straightforward - start `runIde` task in `utbot-intellij` project from IDEA with debug. |
| 20 | + |
| 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: |
22 | 26 | ```
|
23 |
| - runIdeaProcessWithDebug=true |
| 27 | + runEngineProcessWithDebug=true |
| 28 | + runInstrumentedProcessWithDebug=true |
24 | 29 | ```
|
25 |
| -4. Find `EngineProcess.debugArgument` at `org/utbot/intellij/plugin/process/EngineProcess` and check the parameters of the debug run: |
26 |
| - |
27 |
| - `"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=5005"` |
| 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 | + * `engineProcessDebugSuspendPolicy` and `instrumentedProcessSuspendPolicy` - 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 | + |
| 40 | + ``` |
| 41 | + -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,quiet=y,address=5005" |
| 42 | + ``` |
| 43 | + These options regulate values for parts `suspend` and `address`, for example with following in `~/.utbot/settings.properties`: |
| 44 | + ``` |
| 45 | + runEngineProcessWithDebug=true |
| 46 | + engineProcessDebugPort=12345 |
| 47 | + engineProcessDebugSuspendPolicy=false |
| 48 | + ``` |
| 49 | + result switch will be: |
| 50 | + ``` |
| 51 | + -agentlib:jdwp=transport=dt_socket,server=n,suspend=y,quiet=y,address=12345" |
| 52 | + ``` |
| 53 | + See `org.utbot.intellij.plugin.process.EngineProcess.Companion.getDebugArgument` |
| 54 | +5. For information about logs - see [this](InterProcessLogging.md). |
28 | 55 |
|
29 |
| -* The `suspend` mode is enabled. Modify it in the case of some tricky timeouts in your scenario. |
30 |
| -* The port that will be used for debugging (`address`) is set to `5005`. Modify it if the port is already in use on your system. |
| 56 | +### Run configurations for debugging the Engine process |
31 | 57 |
|
32 |
| -### Create a new run configuration for debugging the Engine process |
| 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 |
33 | 62 |
|
34 |
| -In addition to the `runIde` Gradle task that is supposed to run a new IDE instance, we should create another run |
35 |
| -configuration. |
| 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. |
36 | 66 |
|
37 |
| -1. In your IntelliJ IDEA go to **Ru**n > **Edit configurations…**. |
38 |
| -2. In the **Run/Debug Configuration** dialog, click **`+`** on the toolbar. |
39 |
| -3. In the **Run/Debug Configuration Templates** dialog that opens, select a **Remote JVM Debug** configuration type. |
40 |
| -4. Check that **Port** has the same number as the `address` parameter from the `EngineProcess.debugArgument` mentioned above. |
41 |
| -5. Give the new run configuration a meaningful name and save the run configuration. |
| 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. |
42 | 69 |
|
43 | 70 | ### How to debug
|
44 | 71 |
|
45 |
| -1. In your current IntelliJ IDEA, use breakpoints to define where the program needs to be stopped. For example, set the breakpoints at |
46 |
| - |
47 |
| - `EngineProcess.createTestGenerator()`<br> |
48 |
| - `engineModel().createTestGenerator.startSuspending()` |
| 72 | +Let's see through example of how to debug IDE to engine process communication. |
49 | 73 |
|
50 |
| -2. Start the debugger session (**Shift+F9**) for the `runIde` Gradle task (wait for the debug IDE instance to open). |
51 |
| -3. Generate tests with UnitTestBot in the debug IDE instance. Make sure symbolic execution is turned on. |
| 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. |
52 | 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.
|
53 | 79 | 5. When the Engine process started (build processes have finished, and the progress bar says: _"Generate tests: read
|
54 |
| - classes"_), start the debugger session (**Shift+F9**) for your newly created Remote JVM Debug run configuration. |
55 |
| -6. Wait for the program to be suspended upon reaching the first breakpoint. |
| 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 - часть магии может нахуй не случиться |
56 | 83 |
|
57 | 84 | ### Interprocess call mapping
|
58 | 85 |
|
59 | 86 | Now you are standing on a breakpoint in the IDE process, for example, the process stopped on:
|
60 | 87 |
|
61 |
| - `EngineProcess.createTestGenerator()` |
| 88 | + `EngineProcess.generate()` |
62 | 89 |
|
63 |
| -If you resume the process it reaches the next breakpoint (you are still in the IDE process): |
| 90 | +If you would go along execution, it reaches the next line (you are still in the IDE process): |
64 | 91 |
|
65 |
| - `engineModel().createTestGenerator.startSuspending()` |
| 92 | + `engineModel.generate.startBlocking(params)` |
66 | 93 |
|
67 |
| -It seems that the test generation itself should occur in the Engine process and there should be an outbound point of the IDE process. How can we find it? An how can we reach the inbound point of the Engine process? |
| 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? |
68 | 96 |
|
69 |
| -Standing on the breakpoint` engineModel().createTestGenerator.startSuspending()`, you may **Go to Declaration or |
70 |
| -Usage** and navigate to the definition of `RdCall` (which is responsible for cross-process communication) in `EngineProcessModel.createTestGenerator`. |
| 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`. |
71 | 99 |
|
72 |
| -Now **Find Usages** for `EngineProcessModel.createTestGenerator` and see the point where `RdCall` is passed to the next method: |
| 100 | +Now **Find Usages** for `EngineProcessModel.generate` and see the point where `RdCall` is passed to the next method: |
73 | 101 |
|
74 |
| - synchronizer.measureExecutionForTermination() |
| 102 | + watchdog.wrapActiveCall(generate) |
75 | 103 |
|
76 | 104 | This is the point where `RdCall` is called in the Engine process.
|
77 | 105 |
|
78 | 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.
|
79 | 107 |
|
80 |
| -If you are interested in the trailing lambda of `synchronizer.measureExecutionForTermination()`, set the breakpoint here. |
81 |
| - |
82 |
| -#### Architectural notice |
83 |
| - |
84 |
| -We must place the outbound point of the IDE process and the inbound point of the Engine process as close as possible. |
85 |
| -They may be two lambda-parameters of the same function. In this case we hope that the developer will not spend time on straying around. |
86 |
| - |
| 108 | +If you are interested in the trailing lambda of `watchdog.wrapActiveCall(generate)`, set the breakpoint here. |
0 commit comments